Skip to content

Commit 4737dba

Browse files
committed
fix: preserve call function environments in functors
Prefer a functor's call field over its __call dispatcher when reading or setting its environment. This keeps shared dispatchers unchanged and applies the environment to the function that executes the wrapped code. Backport to the maintained stdlib release branch, adapting the implementation to lib/std/debug.lua and the regression tests to specs/debug_spec.yaml. Current stdlib master delegates these functions to std.normalize. (cherry picked from commit 8b877b249c6f2353cae570647a17fc25464d73c5)
1 parent 0454788 commit 4737dba

2 files changed

Lines changed: 27 additions & 4 deletions

File tree

‎lib/std/debug.lua‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ if debug_setfenv then
119119
return setfenv(n, env), nil
120120
end
121121
if type(fn) ~= 'function' then
122-
fn =(getmetatable(fn) or {}).__call or fn
122+
fn = fn.call or (getmetatable(fn) or {}).__call or fn
123123
end
124124
return debug_setfenv(fn, env)
125125
end
@@ -135,7 +135,7 @@ else
135135
end
136136
fn = debug_getinfo(n, 'f').func
137137
elseif type(fn) ~= 'function' then
138-
fn =(getmetatable(fn) or {}).__call or fn
138+
fn = fn.call or (getmetatable(fn) or {}).__call or fn
139139
end
140140

141141
local up, name = 0
@@ -174,7 +174,7 @@ if debug_getfenv then
174174
-- No need to recurse because Lua doesn't support nested functors.
175175
-- __call can only (sensibly) be a function, so no need to adjust
176176
-- stack frame offset either.
177-
fn =(getmetatable(fn) or {}).__call or fn
177+
fn = fn.call or (getmetatable(fn) or {}).__call or fn
178178
end
179179

180180
-- In Lua 5.1, only debug.getfenv works on C functions; but it
@@ -193,7 +193,7 @@ else
193193
if n then
194194
fn = debug_getinfo(n + 1, 'f').func
195195
elseif type(fn) ~= 'function' then
196-
fn = (getmetatable(fn) or {}).__call or fn
196+
fn = fn.call or (getmetatable(fn) or {}).__call or fn
197197
end
198198

199199
local name, env

‎specs/debug_spec.yaml‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,6 +1051,17 @@ specify std.debug:
10511051
- it understands functables:
10521052
M.setfenv (ft, env)
10531053
expect (f (ft)).to_be (env)
1054+
- it prefers a functable call field environment: |
1055+
expect (luaproc [[
1056+
local M = require "std.debug"
1057+
local env = {tostring = tostring}
1058+
local fn = function (x) return tostring (x) end
1059+
local ft = setmetatable ({call = fn}, {
1060+
__call = function (self, ...) return self.call (...) end,
1061+
})
1062+
M.setfenv (fn, env)
1063+
assert (M.getfenv (ft) == env)
1064+
]]).to_succeed_with ""
10541065
10551066
- describe setfenv:
10561067
- before:
@@ -1063,6 +1074,18 @@ specify std.debug:
10631074
- it understands functables:
10641075
f (ft, env)
10651076
expect (fn (5)).to_be '"5"'
1077+
- it sets a functable call field environment: |
1078+
expect (luaproc [[
1079+
local M = require "std.debug"
1080+
local env = {tostring = function (x) return '"' .. tostring (x) .. '"' end}
1081+
local fn = function (x) return tostring (x) end
1082+
local ft = setmetatable ({call = fn}, {
1083+
__call = function (self, ...) return self.call (...) end,
1084+
})
1085+
M.setfenv (ft, env)
1086+
assert (M.getfenv (fn) == env)
1087+
assert (ft (42) == '"42"')
1088+
]]).to_succeed_with ""
10661089
10671090
10681091
- describe say:

0 commit comments

Comments
 (0)