'__call' metamethod can be any callable object

Removed the restriction that a '__call' metamethod must be an actual
function.
This commit is contained in:
Roberto Ierusalimschy 2019-06-25 17:45:50 -03:00
parent 4487c28ced
commit c1a63c45f8
2 changed files with 31 additions and 14 deletions

28
ldo.c
View File

@ -348,18 +348,18 @@ static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
/* /*
** Check whether __call metafield of 'func' is a function. If so, put ** Check whether 'func' has a '__call' metafield. If so, put it in the
** it in stack below original 'func' so that 'luaD_call' can call ** stack, below original 'func', so that 'luaD_call' can call it. Raise
** it. Raise an error if __call metafield is not a function. ** an error if there is no '__call' metafield.
*/ */
void luaD_tryfuncTM (lua_State *L, StkId func) { void luaD_tryfuncTM (lua_State *L, StkId func) {
const TValue *tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); const TValue *tm = luaT_gettmbyobj(L, s2v(func), TM_CALL);
StkId p; StkId p;
if (unlikely(!ttisfunction(tm))) if (unlikely(ttisnil(tm)))
luaG_typeerror(L, s2v(func), "call"); luaG_typeerror(L, s2v(func), "call"); /* nothing to call */
for (p = L->top; p > func; p--) for (p = L->top; p > func; p--) /* open space for metamethod */
setobjs2s(L, p, p-1); setobjs2s(L, p, p-1);
L->top++; /* assume EXTRA_STACK */ L->top++; /* stack space pre-allocated by the caller */
setobj2s(L, func, tm); /* metamethod is the new function to be called */ setobj2s(L, func, tm); /* metamethod is the new function to be called */
} }
@ -457,13 +457,13 @@ void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1) {
*/ */
void luaD_call (lua_State *L, StkId func, int nresults) { void luaD_call (lua_State *L, StkId func, int nresults) {
lua_CFunction f; lua_CFunction f;
TValue *funcv = s2v(func); retry:
switch (ttypetag(funcv)) { switch (ttypetag(s2v(func))) {
case LUA_TCCL: /* C closure */ case LUA_TCCL: /* C closure */
f = clCvalue(funcv)->f; f = clCvalue(s2v(func))->f;
goto Cfunc; goto Cfunc;
case LUA_TLCF: /* light C function */ case LUA_TLCF: /* light C function */
f = fvalue(funcv); f = fvalue(s2v(func));
Cfunc: { Cfunc: {
int n; /* number of returns */ int n; /* number of returns */
CallInfo *ci; CallInfo *ci;
@ -487,7 +487,7 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
} }
case LUA_TLCL: { /* Lua function */ case LUA_TLCL: { /* Lua function */
CallInfo *ci; CallInfo *ci;
Proto *p = clLvalue(funcv)->p; Proto *p = clLvalue(s2v(func))->p;
int narg = cast_int(L->top - func) - 1; /* number of real arguments */ int narg = cast_int(L->top - func) - 1; /* number of real arguments */
int nfixparams = p->numparams; int nfixparams = p->numparams;
int fsize = p->maxstacksize; /* frame size */ int fsize = p->maxstacksize; /* frame size */
@ -505,9 +505,9 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
break; break;
} }
default: { /* not a function */ default: { /* not a function */
checkstackp(L, 1, func); /* space for metamethod */
luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */ luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */
luaD_call(L, func, nresults); /* now it must be a function */ goto retry; /* try again with metamethod */
break;
} }
} }
} }

View File

@ -151,6 +151,23 @@ end
print('+') print('+')
do -- testing chains of '__call'
local N = 20
local u = table.pack
for i = 1, N do
u = setmetatable({i}, {__call = u})
end
local Res = u("a", "b", "c")
assert(Res.n == N + 3)
for i = 1, N do
assert(Res[i][1] == i)
end
assert(Res[N + 1] == "a" and Res[N + 2] == "b" and Res[N + 3] == "c")
end
a = nil a = nil
(function (x) a=x end)(23) (function (x) a=x end)(23)
assert(a == 23 and (function (x) return x*2 end)(20) == 40) assert(a == 23 and (function (x) return x*2 end)(20) == 40)