mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-24 10:45:42 +00:00
Fix decompilation to preserve braces required by let, and also fix switch body block to have scope for let at top level of case statement (349634, r=mrbkap).
This commit is contained in:
parent
5c16f2b7d7
commit
ca3ef40908
@ -928,6 +928,7 @@ SrcNotes(JSContext *cx, JSScript *script)
|
||||
case SRC_PCBASE:
|
||||
case SRC_PCDELTA:
|
||||
case SRC_DECL:
|
||||
case SRC_BRACE:
|
||||
fprintf(gOutFile, " offset %u", (uintN) js_GetSrcNoteOffset(sn, 0));
|
||||
break;
|
||||
case SRC_LABEL:
|
||||
@ -960,8 +961,12 @@ SrcNotes(JSContext *cx, JSScript *script)
|
||||
break;
|
||||
case SRC_CATCH:
|
||||
delta = (uintN) js_GetSrcNoteOffset(sn, 0);
|
||||
if (delta)
|
||||
fprintf(gOutFile, " guard size %u", delta);
|
||||
if (delta) {
|
||||
if (script->main[offset] == JSOP_LEAVEBLOCK)
|
||||
fprintf(gOutFile, " stack depth %u", delta);
|
||||
else
|
||||
fprintf(gOutFile, " guard delta %u", delta);
|
||||
}
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
|
138
js/src/jsemit.c
138
js/src/jsemit.c
@ -1525,7 +1525,10 @@ js_LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp, JSBool letdecl)
|
||||
break;
|
||||
}
|
||||
|
||||
JS_ASSERT(stmt->flags & SIF_SCOPE);
|
||||
/* Skip "maybe scope" statements that don't contain let bindings. */
|
||||
if (!(stmt->flags & SIF_SCOPE))
|
||||
continue;
|
||||
|
||||
obj = ATOM_TO_OBJECT(stmt->atom);
|
||||
JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass);
|
||||
scope = OBJ_SCOPE(obj);
|
||||
@ -2533,6 +2536,10 @@ EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
|
||||
intN noteIndex;
|
||||
size_t switchSize, tableSize;
|
||||
jsbytecode *pc, *savepc;
|
||||
#if JS_HAS_BLOCK_SCOPE
|
||||
JSObject *obj;
|
||||
jsint count;
|
||||
#endif
|
||||
|
||||
/* Try for most optimal, fall back if not dense ints, and per ECMAv2. */
|
||||
switchOp = JSOP_TABLESWITCH;
|
||||
@ -2540,15 +2547,65 @@ EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
|
||||
hasDefault = constPropagated = JS_FALSE;
|
||||
defaultOffset = -1;
|
||||
|
||||
/* Emit code for the discriminant first. */
|
||||
if (!js_EmitTree(cx, cg, pn->pn_kid1))
|
||||
/*
|
||||
* If the switch contains let variables scoped by its body, model the
|
||||
* resulting block on the stack first, before emitting the discriminant's
|
||||
* bytecode (in case the discriminant contains a stack-model dependency
|
||||
* such as a let expression).
|
||||
*/
|
||||
pn2 = pn->pn_right;
|
||||
#if JS_HAS_BLOCK_SCOPE
|
||||
if (pn2->pn_type == TOK_LEXICALSCOPE) {
|
||||
atom = pn2->pn_atom;
|
||||
obj = ATOM_TO_OBJECT(atom);
|
||||
OBJ_SET_BLOCK_DEPTH(cx, obj, cg->stackDepth);
|
||||
|
||||
count = OBJ_BLOCK_COUNT(cx, obj);
|
||||
cg->stackDepth += count;
|
||||
if ((uintN)cg->stackDepth > cg->maxStackDepth)
|
||||
cg->maxStackDepth = cg->stackDepth;
|
||||
|
||||
/* Emit JSOP_ENTERBLOCK before code to evaluate the discriminant. */
|
||||
ale = js_IndexAtom(cx, atom, &cg->atomList);
|
||||
if (!ale)
|
||||
return JS_FALSE;
|
||||
EMIT_ATOM_INDEX_OP(JSOP_ENTERBLOCK, ALE_INDEX(ale));
|
||||
}
|
||||
#ifdef __GNUC__
|
||||
else {
|
||||
atom = NULL;
|
||||
count = -1;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Emit code for the discriminant first (or nearly first, in the case of a
|
||||
* switch whose body is a block scope).
|
||||
*/
|
||||
if (!js_EmitTree(cx, cg, pn->pn_left))
|
||||
return JS_FALSE;
|
||||
|
||||
/* Switch bytecodes run from here till end of final case. */
|
||||
top = CG_OFFSET(cg);
|
||||
#if !JS_HAS_BLOCK_SCOPE
|
||||
js_PushStatement(&cg->treeContext, stmtInfo, STMT_SWITCH, top);
|
||||
#else
|
||||
if (pn2->pn_type == TOK_LC) {
|
||||
js_PushStatement(&cg->treeContext, stmtInfo, STMT_SWITCH, top);
|
||||
} else {
|
||||
/* Recompute top now that the JSOP_ENTERBLOCK has been emitted. */
|
||||
top = CG_OFFSET(cg);
|
||||
|
||||
/* Push the body's block scope after emitting the discriminant. */
|
||||
js_PushBlockScope(&cg->treeContext, stmtInfo, atom, top);
|
||||
stmtInfo->type = STMT_SWITCH;
|
||||
|
||||
/* Advance pn2 to refer to the switch case list. */
|
||||
pn2 = pn2->pn_expr;
|
||||
}
|
||||
#endif
|
||||
|
||||
pn2 = pn->pn_kid2;
|
||||
caseCount = pn2->pn_count;
|
||||
tableLength = 0;
|
||||
table = NULL;
|
||||
@ -2953,7 +3010,17 @@ EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
|
||||
out:
|
||||
if (table)
|
||||
JS_free(cx, table);
|
||||
return ok && js_PopStatementCG(cx, cg);
|
||||
if (ok) {
|
||||
ok = js_PopStatementCG(cx, cg);
|
||||
|
||||
#if JS_HAS_BLOCK_SCOPE
|
||||
if (ok && pn->pn_right->pn_type == TOK_LEXICALSCOPE) {
|
||||
EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count);
|
||||
cg->stackDepth -= count;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return ok;
|
||||
|
||||
bad:
|
||||
ok = JS_FALSE;
|
||||
@ -4741,13 +4808,19 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
|
||||
case TOK_CATCH:
|
||||
{
|
||||
ptrdiff_t guardJump;
|
||||
ptrdiff_t catchStart, guardJump;
|
||||
|
||||
/* Morph STMT_BLOCK to STMT_CATCH and save the block object atom. */
|
||||
/*
|
||||
* Morph STMT_BLOCK to STMT_CATCH, note the block entry code offset,
|
||||
* and save the block object atom.
|
||||
*/
|
||||
stmt = cg->treeContext.topStmt;
|
||||
JS_ASSERT(stmt->type == STMT_BLOCK && (stmt->flags & SIF_SCOPE));
|
||||
stmt->type = STMT_CATCH;
|
||||
catchStart = stmt->update;
|
||||
atom = stmt->atom;
|
||||
|
||||
/* Go up one statement info record to the TRY or FINALLY record. */
|
||||
stmt = stmt->down;
|
||||
JS_ASSERT(stmt->type == STMT_TRY || stmt->type == STMT_FINALLY);
|
||||
|
||||
@ -4778,11 +4851,10 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
|
||||
/* 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)) {
|
||||
CG_OFFSET(cg) - catchStart)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
/* ifeq <next block> */
|
||||
@ -4877,11 +4949,27 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
#endif
|
||||
|
||||
JS_ASSERT(pn->pn_arity == PN_LIST);
|
||||
|
||||
noteIndex = -1;
|
||||
tmp = CG_OFFSET(cg);
|
||||
if (pn->pn_extra & PNX_NEEDBRACES) {
|
||||
noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0);
|
||||
if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BLOCK, top);
|
||||
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
|
||||
if (!js_EmitTree(cx, cg, pn2))
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (noteIndex >= 0 &&
|
||||
!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0,
|
||||
CG_OFFSET(cg) - tmp)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
ok = js_PopStatementCG(cx, cg);
|
||||
break;
|
||||
|
||||
@ -5621,6 +5709,27 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
if ((uintN)cg->stackDepth > cg->maxStackDepth)
|
||||
cg->maxStackDepth = cg->stackDepth;
|
||||
|
||||
/*
|
||||
* If this lexical scope is not for a catch block, let block, or let
|
||||
* expression, and our container is top-level but not a function body,
|
||||
* or else a block statement, emit a SRC_BRACE note. Other container
|
||||
* statements get braces by default from the decompiler.
|
||||
*/
|
||||
noteIndex = -1;
|
||||
tmp = CG_OFFSET(cg);
|
||||
type = pn->pn_expr->pn_type;
|
||||
if (type != TOK_CATCH && type != TOK_LET &&
|
||||
(!(stmt = stmtInfo.down)
|
||||
? !(cg->treeContext.flags & TCF_IN_FUNCTION)
|
||||
: stmt->type == STMT_BLOCK)) {
|
||||
/* There must be no source note already output for the next op. */
|
||||
JS_ASSERT(CG_NOTE_COUNT(cg) == 0 ||
|
||||
CG_LAST_NOTE_OFFSET(cg) != CG_OFFSET(cg));
|
||||
noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0);
|
||||
if (noteIndex < 0)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
ale = js_IndexAtom(cx, atom, &cg->atomList);
|
||||
if (!ale)
|
||||
return JS_FALSE;
|
||||
@ -5629,6 +5738,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
if (!js_EmitTree(cx, cg, pn->pn_expr))
|
||||
return JS_FALSE;
|
||||
|
||||
if (noteIndex >= 0 &&
|
||||
!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0,
|
||||
CG_OFFSET(cg) - tmp)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
/* Emit the JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR opcode. */
|
||||
EMIT_UINT16_IMM_OP(pn->pn_op, count);
|
||||
cg->stackDepth -= count;
|
||||
@ -6067,6 +6182,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
return ok;
|
||||
}
|
||||
|
||||
/* XXX get rid of offsetBias altogether, it's used only by SRC_FOR */
|
||||
JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = {
|
||||
{"null", 0, 0, 0},
|
||||
{"if", 0, 0, 0},
|
||||
@ -6078,7 +6194,7 @@ JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = {
|
||||
{"pcdelta", 1, 0, 1},
|
||||
{"assignop", 0, 0, 0},
|
||||
{"cond", 1, 0, 1},
|
||||
{"unused10", 0, 0, 0},
|
||||
{"brace", 1, 0, 1},
|
||||
{"hidden", 0, 0, 0},
|
||||
{"pcbase", 1, 0, -1},
|
||||
{"label", 1, 0, 0},
|
||||
@ -6088,7 +6204,7 @@ JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = {
|
||||
{"cont2label", 1, 0, 0},
|
||||
{"switch", 2, 0, 1},
|
||||
{"funcdef", 1, 0, 0},
|
||||
{"catch", 1, 11, 1},
|
||||
{"catch", 1, 0, 1},
|
||||
{"unused21", 0, 0, 0},
|
||||
{"newline", 0, 0, 0},
|
||||
{"setline", 1, 0, 0},
|
||||
|
@ -65,8 +65,8 @@ typedef enum JSStmtType {
|
||||
STMT_LABEL, /* labeled statement: L: s */
|
||||
STMT_IF, /* if (then) statement */
|
||||
STMT_ELSE, /* else clause of if statement */
|
||||
STMT_SWITCH, /* switch statement */
|
||||
STMT_BLOCK, /* compound statement: { s1[;... sN] } */
|
||||
STMT_SWITCH, /* switch statement */
|
||||
STMT_WITH, /* with statement */
|
||||
STMT_CATCH, /* catch block */
|
||||
STMT_TRY, /* try block */
|
||||
@ -109,8 +109,8 @@ struct JSStmtInfo {
|
||||
JSStmtInfo *downScope; /* next enclosing lexical scope */
|
||||
};
|
||||
|
||||
#define SIF_SCOPE 0x0002 /* statement has its own lexical scope */
|
||||
#define SIF_BODY_BLOCK 0x0001 /* STMT_BLOCK type is a function body */
|
||||
#define SIF_SCOPE 0x0001 /* statement has its own lexical scope */
|
||||
#define SIF_BODY_BLOCK 0x0002 /* STMT_BLOCK type is a function body */
|
||||
|
||||
/*
|
||||
* To reuse space in JSStmtInfo, rename breaks and continues for use during
|
||||
@ -158,6 +158,7 @@ struct JSTreeContext { /* tree context for semantic checks */
|
||||
#define TCF_FUN_IS_GENERATOR 0x100 /* parsed yield statement in function */
|
||||
#define TCF_FUN_FLAGS 0x1E0 /* flags to propagate from FunctionBody */
|
||||
#define TCF_HAS_DEFXMLNS 0x200 /* default xml namespace = ...; parsed */
|
||||
#define TCF_HAS_CLOSURE 0x400 /* function statement was parsed */
|
||||
|
||||
#define TREE_CONTEXT_INIT(tc) \
|
||||
((tc)->flags = (tc)->numGlobalVars = 0, \
|
||||
@ -499,7 +500,8 @@ typedef enum JSSrcNoteType {
|
||||
gets and sets */
|
||||
SRC_ASSIGNOP = 8, /* += or another assign-op follows */
|
||||
SRC_COND = 9, /* JSOP_IFEQ is from conditional ?: operator */
|
||||
SRC_UNUSED10 = 10, /* unused */
|
||||
SRC_BRACE = 10, /* mandatory brace, for scope or to avoid
|
||||
dangling else */
|
||||
SRC_HIDDEN = 11, /* opcode shouldn't be decompiled */
|
||||
SRC_PCBASE = 12, /* distance back from annotated get- or setprop
|
||||
op to first obj.prop.subprop bytecode */
|
||||
|
@ -1223,6 +1223,15 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
|
||||
break;
|
||||
|
||||
case SRC_BRACE:
|
||||
js_printf(jp, "\t{\n");
|
||||
jp->indent += 4;
|
||||
len = js_GetSrcNoteOffset(sn, 0);
|
||||
DECOMPILE_CODE(pc + oplen, len - oplen);
|
||||
jp->indent -= 4;
|
||||
js_printf(jp, "\t}\n");
|
||||
break;
|
||||
|
||||
default:;
|
||||
}
|
||||
case JSOP_RETRVAL:
|
||||
@ -1492,10 +1501,25 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
}
|
||||
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
if (sn && SN_TYPE(sn) == SRC_CATCH) {
|
||||
switch (sn ? SN_TYPE(sn) : SRC_NULL) {
|
||||
#if JS_HAS_BLOCK_SCOPE
|
||||
case SRC_BRACE:
|
||||
js_printf(jp, "\t{\n");
|
||||
jp->indent += 4;
|
||||
len = js_GetSrcNoteOffset(sn, 0);
|
||||
ok = Decompile(ss, pc + oplen, len - oplen);
|
||||
if (!ok)
|
||||
goto enterblock_out;
|
||||
jp->indent -= 4;
|
||||
js_printf(jp, "\t}\n");
|
||||
break;
|
||||
#endif
|
||||
|
||||
case SRC_CATCH:
|
||||
jp->indent -= 4;
|
||||
js_printf(jp, "\t} catch (");
|
||||
|
||||
pc2 = pc;
|
||||
pc += oplen;
|
||||
LOCAL_ASSERT(*pc == JSOP_EXCEPTION);
|
||||
pc += JSOP_EXCEPTION_LENGTH;
|
||||
@ -1513,6 +1537,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
|
||||
len = js_GetSrcNoteOffset(sn, 0);
|
||||
if (len) {
|
||||
len -= PTRDIFF(pc, pc2, jsbytecode);
|
||||
JS_ASSERT(len > 0);
|
||||
js_printf(jp, " if ");
|
||||
ok = Decompile(ss, pc, len);
|
||||
if (!ok)
|
||||
@ -1526,6 +1552,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
js_printf(jp, ") {\n");
|
||||
jp->indent += 4;
|
||||
len = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
todo = -2;
|
||||
|
@ -601,7 +601,10 @@ HasFinalReturn(JSParseNode *pn)
|
||||
case TOK_SWITCH:
|
||||
rv = ENDS_IN_RETURN;
|
||||
hasDefault = ENDS_IN_OTHER;
|
||||
for (pn2 = pn->pn_kid2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
|
||||
pn2 = pn->pn_right;
|
||||
if (pn2->pn_type == TOK_LEXICALSCOPE)
|
||||
pn2 = pn2->pn_expr;
|
||||
for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
|
||||
if (pn2->pn_type == TOK_DEFAULT)
|
||||
hasDefault = ENDS_IN_RETURN;
|
||||
pn3 = pn2->pn_right;
|
||||
@ -1375,6 +1378,7 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
* sub-statement.
|
||||
*/
|
||||
op = JSOP_CLOSURE;
|
||||
tc->flags |= TCF_HAS_CLOSURE;
|
||||
} else {
|
||||
op = JSOP_NOP;
|
||||
}
|
||||
@ -2546,7 +2550,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
|
||||
case TOK_SWITCH:
|
||||
{
|
||||
JSParseNode *pn5;
|
||||
JSParseNode *pn5, *saveBlock;
|
||||
JSBool seenDefault = JS_FALSE;
|
||||
|
||||
pn = NewParseNode(cx, ts, PN_BINARY, tc);
|
||||
@ -2566,6 +2570,8 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
pn2 = NewParseNode(cx, ts, PN_LIST, tc);
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
saveBlock = tc->blockNode;
|
||||
tc->blockNode = pn2;
|
||||
PN_INIT_LIST(pn2);
|
||||
|
||||
js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
|
||||
@ -2640,11 +2646,20 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
pn3->pn_right = pn4;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the case where there was a let declaration in any case in
|
||||
* the switch body, but not within an inner block. If it replaced
|
||||
* tc->blockNode with a new block node then we must refresh pn2 and
|
||||
* then restore tc->blockNode.
|
||||
*/
|
||||
if (tc->blockNode != pn2)
|
||||
pn2 = tc->blockNode;
|
||||
tc->blockNode = saveBlock;
|
||||
js_PopStatement(tc);
|
||||
|
||||
pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
|
||||
pn->pn_kid1 = pn1;
|
||||
pn->pn_kid2 = pn2;
|
||||
pn->pn_left = pn1;
|
||||
pn->pn_right = pn2;
|
||||
return pn;
|
||||
}
|
||||
|
||||
@ -3364,6 +3379,11 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
break;
|
||||
|
||||
case TOK_LC:
|
||||
{
|
||||
uintN oldflags;
|
||||
|
||||
oldflags = tc->flags;
|
||||
tc->flags = oldflags & ~TCF_HAS_CLOSURE;
|
||||
js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
|
||||
pn = Statements(cx, ts, tc);
|
||||
if (!pn)
|
||||
@ -3371,7 +3391,18 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
|
||||
js_PopStatement(tc);
|
||||
|
||||
/*
|
||||
* If we contain a function statement and our container is top-level
|
||||
* or another block, flag pn to preserve braces when decompiling.
|
||||
*/
|
||||
if ((tc->flags & TCF_HAS_CLOSURE) &&
|
||||
(!tc->topStmt || tc->topStmt->type == STMT_BLOCK)) {
|
||||
pn->pn_extra |= PNX_NEEDBRACES;
|
||||
}
|
||||
tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
|
||||
return pn;
|
||||
}
|
||||
|
||||
case TOK_EOL:
|
||||
case TOK_SEMI:
|
||||
|
@ -82,7 +82,10 @@ JS_BEGIN_EXTERN_C
|
||||
* TOK_IF ternary pn_kid1: cond, pn_kid2: then, pn_kid3: else or null
|
||||
* TOK_SWITCH binary pn_left: discriminant
|
||||
* pn_right: list of TOK_CASE nodes, with at most one
|
||||
* TOK_DEFAULT node
|
||||
* TOK_DEFAULT node, or if there are let bindings
|
||||
* in the top level of the switch body's cases, a
|
||||
* TOK_LEXICALSCOPE node that contains the list of
|
||||
* TOK_CASE nodes.
|
||||
* TOK_CASE, binary pn_left: case expr or null if TOK_DEFAULT
|
||||
* TOK_DEFAULT pn_right: TOK_LC node for this case's statements
|
||||
* pn_val: constant value if lookup or table switch
|
||||
@ -349,6 +352,7 @@ struct JSParseNode {
|
||||
#define PNX_ENDCOMMA 0x10 /* array literal has comma at end */
|
||||
#define PNX_XMLROOT 0x20 /* top-most node in XML literal tree */
|
||||
#define PNX_GROUPINIT 0x40 /* var [a, b] = [c, d]; unit list */
|
||||
#define PNX_NEEDBRACES 0x80 /* braces necessary due to closure */
|
||||
|
||||
/*
|
||||
* Move pn2 into pn, preserving pn->pn_pos and pn->pn_offset and handing off
|
||||
|
Loading…
Reference in New Issue
Block a user