зеркало из https://github.com/mozilla/gecko-dev.git
Change catch clauses to use lexical scope, etc. (336379, r=mrbkap).
This commit is contained in:
Родитель
8ea9c86502
Коммит
33cfdbad7e
|
@ -129,7 +129,7 @@ const char XUL_FASTLOAD_FILE_BASENAME[] = "XUL";
|
||||||
// Increase the subtractor when changing version, say when changing the
|
// Increase the subtractor when changing version, say when changing the
|
||||||
// (opaque to FastLoad code) format of JS script, function, regexp, etc.
|
// (opaque to FastLoad code) format of JS script, function, regexp, etc.
|
||||||
// XDR serializations.
|
// 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_SERIALIZATION_BUFFER_SIZE (64 * 1024)
|
||||||
#define XUL_DESERIALIZATION_BUFFER_SIZE (8 * 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_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")
|
||||||
|
|
286
js/src/jsemit.c
286
js/src/jsemit.c
|
@ -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 = GUARDJUMP(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, GUARDJUMP(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;
|
||||||
|
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:
|
case TOK_VAR:
|
||||||
if (!EmitVariables(cx, cg, pn, JS_FALSE, ¬eIndex))
|
if (!EmitVariables(cx, cg, pn, JS_FALSE, ¬eIndex))
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
|
|
|
@ -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 GUARDJUMP(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.
|
||||||
|
|
|
@ -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);
|
||||||
|
@ -5994,7 +5960,11 @@ interrupt:
|
||||||
JS_ASSERT(op == JSOP_LEAVEBLOCKEXPR
|
JS_ASSERT(op == JSOP_LEAVEBLOCKEXPR
|
||||||
? fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp - 1
|
? fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp - 1
|
||||||
: fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp);
|
: fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp);
|
||||||
|
|
||||||
*chainp = OBJ_GET_PARENT(cx, obj);
|
*chainp = OBJ_GET_PARENT(cx, obj);
|
||||||
|
JS_ASSERT(chainp != &fp->blockChain ||
|
||||||
|
!*chainp ||
|
||||||
|
OBJ_GET_CLASS(cx, *chainp) == &js_BlockClass);
|
||||||
}
|
}
|
||||||
END_CASE(JSOP_LEAVEBLOCK)
|
END_CASE(JSOP_LEAVEBLOCK)
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Загрузка…
Ссылка в новой задаче