Bug 1168992 - Part 3: Change PNK_SUPERPROP to PNK_DOT and fix reflection of super.prop. (r=Waldo)
☠☠ backed out by 857b2baa4312 ☠ ☠
authorEric Faust <efaustbmo@mozilla.com>
Wed, 02 Sep 2015 15:09:04 -0700
changeset 260649 4f7c8a5677070cfd021dd205c90d60cebc449ee2
parent 260648 a14eb650f5be7810203729832da49a95a802306b
child 260650 8534fa84c4902fe0caad46697c7ef15cb2d19928
push id17297
push usercbook@mozilla.com
push dateThu, 03 Sep 2015 12:21:04 +0000
treeherderb2g-inbound@70f7c00f7fe6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs1168992
milestone43.0a1
Bug 1168992 - Part 3: Change PNK_SUPERPROP to PNK_DOT and fix reflection of super.prop. (r=Waldo)
js/src/builtin/ReflectParse.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/FoldConstants.cpp
js/src/frontend/FullParseHandler.h
js/src/frontend/NameFunctions.cpp
js/src/frontend/ParseNode.cpp
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/SyntaxParseHandler.h
js/src/jsast.tbl
js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -753,16 +753,18 @@ class NodeBuilder
     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);
+
     /*
      * declarations
      */
 
     bool variableDeclaration(NodeVector& elts, VarDeclKind kind, TokenPos* pos,
                              MutableHandleValue dst);
 
     /*
@@ -1833,16 +1835,26 @@ NodeBuilder::metaProperty(HandleValue me
         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]);
+    if (!cb.isNull())
+        return callback(cb, pos, dst);
+
+    return newNode(AST_SUPER, pos, dst);
+}
+
 namespace {
 
 /*
  * Serialization of parse nodes to JavaScript objects.
  *
  * All serialization methods take a non-nullable ParseNode pointer.
  */
 class ASTSerializer
@@ -3064,17 +3076,16 @@ 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:
@@ -3127,31 +3138,30 @@ 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), id(cx);
+        RootedValue expr(cx);
+        RootedValue propname(cx);
         RootedAtom pnAtom(cx, pn->pn_atom);
-        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);
+
+        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);
       }
 
       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);
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1942,22 +1942,16 @@ BytecodeEmitter::checkSideEffects(ParseN
       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:
@@ -2013,17 +2007,16 @@ 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.
@@ -2555,33 +2548,36 @@ 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))
+            if (!pndown->isKind(PNK_DOT) || pndown->as<PropertyAccess>().isSuper())
                 break;
             pnup = pndot;
             pndot = pndown;
         }
 
         /* pndown is a primary expression, not a dotted property reference. */
         if (!emitTree(pndown))
             return false;
@@ -2651,94 +2647,65 @@ 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 (!emitPropLHS(pn->pn_kid))                   // OBJ
-        return false;
-    if (!emit1(JSOP_DUP))                           // OBJ OBJ
-        return false;
-    if (!emitAtomOp(pn->pn_kid, JSOP_GETPROP))      // OBJ V
+    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
         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))                   // 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 (!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 (!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);
 
@@ -3786,34 +3753,30 @@ 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.
-            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;
+            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 (!emitAtomOp(target, setOp))
                 return false;
             break;
           }
 
           case PNK_ELEM:
           {
             // See the comment at `case PNK_DOT:` above. This case,
@@ -4451,26 +4414,25 @@ BytecodeEmitter::emitAssignment(ParseNod
                     bindOp = JSOP_BINDINTRINSIC;
                 if (!emitIndex32(bindOp, atomIndex))
                     return false;
                 offset++;
             }
         }
         break;
       case PNK_DOT:
-        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 (lhs->as<PropertyAccess>().isSuper()) {
+            if (!emitSuperPropLHS())
+                return false;
+            offset += 2;
+        } else {
+            if (!emitTree(lhs->expr()))
+                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))
@@ -4533,29 +4495,31 @@ 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: {
-            if (!emit1(JSOP_DUP))
-                return false;
-            bool isLength = (lhs->pn_atom == cx->names().length);
-            if (!emitIndex32(isLength ? JSOP_LENGTH : JSOP_GETPROP, atomIndex))
+            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))
                 return false;
             break;
           }
-          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))
@@ -4620,24 +4584,19 @@ BytecodeEmitter::emitAssignment(ParseNod
                 return false;
         } else {
             if (!emitIndexOp(lhs->getOp(), atomIndex))
                 return false;
         }
         break;
       case PNK_DOT:
       {
-        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;
+        JSOp setOp = lhs->as<PropertyAccess>().isSuper() ?
+                       (sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER) :
+                       (sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP);
         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;
@@ -6521,37 +6480,31 @@ 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));
 
@@ -6737,22 +6690,23 @@ 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 (!emitPropOp(pn2, callop ? JSOP_CALLPROP : JSOP_GETPROP))
-            return false;
-        break;
-      case PNK_SUPERPROP:
-        if (!emitSuperPropOp(pn2, JSOP_GETPROP_SUPER, /* isCall = */ callop))
-            return false;
+        if (pn2->as<PropertyAccess>().isSuper()) {
+            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;
         }
@@ -6912,20 +6866,16 @@ 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;
@@ -7887,39 +7837,36 @@ 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:
-        ok = emitPropOp(pn, JSOP_GETPROP);
-        break;
-
-      case PNK_SUPERPROP:
-        if (!emitSuperPropOp(pn, JSOP_GETPROP_SUPER))
-            return false;
+        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))
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -549,17 +549,16 @@ 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);
@@ -606,17 +605,16 @@ 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 */
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -329,17 +329,16 @@ 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:
@@ -409,17 +408,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_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 "
@@ -655,19 +653,19 @@ FoldDeleteElement(ExclusiveContext* cx, 
 
     return true;
 }
 
 static bool
 FoldDeleteProperty(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
                    bool inGenexpLambda)
 {
-    MOZ_ASSERT(node->isKind(PNK_DELETEPROP) || node->isKind(PNK_DELETESUPERPROP));
+    MOZ_ASSERT(node->isKind(PNK_DELETEPROP));
     MOZ_ASSERT(node->isArity(PN_UNARY));
-    MOZ_ASSERT(node->pn_kid->isKind(PNK_DOT) || node->pn_kid->isKind(PNK_SUPERPROP));
+    MOZ_ASSERT(node->pn_kid->isKind(PNK_DOT));
 
     ParseNode*& expr = node->pn_kid;
 #ifdef DEBUG
     ParseNodeKind oldKind = expr->getKind();
 #endif
 
     if (!Fold(cx, &expr, parser, inGenexpLambda))
         return false;
@@ -1721,17 +1719,16 @@ Fold(ExclusiveContext* cx, ParseNode** p
       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));
@@ -1750,17 +1747,16 @@ Fold(ExclusiveContext* cx, ParseNode** p
       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);
 
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -70,17 +70,17 @@ class FullParseHandler
     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_SUPERPROP) || node->isKind(PNK_SUPERELEM);
+        return 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) {
@@ -220,18 +220,16 @@ 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);
     }
@@ -341,28 +339,34 @@ 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* newSuperProperty(JSAtom* atom, const TokenPos& pos) {
-        return new_<SuperProperty>(atom, 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);
+#ifdef DEBUG
+        // Set the atom for assertion purposes
+        if (node)
+            node->pn_atom = cx->names().super;
+#endif
+        return node;
+    }
 
     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);
         if (!mutation)
@@ -709,16 +713,21 @@ 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,17 +370,16 @@ 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:
             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());
@@ -395,17 +394,16 @@ class NameResolver
           // 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:
@@ -752,18 +750,27 @@ 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_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:
-          case PNK_DOT:
             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));
@@ -779,17 +786,17 @@ 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
+          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,33 +209,31 @@ 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_SUPERPROP:
       case PNK_POSHOLDER:
         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:
@@ -1102,17 +1100,20 @@ 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);
-            DumpParseTree(expr(), indent + 2);
+            if (as<PropertyAccess>().isSuper())
+                fprintf(stderr, "super");
+            else
+                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,16 @@ 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) \
@@ -171,17 +170,16 @@ 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) \
@@ -428,17 +426,16 @@ 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 .
@@ -1319,16 +1316,21 @@ 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))
     {
@@ -1442,32 +1444,16 @@ 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));
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -4960,17 +4960,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_SUPERPROP:
       case PNK_ELEM:
       case PNK_SUPERELEM:
       case PNK_NAME:
       case PNK_OBJECT:
         return true;
 
       default:
         return false;
@@ -8245,17 +8244,16 @@ 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))
@@ -8282,76 +8280,73 @@ 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 = null();
-        isSuper = true;
+        lhs = handler.newSuperBase(pos(), context);
+        if (!lhs)
+            return null();
     } 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 (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);
+                if (handler.isSuperBase(lhs, context) && !checkAndMarkSuperScope()) {
+                    report(ParseError, false, null(), JSMSG_BAD_SUPERPROP, "property");
+                    return null();
                 }
+                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 (isSuper) {
-                isSuper = false;
+            if (handler.isSuperBase(lhs, context)) {
                 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);
             }
             if (!nextMember)
                 return null();
         } else if ((allowCallSyntax && tt == TOK_LP) ||
                    tt == TOK_TEMPLATE_HEAD ||
                    tt == TOK_NO_SUBS_TEMPLATE)
         {
-            if (isSuper) {
+            if (handler.isSuperBase(lhs, context)) {
                 // For now...
                 report(ParseError, false, null(), JSMSG_BAD_SUPER);
                 return null();
             }
 
             nextMember = tt == TOK_LP ? handler.newCall() : handler.newTaggedTemplate();
             if (!nextMember)
                 return null();
@@ -8405,28 +8400,26 @@ 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 (isSuper) {
+    if (handler.isSuperBase(lhs, context)) {
         report(ParseError, false, null(), JSMSG_BAD_SUPER);
         return null();
     }
 
     return lhs;
 }
 
 template <typename ParseHandler>
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -42,17 +42,16 @@ 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,
@@ -127,23 +126,27 @@ 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
+        NodeUnparenthesizedAssignment,
+
+        // 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 == NodeSuperProperty || node == NodeSuperElement;
+               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,25 +272,22 @@ 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 newSuperProperty(PropertyName* prop, const TokenPos& pos) {
-        return NodeSuperProperty;
-    }
-
     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; }
     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; }
@@ -432,16 +432,22 @@ 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/jsast.tbl
+++ b/js/src/jsast.tbl
@@ -34,16 +34,17 @@ ASTDEF(AST_ARRAY_EXPR,            "Array
 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_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/tests/js1_8_5/reflect-parse/PatternBuilders.js
+++ b/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js
@@ -119,17 +119,17 @@ 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(ident("super"), id);
+    return dotExpr(Pattern({ type: "Super" }), id);
 }
 function superElem(id) {
     return memExpr(ident("super"), id);
 }
 
 function classStmt(id, heritage, body) {
     return Pattern({ type: "ClassStatement",
                      id: id,