Bug #331783 - separate initialization from increment for 'for in' loop
with index expression in order to prevent side-effects from occuring when the for loop stops (as in 'for (p[i++] in obj)...') git-svn-id: svn://10.0.0.236/branches/SpiderMonkey140_BRANCH@18831 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
@@ -1091,17 +1091,28 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
return JS_FALSE;
|
||||
break;
|
||||
case TOK_LB:
|
||||
if (!EmitElemOp(cx, pn3, JSOP_FORELEM2, cg))
|
||||
/*
|
||||
we separate out the initialization and incrementing
|
||||
in order to avoid unwanted side-effects from the
|
||||
index expression
|
||||
*/
|
||||
if (!js_Emit1(cx, cg, JSOP_FORELEM2))
|
||||
return JS_FALSE;
|
||||
beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0);
|
||||
if (beq < 0)
|
||||
return JS_FALSE;
|
||||
if (!EmitElemOp(cx, pn3, JSOP_ENUMELEM, cg))
|
||||
return JS_FALSE;
|
||||
break;
|
||||
default:
|
||||
JS_ASSERT(0);
|
||||
}
|
||||
|
||||
/* Pop and test the loop condition generated by JSOP_FOR*2. */
|
||||
beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0);
|
||||
if (beq < 0)
|
||||
return JS_FALSE;
|
||||
if (pn3->pn_type != TOK_LB) {
|
||||
/* Pop and test the loop condition generated by JSOP_FOR*2. */
|
||||
beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0);
|
||||
if (beq < 0)
|
||||
return JS_FALSE;
|
||||
}
|
||||
} else {
|
||||
if (!pn2->pn_kid1) {
|
||||
/* No initializer: emit an annotated nop for the decompiler. */
|
||||
@@ -1199,8 +1210,20 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
* Generate the object and iterator pop opcodes after popping the
|
||||
* stmtInfo stack, so breaks will go to this pop bytecode.
|
||||
*/
|
||||
if (js_Emit1(cx, cg, JSOP_POP2) < 0)
|
||||
return JS_FALSE;
|
||||
if (pn3->pn_type != TOK_LB) {
|
||||
if (js_Emit1(cx, cg, JSOP_POP2) < 0)
|
||||
return JS_FALSE;
|
||||
}
|
||||
else {
|
||||
/*
|
||||
for 'for(x[i]...)' there's only the object on the stack,
|
||||
so we need to hide the pop.
|
||||
*/
|
||||
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
|
||||
return JS_FALSE;
|
||||
if (js_Emit1(cx, cg, JSOP_POP) < 0)
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -1270,8 +1270,10 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
rval = POP();
|
||||
/* FALL THROUGH */
|
||||
case JSOP_FORELEM2:
|
||||
POP_ELEMENT_ID(id);
|
||||
lval = POP();
|
||||
/* FORELEM2 simply initializes the iteration state and
|
||||
leaves the assignment to the enumerator until after
|
||||
the next property has been acquired.
|
||||
*/
|
||||
|
||||
do_forinloop:
|
||||
/*
|
||||
@@ -1384,28 +1386,34 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert lval to a non-null object containing id. */
|
||||
VALUE_TO_OBJECT(cx, lval, obj);
|
||||
if (op != JSOP_FORELEM2) {
|
||||
/* Convert lval to a non-null object containing id. */
|
||||
VALUE_TO_OBJECT(cx, lval, obj);
|
||||
|
||||
/* Make sure rval is a string for uniformity and compatibility. */
|
||||
if (!JSVAL_IS_INT(rval)) {
|
||||
rval = ATOM_KEY((JSAtom *)rval);
|
||||
} else if ((cx->version <= JSVERSION_1_1) &&
|
||||
(cx->version >= JSVERSION_1_0)) {
|
||||
str = js_NumberToString(cx, (jsdouble) JSVAL_TO_INT(rval));
|
||||
if (!str) {
|
||||
ok = JS_FALSE;
|
||||
/* Make sure rval is a string for uniformity and compatibility. */
|
||||
if (!JSVAL_IS_INT(rval)) {
|
||||
rval = ATOM_KEY((JSAtom *)rval);
|
||||
} else if ((cx->version <= JSVERSION_1_1) &&
|
||||
(cx->version >= JSVERSION_1_0)) {
|
||||
str = js_NumberToString(cx, (jsdouble) JSVAL_TO_INT(rval));
|
||||
if (!str) {
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Hold via sp[0] in case the GC runs under OBJ_SET_PROPERTY. */
|
||||
rval = sp[0] = STRING_TO_JSVAL(str);
|
||||
}
|
||||
|
||||
|
||||
/* Set the variable obj[id] to refer to rval. */
|
||||
ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
|
||||
if (!ok)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Hold via sp[0] in case the GC runs under OBJ_SET_PROPERTY. */
|
||||
rval = sp[0] = STRING_TO_JSVAL(str);
|
||||
}
|
||||
|
||||
/* Set the variable obj[id] to refer to rval. */
|
||||
ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
|
||||
if (!ok)
|
||||
goto out;
|
||||
}
|
||||
else {
|
||||
PUSH_OPND(rval);
|
||||
}
|
||||
|
||||
/* Push true to keep looping through properties. */
|
||||
rval = JSVAL_TRUE;
|
||||
@@ -2172,6 +2180,16 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
PUSH_OPND(rval);
|
||||
break;
|
||||
|
||||
case JSOP_ENUMELEM:
|
||||
POP_ELEMENT_ID(id);
|
||||
lval = POP();
|
||||
VALUE_TO_OBJECT(cx, lval, obj);
|
||||
rval = POP();
|
||||
CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval));
|
||||
if (!ok)
|
||||
goto out;
|
||||
break;
|
||||
|
||||
case JSOP_PUSHOBJ:
|
||||
PUSH_OPND(OBJECT_TO_JSVAL(obj));
|
||||
break;
|
||||
|
||||
@@ -690,7 +690,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
{
|
||||
JSContext *cx;
|
||||
JSPrinter *jp;
|
||||
jsbytecode *endpc, *done;
|
||||
jsbytecode *endpc, *done, *forelem_tgt;
|
||||
ptrdiff_t len, todo, oplen, cond, next, tail;
|
||||
JSOp op, lastop, saveop;
|
||||
JSCodeSpec *cs, *topcs;
|
||||
@@ -1231,8 +1231,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
|
||||
case JSOP_FORELEM:
|
||||
rval = POP_STR();
|
||||
/* FALL THROUGH */
|
||||
case JSOP_FORELEM2:
|
||||
xval = POP_STR();
|
||||
atom = NULL;
|
||||
lval = POP_STR();
|
||||
@@ -1243,6 +1241,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
LOCAL_ASSERT(*pc == JSOP_IFEQ);
|
||||
oplen = js_CodeSpec[JSOP_IFEQ].length;
|
||||
len = GET_JUMP_OFFSET(pc);
|
||||
do_forinbody:
|
||||
js_printf(jp, "\tfor (%s%s",
|
||||
(sn && SN_TYPE(sn) == SRC_VAR) ? "var " : "", lval);
|
||||
if (atom)
|
||||
@@ -1260,6 +1259,32 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
todo = -2;
|
||||
break;
|
||||
|
||||
case JSOP_FORELEM2:
|
||||
pc++;
|
||||
LOCAL_ASSERT(*pc == JSOP_IFEQ);
|
||||
len = js_CodeSpec[JSOP_IFEQ].length;
|
||||
/*
|
||||
this gets a little wacky. Only the length of the body of
|
||||
the for statement PLUS the indexing expression is known
|
||||
here, so we pass it to the enumelem decompilation via this
|
||||
local. Hopefully no intervening code can mess up the value?
|
||||
*/
|
||||
forelem_tgt = pc + GET_JUMP_OFFSET(pc);
|
||||
break;
|
||||
|
||||
case JSOP_ENUMELEM:
|
||||
/*
|
||||
the stack has the object and the index expression.
|
||||
The for body length can now be adjusted to account for
|
||||
the length of the indexing epxression.
|
||||
*/
|
||||
atom = NULL;
|
||||
xval = POP_STR();
|
||||
lval = POP_STR();
|
||||
rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]);
|
||||
len = forelem_tgt - pc;
|
||||
goto do_forinbody;
|
||||
|
||||
case JSOP_DUP2:
|
||||
rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-2]);
|
||||
todo = SprintPut(&ss->sprinter, rval, strlen(rval));
|
||||
|
||||
@@ -177,7 +177,7 @@ OPDEF(JSOP_VARDEC, 102,"vardec", NULL, 3, 0, 1, 10, JOF_QVAR |
|
||||
OPDEF(JSOP_TOOBJECT, 103,"toobject", NULL, 1, 1, 1, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_FORNAME2, 104,"forname2", NULL, 3, 0, 1, 0, JOF_CONST|JOF_NAME|JOF_SET|JOF_FOR2)
|
||||
OPDEF(JSOP_FORPROP2, 105,"forprop2", NULL, 3, 1, 1, 0, JOF_CONST|JOF_PROP|JOF_SET|JOF_FOR2)
|
||||
OPDEF(JSOP_FORELEM2, 106,"forelem2", NULL, 1, 2, 1, 0, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_FOR2)
|
||||
OPDEF(JSOP_FORELEM2, 106,"forelem2", NULL, 1, 2, 4, 0, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_FOR2)
|
||||
OPDEF(JSOP_POP2, 107,"pop2", NULL, 1, 2, 0, 0, JOF_BYTE)
|
||||
|
||||
/* ECMA-complaint assignment ops. */
|
||||
@@ -214,4 +214,5 @@ OPDEF(JSOP_DEFAULT, 120,"default", NULL, 3, 1, 0, 0, JOF_JUMP)
|
||||
/*
|
||||
* ECMA-compliant call to eval op
|
||||
*/
|
||||
OPDEF(JSOP_CALLSPECIAL,121,"callspecial",NULL, 3, -1, 1, 11, JOF_UINT16)
|
||||
OPDEF(JSOP_CALLSPECIAL,121,"callspecial",NULL, 3, -1, 1, 11, JOF_UINT16)
|
||||
OPDEF(JSOP_ENUMELEM, 122,"enumelem", NULL, 1, 3, 0, 1, JOF_BYTE |JOF_ELEM|JOF_SET)
|
||||
|
||||
Reference in New Issue
Block a user