/* tolua: event functions ** Support code for Lua bindings. ** Written by Waldemar Celes ** TeCGraf/PUC-Rio ** Apr 2003 ** $Id: $ */ /* This code is free software; you can redistribute it and/or modify it. ** The software provided hereunder is on an "as is" basis, and ** the author has no obligation to provide maintenance, support, updates, ** enhancements, or modifications. */ #include #include "tolua++.h" /* Store at ubox * It stores, creating the corresponding table if needed, * the pair key/value in the corresponding ubox table */ static void storeatubox (lua_State* L, int lo) { #ifdef LUA_VERSION_NUM lua_getfenv(L, lo); if (lua_rawequal(L, -1, TOLUA_NOPEER)) { lua_pop(L, 1); lua_newtable(L); lua_pushvalue(L, -1); lua_setfenv(L, lo); /* stack: k,v,table */ }; lua_insert(L, -3); lua_settable(L, -3); /* on lua 5.1, we trade the "tolua_peers" lookup for a settable call */ lua_pop(L, 1); #else /* stack: key value (to be stored) */ lua_pushstring(L,"tolua_peers"); lua_rawget(L,LUA_REGISTRYINDEX); /* stack: k v ubox */ lua_pushvalue(L,lo); lua_rawget(L,-2); /* stack: k v ubox ubox[u] */ if (!lua_istable(L,-1)) { lua_pop(L,1); /* stack: k v ubox */ lua_newtable(L); /* stack: k v ubox table */ lua_pushvalue(L,1); lua_pushvalue(L,-2); /* stack: k v ubox table u table */ lua_rawset(L,-4); /* stack: k v ubox ubox[u]=table */ } lua_insert(L,-4); /* put table before k */ lua_pop(L,1); /* pop ubox */ lua_rawset(L,-3); /* store at table */ lua_pop(L,1); /* pop ubox[u] */ #endif } /* Module index function */ static int module_index_event (lua_State* L) { lua_pushstring(L,".get"); lua_rawget(L,-3); if (lua_istable(L,-1)) { lua_pushvalue(L,2); /* key */ lua_rawget(L,-2); if (lua_iscfunction(L,-1)) { lua_call(L,0,1); return 1; } else if (lua_istable(L,-1)) return 1; } /* call old index meta event */ if (lua_getmetatable(L,1)) { lua_pushstring(L,"__index"); lua_rawget(L,-2); lua_pushvalue(L,1); lua_pushvalue(L,2); if (lua_isfunction(L,-1)) { lua_call(L,2,1); return 1; } else if (lua_istable(L,-1)) { lua_gettable(L,-3); return 1; } } lua_pushnil(L); return 1; } /* Module newindex function */ static int module_newindex_event (lua_State* L) { lua_pushstring(L,".set"); lua_rawget(L,-4); if (lua_istable(L,-1)) { lua_pushvalue(L,2); /* key */ lua_rawget(L,-2); if (lua_iscfunction(L,-1)) { lua_pushvalue(L,1); /* only to be compatible with non-static vars */ lua_pushvalue(L,3); /* value */ lua_call(L,2,0); return 0; } } /* call old newindex meta event */ if (lua_getmetatable(L,1) && lua_getmetatable(L,-1)) { lua_pushstring(L,"__newindex"); lua_rawget(L,-2); if (lua_isfunction(L,-1)) { lua_pushvalue(L,1); lua_pushvalue(L,2); lua_pushvalue(L,3); lua_call(L,3,0); } } lua_settop(L,3); lua_rawset(L,-3); return 0; } static int class_table_get_index (lua_State* L) { // stack: obj key ... obj while (lua_getmetatable(L,-1)) { /* stack: obj key obj mt */ lua_remove(L,-2); /* stack: ... mt */ lua_pushvalue(L,2); /* stack: ... mt key */ lua_rawget(L,-2); /* stack: ... mt value */ if (!lua_isnil(L,-1)) { return 1; } else { lua_pop(L,1); } /* try C/C++ variable */ lua_pushstring(L,".get"); lua_rawget(L,-2); /* stack: obj key ... mt tget */ if (lua_istable(L,-1)) { lua_pushvalue(L,2); /* stack: obj key ... mt tget key */ lua_rawget(L,-2); /* stack: obj key ... mt tget value */ if (lua_iscfunction(L,-1)) { lua_call(L,0,1); return 1; } else if (lua_istable(L,-1)) { return 1; } lua_pop(L, 2); } } lua_pushnil(L); return 1; } /* Class index function * If the object is a userdata (ie, an object), it searches the field in * the alternative table stored in the corresponding "ubox" table. */ static int class_index_event (lua_State* L) { int t = lua_type(L,1); if (t == LUA_TUSERDATA) { /* Access alternative table */ #ifdef LUA_VERSION_NUM /* new macro on version 5.1 */ lua_getfenv(L,1); if (!lua_rawequal(L, -1, TOLUA_NOPEER)) { lua_pushvalue(L, 2); /* key */ lua_gettable(L, -2); /* on lua 5.1, we trade the "tolua_peers" lookup for a gettable call */ if (!lua_isnil(L, -1)) return 1; }; #else lua_pushstring(L,"tolua_peers"); lua_rawget(L,LUA_REGISTRYINDEX); /* stack: obj key ubox */ lua_pushvalue(L,1); lua_rawget(L,-2); /* stack: obj key ubox ubox[u] */ if (lua_istable(L,-1)) { lua_pushvalue(L,2); /* key */ lua_rawget(L,-2); /* stack: obj key ubox ubox[u] value */ if (!lua_isnil(L,-1)) return 1; } #endif lua_settop(L,2); /* stack: obj key */ /* Try metatables */ lua_pushvalue(L,1); /* stack: obj key obj */ while (lua_getmetatable(L,-1)) { /* stack: obj key obj mt */ lua_remove(L,-2); /* stack: obj key mt */ if (lua_isnumber(L,2)) /* check if key is a numeric value */ { /* try operator[] */ lua_pushstring(L,".geti"); lua_rawget(L,-2); /* stack: obj key mt func */ if (lua_isfunction(L,-1)) { lua_pushvalue(L,1); lua_pushvalue(L,2); lua_call(L,2,1); return 1; } } else { lua_pushvalue(L,2); /* stack: obj key mt key */ lua_rawget(L,-2); /* stack: obj key mt value */ if (!lua_isnil(L,-1)) return 1; else lua_pop(L,1); /* try C/C++ variable */ lua_pushstring(L,".get"); lua_rawget(L,-2); /* stack: obj key mt tget */ if (lua_istable(L,-1)) { lua_pushvalue(L,2); lua_rawget(L,-2); /* stack: obj key mt value */ if (lua_iscfunction(L,-1)) { lua_pushvalue(L,1); lua_pushvalue(L,2); lua_call(L,2,1); return 1; } else if (lua_istable(L,-1)) { /* deal with array: create table to be returned and cache it in ubox */ void* u = *((void**)lua_touserdata(L,1)); lua_newtable(L); /* stack: obj key mt value table */ lua_pushstring(L,".self"); lua_pushlightuserdata(L,u); lua_rawset(L,-3); /* store usertype in ".self" */ lua_insert(L,-2); /* stack: obj key mt table value */ lua_setmetatable(L,-2); /* set stored value as metatable */ lua_pushvalue(L,-1); /* stack: obj key met table table */ lua_pushvalue(L,2); /* stack: obj key mt table table key */ lua_insert(L,-2); /* stack: obj key mt table key table */ storeatubox(L,1); /* stack: obj key mt table */ return 1; } } } lua_settop(L,3); } lua_pushnil(L); return 1; } else if (t== LUA_TTABLE) { lua_pushvalue(L,1); class_table_get_index(L); return 1; } lua_pushnil(L); return 1; } static int class_backup_before_newindex (lua_State* L) { /* stack: t k v */ int m; lua_pushvalue(L, 1); m = lua_getmetatable(L,-1); /* stack: t k v t mt */ while (m>0 && lua_istable(L,-1)) { lua_remove(L, -2); /* stack: t k v mt */ //Check if key had been backup lua_pushstring(L, ".backup"); lua_rawget(L, -2); /* stack: t k v mt mt[".backup"] */ if (!lua_isnil(L, -1)) { lua_pushvalue(L, 2); /* stack: t k v mt mt[".backup"] k */ lua_rawget(L, -2); if (!lua_isnil(L, -1)) { // key had been backup return 0; } lua_pop(L, 1); } lua_pop(L, 1); /* stack: t k v mt */ //Check if key is exist in mt lua_pushvalue(L, 2); lua_rawget(L, -2); /* stack: t k v mt mt[k] */ if (!lua_isnil(L, -1)) { lua_pushvalue(L, -2); /* stack: t k v mt mt[k] mt */ lua_pushstring(L, ".backup"); lua_rawget(L, -2); /* stack: t k v mt mt[k] mt mt[".backup"] */ if (lua_isnil(L, -1)) { //Create a table and set to mt[".backup"] lua_pop(L, 1); /* stack: t k v mt mt[k] mt */ lua_pushstring(L, ".backup"); lua_newtable(L); lua_rawset(L, -3); lua_pushstring(L, ".backup"); lua_rawget(L, -2); /* stack: t k v mt mt[k] mt mt[".backup"] */ } lua_pushvalue(L, 2); /* stack: t k v mt mt[k] mt mt[".backup"] k */ lua_pushvalue(L, -4); /* stack: t k v mt mt[k] mt mt[".backup"] k mt[k] */ lua_rawset(L, -3); return 0; } lua_pop(L, 1); /* stack: t k v mt */ m = lua_getmetatable(L,-1); /* stack: t k v mt base_mt */ } return 0; } /* Newindex function * It first searches for a C/C++ varaible to be set. * Then, it either stores it in the alternative ubox table (in the case it is * an object) or in the own table (that represents the class or module). */ static int class_newindex_event (lua_State* L) { int t = lua_type(L,1); if (t == LUA_TUSERDATA) { /* Try accessing a C/C++ variable to be set */ lua_getmetatable(L,1); while (lua_istable(L,-1)) /* stack: t k v mt */ { if (lua_isnumber(L,2)) /* check if key is a numeric value */ { /* try operator[] */ lua_pushstring(L,".seti"); lua_rawget(L,-2); /* stack: obj key mt func */ if (lua_isfunction(L,-1)) { lua_pushvalue(L,1); lua_pushvalue(L,2); lua_pushvalue(L,3); lua_call(L,3,0); return 0; } } else { lua_pushstring(L,".set"); lua_rawget(L,-2); /* stack: t k v mt tset */ if (lua_istable(L,-1)) { lua_pushvalue(L,2); lua_rawget(L,-2); /* stack: t k v mt tset func */ if (lua_iscfunction(L,-1)) { lua_pushvalue(L,1); lua_pushvalue(L,3); lua_call(L,2,0); return 0; } lua_pop(L,1); /* stack: t k v mt tset */ } lua_pop(L,1); /* stack: t k v mt */ if (!lua_getmetatable(L,-1)) /* stack: t k v mt mt */ lua_pushnil(L); lua_remove(L,-2); /* stack: t k v mt */ } } lua_settop(L,3); /* stack: t k v */ /* then, store as a new field */ storeatubox(L,1); } else if (t== LUA_TTABLE) { lua_getmetatable(L,1); /* stack: t k v mt */ lua_pushstring(L,".set"); lua_rawget(L,-2); /* stack: t k v mt tset */ if (lua_istable(L,-1)) { lua_pushvalue(L,2); /* stack: t k v mt tset k */ lua_rawget(L,-2); if (lua_iscfunction(L,-1)) { /* ... func */ lua_pushvalue(L,1); /* ... func t */ lua_pushvalue(L,3); /* ... func t v */ lua_call(L,2,0); return 0; } } lua_settop(L,3); class_backup_before_newindex(L); lua_settop(L,3); lua_getmetatable(L,1); /* stack: t k v mt */ lua_replace(L, 1); /* stack: mt k v */ lua_rawset(L,1); } return 0; } static int class_call_event(lua_State* L) { if (lua_istable(L, 1)) { //class is not a metatable now, so must get it's metatable to access ".call" function. 2014.6.5 by SunLightJuly if (lua_getmetatable(L, 1)) { lua_replace(L, 1); lua_pushstring(L, ".call"); lua_rawget(L, 1); if (lua_isfunction(L, -1)) { lua_insert(L, 1); lua_call(L, lua_gettop(L)-1, 1); return 1; } } } tolua_error(L,"Attempt to call a non-callable object.",NULL); return 0; }; static int do_operator (lua_State* L, const char* op) { if (lua_isuserdata(L,1)) { /* Try metatables */ lua_pushvalue(L,1); /* stack: op1 op2 */ while (lua_getmetatable(L,-1)) { /* stack: op1 op2 op1 mt */ lua_remove(L,-2); /* stack: op1 op2 mt */ lua_pushstring(L,op); /* stack: op1 op2 mt key */ lua_rawget(L,-2); /* stack: obj key mt func */ if (lua_isfunction(L,-1)) { lua_pushvalue(L,1); lua_pushvalue(L,2); lua_call(L,2,1); return 1; } lua_settop(L,3); } } tolua_error(L,"Attempt to perform operation on an invalid operand",NULL); return 0; } static int class_add_event (lua_State* L) { return do_operator(L,".add"); } int class_sub_event (lua_State* L) { return do_operator(L,".sub"); } static int class_mul_event (lua_State* L) { return do_operator(L,".mul"); } static int class_div_event (lua_State* L) { return do_operator(L,".div"); } static int class_lt_event (lua_State* L) { return do_operator(L,".lt"); } static int class_le_event (lua_State* L) { return do_operator(L,".le"); } static int class_eq_event (lua_State* L) { /* copying code from do_operator here to return false when no operator is found */ if (lua_isuserdata(L,1)) { /* Try metatables */ lua_pushvalue(L,1); /* stack: op1 op2 */ while (lua_getmetatable(L,-1)) { /* stack: op1 op2 op1 mt */ lua_remove(L,-2); /* stack: op1 op2 mt */ lua_pushstring(L,".eq"); /* stack: op1 op2 mt key */ lua_rawget(L,-2); /* stack: obj key mt func */ if (lua_isfunction(L,-1)) { lua_pushvalue(L,1); lua_pushvalue(L,2); lua_call(L,2,1); return 1; } lua_settop(L,3); } } lua_settop(L, 3); lua_pushboolean(L, 0); return 1; } /* static int class_gc_event (lua_State* L) { void* u = *((void**)lua_touserdata(L,1)); fprintf(stderr, "collecting: looking at %p\n", u); lua_pushstring(L,"tolua_gc"); lua_rawget(L,LUA_REGISTRYINDEX); lua_pushlightuserdata(L,u); lua_rawget(L,-2); if (lua_isfunction(L,-1)) { lua_pushvalue(L,1); lua_call(L,1,0); lua_pushlightuserdata(L,u); lua_pushnil(L); lua_rawset(L,-3); } lua_pop(L,2); return 0; } */ TOLUA_API int class_gc_event (lua_State* L) { void** pu = (void**)lua_touserdata(L, 1); if (!pu) return 0; void* u = *pu; int top; /*fprintf(stderr, "collecting: looking at %p\n", u);*/ /* lua_pushstring(L,"tolua_gc"); lua_rawget(L,LUA_REGISTRYINDEX); */ lua_pushvalue(L, lua_upvalueindex(1)); lua_pushlightuserdata(L,u); lua_rawget(L,-2); /* stack: gc umt */ lua_getmetatable(L,1); /* stack: gc umt mt */ /*fprintf(stderr, "checking type\n");*/ top = lua_gettop(L); if (tolua_fast_isa(L,top,top-1, lua_upvalueindex(2))) /* make sure we collect correct type */ { /*fprintf(stderr, "Found type!\n");*/ /* get gc function */ lua_pushliteral(L,".collector"); lua_rawget(L,-2); /* stack: gc umt mt collector */ if (lua_isfunction(L,-1)) { /*fprintf(stderr, "Found .collector!\n");*/ } else { lua_pop(L,1); /*fprintf(stderr, "Using default cleanup\n");*/ lua_pushcfunction(L,tolua_default_collect); } lua_pushvalue(L,1); /* stack: gc umt mt collector u */ lua_call(L,1,0); lua_pushlightuserdata(L,u); /* stack: gc umt mt u */ lua_pushnil(L); /* stack: gc umt mt u nil */ lua_rawset(L,-5); /* stack: gc umt mt */ } lua_pop(L,3); return 0; } /* Register module events * It expects the metatable on the top of the stack */ TOLUA_API void tolua_moduleevents (lua_State* L) { lua_pushstring(L,"__index"); lua_pushcfunction(L,module_index_event); lua_rawset(L,-3); lua_pushstring(L,"__newindex"); lua_pushcfunction(L,module_newindex_event); lua_rawset(L,-3); } /* Check if the object on the top has a module metatable */ TOLUA_API int tolua_ismodulemetatable (lua_State* L) { int r = 0; if (lua_getmetatable(L,-1)) { lua_pushstring(L,"__index"); lua_rawget(L,-2); r = (lua_tocfunction(L,-1) == module_index_event); lua_pop(L,2); } return r; } /* Register class events * It expects the metatable on the top of the stack */ TOLUA_API void tolua_classevents (lua_State* L) { lua_pushstring(L,"__index"); lua_pushcfunction(L,class_index_event); lua_rawset(L,-3); lua_pushstring(L,"__newindex"); lua_pushcfunction(L,class_newindex_event); lua_rawset(L,-3); lua_pushstring(L,"__add"); lua_pushcfunction(L,class_add_event); lua_rawset(L,-3); lua_pushstring(L,"__sub"); lua_pushcfunction(L,class_sub_event); lua_rawset(L,-3); lua_pushstring(L,"__mul"); lua_pushcfunction(L,class_mul_event); lua_rawset(L,-3); lua_pushstring(L,"__div"); lua_pushcfunction(L,class_div_event); lua_rawset(L,-3); lua_pushstring(L,"__lt"); lua_pushcfunction(L,class_lt_event); lua_rawset(L,-3); lua_pushstring(L,"__le"); lua_pushcfunction(L,class_le_event); lua_rawset(L,-3); lua_pushstring(L,"__eq"); lua_pushcfunction(L,class_eq_event); lua_rawset(L,-3); lua_pushstring(L,"__call"); lua_pushcfunction(L,class_call_event); lua_rawset(L,-3); lua_pushstring(L,"__gc"); lua_pushstring(L, "tolua_gc_event"); lua_rawget(L, LUA_REGISTRYINDEX); /*lua_pushcfunction(L,class_gc_event);*/ lua_rawset(L,-3); }