Browse Source

Add Data:getFFIPointer. Similar to Data:getPointer but returns a cdata pointer directly (or nil if the FFI isn't available).

It should be preferred instead of Data:getPointer because the latter uses lightuserdata which can't store more all possible memory addresses on some new arm64 architectures, when LuaJIT is used.
Alex Szpakowski 6 years ago
parent
commit
cb4b45dcf1

+ 2 - 0
platform/xcode/liblove.xcodeproj/project.pbxproj

@@ -1811,6 +1811,7 @@
 		FA2AF6731DAD64970032B62C /* vertex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = vertex.cpp; sourceTree = "<group>"; };
 		FA2E9BFE1C19E00C0004A1EE /* wrap_RandomGenerator.lua */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = wrap_RandomGenerator.lua; sourceTree = "<group>"; };
 		FA317EB918F28B6D00B0BCD7 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
+		FA34AF6A22E2977700F77015 /* wrap_Data.lua */ = {isa = PBXFileReference; lastKnownFileType = text; path = wrap_Data.lua; sourceTree = "<group>"; };
 		FA3C5E401F8C368C0003C579 /* ShaderStage.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ShaderStage.cpp; sourceTree = "<group>"; };
 		FA3C5E411F8C368C0003C579 /* ShaderStage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShaderStage.h; sourceTree = "<group>"; };
 		FA3C5E451F8D80CA0003C579 /* ShaderStage.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ShaderStage.cpp; sourceTree = "<group>"; };
@@ -3539,6 +3540,7 @@
 				FACA02E91F5E396B0084B28F /* wrap_CompressedData.h */,
 				FA6A2B651F5F7B6B0074C308 /* wrap_Data.cpp */,
 				FA6A2B641F5F7B6B0074C308 /* wrap_Data.h */,
+				FA34AF6A22E2977700F77015 /* wrap_Data.lua */,
 				FACA02EA1F5E396B0084B28F /* wrap_DataModule.cpp */,
 				FACA02EB1F5E396B0084B28F /* wrap_DataModule.h */,
 				FA6A2B6E1F5F845F0074C308 /* wrap_DataView.cpp */,

+ 29 - 1
src/common/runtime.cpp

@@ -209,6 +209,13 @@ void luax_pushstring(lua_State *L, const std::string &str)
 	lua_pushlstring(L, str.data(), str.size());
 }
 
+void luax_pushpointerasstring(lua_State *L, const void *pointer)
+{
+	char str[sizeof(void *)];
+	memcpy(str, &pointer, sizeof(void *));
+	lua_pushlstring(L, str, sizeof(void *));
+}
+
 bool luax_boolflag(lua_State *L, int table_index, const char *key, bool defaultValue)
 {
 	lua_getfield(L, table_index, key);
@@ -452,7 +459,7 @@ int luax_register_type(lua_State *L, love::Type *type, ...)
 	return 0;
 }
 
-void luax_gettypemetatable(lua_State *L, love::Type &type)
+void luax_gettypemetatable(lua_State *L, const love::Type &type)
 {
 	const char *name = type.getName();
 	lua_getfield(L, LUA_REGISTRYINDEX, name);
@@ -886,6 +893,27 @@ void luax_register(lua_State *L, const char *name, const luaL_Reg *l)
 	}
 }
 
+void luax_runwrapper(lua_State *L, const char *filedata, size_t datalen, const char *filename, const love::Type &type, void *ffifuncs)
+{
+	luax_gettypemetatable(L, type);
+
+	// Load and execute ImageData.lua, sending the metatable and the ffi
+	// functions struct pointer as arguments.
+	if (lua_istable(L, -1))
+	{
+		luaL_loadbuffer(L, filedata, datalen, filename);
+		lua_pushvalue(L, -2);
+		if (ffifuncs != nullptr)
+			luax_pushpointerasstring(L, ffifuncs);
+		else
+			lua_pushnil(L);
+		lua_call(L, 2, 0);
+	}
+
+	// Pop the metatable.
+	lua_pop(L, 1);
+}
+
 Type *luax_type(lua_State *L, int idx)
 {
 	return Type::byName(luaL_checkstring(L, idx));

+ 20 - 1
src/common/runtime.h

@@ -177,6 +177,14 @@ std::string luax_checkstring(lua_State *L, int idx);
  **/
 void luax_pushstring(lua_State *L, const std::string &str);
 
+/**
+ * Pushes a pointer onto the stack as a string (i.e. a new string with a length
+ * of 4 or 8 will be created, containing the given address in its bytes).
+ * This is a workaround for lua_pushlightuserdata not working on systems which
+ * use more than the lower 47 bits of address space, when LuaJIT is used.
+ **/
+void luax_pushpointerasstring(lua_State *L, const void *pointer);
+
 
 bool luax_boolflag(lua_State *L, int table_index, const char *key, bool defaultValue);
 int luax_intflag(lua_State *L, int table_index, const char *key, int defaultValue);
@@ -291,7 +299,7 @@ int luax_register_type(lua_State *L, love::Type *type, ...);
 /**
  * Pushes the metatable of the specified type onto the stack.
 **/
-void luax_gettypemetatable(lua_State *L, love::Type &type);
+void luax_gettypemetatable(lua_State *L, const love::Type &type);
 
 /**
  * Do a table.insert from C
@@ -498,6 +506,8 @@ void luax_checktablefields(lua_State *L, int idx, const char *enumName, bool (*g
 	}
 }
 
+void luax_runwrapper(lua_State *L, const char *filedata, size_t datalen, const char *filename, const love::Type &type, void *ffifuncs);
+
 /**
  * Calls luax_objlen/lua_rawlen depending on version
  **/
@@ -544,6 +554,15 @@ T *luax_checktype(lua_State *L, int idx)
 	return luax_checktype<T>(L, idx, T::type);
 }
 
+template <typename T>
+T *luax_ffi_checktype(Proxy *p, const love::Type &type = T::type)
+{
+	// FIXME: We need better type-checking...
+	if (p == nullptr || p->object == nullptr || p->type == nullptr || !p->type->isa(type))
+		return nullptr;
+	return (T *) p->object;
+}
+
 template <typename T>
 T *luax_getmodule(lua_State *L, const love::Type &type)
 {

+ 1 - 0
src/modules/data/wrap_ByteData.cpp

@@ -39,6 +39,7 @@ static const luaL_Reg w_ByteData_functions[] =
 int luaopen_bytedata(lua_State *L)
 {
 	luax_register_type(L, &ByteData::type, w_Data_functions, w_ByteData_functions, nullptr);
+	love::data::luax_rundatawrapper(L, ByteData::type);
 	return 0;
 }
 

+ 3 - 1
src/modules/data/wrap_CompressedData.cpp

@@ -63,7 +63,9 @@ static const luaL_Reg w_CompressedData_functions[] =
 
 extern "C" int luaopen_compresseddata(lua_State *L)
 {
-	return luax_register_type(L, &CompressedData::type, w_Data_functions, w_CompressedData_functions, nullptr);
+	int ret = luax_register_type(L, &CompressedData::type, w_Data_functions, w_CompressedData_functions, nullptr);
+	love::data::luax_rundatawrapper(L, CompressedData::type);
+	return ret;
 }
 
 } // data

+ 36 - 2
src/modules/data/wrap_Data.cpp

@@ -20,6 +20,11 @@
 
 #include "wrap_Data.h"
 
+// Put the Lua code directly into a raw string literal.
+static const char data_lua[] =
+#include "wrap_Data.lua"
+;
+
 namespace love
 {
 namespace data
@@ -44,6 +49,13 @@ int w_Data_getPointer(lua_State *L)
 	return 1;
 }
 
+// Placeholder, overridden by the FFI code when the FFI is available.
+int w_Data_getFFIPointer(lua_State *L)
+{
+	lua_pushnil(L);
+	return 1;
+}
+
 int w_Data_getSize(lua_State *L)
 {
 	Data *t = luax_checkdata(L, 1);
@@ -51,18 +63,40 @@ int w_Data_getSize(lua_State *L)
 	return 1;
 }
 
+// C functions in a struct, necessary for the FFI versions of Data methods.
+struct FFI_Data
+{
+	void *(*getFFIPointer)(Proxy *p);
+};
+
+static FFI_Data ffifuncs =
+{
+	[](Proxy *p) -> void * // getFFIPointer
+	{
+		auto data = luax_ffi_checktype<Data>(p);
+		return data != nullptr ? data->getData() : nullptr;
+	}
+};
+
 const luaL_Reg w_Data_functions[] =
 {
 	{ "getString", w_Data_getString },
 	{ "getPointer", w_Data_getPointer },
+	{ "getFFIPointer", w_Data_getFFIPointer },
 	{ "getSize", w_Data_getSize },
 	{ 0, 0 }
 };
 
+void luax_rundatawrapper(lua_State *L, const love::Type &type)
+{
+	luax_runwrapper(L, data_lua, sizeof(data_lua), "Data.lua", type, &ffifuncs);
+}
+
 int luaopen_data(lua_State *L)
 {
-	luax_register_type(L, &Data::type, w_Data_functions, nullptr);
-	return 0;
+	int n = luax_register_type(L, &Data::type, w_Data_functions, nullptr);
+	luax_rundatawrapper(L, Data::type);
+	return n;
 }
 
 } // data

+ 1 - 0
src/modules/data/wrap_Data.h

@@ -29,6 +29,7 @@ namespace love
 namespace data
 {
 
+void luax_rundatawrapper(lua_State *L, const love::Type &type);
 Data *luax_checkdata(lua_State *L, int idx);
 int luaopen_data(lua_State *L);
 extern const luaL_Reg w_Data_functions[];

+ 55 - 0
src/modules/data/wrap_Data.lua

@@ -0,0 +1,55 @@
+R"luastring"--(
+-- DO NOT REMOVE THE ABOVE LINE. It is used to load this file as a C++ string.
+-- There is a matching delimiter at the bottom of the file.
+
+--[[
+Copyright (c) 2006-2019 LOVE Development Team
+
+This software is provided 'as-is', without any express or implied
+warranty.  In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+claim that you wrote the original software. If you use this software
+in a product, an acknowledgment in the product documentation would be
+appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+--]]
+
+local Data_mt, ffifuncspointer_str = ...
+local Data = Data_mt.__index
+
+local type, error = type, error
+
+if type(jit) ~= "table" then return end
+
+local status, ffi = pcall(require, "ffi")
+if not status then return end
+
+pcall(ffi.cdef, [[
+typedef struct Proxy Proxy;
+
+typedef struct FFI_Data
+{
+	void *(*getFFIPointer)(Proxy *p);
+} FFI_Data;
+]])
+
+local ffifuncs = ffi.cast("const FFI_Data **", ffifuncspointer_str)[0]
+
+-- Overwrite placeholder method with the FFI implementation.
+
+function Data:getFFIPointer()
+	-- TODO: This should ideally be handled inside the C function
+	if self == nil then error("bad argument #1 to 'getFFIPointer' (Data expected, got no value)", 2) end
+	return ffifuncs.getFFIPointer(self)
+end
+
+-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
+--)luastring"--"

+ 1 - 0
src/modules/data/wrap_DataView.cpp

@@ -39,6 +39,7 @@ static const luaL_Reg w_DataView_functions[] =
 int luaopen_dataview(lua_State *L)
 {
 	luax_register_type(L, &DataView::type, w_Data_functions, w_DataView_functions, nullptr);
+	love::data::luax_rundatawrapper(L, DataView::type);
 	return 0;
 }
 

+ 3 - 1
src/modules/filesystem/wrap_FileData.cpp

@@ -66,7 +66,9 @@ static const luaL_Reg w_FileData_functions[] =
 
 extern "C" int luaopen_filedata(lua_State *L)
 {
-	return luax_register_type(L, &FileData::type, data::w_Data_functions, w_FileData_functions, nullptr);
+	int ret = luax_register_type(L, &FileData::type, data::w_Data_functions, w_FileData_functions, nullptr);
+	love::data::luax_rundatawrapper(L, FileData::type);
+	return ret;
 }
 
 } // filesystem

+ 3 - 1
src/modules/font/wrap_GlyphData.cpp

@@ -141,7 +141,9 @@ const luaL_Reg w_GlyphData_functions[] =
 
 extern "C" int luaopen_glyphdata(lua_State *L)
 {
-	return luax_register_type(L, &GlyphData::type, data::w_Data_functions, w_GlyphData_functions, nullptr);
+	int ret = luax_register_type(L, &GlyphData::type, data::w_Data_functions, w_GlyphData_functions, nullptr);
+	love::data::luax_rundatawrapper(L, GlyphData::type);
+	return ret;
 }
 
 } // font

+ 3 - 1
src/modules/image/wrap_CompressedImageData.cpp

@@ -116,7 +116,9 @@ static const luaL_Reg w_CompressedImageData_functions[] =
 
 extern "C" int luaopen_compressedimagedata(lua_State *L)
 {
-	return luax_register_type(L, &CompressedImageData::type, data::w_Data_functions, w_CompressedImageData_functions, nullptr);
+	int ret = luax_register_type(L, &CompressedImageData::type, data::w_Data_functions, w_CompressedImageData_functions, nullptr);
+	love::data::luax_rundatawrapper(L, CompressedImageData::type);
+	return ret;
 }
 
 } // image

+ 2 - 14
src/modules/image/wrap_ImageData.cpp

@@ -510,20 +510,8 @@ extern "C" int luaopen_imagedata(lua_State *L)
 
 	int ret = luax_register_type(L, &ImageData::type, data::w_Data_functions, w_ImageData_functions, nullptr);
 
-	luax_gettypemetatable(L, ImageData::type);
-
-	// Load and execute ImageData.lua, sending the metatable and the ffi
-	// functions struct pointer as arguments.
-	if (lua_istable(L, -1))
-	{
-		luaL_loadbuffer(L, imagedata_lua, sizeof(imagedata_lua), "ImageData.lua");
-		lua_pushvalue(L, -2);
-		lua_pushlightuserdata(L, &ffifuncs);
-		lua_call(L, 2, 0);
-	}
-
-	// Pop the metatable.
-	lua_pop(L, 1);
+	love::data::luax_rundatawrapper(L, ImageData::type);
+	luax_runwrapper(L, imagedata_lua, sizeof(imagedata_lua), "ImageData.lua", ImageData::type, &ffifuncs);
 
 	return ret;
 }

+ 2 - 2
src/modules/image/wrap_ImageData.lua

@@ -22,7 +22,7 @@ misrepresented as being the original software.
 3. This notice may not be removed or altered from any source distribution.
 --]]
 
-local ImageData_mt, ffifuncspointer = ...
+local ImageData_mt, ffifuncspointer_str = ...
 local ImageData = ImageData_mt.__index
 
 local tonumber, assert, error = tonumber, assert, error
@@ -103,7 +103,7 @@ struct ImageData_Pixel_RG32F { float r, g; };
 struct ImageData_Pixel_RGBA32F { float r, g, b, a; };
 ]])
 
-local ffifuncs = ffi.cast("FFI_ImageData *", ffifuncspointer)
+local ffifuncs = ffi.cast("FFI_ImageData **", ffifuncspointer_str)[0]
 
 local conversions = {
 	r8 = {

+ 5 - 26
src/modules/math/wrap_RandomGenerator.cpp

@@ -126,22 +126,14 @@ static FFI_RandomGenerator ffifuncs =
 {
 	[](Proxy *p) -> double // random()
 	{
-		// FIXME: We need better type-checking...
-		if (p == nullptr || p->object == nullptr || p->type == nullptr || !p->type->isa(RandomGenerator::type))
-			return 0.0;
-
-		RandomGenerator *rng = (RandomGenerator *) p->object;
-		return rng->random();
+		auto rng = luax_ffi_checktype<RandomGenerator>(p);
+		return rng != nullptr ? rng->random() : 0.0;
 	},
 
 	[](Proxy *p, double stdddev, double mean) -> double // randomNormal
 	{
-		// FIXME: We need better type-checking...
-		if (p == nullptr || p->object == nullptr || p->type == nullptr || !p->type->isa(RandomGenerator::type))
-			return 0.0;
-
-		RandomGenerator *rng = (RandomGenerator *) p->object;
-		return rng->randomNormal(stdddev) + mean;
+		auto rng = luax_ffi_checktype<RandomGenerator>(p);
+		return rng != nullptr ? (rng->randomNormal(stdddev) + mean) : 0.0;
 	}
 };
 
@@ -160,20 +152,7 @@ extern "C" int luaopen_randomgenerator(lua_State *L)
 {
 	int n = luax_register_type(L, &RandomGenerator::type, w_RandomGenerator_functions, nullptr);
 
-	luax_gettypemetatable(L, RandomGenerator::type);
-
-	// Load and execute wrap_RandomGenerator.lua, sending the metatable and the
-	// ffi functions struct pointer as arguments.
-	if (lua_istable(L, -1))
-	{
-		luaL_loadbuffer(L, randomgenerator_lua, sizeof(randomgenerator_lua), "wrap_RandomGenerator.lua");
-		lua_pushvalue(L, -2);
-		lua_pushlightuserdata(L, &ffifuncs);
-		lua_call(L, 2, 0);
-	}
-
-	// Pop the metatable.
-	lua_pop(L, 1);
+	luax_runwrapper(L, randomgenerator_lua, sizeof(randomgenerator_lua), "RandomGenerator.lua", RandomGenerator::type, &ffifuncs);
 
 	return n;
 }

+ 2 - 2
src/modules/math/wrap_RandomGenerator.lua

@@ -22,7 +22,7 @@ misrepresented as being the original software.
 3. This notice may not be removed or altered from any source distribution.
 --]]
 
-local RandomGenerator_mt, ffifuncspointer = ...
+local RandomGenerator_mt, ffifuncspointer_str = ...
 local RandomGenerator = RandomGenerator_mt.__index
 
 local type, tonumber, error = type, tonumber, error
@@ -67,7 +67,7 @@ typedef struct FFI_RandomGenerator
 } FFI_RandomGenerator;
 ]])
 
-local ffifuncs = ffi.cast("FFI_RandomGenerator *", ffifuncspointer)
+local ffifuncs = ffi.cast("FFI_RandomGenerator **", ffifuncspointer_str)[0]
 
 
 -- Overwrite some regular love.math functions with FFI implementations.

+ 2 - 12
src/modules/sound/wrap_SoundData.cpp

@@ -148,18 +148,8 @@ extern "C" int luaopen_sounddata(lua_State *L)
 {
 	int ret = luax_register_type(L, &SoundData::type, data::w_Data_functions, w_SoundData_functions, nullptr);
 
-	luax_gettypemetatable(L, SoundData::type);
-
-	// Load and execute SoundData.lua, sending the metatable as an argument.
-	if (lua_istable(L, -1))
-	{
-		luaL_loadbuffer(L, sounddata_lua, sizeof(sounddata_lua), "SoundData.lua");
-		lua_pushvalue(L, -2);
-		lua_call(L, 1, 0);
-	}
-
-	// Pop the metatable.
-	lua_pop(L, 1);
+	love::data::luax_rundatawrapper(L, SoundData::type);
+	luax_runwrapper(L, sounddata_lua, sizeof(sounddata_lua), "SoundData.lua", SoundData::type, nullptr);
 
 	return ret;
 }