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:
Jeff Walden 2014-08-30 14:27:19 -07:00
Родитель 776b785711
Коммит 2d218bc090
9 изменённых файлов: 140 добавлений и 63 удалений

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

@ -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)));