diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2019-06-25 17:45:50 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2019-06-25 17:45:50 -0300 |
| commit | c1a63c45f8ec5932993c8cec40d3c5ec0743349c (patch) | |
| tree | a61e8edb10b498c25e961cd7b2c7cb857e9db4c7 | |
| parent | 4487c28ced3dcf47c3ee19b6f6eeb0089ec64ba5 (diff) | |
| download | lua-c1a63c45f8ec5932993c8cec40d3c5ec0743349c.tar.gz lua-c1a63c45f8ec5932993c8cec40d3c5ec0743349c.tar.bz2 lua-c1a63c45f8ec5932993c8cec40d3c5ec0743349c.zip | |
'__call' metamethod can be any callable object
Removed the restriction that a '__call' metamethod must be an actual
function.
| -rw-r--r-- | ldo.c | 28 | ||||
| -rw-r--r-- | testes/calls.lua | 17 |
2 files changed, 31 insertions, 14 deletions
| @@ -348,18 +348,18 @@ static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) { | |||
| 348 | 348 | ||
| 349 | 349 | ||
| 350 | /* | 350 | /* |
| 351 | ** Check whether __call metafield of 'func' is a function. If so, put | 351 | ** Check whether 'func' has a '__call' metafield. If so, put it in the |
| 352 | ** it in stack below original 'func' so that 'luaD_call' can call | 352 | ** stack, below original 'func', so that 'luaD_call' can call it. Raise |
| 353 | ** it. Raise an error if __call metafield is not a function. | 353 | ** an error if there is no '__call' metafield. |
| 354 | */ | 354 | */ |
| 355 | void luaD_tryfuncTM (lua_State *L, StkId func) { | 355 | void luaD_tryfuncTM (lua_State *L, StkId func) { |
| 356 | const TValue *tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); | 356 | const TValue *tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); |
| 357 | StkId p; | 357 | StkId p; |
| 358 | if (unlikely(!ttisfunction(tm))) | 358 | if (unlikely(ttisnil(tm))) |
| 359 | luaG_typeerror(L, s2v(func), "call"); | 359 | luaG_typeerror(L, s2v(func), "call"); /* nothing to call */ |
| 360 | for (p = L->top; p > func; p--) | 360 | for (p = L->top; p > func; p--) /* open space for metamethod */ |
| 361 | setobjs2s(L, p, p-1); | 361 | setobjs2s(L, p, p-1); |
| 362 | L->top++; /* assume EXTRA_STACK */ | 362 | L->top++; /* stack space pre-allocated by the caller */ |
| 363 | setobj2s(L, func, tm); /* metamethod is the new function to be called */ | 363 | setobj2s(L, func, tm); /* metamethod is the new function to be called */ |
| 364 | } | 364 | } |
| 365 | 365 | ||
| @@ -457,13 +457,13 @@ void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1) { | |||
| 457 | */ | 457 | */ |
| 458 | void luaD_call (lua_State *L, StkId func, int nresults) { | 458 | void luaD_call (lua_State *L, StkId func, int nresults) { |
| 459 | lua_CFunction f; | 459 | lua_CFunction f; |
| 460 | TValue *funcv = s2v(func); | 460 | retry: |
| 461 | switch (ttypetag(funcv)) { | 461 | switch (ttypetag(s2v(func))) { |
| 462 | case LUA_TCCL: /* C closure */ | 462 | case LUA_TCCL: /* C closure */ |
| 463 | f = clCvalue(funcv)->f; | 463 | f = clCvalue(s2v(func))->f; |
| 464 | goto Cfunc; | 464 | goto Cfunc; |
| 465 | case LUA_TLCF: /* light C function */ | 465 | case LUA_TLCF: /* light C function */ |
| 466 | f = fvalue(funcv); | 466 | f = fvalue(s2v(func)); |
| 467 | Cfunc: { | 467 | Cfunc: { |
| 468 | int n; /* number of returns */ | 468 | int n; /* number of returns */ |
| 469 | CallInfo *ci; | 469 | CallInfo *ci; |
| @@ -487,7 +487,7 @@ void luaD_call (lua_State *L, StkId func, int nresults) { | |||
| 487 | } | 487 | } |
| 488 | case LUA_TLCL: { /* Lua function */ | 488 | case LUA_TLCL: { /* Lua function */ |
| 489 | CallInfo *ci; | 489 | CallInfo *ci; |
| 490 | Proto *p = clLvalue(funcv)->p; | 490 | Proto *p = clLvalue(s2v(func))->p; |
| 491 | int narg = cast_int(L->top - func) - 1; /* number of real arguments */ | 491 | int narg = cast_int(L->top - func) - 1; /* number of real arguments */ |
| 492 | int nfixparams = p->numparams; | 492 | int nfixparams = p->numparams; |
| 493 | int fsize = p->maxstacksize; /* frame size */ | 493 | int fsize = p->maxstacksize; /* frame size */ |
| @@ -505,9 +505,9 @@ void luaD_call (lua_State *L, StkId func, int nresults) { | |||
| 505 | break; | 505 | break; |
| 506 | } | 506 | } |
| 507 | default: { /* not a function */ | 507 | default: { /* not a function */ |
| 508 | checkstackp(L, 1, func); /* space for metamethod */ | ||
| 508 | luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */ | 509 | luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */ |
| 509 | luaD_call(L, func, nresults); /* now it must be a function */ | 510 | goto retry; /* try again with metamethod */ |
| 510 | break; | ||
| 511 | } | 511 | } |
| 512 | } | 512 | } |
| 513 | } | 513 | } |
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 | |||
| 151 | print('+') | 151 | print('+') |
| 152 | 152 | ||
| 153 | 153 | ||
| 154 | do -- testing chains of '__call' | ||
| 155 | local N = 20 | ||
| 156 | local u = table.pack | ||
| 157 | for i = 1, N do | ||
| 158 | u = setmetatable({i}, {__call = u}) | ||
| 159 | end | ||
| 160 | |||
| 161 | local Res = u("a", "b", "c") | ||
| 162 | |||
| 163 | assert(Res.n == N + 3) | ||
| 164 | for i = 1, N do | ||
| 165 | assert(Res[i][1] == i) | ||
| 166 | end | ||
| 167 | assert(Res[N + 1] == "a" and Res[N + 2] == "b" and Res[N + 3] == "c") | ||
| 168 | end | ||
| 169 | |||
| 170 | |||
| 154 | a = nil | 171 | a = nil |
| 155 | (function (x) a=x end)(23) | 172 | (function (x) a=x end)(23) |
| 156 | assert(a == 23 and (function (x) return x*2 end)(20) == 40) | 173 | assert(a == 23 and (function (x) return x*2 end)(20) == 40) |
