Optimizations of instantiation (no longer creates table until
necessary).
This commit is contained in:
parent
fa456525df
commit
04c20ab009
99
limmutable.c
99
limmutable.c
@ -38,60 +38,43 @@ static int SHARED_CLASS_METATABLE;
|
||||
|
||||
static int Lcreate_instance(lua_State *L)
|
||||
{
|
||||
int n_args = lua_gettop(L);
|
||||
// arg 1: class table, ...
|
||||
|
||||
lua_getfield(L, 1, "__fields");
|
||||
// Stack: [__fields]
|
||||
size_t n = lua_objlen(L,-1);
|
||||
if ((size_t)n_args-1 != n) {
|
||||
lua_pushstring(L, "incorrect number of arguments: expected ");
|
||||
lua_pushinteger(L, n);
|
||||
lua_pushstring(L, ", but got ");
|
||||
lua_pushinteger(L, n_args-1);
|
||||
lua_concat(L, 4);
|
||||
lua_error(L);
|
||||
}
|
||||
lua_pop(L,1);
|
||||
// Stack: []
|
||||
|
||||
lua_createtable(L, n, 0);
|
||||
// Stack [inst]
|
||||
size_t n = lua_objlen(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Copy in all the values, and simultaneously compute the hash:
|
||||
// Compute the hash:
|
||||
lua_Integer hash = (lua_Integer)lua_topointer(L, 1); // Hash depends on the metatable used in creation
|
||||
for (lua_Integer i=1; i <=(lua_Integer)n; i++) {
|
||||
lua_pushvalue(L, i+1);
|
||||
// Stack [inst, args[i+1]]
|
||||
lua_Integer item_hash;
|
||||
switch (lua_type(L, -1)) {
|
||||
case LUA_TNIL:
|
||||
switch (lua_type(L, 1+i)) {
|
||||
case LUA_TNIL: case LUA_TNONE:
|
||||
// Arbitrarily chosen value
|
||||
item_hash = 0x97167da9;
|
||||
break;
|
||||
case LUA_TNUMBER:
|
||||
{
|
||||
// Cast float bits to integer
|
||||
lua_Number n = lua_tonumber(L, -1);
|
||||
lua_Number n = lua_tonumber(L, 1+i);
|
||||
item_hash = *((lua_Integer*)&n);
|
||||
break;
|
||||
}
|
||||
case LUA_TBOOLEAN:
|
||||
// Arbitrarily chosen values
|
||||
item_hash = lua_toboolean(L, -1)? 0x82684f71 : 0x88d66f2a;
|
||||
item_hash = lua_toboolean(L, 1+i)? 0x82684f71 : 0x88d66f2a;
|
||||
break;
|
||||
case LUA_TTABLE:
|
||||
case LUA_TFUNCTION:
|
||||
case LUA_TUSERDATA:
|
||||
case LUA_TTHREAD:
|
||||
case LUA_TLIGHTUSERDATA:
|
||||
item_hash = (lua_Integer)lua_topointer(L, -1);
|
||||
item_hash = (lua_Integer)lua_topointer(L, 1+i);
|
||||
break;
|
||||
case LUA_TSTRING:
|
||||
{
|
||||
// Algorithm taken from Lua 5.3's implementation
|
||||
size_t len;
|
||||
const char *str = lua_tolstring(L, -1, &len);
|
||||
const char *str = lua_tolstring(L, 1+i, &len);
|
||||
item_hash = len ^ 0xd2e9e9ac; // Arbitrary seed
|
||||
size_t step = (len >> LUAI_HASHLIMIT) + 1;
|
||||
for (; len >= step; len -= step)
|
||||
@ -102,81 +85,83 @@ static int Lcreate_instance(lua_State *L)
|
||||
item_hash = 0;
|
||||
}
|
||||
hash = (1000003 * hash) ^ item_hash;
|
||||
lua_rawseti(L, -2, i);
|
||||
// Stack [inst]
|
||||
}
|
||||
|
||||
lua_getfield(L, 1, "__instances");
|
||||
|
||||
// Stack: [inst, buckets]
|
||||
// Stack: [buckets]
|
||||
// Find bucket
|
||||
lua_rawgeti(L, -1, hash);
|
||||
// Stack: [inst, buckets, bucket]
|
||||
// Stack: [buckets, bucket]
|
||||
if (lua_isnil(L, -1)) {
|
||||
// Make a new bucket
|
||||
// Stack: [inst, buckets, nil]
|
||||
// Stack: [buckets, nil]
|
||||
lua_pop(L, 1);
|
||||
// Stack: [inst, buckets]
|
||||
// Stack: [buckets]
|
||||
lua_createtable(L, 1, 0);
|
||||
// Stack: [inst, buckets, bucket]
|
||||
// Stack: [buckets, bucket]
|
||||
lua_pushlightuserdata(L, (void*)&SHARED_BUCKET_METATABLE);
|
||||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||||
// Stack: [inst, buckets, bucket, {'__mode'='k'}]
|
||||
// Stack: [buckets, bucket, {'__mode'='k'}]
|
||||
lua_setmetatable(L, -2);
|
||||
// Stack: [inst, buckets, bucket]
|
||||
// Stack: [buckets, bucket]
|
||||
lua_pushvalue(L, -1);
|
||||
// Stack: [inst, buckets, bucket, bucket]
|
||||
// Stack: [buckets, bucket, bucket]
|
||||
lua_rawseti(L, -3, hash);
|
||||
// Stack: [inst, buckets, bucket]
|
||||
// Stack: [buckets, bucket]
|
||||
}
|
||||
// Stack: [inst, buckets, bucket]
|
||||
// Stack: [buckets, bucket]
|
||||
// scan bucket
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2) != 0) { // for hash_collider_inst, hash_collider in pairs(bucket) do
|
||||
// Stack: [inst, buckets, bucket, hash_collider_inst, hash_collider]
|
||||
// Stack: [buckets, bucket, hash_collider_inst, hash_collider]
|
||||
int bucket_item_matches = 1;
|
||||
// Perform a full equality check
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2) != 0) { // for i, collider_value in pairs(hash_collider) do
|
||||
// Stack: [inst, buckets, bucket, hash_collider_inst, hash_collider, i, value]
|
||||
// Stack: [buckets, bucket, hash_collider_inst, hash_collider, i, value]
|
||||
if (!lua_rawequal(L, -1, 1+lua_tonumber(L, -2))) { // If the i'th entry doesn't match the i'th arg
|
||||
bucket_item_matches = 0;
|
||||
// Stack: [inst, buckets, bucket, hash_collider_inst, hash_collider, i, value]
|
||||
// Stack: [buckets, bucket, hash_collider_inst, hash_collider, i, value]
|
||||
lua_pop(L, 3);
|
||||
// Stack: [inst, buckets, bucket, hash_collider_inst]
|
||||
// Stack: [buckets, bucket, hash_collider_inst]
|
||||
break; // go to next item in the bucket
|
||||
} else {
|
||||
// Stack: [inst, buckets, bucket, hash_collider_inst, hash_collider, i, value]
|
||||
// Stack: [buckets, bucket, hash_collider_inst, hash_collider, i, value]
|
||||
lua_pop(L, 1);
|
||||
// Stack: [inst, buckets, bucket, hash_collider_inst, hash_collider, i]
|
||||
// Stack: [buckets, bucket, hash_collider_inst, hash_collider, i]
|
||||
}
|
||||
}
|
||||
if (bucket_item_matches) {
|
||||
// Stack: [inst, buckets, bucket, hash_collider_inst, hash_collider]
|
||||
// Stack: [buckets, bucket, hash_collider_inst, hash_collider]
|
||||
lua_pop(L, 1);
|
||||
// Found matching singleton
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// failed to find a singleton
|
||||
// Stack: [inst, buckets, bucket]
|
||||
|
||||
// Failed to find an existing instance, so create a new one
|
||||
// Stack: [buckets, bucket]
|
||||
lua_Integer* userdata = (lua_Integer*)lua_newuserdata(L, sizeof(lua_Integer));
|
||||
*userdata = hash;
|
||||
|
||||
// Stack [inst, buckets, bucket, inst_userdata]
|
||||
// Stack [buckets, bucket, inst_userdata]
|
||||
lua_pushvalue(L, 1);
|
||||
// Stack [inst, buckets, bucket, inst_userdata, metatable]
|
||||
// Stack [buckets, bucket, inst_userdata, metatable]
|
||||
lua_setmetatable(L, -2);
|
||||
// Stack [inst, buckets, bucket, inst_userdata]
|
||||
|
||||
// Stack [buckets, bucket, inst_userdata]
|
||||
lua_pushvalue(L, -1);
|
||||
// Stack [inst, buckets, bucket, inst_userdata, inst_userdata]
|
||||
lua_pushvalue(L, -5);
|
||||
// Stack [inst, buckets, bucket, inst_userdata, inst_userdata, inst]
|
||||
lua_settable(L, -4); // buckets[inst_userdata] = inst
|
||||
// Stack [inst, buckets, bucket, inst_userdata]
|
||||
// Stack [buckets, bucket, inst_userdata, inst_userdata]
|
||||
lua_createtable(L, n, 0); // Create the table to store the instance's data
|
||||
// Stack [buckets, bucket, inst_userdata, inst_userdata, inst_table]
|
||||
for (lua_Integer i=1; i <=(lua_Integer)n; i++) {
|
||||
lua_pushvalue(L, i+1);
|
||||
// Stack [buckets, bucket, inst_userdata, inst_userdata, inst_table, arg #1+i]
|
||||
lua_rawseti(L, -2, i);
|
||||
}
|
||||
// Stack [buckets, bucket, inst_userdata, inst_userdata, inst_table]
|
||||
lua_settable(L, -4); // buckets[inst_userdata] = inst_table
|
||||
// Stack [buckets, bucket, inst_userdata]
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -177,7 +177,7 @@ test("Testing spoofing", function()
|
||||
local t = {99,100}
|
||||
setmetatable(t, Vec)
|
||||
assert(not pcall(function() return t.x end))
|
||||
assert(not pcall(function() return Vec(88) end))
|
||||
assert(not pcall(function() return Vec.__index(88, 9) end))
|
||||
assert(not pcall(function() return Vec.__tostring(t) end))
|
||||
end)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user