Bug 677957 - Fix peculiarly dynamically-nested for-in loops. ("Assertion failure: !cx->iterValue.isMagic(JS_NO_ITER_VALUE), at jsiter.cpp:1017") r=dvander.

This commit is contained in:
Jason Orendorff 2011-09-02 16:52:13 -05:00
parent a622e35128
commit 08f5a5a5bd
13 changed files with 87 additions and 39 deletions

View File

@ -0,0 +1,4 @@
function test() {
for each (var i in []) {}
}
for each (new test().p in [0]) {}

View File

@ -0,0 +1,13 @@
var x = {f: 1, g: 0};
function f() {
for each (new f().nosuch.prop in x)
throw 'FAIL';
}
var e;
try {
f();
} catch (exc) {
e = exc;
}
assertEq(e instanceof InternalError, true);

View File

@ -1,15 +1,15 @@
// |jit-test| debug
f = (function() {
function b() {
"use strict";
Object.defineProperty(this, "x", ({}));
}
for each(let d in [0, 0]) {
try {
b(d);
} catch (e) {}
}
})
trap(f, 54, undefined);
f()
function b() {
"use strict";
Object.defineProperty(this, "x", ({}));
}
for each(let d in [0, 0]) {
try {
b(d);
} catch (e) {}
}
});
trap(f, 53, undefined);
f();

View File

@ -4640,7 +4640,8 @@ EmitAssignment(JSContext *cx, JSCodeGenerator *cg, JSParseNode *lhs, JSOp op, JS
case TOK_LP:
if (!js_EmitTree(cx, cg, lhs))
return false;
offset++;
JS_ASSERT(lhs->pn_xflags & PNX_SETCALL);
offset += 2;
break;
#if JS_HAS_XML_SUPPORT
case TOK_UNARYOP:
@ -4717,8 +4718,13 @@ EmitAssignment(JSContext *cx, JSCodeGenerator *cg, JSParseNode *lhs, JSOp op, JS
if (!js_EmitTree(cx, cg, rhs))
return false;
} else {
/* The value to assign is the next enumeration value in a for-in loop. */
if (js_Emit2(cx, cg, JSOP_ITERNEXT, offset) < 0)
/*
* The value to assign is the next enumeration value in a for-in loop.
* That value is produced by a JSOP_ITERNEXT op, previously emitted.
* If offset == 1, that slot is already at the top of the
* stack. Otherwise, rearrange the stack to put that value on top.
*/
if (offset != 1 && js_Emit2(cx, cg, JSOP_PICK, offset - 1) < 0)
return false;
}
@ -5421,7 +5427,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
SET_STATEMENT_TOP(&stmtInfo, top);
if (EmitTraceOp(cx, cg, NULL) < 0)
return JS_FALSE;
#ifdef DEBUG
intN loopDepth = cg->stackDepth;
#endif
@ -5432,6 +5437,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
* so that the decompiler can distinguish 'for (x in y)' from
* 'for (var x in y)'.
*/
if (js_Emit1(cx, cg, JSOP_ITERNEXT) < 0)
return false;
if (!EmitAssignment(cx, cg, pn2->pn_kid2, JSOP_NOP, NULL))
return false;
tmp2 = CG_OFFSET(cg);

View File

@ -2697,11 +2697,10 @@ END_CASE(JSOP_MOREITER)
BEGIN_CASE(JSOP_ITERNEXT)
{
Value *itervp = regs.sp - GET_INT8(regs.pc);
JS_ASSERT(itervp >= regs.fp()->base());
JS_ASSERT(itervp->isObject());
JS_ASSERT(regs.sp - 1 >= regs.fp()->base());
JS_ASSERT(regs.sp[-1].isObject());
PUSH_NULL();
if (!IteratorNext(cx, &itervp->toObject(), &regs.sp[-1]))
if (!IteratorNext(cx, &regs.sp[-2].toObject(), &regs.sp[-1]))
goto error;
}
END_CASE(JSOP_ITERNEXT)

View File

@ -2723,6 +2723,26 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
sn = NULL;
break;
case JSOP_PICK:
{
uintN i = pc[1];
LOCAL_ASSERT(ss->top > i + 1);
uintN bottom = ss->top - (i + 1);
ptrdiff_t pickedOffset = ss->offsets[bottom];
memmove(ss->offsets + bottom, ss->offsets + bottom + 1,
i * sizeof(ss->offsets[0]));
ss->offsets[ss->top - 1] = pickedOffset;
jsbytecode pickedOpcode = ss->opcodes[bottom];
memmove(ss->opcodes + bottom, ss->opcodes + bottom + 1,
i * sizeof(ss->opcodes[0]));
ss->opcodes[ss->top - 1] = pickedOpcode;
todo = -2;
break;
}
case JSOP_ENTERWITH:
LOCAL_ASSERT(!js_GetSrcNote(jp->script, pc));
rval = POP_STR();
@ -3231,7 +3251,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
* <<RHS>>
* iter
* pc: goto/gotox C [src_for_in(B, D)]
* A: <<LHS = iternext>>
* A: trace
* iternext
* <<LHS = result_of_iternext>>
* B: pop [maybe a src_decl_var/let]
* <<S>>
* C: moreiter
@ -3254,11 +3276,14 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
* JSOP_MOREITER or JSOP_IFNE, though we do quick asserts
* to check that they are there.
*/
LOCAL_ASSERT(pc[-JSOP_ITER_LENGTH] == JSOP_ITER);
LOCAL_ASSERT(pc[js_CodeSpec[op].length] == JSOP_TRACE);
LOCAL_ASSERT(pc[js_CodeSpec[op].length + JSOP_TRACE_LENGTH] == JSOP_ITERNEXT);
cond = GetJumpOffset(pc, pc);
next = js_GetSrcNoteOffset(sn, 0);
tail = js_GetSrcNoteOffset(sn, 1);
JS_ASSERT(pc[next] == JSOP_POP);
JS_ASSERT(pc[cond] == JSOP_MOREITER);
LOCAL_ASSERT(pc[next] == JSOP_POP);
LOCAL_ASSERT(pc[cond] == JSOP_MOREITER);
DECOMPILE_CODE(pc + oplen, next - oplen);
lval = POP_STR();
@ -3266,7 +3291,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
* This string "<next>" comes from jsopcode.tbl. It stands
* for the result pushed by JSOP_ITERNEXT.
*/
JS_ASSERT(strcmp(lval + strlen(lval) - 9, " = <next>") == 0);
LOCAL_ASSERT(strcmp(lval + strlen(lval) - 9, " = <next>") == 0);
const_cast<char *>(lval)[strlen(lval) - 9] = '\0';
LOCAL_ASSERT(ss->top >= 1);

View File

@ -199,8 +199,9 @@ OPDEF(JSOP_STRICTEQ, 72, "stricteq", "===", 1, 2, 1, 10, JOF_BYTE|J
OPDEF(JSOP_STRICTNE, 73, "strictne", "!==", 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC)
/*
* Host object extension: given 'o.item(i) = j', the left-hand side compiles
* JSOP_SETCALL after JSOP_CALL, JSOP_EVAL, JSOP_FUNAPPLY, or JSOP_FUNCALL.
* Sometimes web pages do 'o.Item(i) = j'. This is not an early SyntaxError,
* for web compatibility. Instead we emit JSOP_SETCALL after the function call,
* an opcode that always throws.
*/
OPDEF(JSOP_SETCALL, 74, "setcall", NULL, 1, 1, 2, 18, JOF_BYTE)
@ -218,7 +219,7 @@ OPDEF(JSOP_SETCALL, 74, "setcall", NULL, 1, 1, 2, 18, JOF_BYTE)
*/
OPDEF(JSOP_ITER, 75, "iter", NULL, 2, 1, 1, 0, JOF_UINT8)
OPDEF(JSOP_MOREITER, 76, "moreiter", NULL, 1, 1, 2, 0, JOF_BYTE)
OPDEF(JSOP_ITERNEXT, 77, "iternext", "<next>", 2, 0, 1, 0, JOF_UINT8)
OPDEF(JSOP_ITERNEXT, 77, "iternext", "<next>", 1, 0, 1, 0, JOF_BYTE)
OPDEF(JSOP_ENDITER, 78, "enditer", NULL, 1, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_FUNAPPLY, 79, "funapply", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE|JOF_TYPESET)

View File

@ -14862,7 +14862,7 @@ JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_ITERNEXT()
{
LIns* v_ins;
Value &iterobj_val = stackval(-GET_INT8(cx->regs().pc));
Value &iterobj_val = stackval(-1);
CHECK_STATUS_A(unboxNextValue(iterobj_val, v_ins));
stack(0, v_ins);
return ARECORD_CONTINUE;

View File

@ -222,7 +222,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
* before deserialization of bytecode. If the saved version does not match
* the current version, abort deserialization and invalidate the file.
*/
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 93)
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 94)
/*
* Library-private functions.

View File

@ -1682,7 +1682,7 @@ mjit::Compiler::generateMethod()
END_CASE(JSOP_ARGUMENTS)
BEGIN_CASE(JSOP_ITERNEXT)
iterNext(GET_INT8(PC));
iterNext();
END_CASE(JSOP_ITERNEXT)
BEGIN_CASE(JSOP_DUP)
@ -5583,9 +5583,9 @@ mjit::Compiler::iter(uintN flags)
* of a for-in loop to put the next value on the stack.
*/
void
mjit::Compiler::iterNext(ptrdiff_t offset)
mjit::Compiler::iterNext()
{
FrameEntry *fe = frame.peek(-offset);
FrameEntry *fe = frame.peek(-1);
RegisterID reg = frame.tempRegForData(fe);
/* Is it worth trying to pin this longer? Prolly not. */
@ -5629,7 +5629,6 @@ mjit::Compiler::iterNext(ptrdiff_t offset)
frame.freeReg(T2);
stubcc.leave();
stubcc.masm.move(Imm32(offset), Registers::ArgReg1);
OOL_STUBCALL(stubs::IterNext, REJOIN_FALLTHROUGH);
frame.pushUntypedPayload(JSVAL_TYPE_STRING, T3);

View File

@ -578,7 +578,7 @@ class Compiler : public BaseCompiler
bool constantFoldBranch(jsbytecode *target, bool taken);
bool emitStubCmpOp(BoolStub stub, jsbytecode *target, JSOp fused);
bool iter(uintN flags);
void iterNext(ptrdiff_t offset);
void iterNext();
bool iterMore(jsbytecode *target);
void iterEnd();
MaybeJump loadDouble(FrameEntry *fe, FPRegisterID *fpReg, bool *allocated);

View File

@ -1843,12 +1843,12 @@ stubs::InitMethod(VMFrame &f, JSAtom *atom)
}
void JS_FASTCALL
stubs::IterNext(VMFrame &f, int32 offset)
stubs::IterNext(VMFrame &f)
{
JS_ASSERT(f.regs.sp - offset >= f.fp()->base());
JS_ASSERT(f.regs.sp[-offset].isObject());
JS_ASSERT(f.regs.sp - 1 >= f.fp()->base());
JS_ASSERT(f.regs.sp[-1].isObject());
JSObject *iterobj = &f.regs.sp[-offset].toObject();
JSObject *iterobj = &f.regs.sp[-1].toObject();
f.regs.sp[0].setNull();
f.regs.sp++;
if (!js_IteratorNext(f.cx, iterobj, &f.regs.sp[-1]))

View File

@ -192,7 +192,7 @@ void JS_FASTCALL StrictEq(VMFrame &f);
void JS_FASTCALL StrictNe(VMFrame &f);
void JS_FASTCALL Iter(VMFrame &f, uint32 flags);
void JS_FASTCALL IterNext(VMFrame &f, int32 offset);
void JS_FASTCALL IterNext(VMFrame &f);
JSBool JS_FASTCALL IterMore(VMFrame &f);
void JS_FASTCALL EndIter(VMFrame &f);