void HybridBase::setMethod(lua_State *L, ClassInfo *ci) { SLB_DEBUG_CALL; SLB_DEBUG_CLEAN_STACK(L,-2); // key // value [top] int top = lua_gettop(L); // checks key, value assert( "Invalid key for method" && lua_type(L,top-1) == LUA_TSTRING); assert( "Invalid type of method" && lua_type(L,top) == LUA_TFUNCTION); ci->push(L); // top +1 lua_getmetatable(L,-1); // top +2 lua_getfield(L,-1, "__hybrid"); // top +3 // create if not exists if (lua_isnil(L,-1)) { lua_pop(L,1); // remove nil lua_newtable(L); // top +3 lua_pushstring(L, "__hybrid"); lua_pushvalue(L,-2); // a copy for ClassInfo lua_rawset(L, top+2); // set to he metatable } lua_insert(L,top-2); // put the __hybrid table below key,value lua_settop(L, top+1); // table, key, and value lua_rawset(L,top-2); // set elements lua_settop(L, top-2); // remove everything :) }
int HybridBase::class__index(lua_State *L) { SLB_DEBUG_CALL; SLB_DEBUG_CLEAN_STACK(L,+1); SLB_DEBUG_STACK(6, L, "Call class__index"); // trying to traverse the class... create a new InternalHybridSubclass ClassInfo *ci = Manager::getInstance().getClass(L,1); if (ci == 0) luaL_error(L, "Expected a valid class."); luaL_checkstring(L,2); // only valid with strings if (!ci->hasConstructor()) { luaL_error(L, "Hybrid Class(%s) doesn't have constructor." " You can not subclass(%s) from it", ci->getName().c_str(), lua_tostring(L,2)); } ref_ptr<InternalHybridSubclass> subc = new InternalHybridSubclass(ci); subc->push(L); // -- set cache... lua_pushvalue(L,2); // [+1] key lua_pushvalue(L,-2); // [+1] copy of new InternalHybrid... ci->setCache(L); // [-2] keep a copy in the cache // -- set cache done return 1; }
int HybridBase::class__newindex(lua_State *L) { SLB_DEBUG_CALL; SLB_DEBUG_CLEAN_STACK(L,-2); // 1 - obj (table with classInfo) ClassInfo *ci = Manager::getInstance().getClass(L,1); if (ci == 0) luaL_error(L, "Invalid Class at #1"); // 2 - key (string) const int key = 2; // 3 - value (func) const int value = 3; if (lua_isstring(L,key) && lua_isfunction(L,value)) { // create a closure with the function to call lua_pushcclosure(L, HybridBase::call_lua_method, 1); // replaces [value] setMethod(L, ci); } else { luaL_error(L, "hybrid instances can only have new methods (functions) " "indexed by strings ( called with: class[ (%s) ] = (%s) )", lua_typename(L, lua_type(L,key)), lua_typename(L, lua_type(L,value)) ); } return 0; }
int HybridBase::object__index(lua_State *L) { SLB_DEBUG_CALL; SLB_DEBUG_CLEAN_STACK(L,+1); SLB_DEBUG(4, "HybridBase::object__index"); // 1 - obj (table with classInfo) HybridBase* obj = get<HybridBase*>(L,1); if (obj == 0) luaL_error(L, "Invalid instance at #1"); if (!obj->_L) luaL_error(L, "Hybrid instance not attached or invalid method"); if (obj->_L != L) luaL_error(L, "Can not use that object outside its lua_state(%p)", obj->_L); // 2 - key (string) (at top) const char *key = lua_tostring(L,2); // call getMethod of hybrid (basic) if(!obj->getMethod(key)) luaL_error(L, "Invalid method %s", key); assert("Invalid stored function" && (lua_type(L,-1) == LUA_TFUNCTION) ); return 1; }
void Table::getCache(lua_State *L) { SLB_DEBUG_CALL; SLB_DEBUG_CLEAN_STACK(L,0); SLB_DEBUG_STACK(8, L, "Table(%p) :: getCache BEGIN ", this); int top = lua_gettop(L); if (top < 1 ) luaL_error(L, "Not enough elements to perform Table::getCache"); push(L); // [+1] push ::Table if (!luaL_getmetafield(L,-1, "__indexCache")) // [+1] { luaL_error(L, "Invalid setCache; %s:%d", __FILE__, __LINE__ ); } lua_pushvalue(L, top); // [+1] copy value lua_rawget(L, -2); // [-2] SLB_DEBUG_STACK(8, L, "Table(%p) :: getCache END (result is at top) top was = %d", this, top); lua_replace(L, top); // [-1,+1] as result lua_settop(L, top); }
int __newindex(lua_State *L) { SLB_DEBUG_CALL; SLB_DEBUG_CLEAN_STACK(L,-2); SLB_DEBUG_STACK(6,L, "Call InternalHybridSubclass(%p)::__nexindex", this); //1 = table //2 = string //3 = function luaL_checkstring(L,2); if (lua_type(L,3) != LUA_TFUNCTION) { luaL_error(L, "Only functions can be added to hybrid classes" " (invalid %s of type %s)", lua_tostring(L,2), lua_typename(L, 3)); } SLB_DEBUG(4, "Added method to an hybrid-subclass:%s", lua_tostring(L,2)); lua_pushcclosure(L, HybridBase::call_lua_method, 1); // replaces setCache(L); return 0; }
int __call(lua_State *L) { SLB_DEBUG_CALL; SLB_DEBUG_STACK(6,L, "Call InternalHybridSubclass(%p)::__call", this); // create new instance: ref_ptr<FuncCall> fc = _CI->getConstructor(); assert("Invalid Constructor!" && fc.valid()); fc->push(L); lua_replace(L,1); // table of metamethod __call lua_call(L, lua_gettop(L) -1 , LUA_MULTRET); { SLB_DEBUG_CLEAN_STACK(L,0); // at 1 we should have an HybridBase instance... HybridBase *obj = SLB::get<HybridBase*>(L,1); if (obj == 0) luaL_error(L, "Output(1) of constructor should be an HybridBase instance"); // now our table... to find methods obj->_subclassMethods = this; } return lua_gettop(L); }
void Table::setCache(lua_State *L) { SLB_DEBUG_CALL; SLB_DEBUG_CLEAN_STACK(L,-2); SLB_DEBUG_STACK(8, L, "Table(%p) :: setCache BEGIN ", this); int top = lua_gettop(L); if (top < 2 ) luaL_error(L, "Not enough elements to perform Table::setCache"); push(L); // push ::Table if (luaL_getmetafield(L,-1, "__indexCache")) { lua_insert(L, top - 1); // move the metatable above key,value lua_settop(L, top + 1); // remove everything else lua_rawset(L,-3); } else { luaL_error(L, "Invalid setCache; %s:%d", __FILE__, __LINE__ ); } SLB_DEBUG_STACK(8, L, "Table(%p) :: setCache END original top = %d", this, top); lua_settop(L, top - 2); }
void HybridBase::attach(lua_State *L) { SLB_DEBUG_CALL; //TODO allow reattaching... if (_L) throw std::runtime_error("Trying to reattach an Hybrid instance"); if (L) { SLB_DEBUG_CLEAN_STACK(L,0); _L = L; lua_newtable(_L); // [+1] //TODO this can be improved a little bit... by storing this metatable //somewhere.... lua_newtable(_L); // [+1] metatable lua_pushvalue(_L, LUA_GLOBALSINDEX); // [+1] globals _G lua_setfield(_L, -2, "__index"); // [-1] metatable.__index = _G lua_setmetatable(L,-2); // [-1] // done _global_environment = luaL_ref(_L, LUA_REGISTRYINDEX); // [-1] } }