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 /lvm.c | |
parent | 0d4a1f71db1400a21654fc46b7e93a27db7641ae (diff) | |
download | lua-ae76c39712852d14514f9ef19ac332e961749d93.tar.gz lua-ae76c39712852d14514f9ef19ac332e961749d93.tar.bz2 lua-ae76c39712852d14514f9ef19ac332e961749d93.zip |
Bug: suspended '__le' metamethod can give wrong result
Diffstat (limited to 'lvm.c')
-rw-r--r-- | lvm.c | 32 |
1 files changed, 21 insertions, 11 deletions
@@ -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 */ |