Created
November 29, 2017 14:28
-
-
Save starwing/0e91c2898d3621d41f785f7d927b2b99 to your computer and use it in GitHub Desktop.
luastate -- bind lua to lua.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define LUA_LIB | |
#include <lua.h> | |
#include <lauxlib.h> | |
#include <lualib.h> | |
#include <assert.h> | |
#include <string.h> | |
#define LL_STATE_TYPE "lua.State" | |
#define LL_PROXY_TYPE "lua.Proxy" | |
#define LL_PROXY_TABLE 0x980877AB | |
#define LL_INIT_SPACE 20 | |
#define LL_EXTRA_SPACE 5 | |
#define ll_prepare(L,L2) ll_checkstack((L), (L2), LL_INIT_SPACE) | |
#define ll_state(p) ((lua_State*)(p)->ptr) | |
#define ll_returnself(L) do { lua_settop((L),1); return 1; } while (0) | |
static void ll_setmetafield(lua_State *L, int idx, const char *field) { | |
lua_createtable(L, 0, 1); | |
lua_insert(L, -2); | |
lua_setfield(L, -2, field); | |
lua_setmetatable(L, idx); | |
} | |
static lua_State *ll_mainthread(lua_State *L) { | |
lua_State *ML; | |
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD); | |
ML = lua_tothread(L, -1); | |
lua_pop(L, 1); | |
return ML; | |
} | |
static int ll_traceback(lua_State *L) { | |
const char *msg = lua_tostring(L, 1); | |
if (msg) | |
luaL_traceback(L, L, msg, 1); | |
else if (!lua_isnoneornil(L, 1)) { /* is there an error object? */ | |
if (!luaL_callmeta(L, 1, "__tostring")) /* try its 'tostring' metamethod */ | |
lua_pushliteral(L, "(no error message)"); | |
} | |
return 1; | |
} | |
static const char *ll_dumpstack(lua_State *L, const char *msg) { | |
int i, top = lua_gettop(L); | |
luaL_Buffer b; | |
luaL_buffinit(L, &b); | |
luaL_addstring(&b, "dump stack: "); | |
luaL_addstring(&b, msg != NULL ? msg : ""); | |
luaL_addstring(&b, "\n---------------------------\n"); | |
for (i = 1; i <= top; ++i) { | |
lua_pushfstring(L, "%d: ", i); | |
luaL_addvalue(&b); | |
luaL_tolstring(L, i, NULL); | |
luaL_addvalue(&b); | |
luaL_addstring(&b, "\n"); | |
} | |
luaL_addstring(&b, "---------------------------\n"); | |
luaL_pushresult(&b); | |
return lua_tostring(L, -1); | |
} | |
/* proxy */ | |
typedef struct ll_Proxy { | |
lua_State *L; | |
void *ptr; | |
int ref; | |
int type; | |
} ll_Proxy; | |
static ll_Proxy *ll_rawproxy(lua_State *L, int idx) { | |
ll_Proxy *p = (ll_Proxy*)luaL_testudata(L, idx, LL_PROXY_TYPE); | |
return p ? p : (ll_Proxy*)luaL_testudata(L, idx, LL_STATE_TYPE); | |
} | |
static ll_Proxy *ll_testproxy(lua_State *L, int idx) { | |
ll_Proxy *p = ll_rawproxy(L, idx); | |
luaL_argcheck(L, idx, !p || p->ptr, "invalid proxy object"); | |
return p; | |
} | |
static int ll_typeerror(lua_State *L, int idx, const char *tname) { | |
ll_Proxy *p = ll_rawproxy(L, idx); | |
if (p == NULL) | |
lua_pushfstring(L, "%s expected, got %s", tname, luaL_typename(L, idx)); | |
else if (p->ptr == NULL) | |
lua_pushfstring(L, "%s expected, got invalid %s proxy", | |
tname, lua_typename(L, p->type)); | |
else | |
lua_pushfstring(L, "%s expected, got %s proxy", | |
tname, lua_typename(L, p->type)); | |
return luaL_argerror(L, idx, lua_tostring(L, -1)); | |
} | |
static ll_Proxy *ll_checkproxy(lua_State *L, int idx) { | |
ll_Proxy *p = ll_testproxy(L, idx); | |
if (p == NULL) ll_typeerror(L, idx, "state/proxy"); | |
return p; | |
} | |
static void ll_pushstring(lua_State *to, lua_State *from, int idx) { | |
size_t len; | |
const char *s = lua_tolstring(from, idx, &len); | |
assert(s != NULL); | |
lua_pushlstring(to, s, len); | |
} | |
static void ll_getproxybox(lua_State *L) { | |
if (lua_rawgetp(L, LUA_REGISTRYINDEX, (void*)LL_PROXY_TABLE) != LUA_TTABLE) { | |
lua_pop(L, 1); | |
lua_createtable(L, 0, 1); | |
lua_pushliteral(L, "v"); | |
ll_setmetafield(L, -2, "__mode"); | |
lua_pushvalue(L, -1); | |
lua_rawsetp(L, LUA_REGISTRYINDEX, (void*)LL_PROXY_TABLE); | |
} | |
} | |
static ll_Proxy *ll_makeproxy(lua_State *to, lua_State *from, int idx) { | |
ll_Proxy *p; | |
const void *ptr = lua_topointer(from, idx); | |
ll_getproxybox(to); | |
if (lua_rawgetp(to, -1, ptr) == LUA_TUSERDATA) { | |
lua_remove(to, -2); | |
return (ll_Proxy*)lua_touserdata(to, -1); | |
} | |
assert(ll_mainthread(from) != ll_mainthread(to)); | |
p = lua_newuserdata(to, sizeof(ll_Proxy)); | |
memset(p, 0, sizeof(*p)); | |
luaL_setmetatable(to, LL_PROXY_TYPE); | |
lua_pushvalue(from, idx); | |
p->L = ll_mainthread(from); | |
p->ptr = (void*)ptr; | |
p->ref = luaL_ref(from, LUA_REGISTRYINDEX); | |
p->type = lua_type(from, idx); | |
lua_copy(to, -1, -2); | |
lua_rawsetp(to, -3, ptr); | |
lua_remove(to, -2); | |
return p; | |
} | |
static ll_Proxy *ll_fromproxy(lua_State *to, lua_State *from, int idx) { | |
ll_Proxy *p = ll_rawproxy(from, idx); | |
if (p == NULL) return ll_makeproxy(to, from, idx); | |
lua_rawgeti(p->L, LUA_REGISTRYINDEX, p->ref); | |
if (ll_mainthread(to) != p->L) { | |
ll_makeproxy(to, p->L, -1); | |
lua_pop(p->L, 1); | |
} | |
return p; | |
} | |
static int ll_pushvalue(lua_State *to, lua_State *from, int idx) { | |
assert(from != to); | |
switch (lua_type(from, idx)) { | |
case LUA_TNONE: | |
case LUA_TNIL: | |
lua_pushnil(to); | |
break; | |
case LUA_TBOOLEAN: | |
lua_pushboolean(to, lua_toboolean(from, idx)); | |
break; | |
case LUA_TNUMBER: | |
lua_pushnumber(to, lua_tonumber(from, idx)); | |
break; | |
case LUA_TSTRING: | |
ll_pushstring(to, from, idx); | |
break; | |
case LUA_TUSERDATA: | |
ll_fromproxy(to, from, idx); | |
break; | |
default: | |
ll_makeproxy(to, from, idx); | |
break; | |
} | |
return 1; | |
} | |
static void ll_checkstack(lua_State *L, lua_State *from, int n) { | |
n = n > LL_INIT_SPACE ? n : LL_INIT_SPACE; | |
if (!lua_checkstack(from, n)) | |
luaL_error(L, "too many arguments"); | |
} | |
static int ll_call(lua_State *to, lua_State *from, lua_CFunction f, int args, int rets) { | |
int i, top, base = lua_gettop(from) - args; | |
assert(base >= 0); | |
lua_pushcfunction(from, ll_traceback); | |
lua_pushcfunction(from, f); | |
lua_pushlightuserdata(from, to); | |
if (args) lua_rotate(from, base+1, 3); | |
if (lua_pcall(from, args+1, rets, base+1) != LUA_OK) { | |
ll_pushvalue(to, from, -1); | |
lua_settop(from, base); | |
return lua_error(to); | |
} | |
top = lua_gettop(from); | |
if (!lua_checkstack(to, top - base - 1)) { | |
lua_settop(from, base); | |
return luaL_error(to, "too many return values"); | |
} | |
for (i = base+2; i <= top; ++i) | |
ll_pushvalue(to, from, i); | |
lua_settop(from, base); | |
return top-base-1; | |
} | |
/* proxy routines */ | |
static int ll_binary(lua_State *L, lua_CFunction f, int op) { | |
ll_Proxy *p; | |
if (!(p = ll_testproxy(L, 1)) && !(p = ll_testproxy(L, 2))) | |
return ll_typeerror(L, 1, "proxy"); | |
ll_prepare(L, p->L); | |
lua_pushinteger(p->L, op); | |
return ll_call(L, p->L, f, 1, 1); | |
} | |
static int ll_concat(lua_State *L) { | |
lua_State *from = (lua_State*)lua_touserdata(L, 1); | |
ll_pushvalue(L, from, 1); | |
ll_pushvalue(L, from, 2); | |
lua_concat(L, 2); | |
return 1; | |
} | |
static int ll_op(lua_State *L) { | |
lua_State *from = (lua_State*)lua_touserdata(L, 1); | |
int op = (int)lua_tointeger(L, 2); | |
ll_pushvalue(L, from, 1); | |
ll_pushvalue(L, from, 2); | |
lua_arith(L, op); | |
return 1; | |
} | |
static int ll_cmp(lua_State *L) { | |
lua_State *from = (lua_State*)lua_touserdata(L, 1); | |
int op = (int)lua_tointeger(L, 2); | |
ll_pushvalue(L, from, 1); | |
ll_pushvalue(L, from, 2); | |
lua_pushboolean(L, lua_compare(L, -2, -1, op)); | |
return 1; | |
} | |
static int Lproxy_concat(lua_State *L) { return ll_binary(L, ll_concat, 0); } | |
static int Lproxy_add(lua_State *L) { return ll_binary(L, ll_op, LUA_OPADD); } | |
static int Lproxy_sub(lua_State *L) { return ll_binary(L, ll_op, LUA_OPSUB); } | |
static int Lproxy_mul(lua_State *L) { return ll_binary(L, ll_op, LUA_OPMUL); } | |
static int Lproxy_mod(lua_State *L) { return ll_binary(L, ll_op, LUA_OPMOD); } | |
static int Lproxy_pow(lua_State *L) { return ll_binary(L, ll_op, LUA_OPPOW); } | |
static int Lproxy_div(lua_State *L) { return ll_binary(L, ll_op, LUA_OPDIV); } | |
static int Lproxy_idiv(lua_State *L) { return ll_binary(L, ll_op, LUA_OPIDIV); } | |
static int Lproxy_band(lua_State *L) { return ll_binary(L, ll_op, LUA_OPBAND); } | |
static int Lproxy_bor(lua_State *L) { return ll_binary(L, ll_op, LUA_OPBOR); } | |
static int Lproxy_bxor(lua_State *L) { return ll_binary(L, ll_op, LUA_OPBXOR); } | |
static int Lproxy_shl(lua_State *L) { return ll_binary(L, ll_op, LUA_OPSHL); } | |
static int Lproxy_shr(lua_State *L) { return ll_binary(L, ll_op, LUA_OPSHR); } | |
static int Lproxy_unm(lua_State *L) { return ll_binary(L, ll_op, LUA_OPUNM); } | |
static int Lproxy_bnot(lua_State *L) { return ll_binary(L, ll_op, LUA_OPBNOT); } | |
static int Lproxy_eq(lua_State *L) { return ll_binary(L, ll_cmp, LUA_OPEQ); } | |
static int Lproxy_le(lua_State *L) { return ll_binary(L, ll_cmp, LUA_OPLE); } | |
static int Lproxy_lt(lua_State *L) { return ll_binary(L, ll_cmp, LUA_OPLT); } | |
static int ll_tostring(lua_State *L) { | |
ll_pushvalue(L, (lua_State*)lua_touserdata(L, 1), 1); | |
luaL_tolstring(L, -1, NULL); | |
return 1; | |
} | |
static int ll_len(lua_State *L) { | |
ll_pushvalue(L, (lua_State*)lua_touserdata(L, 1), 1); | |
lua_len(L, 1); | |
return 1; | |
} | |
static int Lproxy_tostring(lua_State *L) { | |
ll_Proxy *p = ll_rawproxy(L, 1); | |
if (p->ptr == NULL) | |
lua_pushfstring(L, "%s[D]: %p", lua_typename(L, p->type), p->ptr); | |
else { | |
ll_prepare(L, p->L); | |
ll_call(L, p->L, ll_tostring, 0, 1); | |
} | |
lua_pushfstring(L, " (" LL_STATE_TYPE ": %p[%d])", p->L, p->ref); | |
lua_concat(L, 2); | |
return 1; | |
} | |
static int Lproxy_len(lua_State *L) { | |
ll_Proxy *p = ll_checkproxy(L, 1); | |
if (p->type == LUA_TTHREAD) { | |
lua_pushinteger(L, lua_gettop((lua_State*)p->ptr)); | |
return 1; | |
} | |
ll_prepare(L, p->L); | |
return ll_call(L, p->L, ll_len, 0, 1); | |
} | |
static int ll_gettable(lua_State *L) { | |
lua_State *from = (lua_State*)lua_touserdata(L, 1); | |
ll_Proxy *p = lua_touserdata(from, 1); | |
if (p && p->type == LUA_TTHREAD) | |
lua_pushglobaltable(L); | |
else | |
ll_pushvalue(L, from, 1); | |
ll_pushvalue(L, from, 2); | |
lua_gettable(L, -2); | |
return 1; | |
} | |
static int ll_settable(lua_State *L) { | |
lua_State *from = (lua_State*)lua_touserdata(L, 1); | |
ll_Proxy *p = lua_touserdata(from, 1); | |
if (p && p->type == LUA_TTHREAD) | |
lua_pushglobaltable(L); | |
else | |
ll_pushvalue(L, from, 1); | |
ll_pushvalue(L, from, 2); | |
ll_pushvalue(L, from, 3); | |
lua_gettable(L, -3); | |
return 0; | |
} | |
static int ll_callp(lua_State *L) { | |
lua_State *from = (lua_State*)lua_touserdata(L, 1); | |
int start = (int)lua_tointeger(L, 2); | |
int i, top = lua_gettop(from); | |
luaL_checkstack(L, top, "too many arguments"); | |
lua_settop(L, 0); | |
lua_pushcfunction(L, ll_traceback); | |
if (start == 0) start = 1; | |
for (i = start; i <= top; ++i) | |
ll_pushvalue(L, from, i); | |
if (lua_pcall(L, top-start, LUA_MULTRET, 1) != LUA_OK) | |
lua_error(L); | |
return lua_gettop(L) - 1; | |
} | |
static int Lproxy_index(lua_State *L) { | |
ll_Proxy *p = ll_checkproxy(L, 1); | |
lua_State *from = p->L; | |
if (lua_getmetatable(L, 1)) { | |
lua_pushvalue(L, 2); | |
if (lua_rawget(L, -2) != LUA_TNIL) return 1; | |
} | |
if (p->type == LUA_TTHREAD) { | |
int isint, idx = (int)lua_tointegerx(L, 2, &isint); | |
from = (lua_State*)p->ptr; | |
if (isint) { | |
int top = lua_gettop(from); | |
if (idx > top || idx < -top) | |
luaL_argerror(L, 2, "idx out of bound"); | |
return ll_pushvalue(L, from, idx); | |
} | |
} | |
ll_prepare(L, p->L); | |
return ll_call(L, from, ll_gettable, 0, 1); | |
} | |
static int Lproxy_newindex(lua_State *L) { | |
ll_Proxy *p = ll_checkproxy(L, 1); | |
lua_State *from = p->L; | |
if (p->type == LUA_TTHREAD) { | |
int isint, idx = (int)lua_tointegerx(L, 2, &isint); | |
from = (lua_State*)p->ptr; | |
if (isint) { | |
int top = lua_gettop(from); | |
if (idx < -top) luaL_argerror(L, 2, "idx out of bound"); | |
if (idx > top) { | |
if (!lua_checkstack(from, idx-top)) | |
luaL_error(L, "too many values"); | |
lua_settop(from, idx-1); | |
ll_pushvalue(from, L, 3); | |
return 0; | |
} | |
if (!lua_checkstack(from, 1)) | |
luaL_error(L, "Operation failure"); | |
ll_pushvalue(L, from, idx); | |
lua_replace(from, idx); | |
return 0; | |
} | |
} | |
ll_prepare(L, p->L); | |
return ll_call(L, from, ll_gettable, 0, 0); | |
} | |
static int Lproxy_call(lua_State *L) { | |
ll_Proxy *p = ll_checkproxy(L, 1); | |
ll_prepare(L, p->L); | |
return ll_call(L, p->L, ll_callp, 0, LUA_MULTRET); | |
} | |
static int Lproxy_gc(lua_State *L) { | |
ll_Proxy *p = (ll_Proxy*)luaL_testudata(L, 1, LL_PROXY_TYPE); | |
if (p && p->ptr) { | |
ll_getproxybox(L); | |
lua_pushnil(L); | |
lua_rawsetp(L, -2, p->ptr); | |
if (lua_checkstack(p->L, 1)) { | |
lua_rawgeti(p->L, LUA_REGISTRYINDEX, p->ref); | |
luaL_unref(p->L, LUA_REGISTRYINDEX, p->ref); | |
} | |
p->L = NULL; | |
p->ptr = NULL; | |
p->ref = LUA_NOREF; | |
} | |
return 0; | |
} | |
static void open_proxy(lua_State *L) { | |
luaL_Reg libs[] = { | |
{ "__gc", Lproxy_gc }, | |
{ "__index", Lproxy_index }, | |
{ "__newindex", Lproxy_newindex }, | |
{ "__tostring", Lproxy_tostring }, | |
{ "__concat", Lproxy_concat }, | |
{ "__len", Lproxy_len }, | |
{ "__call", Lproxy_call }, | |
{ "__add", Lproxy_add }, | |
{ "__sub", Lproxy_sub }, | |
{ "__mul", Lproxy_mul }, | |
{ "__mod", Lproxy_mod }, | |
{ "__pow", Lproxy_pow }, | |
{ "__div", Lproxy_div }, | |
{ "__idiv", Lproxy_idiv }, | |
{ "__band", Lproxy_band }, | |
{ "__bor", Lproxy_bor }, | |
{ "__bxor", Lproxy_bxor }, | |
{ "__shl", Lproxy_shl }, | |
{ "__shr", Lproxy_shr }, | |
{ "__unm", Lproxy_unm }, | |
{ "__bnot", Lproxy_bnot }, | |
{ "__eq", Lproxy_eq }, | |
{ "__le", Lproxy_le }, | |
{ "__lt", Lproxy_lt }, | |
{ NULL, NULL } | |
}; | |
luaL_newmetatable(L, LL_PROXY_TYPE); | |
luaL_setfuncs(L, libs, 0); | |
} | |
/* global routines */ | |
static lua_State *ll_testthread(lua_State *L, int idx, ll_Proxy **pp) { | |
ll_Proxy *p = ll_testproxy(L, idx); | |
if (pp) *pp = p; | |
return p && p->type == LUA_TTHREAD ? (lua_State*)p->ptr : NULL; | |
} | |
static lua_State *ll_checkthread(lua_State *L, int idx, ll_Proxy **pp) { | |
lua_State *ret = ll_testthread(L, idx, pp); | |
if (ret == NULL) ll_typeerror(L, idx, "state/thread proxy"); | |
return (lua_State*)ret; | |
} | |
static int ll_checkindex(lua_State *L, int idx, int top, int onstack) { | |
int n = (int)luaL_checkinteger(L, idx); | |
luaL_argcheck(L, idx, top >= onstack, "no enough arguments"); | |
if (n <= LUA_REGISTRYINDEX) return n; /* pesudo index */ | |
luaL_argcheck(L, idx, n <= top-onstack && n >= -top+onstack, | |
"index out of bound"); | |
return n; | |
} | |
static int ll_rawget(lua_State *L) { | |
lua_State *from = (lua_State*)lua_touserdata(L, 1); | |
ll_pushvalue(L, from, 1); | |
ll_pushvalue(L, from, 2); | |
lua_rawget(L, -2); | |
return 0; | |
} | |
static int ll_rawset(lua_State *L) { | |
lua_State *from = (lua_State*)lua_touserdata(L, 1); | |
ll_pushvalue(L, from, 1); | |
ll_pushvalue(L, from, 2); | |
ll_pushvalue(L, from, 3); | |
lua_rawset(L, -3); | |
return 0; | |
} | |
static int Lrawget(lua_State *L) { | |
ll_Proxy *p; | |
lua_State *from = ll_testthread(L, 1, &p); | |
if (from) { | |
int idx = ll_checkindex(L, 2, lua_gettop(from), 1); | |
int ret = lua_toboolean(L, 3); | |
luaL_argcheck(L, 2, lua_type(from, idx) == LUA_TTABLE, | |
"table request on index"); | |
lua_rawget(from, idx); | |
if (ret) { ll_pushvalue(L, from, -1); return 1; } | |
ll_returnself(L); | |
} | |
if (p->type == LUA_TTABLE) { | |
ll_prepare(L, p->L); | |
return ll_call(L, p->L, ll_rawget, 0, 1); | |
} | |
return ll_typeerror(L, 1, "state/table/thread p"); | |
} | |
static int Lrawset(lua_State *L) { | |
ll_Proxy *p = ll_checkproxy(L, 1); | |
lua_State *from = p->L; | |
if (p->type != LUA_TTHREAD && p->type != LUA_TTABLE) | |
ll_typeerror(L, 1, "table/thread p"); | |
if (p->type == LUA_TTHREAD) { | |
int idx = ll_checkindex(L, 2, lua_gettop(from = ll_state(p)), 2); | |
luaL_argcheck(L, 2, lua_type(from, idx) == LUA_TTABLE, | |
"table request on index"); | |
ll_pushvalue(L, from, idx); | |
lua_replace(L, 2); | |
} | |
ll_prepare(L, p->L); | |
ll_call(L, from, ll_rawset, 0, 0); | |
ll_returnself(L); | |
} | |
static int Lrawlen(lua_State *L) { | |
ll_Proxy *p = ll_checkproxy(L, 1); | |
if (p->type == LUA_TTHREAD) { | |
lua_State *from = ll_state(p); | |
int idx = ll_checkindex(L, 2, lua_gettop(from), 0); | |
luaL_argcheck(L, 2, lua_type(from, idx) == LUA_TTABLE, | |
"table request on index"); | |
lua_pushinteger(L, lua_rawlen(from, idx)); | |
return 1; | |
} | |
if (p->type == LUA_TTABLE) { | |
if (!lua_checkstack(p->L, 1)) | |
luaL_error(L, "Operation failure"); | |
lua_rawgeti(p->L, LUA_REGISTRYINDEX, p->ref); | |
lua_pushinteger(L, lua_rawlen(p->L, -1)); | |
lua_pop(p->L, 1); | |
return 1; | |
} | |
return ll_typeerror(L, 1, "state/table/thread p"); | |
} | |
static int Lrawequal(lua_State *L) { | |
ll_Proxy *p = ll_checkproxy(L, 1); | |
if (p->type == LUA_TTHREAD) { | |
lua_State *from = ll_state(p); | |
if (lua_type(L, 2) == LUA_TNUMBER) { | |
int n1, n2, top = lua_gettop(from); | |
n1 = ll_checkindex(L, 2, top, 0); | |
n2 = ll_checkindex(L, 3, top, 0); | |
lua_pushboolean(L, lua_rawequal(from, n1, n2)); | |
return 1; | |
} | |
} | |
lua_pushboolean(L, p->ptr == ll_checkproxy(L, 2)->ptr); | |
return 1; | |
} | |
static int Lgettable(lua_State *L) { | |
ll_Proxy *p = ll_checkproxy(L, 1); | |
lua_State *from = p->L; | |
if (p->type == LUA_TTHREAD) { | |
int idx = ll_checkindex(L, 2, lua_gettop(from = ll_state(p)), 2); | |
ll_pushvalue(L, from, idx); | |
lua_replace(L, 2); | |
} | |
ll_prepare(L, p->L); | |
ll_call(L, from, ll_gettable, 0, 0); | |
ll_returnself(L); | |
} | |
static int Lsettable(lua_State *L) { | |
ll_Proxy *p = ll_checkproxy(L, 1); | |
lua_State *from = p->L; | |
if (p->type == LUA_TTHREAD) { | |
int idx = ll_checkindex(L, 2, lua_gettop(from = ll_state(p)), 2); | |
ll_pushvalue(L, from, idx); | |
lua_replace(L, 2); | |
} | |
ll_prepare(L, p->L); | |
ll_call(L, from, ll_settable, 0, 0); | |
ll_returnself(L); | |
} | |
static int Llen(lua_State *L) { | |
ll_Proxy *p = ll_checkproxy(L, 1); | |
lua_State *from = p->L; | |
if (p->type == LUA_TTHREAD) { | |
int idx = ll_checkindex(L, 2, lua_gettop(from = ll_state(p)), 2); | |
ll_pushvalue(L, from, idx); | |
lua_replace(L, 1); | |
} | |
ll_prepare(L, p->L); | |
return ll_call(L, from, ll_len, 0, 1); | |
} | |
static int Ltype(lua_State *L) { | |
ll_Proxy *p = ll_checkproxy(L, 1); | |
int isint, idx = (int)lua_tointegerx(L, 2, &isint); | |
if (p->type != LUA_TTHREAD || !isint) | |
lua_pushstring(L, lua_typename(L, p->type)); | |
else { | |
lua_State *from = (lua_State*)p->ptr; | |
int top = lua_gettop(from); | |
if (idx > top || idx < -top) | |
lua_pushstring(L, "none"); | |
else | |
lua_pushstring(L, luaL_typename(from, idx)); | |
} | |
return 1; | |
} | |
typedef struct ll_LoadS { | |
int first; | |
size_t len; | |
const char *s; | |
} ll_LoadS; | |
static const char *ll_reader(lua_State *L, void *ud, size_t *plen) { | |
ll_LoadS *ctx = (ll_LoadS*)ud; | |
const char *s = ctx->s; | |
int first = ctx->first; | |
ctx->first = 0; | |
if (s && s[0] == '=' && first) { | |
++ctx->s, --ctx->len, *plen = 7; | |
return "return "; | |
} | |
*plen = ctx->len; | |
ctx->len = 0; | |
ctx->s = NULL; | |
return s; | |
} | |
static int Lcall(lua_State *L) { | |
lua_State *from = ll_checkthread(L, 1, NULL); | |
ll_LoadS ctx = { 1 }; | |
ctx.s = lua_tolstring(L, 2, &ctx.len); | |
if (ctx.s != NULL) { | |
int ret = lua_load(from, ll_reader, &ctx, "<chunk>", "t"); | |
ll_pushvalue(L, from, -1); | |
lua_pop(from, 1); | |
if (ret != LUA_OK) lua_error(L); | |
lua_replace(L, 2); | |
} | |
ll_prepare(L, from); | |
lua_pushinteger(from, 2); | |
return ll_call(L, from, ll_callp, 1, LUA_MULTRET); | |
} | |
static int Lcheckstack(lua_State *L) { | |
lua_State *from = ll_checkthread(L, 1, NULL); | |
int len = (int)luaL_checkinteger(L, 2); | |
if (!lua_checkstack(from, len)) | |
luaL_argerror(L, 2, "too many values"); | |
ll_returnself(L); | |
} | |
static int Ldumpstack(lua_State *L) { | |
lua_State *from = ll_checkthread(L, 1, NULL); | |
const char *msg = luaL_optstring(L, 2, NULL); | |
if (!lua_checkstack(from, 1)) | |
luaL_error(L, "Operation failure"); | |
ll_dumpstack(from, msg); | |
ll_pushvalue(L, from, -1); | |
lua_pop(from, 1); | |
return 1; | |
} | |
static int Labsindex(lua_State *L) { | |
lua_State *from = ll_checkthread(L, 1, NULL); | |
lua_pushinteger(L, lua_absindex(from, (int)luaL_checkinteger(L, 2))); | |
return 1; | |
} | |
static int Ltop(lua_State *L) { | |
lua_State *from = ll_checkthread(L, 1, NULL); | |
if (lua_isnoneornil(L, 2)) { | |
lua_pushinteger(L, lua_gettop(from)); | |
return 1; | |
} | |
lua_settop(from, (int)luaL_checkinteger(L, 2)); | |
ll_returnself(L); | |
} | |
static int Lpushindex(lua_State *L) { | |
lua_State *from = ll_checkthread(L, 1, NULL); | |
int idx = (int)luaL_checkinteger(L, 2); | |
int top = lua_gettop(from); | |
if (idx > top || idx < -top) | |
lua_pushnil(from); | |
else | |
lua_pushvalue(from, idx); | |
ll_returnself(L); | |
} | |
static int Lrotate(lua_State *L) { | |
lua_State *from = ll_checkthread(L, 1, NULL); | |
int idx = ll_checkindex(L, 2, lua_gettop(from), 0); | |
int n = (int)luaL_checkinteger(L, 3); | |
n = n % lua_gettop(from); | |
lua_rotate(from, idx, n); | |
ll_returnself(L); | |
} | |
static int Lcopy(lua_State *L) { | |
lua_State *from = ll_checkthread(L, 1, NULL); | |
int top = lua_gettop(from); | |
int n1 = ll_checkindex(L, 2, top, 0); | |
int n2 = ll_checkindex(L, 3, top, 0); | |
lua_copy(from, n1, n2); | |
ll_returnself(L); | |
} | |
static int Lxmove(lua_State *L) { | |
ll_Proxy *p1, *p2; | |
lua_State *from = ll_checkthread(L, 1, &p1); | |
lua_State *to = ll_checkthread(L, 2, &p2); | |
int n = (int)luaL_checkinteger(L, 3); | |
int top = lua_gettop(from); | |
luaL_argcheck(L, 2, p1->L == p2->L, | |
"attempt to xmove values in same state"); | |
luaL_argcheck(L, 2, p1->ptr != p2->ptr, | |
"attempt to xmove values from same thread"); | |
if (!lua_checkstack(to, n)) | |
luaL_error(L, "Operation failure"); | |
lua_xmove(from, to, n > top ? top : n); | |
if (n > top) lua_settop(to, n-top); | |
ll_returnself(L); | |
} | |
static int Lpush(lua_State *L) { | |
lua_State *from = ll_checkthread(L, 1, NULL); | |
ll_pushvalue(from, L, 2); | |
ll_returnself(L); | |
} | |
static int Lpop(lua_State *L) { | |
lua_State *from = ll_checkthread(L, 1, NULL); | |
int n = (int)luaL_checkinteger(L, 2); | |
lua_pop(from, n); | |
ll_returnself(L); | |
} | |
/* interface */ | |
static lua_State *ll_checkstate(lua_State *L, int idx) { | |
ll_Proxy *p = (ll_Proxy*)luaL_checkudata(L, idx, LL_STATE_TYPE); | |
if (p->ptr == NULL) luaL_argerror(L, idx, "invalid lua state"); | |
assert(p->L == p->ptr); | |
return p->L; | |
} | |
static ll_Proxy *ll_new(lua_State *L) { | |
ll_Proxy *p = (ll_Proxy*)lua_newuserdata(L, sizeof(ll_Proxy)); | |
memset(p, 0, sizeof(*p)); | |
if ((p->L = luaL_newstate()) == NULL) | |
luaL_error(L, "can not create new lua state"); | |
p->ptr = p->L; | |
p->ref = LUA_RIDX_MAINTHREAD; | |
p->type = LUA_TTHREAD; | |
luaL_setmetatable(L, LL_STATE_TYPE); | |
ll_getproxybox(L); | |
lua_pushvalue(L, -2); | |
lua_rawsetp(L, -2, p->ptr); | |
lua_pop(L, 1); | |
return p; | |
} | |
static void ll_delete(lua_State *L, ll_Proxy *p) { | |
lua_State *from = (lua_State*)p->ptr; | |
ll_getproxybox(L); | |
lua_pushnil(L); | |
while (lua_next(L, -2)) { | |
ll_Proxy *v = (ll_Proxy*)lua_touserdata(L, -1); | |
if (v && v->ptr && v->L == from) v->ptr = NULL; | |
lua_pop(L, 1); | |
} | |
lua_close(from); | |
assert(p->ptr == NULL); | |
} | |
static int ll_newtable(lua_State *L) { | |
int na = (int)lua_tointeger(L, -2); | |
int nh = (int)lua_tointeger(L, -1); | |
lua_createtable(L, na, nh); | |
return 1; | |
} | |
static int ll_newthread(lua_State *L) { | |
lua_newthread(L); | |
return 1; | |
} | |
static int ll_newuserdata(lua_State *L) { | |
lua_State *from = (lua_State*)lua_touserdata(L, 1); | |
const char *s = NULL; | |
void *ud; | |
int isint; | |
size_t len = (size_t)lua_tointegerx(from, 2, &isint); | |
if (!isint) s = lua_tolstring(L, -1, &len); | |
ud = lua_newuserdata(L, len); | |
memset(ud, 0, len); | |
if (s) memcpy(ud, s, len); | |
return 1; | |
} | |
static int Lnew(lua_State *L) { | |
ll_Proxy *p = ll_new(L); | |
luaL_openlibs(p->L); | |
open_proxy(p->L); | |
lua_settop(p->L, 0); | |
return 1; | |
} | |
static int Lclose(lua_State *L) { | |
ll_Proxy *p = (ll_Proxy*)luaL_testudata(L, 1, LL_STATE_TYPE); | |
if (p->ptr != NULL) ll_delete(L, p); | |
return 0; | |
} | |
static int Ltostring(lua_State *L) { | |
ll_Proxy *p = (ll_Proxy*)luaL_checkudata(L, 1, LL_STATE_TYPE); | |
if (p->ptr == NULL) | |
lua_pushfstring(L, LL_STATE_TYPE "[D]: %p", p->ptr); | |
else | |
lua_pushfstring(L, LL_STATE_TYPE ": %p", p->ptr); | |
return 1; | |
} | |
static int Lnewtable(lua_State *L) { | |
lua_State *from = ll_checkstate(L, 1); | |
int na = (int)luaL_optinteger(L, 2, 0); | |
int nh = (int)luaL_optinteger(L, 3, 0); | |
ll_prepare(L, from); | |
lua_pushinteger(from, na); | |
lua_pushinteger(from, nh); | |
return ll_call(L, from, ll_newtable, 2, 1); | |
} | |
static int Lnewthread(lua_State *L) { | |
lua_State *from = ll_checkstate(L, 1); | |
ll_prepare(L, from); | |
return ll_call(L, from, ll_newthread, 0, 1); | |
} | |
static int Lnewuserdata(lua_State *L) { | |
lua_State *from = ll_checkstate(L, 1); | |
int type = lua_type(L, 2); | |
if (type != LUA_TNUMBER && type != LUA_TSTRING) | |
ll_typeerror(L, 2, "number/string"); | |
ll_prepare(L, from); | |
return ll_call(L, from, ll_newuserdata, 0, 1); | |
} | |
static int Luserdata(lua_State *L) { | |
ll_Proxy *p = ll_checkproxy(L, 1); | |
size_t len, size; void *data; | |
const char *s = luaL_optlstring(L, 2, NULL, &len); | |
if (p->type != LUA_TUSERDATA) | |
ll_typeerror(L, 1, "userdata proxy"); | |
if (!lua_checkstack(p->L, 1)) | |
luaL_error(L, "Operation failure"); | |
lua_rawgeti(p->L, LUA_REGISTRYINDEX, p->ref); | |
size = lua_rawlen(p->L, -1); | |
data = lua_touserdata(p->L, -1); | |
lua_pop(p->L, 1); | |
if (lua_isnoneornil(L, 2)) { | |
lua_pushlstring(L, (const char*)data, len); | |
return 1; | |
} | |
memcpy(data, s, size < len+1 ? size : len+1); | |
ll_returnself(L); | |
} | |
static int Lversion(lua_State *L) { | |
lua_State *from = ll_checkstate(L, 1); | |
lua_pushnumber(L, *lua_version(from)); | |
return 1; | |
} | |
LUALIB_API int luaopen_luastate(lua_State *L) { | |
luaL_Reg libs[] = { | |
{ "__gc", Lclose }, | |
{ "__index", Lproxy_index }, | |
{ "__newindex", Lproxy_newindex }, | |
{ "__tostring", Ltostring }, | |
{ "__len", Lproxy_len }, | |
{ "__call", Lcall }, | |
#define ENTRY(name) { #name, L##name } | |
ENTRY(new), | |
ENTRY(close), | |
ENTRY(newtable), | |
ENTRY(newthread), | |
ENTRY(newuserdata), | |
ENTRY(version), | |
ENTRY(userdata), | |
/* replacement global routines */ | |
/*ENTRY(error),*/ | |
/*ENTRY(next),*/ | |
/*ENTRY(collectgarbage),*/ | |
/*ENTRY(load),*/ | |
ENTRY(type), | |
ENTRY(rawget), | |
ENTRY(rawset), | |
ENTRY(rawlen), | |
ENTRY(rawequal), | |
ENTRY(gettable), | |
ENTRY(settable), | |
ENTRY(len), | |
/* stack manipulation */ | |
ENTRY(checkstack), | |
ENTRY(dumpstack), | |
ENTRY(top), | |
ENTRY(push), | |
ENTRY(pop), | |
ENTRY(absindex), | |
ENTRY(pushindex), | |
ENTRY(rotate), | |
ENTRY(copy), | |
ENTRY(xmove), | |
/* coroutines */ | |
/*ENTRY(yield),*/ | |
/*ENTRY(resume),*/ | |
/*ENTRY(status),*/ | |
/*ENTRY(isyieldable),*/ | |
/* misc */ | |
/*ENTRY(createtable),*/ | |
/*ENTRY(getmetatable),*/ | |
/*ENTRY(getuservalue),*/ | |
/*ENTRY(setmettable),*/ | |
/*ENTRY(setuservalue),*/ | |
#undef ENTRY | |
{ NULL, NULL } | |
}; | |
open_proxy(L); | |
if (luaL_newmetatable(L, LL_STATE_TYPE)) { | |
luaL_setfuncs(L, libs, 0); | |
lua_pushstring(L, LUA_VERSION); | |
lua_setfield(L, -2, "version"); | |
lua_pushstring(L, LUA_RELEASE); | |
lua_setfield(L, -2, "release"); | |
lua_pushstring(L, LUA_COPYRIGHT); | |
lua_setfield(L, -2, "copyright"); | |
lua_pushstring(L, LUA_AUTHORS); | |
lua_setfield(L, -2, "authors"); | |
/*lua_pushstring(L, lua_ident); XXX*/ | |
/*lua_setfield(L, -2, "ident");*/ | |
} | |
return 1; | |
} | |
/* win32cc: flags+='-ggdb -O2 -mdll -DLUA_BUILD_AS_DLL' | |
* win32cc: libs+='-llua53' output='luastate.dll' | |
* unixcc: flags+='-ggdb -Wall -O2 -shared -undefined dynamic_lookup' | |
* unixcc: output='luastate.so' | |
* cc: run='lua -- luastate_test.lua' */ | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local lua = require "luastate" | |
local function test_lifetime() | |
local L = lua.new() | |
local fref = L.print | |
local tref = L:newtable() | |
local cref = L:newthread() | |
local uref = L:newuserdata(32) | |
assert(L"=nil" == nil) | |
assert(L"=true" == true) | |
assert(L"=false" == false) | |
assert(L"=1" == 1) | |
assert(L"='abc'" == "abc") | |
assert(tostring(L):match "^lua.State: ") | |
assert(tostring(fref):match "^function: .*%(lua.State: ") | |
assert(tostring(tref):match "^table: .*%(lua.State: ") | |
assert(tostring(cref):match "^thread: .*%(lua.State: ") | |
assert(tostring(uref):match "^userdata: .*%(lua.State: ") | |
L:close() | |
assert(tostring(L):match "%[D]") | |
assert(tostring(fref):match "%[D]") | |
assert(tostring(tref):match "%[D]") | |
assert(tostring(cref):match "%[D]") | |
assert(tostring(uref):match "%[D]") | |
print "lifetime: OK" | |
end | |
local function test_op() | |
local metas = { | |
"add", "sub", "mul", "mod", | |
"pow", "div", "idiv", "band", | |
"bor", "bxor", "shl", "shr", | |
"unm", "bnot", "eq", "le", "lt", | |
} | |
local t = { "local meta = {" } | |
for _, v in ipairs(metas) do | |
t[#t+1] = ("__%s = function(...) return '%s' end,"):format(v, v) | |
end | |
t[#t+1] = "}" | |
t[#t+1] = "function setmeta(t) return setmetatable(t, meta) end\n" | |
local L = lua.new() | |
L(table.concat(t, "\n")) | |
assert(L.type(L.setmeta) == "function") | |
L [[ | |
t1, t2 = {}, {} | |
setmeta(t1) | |
setmeta(t2) | |
]] | |
local t1, t2 = L.t1, L.t2 | |
assert(t1 + t2 == "add") | |
assert(t1 - t2 == "sub") | |
assert(t1 * t2 == "mul") | |
assert(t1 % t2 == "mod") | |
assert(t1 ^ t2 == "pow") | |
assert(t1 / t2 == "div") | |
assert(t1 // t2 == "idiv") | |
assert(t1 & t2 == "band") | |
assert(t1 | t2 == "bor") | |
assert(t1 ~ t2 == "bxor") | |
assert(t1 << t2 == "shl") | |
assert(t1 >> t2 == "shr") | |
assert( -t1 == "unm") | |
assert( ~t1 == "bnot") | |
assert( (t1 == t2) == true) | |
assert( (t1 >= t2) == true) | |
assert( (t1 <= t2) == true) | |
L:close() | |
print "op OK" | |
end | |
local function test_proxy() | |
end | |
local function test_thread() | |
end | |
test_lifetime() | |
test_op() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment