mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-28 19:38:13 +00:00
Change catch clauses to use lexical scope, etc. (336379, r=mrbkap).
This commit is contained in:
parent
8c9e1d1eb7
commit
02f6c7133c
@ -129,7 +129,7 @@ const char XUL_FASTLOAD_FILE_BASENAME[] = "XUL";
|
||||
// Increase the subtractor when changing version, say when changing the
|
||||
// (opaque to FastLoad code) format of JS script, function, regexp, etc.
|
||||
// XDR serializations.
|
||||
#define XUL_FASTLOAD_FILE_VERSION (0xfeedbeef - 15)
|
||||
#define XUL_FASTLOAD_FILE_VERSION (0xfeedbeef - 16)
|
||||
|
||||
#define XUL_SERIALIZATION_BUFFER_SIZE (64 * 1024)
|
||||
#define XUL_DESERIALIZATION_BUFFER_SIZE (8 * 1024)
|
||||
|
@ -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_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_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_CHARACTER, 171, 0, JSEXN_SYNTAXERR, "illegal XML character")
|
||||
MSG_DEF(JSMSG_BAD_DEFAULT_XML_NAMESPACE,172,0,JSEXN_SYNTAXERR, "invalid default XML namespace")
|
||||
|
286
js/src/jsemit.c
286
js/src/jsemit.c
@ -1208,15 +1208,29 @@ js_InStatement(JSTreeContext *tc, JSStmtType type)
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_InCatchBlock(JSTreeContext *tc, JSAtom *atom)
|
||||
js_IsGlobalReference(JSTreeContext *tc, JSAtom *atom, JSBool *loopyp)
|
||||
{
|
||||
JSStmtInfo *stmt;
|
||||
JSObject *obj;
|
||||
JSScope *scope;
|
||||
|
||||
*loopyp = JS_FALSE;
|
||||
for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
|
||||
if (stmt->type == STMT_CATCH && stmt->atom == atom)
|
||||
return JS_TRUE;
|
||||
if (stmt->type == STMT_WITH)
|
||||
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
|
||||
@ -1344,7 +1358,6 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
|
||||
break;
|
||||
|
||||
case STMT_WITH:
|
||||
case STMT_CATCH:
|
||||
/* There's a With object on the stack that we need to pop. */
|
||||
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
|
||||
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
|
||||
* comprehension) named by atom, looking in tc's compile-time scopes.
|
||||
*
|
||||
* Return null on error. If atom is found, return the statement info record
|
||||
* in which it was found directly, and set *slotp to its stack slot (if any).
|
||||
* If atom is not found, return &LL_NOT_FOUND.
|
||||
* If a WITH statement is reached along the scope stack, return its statement
|
||||
* info record, so callers can tell that atom is ambiguous. If atom is 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 *
|
||||
LexicalLookup(JSContext *cx, JSTreeContext *tc, JSAtom *atom, jsint *slotp)
|
||||
LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp)
|
||||
{
|
||||
JSStmtInfo *stmt;
|
||||
JSObject *obj, *pobj;
|
||||
JSProperty *prop;
|
||||
JSObject *obj;
|
||||
JSScope *scope;
|
||||
JSScopeProperty *sprop;
|
||||
jsval v;
|
||||
|
||||
*slotp = -1;
|
||||
for (stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) {
|
||||
if (stmt->type == STMT_WITH)
|
||||
return stmt;
|
||||
if (stmt->type == STMT_CATCH) {
|
||||
if (stmt->atom == atom)
|
||||
return stmt;
|
||||
continue;
|
||||
}
|
||||
|
||||
JS_ASSERT(stmt->flags & SIF_SCOPE);
|
||||
obj = ATOM_TO_OBJECT(stmt->atom);
|
||||
if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop))
|
||||
return NULL;
|
||||
if (prop) {
|
||||
if (pobj != obj) {
|
||||
stmt = &LL_NOT_FOUND;
|
||||
} else {
|
||||
sprop = (JSScopeProperty *) prop;
|
||||
JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
|
||||
*slotp = OBJ_BLOCK_DEPTH(cx, obj) + sprop->shortid;
|
||||
}
|
||||
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
||||
JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass);
|
||||
scope = OBJ_SCOPE(obj);
|
||||
sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom));
|
||||
if (sprop) {
|
||||
JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
|
||||
|
||||
/*
|
||||
* Use LOCKED_OBJ_GET_SLOT since we know obj is single-threaded
|
||||
* and owned by this compiler activation.
|
||||
*/
|
||||
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 &LL_NOT_FOUND;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JSBool
|
||||
@ -1575,13 +1586,9 @@ js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom,
|
||||
obj = fp->varobj;
|
||||
if (obj == fp->scopeChain) {
|
||||
/* XXX this will need revising when 'let const' is added. */
|
||||
stmt = LexicalLookup(cx, &cg->treeContext, atom, &slot);
|
||||
if (!stmt)
|
||||
return JS_FALSE;
|
||||
if (stmt != &LL_NOT_FOUND) {
|
||||
fp = fp->down;
|
||||
continue;
|
||||
}
|
||||
stmt = LexicalLookup(&cg->treeContext, atom, &slot);
|
||||
if (stmt)
|
||||
return JS_TRUE;
|
||||
|
||||
ATOM_LIST_SEARCH(ale, &cg->constList, atom);
|
||||
if (ale) {
|
||||
@ -1871,17 +1878,10 @@ BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
|
||||
* block-locals.
|
||||
*/
|
||||
atom = pn->pn_atom;
|
||||
stmt = LexicalLookup(cx, tc, atom, &slot);
|
||||
if (!stmt)
|
||||
return JS_FALSE;
|
||||
|
||||
if (stmt != &LL_NOT_FOUND) {
|
||||
stmt = LexicalLookup(tc, atom, &slot);
|
||||
if (stmt) {
|
||||
if (stmt->type == STMT_WITH)
|
||||
return JS_TRUE;
|
||||
if (stmt->type == STMT_CATCH) {
|
||||
JS_ASSERT(stmt->atom == atom);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JS_ASSERT(stmt->flags & SIF_SCOPE);
|
||||
JS_ASSERT(slot >= 0);
|
||||
@ -4314,18 +4314,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
|
||||
case TOK_TRY:
|
||||
{
|
||||
ptrdiff_t start, end, catchStart, guardJump, finallyCatch;
|
||||
JSParseNode *lastCatch;
|
||||
ptrdiff_t start, end, catchJump, catchStart, finallyCatch;
|
||||
intN depth;
|
||||
JSParseNode *lastCatch;
|
||||
|
||||
/* Quell GCC overwarnings. */
|
||||
end = catchStart = guardJump = finallyCatch = -1;
|
||||
catchJump = catchStart = finallyCatch = -1;
|
||||
|
||||
/*
|
||||
* Push stmtInfo to track jumps-over-catches and gosubs-to-finally
|
||||
* 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
|
||||
* being written into the bytecode stream and fixed-up later (c.f.
|
||||
* 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
|
||||
* 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
|
||||
* 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
|
||||
@ -4362,12 +4361,15 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
&GOSUBS(stmtInfo));
|
||||
if (jmp < 0)
|
||||
return JS_FALSE;
|
||||
|
||||
/* JSOP_RETSUB pops the return pc-index, balancing the stack. */
|
||||
cg->stackDepth = depth;
|
||||
}
|
||||
|
||||
/* Emit (hidden) jump over catch and/or finally. */
|
||||
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
|
||||
return JS_FALSE;
|
||||
jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &CATCHJUMPS(stmtInfo));
|
||||
jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &catchJump);
|
||||
if (jmp < 0)
|
||||
return JS_FALSE;
|
||||
|
||||
@ -4377,6 +4379,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
pn2 = pn->pn_kid2;
|
||||
lastCatch = NULL;
|
||||
if (pn2) {
|
||||
jsint count = 0; /* previous catch block's population */
|
||||
|
||||
catchStart = end;
|
||||
|
||||
/*
|
||||
@ -4402,99 +4406,50 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
* thrown from catch{} blocks.
|
||||
*/
|
||||
for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
|
||||
JSStmtInfo stmtInfo2;
|
||||
JSParseNode *catchHead;
|
||||
ptrdiff_t catchNote;
|
||||
ptrdiff_t guardJump, catchNote;
|
||||
|
||||
if (!UpdateLineNumberNotes(cx, cg, pn3))
|
||||
return JS_FALSE;
|
||||
|
||||
if (guardJump != -1) {
|
||||
guardJump = GUARDJUMP(stmtInfo);
|
||||
if (guardJump == -1) {
|
||||
/* Set stack to original depth (see SETSP comment above). */
|
||||
EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth);
|
||||
cg->stackDepth = depth;
|
||||
} else {
|
||||
JS_ASSERT(cg->stackDepth == depth);
|
||||
|
||||
/* Fix up and clean up previous catch block. */
|
||||
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, guardJump);
|
||||
|
||||
/* Compensate for the [leavewith]. */
|
||||
cg->stackDepth++;
|
||||
JS_ASSERT((uintN) cg->stackDepth <= cg->maxStackDepth);
|
||||
|
||||
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
|
||||
js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) {
|
||||
/*
|
||||
* Emit an unbalanced [leaveblock] for the previous catch,
|
||||
* whose block object count is saved below.
|
||||
*/
|
||||
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
|
||||
return JS_FALSE;
|
||||
}
|
||||
} else {
|
||||
/* Set stack to original depth (see SETSP comment above). */
|
||||
EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth);
|
||||
cg->stackDepth = depth;
|
||||
JS_ASSERT(count > 0);
|
||||
EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count);
|
||||
}
|
||||
|
||||
/* 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.
|
||||
* This counts as a non-local jump, so do the finally thing.
|
||||
* Annotate the JSOP_ENTERBLOCK that's about to be generated
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* leavewith, annotated so the decompiler knows to pop */
|
||||
off = cg->stackDepth - 1;
|
||||
if (js_NewSrcNote2(cx, cg, SRC_CATCH, off) < 0 ||
|
||||
js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) {
|
||||
catchNote = js_NewSrcNote2(cx, cg, SRC_CATCH, 0);
|
||||
if (catchNote < 0)
|
||||
return JS_FALSE;
|
||||
CATCHNOTE(stmtInfo) = catchNote;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/* gosub <finally>, if required */
|
||||
if (pn->pn_kid3) {
|
||||
@ -4506,15 +4461,21 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
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)
|
||||
return JS_FALSE;
|
||||
jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH,
|
||||
&CATCHJUMPS(stmtInfo));
|
||||
jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &catchJump);
|
||||
if (jmp < 0)
|
||||
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.
|
||||
*/
|
||||
if (lastCatch && lastCatch->pn_kid2)
|
||||
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, guardJump);
|
||||
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, GUARDJUMP(stmtInfo));
|
||||
|
||||
EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)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. */
|
||||
if (!BackPatch(cx, cg, CATCHJUMPS(stmtInfo), CG_NEXT(cg), JSOP_GOTO))
|
||||
if (!BackPatch(cx, cg, catchJump, CG_NEXT(cg), JSOP_GOTO))
|
||||
return JS_FALSE;
|
||||
|
||||
/*
|
||||
@ -4666,6 +4627,51 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
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;
|
||||
GUARDJUMP(*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:
|
||||
if (!EmitVariables(cx, cg, pn, JS_FALSE, ¬eIndex))
|
||||
return JS_FALSE;
|
||||
|
@ -103,21 +103,25 @@ struct JSStmtInfo {
|
||||
ptrdiff_t update; /* loop update offset (top if none) */
|
||||
ptrdiff_t breaks; /* offset of last break 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 *downScope; /* next enclosing lexical scope */
|
||||
};
|
||||
|
||||
#define SIF_BODY_BLOCK 0x0001 /* STMT_BLOCK type is a function body */
|
||||
#define SIF_SCOPE 0x0002 /* This statement contains a scope. */
|
||||
#define SIF_SCOPE 0x0002 /* statement has its own lexical 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
|
||||
* 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 CATCHJUMPS(stmt) ((stmt).continues)
|
||||
#define GUARDJUMP(stmt) ((stmt).continues)
|
||||
|
||||
#define AT_TOP_LEVEL(tc) \
|
||||
(!(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. */
|
||||
#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
|
||||
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.
|
||||
|
@ -1981,14 +1981,6 @@ InternNonIntElementId(JSContext *cx, jsval idval, jsid *idp)
|
||||
# undef JS_THREADED_INTERP
|
||||
#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
|
||||
js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result)
|
||||
{
|
||||
@ -4256,7 +4248,6 @@ interrupt:
|
||||
case JSOP_GETMETHOD: goto do_JSOP_GETMETHOD;
|
||||
case JSOP_SETMETHOD: goto do_JSOP_SETMETHOD;
|
||||
#endif
|
||||
case JSOP_INITCATCHVAR: goto do_JSOP_INITCATCHVAR;
|
||||
case JSOP_NAMEDFUNOBJ: goto do_JSOP_NAMEDFUNOBJ;
|
||||
case JSOP_NUMBER: goto do_JSOP_NUMBER;
|
||||
case JSOP_OBJECT: goto do_JSOP_OBJECT;
|
||||
@ -5497,41 +5488,16 @@ interrupt:
|
||||
/* let the code at out try to catch the exception. */
|
||||
goto out;
|
||||
|
||||
BEGIN_LITOPX_CASE(JSOP_INITCATCHVAR, 0)
|
||||
/* 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);
|
||||
|
||||
BEGIN_CASE(JSOP_INITCATCHVAR)
|
||||
/*
|
||||
* It's possible for an evil script to substitute a random object
|
||||
* for the new object. Check to make sure that we don't override a
|
||||
* readonly property with the below OBJ_DEFINE_PROPERTY.
|
||||
* The stack must have a block with at least one local slot below
|
||||
* the exception object.
|
||||
*/
|
||||
ok = OBJ_GET_ATTRIBUTES(cx, obj, id, NULL, &attrs);
|
||||
if (!ok)
|
||||
goto out;
|
||||
if (!(attrs & (JSPROP_READONLY | JSPROP_PERMANENT |
|
||||
JSPROP_GETTER | JSPROP_SETTER))) {
|
||||
/* 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)
|
||||
JS_ASSERT(sp - fp->spbase >= 2);
|
||||
slot = GET_UINT16(pc);
|
||||
JS_ASSERT(slot + 1 < (uintN)depth);
|
||||
fp->spbase[slot] = POP_OPND();
|
||||
END_CASE(JSOP_INITCATCHVAR)
|
||||
|
||||
BEGIN_CASE(JSOP_INSTANCEOF)
|
||||
SAVE_SP_AND_PC(fp);
|
||||
@ -5994,7 +5960,11 @@ interrupt:
|
||||
JS_ASSERT(op == JSOP_LEAVEBLOCKEXPR
|
||||
? fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp - 1
|
||||
: fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp);
|
||||
|
||||
*chainp = OBJ_GET_PARENT(cx, obj);
|
||||
JS_ASSERT(chainp != &fp->blockChain ||
|
||||
!*chainp ||
|
||||
OBJ_GET_CLASS(cx, *chainp) == &js_BlockClass);
|
||||
}
|
||||
END_CASE(JSOP_LEAVEBLOCK)
|
||||
|
||||
|
@ -793,9 +793,7 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength,
|
||||
jp->indent -= 4;
|
||||
}
|
||||
|
||||
caseExprOff = isCondSwitch
|
||||
? (ptrdiff_t) js_CodeSpec[JSOP_CONDSWITCH].length
|
||||
: 0;
|
||||
caseExprOff = isCondSwitch ? JSOP_CONDSWITCH_LENGTH : 0;
|
||||
|
||||
for (i = 0; i < tableLength; i++) {
|
||||
off = table[i].offset;
|
||||
@ -940,7 +938,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
#endif
|
||||
jsval val;
|
||||
|
||||
static const char catch_cookie[] = "/*CATCH*/";
|
||||
static const char finally_cookie[] = "/*FINALLY*/";
|
||||
static const char iter_cookie[] = "/*ITER*/";
|
||||
static const char with_cookie[] = "/*WITH*/";
|
||||
@ -1156,55 +1153,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
js_printf(jp, "\t}\n");
|
||||
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:
|
||||
atom = js_GetAtom(cx, &jp->script->atomMap,
|
||||
(jsatomid) js_GetSrcNoteOffset(sn, 0));
|
||||
@ -1327,9 +1275,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
case JSOP_EXCEPTION:
|
||||
/*
|
||||
* The only other JSOP_EXCEPTION cases occur as part of a code
|
||||
* sequence that follows a SRC_CATCH-annotated JSOP_NOP or
|
||||
* precedes a SRC_HIDDEN-annotated JSOP_POP emitted when
|
||||
* returning from within a finally clause.
|
||||
* sequence that follows a SRC_CATCH-annotated JSOP_ENTERBLOCK
|
||||
* or that precedes a SRC_HIDDEN-annotated JSOP_POP emitted
|
||||
* when returning from within a finally clause.
|
||||
*/
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
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)
|
||||
break;
|
||||
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);
|
||||
jp->indent -= 4;
|
||||
js_printf(jp, "\t}\n");
|
||||
@ -1471,6 +1414,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
/* From here on, control must flow through enterblock_out. */
|
||||
for (sprop = OBJ_SCOPE(obj)->lastProp; sprop;
|
||||
sprop = sprop->parent) {
|
||||
if (!(sprop->flags & SPROP_HAS_SHORTID))
|
||||
@ -1485,15 +1429,54 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
if (!rval ||
|
||||
!PushOff(ss, STR2OFF(&ss->sprinter, rval), op)) {
|
||||
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)
|
||||
JS_free(cx, atomv);
|
||||
if (!ok)
|
||||
return JS_FALSE;
|
||||
todo = -2;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1504,9 +1487,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
todo = -2;
|
||||
if (sn && SN_TYPE(sn) == SRC_HIDDEN) {
|
||||
if (sn) {
|
||||
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)
|
||||
rval = POP_STR();
|
||||
|
@ -60,6 +60,14 @@ typedef enum JSOp {
|
||||
JSOP_LIMIT
|
||||
} 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.
|
||||
*/
|
||||
|
@ -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)
|
||||
|
||||
/*
|
||||
* Like JSOP_INITPROP, but specialized to make a DontDelete property for ECMA
|
||||
* Edition 3 catch variables.
|
||||
* Like JSOP_SETLOCAL, but specialized to avoid requiring JSOP_POP immediately
|
||||
* 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. */
|
||||
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_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_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_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)
|
||||
|
@ -2922,6 +2922,9 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
lastCatch = NULL;
|
||||
|
||||
do {
|
||||
JSParseNode *pnblock;
|
||||
BindData data;
|
||||
|
||||
/* Check for another catch after unconditional catch. */
|
||||
if (lastCatch && !lastCatch->pn_kid2) {
|
||||
js_ReportCompileErrorNumber(cx, ts,
|
||||
@ -2930,6 +2933,15 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
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:
|
||||
* catch (lhs)
|
||||
@ -2940,21 +2952,38 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
pn2 = NewParseNode(cx, ts, PN_TERNARY, tc);
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
pnblock->pn_expr = pn2;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* 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_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);
|
||||
if (!pn3)
|
||||
return NULL;
|
||||
|
||||
pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
|
||||
pn3->pn_atom = label;
|
||||
pn3->pn_expr = NULL;
|
||||
pn3->pn_slot = 0;
|
||||
pn2->pn_kid1 = pn3;
|
||||
pn2->pn_kid2 = NULL;
|
||||
|
||||
#if JS_HAS_CATCH_GUARD
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
#endif
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_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);
|
||||
if (!pn2->pn_kid3)
|
||||
return NULL;
|
||||
MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
|
||||
js_PopStatement(tc);
|
||||
|
||||
PN_APPEND(catchList, pn2);
|
||||
PN_APPEND(catchList, pnblock);
|
||||
lastCatch = pn2;
|
||||
} while ((tt = js_GetToken(cx, ts)) == TOK_CATCH);
|
||||
}
|
||||
@ -3186,13 +3212,14 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
} else {
|
||||
if (!stmt) {
|
||||
/*
|
||||
* XXX This is a hard case that requires more work. In
|
||||
* particular, in many cases, we're trying to emit code as
|
||||
* we go. However, this means that we haven't necessarily
|
||||
* finished processing all let declarations in the
|
||||
* implicit top-level block when we emit a reference to
|
||||
* one of them. For now, punt on this and pretend this is
|
||||
* a var declaration.
|
||||
* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=346749
|
||||
*
|
||||
* This is a hard case that requires more work. In particular,
|
||||
* in many cases, we're trying to emit code as we go. However,
|
||||
* this means that we haven't necessarily finished processing
|
||||
* all let declarations in the implicit top-level block when
|
||||
* 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).t_op = JSOP_DEFVAR;
|
||||
@ -5455,22 +5482,17 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
} else {
|
||||
JSAtomListElement *ale;
|
||||
JSStackFrame *fp;
|
||||
JSStmtInfo *stmt;
|
||||
JSBool loopy;
|
||||
|
||||
/* Measure optimizable global variable uses. */
|
||||
ATOM_LIST_SEARCH(ale, &tc->decls, pn->pn_atom);
|
||||
if (ale &&
|
||||
!(fp = cx->fp)->fun &&
|
||||
fp->scopeChain == fp->varobj &&
|
||||
!js_InWithStatement(tc) &&
|
||||
!js_InCatchBlock(tc, pn->pn_atom)) {
|
||||
js_IsGlobalReference(tc, pn->pn_atom, &loopy)) {
|
||||
tc->globalUses++;
|
||||
for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
|
||||
if (STMT_IS_LOOP(stmt)) {
|
||||
tc->loopyGlobalUses++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (loopy)
|
||||
tc->loopyGlobalUses++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -102,9 +102,11 @@ JS_BEGIN_EXTERN_C
|
||||
* pn_right: body
|
||||
* TOK_THROW unary pn_op: JSOP_THROW, pn_kid: exception
|
||||
* 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
|
||||
* 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)
|
||||
* pn_kid2: null or the catch guard expression
|
||||
* pn_kid3: catch block statements
|
||||
|
Loading…
Reference in New Issue
Block a user