diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2024-11-16 12:00:28 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2024-11-16 12:00:28 -0300 |
commit | b117bdb3448778d9e7f9a0302791e8ac3bb97ddd (patch) | |
tree | cf83da22ab3ea7fae6bb2602c790f1a0ca9533c9 | |
parent | f12ce4029dfbce7b89ec136e6b7ba5f6bca039da (diff) | |
download | lua-b117bdb3448778d9e7f9a0302791e8ac3bb97ddd.tar.gz lua-b117bdb3448778d9e7f9a0302791e8ac3bb97ddd.tar.bz2 lua-b117bdb3448778d9e7f9a0302791e8ac3bb97ddd.zip |
Counter for length of chains of __call metamethods
This counter will allow (in a later commit) error messages to correct
argument numbers in functions called through __call metamethods.
-rw-r--r-- | ldo.c | 41 | ||||
-rw-r--r-- | lstate.h | 28 | ||||
-rw-r--r-- | manual/manual.of | 4 | ||||
-rw-r--r-- | testes/calls.lua | 23 |
4 files changed, 68 insertions, 28 deletions
@@ -464,21 +464,26 @@ static void rethook (lua_State *L, CallInfo *ci, int nres) { | |||
464 | 464 | ||
465 | /* | 465 | /* |
466 | ** Check whether 'func' has a '__call' metafield. If so, put it in the | 466 | ** Check whether 'func' has a '__call' metafield. If so, put it in the |
467 | ** stack, below original 'func', so that 'luaD_precall' can call it. Raise | 467 | ** stack, below original 'func', so that 'luaD_precall' can call it. |
468 | ** an error if there is no '__call' metafield. | 468 | ** Raise an error if there is no '__call' metafield. |
469 | ** Bits CIST_CCMT in status count how many _call metamethods were | ||
470 | ** invoked and how many corresponding extra arguments were pushed. | ||
471 | ** (This count will be saved in the 'callstatus' of the call). | ||
472 | ** Raise an error if this counter overflows. | ||
469 | */ | 473 | */ |
470 | static StkId tryfuncTM (lua_State *L, StkId func) { | 474 | static unsigned tryfuncTM (lua_State *L, StkId func, unsigned status) { |
471 | const TValue *tm; | 475 | const TValue *tm; |
472 | StkId p; | 476 | StkId p; |
473 | checkstackp(L, 1, func); /* space for metamethod */ | 477 | tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); |
474 | tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); /* (after previous GC) */ | 478 | if (l_unlikely(ttisnil(tm))) /* no metamethod? */ |
475 | if (l_unlikely(ttisnil(tm))) | 479 | luaG_callerror(L, s2v(func)); |
476 | luaG_callerror(L, s2v(func)); /* nothing to call */ | ||
477 | for (p = L->top.p; p > func; p--) /* open space for metamethod */ | 480 | for (p = L->top.p; p > func; p--) /* open space for metamethod */ |
478 | setobjs2s(L, p, p-1); | 481 | setobjs2s(L, p, p-1); |
479 | L->top.p++; /* stack space pre-allocated by the caller */ | 482 | L->top.p++; /* stack space pre-allocated by the caller */ |
480 | setobj2s(L, func, tm); /* metamethod is the new function to be called */ | 483 | setobj2s(L, func, tm); /* metamethod is the new function to be called */ |
481 | return func; | 484 | if ((status & MAX_CCMT) == MAX_CCMT) /* is counter full? */ |
485 | luaG_runerror(L, "'__call' chain too long"); | ||
486 | return status + (1u << CIST_CCMT); /* increment counter */ | ||
482 | } | 487 | } |
483 | 488 | ||
484 | 489 | ||
@@ -564,11 +569,17 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { | |||
564 | #define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L)) | 569 | #define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L)) |
565 | 570 | ||
566 | 571 | ||
572 | /* | ||
573 | ** Allocate and initialize CallInfo structure. At this point, the | ||
574 | ** only valid fields in the call status are number of results, | ||
575 | ** CIST_C (if it's a C function), and number of extra arguments. | ||
576 | ** (All these bit-fields fit in 16-bit values.) | ||
577 | */ | ||
567 | l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, unsigned status, | 578 | l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, unsigned status, |
568 | StkId top) { | 579 | StkId top) { |
569 | CallInfo *ci = L->ci = next_ci(L); /* new frame */ | 580 | CallInfo *ci = L->ci = next_ci(L); /* new frame */ |
570 | ci->func.p = func; | 581 | ci->func.p = func; |
571 | lua_assert((status & ~(CIST_NRESULTS | CIST_C)) == 0); | 582 | lua_assert((status & ~(CIST_NRESULTS | CIST_C | MAX_CCMT)) == 0); |
572 | ci->callstatus = status; | 583 | ci->callstatus = status; |
573 | ci->top.p = top; | 584 | ci->top.p = top; |
574 | return ci; | 585 | return ci; |
@@ -607,12 +618,13 @@ l_sinline int precallC (lua_State *L, StkId func, unsigned status, | |||
607 | */ | 618 | */ |
608 | int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, | 619 | int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, |
609 | int narg1, int delta) { | 620 | int narg1, int delta) { |
621 | unsigned status = LUA_MULTRET + 1; | ||
610 | retry: | 622 | retry: |
611 | switch (ttypetag(s2v(func))) { | 623 | switch (ttypetag(s2v(func))) { |
612 | case LUA_VCCL: /* C closure */ | 624 | case LUA_VCCL: /* C closure */ |
613 | return precallC(L, func, LUA_MULTRET + 1, clCvalue(s2v(func))->f); | 625 | return precallC(L, func, status, clCvalue(s2v(func))->f); |
614 | case LUA_VLCF: /* light C function */ | 626 | case LUA_VLCF: /* light C function */ |
615 | return precallC(L, func, LUA_MULTRET + 1, fvalue(s2v(func))); | 627 | return precallC(L, func, status, fvalue(s2v(func))); |
616 | case LUA_VLCL: { /* Lua function */ | 628 | case LUA_VLCL: { /* Lua function */ |
617 | Proto *p = clLvalue(s2v(func))->p; | 629 | Proto *p = clLvalue(s2v(func))->p; |
618 | int fsize = p->maxstacksize; /* frame size */ | 630 | int fsize = p->maxstacksize; /* frame size */ |
@@ -633,8 +645,8 @@ int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, | |||
633 | return -1; | 645 | return -1; |
634 | } | 646 | } |
635 | default: { /* not a function */ | 647 | default: { /* not a function */ |
636 | func = tryfuncTM(L, func); /* try to get '__call' metamethod */ | 648 | checkstackp(L, 1, func); /* space for metamethod */ |
637 | /* return luaD_pretailcall(L, ci, func, narg1 + 1, delta); */ | 649 | status = tryfuncTM(L, func, status); /* try '__call' metamethod */ |
638 | narg1++; | 650 | narg1++; |
639 | goto retry; /* try again */ | 651 | goto retry; /* try again */ |
640 | } | 652 | } |
@@ -676,7 +688,8 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) { | |||
676 | return ci; | 688 | return ci; |
677 | } | 689 | } |
678 | default: { /* not a function */ | 690 | default: { /* not a function */ |
679 | func = tryfuncTM(L, func); /* try to get '__call' metamethod */ | 691 | checkstackp(L, 1, func); /* space for metamethod */ |
692 | status = tryfuncTM(L, func, status); /* try '__call' metamethod */ | ||
680 | goto retry; /* try again with metamethod */ | 693 | goto retry; /* try again with metamethod */ |
681 | } | 694 | } |
682 | } | 695 | } |
@@ -221,16 +221,24 @@ struct CallInfo { | |||
221 | */ | 221 | */ |
222 | /* bits 0-7 are the expected number of results from this function + 1 */ | 222 | /* bits 0-7 are the expected number of results from this function + 1 */ |
223 | #define CIST_NRESULTS 0xffu | 223 | #define CIST_NRESULTS 0xffu |
224 | /* Bits 8-10 are used for CIST_RECST (see below) */ | 224 | |
225 | #define CIST_RECST 8 /* the offset, not the mask */ | 225 | /* bits 8-11 count call metamethods (and their extra arguments) */ |
226 | /* original value of 'allowhook' */ | 226 | #define CIST_CCMT 8 /* the offset, not the mask */ |
227 | #define CIST_OAH (cast(l_uint32, 1) << 11) | 227 | #define MAX_CCMT (0xfu << CIST_CCMT) |
228 | /* call is running a C function */ | 228 | |
229 | #define CIST_C (CIST_OAH << 1) | 229 | /* Bits 12-14 are used for CIST_RECST (see below) */ |
230 | #define CIST_RECST 12 /* the offset, not the mask */ | ||
231 | |||
232 | /* call is running a C function (still in first 16 bits) */ | ||
233 | #define CIST_C (1u << (CIST_RECST + 3)) | ||
230 | /* call is on a fresh "luaV_execute" frame */ | 234 | /* call is on a fresh "luaV_execute" frame */ |
231 | #define CIST_FRESH (CIST_C << 1) | 235 | #define CIST_FRESH cast(l_uint32, CIST_C << 1) |
236 | /* function is closing tbc variables */ | ||
237 | #define CIST_CLSRET (CIST_FRESH << 1) | ||
238 | /* original value of 'allowhook' */ | ||
239 | #define CIST_OAH (CIST_CLSRET << 1) | ||
232 | /* call is running a debug hook */ | 240 | /* call is running a debug hook */ |
233 | #define CIST_HOOKED (CIST_FRESH << 1) | 241 | #define CIST_HOOKED (CIST_OAH << 1) |
234 | /* doing a yieldable protected call */ | 242 | /* doing a yieldable protected call */ |
235 | #define CIST_YPCALL (CIST_HOOKED << 1) | 243 | #define CIST_YPCALL (CIST_HOOKED << 1) |
236 | /* call was tail called */ | 244 | /* call was tail called */ |
@@ -239,11 +247,9 @@ struct CallInfo { | |||
239 | #define CIST_HOOKYIELD (CIST_TAIL << 1) | 247 | #define CIST_HOOKYIELD (CIST_TAIL << 1) |
240 | /* function "called" a finalizer */ | 248 | /* function "called" a finalizer */ |
241 | #define CIST_FIN (CIST_HOOKYIELD << 1) | 249 | #define CIST_FIN (CIST_HOOKYIELD << 1) |
242 | /* function is closing tbc variables */ | ||
243 | #define CIST_CLSRET (CIST_FIN << 1) | ||
244 | #if defined(LUA_COMPAT_LT_LE) | 250 | #if defined(LUA_COMPAT_LT_LE) |
245 | /* using __lt for __le */ | 251 | /* using __lt for __le */ |
246 | #define CIST_LEQ (CIST_CLSRET << 1) | 252 | #define CIST_LEQ (CIST_FIN << 1) |
247 | #endif | 253 | #endif |
248 | 254 | ||
249 | 255 | ||
diff --git a/manual/manual.of b/manual/manual.of index f0b17b4c..ce42ff51 100644 --- a/manual/manual.of +++ b/manual/manual.of | |||
@@ -9392,6 +9392,10 @@ If you need to change it, | |||
9392 | declare a local variable with the same name in the loop body. | 9392 | declare a local variable with the same name in the loop body. |
9393 | } | 9393 | } |
9394 | 9394 | ||
9395 | @item{ | ||
9396 | A chain of @id{__call} metamethods can have at most 15 objects. | ||
9397 | } | ||
9398 | |||
9395 | } | 9399 | } |
9396 | 9400 | ||
9397 | } | 9401 | } |
diff --git a/testes/calls.lua b/testes/calls.lua index 409a275d..12312d60 100644 --- a/testes/calls.lua +++ b/testes/calls.lua | |||
@@ -178,7 +178,7 @@ do -- tail calls x chain of __call | |||
178 | end | 178 | end |
179 | 179 | ||
180 | -- build a chain of __call metamethods ending in function 'foo' | 180 | -- build a chain of __call metamethods ending in function 'foo' |
181 | for i = 1, 100 do | 181 | for i = 1, 15 do |
182 | foo = setmetatable({}, {__call = foo}) | 182 | foo = setmetatable({}, {__call = foo}) |
183 | end | 183 | end |
184 | 184 | ||
@@ -190,8 +190,8 @@ end | |||
190 | print('+') | 190 | print('+') |
191 | 191 | ||
192 | 192 | ||
193 | do -- testing chains of '__call' | 193 | do print"testing chains of '__call'" |
194 | local N = 20 | 194 | local N = 15 |
195 | local u = table.pack | 195 | local u = table.pack |
196 | for i = 1, N do | 196 | for i = 1, N do |
197 | u = setmetatable({i}, {__call = u}) | 197 | u = setmetatable({i}, {__call = u}) |
@@ -207,6 +207,23 @@ do -- testing chains of '__call' | |||
207 | end | 207 | end |
208 | 208 | ||
209 | 209 | ||
210 | do -- testing chains too long | ||
211 | local a = {} | ||
212 | for i = 1, 16 do -- one too many | ||
213 | a = setmetatable({}, {__call = a}) | ||
214 | end | ||
215 | local status, msg = pcall(a) | ||
216 | assert(not status and string.find(msg, "too long")) | ||
217 | |||
218 | setmetatable(a, {__call = a}) -- infinite chain | ||
219 | local status, msg = pcall(a) | ||
220 | assert(not status and string.find(msg, "too long")) | ||
221 | |||
222 | -- again, with a tail call | ||
223 | local status, msg = pcall(function () return a() end) | ||
224 | assert(not status and string.find(msg, "too long")) | ||
225 | end | ||
226 | |||
210 | a = nil | 227 | a = nil |
211 | (function (x) a=x end)(23) | 228 | (function (x) a=x end)(23) |
212 | assert(a == 23 and (function (x) return x*2 end)(20) == 40) | 229 | assert(a == 23 and (function (x) return x*2 end)(20) == 40) |