author | Wes Kocher <wkocher@mozilla.com> |
Wed, 02 Sep 2015 15:51:40 -0700 | |
changeset 260634 | c77c5698e85964fdbc3cad110c50b50737f22856 |
parent 260633 | cf61909d89df9774843a3f8d6623039e1900e1f1 |
child 260635 | d6a168f9d80a3e8e35c24e60ffdb5c5176694fe0 |
push id | 29318 |
push user | cbook@mozilla.com |
push date | Thu, 03 Sep 2015 11:15:07 +0000 |
treeherder | mozilla-central@74fbd245369c [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
bugs | 1168992 |
milestone | 43.0a1 |
backs out | cb3a01796c021e38cdfcaefe64c7a8e80b56668b 45e16ff50caa40ad30f03f0caf9f0c9e9e8798b5 6155cc98258d05da277d70d53fa828f0515f1a64 0ff820964ea067091de3d6171577b6acb83f1cb9 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -751,19 +751,17 @@ class NodeBuilder bool comprehensionIf(HandleValue test, TokenPos* pos, MutableHandleValue dst); bool comprehensionExpression(HandleValue body, NodeVector& blocks, HandleValue filter, bool isLegacy, TokenPos* pos, MutableHandleValue dst); bool generatorExpression(HandleValue body, NodeVector& blocks, HandleValue filter, bool isLegacy, TokenPos* pos, MutableHandleValue dst); - bool metaProperty(HandleValue meta, HandleValue property, TokenPos* pos, MutableHandleValue dst); - - bool super(TokenPos* pos, MutableHandleValue dst); + bool newTargetExpression(TokenPos* pos, MutableHandleValue dst); /* * declarations */ bool variableDeclaration(NodeVector& elts, VarDeclKind kind, TokenPos* pos, MutableHandleValue dst); @@ -1816,43 +1814,30 @@ NodeBuilder::classDefinition(bool expr, TokenPos* pos, MutableHandleValue dst) { ASTType type = expr ? AST_CLASS_EXPR : AST_CLASS_STMT; RootedValue cb(cx, callbacks[type]); if (!cb.isNull()) return callback(cb, name, heritage, block, pos, dst); return newNode(type, pos, - "id", name, - "superClass", heritage, + "name", name, + "heritage", heritage, "body", block, dst); } bool -NodeBuilder::metaProperty(HandleValue meta, HandleValue property, TokenPos* pos, MutableHandleValue dst) +NodeBuilder::newTargetExpression(TokenPos* pos, MutableHandleValue dst) { - RootedValue cb(cx, callbacks[AST_METAPROPERTY]); - if (!cb.isNull()) - return callback(cb, meta, property, pos, dst); - - return newNode(AST_METAPROPERTY, pos, - "meta", meta, - "property", property, - dst); -} - -bool -NodeBuilder::super(TokenPos* pos, MutableHandleValue dst) -{ - RootedValue cb(cx, callbacks[AST_SUPER]); + RootedValue cb(cx, callbacks[AST_NEWTARGET_EXPR]); if (!cb.isNull()) return callback(cb, pos, dst); - return newNode(AST_SUPER, pos, dst); + return newNode(AST_NEWTARGET_EXPR, pos, dst); } namespace { /* * Serialization of parse nodes to JavaScript objects. * * All serialization methods take a non-nullable ParseNode pointer. @@ -3076,17 +3061,19 @@ ASTSerializer::expression(ParseNode* pn, case PNK_INSTANCEOF: return leftAssociate(pn, dst); case PNK_POW: return rightAssociate(pn, dst); case PNK_DELETENAME: case PNK_DELETEPROP: + case PNK_DELETESUPERPROP: case PNK_DELETEELEM: + case PNK_DELETESUPERELEM: case PNK_DELETEEXPR: case PNK_TYPEOFNAME: case PNK_TYPEOFEXPR: case PNK_VOID: case PNK_NOT: case PNK_BITNOT: case PNK_POS: case PNK_NEG: { @@ -3137,51 +3124,55 @@ ASTSerializer::expression(ParseNode* pn, : builder.callExpression(callee, args, &pn->pn_pos, dst); } case PNK_DOT: { MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_expr->pn_pos)); - RootedValue expr(cx); - RootedValue propname(cx); + RootedValue expr(cx), id(cx); RootedAtom pnAtom(cx, pn->pn_atom); - - if (pn->as<PropertyAccess>().isSuper()) { - if (!builder.super(&pn->pn_expr->pn_pos, &expr)) - return false; - } else { - if (!expression(pn->pn_expr, &expr)) - return false; - } - - return identifier(pnAtom, nullptr, &propname) && - builder.memberExpression(false, expr, propname, &pn->pn_pos, dst); + return expression(pn->pn_expr, &expr) && + identifier(pnAtom, nullptr, &id) && + builder.memberExpression(false, expr, id, &pn->pn_pos, dst); + } + + case PNK_SUPERPROP: + { + RootedValue superBase(cx), id(cx); + RootedAtom superAtom(cx, cx->names().super); + RootedAtom pnAtom(cx, pn->pn_atom); + return identifier(superAtom, nullptr, &superBase) && + identifier(pnAtom, nullptr, &id) && + builder.memberExpression(false, superBase, id, &pn->pn_pos, dst); } case PNK_ELEM: { MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); RootedValue left(cx), right(cx); - - if (pn->as<PropertyByValue>().isSuper()) { - if (!builder.super(&pn->pn_left->pn_pos, &left)) - return false; - } else { - if (!expression(pn->pn_left, &left)) - return false; - } - - return expression(pn->pn_right, &right) && + return expression(pn->pn_left, &left) && + expression(pn->pn_right, &right) && builder.memberExpression(true, left, right, &pn->pn_pos, dst); } + case PNK_SUPERELEM: + { + MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos)); + + RootedValue superBase(cx), expr(cx); + RootedAtom superAtom(cx, cx->names().super); + return identifier(superAtom, nullptr, &superBase) && + expression(pn->pn_kid, &expr) && + builder.memberExpression(true, superBase, expr, &pn->pn_pos, dst); + } + case PNK_CALLSITEOBJ: { NodeVector raw(cx); if (!raw.reserve(pn->pn_head->pn_count)) return false; for (ParseNode* next = pn->pn_head->pn_head; next; next = next->pn_next) { MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos)); @@ -3316,32 +3307,17 @@ ASTSerializer::expression(ParseNode* pn, /* NB: it's no longer the case that pn_count could be 2. */ LOCAL_ASSERT(pn->pn_count == 1); return comprehension(pn->pn_head, dst); case PNK_CLASS: return classDefinition(pn, true, dst); case PNK_NEWTARGET: - { - MOZ_ASSERT(pn->pn_left->isKind(PNK_POSHOLDER)); - MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); - MOZ_ASSERT(pn->pn_right->isKind(PNK_POSHOLDER)); - MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); - - RootedValue newIdent(cx); - RootedValue targetIdent(cx); - - RootedAtom newStr(cx, cx->names().new_); - RootedAtom targetStr(cx, cx->names().target); - - return identifier(newStr, &pn->pn_left->pn_pos, &newIdent) && - identifier(targetStr, &pn->pn_right->pn_pos, &targetIdent) && - builder.metaProperty(newIdent, targetIdent, &pn->pn_pos, dst); - } + return builder.newTargetExpression(&pn->pn_pos, dst); default: LOCAL_NOT_REACHED("unexpected expression type"); } } bool ASTSerializer::propertyName(ParseNode* pn, MutableHandleValue dst)
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1910,48 +1910,47 @@ bool BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) { JS_CHECK_RECURSION(cx, return false); restart: switch (pn->getKind()) { // Trivial cases with no side effects. + case PNK_NEWTARGET: case PNK_NOP: case PNK_STRING: case PNK_TEMPLATE_STRING: case PNK_REGEXP: case PNK_TRUE: case PNK_FALSE: case PNK_NULL: case PNK_THIS: case PNK_ELISION: case PNK_GENERATOR: case PNK_NUMBER: case PNK_OBJECT_PROPERTY_NAME: MOZ_ASSERT(pn->isArity(PN_NULLARY)); *answer = false; return true; - // Trivial binary nodes with more token pos holders. - case PNK_NEWTARGET: - MOZ_ASSERT(pn->isArity(PN_BINARY)); - MOZ_ASSERT(pn->pn_left->isKind(PNK_POSHOLDER)); - MOZ_ASSERT(pn->pn_right->isKind(PNK_POSHOLDER)); - *answer = false; - return true; - case PNK_BREAK: case PNK_CONTINUE: case PNK_DEBUGGER: MOZ_ASSERT(pn->isArity(PN_NULLARY)); *answer = true; return true; // Watch out for getters! + case PNK_SUPERPROP: + MOZ_ASSERT(pn->isArity(PN_NULLARY)); + *answer = true; + return true; + + // Again, getters. case PNK_DOT: MOZ_ASSERT(pn->isArity(PN_NAME)); *answer = true; return true; // Unary cases with side effects only if the child has them. case PNK_TYPEOFEXPR: case PNK_VOID: @@ -2007,17 +2006,19 @@ BytecodeEmitter::checkSideEffects(ParseN case PNK_YIELD: MOZ_ASSERT(pn->isArity(PN_BINARY)); *answer = true; return true; // Deletion generally has side effects, even if isolated cases have none. case PNK_DELETENAME: case PNK_DELETEPROP: + case PNK_DELETESUPERPROP: case PNK_DELETEELEM: + case PNK_DELETESUPERELEM: MOZ_ASSERT(pn->isArity(PN_UNARY)); *answer = true; return true; // Deletion of a non-Reference expression has side effects only through // evaluating the expression. case PNK_DELETEEXPR: { MOZ_ASSERT(pn->isArity(PN_UNARY)); @@ -2115,16 +2116,22 @@ BytecodeEmitter::checkSideEffects(ParseN return checkSideEffects(pn->pn_right, answer); // More getters. case PNK_ELEM: MOZ_ASSERT(pn->isArity(PN_BINARY)); *answer = true; return true; + // Again, getters. + case PNK_SUPERELEM: + MOZ_ASSERT(pn->isArity(PN_UNARY)); + *answer = true; + return true; + // These affect visible names in this code, or in other code. case PNK_IMPORT: case PNK_EXPORT_FROM: case PNK_EXPORT_DEFAULT: MOZ_ASSERT(pn->isArity(PN_BINARY)); *answer = true; return true; @@ -2308,17 +2315,16 @@ BytecodeEmitter::checkSideEffects(ParseN case PNK_CLASSNAMES: // by PNK_CLASS case PNK_CLASSMETHODLIST: // by PNK_CLASS case PNK_IMPORT_SPEC_LIST: // by PNK_IMPORT case PNK_IMPORT_SPEC: // by PNK_IMPORT case PNK_EXPORT_BATCH_SPEC:// by PNK_EXPORT case PNK_EXPORT_SPEC_LIST: // by PNK_EXPORT case PNK_EXPORT_SPEC: // by PNK_EXPORT case PNK_CALLSITEOBJ: // by PNK_TAGGED_TEMPLATE - case PNK_POSHOLDER: // by PNK_NEWTARGET MOZ_CRASH("handled by parent nodes"); case PNK_LIMIT: // invalid sentinel value MOZ_CRASH("invalid node kind"); } MOZ_CRASH("invalid, unenumerated ParseNodeKind value encountered in " "BytecodeEmitter::checkSideEffects"); @@ -2541,36 +2547,33 @@ BytecodeEmitter::emitNameOp(ParseNode* p } bool BytecodeEmitter::emitPropLHS(ParseNode* pn) { MOZ_ASSERT(pn->isKind(PNK_DOT)); ParseNode* pn2 = pn->maybeExpr(); - // Don't want super sneaking in here. - MOZ_ASSERT(!pn2->isKind(PNK_POSHOLDER)); - /* * If the object operand is also a dotted property reference, reverse the * list linked via pn_expr temporarily so we can iterate over it from the * bottom up (reversing again as we go), to avoid excessive recursion. */ if (pn2->isKind(PNK_DOT)) { ParseNode* pndot = pn2; ParseNode* pnup = nullptr; ParseNode* pndown; ptrdiff_t top = offset(); for (;;) { /* Reverse pndot->pn_expr to point up, not down. */ pndot->pn_offset = top; MOZ_ASSERT(!pndot->isUsed()); pndown = pndot->pn_expr; pndot->pn_expr = pnup; - if (!pndown->isKind(PNK_DOT) || pndown->as<PropertyAccess>().isSuper()) + if (!pndown->isKind(PNK_DOT)) break; pnup = pndot; pndot = pndown; } /* pndown is a primary expression, not a dotted property reference. */ if (!emitTree(pndown)) return false; @@ -2640,65 +2643,94 @@ BytecodeEmitter::emitSuperPropOp(ParseNo } bool BytecodeEmitter::emitPropIncDec(ParseNode* pn) { MOZ_ASSERT(pn->pn_kid->isKind(PNK_DOT)); bool post; - bool isSuper = pn->pn_kid->as<PropertyAccess>().isSuper(); JSOp binop = GetIncDecInfo(pn->getKind(), &post); - if (isSuper) { - if (!emitSuperPropLHS()) // THIS OBJ - return false; - if (!emit1(JSOP_DUP2)) // THIS OBJ THIS OBJ - return false; - } else { - if (!emitPropLHS(pn->pn_kid)) // OBJ - return false; - if (!emit1(JSOP_DUP)) // OBJ OBJ - return false; - } - if (!emitAtomOp(pn->pn_kid, isSuper? JSOP_GETPROP_SUPER : JSOP_GETPROP)) // OBJ V + if (!emitPropLHS(pn->pn_kid)) // OBJ + return false; + if (!emit1(JSOP_DUP)) // OBJ OBJ + return false; + if (!emitAtomOp(pn->pn_kid, JSOP_GETPROP)) // OBJ V return false; if (!emit1(JSOP_POS)) // OBJ N return false; if (post && !emit1(JSOP_DUP)) // OBJ N? N return false; if (!emit1(JSOP_ONE)) // OBJ N? N 1 return false; if (!emit1(binop)) // OBJ N? N+1 return false; if (post) { - if (!emit2(JSOP_PICK, 2 + isSuper)) // N? N+1 OBJ - return false; - if (!emit1(JSOP_SWAP)) // N? OBJ N+1 - return false; - if (isSuper) { - if (!emit2(JSOP_PICK, 3)) // N THIS N+1 OBJ - return false; - if (!emit1(JSOP_SWAP)) // N THIS OBJ N+1 - return false; - } - } - - JSOp setOp = isSuper ? sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER - : sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP; + if (!emit2(JSOP_PICK, 2)) // N? N+1 OBJ + return false; + if (!emit1(JSOP_SWAP)) // N? OBJ N+1 + return false; + } + + JSOp setOp = sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP; if (!emitAtomOp(pn->pn_kid, setOp)) // N? N+1 return false; if (post && !emit1(JSOP_POP)) // RESULT return false; return true; } bool +BytecodeEmitter::emitSuperPropIncDec(ParseNode* pn) +{ + MOZ_ASSERT(pn->pn_kid->isKind(PNK_SUPERPROP)); + + bool post; + JSOp binop = GetIncDecInfo(pn->getKind(), &post); + + if (!emitSuperPropLHS()) // THIS OBJ + return false; + + if (!emit1(JSOP_DUP2)) // THIS OBJ THIS OBJ + return false; + if (!emitAtomOp(pn->pn_kid, JSOP_GETPROP_SUPER)) // THIS OBJ V + return false; + if (!emit1(JSOP_POS)) // THIS OBJ N + return false; + if (post && !emit1(JSOP_DUP)) // THIS OBJ N? N + return false; + if (!emit1(JSOP_ONE)) // THIS OBJ N? N 1 + return false; + if (!emit1(binop)) // THIS OBJ N? N+1 + return false; + + if (post) { + if (!emit2(JSOP_PICK, 3)) // OBJ N N+1 THIS + return false; + if (!emit1(JSOP_SWAP)) // OBJ N THIS N+1 + return false; + if (!emit2(JSOP_PICK, 3)) // N THIS N+1 OBJ + return false; + if (!emit1(JSOP_SWAP)) // N THIS OBJ N+1 + return false; + } + + JSOp setOp = sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER; + if (!emitAtomOp(pn->pn_kid, setOp)) // N? N+1 + return false; + if (post && !emit1(JSOP_POP)) // RESULT + return false; + + return true; +} + +bool BytecodeEmitter::emitNameIncDec(ParseNode* pn) { const JSCodeSpec* cs = &js_CodeSpec[pn->pn_kid->getOp()]; bool global = (cs->format & JOF_GNAME); bool post; JSOp binop = GetIncDecInfo(pn->getKind(), &post); @@ -2749,24 +2781,24 @@ BytecodeEmitter::emitElemOperands(ParseN if (isSetElem && !emit2(JSOP_PICK, 2)) return false; return true; } bool BytecodeEmitter::emitSuperElemOperands(ParseNode* pn, SuperElemOptions opts) { - MOZ_ASSERT(pn->isKind(PNK_ELEM) && pn->as<PropertyByValue>().isSuper()); + MOZ_ASSERT(pn->isKind(PNK_SUPERELEM)); // The ordering here is somewhat screwy. We need to evaluate the propval // first, by spec. Do a little dance to not emit more than one JSOP_THIS. // Since JSOP_THIS might throw in derived class constructors, we cannot // just push it earlier as the receiver. We have to swap it down instead. - if (!emitTree(pn->pn_right)) + if (!emitTree(pn->pn_kid)) return false; // We need to convert the key to an object id first, so that we do not do // it inside both the GETELEM and the SETELEM. if (opts == SuperElem_IncDec && !emit1(JSOP_TOID)) return false; if (!emit1(JSOP_THIS)) @@ -2826,87 +2858,110 @@ BytecodeEmitter::emitSuperElemOp(ParseNo return true; } bool BytecodeEmitter::emitElemIncDec(ParseNode* pn) { MOZ_ASSERT(pn->pn_kid->isKind(PNK_ELEM)); - bool isSuper = pn->pn_kid->as<PropertyByValue>().isSuper(); - - if (isSuper) { - if (!emitSuperElemOperands(pn->pn_kid, SuperElem_IncDec)) - return false; - } else { - if (!emitElemOperands(pn->pn_kid, JSOP_GETELEM)) - return false; - } + if (!emitElemOperands(pn->pn_kid, JSOP_GETELEM)) + return false; bool post; JSOp binop = GetIncDecInfo(pn->getKind(), &post); - JSOp getOp; - if (isSuper) { - // There's no such thing as JSOP_DUP3, so we have to be creative. - // Note that pushing things again is no fewer JSOps. - if (!emitDupAt(2)) // KEY THIS OBJ KEY - return false; - if (!emitDupAt(2)) // KEY THIS OBJ KEY THIS - return false; - if (!emitDupAt(2)) // KEY THIS OBJ KEY THIS OBJ - return false; - getOp = JSOP_GETELEM_SUPER; - } else { - // We need to convert the key to an object id first, so that we do not do - // it inside both the GETELEM and the SETELEM. In the super case, this is - // done by emitSuperElemOperands. - // OBJ KEY* - if (!emit1(JSOP_TOID)) // OBJ KEY - return false; - if (!emit1(JSOP_DUP2)) // OBJ KEY OBJ KEY - return false; - getOp = JSOP_GETELEM; - } - if (!emitElemOpBase(getOp)) // OBJ KEY V - return false; - if (!emit1(JSOP_POS)) // OBJ KEY N - return false; - if (post && !emit1(JSOP_DUP)) // OBJ KEY N? N - return false; - if (!emit1(JSOP_ONE)) // OBJ KEY N? N 1 - return false; - if (!emit1(binop)) // OBJ KEY N? N+1 + /* + * We need to convert the key to an object id first, so that we do not do + * it inside both the GETELEM and the SETELEM. + */ + // OBJ KEY* + if (!emit1(JSOP_TOID)) // OBJ KEY + return false; + if (!emit1(JSOP_DUP2)) // OBJ KEY OBJ KEY + return false; + if (!emitElemOpBase(JSOP_GETELEM)) // OBJ KEY V + return false; + if (!emit1(JSOP_POS)) // OBJ KEY N + return false; + if (post && !emit1(JSOP_DUP)) // OBJ KEY N? N + return false; + if (!emit1(JSOP_ONE)) // OBJ KEY N? N 1 + return false; + if (!emit1(binop)) // OBJ KEY N? N+1 + return false; if (post) { - if (isSuper) { - // We have one more value to rotate around, because of |this| - // on the stack - if (!emit2(JSOP_PICK, 4)) - return false; - } - if (!emit2(JSOP_PICK, 3 + isSuper)) // KEY N N+1 OBJ - return false; - if (!emit2(JSOP_PICK, 3 + isSuper)) // N N+1 OBJ KEY - return false; - if (!emit2(JSOP_PICK, 2 + isSuper)) // N OBJ KEY N+1 - return false; - } - - JSOp setOp = isSuper ? (sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER) - : (sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM); - if (!emitElemOpBase(setOp)) // N? N+1 - return false; - if (post && !emit1(JSOP_POP)) // RESULT + if (!emit2(JSOP_PICK, 3)) // KEY N N+1 OBJ + return false; + if (!emit2(JSOP_PICK, 3)) // N N+1 OBJ KEY + return false; + if (!emit2(JSOP_PICK, 2)) // N OBJ KEY N+1 + return false; + } + + JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM; + if (!emitElemOpBase(setOp)) // N? N+1 + return false; + if (post && !emit1(JSOP_POP)) // RESULT return false; return true; } bool +BytecodeEmitter::emitSuperElemIncDec(ParseNode* pn) +{ + MOZ_ASSERT(pn->pn_kid->isKind(PNK_SUPERELEM)); + + if (!emitSuperElemOperands(pn->pn_kid, SuperElem_IncDec)) + return false; + + bool post; + JSOp binop = GetIncDecInfo(pn->getKind(), &post); + + // There's no such thing as JSOP_DUP3, so we have to be creative. + // Note that pushing things again is no fewer JSOps. + if (!emitDupAt(2)) // KEY THIS OBJ KEY + return false; + if (!emitDupAt(2)) // KEY THIS OBJ KEY THIS + return false; + if (!emitDupAt(2)) // KEY THIS OBJ KEY THIS OBJ + return false; + if (!emitElemOpBase(JSOP_GETELEM_SUPER)) // KEY THIS OBJ V + return false; + if (!emit1(JSOP_POS)) // KEY THIS OBJ N + return false; + if (post && !emit1(JSOP_DUP)) // KEY THIS OBJ N? N + return false; + if (!emit1(JSOP_ONE)) // KEY THIS OBJ N? N 1 + return false; + if (!emit1(binop)) // KEY THIS OBJ N? N+1 + return false; + + if (post) { + if (!emit2(JSOP_PICK, 4)) // THIS OBJ N N+1 KEY + return false; + if (!emit2(JSOP_PICK, 4)) // OBJ N N+1 KEY THIS + return false; + if (!emit2(JSOP_PICK, 4)) // N N+1 KEY THIS OBJ + return false; + if (!emit2(JSOP_PICK, 3)) // N KEY THIS OBJ N+1 + return false; + } + + JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER; + if (!emitElemOpBase(setOp)) // N? N+1 + return false; + if (post && !emit1(JSOP_POP)) // RESULT + return false; + + return true; +} +bool BytecodeEmitter::emitNumberOp(double dval) { int32_t ival; if (NumberIsInt32(dval, &ival)) { if (ival == 0) return emit1(JSOP_ZERO); if (ival == 1) return emit1(JSOP_ONE); @@ -3723,49 +3778,57 @@ BytecodeEmitter::emitDestructuringLHS(Pa // See the (PNK_NAME, JSOP_SETNAME) case above. // // In `a.x = b`, `a` is evaluated first, then `b`, then a // JSOP_SETPROP instruction. // // In `[a.x] = [b]`, per spec, `b` is evaluated before `a`. Then we // need a property set -- but the operands are on the stack in the // wrong order for JSOP_SETPROP, so we have to add a JSOP_SWAP. - JSOp setOp; - if (target->as<PropertyAccess>().isSuper()) { - if (!emitSuperPropLHS()) - return false; - if (!emit2(JSOP_PICK, 2)) - return false; - setOp = sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER; - } else { - if (!emitTree(target->pn_expr)) - return false; - if (!emit1(JSOP_SWAP)) - return false; - setOp = sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP; - } + if (!emitTree(target->pn_expr)) + return false; + if (!emit1(JSOP_SWAP)) + return false; + JSOp setOp = sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP; + if (!emitAtomOp(target, setOp)) + return false; + break; + } + + case PNK_SUPERPROP: + { + // See comment above at PNK_DOT. Pick up the pushed value, to fix ordering. + if (!emitSuperPropLHS()) + return false; + if (!emit2(JSOP_PICK, 2)) + return false; + JSOp setOp = sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER; if (!emitAtomOp(target, setOp)) return false; break; } case PNK_ELEM: { // See the comment at `case PNK_DOT:` above. This case, // `[a[x]] = [b]`, is handled much the same way. The JSOP_SWAP // is emitted by emitElemOperands. - if (target->as<PropertyByValue>().isSuper()) { - JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER; - if (!emitSuperElemOp(target, setOp)) - return false; - } else { - JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM; - if (!emitElemOp(target, setOp)) - return false; - } + JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM; + if (!emitElemOp(target, setOp)) + return false; + break; + } + + case PNK_SUPERELEM: + { + // See comment above in the PNK_ELEM case. Just as there, the + // reordering is handled by emitSuperElemOp. + JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER; + if (!emitSuperElemOp(target, setOp)) + return false; break; } case PNK_CALL: MOZ_ASSERT(target->pn_xflags & PNX_SETCALL); if (!emitTree(target)) return false; @@ -4380,41 +4443,41 @@ BytecodeEmitter::emitAssignment(ParseNod bindOp = JSOP_BINDINTRINSIC; if (!emitIndex32(bindOp, atomIndex)) return false; offset++; } } break; case PNK_DOT: - if (lhs->as<PropertyAccess>().isSuper()) { - if (!emitSuperPropLHS()) - return false; - offset += 2; - } else { - if (!emitTree(lhs->expr())) - return false; - offset += 1; - } + if (!emitTree(lhs->expr())) + return false; + offset++; + if (!makeAtomIndex(lhs->pn_atom, &atomIndex)) + return false; + break; + case PNK_SUPERPROP: + if (!emitSuperPropLHS()) + return false; + offset += 2; if (!makeAtomIndex(lhs->pn_atom, &atomIndex)) return false; break; case PNK_ELEM: MOZ_ASSERT(lhs->isArity(PN_BINARY)); - if (lhs->as<PropertyByValue>().isSuper()) { - if (!emitSuperElemOperands(lhs)) - return false; - offset += 3; - } else { - if (!emitTree(lhs->pn_left)) - return false; - if (!emitTree(lhs->pn_right)) - return false; - offset += 2; - } + if (!emitTree(lhs->pn_left)) + return false; + if (!emitTree(lhs->pn_right)) + return false; + offset += 2; + break; + case PNK_SUPERELEM: + if (!emitSuperElemOperands(lhs)) + return false; + offset += 3; break; case PNK_ARRAY: case PNK_OBJECT: break; case PNK_CALL: MOZ_ASSERT(lhs->pn_xflags & PNX_SETCALL); if (!emitTree(lhs)) return false; @@ -4462,50 +4525,45 @@ BytecodeEmitter::emitAssignment(ParseNod case JSOP_SETALIASEDVAR: op = JSOP_GETALIASEDVAR; break; default: MOZ_CRASH("Bad op"); } if (!emitVarOp(lhs, op)) return false; } break; case PNK_DOT: { - JSOp getOp; - if (lhs->as<PropertyAccess>().isSuper()) { - if (!emit1(JSOP_DUP2)) - return false; - getOp = JSOP_GETPROP_SUPER; - } else { - if (!emit1(JSOP_DUP)) - return false; - bool isLength = (lhs->pn_atom == cx->names().length); - getOp = isLength ? JSOP_LENGTH : JSOP_GETPROP; - } - if (!emitIndex32(getOp, atomIndex)) + if (!emit1(JSOP_DUP)) + return false; + bool isLength = (lhs->pn_atom == cx->names().length); + if (!emitIndex32(isLength ? JSOP_LENGTH : JSOP_GETPROP, atomIndex)) return false; break; } - case PNK_ELEM: { - JSOp elemOp; - if (lhs->as<PropertyByValue>().isSuper()) { - if (!emitDupAt(2)) - return false; - if (!emitDupAt(2)) - return false; - if (!emitDupAt(2)) - return false; - elemOp = JSOP_GETELEM_SUPER; - } else { - if (!emit1(JSOP_DUP2)) - return false; - elemOp = JSOP_GETELEM; - } - if (!emitElemOpBase(elemOp)) + case PNK_SUPERPROP: + if (!emit1(JSOP_DUP2)) + return false; + if (!emitIndex32(JSOP_GETPROP_SUPER, atomIndex)) return false; break; - } + case PNK_ELEM: + if (!emit1(JSOP_DUP2)) + return false; + if (!emitElemOpBase(JSOP_GETELEM)) + return false; + break; + case PNK_SUPERELEM: + if (!emitDupAt(2)) + return false; + if (!emitDupAt(2)) + return false; + if (!emitDupAt(2)) + return false; + if (!emitElemOpBase(JSOP_GETELEM_SUPER)) + return false; + break; case PNK_CALL: /* * We just emitted a JSOP_SETCALL (which will always throw) and * popped the call's return value. Push a random value to make sure * the stack depth is correct. */ MOZ_ASSERT(lhs->pn_xflags & PNX_SETCALL); if (!emit1(JSOP_NULL)) @@ -4554,32 +4612,42 @@ BytecodeEmitter::emitAssignment(ParseNod return false; } else { if (!emitIndexOp(lhs->getOp(), atomIndex)) return false; } break; case PNK_DOT: { - JSOp setOp = lhs->as<PropertyAccess>().isSuper() ? - (sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER) : - (sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP); + JSOp setOp = sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP; + if (!emitIndexOp(setOp, atomIndex)) + return false; + break; + } + case PNK_SUPERPROP: + { + JSOp setOp = sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER; if (!emitIndexOp(setOp, atomIndex)) return false; break; } case PNK_CALL: /* Do nothing. The JSOP_SETCALL we emitted will always throw. */ MOZ_ASSERT(lhs->pn_xflags & PNX_SETCALL); break; case PNK_ELEM: { - JSOp setOp = lhs->as<PropertyByValue>().isSuper() ? - sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER : - sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM; + JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM; + if (!emit1(setOp)) + return false; + break; + } + case PNK_SUPERELEM: + { + JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER; if (!emit1(setOp)) return false; break; } case PNK_ARRAY: case PNK_OBJECT: if (!emitDestructuringOps(lhs)) return false; @@ -6445,60 +6513,74 @@ bool BytecodeEmitter::emitDeleteProperty(ParseNode* node) { MOZ_ASSERT(node->isKind(PNK_DELETEPROP)); MOZ_ASSERT(node->isArity(PN_UNARY)); ParseNode* propExpr = node->pn_kid; MOZ_ASSERT(propExpr->isKind(PNK_DOT)); - if (propExpr->as<PropertyAccess>().isSuper()) { - // Still have to calculate the base, even though we are are going - // to throw unconditionally, as calculating the base could also - // throw. - if (!emit1(JSOP_SUPERBASE)) - return false; - - return emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER); - } - JSOp delOp = sc->strict() ? JSOP_STRICTDELPROP : JSOP_DELPROP; return emitPropOp(propExpr, delOp); } bool +BytecodeEmitter::emitDeleteSuperProperty(ParseNode* node) +{ + MOZ_ASSERT(node->isKind(PNK_DELETESUPERPROP)); + MOZ_ASSERT(node->isArity(PN_UNARY)); + MOZ_ASSERT(node->pn_kid->isKind(PNK_SUPERPROP)); + + // Still have to calculate the base, even though we are are going + // to throw unconditionally, as calculating the base could also + // throw. + if (!emit1(JSOP_SUPERBASE)) + return false; + + return emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER); +} + +bool BytecodeEmitter::emitDeleteElement(ParseNode* node) { MOZ_ASSERT(node->isKind(PNK_DELETEELEM)); MOZ_ASSERT(node->isArity(PN_UNARY)); ParseNode* elemExpr = node->pn_kid; MOZ_ASSERT(elemExpr->isKind(PNK_ELEM)); - if (elemExpr->as<PropertyByValue>().isSuper()) { - // Still have to calculate everything, even though we're gonna throw - // since it may have side effects - if (!emitTree(elemExpr->pn_right)) - return false; - - if (!emit1(JSOP_SUPERBASE)) - return false; - if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER)) - return false; - - // Another wrinkle: Balance the stack from the emitter's point of view. - // Execution will not reach here, as the last bytecode threw. - return emit1(JSOP_POP); - } - JSOp delOp = sc->strict() ? JSOP_STRICTDELELEM : JSOP_DELELEM; return emitElemOp(elemExpr, delOp); } bool +BytecodeEmitter::emitDeleteSuperElement(ParseNode* node) +{ + MOZ_ASSERT(node->isKind(PNK_DELETESUPERELEM)); + MOZ_ASSERT(node->isArity(PN_UNARY)); + + ParseNode* superElemExpr = node->pn_kid; + MOZ_ASSERT(superElemExpr->isKind(PNK_SUPERELEM)); + + // Still have to calculate everything, even though we're gonna throw + // since it may have side effects + MOZ_ASSERT(superElemExpr->isArity(PN_UNARY)); + if (!emitTree(superElemExpr->pn_kid)) + return false; + if (!emit1(JSOP_SUPERBASE)) + return false; + if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER)) + return false; + + // Another wrinkle: Balance the stack from the emitter's point of view. + // Execution will not reach here, as the last bytecode threw. + return emit1(JSOP_POP); +} + +bool BytecodeEmitter::emitDeleteExpression(ParseNode* node) { MOZ_ASSERT(node->isKind(PNK_DELETEEXPR)); MOZ_ASSERT(node->isArity(PN_UNARY)); ParseNode* expression = node->pn_kid; // If useless, just emit JSOP_TRUE; otherwise convert |delete <expr>| to @@ -6647,36 +6729,34 @@ BytecodeEmitter::emitCallOrNew(ParseNode if (pn2->name() == cx->names().forceInterpreter) return emitSelfHostedForceInterpreter(pn); // Fall through. } if (!emitNameOp(pn2, callop)) return false; break; case PNK_DOT: - if (pn2->as<PropertyAccess>().isSuper()) { - if (!emitSuperPropOp(pn2, JSOP_GETPROP_SUPER, /* isCall = */ callop)) - return false; - } else { - if (!emitPropOp(pn2, callop ? JSOP_CALLPROP : JSOP_GETPROP)) + if (!emitPropOp(pn2, callop ? JSOP_CALLPROP : JSOP_GETPROP)) + return false; + break; + case PNK_SUPERPROP: + if (!emitSuperPropOp(pn2, JSOP_GETPROP_SUPER, /* isCall = */ callop)) + return false; + break; + case PNK_ELEM: + if (!emitElemOp(pn2, callop ? JSOP_CALLELEM : JSOP_GETELEM)) + return false; + if (callop) { + if (!emit1(JSOP_SWAP)) return false; } break; - case PNK_ELEM: - if (pn2->as<PropertyByValue>().isSuper()) { - if (!emitSuperElemOp(pn2, JSOP_GETELEM_SUPER, /* isCall = */ callop)) - return false; - } else { - if (!emitElemOp(pn2, callop ? JSOP_CALLELEM : JSOP_GETELEM)) - return false; - if (callop) { - if (!emit1(JSOP_SWAP)) - return false; - } - } + case PNK_SUPERELEM: + if (!emitSuperElemOp(pn2, JSOP_GETELEM_SUPER, /* isCall = */ callop)) + return false; break; case PNK_FUNCTION: /* * Top level lambdas which are immediately invoked should be * treated as only running once. Every time they execute we will * create new types and scripts for their contents, to increase * the quality of type information within them and enable more * backend optimizations. Note that this does not depend on the @@ -6824,20 +6904,28 @@ BytecodeEmitter::emitIncOrDec(ParseNode* { /* Emit lvalue-specialized code for ++/-- operators. */ ParseNode* pn2 = pn->pn_kid; switch (pn2->getKind()) { case PNK_DOT: if (!emitPropIncDec(pn)) return false; break; + case PNK_SUPERPROP: + if (!emitSuperPropIncDec(pn)) + return false; + break; case PNK_ELEM: if (!emitElemIncDec(pn)) return false; break; + case PNK_SUPERELEM: + if (!emitSuperElemIncDec(pn)) + return false; + break; case PNK_CALL: MOZ_ASSERT(pn2->pn_xflags & PNX_SETCALL); if (!emitTree(pn2)) return false; break; default: MOZ_ASSERT(pn2->isKind(PNK_NAME)); pn2->setOp(JSOP_SETNAME); @@ -7791,42 +7879,48 @@ BytecodeEmitter::emitTree(ParseNode* pn) case PNK_DELETENAME: ok = emitDeleteName(pn); break; case PNK_DELETEPROP: ok = emitDeleteProperty(pn); break; + case PNK_DELETESUPERPROP: + ok = emitDeleteSuperProperty(pn); + break; + case PNK_DELETEELEM: ok = emitDeleteElement(pn); break; + case PNK_DELETESUPERELEM: + ok = emitDeleteSuperElement(pn); + break; + case PNK_DELETEEXPR: ok = emitDeleteExpression(pn); break; case PNK_DOT: - if (pn->as<PropertyAccess>().isSuper()) { - if (!emitSuperPropOp(pn, JSOP_GETPROP_SUPER)) - return false; - } else { - if (!emitPropOp(pn, JSOP_GETPROP)) - return false; - } + ok = emitPropOp(pn, JSOP_GETPROP); + break; + + case PNK_SUPERPROP: + if (!emitSuperPropOp(pn, JSOP_GETPROP_SUPER)) + return false; break; case PNK_ELEM: - if (pn->as<PropertyByValue>().isSuper()) { - if (!emitSuperElemOp(pn, JSOP_GETELEM_SUPER)) - return false; - } else { - if (!emitElemOp(pn, JSOP_GETELEM)) - return false; - } + ok = emitElemOp(pn, JSOP_GETELEM); + break; + + case PNK_SUPERELEM: + if (!emitSuperElemOp(pn, JSOP_GETELEM_SUPER)) + return false; break; case PNK_NEW: case PNK_TAGGED_TEMPLATE: case PNK_CALL: case PNK_GENEXP: ok = emitCallOrNew(pn); break; @@ -7989,19 +8083,16 @@ BytecodeEmitter::emitTree(ParseNode* pn) ok = emitClass(pn); break; case PNK_NEWTARGET: if (!emit1(JSOP_NEWTARGET)) return false; break; - case PNK_POSHOLDER: - MOZ_ASSERT_UNREACHABLE("Should never try to emit PNK_POSHOLDER"); - default: MOZ_ASSERT(0); } /* bce->emitLevel == 1 means we're last on the stack, so finish up. */ if (ok && emitLevel == 1) { if (!updateSourceCoordNotes(pn->pn_pos.end)) return false;
--- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -549,17 +549,19 @@ struct BytecodeEmitter bool emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs); bool emitReturn(ParseNode* pn); bool emitStatement(ParseNode* pn); bool emitStatementList(ParseNode* pn, ptrdiff_t top); bool emitDeleteName(ParseNode* pn); bool emitDeleteProperty(ParseNode* pn); + bool emitDeleteSuperProperty(ParseNode* pn); bool emitDeleteElement(ParseNode* pn); + bool emitDeleteSuperElement(ParseNode* pn); bool emitDeleteExpression(ParseNode* pn); // |op| must be JSOP_TYPEOF or JSOP_TYPEOFEXPR. bool emitTypeof(ParseNode* node, JSOp op); bool emitLogical(ParseNode* pn); bool emitUnary(ParseNode* pn); @@ -604,17 +606,19 @@ struct BytecodeEmitter // // Please refer the comment above emitSpread for additional information about // stack convention. bool emitForOf(StmtType type, ParseNode* pn, ptrdiff_t top); bool emitClass(ParseNode* pn); bool emitSuperPropLHS(bool isCall = false); bool emitSuperPropOp(ParseNode* pn, JSOp op, bool isCall = false); + bool emitSuperPropIncDec(ParseNode* pn); enum SuperElemOptions { SuperElem_Get, SuperElem_Set, SuperElem_Call, SuperElem_IncDec }; bool emitSuperElemOperands(ParseNode* pn, SuperElemOptions opts = SuperElem_Get); bool emitSuperElemOp(ParseNode* pn, JSOp op, bool isCall = false); + bool emitSuperElemIncDec(ParseNode* pn); }; } /* namespace frontend */ } /* namespace js */ #endif /* frontend_BytecodeEmitter_h */
--- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -329,17 +329,19 @@ ContainsHoistedDeclaration(ExclusiveCont case PNK_CONDITIONAL: case PNK_TYPEOFNAME: case PNK_TYPEOFEXPR: case PNK_VOID: case PNK_NOT: case PNK_BITNOT: case PNK_DELETENAME: case PNK_DELETEPROP: + case PNK_DELETESUPERPROP: case PNK_DELETEELEM: + case PNK_DELETESUPERELEM: case PNK_DELETEEXPR: case PNK_POS: case PNK_NEG: case PNK_PREINCREMENT: case PNK_POSTINCREMENT: case PNK_PREDECREMENT: case PNK_POSTDECREMENT: case PNK_OR: @@ -407,18 +409,19 @@ ContainsHoistedDeclaration(ExclusiveCont case PNK_CATCH: case PNK_FORIN: case PNK_FOROF: case PNK_FORHEAD: case PNK_FRESHENBLOCK: case PNK_CLASSMETHOD: case PNK_CLASSMETHODLIST: case PNK_CLASSNAMES: + case PNK_SUPERPROP: + case PNK_SUPERELEM: case PNK_NEWTARGET: - case PNK_POSHOLDER: MOZ_CRASH("ContainsHoistedDeclaration should have indicated false on " "some parent node without recurring to test this node"); case PNK_GLOBALCONST: MOZ_CRASH("ContainsHoistedDeclaration is only called on nested nodes where " "globalconst nodes should never have been generated"); case PNK_LIMIT: // invalid sentinel value @@ -624,44 +627,46 @@ FoldDeleteExpr(ExclusiveContext* cx, Par return true; } static bool FoldDeleteElement(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser, bool inGenexpLambda) { - MOZ_ASSERT(node->isKind(PNK_DELETEELEM)); + MOZ_ASSERT(node->isKind(PNK_DELETEELEM) || node->isKind(PNK_DELETESUPERELEM)); MOZ_ASSERT(node->isArity(PN_UNARY)); - MOZ_ASSERT(node->pn_kid->isKind(PNK_ELEM)); + MOZ_ASSERT(node->pn_kid->isKind(PNK_ELEM) || node->pn_kid->isKind(PNK_SUPERELEM)); ParseNode*& expr = node->pn_kid; if (!Fold(cx, &expr, parser, inGenexpLambda)) return false; // If we're deleting an element, but constant-folding converted our // element reference into a dotted property access, we must *also* // morph the node's kind. // // In principle this also applies to |super["foo"] -> super.foo|, // but we don't constant-fold |super["foo"]| yet. - MOZ_ASSERT(expr->isKind(PNK_ELEM) || expr->isKind(PNK_DOT)); - if (expr->isKind(PNK_DOT)) - node->setKind(PNK_DELETEPROP); + if (node->isKind(PNK_DELETEELEM)) { + MOZ_ASSERT(expr->isKind(PNK_ELEM) || expr->isKind(PNK_DOT)); + if (expr->isKind(PNK_DOT)) + node->setKind(PNK_DELETEPROP); + } return true; } static bool FoldDeleteProperty(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser, bool inGenexpLambda) { - MOZ_ASSERT(node->isKind(PNK_DELETEPROP)); + MOZ_ASSERT(node->isKind(PNK_DELETEPROP) || node->isKind(PNK_DELETESUPERPROP)); MOZ_ASSERT(node->isArity(PN_UNARY)); - MOZ_ASSERT(node->pn_kid->isKind(PNK_DOT)); + MOZ_ASSERT(node->pn_kid->isKind(PNK_DOT) || node->pn_kid->isKind(PNK_SUPERPROP)); ParseNode*& expr = node->pn_kid; #ifdef DEBUG ParseNodeKind oldKind = expr->getKind(); #endif if (!Fold(cx, &expr, parser, inGenexpLambda)) return false; @@ -1699,34 +1704,35 @@ FoldName(ExclusiveContext* cx, ParseNode bool Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bool inGenexpLambda) { JS_CHECK_RECURSION(cx, return false); ParseNode* pn = *pnp; switch (pn->getKind()) { + case PNK_NEWTARGET: case PNK_NOP: case PNK_REGEXP: case PNK_STRING: case PNK_TRUE: case PNK_FALSE: case PNK_NULL: case PNK_ELISION: case PNK_NUMBER: case PNK_DEBUGGER: case PNK_BREAK: case PNK_CONTINUE: case PNK_TEMPLATE_STRING: case PNK_THIS: case PNK_GENERATOR: case PNK_EXPORT_BATCH_SPEC: case PNK_OBJECT_PROPERTY_NAME: + case PNK_SUPERPROP: case PNK_FRESHENBLOCK: - case PNK_POSHOLDER: MOZ_ASSERT(pn->isArity(PN_NULLARY)); return true; case PNK_TYPEOFNAME: MOZ_ASSERT(pn->isArity(PN_UNARY)); MOZ_ASSERT(pn->pn_kid->isKind(PNK_NAME)); MOZ_ASSERT(!pn->pn_kid->maybeExpr()); return true; @@ -1739,19 +1745,21 @@ Fold(ExclusiveContext* cx, ParseNode** p MOZ_ASSERT(pn->pn_kid->isKind(PNK_NAME)); return true; } case PNK_DELETEEXPR: return FoldDeleteExpr(cx, pn, parser, inGenexpLambda); case PNK_DELETEELEM: + case PNK_DELETESUPERELEM: return FoldDeleteElement(cx, pn, parser, inGenexpLambda); case PNK_DELETEPROP: + case PNK_DELETESUPERPROP: return FoldDeleteProperty(cx, pn, parser, inGenexpLambda); case PNK_CONDITIONAL: return FoldConditional(cx, pnp, parser, inGenexpLambda); case PNK_IF: return FoldIf(cx, pnp, parser, inGenexpLambda); @@ -1769,16 +1777,17 @@ Fold(ExclusiveContext* cx, ParseNode** p case PNK_POSTDECREMENT: return FoldIncrementDecrement(cx, pn, parser, inGenexpLambda); case PNK_THROW: case PNK_ARRAYPUSH: case PNK_MUTATEPROTO: case PNK_COMPUTED_NAME: case PNK_SPREAD: + case PNK_SUPERELEM: case PNK_EXPORT: case PNK_EXPORT_DEFAULT: case PNK_VOID: MOZ_ASSERT(pn->isArity(PN_UNARY)); return Fold(cx, &pn->pn_kid, parser, inGenexpLambda); case PNK_SEMI: MOZ_ASSERT(pn->isArity(PN_UNARY)); @@ -1904,22 +1913,16 @@ Fold(ExclusiveContext* cx, ParseNode** p case PNK_FOR: case PNK_CLASSMETHOD: case PNK_IMPORT_SPEC: case PNK_EXPORT_SPEC: MOZ_ASSERT(pn->isArity(PN_BINARY)); return Fold(cx, &pn->pn_left, parser, inGenexpLambda) && Fold(cx, &pn->pn_right, parser, inGenexpLambda); - case PNK_NEWTARGET: - MOZ_ASSERT(pn->isArity(PN_BINARY)); - MOZ_ASSERT(pn->pn_left->isKind(PNK_POSHOLDER)); - MOZ_ASSERT(pn->pn_right->isKind(PNK_POSHOLDER)); - return true; - case PNK_CLASSNAMES: MOZ_ASSERT(pn->isArity(PN_BINARY)); if (ParseNode*& outerBinding = pn->pn_left) { if (!Fold(cx, &outerBinding, parser, inGenexpLambda)) return false; } return Fold(cx, &pn->pn_right, parser, inGenexpLambda);
--- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -68,17 +68,19 @@ class FullParseHandler /* new_ methods for creating parse nodes. These report OOM on context. */ JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline) typedef ParseNode* Node; typedef Definition* DefinitionNode; bool isPropertyAccess(ParseNode* node) { - return node->isKind(PNK_DOT) || node->isKind(PNK_ELEM); + if (node->isKind(PNK_DOT) || node->isKind(PNK_ELEM)) + return true; + return node->isKind(PNK_SUPERPROP) || node->isKind(PNK_SUPERELEM); } bool isFunctionCall(ParseNode* node) { // Note: super() is a special form, *not* a function call. return node->isKind(PNK_CALL); } static bool isUnparenthesizedDestructuringPattern(ParseNode* node) { @@ -218,19 +220,23 @@ class FullParseHandler if (expr->isKind(PNK_NAME)) { expr->pn_dflags |= PND_DEOPTIMIZED; expr->setOp(JSOP_DELNAME); return newUnary(PNK_DELETENAME, JSOP_NOP, begin, expr); } if (expr->isKind(PNK_DOT)) return newUnary(PNK_DELETEPROP, JSOP_NOP, begin, expr); + if (expr->isKind(PNK_SUPERPROP)) + return newUnary(PNK_DELETESUPERPROP, JSOP_NOP, begin, expr); if (expr->isKind(PNK_ELEM)) return newUnary(PNK_DELETEELEM, JSOP_NOP, begin, expr); + if (expr->isKind(PNK_SUPERELEM)) + return newUnary(PNK_DELETESUPERELEM, JSOP_NOP, begin, expr); return newUnary(PNK_DELETEEXPR, JSOP_NOP, begin, expr); } ParseNode* newTypeof(uint32_t begin, ParseNode* kid) { TokenPos pos(begin, kid->pn_pos.end); ParseNodeKind kind = kid->isKind(PNK_NAME) ? PNK_TYPEOFNAME : PNK_TYPEOFEXPR; return new_<UnaryNode>(kind, JSOP_NOP, pos, kid); @@ -335,30 +341,24 @@ class FullParseHandler return new_<ClassNode>(name, heritage, methodBlock); } ParseNode* newClassMethodList(uint32_t begin) { return new_<ListNode>(PNK_CLASSMETHODLIST, TokenPos(begin, begin + 1)); } ParseNode* newClassNames(ParseNode* outer, ParseNode* inner, const TokenPos& pos) { return new_<ClassNames>(outer, inner, pos); } - ParseNode* newNewTarget(ParseNode* newHolder, ParseNode* targetHolder) { - return new_<BinaryNode>(PNK_NEWTARGET, JSOP_NOP, newHolder, targetHolder); - } - ParseNode* newPosHolder(const TokenPos& pos) { - return new_<NullaryNode>(PNK_POSHOLDER, pos); + ParseNode* newSuperProperty(JSAtom* atom, const TokenPos& pos) { + return new_<SuperProperty>(atom, pos); } - ParseNode* newSuperBase(const TokenPos& pos, ExclusiveContext* cx) { - ParseNode* node = newPosHolder(pos); -#ifdef DEBUG - // Set the atom for assertion purposes - if (node) - node->pn_atom = cx->names().super; -#endif - return node; + ParseNode* newSuperElement(ParseNode* expr, const TokenPos& pos) { + return new_<SuperElement>(expr, pos); + } + ParseNode* newNewTarget(const TokenPos& pos) { + return new_<NullaryNode>(PNK_NEWTARGET, pos); } 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); @@ -706,21 +706,16 @@ class FullParseHandler } bool isStatementPermittedAfterReturnStatement(ParseNode *node) { ParseNodeKind kind = node->getKind(); return kind == PNK_FUNCTION || kind == PNK_VAR || kind == PNK_BREAK || kind == PNK_THROW || (kind == PNK_SEMI && !node->pn_kid); } - bool isSuperBase(ParseNode* node, ExclusiveContext* cx) { - MOZ_ASSERT_IF(node->isKind(PNK_POSHOLDER), node->pn_atom == cx->names().super); - return node->isKind(PNK_POSHOLDER); - } - inline bool finishInitializerAssignment(ParseNode* pn, ParseNode* init, JSOp op); void setBeginPosition(ParseNode* pn, ParseNode* oth) { setBeginPosition(pn, oth->pn_pos.begin); } void setBeginPosition(ParseNode* pn, uint32_t begin) { pn->pn_pos.begin = begin; MOZ_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end);
--- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -370,52 +370,51 @@ class NameResolver case PNK_ELISION: case PNK_GENERATOR: case PNK_NUMBER: case PNK_BREAK: case PNK_CONTINUE: case PNK_DEBUGGER: case PNK_EXPORT_BATCH_SPEC: case PNK_FRESHENBLOCK: + case PNK_SUPERPROP: case PNK_OBJECT_PROPERTY_NAME: + case PNK_NEWTARGET: MOZ_ASSERT(cur->isArity(PN_NULLARY)); break; case PNK_TYPEOFNAME: MOZ_ASSERT(cur->isArity(PN_UNARY)); MOZ_ASSERT(cur->pn_kid->isKind(PNK_NAME)); MOZ_ASSERT(!cur->pn_kid->maybeExpr()); break; - case PNK_NEWTARGET: - MOZ_ASSERT(cur->isArity(PN_BINARY)); - MOZ_ASSERT(cur->pn_left->isKind(PNK_POSHOLDER)); - MOZ_ASSERT(cur->pn_right->isKind(PNK_POSHOLDER)); - break; - // Nodes with a single non-null child requiring name resolution. case PNK_TYPEOFEXPR: case PNK_VOID: case PNK_NOT: case PNK_BITNOT: case PNK_THROW: case PNK_DELETENAME: case PNK_DELETEPROP: + case PNK_DELETESUPERPROP: case PNK_DELETEELEM: + case PNK_DELETESUPERELEM: case PNK_DELETEEXPR: case PNK_NEG: case PNK_POS: case PNK_PREINCREMENT: case PNK_POSTINCREMENT: case PNK_PREDECREMENT: case PNK_POSTDECREMENT: case PNK_COMPUTED_NAME: case PNK_ARRAYPUSH: case PNK_SPREAD: case PNK_MUTATEPROTO: + case PNK_SUPERELEM: case PNK_EXPORT: case PNK_EXPORT_DEFAULT: MOZ_ASSERT(cur->isArity(PN_UNARY)); if (!resolve(cur->pn_kid, prefix)) return false; break; // Nodes with a single nullable child. @@ -436,40 +435,33 @@ class NameResolver case PNK_BITANDASSIGN: case PNK_LSHASSIGN: case PNK_RSHASSIGN: case PNK_URSHASSIGN: case PNK_MULASSIGN: case PNK_DIVASSIGN: case PNK_MODASSIGN: case PNK_POWASSIGN: + case PNK_ELEM: case PNK_COLON: case PNK_CASE: case PNK_SHORTHAND: case PNK_DOWHILE: case PNK_WHILE: case PNK_SWITCH: case PNK_LETBLOCK: case PNK_FOR: case PNK_CLASSMETHOD: MOZ_ASSERT(cur->isArity(PN_BINARY)); if (!resolve(cur->pn_left, prefix)) return false; if (!resolve(cur->pn_right, prefix)) return false; break; - case PNK_ELEM: - MOZ_ASSERT(cur->isArity(PN_BINARY)); - if (!cur->as<PropertyByValue>().isSuper() && !resolve(cur->pn_left, prefix)) - return false; - if (!resolve(cur->pn_right, prefix)) - return false; - break; - case PNK_WITH: MOZ_ASSERT(cur->isArity(PN_BINARY_OBJ)); if (!resolve(cur->pn_left, prefix)) return false; if (!resolve(cur->pn_right, prefix)) return false; break; @@ -755,28 +747,19 @@ class NameResolver MOZ_ASSERT(catchNode->expr()->isKind(PNK_CATCH)); MOZ_ASSERT(catchNode->expr()->isArity(PN_TERNARY)); if (!resolve(catchNode->expr(), prefix)) return false; } break; } + case PNK_LABEL: case PNK_DOT: MOZ_ASSERT(cur->isArity(PN_NAME)); - - // Super prop nodes do not have a meaningful LHS - if (cur->as<PropertyAccess>().isSuper()) - break; - if (!resolve(cur->expr(), prefix)) - return false; - break; - - case PNK_LABEL: - MOZ_ASSERT(cur->isArity(PN_NAME)); if (!resolve(cur->expr(), prefix)) return false; break; case PNK_LEXICALSCOPE: case PNK_NAME: MOZ_ASSERT(cur->isArity(PN_NAME)); if (!resolve(cur->maybeExpr(), prefix)) @@ -791,17 +774,16 @@ class NameResolver break; // Kinds that should be handled by parent node resolution. case PNK_IMPORT_SPEC: // by PNK_IMPORT_SPEC_LIST case PNK_EXPORT_SPEC: // by PNK_EXPORT_SPEC_LIST case PNK_CALLSITEOBJ: // by PNK_TAGGED_TEMPLATE case PNK_CLASSNAMES: // by PNK_CLASS - case PNK_POSHOLDER: // by PNK_NEWTARGET, PNK_DOT MOZ_CRASH("should have been handled by a parent node"); case PNK_LIMIT: // invalid sentinel value MOZ_CRASH("invalid node kind"); } nparents--; return true;
--- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -209,45 +209,49 @@ PushNodeChildren(ParseNode* pn, NodeStac case PNK_GENERATOR: case PNK_NUMBER: case PNK_BREAK: case PNK_CONTINUE: case PNK_DEBUGGER: case PNK_EXPORT_BATCH_SPEC: case PNK_OBJECT_PROPERTY_NAME: case PNK_FRESHENBLOCK: - case PNK_POSHOLDER: + case PNK_SUPERPROP: + case PNK_NEWTARGET: MOZ_ASSERT(pn->isArity(PN_NULLARY)); MOZ_ASSERT(!pn->isUsed(), "handle non-trivial cases separately"); MOZ_ASSERT(!pn->isDefn(), "handle non-trivial cases separately"); return PushResult::Recyclable; // Nodes with a single non-null child. case PNK_TYPEOFNAME: case PNK_TYPEOFEXPR: case PNK_VOID: case PNK_NOT: case PNK_BITNOT: case PNK_THROW: case PNK_DELETENAME: case PNK_DELETEPROP: + case PNK_DELETESUPERPROP: case PNK_DELETEELEM: + case PNK_DELETESUPERELEM: case PNK_DELETEEXPR: case PNK_POS: case PNK_NEG: case PNK_PREINCREMENT: case PNK_POSTINCREMENT: case PNK_PREDECREMENT: case PNK_POSTDECREMENT: case PNK_COMPUTED_NAME: case PNK_ARRAYPUSH: case PNK_SPREAD: case PNK_MUTATEPROTO: case PNK_EXPORT: case PNK_EXPORT_DEFAULT: + case PNK_SUPERELEM: return PushUnaryNodeChild(pn, stack); // Nodes with a single nullable child. case PNK_SEMI: { MOZ_ASSERT(pn->isArity(PN_UNARY)); if (pn->pn_kid) stack->push(pn->pn_kid); return PushResult::Recyclable; @@ -276,17 +280,16 @@ PushNodeChildren(ParseNode* pn, NodeStac case PNK_COLON: case PNK_CASE: case PNK_SHORTHAND: case PNK_DOWHILE: case PNK_WHILE: case PNK_SWITCH: case PNK_LETBLOCK: case PNK_CLASSMETHOD: - case PNK_NEWTARGET: case PNK_FOR: { MOZ_ASSERT(pn->isArity(PN_BINARY)); stack->push(pn->pn_left); stack->push(pn->pn_right); return PushResult::Recyclable; } // Named class expressions do not have outer binding nodes @@ -1098,20 +1101,17 @@ NameNode::dump(int indent) if (pn_atom->hasLatin1Chars()) DumpName(pn_atom->latin1Chars(nogc), pn_atom->length()); else DumpName(pn_atom->twoByteChars(nogc), pn_atom->length()); } if (isKind(PNK_DOT)) { fputc(' ', stderr); - if (as<PropertyAccess>().isSuper()) - fprintf(stderr, "super"); - else - DumpParseTree(expr(), indent + 2); + DumpParseTree(expr(), indent + 2); fputc(')', stderr); } return; } MOZ_ASSERT(!isUsed()); const char* name = parseNodeNames[getKind()]; if (isUsed())
--- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -133,17 +133,19 @@ class PackedScopeCoordinate F(CONST) \ F(GLOBALCONST) \ F(WITH) \ F(RETURN) \ F(NEW) \ /* Delete operations. These must be sequential. */ \ F(DELETENAME) \ F(DELETEPROP) \ + F(DELETESUPERPROP) \ F(DELETEELEM) \ + F(DELETESUPERELEM) \ F(DELETEEXPR) \ F(TRY) \ F(CATCH) \ F(CATCHLIST) \ F(THROW) \ F(DEBUGGER) \ F(GENERATOR) \ F(YIELD) \ @@ -169,18 +171,19 @@ class PackedScopeCoordinate F(FRESHENBLOCK) \ F(ARGSBODY) \ F(SPREAD) \ F(MUTATEPROTO) \ F(CLASS) \ F(CLASSMETHOD) \ F(CLASSMETHODLIST) \ F(CLASSNAMES) \ + F(SUPERPROP) \ + F(SUPERELEM) \ F(NEWTARGET) \ - F(POSHOLDER) \ \ /* Unary operators. */ \ F(TYPEOFNAME) \ F(TYPEOFEXPR) \ F(VOID) \ F(NOT) \ F(BITNOT) \ \ @@ -424,16 +427,17 @@ IsDeleteKind(ParseNodeKind kind) * PNK_POSTINCREMENT, * PNK_PREDECREMENT, * PNK_POSTDECREMENT * PNK_NEW list pn_head: list of ctor, arg1, arg2, ... argN * pn_count: 1 + N (where N is number of args) * ctor is a MEMBER expr * PNK_DELETENAME unary pn_kid: PNK_NAME expr * PNK_DELETEPROP unary pn_kid: PNK_DOT expr + * PNK_DELETESUPERPROP unary pn_kid: PNK_SUPERPROP expr * PNK_DELETEELEM unary pn_kid: PNK_ELEM expr * PNK_DELETESUPERELEM unary pn_kid: PNK_SUPERELEM expr * PNK_DELETEEXPR unary pn_kid: MEMBER expr that's evaluated, then the * overall delete evaluates to true; can't be a kind * for a more-specific PNK_DELETE* unless constant * folding (or a similar parse tree manipulation) has * occurred * PNK_DOT name pn_expr: MEMBER expr to left of . @@ -1314,43 +1318,27 @@ class PropertyAccess : public ParseNode ParseNode& expression() const { return *pn_u.name.expr; } PropertyName& name() const { return *pn_u.name.atom->asPropertyName(); } - - bool isSuper() const { - // PNK_POSHOLDER cannot result from any expression syntax. - return expression().isKind(PNK_POSHOLDER); - } }; class PropertyByValue : public ParseNode { public: PropertyByValue(ParseNode* lhs, ParseNode* propExpr, uint32_t begin, uint32_t end) : ParseNode(PNK_ELEM, JSOP_NOP, PN_BINARY, TokenPos(begin, end)) { pn_u.binary.left = lhs; pn_u.binary.right = propExpr; } - - static bool test(const ParseNode& node) { - bool match = node.isKind(PNK_ELEM); - MOZ_ASSERT_IF(match, node.isArity(PN_BINARY)); - return match; - } - - bool isSuper() const { - // Like PropertyAccess above, PNK_POSHOLDER is "good enough". - return pn_left->isKind(PNK_POSHOLDER); - } }; /* * A CallSiteNode represents the implicit call site object argument in a TaggedTemplate. */ struct CallSiteNode : public ListNode { explicit CallSiteNode(uint32_t begin): ListNode(PNK_CALLSITEOBJ, TokenPos(begin, begin + 1)) {} @@ -1453,16 +1441,48 @@ struct ClassNode : public TernaryNode { return list; } ObjectBox* scopeObject() const { MOZ_ASSERT(pn_kid3->is<LexicalScopeNode>()); return pn_kid3->pn_objbox; } }; +struct SuperProperty : public NullaryNode { + SuperProperty(JSAtom* atom, const TokenPos& pos) + : NullaryNode(PNK_SUPERPROP, JSOP_NOP, pos, atom) + { } + + static bool test(const ParseNode& node) { + bool match = node.isKind(PNK_SUPERPROP); + MOZ_ASSERT_IF(match, node.isArity(PN_NULLARY)); + return match; + } + + JSAtom* propName() const { + return pn_atom; + } +}; + +struct SuperElement : public UnaryNode { + SuperElement(ParseNode* expr, const TokenPos& pos) + : UnaryNode(PNK_SUPERELEM, JSOP_NOP, pos, expr) + { } + + static bool test(const ParseNode& node) { + bool match = node.isKind(PNK_SUPERELEM); + MOZ_ASSERT_IF(match, node.isArity(PN_UNARY)); + return match; + } + + ParseNode* expr() const { + return pn_kid; + } +}; + #ifdef DEBUG void DumpParseTree(ParseNode* pn, int indent = 0); #endif /* * js::Definition is a degenerate subtype of the PN_FUNC and PN_NAME variants * of js::ParseNode, allocated only for function, var, const, and let * declarations that define truly lexical bindings. This means that a child of
--- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -4960,17 +4960,19 @@ Parser<FullParseHandler>::isValidForStat { if (isForDecl) return pn1->pn_count < 2 && !pn1->isKind(PNK_CONST); switch (pn1->getKind()) { case PNK_ARRAY: case PNK_CALL: case PNK_DOT: + case PNK_SUPERPROP: case PNK_ELEM: + case PNK_SUPERELEM: case PNK_NAME: case PNK_OBJECT: return true; default: return false; } } @@ -8243,16 +8245,19 @@ Parser<ParseHandler>::memberExpr(YieldHa InvokedPrediction invoked) { MOZ_ASSERT(tokenStream.isCurrentTokenType(tt)); Node lhs; JS_CHECK_RECURSION(context, return null()); + bool isSuper = false; + uint32_t superBegin = pos().begin; + /* Check for new expression first. */ if (tt == TOK_NEW) { uint32_t newBegin = pos().begin; // Make sure this wasn't a |new.target| in disguise. Node newTarget; if (!tryNewTarget(newTarget)) return null(); if (newTarget) { @@ -8277,69 +8282,76 @@ Parser<ParseHandler>::memberExpr(YieldHa bool isSpread = false; if (!argumentList(yieldHandling, lhs, &isSpread)) return null(); if (isSpread) handler.setOp(lhs, JSOP_SPREADNEW); } } } else if (tt == TOK_SUPER) { - lhs = handler.newSuperBase(pos(), context); - if (!lhs) - return null(); + lhs = null(); + isSuper = true; } else { lhs = primaryExpr(yieldHandling, tt, invoked); if (!lhs) return null(); } - MOZ_ASSERT_IF(handler.isSuperBase(lhs, context), tokenStream.isCurrentTokenType(TOK_SUPER)); - while (true) { if (!tokenStream.getToken(&tt)) return null(); if (tt == TOK_EOF) break; Node nextMember; if (tt == TOK_DOT) { if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName)) return null(); if (tt == TOK_NAME) { PropertyName* field = tokenStream.currentName(); - if (handler.isSuperBase(lhs, context) && !checkAndMarkSuperScope()) { - report(ParseError, false, null(), JSMSG_BAD_SUPERPROP, "property"); - return null(); + if (isSuper) { + isSuper = false; + if (!checkAndMarkSuperScope()) { + report(ParseError, false, null(), JSMSG_BAD_SUPERPROP, "property"); + return null(); + } + nextMember = handler.newSuperProperty(field, TokenPos(superBegin, pos().end)); + } else { + nextMember = handler.newPropertyAccess(lhs, field, pos().end); } - nextMember = handler.newPropertyAccess(lhs, field, pos().end); if (!nextMember) return null(); } else { report(ParseError, false, null(), JSMSG_NAME_AFTER_DOT); return null(); } } else if (tt == TOK_LB) { Node propExpr = expr(InAllowed, yieldHandling); if (!propExpr) return null(); MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX); - if (handler.isSuperBase(lhs, context) && !checkAndMarkSuperScope()) { + if (isSuper) { + isSuper = false; + if (!checkAndMarkSuperScope()) { report(ParseError, false, null(), JSMSG_BAD_SUPERPROP, "member"); return null(); + } + nextMember = handler.newSuperElement(propExpr, TokenPos(superBegin, pos().end)); + } else { + nextMember = handler.newPropertyByValue(lhs, propExpr, pos().end); } - nextMember = handler.newPropertyByValue(lhs, propExpr, pos().end); if (!nextMember) return null(); } else if ((allowCallSyntax && tt == TOK_LP) || tt == TOK_TEMPLATE_HEAD || tt == TOK_NO_SUBS_TEMPLATE) { - if (handler.isSuperBase(lhs, context)) { + if (isSuper) { // For now... report(ParseError, false, null(), JSMSG_BAD_SUPER); return null(); } nextMember = tt == TOK_LP ? handler.newCall() : handler.newTaggedTemplate(); if (!nextMember) return null(); @@ -8393,26 +8405,28 @@ Parser<ParseHandler>::memberExpr(YieldHa op = JSOP_SPREADCALL; } } else { if (!taggedTemplate(yieldHandling, nextMember, tt)) return null(); } handler.setOp(nextMember, op); } else { + if (isSuper) { + report(ParseError, false, null(), JSMSG_BAD_SUPER); + return null(); + } tokenStream.ungetToken(); - if (handler.isSuperBase(lhs, context)) - break; return lhs; } lhs = nextMember; } - if (handler.isSuperBase(lhs, context)) { + if (isSuper) { report(ParseError, false, null(), JSMSG_BAD_SUPER); return null(); } return lhs; } template <typename ParseHandler> @@ -8930,24 +8944,19 @@ Parser<ParseHandler>::methodDefinition(Y } template <typename ParseHandler> bool Parser<ParseHandler>::tryNewTarget(Node &newTarget) { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NEW)); + uint32_t begin = pos().begin; newTarget = null(); - Node newHolder = handler.newPosHolder(pos()); - if (!newHolder) - return false; - - uint32_t begin = pos().begin; - // |new| expects to look for an operand, so we will honor that. TokenKind next; if (!tokenStream.getToken(&next, TokenStream::Operand)) return false; // Don't unget the token, since lookahead cannot handle someone calling // getToken() with a different modifier. Callers should inspect currentToken(). if (next != TOK_DOT) @@ -8961,21 +8970,17 @@ Parser<ParseHandler>::tryNewTarget(Node return false; } if (!pc->sc->allowNewTarget()) { reportWithOffset(ParseError, false, begin, JSMSG_BAD_NEWTARGET); return false; } - Node targetHolder = handler.newPosHolder(pos()); - if (!targetHolder) - return false; - - newTarget = handler.newNewTarget(newHolder, targetHolder); + newTarget = handler.newNewTarget(TokenPos(begin, pos().end)); return !!newTarget; } template <typename ParseHandler> typename ParseHandler::Node Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TokenKind tt, InvokedPrediction invoked) {
--- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -42,16 +42,19 @@ class SyntaxParseHandler NodeGetProp, NodeStringExprStatement, NodeReturn, NodeHoistableDeclaration, NodeBreak, NodeThrow, NodeEmptyStatement, + NodeSuperProperty, + NodeSuperElement, + // This is needed for proper assignment-target handling. ES6 formally // requires function calls *not* pass IsValidSimpleAssignmentTarget, // but at last check there were still sites with |f() = 5| and similar // in code not actually executed (or at least not executed enough to be // noticed). NodeFunctionCall, // Nodes representing *parenthesized* IsValidSimpleAssignmentTarget @@ -124,26 +127,23 @@ class SyntaxParseHandler // equality checks. (Think |if (x = y)| versus |if (x == y)|.) Thus // we need this to treat |if (x = y)| as a possible typo and // |if ((x = y))| as a deliberate assignment within a condition. // // (Technically this isn't needed, as these are *only* extraWarnings // warnings, and parsing with that option disables syntax parsing. But // it seems best to be consistent, and perhaps the syntax parser will // eventually enforce extraWarnings and will require this then.) - NodeUnparenthesizedAssignment, - - // This node is necessary to determine if the LHS of a property access is - // super related. - NodeSuperBase + NodeUnparenthesizedAssignment }; typedef Definition::Kind DefinitionNode; bool isPropertyAccess(Node node) { - return node == NodeDottedProperty || node == NodeElement; + return node == NodeDottedProperty || node == NodeElement || + node == NodeSuperProperty || node == NodeSuperElement; } bool isFunctionCall(Node node) { // Note: super() is a special form, *not* a function call. return node == NodeFunctionCall; } static bool isUnparenthesizedDestructuringPattern(Node node) { @@ -269,19 +269,24 @@ class SyntaxParseHandler void addArrayElement(Node literal, Node element) { } Node newCall() { return NodeFunctionCall; } Node newTaggedTemplate() { return NodeGeneric; } Node newObjectLiteral(uint32_t begin) { return NodeUnparenthesizedObject; } Node newClassMethodList(uint32_t begin) { return NodeGeneric; } - Node newNewTarget(Node newHolder, Node targetHolder) { return NodeGeneric; } - Node newPosHolder(const TokenPos& pos) { return NodeGeneric; } - Node newSuperBase(const TokenPos& pos, ExclusiveContext* cx) { return NodeSuperBase; } + Node newSuperProperty(PropertyName* prop, const TokenPos& pos) { + return NodeSuperProperty; + } + + Node newSuperElement(Node expr, const TokenPos& pos) { + return NodeSuperElement; + } + Node newNewTarget(const TokenPos& pos) { return NodeGeneric; } bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; } bool addPropertyDefinition(Node literal, Node name, Node expr) { return true; } bool addShorthand(Node literal, Node name, Node expr) { return true; } bool addObjectMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; } bool addClassMethodDefinition(Node literal, Node name, Node fn, JSOp op, bool isStatic) { return true; } Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeUnparenthesizedYieldExpr; } Node newYieldStarExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; } @@ -426,22 +431,16 @@ class SyntaxParseHandler return node == NodeReturn; } bool isStatementPermittedAfterReturnStatement(Node pn) { return pn == NodeHoistableDeclaration || pn == NodeBreak || pn == NodeThrow || pn == NodeEmptyStatement; } - bool isSuperBase(Node pn, ExclusiveContext* cx) { - // While NodePosHolder is used in other places than just as super-base, - // it is unique enough for our purposes. - return pn == NodeSuperBase; - } - void setOp(Node pn, JSOp op) {} void setBlockId(Node pn, unsigned blockid) {} void setFlag(Node pn, unsigned flag) {} void setListFlag(Node pn, unsigned flag) {} MOZ_WARN_UNUSED_RESULT Node parenthesize(Node node) { // A number of nodes have different behavior upon parenthesization, but // only in some circumstances. Convert these nodes to special // parenthesized forms.
--- a/js/src/jit-test/tests/modules/export-declaration.js +++ b/js/src/jit-test/tests/modules/export-declaration.js @@ -33,17 +33,17 @@ functionDeclaration = (id, params, body) params: params, defaults: [], body: body, rest: null, generator: false }); classDeclaration = (name) => Pattern({ type: "ClassStatement", - id: name + name: name }); variableDeclaration = (decls) => Pattern({ type: "VariableDeclaration", kind: "var", declarations: decls }); constDeclaration = (decls) => Pattern({ type: "VariableDeclaration",
--- a/js/src/jsast.tbl +++ b/js/src/jsast.tbl @@ -33,18 +33,17 @@ ASTDEF(AST_ARROW_EXPR, "Arrow ASTDEF(AST_ARRAY_EXPR, "ArrayExpression", "arrayExpression") ASTDEF(AST_SPREAD_EXPR, "SpreadExpression", "spreadExpression") ASTDEF(AST_OBJECT_EXPR, "ObjectExpression", "objectExpression") ASTDEF(AST_THIS_EXPR, "ThisExpression", "thisExpression") ASTDEF(AST_COMP_EXPR, "ComprehensionExpression", "comprehensionExpression") ASTDEF(AST_GENERATOR_EXPR, "GeneratorExpression", "generatorExpression") ASTDEF(AST_YIELD_EXPR, "YieldExpression", "yieldExpression") ASTDEF(AST_CLASS_EXPR, "ClassExpression", "classExpression") -ASTDEF(AST_METAPROPERTY, "MetaProperty", "metaProperty") -ASTDEF(AST_SUPER, "Super", "super") +ASTDEF(AST_NEWTARGET_EXPR, "NewTargetExpression", "newTargetExpression") ASTDEF(AST_EMPTY_STMT, "EmptyStatement", "emptyStatement") ASTDEF(AST_BLOCK_STMT, "BlockStatement", "blockStatement") ASTDEF(AST_EXPR_STMT, "ExpressionStatement", "expressionStatement") ASTDEF(AST_LAB_STMT, "LabeledStatement", "labeledStatement") ASTDEF(AST_IF_STMT, "IfStatement", "ifStatement") ASTDEF(AST_SWITCH_STMT, "SwitchStatement", "switchStatement") ASTDEF(AST_WHILE_STMT, "WhileStatement", "whileStatement")
--- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -71,16 +71,17 @@ const char js_finally_str[] = "f const char js_for_str[] = "for"; const char js_getter_str[] = "getter"; const char js_if_str[] = "if"; const char js_implements_str[] = "implements"; const char js_import_str[] = "import"; const char js_in_str[] = "in"; const char js_instanceof_str[] = "instanceof"; const char js_interface_str[] = "interface"; +const char js_new_str[] = "new"; const char js_package_str[] = "package"; const char js_private_str[] = "private"; const char js_protected_str[] = "protected"; const char js_public_str[] = "public"; const char js_send_str[] = "send"; const char js_setter_str[] = "setter"; const char js_switch_str[] = "switch"; const char js_this_str[] = "this";
--- a/js/src/jsatom.h +++ b/js/src/jsatom.h @@ -171,16 +171,17 @@ extern const char js_finally_str[]; extern const char js_for_str[]; extern const char js_getter_str[]; extern const char js_if_str[]; extern const char js_implements_str[]; extern const char js_import_str[]; extern const char js_in_str[]; extern const char js_instanceof_str[]; extern const char js_interface_str[]; +extern const char js_new_str[]; extern const char js_package_str[]; extern const char js_private_str[]; extern const char js_protected_str[]; extern const char js_public_str[]; extern const char js_send_str[]; extern const char js_setter_str[]; extern const char js_static_str[]; extern const char js_super_str[];
--- a/js/src/tests/ecma_6/Class/superPropDVG.js +++ b/js/src/tests/ecma_6/Class/superPropDVG.js @@ -2,24 +2,20 @@ var test = ` class testNonExistent { constructor() { super["prop"](); } } -// Should fold to super.prop -assertThrownErrorContains(() => new testNonExistent(), 'super.prop'); +assertThrownErrorContains(() => new testNonExistent(), 'super["prop"]'); var ol = { testNonExistent() { super.prop(); } }; assertThrownErrorContains(() => ol.testNonExistent(), "super.prop"); -var olElem = { testNonExistent() { var prop = "prop"; super[prop](); } }; -assertThrownErrorContains(() => olElem.testNonExistent(), "super[prop]"); - `; if (classesEnabled()) eval(test); if (typeof reportCompare === 'function') reportCompare(0,0,"OK");
--- a/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js +++ b/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js @@ -119,32 +119,32 @@ function catchClause(id, guard, body) { function tryStmt(body, guarded, unguarded, fin) { return Pattern({ type: "TryStatement", block: body, guardedHandlers: guarded, handler: unguarded, finalizer: fin }); } function letStmt(head, body) { return Pattern({ type: "LetStatement", head: head, body: body }); } function superProp(id) { - return dotExpr(Pattern({ type: "Super" }), id); + return dotExpr(ident("super"), id); } function superElem(id) { return memExpr(ident("super"), id); } function classStmt(id, heritage, body) { return Pattern({ type: "ClassStatement", - id: id, - superClass: heritage, + name: id, + heritage: heritage, body: body}); } function classExpr(id, heritage, body) { return Pattern({ type: "ClassExpression", - id: id, - superClass: heritage, + name: id, + heritage: heritage, body: body}); } function classMethod(id, body, kind, static) { return Pattern({ type: "ClassMethod", name: id, body: body, kind: kind, static: static }); @@ -165,25 +165,19 @@ function genFunExpr(id, args, body) { generator: true }); } function arrowExpr(args, body) { return Pattern({ type: "ArrowFunctionExpression", params: args, body: body }); } -function metaProperty(meta, property) { - return Pattern({ type: "MetaProperty", - meta: meta, - property: property }); +function newTarget() { + return Pattern({ type: "NewTargetExpression" }); } -function newTarget() { - return metaProperty(ident("new"), ident("target")); -} - function unExpr(op, arg) { return Pattern({ type: "UnaryExpression", operator: op, argument: arg }); } function binExpr(op, left, right) { return Pattern({ type: "BinaryExpression", operator: op, left: left, right: right }); } function aExpr(op, left, right) { return Pattern({ type: "AssignmentExpression", operator: op, left: left, right: right });
--- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -145,17 +145,16 @@ macro(minimumFractionDigits, minimumFractionDigits, "minimumFractionDigits") \ macro(minimumIntegerDigits, minimumIntegerDigits, "minimumIntegerDigits") \ macro(minimumSignificantDigits, minimumSignificantDigits, "minimumSignificantDigits") \ macro(missingArguments, missingArguments, "missingArguments") \ macro(module, module, "module") \ macro(multiline, multiline, "multiline") \ macro(name, name, "name") \ macro(NaN, NaN, "NaN") \ - macro(new, new_, "new") \ macro(next, next, "next") \ macro(NFC, NFC, "NFC") \ macro(NFD, NFD, "NFD") \ macro(NFKC, NFKC, "NFKC") \ macro(NFKD, NFKD, "NFKD") \ macro(nonincrementalReason, nonincrementalReason, "nonincrementalReason") \ macro(noStack, noStack, "noStack") \ macro(noSuchMethod, noSuchMethod, "__noSuchMethod__") \
--- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -764,17 +764,17 @@ 1234567890123456789012345678901234567890 * Invokes 'callee' as a constructor with 'this' and 'args', pushes return * value onto the stack. * Category: Statements * Type: Function * Operands: uint16_t argc * Stack: callee, this, args[0], ..., args[argc-1], newTarget => rval * nuses: (argc+3) */ \ - macro(JSOP_NEW, 82, "new", NULL, 3, -1, 1, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) \ + macro(JSOP_NEW, 82, js_new_str, NULL, 3, -1, 1, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) \ /* * Pushes newly created object onto the stack with provided [[Prototype]]. * * Category: Literals * Type: Object * Operands: * Stack: proto => obj */ \