зеркало из https://github.com/mozilla/gecko-dev.git
Destructuring decompilation (346642, anticipating r=mrbkap).
This commit is contained in:
Родитель
4975edd6b9
Коммит
9ab0984ab2
167
js/src/jsemit.c
167
js/src/jsemit.c
|
@ -2123,7 +2123,9 @@ CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn,
|
|||
case PN_LIST:
|
||||
if (pn->pn_type == TOK_NEW ||
|
||||
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)
|
||||
* 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
|
||||
* (the JSOP_ARGUMENTS special case below, in the PN_BINARY case,
|
||||
* 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;
|
||||
} 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
|
||||
* of a TOK_DOT node. See the TOK_FOR/IN case in js_EmitTree, and
|
||||
* EmitDestructuring nearer below. In the destructuring case, the
|
||||
* base expression (pn_expr) of the name may be null, which means
|
||||
* we have to emit a JSOP_BINDNAME.
|
||||
* EmitDestructuringOps nearer below. In the destructuring case,
|
||||
* the base expression (pn_expr) of the name may be null, which
|
||||
* means we have to emit a JSOP_BINDNAME.
|
||||
*/
|
||||
left = pn->pn_expr;
|
||||
if (!left) {
|
||||
|
@ -2440,7 +2446,10 @@ EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
|
|||
}
|
||||
right = &rtmp;
|
||||
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_pos = pn->pn_pos;
|
||||
right->pn_atom = pn->pn_atom;
|
||||
|
@ -3326,7 +3335,6 @@ EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
{
|
||||
jsuint index;
|
||||
JSParseNode *pn2, *pn3;
|
||||
ptrdiff_t top;
|
||||
JSBool doElemOp;
|
||||
|
||||
#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);
|
||||
#endif
|
||||
|
||||
index = 0;
|
||||
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;
|
||||
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;
|
||||
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
|
||||
/*
|
||||
* Duplicate the value being destructured to use as a reference base.
|
||||
*/
|
||||
top = CG_OFFSET(cg);
|
||||
if (js_Emit1(cx, cg, JSOP_DUP) < 0)
|
||||
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
|
||||
* not decompiling an array initialiser.
|
||||
*/
|
||||
if (pn->pn_type == TOK_RC &&
|
||||
js_NewSrcNote(cx, cg, SRC_INITPROP) < 0) {
|
||||
if (js_NewSrcNote(cx, cg, SRC_INITPROP) < 0)
|
||||
return JS_FALSE;
|
||||
}
|
||||
if (!EmitNumberOp(cx, pn3->pn_dval, cg))
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
|
@ -3395,15 +3399,21 @@ EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
* that value on top of the value being destructured, so the stack
|
||||
* 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)
|
||||
return JS_FALSE;
|
||||
JS_ASSERT(cg->stackDepth == stackDepth + 1);
|
||||
}
|
||||
|
||||
/* Nullary comma node makes a hole in the array destructurer. */
|
||||
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);
|
||||
++index;
|
||||
|
@ -3412,35 +3422,33 @@ EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
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
|
||||
EmitDestructuringOps(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp,
|
||||
EmitDestructuringOps(JSContext *cx, JSCodeGenerator *cg, JSOp declOp,
|
||||
JSParseNode *pn)
|
||||
{
|
||||
ptrdiff_t declType;
|
||||
|
||||
/*
|
||||
* If we're called from a variable declaration, help the decompiler by
|
||||
* annotating the first JSOP_DUP that EmitDestructuringOpsHelper emits.
|
||||
* The parser ensures that a destructuring initialiser has at least one
|
||||
* element lvalue, so we know that JSOP_DUP will be emitted.
|
||||
* If the destructuring initialiser is empty, our helper will emit a
|
||||
* JSOP_DUP followed by a JSOP_POP for the decompiler.
|
||||
*/
|
||||
switch (prologOp) {
|
||||
case JSOP_DEFCONST:
|
||||
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)
|
||||
if (js_NewSrcNote2(cx, cg, SRC_DESTRUCT, OpToDeclType(declOp)) < 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
|
||||
|
@ -3450,8 +3458,8 @@ EmitDestructuringOps(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp,
|
|||
}
|
||||
|
||||
static JSBool
|
||||
EmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSParseNode *lhs,
|
||||
JSParseNode *rhs)
|
||||
EmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp declOp,
|
||||
JSParseNode *lhs, JSParseNode *rhs)
|
||||
{
|
||||
jsuint depth, limit, slot;
|
||||
JSParseNode *pn;
|
||||
|
@ -3475,6 +3483,9 @@ EmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSParseNode *lhs,
|
|||
++limit;
|
||||
}
|
||||
|
||||
if (js_NewSrcNote2(cx, cg, SRC_GROUPASSIGN, OpToDeclType(declOp)) < 0)
|
||||
return JS_FALSE;
|
||||
|
||||
slot = depth;
|
||||
for (pn = lhs->pn_head; pn; pn = pn->pn_next) {
|
||||
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.
|
||||
*/
|
||||
static JSBool
|
||||
MaybeEmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
|
||||
JSOp *pop)
|
||||
MaybeEmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp declOp,
|
||||
JSParseNode *pn, JSOp *pop)
|
||||
{
|
||||
JSParseNode *lhs, *rhs;
|
||||
|
||||
|
@ -3511,7 +3522,7 @@ MaybeEmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
|
|||
lhs = pn->pn_left;
|
||||
rhs = pn->pn_right;
|
||||
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;
|
||||
*pop = JSOP_NOP;
|
||||
}
|
||||
|
@ -3553,7 +3564,7 @@ EmitVariables(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
|
|||
*/
|
||||
tc = &cg->treeContext;
|
||||
let = (pn->pn_op == JSOP_NOP);
|
||||
forInVar = (pn->pn_extra & PNX_FORINVAR);
|
||||
forInVar = (pn->pn_extra & PNX_FORINVAR) != 0;
|
||||
#if JS_HAS_BLOCK_SCOPE
|
||||
forInLet = let && forInVar;
|
||||
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.
|
||||
*/
|
||||
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,
|
||||
* 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);
|
||||
op = JSOP_POP;
|
||||
if (!MaybeEmitGroupAssignment(cx, cg, pn2, &op))
|
||||
if (!MaybeEmitGroupAssignment(cx, cg,
|
||||
inLetHead ? JSOP_POP : pn->pn_op,
|
||||
pn2, &op)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
if (op == JSOP_NOP) {
|
||||
pn->pn_extra = (pn->pn_extra & ~PNX_POPVAR) | PNX_GROUPINIT;
|
||||
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
|
||||
* for-in loop, without hoisting the let variables, which must of
|
||||
* 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;
|
||||
if (forInLet)
|
||||
break;
|
||||
}
|
||||
#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;
|
||||
}
|
||||
goto emit_note_pop;
|
||||
}
|
||||
#else
|
||||
|
@ -3794,7 +3819,8 @@ EmitVariables(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
|
|||
*headNoteIndex = js_NewSrcNote(cx, cg, SRC_DECL);
|
||||
if (*headNoteIndex < 0)
|
||||
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;
|
||||
|
@ -4214,9 +4240,18 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
if (js_Emit1(cx, cg, pn->pn_op) < 0)
|
||||
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;
|
||||
op = JSOP_NOP;
|
||||
op = JSOP_SETNAME;
|
||||
switch (type) {
|
||||
#if JS_HAS_BLOCK_SCOPE
|
||||
case TOK_LET:
|
||||
|
@ -4400,7 +4435,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
#if JS_HAS_DESTRUCTURING
|
||||
pn3 = pn2->pn_kid1;
|
||||
if (pn3->pn_type == TOK_ASSIGN &&
|
||||
!MaybeEmitGroupAssignment(cx, cg, pn3, &op)) {
|
||||
!MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
#endif
|
||||
|
@ -4472,7 +4507,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
op = JSOP_POP;
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
if (pn3->pn_type == TOK_ASSIGN &&
|
||||
!MaybeEmitGroupAssignment(cx, cg, pn3, &op)) {
|
||||
!MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
#endif
|
||||
|
@ -5043,6 +5078,16 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
ok = js_PopStatementCG(cx, cg);
|
||||
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:
|
||||
pn2 = pn->pn_kid;
|
||||
if (pn2) {
|
||||
|
@ -5083,7 +5128,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
#if JS_HAS_DESTRUCTURING
|
||||
if (!wantval &&
|
||||
pn2->pn_type == TOK_ASSIGN &&
|
||||
!MaybeEmitGroupAssignment(cx, cg, pn2, &op)) {
|
||||
!MaybeEmitGroupAssignment(cx, cg, op, pn2, &op)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
#endif
|
||||
|
@ -5323,7 +5368,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
#if JS_HAS_DESTRUCTURING
|
||||
case TOK_RB:
|
||||
case TOK_RC:
|
||||
if (!EmitDestructuringOps(cx, cg, JSOP_NOP, pn2))
|
||||
if (!EmitDestructuringOps(cx, cg, JSOP_SETNAME, pn2))
|
||||
return JS_FALSE;
|
||||
break;
|
||||
#endif
|
||||
|
@ -6274,7 +6319,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
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[] = {
|
||||
{"null", 0, 0, 0},
|
||||
{"if", 0, 0, 0},
|
||||
|
@ -6282,7 +6327,7 @@ JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = {
|
|||
{"while", 1, 0, 1},
|
||||
{"for", 3, 1, 1},
|
||||
{"continue", 0, 0, 0},
|
||||
{"decl", 1, 0, 1},
|
||||
{"decl", 1, 1, 1},
|
||||
{"pcdelta", 1, 0, 1},
|
||||
{"assignop", 0, 0, 0},
|
||||
{"cond", 1, 0, 1},
|
||||
|
|
|
@ -65,6 +65,8 @@ typedef enum JSStmtType {
|
|||
STMT_LABEL, /* labeled statement: L: s */
|
||||
STMT_IF, /* if (then) 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_SWITCH, /* switch 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)))
|
||||
|
||||
/*
|
||||
* 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
|
||||
* become, a lexical scope. It therefore includes block and switch (the two
|
||||
* "maybe" scopes) and excludes with (which has dynamic scope, pending the
|
||||
* "reformed with" in ES4/JS2). It includes all try-catch-finally types.
|
||||
* low-numbered "maybe" scope types) and excludes with (with has dynamic scope
|
||||
* 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
|
||||
* links to other scoping statement info records. It excludes the two "maybe"
|
||||
* types, block and switch, as well as the try and both finally types, since
|
||||
* try, etc., don't need block scope unless they contain let declarations.
|
||||
* links to other scoping statement info records. It excludes the two early
|
||||
* "maybe" types, block and switch, as well as the try and both finally types,
|
||||
* 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
|
||||
* 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
|
||||
at end of array literal: [1,2,,] */
|
||||
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
|
||||
next POP, or from CONDSWITCH to first CASE
|
||||
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_COND = 9, /* JSOP_IFEQ is from conditional ?: operator */
|
||||
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
|
||||
* 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_CONST 1
|
||||
#define SRC_DECL_LET 2
|
||||
#define SRC_DECL_NONE 3
|
||||
|
||||
#define SN_TYPE_BITS 5
|
||||
#define SN_DELTA_BITS 3
|
||||
|
|
|
@ -5330,7 +5330,6 @@ interrupt:
|
|||
* Getters and setters are just like watchpoints from an access
|
||||
* control point of view.
|
||||
*/
|
||||
SAVE_SP_AND_PC(fp);
|
||||
ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &rtmp, &attrs);
|
||||
if (!ok)
|
||||
goto out;
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -80,7 +80,7 @@
|
|||
* 14 *, /, % JSOP_MUL, JSOP_DIV, JSOP_MOD
|
||||
* 15 !, ~, etc. JSOP_NOT, JSOP_BITNOT, 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.
|
||||
* 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_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_DELNAME, 36, "delname", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_DEL)
|
||||
OPDEF(JSOP_DELPROP, 37, "delprop", NULL, 3, 1, 1, 18, JOF_CONST|JOF_PROP|JOF_DEL)
|
||||
OPDEF(JSOP_DELELEM, 38, "delelem", NULL, 1, 2, 1, 18, JOF_BYTE |JOF_ELEM|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, 17, JOF_CONST|JOF_PROP|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_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)
|
||||
|
@ -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)
|
||||
|
||||
/* 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)
|
||||
|
||||
/* 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_GETFUNNS, 185,"getfunns", NULL, 1, 0, 1, 19, 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
|
||||
|
|
112
js/src/jsparse.c
112
js/src/jsparse.c
|
@ -960,6 +960,22 @@ BindLocalVariable(JSContext *cx, BindData *data, JSAtom *atom)
|
|||
{
|
||||
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;
|
||||
if (!js_AddHiddenProperty(cx, data->obj, ATOM_TO_JSID(atom),
|
||||
data->u.var.getter, data->u.var.setter,
|
||||
|
@ -1022,17 +1038,9 @@ BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
|
|||
}
|
||||
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
||||
} else {
|
||||
/*
|
||||
* 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)) {
|
||||
if (!BindLocalVariable(cx, data, atom))
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
return JS_TRUE;
|
||||
}
|
||||
#endif /* JS_HAS_DESTRUCTURING */
|
||||
|
@ -1308,10 +1316,28 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
#if JS_HAS_DESTRUCTURING
|
||||
/*
|
||||
* 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) {
|
||||
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);
|
||||
if (!item)
|
||||
return NULL;
|
||||
|
@ -1683,7 +1709,7 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
|
|||
JSObject *obj, *pobj;
|
||||
JSProperty *prop;
|
||||
JSBool ok;
|
||||
JSPropertyOp currentGetter, currentSetter;
|
||||
JSPropertyOp getter, setter;
|
||||
JSScopeProperty *sprop;
|
||||
|
||||
stmt = js_LexicalLookup(tc, atom, NULL, JS_FALSE);
|
||||
|
@ -1738,8 +1764,8 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
|
|||
}
|
||||
|
||||
ok = JS_TRUE;
|
||||
currentGetter = data->u.var.getter;
|
||||
currentSetter = data->u.var.setter;
|
||||
getter = data->u.var.getter;
|
||||
setter = data->u.var.setter;
|
||||
|
||||
if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) {
|
||||
sprop = (JSScopeProperty *)prop;
|
||||
|
@ -1755,8 +1781,8 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
|
|||
name);
|
||||
ok = JS_FALSE;
|
||||
} else {
|
||||
currentGetter = js_GetArgument;
|
||||
currentSetter = js_SetArgument;
|
||||
getter = js_GetArgument;
|
||||
setter = js_SetArgument;
|
||||
ok = js_ReportCompileErrorNumber(cx,
|
||||
BIND_DATA_REPORT_ARGS(data,
|
||||
JSREPORT_WARNING |
|
||||
|
@ -1765,6 +1791,8 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
|
|||
name);
|
||||
}
|
||||
} else {
|
||||
JS_ASSERT(getter == js_GetLocalVariable);
|
||||
|
||||
if (fun) {
|
||||
/* Not an argument, must be a redeclared local var. */
|
||||
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
|
||||
* allocate a slot in the frame.
|
||||
*/
|
||||
currentGetter = sprop->getter;
|
||||
currentSetter = sprop->setter;
|
||||
getter = sprop->getter;
|
||||
setter = sprop->setter;
|
||||
}
|
||||
}
|
||||
|
||||
/* Override the old getter and setter, to handle eval. */
|
||||
sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop,
|
||||
0, sprop->attrs,
|
||||
currentGetter,
|
||||
currentSetter);
|
||||
getter, setter);
|
||||
if (!sprop)
|
||||
ok = JS_FALSE;
|
||||
}
|
||||
|
@ -1816,14 +1843,8 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
|
|||
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
||||
prop = NULL;
|
||||
}
|
||||
if (currentGetter == js_GetCallVariable) {
|
||||
/* Can't increase fun->nvars in an active frame! */
|
||||
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 &&
|
||||
|
||||
if (cx->fp->scopeChain == obj &&
|
||||
!js_InWithStatement(tc) &&
|
||||
!BindLocalVariable(cx, data, atom)) {
|
||||
return JS_FALSE;
|
||||
|
@ -2078,7 +2099,6 @@ CheckDestructuring(JSContext *cx, BindData *data,
|
|||
JSBool ok;
|
||||
FindPropValData fpvd;
|
||||
JSParseNode *lhs, *rhs, *pn, *pn2;
|
||||
uint32 count;
|
||||
|
||||
if (left->pn_type == TOK_ARRAYCOMP) {
|
||||
js_ReportCompileErrorNumber(cx, left, JSREPORT_PN | JSREPORT_ERROR,
|
||||
|
@ -2089,8 +2109,8 @@ CheckDestructuring(JSContext *cx, BindData *data,
|
|||
ok = JS_TRUE;
|
||||
fpvd.table.ops = NULL;
|
||||
lhs = left->pn_head;
|
||||
if (left->pn_count == 0 || lhs->pn_type == TOK_DEFSHARP) {
|
||||
pn = left;
|
||||
if (lhs && lhs->pn_type == TOK_DEFSHARP) {
|
||||
pn = lhs;
|
||||
goto no_var_name;
|
||||
}
|
||||
|
||||
|
@ -2099,7 +2119,6 @@ CheckDestructuring(JSContext *cx, BindData *data,
|
|||
? right->pn_head
|
||||
: NULL;
|
||||
|
||||
count = 0;
|
||||
while (lhs) {
|
||||
pn = lhs, pn2 = rhs;
|
||||
if (!data) {
|
||||
|
@ -2128,18 +2147,12 @@ CheckDestructuring(JSContext *cx, BindData *data,
|
|||
}
|
||||
if (!ok)
|
||||
goto out;
|
||||
++count;
|
||||
}
|
||||
|
||||
lhs = lhs->pn_next;
|
||||
if (rhs)
|
||||
rhs = rhs->pn_next;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
pn = left;
|
||||
goto no_var_name;
|
||||
}
|
||||
} else {
|
||||
JS_ASSERT(left->pn_type == TOK_RC);
|
||||
fpvd.numvars = left->pn_count;
|
||||
|
@ -2250,6 +2263,8 @@ ContainsStmt(JSParseNode *pn, JSTokenType tt)
|
|||
if (pn->pn_op != JSOP_NOP)
|
||||
return NULL;
|
||||
return ContainsStmt(pn->pn_kid, tt);
|
||||
case PN_NAME:
|
||||
return ContainsStmt(pn->pn_expr, tt);
|
||||
default:;
|
||||
}
|
||||
return NULL;
|
||||
|
@ -2787,11 +2802,20 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
/* Check that the left side of the 'in' is valid. */
|
||||
JS_ASSERT(!TOKEN_TYPE_IS_DECL(tt) || pn1->pn_type == 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_DOT &&
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
pn1->pn_type != TOK_RB && pn1->pn_type != TOK_RC &&
|
||||
(pn1->pn_type != TOK_RB || pn1->pn_count != 2) &&
|
||||
#endif
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
pn1->pn_type != TOK_LP &&
|
||||
|
@ -5314,6 +5338,14 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
if (!pnlet)
|
||||
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. */
|
||||
if (pn2->pn_op != JSOP_FOREACH)
|
||||
pn2->pn_op = JSOP_FOREACHKEYVAL;
|
||||
|
|
|
@ -136,6 +136,8 @@ typedef enum JSTokenType {
|
|||
TOK_ARRAYPUSH = 81, /* array push within comprehension */
|
||||
TOK_LEXICALSCOPE = 82, /* block scope AST node label */
|
||||
TOK_LET = 83, /* let keyword */
|
||||
TOK_BODY = 84, /* synthetic body of function with
|
||||
destructuring formal parameters */
|
||||
TOK_RESERVED, /* reserved keywords */
|
||||
TOK_LIMIT /* domain size */
|
||||
} JSTokenType;
|
||||
|
|
|
@ -200,7 +200,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
|
|||
* before deserialization of bytecode. If the saved version does not match
|
||||
* the current version, abort deserialization and invalidate the file.
|
||||
*/
|
||||
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 3)
|
||||
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 4)
|
||||
|
||||
/*
|
||||
* Library-private functions.
|
||||
|
|
Загрузка…
Ссылка в новой задаче