aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2015-04-10 14:56:25 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2015-04-10 14:56:25 -0300
commitae76c39712852d14514f9ef19ac332e961749d93 (patch)
tree33fbd2d0a5c375048eade2a14c0cb606a8c5bba4
parent0d4a1f71db1400a21654fc46b7e93a27db7641ae (diff)
downloadlua-ae76c39712852d14514f9ef19ac332e961749d93.tar.gz
lua-ae76c39712852d14514f9ef19ac332e961749d93.tar.bz2
lua-ae76c39712852d14514f9ef19ac332e961749d93.zip
Bug: suspended '__le' metamethod can give wrong result
-rw-r--r--bugs67
-rw-r--r--lstate.h3
-rw-r--r--lvm.c32
3 files changed, 90 insertions, 12 deletions
diff --git a/bugs b/bugs
index ab8a8369..8c163475 100644
--- a/bugs
+++ b/bugs
@@ -3376,6 +3376,73 @@ patch = [[
3376} 3376}
3377 3377
3378 3378
3379Bug{
3380what = [[suspended '__le' metamethod can give wrong result]],
3381report = [[Eric Zhong, 2015/04/07]],
3382since = [[5.2]],
3383fix = nil,
3384
3385example = [[
3386mt = {__le = function (a,b) coroutine.yield("yield"); return a.x <= b.x end}
3387t1 = setmetatable({x=1}, mt)
3388t2 = {x=2}
3389co = coroutine.wrap(function (a,b) return t2 <= t1 end)
3390co()
3391print(co()) --> true (should be false)
3392]],
3393
3394patch = [[
3395--- lstate.h 2015/03/04 13:31:21 2.120
3396+++ lstate.h 2015/04/08 16:30:40
3397@@ -94,6 +94,7 @@
3398 #define CIST_YPCALL (1<<4) /* call is a yieldable protected call */
3399 #define CIST_TAIL (1<<5) /* call was tail called */
3400 #define CIST_HOOKYIELD (1<<6) /* last hook called yielded */
3401+#define CIST_LEQ (1<<7) /* using __lt for __le */
3402
3403 #define isLua(ci) ((ci)->callstatus & CIST_LUA)
3404
3405
3406--- lvm.c 2015/03/30 15:45:01 2.238
3407+++ lvm.c 2015/04/09 15:30:13
3408@@ -275,9 +275,14 @@
3409 return l_strcmp(tsvalue(l), tsvalue(r)) <= 0;
3410 else if ((res = luaT_callorderTM(L, l, r, TM_LE)) >= 0) /* first try 'le' */
3411 return res;
3412- else if ((res = luaT_callorderTM(L, r, l, TM_LT)) < 0) /* else try 'lt' */
3413- luaG_ordererror(L, l, r);
3414- return !res;
3415+ else { /* try 'lt': */
3416+ L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */
3417+ res = luaT_callorderTM(L, r, l, TM_LT);
3418+ L->ci->callstatus ^= CIST_LEQ; /* clear mark */
3419+ if (res < 0)
3420+ luaG_ordererror(L, l, r);
3421+ return !res; /* result is negated */
3422+ }
3423 }
3424
3425@@ -542,11 +547,11 @@
3426 case OP_LE: case OP_LT: case OP_EQ: {
3427 int res = !l_isfalse(L->top - 1);
3428 L->top--;
3429- /* metamethod should not be called when operand is K */
3430- lua_assert(!ISK(GETARG_B(inst)));
3431- if (op == OP_LE && /* "<=" using "<" instead? */
3432- ttisnil(luaT_gettmbyobj(L, base + GETARG_B(inst), TM_LE)))
3433- res = !res; /* invert result */
3434+ if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */
3435+ lua_assert(op == OP_LE);
3436+ ci->callstatus ^= CIST_LEQ; /* clear mark */
3437+ res = !res; /* negate result */
3438+ }
3439 lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP);
3440 if (res != GETARG_A(inst)) /* condition failed? */
3441 ci->u.l.savedpc++; /* skip jump instruction */
3442]]
3443}
3444
3445
3379--[=[ 3446--[=[
3380Bug{ 3447Bug{
3381what = [[ ]], 3448what = [[ ]],
diff --git a/lstate.h b/lstate.h
index 285dfd82..5c9b0233 100644
--- a/lstate.h
+++ b/lstate.h
@@ -1,5 +1,5 @@
1/* 1/*
2** $Id: lstate.h,v 2.119 2014/10/30 18:53:28 roberto Exp roberto $ 2** $Id: lstate.h,v 2.120 2015/03/04 13:31:21 roberto Exp roberto $
3** Global State 3** Global State
4** See Copyright Notice in lua.h 4** See Copyright Notice in lua.h
5*/ 5*/
@@ -94,6 +94,7 @@ typedef struct CallInfo {
94#define CIST_YPCALL (1<<4) /* call is a yieldable protected call */ 94#define CIST_YPCALL (1<<4) /* call is a yieldable protected call */
95#define CIST_TAIL (1<<5) /* call was tail called */ 95#define CIST_TAIL (1<<5) /* call was tail called */
96#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */ 96#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */
97#define CIST_LEQ (1<<7) /* using __lt for __le */
97 98
98#define isLua(ci) ((ci)->callstatus & CIST_LUA) 99#define isLua(ci) ((ci)->callstatus & CIST_LUA)
99 100
diff --git a/lvm.c b/lvm.c
index c1e4fd09..1025e885 100644
--- a/lvm.c
+++ b/lvm.c
@@ -1,5 +1,5 @@
1/* 1/*
2** $Id: lvm.c,v 2.237 2015/03/07 19:30:16 roberto Exp roberto $ 2** $Id: lvm.c,v 2.238 2015/03/30 15:45:01 roberto Exp roberto $
3** Lua virtual machine 3** Lua virtual machine
4** See Copyright Notice in lua.h 4** See Copyright Notice in lua.h
5*/ 5*/
@@ -262,7 +262,12 @@ int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) {
262 262
263 263
264/* 264/*
265** Main operation less than or equal to; return 'l <= r'. 265** Main operation less than or equal to; return 'l <= r'. If it needs
266** a metamethod and there is no '__le', try '__lt', based on
267** l <= r iff !(r < l) (assuming a total order). If the metamethod
268** yields during this substitution, the continuation has to know
269** about it (to negate the result of r<l); bit CIST_LEQ in the call
270** status keeps that information.
266*/ 271*/
267int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r) { 272int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r) {
268 int res; 273 int res;
@@ -273,11 +278,16 @@ int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r) {
273 return luai_numle(nl, nr); 278 return luai_numle(nl, nr);
274 else if (ttisstring(l) && ttisstring(r)) /* both are strings? */ 279 else if (ttisstring(l) && ttisstring(r)) /* both are strings? */
275 return l_strcmp(tsvalue(l), tsvalue(r)) <= 0; 280 return l_strcmp(tsvalue(l), tsvalue(r)) <= 0;
276 else if ((res = luaT_callorderTM(L, l, r, TM_LE)) >= 0) /* first try 'le' */ 281 else if ((res = luaT_callorderTM(L, l, r, TM_LE)) >= 0) /* try 'le' */
277 return res; 282 return res;
278 else if ((res = luaT_callorderTM(L, r, l, TM_LT)) < 0) /* else try 'lt' */ 283 else { /* try 'lt': */
279 luaG_ordererror(L, l, r); 284 L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */
280 return !res; 285 res = luaT_callorderTM(L, r, l, TM_LT);
286 L->ci->callstatus ^= CIST_LEQ; /* clear mark */
287 if (res < 0)
288 luaG_ordererror(L, l, r);
289 return !res; /* result is negated */
290 }
281} 291}
282 292
283 293
@@ -542,11 +552,11 @@ void luaV_finishOp (lua_State *L) {
542 case OP_LE: case OP_LT: case OP_EQ: { 552 case OP_LE: case OP_LT: case OP_EQ: {
543 int res = !l_isfalse(L->top - 1); 553 int res = !l_isfalse(L->top - 1);
544 L->top--; 554 L->top--;
545 /* metamethod should not be called when operand is K */ 555 if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */
546 lua_assert(!ISK(GETARG_B(inst))); 556 lua_assert(op == OP_LE);
547 if (op == OP_LE && /* "<=" using "<" instead? */ 557 ci->callstatus ^= CIST_LEQ; /* clear mark */
548 ttisnil(luaT_gettmbyobj(L, base + GETARG_B(inst), TM_LE))) 558 res = !res; /* negate result */
549 res = !res; /* invert result */ 559 }
550 lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP); 560 lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP);
551 if (res != GETARG_A(inst)) /* condition failed? */ 561 if (res != GETARG_A(inst)) /* condition failed? */
552 ci->u.l.savedpc++; /* skip jump instruction */ 562 ci->u.l.savedpc++; /* skip jump instruction */