c-golua.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. #include "golua.h"
  2. GoInterface golua_idtointerface(unsigned int);
  3. GoInterface golua_cfunctiontointerface(lua_CFunction);
  4. GoUintptr golua_callallocf(GoUintptr,GoUintptr,size_t,size_t);
  5. #define MT_GOFUNCTION "GoLua.GoFunction"
  6. #define MT_GOINTERFACE "GoLua.GoInterface"
  7. #define GOLUA_DEFAULT_MSGHANDLER "golua_default_msghandler"
  8. static const char GoStateRegistryKey = 'k'; //golua registry key
  9. static const char PanicFIDRegistryKey = 'k';
  10. /* taken from lua5.2 source */
  11. void *testudata(lua_State *L, int ud, const char *tname)
  12. {
  13. void *p = lua_touserdata(L, ud);
  14. if (p != NULL)
  15. { /* value is a userdata? */
  16. if (lua_getmetatable(L, ud))
  17. { /* does it have a metatable? */
  18. luaL_getmetatable(L, tname); /* get correct metatable */
  19. if (!lua_rawequal(L, -1, -2)) /* not the same? */
  20. p = NULL; /* value is a userdata with wrong metatable */
  21. lua_pop(L, 2); /* remove both metatables */
  22. return p;
  23. }
  24. }
  25. return NULL; /* value is not a userdata with a metatable */
  26. }
  27. int clua_isgofunction(lua_State *L, int n)
  28. {
  29. return testudata(L, n, MT_GOFUNCTION) != NULL;
  30. }
  31. int clua_isgostruct(lua_State *L, int n)
  32. {
  33. return testudata(L, n, MT_GOINTERFACE) != NULL;
  34. }
  35. unsigned int* clua_checkgosomething(lua_State* L, int index, const char *desired_metatable)
  36. {
  37. if (desired_metatable != NULL)
  38. {
  39. return testudata(L, index, desired_metatable);
  40. }
  41. else
  42. {
  43. unsigned int *sid = testudata(L, index, MT_GOFUNCTION);
  44. if (sid != NULL) return sid;
  45. return testudata(L, index, MT_GOINTERFACE);
  46. }
  47. }
  48. size_t clua_getgostate(lua_State* L)
  49. {
  50. size_t gostateindex;
  51. //get gostate from registry entry
  52. lua_pushlightuserdata(L,(void*)&GoStateRegistryKey);
  53. lua_gettable(L, LUA_REGISTRYINDEX);
  54. gostateindex = (size_t)lua_touserdata(L,-1);
  55. lua_pop(L,1);
  56. return gostateindex;
  57. }
  58. //wrapper for callgofunction
  59. int callback_function(lua_State* L)
  60. {
  61. int r;
  62. unsigned int *fid = clua_checkgosomething(L, 1, MT_GOFUNCTION);
  63. size_t gostateindex = clua_getgostate(L);
  64. //remove the go function from the stack (to present same behavior as lua_CFunctions)
  65. lua_remove(L,1);
  66. return golua_callgofunction(gostateindex, fid!=NULL ? *fid : -1);
  67. }
  68. //wrapper for gchook
  69. int gchook_wrapper(lua_State* L)
  70. {
  71. //printf("Garbage collection wrapper\n");
  72. unsigned int* fid = clua_checkgosomething(L, -1, NULL);
  73. size_t gostateindex = clua_getgostate(L);
  74. if (fid != NULL)
  75. return golua_gchook(gostateindex,*fid);
  76. return 0;
  77. }
  78. unsigned int clua_togofunction(lua_State* L, int index)
  79. {
  80. unsigned int *r = clua_checkgosomething(L, index, MT_GOFUNCTION);
  81. return (r != NULL) ? *r : -1;
  82. }
  83. unsigned int clua_togostruct(lua_State *L, int index)
  84. {
  85. unsigned int *r = clua_checkgosomething(L, index, MT_GOINTERFACE);
  86. return (r != NULL) ? *r : -1;
  87. }
  88. void clua_pushgofunction(lua_State* L, unsigned int fid)
  89. {
  90. unsigned int* fidptr = (unsigned int *)lua_newuserdata(L, sizeof(unsigned int));
  91. *fidptr = fid;
  92. luaL_getmetatable(L, MT_GOFUNCTION);
  93. lua_setmetatable(L, -2);
  94. }
  95. static int callback_c (lua_State* L)
  96. {
  97. int fid = clua_togofunction(L,lua_upvalueindex(1));
  98. size_t gostateindex = clua_getgostate(L);
  99. return golua_callgofunction(gostateindex,fid);
  100. }
  101. void clua_pushcallback(lua_State* L)
  102. {
  103. lua_pushcclosure(L,callback_c,1);
  104. }
  105. void clua_pushgostruct(lua_State* L, unsigned int iid)
  106. {
  107. unsigned int* iidptr = (unsigned int *)lua_newuserdata(L, sizeof(unsigned int));
  108. *iidptr = iid;
  109. luaL_getmetatable(L, MT_GOINTERFACE);
  110. lua_setmetatable(L,-2);
  111. }
  112. int default_panicf(lua_State *L)
  113. {
  114. const char *s = lua_tostring(L, -1);
  115. printf("Lua unprotected panic: %s\n", s);
  116. abort();
  117. }
  118. void clua_setgostate(lua_State* L, size_t gostateindex)
  119. {
  120. lua_atpanic(L, default_panicf);
  121. lua_pushlightuserdata(L,(void*)&GoStateRegistryKey);
  122. lua_pushlightuserdata(L, (void*)gostateindex);
  123. //set into registry table
  124. lua_settable(L, LUA_REGISTRYINDEX);
  125. }
  126. /* called when lua code attempts to access a field of a published go object */
  127. int interface_index_callback(lua_State *L)
  128. {
  129. unsigned int *iid = clua_checkgosomething(L, 1, MT_GOINTERFACE);
  130. if (iid == NULL)
  131. {
  132. lua_pushnil(L);
  133. return 1;
  134. }
  135. char *field_name = (char *)lua_tostring(L, 2);
  136. if (field_name == NULL)
  137. {
  138. lua_pushnil(L);
  139. return 1;
  140. }
  141. size_t gostateindex = clua_getgostate(L);
  142. int r = golua_interface_index_callback(gostateindex, *iid, field_name);
  143. if (r < 0)
  144. {
  145. lua_error(L);
  146. return 0;
  147. }
  148. else
  149. {
  150. return r;
  151. }
  152. }
  153. /* called when lua code attempts to set a field of a published go object */
  154. int interface_newindex_callback(lua_State *L)
  155. {
  156. unsigned int *iid = clua_checkgosomething(L, 1, MT_GOINTERFACE);
  157. if (iid == NULL)
  158. {
  159. lua_pushnil(L);
  160. return 1;
  161. }
  162. char *field_name = (char *)lua_tostring(L, 2);
  163. if (field_name == NULL)
  164. {
  165. lua_pushnil(L);
  166. return 1;
  167. }
  168. size_t gostateindex = clua_getgostate(L);
  169. int r = golua_interface_newindex_callback(gostateindex, *iid, field_name);
  170. if (r < 0)
  171. {
  172. lua_error(L);
  173. return 0;
  174. }
  175. else
  176. {
  177. return r;
  178. }
  179. }
  180. int panic_msghandler(lua_State *L)
  181. {
  182. size_t gostateindex = clua_getgostate(L);
  183. go_panic_msghandler(gostateindex, (char *)lua_tolstring(L, -1, NULL));
  184. return 0;
  185. }
  186. void clua_hide_pcall(lua_State *L)
  187. {
  188. lua_getglobal(L, "pcall");
  189. lua_setglobal(L, "unsafe_pcall");
  190. lua_pushnil(L);
  191. lua_setglobal(L, "pcall");
  192. lua_getglobal(L, "xpcall");
  193. lua_setglobal(L, "unsafe_xpcall");
  194. lua_pushnil(L);
  195. lua_setglobal(L, "xpcall");
  196. }
  197. void clua_initstate(lua_State* L)
  198. {
  199. /* create the GoLua.GoFunction metatable */
  200. luaL_newmetatable(L, MT_GOFUNCTION);
  201. // gofunction_metatable[__call] = &callback_function
  202. lua_pushliteral(L,"__call");
  203. lua_pushcfunction(L,&callback_function);
  204. lua_settable(L,-3);
  205. // gofunction_metatable[__gc] = &gchook_wrapper
  206. lua_pushliteral(L,"__gc");
  207. lua_pushcfunction(L,&gchook_wrapper);
  208. lua_settable(L,-3);
  209. lua_pop(L,1);
  210. luaL_newmetatable(L, MT_GOINTERFACE);
  211. // gointerface_metatable[__gc] = &gchook_wrapper
  212. lua_pushliteral(L, "__gc");
  213. lua_pushcfunction(L, &gchook_wrapper);
  214. lua_settable(L, -3);
  215. // gointerface_metatable[__index] = &interface_index_callback
  216. lua_pushliteral(L, "__index");
  217. lua_pushcfunction(L, &interface_index_callback);
  218. lua_settable(L, -3);
  219. // gointerface_metatable[__newindex] = &interface_newindex_callback
  220. lua_pushliteral(L, "__newindex");
  221. lua_pushcfunction(L, &interface_newindex_callback);
  222. lua_settable(L, -3);
  223. lua_register(L, GOLUA_DEFAULT_MSGHANDLER, &panic_msghandler);
  224. lua_pop(L, 1);
  225. }
  226. int callback_panicf(lua_State* L)
  227. {
  228. lua_pushlightuserdata(L,(void*)&PanicFIDRegistryKey);
  229. lua_gettable(L,LUA_REGISTRYINDEX);
  230. unsigned int fid = lua_tointeger(L,-1);
  231. lua_pop(L,1);
  232. size_t gostateindex = clua_getgostate(L);
  233. return golua_callpanicfunction(gostateindex,fid);
  234. }
  235. //TODO: currently setting garbage when panicf set to null
  236. GoInterface clua_atpanic(lua_State* L, unsigned int panicf_id)
  237. {
  238. //get old panicfid
  239. unsigned int old_id;
  240. lua_pushlightuserdata(L, (void*)&PanicFIDRegistryKey);
  241. lua_gettable(L,LUA_REGISTRYINDEX);
  242. if(lua_isnil(L, -1) == 0)
  243. old_id = lua_tointeger(L,-1);
  244. lua_pop(L, 1);
  245. //set registry key for function id of go panic function
  246. lua_pushlightuserdata(L, (void*)&PanicFIDRegistryKey);
  247. //push id value
  248. lua_pushinteger(L, panicf_id);
  249. //set into registry table
  250. lua_settable(L, LUA_REGISTRYINDEX);
  251. //now set the panic function
  252. lua_CFunction pf = lua_atpanic(L,&callback_panicf);
  253. //make a GoInterface with a wrapped C panicf or the original go panicf
  254. if(pf == &callback_panicf)
  255. {
  256. return golua_idtointerface(old_id);
  257. }
  258. else
  259. {
  260. //TODO: technically UB, function ptr -> non function ptr
  261. return golua_cfunctiontointerface((GoUintptr *)pf);
  262. }
  263. }
  264. int clua_callluacfunc(lua_State* L, lua_CFunction f)
  265. {
  266. return f(L);
  267. }
  268. void* allocwrapper(void* ud, void *ptr, size_t osize, size_t nsize)
  269. {
  270. return (void*)golua_callallocf((GoUintptr)ud,(GoUintptr)ptr,osize,nsize);
  271. }
  272. lua_State* clua_newstate(void* goallocf)
  273. {
  274. return lua_newstate(&allocwrapper,goallocf);
  275. }
  276. void clua_setallocf(lua_State* L, void* goallocf)
  277. {
  278. lua_setallocf(L,&allocwrapper,goallocf);
  279. }
  280. void clua_openbase(lua_State* L)
  281. {
  282. lua_pushcfunction(L,&luaopen_base);
  283. lua_pushstring(L,"");
  284. lua_call(L, 1, 0);
  285. clua_hide_pcall(L);
  286. }
  287. void clua_hook_function(lua_State *L, lua_Debug *ar)
  288. {
  289. lua_checkstack(L, 2);
  290. size_t gostateindex = clua_getgostate(L);
  291. golua_callgohook(gostateindex);
  292. }
  293. void clua_sethook(lua_State* L, int n)
  294. {
  295. lua_sethook(L, &clua_hook_function, LUA_MASKCOUNT, n);
  296. }