Skip to content

Instantly share code, notes, and snippets.

@starwing
Created November 29, 2017 14:28
Show Gist options
  • Save starwing/0e91c2898d3621d41f785f7d927b2b99 to your computer and use it in GitHub Desktop.
Save starwing/0e91c2898d3621d41f785f7d927b2b99 to your computer and use it in GitHub Desktop.
luastate -- bind lua to lua.
#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' */
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