diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2015-04-10 14:56:25 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2015-04-10 14:56:25 -0300 |
| commit | ae76c39712852d14514f9ef19ac332e961749d93 (patch) | |
| tree | 33fbd2d0a5c375048eade2a14c0cb606a8c5bba4 | |
| parent | 0d4a1f71db1400a21654fc46b7e93a27db7641ae (diff) | |
| download | lua-ae76c39712852d14514f9ef19ac332e961749d93.tar.gz lua-ae76c39712852d14514f9ef19ac332e961749d93.tar.bz2 lua-ae76c39712852d14514f9ef19ac332e961749d93.zip | |
Bug: suspended '__le' metamethod can give wrong result
| -rw-r--r-- | bugs | 67 | ||||
| -rw-r--r-- | lstate.h | 3 | ||||
| -rw-r--r-- | lvm.c | 32 |
3 files changed, 90 insertions, 12 deletions
| @@ -3376,6 +3376,73 @@ patch = [[ | |||
| 3376 | } | 3376 | } |
| 3377 | 3377 | ||
| 3378 | 3378 | ||
| 3379 | Bug{ | ||
| 3380 | what = [[suspended '__le' metamethod can give wrong result]], | ||
| 3381 | report = [[Eric Zhong, 2015/04/07]], | ||
| 3382 | since = [[5.2]], | ||
| 3383 | fix = nil, | ||
| 3384 | |||
| 3385 | example = [[ | ||
| 3386 | mt = {__le = function (a,b) coroutine.yield("yield"); return a.x <= b.x end} | ||
| 3387 | t1 = setmetatable({x=1}, mt) | ||
| 3388 | t2 = {x=2} | ||
| 3389 | co = coroutine.wrap(function (a,b) return t2 <= t1 end) | ||
| 3390 | co() | ||
| 3391 | print(co()) --> true (should be false) | ||
| 3392 | ]], | ||
| 3393 | |||
| 3394 | patch = [[ | ||
| 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 | --[=[ |
| 3380 | Bug{ | 3447 | Bug{ |
| 3381 | what = [[ ]], | 3448 | what = [[ ]], |
| @@ -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 | ||
| @@ -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 | */ |
| 267 | int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r) { | 272 | int 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 */ |
