/* * void= mt.__gc( proxy_ud ) * * End of life for a proxy object; reduce the deep reference count and clean * it up if reaches 0. */ static int deep_userdata_gc( lua_State *L ) { DEEP_PRELUDE **proxy= (DEEP_PRELUDE**)lua_touserdata( L, 1 ); DEEP_PRELUDE *p= *proxy; int v; *proxy= 0; // make sure we don't use it any more MUTEX_LOCK( &deep_lock ); v= --(p->refcount); MUTEX_UNLOCK( &deep_lock ); if (v==0) { int pushed; // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup // lua_CFunction idfunc= get_idfunc(L,1); ASSERT_L(idfunc); lua_settop(L,0); // clean stack so we can call 'idfunc' directly // void= idfunc( "delete", lightuserdata ) // lua_pushliteral( L, "delete" ); lua_pushlightuserdata( L, p->deep ); pushed= idfunc(L); if (pushed) luaL_error( L, "Bad idfunc on \"delete\": returned something" ); DEEP_FREE( (void*)p ); } return 0; }
/* * Push a registry subtable (keyed by unique 'token') onto the stack. * If the subtable does not exist, it is created and chained. */ static void push_registry_subtable( lua_State *L, void *token ) { STACK_GROW(L,3); STACK_CHECK(L) lua_pushlightuserdata( L, token ); lua_rawget( L, LUA_REGISTRYINDEX ); // // [-1]: nil/subtable if (lua_isnil(L,-1)) { lua_pop(L,1); lua_newtable(L); // value lua_pushlightuserdata( L, token ); // key lua_pushvalue(L,-2); // // [-3]: value (2nd ref) // [-2]: key // [-1]: value lua_rawset( L, LUA_REGISTRYINDEX ); } STACK_END(L,1) ASSERT_L( lua_istable(L,-1) ); }
/* * Does what the original 'push_registry_subtable' function did, but adds an optional mode argument to it */ void push_registry_subtable_mode( lua_State* L, void* key_, const char* mode_) { STACK_GROW( L, 3); STACK_CHECK( L); lua_pushlightuserdata( L, key_); // key lua_rawget( L, LUA_REGISTRYINDEX); // {}|nil if( lua_isnil( L, -1)) { lua_pop( L, 1); // lua_newtable( L); // {} lua_pushlightuserdata( L, key_); // {} key lua_pushvalue( L, -2); // {} key {} // _R[key_] = {} lua_rawset( L, LUA_REGISTRYINDEX); // {} // Set its metatable if requested if( mode_) { lua_newtable( L); // {} mt lua_pushliteral( L, "__mode"); // {} mt "__mode" lua_pushstring( L, mode_); // {} mt "__mode" mode lua_rawset( L, -3); // {} mt lua_setmetatable( L, -2); // {} } } STACK_END( L, 1); ASSERT_L( lua_istable( L, -1)); }
void free_deep_prelude( lua_State* L, DEEP_PRELUDE* prelude_) { // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup lua_pushlightuserdata( L, prelude_->deep); ASSERT_L( prelude_->idfunc); prelude_->idfunc( L, eDO_delete); DEEP_FREE( (void*) prelude_); }
/* * Create a deep userdata * * proxy_ud= deep_userdata( idfunc [, ...] ) * * Creates a deep userdata entry of the type defined by 'idfunc'. * Other parameters are passed on to the 'idfunc' "new" invocation. * * 'idfunc' must fulfill the following features: * * lightuserdata= idfunc( "new" [, ...] ) -- creates a new deep data instance * void= idfunc( "delete", lightuserdata ) -- releases a deep data instance * tbl= idfunc( "metatable" ) -- gives metatable for userdata proxies * * Reference counting and true userdata proxying are taken care of for the * actual data type. * * Types using the deep userdata system (and only those!) can be passed between * separate Lua states via 'luaG_inter_move()'. * * Returns: 'proxy' userdata for accessing the deep data via 'luaG_todeep()' */ int luaG_deep_userdata( lua_State *L ) { lua_CFunction idfunc= lua_tocfunction( L,1 ); int pushed; DEEP_PRELUDE *prelude= DEEP_MALLOC( sizeof(DEEP_PRELUDE) ); ASSERT_L(prelude); prelude->refcount= 0; // 'luaG_push_proxy' will lift it to 1 STACK_GROW(L,1); STACK_CHECK(L) // Replace 'idfunc' with "new" in the stack (keep possible other params) // lua_remove(L,1); lua_pushliteral( L, "new" ); lua_insert(L,1); // lightuserdata= idfunc( "new" [, ...] ) // pushed= idfunc(L); if ((pushed!=1) || lua_type(L,-1) != LUA_TLIGHTUSERDATA) luaL_error( L, "Bad idfunc on \"new\": did not return light userdata" ); prelude->deep= lua_touserdata(L,-1); ASSERT_L(prelude->deep); lua_pop(L,1); // pop deep data luaG_push_proxy( L, idfunc, prelude ); // // [-1]: proxy userdata STACK_END(L,1) return 1; }
/** * Free a memory block. * * @param pHeapInt The heap. * @param pBlock The memory block to free. */ static void rtHeapOffsetFreeBlock(PRTHEAPOFFSETINTERNAL pHeapInt, PRTHEAPOFFSETBLOCK pBlock) { PRTHEAPOFFSETFREE pFree = (PRTHEAPOFFSETFREE)pBlock; PRTHEAPOFFSETFREE pLeft; PRTHEAPOFFSETFREE pRight; #ifdef RTHEAPOFFSET_STRICT rtHeapOffsetAssertAll(pHeapInt); #endif /* * Look for the closest free list blocks by walking the blocks right * of us (both lists are sorted by address). */ pLeft = NULL; pRight = NULL; if (pHeapInt->offFreeTail) { pRight = RTHEAPOFF_TO_PTR_N(pHeapInt, pFree->Core.offNext, PRTHEAPOFFSETFREE); while (pRight && !RTHEAPOFFSETBLOCK_IS_FREE(&pRight->Core)) { ASSERT_BLOCK(pHeapInt, &pRight->Core); pRight = RTHEAPOFF_TO_PTR_N(pHeapInt, pRight->Core.offNext, PRTHEAPOFFSETFREE); } if (!pRight) pLeft = RTHEAPOFF_TO_PTR_N(pHeapInt, pHeapInt->offFreeTail, PRTHEAPOFFSETFREE); else { ASSERT_BLOCK_FREE(pHeapInt, pRight); pLeft = RTHEAPOFF_TO_PTR_N(pHeapInt, pRight->offPrev, PRTHEAPOFFSETFREE); } if (pLeft) ASSERT_BLOCK_FREE(pHeapInt, pLeft); } AssertMsgReturnVoid(pLeft != pFree, ("Freed twice! pv=%p (pBlock=%p)\n", pBlock + 1, pBlock)); ASSERT_L(RTHEAPOFF_TO_OFF(pHeapInt, pLeft), RTHEAPOFF_TO_OFF(pHeapInt, pFree)); Assert(!pRight || (uintptr_t)pRight > (uintptr_t)pFree); Assert(!pLeft || RTHEAPOFF_TO_PTR_N(pHeapInt, pLeft->offNext, PRTHEAPOFFSETFREE) == pRight); /* * Insert at the head of the free block list? */ if (!pLeft) { Assert(pRight == RTHEAPOFF_TO_PTR_N(pHeapInt, pHeapInt->offFreeHead, PRTHEAPOFFSETFREE)); pFree->Core.fFlags |= RTHEAPOFFSETBLOCK_FLAGS_FREE; pFree->offPrev = 0; pFree->offNext = RTHEAPOFF_TO_OFF(pHeapInt, pRight); if (pRight) pRight->offPrev = RTHEAPOFF_TO_OFF(pHeapInt, pFree); else pHeapInt->offFreeTail = RTHEAPOFF_TO_OFF(pHeapInt, pFree); pHeapInt->offFreeHead = RTHEAPOFF_TO_OFF(pHeapInt, pFree); } else { /* * Can we merge with left hand free block? */ if (pLeft->Core.offNext == RTHEAPOFF_TO_OFF(pHeapInt, pFree)) { pLeft->Core.offNext = pFree->Core.offNext; if (pFree->Core.offNext) RTHEAPOFF_TO_PTR(pHeapInt, pFree->Core.offNext, PRTHEAPOFFSETBLOCK)->offPrev = RTHEAPOFF_TO_OFF(pHeapInt, pLeft); pHeapInt->cbFree -= pLeft->cb; pFree = pLeft; } /* * No, just link it into the free list then. */ else { pFree->Core.fFlags |= RTHEAPOFFSETBLOCK_FLAGS_FREE; pFree->offNext = RTHEAPOFF_TO_OFF(pHeapInt, pRight); pFree->offPrev = RTHEAPOFF_TO_OFF(pHeapInt, pLeft); pLeft->offNext = RTHEAPOFF_TO_OFF(pHeapInt, pFree); if (pRight) pRight->offPrev = RTHEAPOFF_TO_OFF(pHeapInt, pFree); else pHeapInt->offFreeTail = RTHEAPOFF_TO_OFF(pHeapInt, pFree); } } /* * Can we merge with right hand free block? */ if ( pRight && pRight->Core.offPrev == RTHEAPOFF_TO_OFF(pHeapInt, pFree)) { /* core */ pFree->Core.offNext = pRight->Core.offNext; if (pRight->Core.offNext) RTHEAPOFF_TO_PTR(pHeapInt, pRight->Core.offNext, PRTHEAPOFFSETBLOCK)->offPrev = RTHEAPOFF_TO_OFF(pHeapInt, pFree); /* free */ pFree->offNext = pRight->offNext; if (pRight->offNext) RTHEAPOFF_TO_PTR(pHeapInt, pRight->offNext, PRTHEAPOFFSETFREE)->offPrev = RTHEAPOFF_TO_OFF(pHeapInt, pFree); else pHeapInt->offFreeTail = RTHEAPOFF_TO_OFF(pHeapInt, pFree); pHeapInt->cbFree -= pRight->cb; } /* * Calculate the size and update free stats. */ pFree->cb = (pFree->Core.offNext ? pFree->Core.offNext : pHeapInt->cbHeap) - RTHEAPOFF_TO_OFF(pHeapInt, pFree) - sizeof(RTHEAPOFFSETBLOCK); pHeapInt->cbFree += pFree->cb; ASSERT_BLOCK_FREE(pHeapInt, pFree); #ifdef RTHEAPOFFSET_STRICT rtHeapOffsetAssertAll(pHeapInt); #endif }
static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) { lua_CFunction cfunc= lua_tocfunction( L,i ); unsigned n; ASSERT_L( L2_cache_i != 0 ); STACK_GROW(L,2); STACK_CHECK(L) if (!cfunc) { // Lua function luaL_Buffer b; const char *s; size_t sz; int tmp; const char *name= NULL; #if 0 // "To get information about a function you push it onto the // stack and start the what string with the character '>'." // { lua_Debug ar; lua_pushvalue( L, i ); lua_getinfo(L, ">n", &ar); // fills 'name' and 'namewhat', pops function name= ar.namewhat; fprintf( stderr, "NAME: %s\n", name ); // just gives NULL } #endif // 'lua_dump()' needs the function at top of stack // if (i!=-1) lua_pushvalue( L, i ); luaL_buffinit(L,&b); tmp= lua_dump(L, buf_writer, &b); ASSERT_L(tmp==0); // // "value returned is the error code returned by the last call // to the writer" (and we only return 0) luaL_pushresult(&b); // pushes dumped string on 'L' s= lua_tolstring(L,-1,&sz); ASSERT_L( s && sz ); if (i!=-1) lua_remove( L, -2 ); // Note: Line numbers seem to be taken precisely from the // original function. 'name' is not used since the chunk // is precompiled (it seems...). // // TBD: Can we get the function's original name through, as well? // if (luaL_loadbuffer(L2, s, sz, name) != 0) { // chunk is precompiled so only LUA_ERRMEM can happen // "Otherwise, it pushes an error message" // STACK_GROW( L,1 ); luaL_error( L, "%s", lua_tostring(L2,-1) ); } lua_pop(L,1); // remove the dumped string STACK_MID(L,0) }
static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) { // TBD: Merge this and same code for tables ASSERT_L( hijacked_tostring ); ASSERT_L( L2_cache_i != 0 ); STACK_GROW(L,2); STACK_GROW(L2,3); STACK_CHECK(L) lua_pushcfunction( L, hijacked_tostring ); lua_pushvalue( L, i ); lua_call( L, 1 /*args*/, 1 /*retvals*/ ); // // [-1]: "function: 0x...." STACK_END(L,1) ASSERT_L( lua_type(L,-1) == LUA_TSTRING ); // L2_cache[id_str]= function // STACK_CHECK(L2) // We don't need to use the from state ('L') in ID since the life span // is only for the duration of a copy (both states are locked). // lua_pushstring( L2, lua_tostring(L,-1) ); lua_pop(L,1); // remove the 'tostring(tbl)' value (in L!) //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) ); lua_pushvalue( L2, -1 ); lua_rawget( L2, L2_cache_i ); // // [-2]: identity string ("function: 0x...") // [-1]: function|nil|true (true means: we're working on it; recursive) if (lua_isnil(L2,-1)) { lua_pop(L2,1); // Set to 'true' for the duration of creation; need to find self-references // via upvalues // lua_pushboolean(L2,TRUE); lua_setfield( L2, L2_cache_i, lua_tostring(L2,-2) ); inter_copy_func( L2, L2_cache_i, L, i ); // pushes a copy of the func lua_pushvalue(L2,-1); lua_insert(L2,-3); // // [-3]: function (2nd ref) // [-2]: identity string // [-1]: function lua_rawset(L2,L2_cache_i); // // [-1]: function (tied to 'L2_cache' table') } else if (lua_isboolean(L2,-1)) { // Loop in preparing upvalues; either direct or via a table // // Note: This excludes the case where a function directly addresses // itself as an upvalue (recursive lane creation). // luaL_error( L, "Recursive use of upvalues; cannot copy the function" ); } else { lua_remove(L2,-2); } STACK_END(L2,1) // // L2 [-1]: function ASSERT_L( lua_isfunction(L2,-1) ); }
/* * Check if we've already copied the same table from 'L', and * reuse the old copy. This allows table upvalues shared by multiple * local functions to point to the same table, also in the target. * * Always pushes a table to 'L2'. * * Returns TRUE if the table was cached (no need to fill it!); FALSE if * it's a virgin. */ static bool_t push_cached_table( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) { bool_t ret; ASSERT_L( hijacked_tostring ); ASSERT_L( L2_cache_i != 0 ); STACK_GROW(L,2); STACK_GROW(L2,3); // Create an identity string for table at [i]; it should stay unique at // least during copying of the data (then we can clear the caches). // STACK_CHECK(L) lua_pushcfunction( L, hijacked_tostring ); lua_pushvalue( L, i ); lua_call( L, 1 /*args*/, 1 /*retvals*/ ); // // [-1]: "table: 0x...." STACK_END(L,1) ASSERT_L( lua_type(L,-1) == LUA_TSTRING ); // L2_cache[id_str]= [{...}] // STACK_CHECK(L2) // We don't need to use the from state ('L') in ID since the life span // is only for the duration of a copy (both states are locked). // lua_pushstring( L2, lua_tostring(L,-1) ); lua_pop(L,1); // remove the 'tostring(tbl)' value (in L!) //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) ); lua_pushvalue( L2, -1 ); lua_rawget( L2, L2_cache_i ); // // [-2]: identity string ("table: 0x...") // [-1]: table|nil if (lua_isnil(L2,-1)) { lua_pop(L2,1); lua_newtable(L2); lua_pushvalue(L2,-1); lua_insert(L2,-3); // // [-3]: new table (2nd ref) // [-2]: identity string // [-1]: new table lua_rawset(L2, L2_cache_i); // // [-1]: new table (tied to 'L2_cache' table') ret= FALSE; // brand new } else { lua_remove(L2,-2); ret= TRUE; // from cache } STACK_END(L2,1) // // L2 [-1]: table to use as destination ASSERT_L( lua_istable(L2,-1) ); return ret; }
/* * Push a proxy userdata on the stack. * * Initializes necessary structures if it's the first time 'idfunc' is being * used in this Lua state (metatable, registring it). Otherwise, increments the * reference count. */ void luaG_push_proxy( lua_State *L, lua_CFunction idfunc, DEEP_PRELUDE *prelude ) { DEEP_PRELUDE **proxy; MUTEX_LOCK( &deep_lock ); ++(prelude->refcount); // one more proxy pointing to this deep data MUTEX_UNLOCK( &deep_lock ); STACK_GROW(L,4); STACK_CHECK(L) proxy= lua_newuserdata( L, sizeof( DEEP_PRELUDE* ) ); ASSERT_L(proxy); *proxy= prelude; // Get/create metatable for 'idfunc' (in this state) // lua_pushcfunction( L, idfunc ); // key get_deep_lookup(L); // // [-2]: proxy // [-1]: metatable / nil if (lua_isnil(L,-1)) { // No metatable yet; make one and register it // lua_pop(L,1); // tbl= idfunc( "metatable" ) // lua_pushcfunction( L, idfunc ); lua_pushliteral( L, "metatable" ); lua_call( L, 1 /*args*/, 1 /*results*/ ); // // [-2]: proxy // [-1]: metatable (returned by 'idfunc') if (!lua_istable(L,-1)) luaL_error( L, "Bad idfunc on \"metatable\": did not return one" ); // Add '__gc' method // lua_pushcfunction( L, deep_userdata_gc ); lua_setfield( L, -2, "__gc" ); // Memorize for later rounds // lua_pushvalue( L,-1 ); lua_pushcfunction( L, idfunc ); // // [-4]: proxy // [-3]: metatable (2nd ref) // [-2]: metatable // [-1]: idfunc set_deep_lookup(L); } STACK_MID(L,2) ASSERT_L( lua_isuserdata(L,-2) ); ASSERT_L( lua_istable(L,-1) ); // [-2]: proxy userdata // [-1]: metatable to use lua_setmetatable( L, -2 ); STACK_END(L,1) // [-1]: proxy userdata }
/** * Free a memory block. * * @param pHeapInt The heap. * @param pBlock The memory block to free. */ static void rtHeapSimpleFreeBlock(PRTHEAPSIMPLEINTERNAL pHeapInt, PRTHEAPSIMPLEBLOCK pBlock) { PRTHEAPSIMPLEFREE pFree = (PRTHEAPSIMPLEFREE)pBlock; PRTHEAPSIMPLEFREE pLeft; PRTHEAPSIMPLEFREE pRight; #ifdef RTHEAPSIMPLE_STRICT rtHeapSimpleAssertAll(pHeapInt); #endif /* * Look for the closest free list blocks by walking the blocks right * of us (both lists are sorted by address). */ pLeft = NULL; pRight = NULL; if (pHeapInt->pFreeTail) { pRight = (PRTHEAPSIMPLEFREE)pFree->Core.pNext; while (pRight && !RTHEAPSIMPLEBLOCK_IS_FREE(&pRight->Core)) { ASSERT_BLOCK(pHeapInt, &pRight->Core); pRight = (PRTHEAPSIMPLEFREE)pRight->Core.pNext; } if (!pRight) pLeft = pHeapInt->pFreeTail; else { ASSERT_BLOCK_FREE(pHeapInt, pRight); pLeft = pRight->pPrev; } if (pLeft) ASSERT_BLOCK_FREE(pHeapInt, pLeft); } AssertMsgReturnVoid(pLeft != pFree, ("Freed twice! pv=%p (pBlock=%p)\n", pBlock + 1, pBlock)); ASSERT_L(pLeft, pFree); Assert(!pRight || (uintptr_t)pRight > (uintptr_t)pFree); Assert(!pLeft || pLeft->pNext == pRight); /* * Insert at the head of the free block list? */ if (!pLeft) { Assert(pRight == pHeapInt->pFreeHead); pFree->Core.fFlags |= RTHEAPSIMPLEBLOCK_FLAGS_FREE; pFree->pPrev = NULL; pFree->pNext = pRight; if (pRight) pRight->pPrev = pFree; else pHeapInt->pFreeTail = pFree; pHeapInt->pFreeHead = pFree; } else { /* * Can we merge with left hand free block? */ if (pLeft->Core.pNext == &pFree->Core) { pLeft->Core.pNext = pFree->Core.pNext; if (pFree->Core.pNext) pFree->Core.pNext->pPrev = &pLeft->Core; pHeapInt->cbFree -= pLeft->cb; pFree = pLeft; } /* * No, just link it into the free list then. */ else { pFree->Core.fFlags |= RTHEAPSIMPLEBLOCK_FLAGS_FREE; pFree->pNext = pRight; pFree->pPrev = pLeft; pLeft->pNext = pFree; if (pRight) pRight->pPrev = pFree; else pHeapInt->pFreeTail = pFree; } } /* * Can we merge with right hand free block? */ if ( pRight && pRight->Core.pPrev == &pFree->Core) { /* core */ pFree->Core.pNext = pRight->Core.pNext; if (pRight->Core.pNext) pRight->Core.pNext->pPrev = &pFree->Core; /* free */ pFree->pNext = pRight->pNext; if (pRight->pNext) pRight->pNext->pPrev = pFree; else pHeapInt->pFreeTail = pFree; pHeapInt->cbFree -= pRight->cb; } /* * Calculate the size and update free stats. */ pFree->cb = (pFree->Core.pNext ? (uintptr_t)pFree->Core.pNext : (uintptr_t)pHeapInt->pvEnd) - (uintptr_t)pFree - sizeof(RTHEAPSIMPLEBLOCK); pHeapInt->cbFree += pFree->cb; ASSERT_BLOCK_FREE(pHeapInt, pFree); #ifdef RTHEAPSIMPLE_STRICT rtHeapSimpleAssertAll(pHeapInt); #endif }
/* * Push a proxy userdata on the stack. * returns NULL if ok, else some error string related to bad idfunc behavior or module require problem * (error cannot happen with mode_ == eLM_ToKeeper) * * Initializes necessary structures if it's the first time 'idfunc' is being * used in this Lua state (metatable, registring it). Otherwise, increments the * reference count. */ char const* push_deep_proxy( struct s_Universe* U, lua_State* L, DEEP_PRELUDE* prelude, enum eLookupMode mode_) { DEEP_PRELUDE** proxy; // Check if a proxy already exists push_registry_subtable_mode( L, DEEP_PROXY_CACHE_KEY, "v"); // DPC lua_pushlightuserdata( L, prelude->deep); // DPC deep lua_rawget( L, -2); // DPC proxy if ( !lua_isnil( L, -1)) { lua_remove( L, -2); // proxy return NULL; } else { lua_pop( L, 1); // DPC } MUTEX_LOCK( &U->deep_lock); ++ (prelude->refcount); // one more proxy pointing to this deep data MUTEX_UNLOCK( &U->deep_lock); STACK_GROW( L, 7); STACK_CHECK( L); proxy = lua_newuserdata( L, sizeof( DEEP_PRELUDE*)); // DPC proxy ASSERT_L( proxy); *proxy = prelude; // Get/create metatable for 'idfunc' (in this state) lua_pushlightuserdata( L, prelude->idfunc); // DPC proxy idfunc get_deep_lookup( L); // DPC proxy metatable? if( lua_isnil( L, -1)) // // No metatable yet. { char const* modname; int oldtop = lua_gettop( L); // DPC proxy nil lua_pop( L, 1); // DPC proxy // 1 - make one and register it if( mode_ != eLM_ToKeeper) { prelude->idfunc( L, eDO_metatable); // DPC proxy metatable deepversion if( lua_gettop( L) - oldtop != 1 || !lua_istable( L, -2) || !lua_isstring( L, -1)) { lua_settop( L, oldtop); // DPC proxy X lua_pop( L, 3); // return "Bad idfunc(eOP_metatable): unexpected pushed value"; } luaG_pushdeepversion( L); // DPC proxy metatable deepversion deepversion if( !lua501_equal( L, -1, -2)) { lua_pop( L, 5); // return "Bad idfunc(eOP_metatable): mismatched deep version"; } lua_pop( L, 2); // DPC proxy metatable // make sure the idfunc didn't export __gc, as we will store our own lua_getfield( L, -1, "__gc"); // DPC proxy metatable __gc if( !lua_isnil( L, -1)) { lua_pop( L, 4); // return "idfunc-created metatable shouldn't contain __gc"; } lua_pop( L, 1); // DPC proxy metatable } else { // keepers need a minimal metatable that only contains __gc lua_newtable( L); // DPC proxy metatable } // Add our own '__gc' method lua_pushcfunction( L, deep_userdata_gc); // DPC proxy metatable __gc lua_setfield( L, -2, "__gc"); // DPC proxy metatable // Memorize for later rounds lua_pushvalue( L, -1); // DPC proxy metatable metatable lua_pushlightuserdata( L, prelude->idfunc); // DPC proxy metatable metatable idfunc set_deep_lookup( L); // DPC proxy metatable // 2 - cause the target state to require the module that exported the idfunc // this is needed because we must make sure the shared library is still loaded as long as we hold a pointer on the idfunc { int oldtop = lua_gettop( L); modname = (char const*) prelude->idfunc( L, eDO_module); // DPC proxy metatable // make sure the function pushed nothing on the stack! if( lua_gettop( L) - oldtop != 0) { lua_pop( L, 3); // return "Bad idfunc(eOP_module): should not push anything"; } } if( modname) // we actually got a module name { // somehow, L.registry._LOADED can exist without having registered the 'package' library. lua_getglobal( L, "require"); // DPC proxy metatable require() // check that the module is already loaded (or being loaded, we are happy either way) if( lua_isfunction( L, -1)) { lua_pushstring( L, modname); // DPC proxy metatable require() "module" lua_getfield( L, LUA_REGISTRYINDEX, "_LOADED"); // DPC proxy metatable require() "module" _R._LOADED if( lua_istable( L, -1)) { bool_t alreadyloaded; lua_pushvalue( L, -2); // DPC proxy metatable require() "module" _R._LOADED "module" lua_rawget( L, -2); // DPC proxy metatable require() "module" _R._LOADED module alreadyloaded = lua_toboolean( L, -1); if( !alreadyloaded) // not loaded { int require_result; lua_pop( L, 2); // DPC proxy metatable require() "module" // require "modname" require_result = lua_pcall( L, 1, 0, 0); // DPC proxy metatable error? if( require_result != LUA_OK) { // failed, return the error message lua_pushfstring( L, "error while requiring '%s' identified by idfunc(eOP_module): ", modname); lua_insert( L, -2); // DPC proxy metatable prefix error lua_concat( L, 2); // DPC proxy metatable error return lua_tostring( L, -1); } } else // already loaded, we are happy { lua_pop( L, 4); // DPC proxy metatable } } else // no L.registry._LOADED; can this ever happen? { lua_pop( L, 6); // return "unexpected error while requiring a module identified by idfunc(eOP_module)"; } } else // a module name, but no require() function :-( { lua_pop( L, 4); // return "lanes receiving deep userdata should register the 'package' library"; } } } STACK_MID( L, 2); // DPC proxy metatable ASSERT_L( lua_isuserdata( L, -2)); ASSERT_L( lua_istable( L, -1)); lua_setmetatable( L, -2); // DPC proxy // If we're here, we obviously had to create a new proxy, so cache it. lua_pushlightuserdata( L, (*proxy)->deep); // DPC proxy deep lua_pushvalue( L, -2); // DPC proxy deep proxy lua_rawset( L, -4); // DPC proxy lua_remove( L, -2); // proxy ASSERT_L( lua_isuserdata( L, -1)); STACK_END( L, 0); return NULL; }
// in: linda_ud [, key [, ...]] int keepercall_count( lua_State* L) { int top; push_table( L, 1); // ud keys fifos switch( lua_gettop( L)) { // no key is specified: return a table giving the count of all known keys case 2: // ud fifos lua_newtable( L); // ud fifos out lua_replace( L, 1); // out fifos lua_pushnil( L); // out fifos nil while( lua_next( L, 2)) // out fifos key fifo { keeper_fifo* fifo = prepare_fifo_access( L, -1); // out fifos key fifo lua_pop( L, 1); // out fifos key lua_pushvalue( L, -1); // out fifos key key lua_pushinteger( L, fifo->count); // out fifos key key count lua_rawset( L, -5); // out fifos key } lua_pop( L, 1); // out break; // 1 key is specified: return its count case 3: // ud key fifos { keeper_fifo* fifo; lua_replace( L, 1); // fifos key lua_rawget( L, -2); // fifos fifo|nil if( lua_isnil( L, -1)) // the key is unknown { // fifos nil lua_remove( L, -2); // nil } else // the key is known { // fifos fifo fifo = prepare_fifo_access( L, -1); // fifos fifo lua_pushinteger( L, fifo->count); // fifos fifo count lua_replace( L, -3); // count fifo lua_pop( L, 1); // count } } break; // a variable number of keys is specified: return a table of their counts default: // ud keys fifos lua_newtable( L); // ud keys fifos out lua_replace( L, 1); // out keys fifos // shifts all keys up in the stack. potentially slow if there are a lot of them, but then it should be bearable lua_insert( L, 2); // out fifos keys while( (top = lua_gettop( L)) > 2) { keeper_fifo* fifo; lua_pushvalue( L, -1); // out fifos keys key lua_rawget( L, 2); // out fifos keys fifo|nil fifo = prepare_fifo_access( L, -1); // out fifos keys fifo|nil lua_pop( L, 1); // out fifos keys if( fifo != NULL) // the key is known { lua_pushinteger( L, fifo->count); // out fifos keys count lua_rawset( L, 1); // out fifos keys } else // the key is unknown { lua_pop( L, 1); // out fifos keys } } lua_pop( L, 1); // out } ASSERT_L( lua_gettop( L) == 1); return 1; }