From cef5bfd081448859015383cca1ade28d95f5ccd8 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Fri, 24 Jan 2014 19:54:53 -0800 Subject: [PATCH] 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 --- js/src/frontend/BytecodeEmitter.cpp | 82 ++++++++++--------- js/src/frontend/FullParseHandler.h | 8 -- js/src/frontend/ParseNode.h | 1 - js/src/frontend/Parser.cpp | 53 +++++------- ...proto__-shorthand-assignment-before-var.js | 49 +++++++++++ ...ucturing-__proto__-shorthand-assignment.js | 49 +++++++++++ ...structuring-__proto__-target-assignment.js | 50 +++++++++++ 7 files changed, 212 insertions(+), 80 deletions(-) create mode 100644 js/src/tests/ecma_5/extensions/destructuring-__proto__-shorthand-assignment-before-var.js create mode 100644 js/src/tests/ecma_5/extensions/destructuring-__proto__-shorthand-assignment.js create mode 100644 js/src/tests/ecma_5/extensions/destructuring-__proto__-target-assignment.js diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 512c8e7003c..907e49e955d 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -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()); diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 90e9e82f7e8..b9356efcf4d 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -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) diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 859a84a7438..10874e384bd 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -139,7 +139,6 @@ class UpvarCookie F(FORHEAD) \ F(ARGSBODY) \ F(SPREAD) \ - F(MUTATEPROTO) \ \ /* Unary operators. */ \ F(TYPEOF) \ diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index edef52dae1a..828184fe203 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -3099,32 +3099,31 @@ Parser::checkDestructuring(BindData *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::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::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::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::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::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::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; diff --git a/js/src/tests/ecma_5/extensions/destructuring-__proto__-shorthand-assignment-before-var.js b/js/src/tests/ecma_5/extensions/destructuring-__proto__-shorthand-assignment-before-var.js new file mode 100644 index 00000000000..da0da55c8a0 --- /dev/null +++ b/js/src/tests/ecma_5/extensions/destructuring-__proto__-shorthand-assignment-before-var.js @@ -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"); diff --git a/js/src/tests/ecma_5/extensions/destructuring-__proto__-shorthand-assignment.js b/js/src/tests/ecma_5/extensions/destructuring-__proto__-shorthand-assignment.js new file mode 100644 index 00000000000..0ab26465f58 --- /dev/null +++ b/js/src/tests/ecma_5/extensions/destructuring-__proto__-shorthand-assignment.js @@ -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"); diff --git a/js/src/tests/ecma_5/extensions/destructuring-__proto__-target-assignment.js b/js/src/tests/ecma_5/extensions/destructuring-__proto__-target-assignment.js new file mode 100644 index 00000000000..9e8ec72179d --- /dev/null +++ b/js/src/tests/ecma_5/extensions/destructuring-__proto__-target-assignment.js @@ -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");