Bug 1168992 - Part 4: Convert PNK_SUPERELEM to PNK_ELEM and fix reflection of super[elem]. (r=Waldo)
☠☠ backed out by 26951c3aa267 ☠ ☠
authorEric Faust <efaustbmo@mozilla.com>
Wed, 02 Sep 2015 15:09:06 -0700
changeset 260643 8534fa84c4902fe0caad46697c7ef15cb2d19928
parent 260642 4f7c8a5677070cfd021dd205c90d60cebc449ee2
child 260644 ff553f6079f458fd43fc59dcbdae2d10879d2725
push id29318
push usercbook@mozilla.com
push dateThu, 03 Sep 2015 11:15:07 +0000
treeherdermozilla-central@74fbd245369c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs1168992
milestone43.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
Bug 1168992 - Part 4: Convert PNK_SUPERELEM to PNK_ELEM and fix reflection of super[elem]. (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/tests/ecma_6/Class/superPropDVG.js
--- 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");