diff --git a/platform/xcode/liblove.xcodeproj/project.pbxproj b/platform/xcode/liblove.xcodeproj/project.pbxproj index 51064b45f..266df0620 100644 --- a/platform/xcode/liblove.xcodeproj/project.pbxproj +++ b/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 = ""; }; FA2E9BFE1C19E00C0004A1EE /* wrap_RandomGenerator.lua */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = wrap_RandomGenerator.lua; sourceTree = ""; }; 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 = ""; }; FA3C5E401F8C368C0003C579 /* ShaderStage.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ShaderStage.cpp; sourceTree = ""; }; FA3C5E411F8C368C0003C579 /* ShaderStage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShaderStage.h; sourceTree = ""; }; FA3C5E451F8D80CA0003C579 /* ShaderStage.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ShaderStage.cpp; sourceTree = ""; }; @@ -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 */, diff --git a/src/common/runtime.cpp b/src/common/runtime.cpp index 11b6d2dae..7b784aafb 100644 --- a/src/common/runtime.cpp +++ b/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)); diff --git a/src/common/runtime.h b/src/common/runtime.h index 7f2058550..3ffa12cfa 100644 --- a/src/common/runtime.h +++ b/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(L, idx, T::type); } +template +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 T *luax_getmodule(lua_State *L, const love::Type &type) { diff --git a/src/modules/data/wrap_ByteData.cpp b/src/modules/data/wrap_ByteData.cpp index 6f6caae7b..920d53578 100644 --- a/src/modules/data/wrap_ByteData.cpp +++ b/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; } diff --git a/src/modules/data/wrap_CompressedData.cpp b/src/modules/data/wrap_CompressedData.cpp index f77104aea..e1f601c7f 100644 --- a/src/modules/data/wrap_CompressedData.cpp +++ b/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 diff --git a/src/modules/data/wrap_Data.cpp b/src/modules/data/wrap_Data.cpp index 85cae983e..47fe5d54f 100644 --- a/src/modules/data/wrap_Data.cpp +++ b/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(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 diff --git a/src/modules/data/wrap_Data.h b/src/modules/data/wrap_Data.h index a01b25d2d..c6041c8c1 100644 --- a/src/modules/data/wrap_Data.h +++ b/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[]; diff --git a/src/modules/data/wrap_Data.lua b/src/modules/data/wrap_Data.lua new file mode 100644 index 000000000..23871f6fd --- /dev/null +++ b/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"--" diff --git a/src/modules/data/wrap_DataView.cpp b/src/modules/data/wrap_DataView.cpp index 3f8ba0639..9f01447b6 100644 --- a/src/modules/data/wrap_DataView.cpp +++ b/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; } diff --git a/src/modules/filesystem/wrap_FileData.cpp b/src/modules/filesystem/wrap_FileData.cpp index fb57a9096..8e3b0bce2 100644 --- a/src/modules/filesystem/wrap_FileData.cpp +++ b/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 diff --git a/src/modules/font/wrap_GlyphData.cpp b/src/modules/font/wrap_GlyphData.cpp index fe141f951..3d26b372d 100644 --- a/src/modules/font/wrap_GlyphData.cpp +++ b/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 diff --git a/src/modules/image/wrap_CompressedImageData.cpp b/src/modules/image/wrap_CompressedImageData.cpp index bbf9b2f21..1c45890dd 100644 --- a/src/modules/image/wrap_CompressedImageData.cpp +++ b/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 diff --git a/src/modules/image/wrap_ImageData.cpp b/src/modules/image/wrap_ImageData.cpp index ddf493f95..ea8921a76 100644 --- a/src/modules/image/wrap_ImageData.cpp +++ b/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; } diff --git a/src/modules/image/wrap_ImageData.lua b/src/modules/image/wrap_ImageData.lua index 63ff3c6de..5b8ac0b2c 100644 --- a/src/modules/image/wrap_ImageData.lua +++ b/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 = { diff --git a/src/modules/math/wrap_RandomGenerator.cpp b/src/modules/math/wrap_RandomGenerator.cpp index b7c3c26d8..10cd67ad0 100644 --- a/src/modules/math/wrap_RandomGenerator.cpp +++ b/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(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(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; } diff --git a/src/modules/math/wrap_RandomGenerator.lua b/src/modules/math/wrap_RandomGenerator.lua index 2e7dbf347..66a34039e 100644 --- a/src/modules/math/wrap_RandomGenerator.lua +++ b/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. diff --git a/src/modules/sound/wrap_SoundData.cpp b/src/modules/sound/wrap_SoundData.cpp index fd3fcf09f..7158254af 100644 --- a/src/modules/sound/wrap_SoundData.cpp +++ b/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; }