Skip to content

Commit

Permalink
adjust file metatables even in compat53.module mode (#67)
Browse files Browse the repository at this point in the history
* adjust file metatables even in compat53.module mode

* apply tweaks only to LuaJIT; file:write() only to compat=none
  • Loading branch information
hishamhm authored Aug 29, 2024
1 parent 1c679a2 commit 7a82c38
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 65 deletions.
71 changes: 71 additions & 0 deletions compat53/file_mt.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
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, is_luajit52)

-- 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

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
end
end

return M
62 changes: 7 additions & 55 deletions compat53/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,57 +17,15 @@ 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
-- 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

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

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, is_luajit52)
end -- got a valid metatable for file objects


Expand All @@ -85,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, {
Expand Down
66 changes: 65 additions & 1 deletion compat53/module.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {}
Expand Down Expand Up @@ -451,7 +455,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 })

Expand Down Expand Up @@ -823,6 +826,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
Expand Down
2 changes: 1 addition & 1 deletion liolib.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
1 change: 1 addition & 0 deletions rockspecs/compat53-scm-0.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
23 changes: 15 additions & 8 deletions tests/test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -576,7 +578,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

Expand All @@ -589,13 +591,18 @@ 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()
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
Expand Down

0 comments on commit 7a82c38

Please sign in to comment.