- Decorate PN_LIST nodes with pn_extra flags whose constant names describe

their meaning better, adding a flag for 'for (var x ... in o)'.
- Set these flags from the parser, in a future-proof way (|=, not =, given the
  zero initialized pn_extra flags member of pn_list).
- Test list flags in the code generator, including PNX_FORINVAR in conjunction
  with whether the for..in loop is of the oddball form 'for (var x = i in o)'.
- Thereby fix failure to emit a JSOP_DEFVAR in the case that a function wraps
  a for..in loop in a with statement (252892, r=shaver).
- The same fix cured another bug, not reported, where 'for (var x = i in o)'
  in a function without any 'with' statement would emit the wrong opcode
  (JSOP_FORNAME, not JSOP_FORVAR).


git-svn-id: svn://10.0.0.236/trunk@160101 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
brendan%mozilla.org 2004-07-30 00:00:09 +00:00
parent 11e778476e
commit 340570a787
3 changed files with 77 additions and 24 deletions

View File

@ -2978,15 +2978,29 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
stmtInfo.type = STMT_FOR_IN_LOOP;
noteIndex = -1;
/* If the left part is var x = i, bind x, evaluate i, and pop. */
/*
* If the left part is 'var x', emit code to define x if necessary
* using a prolog opcode, but do not emit a pop. If the left part
* is 'var x = i', emit prolog code to define x if necessary; then
* emit code to evaluate i, assign the result to x, and pop the
* result off the stack.
*
* All the logic to do this is implemented in the outer switch's
* TOK_VAR case, conditioned on pn_extra flags set by the parser.
*
* In the 'for (var x = i in o) ...' case, the js_EmitTree(...pn3)
* called here will generate the SRC_VAR note for the assignment
* op that sets x = i, hoisting the initialized var declaration
* out of the loop: 'var x = i; for (x in o) ...'.
*
* In the 'for (var x in o) ...' case, nothing but the prolog op
* (if needed) should be generated here, we must emit the SRC_VAR
* just before the JSOP_FOR* opcode in the switch on pn3->pn_type
* a bit below, so nothing is hoisted: 'for (var x in o) ...'.
*/
pn3 = pn2->pn_left;
if (pn3->pn_type == TOK_VAR && pn3->pn_head->pn_expr) {
if (!js_EmitTree(cx, cg, pn3))
return JS_FALSE;
/* Set pn3 to the variable name, to avoid another var note. */
pn3 = pn3->pn_head;
JS_ASSERT(pn3->pn_type == TOK_NAME);
}
if (pn3->pn_type == TOK_VAR && !js_EmitTree(cx, cg, pn3))
return JS_FALSE;
/* Emit a push to allocate the iterator. */
if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
@ -3005,14 +3019,26 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
switch (pn3->pn_type) {
case TOK_VAR:
pn3 = pn3->pn_head;
if (js_NewSrcNote(cx, cg, SRC_VAR) < 0)
JS_ASSERT(pn3->pn_type == TOK_NAME);
if (!pn3->pn_expr && js_NewSrcNote(cx, cg, SRC_VAR) < 0)
return JS_FALSE;
/* FALL THROUGH */
case TOK_NAME:
pn3->pn_op = JSOP_FORNAME;
if (!LookupArgOrVar(cx, &cg->treeContext, pn3))
return JS_FALSE;
op = pn3->pn_op;
if (pn3->pn_slot >= 0) {
op = pn3->pn_op;
switch (op) {
case JSOP_GETARG: /* FALL THROUGH */
case JSOP_SETARG: op = JSOP_FORARG; break;
case JSOP_GETVAR: /* FALL THROUGH */
case JSOP_SETVAR: op = JSOP_FORVAR; break;
default: JS_ASSERT(0);
}
} else {
pn3->pn_op = JSOP_FORNAME;
if (!LookupArgOrVar(cx, &cg->treeContext, pn3))
return JS_FALSE;
op = pn3->pn_op;
}
if (pn3->pn_slot >= 0) {
if (pn3->pn_attrs & JSPROP_READONLY)
op = JSOP_GETVAR;
@ -3589,6 +3615,23 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
return JS_FALSE;
}
}
/*
* 'for (var x in o) ...' and 'for (var x = i in o) ...' call the
* TOK_VAR case, but only the initialized case (a strange one that
* falls out of ECMA-262's grammar) wants to run past this point.
* Both cases must conditionally emit a JSOP_DEFVAR, above. Note
* that the parser error-checks to ensure that pn->pn_count is 1.
*
* XXX Narcissus keeps track of variable declarations in the node
* for the script being compiled, so there's no need to share any
* conditional prolog code generation there. We could do likewise,
* but it's a big change, requiring extra allocation, so probably
* not worth the trouble for SpiderMonkey.
*/
if ((pn->pn_extra & PNX_FORINVAR) && !pn2->pn_expr)
break;
if (pn2 == pn->pn_head &&
js_NewSrcNote(cx, cg,
(pn->pn_op == JSOP_DEFCONST)
@ -3616,7 +3659,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
return JS_FALSE;
}
}
if (pn->pn_extra) {
if (pn->pn_extra & PNX_POPVAR) {
if (js_Emit1(cx, cg, JSOP_POP) < 0)
return JS_FALSE;
}
@ -4200,7 +4243,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
atomIndex++;
}
if (pn->pn_extra) {
if (pn->pn_extra & PNX_ENDCOMMA) {
/* Emit a source note so we know to decompile an extra comma. */
if (js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0)
return JS_FALSE;

View File

@ -1508,9 +1508,13 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
}
if (pn1->pn_type == TOK_VAR) {
/* Tell js_EmitTree(TOK_VAR) to generate a final POP. */
pn1->pn_extra = JS_TRUE;
/* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
pn1->pn_extra |= PNX_FORINVAR;
/* Generate a final POP only if the var has an initializer. */
pn2 = pn1->pn_head;
if (pn2->pn_expr)
pn1->pn_extra |= PNX_POPVAR;
} else {
pn2 = pn1;
}
@ -1830,7 +1834,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
return NULL;
/* Tell js_EmitTree to generate a final POP. */
pn->pn_extra = JS_TRUE;
pn->pn_extra |= PNX_POPVAR;
break;
case TOK_RETURN:
@ -1996,7 +2000,7 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
if (!pn)
return NULL;
pn->pn_op = CURRENT_TOKEN(ts).t_op;
pn->pn_extra = JS_FALSE; /* assume no JSOP_POP needed */
pn->pn_extra = 0; /* assume no JSOP_POP needed */
PN_INIT_LIST(pn);
/*
@ -2836,7 +2840,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
if (!pn)
return NULL;
pn->pn_type = TOK_RB;
pn->pn_extra = JS_FALSE;
pn->pn_extra = 0;
#if JS_HAS_SHARP_VARS
if (defsharp) {
@ -2855,7 +2859,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
tt = js_PeekToken(cx, ts);
ts->flags &= ~TSF_REGEXP;
if (tt == TOK_RB) {
pn->pn_extra = JS_TRUE;
pn->pn_extra |= PNX_ENDCOMMA;
break;
}

View File

@ -89,6 +89,8 @@ JS_BEGIN_EXTERN_C
* TOK_FOR binary pn_left: either
* for/in loop: a binary TOK_IN node with
* pn_left: TOK_VAR or TOK_NAME to left of 'in'
* if TOK_VAR, its pn_extra may have PNX_POPVAR
* and PNX_FORINVAR bits set
* pn_right: object expr to right of 'in'
* for(;;) loop: a ternary TOK_RESERVED node with
* pn_kid1: init expr before first ';'
@ -163,7 +165,7 @@ JS_BEGIN_EXTERN_C
* TOK_RB list pn_head: list of pn_count array element exprs
* [,,] holes are represented by TOK_COMMA nodes
* #n=[...] produces TOK_DEFSHARP at head of list
* pn_extra: true if extra comma at end
* pn_extra: PN_ENDCOMMA if extra comma at end
* TOK_RC list pn_head: list of pn_count TOK_COLON nodes where
* each has pn_left: property id, pn_right: value
* #n={...} produces TOK_DEFSHARP at head of list
@ -258,8 +260,12 @@ struct JSParseNode {
#define pn_dval pn_u.dval
/* PN_LIST pn_extra flags. */
#define PNX_STRCAT 0x1 /* TOK_PLUS list has string term */
#define PNX_CANTFOLD 0x2 /* TOK_PLUS list has unfoldable term */
#define PNX_STRCAT 0x01 /* TOK_PLUS list has string term */
#define PNX_CANTFOLD 0x02 /* TOK_PLUS list has unfoldable term */
#define PNX_POPVAR 0x04 /* TOK_VAR last result needs popping */
#define PNX_FORINVAR 0x08 /* TOK_VAR is left kid of TOK_IN node,
which is left kid of TOK_FOR */
#define PNX_ENDCOMMA 0x10 /* array literal has comma at end */
/*
* Move pn2 into pn, preserving pn->pn_pos and pn->pn_offset and handing off