From 4f491c2e6162ff880818b4725a0c59912ca2c6c7 Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Thu, 7 Jan 2021 11:49:11 +0800 Subject: [PATCH] Allow full 64-bit lightuserdata usage if the LuaJIT supports it. Issue love2d/love-android#200 Original patch was sent by slime in Discord. Modified a bit to add code that checks for 64-bit lightuserdata support. --- src/common/runtime.cpp | 64 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/src/common/runtime.cpp b/src/common/runtime.cpp index 72c1bbe94..3570890e2 100644 --- a/src/common/runtime.cpp +++ b/src/common/runtime.cpp @@ -93,11 +93,42 @@ static int w__eq(lua_State *L) return 1; } +typedef uint64 ObjectKey; + +static bool luax_isfulllightuserdatasupported(lua_State *L) +{ + // LuaJIT prior to commit e9af1abec542e6f9851ff2368e7f196b6382a44c doesn't + // support lightuserdata > 48 bits. This is not a problem with Android, + // Windows, macOS, and iOS as they'll use updated LuaJIT or won't use + // pointers > 48 bits, but this is not the case for Linux. So check for + // this capability first! + static bool checked = false; + static bool supported = false; + + if (!checked) + { + lua_pushcclosure(L, [](lua_State *L) -> int + { + // Try to push pointer with all bits set. + lua_pushlightuserdata(L, (void *) (~((size_t) 0))); + return 1; + }, 0); + + supported = lua_pcall(L, 0, 1, 0) == 0; + checked = true; + + lua_pop(L, 1); + } + + return supported; +} + // 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) +// (before a commit to 2.1 in 2020) cannot use lightuserdata with more than 47 +// bits whereas some newer arm64 architectures allow pointers which use more +// than that. +static ObjectKey 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) @@ -118,11 +149,20 @@ static lua_Number luax_computeloveobjectkey(lua_State *L, love::Object *object) 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 (ObjectKey) key; +} - return (lua_Number) key; +static void luax_pushloveobjectkey(lua_State *L, ObjectKey key) +{ + // If full 64-bit lightuserdata is supported, always use that. Otherwise, + // if the key is smaller than 2^53 (which is integer precision for double + // datatype), then push number. Otherwise, throw error. + if (luax_isfulllightuserdatasupported(L)) + lua_pushlightuserdata(L, (void *) key); + else if (key > 0x20000000000000ULL) // 2^53 + luaL_error(L, "Cannot push love object to Lua: pointer value %p is too large", key); + else + lua_pushnumber(L, (lua_Number) key); } static int w__release(lua_State *L) @@ -141,8 +181,8 @@ static int w__release(lua_State *L) if (lua_istable(L, -1)) { // loveobjects[object] = nil - lua_Number objectkey = luax_computeloveobjectkey(L, object); - lua_pushnumber(L, objectkey); + ObjectKey objectkey = luax_computeloveobjectkey(L, object); + luax_pushloveobjectkey(L, objectkey); lua_pushnil(L); lua_settable(L, -3); } @@ -605,10 +645,10 @@ void luax_pushtype(lua_State *L, love::Type &type, love::Object *object) return luax_rawnewtype(L, type, object); } - lua_Number objectkey = luax_computeloveobjectkey(L, object); + ObjectKey objectkey = luax_computeloveobjectkey(L, object); // Get the value of loveobjects[object] on the stack. - lua_pushnumber(L, objectkey); + luax_pushloveobjectkey(L, objectkey); lua_gettable(L, -2); // If the Proxy userdata isn't in the instantiated types table yet, add it. @@ -618,7 +658,7 @@ void luax_pushtype(lua_State *L, love::Type &type, love::Object *object) luax_rawnewtype(L, type, object); - lua_pushnumber(L, objectkey); + luax_pushloveobjectkey(L, objectkey); lua_pushvalue(L, -2); // loveobjects[object] = Proxy.