Optimizations of instantiation (no longer creates table until

necessary).
This commit is contained in:
Bruce Hill 2018-02-11 17:22:00 -08:00
parent fa456525df
commit 04c20ab009
2 changed files with 43 additions and 58 deletions

View File

@ -38,60 +38,43 @@ static int SHARED_CLASS_METATABLE;
static int Lcreate_instance(lua_State *L) static int Lcreate_instance(lua_State *L)
{ {
int n_args = lua_gettop(L);
// arg 1: class table, ... // arg 1: class table, ...
lua_getfield(L, 1, "__fields"); lua_getfield(L, 1, "__fields");
// Stack: [__fields] size_t n = lua_objlen(L, -1);
size_t n = lua_objlen(L,-1); lua_pop(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); // Compute the hash:
// Stack [inst]
// Copy in all the values, and simultaneously compute the hash:
lua_Integer hash = (lua_Integer)lua_topointer(L, 1); // Hash depends on the metatable used in creation 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++) { for (lua_Integer i=1; i <=(lua_Integer)n; i++) {
lua_pushvalue(L, i+1);
// Stack [inst, args[i+1]]
lua_Integer item_hash; lua_Integer item_hash;
switch (lua_type(L, -1)) { switch (lua_type(L, 1+i)) {
case LUA_TNIL: case LUA_TNIL: case LUA_TNONE:
// Arbitrarily chosen value // Arbitrarily chosen value
item_hash = 0x97167da9; item_hash = 0x97167da9;
break; break;
case LUA_TNUMBER: case LUA_TNUMBER:
{ {
// Cast float bits to integer // 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); item_hash = *((lua_Integer*)&n);
break; break;
} }
case LUA_TBOOLEAN: case LUA_TBOOLEAN:
// Arbitrarily chosen values // Arbitrarily chosen values
item_hash = lua_toboolean(L, -1)? 0x82684f71 : 0x88d66f2a; item_hash = lua_toboolean(L, 1+i)? 0x82684f71 : 0x88d66f2a;
break; break;
case LUA_TTABLE: case LUA_TTABLE:
case LUA_TFUNCTION: case LUA_TFUNCTION:
case LUA_TUSERDATA: case LUA_TUSERDATA:
case LUA_TTHREAD: case LUA_TTHREAD:
case LUA_TLIGHTUSERDATA: case LUA_TLIGHTUSERDATA:
item_hash = (lua_Integer)lua_topointer(L, -1); item_hash = (lua_Integer)lua_topointer(L, 1+i);
break; break;
case LUA_TSTRING: case LUA_TSTRING:
{ {
// Algorithm taken from Lua 5.3's implementation // Algorithm taken from Lua 5.3's implementation
size_t len; 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 item_hash = len ^ 0xd2e9e9ac; // Arbitrary seed
size_t step = (len >> LUAI_HASHLIMIT) + 1; size_t step = (len >> LUAI_HASHLIMIT) + 1;
for (; len >= step; len -= step) for (; len >= step; len -= step)
@ -102,81 +85,83 @@ static int Lcreate_instance(lua_State *L)
item_hash = 0; item_hash = 0;
} }
hash = (1000003 * hash) ^ item_hash; hash = (1000003 * hash) ^ item_hash;
lua_rawseti(L, -2, i);
// Stack [inst]
} }
lua_getfield(L, 1, "__instances"); lua_getfield(L, 1, "__instances");
// Stack: [inst, buckets] // Stack: [buckets]
// Find bucket // Find bucket
lua_rawgeti(L, -1, hash); lua_rawgeti(L, -1, hash);
// Stack: [inst, buckets, bucket] // Stack: [buckets, bucket]
if (lua_isnil(L, -1)) { if (lua_isnil(L, -1)) {
// Make a new bucket // Make a new bucket
// Stack: [inst, buckets, nil] // Stack: [buckets, nil]
lua_pop(L, 1); lua_pop(L, 1);
// Stack: [inst, buckets] // Stack: [buckets]
lua_createtable(L, 1, 0); lua_createtable(L, 1, 0);
// Stack: [inst, buckets, bucket] // Stack: [buckets, bucket]
lua_pushlightuserdata(L, (void*)&SHARED_BUCKET_METATABLE); lua_pushlightuserdata(L, (void*)&SHARED_BUCKET_METATABLE);
lua_gettable(L, LUA_REGISTRYINDEX); lua_gettable(L, LUA_REGISTRYINDEX);
// Stack: [inst, buckets, bucket, {'__mode'='k'}] // Stack: [buckets, bucket, {'__mode'='k'}]
lua_setmetatable(L, -2); lua_setmetatable(L, -2);
// Stack: [inst, buckets, bucket] // Stack: [buckets, bucket]
lua_pushvalue(L, -1); lua_pushvalue(L, -1);
// Stack: [inst, buckets, bucket, bucket] // Stack: [buckets, bucket, bucket]
lua_rawseti(L, -3, hash); lua_rawseti(L, -3, hash);
// Stack: [inst, buckets, bucket] // Stack: [buckets, bucket]
} }
// Stack: [inst, buckets, bucket] // Stack: [buckets, bucket]
// scan bucket // scan bucket
lua_pushnil(L); lua_pushnil(L);
while (lua_next(L, -2) != 0) { // for hash_collider_inst, hash_collider in pairs(bucket) do 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; int bucket_item_matches = 1;
// Perform a full equality check // Perform a full equality check
lua_pushnil(L); lua_pushnil(L);
while (lua_next(L, -2) != 0) { // for i, collider_value in pairs(hash_collider) do 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 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; 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); 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 break; // go to next item in the bucket
} else { } 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); 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) { 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); lua_pop(L, 1);
// Found matching singleton // Found matching singleton
return 1; return 1;
} }
} }
// failed to find a singleton // Failed to find an existing instance, so create a new one
// Stack: [inst, buckets, bucket] // Stack: [buckets, bucket]
lua_Integer* userdata = (lua_Integer*)lua_newuserdata(L, sizeof(lua_Integer)); lua_Integer* userdata = (lua_Integer*)lua_newuserdata(L, sizeof(lua_Integer));
*userdata = hash; *userdata = hash;
// Stack [inst, buckets, bucket, inst_userdata] // Stack [buckets, bucket, inst_userdata]
lua_pushvalue(L, 1); lua_pushvalue(L, 1);
// Stack [inst, buckets, bucket, inst_userdata, metatable] // Stack [buckets, bucket, inst_userdata, metatable]
lua_setmetatable(L, -2); lua_setmetatable(L, -2);
// Stack [inst, buckets, bucket, inst_userdata] // Stack [buckets, bucket, inst_userdata]
lua_pushvalue(L, -1); lua_pushvalue(L, -1);
// Stack [inst, buckets, bucket, inst_userdata, inst_userdata] // Stack [buckets, bucket, inst_userdata, inst_userdata]
lua_pushvalue(L, -5); lua_createtable(L, n, 0); // Create the table to store the instance's data
// Stack [inst, buckets, bucket, inst_userdata, inst_userdata, inst] // Stack [buckets, bucket, inst_userdata, inst_userdata, inst_table]
lua_settable(L, -4); // buckets[inst_userdata] = inst for (lua_Integer i=1; i <=(lua_Integer)n; i++) {
// Stack [inst, buckets, bucket, inst_userdata] 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; return 1;
} }

View File

@ -177,7 +177,7 @@ test("Testing spoofing", function()
local t = {99,100} local t = {99,100}
setmetatable(t, Vec) setmetatable(t, Vec)
assert(not pcall(function() return t.x end)) 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)) assert(not pcall(function() return Vec.__tostring(t) end))
end) end)