// +build lua #include #include #include #include #include #include "_cgo_export.h" #define MT_GOFUNCTION "GoLua.GoFunction" #define MT_GOINTERFACE "GoLua.GoInterface" #define GOLUA_DEFAULT_MSGHANDLER "golua_default_msghandler" static const char GoStateRegistryKey = 'k'; //golua registry key static const char PanicFIDRegistryKey = 'k'; /* taken from lua5.2 source */ void *testudata(lua_State *L, int ud, const char *tname) { void *p = lua_touserdata(L, ud); if (p != NULL) { /* value is a userdata? */ if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ luaL_getmetatable(L, tname); /* get correct metatable */ if (!lua_rawequal(L, -1, -2)) /* not the same? */ p = NULL; /* value is a userdata with wrong metatable */ lua_pop(L, 2); /* remove both metatables */ return p; } } return NULL; /* value is not a userdata with a metatable */ } int clua_isgofunction(lua_State *L, int n) { return testudata(L, n, MT_GOFUNCTION) != NULL; } int clua_isgostruct(lua_State *L, int n) { return testudata(L, n, MT_GOINTERFACE) != NULL; } unsigned int* clua_checkgosomething(lua_State* L, int index, const char *desired_metatable) { if (desired_metatable != NULL) { return testudata(L, index, desired_metatable); } else { unsigned int *sid = testudata(L, index, MT_GOFUNCTION); if (sid != NULL) return sid; return testudata(L, index, MT_GOINTERFACE); } } GoInterface* clua_getgostate(lua_State* L) { //get gostate from registry entry lua_pushlightuserdata(L,(void*)&GoStateRegistryKey); lua_gettable(L, LUA_REGISTRYINDEX); GoInterface* gip = lua_touserdata(L,-1); lua_pop(L,1); return gip; } //wrapper for callgofunction int callback_function(lua_State* L) { int r; unsigned int *fid = clua_checkgosomething(L, 1, MT_GOFUNCTION); GoInterface* gi = clua_getgostate(L); //remove the go function from the stack (to present same behavior as lua_CFunctions) lua_remove(L,1); return golua_callgofunction(*gi, fid!=NULL ? *fid : -1); } //wrapper for gchook int gchook_wrapper(lua_State* L) { //printf("Garbage collection wrapper\n"); unsigned int* fid = clua_checkgosomething(L, -1, NULL); GoInterface* gi = clua_getgostate(L); if (fid != NULL) return golua_gchook(*gi,*fid); return 0; } unsigned int clua_togofunction(lua_State* L, int index) { unsigned int *r = clua_checkgosomething(L, index, MT_GOFUNCTION); return (r != NULL) ? *r : -1; } unsigned int clua_togostruct(lua_State *L, int index) { unsigned int *r = clua_checkgosomething(L, index, MT_GOINTERFACE); return (r != NULL) ? *r : -1; } void clua_pushgofunction(lua_State* L, unsigned int fid) { unsigned int* fidptr = (unsigned int *)lua_newuserdata(L, sizeof(unsigned int)); *fidptr = fid; luaL_getmetatable(L, MT_GOFUNCTION); lua_setmetatable(L, -2); } static int callback_c (lua_State* L) { int fid = clua_togofunction(L,lua_upvalueindex(1)); GoInterface *gi = clua_getgostate(L); return golua_callgofunction(*gi,fid); } void clua_pushcallback(lua_State* L) { lua_pushcclosure(L,callback_c,1); } void clua_pushgostruct(lua_State* L, unsigned int iid) { unsigned int* iidptr = (unsigned int *)lua_newuserdata(L, sizeof(unsigned int)); *iidptr = iid; luaL_getmetatable(L, MT_GOINTERFACE); lua_setmetatable(L,-2); } int default_panicf(lua_State *L) { const char *s = lua_tostring(L, -1); printf("Lua unprotected panic: %s\n", s); abort(); } void clua_setgostate(lua_State* L, GoInterface gi) { lua_atpanic(L, default_panicf); lua_pushlightuserdata(L,(void*)&GoStateRegistryKey); GoInterface* gip = (GoInterface*)lua_newuserdata(L,sizeof(GoInterface)); //copy interface value to userdata gip->v = gi.v; gip->t = gi.t; //set into registry table lua_settable(L, LUA_REGISTRYINDEX); } /* called when lua code attempts to access a field of a published go object */ int interface_index_callback(lua_State *L) { unsigned int *iid = clua_checkgosomething(L, 1, MT_GOINTERFACE); if (iid == NULL) { lua_pushnil(L); return 1; } char *field_name = (char *)lua_tostring(L, 2); if (field_name == NULL) { lua_pushnil(L); return 1; } GoInterface* gi = clua_getgostate(L); int r = golua_interface_index_callback(*gi, *iid, field_name); if (r < 0) { lua_error(L); return 0; } else { return r; } } /* called when lua code attempts to set a field of a published go object */ int interface_newindex_callback(lua_State *L) { unsigned int *iid = clua_checkgosomething(L, 1, MT_GOINTERFACE); if (iid == NULL) { lua_pushnil(L); return 1; } char *field_name = (char *)lua_tostring(L, 2); if (field_name == NULL) { lua_pushnil(L); return 1; } GoInterface* gi = clua_getgostate(L); int r = golua_interface_newindex_callback(*gi, *iid, field_name); if (r < 0) { lua_error(L); return 0; } else { return r; } } int panic_msghandler(lua_State *L) { GoInterface* gi = clua_getgostate(L); go_panic_msghandler(*gi, (char *)lua_tolstring(L, -1, NULL)); return 0; } void clua_hide_pcall(lua_State *L) { lua_getglobal(L, "pcall"); lua_setglobal(L, "unsafe_pcall"); lua_pushnil(L); lua_setglobal(L, "pcall"); lua_getglobal(L, "xpcall"); lua_setglobal(L, "unsafe_xpcall"); lua_pushnil(L); lua_setglobal(L, "xpcall"); } void clua_initstate(lua_State* L) { /* create the GoLua.GoFunction metatable */ luaL_newmetatable(L, MT_GOFUNCTION); // gofunction_metatable[__call] = &callback_function lua_pushliteral(L,"__call"); lua_pushcfunction(L,&callback_function); lua_settable(L,-3); // gofunction_metatable[__gc] = &gchook_wrapper lua_pushliteral(L,"__gc"); lua_pushcfunction(L,&gchook_wrapper); lua_settable(L,-3); lua_pop(L,1); luaL_newmetatable(L, MT_GOINTERFACE); // gointerface_metatable[__gc] = &gchook_wrapper lua_pushliteral(L, "__gc"); lua_pushcfunction(L, &gchook_wrapper); lua_settable(L, -3); // gointerface_metatable[__index] = &interface_index_callback lua_pushliteral(L, "__index"); lua_pushcfunction(L, &interface_index_callback); lua_settable(L, -3); // gointerface_metatable[__newindex] = &interface_newindex_callback lua_pushliteral(L, "__newindex"); lua_pushcfunction(L, &interface_newindex_callback); lua_settable(L, -3); lua_register(L, GOLUA_DEFAULT_MSGHANDLER, &panic_msghandler); lua_pop(L, 1); } int callback_panicf(lua_State* L) { lua_pushlightuserdata(L,(void*)&PanicFIDRegistryKey); lua_gettable(L,LUA_REGISTRYINDEX); unsigned int fid = lua_tointeger(L,-1); lua_pop(L,1); GoInterface* gi = clua_getgostate(L); return golua_callpanicfunction(*gi,fid); } //TODO: currently setting garbage when panicf set to null GoInterface clua_atpanic(lua_State* L, unsigned int panicf_id) { //get old panicfid unsigned int old_id; lua_pushlightuserdata(L, (void*)&PanicFIDRegistryKey); lua_gettable(L,LUA_REGISTRYINDEX); if(lua_isnil(L, -1) == 0) old_id = lua_tointeger(L,-1); lua_pop(L, 1); //set registry key for function id of go panic function lua_pushlightuserdata(L, (void*)&PanicFIDRegistryKey); //push id value lua_pushinteger(L, panicf_id); //set into registry table lua_settable(L, LUA_REGISTRYINDEX); //now set the panic function lua_CFunction pf = lua_atpanic(L,&callback_panicf); //make a GoInterface with a wrapped C panicf or the original go panicf if(pf == &callback_panicf) { return golua_idtointerface(old_id); } else { //TODO: technically UB, function ptr -> non function ptr return golua_cfunctiontointerface((GoUintptr *)pf); } } int clua_callluacfunc(lua_State* L, lua_CFunction f) { return f(L); } void* allocwrapper(void* ud, void *ptr, size_t osize, size_t nsize) { return (void*)golua_callallocf((GoUintptr)ud,(GoUintptr)ptr,osize,nsize); } lua_State* clua_newstate(void* goallocf) { return lua_newstate(&allocwrapper,goallocf); } void clua_setallocf(lua_State* L, void* goallocf) { lua_setallocf(L,&allocwrapper,goallocf); } void clua_openbase(lua_State* L) { lua_pushcfunction(L,&luaopen_base); lua_pushstring(L,""); lua_call(L, 1, 0); clua_hide_pcall(L); } void clua_openio(lua_State* L) { lua_pushcfunction(L,&luaopen_io); lua_pushstring(L,"io"); lua_call(L, 1, 0); } void clua_openmath(lua_State* L) { lua_pushcfunction(L,&luaopen_math); lua_pushstring(L,"math"); lua_call(L, 1, 0); } void clua_openpackage(lua_State* L) { lua_pushcfunction(L,&luaopen_package); lua_pushstring(L,"package"); lua_call(L, 1, 0); } void clua_openstring(lua_State* L) { lua_pushcfunction(L,&luaopen_string); lua_pushstring(L,"string"); lua_call(L, 1, 0); } void clua_opentable(lua_State* L) { lua_pushcfunction(L,&luaopen_table); lua_pushstring(L,"table"); lua_call(L, 1, 0); } void clua_openos(lua_State* L) { lua_pushcfunction(L,&luaopen_os); lua_pushstring(L,"os"); lua_call(L, 1, 0); } void clua_hook_function(lua_State *L, lua_Debug *ar) { lua_checkstack(L, 2); lua_pushstring(L, "Lua execution quantum exceeded"); lua_error(L); } void clua_setexecutionlimit(lua_State* L, int n) { lua_sethook(L, &clua_hook_function, LUA_MASKCOUNT, n); } LUALIB_API int (luaopen_cjson) (lua_State *L); LUALIB_API int (luaopen_struct) (lua_State *L); LUALIB_API int (luaopen_cmsgpack) (lua_State *L); void clua_opencjson(lua_State* L) { lua_pushcfunction(L,&luaopen_cjson); lua_pushstring(L,"cjson"); lua_call(L, 1, 0); } void clua_openstruct(lua_State* L) { lua_pushcfunction(L,&luaopen_struct); lua_pushstring(L,"struct"); lua_call(L, 1, 0); } void clua_opencmsgpack(lua_State* L) { lua_pushcfunction(L,&luaopen_cmsgpack); lua_pushstring(L,"cmsgpack"); lua_call(L, 1, 0); }