From 37724bb37815a5da29176a10c65b795bd64487f1 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Tue, 28 Oct 2008 10:49:06 -0700 Subject: [PATCH 1/2] Backed out changeset d4fe79372140 (bug 458851) due to persistent orange on TraceMonkey tinderboxes. --- js/src/jsemit.cpp | 111 ++++++++++++++---------- js/src/jsinterp.cpp | 201 ++++++++++++++++++++++++++++---------------- js/src/jsobj.cpp | 2 +- js/src/jsopcode.cpp | 65 ++++++-------- js/src/jsopcode.h | 4 +- js/src/jsopcode.tbl | 46 +++++----- js/src/jstracer.cpp | 132 ++++++++++++----------------- js/src/jstracer.h | 1 - js/src/jsxdrapi.h | 2 +- 9 files changed, 300 insertions(+), 264 deletions(-) diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index 3ead7dcced6..d6fba368dff 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -2365,14 +2365,14 @@ EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg, do_indexconst: { JSAtomListElement *ale; jsatomid atomIndex; - + ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList); if (!ale) return JS_FALSE; atomIndex = ALE_INDEX(ale); return EmitSlotIndexOp(cx, op, pn2->pn_slot, atomIndex, cg); } - + default:; } } @@ -3849,6 +3849,28 @@ EmitFunctionDefNop(JSContext *cx, JSCodeGenerator *cg, uintN index) js_Emit1(cx, cg, JSOP_NOP) >= 0; } +/* FIXME: 458851 -- that bug's patch should re-inline this into one place. */ +static JSBool +EmitForInLoopBody(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *stmt, + JSParseNode *body, intN noteIndex, ptrdiff_t jmp) +{ + /* Set the first srcnote offset so we can find the start of the loop body. */ + if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, CG_OFFSET(cg) - jmp)) + return JS_FALSE; + + /* Emit code for the loop body. */ + if (!js_EmitTree(cx, cg, body)) + return JS_FALSE; + + /* Set loop and enclosing "update" offsets, for continue. */ + do { + stmt->update = CG_OFFSET(cg); + } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL); + + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); + return JS_TRUE; +} + JSBool js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) { @@ -3932,7 +3954,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) cg2->staticDepth = cg->staticDepth + 1; cg2->parent = cg; - /* We metered the max scope depth when parsed the function. */ + /* We metered the max scope depth when parsed the function. */ JS_SCOPE_DEPTH_METERING(cg2->treeContext.maxScopeDepth = (uintN) -1); if (!js_EmitFunctionScript(cx, cg2, pn->pn_body)) { pn = NULL; @@ -4217,7 +4239,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) * destructuring for-in). */ JS_ASSERT(pn->pn_op == JSOP_ITER); - if (js_Emit2(cx, cg, JSOP_ITER, (uint8) pn->pn_iflags) < 0) + if (js_Emit2(cx, cg, PN_OP(pn), (uint8) pn->pn_iflags) < 0) return JS_FALSE; /* Annotate so the decompiler can find the loop-closing jump. */ @@ -4236,10 +4258,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) top = CG_OFFSET(cg); SET_STATEMENT_TOP(&stmtInfo, top); -#ifdef DEBUG - intN loopDepth = cg->stackDepth; -#endif - /* * Compile a JSOP_FOR* bytecode based on the left hand side. * @@ -4273,6 +4291,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) /* FALL THROUGH */ case TOK_NAME: + if (!EmitForInLoopBody(cx, cg, &stmtInfo, pn->pn_right, noteIndex, jmp)) + return JS_FALSE; + /* * Always annotate JSOP_FORLOCAL if given input of the form * 'for (let x in * o)' -- the decompiler must not hoist the @@ -4312,9 +4333,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (pn3->pn_slot >= 0) { if (pn3->pn_const) { JS_ASSERT(op == JSOP_FORLOCAL); - js_ReportCompileErrorNumber(cx, CG_TS(cg), pn3, JSREPORT_ERROR, - JSMSG_BAD_FOR_LEFTSIDE); - return JS_FALSE; + op = JSOP_FORCONST; } atomIndex = (jsatomid) pn3->pn_slot; EMIT_UINT16_IMM_OP(op, atomIndex); @@ -4333,6 +4352,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (!CheckSideEffects(cx, cg, pn3->pn_expr, &useful)) return JS_FALSE; if (!useful) { + if (!EmitForInLoopBody(cx, cg, &stmtInfo, pn->pn_right, noteIndex, jmp)) + return JS_FALSE; if (!EmitPropOp(cx, pn3, JSOP_FORPROP, cg, JS_FALSE)) return JS_FALSE; break; @@ -4343,10 +4364,19 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) destructuring_for: #endif default: - if (js_Emit1(cx, cg, JSOP_FORELEM) < 0) - return JS_FALSE; - JS_ASSERT(cg->stackDepth >= 3); - + /* + * We separate the first/next bytecode from the enumerator + * variable binding to avoid any side-effects in the index + * expression (e.g., for (x[i++] in {}) should not bind x[i] + * or increment i at all). + * + * At this point, JSOP_FORELEM (emitted after the loop body) + * has pushed the next value to iterate, but it is downstream + * of us in js_Emit* order, so we must adjust the stack depth + * manually. + */ + if ((uintN) ++cg->stackDepth > cg->maxStackDepth) + cg->maxStackDepth = cg->stackDepth; #if JS_HAS_DESTRUCTURING if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) { if (!EmitDestructuringOps(cx, cg, op, pn3)) @@ -4375,32 +4405,24 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) #endif if (!EmitElemOp(cx, pn3, JSOP_ENUMELEM, cg)) return JS_FALSE; + + if (!EmitForInLoopBody(cx, cg, &stmtInfo, pn->pn_right, noteIndex, jmp)) + return JS_FALSE; + + /* + * JSOP_FORELEM has nuses 1, ndefs 3, modeling the case where + * it pushes the next value and then true, to keep iterating. + * For symmetry here, we manually drop cg->stackDepth after to + * reflect the fact that we've emitted JSOP_ENUMELEM already. + */ + if (js_Emit1(cx, cg, JSOP_FORELEM) < 0) + return JS_FALSE; + JS_ASSERT(cg->stackDepth >= 3); + --cg->stackDepth; break; } - /* The stack should be balanced around the JSOP_FOR* opcode sequence. */ - JS_ASSERT(cg->stackDepth == loopDepth); - - /* Set the first srcnote offset so we can find the start of the loop body. */ - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, CG_OFFSET(cg) - jmp)) - return JS_FALSE; - - /* Emit code for the loop body. */ - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - - /* Set loop and enclosing "update" offsets, for continue. */ - stmt = &stmtInfo; - do { - stmt->update = CG_OFFSET(cg); - } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL); - - /* - * Fixup the goto that starts the loop to jump down to JSOP_NEXTITER. - */ - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); - if (js_Emit1(cx, cg, JSOP_NEXTITER) < 0) - return JS_FALSE; + /* Pop and test the loop condition generated by JSOP_FOR*. */ beq = EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg)); if (beq < 0) return JS_FALSE; @@ -4537,13 +4559,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (pn2->pn_type == TOK_IN) { /* - * JSOP_ENDITER must have a slot to save an exception thrown from - * the body of for-in loop when closing the iterator object, and - * fortunately it does: the slot that was set by JSOP_NEXTITER to - * the return value of iterator.next(). + * JSOP_ENDITER needs a slot to save an exception thrown from the + * body of for-in loop when closing the iterator object. */ - JS_ASSERT(js_CodeSpec[JSOP_ENDITER].nuses == 2); - if (!NewTryNote(cx, cg, JSTRY_ITER, cg->stackDepth, top, CG_OFFSET(cg)) || + JS_ASSERT(js_CodeSpec[JSOP_ENDITER].format & JOF_TMPSLOT); + if (!NewTryNote(cx, cg, JSTRY_ITER, cg->stackDepth, top, + CG_OFFSET(cg)) || js_Emit1(cx, cg, JSOP_ENDITER) < 0) { return JS_FALSE; } @@ -5835,7 +5856,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) argc = pn->pn_count - 1; if (js_Emit3(cx, cg, PN_OP(pn), ARGC_HI(argc), ARGC_LO(argc)) < 0) return JS_FALSE; - if (PN_OP(pn) == JSOP_EVAL) + if (PN_OP(pn) == JSOP_EVAL) EMIT_UINT16_IMM_OP(JSOP_LINENO, pn->pn_pos.begin.lineno); break; } diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 916faadcc86..48007a73f63 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -2002,6 +2002,8 @@ js_TraceOpcode(JSContext *cx, jsint len) prevop = (JSOp) regs->pc[-len]; ndefs = js_CodeSpec[prevop].ndefs; if (ndefs != 0) { + if (prevop == JSOP_FORELEM && regs->sp[-1] == JSVAL_FALSE) + --ndefs; for (n = -ndefs; n < 0; n++) { char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n], NULL); @@ -3190,88 +3192,130 @@ js_Interpret(JSContext *cx) END_CASE(JSOP_IN) BEGIN_CASE(JSOP_ITER) - JS_ASSERT(regs.sp > StackBase(fp)); flags = regs.pc[1]; + JS_ASSERT(regs.sp > StackBase(fp)); if (!js_ValueToIterator(cx, flags, ®s.sp[-1])) goto error; - LOAD_INTERRUPT_HANDLER(cx); JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1])); - PUSH(JSVAL_VOID); END_CASE(JSOP_ITER) - BEGIN_CASE(JSOP_NEXTITER) - JS_ASSERT(regs.sp - 2 >= StackBase(fp)); - JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-2])); - if (!js_CallIteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[-2]), ®s.sp[-1])) - goto error; - LOAD_INTERRUPT_HANDLER(cx); - PUSH(BOOLEAN_TO_JSVAL(regs.sp[-1] != JSVAL_HOLE)); - TRACE_0(IteratorNextComplete); - END_CASE(JSOP_NEXTITER) - - BEGIN_CASE(JSOP_ENDITER) + BEGIN_CASE(JSOP_FORPROP) /* - * Decrease the stack pointer even when !ok -- see comments in the - * exception capturing code for details. + * Handle JSOP_FORPROP first, so the cost of the goto do_forinloop + * is not paid for the more common cases. */ - JS_ASSERT(regs.sp - 2 >= StackBase(fp)); - ok = js_CloseIterator(cx, regs.sp[-2]); - regs.sp -= 2; - if (!ok) - goto error; - END_CASE(JSOP_ENDITER) - - BEGIN_CASE(JSOP_FORARG) - JS_ASSERT(regs.sp - 2 >= StackBase(fp)); - slot = GET_ARGNO(regs.pc); - JS_ASSERT(slot < fp->fun->nargs); - fp->argv[slot] = regs.sp[-1]; - END_CASE(JSOP_FORARG) - - BEGIN_CASE(JSOP_FORLOCAL) - JS_ASSERT(regs.sp - 2 >= StackBase(fp)); - slot = GET_SLOTNO(regs.pc); - JS_ASSERT(slot < fp->script->nslots); - vp = &fp->slots[slot]; - GC_POKE(cx, *vp); - *vp = regs.sp[-1]; - END_CASE(JSOP_FORLOCAL) + LOAD_ATOM(0); + id = ATOM_TO_JSID(atom); + i = -2; + goto do_forinloop; BEGIN_CASE(JSOP_FORNAME) - JS_ASSERT(regs.sp - 2 >= StackBase(fp)); LOAD_ATOM(0); id = ATOM_TO_JSID(atom); - if (!js_FindProperty(cx, id, &obj, &obj2, &prop)) - goto error; - if (prop) - OBJ_DROP_PROPERTY(cx, obj2, prop); - ok = OBJ_SET_PROPERTY(cx, obj, id, ®s.sp[-1]); - if (!ok) - goto error; - END_CASE(JSOP_FORNAME) + /* FALL THROUGH */ - BEGIN_CASE(JSOP_FORPROP) - JS_ASSERT(regs.sp - 2 >= StackBase(fp)); - LOAD_ATOM(0); - id = ATOM_TO_JSID(atom); - FETCH_OBJECT(cx, -1, lval, obj); - ok = OBJ_SET_PROPERTY(cx, obj, id, ®s.sp[-2]); - if (!ok) - goto error; - regs.sp--; - END_CASE(JSOP_FORPROP) + BEGIN_CASE(JSOP_FORARG) + BEGIN_CASE(JSOP_FORCONST) + BEGIN_CASE(JSOP_FORLOCAL) + /* + * These bytecodes don't require any lval computation here, + * because they address slots on the stack (in fp->args or + * fp->slots). + */ + /* FALL THROUGH */ BEGIN_CASE(JSOP_FORELEM) /* - * JSOP_FORELEM simply dups the property identifier at top of stack - * and lets the subsequent JSOP_ENUMELEM opcode sequence handle the - * left-hand side expression evaluation and assignment. This opcode - * exists solely to help the decompiler. + * JSOP_FORELEM simply initializes or updates the iteration state + * and leaves the index expression evaluation and assignment to the + * enumerator until after the next property has been acquired, via + * a JSOP_ENUMELEM bytecode. */ - JS_ASSERT(regs.sp - 2 >= StackBase(fp)); - rval = FETCH_OPND(-1); - PUSH(rval); - END_CASE(JSOP_FORELEM) + i = -1; + + do_forinloop: + /* + * Reach under the top of stack to find our property iterator, a + * JSObject that contains the iteration state. + */ + JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[i])); + if (!js_CallIteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[i]), &rval)) + goto error; + if (rval == JSVAL_HOLE) { + rval = JSVAL_FALSE; +#ifdef JS_TRACER + if (TRACE_RECORDER(cx)) { + js_AbortRecording(cx, "Untraceable for-in loop"); + ENABLE_TRACER(0); + } +#endif + goto end_forinloop; + } + + switch (op) { + case JSOP_FORARG: + slot = GET_ARGNO(regs.pc); + JS_ASSERT(slot < fp->fun->nargs); + fp->argv[slot] = rval; + break; + + case JSOP_FORCONST: + /* Don't update the const slot. */ + break; + + case JSOP_FORLOCAL: + slot = GET_SLOTNO(regs.pc); + JS_ASSERT(slot < fp->script->nslots); + vp = &fp->slots[slot]; + GC_POKE(cx, *vp); + *vp = rval; + break; + + case JSOP_FORELEM: + /* FORELEM is not a SET operation, it's more like BINDNAME. */ + PUSH_OPND(rval); + break; + + case JSOP_FORPROP: + /* + * We fetch object here to ensure that the iterator is called + * even if lval is null or undefined that throws in + * FETCH_OBJECT. See bug 372331. + */ + FETCH_OBJECT(cx, -1, lval, obj); + goto set_for_property; + + default: + JS_ASSERT(op == JSOP_FORNAME); + + /* + * We find property here after the iterator call to ensure + * that we take into account side effects of the iterator + * call. See bug 372331. + */ + if (!js_FindProperty(cx, id, &obj, &obj2, &prop)) + goto error; + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, prop); + + set_for_property: + /* Set the variable obj[id] to refer to rval. */ + fp->flags |= JSFRAME_ASSIGNING; + ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); + fp->flags &= ~JSFRAME_ASSIGNING; + if (!ok) + goto error; + break; + } + + /* Push true to keep looping through properties. */ + rval = JSVAL_TRUE; + + end_forinloop: + regs.sp += i + 1; + PUSH_OPND(rval); + len = js_CodeSpec[op].length; + DO_NEXT_OP(len); BEGIN_CASE(JSOP_DUP) JS_ASSERT(regs.sp > StackBase(fp)); @@ -6668,6 +6712,17 @@ js_Interpret(JSContext *cx) } END_CASE(JSOP_LEAVEBLOCK) + BEGIN_CASE(JSOP_ENDITER) + /* + * Decrease the stack pointer even when !ok, see comments in the + * exception capturing code for details. + */ + ok = js_CloseIterator(cx, regs.sp[-1]); + regs.sp--; + if (!ok) + goto error; + END_CASE(JSOP_ENDITER) + #if JS_HAS_GENERATORS BEGIN_CASE(JSOP_GENERATOR) ASSERT_NOT_THROWING(cx); @@ -6768,9 +6823,11 @@ js_Interpret(JSContext *cx) L_JSOP_DEFXMLNS: # endif + L_JSOP_UNUSED74: + L_JSOP_UNUSED76: + L_JSOP_UNUSED77: L_JSOP_UNUSED78: L_JSOP_UNUSED79: - L_JSOP_UNUSED103: L_JSOP_UNUSED131: L_JSOP_UNUSED201: L_JSOP_UNUSED202: @@ -6779,8 +6836,6 @@ js_Interpret(JSContext *cx) L_JSOP_UNUSED205: L_JSOP_UNUSED206: L_JSOP_UNUSED207: - L_JSOP_UNUSED208: - L_JSOP_UNUSED209: L_JSOP_UNUSED219: L_JSOP_UNUSED226: @@ -6929,14 +6984,12 @@ js_Interpret(JSContext *cx) case JSTRY_ITER: /* - * This is similar to JSOP_ENDITER in the interpreter loop, - * except the code now uses the stack slot normally used by - * JSOP_NEXTITER, namely regs.sp[-1] before the regs.sp -= 2 - * adjustment and regs.sp[1] after, to save and restore the - * pending exception. + * This is similar to JSOP_ENDITER in the interpreter loop + * except the code now uses a reserved stack slot to save and + * restore the exception. */ JS_ASSERT(*regs.pc == JSOP_ENDITER); - regs.sp[-1] = cx->exception; + PUSH(cx->exception); cx->throwing = JS_FALSE; ok = js_CloseIterator(cx, regs.sp[-2]); regs.sp -= 2; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index e143bf03ecf..17b4fae86ec 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -3414,7 +3414,7 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, format = cs->format; if (JOF_MODE(format) != JOF_NAME) flags |= JSRESOLVE_QUALIFIED; - if ((format & (JOF_SET | JOF_FOR)) || + if ((format & JOF_ASSIGNING) || (cx->fp->flags & JSFRAME_ASSIGNING)) { flags |= JSRESOLVE_ASSIGNING; } else { diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 8c13cdeebc2..d60d5551fb3 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -1740,7 +1740,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) static const char exception_cookie[] = "/*EXCEPTION*/"; static const char retsub_pc_cookie[] = "/*RETSUB_PC*/"; - static const char iter_cookie[] = "/*ITER*/"; static const char forelem_cookie[] = "/*FORELEM*/"; static const char with_cookie[] = "/*WITH*/"; static const char dot_format[] = "%s.%s"; @@ -1761,7 +1760,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL) #define DECOMPILE_CODE(pc,nb) if (!Decompile(ss, pc, nb, JSOP_NOP)) return NULL #define NEXT_OP(pc) (((pc) + (len) == endpc) ? nextop : pc[len]) -#define TOP_STR() GetStr(ss, ss->top - 1) #define POP_STR() PopStr(ss, op) #define POP_STR_PREC(prec) PopStrPrec(ss, prec) @@ -2504,6 +2502,14 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) sn = NULL; break; + case JSOP_ENDITER: + sn = js_GetSrcNote(jp->script, pc); + todo = -2; + if (sn && SN_TYPE(sn) == SRC_HIDDEN) + break; + (void) PopOff(ss, op); + break; + case JSOP_ENTERWITH: LOCAL_ASSERT(!js_GetSrcNote(jp->script, pc)); rval = POP_STR(); @@ -2877,7 +2883,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) pos = ss->top; while (pos != 0) { op = (JSOp) ss->opcodes[--pos]; - if (op == JSOP_ENTERBLOCK) + if (op != JSOP_FORLOCAL) break; } JS_ASSERT(op == JSOP_ENTERBLOCK); @@ -2887,7 +2893,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * in the single string of accumulated |for| heads and optional * final |if (condition)|. */ - forpos = pos + 2; + forpos = pos + 1; LOCAL_ASSERT(forpos < ss->top); /* @@ -2959,25 +2965,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) js_printf(jp, "\t%s %s;\n", js_throw_str, rval); break; - case JSOP_ITER: - foreach = (pc[1] & (JSITER_FOREACH | JSITER_KEYVALUE)) == - JSITER_FOREACH; - todo = SprintCString(&ss->sprinter, iter_cookie); - break; - - case JSOP_NEXTITER: - JS_NOT_REACHED("JSOP_NEXTITER"); - break; - - case JSOP_ENDITER: - sn = js_GetSrcNote(jp->script, pc); - todo = -2; - if (sn && SN_TYPE(sn) == SRC_HIDDEN) - break; - (void) PopOff(ss, op); - (void) PopOff(ss, op); - break; - case JSOP_GOTO: case JSOP_GOTOX: sn = js_GetSrcNote(jp->script, pc); @@ -2986,22 +2973,19 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) /* * The loop back-edge carries +1 stack balance, for the * flag processed by JSOP_IFNE. We do not decompile the - * JSOP_IFNE, and instead push the left-hand side of 'in' - * after the loop edge in this stack slot (the JSOP_FOR* - * opcodes' decompilers do this pushing). + * JSOP_IFNE, and instead pass the left-hand side of 'in' + * along the loop edge in this pushed stack slot. */ cond = GetJumpOffset(pc, pc); next = js_GetSrcNoteOffset(sn, 0); tail = js_GetSrcNoteOffset(sn, 1); - JS_ASSERT(pc[cond] == JSOP_NEXTITER); + DECOMPILE_CODE(pc + cond, tail - cond); DECOMPILE_CODE(pc + oplen, next - oplen); - lval = POP_STR(); if (ss->inArrayInit || ss->inGenExp) { - (void) PopOff(ss, JSOP_NOP); - rval = TOP_STR(); - LOCAL_ASSERT(ss->top >= 2); - if (ss->opcodes[ss->top - 2] == JSOP_FORLOCAL) { - ss->sprinter.offset = ss->offsets[ss->top - 1] - PAREN_SLOP; + lval = POP_STR(); + rval = POP_STR(); + if (ss->opcodes[ss->top - 1] == JSOP_FORLOCAL) { + ss->sprinter.offset -= PAREN_SLOP; if (Sprint(&ss->sprinter, " %s (%s in %s)", foreach ? js_for_each_str : js_for_str, lval, rval) < 0) { @@ -3016,7 +3000,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) */ todo = ss->offsets[ss->top - 1]; } else { - LOCAL_ASSERT(ss->opcodes[ss->top - 2] == JSOP_ENTERBLOCK); + LOCAL_ASSERT(ss->opcodes[ss->top - 1] == JSOP_ENTERBLOCK); todo = Sprint(&ss->sprinter, " %s (%s in %s)", foreach ? js_for_each_str : js_for_str, lval, rval); @@ -3029,7 +3013,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * As above, rval or an extension of it must remain * stacked during loop body decompilation. */ - rval = GetStr(ss, ss->top - 2); + lval = POP_STR(); + rval = GetStr(ss, ss->top - 1); js_printf(jp, "\t%s (%s in %s) {\n", foreach ? js_for_each_str : js_for_str, lval, rval); @@ -3237,6 +3222,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) i = GET_ARGNO(pc); goto do_forvarslot; + case JSOP_FORCONST: case JSOP_FORLOCAL: sn = js_GetSrcNote(jp->script, pc); if (!IsVarSlot(jp, pc, &i)) { @@ -3284,6 +3270,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) break; case JSOP_FORELEM: + LOCAL_ASSERT(pc[1] == JSOP_IFNE || pc[1] == JSOP_IFNEX); todo = SprintCString(&ss->sprinter, forelem_cookie); break; @@ -4549,6 +4536,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) inXML = JS_FALSE; break; + case JSOP_ITER: + foreach = (pc[1] & (JSITER_FOREACH | JSITER_KEYVALUE)) == + JSITER_FOREACH; + todo = -2; + break; + case JSOP_TOXML: case JSOP_CALLXMLNAME: case JSOP_XMLNAME: @@ -4646,9 +4639,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) #undef inXML #undef DECOMPILE_CODE #undef NEXT_OP -#undef TOP_STR #undef POP_STR -#undef POP_STR_PREC #undef LOCAL_ASSERT #undef ATOM_IS_IDENTIFIER #undef GET_QUOTE_AND_FMT diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index 6ce8a4093f3..30965f19e7c 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -97,7 +97,7 @@ typedef enum JSOp { #define JOF_INC (2U<<10) /* increment (++, not --) opcode */ #define JOF_INCDEC (3U<<10) /* increment or decrement opcode */ #define JOF_POST (1U<<12) /* postorder increment or decrement */ -#define JOF_FOR (1U<<13) /* for-in property op (akin to JOF_SET) */ +#define JOF_FOR (1U<<13) /* for-in property op */ #define JOF_ASSIGNING JOF_SET /* hint for JSClass.resolve, used for ops that do simplex assignment */ #define JOF_DETECTING (1U<<14) /* object detection for JSNewResolveOp */ @@ -118,6 +118,8 @@ typedef enum JSOp { #define JOF_TMPSLOT_SHIFT 22 #define JOF_TMPSLOT_MASK (JS_BITMASK(2) << JOF_TMPSLOT_SHIFT) +#define JOF_RETVAL (1U<<24) /* op leaves jsval return value on stack */ + /* Shorthands for type from format and type from opcode. */ #define JOF_TYPE(fmt) ((fmt) & JOF_TYPEMASK) #define JOF_OPTYPE(op) JOF_TYPE(js_CodeSpec[op].format) diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index 9e9b9cd82d0..168ba11d52a 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -111,8 +111,8 @@ OPDEF(JSOP_IFNE, 8, "ifne", NULL, 3, 1, 0, 0, JOF_JUMP|J OPDEF(JSOP_ARGUMENTS, 9, js_arguments_str, js_arguments_str, 1, 0, 1, 18, JOF_BYTE) /* ECMA-compliant for-in loop with argument or local loop control. */ -OPDEF(JSOP_FORARG, 10, "forarg", NULL, 3, 2, 2, 19, JOF_QARG|JOF_NAME|JOF_FOR) -OPDEF(JSOP_FORLOCAL, 11, "forlocal", NULL, 3, 2, 2, 19, JOF_LOCAL|JOF_NAME|JOF_FOR) +OPDEF(JSOP_FORARG, 10, "forarg", NULL, 3, 0, 1, 19, JOF_QARG|JOF_NAME|JOF_FOR) +OPDEF(JSOP_FORLOCAL, 11, "forlocal", NULL, 3, 0, 1, 19, JOF_LOCAL|JOF_NAME|JOF_FOR) /* More longstanding bytecodes. */ OPDEF(JSOP_DUP, 12, "dup", NULL, 1, 1, 2, 0, JOF_BYTE) @@ -163,7 +163,7 @@ OPDEF(JSOP_SETPROP, 54, "setprop", NULL, 3, 2, 1, 3, JOF_ATOM|J OPDEF(JSOP_GETELEM, 55, "getelem", NULL, 1, 2, 1, 18, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) OPDEF(JSOP_SETELEM, 56, "setelem", NULL, 1, 3, 1, 3, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING) OPDEF(JSOP_CALLNAME, 57, "callname", NULL, 3, 0, 2, 19, JOF_ATOM|JOF_NAME|JOF_CALLOP) -OPDEF(JSOP_CALL, 58, "call", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE) +OPDEF(JSOP_CALL, 58, "call", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE|JOF_RETVAL) OPDEF(JSOP_NAME, 59, "name", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_NAME) OPDEF(JSOP_DOUBLE, 60, "double", NULL, 3, 0, 1, 16, JOF_ATOM) OPDEF(JSOP_STRING, 61, "string", NULL, 3, 0, 1, 19, JOF_ATOM) @@ -184,25 +184,13 @@ OPDEF(JSOP_LOOKUPSWITCH, 71, "lookupswitch", NULL, -1, 1, 0, 0, JOF_LOOKUP OPDEF(JSOP_STRICTEQ, 72, "stricteq", "===", 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC) OPDEF(JSOP_STRICTNE, 73, "strictne", "!==", 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC) +OPDEF(JSOP_UNUSED74, 74, "unused74", NULL, 1, 0, 0, 0, JOF_BYTE) + /* Variant of JSOP_NULL for default (global) |this| parameter pushing. */ -OPDEF(JSOP_NULLTHIS, 74, js_null_str, js_null_str, 1, 0, 1, 19, JOF_BYTE) - -/* - * JSOP_ITER sets up a for-in or for-each-in loop using the JSITER_* flag bits - * in this op's uint8 immediate operand. It replaces the top of stack object - * with an iterator for that object, and pushes a slot used by JSOP_NEXTITER. - * - * JSOP_NEXTITER stores the next iterated value in the top of stack slot which - * was allocated by JSOP_ITER and pushes true, or stores JSVAL_HOLE and pushes - * false. It is followed immediately by JSOP_IFNE{,X}. - * - * JSOP_ENDITER cleans up after the loop. It uses the slot above the iterator - * for temporary GC rooting. - */ -OPDEF(JSOP_ITER, 75, "iter", NULL, 2, 1, 2, 0, JOF_UINT8) -OPDEF(JSOP_NEXTITER, 76, "nextiter", NULL, 1, 2, 3, 0, JOF_BYTE) -OPDEF(JSOP_ENDITER, 77, "enditer", NULL, 1, 2, 0, 0, JOF_BYTE) +OPDEF(JSOP_NULLTHIS, 75, js_null_str, js_null_str, 1, 0, 1, 19, JOF_BYTE) +OPDEF(JSOP_UNUSED76, 76, "unused76", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_UNUSED77, 77, "unused77", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_UNUSED78, 78, "unused78", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_UNUSED79, 79, "unused79", NULL, 1, 0, 0, 0, JOF_BYTE) @@ -246,12 +234,16 @@ OPDEF(JSOP_DECLOCAL, 100,"declocal", NULL, 3, 0, 1, 15, JOF_LOCAL| OPDEF(JSOP_LOCALINC, 101,"localinc", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_INC|JOF_POST) OPDEF(JSOP_LOCALDEC, 102,"localdec", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_DEC|JOF_POST) -OPDEF(JSOP_UNUSED103, 103,"unused103", NULL, 1, 0, 0, 0, JOF_BYTE) +/* + * Initialize for-in iterator using the JSITER_* flag bits in this op's uint8 + * immediate operand. See also JSOP_ENDITER. + */ +OPDEF(JSOP_ITER, 103,"iter", NULL, 2, 1, 1, 0, JOF_UINT8) /* ECMA-compliant for/in ops. */ -OPDEF(JSOP_FORNAME, 104,"forname", NULL, 3, 2, 2, 19, JOF_ATOM|JOF_NAME|JOF_FOR) -OPDEF(JSOP_FORPROP, 105,"forprop", NULL, 3, 3, 2, 18, JOF_ATOM|JOF_PROP|JOF_FOR) -OPDEF(JSOP_FORELEM, 106,"forelem", NULL, 1, 2, 3, 18, JOF_BYTE |JOF_ELEM|JOF_FOR) +OPDEF(JSOP_FORNAME, 104,"forname", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_NAME|JOF_FOR) +OPDEF(JSOP_FORPROP, 105,"forprop", NULL, 3, 1, 1, 18, JOF_ATOM|JOF_PROP|JOF_FOR) +OPDEF(JSOP_FORELEM, 106,"forelem", NULL, 1, 1, 3, 18, JOF_BYTE |JOF_ELEM|JOF_FOR) OPDEF(JSOP_POPN, 107,"popn", NULL, 3, -1, 0, 0, JOF_UINT16) /* ECMA-compliant assignment ops. */ @@ -482,12 +474,12 @@ OPDEF(JSOP_UNUSED204, 204,"unused204", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_UNUSED205, 205,"unused205", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_UNUSED206, 206,"unused206", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_UNUSED207, 207,"unused207", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_UNUSED208, 208,"unused208", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_UNUSED209, 209,"unused209", NULL, 1, 0, 0, 0, JOF_BYTE) /* - * Generator and array comprehension support. + * Iterator, generator, and array comprehension support. */ +OPDEF(JSOP_FORCONST, 208,"forconst", NULL, 3, 0, 1, 19, JOF_LOCAL|JOF_NAME|JOF_FOR) +OPDEF(JSOP_ENDITER, 209,"enditer", NULL, 1, 1, 0, 0, JOF_BYTE|JOF_TMPSLOT) OPDEF(JSOP_GENERATOR, 210,"generator", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_YIELD, 211,"yield", NULL, 1, 1, 1, 1, JOF_BYTE) OPDEF(JSOP_ARRAYPUSH, 212,"arraypush", NULL, 3, 1, 0, 3, JOF_LOCAL) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index b98419dc81a..1a8a0906e56 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -165,7 +165,7 @@ jitstats_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) #define JITSTAT(x) case STAT ## x ## ID: result = jitstats.x; break; #include "jitstats.tbl" #undef JITSTAT - default: + default: *vp = JSVAL_VOID; return JS_TRUE; } @@ -1695,13 +1695,13 @@ static bool js_IsLoopEdge(jsbytecode* pc, jsbytecode* header) { switch (*pc) { - case JSOP_IFEQ: - case JSOP_IFNE: + case JSOP_IFEQ: + case JSOP_IFNE: return ((pc + GET_JUMP_OFFSET(pc)) == header); - case JSOP_IFEQX: - case JSOP_IFNEX: + case JSOP_IFEQX: + case JSOP_IFNEX: return ((pc + GET_JUMPX_OFFSET(pc)) == header); - default: + default: JS_ASSERT((*pc == JSOP_AND) || (*pc == JSOP_ANDX) || (*pc == JSOP_OR) || (*pc == JSOP_ORX)); } @@ -1788,7 +1788,7 @@ TraceRecorder::snapshot(ExitType exitType) bool resumeAfter = (pendingTraceableNative && JSTN_ERRTYPE(pendingTraceableNative) == FAIL_JSVAL); if (resumeAfter) { - JS_ASSERT(*pc == JSOP_CALL || *pc == JSOP_NEXTITER); + JS_ASSERT(cs.format & JOF_RETVAL); pc += cs.length; regs->pc = pc; MUST_FLOW_THROUGH(restore_pc); @@ -1815,11 +1815,10 @@ TraceRecorder::snapshot(ExitType exitType) ); JS_ASSERT(unsigned(m - typemap) == ngslots + stackSlots); - /* If we are capturing the stack state on a specific instruction, the value on or near - the top of the stack is a boxed value. Either pc[-cs.length] is JSOP_NEXTITER and we - want one below top of stack, or else it's JSOP_CALL and we want top of stack. */ + /* If we are capturing the stack state on a JOF_RETVAL instruction, the value on top of + the stack is a boxed value. */ if (resumeAfter) { - m[(pc[-cs.length] == JSOP_NEXTITER) ? -2 : -1] = JSVAL_BOXED; + m[-1] = JSVAL_BOXED; /* Now restore the the original pc (after which early returns are ok). */ MUST_FLOW_LABEL(restore_pc); @@ -1959,8 +1958,7 @@ TraceRecorder::checkType(jsval& v, uint8 t, jsval*& stage_val, LIns*& stage_ins, typeChar[t]);); } #endif - debug_only_v(printf("checkType(tag=%d, t=%d) stage_count=%d\n", - (int) JSVAL_TAG(v), t, stage_count);) + debug_only_v(printf("checkType(tag=%d, t=%d) stage_count=%d\n", JSVAL_TAG(v), t, stage_count);) return JSVAL_TAG(v) == t; } @@ -2376,17 +2374,17 @@ TraceRecorder::flipIf(jsbytecode* pc, bool& cond) { if (js_IsLoopEdge(pc, (jsbytecode*)fragment->root->ip)) { switch (*pc) { - case JSOP_IFEQ: - case JSOP_IFEQX: + case JSOP_IFEQ: + case JSOP_IFEQX: if (!cond) return; break; - case JSOP_IFNE: - case JSOP_IFNEX: + case JSOP_IFNE: + case JSOP_IFNEX: if (cond) return; break; - default: + default: JS_NOT_REACHED("flipIf"); } /* We are about to walk out of the loop, so terminate it with @@ -2958,7 +2956,7 @@ js_RecordLoopEdge(JSContext* cx, TraceRecorder* r, uintN& inlineCallCount) } Fragment* old; switch (lr->exitType) { - case LOOP_EXIT: + case LOOP_EXIT: /* If the inner tree exited on an unknown loop exit, grow the tree around it. */ if (innermostNestedGuard) { js_AbortRecording(cx, "Inner tree took different side exit, abort recording"); @@ -3495,15 +3493,15 @@ monitor_loop: of a branch exit), or the tree nested around the tree we exited from (in case of the tree call guard). */ switch (lr->exitType) { - case UNSTABLE_LOOP_EXIT: + case UNSTABLE_LOOP_EXIT: return js_AttemptToStabilizeTree(cx, lr, match, NULL); - case BRANCH_EXIT: + case BRANCH_EXIT: return js_AttemptToExtendTree(cx, lr, NULL, NULL); - case LOOP_EXIT: + case LOOP_EXIT: if (innermostNestedGuard) return js_AttemptToExtendTree(cx, innermostNestedGuard, lr, NULL); return false; - default: + default: /* No, this was an unusual exit (i.e. out of memory/GC), so just resume interpretation. */ return false; } @@ -5953,28 +5951,28 @@ TraceRecorder::record_FastNativeCallComplete() function, which might have side effects. Instead, snapshot(), which is invoked from unbox_jsval(), will see that - we are currently parked on a traceable native's JSOP_CALL instruction, - and it will advance the pc to restore by the length of the current - opcode, and indicate in the type map that the element on top of the - stack is a boxed value which doesn't need to be boxed if the type guard - generated by unbox_jsval() fails. */ - JS_ASSERT(*cx->fp->regs->pc == JSOP_CALL); + we are currently parked on a JOF_RETVAL instruction, and it will advance + the pc to restore by the length of the current opcode, and indicate in + the type map that the element on top of the stack is a boxed value which + doesn't need to be boxed if the type guard generated by unbox_jsval() + fails. */ + JS_ASSERT(js_CodeSpec[*cx->fp->regs->pc].format & JOF_RETVAL); jsval& v = stackval(-1); LIns* v_ins = get(&v); bool ok = true; switch (JSTN_ERRTYPE(pendingTraceableNative)) { - case FAIL_JSVAL: + case FAIL_JSVAL: ok = unbox_jsval(v, v_ins); if (ok) set(&v, v_ins); break; - case FAIL_NEG: + case FAIL_NEG: /* Already added i2f in functionCall. */ JS_ASSERT(JSVAL_IS_NUMBER(v)); break; - default: + default: /* Convert the result to double if the builtin returns int32. */ if (JSVAL_IS_NUMBER(v) && (pendingTraceableNative->builtin->_argtypes & 3) == nanojit::ARGSIZE_LO) { @@ -6488,62 +6486,41 @@ TraceRecorder::record_JSOP_ITER() LIns* v_ins = lir->insCall(&js_FastValueToIterator_ci, args); guard(false, lir->ins_eq0(v_ins), MISMATCH_EXIT); set(&v, v_ins); - - LIns* void_ins = INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_VOID)); - stack(0, void_ins); return true; } ABORT_TRACE("for-in on a primitive value"); } -static JSTraceableNative js_FastCallIteratorNext_tn = { - NULL, // JSFastNative native; - &js_FastCallIteratorNext_ci, // const nanojit::CallInfo *builtin; - "C", // const char *prefix; - "o", // const char *argtypes; - FAIL_JSVAL // uintN flags; -}; - bool -TraceRecorder::record_JSOP_NEXTITER() +TraceRecorder::forInLoop(jsval* vp) { - jsval& iterobj_val = stackval(-2); + jsval& iterobj_val = stackval(-1); if (!JSVAL_IS_PRIMITIVE(iterobj_val)) { LIns* args[] = { get(&iterobj_val), cx_ins }; LIns* v_ins = lir->insCall(&js_FastCallIteratorNext_ci, args); guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), OOM_EXIT); LIns* flag_ins = lir->ins_eq0(lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_HOLE))); - stack(-1, v_ins); + LIns* iter_ins = get(vp); + jsval expected = JSVAL_IS_VOID(*vp) ? JSVAL_STRING : JSVAL_TAG(*vp); + if (!box_jsval(expected, iter_ins)) + return false; + iter_ins = lir->ins_choose(flag_ins, v_ins, iter_ins); + if (!unbox_jsval(expected, iter_ins)) + return false; + set(vp, iter_ins); stack(0, flag_ins); - - pendingTraceableNative = &js_FastCallIteratorNext_tn; return true; } ABORT_TRACE("for-in on a primitive value"); } -bool -TraceRecorder::record_IteratorNextComplete() -{ - JS_ASSERT(pendingTraceableNative); - JS_ASSERT(*cx->fp->regs->pc == JSOP_NEXTITER); - - jsval& v = stackval(-2); - LIns* v_ins = get(&v); - if (unbox_jsval(v, v_ins)) { - set(&v, v_ins); - return true; - } - return false; -} - bool TraceRecorder::record_JSOP_ENDITER() { - LIns* args[] = { stack(-2), cx_ins }; + LIns* args[] = { stack(-1), cx_ins }; LIns* ok_ins = lir->insCall(&js_CloseIterator_ci, args); guard(false, lir->ins_eq0(ok_ins), MISMATCH_EXIT); return true; @@ -6553,11 +6530,7 @@ bool TraceRecorder::record_JSOP_FORNAME() { jsval* vp; - if (name(vp)) { - set(vp, stack(-1)); - return true; - } - return false; + return name(vp) && forInLoop(vp); } bool @@ -6569,19 +6542,25 @@ TraceRecorder::record_JSOP_FORPROP() bool TraceRecorder::record_JSOP_FORELEM() { - return record_JSOP_DUP(); + return false; } bool TraceRecorder::record_JSOP_FORARG() { - return record_JSOP_SETARG(); + return forInLoop(&argval(GET_ARGNO(cx->fp->regs->pc))); } bool TraceRecorder::record_JSOP_FORLOCAL() { - return record_JSOP_SETLOCAL(); + return forInLoop(&varval(GET_SLOTNO(cx->fp->regs->pc))); +} + +bool +TraceRecorder::record_JSOP_FORCONST() +{ + return false; } bool @@ -6656,8 +6635,7 @@ TraceRecorder::record_JSOP_IN() // Expect what we see at trace recording time (hit or miss) to be the same // when executing the trace. Use a builtin helper for named properties, as - // the for-in tracing code does. First, handle indexes in dense arrays as a - // special case. + // forInLoop does. First, handle indexes in dense arrays as a special case. JSObject* obj = JSVAL_TO_OBJECT(rval); LIns* obj_ins = get(&rval); @@ -7657,9 +7635,11 @@ js_DumpPeerStability(Fragmento* frago, const void* ip) #define UNUSED(n) bool TraceRecorder::record_JSOP_UNUSED##n() { return false; } +UNUSED(74) +UNUSED(76) +UNUSED(77) UNUSED(78) UNUSED(79) -UNUSED(103) UNUSED(131) UNUSED(201) UNUSED(202) @@ -7668,7 +7648,5 @@ UNUSED(204) UNUSED(205) UNUSED(206) UNUSED(207) -UNUSED(208) -UNUSED(209) UNUSED(219) UNUSED(226) diff --git a/js/src/jstracer.h b/js/src/jstracer.h index cd0ebf7aa00..15b9e783051 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -378,7 +378,6 @@ public: bool record_SetPropMiss(JSPropCacheEntry* entry); bool record_DefLocalFunSetSlot(uint32 slot, JSObject* obj); bool record_FastNativeCallComplete(); - bool record_IteratorNextComplete(); nanojit::Fragment* getOuterToBlacklist() { return outerToBlacklist; } void deepAbort() { deepAborted = true; } diff --git a/js/src/jsxdrapi.h b/js/src/jsxdrapi.h index 566698649b7..31f05fd4025 100644 --- a/js/src/jsxdrapi.h +++ b/js/src/jsxdrapi.h @@ -204,7 +204,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 - 36) +#define JSXDR_BYTECODE_VERSION (0xb973c0de - 35) /* * Library-private functions. From 5258c4d28803d163f2493ff24c9097b1585b1b2a Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Tue, 28 Oct 2008 11:52:38 -0700 Subject: [PATCH 2/2] Bug 461723 - TM: "Assertion failure: (m != JSVAL_INT) || isInt32(*vp)" with "(0 + void 0) && 0". r=gal. --- js/src/jstracer.cpp | 10 ++++++---- js/src/trace-test.js | 6 ++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 1a8a0906e56..a728fc69393 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -4269,12 +4269,14 @@ TraceRecorder::binary(LOpcode op) rightNumber = true; } } - if (l == JSVAL_VOID) { - a = lir->insImmq(0); + if (JSVAL_TAG(l) == JSVAL_BOOLEAN) { + LIns* args[] = { a, cx_ins }; + a = lir->insCall(&js_BooleanToNumber_ci, args); leftNumber = true; } - if (r == JSVAL_VOID) { - b = lir->insImmq(0); + if (JSVAL_TAG(r) == JSVAL_BOOLEAN) { + LIns* args[] = { b, cx_ins }; + b = lir->insCall(&js_BooleanToNumber_ci, args); rightNumber = true; } if (leftNumber && rightNumber) { diff --git a/js/src/trace-test.js b/js/src/trace-test.js index a1551016486..d1ea7098ba6 100644 --- a/js/src/trace-test.js +++ b/js/src/trace-test.js @@ -2216,6 +2216,12 @@ this.__proto__.a = 3; for (var j = 0; j < 4; ++j) { [a]; } testGlobalProtoAccess.expected = "ok"; test(testGlobalProtoAccess); +function testAddUndefined() { + for (var j = 0; j < 3; ++j) + (0 + void 0) && 0; +} +test(testAddUndefined); + /* Keep these at the end so that we can see the summary after the trace-debug spew. */ print("\npassed:", passes.length && passes.join(",")); print("\nFAILED:", fails.length && fails.join(","));