Change catch clauses to use lexical scope, etc. (336379, r=mrbkap).

This commit is contained in:
brendan%mozilla.org 2006-08-15 01:29:37 +00:00
parent d50f558cb7
commit 044d51fe52
9 changed files with 281 additions and 283 deletions

View File

@ -248,7 +248,7 @@ MSG_DEF(JSMSG_NEWREGEXP_FLAGGED, 165, 0, JSEXN_TYPEERR, "can't supply flags
MSG_DEF(JSMSG_RESERVED_SLOT_RANGE, 166, 0, JSEXN_RANGEERR, "reserved slot index out of range") MSG_DEF(JSMSG_RESERVED_SLOT_RANGE, 166, 0, JSEXN_RANGEERR, "reserved slot index out of range")
MSG_DEF(JSMSG_CANT_DECODE_PRINCIPALS, 167, 0, JSEXN_INTERNALERR, "can't decode JSPrincipals") MSG_DEF(JSMSG_CANT_DECODE_PRINCIPALS, 167, 0, JSEXN_INTERNALERR, "can't decode JSPrincipals")
MSG_DEF(JSMSG_CANT_SEAL_OBJECT, 168, 1, JSEXN_ERR, "can't seal {0} objects") MSG_DEF(JSMSG_CANT_SEAL_OBJECT, 168, 1, JSEXN_ERR, "can't seal {0} objects")
MSG_DEF(JSMSG_UNUSED169, 169, 0, JSEXN_NONE, "") MSG_DEF(JSMSG_TOO_MANY_CATCH_VARS, 169, 0, JSEXN_SYNTAXERR, "too many catch variables")
MSG_DEF(JSMSG_BAD_XML_MARKUP, 170, 0, JSEXN_SYNTAXERR, "invalid XML markup") MSG_DEF(JSMSG_BAD_XML_MARKUP, 170, 0, JSEXN_SYNTAXERR, "invalid XML markup")
MSG_DEF(JSMSG_BAD_XML_CHARACTER, 171, 0, JSEXN_SYNTAXERR, "illegal XML character") MSG_DEF(JSMSG_BAD_XML_CHARACTER, 171, 0, JSEXN_SYNTAXERR, "illegal XML character")
MSG_DEF(JSMSG_BAD_DEFAULT_XML_NAMESPACE,172,0,JSEXN_SYNTAXERR, "invalid default XML namespace") MSG_DEF(JSMSG_BAD_DEFAULT_XML_NAMESPACE,172,0,JSEXN_SYNTAXERR, "invalid default XML namespace")

View File

@ -1208,15 +1208,29 @@ js_InStatement(JSTreeContext *tc, JSStmtType type)
} }
JSBool JSBool
js_InCatchBlock(JSTreeContext *tc, JSAtom *atom) js_IsGlobalReference(JSTreeContext *tc, JSAtom *atom, JSBool *loopyp)
{ {
JSStmtInfo *stmt; JSStmtInfo *stmt;
JSObject *obj;
JSScope *scope;
*loopyp = JS_FALSE;
for (stmt = tc->topStmt; stmt; stmt = stmt->down) { for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
if (stmt->type == STMT_CATCH && stmt->atom == atom) if (stmt->type == STMT_WITH)
return JS_TRUE; return JS_FALSE;
if (STMT_IS_LOOP(stmt)) {
*loopyp = JS_TRUE;
continue;
}
if (stmt->flags & SIF_SCOPE) {
obj = ATOM_TO_OBJECT(stmt->atom);
JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass);
scope = OBJ_SCOPE(obj);
if (SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom)))
return JS_FALSE;
}
} }
return JS_FALSE; return JS_TRUE;
} }
void void
@ -1344,7 +1358,6 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
break; break;
case STMT_WITH: case STMT_WITH:
case STMT_CATCH:
/* There's a With object on the stack that we need to pop. */ /* There's a With object on the stack that we need to pop. */
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
return JS_FALSE; return JS_FALSE;
@ -1502,48 +1515,46 @@ js_DefineCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom,
* Find a lexically scoped variable (one declared by let, catch, or an array * Find a lexically scoped variable (one declared by let, catch, or an array
* comprehension) named by atom, looking in tc's compile-time scopes. * comprehension) named by atom, looking in tc's compile-time scopes.
* *
* Return null on error. If atom is found, return the statement info record * If a WITH statement is reached along the scope stack, return its statement
* in which it was found directly, and set *slotp to its stack slot (if any). * info record, so callers can tell that atom is ambiguous. If atom is found,
* If atom is not found, return &LL_NOT_FOUND. * set *slotp to its stack slot, and return the statement info record in which
* it was found directly. Otherwise (atom was not found and no WITH statement
* was reached) return null.
*/ */
static JSStmtInfo LL_NOT_FOUND;
static JSStmtInfo * static JSStmtInfo *
LexicalLookup(JSContext *cx, JSTreeContext *tc, JSAtom *atom, jsint *slotp) LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp)
{ {
JSStmtInfo *stmt; JSStmtInfo *stmt;
JSObject *obj, *pobj; JSObject *obj;
JSProperty *prop; JSScope *scope;
JSScopeProperty *sprop; JSScopeProperty *sprop;
jsval v;
*slotp = -1; *slotp = -1;
for (stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) { for (stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) {
if (stmt->type == STMT_WITH) if (stmt->type == STMT_WITH)
return stmt; return stmt;
if (stmt->type == STMT_CATCH) {
if (stmt->atom == atom)
return stmt;
continue;
}
JS_ASSERT(stmt->flags & SIF_SCOPE); JS_ASSERT(stmt->flags & SIF_SCOPE);
obj = ATOM_TO_OBJECT(stmt->atom); obj = ATOM_TO_OBJECT(stmt->atom);
if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass);
return NULL; scope = OBJ_SCOPE(obj);
if (prop) { sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom));
if (pobj != obj) { if (sprop) {
stmt = &LL_NOT_FOUND; JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
} else {
sprop = (JSScopeProperty *) prop; /*
JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); * Use LOCKED_OBJ_GET_SLOT since we know obj is single-threaded
*slotp = OBJ_BLOCK_DEPTH(cx, obj) + sprop->shortid; * and owned by this compiler activation.
} */
OBJ_DROP_PROPERTY(cx, pobj, prop); v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_BLOCK_DEPTH);
JS_ASSERT(JSVAL_IS_INT(v) && JSVAL_TO_INT(v) >= 0);
*slotp = JSVAL_TO_INT(v) + sprop->shortid;
return stmt; return stmt;
} }
} }
return &LL_NOT_FOUND; return NULL;
} }
JSBool JSBool
@ -1575,13 +1586,9 @@ js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom,
obj = fp->varobj; obj = fp->varobj;
if (obj == fp->scopeChain) { if (obj == fp->scopeChain) {
/* XXX this will need revising when 'let const' is added. */ /* XXX this will need revising when 'let const' is added. */
stmt = LexicalLookup(cx, &cg->treeContext, atom, &slot); stmt = LexicalLookup(&cg->treeContext, atom, &slot);
if (!stmt) if (stmt)
return JS_FALSE; return JS_TRUE;
if (stmt != &LL_NOT_FOUND) {
fp = fp->down;
continue;
}
ATOM_LIST_SEARCH(ale, &cg->constList, atom); ATOM_LIST_SEARCH(ale, &cg->constList, atom);
if (ale) { if (ale) {
@ -1871,17 +1878,10 @@ BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
* block-locals. * block-locals.
*/ */
atom = pn->pn_atom; atom = pn->pn_atom;
stmt = LexicalLookup(cx, tc, atom, &slot); stmt = LexicalLookup(tc, atom, &slot);
if (!stmt) if (stmt) {
return JS_FALSE;
if (stmt != &LL_NOT_FOUND) {
if (stmt->type == STMT_WITH) if (stmt->type == STMT_WITH)
return JS_TRUE; return JS_TRUE;
if (stmt->type == STMT_CATCH) {
JS_ASSERT(stmt->atom == atom);
return JS_TRUE;
}
JS_ASSERT(stmt->flags & SIF_SCOPE); JS_ASSERT(stmt->flags & SIF_SCOPE);
JS_ASSERT(slot >= 0); JS_ASSERT(slot >= 0);
@ -4314,18 +4314,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
case TOK_TRY: case TOK_TRY:
{ {
ptrdiff_t start, end, catchStart, guardJump, finallyCatch; ptrdiff_t start, end, catchJump, catchStart, finallyCatch;
JSParseNode *lastCatch;
intN depth; intN depth;
JSParseNode *lastCatch;
/* Quell GCC overwarnings. */ catchJump = catchStart = finallyCatch = -1;
end = catchStart = guardJump = finallyCatch = -1;
/* /*
* Push stmtInfo to track jumps-over-catches and gosubs-to-finally * Push stmtInfo to track jumps-over-catches and gosubs-to-finally
* for later fixup. * for later fixup.
* *
* When a finally block is `active' (STMT_FINALLY on the treeContext), * When a finally block is 'active' (STMT_FINALLY on the treeContext),
* non-local jumps (including jumps-over-catches) result in a GOSUB * non-local jumps (including jumps-over-catches) result in a GOSUB
* being written into the bytecode stream and fixed-up later (c.f. * being written into the bytecode stream and fixed-up later (c.f.
* EmitBackPatchOp and BackPatch). * EmitBackPatchOp and BackPatch).
@ -4339,7 +4338,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
* an unbalanced state, and this imbalance causes problems with things * an unbalanced state, and this imbalance causes problems with things
* like function invocation later on. * like function invocation later on.
* *
* To fix this, we compute the `balanced' stack depth upon try entry, * To fix this, we compute the 'balanced' stack depth upon try entry,
* and then restore the stack to this depth when we hit the first catch * and then restore the stack to this depth when we hit the first catch
* or finally block. We can't just zero the stack, because things like * or finally block. We can't just zero the stack, because things like
* for/in and with that are active upon entry to the block keep state * for/in and with that are active upon entry to the block keep state
@ -4362,12 +4361,15 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
&GOSUBS(stmtInfo)); &GOSUBS(stmtInfo));
if (jmp < 0) if (jmp < 0)
return JS_FALSE; return JS_FALSE;
/* JSOP_RETSUB pops the return pc-index, balancing the stack. */
cg->stackDepth = depth;
} }
/* Emit (hidden) jump over catch and/or finally. */ /* Emit (hidden) jump over catch and/or finally. */
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
return JS_FALSE; return JS_FALSE;
jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &CATCHJUMPS(stmtInfo)); jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &catchJump);
if (jmp < 0) if (jmp < 0)
return JS_FALSE; return JS_FALSE;
@ -4377,6 +4379,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
pn2 = pn->pn_kid2; pn2 = pn->pn_kid2;
lastCatch = NULL; lastCatch = NULL;
if (pn2) { if (pn2) {
jsint count = -1; /* previous catch block's population */
catchStart = end; catchStart = end;
/* /*
@ -4402,99 +4406,50 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
* thrown from catch{} blocks. * thrown from catch{} blocks.
*/ */
for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
JSStmtInfo stmtInfo2; ptrdiff_t guardJump, catchNote;
JSParseNode *catchHead;
ptrdiff_t catchNote;
if (!UpdateLineNumberNotes(cx, cg, pn3)) guardJump = GUARDJUMPS(stmtInfo);
return JS_FALSE; if (guardJump == -1) {
/* Set stack to original depth (see SETSP comment above). */
if (guardJump != -1) { EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth);
cg->stackDepth = depth;
} else {
JS_ASSERT(cg->stackDepth == depth); JS_ASSERT(cg->stackDepth == depth);
/* Fix up and clean up previous catch block. */ /* Fix up and clean up previous catch block. */
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, guardJump); CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, guardJump);
/* Compensate for the [leavewith]. */ /*
cg->stackDepth++; * Emit an unbalanced [leaveblock] for the previous catch,
JS_ASSERT((uintN) cg->stackDepth <= cg->maxStackDepth); * whose block object count is saved below.
*/
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) {
return JS_FALSE; return JS_FALSE;
} JS_ASSERT(count >= 0);
} else { EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count);
/* Set stack to original depth (see SETSP comment above). */
EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth);
cg->stackDepth = depth;
} }
/* Non-negative catchNote offset is length of catchguard. */
catchNote = js_NewSrcNote2(cx, cg, SRC_CATCH, 0);
if (catchNote < 0 ||
js_Emit1(cx, cg, JSOP_NOP) < 0) {
return JS_FALSE;
}
/* Construct the scope holder and push it on. */
ale = js_IndexAtom(cx, CLASS_ATOM(cx, Object), &cg->atomList);
if (!ale)
return JS_FALSE;
EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale));
if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0 ||
js_Emit1(cx, cg, JSOP_NEWINIT) < 0 ||
js_Emit1(cx, cg, JSOP_EXCEPTION) < 0) {
return JS_FALSE;
}
/* initcatchvar <atomIndex> */
catchHead = pn3->pn_kid1;
ale = js_IndexAtom(cx, catchHead->pn_atom, &cg->atomList);
if (!ale)
return JS_FALSE;
EMIT_ATOM_INDEX_OP(JSOP_INITCATCHVAR, ALE_INDEX(ale));
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
js_Emit1(cx, cg, JSOP_ENTERWITH) < 0) {
return JS_FALSE;
}
/* boolean_expr */
if (pn3->pn_kid2) {
ptrdiff_t guardStart = CG_OFFSET(cg);
if (!js_EmitTree(cx, cg, pn3->pn_kid2))
return JS_FALSE;
if (!js_SetSrcNoteOffset(cx, cg, catchNote, 0,
CG_OFFSET(cg) - guardStart)) {
return JS_FALSE;
}
/* ifeq <next block> */
guardJump = EmitJump(cx, cg, JSOP_IFEQ, 0);
if (guardJump < 0)
return JS_FALSE;
}
/* Emit catch block. */
js_PushStatement(&cg->treeContext, &stmtInfo2, STMT_CATCH,
CG_OFFSET(cg));
stmtInfo2.atom = catchHead->pn_atom;
if (!js_EmitTree(cx, cg, pn3->pn_kid3))
return JS_FALSE;
if (!js_PopStatementCG(cx, cg))
return JS_FALSE;
/* /*
* Jump over the remaining catch blocks. * Annotate the JSOP_ENTERBLOCK that's about to be generated
* This counts as a non-local jump, so do the finally thing. * by the call to js_EmitTree immediately below. Save this
* source note's index in stmtInfo for use by the TOK_CATCH:
* case, where the length of the catch guard is set as the
* note's offset.
*/ */
catchNote = js_NewSrcNote2(cx, cg, SRC_CATCH, 0);
/* leavewith, annotated so the decompiler knows to pop */ if (catchNote < 0)
off = cg->stackDepth - 1; return JS_FALSE;
if (js_NewSrcNote2(cx, cg, SRC_CATCH, off) < 0 || CATCHNOTE(stmtInfo) = catchNote;
js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) {
/*
* Emit the lexical scope and catch body. Save the catch's
* block object population via count, for use when targeting
* guardJump at the next catch (the guard mismatch case).
*/
JS_ASSERT(pn3->pn_type == TOK_LEXICALSCOPE);
count = OBJ_BLOCK_COUNT(cx, ATOM_TO_OBJECT(pn3->pn_atom));
if (!js_EmitTree(cx, cg, pn3))
return JS_FALSE; return JS_FALSE;
}
/* gosub <finally>, if required */ /* gosub <finally>, if required */
if (pn->pn_kid3) { if (pn->pn_kid3) {
@ -4506,15 +4461,21 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
cg->stackDepth = depth; cg->stackDepth = depth;
} }
/* This will get fixed up to jump to after catch/finally. */ /*
* Jump over the remaining catch blocks. This will get fixed
* up to jump to after catch/finally.
*/
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
return JS_FALSE; return JS_FALSE;
jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &catchJump);
&CATCHJUMPS(stmtInfo));
if (jmp < 0) if (jmp < 0)
return JS_FALSE; return JS_FALSE;
lastCatch = pn3; /* save pointer to last catch */ /*
* Save a pointer to the last catch node to handle try-finally
* and try-catch(guard)-finally special cases.
*/
lastCatch = pn3->pn_expr;
} }
} }
@ -4546,7 +4507,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
* stack budget. * stack budget.
*/ */
if (lastCatch && lastCatch->pn_kid2) if (lastCatch && lastCatch->pn_kid2)
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, guardJump); CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, GUARDJUMPS(stmtInfo));
EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth); EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth);
cg->stackDepth = depth; cg->stackDepth = depth;
@ -4640,7 +4601,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
} }
/* Fix up the end-of-try/catch jumps to come here. */ /* Fix up the end-of-try/catch jumps to come here. */
if (!BackPatch(cx, cg, CATCHJUMPS(stmtInfo), CG_NEXT(cg), JSOP_GOTO)) if (!BackPatch(cx, cg, catchJump, CG_NEXT(cg), JSOP_GOTO))
return JS_FALSE; return JS_FALSE;
/* /*
@ -4666,6 +4627,51 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
break; break;
} }
case TOK_CATCH:
{
ptrdiff_t guardJump;
stmt = cg->treeContext.topStmt;
JS_ASSERT(stmt->type == STMT_BLOCK);
stmt->type = STMT_CATCH;
stmt = stmt->down;
JS_ASSERT(stmt->type == STMT_TRY || stmt->type == STMT_FINALLY);
/* Pick up the pending exception and bind it to the catch variable. */
if (js_Emit1(cx, cg, JSOP_EXCEPTION) < 0)
return JS_FALSE;
EMIT_UINT16_IMM_OP(JSOP_INITCATCHVAR, pn->pn_kid1->pn_slot);
/* Emit the guard expression, if there is one. */
if (pn->pn_kid2) {
ptrdiff_t guardStart = CG_OFFSET(cg);
if (!js_EmitTree(cx, cg, pn->pn_kid2))
return JS_FALSE;
if (!js_SetSrcNoteOffset(cx, cg, CATCHNOTE(*stmt), 0,
CG_OFFSET(cg) - guardStart)) {
return JS_FALSE;
}
/* ifeq <next block> */
guardJump = EmitJump(cx, cg, JSOP_IFEQ, 0);
if (guardJump < 0)
return JS_FALSE;
GUARDJUMPS(*stmt) = guardJump;
}
/* Emit the catch body. */
if (!js_EmitTree(cx, cg, pn->pn_kid3))
return JS_FALSE;
/*
* Annotate the JSOP_LEAVEBLOCK that will be emitted as we unwind via
* our TOK_LEXICALSCOPE parent, so the decompiler knows to pop.
*/
off = cg->stackDepth;
if (js_NewSrcNote2(cx, cg, SRC_CATCH, off) < 0)
return JS_FALSE;
break;
}
case TOK_VAR: case TOK_VAR:
if (!EmitVariables(cx, cg, pn, JS_FALSE, &noteIndex)) if (!EmitVariables(cx, cg, pn, JS_FALSE, &noteIndex))
return JS_FALSE; return JS_FALSE;

View File

@ -103,21 +103,25 @@ struct JSStmtInfo {
ptrdiff_t update; /* loop update offset (top if none) */ ptrdiff_t update; /* loop update offset (top if none) */
ptrdiff_t breaks; /* offset of last break in loop */ ptrdiff_t breaks; /* offset of last break in loop */
ptrdiff_t continues; /* offset of last continue in loop */ ptrdiff_t continues; /* offset of last continue in loop */
JSAtom *atom; /* name of LABEL or CATCH var */ JSAtom *atom; /* name of LABEL, or block scope object */
JSStmtInfo *down; /* info for enclosing statement */ JSStmtInfo *down; /* info for enclosing statement */
JSStmtInfo *downScope; /* next enclosing lexical scope */ JSStmtInfo *downScope; /* next enclosing lexical scope */
}; };
#define SIF_BODY_BLOCK 0x0001 /* STMT_BLOCK type is a function body */ #define SIF_SCOPE 0x0002 /* statement has its own lexical scope */
#define SIF_SCOPE 0x0002 /* This statement contains a scope. */ #define SIF_BODY_BLOCK 0x0001 /* STMT_BLOCK type is a function body */
/* /*
* To reuse space in JSStmtInfo, rename breaks and continues for use during * To reuse space in JSStmtInfo, rename breaks and continues for use during
* try/catch/finally code generation and backpatching. To match most common * try/catch/finally code generation and backpatching. To match most common
* use cases, the macro argument is a struct, not a struct pointer. * use cases, the macro argument is a struct, not a struct pointer. Only a
* loop, switch, or label statement info record can have breaks and continues,
* and only a for loop has an update backpatch chain, so it's safe to overlay
* these for the "trying" JSStmtTypes.
*/ */
#define CATCHNOTE(stmt) ((stmt).update)
#define GOSUBS(stmt) ((stmt).breaks) #define GOSUBS(stmt) ((stmt).breaks)
#define CATCHJUMPS(stmt) ((stmt).continues) #define GUARDJUMPS(stmt) ((stmt).continues)
#define AT_TOP_LEVEL(tc) \ #define AT_TOP_LEVEL(tc) \
(!(tc)->topStmt || ((tc)->topStmt->flags & SIF_BODY_BLOCK)) (!(tc)->topStmt || ((tc)->topStmt->flags & SIF_BODY_BLOCK))
@ -361,9 +365,13 @@ js_InStatement(JSTreeContext *tc, JSStmtType type);
/* Test whether we're in a with statement. */ /* Test whether we're in a with statement. */
#define js_InWithStatement(tc) js_InStatement(tc, STMT_WITH) #define js_InWithStatement(tc) js_InStatement(tc, STMT_WITH)
/* Test whether we're in a catch block with exception named by atom. */ /*
* Test whether atom refers to a global variable (or is a reference error).
* Return true in *loopyp if any loops enclose the lexical reference, false
* otherwise.
*/
extern JSBool extern JSBool
js_InCatchBlock(JSTreeContext *tc, JSAtom *atom); js_IsGlobalReference(JSTreeContext *tc, JSAtom *atom, JSBool *loopyp);
/* /*
* Push the C-stack-allocated struct at stmt onto the stmtInfo stack. * Push the C-stack-allocated struct at stmt onto the stmtInfo stack.

View File

@ -1981,14 +1981,6 @@ InternNonIntElementId(JSContext *cx, jsval idval, jsid *idp)
# undef JS_THREADED_INTERP # undef JS_THREADED_INTERP
#endif #endif
typedef enum JSOpLength {
#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
op##_LENGTH = length,
#include "jsopcode.tbl"
#undef OPDEF
JSOP_LIMIT_LENGTH
} JSOpLength;
JSBool JSBool
js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result) js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result)
{ {
@ -4256,7 +4248,6 @@ interrupt:
case JSOP_GETMETHOD: goto do_JSOP_GETMETHOD; case JSOP_GETMETHOD: goto do_JSOP_GETMETHOD;
case JSOP_SETMETHOD: goto do_JSOP_SETMETHOD; case JSOP_SETMETHOD: goto do_JSOP_SETMETHOD;
#endif #endif
case JSOP_INITCATCHVAR: goto do_JSOP_INITCATCHVAR;
case JSOP_NAMEDFUNOBJ: goto do_JSOP_NAMEDFUNOBJ; case JSOP_NAMEDFUNOBJ: goto do_JSOP_NAMEDFUNOBJ;
case JSOP_NUMBER: goto do_JSOP_NUMBER; case JSOP_NUMBER: goto do_JSOP_NUMBER;
case JSOP_OBJECT: goto do_JSOP_OBJECT; case JSOP_OBJECT: goto do_JSOP_OBJECT;
@ -5497,41 +5488,16 @@ interrupt:
/* let the code at out try to catch the exception. */ /* let the code at out try to catch the exception. */
goto out; goto out;
BEGIN_LITOPX_CASE(JSOP_INITCATCHVAR, 0) BEGIN_CASE(JSOP_INITCATCHVAR)
/* Load the value into rval, while keeping it live on stack. */
JS_ASSERT(sp - fp->spbase >= 2);
rval = FETCH_OPND(-1);
/* Get the immediate catch variable name into id. */
id = ATOM_TO_JSID(atom);
/* Find the object being initialized at top of stack. */
lval = FETCH_OPND(-2);
JS_ASSERT(JSVAL_IS_OBJECT(lval));
obj = JSVAL_TO_OBJECT(lval);
SAVE_SP_AND_PC(fp);
/* /*
* It's possible for an evil script to substitute a random object * The stack must have a block with at least one local slot below
* for the new object. Check to make sure that we don't override a * the exception object.
* readonly property with the below OBJ_DEFINE_PROPERTY.
*/ */
ok = OBJ_GET_ATTRIBUTES(cx, obj, id, NULL, &attrs); JS_ASSERT(sp - fp->spbase >= 2);
if (!ok) slot = GET_UINT16(pc);
goto out; JS_ASSERT(slot + 1 < (uintN)depth);
if (!(attrs & (JSPROP_READONLY | JSPROP_PERMANENT | fp->spbase[slot] = POP_OPND();
JSPROP_GETTER | JSPROP_SETTER))) { END_CASE(JSOP_INITCATCHVAR)
/* Define obj[id] to contain rval and to be permanent. */
ok = OBJ_DEFINE_PROPERTY(cx, obj, id, rval, NULL, NULL,
JSPROP_PERMANENT, NULL);
if (!ok)
goto out;
}
/* Now that we're done with rval, pop it. */
sp--;
END_LITOPX_CASE(JSOP_INITCATCHVAR)
BEGIN_CASE(JSOP_INSTANCEOF) BEGIN_CASE(JSOP_INSTANCEOF)
SAVE_SP_AND_PC(fp); SAVE_SP_AND_PC(fp);

View File

@ -793,9 +793,7 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength,
jp->indent -= 4; jp->indent -= 4;
} }
caseExprOff = isCondSwitch caseExprOff = isCondSwitch ? JSOP_CONDSWITCH_LENGTH : 0;
? (ptrdiff_t) js_CodeSpec[JSOP_CONDSWITCH].length
: 0;
for (i = 0; i < tableLength; i++) { for (i = 0; i < tableLength; i++) {
off = table[i].offset; off = table[i].offset;
@ -940,7 +938,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
#endif #endif
jsval val; jsval val;
static const char catch_cookie[] = "/*CATCH*/";
static const char finally_cookie[] = "/*FINALLY*/"; static const char finally_cookie[] = "/*FINALLY*/";
static const char iter_cookie[] = "/*ITER*/"; static const char iter_cookie[] = "/*ITER*/";
static const char with_cookie[] = "/*WITH*/"; static const char with_cookie[] = "/*WITH*/";
@ -1156,55 +1153,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
js_printf(jp, "\t}\n"); js_printf(jp, "\t}\n");
break; break;
case SRC_CATCH:
jp->indent -= 4;
sn = js_GetSrcNote(jp->script, pc);
pc += oplen;
js_printf(jp, "\t} catch (");
LOCAL_ASSERT(*pc == JSOP_NAME);
pc += js_CodeSpec[JSOP_NAME].length;
LOCAL_ASSERT(*pc == JSOP_PUSHOBJ);
pc += js_CodeSpec[JSOP_PUSHOBJ].length;
LOCAL_ASSERT(*pc == JSOP_NEWINIT);
pc += js_CodeSpec[JSOP_NEWINIT].length;
LOCAL_ASSERT(*pc == JSOP_EXCEPTION);
pc += js_CodeSpec[JSOP_EXCEPTION].length;
if (*pc == JSOP_LITOPX) {
atomIndex = GET_LITERAL_INDEX(pc);
pc += 1 + LITERAL_INDEX_LEN;
LOCAL_ASSERT(*pc == JSOP_INITCATCHVAR);
++pc;
} else {
LOCAL_ASSERT(*pc == JSOP_INITCATCHVAR);
atomIndex = GET_ATOM_INDEX(pc);
pc += js_CodeSpec[JSOP_INITCATCHVAR].length;
}
atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex);
rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
if (!rval)
return JS_FALSE;
RETRACT(&ss->sprinter, rval);
js_printf(jp, "%s", rval);
LOCAL_ASSERT(*pc == JSOP_ENTERWITH);
pc += js_CodeSpec[JSOP_ENTERWITH].length;
len = js_GetSrcNoteOffset(sn, 0);
if (len) {
js_printf(jp, " if ");
DECOMPILE_CODE(pc, len);
js_printf(jp, "%s", POP_STR());
pc += len;
LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
pc += js_CodeSpec[*pc].length;
}
js_printf(jp, ") {\n");
jp->indent += 4;
todo = Sprint(&ss->sprinter, catch_cookie);
len = 0;
break;
case SRC_FUNCDEF: case SRC_FUNCDEF:
atom = js_GetAtom(cx, &jp->script->atomMap, atom = js_GetAtom(cx, &jp->script->atomMap,
(jsatomid) js_GetSrcNoteOffset(sn, 0)); (jsatomid) js_GetSrcNoteOffset(sn, 0));
@ -1327,9 +1275,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
case JSOP_EXCEPTION: case JSOP_EXCEPTION:
/* /*
* The only other JSOP_EXCEPTION cases occur as part of a code * The only other JSOP_EXCEPTION cases occur as part of a code
* sequence that follows a SRC_CATCH-annotated JSOP_NOP or * sequence that follows a SRC_CATCH-annotated JSOP_ENTERBLOCK
* precedes a SRC_HIDDEN-annotated JSOP_POP emitted when * or that precedes a SRC_HIDDEN-annotated JSOP_POP emitted
* returning from within a finally clause. * when returning from within a finally clause.
*/ */
sn = js_GetSrcNote(jp->script, pc); sn = js_GetSrcNote(jp->script, pc);
LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_HIDDEN); LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_HIDDEN);
@ -1444,11 +1392,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
if (sn && SN_TYPE(sn) == SRC_HIDDEN) if (sn && SN_TYPE(sn) == SRC_HIDDEN)
break; break;
rval = POP_STR(); rval = POP_STR();
if (sn && SN_TYPE(sn) == SRC_CATCH) {
LOCAL_ASSERT(strcmp(rval, catch_cookie) == 0);
LOCAL_ASSERT((uintN) js_GetSrcNoteOffset(sn, 0) == ss->top);
break;
}
LOCAL_ASSERT(strcmp(rval, with_cookie) == 0); LOCAL_ASSERT(strcmp(rval, with_cookie) == 0);
jp->indent -= 4; jp->indent -= 4;
js_printf(jp, "\t}\n"); js_printf(jp, "\t}\n");
@ -1471,6 +1414,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
return JS_FALSE; return JS_FALSE;
} }
/* From here on, control must flow through enterblock_out. */
for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; for (sprop = OBJ_SCOPE(obj)->lastProp; sprop;
sprop = sprop->parent) { sprop = sprop->parent) {
if (!(sprop->flags & SPROP_HAS_SHORTID)) if (!(sprop->flags & SPROP_HAS_SHORTID))
@ -1485,15 +1429,54 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
if (!rval || if (!rval ||
!PushOff(ss, STR2OFF(&ss->sprinter, rval), op)) { !PushOff(ss, STR2OFF(&ss->sprinter, rval), op)) {
ok = JS_FALSE; ok = JS_FALSE;
break; goto enterblock_out;
} }
} }
sn = js_GetSrcNote(jp->script, pc);
if (sn && SN_TYPE(sn) == SRC_CATCH) {
jp->indent -= 4;
js_printf(jp, "\t} catch (");
pc += oplen;
LOCAL_ASSERT(*pc == JSOP_EXCEPTION);
pc += JSOP_EXCEPTION_LENGTH;
LOCAL_ASSERT(*pc == JSOP_INITCATCHVAR);
i = GET_UINT16(pc);
pc += JSOP_INITCATCHVAR_LENGTH;
str = ATOM_TO_STRING(atomv[i]);
rval = QuoteString(&ss->sprinter, str, 0);
if (!rval) {
ok = JS_FALSE;
goto enterblock_out;
}
RETRACT(&ss->sprinter, rval);
js_printf(jp, "%s", rval);
len = js_GetSrcNoteOffset(sn, 0);
if (len) {
js_printf(jp, " if ");
ok = Decompile(ss, pc, len);
if (!ok)
goto enterblock_out;
js_printf(jp, "%s", POP_STR());
pc += len;
LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
pc += js_CodeSpec[*pc].length;
}
js_printf(jp, ") {\n");
jp->indent += 4;
len = 0;
}
todo = -2;
enterblock_out:
if (atomv != smallv) if (atomv != smallv)
JS_free(cx, atomv); JS_free(cx, atomv);
if (!ok) if (!ok)
return JS_FALSE; return JS_FALSE;
todo = -2;
break; break;
} }
@ -1504,9 +1487,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
sn = js_GetSrcNote(jp->script, pc); sn = js_GetSrcNote(jp->script, pc);
todo = -2; todo = -2;
if (sn && SN_TYPE(sn) == SRC_HIDDEN) { if (sn) {
JS_ASSERT(op == JSOP_LEAVEBLOCK); JS_ASSERT(op == JSOP_LEAVEBLOCK);
break; if (SN_TYPE(sn) == SRC_HIDDEN)
break;
LOCAL_ASSERT(SN_TYPE(sn) == SRC_CATCH);
LOCAL_ASSERT((uintN)js_GetSrcNoteOffset(sn, 0) == ss->top);
} }
if (op == JSOP_LEAVEBLOCKEXPR) if (op == JSOP_LEAVEBLOCKEXPR)
rval = POP_STR(); rval = POP_STR();

View File

@ -60,6 +60,14 @@ typedef enum JSOp {
JSOP_LIMIT JSOP_LIMIT
} JSOp; } JSOp;
typedef enum JSOpLength {
#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
op##_LENGTH = length,
#include "jsopcode.tbl"
#undef OPDEF
JSOP_LIMIT_LENGTH
} JSOpLength;
/* /*
* JS bytecode formats. * JS bytecode formats.
*/ */

View File

@ -274,10 +274,10 @@ OPDEF(JSOP_ANONFUNOBJ, 128, "anonfunobj", NULL, 3, 0, 1, 13, JOF_CONST)
OPDEF(JSOP_NAMEDFUNOBJ, 129, "namedfunobj", NULL, 3, 0, 1, 13, JOF_CONST) OPDEF(JSOP_NAMEDFUNOBJ, 129, "namedfunobj", NULL, 3, 0, 1, 13, JOF_CONST)
/* /*
* Like JSOP_INITPROP, but specialized to make a DontDelete property for ECMA * Like JSOP_SETLOCAL, but specialized to avoid requiring JSOP_POP immediately
* Edition 3 catch variables. * after to throw away the exception value.
*/ */
OPDEF(JSOP_INITCATCHVAR,130, "initcatchvar",NULL, 3, 1, 0, 0, JOF_CONST) OPDEF(JSOP_INITCATCHVAR,130, "initcatchvar",NULL, 3, 1, 0, 0, JOF_LOCAL|JOF_NAME|JOF_SET)
/* ECMA-mandated parenthesization opcode, which nulls the reference base register, obj; see jsinterp.c. */ /* ECMA-mandated parenthesization opcode, which nulls the reference base register, obj; see jsinterp.c. */
OPDEF(JSOP_GROUP, 131, "group", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_GROUP, 131, "group", NULL, 1, 0, 0, 0, JOF_BYTE)
@ -416,7 +416,7 @@ OPDEF(JSOP_TYPEOFEXPR, 198,js_typeof_str, NULL, 1, 1, 1, 10, JOF_BYTE|J
OPDEF(JSOP_ENTERBLOCK, 199,"enterblock", NULL, 3, 0, 0, 0, JOF_CONST) OPDEF(JSOP_ENTERBLOCK, 199,"enterblock", NULL, 3, 0, 0, 0, JOF_CONST)
OPDEF(JSOP_LEAVEBLOCK, 200,"leaveblock", NULL, 3, 0, 0, 0, JOF_UINT16) OPDEF(JSOP_LEAVEBLOCK, 200,"leaveblock", NULL, 3, 0, 0, 0, JOF_UINT16)
OPDEF(JSOP_GETLOCAL, 201,"getlocal", NULL, 3, 0, 1, 13, JOF_LOCAL|JOF_NAME) OPDEF(JSOP_GETLOCAL, 201,"getlocal", NULL, 3, 0, 1, 13, JOF_LOCAL|JOF_NAME)
OPDEF(JSOP_SETLOCAL, 202,"setlocal", NULL, 3, 1, 1, 1, JOF_LOCAL|JOF_NAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) OPDEF(JSOP_SETLOCAL, 202,"setlocal", NULL, 3, 1, 1, 1, JOF_LOCAL|JOF_NAME|JOF_SET)
OPDEF(JSOP_INCLOCAL, 203,"inclocal", NULL, 3, 0, 1, 10, JOF_LOCAL|JOF_NAME|JOF_INC) OPDEF(JSOP_INCLOCAL, 203,"inclocal", NULL, 3, 0, 1, 10, JOF_LOCAL|JOF_NAME|JOF_INC)
OPDEF(JSOP_DECLOCAL, 204,"declocal", NULL, 3, 0, 1, 10, JOF_LOCAL|JOF_NAME|JOF_DEC) OPDEF(JSOP_DECLOCAL, 204,"declocal", NULL, 3, 0, 1, 10, JOF_LOCAL|JOF_NAME|JOF_DEC)
OPDEF(JSOP_LOCALINC, 205,"localinc", NULL, 3, 0, 1, 10, JOF_LOCAL|JOF_NAME|JOF_INC|JOF_POST) OPDEF(JSOP_LOCALINC, 205,"localinc", NULL, 3, 0, 1, 10, JOF_LOCAL|JOF_NAME|JOF_INC|JOF_POST)

View File

@ -2922,6 +2922,9 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
lastCatch = NULL; lastCatch = NULL;
do { do {
JSParseNode *pnblock;
BindData data;
/* Check for another catch after unconditional catch. */ /* Check for another catch after unconditional catch. */
if (lastCatch && !lastCatch->pn_kid2) { if (lastCatch && !lastCatch->pn_kid2) {
js_ReportCompileErrorNumber(cx, ts, js_ReportCompileErrorNumber(cx, ts,
@ -2930,6 +2933,15 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
return NULL; return NULL;
} }
/*
* Create a lexical scope node around the whole catch clause,
* including the head.
*/
pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo);
if (!pnblock)
return NULL;
stmtInfo.type = STMT_CATCH;
/* /*
* Legal catch forms are: * Legal catch forms are:
* catch (lhs) * catch (lhs)
@ -2940,21 +2952,38 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
pn2 = NewParseNode(cx, ts, PN_TERNARY, tc); pn2 = NewParseNode(cx, ts, PN_TERNARY, tc);
if (!pn2) if (!pn2)
return NULL; return NULL;
pnblock->pn_expr = pn2;
/* /*
* We use a PN_NAME for the variable node, in pn2->pn_kid1. * We use a PN_NAME for the variable node, in pn2->pn_kid1.
* If there is a guard expression, it goes in pn2->pn_kid2. * If there is a guard expression, it goes in pn2->pn_kid2.
* Contrary to ECMA Ed. 3, the catch variable is lexically
* scoped, not a property of a new Object instance. This is
* an intentional change that anticipates ECMA Ed. 4.
*/ */
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH); MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
MUST_MATCH_TOKEN(TOK_NAME, JSMSG_CATCH_IDENTIFIER); MUST_MATCH_TOKEN(TOK_NAME, JSMSG_CATCH_IDENTIFIER);
label = CURRENT_TOKEN(ts).t_atom;
data.pn = NULL;
data.ts = ts;
data.obj = ATOM_TO_OBJECT(pnblock->pn_atom);
data.op = JSOP_NOP;
data.binder = BindLet;
data.u.let.index = 0;
data.u.let.overflow = JSMSG_TOO_MANY_CATCH_VARS;
if (!data.binder(cx, &data, label, tc))
return NULL;
pn3 = NewParseNode(cx, ts, PN_NAME, tc); pn3 = NewParseNode(cx, ts, PN_NAME, tc);
if (!pn3) if (!pn3)
return NULL; return NULL;
pn3->pn_atom = label;
pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
pn3->pn_expr = NULL; pn3->pn_expr = NULL;
pn3->pn_slot = 0;
pn2->pn_kid1 = pn3; pn2->pn_kid1 = pn3;
pn2->pn_kid2 = NULL; pn2->pn_kid2 = NULL;
#if JS_HAS_CATCH_GUARD #if JS_HAS_CATCH_GUARD
/* /*
* We use 'catch (x if x === 5)' (not 'catch (x : x === 5)') * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
@ -2967,19 +2996,16 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
return NULL; return NULL;
} }
#endif #endif
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH); MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH); MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
js_PushStatement(tc, &stmtInfo, STMT_CATCH, -1);
stmtInfo.atom = pn3->pn_atom;
pn2->pn_kid3 = Statements(cx, ts, tc); pn2->pn_kid3 = Statements(cx, ts, tc);
if (!pn2->pn_kid3) if (!pn2->pn_kid3)
return NULL; return NULL;
MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH); MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
js_PopStatement(tc); js_PopStatement(tc);
PN_APPEND(catchList, pn2); PN_APPEND(catchList, pnblock);
lastCatch = pn2; lastCatch = pn2;
} while ((tt = js_GetToken(cx, ts)) == TOK_CATCH); } while ((tt = js_GetToken(cx, ts)) == TOK_CATCH);
} }
@ -3186,13 +3212,14 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
} else { } else {
if (!stmt) { if (!stmt) {
/* /*
* XXX This is a hard case that requires more work. In * FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=346749
* particular, in many cases, we're trying to emit code as *
* we go. However, this means that we haven't necessarily * This is a hard case that requires more work. In particular,
* finished processing all let declarations in the * in many cases, we're trying to emit code as we go. However,
* implicit top-level block when we emit a reference to * this means that we haven't necessarily finished processing
* one of them. For now, punt on this and pretend this is * all let declarations in the implicit top-level block when
* a var declaration. * we emit a reference to one of them. For now, punt on this
* and pretend this is a var declaration.
*/ */
CURRENT_TOKEN(ts).type = TOK_VAR; CURRENT_TOKEN(ts).type = TOK_VAR;
CURRENT_TOKEN(ts).t_op = JSOP_DEFVAR; CURRENT_TOKEN(ts).t_op = JSOP_DEFVAR;
@ -5455,22 +5482,17 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
} else { } else {
JSAtomListElement *ale; JSAtomListElement *ale;
JSStackFrame *fp; JSStackFrame *fp;
JSStmtInfo *stmt; JSBool loopy;
/* Measure optimizable global variable uses. */ /* Measure optimizable global variable uses. */
ATOM_LIST_SEARCH(ale, &tc->decls, pn->pn_atom); ATOM_LIST_SEARCH(ale, &tc->decls, pn->pn_atom);
if (ale && if (ale &&
!(fp = cx->fp)->fun && !(fp = cx->fp)->fun &&
fp->scopeChain == fp->varobj && fp->scopeChain == fp->varobj &&
!js_InWithStatement(tc) && js_IsGlobalReference(tc, pn->pn_atom, &loopy)) {
!js_InCatchBlock(tc, pn->pn_atom)) {
tc->globalUses++; tc->globalUses++;
for (stmt = tc->topStmt; stmt; stmt = stmt->down) { if (loopy)
if (STMT_IS_LOOP(stmt)) { tc->loopyGlobalUses++;
tc->loopyGlobalUses++;
break;
}
}
} }
} }
} }

View File

@ -102,9 +102,11 @@ JS_BEGIN_EXTERN_C
* pn_right: body * pn_right: body
* TOK_THROW unary pn_op: JSOP_THROW, pn_kid: exception * TOK_THROW unary pn_op: JSOP_THROW, pn_kid: exception
* TOK_TRY ternary pn_kid1: try block * TOK_TRY ternary pn_kid1: try block
* pn_kid2: null or TOK_RESERVED list of catch blocks * pn_kid2: null or TOK_RESERVED list of
* TOK_LEXICALSCOPE nodes, each with pn_expr pointing
* to a TOK_CATCH node
* pn_kid3: null or finally block * pn_kid3: null or finally block
* TOK_CATCH ternary pn_kid1: TOK_NAME, TOK_LB, or TOK_LC cath var node * TOK_CATCH ternary pn_kid1: TOK_NAME, TOK_LB, or TOK_LC catch var node
* (TOK_LB or TOK_LC if destructuring) * (TOK_LB or TOK_LC if destructuring)
* pn_kid2: null or the catch guard expression * pn_kid2: null or the catch guard expression
* pn_kid3: catch block statements * pn_kid3: catch block statements