1892 |
|
|
1893 |
ATOM_LIST_SEARCH(ale, &cg->upvarList, atom); |
ATOM_LIST_SEARCH(ale, &cg->upvarList, atom); |
1894 |
if (!ale) { |
if (!ale) { |
1895 |
uint32 cookie, length, *vector; |
uint32 length, *vector; |
1896 |
|
|
1897 |
ale = js_IndexAtom(cx, atom, &cg->upvarList); |
ale = js_IndexAtom(cx, atom, &cg->upvarList); |
1898 |
if (!ale) |
if (!ale) |
1919 |
return JS_TRUE; |
return JS_TRUE; |
1920 |
} |
} |
1921 |
|
|
1922 |
cookie = MAKE_UPVAR_COOKIE(1, index); |
JS_ASSERT(cg->staticDepth > caller->fun->u.i.script->staticDepth); |
1923 |
cg->upvarMap.vector[ALE_INDEX(ale)] = cookie; |
uintN skip = cg->staticDepth - caller->fun->u.i.script->staticDepth; |
1924 |
|
cg->upvarMap.vector[ALE_INDEX(ale)] = MAKE_UPVAR_COOKIE(skip, index); |
1925 |
} |
} |
1926 |
|
|
1927 |
pn->pn_op = JSOP_GETUPVAR; |
pn->pn_op = JSOP_GETUPVAR; |
2079 |
break; |
break; |
2080 |
|
|
2081 |
case PN_LIST: |
case PN_LIST: |
2082 |
if (pn->pn_type == TOK_NEW || |
if (pn->pn_op == JSOP_NOP || |
2083 |
pn->pn_type == TOK_LP || |
pn->pn_op == JSOP_OR || pn->pn_op == JSOP_AND || |
2084 |
pn->pn_type == TOK_LB || |
pn->pn_op == JSOP_STRICTEQ || pn->pn_op == JSOP_STRICTNE) { |
2085 |
pn->pn_type == TOK_RB || |
/* |
2086 |
pn->pn_type == TOK_RC) { |
* Non-operators along with ||, &&, ===, and !== never invoke |
2087 |
|
* toString or valueOf. |
2088 |
|
*/ |
2089 |
|
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) |
2090 |
|
ok &= CheckSideEffects(cx, cg, pn2, answer); |
2091 |
|
} else { |
2092 |
/* |
/* |
2093 |
* All invocation operations (construct: TOK_NEW, call: TOK_LP) |
* All invocation operations (construct: TOK_NEW, call: TOK_LP) |
2094 |
* are presumed to be useful, because they may have side effects |
* are presumed to be useful, because they may have side effects |
2100 |
* (the JSOP_ARGUMENTS special case below, in the PN_BINARY case, |
* (the JSOP_ARGUMENTS special case below, in the PN_BINARY case, |
2101 |
* does not apply here: arguments[i][j] might invoke a getter). |
* does not apply here: arguments[i][j] might invoke a getter). |
2102 |
* |
* |
2103 |
* Array and object initializers (TOK_RB and TOK_RC lists) must be |
* Likewise, array and object initialisers may call prototype |
2104 |
* considered useful, because they are sugar for constructor calls |
* setters (the __defineSetter__ built-in, and writable __proto__ |
2105 |
* (to Array and Object, respectively). |
* on Array.prototype create this hazard). Initialiser list nodes |
2106 |
|
* have JSOP_NEWINIT in their pn_op. |
2107 |
*/ |
*/ |
2108 |
*answer = JS_TRUE; |
*answer = JS_TRUE; |
|
} else { |
|
|
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) |
|
|
ok &= CheckSideEffects(cx, cg, pn2, answer); |
|
2109 |
} |
} |
2110 |
break; |
break; |
2111 |
|
|
2142 |
} |
} |
2143 |
} |
} |
2144 |
} else { |
} else { |
2145 |
/* |
if (pn->pn_op == JSOP_OR || pn->pn_op == JSOP_AND || |
2146 |
* We can't easily prove that neither operand ever denotes an |
pn->pn_op == JSOP_STRICTEQ || pn->pn_op == JSOP_STRICTNE) { |
2147 |
* object with a toString or valueOf method. |
/* |
2148 |
*/ |
* ||, &&, ===, and !== do not convert their operands via |
2149 |
*answer = JS_TRUE; |
* toString or valueOf method calls. |
2150 |
|
*/ |
2151 |
|
ok = CheckSideEffects(cx, cg, pn->pn_left, answer) && |
2152 |
|
CheckSideEffects(cx, cg, pn->pn_right, answer); |
2153 |
|
} else { |
2154 |
|
/* |
2155 |
|
* We can't easily prove that neither operand ever denotes an |
2156 |
|
* object with a toString or valueOf method. |
2157 |
|
*/ |
2158 |
|
*answer = JS_TRUE; |
2159 |
|
} |
2160 |
} |
} |
2161 |
break; |
break; |
2162 |
|
|
2187 |
} |
} |
2188 |
break; |
break; |
2189 |
|
|
2190 |
|
case TOK_UNARYOP: |
2191 |
|
if (pn->pn_op == JSOP_NOT) { |
2192 |
|
/* ! does not convert its operand via toString or valueOf. */ |
2193 |
|
ok = CheckSideEffects(cx, cg, pn->pn_kid, answer); |
2194 |
|
break; |
2195 |
|
} |
2196 |
|
/* FALL THROUGH */ |
2197 |
|
|
2198 |
default: |
default: |
2199 |
/* |
/* |
2200 |
* All of TOK_INC, TOK_DEC, TOK_THROW, TOK_YIELD, and TOK_DEFSHARP |
* All of TOK_INC, TOK_DEC, TOK_THROW, TOK_YIELD, and TOK_DEFSHARP |
2366 |
do_indexconst: { |
do_indexconst: { |
2367 |
JSAtomListElement *ale; |
JSAtomListElement *ale; |
2368 |
jsatomid atomIndex; |
jsatomid atomIndex; |
2369 |
|
|
2370 |
ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList); |
ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList); |
2371 |
if (!ale) |
if (!ale) |
2372 |
return JS_FALSE; |
return JS_FALSE; |
2373 |
atomIndex = ALE_INDEX(ale); |
atomIndex = ALE_INDEX(ale); |
2374 |
return EmitSlotIndexOp(cx, op, pn2->pn_slot, atomIndex, cg); |
return EmitSlotIndexOp(cx, op, pn2->pn_slot, atomIndex, cg); |
2375 |
} |
} |
2376 |
|
|
2377 |
default:; |
default:; |
2378 |
} |
} |
2379 |
} |
} |
3653 |
} |
} |
3654 |
|
|
3655 |
/* |
/* |
3656 |
* A destructuring initialiser assignment preceded by var is |
* A destructuring initialiser assignment preceded by var will |
3657 |
* always evaluated promptly, even if it is to the left of 'in' |
* never occur to the left of 'in' in a for-in loop. As with 'for |
3658 |
* in a for-in loop. As with 'for (var x = i in o)...', this |
* (var x = i in o)...', this will cause the entire 'var [a, b] = |
3659 |
* will cause the entire 'var [a, b] = i' to be hoisted out of |
* i' to be hoisted out of the loop. |
|
* the head of the loop. |
|
3660 |
*/ |
*/ |
3661 |
JS_ASSERT(pn2->pn_type == TOK_ASSIGN); |
JS_ASSERT(pn2->pn_type == TOK_ASSIGN); |
3662 |
if (pn->pn_count == 1 && !forInLet) { |
JS_ASSERT(!forInVar); |
3663 |
|
if (pn->pn_count == 1) { |
3664 |
/* |
/* |
3665 |
* If this is the only destructuring assignment in the list, |
* If this is the only destructuring assignment in the list, |
3666 |
* try to optimize to a group assignment. If we're in a let |
* try to optimize to a group assignment. If we're in a let |
3670 |
JS_ASSERT(noteIndex < 0 && !pn2->pn_next); |
JS_ASSERT(noteIndex < 0 && !pn2->pn_next); |
3671 |
op = JSOP_POP; |
op = JSOP_POP; |
3672 |
if (!MaybeEmitGroupAssignment(cx, cg, |
if (!MaybeEmitGroupAssignment(cx, cg, |
3673 |
inLetHead ? JSOP_POP : |
inLetHead ? JSOP_POP : PN_OP(pn), |
|
PN_OP(pn), |
|
3674 |
pn2, &op)) { |
pn2, &op)) { |
3675 |
return JS_FALSE; |
return JS_FALSE; |
3676 |
} |
} |
3684 |
if (!EmitDestructuringDecls(cx, cg, PN_OP(pn), pn3)) |
if (!EmitDestructuringDecls(cx, cg, PN_OP(pn), pn3)) |
3685 |
return JS_FALSE; |
return JS_FALSE; |
3686 |
|
|
|
#if JS_HAS_BLOCK_SCOPE |
|
|
/* |
|
|
* If this is a 'for (let [x, y] = i in o) ...' let declaration, |
|
|
* throw away i if it is a useless expression. |
|
|
*/ |
|
|
if (forInLet) { |
|
|
JSBool useful = JS_FALSE; |
|
|
|
|
|
JS_ASSERT(pn->pn_count == 1); |
|
|
if (!CheckSideEffects(cx, cg, pn2->pn_right, &useful)) |
|
|
return JS_FALSE; |
|
|
if (!useful) |
|
|
return JS_TRUE; |
|
|
} |
|
|
#endif |
|
|
|
|
3687 |
if (!js_EmitTree(cx, cg, pn2->pn_right)) |
if (!js_EmitTree(cx, cg, pn2->pn_right)) |
3688 |
return JS_FALSE; |
return JS_FALSE; |
3689 |
|
|
|
#if JS_HAS_BLOCK_SCOPE |
|
|
/* |
|
|
* The expression i in 'for (let [x, y] = i in o) ...', which is |
|
|
* pn2->pn_right above, appears to have side effects. We've just |
|
|
* emitted code to evaluate i, but we must not destructure i yet. |
|
|
* Let the TOK_FOR: code in js_EmitTree do the destructuring to |
|
|
* emit the right combination of source notes and bytecode for the |
|
|
* decompiler. |
|
|
* |
|
|
* This has the effect of hoisting the evaluation of i out of the |
|
|
* for-in loop, without hoisting the let variables, which must of |
|
|
* course be scoped by the loop. Set PNX_POPVAR to cause JSOP_POP |
|
|
* to be emitted, just before returning from this function. |
|
|
*/ |
|
|
if (forInVar) { |
|
|
pn->pn_extra |= PNX_POPVAR; |
|
|
if (forInLet) |
|
|
break; |
|
|
} |
|
|
#endif |
|
|
|
|
3690 |
/* |
/* |
3691 |
* Veto pn->pn_op if inLetHead to avoid emitting a SRC_DESTRUCT |
* Veto pn->pn_op if inLetHead to avoid emitting a SRC_DESTRUCT |
3692 |
* that's redundant with respect to the SRC_DECL/SRC_DECL_LET that |
* that's redundant with respect to the SRC_DECL/SRC_DECL_LET that |
3721 |
|
|
3722 |
pn3 = pn2->pn_expr; |
pn3 = pn2->pn_expr; |
3723 |
if (pn3) { |
if (pn3) { |
3724 |
#if JS_HAS_BLOCK_SCOPE |
JS_ASSERT(!forInVar); |
|
/* |
|
|
* If this is a 'for (let x = i in o) ...' let declaration, |
|
|
* throw away i if it is a useless expression. |
|
|
*/ |
|
|
if (forInLet) { |
|
|
JSBool useful = JS_FALSE; |
|
|
|
|
|
JS_ASSERT(pn->pn_count == 1); |
|
|
if (!CheckSideEffects(cx, cg, pn3, &useful)) |
|
|
return JS_FALSE; |
|
|
if (!useful) |
|
|
return JS_TRUE; |
|
|
} |
|
|
#endif |
|
|
|
|
3725 |
if (op == JSOP_SETNAME) { |
if (op == JSOP_SETNAME) { |
3726 |
JS_ASSERT(!let); |
JS_ASSERT(!let); |
3727 |
EMIT_INDEX_OP(JSOP_BINDNAME, atomIndex); |
EMIT_INDEX_OP(JSOP_BINDNAME, atomIndex); |
3741 |
tc->topStmt = stmt->down; |
tc->topStmt = stmt->down; |
3742 |
tc->topScopeStmt = scopeStmt->downScope; |
tc->topScopeStmt = scopeStmt->downScope; |
3743 |
} |
} |
3744 |
#ifdef __GNUC__ |
# ifdef __GNUC__ |
3745 |
else { |
else stmt = scopeStmt = NULL; /* quell GCC overwarning */ |
3746 |
stmt = scopeStmt = NULL; /* quell GCC overwarning */ |
# endif |
|
} |
|
|
#endif |
|
3747 |
#endif |
#endif |
3748 |
|
|
3749 |
oldflags = cg->treeContext.flags; |
oldflags = cg->treeContext.flags; |
3762 |
} |
} |
3763 |
|
|
3764 |
/* |
/* |
3765 |
* 'for (var x in o) ...' and 'for (var x = i in o) ...' call the |
* The parser rewrites 'for (var x = i in o)' to hoist 'var x = i' -- |
3766 |
* TOK_VAR case, but only the initialized case (a strange one that |
* likewise 'for (let x = i in o)' becomes 'i; for (let x in o)' using |
3767 |
* falls out of ECMA-262's grammar) wants to run past this point. |
* a TOK_SEQ node to make the two statements appear as one. Therefore |
3768 |
* Both cases must conditionally emit a JSOP_DEFVAR, above. Note |
* if this declaration is part of a for-in loop head, we do not need to |
3769 |
* that the parser error-checks to ensure that pn->pn_count is 1. |
* emit op or any source note. Our caller, the TOK_FOR/TOK_IN case in |
3770 |
* |
* js_EmitTree, will annotate appropriately. |
|
* 'for (let x = i in o) ...' must evaluate i before the loop, and |
|
|
* subject it to useless expression elimination. The variable list |
|
|
* in pn is a single let declaration if pn_op == JSOP_NOP. We test |
|
|
* the let local in order to break early in this case, as well as in |
|
|
* the 'for (var x in o)' case. |
|
|
* |
|
|
* 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. |
|
3771 |
*/ |
*/ |
3772 |
JS_ASSERT(pn3 == pn2->pn_expr); |
JS_ASSERT(pn3 == pn2->pn_expr); |
3773 |
if (forInVar && (!pn3 || let)) { |
if (forInVar) { |
3774 |
JS_ASSERT(pn->pn_count == 1); |
JS_ASSERT(pn->pn_count == 1); |
3775 |
|
JS_ASSERT(!pn3); |
3776 |
break; |
break; |
3777 |
} |
} |
3778 |
|
|
3850 |
js_Emit1(cx, cg, JSOP_NOP) >= 0; |
js_Emit1(cx, cg, JSOP_NOP) >= 0; |
3851 |
} |
} |
3852 |
|
|
3853 |
|
/* See the SRC_FOR source note offsetBias comments later in this file. */ |
3854 |
|
JS_STATIC_ASSERT(JSOP_NOP_LENGTH == 1); |
3855 |
|
JS_STATIC_ASSERT(JSOP_POP_LENGTH == 1); |
3856 |
|
|
3857 |
JSBool |
JSBool |
3858 |
js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) |
js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) |
3859 |
{ |
{ |
3937 |
cg2->staticDepth = cg->staticDepth + 1; |
cg2->staticDepth = cg->staticDepth + 1; |
3938 |
cg2->parent = cg; |
cg2->parent = cg; |
3939 |
|
|
3940 |
/* We metered the max scope depth when parsed the function. */ |
/* We metered the max scope depth when parsed the function. */ |
3941 |
JS_SCOPE_DEPTH_METERING(cg2->treeContext.maxScopeDepth = (uintN) -1); |
JS_SCOPE_DEPTH_METERING(cg2->treeContext.maxScopeDepth = (uintN) -1); |
3942 |
if (!js_EmitFunctionScript(cx, cg2, pn->pn_body)) { |
if (!js_EmitFunctionScript(cx, cg2, pn->pn_body)) { |
3943 |
pn = NULL; |
pn = NULL; |
4177 |
js_PushStatement(&cg->treeContext, &stmtInfo, STMT_FOR_LOOP, top); |
js_PushStatement(&cg->treeContext, &stmtInfo, STMT_FOR_LOOP, top); |
4178 |
|
|
4179 |
if (pn2->pn_type == TOK_IN) { |
if (pn2->pn_type == TOK_IN) { |
|
JSBool emitIFEQ; |
|
|
|
|
4180 |
/* Set stmtInfo type for later testing. */ |
/* Set stmtInfo type for later testing. */ |
4181 |
stmtInfo.type = STMT_FOR_IN_LOOP; |
stmtInfo.type = STMT_FOR_IN_LOOP; |
|
noteIndex = -1; |
|
4182 |
|
|
4183 |
/* |
/* |
4184 |
* If the left part is 'var x', emit code to define x if necessary |
* If the left part is 'var x', emit code to define x if necessary |
4222 |
* destructuring for-in). |
* destructuring for-in). |
4223 |
*/ |
*/ |
4224 |
JS_ASSERT(pn->pn_op == JSOP_ITER); |
JS_ASSERT(pn->pn_op == JSOP_ITER); |
4225 |
if (js_Emit2(cx, cg, PN_OP(pn), (uint8) pn->pn_iflags) < 0) |
if (js_Emit2(cx, cg, JSOP_ITER, (uint8) pn->pn_iflags) < 0) |
4226 |
|
return JS_FALSE; |
4227 |
|
|
4228 |
|
/* Annotate so the decompiler can find the loop-closing jump. */ |
4229 |
|
noteIndex = js_NewSrcNote(cx, cg, SRC_FOR_IN); |
4230 |
|
if (noteIndex < 0) |
4231 |
|
return JS_FALSE; |
4232 |
|
|
4233 |
|
/* |
4234 |
|
* Jump down to the loop condition to minimize overhead assuming at |
4235 |
|
* least one iteration, as the other loop forms do. |
4236 |
|
*/ |
4237 |
|
jmp = EmitJump(cx, cg, JSOP_GOTO, 0); |
4238 |
|
if (jmp < 0) |
4239 |
return JS_FALSE; |
return JS_FALSE; |
4240 |
|
|
4241 |
top = CG_OFFSET(cg); |
top = CG_OFFSET(cg); |
4242 |
SET_STATEMENT_TOP(&stmtInfo, top); |
SET_STATEMENT_TOP(&stmtInfo, top); |
4243 |
|
|
4244 |
|
#ifdef DEBUG |
4245 |
|
intN loopDepth = cg->stackDepth; |
4246 |
|
#endif |
4247 |
|
|
4248 |
/* |
/* |
4249 |
* Compile a JSOP_FOR* bytecode based on the left hand side. |
* Compile a JSOP_FOR* bytecode based on the left hand side. |
4250 |
* |
* |
4255 |
* assignment, so JSOP_SETNAME is not critical here; many similar |
* assignment, so JSOP_SETNAME is not critical here; many similar |
4256 |
* ops could be used -- just not JSOP_NOP (which means 'let'). |
* ops could be used -- just not JSOP_NOP (which means 'let'). |
4257 |
*/ |
*/ |
|
emitIFEQ = JS_TRUE; |
|
4258 |
op = JSOP_SETNAME; |
op = JSOP_SETNAME; |
4259 |
switch (type) { |
switch (type) { |
4260 |
#if JS_HAS_BLOCK_SCOPE |
#if JS_HAS_BLOCK_SCOPE |
4275 |
#else |
#else |
4276 |
JS_ASSERT(pn3->pn_type == TOK_NAME); |
JS_ASSERT(pn3->pn_type == TOK_NAME); |
4277 |
#endif |
#endif |
4278 |
|
/* FALL THROUGH */ |
4279 |
|
|
4280 |
|
case TOK_NAME: |
4281 |
/* |
/* |
4282 |
* Always annotate JSOP_FORLOCAL if given input of the form |
* Always annotate JSOP_FORLOCAL if given input of the form |
4283 |
* 'for (let x in * o)' -- the decompiler must not hoist the |
* 'for (let x in * o)' -- the decompiler must not hoist the |
4284 |
* 'let x' out of the loop head, or x will be bound in the |
* 'let x' out of the loop head, or x will be bound in the |
4285 |
* wrong scope. Likewise, but in this case only for the sake |
* wrong scope. Likewise, but in this case only for the sake |
4286 |
* of higher decompilation fidelity only, do not hoist 'var x' |
* of higher decompilation fidelity only, do not hoist 'var x' |
4287 |
* when given 'for (var x in o)'. But 'for (var x = i in o)' |
* when given 'for (var x in o)'. |
|
* requires hoisting in order to preserve the initializer i. |
|
|
* The decompiler can only handle so much! |
|
4288 |
*/ |
*/ |
4289 |
if (( |
if (( |
4290 |
#if JS_HAS_BLOCK_SCOPE |
#if JS_HAS_BLOCK_SCOPE |
4291 |
type == TOK_LET || |
type == TOK_LET || |
4292 |
#endif |
#endif |
4293 |
!pn3->pn_expr) && |
(type == TOK_VAR && !pn3->pn_expr)) && |
4294 |
js_NewSrcNote2(cx, cg, SRC_DECL, |
js_NewSrcNote2(cx, cg, SRC_DECL, |
4295 |
type == TOK_VAR |
(type == TOK_VAR) |
4296 |
? SRC_DECL_VAR |
? SRC_DECL_VAR |
4297 |
: SRC_DECL_LET) < 0) { |
: SRC_DECL_LET) < 0) { |
4298 |
return JS_FALSE; |
return JS_FALSE; |
4299 |
} |
} |
|
/* FALL THROUGH */ |
|
|
case TOK_NAME: |
|
4300 |
if (pn3->pn_slot >= 0) { |
if (pn3->pn_slot >= 0) { |
4301 |
op = PN_OP(pn3); |
op = PN_OP(pn3); |
4302 |
switch (op) { |
switch (op) { |
4317 |
if (pn3->pn_slot >= 0) { |
if (pn3->pn_slot >= 0) { |
4318 |
if (pn3->pn_const) { |
if (pn3->pn_const) { |
4319 |
JS_ASSERT(op == JSOP_FORLOCAL); |
JS_ASSERT(op == JSOP_FORLOCAL); |
4320 |
op = JSOP_FORCONST; |
js_ReportCompileErrorNumber(cx, CG_TS(cg), pn3, JSREPORT_ERROR, |
4321 |
|
JSMSG_BAD_FOR_LEFTSIDE); |
4322 |
|
return JS_FALSE; |
4323 |
} |
} |
4324 |
atomIndex = (jsatomid) pn3->pn_slot; |
atomIndex = (jsatomid) pn3->pn_slot; |
4325 |
EMIT_UINT16_IMM_OP(op, atomIndex); |
EMIT_UINT16_IMM_OP(op, atomIndex); |
4330 |
break; |
break; |
4331 |
|
|
4332 |
case TOK_DOT: |
case TOK_DOT: |
4333 |
|
/* |
4334 |
|
* 'for (o.p in q)' can use JSOP_FORPROP only if evaluating 'o' |
4335 |
|
* has no side effects. |
4336 |
|
*/ |
4337 |
useful = JS_FALSE; |
useful = JS_FALSE; |
4338 |
if (!CheckSideEffects(cx, cg, pn3->pn_expr, &useful)) |
if (!CheckSideEffects(cx, cg, pn3->pn_expr, &useful)) |
4339 |
return JS_FALSE; |
return JS_FALSE; |
4345 |
/* FALL THROUGH */ |
/* FALL THROUGH */ |
4346 |
|
|
4347 |
#if JS_HAS_DESTRUCTURING |
#if JS_HAS_DESTRUCTURING |
|
case TOK_RB: |
|
|
case TOK_RC: |
|
4348 |
destructuring_for: |
destructuring_for: |
4349 |
#endif |
#endif |
4350 |
#if JS_HAS_XML_SUPPORT |
default: |
|
case TOK_UNARYOP: |
|
|
#endif |
|
|
#if JS_HAS_LVALUE_RETURN |
|
|
case TOK_LP: |
|
|
#endif |
|
|
case TOK_LB: |
|
|
/* |
|
|
* 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). |
|
|
*/ |
|
|
emitIFEQ = JS_FALSE; |
|
4351 |
if (js_Emit1(cx, cg, JSOP_FORELEM) < 0) |
if (js_Emit1(cx, cg, JSOP_FORELEM) < 0) |
4352 |
return JS_FALSE; |
return JS_FALSE; |
4353 |
|
JS_ASSERT(cg->stackDepth >= 3); |
|
/* |
|
|
* Emit a SRC_WHILE note with offset telling the distance to |
|
|
* the loop-closing jump (we can't reckon from the branch at |
|
|
* the top of the loop, because the loop-closing jump might |
|
|
* need to be an extended jump, independent of whether the |
|
|
* branch is short or long). |
|
|
*/ |
|
|
noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE); |
|
|
if (noteIndex < 0) |
|
|
return JS_FALSE; |
|
|
beq = EmitJump(cx, cg, JSOP_IFEQ, 0); |
|
|
if (beq < 0) |
|
|
return JS_FALSE; |
|
4354 |
|
|
4355 |
#if JS_HAS_DESTRUCTURING |
#if JS_HAS_DESTRUCTURING |
4356 |
if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) { |
if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) { |
4358 |
return JS_FALSE; |
return JS_FALSE; |
4359 |
if (js_Emit1(cx, cg, JSOP_POP) < 0) |
if (js_Emit1(cx, cg, JSOP_POP) < 0) |
4360 |
return JS_FALSE; |
return JS_FALSE; |
4361 |
break; |
} else |
|
} |
|
4362 |
#endif |
#endif |
4363 |
#if JS_HAS_LVALUE_RETURN |
#if JS_HAS_LVALUE_RETURN |
4364 |
if (pn3->pn_type == TOK_LP) { |
if (pn3->pn_type == TOK_LP) { |
4367 |
return JS_FALSE; |
return JS_FALSE; |
4368 |
if (js_Emit1(cx, cg, JSOP_ENUMELEM) < 0) |
if (js_Emit1(cx, cg, JSOP_ENUMELEM) < 0) |
4369 |
return JS_FALSE; |
return JS_FALSE; |
4370 |
break; |
} else |
|
} |
|
4371 |
#endif |
#endif |
4372 |
#if JS_HAS_XML_SUPPORT |
#if JS_HAS_XML_SUPPORT |
4373 |
if (pn3->pn_type == TOK_UNARYOP) { |
if (pn3->pn_type == TOK_UNARYOP) { |
4376 |
return JS_FALSE; |
return JS_FALSE; |
4377 |
if (js_Emit1(cx, cg, JSOP_ENUMELEM) < 0) |
if (js_Emit1(cx, cg, JSOP_ENUMELEM) < 0) |
4378 |
return JS_FALSE; |
return JS_FALSE; |
4379 |
break; |
} else |
|
} |
|
4380 |
#endif |
#endif |
|
|
|
|
/* Now that we're safely past the IFEQ, commit side effects. */ |
|
4381 |
if (!EmitElemOp(cx, pn3, JSOP_ENUMELEM, cg)) |
if (!EmitElemOp(cx, pn3, JSOP_ENUMELEM, cg)) |
4382 |
return JS_FALSE; |
return JS_FALSE; |
4383 |
break; |
break; |
|
|
|
|
default: |
|
|
JS_ASSERT(0); |
|
4384 |
} |
} |
4385 |
|
|
4386 |
if (emitIFEQ) { |
/* The stack should be balanced around the JSOP_FOR* opcode sequence. */ |
4387 |
/* Annotate so the decompiler can find the loop-closing jump. */ |
JS_ASSERT(cg->stackDepth == loopDepth); |
|
noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE); |
|
|
if (noteIndex < 0) |
|
|
return JS_FALSE; |
|
4388 |
|
|
4389 |
/* Pop and test the loop condition generated by JSOP_FOR*. */ |
/* Set the first srcnote offset so we can find the start of the loop body. */ |
4390 |
beq = EmitJump(cx, cg, JSOP_IFEQ, 0); |
if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, CG_OFFSET(cg) - jmp)) |
4391 |
if (beq < 0) |
return JS_FALSE; |
|
return JS_FALSE; |
|
|
} |
|
4392 |
|
|
4393 |
/* Emit code for the loop body. */ |
/* Emit code for the loop body. */ |
4394 |
if (!js_EmitTree(cx, cg, pn->pn_right)) |
if (!js_EmitTree(cx, cg, pn->pn_right)) |
4395 |
return JS_FALSE; |
return JS_FALSE; |
4396 |
|
|
4397 |
/* Emit the loop-closing jump and fixup all jump offsets. */ |
/* Set loop and enclosing "update" offsets, for continue. */ |
4398 |
jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg)); |
stmt = &stmtInfo; |
4399 |
if (jmp < 0) |
do { |
4400 |
|
stmt->update = CG_OFFSET(cg); |
4401 |
|
} while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL); |
4402 |
|
|
4403 |
|
/* |
4404 |
|
* Fixup the goto that starts the loop to jump down to JSOP_NEXTITER. |
4405 |
|
*/ |
4406 |
|
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); |
4407 |
|
if (js_Emit1(cx, cg, JSOP_NEXTITER) < 0) |
4408 |
|
return JS_FALSE; |
4409 |
|
beq = EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg)); |
4410 |
|
if (beq < 0) |
4411 |
return JS_FALSE; |
return JS_FALSE; |
|
if (beq > 0) |
|
|
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); |
|
4412 |
|
|
4413 |
/* Set the SRC_WHILE note offset so we can find the closing jump. */ |
/* Set the second srcnote offset so we can find the closing jump. */ |
4414 |
JS_ASSERT(noteIndex != -1); |
if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, beq - jmp)) |
|
if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, jmp - beq)) |
|
4415 |
return JS_FALSE; |
return JS_FALSE; |
4416 |
} else { |
} else { |
4417 |
/* C-style for (init; cond; update) ... loop. */ |
/* C-style for (init; cond; update) ... loop. */ |
4445 |
} |
} |
4446 |
cg->treeContext.flags &= ~TCF_IN_FOR_INIT; |
cg->treeContext.flags &= ~TCF_IN_FOR_INIT; |
4447 |
} |
} |
4448 |
|
|
4449 |
|
/* |
4450 |
|
* NB: the SRC_FOR note has offsetBias 1 (JSOP_{NOP,POP}_LENGTH). |
4451 |
|
* Use tmp to hold the biased srcnote "top" offset, which differs |
4452 |
|
* from the top local variable by the length of the JSOP_GOTO{,X} |
4453 |
|
* emitted in between tmp and top if this loop has a condition. |
4454 |
|
*/ |
4455 |
noteIndex = js_NewSrcNote(cx, cg, SRC_FOR); |
noteIndex = js_NewSrcNote(cx, cg, SRC_FOR); |
4456 |
if (noteIndex < 0 || |
if (noteIndex < 0 || js_Emit1(cx, cg, op) < 0) |
|
js_Emit1(cx, cg, op) < 0) { |
|
4457 |
return JS_FALSE; |
return JS_FALSE; |
4458 |
} |
tmp = CG_OFFSET(cg); |
4459 |
|
|
4460 |
if (pn2->pn_kid2) { |
if (pn2->pn_kid2) { |
4461 |
/* Goto the loop condition, which branches back to iterate. */ |
/* Goto the loop condition, which branches back to iterate. */ |
4463 |
if (jmp < 0) |
if (jmp < 0) |
4464 |
return JS_FALSE; |
return JS_FALSE; |
4465 |
} |
} |
4466 |
|
|
4467 |
top = CG_OFFSET(cg); |
top = CG_OFFSET(cg); |
4468 |
SET_STATEMENT_TOP(&stmtInfo, top); |
SET_STATEMENT_TOP(&stmtInfo, top); |
4469 |
|
|
4474 |
/* Set the second note offset so we can find the update part. */ |
/* Set the second note offset so we can find the update part. */ |
4475 |
JS_ASSERT(noteIndex != -1); |
JS_ASSERT(noteIndex != -1); |
4476 |
if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, |
if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, |
4477 |
CG_OFFSET(cg) - top)) { |
CG_OFFSET(cg) - tmp)) { |
4478 |
return JS_FALSE; |
return JS_FALSE; |
4479 |
} |
} |
4480 |
|
|
4494 |
return JS_FALSE; |
return JS_FALSE; |
4495 |
} |
} |
4496 |
#endif |
#endif |
4497 |
if (op == JSOP_POP) { |
if (op == JSOP_POP && !js_EmitTree(cx, cg, pn3)) |
4498 |
if (!js_EmitTree(cx, cg, pn3)) |
return JS_FALSE; |
4499 |
return JS_FALSE; |
|
4500 |
if (js_Emit1(cx, cg, op) < 0) |
/* Always emit the POP or NOP, to help the decompiler. */ |
4501 |
return JS_FALSE; |
if (js_Emit1(cx, cg, op) < 0) |
4502 |
} |
return JS_FALSE; |
4503 |
|
|
4504 |
/* Restore the absolute line number for source note readers. */ |
/* Restore the absolute line number for source note readers. */ |
4505 |
off = (ptrdiff_t) pn->pn_pos.end.lineno; |
off = (ptrdiff_t) pn->pn_pos.end.lineno; |
4512 |
|
|
4513 |
/* Set the first note offset so we can find the loop condition. */ |
/* Set the first note offset so we can find the loop condition. */ |
4514 |
if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, |
if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, |
4515 |
CG_OFFSET(cg) - top)) { |
CG_OFFSET(cg) - tmp)) { |
4516 |
return JS_FALSE; |
return JS_FALSE; |
4517 |
} |
} |
4518 |
|
|
4527 |
|
|
4528 |
/* The third note offset helps us find the loop-closing jump. */ |
/* The third note offset helps us find the loop-closing jump. */ |
4529 |
if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 2, |
if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 2, |
4530 |
CG_OFFSET(cg) - top)) { |
CG_OFFSET(cg) - tmp)) { |
4531 |
return JS_FALSE; |
return JS_FALSE; |
4532 |
} |
} |
4533 |
|
|
4549 |
|
|
4550 |
if (pn2->pn_type == TOK_IN) { |
if (pn2->pn_type == TOK_IN) { |
4551 |
/* |
/* |
4552 |
* JSOP_ENDITER needs a slot to save an exception thrown from the |
* JSOP_ENDITER must have a slot to save an exception thrown from |
4553 |
* body of for-in loop when closing the iterator object. |
* the body of for-in loop when closing the iterator object, and |
4554 |
|
* fortunately it does: the slot that was set by JSOP_NEXTITER to |
4555 |
|
* the return value of iterator.next(). |
4556 |
*/ |
*/ |
4557 |
JS_ASSERT(js_CodeSpec[JSOP_ENDITER].format & JOF_TMPSLOT); |
JS_ASSERT(js_CodeSpec[JSOP_ENDITER].nuses == 2); |
4558 |
if (!NewTryNote(cx, cg, JSTN_ITER, cg->stackDepth, top, |
if (!NewTryNote(cx, cg, JSTRY_ITER, cg->stackDepth, top, CG_OFFSET(cg)) || |
|
CG_OFFSET(cg)) || |
|
4559 |
js_Emit1(cx, cg, JSOP_ENDITER) < 0) { |
js_Emit1(cx, cg, JSOP_ENDITER) < 0) { |
4560 |
return JS_FALSE; |
return JS_FALSE; |
4561 |
} |
} |
4855 |
* (first to last for a given nesting level, inner to outer by level). |
* (first to last for a given nesting level, inner to outer by level). |
4856 |
*/ |
*/ |
4857 |
if (pn->pn_kid2 && |
if (pn->pn_kid2 && |
4858 |
!NewTryNote(cx, cg, JSTN_CATCH, depth, tryStart, tryEnd)) { |
!NewTryNote(cx, cg, JSTRY_CATCH, depth, tryStart, tryEnd)) { |
4859 |
return JS_FALSE; |
return JS_FALSE; |
4860 |
} |
} |
4861 |
|
|
4865 |
* for the try{}finally{} case. |
* for the try{}finally{} case. |
4866 |
*/ |
*/ |
4867 |
if (pn->pn_kid3 && |
if (pn->pn_kid3 && |
4868 |
!NewTryNote(cx, cg, JSTN_FINALLY, depth, tryStart, finallyStart)) { |
!NewTryNote(cx, cg, JSTRY_FINALLY, depth, tryStart, finallyStart)) { |
4869 |
return JS_FALSE; |
return JS_FALSE; |
4870 |
} |
} |
4871 |
break; |
break; |
5087 |
ok = js_PopStatementCG(cx, cg); |
ok = js_PopStatementCG(cx, cg); |
5088 |
break; |
break; |
5089 |
|
|
5090 |
case TOK_BODY: |
case TOK_SEQ: |
5091 |
JS_ASSERT(pn->pn_arity == PN_LIST); |
JS_ASSERT(pn->pn_arity == PN_LIST); |
5092 |
js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BODY, top); |
js_PushStatement(&cg->treeContext, &stmtInfo, STMT_SEQ, top); |
5093 |
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { |
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { |
5094 |
if (!js_EmitTree(cx, cg, pn2)) |
if (!js_EmitTree(cx, cg, pn2)) |
5095 |
return JS_FALSE; |
return JS_FALSE; |
5717 |
default: |
default: |
5718 |
/* |
/* |
5719 |
* If useless, just emit JSOP_TRUE; otherwise convert delete foo() |
* If useless, just emit JSOP_TRUE; otherwise convert delete foo() |
5720 |
* to foo(), true (a comma expression, requiring SRC_PCDELTA, and |
* to foo(), true (a comma expression, requiring SRC_PCDELTA). |
|
* also JSOP_GROUP for correctly parenthesized decompilation). |
|
5721 |
*/ |
*/ |
5722 |
useful = JS_FALSE; |
useful = JS_FALSE; |
5723 |
if (!CheckSideEffects(cx, cg, pn2, &useful)) |
if (!CheckSideEffects(cx, cg, pn2, &useful)) |
5739 |
if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off)) |
if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off)) |
5740 |
return JS_FALSE; |
return JS_FALSE; |
5741 |
} |
} |
|
if (js_Emit1(cx, cg, JSOP_GROUP) < 0) |
|
|
return JS_FALSE; |
|
5742 |
} |
} |
5743 |
break; |
break; |
5744 |
|
|
5847 |
argc = pn->pn_count - 1; |
argc = pn->pn_count - 1; |
5848 |
if (js_Emit3(cx, cg, PN_OP(pn), ARGC_HI(argc), ARGC_LO(argc)) < 0) |
if (js_Emit3(cx, cg, PN_OP(pn), ARGC_HI(argc), ARGC_LO(argc)) < 0) |
5849 |
return JS_FALSE; |
return JS_FALSE; |
5850 |
if (js_Emit1(cx, cg, JSOP_RESUME) < 0) |
if (PN_OP(pn) == JSOP_EVAL) |
|
return JS_FALSE; |
|
|
if (PN_OP(pn) == JSOP_EVAL) |
|
5851 |
EMIT_UINT16_IMM_OP(JSOP_LINENO, pn->pn_pos.begin.lineno); |
EMIT_UINT16_IMM_OP(JSOP_LINENO, pn->pn_pos.begin.lineno); |
5852 |
break; |
break; |
5853 |
} |
} |
6157 |
if (!js_EmitTree(cx, cg, pn->pn_kid)) |
if (!js_EmitTree(cx, cg, pn->pn_kid)) |
6158 |
return JS_FALSE; |
return JS_FALSE; |
6159 |
cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; |
cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; |
|
if (js_Emit1(cx, cg, JSOP_GROUP) < 0) |
|
|
return JS_FALSE; |
|
6160 |
break; |
break; |
6161 |
} |
} |
6162 |
|
|
6367 |
return ok; |
return ok; |
6368 |
} |
} |
6369 |
|
|
6370 |
/* XXX get rid of offsetBias, it's used only by SRC_FOR and SRC_DECL */ |
/* |
6371 |
|
* We should try to get rid of offsetBias (always 0 or 1, where 1 is |
6372 |
|
* JSOP_{NOP,POP}_LENGTH), which is used only by SRC_FOR and SRC_DECL. |
6373 |
|
*/ |
6374 |
JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = { |
JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = { |
6375 |
{"null", 0, 0, 0}, |
{"null", 0, 0, 0}, |
6376 |
{"if", 0, 0, 0}, |
{"if", 0, 0, 0}, |
6377 |
{"if-else", 2, 0, 1}, |
{"if-else", 2, 0, 1}, |
|
{"while", 1, 0, 1}, |
|
6378 |
{"for", 3, 1, 1}, |
{"for", 3, 1, 1}, |
6379 |
|
{"while", 1, 0, 1}, |
6380 |
{"continue", 0, 0, 0}, |
{"continue", 0, 0, 0}, |
6381 |
{"decl", 1, 1, 1}, |
{"decl", 1, 1, 1}, |
6382 |
{"pcdelta", 1, 0, 1}, |
{"pcdelta", 1, 0, 1}, |