From e35e5fc1a2f7b4830ed4bfd5f5c84bc355cb6921 Mon Sep 17 00:00:00 2001 From: "rogerl%netscape.com" Date: Thu, 28 Jan 1999 00:07:24 +0000 Subject: [PATCH] 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 --- mozilla/js/src/jsemit.c | 39 ++++++++++++++++++----- mozilla/js/src/jsinterp.c | 62 ++++++++++++++++++++++++------------- mozilla/js/src/jsopcode.c | 31 +++++++++++++++++-- mozilla/js/src/jsopcode.tbl | 5 +-- 4 files changed, 102 insertions(+), 35 deletions(-) diff --git a/mozilla/js/src/jsemit.c b/mozilla/js/src/jsemit.c index f1954e4b200..640b9529055 100644 --- a/mozilla/js/src/jsemit.c +++ b/mozilla/js/src/jsemit.c @@ -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; diff --git a/mozilla/js/src/jsinterp.c b/mozilla/js/src/jsinterp.c index 72863e8333a..017d2c1dd9d 100644 --- a/mozilla/js/src/jsinterp.c +++ b/mozilla/js/src/jsinterp.c @@ -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; diff --git a/mozilla/js/src/jsopcode.c b/mozilla/js/src/jsopcode.c index b3b0a464419..52de48c40f1 100644 --- a/mozilla/js/src/jsopcode.c +++ b/mozilla/js/src/jsopcode.c @@ -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)); diff --git a/mozilla/js/src/jsopcode.tbl b/mozilla/js/src/jsopcode.tbl index 18062988ddb..71581bfa42f 100644 --- a/mozilla/js/src/jsopcode.tbl +++ b/mozilla/js/src/jsopcode.tbl @@ -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)