feat(lua): add support for copying closures between Lua states
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
c46edcf370
commit
b986af09ed
5 changed files with 113 additions and 1 deletions
|
@ -12,6 +12,7 @@ Author: Leonardo de Moura
|
||||||
#include "util/debug.h"
|
#include "util/debug.h"
|
||||||
#include "util/exception.h"
|
#include "util/exception.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
|
#include "util/buffer.h"
|
||||||
#include "bindings/lua/leanlua_state.h"
|
#include "bindings/lua/leanlua_state.h"
|
||||||
#include "bindings/lua/util.h"
|
#include "bindings/lua/util.h"
|
||||||
#include "bindings/lua/name.h"
|
#include "bindings/lua/name.h"
|
||||||
|
@ -34,6 +35,30 @@ static void open_patch(lua_State * L);
|
||||||
static void open_state(lua_State * L);
|
static void open_state(lua_State * L);
|
||||||
static void open_thread(lua_State * L);
|
static void open_thread(lua_State * L);
|
||||||
environment & to_environment(lua_State * L, int idx);
|
environment & to_environment(lua_State * L, int idx);
|
||||||
|
static int writer(lua_State *, void const * p, size_t sz, void * buf) {
|
||||||
|
buffer<char> & _buf = *static_cast<buffer<char>*>(buf);
|
||||||
|
char const * in = static_cast<char const *>(p);
|
||||||
|
for (size_t i = 0; i < sz; i++)
|
||||||
|
_buf.push_back(in[i]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
struct reader_data {
|
||||||
|
buffer<char> & m_buffer;
|
||||||
|
bool m_done;
|
||||||
|
reader_data(buffer<char> & b):m_buffer(b), m_done(false) {}
|
||||||
|
};
|
||||||
|
static char const * reader(lua_State *, void * data, size_t * sz) {
|
||||||
|
reader_data & _data = *static_cast<reader_data*>(data);
|
||||||
|
if (_data.m_done) {
|
||||||
|
*sz = 0;
|
||||||
|
return nullptr;
|
||||||
|
} else {
|
||||||
|
*sz = _data.m_buffer.size();
|
||||||
|
_data.m_done = true;
|
||||||
|
return _data.m_buffer.data();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void copy_values(lua_State * src, int first, int last, lua_State * tgt) {
|
static void copy_values(lua_State * src, int first, int last, lua_State * tgt) {
|
||||||
for (int i = first; i <= last; i++) {
|
for (int i = first; i <= last; i++) {
|
||||||
if (lua_isstring(src, i)) {
|
if (lua_isstring(src, i)) {
|
||||||
|
@ -42,6 +67,26 @@ static void copy_values(lua_State * src, int first, int last, lua_State * tgt) {
|
||||||
lua_pushnumber(tgt, lua_tonumber(src, i));
|
lua_pushnumber(tgt, lua_tonumber(src, i));
|
||||||
} else if (lua_isboolean(src, i)) {
|
} else if (lua_isboolean(src, i)) {
|
||||||
lua_pushboolean(tgt, lua_toboolean(src, i));
|
lua_pushboolean(tgt, lua_toboolean(src, i));
|
||||||
|
} else if (lua_isfunction(src, i)) {
|
||||||
|
lua_pushvalue(src, i); // copy function to the top of the stack
|
||||||
|
buffer<char> buffer;
|
||||||
|
if (lua_dump(src, writer, &buffer) != 0)
|
||||||
|
throw exception("falied to copy function between State objects");
|
||||||
|
lua_pop(src, 1); // remove function from the top of the stack
|
||||||
|
reader_data data(buffer);
|
||||||
|
if (load(tgt, reader, &data, "temporary buffer for moving functions between states") != 0)
|
||||||
|
throw exception("falied to copy function between State objects");
|
||||||
|
// copy upvalues
|
||||||
|
int j = 1;
|
||||||
|
while (true) {
|
||||||
|
char const * name = lua_getupvalue(src, i, j);
|
||||||
|
if (name == nullptr)
|
||||||
|
break;
|
||||||
|
copy_values(src, lua_gettop(src), lua_gettop(src), tgt); // copy upvalue to tgt stack
|
||||||
|
lua_pop(src, 1); // remove upvalue from src stack
|
||||||
|
lua_setupvalue(tgt, -2, j);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
} else if (is_expr(src, i)) {
|
} else if (is_expr(src, i)) {
|
||||||
push_expr(tgt, to_expr(src, i));
|
push_expr(tgt, to_expr(src, i));
|
||||||
} else if (is_context(src, i)) {
|
} else if (is_context(src, i)) {
|
||||||
|
@ -233,6 +278,15 @@ int state_dostring(lua_State * L) {
|
||||||
return sz_after - sz_before;
|
return sz_after - sz_before;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int state_set_global(lua_State * L) {
|
||||||
|
auto S = to_state(L, 1).m_ptr;
|
||||||
|
char const * name = luaL_checkstring(L, 2);
|
||||||
|
std::lock_guard<std::mutex> lock(S->m_mutex);
|
||||||
|
copy_values(L, 3, 3, S->m_state);
|
||||||
|
lua_setglobal(S->m_state, name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int state_pred(lua_State * L) {
|
static int state_pred(lua_State * L) {
|
||||||
lua_pushboolean(L, is_state(L, 1));
|
lua_pushboolean(L, is_state(L, 1));
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -241,6 +295,8 @@ static int state_pred(lua_State * L) {
|
||||||
static const struct luaL_Reg state_m[] = {
|
static const struct luaL_Reg state_m[] = {
|
||||||
{"__gc", state_gc},
|
{"__gc", state_gc},
|
||||||
{"dostring", safe_function<state_dostring>},
|
{"dostring", safe_function<state_dostring>},
|
||||||
|
{"eval", safe_function<state_dostring>},
|
||||||
|
{"set", safe_function<state_set_global>},
|
||||||
{0, 0}
|
{0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ class leanlua_state {
|
||||||
std::shared_ptr<imp> m_ptr;
|
std::shared_ptr<imp> m_ptr;
|
||||||
friend class leanlua_thread;
|
friend class leanlua_thread;
|
||||||
friend int state_dostring(lua_State * L);
|
friend int state_dostring(lua_State * L);
|
||||||
|
friend int state_set_global(lua_State * L);
|
||||||
friend int mk_thread(lua_State * L);
|
friend int mk_thread(lua_State * L);
|
||||||
friend int thread_wait(lua_State * L);
|
friend int thread_wait(lua_State * L);
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -42,8 +42,16 @@ bool testudata(lua_State * L, int ud, char const * tname) {
|
||||||
return nullptr; // value is not a userdata with a metatable
|
return nullptr; // value is not a userdata with a metatable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int load(lua_State * L, lua_Reader reader, void * data, char const * source) {
|
||||||
|
#if LUA_VERSION_NUM < 502
|
||||||
|
return lua_load(L, reader, data, source);
|
||||||
|
#else
|
||||||
|
return lua_load(L, reader, data, source, nullptr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
size_t objlen(lua_State * L, int idx) {
|
size_t objlen(lua_State * L, int idx) {
|
||||||
#ifdef LEAN_USE_LUA_OBJLEN
|
#if LUA_VERSION_NUM < 502
|
||||||
return lua_objlen(L, idx);
|
return lua_objlen(L, idx);
|
||||||
#else
|
#else
|
||||||
return lua_rawlen(L, idx);
|
return lua_rawlen(L, idx);
|
||||||
|
|
|
@ -10,6 +10,7 @@ Author: Leonardo de Moura
|
||||||
namespace lean {
|
namespace lean {
|
||||||
void setfuncs(lua_State * L, luaL_Reg const * l, int nup);
|
void setfuncs(lua_State * L, luaL_Reg const * l, int nup);
|
||||||
bool testudata(lua_State * L, int idx, char const * mt);
|
bool testudata(lua_State * L, int idx, char const * mt);
|
||||||
|
int load(lua_State * L, lua_Reader reader, void * data, char const * source);
|
||||||
size_t objlen(lua_State * L, int idx);
|
size_t objlen(lua_State * L, int idx);
|
||||||
void dofile(lua_State * L, char const * fname);
|
void dofile(lua_State * L, char const * fname);
|
||||||
void dostring(lua_State * L, char const * str);
|
void dostring(lua_State * L, char const * str);
|
||||||
|
|
46
tests/lua/st3.lua
Normal file
46
tests/lua/st3.lua
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
S = State()
|
||||||
|
function mk(x)
|
||||||
|
return function (y)
|
||||||
|
return x * y
|
||||||
|
end
|
||||||
|
end
|
||||||
|
f = mk(20)
|
||||||
|
S:eval([[
|
||||||
|
g, x = ...
|
||||||
|
print("x", x)
|
||||||
|
print(g(10))
|
||||||
|
]], f, 10)
|
||||||
|
|
||||||
|
function mkcounter()
|
||||||
|
local x = 0
|
||||||
|
function inc()
|
||||||
|
x = x + 1
|
||||||
|
return x
|
||||||
|
end
|
||||||
|
function dec()
|
||||||
|
x = x - 1
|
||||||
|
return x
|
||||||
|
end
|
||||||
|
return inc, dec
|
||||||
|
end
|
||||||
|
|
||||||
|
inc1, dec1 = mkcounter()
|
||||||
|
print(inc1())
|
||||||
|
print(inc1())
|
||||||
|
print(dec1())
|
||||||
|
print(inc1())
|
||||||
|
print(inc1())
|
||||||
|
-- inc1 and dec1 are closures, they share the same upvalue x.
|
||||||
|
-- However, when we copy inc1 and dec1 to S, we get two copies of x.
|
||||||
|
S:eval([[
|
||||||
|
inc2, dec2, x = ...
|
||||||
|
print("in the nested state")
|
||||||
|
print("x", x)
|
||||||
|
print("incrementing", inc2())
|
||||||
|
print("incrementing", inc2())
|
||||||
|
print("decrementing", dec2())
|
||||||
|
]], inc1, dec1, 10)
|
||||||
|
print(inc1())
|
||||||
|
|
||||||
|
S:set("h", f)
|
||||||
|
print(S:eval([[ return h(2) ]]))
|
Loading…
Add table
Reference in a new issue