Bug 458851 - TM: for-in loops skip every other value in certain cases (r=gal/mrbkap).

This commit is contained in:
Brendan Eich 2008-10-27 22:30:52 -07:00
parent bf1a78f4fe
commit 51f987553a
9 changed files with 267 additions and 303 deletions

View File

@ -2365,14 +2365,14 @@ EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg,
do_indexconst: { do_indexconst: {
JSAtomListElement *ale; JSAtomListElement *ale;
jsatomid atomIndex; jsatomid atomIndex;
ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList); ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList);
if (!ale) if (!ale)
return JS_FALSE; return JS_FALSE;
atomIndex = ALE_INDEX(ale); atomIndex = ALE_INDEX(ale);
return EmitSlotIndexOp(cx, op, pn2->pn_slot, atomIndex, cg); return EmitSlotIndexOp(cx, op, pn2->pn_slot, atomIndex, cg);
} }
default:; default:;
} }
} }
@ -3849,28 +3849,6 @@ EmitFunctionDefNop(JSContext *cx, JSCodeGenerator *cg, uintN index)
js_Emit1(cx, cg, JSOP_NOP) >= 0; 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 JSBool
js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
{ {
@ -3954,7 +3932,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
cg2->staticDepth = cg->staticDepth + 1; cg2->staticDepth = cg->staticDepth + 1;
cg2->parent = cg; 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); JS_SCOPE_DEPTH_METERING(cg2->treeContext.maxScopeDepth = (uintN) -1);
if (!js_EmitFunctionScript(cx, cg2, pn->pn_body)) { if (!js_EmitFunctionScript(cx, cg2, pn->pn_body)) {
pn = NULL; pn = NULL;
@ -4239,7 +4217,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
* destructuring for-in). * destructuring for-in).
*/ */
JS_ASSERT(pn->pn_op == JSOP_ITER); JS_ASSERT(pn->pn_op == JSOP_ITER);
if (js_Emit2(cx, cg, PN_OP(pn), (uint8) pn->pn_iflags) < 0) if (js_Emit2(cx, cg, JSOP_ITER, (uint8) pn->pn_iflags) < 0)
return JS_FALSE; return JS_FALSE;
/* Annotate so the decompiler can find the loop-closing jump. */ /* Annotate so the decompiler can find the loop-closing jump. */
@ -4258,6 +4236,10 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
top = CG_OFFSET(cg); top = CG_OFFSET(cg);
SET_STATEMENT_TOP(&stmtInfo, top); SET_STATEMENT_TOP(&stmtInfo, top);
#ifdef DEBUG
intN loopDepth = cg->stackDepth;
#endif
/* /*
* Compile a JSOP_FOR* bytecode based on the left hand side. * Compile a JSOP_FOR* bytecode based on the left hand side.
* *
@ -4291,9 +4273,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
/* FALL THROUGH */ /* FALL THROUGH */
case TOK_NAME: 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 * Always annotate JSOP_FORLOCAL if given input of the form
* 'for (let x in * o)' -- the decompiler must not hoist the * 'for (let x in * o)' -- the decompiler must not hoist the
@ -4333,7 +4312,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
if (pn3->pn_slot >= 0) { if (pn3->pn_slot >= 0) {
if (pn3->pn_const) { if (pn3->pn_const) {
JS_ASSERT(op == JSOP_FORLOCAL); JS_ASSERT(op == JSOP_FORLOCAL);
op = JSOP_FORCONST; js_ReportCompileErrorNumber(cx, CG_TS(cg), pn3, JSREPORT_ERROR,
JSMSG_BAD_FOR_LEFTSIDE);
return JS_FALSE;
} }
atomIndex = (jsatomid) pn3->pn_slot; atomIndex = (jsatomid) pn3->pn_slot;
EMIT_UINT16_IMM_OP(op, atomIndex); EMIT_UINT16_IMM_OP(op, atomIndex);
@ -4352,8 +4333,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
if (!CheckSideEffects(cx, cg, pn3->pn_expr, &useful)) if (!CheckSideEffects(cx, cg, pn3->pn_expr, &useful))
return JS_FALSE; return JS_FALSE;
if (!useful) { if (!useful) {
if (!EmitForInLoopBody(cx, cg, &stmtInfo, pn->pn_right, noteIndex, jmp))
return JS_FALSE;
if (!EmitPropOp(cx, pn3, JSOP_FORPROP, cg, JS_FALSE)) if (!EmitPropOp(cx, pn3, JSOP_FORPROP, cg, JS_FALSE))
return JS_FALSE; return JS_FALSE;
break; break;
@ -4364,19 +4343,10 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
destructuring_for: destructuring_for:
#endif #endif
default: default:
/* if (js_Emit1(cx, cg, JSOP_FORELEM) < 0)
* We separate the first/next bytecode from the enumerator return JS_FALSE;
* variable binding to avoid any side-effects in the index JS_ASSERT(cg->stackDepth >= 3);
* 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 JS_HAS_DESTRUCTURING
if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) { if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) {
if (!EmitDestructuringOps(cx, cg, op, pn3)) if (!EmitDestructuringOps(cx, cg, op, pn3))
@ -4405,24 +4375,32 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
#endif #endif
if (!EmitElemOp(cx, pn3, JSOP_ENUMELEM, cg)) if (!EmitElemOp(cx, pn3, JSOP_ENUMELEM, cg))
return JS_FALSE; 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; break;
} }
/* Pop and test the loop condition generated by JSOP_FOR*. */ /* 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;
beq = EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg)); beq = EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg));
if (beq < 0) if (beq < 0)
return JS_FALSE; return JS_FALSE;
@ -4559,12 +4537,13 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
if (pn2->pn_type == TOK_IN) { if (pn2->pn_type == TOK_IN) {
/* /*
* JSOP_ENDITER needs a slot to save an exception thrown from the * JSOP_ENDITER must have a slot to save an exception thrown from
* body of for-in loop when closing the iterator object. * 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().
*/ */
JS_ASSERT(js_CodeSpec[JSOP_ENDITER].format & JOF_TMPSLOT); JS_ASSERT(js_CodeSpec[JSOP_ENDITER].nuses == 2);
if (!NewTryNote(cx, cg, JSTRY_ITER, cg->stackDepth, top, if (!NewTryNote(cx, cg, JSTRY_ITER, cg->stackDepth, top, CG_OFFSET(cg)) ||
CG_OFFSET(cg)) ||
js_Emit1(cx, cg, JSOP_ENDITER) < 0) { js_Emit1(cx, cg, JSOP_ENDITER) < 0) {
return JS_FALSE; return JS_FALSE;
} }
@ -5856,7 +5835,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
argc = pn->pn_count - 1; argc = pn->pn_count - 1;
if (js_Emit3(cx, cg, PN_OP(pn), ARGC_HI(argc), ARGC_LO(argc)) < 0) if (js_Emit3(cx, cg, PN_OP(pn), ARGC_HI(argc), ARGC_LO(argc)) < 0)
return JS_FALSE; 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); EMIT_UINT16_IMM_OP(JSOP_LINENO, pn->pn_pos.begin.lineno);
break; break;
} }

View File

@ -2002,8 +2002,6 @@ js_TraceOpcode(JSContext *cx, jsint len)
prevop = (JSOp) regs->pc[-len]; prevop = (JSOp) regs->pc[-len];
ndefs = js_CodeSpec[prevop].ndefs; ndefs = js_CodeSpec[prevop].ndefs;
if (ndefs != 0) { if (ndefs != 0) {
if (prevop == JSOP_FORELEM && regs->sp[-1] == JSVAL_FALSE)
--ndefs;
for (n = -ndefs; n < 0; n++) { for (n = -ndefs; n < 0; n++) {
char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n], char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
NULL); NULL);
@ -3192,130 +3190,88 @@ js_Interpret(JSContext *cx)
END_CASE(JSOP_IN) END_CASE(JSOP_IN)
BEGIN_CASE(JSOP_ITER) BEGIN_CASE(JSOP_ITER)
flags = regs.pc[1];
JS_ASSERT(regs.sp > StackBase(fp)); JS_ASSERT(regs.sp > StackBase(fp));
flags = regs.pc[1];
if (!js_ValueToIterator(cx, flags, &regs.sp[-1])) if (!js_ValueToIterator(cx, flags, &regs.sp[-1]))
goto error; goto error;
LOAD_INTERRUPT_HANDLER(cx);
JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1])); JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1]));
PUSH(JSVAL_VOID);
END_CASE(JSOP_ITER) END_CASE(JSOP_ITER)
BEGIN_CASE(JSOP_FORPROP) BEGIN_CASE(JSOP_NEXTITER)
/* JS_ASSERT(regs.sp - 2 >= StackBase(fp));
* Handle JSOP_FORPROP first, so the cost of the goto do_forinloop JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-2]));
* is not paid for the more common cases. if (!js_CallIteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[-2]), &regs.sp[-1]))
*/ goto error;
LOAD_ATOM(0); LOAD_INTERRUPT_HANDLER(cx);
id = ATOM_TO_JSID(atom); PUSH(BOOLEAN_TO_JSVAL(regs.sp[-1] != JSVAL_HOLE));
i = -2; TRACE_0(IteratorNextComplete);
goto do_forinloop; END_CASE(JSOP_NEXTITER)
BEGIN_CASE(JSOP_FORNAME) BEGIN_CASE(JSOP_ENDITER)
LOAD_ATOM(0); /*
id = ATOM_TO_JSID(atom); * Decrease the stack pointer even when !ok -- see comments in the
/* FALL THROUGH */ * exception capturing code for details.
*/
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) BEGIN_CASE(JSOP_FORARG)
BEGIN_CASE(JSOP_FORCONST) 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) BEGIN_CASE(JSOP_FORLOCAL)
/* JS_ASSERT(regs.sp - 2 >= StackBase(fp));
* These bytecodes don't require any lval computation here, slot = GET_SLOTNO(regs.pc);
* because they address slots on the stack (in fp->args or JS_ASSERT(slot < fp->script->nslots);
* fp->slots). vp = &fp->slots[slot];
*/ GC_POKE(cx, *vp);
/* FALL THROUGH */ *vp = regs.sp[-1];
END_CASE(JSOP_FORLOCAL)
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, &regs.sp[-1]);
if (!ok)
goto error;
END_CASE(JSOP_FORNAME)
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, &regs.sp[-2]);
if (!ok)
goto error;
regs.sp--;
END_CASE(JSOP_FORPROP)
BEGIN_CASE(JSOP_FORELEM) BEGIN_CASE(JSOP_FORELEM)
/* /*
* JSOP_FORELEM simply initializes or updates the iteration state * JSOP_FORELEM simply dups the property identifier at top of stack
* and leaves the index expression evaluation and assignment to the * and lets the subsequent JSOP_ENUMELEM opcode sequence handle the
* enumerator until after the next property has been acquired, via * left-hand side expression evaluation and assignment. This opcode
* a JSOP_ENUMELEM bytecode. * exists solely to help the decompiler.
*/ */
i = -1; JS_ASSERT(regs.sp - 2 >= StackBase(fp));
rval = FETCH_OPND(-1);
do_forinloop: PUSH(rval);
/* END_CASE(JSOP_FORELEM)
* 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) BEGIN_CASE(JSOP_DUP)
JS_ASSERT(regs.sp > StackBase(fp)); JS_ASSERT(regs.sp > StackBase(fp));
@ -6712,17 +6668,6 @@ js_Interpret(JSContext *cx)
} }
END_CASE(JSOP_LEAVEBLOCK) 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 #if JS_HAS_GENERATORS
BEGIN_CASE(JSOP_GENERATOR) BEGIN_CASE(JSOP_GENERATOR)
ASSERT_NOT_THROWING(cx); ASSERT_NOT_THROWING(cx);
@ -6823,11 +6768,9 @@ js_Interpret(JSContext *cx)
L_JSOP_DEFXMLNS: L_JSOP_DEFXMLNS:
# endif # endif
L_JSOP_UNUSED74:
L_JSOP_UNUSED76:
L_JSOP_UNUSED77:
L_JSOP_UNUSED78: L_JSOP_UNUSED78:
L_JSOP_UNUSED79: L_JSOP_UNUSED79:
L_JSOP_UNUSED103:
L_JSOP_UNUSED131: L_JSOP_UNUSED131:
L_JSOP_UNUSED201: L_JSOP_UNUSED201:
L_JSOP_UNUSED202: L_JSOP_UNUSED202:
@ -6836,6 +6779,8 @@ js_Interpret(JSContext *cx)
L_JSOP_UNUSED205: L_JSOP_UNUSED205:
L_JSOP_UNUSED206: L_JSOP_UNUSED206:
L_JSOP_UNUSED207: L_JSOP_UNUSED207:
L_JSOP_UNUSED208:
L_JSOP_UNUSED209:
L_JSOP_UNUSED219: L_JSOP_UNUSED219:
L_JSOP_UNUSED226: L_JSOP_UNUSED226:
@ -6984,12 +6929,14 @@ js_Interpret(JSContext *cx)
case JSTRY_ITER: case JSTRY_ITER:
/* /*
* This is similar to JSOP_ENDITER in the interpreter loop * This is similar to JSOP_ENDITER in the interpreter loop,
* except the code now uses a reserved stack slot to save and * except the code now uses the stack slot normally used by
* restore the exception. * JSOP_NEXTITER, namely regs.sp[-1] before the regs.sp -= 2
* adjustment and regs.sp[1] after, to save and restore the
* pending exception.
*/ */
JS_ASSERT(*regs.pc == JSOP_ENDITER); JS_ASSERT(*regs.pc == JSOP_ENDITER);
PUSH(cx->exception); regs.sp[-1] = cx->exception;
cx->throwing = JS_FALSE; cx->throwing = JS_FALSE;
ok = js_CloseIterator(cx, regs.sp[-2]); ok = js_CloseIterator(cx, regs.sp[-2]);
regs.sp -= 2; regs.sp -= 2;

View File

@ -3414,7 +3414,7 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
format = cs->format; format = cs->format;
if (JOF_MODE(format) != JOF_NAME) if (JOF_MODE(format) != JOF_NAME)
flags |= JSRESOLVE_QUALIFIED; flags |= JSRESOLVE_QUALIFIED;
if ((format & JOF_ASSIGNING) || if ((format & (JOF_SET | JOF_FOR)) ||
(cx->fp->flags & JSFRAME_ASSIGNING)) { (cx->fp->flags & JSFRAME_ASSIGNING)) {
flags |= JSRESOLVE_ASSIGNING; flags |= JSRESOLVE_ASSIGNING;
} else { } else {

View File

@ -1740,6 +1740,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
static const char exception_cookie[] = "/*EXCEPTION*/"; static const char exception_cookie[] = "/*EXCEPTION*/";
static const char retsub_pc_cookie[] = "/*RETSUB_PC*/"; static const char retsub_pc_cookie[] = "/*RETSUB_PC*/";
static const char iter_cookie[] = "/*ITER*/";
static const char forelem_cookie[] = "/*FORELEM*/"; static const char forelem_cookie[] = "/*FORELEM*/";
static const char with_cookie[] = "/*WITH*/"; static const char with_cookie[] = "/*WITH*/";
static const char dot_format[] = "%s.%s"; static const char dot_format[] = "%s.%s";
@ -1760,6 +1761,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL) #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
#define DECOMPILE_CODE(pc,nb) if (!Decompile(ss, pc, nb, JSOP_NOP)) return 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 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() PopStr(ss, op)
#define POP_STR_PREC(prec) PopStrPrec(ss, prec) #define POP_STR_PREC(prec) PopStrPrec(ss, prec)
@ -2502,14 +2504,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
sn = NULL; sn = NULL;
break; 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: case JSOP_ENTERWITH:
LOCAL_ASSERT(!js_GetSrcNote(jp->script, pc)); LOCAL_ASSERT(!js_GetSrcNote(jp->script, pc));
rval = POP_STR(); rval = POP_STR();
@ -2883,7 +2877,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
pos = ss->top; pos = ss->top;
while (pos != 0) { while (pos != 0) {
op = (JSOp) ss->opcodes[--pos]; op = (JSOp) ss->opcodes[--pos];
if (op != JSOP_FORLOCAL) if (op == JSOP_ENTERBLOCK)
break; break;
} }
JS_ASSERT(op == JSOP_ENTERBLOCK); JS_ASSERT(op == JSOP_ENTERBLOCK);
@ -2893,7 +2887,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
* in the single string of accumulated |for| heads and optional * in the single string of accumulated |for| heads and optional
* final |if (condition)|. * final |if (condition)|.
*/ */
forpos = pos + 1; forpos = pos + 2;
LOCAL_ASSERT(forpos < ss->top); LOCAL_ASSERT(forpos < ss->top);
/* /*
@ -2965,6 +2959,25 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
js_printf(jp, "\t%s %s;\n", js_throw_str, rval); js_printf(jp, "\t%s %s;\n", js_throw_str, rval);
break; 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_GOTO:
case JSOP_GOTOX: case JSOP_GOTOX:
sn = js_GetSrcNote(jp->script, pc); sn = js_GetSrcNote(jp->script, pc);
@ -2973,19 +2986,22 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
/* /*
* The loop back-edge carries +1 stack balance, for the * The loop back-edge carries +1 stack balance, for the
* flag processed by JSOP_IFNE. We do not decompile the * flag processed by JSOP_IFNE. We do not decompile the
* JSOP_IFNE, and instead pass the left-hand side of 'in' * JSOP_IFNE, and instead push the left-hand side of 'in'
* along the loop edge in this pushed stack slot. * after the loop edge in this stack slot (the JSOP_FOR*
* opcodes' decompilers do this pushing).
*/ */
cond = GetJumpOffset(pc, pc); cond = GetJumpOffset(pc, pc);
next = js_GetSrcNoteOffset(sn, 0); next = js_GetSrcNoteOffset(sn, 0);
tail = js_GetSrcNoteOffset(sn, 1); tail = js_GetSrcNoteOffset(sn, 1);
DECOMPILE_CODE(pc + cond, tail - cond); JS_ASSERT(pc[cond] == JSOP_NEXTITER);
DECOMPILE_CODE(pc + oplen, next - oplen); DECOMPILE_CODE(pc + oplen, next - oplen);
lval = POP_STR();
if (ss->inArrayInit || ss->inGenExp) { if (ss->inArrayInit || ss->inGenExp) {
lval = POP_STR(); (void) PopOff(ss, JSOP_NOP);
rval = POP_STR(); rval = TOP_STR();
if (ss->opcodes[ss->top - 1] == JSOP_FORLOCAL) { LOCAL_ASSERT(ss->top >= 2);
ss->sprinter.offset -= PAREN_SLOP; if (ss->opcodes[ss->top - 2] == JSOP_FORLOCAL) {
ss->sprinter.offset = ss->offsets[ss->top - 1] - PAREN_SLOP;
if (Sprint(&ss->sprinter, " %s (%s in %s)", if (Sprint(&ss->sprinter, " %s (%s in %s)",
foreach ? js_for_each_str : js_for_str, foreach ? js_for_each_str : js_for_str,
lval, rval) < 0) { lval, rval) < 0) {
@ -3000,7 +3016,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
*/ */
todo = ss->offsets[ss->top - 1]; todo = ss->offsets[ss->top - 1];
} else { } else {
LOCAL_ASSERT(ss->opcodes[ss->top - 1] == JSOP_ENTERBLOCK); LOCAL_ASSERT(ss->opcodes[ss->top - 2] == JSOP_ENTERBLOCK);
todo = Sprint(&ss->sprinter, " %s (%s in %s)", todo = Sprint(&ss->sprinter, " %s (%s in %s)",
foreach ? js_for_each_str : js_for_str, foreach ? js_for_each_str : js_for_str,
lval, rval); lval, rval);
@ -3013,8 +3029,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
* As above, rval or an extension of it must remain * As above, rval or an extension of it must remain
* stacked during loop body decompilation. * stacked during loop body decompilation.
*/ */
lval = POP_STR(); rval = GetStr(ss, ss->top - 2);
rval = GetStr(ss, ss->top - 1);
js_printf(jp, "\t%s (%s in %s) {\n", js_printf(jp, "\t%s (%s in %s) {\n",
foreach ? js_for_each_str : js_for_str, foreach ? js_for_each_str : js_for_str,
lval, rval); lval, rval);
@ -3222,7 +3237,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
i = GET_ARGNO(pc); i = GET_ARGNO(pc);
goto do_forvarslot; goto do_forvarslot;
case JSOP_FORCONST:
case JSOP_FORLOCAL: case JSOP_FORLOCAL:
sn = js_GetSrcNote(jp->script, pc); sn = js_GetSrcNote(jp->script, pc);
if (!IsVarSlot(jp, pc, &i)) { if (!IsVarSlot(jp, pc, &i)) {
@ -3270,7 +3284,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
break; break;
case JSOP_FORELEM: case JSOP_FORELEM:
LOCAL_ASSERT(pc[1] == JSOP_IFNE || pc[1] == JSOP_IFNEX);
todo = SprintCString(&ss->sprinter, forelem_cookie); todo = SprintCString(&ss->sprinter, forelem_cookie);
break; break;
@ -4536,12 +4549,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
inXML = JS_FALSE; inXML = JS_FALSE;
break; break;
case JSOP_ITER:
foreach = (pc[1] & (JSITER_FOREACH | JSITER_KEYVALUE)) ==
JSITER_FOREACH;
todo = -2;
break;
case JSOP_TOXML: case JSOP_TOXML:
case JSOP_CALLXMLNAME: case JSOP_CALLXMLNAME:
case JSOP_XMLNAME: case JSOP_XMLNAME:
@ -4639,7 +4646,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
#undef inXML #undef inXML
#undef DECOMPILE_CODE #undef DECOMPILE_CODE
#undef NEXT_OP #undef NEXT_OP
#undef TOP_STR
#undef POP_STR #undef POP_STR
#undef POP_STR_PREC
#undef LOCAL_ASSERT #undef LOCAL_ASSERT
#undef ATOM_IS_IDENTIFIER #undef ATOM_IS_IDENTIFIER
#undef GET_QUOTE_AND_FMT #undef GET_QUOTE_AND_FMT

View File

@ -97,7 +97,7 @@ typedef enum JSOp {
#define JOF_INC (2U<<10) /* increment (++, not --) opcode */ #define JOF_INC (2U<<10) /* increment (++, not --) opcode */
#define JOF_INCDEC (3U<<10) /* increment or decrement opcode */ #define JOF_INCDEC (3U<<10) /* increment or decrement opcode */
#define JOF_POST (1U<<12) /* postorder increment or decrement */ #define JOF_POST (1U<<12) /* postorder increment or decrement */
#define JOF_FOR (1U<<13) /* for-in property op */ #define JOF_FOR (1U<<13) /* for-in property op (akin to JOF_SET) */
#define JOF_ASSIGNING JOF_SET /* hint for JSClass.resolve, used for ops #define JOF_ASSIGNING JOF_SET /* hint for JSClass.resolve, used for ops
that do simplex assignment */ that do simplex assignment */
#define JOF_DETECTING (1U<<14) /* object detection for JSNewResolveOp */ #define JOF_DETECTING (1U<<14) /* object detection for JSNewResolveOp */
@ -118,8 +118,6 @@ typedef enum JSOp {
#define JOF_TMPSLOT_SHIFT 22 #define JOF_TMPSLOT_SHIFT 22
#define JOF_TMPSLOT_MASK (JS_BITMASK(2) << JOF_TMPSLOT_SHIFT) #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. */ /* Shorthands for type from format and type from opcode. */
#define JOF_TYPE(fmt) ((fmt) & JOF_TYPEMASK) #define JOF_TYPE(fmt) ((fmt) & JOF_TYPEMASK)
#define JOF_OPTYPE(op) JOF_TYPE(js_CodeSpec[op].format) #define JOF_OPTYPE(op) JOF_TYPE(js_CodeSpec[op].format)

View File

@ -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) 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. */ /* ECMA-compliant for-in loop with argument or local loop control. */
OPDEF(JSOP_FORARG, 10, "forarg", NULL, 3, 0, 1, 19, JOF_QARG|JOF_NAME|JOF_FOR) OPDEF(JSOP_FORARG, 10, "forarg", NULL, 3, 2, 2, 19, JOF_QARG|JOF_NAME|JOF_FOR)
OPDEF(JSOP_FORLOCAL, 11, "forlocal", NULL, 3, 0, 1, 19, JOF_LOCAL|JOF_NAME|JOF_FOR) OPDEF(JSOP_FORLOCAL, 11, "forlocal", NULL, 3, 2, 2, 19, JOF_LOCAL|JOF_NAME|JOF_FOR)
/* More longstanding bytecodes. */ /* More longstanding bytecodes. */
OPDEF(JSOP_DUP, 12, "dup", NULL, 1, 1, 2, 0, JOF_BYTE) 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_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_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_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|JOF_RETVAL) OPDEF(JSOP_CALL, 58, "call", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE)
OPDEF(JSOP_NAME, 59, "name", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_NAME) 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_DOUBLE, 60, "double", NULL, 3, 0, 1, 16, JOF_ATOM)
OPDEF(JSOP_STRING, 61, "string", NULL, 3, 0, 1, 19, JOF_ATOM) OPDEF(JSOP_STRING, 61, "string", NULL, 3, 0, 1, 19, JOF_ATOM)
@ -184,13 +184,25 @@ 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_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_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. */ /* Variant of JSOP_NULL for default (global) |this| parameter pushing. */
OPDEF(JSOP_NULLTHIS, 75, js_null_str, js_null_str, 1, 0, 1, 19, JOF_BYTE) 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_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_UNUSED78, 78, "unused78", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED79, 79, "unused79", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_UNUSED79, 79, "unused79", NULL, 1, 0, 0, 0, JOF_BYTE)
@ -234,16 +246,12 @@ 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_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_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. */ /* ECMA-compliant for/in ops. */
OPDEF(JSOP_FORNAME, 104,"forname", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_NAME|JOF_FOR) OPDEF(JSOP_FORNAME, 104,"forname", NULL, 3, 2, 2, 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_FORPROP, 105,"forprop", NULL, 3, 3, 2, 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_FORELEM, 106,"forelem", NULL, 1, 2, 3, 18, JOF_BYTE |JOF_ELEM|JOF_FOR)
OPDEF(JSOP_POPN, 107,"popn", NULL, 3, -1, 0, 0, JOF_UINT16) OPDEF(JSOP_POPN, 107,"popn", NULL, 3, -1, 0, 0, JOF_UINT16)
/* ECMA-compliant assignment ops. */ /* ECMA-compliant assignment ops. */
@ -474,12 +482,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_UNUSED205, 205,"unused205", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED206, 206,"unused206", 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_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)
/* /*
* Iterator, generator, and array comprehension support. * 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_GENERATOR, 210,"generator", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_YIELD, 211,"yield", NULL, 1, 1, 1, 1, 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) OPDEF(JSOP_ARRAYPUSH, 212,"arraypush", NULL, 3, 1, 0, 3, JOF_LOCAL)

View File

@ -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; #define JITSTAT(x) case STAT ## x ## ID: result = jitstats.x; break;
#include "jitstats.tbl" #include "jitstats.tbl"
#undef JITSTAT #undef JITSTAT
default: default:
*vp = JSVAL_VOID; *vp = JSVAL_VOID;
return JS_TRUE; return JS_TRUE;
} }
@ -1695,13 +1695,13 @@ static bool
js_IsLoopEdge(jsbytecode* pc, jsbytecode* header) js_IsLoopEdge(jsbytecode* pc, jsbytecode* header)
{ {
switch (*pc) { switch (*pc) {
case JSOP_IFEQ: case JSOP_IFEQ:
case JSOP_IFNE: case JSOP_IFNE:
return ((pc + GET_JUMP_OFFSET(pc)) == header); return ((pc + GET_JUMP_OFFSET(pc)) == header);
case JSOP_IFEQX: case JSOP_IFEQX:
case JSOP_IFNEX: case JSOP_IFNEX:
return ((pc + GET_JUMPX_OFFSET(pc)) == header); return ((pc + GET_JUMPX_OFFSET(pc)) == header);
default: default:
JS_ASSERT((*pc == JSOP_AND) || (*pc == JSOP_ANDX) || JS_ASSERT((*pc == JSOP_AND) || (*pc == JSOP_ANDX) ||
(*pc == JSOP_OR) || (*pc == JSOP_ORX)); (*pc == JSOP_OR) || (*pc == JSOP_ORX));
} }
@ -1788,7 +1788,7 @@ TraceRecorder::snapshot(ExitType exitType)
bool resumeAfter = (pendingTraceableNative && bool resumeAfter = (pendingTraceableNative &&
JSTN_ERRTYPE(pendingTraceableNative) == FAIL_JSVAL); JSTN_ERRTYPE(pendingTraceableNative) == FAIL_JSVAL);
if (resumeAfter) { if (resumeAfter) {
JS_ASSERT(cs.format & JOF_RETVAL); JS_ASSERT(*pc == JSOP_CALL || *pc == JSOP_NEXTITER);
pc += cs.length; pc += cs.length;
regs->pc = pc; regs->pc = pc;
MUST_FLOW_THROUGH(restore_pc); MUST_FLOW_THROUGH(restore_pc);
@ -1815,10 +1815,11 @@ TraceRecorder::snapshot(ExitType exitType)
); );
JS_ASSERT(unsigned(m - typemap) == ngslots + stackSlots); JS_ASSERT(unsigned(m - typemap) == ngslots + stackSlots);
/* If we are capturing the stack state on a JOF_RETVAL instruction, the value on top of /* If we are capturing the stack state on a specific instruction, the value on or near
the stack is a boxed value. */ 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 (resumeAfter) { if (resumeAfter) {
m[-1] = JSVAL_BOXED; m[(pc[-cs.length] == JSOP_NEXTITER) ? -2 : -1] = JSVAL_BOXED;
/* Now restore the the original pc (after which early returns are ok). */ /* Now restore the the original pc (after which early returns are ok). */
MUST_FLOW_LABEL(restore_pc); MUST_FLOW_LABEL(restore_pc);
@ -1958,7 +1959,8 @@ TraceRecorder::checkType(jsval& v, uint8 t, jsval*& stage_val, LIns*& stage_ins,
typeChar[t]);); typeChar[t]););
} }
#endif #endif
debug_only_v(printf("checkType(tag=%d, t=%d) stage_count=%d\n", JSVAL_TAG(v), t, stage_count);) debug_only_v(printf("checkType(tag=%d, t=%d) stage_count=%d\n",
(int) JSVAL_TAG(v), t, stage_count);)
return JSVAL_TAG(v) == t; return JSVAL_TAG(v) == t;
} }
@ -2374,17 +2376,17 @@ TraceRecorder::flipIf(jsbytecode* pc, bool& cond)
{ {
if (js_IsLoopEdge(pc, (jsbytecode*)fragment->root->ip)) { if (js_IsLoopEdge(pc, (jsbytecode*)fragment->root->ip)) {
switch (*pc) { switch (*pc) {
case JSOP_IFEQ: case JSOP_IFEQ:
case JSOP_IFEQX: case JSOP_IFEQX:
if (!cond) if (!cond)
return; return;
break; break;
case JSOP_IFNE: case JSOP_IFNE:
case JSOP_IFNEX: case JSOP_IFNEX:
if (cond) if (cond)
return; return;
break; break;
default: default:
JS_NOT_REACHED("flipIf"); JS_NOT_REACHED("flipIf");
} }
/* We are about to walk out of the loop, so terminate it with /* We are about to walk out of the loop, so terminate it with
@ -2956,7 +2958,7 @@ js_RecordLoopEdge(JSContext* cx, TraceRecorder* r, uintN& inlineCallCount)
} }
Fragment* old; Fragment* old;
switch (lr->exitType) { 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 the inner tree exited on an unknown loop exit, grow the tree around it. */
if (innermostNestedGuard) { if (innermostNestedGuard) {
js_AbortRecording(cx, "Inner tree took different side exit, abort recording"); js_AbortRecording(cx, "Inner tree took different side exit, abort recording");
@ -3493,15 +3495,15 @@ monitor_loop:
of a branch exit), or the tree nested around the tree we exited from (in case of the of a branch exit), or the tree nested around the tree we exited from (in case of the
tree call guard). */ tree call guard). */
switch (lr->exitType) { switch (lr->exitType) {
case UNSTABLE_LOOP_EXIT: case UNSTABLE_LOOP_EXIT:
return js_AttemptToStabilizeTree(cx, lr, match, NULL); return js_AttemptToStabilizeTree(cx, lr, match, NULL);
case BRANCH_EXIT: case BRANCH_EXIT:
return js_AttemptToExtendTree(cx, lr, NULL, NULL); return js_AttemptToExtendTree(cx, lr, NULL, NULL);
case LOOP_EXIT: case LOOP_EXIT:
if (innermostNestedGuard) if (innermostNestedGuard)
return js_AttemptToExtendTree(cx, innermostNestedGuard, lr, NULL); return js_AttemptToExtendTree(cx, innermostNestedGuard, lr, NULL);
return false; return false;
default: default:
/* No, this was an unusual exit (i.e. out of memory/GC), so just resume interpretation. */ /* No, this was an unusual exit (i.e. out of memory/GC), so just resume interpretation. */
return false; return false;
} }
@ -5951,28 +5953,28 @@ TraceRecorder::record_FastNativeCallComplete()
function, which might have side effects. function, which might have side effects.
Instead, snapshot(), which is invoked from unbox_jsval(), will see that Instead, snapshot(), which is invoked from unbox_jsval(), will see that
we are currently parked on a JOF_RETVAL instruction, and it will advance we are currently parked on a traceable native's JSOP_CALL instruction,
the pc to restore by the length of the current opcode, and indicate in and it will advance the pc to restore by the length of the current
the type map that the element on top of the stack is a boxed value which opcode, and indicate in the type map that the element on top of the
doesn't need to be boxed if the type guard generated by unbox_jsval() stack is a boxed value which doesn't need to be boxed if the type guard
fails. */ generated by unbox_jsval() fails. */
JS_ASSERT(js_CodeSpec[*cx->fp->regs->pc].format & JOF_RETVAL); JS_ASSERT(*cx->fp->regs->pc == JSOP_CALL);
jsval& v = stackval(-1); jsval& v = stackval(-1);
LIns* v_ins = get(&v); LIns* v_ins = get(&v);
bool ok = true; bool ok = true;
switch (JSTN_ERRTYPE(pendingTraceableNative)) { switch (JSTN_ERRTYPE(pendingTraceableNative)) {
case FAIL_JSVAL: case FAIL_JSVAL:
ok = unbox_jsval(v, v_ins); ok = unbox_jsval(v, v_ins);
if (ok) if (ok)
set(&v, v_ins); set(&v, v_ins);
break; break;
case FAIL_NEG: case FAIL_NEG:
/* Already added i2f in functionCall. */ /* Already added i2f in functionCall. */
JS_ASSERT(JSVAL_IS_NUMBER(v)); JS_ASSERT(JSVAL_IS_NUMBER(v));
break; break;
default: default:
/* Convert the result to double if the builtin returns int32. */ /* Convert the result to double if the builtin returns int32. */
if (JSVAL_IS_NUMBER(v) && if (JSVAL_IS_NUMBER(v) &&
(pendingTraceableNative->builtin->_argtypes & 3) == nanojit::ARGSIZE_LO) { (pendingTraceableNative->builtin->_argtypes & 3) == nanojit::ARGSIZE_LO) {
@ -6486,41 +6488,62 @@ TraceRecorder::record_JSOP_ITER()
LIns* v_ins = lir->insCall(&js_FastValueToIterator_ci, args); LIns* v_ins = lir->insCall(&js_FastValueToIterator_ci, args);
guard(false, lir->ins_eq0(v_ins), MISMATCH_EXIT); guard(false, lir->ins_eq0(v_ins), MISMATCH_EXIT);
set(&v, v_ins); set(&v, v_ins);
LIns* void_ins = INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_VOID));
stack(0, void_ins);
return true; return true;
} }
ABORT_TRACE("for-in on a primitive value"); 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 bool
TraceRecorder::forInLoop(jsval* vp) TraceRecorder::record_JSOP_NEXTITER()
{ {
jsval& iterobj_val = stackval(-1); jsval& iterobj_val = stackval(-2);
if (!JSVAL_IS_PRIMITIVE(iterobj_val)) { if (!JSVAL_IS_PRIMITIVE(iterobj_val)) {
LIns* args[] = { get(&iterobj_val), cx_ins }; LIns* args[] = { get(&iterobj_val), cx_ins };
LIns* v_ins = lir->insCall(&js_FastCallIteratorNext_ci, args); LIns* v_ins = lir->insCall(&js_FastCallIteratorNext_ci, args);
guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), OOM_EXIT); 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))); LIns* flag_ins = lir->ins_eq0(lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_HOLE)));
LIns* iter_ins = get(vp); stack(-1, v_ins);
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); stack(0, flag_ins);
pendingTraceableNative = &js_FastCallIteratorNext_tn;
return true; return true;
} }
ABORT_TRACE("for-in on a primitive value"); 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 bool
TraceRecorder::record_JSOP_ENDITER() TraceRecorder::record_JSOP_ENDITER()
{ {
LIns* args[] = { stack(-1), cx_ins }; LIns* args[] = { stack(-2), cx_ins };
LIns* ok_ins = lir->insCall(&js_CloseIterator_ci, args); LIns* ok_ins = lir->insCall(&js_CloseIterator_ci, args);
guard(false, lir->ins_eq0(ok_ins), MISMATCH_EXIT); guard(false, lir->ins_eq0(ok_ins), MISMATCH_EXIT);
return true; return true;
@ -6530,7 +6553,11 @@ bool
TraceRecorder::record_JSOP_FORNAME() TraceRecorder::record_JSOP_FORNAME()
{ {
jsval* vp; jsval* vp;
return name(vp) && forInLoop(vp); if (name(vp)) {
set(vp, stack(-1));
return true;
}
return false;
} }
bool bool
@ -6542,25 +6569,19 @@ TraceRecorder::record_JSOP_FORPROP()
bool bool
TraceRecorder::record_JSOP_FORELEM() TraceRecorder::record_JSOP_FORELEM()
{ {
return false; return record_JSOP_DUP();
} }
bool bool
TraceRecorder::record_JSOP_FORARG() TraceRecorder::record_JSOP_FORARG()
{ {
return forInLoop(&argval(GET_ARGNO(cx->fp->regs->pc))); return record_JSOP_SETARG();
} }
bool bool
TraceRecorder::record_JSOP_FORLOCAL() TraceRecorder::record_JSOP_FORLOCAL()
{ {
return forInLoop(&varval(GET_SLOTNO(cx->fp->regs->pc))); return record_JSOP_SETLOCAL();
}
bool
TraceRecorder::record_JSOP_FORCONST()
{
return false;
} }
bool bool
@ -6635,7 +6656,8 @@ TraceRecorder::record_JSOP_IN()
// Expect what we see at trace recording time (hit or miss) to be the same // 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 // when executing the trace. Use a builtin helper for named properties, as
// forInLoop does. First, handle indexes in dense arrays as a special case. // the for-in tracing code does. First, handle indexes in dense arrays as a
// special case.
JSObject* obj = JSVAL_TO_OBJECT(rval); JSObject* obj = JSVAL_TO_OBJECT(rval);
LIns* obj_ins = get(&rval); LIns* obj_ins = get(&rval);
@ -7635,11 +7657,9 @@ js_DumpPeerStability(Fragmento* frago, const void* ip)
#define UNUSED(n) bool TraceRecorder::record_JSOP_UNUSED##n() { return false; } #define UNUSED(n) bool TraceRecorder::record_JSOP_UNUSED##n() { return false; }
UNUSED(74)
UNUSED(76)
UNUSED(77)
UNUSED(78) UNUSED(78)
UNUSED(79) UNUSED(79)
UNUSED(103)
UNUSED(131) UNUSED(131)
UNUSED(201) UNUSED(201)
UNUSED(202) UNUSED(202)
@ -7648,5 +7668,7 @@ UNUSED(204)
UNUSED(205) UNUSED(205)
UNUSED(206) UNUSED(206)
UNUSED(207) UNUSED(207)
UNUSED(208)
UNUSED(209)
UNUSED(219) UNUSED(219)
UNUSED(226) UNUSED(226)

View File

@ -378,6 +378,7 @@ public:
bool record_SetPropMiss(JSPropCacheEntry* entry); bool record_SetPropMiss(JSPropCacheEntry* entry);
bool record_DefLocalFunSetSlot(uint32 slot, JSObject* obj); bool record_DefLocalFunSetSlot(uint32 slot, JSObject* obj);
bool record_FastNativeCallComplete(); bool record_FastNativeCallComplete();
bool record_IteratorNextComplete();
nanojit::Fragment* getOuterToBlacklist() { return outerToBlacklist; } nanojit::Fragment* getOuterToBlacklist() { return outerToBlacklist; }
void deepAbort() { deepAborted = true; } void deepAbort() { deepAborted = true; }

View File

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