Bug 963641 - Remove PNK_MUTATEPROTO, and just make JSOP_MUTATEPROTO be generated for the PNK_INITPROP+__proto__ combination. r=jorendorff

--HG--
extra : rebase_source : 8317d2a90d55a57fb654f72a57ca40575613072c
This commit is contained in:
Jeff Walden 2014-01-24 19:54:53 -08:00
parent 2a2fd41d40
commit cef5bfd081
7 changed files with 212 additions and 80 deletions

View File

@ -2908,29 +2908,28 @@ EmitDestructuringDecl(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp,
}
static bool
EmitDestructuringDecls(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn)
EmitDestructuringDecls(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp,
ParseNode *pattern)
{
ParseNode *pn2, *pn3;
DestructuringDeclEmitter emitter;
if (pn->isKind(PNK_ARRAY)) {
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
if (pn2->isKind(PNK_ELISION))
if (pattern->isKind(PNK_ARRAY)) {
for (ParseNode *element = pattern->pn_head; element; element = element->pn_next) {
if (element->isKind(PNK_ELISION))
continue;
emitter = (pn2->isKind(PNK_NAME))
? EmitDestructuringDecl
: EmitDestructuringDecls;
if (!emitter(cx, bce, prologOp, pn2))
return false;
}
} else {
JS_ASSERT(pn->isKind(PNK_OBJECT));
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
pn3 = pn2->pn_right;
emitter = pn3->isKind(PNK_NAME) ? EmitDestructuringDecl : EmitDestructuringDecls;
if (!emitter(cx, bce, prologOp, pn3))
DestructuringDeclEmitter emitter =
element->isKind(PNK_NAME) ? EmitDestructuringDecl : EmitDestructuringDecls;
if (!emitter(cx, bce, prologOp, element))
return false;
}
return true;
}
MOZ_ASSERT(pattern->isKind(PNK_OBJECT));
for (ParseNode *member = pattern->pn_head; member; member = member->pn_next) {
ParseNode *target = member->pn_right;
DestructuringDeclEmitter emitter =
target->isKind(PNK_NAME) ? EmitDestructuringDecl : EmitDestructuringDecls;
if (!emitter(cx, bce, prologOp, target))
return false;
}
return true;
}
@ -3127,25 +3126,29 @@ EmitDestructuringOpsHelper(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode
} else {
JS_ASSERT(pn->isKind(PNK_OBJECT));
JS_ASSERT(pn2->isKind(PNK_COLON));
pn3 = pn2->pn_left;
if (pn3->isKind(PNK_NUMBER)) {
if (!EmitNumberOp(cx, pn3->pn_dval, bce))
ParseNode *key = pn2->pn_left;
if (key->isKind(PNK_NUMBER)) {
if (!EmitNumberOp(cx, key->pn_dval, bce))
return false;
} else {
MOZ_ASSERT(key->isKind(PNK_STRING) || key->isKind(PNK_NAME));
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 simpliciation of downstream analysis.
JS_ASSERT(pn3->isKind(PNK_STRING) || pn3->isKind(PNK_NAME));
jsid id = NameToId(pn3->pn_atom->asPropertyName());
// as indexes for simplification of downstream analysis.
jsid id = NameToId(name);
if (id != types::IdToTypeId(id)) {
if (!EmitTree(cx, bce, pn3))
if (!EmitTree(cx, bce, key))
return false;
} else {
if (!EmitAtomOp(cx, pn3, JSOP_GETPROP, bce))
if (!EmitAtomOp(cx, name, JSOP_GETPROP, bce))
return false;
doElemOp = false;
}
}
pn3 = pn2->pn_right;
}
@ -5914,16 +5917,6 @@ EmitObject(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
}
for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
/* Handle __proto__ specially because it's not binary. */
if (pn2->isKind(PNK_MUTATEPROTO)) {
if (!EmitTree(cx, bce, pn2->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 *pn3 = pn2->pn_left;
bool isIndex = false;
@ -5968,13 +5961,22 @@ EmitObject(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
return false;
} else {
JS_ASSERT(pn3->isKind(PNK_NAME) || pn3->isKind(PNK_STRING));
// If we have { __proto__: expr }, implement prototype mutation.
if (op == JSOP_INITPROP && pn3->pn_atom == cx->names().proto) {
obj = nullptr;
if (Emit1(cx, bce, JSOP_MUTATEPROTO) < 0)
return false;
continue;
}
jsatomid index;
if (!bce->makeAtomIndex(pn3->pn_atom, &index))
return false;
MOZ_ASSERT((op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER) ||
pn3->pn_atom != cx->names().proto,
"__proto__ shouldn't have been generated as an initprop");
MOZ_ASSERT(op == JSOP_INITPROP ||
op == JSOP_INITPROP_GETTER ||
op == JSOP_INITPROP_SETTER);
if (obj) {
JS_ASSERT(!obj->inDictionaryMode());

View File

@ -235,14 +235,6 @@ class FullParseHandler
return literal;
}
bool addPrototypeMutation(ParseNode *literal, uint32_t begin, ParseNode *expr) {
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) {
ParseNode *propdef = newBinary(PNK_COLON, name, expr, JSOP_INITPROP);
if (!propdef)

View File

@ -139,7 +139,6 @@ class UpvarCookie
F(FORHEAD) \
F(ARGSBODY) \
F(SPREAD) \
F(MUTATEPROTO) \
\
/* Unary operators. */ \
F(TYPEOF) \

View File

@ -3099,32 +3099,31 @@ Parser<FullParseHandler>::checkDestructuring(BindData<FullParseHandler> *data,
}
} else {
JS_ASSERT(left->isKind(PNK_OBJECT));
for (ParseNode *pair = left->pn_head; pair; pair = pair->pn_next) {
JS_ASSERT(pair->isKind(PNK_COLON));
ParseNode *pn = pair->pn_right;
for (ParseNode *member = left->pn_head; member; member = member->pn_next) {
MOZ_ASSERT(member->isKind(PNK_COLON));
ParseNode *expr = member->pn_right;
if (pn->isKind(PNK_ARRAY) || pn->isKind(PNK_OBJECT)) {
ok = checkDestructuring(data, pn, false);
if (expr->isKind(PNK_ARRAY) || expr->isKind(PNK_OBJECT)) {
ok = checkDestructuring(data, expr, false);
} else if (data) {
if (!pn->isKind(PNK_NAME)) {
report(ParseError, false, pn, JSMSG_NO_VARIABLE_NAME);
if (!expr->isKind(PNK_NAME)) {
report(ParseError, false, expr, JSMSG_NO_VARIABLE_NAME);
return false;
}
ok = bindDestructuringVar(data, pn);
ok = bindDestructuringVar(data, expr);
} else {
/*
* If right and left point to the same node, then this is
* destructuring shorthand ({x} = ...). In that case,
* identifierName was not used to parse 'x' so 'x' has not been
* officially linked to its def or registered in lexdeps. Do
* that now.
* If this is a destructuring shorthand ({x} = ...), then
* identifierName wasn't used to parse |x|. As a result, |x|
* hasn't been officially linked to its def or registered in
* lexdeps. Do that now.
*/
if (pair->pn_right == pair->pn_left) {
RootedPropertyName name(context, pn->pn_atom->asPropertyName());
if (!noteNameUse(name, pn))
if (member->pn_right == member->pn_left) {
RootedPropertyName name(context, expr->pn_atom->asPropertyName());
if (!noteNameUse(name, expr))
return false;
}
ok = checkAndMarkAsAssignmentLhs(pn, KeyedDestructuringAssignment);
ok = checkAndMarkAsAssignmentLhs(expr, KeyedDestructuringAssignment);
}
if (!ok)
return false;
@ -3659,8 +3658,9 @@ Parser<FullParseHandler>::letStatement()
JS_ASSERT_IF(pn, pn->isKind(PNK_LET) || pn->isKind(PNK_SEMI));
JS_ASSERT_IF(pn && pn->isKind(PNK_LET) && pn->pn_expr->getOp() != JSOP_POPNV,
pn->pn_expr->isOp(JSOP_POPN));
} else
} else {
pn = letDeclaration();
}
return pn;
}
@ -6807,7 +6807,6 @@ Parser<ParseHandler>::objectLiteral()
JSOp op = JSOP_INITPROP;
Node propname;
uint32_t begin;
switch (ltok) {
case TOK_NUMBER:
atom = DoubleToAtom(context, tokenStream.currentToken().number());
@ -6826,10 +6825,6 @@ Parser<ParseHandler>::objectLiteral()
propname = handler.newIdentifier(atom, pos());
if (!propname)
return null();
if (atom == context->names().proto) {
begin = pos().begin;
op = JSOP_MUTATEPROTO;
}
break;
}
@ -6898,7 +6893,7 @@ Parser<ParseHandler>::objectLiteral()
return null();
}
if (op == JSOP_INITPROP || op == JSOP_MUTATEPROTO) {
if (op == JSOP_INITPROP) {
TokenKind tt = tokenStream.getToken();
Node propexpr;
if (tt == TOK_COLON) {
@ -6914,15 +6909,11 @@ Parser<ParseHandler>::objectLiteral()
* so that we can later assume singleton objects delegate to
* the default Object.prototype.
*/
if (!handler.isConstant(propexpr) || op == JSOP_MUTATEPROTO)
if (!handler.isConstant(propexpr) || atom == context->names().proto)
handler.setListFlag(literal, PNX_NONCONST);
if (op == JSOP_MUTATEPROTO
? !handler.addPrototypeMutation(literal, begin, propexpr)
: !handler.addPropertyDefinition(literal, propname, propexpr))
{
if (!handler.addPropertyDefinition(literal, propname, propexpr))
return null();
}
}
#if JS_HAS_DESTRUCTURING_SHORTHAND
else if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC)) {
@ -6968,7 +6959,7 @@ Parser<ParseHandler>::objectLiteral()
* any part of an accessor property.
*/
AssignmentType assignType;
if (op == JSOP_INITPROP || op == JSOP_MUTATEPROTO)
if (op == JSOP_INITPROP)
assignType = VALUE;
else if (op == JSOP_INITPROP_GETTER)
assignType = GET;

View File

@ -0,0 +1,49 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var gTestfile = 'destructuring-__proto__-shorthand-assignment-before-var.js';
var BUGNUMBER = 963641;
var summary = "{ __proto__ } should work as a destructuring assignment pattern";
print(BUGNUMBER + ": " + summary);
/**************
* BEGIN TEST *
**************/
function objectWithProtoProperty(v)
{
var obj = {};
return Object.defineProperty(obj, "__proto__",
{
enumerable: true,
configurable: true,
writable: true,
value: v
});
}
({ __proto__ } = objectWithProtoProperty(17));
assertEq(__proto__, 17);
var { __proto__ } = objectWithProtoProperty(42);
assertEq(__proto__, 42);
function nested()
{
({ __proto__ } = objectWithProtoProperty(undefined));
assertEq(__proto__, undefined);
var { __proto__ } = objectWithProtoProperty("fnord");
assertEq(__proto__, "fnord");
}
nested();
/******************************************************************************/
if (typeof reportCompare === "function")
reportCompare(true, true);
print("Tests complete");

View File

@ -0,0 +1,49 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var gTestfile = 'destructuring-__proto__-shorthand-assignment.js';
var BUGNUMBER = 963641;
var summary = "{ __proto__ } should work as a destructuring assignment pattern";
print(BUGNUMBER + ": " + summary);
/**************
* BEGIN TEST *
**************/
function objectWithProtoProperty(v)
{
var obj = {};
return Object.defineProperty(obj, "__proto__",
{
enumerable: true,
configurable: true,
writable: true,
value: v
});
}
var { __proto__ } = objectWithProtoProperty(42);
assertEq(__proto__, 42);
({ __proto__ } = objectWithProtoProperty(17));
assertEq(__proto__, 17);
function nested()
{
var { __proto__ } = objectWithProtoProperty("fnord");
assertEq(__proto__, "fnord");
({ __proto__ } = objectWithProtoProperty(undefined));
assertEq(__proto__, undefined);
}
nested();
/******************************************************************************/
if (typeof reportCompare === "function")
reportCompare(true, true);
print("Tests complete");

View File

@ -0,0 +1,50 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var gTestfile = 'destructuring-__proto__-target--assignment.js';
var BUGNUMBER = 963641;
var summary =
"{ __proto__: target } should work as a destructuring assignment pattern";
print(BUGNUMBER + ": " + summary);
/**************
* BEGIN TEST *
**************/
function objectWithProtoProperty(v)
{
var obj = {};
return Object.defineProperty(obj, "__proto__",
{
enumerable: true,
configurable: true,
writable: true,
value: v
});
}
var { __proto__: target } = objectWithProtoProperty(null);
assertEq(target, null);
({ __proto__: target } = objectWithProtoProperty("aacchhorrt"));
assertEq(target, "aacchhorrt");
function nested()
{
var { __proto__: target } = objectWithProtoProperty(3.141592654);
assertEq(target, 3.141592654);
({ __proto__: target } = objectWithProtoProperty(-0));
assertEq(target, -0);
}
nested();
/******************************************************************************/
if (typeof reportCompare === "function")
reportCompare(true, true);
print("Tests complete");