From 4a9887c68512cea1872127453d49c92979608337 Mon Sep 17 00:00:00 2001 From: Eric Faust Date: Wed, 2 Sep 2015 15:09:06 -0700 Subject: [PATCH] Bug 1168992 - Part 4: Convert PNK_SUPERELEM to PNK_ELEM and fix reflection of super[elem]. (r=Waldo) --- js/src/builtin/ReflectParse.cpp | 24 +- js/src/frontend/BytecodeEmitter.cpp | 294 +++++++++------------- js/src/frontend/BytecodeEmitter.h | 2 - js/src/frontend/FoldConstants.cpp | 16 +- js/src/frontend/FullParseHandler.h | 9 +- js/src/frontend/NameFunctions.cpp | 11 +- js/src/frontend/ParseNode.cpp | 2 - js/src/frontend/ParseNode.h | 29 +-- js/src/frontend/Parser.cpp | 11 +- js/src/frontend/SyntaxParseHandler.h | 8 +- js/src/tests/ecma_6/Class/superPropDVG.js | 6 +- 11 files changed, 166 insertions(+), 246 deletions(-) diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index fec8b911108b..f6272cad602a 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -3082,7 +3082,6 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) case PNK_DELETENAME: case PNK_DELETEPROP: case PNK_DELETEELEM: - case PNK_DELETESUPERELEM: case PNK_DELETEEXPR: case PNK_TYPEOFNAME: case PNK_TYPEOFEXPR: @@ -3165,22 +3164,19 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) 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().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); diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 36853277fa87..93b9df35c452 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -2013,7 +2013,6 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) case PNK_DELETENAME: case PNK_DELETEPROP: case PNK_DELETEELEM: - case PNK_DELETESUPERELEM: MOZ_ASSERT(pn->isArity(PN_UNARY)); *answer = true; return true; @@ -2121,12 +2120,6 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) *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: @@ -2761,14 +2754,14 @@ BytecodeEmitter::emitElemOperands(ParseNode* pn, JSOp op) bool BytecodeEmitter::emitSuperElemOperands(ParseNode* pn, SuperElemOptions opts) { - MOZ_ASSERT(pn->isKind(PNK_SUPERELEM)); + MOZ_ASSERT(pn->isKind(PNK_ELEM) && pn->as().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 @@ -2838,99 +2831,77 @@ 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().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 + 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_DUP2)) // OBJ KEY OBJ KEY + if (!emit1(JSOP_POS)) // OBJ KEY N return false; - if (!emitElemOpBase(JSOP_GETELEM)) // OBJ KEY V + if (post && !emit1(JSOP_DUP)) // OBJ KEY N? N return false; - if (!emit1(JSOP_POS)) // OBJ KEY N + if (!emit1(JSOP_ONE)) // OBJ KEY N? N 1 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 + if (!emit1(binop)) // OBJ KEY N? N+1 return false; if (post) { - if (!emit2(JSOP_PICK, 3)) // KEY N N+1 OBJ + 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)) // N N+1 OBJ KEY + if (!emit2(JSOP_PICK, 3 + isSuper)) // N N+1 OBJ KEY return false; - if (!emit2(JSOP_PICK, 2)) // N OBJ KEY N+1 + if (!emit2(JSOP_PICK, 2 + isSuper)) // N OBJ KEY N+1 return false; } - JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM; - if (!emitElemOpBase(setOp)) // N? N+1 + 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 (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) { @@ -3782,19 +3753,15 @@ BytecodeEmitter::emitDestructuringLHS(ParseNode* target, VarEmitOption emitOptio // 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().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; } @@ -4433,16 +4400,17 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs) 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().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: @@ -4515,22 +4483,25 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs) 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)) + case PNK_ELEM: { + JSOp elemOp; + if (lhs->as().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_CALL: /* * We just emitted a JSOP_SETCALL (which will always throw) and @@ -4602,14 +4573,9 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs) 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().isSuper() ? + sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER : + sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM; if (!emit1(setOp)) return false; break; @@ -6508,34 +6474,26 @@ BytecodeEmitter::emitDeleteElement(ParseNode* node) ParseNode* elemExpr = node->pn_kid; MOZ_ASSERT(elemExpr->isKind(PNK_ELEM)); + if (elemExpr->as().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) { @@ -6704,17 +6662,18 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn) } break; case PNK_ELEM: - if (!emitElemOp(pn2, callop ? JSOP_CALLELEM : JSOP_GETELEM)) - return false; - if (callop) { - if (!emit1(JSOP_SWAP)) + if (pn2->as().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_SUPERELEM: - if (!emitSuperElemOp(pn2, JSOP_GETELEM_SUPER, /* isCall = */ callop)) - return false; - break; case PNK_FUNCTION: /* * Top level lambdas which are immediately invoked should be @@ -6875,10 +6834,6 @@ BytecodeEmitter::emitIncOrDec(ParseNode* pn) 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)) @@ -7846,10 +7801,6 @@ BytecodeEmitter::emitTree(ParseNode* pn) ok = emitDeleteElement(pn); break; - case PNK_DELETESUPERELEM: - ok = emitDeleteSuperElement(pn); - break; - case PNK_DELETEEXPR: ok = emitDeleteExpression(pn); break; @@ -7865,12 +7816,13 @@ BytecodeEmitter::emitTree(ParseNode* pn) break; case PNK_ELEM: - ok = emitElemOp(pn, JSOP_GETELEM); - break; - - case PNK_SUPERELEM: - if (!emitSuperElemOp(pn, JSOP_GETELEM_SUPER)) - return false; + if (pn->as().isSuper()) { + if (!emitSuperElemOp(pn, JSOP_GETELEM_SUPER)) + return false; + } else { + if (!emitElemOp(pn, JSOP_GETELEM)) + return false; + } break; case PNK_NEW: diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index da726491217b..339a41d613b5 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -555,7 +555,6 @@ struct BytecodeEmitter 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. @@ -613,7 +612,6 @@ struct BytecodeEmitter 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 */ diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index 5675d7c4961c..86589cb68c45 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -335,7 +335,6 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result) case PNK_DELETENAME: case PNK_DELETEPROP: case PNK_DELETEELEM: - case PNK_DELETESUPERELEM: case PNK_DELETEEXPR: case PNK_POS: case PNK_NEG: @@ -413,7 +412,6 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result) 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 " @@ -631,9 +629,9 @@ static bool FoldDeleteElement(ExclusiveContext* cx, ParseNode* node, Parser& 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)) @@ -645,11 +643,9 @@ FoldDeleteElement(ExclusiveContext* cx, ParseNode* node, Parser 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; } @@ -1748,7 +1744,6 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser& parser, bo return FoldDeleteExpr(cx, pn, parser, inGenexpLambda); case PNK_DELETEELEM: - case PNK_DELETESUPERELEM: return FoldDeleteElement(cx, pn, parser, inGenexpLambda); case PNK_DELETEPROP: @@ -1779,7 +1774,6 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser& parser, bo case PNK_MUTATEPROTO: case PNK_COMPUTED_NAME: case PNK_SPREAD: - case PNK_SUPERELEM: case PNK_EXPORT: case PNK_EXPORT_DEFAULT: case PNK_VOID: diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 906761541d2b..32d686e87f87 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -73,9 +73,7 @@ class FullParseHandler 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) { @@ -228,8 +226,6 @@ class FullParseHandler 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); } @@ -344,9 +340,6 @@ class FullParseHandler ParseNode* newClassNames(ParseNode* outer, ParseNode* inner, const TokenPos& pos) { return new_(outer, inner, pos); } - ParseNode* newSuperElement(ParseNode* expr, const TokenPos& pos) { - return new_(expr, pos); - } ParseNode* newNewTarget(ParseNode* newHolder, ParseNode* targetHolder) { return new_(PNK_NEWTARGET, JSOP_NOP, newHolder, targetHolder); } diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp index 3d62a89bc39a..d682c81afffc 100644 --- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -400,7 +400,6 @@ class NameResolver case PNK_DELETENAME: case PNK_DELETEPROP: case PNK_DELETEELEM: - case PNK_DELETESUPERELEM: case PNK_DELETEEXPR: case PNK_NEG: case PNK_POS: @@ -412,7 +411,6 @@ class NameResolver 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)); @@ -443,7 +441,6 @@ class NameResolver case PNK_DIVASSIGN: case PNK_MODASSIGN: case PNK_POWASSIGN: - case PNK_ELEM: case PNK_COLON: case PNK_CASE: case PNK_SHORTHAND: @@ -460,6 +457,14 @@ class NameResolver return false; break; + case PNK_ELEM: + MOZ_ASSERT(cur->isArity(PN_BINARY)); + if (!cur->as().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)) diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index 58fcb122e288..92e9590da151 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -230,7 +230,6 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack) case PNK_DELETENAME: case PNK_DELETEPROP: case PNK_DELETEELEM: - case PNK_DELETESUPERELEM: case PNK_DELETEEXPR: case PNK_POS: case PNK_NEG: @@ -244,7 +243,6 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack) case PNK_MUTATEPROTO: case PNK_EXPORT: case PNK_EXPORT_DEFAULT: - case PNK_SUPERELEM: return PushUnaryNodeChild(pn, stack); // Nodes with a single nullable child. diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index dcd3b90c1549..dbbd2fae51fe 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -139,7 +139,6 @@ class PackedScopeCoordinate F(DELETENAME) \ F(DELETEPROP) \ F(DELETEELEM) \ - F(DELETESUPERELEM) \ F(DELETEEXPR) \ F(TRY) \ F(CATCH) \ @@ -175,7 +174,6 @@ class PackedScopeCoordinate F(CLASSMETHOD) \ F(CLASSMETHODLIST) \ F(CLASSNAMES) \ - F(SUPERELEM) \ F(NEWTARGET) \ F(POSHOLDER) \ \ @@ -1337,6 +1335,17 @@ class PropertyByValue : public ParseNode 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); + } }; /* @@ -1449,22 +1458,6 @@ struct ClassNode : public TernaryNode { } }; -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 diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 79e8b7cf8e05..39f17ed3918a 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -4966,7 +4966,6 @@ Parser::isValidForStatementLHS(ParseNode* pn1, JSVersion versi case PNK_CALL: case PNK_DOT: case PNK_ELEM: - case PNK_SUPERELEM: case PNK_NAME: case PNK_OBJECT: return true; @@ -8249,8 +8248,6 @@ Parser::memberExpr(YieldHandling yieldHandling, TokenKind tt, bool 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; @@ -8326,15 +8323,11 @@ Parser::memberExpr(YieldHandling yieldHandling, TokenKind tt, bool 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) || diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index b7c5d8b4cfd2..da8b1841c5ed 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -47,8 +47,6 @@ class SyntaxParseHandler 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 @@ -140,8 +138,7 @@ class SyntaxParseHandler typedef Definition::Kind DefinitionNode; bool isPropertyAccess(Node node) { - return node == NodeDottedProperty || node == NodeElement || - node == NodeSuperElement; + return node == NodeDottedProperty || node == NodeElement; } bool isFunctionCall(Node node) { @@ -277,9 +274,6 @@ class SyntaxParseHandler 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; } diff --git a/js/src/tests/ecma_6/Class/superPropDVG.js b/js/src/tests/ecma_6/Class/superPropDVG.js index 7281e3dfcfb1..e0a08f20ca40 100644 --- a/js/src/tests/ecma_6/Class/superPropDVG.js +++ b/js/src/tests/ecma_6/Class/superPropDVG.js @@ -7,11 +7,15 @@ class testNonExistent { 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())