☠☠ backed out by 26951c3aa267 ☠ ☠ | |
author | Eric Faust <efaustbmo@mozilla.com> |
Wed, 02 Sep 2015 15:09:06 -0700 | |
changeset 260643 | 8534fa84c4902fe0caad46697c7ef15cb2d19928 |
parent 260642 | 4f7c8a5677070cfd021dd205c90d60cebc449ee2 |
child 260644 | ff553f6079f458fd43fc59dcbdae2d10879d2725 |
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) |
reviewers | Waldo |
bugs | 1168992 |
milestone | 43.0a1 |
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 @@ -3077,17 +3077,16 @@ ASTSerializer::expression(ParseNode* pn, return leftAssociate(pn, dst); case PNK_POW: return rightAssociate(pn, dst); case PNK_DELETENAME: case PNK_DELETEPROP: 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: { @@ -3160,32 +3159,29 @@ ASTSerializer::expression(ParseNode* pn, } 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); - return expression(pn->pn_left, &left) && - expression(pn->pn_right, &right) && + + 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) && 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));
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -2008,17 +2008,16 @@ BytecodeEmitter::checkSideEffects(ParseN 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_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)); @@ -2116,22 +2115,16 @@ 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; @@ -2756,24 +2749,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_SUPERELEM)); + MOZ_ASSERT(pn->isKind(PNK_ELEM) && pn->as<PropertyByValue>().isSuper()); // 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_kid)) + if (!emitTree(pn->pn_right)) 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)) @@ -2833,110 +2826,88 @@ BytecodeEmitter::emitSuperElemOp(ParseNo return true; } bool BytecodeEmitter::emitElemIncDec(ParseNode* pn) { MOZ_ASSERT(pn->pn_kid->isKind(PNK_ELEM)); - if (!emitElemOperands(pn->pn_kid, JSOP_GETELEM)) - return false; + 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; + } bool post; JSOp binop = GetIncDecInfo(pn->getKind(), &post); - /* - * 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 + 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 return false; if (post) { - 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 + 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 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); @@ -3777,29 +3748,25 @@ BytecodeEmitter::emitDestructuringLHS(Pa 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. - 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; + 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; + } break; } case PNK_CALL: MOZ_ASSERT(target->pn_xflags & PNX_SETCALL); if (!emitTree(target)) return false; @@ -4428,26 +4395,27 @@ BytecodeEmitter::emitAssignment(ParseNod return false; offset += 1; } if (!makeAtomIndex(lhs->pn_atom, &atomIndex)) return false; break; case PNK_ELEM: MOZ_ASSERT(lhs->isArity(PN_BINARY)); - 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; + 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; + } break; case PNK_ARRAY: case PNK_OBJECT: break; case PNK_CALL: MOZ_ASSERT(lhs->pn_xflags & PNX_SETCALL); if (!emitTree(lhs)) return false; @@ -4510,32 +4478,35 @@ BytecodeEmitter::emitAssignment(ParseNod return false; bool isLength = (lhs->pn_atom == cx->names().length); getOp = isLength ? JSOP_LENGTH : JSOP_GETPROP; } if (!emitIndex32(getOp, atomIndex)) return false; break; } - case PNK_ELEM: - if (!emit1(JSOP_DUP2)) - return false; - if (!emitElemOpBase(JSOP_GETELEM)) + 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)) 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)) @@ -4597,24 +4568,19 @@ BytecodeEmitter::emitAssignment(ParseNod 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 = 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; + JSOp setOp = lhs->as<PropertyByValue>().isSuper() ? + sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER : + sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM; if (!emit1(setOp)) return false; break; } case PNK_ARRAY: case PNK_OBJECT: if (!emitDestructuringOps(lhs)) return false; @@ -6503,45 +6469,37 @@ 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 @@ -6699,26 +6657,27 @@ BytecodeEmitter::emitCallOrNew(ParseNode if (!emitSuperPropOp(pn2, JSOP_GETPROP_SUPER, /* isCall = */ callop)) return false; } else { if (!emitPropOp(pn2, callop ? JSOP_CALLPROP : JSOP_GETPROP)) 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_SUPERELEM: - if (!emitSuperElemOp(pn2, JSOP_GETELEM_SUPER, /* isCall = */ callop)) - return false; + 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; + } + } 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 @@ -6870,20 +6829,16 @@ BytecodeEmitter::emitIncOrDec(ParseNode* case PNK_DOT: if (!emitPropIncDec(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); @@ -7841,41 +7796,38 @@ BytecodeEmitter::emitTree(ParseNode* pn) case PNK_DELETEPROP: ok = emitDeleteProperty(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; } break; case PNK_ELEM: - ok = emitElemOp(pn, JSOP_GETELEM); - break; - - case PNK_SUPERELEM: - if (!emitSuperElemOp(pn, JSOP_GETELEM_SUPER)) - return false; + if (pn->as<PropertyByValue>().isSuper()) { + if (!emitSuperElemOp(pn, JSOP_GETELEM_SUPER)) + return false; + } else { + if (!emitElemOp(pn, JSOP_GETELEM)) + return false; + } break; case PNK_NEW: case PNK_TAGGED_TEMPLATE: case PNK_CALL: case PNK_GENEXP: ok = emitCallOrNew(pn); break;
--- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -550,17 +550,16 @@ struct BytecodeEmitter bool emitReturn(ParseNode* pn); bool emitStatement(ParseNode* pn); bool emitStatementList(ParseNode* pn, ptrdiff_t top); bool emitDeleteName(ParseNode* pn); bool emitDeleteProperty(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); @@ -608,15 +607,14 @@ struct BytecodeEmitter 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); 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 @@ -330,17 +330,16 @@ ContainsHoistedDeclaration(ExclusiveCont case PNK_TYPEOFNAME: case PNK_TYPEOFEXPR: case PNK_VOID: case PNK_NOT: case PNK_BITNOT: case PNK_DELETENAME: case PNK_DELETEPROP: 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: @@ -408,17 +407,16 @@ 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_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"); @@ -626,35 +624,33 @@ FoldDeleteExpr(ExclusiveContext* cx, Par return true; } static bool FoldDeleteElement(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser, bool inGenexpLambda) { - MOZ_ASSERT(node->isKind(PNK_DELETEELEM) || node->isKind(PNK_DELETESUPERELEM)); + MOZ_ASSERT(node->isKind(PNK_DELETEELEM)); MOZ_ASSERT(node->isArity(PN_UNARY)); - MOZ_ASSERT(node->pn_kid->isKind(PNK_ELEM) || node->pn_kid->isKind(PNK_SUPERELEM)); + MOZ_ASSERT(node->pn_kid->isKind(PNK_ELEM)); 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. - if (node->isKind(PNK_DELETEELEM)) { - MOZ_ASSERT(expr->isKind(PNK_ELEM) || expr->isKind(PNK_DOT)); - if (expr->isKind(PNK_DOT)) - node->setKind(PNK_DELETEPROP); - } + 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) { @@ -1743,17 +1739,16 @@ 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: return FoldDeleteProperty(cx, pn, parser, inGenexpLambda); case PNK_CONDITIONAL: return FoldConditional(cx, pnp, parser, inGenexpLambda); @@ -1774,17 +1769,16 @@ 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));
--- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -68,19 +68,17 @@ 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) { - if (node->isKind(PNK_DOT) || node->isKind(PNK_ELEM)) - return true; - return node->isKind(PNK_SUPERELEM); + return node->isKind(PNK_DOT) || node->isKind(PNK_ELEM); } bool isFunctionCall(ParseNode* node) { // Note: super() is a special form, *not* a function call. return node->isKind(PNK_CALL); } static bool isUnparenthesizedDestructuringPattern(ParseNode* node) { @@ -223,18 +221,16 @@ class FullParseHandler 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_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); @@ -339,19 +335,16 @@ 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* newSuperElement(ParseNode* expr, const TokenPos& pos) { - return new_<SuperElement>(expr, 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* newSuperBase(const TokenPos& pos, ExclusiveContext* cx) { ParseNode* node = newPosHolder(pos);
--- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -395,29 +395,27 @@ class NameResolver case PNK_TYPEOFEXPR: case PNK_VOID: case PNK_NOT: case PNK_BITNOT: case PNK_THROW: case PNK_DELETENAME: case PNK_DELETEPROP: 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. @@ -438,33 +436,40 @@ 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;
--- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -225,31 +225,29 @@ PushNodeChildren(ParseNode* pn, NodeStac case PNK_TYPEOFEXPR: case PNK_VOID: case PNK_NOT: case PNK_BITNOT: case PNK_THROW: case PNK_DELETENAME: case PNK_DELETEPROP: 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;
--- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -134,17 +134,16 @@ class PackedScopeCoordinate F(GLOBALCONST) \ F(WITH) \ F(RETURN) \ F(NEW) \ /* Delete operations. These must be sequential. */ \ F(DELETENAME) \ F(DELETEPROP) \ F(DELETEELEM) \ - F(DELETESUPERELEM) \ F(DELETEEXPR) \ F(TRY) \ F(CATCH) \ F(CATCHLIST) \ F(THROW) \ F(DEBUGGER) \ F(GENERATOR) \ F(YIELD) \ @@ -170,17 +169,16 @@ class PackedScopeCoordinate F(FRESHENBLOCK) \ F(ARGSBODY) \ F(SPREAD) \ F(MUTATEPROTO) \ F(CLASS) \ F(CLASSMETHOD) \ F(CLASSMETHODLIST) \ F(CLASSNAMES) \ - F(SUPERELEM) \ F(NEWTARGET) \ F(POSHOLDER) \ \ /* Unary operators. */ \ F(TYPEOFNAME) \ F(TYPEOFEXPR) \ F(VOID) \ F(NOT) \ @@ -1332,16 +1330,27 @@ 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)) {} @@ -1444,32 +1453,16 @@ struct ClassNode : public TernaryNode { return list; } ObjectBox* scopeObject() const { MOZ_ASSERT(pn_kid3->is<LexicalScopeNode>()); return pn_kid3->pn_objbox; } }; -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 @@ -4961,17 +4961,16 @@ 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_ELEM: - case PNK_SUPERELEM: case PNK_NAME: case PNK_OBJECT: return true; default: return false; } } @@ -8244,18 +8243,16 @@ Parser<ParseHandler>::memberExpr(YieldHa InvokedPrediction invoked) { MOZ_ASSERT(tokenStream.isCurrentTokenType(tt)); Node lhs; JS_CHECK_RECURSION(context, return null()); - 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) { @@ -8321,25 +8318,21 @@ Parser<ParseHandler>::memberExpr(YieldHa } } 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)) { - if (!checkAndMarkSuperScope()) { + if (handler.isSuperBase(lhs, context) && !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)) { // For now...
--- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -42,18 +42,16 @@ class SyntaxParseHandler NodeGetProp, NodeStringExprStatement, NodeReturn, NodeHoistableDeclaration, NodeBreak, NodeThrow, NodeEmptyStatement, - 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 @@ -135,18 +133,17 @@ class SyntaxParseHandler // This node is necessary to determine if the LHS of a property access is // super related. NodeSuperBase }; typedef Definition::Kind DefinitionNode; bool isPropertyAccess(Node node) { - return node == NodeDottedProperty || node == NodeElement || - node == NodeSuperElement; + return node == NodeDottedProperty || node == NodeElement; } bool isFunctionCall(Node node) { // Note: super() is a special form, *not* a function call. return node == NodeFunctionCall; } static bool isUnparenthesizedDestructuringPattern(Node node) { @@ -272,19 +269,16 @@ 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 newSuperElement(Node expr, const TokenPos& pos) { - return NodeSuperElement; - } Node newNewTarget(Node newHolder, Node targetHolder) { return NodeGeneric; } Node newPosHolder(const TokenPos& pos) { return NodeGeneric; } Node newSuperBase(const TokenPos& pos, ExclusiveContext* cx) { return NodeSuperBase; } 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; }
--- a/js/src/tests/ecma_6/Class/superPropDVG.js +++ b/js/src/tests/ecma_6/Class/superPropDVG.js @@ -2,20 +2,24 @@ var test = ` class testNonExistent { constructor() { super["prop"](); } } -assertThrownErrorContains(() => new testNonExistent(), 'super["prop"]'); +// Should fold to 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");