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:
rogerl%netscape.com
1999-01-28 00:07:24 +00:00
parent 72970118bb
commit e35e5fc1a2
4 changed files with 102 additions and 35 deletions

View File

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

View File

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

View File

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

View File

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