ledisdb/lua/c-golua.c

423 lines
9.4 KiB
C

// +build lua
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdint.h>
#include <stdio.h>
#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);
}