зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1061853 - Reintroduce PNK_MUTATEPROTO to distinguish ({ __proto__: v }) as mutating the [[Prototype]] from ({ __proto__() {} }) as not doing so. r=shu
--HG-- extra : rebase_source : e80acc54962d40471e98f5c202b64c12788db9d6
This commit is contained in:
Родитель
776b785711
Коммит
2d218bc090
|
@ -3041,7 +3041,11 @@ EmitDestructuringDecls(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp
|
|||
|
||||
MOZ_ASSERT(pattern->isKind(PNK_OBJECT));
|
||||
for (ParseNode *member = pattern->pn_head; member; member = member->pn_next) {
|
||||
ParseNode *target = member->pn_right;
|
||||
MOZ_ASSERT(member->isKind(PNK_MUTATEPROTO) ||
|
||||
member->isKind(PNK_COLON) ||
|
||||
member->isKind(PNK_SHORTHAND));
|
||||
|
||||
ParseNode *target = member->isKind(PNK_MUTATEPROTO) ? member->pn_kid : member->pn_right;
|
||||
DestructuringDeclEmitter emitter =
|
||||
target->isKind(PNK_NAME) ? EmitDestructuringDecl : EmitDestructuringDecls;
|
||||
if (!emitter(cx, bce, prologOp, target))
|
||||
|
@ -3361,31 +3365,41 @@ EmitDestructuringOpsObjectHelper(ExclusiveContext *cx, BytecodeEmitter *bce, Par
|
|||
// initialiser.
|
||||
bool needsGetElem = true;
|
||||
|
||||
JS_ASSERT(member->isKind(PNK_COLON) || member->isKind(PNK_SHORTHAND));
|
||||
ParseNode *key = member->pn_left;
|
||||
|
||||
if (key->isKind(PNK_NUMBER)) {
|
||||
if (!EmitNumberOp(cx, key->pn_dval, bce)) // ... OBJ OBJ KEY
|
||||
ParseNode *subpattern;
|
||||
if (member->isKind(PNK_MUTATEPROTO)) {
|
||||
if (!EmitAtomOp(cx, cx->names().proto, JSOP_GETPROP, bce)) // ... OBJ PROP
|
||||
return false;
|
||||
} else if (key->isKind(PNK_NAME) || key->isKind(PNK_STRING)) {
|
||||
PropertyName *name = key->pn_atom->asPropertyName();
|
||||
|
||||
// The parser already checked for atoms representing indexes and
|
||||
// used PNK_NUMBER instead, but also watch for ids which TI treats
|
||||
// as indexes for simplification of downstream analysis.
|
||||
jsid id = NameToId(name);
|
||||
if (id != types::IdToTypeId(id)) {
|
||||
if (!EmitTree(cx, bce, key)) // ... OBJ OBJ KEY
|
||||
return false;
|
||||
} else {
|
||||
if (!EmitAtomOp(cx, name, JSOP_GETPROP, bce)) // ... OBJ PROP
|
||||
return false;
|
||||
needsGetElem = false;
|
||||
}
|
||||
needsGetElem = false;
|
||||
subpattern = member->pn_kid;
|
||||
} else {
|
||||
JS_ASSERT(key->isKind(PNK_COMPUTED_NAME));
|
||||
if (!EmitTree(cx, bce, key->pn_kid)) // ... OBJ OBJ KEY
|
||||
return false;
|
||||
MOZ_ASSERT(member->isKind(PNK_COLON) || member->isKind(PNK_SHORTHAND));
|
||||
|
||||
ParseNode *key = member->pn_left;
|
||||
if (key->isKind(PNK_NUMBER)) {
|
||||
if (!EmitNumberOp(cx, key->pn_dval, bce)) // ... OBJ OBJ KEY
|
||||
return false;
|
||||
} else if (key->isKind(PNK_NAME) || key->isKind(PNK_STRING)) {
|
||||
PropertyName *name = key->pn_atom->asPropertyName();
|
||||
|
||||
// The parser already checked for atoms representing indexes and
|
||||
// used PNK_NUMBER instead, but also watch for ids which TI treats
|
||||
// as indexes for simplification of downstream analysis.
|
||||
jsid id = NameToId(name);
|
||||
if (id != types::IdToTypeId(id)) {
|
||||
if (!EmitTree(cx, bce, key)) // ... OBJ OBJ KEY
|
||||
return false;
|
||||
} else {
|
||||
if (!EmitAtomOp(cx, name, JSOP_GETPROP, bce)) // ...OBJ PROP
|
||||
return false;
|
||||
needsGetElem = false;
|
||||
}
|
||||
} else {
|
||||
JS_ASSERT(key->isKind(PNK_COMPUTED_NAME));
|
||||
if (!EmitTree(cx, bce, key->pn_kid)) // ... OBJ OBJ KEY
|
||||
return false;
|
||||
}
|
||||
|
||||
subpattern = member->pn_right;
|
||||
}
|
||||
|
||||
// Get the property value if not done already.
|
||||
|
@ -3393,7 +3407,6 @@ EmitDestructuringOpsObjectHelper(ExclusiveContext *cx, BytecodeEmitter *bce, Par
|
|||
return false;
|
||||
|
||||
// Destructure PROP per this member's subpattern.
|
||||
ParseNode *subpattern = member->pn_right;
|
||||
int32_t depthBefore = bce->stackDepth;
|
||||
if (!EmitDestructuringLHS(cx, bce, subpattern, emitOption))
|
||||
return false;
|
||||
|
@ -6060,6 +6073,17 @@ EmitObject(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
if (!UpdateSourceCoordNotes(cx, bce, member->pn_pos.begin))
|
||||
return false;
|
||||
|
||||
// Handle __proto__: v specially because *only* this form, and no other
|
||||
// involving "__proto__", performs [[Prototype]] mutation.
|
||||
if (member->isKind(PNK_MUTATEPROTO)) {
|
||||
if (!EmitTree(cx, bce, member->pn_kid))
|
||||
return false;
|
||||
obj = nullptr;
|
||||
if (!Emit1(cx, bce, JSOP_MUTATEPROTO))
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Emit an index for t[2] for later consumption by JSOP_INITELEM. */
|
||||
ParseNode *key = member->pn_left;
|
||||
bool isIndex = false;
|
||||
|
@ -6089,9 +6113,9 @@ EmitObject(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
return false;
|
||||
|
||||
JSOp op = member->getOp();
|
||||
JS_ASSERT(op == JSOP_INITPROP ||
|
||||
op == JSOP_INITPROP_GETTER ||
|
||||
op == JSOP_INITPROP_SETTER);
|
||||
MOZ_ASSERT(op == JSOP_INITPROP ||
|
||||
op == JSOP_INITPROP_GETTER ||
|
||||
op == JSOP_INITPROP_SETTER);
|
||||
|
||||
if (op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER)
|
||||
obj = nullptr;
|
||||
|
@ -6109,22 +6133,10 @@ EmitObject(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
} else {
|
||||
JS_ASSERT(key->isKind(PNK_NAME) || key->isKind(PNK_STRING));
|
||||
|
||||
// If we have { __proto__: expr }, implement prototype mutation.
|
||||
if (op == JSOP_INITPROP && key->pn_atom == cx->names().proto) {
|
||||
obj = nullptr;
|
||||
if (Emit1(cx, bce, JSOP_MUTATEPROTO) < 0)
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
jsatomid index;
|
||||
if (!bce->makeAtomIndex(key->pn_atom, &index))
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(op == JSOP_INITPROP ||
|
||||
op == JSOP_INITPROP_GETTER ||
|
||||
op == JSOP_INITPROP_SETTER);
|
||||
|
||||
if (obj) {
|
||||
JS_ASSERT(!obj->inDictionaryMode());
|
||||
Rooted<jsid> id(cx, AtomToId(key->pn_atom));
|
||||
|
|
|
@ -286,6 +286,18 @@ class FullParseHandler
|
|||
return literal;
|
||||
}
|
||||
|
||||
bool addPrototypeMutation(ParseNode *literal, uint32_t begin, ParseNode *expr) {
|
||||
// Object literals with mutated [[Prototype]] are non-constant so that
|
||||
// singleton objects will have Object.prototype as their [[Prototype]].
|
||||
setListFlag(literal, PNX_NONCONST);
|
||||
|
||||
ParseNode *mutation = newUnary(PNK_MUTATEPROTO, JSOP_NOP, begin, expr);
|
||||
if (!mutation)
|
||||
return false;
|
||||
literal->append(mutation);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addPropertyDefinition(ParseNode *literal, ParseNode *name, ParseNode *expr,
|
||||
bool isShorthand = false) {
|
||||
JS_ASSERT(literal->isArity(PN_LIST));
|
||||
|
|
|
@ -459,17 +459,24 @@ Parser<FullParseHandler>::cloneLeftHandSide(ParseNode *opn)
|
|||
for (ParseNode *opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) {
|
||||
ParseNode *pn2;
|
||||
if (opn->isKind(PNK_OBJECT)) {
|
||||
JS_ASSERT(opn2->isArity(PN_BINARY));
|
||||
JS_ASSERT(opn2->isKind(PNK_COLON) || opn2->isKind(PNK_SHORTHAND));
|
||||
if (opn2->isKind(PNK_MUTATEPROTO)) {
|
||||
ParseNode *target = cloneLeftHandSide(opn2->pn_kid);
|
||||
if (!target)
|
||||
return nullptr;
|
||||
pn2 = handler.new_<UnaryNode>(PNK_MUTATEPROTO, JSOP_NOP, opn2->pn_pos, target);
|
||||
} else {
|
||||
JS_ASSERT(opn2->isArity(PN_BINARY));
|
||||
JS_ASSERT(opn2->isKind(PNK_COLON) || opn2->isKind(PNK_SHORTHAND));
|
||||
|
||||
ParseNode *tag = cloneParseTree(opn2->pn_left);
|
||||
if (!tag)
|
||||
return nullptr;
|
||||
ParseNode *target = cloneLeftHandSide(opn2->pn_right);
|
||||
if (!target)
|
||||
return nullptr;
|
||||
ParseNode *tag = cloneParseTree(opn2->pn_left);
|
||||
if (!tag)
|
||||
return nullptr;
|
||||
ParseNode *target = cloneLeftHandSide(opn2->pn_right);
|
||||
if (!target)
|
||||
return nullptr;
|
||||
|
||||
pn2 = handler.new_<BinaryNode>(opn2->getKind(), JSOP_INITPROP, opn2->pn_pos, tag, target);
|
||||
pn2 = handler.new_<BinaryNode>(opn2->getKind(), JSOP_INITPROP, opn2->pn_pos, tag, target);
|
||||
}
|
||||
} else if (opn2->isArity(PN_NULLARY)) {
|
||||
JS_ASSERT(opn2->isKind(PNK_ELISION));
|
||||
pn2 = cloneParseTree(opn2);
|
||||
|
|
|
@ -145,6 +145,7 @@ class UpvarCookie
|
|||
F(FORHEAD) \
|
||||
F(ARGSBODY) \
|
||||
F(SPREAD) \
|
||||
F(MUTATEPROTO) \
|
||||
\
|
||||
/* Unary operators. */ \
|
||||
F(TYPEOF) \
|
||||
|
|
|
@ -3044,8 +3044,13 @@ Parser<FullParseHandler>::checkDestructuringObject(BindData<FullParseHandler> *d
|
|||
MOZ_ASSERT(objectPattern->isKind(PNK_OBJECT));
|
||||
|
||||
for (ParseNode *member = objectPattern->pn_head; member; member = member->pn_next) {
|
||||
MOZ_ASSERT(member->isKind(PNK_COLON) || member->isKind(PNK_SHORTHAND));
|
||||
ParseNode *expr = member->pn_right;
|
||||
ParseNode *expr;
|
||||
if (member->isKind(PNK_MUTATEPROTO)) {
|
||||
expr = member->pn_kid;
|
||||
} else {
|
||||
MOZ_ASSERT(member->isKind(PNK_COLON) || member->isKind(PNK_SHORTHAND));
|
||||
expr = member->pn_right;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
if (expr->isKind(PNK_ARRAY) || expr->isKind(PNK_OBJECT)) {
|
||||
|
@ -3063,7 +3068,7 @@ Parser<FullParseHandler>::checkDestructuringObject(BindData<FullParseHandler> *d
|
|||
* hasn't been officially linked to its def or registered in
|
||||
* lexdeps. Do that now.
|
||||
*/
|
||||
if (member->pn_right == member->pn_left) {
|
||||
if (!member->isKind(PNK_MUTATEPROTO) && member->pn_right == member->pn_left) {
|
||||
RootedPropertyName name(context, expr->pn_atom->asPropertyName());
|
||||
if (!noteNameUse(name, expr))
|
||||
return false;
|
||||
|
@ -7158,6 +7163,8 @@ Parser<ParseHandler>::objectLiteral()
|
|||
ltok = tokenStream.getToken(TokenStream::KeywordIsName);
|
||||
}
|
||||
|
||||
atom = nullptr;
|
||||
|
||||
JSOp op = JSOP_INITPROP;
|
||||
Node propname;
|
||||
switch (ltok) {
|
||||
|
@ -7269,29 +7276,37 @@ Parser<ParseHandler>::objectLiteral()
|
|||
|
||||
if (op == JSOP_INITPROP) {
|
||||
TokenKind tt = tokenStream.getToken();
|
||||
Node propexpr;
|
||||
if (tt == TOK_ERROR)
|
||||
return null();
|
||||
|
||||
if (tt == TOK_COLON) {
|
||||
if (isGenerator) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
|
||||
return null();
|
||||
}
|
||||
propexpr = assignExpr();
|
||||
|
||||
Node propexpr = assignExpr();
|
||||
if (!propexpr)
|
||||
return null();
|
||||
|
||||
if (foldConstants && !FoldConstants(context, &propexpr, this))
|
||||
return null();
|
||||
|
||||
/*
|
||||
* Treat initializers which mutate __proto__ as non-constant,
|
||||
* so that we can later assume singleton objects delegate to
|
||||
* the default Object.prototype.
|
||||
*/
|
||||
if (!handler.isConstant(propexpr) || atom == context->names().proto)
|
||||
handler.setListFlag(literal, PNX_NONCONST);
|
||||
if (atom == context->names().proto) {
|
||||
// Note: this occurs *only* if we observe TOK_COLON! Only
|
||||
// __proto__: v mutates [[Prototype]]. Getters, setters,
|
||||
// method/generator definitions, computed property name
|
||||
// versions of all of these, and shorthands do not.
|
||||
uint32_t begin = handler.getPosition(propname).begin;
|
||||
if (!handler.addPrototypeMutation(literal, begin, propexpr))
|
||||
return null();
|
||||
} else {
|
||||
if (!handler.isConstant(propexpr))
|
||||
handler.setListFlag(literal, PNX_NONCONST);
|
||||
|
||||
if (!handler.addPropertyDefinition(literal, propname, propexpr))
|
||||
return null();
|
||||
if (!handler.addPropertyDefinition(literal, propname, propexpr))
|
||||
return null();
|
||||
}
|
||||
} else if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC)) {
|
||||
/*
|
||||
* Support, e.g., |var {x, y} = o| as destructuring shorthand
|
||||
|
|
|
@ -11,6 +11,7 @@ ASTDEF(AST_PROGRAM, "Program", "program")
|
|||
ASTDEF(AST_IDENTIFIER, "Identifier", "identifier")
|
||||
ASTDEF(AST_LITERAL, "Literal", "literal")
|
||||
ASTDEF(AST_PROPERTY, "Property", "property")
|
||||
ASTDEF(AST_PROTOTYPEMUTATION, "PrototypeMutation", "prototypeMutation")
|
||||
|
||||
ASTDEF(AST_MODULE_DECL, "ModuleDeclaration", "moduleDeclaration")
|
||||
ASTDEF(AST_FUNC_DECL, "FunctionDeclaration", "functionDeclaration")
|
||||
|
|
|
@ -551,6 +551,7 @@ class NodeBuilder
|
|||
bool catchClause(HandleValue var, HandleValue guard, HandleValue body, TokenPos *pos,
|
||||
MutableHandleValue dst);
|
||||
|
||||
bool prototypeMutation(HandleValue val, TokenPos *pos, MutableHandleValue dst);
|
||||
bool propertyInitializer(HandleValue key, HandleValue val, PropKind kind, bool isShorthand,
|
||||
bool isMethod, TokenPos *pos, MutableHandleValue dst);
|
||||
|
||||
|
@ -1312,6 +1313,18 @@ NodeBuilder::propertyPattern(HandleValue key, HandleValue patt, bool isShorthand
|
|||
dst);
|
||||
}
|
||||
|
||||
bool
|
||||
NodeBuilder::prototypeMutation(HandleValue val, TokenPos *pos, MutableHandleValue dst)
|
||||
{
|
||||
RootedValue cb(cx, callbacks[AST_PROTOTYPEMUTATION]);
|
||||
if (!cb.isNull())
|
||||
return callback(cb, val, pos, dst);
|
||||
|
||||
return newNode(AST_PROTOTYPEMUTATION, pos,
|
||||
"value", val,
|
||||
dst);
|
||||
}
|
||||
|
||||
bool
|
||||
NodeBuilder::propertyInitializer(HandleValue key, HandleValue val, PropKind kind, bool isShorthand,
|
||||
bool isMethod, TokenPos *pos, MutableHandleValue dst)
|
||||
|
@ -3013,6 +3026,12 @@ ASTSerializer::propertyName(ParseNode *pn, MutableHandleValue dst)
|
|||
bool
|
||||
ASTSerializer::property(ParseNode *pn, MutableHandleValue dst)
|
||||
{
|
||||
if (pn->isKind(PNK_MUTATEPROTO)) {
|
||||
RootedValue val(cx);
|
||||
return expression(pn->pn_kid, &val) &&
|
||||
builder.prototypeMutation(val, &pn->pn_pos, dst);
|
||||
}
|
||||
|
||||
PropKind kind;
|
||||
switch (pn->getOp()) {
|
||||
case JSOP_INITPROP:
|
||||
|
|
|
@ -82,6 +82,7 @@ enum PropKind {
|
|||
PROP_INIT = 0,
|
||||
PROP_GETTER,
|
||||
PROP_SETTER,
|
||||
PROP_MUTATEPROTO,
|
||||
PROP_LIMIT
|
||||
};
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ function throwStmt(expr) Pattern({ type: "ThrowStatement", argument: expr })
|
|||
function returnStmt(expr) Pattern({ type: "ReturnStatement", argument: expr })
|
||||
function yieldExpr(expr) Pattern({ type: "YieldExpression", argument: expr })
|
||||
function lit(val) Pattern({ type: "Literal", value: val })
|
||||
function comp(name) Pattern({ type: "ComputedName", name: name })
|
||||
function spread(val) Pattern({ type: "SpreadExpression", expression: val})
|
||||
var thisExpr = Pattern({ type: "ThisExpression" });
|
||||
function funDecl(id, params, body, defaults=[], rest=null) Pattern(
|
||||
|
@ -364,6 +365,14 @@ assertExpr("({'x':1, 'y':2, z:3})", objExpr([{ key: lit("x"), value: lit(1) },
|
|||
assertExpr("({'x':1, 'y':2, 3:3})", objExpr([{ key: lit("x"), value: lit(1) },
|
||||
{ key: lit("y"), value: lit(2) },
|
||||
{ key: lit(3), value: lit(3) } ]));
|
||||
assertExpr("({__proto__:x})", objExpr([{ type: "PrototypeMutation", value: ident("x") }]));
|
||||
assertExpr("({'__proto__':x})", objExpr([{ type: "PrototypeMutation", value: ident("x") }]));
|
||||
assertExpr("({['__proto__']:x})", objExpr([{ type: "Property", key: comp(lit("__proto__")), value: ident("x") }]));
|
||||
assertExpr("({['__proto__']:q, __proto__() {}, __proto__: null })",
|
||||
objExpr([{ type: "Property", key: comp(lit("__proto__")), value: ident("q") },
|
||||
{ type: "Property", key: ident("__proto__"), method: true },
|
||||
{ type: "PrototypeMutation", value: lit(null) }]));
|
||||
|
||||
|
||||
// Bug 571617: eliminate constant-folding
|
||||
assertExpr("2 + 3", binExpr("+", lit(2), lit(3)));
|
||||
|
|
Загрузка…
Ссылка в новой задаче