Skip to content

Commit

Permalink
Add support for Unicorn 1.0.2's uc_context_free
Browse files Browse the repository at this point in the history
  • Loading branch information
Diego Argueta committed Jan 18, 2021
1 parent e155bdb commit abfed33
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 47 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ language: cpp

env:
global:
- UNICORN_VERSION=1.0.1
- UNICORN_VERSION=1.0.2
matrix:
- LUA_VERSION=5.1
- LUA_VERSION=5.2
Expand Down
15 changes: 13 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
Changes
=======

1.0 (2021-01-18)
----------------
1.1.0 (2021-01-18)
------------------

New Features
~~~~~~~~~~~~

* Added support for Unicorn 1.0.2.
* Context objects now have an instance method, ``free()`` which can be used to
release the context's resources.


1.0.0 (2021-01-18)
------------------

**First stable release!**

Expand Down
13 changes: 13 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,19 @@ Arguments
``values``: A table mapping register IDs to the values to write to those registers.


Contexts
--------

``free()``
~~~~~~~~~~

Release the resources of this context object. It can no longer be used.
Note: (This still works correctly if the library is compiled against Unicorn
1.0.1 and older, before Unicorn added ``uc_context_free()``.)

*New in 1.1.0*


Global Functions
----------------

Expand Down
8 changes: 8 additions & 0 deletions include/unicornlua/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

extern const char * const kContextMetatableName;
extern const luaL_Reg kContextMetamethods[];
extern const luaL_Reg kContextInstanceMethods[];


class Context {
Expand All @@ -40,6 +41,13 @@ class Context {
int ul_context_save(lua_State *L);
int ul_context_restore(lua_State *L);

/** Deallocate a context object.
*
* This function calls `uc_free()` on versions of Unicorn before 1.0.2, and calls
* `uc_context_free()` on 1.0.2+. In either case, it will behave as expected.
*/
int ul_context_free(lua_State *L);


#define get_context_struct(L, index) \
reinterpret_cast<Context *>(luaL_checkudata((L), (index), kContextMetatableName))
Expand Down
3 changes: 3 additions & 0 deletions include/unicornlua/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ class UCLuaEngine {
* there's no reason to keep a context around once it's no longer used inside Lua.
* Thus, there isn't really any use in allowing a @ref Context to be created in the
* heap.
*
* Changed in 1.1.0: This now automatically saves the engine state in the context.
* Before, it was necessary to call `update()` on the returned context object.
*/
Context *create_context_in_lua();
void restore_from_context(Context *context);
Expand Down
3 changes: 3 additions & 0 deletions include/unicornlua/unicornlua.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,7 @@
#error "Library must be compiled against version 1.x of Unicorn."
#endif


#define UNICORNLUA_UNICORN_MAJOR_MINOR_PATCH ((UC_VERSION_MAJOR << 16) | (UC_VERSION_MINOR << 8) | UC_VERSION_EXTRA)

#endif /* INCLUDE_UNICORNLUA_UNICORNLUA_H_ */
36 changes: 32 additions & 4 deletions src/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ const luaL_Reg kContextMetamethods[] = {
};


const luaL_Reg kContextInstanceMethods[] = {
{"free", call_release},
{nullptr, nullptr}
};


Context::Context(UCLuaEngine& engine, uc_context *context)
: engine_(engine), context_(context) {}

Expand Down Expand Up @@ -55,7 +61,21 @@ void Context::update() {


void Context::release() {
uc_err error = uc_free(context_);
uc_err error;

if (context_ == nullptr)
throw LuaBindingError(
"Attempted to free a context object that has already been freed."
);

#if UNICORNLUA_UNICORN_MAJOR_MINOR_PATCH >= 0x010002
/* Unicorn 1.0.2 added its own separate function for freeing contexts. */
error = uc_context_free(context_);
#else
/* Unicorn 1.0.1 and lower uses uc_free(). */
error = uc_free(context_);
#endif

context_ = nullptr;
if (error != UC_ERR_OK)
throw UnicornLibraryError(error);
Expand All @@ -76,10 +96,10 @@ int ul_context_save(lua_State *L) {
// so we can return it to the caller.
context = engine->create_context_in_lua();
}
else
else {
context = get_context_struct(L, 2);

context->update();
context->update();
}
return 1;
}

Expand All @@ -91,3 +111,11 @@ int ul_context_restore(lua_State *L) {
engine->restore_from_context(context);
return 0;
}


int ul_context_free(lua_State *L) {
auto context = get_context_struct(L, 1);

context->release();
return 0;
}
14 changes: 13 additions & 1 deletion src/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,19 @@ Context *UCLuaEngine::create_context_in_lua() {
new (context) Context(*this);

luaL_setmetatable(L, kContextMetatableName);
context->update();
return context;
}


void UCLuaEngine::restore_from_context(Context *context) {
uc_err error = uc_context_restore(engine, context->get_handle());
auto handle = context->get_handle();
if (handle == nullptr)
throw LuaBindingError(
"Attempted to use a context object that has already been freed."
);

uc_err error = uc_context_restore(engine, handle);
if (error != UC_ERR_OK)
throw UnicornLibraryError(error);
}
Expand All @@ -149,6 +156,11 @@ void ul_init_engines_lib(lua_State *L) {

luaL_newmetatable(L, kContextMetatableName);
luaL_setfuncs(L, kContextMetamethods, 0);

lua_newtable(L);
luaL_setfuncs(L, kContextInstanceMethods, 0);
lua_setfield(L, -2, "__index");

lua_pop(L, 2);
}

Expand Down
39 changes: 0 additions & 39 deletions tests/c/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,42 +46,3 @@ TEST_CASE_FIXTURE(AutoclosingEngineFixture, "errno() on clean engine works") {
}

// TODO (dargueta): Force the engine into a bad state to verify ::get_errno()


TEST_CASE_FIXTURE(AutoclosingEngineFixture, "Test creating a context") {
Context *context = uclua_engine->create_context_in_lua();
CHECK_NE(context, nullptr);

CHECK_MESSAGE(
lua_gettop(L) == 1, "Expecting a context object on the stack."
);
CHECK_MESSAGE(
lua_type(L, 1) == LUA_TUSERDATA, "Object at top of stack should be userdata."
);

CHECK_MESSAGE(
lua_touserdata(L, 1) == context,
"TOS isn't the context object we were expecting."
);

// Metatable of the context is at index 2, the expected metatable is at index 3.
CHECK_MESSAGE(
lua_getmetatable(L, 1) != 0, "Context object has no metatable."
);
luaL_getmetatable(L, kContextMetatableName);

CHECK(lua_gettop(L) == 3);

#if LUA_VERSION_NUM < 502
// lua_compare() was added in 5.2, so we have to use lua_equal() here.
CHECK_MESSAGE(
lua_equal(L, 2, 3) == 1,
"Context metatable doesn't match the expected one."
);
#else
CHECK_MESSAGE(
lua_compare(L, 2, 3, LUA_OPEQ) == 1,
"Context metatable doesn't match the expected one."
);
#endif
}

0 comments on commit abfed33

Please sign in to comment.