From ae76c39712852d14514f9ef19ac332e961749d93 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Fri, 10 Apr 2015 14:56:25 -0300 Subject: Bug: suspended '__le' metamethod can give wrong result --- lvm.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) (limited to 'lvm.c') diff --git a/lvm.c b/lvm.c index c1e4fd09..1025e885 100644 --- a/lvm.c +++ b/lvm.c @@ -1,5 +1,5 @@ /* -** $Id: lvm.c,v 2.237 2015/03/07 19:30:16 roberto Exp roberto $ +** $Id: lvm.c,v 2.238 2015/03/30 15:45:01 roberto Exp roberto $ ** Lua virtual machine ** See Copyright Notice in lua.h */ @@ -262,7 +262,12 @@ int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) { /* -** Main operation less than or equal to; return 'l <= r'. +** Main operation less than or equal to; return 'l <= r'. If it needs +** a metamethod and there is no '__le', try '__lt', based on +** l <= r iff !(r < l) (assuming a total order). If the metamethod +** yields during this substitution, the continuation has to know +** about it (to negate the result of r= 0) /* first try 'le' */ + else if ((res = luaT_callorderTM(L, l, r, TM_LE)) >= 0) /* try 'le' */ return res; - else if ((res = luaT_callorderTM(L, r, l, TM_LT)) < 0) /* else try 'lt' */ - luaG_ordererror(L, l, r); - return !res; + else { /* try 'lt': */ + L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */ + res = luaT_callorderTM(L, r, l, TM_LT); + L->ci->callstatus ^= CIST_LEQ; /* clear mark */ + if (res < 0) + luaG_ordererror(L, l, r); + return !res; /* result is negated */ + } } @@ -542,11 +552,11 @@ void luaV_finishOp (lua_State *L) { case OP_LE: case OP_LT: case OP_EQ: { int res = !l_isfalse(L->top - 1); L->top--; - /* metamethod should not be called when operand is K */ - lua_assert(!ISK(GETARG_B(inst))); - if (op == OP_LE && /* "<=" using "<" instead? */ - ttisnil(luaT_gettmbyobj(L, base + GETARG_B(inst), TM_LE))) - res = !res; /* invert result */ + if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */ + lua_assert(op == OP_LE); + ci->callstatus ^= CIST_LEQ; /* clear mark */ + res = !res; /* negate result */ + } lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP); if (res != GETARG_A(inst)) /* condition failed? */ ci->u.l.savedpc++; /* skip jump instruction */ -- cgit v1.2.3-55-g6feb