Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ae76c39

Browse files
committed
Bug: suspended '__le' metamethod can give wrong result
1 parent 0d4a1f7 commit ae76c39

File tree

3 files changed

+90
-12
lines changed

3 files changed

+90
-12
lines changed

bugs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3376,6 +3376,73 @@ patch = [[
33763376
}
33773377

33783378

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+
33793446
--[=[
33803447
Bug{
33813448
what = [[ ]],

lstate.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
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 $
33
** Global State
44
** See Copyright Notice in lua.h
55
*/
@@ -94,6 +94,7 @@ typedef struct CallInfo {
9494
#define CIST_YPCALL (1<<4) /* call is a yieldable protected call */
9595
#define CIST_TAIL (1<<5) /* call was tail called */
9696
#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */
97+
#define CIST_LEQ (1<<7) /* using __lt for __le */
9798

9899
#define isLua(ci) ((ci)->callstatus & CIST_LUA)
99100

lvm.c

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
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 $
33
** Lua virtual machine
44
** See Copyright Notice in lua.h
55
*/
@@ -262,7 +262,12 @@ int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) {
262262

263263

264264
/*
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.
266271
*/
267272
int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r) {
268273
int res;
@@ -273,11 +278,16 @@ int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r) {
273278
return luai_numle(nl, nr);
274279
else if (ttisstring(l) && ttisstring(r)) /* both are strings? */
275280
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' */
277282
return res;
278-
else if ((res = luaT_callorderTM(L, r, l, TM_LT)) < 0) /* else try 'lt' */
279-
luaG_ordererror(L, l, r);
280-
return !res;
283+
else { /* try 'lt': */
284+
L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */
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+
}
281291
}
282292

283293

@@ -542,11 +552,11 @@ void luaV_finishOp (lua_State *L) {
542552
case OP_LE: case OP_LT: case OP_EQ: {
543553
int res = !l_isfalse(L->top - 1);
544554
L->top--;
545-
/* metamethod should not be called when operand is K */
546-
lua_assert(!ISK(GETARG_B(inst)));
547-
if (op == OP_LE && /* "<=" using "<" instead? */
548-
ttisnil(luaT_gettmbyobj(L, base + GETARG_B(inst), TM_LE)))
549-
res = !res; /* invert result */
555+
if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */
556+
lua_assert(op == OP_LE);
557+
ci->callstatus ^= CIST_LEQ; /* clear mark */
558+
res = !res; /* negate result */
559+
}
550560
lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP);
551561
if (res != GETARG_A(inst)) /* condition failed? */
552562
ci->u.l.savedpc++; /* skip jump instruction */

0 commit comments

Comments
 (0)