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 260650 8534fa84c4902fe0caad46697c7ef15cb2d19928
parent 260649 4f7c8a5677070cfd021dd205c90d60cebc449ee2
child 260651 ff553f6079f458fd43fc59dcbdae2d10879d2725
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 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");