impl_c_function_support (#170)

Implement C function support
This commit is contained in:
mihacooper 2022-02-23 15:28:43 +03:00 committed by GitHub
parent 2333863a68
commit 925494e7d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 79 additions and 2 deletions

View File

@ -209,6 +209,8 @@ Thread result: 3
Effil allows to transmit data between threads (Lua interpreter states) using `effil.channel`, `effil.table` or directly as parameters of `effil.thread`.
- Primitive types are transmitted 'as is' by copy: `nil`, `boolean`, `number`, `string`
- Functions are dumped using [`lua_dump`](#https://www.lua.org/manual/5.3/manual.html#lua_dump). Upvalues are captured according to [the rules](#functions-upvalues).
- C functions (for which `lua_iscfunction` returns true) are transmitted just by a pointer using `lua_tocfunction` (in original lua_State) and lua_pushcfunction (in new lua_State).
**caution**: in LuaJIT standard functions like tonumber are not real C function, so `lua_iscfunction` returns true but `lua_tocfunction` returns nullptr. Due to that we don't find way to transmit it between lua_States.
- **Userdata and Lua threads (coroutines)** are not supported.
- Tables are serialized to `effil.table` recursively. So, any Lua table becomes `effil.table`. Table serialization may take a lot of time for big table. Thus, it's better to put data directly to `effil.table` avoiding a table serialization. Let's consider 2 examples:
```Lua

View File

@ -108,7 +108,6 @@ class FunctionHolder : public GCObjectHolder<Function> {
public:
template <typename SolType>
FunctionHolder(const SolType& luaObject) : GCObjectHolder<Function>(luaObject) {}
FunctionHolder(GCHandle handle) : GCObjectHolder(handle) {}
sol::object unpack(sol::this_state state) const final {
return GC::instance().get<Function>(handle_).loadFunction(state);
@ -119,6 +118,29 @@ public:
}
};
class CFunctionHolder : public BaseHolder {
public:
CFunctionHolder(sol::state_view state, int stack_index)
: BaseHolder()
{
cfunction_ = lua_tocfunction(state, stack_index);
REQUIRE(cfunction_ != nullptr) << "can't get C function pointer";
}
sol::object unpack(sol::this_state state) const final {
lua_pushcfunction(state, cfunction_);
return sol::stack::pop<sol::object>(state);
}
bool rawCompare(const BaseHolder* other) const override {
const auto cfh = dynamic_cast<const CFunctionHolder*>(other);
return cfh && cfh->cfunction_ == cfunction_;
}
private:
lua_CFunction cfunction_;
};
void dumpTable(SharedTable& target, const sol::table& luaTable, SolTableToShared& visited);
StoredObject makeStoredObject(const sol::object& luaObject, SolTableToShared& visited) {
@ -186,6 +208,11 @@ StoredObject fromSolObject(const SolObject& luaObject, SolTableToShared& visited
else
throw Exception() << "Unable to store userdata object";
case sol::type::function: {
{
auto poper = sol::stack::push_pop(luaObject);
if (lua_iscfunction(luaObject.lua_state(), -1))
return std::make_unique<CFunctionHolder>(luaObject.lua_state(), -1);
}
Function func = GC::instance().create<Function>(luaObject, visited);
return std::make_unique<FunctionHolder>(func.handle());
}

43
tests/lua/function.lua Normal file
View File

@ -0,0 +1,43 @@
require "bootstrap-tests"
test.func.tear_down = default_tear_down
test.func.check_truly_c_functions_p = function(func)
test.equal(type(func), "function")
local t = effil.table()
local ret, _ = pcall(function() t["func"] = func end)
test.equal(ret, true)
end
test.func.check_truly_c_functions_p(coroutine.create)
test.func.check_truly_c_functions_p(effil.size)
test.func.check_tosting_c_functions = function()
local t = effil.table()
if jit then
-- in LuaJIT it's not real C function
local ret, msg = pcall(function() t["tostring"] = tostring end)
test.equal(ret, false)
test.equal(msg, "effil.table: can't get C function pointer")
else
t["tostring"] = tostring
if LUA_VERSION > 51 then
test.equal(t["tostring"], tostring)
else
test.not_equal(t["tostring"], tostring)
end -- LUA_VERSION > 51
test.equal(t["tostring"](123), tostring(123))
test.equal(effil.thread(function() return t["tostring"](123) end)():get(), tostring(123))
local foo = tostring
test.equal(foo, tostring)
test.equal(foo(123), tostring(123))
test.equal(effil.thread(function() return foo(123) end)():get(), tostring(123))
end -- jit
end

View File

@ -1,6 +1,10 @@
#!/usr/bin/env lua
if jit then
print("Using LuaJIT")
else
print("Using " .. _VERSION)
end
local scripts_path = arg[0]:gsub("[\\/]run_tests", "")
local src_path = scripts_path .. "/../.."
@ -17,6 +21,7 @@ require "metatable"
require "type_mismatch"
require "upvalues"
require "dump_table"
require "function"
if os.getenv("STRESS") then
require "channel-stress"