From 9c0a31f8ed30e5824f25f453f7b7f987ccce3376 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Wed, 28 Aug 2024 01:37:02 -0300 Subject: [PATCH 1/2] adjust file metatables even in compat53.module mode --- compat53/file_mt.lua | 69 +++++++++++++++++++++++++++++++ compat53/init.lua | 52 +---------------------- compat53/module.lua | 64 +++++++++++++++++++++++++++- liolib.c | 2 +- rockspecs/compat53-scm-0.rockspec | 1 + tests/test.lua | 21 ++++++---- 6 files changed, 148 insertions(+), 61 deletions(-) create mode 100644 compat53/file_mt.lua diff --git a/compat53/file_mt.lua b/compat53/file_mt.lua new file mode 100644 index 0000000..5df2e41 --- /dev/null +++ b/compat53/file_mt.lua @@ -0,0 +1,69 @@ +local lua_version = _VERSION:sub(-3) + +local M = {} + +local unpack = lua_version == "5.1" and unpack or table.unpack + +local function addasterisk(fmt) + if type(fmt) == "string" and fmt:sub(1, 1) ~= "*" then + return "*"..fmt + else + return fmt + end +end + +function M.update_file_meta(file_meta) + + -- make '*' optional for file:read and file:lines + + local file_lines = file_meta.__index.lines + file_meta.__index.lines = function(self, ...) + local n = select('#', ...) + for i = 1, n do + local a = select(i, ...) + local b = addasterisk(a) + -- as an optimization we only allocate a table for the + -- modified format arguments when we have a '*' somewhere + if a ~= b then + local args = { ... } + args[i] = b + for j = i+1, n do + args[j] = addasterisk(args[j]) + end + return file_lines(self, unpack(args, 1, n)) + end + end + return file_lines(self, ...) + end + + local file_read = file_meta.__index.read + file_meta.__index.read = function(self, ...) + local n = select('#', ...) + for i = 1, n do + local a = select(i, ...) + local b = addasterisk(a) + -- as an optimization we only allocate a table for the + -- modified format arguments when we have a '*' somewhere + if a ~= b then + local args = { ... } + args[i] = b + for j = i+1, n do + args[j] = addasterisk(args[j]) + end + return file_read(self, unpack(args, 1, n)) + end + end + return file_read(self, ...) + end + + local file_write = file_meta.__index.write + file_meta.__index.write = function(self, ...) + local ret, err = file_write(self, ...) + if ret then + return self + end + return ret, err + end +end + +return M diff --git a/compat53/init.lua b/compat53/init.lua index a7f0c80..04478a0 100644 --- a/compat53/init.lua +++ b/compat53/init.lua @@ -17,57 +17,9 @@ if lua_version < "5.3" then local file_meta = gmt(io.stdout) - -- make '*' optional for file:read and file:lines if type(file_meta) == "table" and type(file_meta.__index) == "table" then - - local function addasterisk(fmt) - if type(fmt) == "string" and fmt:sub(1, 1) ~= "*" then - return "*"..fmt - else - return fmt - end - end - - local file_lines = file_meta.__index.lines - file_meta.__index.lines = function(self, ...) - local n = select('#', ...) - for i = 1, n do - local a = select(i, ...) - local b = addasterisk(a) - -- as an optimization we only allocate a table for the - -- modified format arguments when we have a '*' somewhere - if a ~= b then - local args = { ... } - args[i] = b - for j = i+1, n do - args[j] = addasterisk(args[j]) - end - return file_lines(self, unpack(args, 1, n)) - end - end - return file_lines(self, ...) - end - - local file_read = file_meta.__index.read - file_meta.__index.read = function(self, ...) - local n = select('#', ...) - for i = 1, n do - local a = select(i, ...) - local b = addasterisk(a) - -- as an optimization we only allocate a table for the - -- modified format arguments when we have a '*' somewhere - if a ~= b then - local args = { ... } - args[i] = b - for j = i+1, n do - args[j] = addasterisk(args[j]) - end - return file_read(self, unpack(args, 1, n)) - end - end - return file_read(self, ...) - end - + local file_mt = require("compat53.file_mt") + file_mt.update_file_meta(file_meta) end -- got a valid metatable for file objects diff --git a/compat53/module.lua b/compat53/module.lua index 225d8ca..ff8eac7 100644 --- a/compat53/module.lua +++ b/compat53/module.lua @@ -13,7 +13,11 @@ if lua_version < "5.3" then debug, io, math, package, string, table local io_lines = io.lines local io_read = io.read + local io_open = io.open + local io_popen = io.popen + local io_tmpfile = io.tmpfile local unpack = lua_version == "5.1" and unpack or table.unpack + local debug_setmetatable = type(debug) == "table" and debug.setmetatable -- create module table M = {} @@ -201,6 +205,65 @@ if lua_version < "5.3" then end end + local compat_file_meta = {} + local compat_file_meta_loaded = false + + local function load_compat_file_meta(file_meta) + -- fill compat_file_meta with original entries + for k, v in pairs(file_meta) do + compat_file_meta[k] = v + end + compat_file_meta.__index = {} + for k, v in pairs(file_meta.__index) do + compat_file_meta.__index[k] = v + end + + -- update it with compatibility functions + local file_mt = require("compat53.file_mt") + file_mt.update_file_meta(compat_file_meta) + + compat_file_meta_loaded = true + end + + function M.io.open(...) + local fd, err = io_open(...) + if fd and debug_setmetatable then + if not compat_file_meta_loaded then + local file_meta = gmt(fd) + load_compat_file_meta(file_meta) + end + debug_setmetatable(fd, compat_file_meta) + end + + return fd, err + end + + function M.io.popen(...) + local fd, err = io_popen(...) + if fd and debug_setmetatable then + if not compat_file_meta_loaded then + local file_meta = gmt(fd) + load_compat_file_meta(file_meta) + end + debug_setmetatable(fd, compat_file_meta) + end + + return fd, err + end + + function M.io.tmpfile(...) + local fd, err = io_tmpfile(...) + if fd and debug_setmetatable then + if not compat_file_meta_loaded then + local file_meta = gmt(fd) + load_compat_file_meta(file_meta) + end + debug_setmetatable(fd, compat_file_meta) + end + + return fd, err + end + function M.io.read(...) local n = select('#', ...) for i = 1, n do @@ -450,7 +513,6 @@ if lua_version < "5.3" then if type(debug) == "table" then local debug_setfenv = debug.setfenv local debug_getfenv = debug.getfenv - local debug_setmetatable = debug.setmetatable M.debug = setmetatable({}, { __index = debug }) diff --git a/liolib.c b/liolib.c index 8a9e75c..7f425c1 100644 --- a/liolib.c +++ b/liolib.c @@ -142,7 +142,7 @@ static int l_checkmode (const char *mode) { typedef luaL_Stream LStream; -#define tolstream(L) ((LStream *)luaL_checkudata(L, 1, LUA_FILEHANDLE)) +#define tolstream(L) (luaL_checktype(L, 1, LUA_TUSERDATA), (LStream *)lua_touserdata(L, 1)) #define isclosed(p) ((p)->closef == NULL) diff --git a/rockspecs/compat53-scm-0.rockspec b/rockspecs/compat53-scm-0.rockspec index e0820d4..702036b 100644 --- a/rockspecs/compat53-scm-0.rockspec +++ b/rockspecs/compat53-scm-0.rockspec @@ -24,6 +24,7 @@ build = { modules = { ["compat53.init"] = "compat53/init.lua", ["compat53.module"] = "compat53/module.lua", + ["compat53.file_mt"] = "compat53/file_mt.lua", ["compat53.utf8"] = "lutf8lib.c", ["compat53.table"] = "ltablib.c", ["compat53.string"] = "lstrlib.c", diff --git a/tests/test.lua b/tests/test.lua index 443f6f7..8ed3ebb 100755 --- a/tests/test.lua +++ b/tests/test.lua @@ -575,7 +575,7 @@ ___'' do print("io.write()", io.type(io.write("hello world\n"))) local f = assert(io.tmpfile()) - print("file:write()", io.type(f:write("hello world\n"))) + print("io.tmpfile => file:write()", io.type(f:write("hello world\n"))) f:close() end @@ -588,14 +588,17 @@ do io.input("data.txt") print("io.read()", io.read("n", "number", "l", "a")) io.input(io.stdin) - if mode ~= "module" then - local f = assert(io.open("data.txt", "r")) - print("file:read()", f:read("*n", "*number", "*l", "*a")) - f:close() - f = assert(io.open("data.txt", "r")) - print("file:read()", f:read("n", "number", "l", "a")) - f:close() - end + local f = assert(io.open("data.txt", "r")) + print("file:read()", f:read("*n", "*number", "*l", "*a")) + f:close() + f = assert(io.open("data.txt", "r")) + print("file:read()", f:read("n", "number", "l", "a")) + f:close() + os.remove("data.txt") + + local g = assert(io.open("data.txt", "w")) + print("io.open => file:write()", type(g:write("hello"))) + g:close() os.remove("data.txt") end From 2cdfd42b97379688496c3c69703badcb39993f00 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Thu, 29 Aug 2024 17:07:24 -0300 Subject: [PATCH 2/2] Apply tweaks only to LuaJIT; file:write() only to compat=none --- compat53/file_mt.lua | 16 +++--- compat53/init.lua | 14 ++--- compat53/module.lua | 120 ++++++++++++++++++++++--------------------- tests/test.lua | 26 ++++++---- 4 files changed, 92 insertions(+), 84 deletions(-) diff --git a/compat53/file_mt.lua b/compat53/file_mt.lua index 5df2e41..6433619 100644 --- a/compat53/file_mt.lua +++ b/compat53/file_mt.lua @@ -12,7 +12,7 @@ local function addasterisk(fmt) end end -function M.update_file_meta(file_meta) +function M.update_file_meta(file_meta, is_luajit52) -- make '*' optional for file:read and file:lines @@ -56,13 +56,15 @@ function M.update_file_meta(file_meta) return file_read(self, ...) end - local file_write = file_meta.__index.write - file_meta.__index.write = function(self, ...) - local ret, err = file_write(self, ...) - if ret then - return self + if not is_luajit52 then + local file_write = file_meta.__index.write + file_meta.__index.write = function(self, ...) + local ret, err = file_write(self, ...) + if ret then + return self + end + return ret, err end - return ret, err end end diff --git a/compat53/init.lua b/compat53/init.lua index 04478a0..b507571 100644 --- a/compat53/init.lua +++ b/compat53/init.lua @@ -17,9 +17,15 @@ if lua_version < "5.3" then local file_meta = gmt(io.stdout) + -- detect LuaJIT (including LUAJIT_ENABLE_LUA52COMPAT compilation flag) + local is_luajit = (string.dump(function() end) or ""):sub(1, 3) == "\027LJ" + local is_luajit52 = is_luajit and + #setmetatable({}, { __len = function() return 1 end }) == 1 + + if type(file_meta) == "table" and type(file_meta.__index) == "table" then local file_mt = require("compat53.file_mt") - file_mt.update_file_meta(file_meta) + file_mt.update_file_meta(file_meta, is_luajit52) end -- got a valid metatable for file objects @@ -37,12 +43,6 @@ if lua_version < "5.3" then local io_type = io.type - -- detect LuaJIT (including LUAJIT_ENABLE_LUA52COMPAT compilation flag) - local is_luajit = (string.dump(function() end) or ""):sub(1, 3) == "\027LJ" - local is_luajit52 = is_luajit and - #setmetatable({}, { __len = function() return 1 end }) == 1 - - -- make package.searchers available as an alias for package.loaders local p_index = { searchers = package.loaders } setmetatable(package, { diff --git a/compat53/module.lua b/compat53/module.lua index ff8eac7..f9d8ebb 100644 --- a/compat53/module.lua +++ b/compat53/module.lua @@ -205,65 +205,6 @@ if lua_version < "5.3" then end end - local compat_file_meta = {} - local compat_file_meta_loaded = false - - local function load_compat_file_meta(file_meta) - -- fill compat_file_meta with original entries - for k, v in pairs(file_meta) do - compat_file_meta[k] = v - end - compat_file_meta.__index = {} - for k, v in pairs(file_meta.__index) do - compat_file_meta.__index[k] = v - end - - -- update it with compatibility functions - local file_mt = require("compat53.file_mt") - file_mt.update_file_meta(compat_file_meta) - - compat_file_meta_loaded = true - end - - function M.io.open(...) - local fd, err = io_open(...) - if fd and debug_setmetatable then - if not compat_file_meta_loaded then - local file_meta = gmt(fd) - load_compat_file_meta(file_meta) - end - debug_setmetatable(fd, compat_file_meta) - end - - return fd, err - end - - function M.io.popen(...) - local fd, err = io_popen(...) - if fd and debug_setmetatable then - if not compat_file_meta_loaded then - local file_meta = gmt(fd) - load_compat_file_meta(file_meta) - end - debug_setmetatable(fd, compat_file_meta) - end - - return fd, err - end - - function M.io.tmpfile(...) - local fd, err = io_tmpfile(...) - if fd and debug_setmetatable then - if not compat_file_meta_loaded then - local file_meta = gmt(fd) - load_compat_file_meta(file_meta) - end - debug_setmetatable(fd, compat_file_meta) - end - - return fd, err - end - function M.io.read(...) local n = select('#', ...) for i = 1, n do @@ -884,6 +825,67 @@ if lua_version < "5.3" then end end -- not luajit + if is_luajit then + local compat_file_meta = {} + local compat_file_meta_loaded = false + + local function load_compat_file_meta(file_meta) + -- fill compat_file_meta with original entries + for k, v in pairs(file_meta) do + compat_file_meta[k] = v + end + compat_file_meta.__index = {} + for k, v in pairs(file_meta.__index) do + compat_file_meta.__index[k] = v + end + + -- update it with compatibility functions + local file_mt = require("compat53.file_mt") + file_mt.update_file_meta(compat_file_meta, is_luajit52) + + compat_file_meta_loaded = true + end + + function M.io.open(...) + local fd, err = io_open(...) + if fd and debug_setmetatable then + if not compat_file_meta_loaded then + local file_meta = gmt(fd) + load_compat_file_meta(file_meta) + end + debug_setmetatable(fd, compat_file_meta) + end + + return fd, err + end + + function M.io.popen(...) + local fd, err = io_popen(...) + if fd and debug_setmetatable then + if not compat_file_meta_loaded then + local file_meta = gmt(fd) + load_compat_file_meta(file_meta) + end + debug_setmetatable(fd, compat_file_meta) + end + + return fd, err + end + + function M.io.tmpfile(...) + local fd, err = io_tmpfile(...) + if fd and debug_setmetatable then + if not compat_file_meta_loaded then + local file_meta = gmt(fd) + load_compat_file_meta(file_meta) + end + debug_setmetatable(fd, compat_file_meta) + end + + return fd, err + end + end + end -- lua 5.1 -- further write should be forwarded to _G diff --git a/tests/test.lua b/tests/test.lua index 8ed3ebb..5a4eca9 100755 --- a/tests/test.lua +++ b/tests/test.lua @@ -36,6 +36,8 @@ end local V = _VERSION:gsub("^.*(%d+)%.(%d+)$", "%1%2") if jit then V = "jit" end +local is_puclua51 = (_VERSION == "Lua 5.1" and not jit) + local mode = "global" if arg[1] == "module" then mode = "module" @@ -588,17 +590,19 @@ do io.input("data.txt") print("io.read()", io.read("n", "number", "l", "a")) io.input(io.stdin) - local f = assert(io.open("data.txt", "r")) - print("file:read()", f:read("*n", "*number", "*l", "*a")) - f:close() - f = assert(io.open("data.txt", "r")) - print("file:read()", f:read("n", "number", "l", "a")) - f:close() - os.remove("data.txt") - - local g = assert(io.open("data.txt", "w")) - print("io.open => file:write()", type(g:write("hello"))) - g:close() + if not is_puclua51 then + local f = assert(io.open("data.txt", "r")) + print("file:read()", f:read("*n", "*number", "*l", "*a")) + f:close() + f = assert(io.open("data.txt", "r")) + print("file:read()", f:read("n", "number", "l", "a")) + f:close() + os.remove("data.txt") + + local g = assert(io.open("data.txt", "w")) + print("io.open => file:write()", type(g:write("hello"))) + g:close() + end os.remove("data.txt") end