Browse Source

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.
Miku AuahDark 4 years ago
parent
commit
4f491c2e61
1 changed files with 52 additions and 12 deletions
  1. 52 12
      src/common/runtime.cpp

+ 52 - 12
src/common/runtime.cpp

@@ -93,11 +93,42 @@ static int w__eq(lua_State *L)
 	return 1;
 	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.
 // For use with the love object pointer -> Proxy pointer registry.
 // Using the pointer directly via lightuserdata would be ideal, but LuaJIT
 // 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
 	// love objects should be allocated on the heap, and thus are subject
 	// to the alignment rules of operator new / malloc. Lua numbers (doubles)
 	// 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;
 	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)
 static int w__release(lua_State *L)
@@ -141,8 +181,8 @@ static int w__release(lua_State *L)
 		if (lua_istable(L, -1))
 		if (lua_istable(L, -1))
 		{
 		{
 			// loveobjects[object] = nil
 			// 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_pushnil(L);
 			lua_settable(L, -3);
 			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);
 		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.
 	// Get the value of loveobjects[object] on the stack.
-	lua_pushnumber(L, objectkey);
+	luax_pushloveobjectkey(L, objectkey);
 	lua_gettable(L, -2);
 	lua_gettable(L, -2);
 
 
 	// If the Proxy userdata isn't in the instantiated types table yet, add it.
 	// 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);
 		luax_rawnewtype(L, type, object);
 
 
-		lua_pushnumber(L, objectkey);
+		luax_pushloveobjectkey(L, objectkey);
 		lua_pushvalue(L, -2);
 		lua_pushvalue(L, -2);
 
 
 		// loveobjects[object] = Proxy.
 		// loveobjects[object] = Proxy.