This commit is contained in:
David Anderson 2008-10-28 14:00:27 -07:00
commit 06ec3d4965
10 changed files with 312 additions and 268 deletions

View File

@ -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;
}

View File

@ -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, &regs.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]), &regs.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, &regs.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, &regs.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;

View File

@ -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 {

View File

@ -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

View File

@ -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)

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)
/* 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)

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;
#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;
}
@ -4271,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) {
@ -5953,28 +5953,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 +6488,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 +6532,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 +6544,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 +6637,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 +7637,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 +7650,5 @@ UNUSED(204)
UNUSED(205)
UNUSED(206)
UNUSED(207)
UNUSED(208)
UNUSED(209)
UNUSED(219)
UNUSED(226)

View File

@ -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; }

View File

@ -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.

View File

@ -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(","));