aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2024-11-16 12:00:28 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2024-11-16 12:00:28 -0300
commitb117bdb3448778d9e7f9a0302791e8ac3bb97ddd (patch)
treecf83da22ab3ea7fae6bb2602c790f1a0ca9533c9
parentf12ce4029dfbce7b89ec136e6b7ba5f6bca039da (diff)
downloadlua-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.c41
-rw-r--r--lstate.h28
-rw-r--r--manual/manual.of4
-rw-r--r--testes/calls.lua23
4 files changed, 68 insertions, 28 deletions
diff --git a/ldo.c b/ldo.c
index 72a1e306..cb7e5aef 100644
--- a/ldo.c
+++ b/ldo.c
@@ -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*/
470static StkId tryfuncTM (lua_State *L, StkId func) { 474static 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*/
567l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, unsigned status, 578l_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*/
608int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, 619int 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 }
diff --git a/lstate.h b/lstate.h
index ab567213..1c81b6ed 100644
--- a/lstate.h
+++ b/lstate.h
@@ -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,
9392declare a local variable with the same name in the loop body. 9392declare a local variable with the same name in the loop body.
9393} 9393}
9394 9394
9395@item{
9396A 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
190print('+') 190print('+')
191 191
192 192
193do -- testing chains of '__call' 193do 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'
207end 207end
208 208
209 209
210do -- 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"))
225end
226
210a = nil 227a = nil
211(function (x) a=x end)(23) 228(function (x) a=x end)(23)
212assert(a == 23 and (function (x) return x*2 end)(20) == 40) 229assert(a == 23 and (function (x) return x*2 end)(20) == 40)