Destructuring decompilation (346642, anticipating r=mrbkap).

This commit is contained in:
brendan%mozilla.org 2006-09-30 06:46:56 +00:00
Родитель 4975edd6b9
Коммит 9ab0984ab2
8 изменённых файлов: 950 добавлений и 249 удалений

Просмотреть файл

@ -2123,7 +2123,9 @@ CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn,
case PN_LIST: case PN_LIST:
if (pn->pn_type == TOK_NEW || if (pn->pn_type == TOK_NEW ||
pn->pn_type == TOK_LP || pn->pn_type == TOK_LP ||
pn->pn_type == TOK_LB) { pn->pn_type == TOK_LB ||
pn->pn_type == TOK_RB ||
pn->pn_type == TOK_RC) {
/* /*
* All invocation operations (construct: TOK_NEW, call: TOK_LP) * All invocation operations (construct: TOK_NEW, call: TOK_LP)
* are presumed to be useful, because they may have side effects * are presumed to be useful, because they may have side effects
@ -2134,6 +2136,10 @@ CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn,
* to be useful because each index operation could invoke a getter * to be useful because each index operation could invoke a getter
* (the JSOP_ARGUMENTS special case below, in the PN_BINARY case, * (the JSOP_ARGUMENTS special case below, in the PN_BINARY case,
* does not apply here: arguments[i][j] might invoke a getter). * does not apply here: arguments[i][j] might invoke a getter).
*
* Array and object initializers (TOK_RB and TOK_RC lists) must be
* considered useful, because they are sugar for constructor calls
* (to Array and Object, respectively).
*/ */
*answer = JS_TRUE; *answer = JS_TRUE;
} else { } else {
@ -2425,9 +2431,9 @@ EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
/* /*
* Set left and right so pn appears to be a TOK_LB node, instead * Set left and right so pn appears to be a TOK_LB node, instead
* of a TOK_DOT node. See the TOK_FOR/IN case in js_EmitTree, and * of a TOK_DOT node. See the TOK_FOR/IN case in js_EmitTree, and
* EmitDestructuring nearer below. In the destructuring case, the * EmitDestructuringOps nearer below. In the destructuring case,
* base expression (pn_expr) of the name may be null, which means * the base expression (pn_expr) of the name may be null, which
* we have to emit a JSOP_BINDNAME. * means we have to emit a JSOP_BINDNAME.
*/ */
left = pn->pn_expr; left = pn->pn_expr;
if (!left) { if (!left) {
@ -2440,7 +2446,10 @@ EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
} }
right = &rtmp; right = &rtmp;
right->pn_type = TOK_STRING; right->pn_type = TOK_STRING;
right->pn_op = JSOP_STRING; JS_ASSERT(ATOM_IS_STRING(pn->pn_atom));
right->pn_op = js_IsIdentifier(ATOM_TO_STRING(pn->pn_atom))
? JSOP_QNAMEPART
: JSOP_STRING;
right->pn_arity = PN_NULLARY; right->pn_arity = PN_NULLARY;
right->pn_pos = pn->pn_pos; right->pn_pos = pn->pn_pos;
right->pn_atom = pn->pn_atom; right->pn_atom = pn->pn_atom;
@ -3326,7 +3335,6 @@ EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
{ {
jsuint index; jsuint index;
JSParseNode *pn2, *pn3; JSParseNode *pn2, *pn3;
ptrdiff_t top;
JSBool doElemOp; JSBool doElemOp;
#ifdef DEBUG #ifdef DEBUG
@ -3336,19 +3344,17 @@ EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
JS_ASSERT(pn->pn_type == TOK_RB || pn->pn_type == TOK_RC); JS_ASSERT(pn->pn_type == TOK_RB || pn->pn_type == TOK_RC);
#endif #endif
if (pn->pn_count == 0) {
/* Emit a DUP;POP sequence for the decompiler. */
return js_Emit1(cx, cg, JSOP_DUP) >= 0 &&
js_Emit1(cx, cg, JSOP_POP) >= 0;
}
index = 0; index = 0;
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
/* Nullary comma node makes a hole in the array destructurer. */
if (pn2->pn_type == TOK_COMMA && pn2->pn_arity == PN_NULLARY) {
JS_ASSERT(pn->pn_type == TOK_RB);
++index;
continue;
}
/* /*
* Duplicate the value being destructured to use as a reference base. * Duplicate the value being destructured to use as a reference base.
*/ */
top = CG_OFFSET(cg);
if (js_Emit1(cx, cg, JSOP_DUP) < 0) if (js_Emit1(cx, cg, JSOP_DUP) < 0)
return JS_FALSE; return JS_FALSE;
@ -3373,10 +3379,8 @@ EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
* annotate the index op with SRC_INITPROP so we know we are * annotate the index op with SRC_INITPROP so we know we are
* not decompiling an array initialiser. * not decompiling an array initialiser.
*/ */
if (pn->pn_type == TOK_RC && if (js_NewSrcNote(cx, cg, SRC_INITPROP) < 0)
js_NewSrcNote(cx, cg, SRC_INITPROP) < 0) {
return JS_FALSE; return JS_FALSE;
}
if (!EmitNumberOp(cx, pn3->pn_dval, cg)) if (!EmitNumberOp(cx, pn3->pn_dval, cg))
return JS_FALSE; return JS_FALSE;
} else { } else {
@ -3395,15 +3399,21 @@ EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
* that value on top of the value being destructured, so the stack * that value on top of the value being destructured, so the stack
* is one deeper than when we started. * is one deeper than when we started.
*/ */
if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0)
return JS_FALSE;
if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) if (js_Emit1(cx, cg, JSOP_GETELEM) < 0)
return JS_FALSE; return JS_FALSE;
JS_ASSERT(cg->stackDepth == stackDepth + 1); JS_ASSERT(cg->stackDepth == stackDepth + 1);
} }
if (!EmitDestructuringLHS(cx, cg, pn3, JS_TRUE)) /* Nullary comma node makes a hole in the array destructurer. */
return JS_FALSE; if (pn3->pn_type == TOK_COMMA && pn3->pn_arity == PN_NULLARY) {
JS_ASSERT(pn->pn_type == TOK_RB);
JS_ASSERT(pn2 == pn3);
if (js_Emit1(cx, cg, JSOP_POP) < 0)
return JS_FALSE;
} else {
if (!EmitDestructuringLHS(cx, cg, pn3, JS_TRUE))
return JS_FALSE;
}
JS_ASSERT(cg->stackDepth == stackDepth); JS_ASSERT(cg->stackDepth == stackDepth);
++index; ++index;
@ -3412,35 +3422,33 @@ EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
return JS_TRUE; return JS_TRUE;
} }
static ptrdiff_t
OpToDeclType(JSOp op)
{
switch (op) {
case JSOP_NOP:
return SRC_DECL_LET;
case JSOP_DEFCONST:
return SRC_DECL_CONST;
case JSOP_DEFVAR:
return SRC_DECL_VAR;
default:
return SRC_DECL_NONE;
}
}
static JSBool static JSBool
EmitDestructuringOps(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, EmitDestructuringOps(JSContext *cx, JSCodeGenerator *cg, JSOp declOp,
JSParseNode *pn) JSParseNode *pn)
{ {
ptrdiff_t declType;
/* /*
* If we're called from a variable declaration, help the decompiler by * If we're called from a variable declaration, help the decompiler by
* annotating the first JSOP_DUP that EmitDestructuringOpsHelper emits. * annotating the first JSOP_DUP that EmitDestructuringOpsHelper emits.
* The parser ensures that a destructuring initialiser has at least one * If the destructuring initialiser is empty, our helper will emit a
* element lvalue, so we know that JSOP_DUP will be emitted. * JSOP_DUP followed by a JSOP_POP for the decompiler.
*/ */
switch (prologOp) { if (js_NewSrcNote2(cx, cg, SRC_DESTRUCT, OpToDeclType(declOp)) < 0)
case JSOP_DEFCONST: return JS_FALSE;
declType = SRC_DECL_CONST;
goto emit_decl_note;
case JSOP_DEFVAR:
declType = SRC_DECL_VAR;
emit_decl_note:
if (js_NewSrcNote2(cx, cg, SRC_DECL, declType) < 0)
return JS_FALSE;
break;
default:
if (js_NewSrcNote(cx, cg, SRC_ASSIGNOP) < 0)
return JS_FALSE;
break;
}
/* /*
* Call our recursive helper to emit the destructuring assignments and * Call our recursive helper to emit the destructuring assignments and
@ -3450,8 +3458,8 @@ EmitDestructuringOps(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp,
} }
static JSBool static JSBool
EmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSParseNode *lhs, EmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp declOp,
JSParseNode *rhs) JSParseNode *lhs, JSParseNode *rhs)
{ {
jsuint depth, limit, slot; jsuint depth, limit, slot;
JSParseNode *pn; JSParseNode *pn;
@ -3475,6 +3483,9 @@ EmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSParseNode *lhs,
++limit; ++limit;
} }
if (js_NewSrcNote2(cx, cg, SRC_GROUPASSIGN, OpToDeclType(declOp)) < 0)
return JS_FALSE;
slot = depth; slot = depth;
for (pn = lhs->pn_head; pn; pn = pn->pn_next) { for (pn = lhs->pn_head; pn; pn = pn->pn_next) {
if (pn->pn_type != TOK_COMMA) { if (pn->pn_type != TOK_COMMA) {
@ -3501,8 +3512,8 @@ EmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSParseNode *lhs,
* we set *pop to JSOP_NOP so callers can veto emitting pn followed by a pop. * we set *pop to JSOP_NOP so callers can veto emitting pn followed by a pop.
*/ */
static JSBool static JSBool
MaybeEmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, MaybeEmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp declOp,
JSOp *pop) JSParseNode *pn, JSOp *pop)
{ {
JSParseNode *lhs, *rhs; JSParseNode *lhs, *rhs;
@ -3511,7 +3522,7 @@ MaybeEmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
lhs = pn->pn_left; lhs = pn->pn_left;
rhs = pn->pn_right; rhs = pn->pn_right;
if (lhs->pn_type == TOK_RB && rhs->pn_type == TOK_RB) { if (lhs->pn_type == TOK_RB && rhs->pn_type == TOK_RB) {
if (!EmitGroupAssignment(cx, cg, lhs, rhs)) if (!EmitGroupAssignment(cx, cg, declOp, lhs, rhs))
return JS_FALSE; return JS_FALSE;
*pop = JSOP_NOP; *pop = JSOP_NOP;
} }
@ -3553,7 +3564,7 @@ EmitVariables(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
*/ */
tc = &cg->treeContext; tc = &cg->treeContext;
let = (pn->pn_op == JSOP_NOP); let = (pn->pn_op == JSOP_NOP);
forInVar = (pn->pn_extra & PNX_FORINVAR); forInVar = (pn->pn_extra & PNX_FORINVAR) != 0;
#if JS_HAS_BLOCK_SCOPE #if JS_HAS_BLOCK_SCOPE
forInLet = let && forInVar; forInLet = let && forInVar;
popScope = (inLetHead || (let && (tc->flags & TCF_IN_FOR_INIT))); popScope = (inLetHead || (let && (tc->flags & TCF_IN_FOR_INIT)));
@ -3588,15 +3599,20 @@ EmitVariables(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
* the head of the loop. * the head of the loop.
*/ */
JS_ASSERT(pn2->pn_type == TOK_ASSIGN); JS_ASSERT(pn2->pn_type == TOK_ASSIGN);
if (pn->pn_count == 1) { if (pn->pn_count == 1 && !forInLet) {
/* /*
* If this is the only destructuring assignment in the list, * If this is the only destructuring assignment in the list,
* try to optimize to a group assignment. * try to optimize to a group assignment. If we're in a let
* head, pass JSOP_POP rather than the pseudo-prolog JSOP_NOP
* in pn->pn_op, to suppress a second (and misplaced) 'let'.
*/ */
JS_ASSERT(noteIndex < 0 && !pn2->pn_next); JS_ASSERT(noteIndex < 0 && !pn2->pn_next);
op = JSOP_POP; op = JSOP_POP;
if (!MaybeEmitGroupAssignment(cx, cg, pn2, &op)) if (!MaybeEmitGroupAssignment(cx, cg,
inLetHead ? JSOP_POP : pn->pn_op,
pn2, &op)) {
return JS_FALSE; return JS_FALSE;
}
if (op == JSOP_NOP) { if (op == JSOP_NOP) {
pn->pn_extra = (pn->pn_extra & ~PNX_POPVAR) | PNX_GROUPINIT; pn->pn_extra = (pn->pn_extra & ~PNX_POPVAR) | PNX_GROUPINIT;
break; break;
@ -3638,16 +3654,25 @@ EmitVariables(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
* This has the effect of hoisting the evaluation of i out of the * This has the effect of hoisting the evaluation of i out of the
* for-in loop, without hoisting the let variables, which must of * for-in loop, without hoisting the let variables, which must of
* course be scoped by the loop. Set PNX_POPVAR to cause JSOP_POP * course be scoped by the loop. Set PNX_POPVAR to cause JSOP_POP
* to be emitted, just before the bottom of this function. * to be emitted, just before returning from this function.
*/ */
if (forInLet) { if (forInVar) {
pn->pn_extra |= PNX_POPVAR; pn->pn_extra |= PNX_POPVAR;
break; if (forInLet)
break;
} }
#endif #endif
if (!EmitDestructuringOps(cx, cg, pn->pn_op, pn3)) /*
* Veto pn->pn_op if inLetHead to avoid emitting a SRC_DESTRUCT
* that's redundant with respect to the SRC_DECL/SRC_DECL_LET that
* we will emit at the bottom of this function.
*/
if (!EmitDestructuringOps(cx, cg,
inLetHead ? JSOP_POP : pn->pn_op,
pn3)) {
return JS_FALSE; return JS_FALSE;
}
goto emit_note_pop; goto emit_note_pop;
} }
#else #else
@ -3794,7 +3819,8 @@ EmitVariables(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
*headNoteIndex = js_NewSrcNote(cx, cg, SRC_DECL); *headNoteIndex = js_NewSrcNote(cx, cg, SRC_DECL);
if (*headNoteIndex < 0) if (*headNoteIndex < 0)
return JS_FALSE; return JS_FALSE;
JS_ASSERT(pn->pn_extra & PNX_POPVAR); if (!(pn->pn_extra & PNX_POPVAR))
return js_Emit1(cx, cg, JSOP_NOP) >= 0;
} }
return !(pn->pn_extra & PNX_POPVAR) || js_Emit1(cx, cg, JSOP_POP) >= 0; return !(pn->pn_extra & PNX_POPVAR) || js_Emit1(cx, cg, JSOP_POP) >= 0;
@ -4214,9 +4240,18 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
if (js_Emit1(cx, cg, pn->pn_op) < 0) if (js_Emit1(cx, cg, pn->pn_op) < 0)
return JS_FALSE; return JS_FALSE;
/* Compile a JSOP_FOR* bytecode based on the left hand side. */ /*
* Compile a JSOP_FOR* bytecode based on the left hand side.
*
* Initialize op to JSOP_SETNAME in case of |for ([a, b] in o)...|
* or similar, to signify assignment, rather than declaration, to
* the decompiler. EmitDestructuringOps takes a prolog bytecode
* parameter and emits the appropriate source note, defaulting to
* assignment, so JSOP_SETNAME is not critical here; many similar
* ops could be used -- just not JSOP_NOP (which means 'let').
*/
emitIFEQ = JS_TRUE; emitIFEQ = JS_TRUE;
op = JSOP_NOP; op = JSOP_SETNAME;
switch (type) { switch (type) {
#if JS_HAS_BLOCK_SCOPE #if JS_HAS_BLOCK_SCOPE
case TOK_LET: case TOK_LET:
@ -4400,7 +4435,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
#if JS_HAS_DESTRUCTURING #if JS_HAS_DESTRUCTURING
pn3 = pn2->pn_kid1; pn3 = pn2->pn_kid1;
if (pn3->pn_type == TOK_ASSIGN && if (pn3->pn_type == TOK_ASSIGN &&
!MaybeEmitGroupAssignment(cx, cg, pn3, &op)) { !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) {
return JS_FALSE; return JS_FALSE;
} }
#endif #endif
@ -4472,7 +4507,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
op = JSOP_POP; op = JSOP_POP;
#if JS_HAS_DESTRUCTURING #if JS_HAS_DESTRUCTURING
if (pn3->pn_type == TOK_ASSIGN && if (pn3->pn_type == TOK_ASSIGN &&
!MaybeEmitGroupAssignment(cx, cg, pn3, &op)) { !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) {
return JS_FALSE; return JS_FALSE;
} }
#endif #endif
@ -5043,6 +5078,16 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
ok = js_PopStatementCG(cx, cg); ok = js_PopStatementCG(cx, cg);
break; break;
case TOK_BODY:
JS_ASSERT(pn->pn_arity == PN_LIST);
js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BODY, top);
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
if (!js_EmitTree(cx, cg, pn2))
return JS_FALSE;
}
ok = js_PopStatementCG(cx, cg);
break;
case TOK_SEMI: case TOK_SEMI:
pn2 = pn->pn_kid; pn2 = pn->pn_kid;
if (pn2) { if (pn2) {
@ -5083,7 +5128,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
#if JS_HAS_DESTRUCTURING #if JS_HAS_DESTRUCTURING
if (!wantval && if (!wantval &&
pn2->pn_type == TOK_ASSIGN && pn2->pn_type == TOK_ASSIGN &&
!MaybeEmitGroupAssignment(cx, cg, pn2, &op)) { !MaybeEmitGroupAssignment(cx, cg, op, pn2, &op)) {
return JS_FALSE; return JS_FALSE;
} }
#endif #endif
@ -5323,7 +5368,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
#if JS_HAS_DESTRUCTURING #if JS_HAS_DESTRUCTURING
case TOK_RB: case TOK_RB:
case TOK_RC: case TOK_RC:
if (!EmitDestructuringOps(cx, cg, JSOP_NOP, pn2)) if (!EmitDestructuringOps(cx, cg, JSOP_SETNAME, pn2))
return JS_FALSE; return JS_FALSE;
break; break;
#endif #endif
@ -6274,7 +6319,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
return ok; return ok;
} }
/* XXX get rid of offsetBias altogether, it's used only by SRC_FOR */ /* XXX get rid of offsetBias, it's used only by SRC_FOR and SRC_DECL */
JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = { JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = {
{"null", 0, 0, 0}, {"null", 0, 0, 0},
{"if", 0, 0, 0}, {"if", 0, 0, 0},
@ -6282,7 +6327,7 @@ JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = {
{"while", 1, 0, 1}, {"while", 1, 0, 1},
{"for", 3, 1, 1}, {"for", 3, 1, 1},
{"continue", 0, 0, 0}, {"continue", 0, 0, 0},
{"decl", 1, 0, 1}, {"decl", 1, 1, 1},
{"pcdelta", 1, 0, 1}, {"pcdelta", 1, 0, 1},
{"assignop", 0, 0, 0}, {"assignop", 0, 0, 0},
{"cond", 1, 0, 1}, {"cond", 1, 0, 1},

Просмотреть файл

@ -65,6 +65,8 @@ typedef enum JSStmtType {
STMT_LABEL, /* labeled statement: L: s */ STMT_LABEL, /* labeled statement: L: s */
STMT_IF, /* if (then) statement */ STMT_IF, /* if (then) statement */
STMT_ELSE, /* else clause of if statement */ STMT_ELSE, /* else clause of if statement */
STMT_BODY, /* synthetic body of function with
destructuring formal parameters */
STMT_BLOCK, /* compound statement: { s1[;... sN] } */ STMT_BLOCK, /* compound statement: { s1[;... sN] } */
STMT_SWITCH, /* switch statement */ STMT_SWITCH, /* switch statement */
STMT_WITH, /* with statement */ STMT_WITH, /* with statement */
@ -81,15 +83,19 @@ typedef enum JSStmtType {
#define STMT_TYPE_IN_RANGE(t,b,e) ((uint)((t) - (b)) <= (uintN)((e) - (b))) #define STMT_TYPE_IN_RANGE(t,b,e) ((uint)((t) - (b)) <= (uintN)((e) - (b)))
/* /*
* A comment on the encoding of the JSStmtType enum and type-testing macros:
*
* STMT_TYPE_MAYBE_SCOPE tells whether a statement type is always, or may * STMT_TYPE_MAYBE_SCOPE tells whether a statement type is always, or may
* become, a lexical scope. It therefore includes block and switch (the two * become, a lexical scope. It therefore includes block and switch (the two
* "maybe" scopes) and excludes with (which has dynamic scope, pending the * low-numbered "maybe" scope types) and excludes with (with has dynamic scope
* "reformed with" in ES4/JS2). It includes all try-catch-finally types. * pending the "reformed with" in ES4/JS2). It includes all try-catch-finally
* types, which are high-numbered maybe-scope types.
* *
* STMT_TYPE_LINKS_SCOPE tells whether a JSStmtInfo of the given type eagerly * STMT_TYPE_LINKS_SCOPE tells whether a JSStmtInfo of the given type eagerly
* links to other scoping statement info records. It excludes the two "maybe" * links to other scoping statement info records. It excludes the two early
* types, block and switch, as well as the try and both finally types, since * "maybe" types, block and switch, as well as the try and both finally types,
* try, etc., don't need block scope unless they contain let declarations. * since try and the other trailing maybe-scope types don't need block scope
* unless they contain let declarations.
* *
* We treat with as a static scope because it prevents lexical binding from * We treat with as a static scope because it prevents lexical binding from
* continuing further up the static scope chain. With the "reformed with" * continuing further up the static scope chain. With the "reformed with"
@ -523,9 +529,12 @@ typedef enum JSSrcNoteType {
also used on JSOP_ENDINIT if extra comma also used on JSOP_ENDINIT if extra comma
at end of array literal: [1,2,,] */ at end of array literal: [1,2,,] */
SRC_DECL = 6, /* type of a declaration (var, const, let*) */ SRC_DECL = 6, /* type of a declaration (var, const, let*) */
SRC_DESTRUCT = 6, /* JSOP_DUP starting a destructuring assignment
operation, with SRC_DECL_* offset operand */
SRC_PCDELTA = 7, /* distance forward from comma-operator to SRC_PCDELTA = 7, /* distance forward from comma-operator to
next POP, or from CONDSWITCH to first CASE next POP, or from CONDSWITCH to first CASE
opcode, etc. -- always a forward delta */ opcode, etc. -- always a forward delta */
SRC_GROUPASSIGN = 7, /* SRC_DESTRUCT variant for [a, b] = [c, d] */
SRC_ASSIGNOP = 8, /* += or another assign-op follows */ SRC_ASSIGNOP = 8, /* += or another assign-op follows */
SRC_COND = 9, /* JSOP_IFEQ is from conditional ?: operator */ SRC_COND = 9, /* JSOP_IFEQ is from conditional ?: operator */
SRC_BRACE = 10, /* mandatory brace, for scope or to avoid SRC_BRACE = 10, /* mandatory brace, for scope or to avoid
@ -559,11 +568,13 @@ typedef enum JSSrcNoteType {
* instruction, so can be used to denote distinct declaration syntaxes to the * instruction, so can be used to denote distinct declaration syntaxes to the
* decompiler. * decompiler.
* *
* NB: the var_prefix array in jsopcode.c depends on these dense indexes. * NB: the var_prefix array in jsopcode.c depends on these dense indexes from
* SRC_DECL_VAR through SRC_DECL_LET.
*/ */
#define SRC_DECL_VAR 0 #define SRC_DECL_VAR 0
#define SRC_DECL_CONST 1 #define SRC_DECL_CONST 1
#define SRC_DECL_LET 2 #define SRC_DECL_LET 2
#define SRC_DECL_NONE 3
#define SN_TYPE_BITS 5 #define SN_TYPE_BITS 5
#define SN_DELTA_BITS 3 #define SN_DELTA_BITS 3

Просмотреть файл

@ -5330,7 +5330,6 @@ interrupt:
* Getters and setters are just like watchpoints from an access * Getters and setters are just like watchpoints from an access
* control point of view. * control point of view.
*/ */
SAVE_SP_AND_PC(fp);
ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &rtmp, &attrs); ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &rtmp, &attrs);
if (!ok) if (!ok)
goto out; goto out;

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -80,7 +80,7 @@
* 14 *, /, % JSOP_MUL, JSOP_DIV, JSOP_MOD * 14 *, /, % JSOP_MUL, JSOP_DIV, JSOP_MOD
* 15 !, ~, etc. JSOP_NOT, JSOP_BITNOT, etc. * 15 !, ~, etc. JSOP_NOT, JSOP_BITNOT, etc.
* 16 0, function(){} etc. JSOP_ZERO, JSOP_ANONFUNOBJ, etc. * 16 0, function(){} etc. JSOP_ZERO, JSOP_ANONFUNOBJ, etc.
* 17 new JSOP_NEW * 17 delete, new JSOP_DEL*, JSOP_NEW
* 18 x.y, f(), etc. JSOP_GETPROP, JSOP_CALL, etc. * 18 x.y, f(), etc. JSOP_GETPROP, JSOP_CALL, etc.
* 19 x, null, etc. JSOP_NAME, JSOP_NULL, etc. * 19 x, null, etc. JSOP_NAME, JSOP_NULL, etc.
* *
@ -139,9 +139,9 @@ OPDEF(JSOP_NOT, 32, "not", "!", 1, 1, 1, 15, JOF_BYTE|J
OPDEF(JSOP_BITNOT, 33, "bitnot", "~", 1, 1, 1, 15, JOF_BYTE) OPDEF(JSOP_BITNOT, 33, "bitnot", "~", 1, 1, 1, 15, JOF_BYTE)
OPDEF(JSOP_NEG, 34, "neg", "- ", 1, 1, 1, 15, JOF_BYTE) OPDEF(JSOP_NEG, 34, "neg", "- ", 1, 1, 1, 15, JOF_BYTE)
OPDEF(JSOP_NEW, 35, js_new_str, NULL, 3, -1, 1, 17, JOF_UINT16) OPDEF(JSOP_NEW, 35, js_new_str, NULL, 3, -1, 1, 17, JOF_UINT16)
OPDEF(JSOP_DELNAME, 36, "delname", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_DEL) OPDEF(JSOP_DELNAME, 36, "delname", NULL, 3, 0, 1, 17, JOF_CONST|JOF_NAME|JOF_DEL)
OPDEF(JSOP_DELPROP, 37, "delprop", NULL, 3, 1, 1, 18, JOF_CONST|JOF_PROP|JOF_DEL) OPDEF(JSOP_DELPROP, 37, "delprop", NULL, 3, 1, 1, 17, JOF_CONST|JOF_PROP|JOF_DEL)
OPDEF(JSOP_DELELEM, 38, "delelem", NULL, 1, 2, 1, 18, JOF_BYTE |JOF_ELEM|JOF_DEL) OPDEF(JSOP_DELELEM, 38, "delelem", NULL, 1, 2, 1, 17, JOF_BYTE |JOF_ELEM|JOF_DEL)
OPDEF(JSOP_TYPEOF, 39, js_typeof_str,NULL, 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING) OPDEF(JSOP_TYPEOF, 39, js_typeof_str,NULL, 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING)
OPDEF(JSOP_VOID, 40, js_void_str, NULL, 1, 1, 1, 15, JOF_BYTE) OPDEF(JSOP_VOID, 40, js_void_str, NULL, 1, 1, 1, 15, JOF_BYTE)
OPDEF(JSOP_INCNAME, 41, "incname", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_INC) OPDEF(JSOP_INCNAME, 41, "incname", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_INC)
@ -239,7 +239,7 @@ OPDEF(JSOP_FORELEM, 106,"forelem", NULL, 1, 2, 4, 18, JOF_BYTE |
OPDEF(JSOP_POP2, 107,"pop2", NULL, 1, 2, 0, 0, JOF_BYTE) OPDEF(JSOP_POP2, 107,"pop2", NULL, 1, 2, 0, 0, JOF_BYTE)
/* ECMA-compliant assignment ops. */ /* ECMA-compliant assignment ops. */
OPDEF(JSOP_BINDNAME, 108,"bindname", NULL, 3, 0, 1, 3, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING) OPDEF(JSOP_BINDNAME, 108,"bindname", NULL, 3, 0, 1, 0, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING)
OPDEF(JSOP_SETNAME, 109,"setname", NULL, 3, 2, 1, 3, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) OPDEF(JSOP_SETNAME, 109,"setname", NULL, 3, 2, 1, 3, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING)
/* Exception handling ops. */ /* Exception handling ops. */
@ -400,7 +400,7 @@ OPDEF(JSOP_XMLPI, 183,"xmlpi", NULL, 3, 1, 1, 19, JOF_CONST)
OPDEF(JSOP_GETMETHOD, 184,"getmethod", NULL, 3, 1, 1, 18, JOF_CONST|JOF_PROP) OPDEF(JSOP_GETMETHOD, 184,"getmethod", NULL, 3, 1, 1, 18, JOF_CONST|JOF_PROP)
OPDEF(JSOP_GETFUNNS, 185,"getfunns", NULL, 1, 0, 1, 19, JOF_BYTE) OPDEF(JSOP_GETFUNNS, 185,"getfunns", NULL, 1, 0, 1, 19, JOF_BYTE)
OPDEF(JSOP_FOREACH, 186,"foreach", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_FOREACH, 186,"foreach", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_DELDESC, 187,"deldesc", NULL, 1, 2, 1, 18, JOF_BYTE |JOF_ELEM|JOF_DEL) OPDEF(JSOP_DELDESC, 187,"deldesc", NULL, 1, 2, 1, 17, JOF_BYTE |JOF_ELEM|JOF_DEL)
/* /*
* Opcodes for extended literal addressing, using unsigned 24-bit immediate * Opcodes for extended literal addressing, using unsigned 24-bit immediate

Просмотреть файл

@ -960,6 +960,22 @@ BindLocalVariable(JSContext *cx, BindData *data, JSAtom *atom)
{ {
JSFunction *fun; JSFunction *fun;
/*
* Can't increase fun->nvars in an active frame, so insist that getter is
* js_GetLocalVariable, not js_GetCallVariable or anything else.
*/
if (data->u.var.getter != js_GetLocalVariable)
return JS_TRUE;
/*
* Don't bind a variable with the hidden name 'arguments', per ECMA-262.
* Instead 'var arguments' always restates the predefined property of the
* activation objects with unhidden name 'arguments'. Assignment to such
* a variable must be handled specially.
*/
if (atom == cx->runtime->atomState.argumentsAtom)
return JS_TRUE;
fun = data->u.var.fun; fun = data->u.var.fun;
if (!js_AddHiddenProperty(cx, data->obj, ATOM_TO_JSID(atom), if (!js_AddHiddenProperty(cx, data->obj, ATOM_TO_JSID(atom),
data->u.var.getter, data->u.var.setter, data->u.var.getter, data->u.var.setter,
@ -1022,16 +1038,8 @@ BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
} }
OBJ_DROP_PROPERTY(cx, pobj, prop); OBJ_DROP_PROPERTY(cx, pobj, prop);
} else { } else {
/* if (!BindLocalVariable(cx, data, atom))
* Don't bind a variable with hidden name 'arguments', per ECMA-262.
* Instead, var arguments always restates the predefined property of
* the activation objects with unhidden name 'arguments'. Assignment
* to such a variable must be handled specially.
*/
if (atom != cx->runtime->atomState.argumentsAtom &&
!BindLocalVariable(cx, data, atom)) {
return JS_FALSE; return JS_FALSE;
}
} }
return JS_TRUE; return JS_TRUE;
} }
@ -1308,10 +1316,28 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
#if JS_HAS_DESTRUCTURING #if JS_HAS_DESTRUCTURING
/* /*
* If there were destructuring formal parameters, prepend the initializing * If there were destructuring formal parameters, prepend the initializing
* comma expression that we synthesized to body. * comma expression that we synthesized to body. If the body is a lexical
* scope node, we must make a special TOK_BODY node, to prepend the formal
* parameter destructuring code without bracing the decompilation of the
* function body's lexical scope.
*/ */
if (list) { if (list) {
JS_ASSERT(body->pn_arity == PN_LIST); if (body->pn_arity != PN_LIST) {
JSParseNode *block;
JS_ASSERT(body->pn_type == TOK_LEXICALSCOPE);
JS_ASSERT(body->pn_arity == PN_NAME);
block = NewParseNode(cx, ts, PN_LIST, tc);
if (!block)
return NULL;
block->pn_type = TOK_BODY;
block->pn_pos = body->pn_pos;
PN_INIT_LIST_1(block, body);
body = block;
}
item = NewParseNode(cx, ts, PN_UNARY, tc); item = NewParseNode(cx, ts, PN_UNARY, tc);
if (!item) if (!item)
return NULL; return NULL;
@ -1683,7 +1709,7 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
JSObject *obj, *pobj; JSObject *obj, *pobj;
JSProperty *prop; JSProperty *prop;
JSBool ok; JSBool ok;
JSPropertyOp currentGetter, currentSetter; JSPropertyOp getter, setter;
JSScopeProperty *sprop; JSScopeProperty *sprop;
stmt = js_LexicalLookup(tc, atom, NULL, JS_FALSE); stmt = js_LexicalLookup(tc, atom, NULL, JS_FALSE);
@ -1738,8 +1764,8 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
} }
ok = JS_TRUE; ok = JS_TRUE;
currentGetter = data->u.var.getter; getter = data->u.var.getter;
currentSetter = data->u.var.setter; setter = data->u.var.setter;
if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) { if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) {
sprop = (JSScopeProperty *)prop; sprop = (JSScopeProperty *)prop;
@ -1755,8 +1781,8 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
name); name);
ok = JS_FALSE; ok = JS_FALSE;
} else { } else {
currentGetter = js_GetArgument; getter = js_GetArgument;
currentSetter = js_SetArgument; setter = js_SetArgument;
ok = js_ReportCompileErrorNumber(cx, ok = js_ReportCompileErrorNumber(cx,
BIND_DATA_REPORT_ARGS(data, BIND_DATA_REPORT_ARGS(data,
JSREPORT_WARNING | JSREPORT_WARNING |
@ -1765,6 +1791,8 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
name); name);
} }
} else { } else {
JS_ASSERT(getter == js_GetLocalVariable);
if (fun) { if (fun) {
/* Not an argument, must be a redeclared local var. */ /* Not an argument, must be a redeclared local var. */
if (data->u.var.clasp == &js_FunctionClass) { if (data->u.var.clasp == &js_FunctionClass) {
@ -1786,16 +1814,15 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
* use the special getters and setters since we can't * use the special getters and setters since we can't
* allocate a slot in the frame. * allocate a slot in the frame.
*/ */
currentGetter = sprop->getter; getter = sprop->getter;
currentSetter = sprop->setter; setter = sprop->setter;
} }
} }
/* Override the old getter and setter, to handle eval. */ /* Override the old getter and setter, to handle eval. */
sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop,
0, sprop->attrs, 0, sprop->attrs,
currentGetter, getter, setter);
currentSetter);
if (!sprop) if (!sprop)
ok = JS_FALSE; ok = JS_FALSE;
} }
@ -1816,14 +1843,8 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
OBJ_DROP_PROPERTY(cx, pobj, prop); OBJ_DROP_PROPERTY(cx, pobj, prop);
prop = NULL; prop = NULL;
} }
if (currentGetter == js_GetCallVariable) {
/* Can't increase fun->nvars in an active frame! */ if (cx->fp->scopeChain == obj &&
currentGetter = data->u.var.clasp->getProperty;
currentSetter = data->u.var.clasp->setProperty;
}
if (currentGetter == js_GetLocalVariable &&
atom != cx->runtime->atomState.argumentsAtom &&
cx->fp->scopeChain == obj &&
!js_InWithStatement(tc) && !js_InWithStatement(tc) &&
!BindLocalVariable(cx, data, atom)) { !BindLocalVariable(cx, data, atom)) {
return JS_FALSE; return JS_FALSE;
@ -2078,7 +2099,6 @@ CheckDestructuring(JSContext *cx, BindData *data,
JSBool ok; JSBool ok;
FindPropValData fpvd; FindPropValData fpvd;
JSParseNode *lhs, *rhs, *pn, *pn2; JSParseNode *lhs, *rhs, *pn, *pn2;
uint32 count;
if (left->pn_type == TOK_ARRAYCOMP) { if (left->pn_type == TOK_ARRAYCOMP) {
js_ReportCompileErrorNumber(cx, left, JSREPORT_PN | JSREPORT_ERROR, js_ReportCompileErrorNumber(cx, left, JSREPORT_PN | JSREPORT_ERROR,
@ -2089,8 +2109,8 @@ CheckDestructuring(JSContext *cx, BindData *data,
ok = JS_TRUE; ok = JS_TRUE;
fpvd.table.ops = NULL; fpvd.table.ops = NULL;
lhs = left->pn_head; lhs = left->pn_head;
if (left->pn_count == 0 || lhs->pn_type == TOK_DEFSHARP) { if (lhs && lhs->pn_type == TOK_DEFSHARP) {
pn = left; pn = lhs;
goto no_var_name; goto no_var_name;
} }
@ -2099,7 +2119,6 @@ CheckDestructuring(JSContext *cx, BindData *data,
? right->pn_head ? right->pn_head
: NULL; : NULL;
count = 0;
while (lhs) { while (lhs) {
pn = lhs, pn2 = rhs; pn = lhs, pn2 = rhs;
if (!data) { if (!data) {
@ -2128,18 +2147,12 @@ CheckDestructuring(JSContext *cx, BindData *data,
} }
if (!ok) if (!ok)
goto out; goto out;
++count;
} }
lhs = lhs->pn_next; lhs = lhs->pn_next;
if (rhs) if (rhs)
rhs = rhs->pn_next; rhs = rhs->pn_next;
} }
if (count == 0) {
pn = left;
goto no_var_name;
}
} else { } else {
JS_ASSERT(left->pn_type == TOK_RC); JS_ASSERT(left->pn_type == TOK_RC);
fpvd.numvars = left->pn_count; fpvd.numvars = left->pn_count;
@ -2250,6 +2263,8 @@ ContainsStmt(JSParseNode *pn, JSTokenType tt)
if (pn->pn_op != JSOP_NOP) if (pn->pn_op != JSOP_NOP)
return NULL; return NULL;
return ContainsStmt(pn->pn_kid, tt); return ContainsStmt(pn->pn_kid, tt);
case PN_NAME:
return ContainsStmt(pn->pn_expr, tt);
default:; default:;
} }
return NULL; return NULL;
@ -2787,11 +2802,20 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
/* Check that the left side of the 'in' is valid. */ /* Check that the left side of the 'in' is valid. */
JS_ASSERT(!TOKEN_TYPE_IS_DECL(tt) || pn1->pn_type == tt); JS_ASSERT(!TOKEN_TYPE_IS_DECL(tt) || pn1->pn_type == tt);
if (TOKEN_TYPE_IS_DECL(tt) if (TOKEN_TYPE_IS_DECL(tt)
? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST) ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST
#if JS_HAS_DESTRUCTURING
|| pn1->pn_head->pn_type == TOK_RC
|| (pn1->pn_head->pn_type == TOK_RB &&
pn1->pn_head->pn_count != 2)
|| (pn1->pn_head->pn_type == TOK_ASSIGN &&
(pn1->pn_head->pn_left->pn_type != TOK_RB ||
pn1->pn_head->pn_left->pn_count != 2))
#endif
)
: (pn1->pn_type != TOK_NAME && : (pn1->pn_type != TOK_NAME &&
pn1->pn_type != TOK_DOT && pn1->pn_type != TOK_DOT &&
#if JS_HAS_DESTRUCTURING #if JS_HAS_DESTRUCTURING
pn1->pn_type != TOK_RB && pn1->pn_type != TOK_RC && (pn1->pn_type != TOK_RB || pn1->pn_count != 2) &&
#endif #endif
#if JS_HAS_LVALUE_RETURN #if JS_HAS_LVALUE_RETURN
pn1->pn_type != TOK_LP && pn1->pn_type != TOK_LP &&
@ -5314,6 +5338,14 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
if (!pnlet) if (!pnlet)
return NULL; return NULL;
if (pnlet->pn_type != TOK_RB || pnlet->pn_count != 2) {
js_ReportCompileErrorNumber(cx, ts,
JSREPORT_TS |
JSREPORT_ERROR,
JSMSG_BAD_FOR_LEFTSIDE);
return NULL;
}
/* Destructuring requires [key, value] enumeration. */ /* Destructuring requires [key, value] enumeration. */
if (pn2->pn_op != JSOP_FOREACH) if (pn2->pn_op != JSOP_FOREACH)
pn2->pn_op = JSOP_FOREACHKEYVAL; pn2->pn_op = JSOP_FOREACHKEYVAL;

Просмотреть файл

@ -136,6 +136,8 @@ typedef enum JSTokenType {
TOK_ARRAYPUSH = 81, /* array push within comprehension */ TOK_ARRAYPUSH = 81, /* array push within comprehension */
TOK_LEXICALSCOPE = 82, /* block scope AST node label */ TOK_LEXICALSCOPE = 82, /* block scope AST node label */
TOK_LET = 83, /* let keyword */ TOK_LET = 83, /* let keyword */
TOK_BODY = 84, /* synthetic body of function with
destructuring formal parameters */
TOK_RESERVED, /* reserved keywords */ TOK_RESERVED, /* reserved keywords */
TOK_LIMIT /* domain size */ TOK_LIMIT /* domain size */
} JSTokenType; } JSTokenType;

Просмотреть файл

@ -200,7 +200,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
* before deserialization of bytecode. If the saved version does not match * before deserialization of bytecode. If the saved version does not match
* the current version, abort deserialization and invalidate the file. * the current version, abort deserialization and invalidate the file.
*/ */
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 3) #define JSXDR_BYTECODE_VERSION (0xb973c0de - 4)
/* /*
* Library-private functions. * Library-private functions.