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:
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

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

@ -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.