Destructuring catch variables, plus TOK_LEXICALSCOPE cleanup (336379, r=mrbkap).

This commit is contained in:
brendan%mozilla.org 2006-08-17 08:13:18 +00:00
parent 6e01d43af8
commit df41627c20
7 changed files with 99 additions and 57 deletions

View File

@ -3130,10 +3130,17 @@ EmitDestructuringLHS(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
return JS_FALSE;
break;
case JSOP_SETLOCAL:
if (wantpop) {
slot = (jsuint) pn->pn_slot;
EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, slot);
break;
}
/* FALL THROUGH */
case JSOP_SETARG:
case JSOP_SETVAR:
case JSOP_SETGVAR:
case JSOP_SETLOCAL:
slot = (jsuint) pn->pn_slot;
EMIT_UINT16_IMM_OP(pn->pn_op, slot);
if (wantpop && js_Emit1(cx, cg, JSOP_POP) < 0)
@ -4386,17 +4393,14 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
/*
* The emitted code for a catch block looks like:
*
* [ popscope ] only if 2nd+ catch block
* name Object
* pushobj
* newinit
* [ leaveblock ] only if 2nd+ catch block
* enterblock with SRC_CATCH
* exception
* initcatchvar <atom>
* enterwith
* setlocalpop <slot> or destructuring code
* [< catchguard code >] if there's a catchguard
* [ifeq <offset to next catch block>] " "
* < catch block contents >
* leavewith
* leaveblock
* goto <end of catch blocks> non-local; finally applies
*
* If there's no catch block without a catchguard, the last
@ -4639,15 +4643,30 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
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.
* Inline BindNameToSlot, fixing up the slot by adding block depth.
*/
/* Pick up the pending exception and bind it to the catch variable. */
if (js_Emit1(cx, cg, JSOP_EXCEPTION) < 0)
return JS_FALSE;
pn2 = pn->pn_kid1;
pn2->pn_slot += OBJ_BLOCK_DEPTH(cx, ATOM_TO_OBJECT(atom));
EMIT_UINT16_IMM_OP(JSOP_INITCATCHVAR, pn2->pn_slot);
switch (pn2->pn_type) {
#if JS_HAS_DESTRUCTURING
case TOK_RB:
case TOK_RC:
if (!EmitDestructuringOps(cx, cg, JSOP_NOP, pn2))
return JS_FALSE;
if (js_Emit1(cx, cg, JSOP_POP) < 0)
return JS_FALSE;
break;
#endif
case TOK_NAME:
/* Inline BindNameToSlot, adding block depth to pn2->pn_slot. */
pn2->pn_slot += OBJ_BLOCK_DEPTH(cx, ATOM_TO_OBJECT(atom));
EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, pn2->pn_slot);
break;
default:
JS_ASSERT(0);
}
/* Emit the guard expression, if there is one. */
if (pn->pn_kid2) {
@ -5022,7 +5041,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
return JS_FALSE;
break;
#endif
default:;
default:
JS_ASSERT(0);
}
break;
@ -5421,11 +5441,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
if (!js_EmitTree(cx, cg, pn->pn_expr))
return JS_FALSE;
op = (pn->pn_extra & PNX_BLOCKEXPR)
? JSOP_LEAVEBLOCKEXPR
: JSOP_LEAVEBLOCK;
EMIT_UINT16_IMM_OP(op, count);
/* Emit the JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR opcode. */
EMIT_UINT16_IMM_OP(pn->pn_op, count);
cg->stackDepth -= count;
ok = js_PopStatementCG(cx, cg);

View File

@ -5488,7 +5488,7 @@ interrupt:
/* let the code at out try to catch the exception. */
goto out;
BEGIN_CASE(JSOP_INITCATCHVAR)
BEGIN_CASE(JSOP_SETLOCALPOP)
/*
* The stack must have a block with at least one local slot below
* the exception object.
@ -5497,7 +5497,7 @@ interrupt:
slot = GET_UINT16(pc);
JS_ASSERT(slot + 1 < (uintN)depth);
fp->spbase[slot] = POP_OPND();
END_CASE(JSOP_INITCATCHVAR)
END_CASE(JSOP_SETLOCALPOP)
BEGIN_CASE(JSOP_INSTANCEOF)
SAVE_SP_AND_PC(fp);

View File

@ -2017,6 +2017,9 @@ block_xdrObject(JSXDRState *xdr, JSObject **objp)
JSBool ok;
cx = xdr->cx;
#ifdef __GNUC__
obj = NULL; /* quell GCC overwarning */
#endif
atomMap = &xdr->script->atomMap;
if (xdr->mode == JSXDR_ENCODE) {

View File

@ -1325,7 +1325,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
case SRC_DECL:
/* This pop is at the end of the head of a let form. */
pc += js_CodeSpec[JSOP_POP].length;
pc += JSOP_POP_LENGTH;
len = js_GetSrcNoteOffset(sn, 0);
if (pc[len] == JSOP_LEAVEBLOCK) {
js_printf(jp, "\tlet (%s) {\n", POP_STR());
@ -1441,9 +1441,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
pc += oplen;
LOCAL_ASSERT(*pc == JSOP_EXCEPTION);
pc += JSOP_EXCEPTION_LENGTH;
LOCAL_ASSERT(*pc == JSOP_INITCATCHVAR);
LOCAL_ASSERT(*pc == JSOP_SETLOCALPOP);
i = GET_UINT16(pc);
pc += JSOP_INITCATCHVAR_LENGTH;
pc += JSOP_SETLOCALPOP_LENGTH;
str = ATOM_TO_STRING(atomv[i]);
rval = QuoteString(&ss->sprinter, str, 0);
if (!rval) {

View File

@ -277,7 +277,7 @@ OPDEF(JSOP_NAMEDFUNOBJ, 129, "namedfunobj", NULL, 3, 0, 1, 13, JOF_CONST)
* 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_LOCAL|JOF_NAME|JOF_SET)
OPDEF(JSOP_SETLOCALPOP, 130, "setlocalpop", 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)

View File

@ -2330,10 +2330,9 @@ PushLexicalScope(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
js_PushBlockScope(tc, stmtInfo, atom, -1);
pn->pn_type = TOK_LEXICALSCOPE;
pn->pn_op = JSOP_NOP;
pn->pn_op = JSOP_LEAVEBLOCK;
pn->pn_atom = atom;
pn->pn_expr = NULL;
pn->pn_extra = 0;
return pn;
}
@ -2382,6 +2381,11 @@ LetBlock(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool statement)
pn1->pn_kid = pn;
pn = pn1;
/*
* Change pnblock's opcode to the variant that propagates the last
* result down after popping the block, and clear statement.
*/
pnblock->pn_op = JSOP_LEAVEBLOCKEXPR;
statement = JS_FALSE;
}
@ -2391,8 +2395,6 @@ LetBlock(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool statement)
if (statement)
MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
else
pnblock->pn_extra = PNX_BLOCKEXPR;
js_PopStatement(tc);
return pn;
@ -2892,11 +2894,11 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
* kid2 is the catch guard or null if no guard
* kid3 is the catch block
*
* catch discriminant nodes are binary
* atom is the receptacle
* expr is the discriminant code
* catch lvalue nodes are either:
* TOK_NAME for a single identifier
* TOK_RB or TOK_RC for a destructuring left-hand side
*
* finally nodes are unary (just the finally expression)
* finally nodes are TOK_LC Statement lists.
*/
pn = NewParseNode(cx, ts, PN_TERNARY, tc);
if (!pn)
@ -2953,18 +2955,13 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
if (!pn2)
return NULL;
pnblock->pn_expr = pn2;
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
/*
* 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);
@ -2972,18 +2969,44 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
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)
tt = js_GetToken(cx, ts);
switch (tt) {
#if JS_HAS_DESTRUCTURING
case TOK_LB:
case TOK_LC:
pn3 = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
if (!pn3)
return NULL;
if (!CheckDestructuring(cx, &data, pn3, NULL, tc))
return NULL;
break;
#endif
case TOK_NAME:
label = CURRENT_TOKEN(ts).t_atom;
if (!data.binder(cx, &data, label, tc))
return NULL;
pn3 = NewParseNode(cx, ts, PN_NAME, tc);
if (!pn3)
return NULL;
pn3->pn_atom = label;
pn3->pn_expr = NULL;
pn3->pn_slot = 0;
pn3->pn_attrs = 0;
break;
default:
js_ReportCompileErrorNumber(cx, ts,
JSREPORT_TS | JSREPORT_ERROR,
JSMSG_CATCH_IDENTIFIER);
return NULL;
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)')
@ -3191,7 +3214,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
/* Check for a let statement or let expression. */
if (js_PeekToken(cx, ts) == TOK_LP) {
pn = LetBlock(cx, ts, tc, JS_TRUE);
if (!pn || !(pn->pn_extra & PNX_BLOCKEXPR))
if (!pn || pn->pn_op == JSOP_LEAVEBLOCK)
return pn;
/* Let expressions require automatic semicolon insertion. */
break;
@ -3264,7 +3287,6 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
pn1->pn_pos = tc->blockNode->pn_pos;
pn1->pn_atom = atom;
pn1->pn_expr = tc->blockNode;
pn1->pn_extra = 0;
tc->blockNode = pn1;
}
@ -3526,7 +3548,7 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
pn2->pn_atom = atom;
pn2->pn_expr = NULL;
pn2->pn_slot = -1;
pn2->pn_attrs = !let ? data.u.var.attrs : 0;
pn2->pn_attrs = let ? 0 : data.u.var.attrs;
PN_APPEND(pn, pn2);
if (js_MatchToken(cx, ts, TOK_ASSIGN)) {

View File

@ -106,8 +106,8 @@ JS_BEGIN_EXTERN_C
* 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 catch var node
* (TOK_LB or TOK_LC if destructuring)
* TOK_CATCH ternary pn_kid1: TOK_NAME, TOK_RB, or TOK_RC catch var node
* (TOK_RB or TOK_RC if destructuring)
* pn_kid2: null or the catch guard expression
* pn_kid3: catch block statements
* TOK_BREAK name pn_atom: label or null
@ -245,7 +245,8 @@ JS_BEGIN_EXTERN_C
*
* Label Variant Members
* ----- ------- -------
* TOK_LEXICALSCOPE name pn_atom: block object
* TOK_LEXICALSCOPE name pn_op: JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR
* pn_atom: block object
* pn_expr: block body
* TOK_ARRAYCOMP list pn_head: list of pn_count (1 or 2) elements
* if pn_count is 2, first element is #n=[...]
@ -283,7 +284,7 @@ struct JSParseNode {
JSParseNode *head; /* first node in list */
JSParseNode **tail; /* ptr to ptr to last node in list */
uint32 count; /* number of nodes in list */
uint32 extra; /* extra comma flag for [1,2,,] */
uint32 extra; /* extra flags, see below */
} list;
struct { /* ternary: if, for(;;), ?: */
JSParseNode *kid1; /* condition, discriminant, etc. */
@ -350,8 +351,7 @@ struct JSParseNode {
which is left kid of TOK_FOR */
#define PNX_ENDCOMMA 0x10 /* array literal has comma at end */
#define PNX_XMLROOT 0x20 /* top-most node in XML literal tree */
#define PNX_BLOCKEXPR 0x40 /* this block is an expression */
#define PNX_GROUPINIT 0x80 /* var [a, b] = [c, d]; unit list */
#define PNX_GROUPINIT 0x40 /* var [a, b] = [c, d]; unit list */
/*
* Move pn2 into pn, preserving pn->pn_pos and pn->pn_offset and handing off