mirror of https://github.com/axmolengine/axmol.git
1318 lines
38 KiB
C
1318 lines
38 KiB
C
|
|
#include "llthreads.h"
|
|
|
|
// luasocket
|
|
#include "luasocket.h"
|
|
#include "mime.h"
|
|
#include "luasocketscripts.h"
|
|
|
|
// cjson
|
|
#include "lua_cjson.h"
|
|
|
|
|
|
static luaL_Reg luax_exts[] =
|
|
{
|
|
{"socket.core", luaopen_socket_core},
|
|
{"mime.core", luaopen_mime_core},
|
|
{"socket", luaopen_socket},
|
|
{"socket.smtp", luaopen_socket_smtp},
|
|
{"socket.http", luaopen_socket_http},
|
|
{"socket.ftp", luaopen_socket_ftp},
|
|
{"socket.tp", luaopen_socket_tp},
|
|
{"socket.url", luaopen_socket_url},
|
|
{"mime", luaopen_socket_mime},
|
|
{"ltn12", luaopen_socket_ltn12},
|
|
{"cjson", luaopen_cjson},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
/* use static pointer as key to weak userdata table. */
|
|
static char *obj_udata_weak_ref_key = "obj_udata_weak_ref_key";
|
|
|
|
#define obj_type_id_Lua_LLThread 0
|
|
#define obj_type_Lua_LLThread (obj_types[obj_type_id_Lua_LLThread])
|
|
|
|
static obj_type obj_types[] =
|
|
{
|
|
{NULL, 0, OBJ_TYPE_FLAG_WEAK_REF, "Lua_LLThread"},
|
|
{NULL, -1, 0, NULL},
|
|
};
|
|
|
|
|
|
static lua_State* parent_L = 0;
|
|
|
|
|
|
FUNC_UNUSED obj_udata *obj_udata_toobj(lua_State *L, int _index)
|
|
{
|
|
obj_udata *ud;
|
|
size_t len;
|
|
|
|
/* make sure it's a userdata value. */
|
|
ud = (obj_udata *)lua_touserdata(L, _index);
|
|
if(ud == NULL)
|
|
{
|
|
luaL_typerror(L, _index, "userdata"); /* is not a userdata value. */
|
|
}
|
|
/* verify userdata size. */
|
|
len = lua_objlen(L, _index);
|
|
if(len != sizeof(obj_udata))
|
|
{
|
|
/* This shouldn't be possible */
|
|
luaL_error(L, "invalid userdata size: size=%d, expected=%d", len, sizeof(obj_udata));
|
|
}
|
|
return ud;
|
|
}
|
|
|
|
FUNC_UNUSED int obj_udata_is_compatible(lua_State *L, obj_udata *ud, void **obj, base_caster_t *caster, obj_type *type)
|
|
{
|
|
obj_base *base;
|
|
obj_type *ud_type;
|
|
lua_pushlightuserdata(L, type);
|
|
lua_rawget(L, LUA_REGISTRYINDEX); /* type's metatable. */
|
|
if(lua_rawequal(L, -1, -2))
|
|
{
|
|
*obj = ud->obj;
|
|
/* same type no casting needed. */
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
/* Different types see if we can cast to the required type. */
|
|
lua_rawgeti(L, -2, type->id);
|
|
base = lua_touserdata(L, -1);
|
|
lua_pop(L, 1); /* pop obj_base or nil */
|
|
if(base != NULL)
|
|
{
|
|
*caster = base->bcaster;
|
|
/* get the obj_type for this userdata. */
|
|
lua_pushliteral(L, ".type");
|
|
lua_rawget(L, -3); /* type's metatable. */
|
|
ud_type = lua_touserdata(L, -1);
|
|
lua_pop(L, 1); /* pop obj_type or nil */
|
|
if(base == NULL)
|
|
{
|
|
luaL_error(L, "bad userdata, missing type info.");
|
|
return 0;
|
|
}
|
|
/* check if userdata is a simple object. */
|
|
if(ud_type->flags & OBJ_TYPE_SIMPLE)
|
|
{
|
|
*obj = ud;
|
|
}
|
|
else
|
|
{
|
|
*obj = ud->obj;
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
FUNC_UNUSED obj_udata *obj_udata_luacheck_internal(lua_State *L, int _index, void **obj, obj_type *type, int not_delete)
|
|
{
|
|
obj_udata *ud;
|
|
base_caster_t caster = NULL;
|
|
/* make sure it's a userdata value. */
|
|
ud = (obj_udata *)lua_touserdata(L, _index);
|
|
if(ud != NULL)
|
|
{
|
|
/* check object type by comparing metatables. */
|
|
if(lua_getmetatable(L, _index))
|
|
{
|
|
if(obj_udata_is_compatible(L, ud, obj, &(caster), type))
|
|
{
|
|
lua_pop(L, 2); /* pop both metatables. */
|
|
/* apply caster function if needed. */
|
|
if(caster != NULL && *obj != NULL)
|
|
{
|
|
caster(obj);
|
|
}
|
|
/* check object pointer. */
|
|
if(*obj == NULL)
|
|
{
|
|
if(not_delete)
|
|
{
|
|
luaL_error(L, "null %s", type->name); /* object was garbage collected? */
|
|
}
|
|
return NULL;
|
|
}
|
|
return ud;
|
|
}
|
|
}
|
|
}
|
|
if(not_delete)
|
|
{
|
|
luaL_typerror(L, _index, type->name); /* is not a userdata value. */
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
FUNC_UNUSED void *obj_udata_luacheck(lua_State *L, int _index, obj_type *type)
|
|
{
|
|
void *obj = NULL;
|
|
obj_udata_luacheck_internal(L, _index, &(obj), type, 1);
|
|
return obj;
|
|
}
|
|
|
|
FUNC_UNUSED void *obj_udata_luadelete(lua_State *L, int _index, obj_type *type, int *flags)
|
|
{
|
|
void *obj;
|
|
obj_udata *ud = obj_udata_luacheck_internal(L, _index, &(obj), type, 0);
|
|
if(ud == NULL) return NULL;
|
|
*flags = ud->flags;
|
|
/* null userdata. */
|
|
ud->obj = NULL;
|
|
ud->flags = 0;
|
|
return obj;
|
|
}
|
|
|
|
FUNC_UNUSED void obj_udata_luapush(lua_State *L, void *obj, obj_type *type, int flags)
|
|
{
|
|
obj_udata *ud;
|
|
/* convert NULL's into Lua nil's. */
|
|
if(obj == NULL)
|
|
{
|
|
lua_pushnil(L);
|
|
return;
|
|
}
|
|
/* check for type caster. */
|
|
if(type->dcaster)
|
|
{
|
|
(type->dcaster)(&obj, &type);
|
|
}
|
|
/* create new userdata. */
|
|
ud = (obj_udata *)lua_newuserdata(L, sizeof(obj_udata));
|
|
ud->obj = obj;
|
|
ud->flags = flags;
|
|
/* get obj_type metatable. */
|
|
lua_pushlightuserdata(L, type);
|
|
lua_rawget(L, LUA_REGISTRYINDEX); /* type's metatable. */
|
|
lua_setmetatable(L, -2);
|
|
}
|
|
|
|
FUNC_UNUSED void *obj_udata_luadelete_weak(lua_State *L, int _index, obj_type *type, int *flags)
|
|
{
|
|
void *obj;
|
|
obj_udata *ud = obj_udata_luacheck_internal(L, _index, &(obj), type, 0);
|
|
if(ud == NULL) return NULL;
|
|
*flags = ud->flags;
|
|
/* null userdata. */
|
|
ud->obj = NULL;
|
|
ud->flags = 0;
|
|
/* get objects weak table. */
|
|
lua_pushlightuserdata(L, obj_udata_weak_ref_key);
|
|
lua_rawget(L, LUA_REGISTRYINDEX); /* weak ref table. */
|
|
/* remove object from weak table. */
|
|
lua_pushlightuserdata(L, obj);
|
|
lua_pushnil(L);
|
|
lua_rawset(L, -3);
|
|
return obj;
|
|
}
|
|
|
|
FUNC_UNUSED void obj_udata_luapush_weak(lua_State *L, void *obj, obj_type *type, int flags)
|
|
{
|
|
obj_udata *ud;
|
|
|
|
/* convert NULL's into Lua nil's. */
|
|
if(obj == NULL)
|
|
{
|
|
lua_pushnil(L);
|
|
return;
|
|
}
|
|
/* check for type caster. */
|
|
if(type->dcaster)
|
|
{
|
|
(type->dcaster)(&obj, &type);
|
|
}
|
|
/* get objects weak table. */
|
|
lua_pushlightuserdata(L, obj_udata_weak_ref_key);
|
|
lua_rawget(L, LUA_REGISTRYINDEX); /* weak ref table. */
|
|
/* lookup userdata instance from pointer. */
|
|
lua_pushlightuserdata(L, obj);
|
|
lua_rawget(L, -2);
|
|
if(!lua_isnil(L, -1))
|
|
{
|
|
lua_remove(L, -2); /* remove objects table. */
|
|
return;
|
|
}
|
|
lua_pop(L, 1); /* pop nil. */
|
|
|
|
/* create new userdata. */
|
|
ud = (obj_udata *)lua_newuserdata(L, sizeof(obj_udata));
|
|
|
|
/* init. obj_udata. */
|
|
ud->obj = obj;
|
|
ud->flags = flags;
|
|
/* get obj_type metatable. */
|
|
lua_pushlightuserdata(L, type);
|
|
lua_rawget(L, LUA_REGISTRYINDEX); /* type's metatable. */
|
|
lua_setmetatable(L, -2);
|
|
|
|
/* add weak reference to object. */
|
|
lua_pushlightuserdata(L, obj); /* push object pointer as the 'key' */
|
|
lua_pushvalue(L, -2); /* push object's udata */
|
|
lua_rawset(L, -4); /* add weak reference to object. */
|
|
lua_remove(L, -2); /* remove objects table. */
|
|
}
|
|
|
|
/* default object equal method. */
|
|
FUNC_UNUSED int obj_udata_default_equal(lua_State *L)
|
|
{
|
|
obj_udata *ud1 = obj_udata_toobj(L, 1);
|
|
obj_udata *ud2 = obj_udata_toobj(L, 2);
|
|
|
|
lua_pushboolean(L, (ud1->obj == ud2->obj));
|
|
return 1;
|
|
}
|
|
|
|
/* default object tostring method. */
|
|
FUNC_UNUSED int obj_udata_default_tostring(lua_State *L)
|
|
{
|
|
obj_udata *ud = obj_udata_toobj(L, 1);
|
|
|
|
/* get object's metatable. */
|
|
lua_getmetatable(L, 1);
|
|
lua_remove(L, 1); /* remove userdata. */
|
|
/* get the object's name from the metatable */
|
|
lua_getfield(L, 1, ".name");
|
|
lua_remove(L, 1); /* remove metatable */
|
|
/* push object's pointer */
|
|
lua_pushfstring(L, ": %p, flags=%d", ud->obj, ud->flags);
|
|
lua_concat(L, 2);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Simple userdata objects.
|
|
*/
|
|
FUNC_UNUSED void *obj_simple_udata_toobj(lua_State *L, int _index)
|
|
{
|
|
void *ud;
|
|
|
|
/* make sure it's a userdata value. */
|
|
ud = lua_touserdata(L, _index);
|
|
if(ud == NULL)
|
|
{
|
|
luaL_typerror(L, _index, "userdata"); /* is not a userdata value. */
|
|
}
|
|
return ud;
|
|
}
|
|
|
|
FUNC_UNUSED void * obj_simple_udata_luacheck(lua_State *L, int _index, obj_type *type)
|
|
{
|
|
void *ud;
|
|
/* make sure it's a userdata value. */
|
|
ud = lua_touserdata(L, _index);
|
|
if(ud != NULL)
|
|
{
|
|
/* check object type by comparing metatables. */
|
|
if(lua_getmetatable(L, _index))
|
|
{
|
|
lua_pushlightuserdata(L, type);
|
|
lua_rawget(L, LUA_REGISTRYINDEX); /* type's metatable. */
|
|
if(lua_rawequal(L, -1, -2))
|
|
{
|
|
lua_pop(L, 2); /* pop both metatables. */
|
|
return ud;
|
|
}
|
|
}
|
|
}
|
|
luaL_typerror(L, _index, type->name); /* is not a userdata value. */
|
|
return NULL;
|
|
}
|
|
|
|
FUNC_UNUSED void * obj_simple_udata_luadelete(lua_State *L, int _index, obj_type *type, int *flags)
|
|
{
|
|
void *obj;
|
|
obj = obj_simple_udata_luacheck(L, _index, type);
|
|
*flags = OBJ_UDATA_FLAG_OWN;
|
|
/* clear the metatable to invalidate userdata. */
|
|
lua_pushnil(L);
|
|
lua_setmetatable(L, _index);
|
|
return obj;
|
|
}
|
|
|
|
FUNC_UNUSED void *obj_simple_udata_luapush(lua_State *L, void *obj, int size, obj_type *type)
|
|
{
|
|
/* create new userdata. */
|
|
void *ud = lua_newuserdata(L, size);
|
|
memcpy(ud, obj, size);
|
|
/* get obj_type metatable. */
|
|
lua_pushlightuserdata(L, type);
|
|
lua_rawget(L, LUA_REGISTRYINDEX); /* type's metatable. */
|
|
lua_setmetatable(L, -2);
|
|
|
|
return ud;
|
|
}
|
|
|
|
/* default simple object equal method. */
|
|
FUNC_UNUSED int obj_simple_udata_default_equal(lua_State *L)
|
|
{
|
|
void *ud1 = obj_simple_udata_toobj(L, 1);
|
|
size_t len1 = lua_objlen(L, 1);
|
|
void *ud2 = obj_simple_udata_toobj(L, 2);
|
|
size_t len2 = lua_objlen(L, 2);
|
|
|
|
if(len1 == len2)
|
|
{
|
|
lua_pushboolean(L, (memcmp(ud1, ud2, len1) == 0));
|
|
}
|
|
else
|
|
{
|
|
lua_pushboolean(L, 0);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* default simple object tostring method. */
|
|
FUNC_UNUSED int obj_simple_udata_default_tostring(lua_State *L)
|
|
{
|
|
void *ud = obj_simple_udata_toobj(L, 1);
|
|
|
|
/* get object's metatable. */
|
|
lua_getmetatable(L, 1);
|
|
lua_remove(L, 1); /* remove userdata. */
|
|
/* get the object's name from the metatable */
|
|
lua_getfield(L, 1, ".name");
|
|
lua_remove(L, 1); /* remove metatable */
|
|
/* push object's pointer */
|
|
lua_pushfstring(L, ": %p", ud);
|
|
lua_concat(L, 2);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int obj_constructor_call_wrapper(lua_State *L)
|
|
{
|
|
/* replace '__call' table with constructor function. */
|
|
lua_pushvalue(L, lua_upvalueindex(1));
|
|
lua_replace(L, 1);
|
|
|
|
/* call constructor function with all parameters after the '__call' table. */
|
|
lua_call(L, lua_gettop(L) - 1, LUA_MULTRET);
|
|
/* return all results from constructor. */
|
|
return lua_gettop(L);
|
|
}
|
|
|
|
void obj_type_register_constants(lua_State *L, const obj_const *constants, int tab_idx)
|
|
{
|
|
/* register constants. */
|
|
while(constants->name != NULL)
|
|
{
|
|
lua_pushstring(L, constants->name);
|
|
switch(constants->type)
|
|
{
|
|
case CONST_BOOLEAN:
|
|
lua_pushboolean(L, constants->num != 0.0);
|
|
break;
|
|
case CONST_NUMBER:
|
|
lua_pushnumber(L, constants->num);
|
|
break;
|
|
case CONST_STRING:
|
|
lua_pushstring(L, constants->str);
|
|
break;
|
|
default:
|
|
lua_pushnil(L);
|
|
break;
|
|
}
|
|
lua_rawset(L, tab_idx - 2);
|
|
constants++;
|
|
}
|
|
}
|
|
|
|
void obj_type_register_package(lua_State *L, const reg_sub_module *type_reg)
|
|
{
|
|
const luaL_reg *reg_list = type_reg->pub_funcs;
|
|
|
|
/* create public functions table. */
|
|
if(reg_list != NULL && reg_list[0].name != NULL)
|
|
{
|
|
/* register functions */
|
|
luaL_register(L, NULL, reg_list);
|
|
}
|
|
|
|
obj_type_register_constants(L, type_reg->constants, -1);
|
|
|
|
lua_pop(L, 1); /* drop package table */
|
|
}
|
|
|
|
void obj_type_register(lua_State *L, const reg_sub_module *type_reg, int priv_table)
|
|
{
|
|
const luaL_reg *reg_list;
|
|
obj_type *type = type_reg->type;
|
|
const obj_base *base = type_reg->bases;
|
|
|
|
if(type_reg->is_package == 1)
|
|
{
|
|
obj_type_register_package(L, type_reg);
|
|
return;
|
|
}
|
|
|
|
/* create public functions table. */
|
|
reg_list = type_reg->pub_funcs;
|
|
if(reg_list != NULL && reg_list[0].name != NULL)
|
|
{
|
|
/* register "constructors" as to object's public API */
|
|
luaL_register(L, NULL, reg_list); /* fill public API table. */
|
|
|
|
/* make public API table callable as the default constructor. */
|
|
lua_newtable(L); /* create metatable */
|
|
lua_pushliteral(L, "__call");
|
|
lua_pushcfunction(L, reg_list[0].func); /* push first constructor function. */
|
|
lua_pushcclosure(L, obj_constructor_call_wrapper, 1); /* make __call wrapper. */
|
|
lua_rawset(L, -3); /* metatable.__call = <default constructor> */
|
|
|
|
#if OBJ_DATA_HIDDEN_METATABLE
|
|
lua_pushliteral(L, "__metatable");
|
|
lua_pushboolean(L, 0);
|
|
lua_rawset(L, -3); /* metatable.__metatable = false */
|
|
#endif
|
|
|
|
/* setmetatable on public API table. */
|
|
lua_setmetatable(L, -2);
|
|
|
|
lua_pop(L, 1); /* pop public API table, don't need it any more. */
|
|
/* create methods table. */
|
|
lua_newtable(L);
|
|
}
|
|
else
|
|
{
|
|
/* register all methods as public functions. */
|
|
#if OBJ_DATA_HIDDEN_METATABLE
|
|
lua_pop(L, 1); /* pop public API table, don't need it any more. */
|
|
/* create methods table. */
|
|
lua_newtable(L);
|
|
#endif
|
|
}
|
|
|
|
luaL_register(L, NULL, type_reg->methods); /* fill methods table. */
|
|
|
|
luaL_newmetatable(L, type->name); /* create metatable */
|
|
lua_pushliteral(L, ".name");
|
|
lua_pushstring(L, type->name);
|
|
lua_rawset(L, -3); /* metatable['.name'] = "<object_name>" */
|
|
lua_pushliteral(L, ".type");
|
|
lua_pushlightuserdata(L, type);
|
|
lua_rawset(L, -3); /* metatable['.type'] = lightuserdata -> obj_type */
|
|
lua_pushlightuserdata(L, type);
|
|
lua_pushvalue(L, -2); /* dup metatable. */
|
|
lua_rawset(L, LUA_REGISTRYINDEX); /* REGISTRY[type] = metatable */
|
|
|
|
#if LUAJIT_FFI
|
|
/* add metatable to 'priv_table' */
|
|
lua_pushstring(L, type->name);
|
|
lua_pushvalue(L, -2); /* dup metatable. */
|
|
lua_rawset(L, priv_table); /* priv_table["<object_name>"] = metatable */
|
|
#else
|
|
(void)priv_table;
|
|
#endif
|
|
|
|
luaL_register(L, NULL, type_reg->metas); /* fill metatable */
|
|
|
|
/* add obj_bases to metatable. */
|
|
while(base->id >= 0)
|
|
{
|
|
lua_pushlightuserdata(L, (void *)base);
|
|
lua_rawseti(L, -2, base->id);
|
|
base++;
|
|
}
|
|
|
|
obj_type_register_constants(L, type_reg->constants, -2);
|
|
|
|
lua_pushliteral(L, "__index");
|
|
lua_pushvalue(L, -3); /* dup methods table */
|
|
lua_rawset(L, -3); /* metatable.__index = methods */
|
|
#if OBJ_DATA_HIDDEN_METATABLE
|
|
lua_pushliteral(L, "__metatable");
|
|
lua_pushboolean(L, 0);
|
|
lua_rawset(L, -3); /* hide metatable:
|
|
metatable.__metatable = false */
|
|
#endif
|
|
lua_pop(L, 2); /* drop metatable & methods */
|
|
}
|
|
|
|
FUNC_UNUSED int lua_checktype_ref(lua_State *L, int _index, int _type)
|
|
{
|
|
luaL_checktype(L,_index,_type);
|
|
lua_pushvalue(L,_index);
|
|
return luaL_ref(L, LUA_REGISTRYINDEX);
|
|
}
|
|
|
|
#if LUAJIT_FFI
|
|
int nobj_udata_new_ffi(lua_State *L)
|
|
{
|
|
size_t size = luaL_checkinteger(L, 1);
|
|
luaL_checktype(L, 2, LUA_TTABLE);
|
|
lua_settop(L, 2);
|
|
/* create userdata. */
|
|
lua_newuserdata(L, size);
|
|
lua_replace(L, 1);
|
|
/* set userdata's metatable. */
|
|
lua_setmetatable(L, 1);
|
|
return 1;
|
|
}
|
|
|
|
int nobj_try_loading_ffi(lua_State *L, const char *ffi_mod_name,
|
|
const char *ffi_init_code, const ffi_export_symbol *ffi_exports, int priv_table)
|
|
{
|
|
int err;
|
|
|
|
/* export symbols to priv_table. */
|
|
while(ffi_exports->name != NULL)
|
|
{
|
|
lua_pushstring(L, ffi_exports->name);
|
|
lua_pushlightuserdata(L, ffi_exports->sym);
|
|
lua_settable(L, priv_table);
|
|
ffi_exports++;
|
|
}
|
|
err = luaL_loadbuffer(L, ffi_init_code, strlen(ffi_init_code), ffi_mod_name);
|
|
if(0 == err)
|
|
{
|
|
lua_pushvalue(L, -2); /* dup C module's table. */
|
|
lua_pushvalue(L, priv_table); /* move priv_table to top of stack. */
|
|
lua_remove(L, priv_table);
|
|
lua_pushcfunction(L, nobj_udata_new_ffi);
|
|
err = lua_pcall(L, 3, 0, 0);
|
|
}
|
|
if(err)
|
|
{
|
|
const char *msg = "<err not a string>";
|
|
if(lua_isstring(L, -1))
|
|
{
|
|
msg = lua_tostring(L, -1);
|
|
}
|
|
printf("Failed to install FFI-based bindings: %s\n", msg);
|
|
lua_pop(L, 1); /* pop error message. */
|
|
}
|
|
return err;
|
|
}
|
|
#endif
|
|
|
|
|
|
#define obj_type_Lua_LLThread_check(L, _index) \
|
|
obj_udata_luacheck(L, _index, &(obj_type_Lua_LLThread))
|
|
#define obj_type_Lua_LLThread_delete(L, _index, flags) \
|
|
obj_udata_luadelete_weak(L, _index, &(obj_type_Lua_LLThread), flags)
|
|
#define obj_type_Lua_LLThread_push(L, obj, flags) \
|
|
obj_udata_luapush_weak(L, (void *)obj, &(obj_type_Lua_LLThread), flags)
|
|
|
|
/* maximum recursive depth of table copies. */
|
|
#define MAX_COPY_DEPTH 30
|
|
|
|
#define ERROR_LEN 1024
|
|
|
|
/******************************************************************************
|
|
* traceback() function from Lua 5.1.x source.
|
|
* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
******************************************************************************/
|
|
int traceback (lua_State *L)
|
|
{
|
|
if (!lua_isstring(L, 1)) /* 'message' not a string? */
|
|
return 1; /* keep it intact */
|
|
lua_getfield(L, LUA_GLOBALSINDEX, "debug");
|
|
if (!lua_istable(L, -1))
|
|
{
|
|
lua_pop(L, 1);
|
|
return 1;
|
|
}
|
|
lua_getfield(L, -1, "traceback");
|
|
if (!lua_isfunction(L, -1))
|
|
{
|
|
lua_pop(L, 2);
|
|
return 1;
|
|
}
|
|
lua_pushvalue(L, 1); /* pass error message */
|
|
lua_pushinteger(L, 2); /* skip this function and traceback */
|
|
lua_call(L, 2, 1); /* call debug.traceback */
|
|
return 1;
|
|
}
|
|
|
|
|
|
Lua_LLThread_child *llthread_child_new()
|
|
{
|
|
Lua_LLThread_child *this;
|
|
|
|
this = (Lua_LLThread_child *)calloc(1, sizeof(Lua_LLThread_child));
|
|
/* create new lua_State for the thread. */
|
|
this->L = luaL_newstate();
|
|
/* open standard libraries. */
|
|
luaL_openlibs(this->L);
|
|
|
|
/* load modules */
|
|
luaL_Reg* lib = luax_exts;
|
|
luaL_findtable(this->L, LUA_GLOBALSINDEX, "package.preload", sizeof(luax_exts)/sizeof(luax_exts[0])-1);
|
|
for (; lib->func; lib++)
|
|
{
|
|
lua_pushstring(this->L, lib->name);
|
|
lua_pushcfunction(this->L, lib->func);
|
|
lua_rawset(this->L, -3);
|
|
}
|
|
lua_pop(this->L, 1);
|
|
|
|
/* push traceback function as first value on stack. */
|
|
lua_pushcfunction(this->L, traceback);
|
|
|
|
return this;
|
|
}
|
|
|
|
void llthread_child_destroy(Lua_LLThread_child *this)
|
|
{
|
|
lua_close(this->L);
|
|
free(this);
|
|
}
|
|
|
|
Lua_LLThread *llthread_new()
|
|
{
|
|
Lua_LLThread *this;
|
|
|
|
this = (Lua_LLThread *)calloc(1, sizeof(Lua_LLThread));
|
|
this->state = TSTATE_NONE;
|
|
this->child = llthread_child_new();
|
|
|
|
return this;
|
|
}
|
|
|
|
void llthread_destroy(Lua_LLThread *this)
|
|
{
|
|
/* We still own the child thread object iff the thread was not started or
|
|
* we have joined the thread.
|
|
*/
|
|
if((this->state & TSTATE_JOINED) == TSTATE_JOINED || this->state == TSTATE_NONE)
|
|
{
|
|
if(this->child) llthread_child_destroy(this->child);
|
|
this->child = NULL;
|
|
}
|
|
free(this);
|
|
}
|
|
|
|
RUN_CHILD_THREAD run_child_thread(void *arg)
|
|
{
|
|
Lua_LLThread_child *this = (Lua_LLThread_child *)arg;
|
|
lua_State *L = this->L;
|
|
int nargs = lua_gettop(L) - 2;
|
|
|
|
this->status = lua_pcall(L, nargs, LUA_MULTRET, 1);
|
|
|
|
/* alwasy print errors here, helps with debugging bad code. */
|
|
if(this->status != 0)
|
|
{
|
|
const char *err_msg = lua_tostring(L, -1);
|
|
fprintf(stderr, "Error from thread: %s\n", err_msg);
|
|
fflush(stderr);
|
|
}
|
|
|
|
if (this->callback)
|
|
{
|
|
lua_pushstring(parent_L, LLTHREAD_REFID_FUNC_MAPPING);
|
|
lua_rawget(parent_L, LUA_REGISTRYINDEX); /* stack: refid_func */
|
|
lua_pushinteger(parent_L, this->callback); /* stack: refid_func refid */
|
|
lua_rawget(parent_L, -2); /* stack: refid_func func */
|
|
|
|
// printf("---- parent_L stackDump ----\n");
|
|
// tolua_stack_dump(parent_L);
|
|
// printf("---- child->L stackDump ----\n");
|
|
// tolua_stack_dump(this->L);
|
|
//
|
|
if (!lua_isfunction(parent_L, -1))
|
|
{
|
|
printf("[LLTHREAD ERROR] callback refid '%d' does not reference a Lua function", this->callback);
|
|
lua_pop(parent_L, 2);
|
|
}
|
|
else
|
|
{
|
|
int top = lua_gettop(this->L);
|
|
nargs = llthread_push_results(parent_L, this, 2, top); /* stack: refid_func func ... */
|
|
lua_pcall(parent_L, nargs, 0, 0); /* stack: refid_func */
|
|
|
|
lua_pushinteger(parent_L, this->callback); /* stack: refid_func refid */
|
|
lua_pushnil(parent_L); /* stack: refid_func refid nil */
|
|
lua_rawget(parent_L, -2); /* delete refid_func[refid], stack: refid_func */
|
|
lua_pop(parent_L, 1); /* stack: - */
|
|
}
|
|
}
|
|
|
|
/* if thread is detached, then destroy the child state. */
|
|
if(this->is_detached != 0)
|
|
{
|
|
/* thread is detached, so it must clean-up the child state. */
|
|
llthread_child_destroy(this);
|
|
this = NULL;
|
|
}
|
|
#ifdef __WINDOWS__
|
|
if(this)
|
|
{
|
|
/* attached thread, don't close thread handle. */
|
|
_endthreadex(0);
|
|
}
|
|
else
|
|
{
|
|
/* detached thread, close thread handle. */
|
|
_endthread();
|
|
}
|
|
#else
|
|
return this;
|
|
#endif
|
|
}
|
|
|
|
int llthread_start(Lua_LLThread *this, int start_detached)
|
|
{
|
|
Lua_LLThread_child *child;
|
|
int rc = 0;
|
|
|
|
child = this->child;
|
|
child->is_detached = start_detached;
|
|
#ifdef __WINDOWS__
|
|
this->thread = (HANDLE)_beginthread(run_child_thread, 0, child);
|
|
if(this->thread != (HANDLE)-1L)
|
|
{
|
|
this->state = TSTATE_STARTED;
|
|
if(start_detached)
|
|
{
|
|
this->state |= TSTATE_DETACHED;
|
|
}
|
|
}
|
|
#else
|
|
rc = pthread_create(&(this->thread), NULL, run_child_thread, child);
|
|
if(rc == 0)
|
|
{
|
|
this->state = TSTATE_STARTED;
|
|
if(start_detached)
|
|
{
|
|
this->state |= TSTATE_DETACHED;
|
|
rc = pthread_detach(this->thread);
|
|
}
|
|
}
|
|
#endif
|
|
return rc;
|
|
}
|
|
|
|
int llthread_join(Lua_LLThread *this)
|
|
{
|
|
#ifdef __WINDOWS__
|
|
WaitForSingleObject( this->thread, INFINITE );
|
|
/* Destroy the thread object. */
|
|
CloseHandle( this->thread );
|
|
|
|
return 0;
|
|
#else
|
|
Lua_LLThread_child *child;
|
|
int rc;
|
|
|
|
/* then join the thread. */
|
|
rc = pthread_join(this->thread, (void **)&(child));
|
|
if(rc == 0)
|
|
{
|
|
this->state |= TSTATE_JOINED;
|
|
/* if the child thread returns NULL, then it freed the child object. */
|
|
this->child = child;
|
|
}
|
|
return rc;
|
|
#endif
|
|
}
|
|
|
|
int llthread_copy_table_from_cache(llthread_copy_state *state, int idx)
|
|
{
|
|
void *ptr;
|
|
|
|
/* convert table to pointer for lookup in cache. */
|
|
ptr = (void *)lua_topointer(state->from_L, idx);
|
|
if(ptr == NULL) return 0; /* can't convert to pointer. */
|
|
|
|
/* check if we need to create the cache. */
|
|
if(!state->has_cache)
|
|
{
|
|
lua_newtable(state->to_L);
|
|
lua_replace(state->to_L, state->cache_idx);
|
|
state->has_cache = 1;
|
|
}
|
|
|
|
lua_pushlightuserdata(state->to_L, ptr);
|
|
lua_rawget(state->to_L, state->cache_idx);
|
|
if(lua_isnil(state->to_L, -1))
|
|
{
|
|
/* not in cache. */
|
|
lua_pop(state->to_L, 1);
|
|
/* create new table and add to cache. */
|
|
lua_newtable(state->to_L);
|
|
lua_pushlightuserdata(state->to_L, ptr);
|
|
lua_pushvalue(state->to_L, -2);
|
|
lua_rawset(state->to_L, state->cache_idx);
|
|
return 0;
|
|
}
|
|
/* found table in cache. */
|
|
return 1;
|
|
}
|
|
|
|
int llthread_copy_value(llthread_copy_state *state, int depth, int idx)
|
|
{
|
|
const char *str;
|
|
size_t str_len;
|
|
int kv_pos;
|
|
|
|
/* Maximum recursive depth */
|
|
if(++depth > MAX_COPY_DEPTH)
|
|
{
|
|
return luaL_error(state->from_L, "Hit maximum copy depth (%d > %d).", depth, MAX_COPY_DEPTH);
|
|
}
|
|
|
|
/* only support string/number/boolean/nil/table/lightuserdata. */
|
|
switch(lua_type(state->from_L, idx))
|
|
{
|
|
case LUA_TNIL:
|
|
lua_pushnil(state->to_L);
|
|
break;
|
|
case LUA_TNUMBER:
|
|
lua_pushnumber(state->to_L, lua_tonumber(state->from_L, idx));
|
|
break;
|
|
case LUA_TBOOLEAN:
|
|
lua_pushboolean(state->to_L, lua_toboolean(state->from_L, idx));
|
|
break;
|
|
case LUA_TSTRING:
|
|
str = lua_tolstring(state->from_L, idx, &(str_len));
|
|
lua_pushlstring(state->to_L, str, str_len);
|
|
break;
|
|
case LUA_TLIGHTUSERDATA:
|
|
lua_pushlightuserdata(state->to_L, lua_touserdata(state->from_L, idx));
|
|
break;
|
|
case LUA_TTABLE:
|
|
/* make sure there is room on the new state for 3 values (table,key,value) */
|
|
if(!lua_checkstack(state->to_L, 3))
|
|
{
|
|
return luaL_error(state->from_L, "To stack overflow!");
|
|
}
|
|
/* make room on from stack for key/value pairs. */
|
|
luaL_checkstack(state->from_L, 2, "From stack overflow!");
|
|
|
|
/* check cache for table. */
|
|
if(llthread_copy_table_from_cache(state, idx))
|
|
{
|
|
/* found in cache don't need to copy table. */
|
|
break;
|
|
}
|
|
lua_pushnil(state->from_L);
|
|
while (lua_next(state->from_L, idx) != 0)
|
|
{
|
|
/* key is at (top - 1), value at (top), but we need to normalize these
|
|
* to positive indices */
|
|
kv_pos = lua_gettop(state->from_L);
|
|
/* copy key */
|
|
llthread_copy_value(state, depth, kv_pos - 1);
|
|
/* copy value */
|
|
llthread_copy_value(state, depth, kv_pos);
|
|
/* Copied key and value are now at -2 and -1 in state->to_L. */
|
|
lua_settable(state->to_L, -3);
|
|
/* Pop value for next iteration */
|
|
lua_pop(state->from_L, 1);
|
|
}
|
|
break;
|
|
case LUA_TFUNCTION:
|
|
case LUA_TUSERDATA:
|
|
case LUA_TTHREAD:
|
|
default:
|
|
if (state->is_arg)
|
|
{
|
|
return luaL_argerror(state->from_L, idx, "function/userdata/thread types un-supported.");
|
|
}
|
|
else
|
|
{
|
|
/* convert un-supported types to an error string. */
|
|
lua_pushfstring(state->to_L, "Un-supported value: %s: %p",
|
|
lua_typename(state->from_L, lua_type(state->from_L, idx)), lua_topointer(state->from_L, idx));
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int llthread_copy_values(lua_State *from_L, lua_State *to_L, int idx, int top, int is_arg)
|
|
{
|
|
llthread_copy_state state;
|
|
int nvalues = 0;
|
|
int n;
|
|
|
|
nvalues = (top - idx) + 1;
|
|
/* make sure there is room on the new state for the values. */
|
|
if(!lua_checkstack(to_L, nvalues + 1))
|
|
{
|
|
return luaL_error(from_L, "To stack overflow!");
|
|
}
|
|
|
|
/* setup copy state. */
|
|
state.from_L = from_L;
|
|
state.to_L = to_L;
|
|
state.is_arg = is_arg;
|
|
state.has_cache = 0; /* don't create cache table unless it is needed. */
|
|
lua_pushnil(to_L);
|
|
state.cache_idx = lua_gettop(to_L);
|
|
|
|
nvalues = 0;
|
|
for(n = idx; n <= top; n++)
|
|
{
|
|
llthread_copy_value(&state, 0, n);
|
|
++nvalues;
|
|
}
|
|
|
|
/* remove cache table. */
|
|
lua_remove(to_L, state.cache_idx);
|
|
|
|
return nvalues;
|
|
}
|
|
|
|
int llthread_push_args(lua_State *L, Lua_LLThread_child *child, int idx, int top)
|
|
{
|
|
return llthread_copy_values(L, child->L, idx, top, 1 /* is_arg */);
|
|
}
|
|
|
|
int llthread_push_results(lua_State *L, Lua_LLThread_child *child, int idx, int top)
|
|
{
|
|
return llthread_copy_values(child->L, L, idx, top, 0 /* is_arg */);
|
|
}
|
|
|
|
Lua_LLThread *llthread_create(lua_State *L, const char *code, size_t code_len, int callbackFunctionRefID)
|
|
{
|
|
Lua_LLThread *this;
|
|
Lua_LLThread_child *child;
|
|
const char *str;
|
|
size_t str_len;
|
|
int rc;
|
|
int top;
|
|
|
|
this = llthread_new();
|
|
child = this->child;
|
|
child->callback = callbackFunctionRefID;
|
|
/* load Lua code into child state. */
|
|
rc = luaL_loadbuffer(child->L, code, code_len, code);
|
|
if(rc != 0)
|
|
{
|
|
/* copy error message to parent state. */
|
|
str = lua_tolstring(child->L, -1, &(str_len));
|
|
if(str != NULL)
|
|
{
|
|
lua_pushlstring(L, str, str_len);
|
|
}
|
|
else
|
|
{
|
|
/* non-string error message. */
|
|
lua_pushfstring(L, "luaL_loadbuffer() failed to load Lua code: rc=%d", rc);
|
|
}
|
|
llthread_destroy(this);
|
|
lua_error(L);
|
|
return NULL;
|
|
}
|
|
/* copy extra args from main state to child state. */
|
|
top = lua_gettop(L);
|
|
/* Push all args after the Lua code. */
|
|
llthread_push_args(L, child, 2, top);
|
|
|
|
return this;
|
|
}
|
|
|
|
/* method: delete */
|
|
int Lua_LLThread__delete__meth(lua_State *L)
|
|
{
|
|
int this_flags_idx1 = 0;
|
|
Lua_LLThread * this_idx1 = obj_type_Lua_LLThread_delete(L,1,&(this_flags_idx1));
|
|
Lua_LLThread_child *child;
|
|
|
|
if(!(this_flags_idx1 & OBJ_UDATA_FLAG_OWN))
|
|
{
|
|
return 0;
|
|
}
|
|
/* if the thread has been started and has not been detached/joined. */
|
|
if((this_idx1->state & TSTATE_STARTED) == TSTATE_STARTED &&
|
|
(this_idx1->state & (TSTATE_DETACHED|TSTATE_JOINED)) == 0)
|
|
{
|
|
/* then join the thread. */
|
|
llthread_join(this_idx1);
|
|
child = this_idx1->child;
|
|
if(child && child->status != 0)
|
|
{
|
|
const char *err_msg = lua_tostring(child->L, -1);
|
|
fprintf(stderr, "Error from non-joined thread: %s\n", err_msg);
|
|
fflush(stderr);
|
|
}
|
|
}
|
|
llthread_destroy(this_idx1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* method: start */
|
|
int Lua_LLThread__start__meth(lua_State *L)
|
|
{
|
|
Lua_LLThread * this_idx1 = obj_type_Lua_LLThread_check(L,1);
|
|
bool start_detached_idx2 = lua_toboolean(L,2);
|
|
bool res_idx1 = 0;
|
|
char buf[ERROR_LEN];
|
|
int rc;
|
|
|
|
if(this_idx1->state != TSTATE_NONE)
|
|
{
|
|
lua_pushboolean(L, 0); /* false */
|
|
lua_pushliteral(L, "Thread already started.");
|
|
return 2;
|
|
}
|
|
if((rc = llthread_start(this_idx1, start_detached_idx2)) != 0)
|
|
{
|
|
lua_pushboolean(L, 0); /* false */
|
|
strerror_r(errno, buf, ERROR_LEN);
|
|
lua_pushstring(L, buf);
|
|
return 2;
|
|
}
|
|
res_idx1 = true;
|
|
|
|
lua_pushboolean(L, res_idx1);
|
|
return 1;
|
|
}
|
|
|
|
/* method: join */
|
|
int Lua_LLThread__join__meth(lua_State *L)
|
|
{
|
|
Lua_LLThread * this_idx1 = obj_type_Lua_LLThread_check(L,1);
|
|
bool res_idx1 = 0;
|
|
const char * err_msg_idx2 = NULL;
|
|
Lua_LLThread_child *child;
|
|
char buf[ERROR_LEN];
|
|
int top;
|
|
int rc;
|
|
|
|
if((this_idx1->state & TSTATE_STARTED) == 0)
|
|
{
|
|
lua_pushboolean(L, 0); /* false */
|
|
lua_pushliteral(L, "Can't join a thread that hasn't be started.");
|
|
return 2;
|
|
}
|
|
if((this_idx1->state & TSTATE_DETACHED) == TSTATE_DETACHED)
|
|
{
|
|
lua_pushboolean(L, 0); /* false */
|
|
lua_pushliteral(L, "Can't join a thread that has been detached.");
|
|
return 2;
|
|
}
|
|
if((this_idx1->state & TSTATE_JOINED) == TSTATE_JOINED)
|
|
{
|
|
lua_pushboolean(L, 0); /* false */
|
|
lua_pushliteral(L, "Can't join a thread that has already been joined.");
|
|
return 2;
|
|
}
|
|
/* join the thread. */
|
|
rc = llthread_join(this_idx1);
|
|
child = this_idx1->child;
|
|
|
|
/* Push all results after the Lua code. */
|
|
if(rc == 0 && child)
|
|
{
|
|
if(child->status != 0)
|
|
{
|
|
const char *err_msg = lua_tostring(child->L, -1);
|
|
lua_pushboolean(L, 0);
|
|
lua_pushfstring(L, "Error from child thread: %s", err_msg);
|
|
return 2;
|
|
}
|
|
else
|
|
{
|
|
lua_pushboolean(L, 1);
|
|
}
|
|
top = lua_gettop(child->L);
|
|
/* return results to parent thread. */
|
|
llthread_push_results(L, child, 2, top);
|
|
return top;
|
|
}
|
|
else
|
|
{
|
|
res_idx1 = false;
|
|
err_msg_idx2 = buf;
|
|
strerror_r(errno, buf, ERROR_LEN);
|
|
}
|
|
|
|
lua_pushboolean(L, res_idx1);
|
|
lua_pushstring(L, err_msg_idx2);
|
|
return 2;
|
|
}
|
|
|
|
/* method: new */
|
|
int llthreads__new__func(lua_State *L)
|
|
{
|
|
static int functionRefIDCount = 0;
|
|
++functionRefIDCount;
|
|
|
|
size_t lua_code_len_idx1;
|
|
const char * lua_code_idx1 = luaL_checklstring(L,1,&(lua_code_len_idx1));
|
|
|
|
int callback = 0;
|
|
if (lua_isfunction(L, 2))
|
|
{
|
|
/* reference to callback function */
|
|
lua_pushstring(L, LLTHREAD_REFID_FUNC_MAPPING);
|
|
lua_rawget(L, LUA_REGISTRYINDEX); /* stack: code callback ... refid_func */
|
|
lua_pushinteger(L, functionRefIDCount); /* stack: code callback ... refid_func refid */
|
|
lua_pushvalue(L, 2); /* stack: code callback ... refid_func refid callback */
|
|
lua_rawset(L, -3); /* refid_func[refid] = func, stack: code callback ... refid_func */
|
|
lua_pop(L, 1); /* stack: code callback ... */
|
|
lua_remove(L, 2); /* stack: code ... */
|
|
callback = functionRefIDCount;
|
|
}
|
|
|
|
int this_flags_idx1 = OBJ_UDATA_FLAG_OWN;
|
|
Lua_LLThread * this_idx1;
|
|
this_idx1 = llthread_create(L, lua_code_idx1, lua_code_len_idx1, callback);
|
|
|
|
obj_type_Lua_LLThread_push(L, this_idx1, this_flags_idx1);
|
|
return 1;
|
|
}
|
|
|
|
static const luaL_reg obj_Lua_LLThread_pub_funcs[] =
|
|
{
|
|
{NULL, NULL}
|
|
};
|
|
|
|
static const luaL_reg obj_Lua_LLThread_methods[] =
|
|
{
|
|
{"start", Lua_LLThread__start__meth},
|
|
{"join", Lua_LLThread__join__meth},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
static const luaL_reg obj_Lua_LLThread_metas[] =
|
|
{
|
|
{"__gc", Lua_LLThread__delete__meth},
|
|
{"__tostring", obj_udata_default_tostring},
|
|
{"__eq", obj_udata_default_equal},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
static const obj_base obj_Lua_LLThread_bases[] =
|
|
{
|
|
{-1, NULL}
|
|
};
|
|
|
|
static const obj_field obj_Lua_LLThread_fields[] =
|
|
{
|
|
{NULL, 0, 0, 0}
|
|
};
|
|
|
|
static const obj_const obj_Lua_LLThread_constants[] =
|
|
{
|
|
{NULL, NULL, 0.0 , 0}
|
|
};
|
|
|
|
static const luaL_reg llthreads_function[] =
|
|
{
|
|
{"new", llthreads__new__func},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
static const obj_const llthreads_constants[] =
|
|
{
|
|
{NULL, NULL, 0.0 , 0}
|
|
};
|
|
|
|
|
|
static const reg_sub_module reg_sub_modules[] =
|
|
{
|
|
{ &(obj_type_Lua_LLThread), 0, obj_Lua_LLThread_pub_funcs, obj_Lua_LLThread_methods, obj_Lua_LLThread_metas, obj_Lua_LLThread_bases, obj_Lua_LLThread_fields, obj_Lua_LLThread_constants},
|
|
{NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL}
|
|
};
|
|
|
|
static const luaL_Reg submodule_libs[] =
|
|
{
|
|
{NULL, NULL}
|
|
};
|
|
|
|
|
|
|
|
static void create_object_instance_cache(lua_State *L)
|
|
{
|
|
lua_pushlightuserdata(L, obj_udata_weak_ref_key); /* key for weak table. */
|
|
lua_rawget(L, LUA_REGISTRYINDEX); /* check if weak table exists already. */
|
|
if(!lua_isnil(L, -1))
|
|
{
|
|
lua_pop(L, 1); /* pop weak table. */
|
|
return;
|
|
}
|
|
lua_pop(L, 1); /* pop nil. */
|
|
/* create weak table for object instance references. */
|
|
lua_pushlightuserdata(L, obj_udata_weak_ref_key); /* key for weak table. */
|
|
lua_newtable(L); /* weak table. */
|
|
lua_newtable(L); /* metatable for weak table. */
|
|
lua_pushliteral(L, "__mode");
|
|
lua_pushliteral(L, "v");
|
|
lua_rawset(L, -3); /* metatable.__mode = 'v' weak values. */
|
|
lua_setmetatable(L, -2); /* add metatable to weak table. */
|
|
lua_rawset(L, LUA_REGISTRYINDEX); /* create reference to weak table. */
|
|
}
|
|
|
|
int luaopen_llthreads(lua_State *L)
|
|
{
|
|
parent_L = L;
|
|
const reg_sub_module *reg = reg_sub_modules;
|
|
const luaL_Reg *submodules = submodule_libs;
|
|
int priv_table = -1;
|
|
|
|
/* reference to callback function */
|
|
lua_pushstring(L, LLTHREAD_REFID_FUNC_MAPPING);
|
|
lua_newtable(L);
|
|
lua_rawset(L, LUA_REGISTRYINDEX);
|
|
|
|
#if LUAJIT_FFI
|
|
/* private table to hold reference to object metatables. */
|
|
lua_newtable(L);
|
|
priv_table = lua_gettop(L);
|
|
#endif
|
|
|
|
/* create object cache. */
|
|
create_object_instance_cache(L);
|
|
|
|
/* module table. */
|
|
luaL_register(L, "llthreads", llthreads_function);
|
|
|
|
/* register module constants. */
|
|
obj_type_register_constants(L, llthreads_constants, -1);
|
|
|
|
for(; submodules->func != NULL ; submodules++)
|
|
{
|
|
lua_pushcfunction(L, submodules->func);
|
|
lua_pushstring(L, submodules->name);
|
|
lua_call(L, 1, 0);
|
|
}
|
|
|
|
/* register objects */
|
|
for(; reg->type != NULL ; reg++)
|
|
{
|
|
lua_newtable(L); /* create public API table for object. */
|
|
lua_pushvalue(L, -1); /* dup. object's public API table. */
|
|
lua_setfield(L, -3, reg->type->name); /* module["<object_name>"] = <object public API> */
|
|
#if REG_OBJECTS_AS_GLOBALS
|
|
lua_pushvalue(L, -1); /* dup value. */
|
|
lua_setglobal(L, reg->type->name); /* global: <object_name> = <object public API> */
|
|
#endif
|
|
obj_type_register(L, reg, priv_table);
|
|
}
|
|
|
|
#if LUAJIT_FFI
|
|
nobj_try_loading_ffi(L, "llthreads", llthreads_ffi_lua_code,
|
|
llthreads_ffi_export, priv_table);
|
|
#endif
|
|
return 1;
|
|
}
|