diff --git a/js/src/jit-test/tests/basic/bug677957-1.js b/js/src/jit-test/tests/basic/bug677957-1.js new file mode 100644 index 00000000000..5e90b2a7633 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug677957-1.js @@ -0,0 +1,4 @@ +function test() { + for each (var i in []) {} +} +for each (new test().p in [0]) {} diff --git a/js/src/jit-test/tests/basic/bug677957-2.js b/js/src/jit-test/tests/basic/bug677957-2.js new file mode 100644 index 00000000000..7778dd02a79 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug677957-2.js @@ -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); diff --git a/js/src/jit-test/tests/basic/testBug683470.js b/js/src/jit-test/tests/basic/testBug683470.js index c9ed7a35fb1..8b78deaae17 100644 --- a/js/src/jit-test/tests/basic/testBug683470.js +++ b/js/src/jit-test/tests/basic/testBug683470.js @@ -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(); diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index 21d5385fbb5..bfcf19eb7d3 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -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); diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index b4187a0d265..3fb1447526b 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -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(), ®s.sp[-1])) + if (!IteratorNext(cx, ®s.sp[-2].toObject(), ®s.sp[-1])) goto error; } END_CASE(JSOP_ITERNEXT) diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 04480ebd528..0f577bf8e2f 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -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) * <> * iter * pc: goto/gotox C [src_for_in(B, D)] - * A: <> + * A: trace + * iternext + * <> * B: pop [maybe a src_decl_var/let] * <> * 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 "" comes from jsopcode.tbl. It stands * for the result pushed by JSOP_ITERNEXT. */ - JS_ASSERT(strcmp(lval + strlen(lval) - 9, " = ") == 0); + LOCAL_ASSERT(strcmp(lval + strlen(lval) - 9, " = ") == 0); const_cast(lval)[strlen(lval) - 9] = '\0'; LOCAL_ASSERT(ss->top >= 1); diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index cb9c52712e1..8143f5b5d22 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -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", "", 2, 0, 1, 0, JOF_UINT8) +OPDEF(JSOP_ITERNEXT, 77, "iternext", "", 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) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 6ffe74f5664..6b872cd5398 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -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; diff --git a/js/src/jsxdrapi.h b/js/src/jsxdrapi.h index b9f20f434b7..3b05a0152f2 100644 --- a/js/src/jsxdrapi.h +++ b/js/src/jsxdrapi.h @@ -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. diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 539f7f25810..b149a25a1c7 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -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); diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index 0c689a64398..5db2e55e2e8 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -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); diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 31d46143809..37288b30145 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -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])) diff --git a/js/src/methodjit/StubCalls.h b/js/src/methodjit/StubCalls.h index a59a77ba2ba..f9141dfaa64 100644 --- a/js/src/methodjit/StubCalls.h +++ b/js/src/methodjit/StubCalls.h @@ -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);