diff --git a/src/common/runtime.cpp b/src/common/runtime.cpp index 7b784aafb..837d77b9d 100644 --- a/src/common/runtime.cpp +++ b/src/common/runtime.cpp @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include namespace love @@ -84,6 +86,38 @@ static int w__eq(lua_State *L) return 1; } +// For use with the love object pointer -> Proxy pointer registry. +// Using the pointer directly via lightuserdata would be ideal, but LuaJIT +// cannot use lightuserdata with more than 47 bits whereas some newer arm64 +// architectures allow pointers which use more than that. +static lua_Number luax_computeloveobjectkey(lua_State *L, love::Object *object) +{ + // love objects should be allocated on the heap, and thus are subject + // to the alignment rules of operator new / malloc. Lua numbers (doubles) + // can store all possible integers up to 2^53. We can store pointers that + // use more than 53 bits if their alignment is guaranteed to be more than 1. + // For example an alignment requirement of 8 means we can shift the + // pointer's bits by 3. + const size_t minalign = alignof(std::max_align_t); + uintptr_t key = (uintptr_t) object; + + if ((key & (minalign - 1)) != 0) + { + luaL_error(L, "Cannot push love object to Lua: unexpected alignment " + "(pointer is %p but alignment should be %d)", object, minalign); + } + + static const size_t shift = (size_t) log2(alignof(std::max_align_t)); + + key >>= shift; + + // Make sure our key isn't larger than 2^53. + if (key > 0x20000000000000ULL) + luaL_error(L, "Cannot push love object to Lua: pointer value %p is too large", object); + + return (lua_Number) key; +} + static int w__release(lua_State *L) { Proxy *p = (Proxy *) lua_touserdata(L, 1); @@ -100,7 +134,8 @@ static int w__release(lua_State *L) if (lua_istable(L, -1)) { // loveobjects[object] = nil - lua_pushlightuserdata(L, object); + lua_Number objectkey = luax_computeloveobjectkey(L, object); + lua_pushnumber(L, objectkey); lua_pushnil(L); lua_settable(L, -3); } @@ -557,14 +592,16 @@ void luax_pushtype(lua_State *L, love::Type &type, love::Object *object) luax_getregistry(L, REGISTRY_OBJECTS); // The table might not exist - it should be insisted in luax_register_type. - if (!lua_istable(L, -1)) + if (lua_isnoneornil(L, -1)) { lua_pop(L, 1); return luax_rawnewtype(L, type, object); } + lua_Number objectkey = luax_computeloveobjectkey(L, object); + // Get the value of loveobjects[object] on the stack. - lua_pushlightuserdata(L, object); + lua_pushnumber(L, objectkey); lua_gettable(L, -2); // If the Proxy userdata isn't in the instantiated types table yet, add it. @@ -574,7 +611,7 @@ void luax_pushtype(lua_State *L, love::Type &type, love::Object *object) luax_rawnewtype(L, type, object); - lua_pushlightuserdata(L, object); + lua_pushnumber(L, objectkey); lua_pushvalue(L, -2); // loveobjects[object] = Proxy.