From c1a63c45f8ec5932993c8cec40d3c5ec0743349c Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Tue, 25 Jun 2019 17:45:50 -0300 Subject: '__call' metamethod can be any callable object Removed the restriction that a '__call' metamethod must be an actual function. --- ldo.c | 28 ++++++++++++++-------------- testes/calls.lua | 17 +++++++++++++++++ 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/ldo.c b/ldo.c index 1a327ffd..288aef13 100644 --- a/ldo.c +++ b/ldo.c @@ -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 -** it in stack below original 'func' so that 'luaD_call' can call -** it. Raise an error if __call metafield is not a function. +** Check whether 'func' has a '__call' metafield. If so, put it in the +** stack, below original 'func', so that 'luaD_call' can call it. Raise +** an error if there is no '__call' metafield. */ void luaD_tryfuncTM (lua_State *L, StkId func) { const TValue *tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); StkId p; - if (unlikely(!ttisfunction(tm))) - luaG_typeerror(L, s2v(func), "call"); - for (p = L->top; p > func; p--) + if (unlikely(ttisnil(tm))) + luaG_typeerror(L, s2v(func), "call"); /* nothing to call */ + for (p = L->top; p > func; p--) /* open space for metamethod */ 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 */ } @@ -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) { lua_CFunction f; - TValue *funcv = s2v(func); - switch (ttypetag(funcv)) { + retry: + switch (ttypetag(s2v(func))) { case LUA_TCCL: /* C closure */ - f = clCvalue(funcv)->f; + f = clCvalue(s2v(func))->f; goto Cfunc; case LUA_TLCF: /* light C function */ - f = fvalue(funcv); + f = fvalue(s2v(func)); Cfunc: { int n; /* number of returns */ CallInfo *ci; @@ -487,7 +487,7 @@ void luaD_call (lua_State *L, StkId func, int nresults) { } case LUA_TLCL: { /* Lua function */ 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 nfixparams = p->numparams; int fsize = p->maxstacksize; /* frame size */ @@ -505,9 +505,9 @@ void luaD_call (lua_State *L, StkId func, int nresults) { break; } default: { /* not a function */ + checkstackp(L, 1, func); /* space for metamethod */ luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */ - luaD_call(L, func, nresults); /* now it must be a function */ - break; + goto retry; /* try again with metamethod */ } } } diff --git a/testes/calls.lua b/testes/calls.lua index 56a12ae6..739a624f 100644 --- a/testes/calls.lua +++ b/testes/calls.lua @@ -151,6 +151,23 @@ end 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 (function (x) a=x end)(23) assert(a == 23 and (function (x) return x*2 end)(20) == 40) -- cgit v1.2.3-55-g6feb