Bug 1378808 - Add a new ParseNodeKind::PropertyName to hold location information about property access name. r=jorendorff
☠☠ backed out by e54c4c4cde45 ☠ ☠
authorLogan F Smyth <loganfsmyth@gmail.com>
Thu, 12 Jul 2018 11:29:05 -0700
changeset 430501 6dd9c641346afe439fb10c028becf0a7b1f0aedc
parent 430500 e732697778c260c413047531e372a24286e0b667
child 430502 6c6a609463ab5d71e475354cab8c3ff323d0571d
push id34406
push userncsoregi@mozilla.com
push dateWed, 08 Aug 2018 09:58:58 +0000
treeherdermozilla-central@17116905bc07 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1378808
milestone63.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 1378808 - Add a new ParseNodeKind::PropertyName to hold location information about property access name. r=jorendorff MozReview-Commit-ID: J4vHz4ln5Zt
js/src/builtin/ReflectParse.cpp
js/src/frontend/BinSource-auto.cpp
js/src/frontend/BinSource.yaml
js/src/frontend/BytecodeEmitter.cpp
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/Parser.h
js/src/frontend/SyntaxParseHandler.h
js/src/wasm/AsmJS.cpp
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -2736,27 +2736,27 @@ ASTSerializer::expression(ParseNode* pn,
         return pn->isKind(ParseNodeKind::New)
                ? builder.newExpression(callee, args, &pn->pn_pos, dst)
 
             : builder.callExpression(callee, args, &pn->pn_pos, dst);
       }
 
       case ParseNodeKind::Dot:
       {
-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_expr->pn_pos));
+        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
 
         RootedValue expr(cx);
         RootedValue propname(cx);
-        RootedAtom pnAtom(cx, pn->pn_atom);
+        RootedAtom pnAtom(cx, pn->pn_right->pn_atom);
 
         if (pn->as<PropertyAccess>().isSuper()) {
-            if (!builder.super(&pn->pn_expr->pn_pos, &expr))
+            if (!builder.super(&pn->pn_left->pn_pos, &expr))
                 return false;
         } else {
-            if (!expression(pn->pn_expr, &expr))
+            if (!expression(pn->pn_left, &expr))
                 return false;
         }
 
         return identifier(pnAtom, nullptr, &propname) &&
                builder.memberExpression(false, expr, propname, &pn->pn_pos, dst);
       }
 
       case ParseNodeKind::Elem:
--- a/js/src/frontend/BinSource-auto.cpp
+++ b/js/src/frontend/BinSource-auto.cpp
@@ -6188,23 +6188,28 @@ BinASTParser<Tok>::parseInterfaceStaticM
 {
     MOZ_ASSERT(kind == BinKind::StaticMemberAssignmentTarget);
     BINJS_TRY(CheckRecursionLimit(cx_));
 
 #if defined(DEBUG)
     const BinField expected_fields[2] = { BinField::Object, BinField::Property };
     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif // defined(DEBUG)
+    size_t nameStart;
 
     BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper());
-
     RootedAtom property(cx_);
-    MOZ_TRY_VAR(property, tokenizer_->readAtom());
-
-    BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), tokenizer_->offset()));
+    {
+        nameStart = tokenizer_->offset();
+        MOZ_TRY_VAR(property, tokenizer_->readAtom());
+
+    }
+
+    BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), tokenizer_->pos(nameStart)));
+    BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name));
     return result;
 }
 
 
 /*
  interface StaticMemberExpression : Node {
     (Expression or Super) object;
     IdentifierName property;
@@ -6233,23 +6238,28 @@ BinASTParser<Tok>::parseInterfaceStaticM
 {
     MOZ_ASSERT(kind == BinKind::StaticMemberExpression);
     BINJS_TRY(CheckRecursionLimit(cx_));
 
 #if defined(DEBUG)
     const BinField expected_fields[2] = { BinField::Object, BinField::Property };
     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif // defined(DEBUG)
+    size_t nameStart;
 
     BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper());
-
     RootedAtom property(cx_);
-    MOZ_TRY_VAR(property, tokenizer_->readAtom());
-
-    BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), tokenizer_->offset()));
+    {
+        nameStart = tokenizer_->offset();
+        MOZ_TRY_VAR(property, tokenizer_->readAtom());
+
+    }
+
+    BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), tokenizer_->pos(nameStart)));
+    BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name));
     return result;
 }
 
 
 /*
  interface Super : Node {
  }
 */
--- a/js/src/frontend/BinSource.yaml
+++ b/js/src/frontend/BinSource.yaml
@@ -913,22 +913,38 @@ SwitchStatementWithDefault:
             ParseNode* next = iter->pn_next;
             factory_.addList(cases, iter);
             iter = next;
         }
         BINJS_TRY_DECL(scope, factory_.newLexicalScope(nullptr, cases));
         BINJS_TRY_DECL(result, factory_.newSwitchStatement(start, discriminant, scope, true));
 
 StaticMemberAssignmentTarget:
+    init:
+        size_t nameStart;
+    fields:
+        property:
+            block:
+                before: |
+                    nameStart = tokenizer_->offset();
     build: |
-        BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), tokenizer_->offset()));
+        BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), tokenizer_->pos(nameStart)));
+        BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name));
 
 StaticMemberExpression:
+    init:
+        size_t nameStart;
+    fields:
+        property:
+            block:
+                before: |
+                    nameStart = tokenizer_->offset();
     build: |
-        BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), tokenizer_->offset()));
+        BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), tokenizer_->pos(nameStart)));
+        BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name));
 
 ThisExpression:
     build: |
         if (parseContext_->isFunctionBox())
             parseContext_->functionBox()->usesThis = true;
 
         TokenPos pos = tokenizer_->pos(start);
         ParseNode* thisName(nullptr);
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1033,17 +1033,17 @@ BytecodeEmitter::checkSideEffects(ParseN
       case ParseNodeKind::Continue:
       case ParseNodeKind::Debugger:
         MOZ_ASSERT(pn->isArity(PN_NULLARY));
         *answer = true;
         return true;
 
       // Watch out for getters!
       case ParseNodeKind::Dot:
-        MOZ_ASSERT(pn->isArity(PN_NAME));
+        MOZ_ASSERT(pn->isArity(PN_BINARY));
         *answer = true;
         return true;
 
       // Unary cases with side effects only if the child has them.
       case ParseNodeKind::TypeOfExpr:
       case ParseNodeKind::Void:
       case ParseNodeKind::Not:
         MOZ_ASSERT(pn->isArity(PN_UNARY));
@@ -1413,16 +1413,17 @@ BytecodeEmitter::checkSideEffects(ParseN
       case ParseNodeKind::ImportSpecList: // by ParseNodeKind::Import
       case ParseNodeKind::ImportSpec:      // by ParseNodeKind::Import
       case ParseNodeKind::ExportBatchSpec:// by ParseNodeKind::Export
       case ParseNodeKind::ExportSpecList: // by ParseNodeKind::Export
       case ParseNodeKind::ExportSpec:      // by ParseNodeKind::Export
       case ParseNodeKind::CallSiteObj:      // by ParseNodeKind::TaggedTemplate
       case ParseNodeKind::PosHolder:        // by ParseNodeKind::NewTarget
       case ParseNodeKind::SuperBase:        // by ParseNodeKind::Elem and others
+      case ParseNodeKind::PropertyName:     // by ParseNodeKind::Dot
         MOZ_CRASH("handled by parent nodes");
 
       case ParseNodeKind::Limit: // invalid sentinel value
         MOZ_CRASH("invalid node kind");
     }
 
     MOZ_CRASH("invalid, unenumerated ParseNodeKind value encountered in "
               "BytecodeEmitter::checkSideEffects");
@@ -1884,49 +1885,49 @@ BytecodeEmitter::emitTDZCheckIfNeeded(JS
 }
 
 bool
 BytecodeEmitter::emitPropLHS(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot));
     MOZ_ASSERT(!pn->as<PropertyAccess>().isSuper());
 
-    ParseNode* pn2 = pn->pn_expr;
+    ParseNode* pn2 = pn->pn_left;
 
     /*
      * 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
+     * list linked via pn_left temporarily so we can iterate over it from the
      * bottom up (reversing again as we go), to avoid excessive recursion.
      */
     if (pn2->isKind(ParseNodeKind::Dot) && !pn2->as<PropertyAccess>().isSuper()) {
         ParseNode* pndot = pn2;
         ParseNode* pnup = nullptr;
         ParseNode* pndown;
         for (;;) {
-            /* Reverse pndot->pn_expr to point up, not down. */
-            pndown = pndot->pn_expr;
-            pndot->pn_expr = pnup;
+            /* Reverse pndot->pn_left to point up, not down. */
+            pndown = pndot->pn_left;
+            pndot->pn_left = pnup;
             if (!pndown->isKind(ParseNodeKind::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;
 
         do {
             /* Walk back up the list, emitting annotated name ops. */
-            if (!emitAtomOp(pndot, JSOP_GETPROP))
-                return false;
-
-            /* Reverse the pn_expr link again. */
-            pnup = pndot->pn_expr;
-            pndot->pn_expr = pndown;
+            if (!emitAtomOp(pndot->pn_right, JSOP_GETPROP))
+                return false;
+
+            /* Reverse the pn_left link again. */
+            pnup = pndot->pn_left;
+            pndot->pn_left = pndown;
             pndown = pndot;
         } while ((pndot = pnup) != nullptr);
         return true;
     }
 
     // The non-optimized case.
     return emitTree(pn2);
 }
@@ -1941,41 +1942,41 @@ BytecodeEmitter::emitSuperPropLHS(ParseN
     if (!emit1(JSOP_SUPERBASE))
         return false;
     return true;
 }
 
 bool
 BytecodeEmitter::emitPropOp(ParseNode* pn, JSOp op)
 {
-    MOZ_ASSERT(pn->isArity(PN_NAME));
+    MOZ_ASSERT(pn->isArity(PN_BINARY));
 
     if (!emitPropLHS(pn))
         return false;
 
     if (op == JSOP_CALLPROP && !emit1(JSOP_DUP))
         return false;
 
-    if (!emitAtomOp(pn, op))
+    if (!emitAtomOp(pn->pn_right, op))
         return false;
 
     if (op == JSOP_CALLPROP && !emit1(JSOP_SWAP))
         return false;
 
     return true;
 }
 
 bool
 BytecodeEmitter::emitSuperGetProp(ParseNode* pn, bool isCall)
 {
     ParseNode* base = &pn->as<PropertyAccess>().expression();
     if (!emitSuperPropLHS(base, isCall))
         return false;
 
-    if (!emitAtomOp(pn, JSOP_GETPROP_SUPER))
+    if (!emitAtomOp(pn->pn_right, JSOP_GETPROP_SUPER))
         return false;
 
     if (isCall && !emit1(JSOP_SWAP))
         return false;
 
     return true;
 }
 
@@ -1995,17 +1996,17 @@ BytecodeEmitter::emitPropIncDec(ParseNod
         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
+    if (!emitAtomOp(pn->pn_kid->pn_right, 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
@@ -2021,17 +2022,17 @@ BytecodeEmitter::emitPropIncDec(ParseNod
                 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
+    if (!emitAtomOp(pn->pn_kid->pn_right, setOp))   // N? N+1
         return false;
     if (post && !emit1(JSOP_POP))                   // RESULT
         return false;
 
     return true;
 }
 
 bool
@@ -2711,17 +2712,17 @@ BytecodeEmitter::emitDestructuringLHSRef
 
     switch (target->getKind()) {
       case ParseNodeKind::Dot: {
         if (target->as<PropertyAccess>().isSuper()) {
             if (!emitSuperPropLHS(&target->as<PropertyAccess>().expression()))
                 return false;
             *emitted = 2;
         } else {
-            if (!emitTree(target->pn_expr))
+            if (!emitTree(target->pn_left))
                 return false;
             *emitted = 1;
         }
         break;
       }
 
       case ParseNodeKind::Elem: {
         if (target->as<PropertyByValue>().isSuper()) {
@@ -2829,17 +2830,17 @@ BytecodeEmitter::emitSetOrInitializeDest
 
           case ParseNodeKind::Dot: {
             // The reference is already pushed by emitDestructuringLHSRef.
             JSOp setOp;
             if (target->as<PropertyAccess>().isSuper())
                 setOp = sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER;
             else
                 setOp = sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
-            if (!emitAtomOp(target, setOp))
+            if (!emitAtomOp(target->pn_right, setOp))
                 return false;
             break;
           }
 
           case ParseNodeKind::Elem: {
             // The reference is already pushed by emitDestructuringLHSRef.
             if (target->as<PropertyByValue>().isSuper()) {
                 JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER;
@@ -3941,21 +3942,21 @@ BytecodeEmitter::emitAssignment(ParseNod
 
     switch (lhs->getKind()) {
       case ParseNodeKind::Dot:
         if (lhs->as<PropertyAccess>().isSuper()) {
             if (!emitSuperPropLHS(&lhs->as<PropertyAccess>().expression()))
                 return false;
             offset += 2;
         } else {
-            if (!emitTree(lhs->expr()))
+            if (!emitTree(lhs->pn_left))
                 return false;
             offset += 1;
         }
-        if (!makeAtomIndex(lhs->pn_atom, &atomIndex))
+        if (!makeAtomIndex(lhs->pn_right->pn_atom, &atomIndex))
             return false;
         break;
       case ParseNodeKind::Elem: {
         MOZ_ASSERT(lhs->isArity(PN_BINARY));
         EmitElemOption opt = op == JSOP_NOP ? EmitElemOption::Get : EmitElemOption::CompoundAssign;
         if (lhs->as<PropertyByValue>().isSuper()) {
             if (!emitSuperElemOperands(lhs, opt))
                 return false;
@@ -3994,17 +3995,17 @@ BytecodeEmitter::emitAssignment(ParseNod
             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);
+                bool isLength = (lhs->pn_right->pn_atom == cx->names().length);
                 getOp = isLength ? JSOP_LENGTH : JSOP_GETPROP;
             }
             if (!emitIndex32(getOp, atomIndex))
                 return false;
             break;
           }
           case ParseNodeKind::Elem: {
             JSOp elemOp;
@@ -8690,18 +8691,19 @@ BytecodeEmitter::emitTree(ParseNode* pn,
             return false;
         break;
 
       case ParseNodeKind::SetThis:
         if (!emitSetThis(pn))
             return false;
         break;
 
+      case ParseNodeKind::PropertyName:
       case ParseNodeKind::PosHolder:
-        MOZ_FALLTHROUGH_ASSERT("Should never try to emit ParseNodeKind::PosHolder");
+        MOZ_FALLTHROUGH_ASSERT("Should never try to emit ParseNodeKind::PosHolder or ::Property");
 
       default:
         MOZ_ASSERT(0);
     }
 
     /* bce->emitLevel == 1 means we're last on the stack, so finish up. */
     if (emitLevel == 1) {
         if (!updateSourceCoordNotes(pn->pn_pos.end))
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -342,16 +342,17 @@ ContainsHoistedDeclaration(JSContext* cx
       case ParseNodeKind::UrshAssign:
       case ParseNodeKind::MulAssign:
       case ParseNodeKind::DivAssign:
       case ParseNodeKind::ModAssign:
       case ParseNodeKind::PowAssign:
       case ParseNodeKind::Comma:
       case ParseNodeKind::Array:
       case ParseNodeKind::Object:
+      case ParseNodeKind::PropertyName:
       case ParseNodeKind::Dot:
       case ParseNodeKind::Elem:
       case ParseNodeKind::Arguments:
       case ParseNodeKind::Call:
       case ParseNodeKind::Name:
       case ParseNodeKind::TemplateString:
       case ParseNodeKind::TemplateStringList:
       case ParseNodeKind::TaggedTemplate:
@@ -1249,17 +1250,20 @@ FoldElement(JSContext* cx, ParseNode** n
     }
 
     // If we don't have a name, we can't optimize to getprop.
     if (!name)
         return true;
 
     // Optimization 3: We have expr["foo"] where foo is not an index.  Convert
     // to a property access (like expr.foo) that optimizes better downstream.
-    ParseNode* dottedAccess = parser.newPropertyAccess(expr, name, node->pn_pos.end);
+    ParseNode* nameNode = parser.newPropertyName(name, key->pn_pos);
+    if (!nameNode)
+        return false;
+    ParseNode* dottedAccess = parser.newPropertyAccess(expr, nameNode);
     if (!dottedAccess)
         return false;
     dottedAccess->setInParens(node->isInParens());
     ReplaceNode(nodePtr, dottedAccess);
 
     return true;
 }
 
@@ -1495,24 +1499,24 @@ FoldForHead(JSContext* cx, ParseNode* no
 
     return true;
 }
 
 static bool
 FoldDottedProperty(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::Dot));
-    MOZ_ASSERT(node->isArity(PN_NAME));
+    MOZ_ASSERT(node->isArity(PN_BINARY));
 
     // Iterate through a long chain of dotted property accesses to find the
     // most-nested non-dotted property node, then fold that.
-    ParseNode** nested = &node->pn_expr;
+    ParseNode** nested = &node->pn_left;
     while ((*nested)->isKind(ParseNodeKind::Dot)) {
-        MOZ_ASSERT((*nested)->isArity(PN_NAME));
-        nested = &(*nested)->pn_expr;
+        MOZ_ASSERT((*nested)->isArity(PN_BINARY));
+        nested = &(*nested)->pn_left;
     }
 
     return Fold(cx, nested, parser);
 }
 
 static bool
 FoldName(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
@@ -1793,16 +1797,19 @@ Fold(JSContext* cx, ParseNode** pnp, Per
 
       case ParseNodeKind::ForHead:
         return FoldForHead(cx, pn, parser);
 
       case ParseNodeKind::Label:
         MOZ_ASSERT(pn->isArity(PN_NAME));
         return Fold(cx, &pn->pn_expr, parser);
 
+      case ParseNodeKind::PropertyName:
+        MOZ_CRASH("unreachable, handled by ::Dot");
+
       case ParseNodeKind::Dot:
         return FoldDottedProperty(cx, pn, parser);
 
       case ParseNodeKind::LexicalScope:
         MOZ_ASSERT(pn->isArity(PN_SCOPE));
         if (!pn->scopeBody())
             return true;
         return Fold(cx, &pn->pn_u.scope.body, parser);
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -659,18 +659,22 @@ class FullParseHandler
         TokenPos pos(begin, (finallyBlock ? finallyBlock : catchScope)->pn_pos.end);
         return new_<TernaryNode>(ParseNodeKind::Try, body, catchScope, finallyBlock, pos);
     }
 
     ParseNode* newDebuggerStatement(const TokenPos& pos) {
         return new_<DebuggerStatement>(pos);
     }
 
-    ParseNode* newPropertyAccess(ParseNode* expr, PropertyName* key, uint32_t end) {
-        return new_<PropertyAccess>(expr, key, expr->pn_pos.begin, end);
+    ParseNode* newPropertyName(PropertyName* name, const TokenPos& pos) {
+        return new_<NameNode>(ParseNodeKind::PropertyName, JSOP_NOP, name, pos);
+    }
+
+    ParseNode* newPropertyAccess(ParseNode* expr, ParseNode* key) {
+        return new_<PropertyAccess>(expr, key, expr->pn_pos.begin, key->pn_pos.end);
     }
 
     ParseNode* newPropertyByValue(ParseNode* lhs, ParseNode* index, uint32_t end) {
         return new_<PropertyByValue>(lhs, index, lhs->pn_pos.begin, end);
     }
 
     bool setupCatchScope(ParseNode* lexicalScope, ParseNode* catchName, ParseNode* catchBody) {
         ParseNode* catchpn;
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -70,21 +70,21 @@ class NameResolver
      * Walk over the given ParseNode, attempting to convert it to a stringified
      * name that respresents where the function is being assigned to.
      *
      * |*foundName| is set to true if a name is found for the expression.
      */
     bool nameExpression(ParseNode* n, bool* foundName) {
         switch (n->getKind()) {
           case ParseNodeKind::Dot:
-            if (!nameExpression(n->expr(), foundName))
+            if (!nameExpression(n->pn_left, foundName))
                 return false;
             if (!*foundName)
                 return true;
-            return appendPropertyReference(n->pn_atom);
+            return appendPropertyReference(n->pn_right->pn_atom);
 
           case ParseNodeKind::Name:
             *foundName = true;
             return buf->append(n->pn_atom);
 
           case ParseNodeKind::This:
             *foundName = true;
             return buf->append("this");
@@ -779,22 +779,22 @@ class NameResolver
                 MOZ_ASSERT(item->pn_right->isKind(ParseNodeKind::Name));
                 MOZ_ASSERT(!item->pn_right->expr());
             }
 #endif
             break;
           }
 
           case ParseNodeKind::Dot:
-            MOZ_ASSERT(cur->isArity(PN_NAME));
+            MOZ_ASSERT(cur->isArity(PN_BINARY));
 
             // Super prop nodes do not have a meaningful LHS
             if (cur->as<PropertyAccess>().isSuper())
                 break;
-            if (!resolve(cur->expr(), prefix))
+            if (!resolve(cur->pn_left, prefix))
                 return false;
             break;
 
           case ParseNodeKind::Label:
             MOZ_ASSERT(cur->isArity(PN_NAME));
             if (!resolve(cur->expr(), prefix))
                 return false;
             break;
@@ -823,16 +823,17 @@ class NameResolver
             break;
 
           // Kinds that should be handled by parent node resolution.
 
           case ParseNodeKind::ImportSpec: // by ParseNodeKind::ImportSpecList
           case ParseNodeKind::ExportSpec: // by ParseNodeKind::ExportSpecList
           case ParseNodeKind::CallSiteObj: // by ParseNodeKind::TaggedTemplate
           case ParseNodeKind::ClassNames:  // by ParseNodeKind::Class
+          case ParseNodeKind::PropertyName:  // by ParseNodeKind::Dot
             MOZ_CRASH("should have been handled by a parent node");
 
           case ParseNodeKind::Limit: // invalid sentinel value
             MOZ_CRASH("invalid node kind");
         }
 
         nparents--;
         MOZ_ASSERT(initialParents == nparents, "nparents imbalance detected");
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -211,16 +211,31 @@ UnaryNode::dump(GenericPrinter& out, int
     indent += strlen(name) + 2;
     DumpParseTree(pn_kid, out, indent);
     out.printf(")");
 }
 
 void
 BinaryNode::dump(GenericPrinter& out, int indent)
 {
+    if (isKind(ParseNodeKind::Dot)) {
+        out.put("(.");
+
+        DumpParseTree(pn_right, out, indent + 2);
+
+        out.putChar(' ');
+        if (as<PropertyAccess>().isSuper())
+            out.put("super");
+        else
+            DumpParseTree(pn_left, out, indent + 2);
+
+        out.printf(")");
+        return;
+    }
+
     const char* name = parseNodeNames[size_t(getKind())];
     out.printf("(%s ", name);
     indent += strlen(name) + 2;
     DumpParseTree(pn_left, out, indent);
     IndentNewLine(out, indent);
     DumpParseTree(pn_right, out, indent);
     out.printf(")");
 }
@@ -283,43 +298,31 @@ DumpName(GenericPrinter& out, const Char
         else
             out.printf("\\u%04x", unsigned(c));
     }
 }
 
 void
 NameNode::dump(GenericPrinter& out, int indent)
 {
-    if (isKind(ParseNodeKind::Name) || isKind(ParseNodeKind::Dot)) {
-        if (isKind(ParseNodeKind::Dot))
-            out.put("(.");
-
+    if (isKind(ParseNodeKind::Name) || isKind(ParseNodeKind::PropertyName)) {
         if (!pn_atom) {
             out.put("#<null name>");
         } else if (getOp() == JSOP_GETARG && pn_atom->length() == 0) {
             // Dump destructuring parameter.
             out.put("(#<zero-length name> ");
             DumpParseTree(expr(), out, indent + 21);
             out.printf(")");
         } else {
             JS::AutoCheckCannotGC nogc;
             if (pn_atom->hasLatin1Chars())
                 DumpName(out, pn_atom->latin1Chars(nogc), pn_atom->length());
             else
                 DumpName(out, pn_atom->twoByteChars(nogc), pn_atom->length());
         }
-
-        if (isKind(ParseNodeKind::Dot)) {
-            out.putChar(' ');
-            if (as<PropertyAccess>().isSuper())
-                out.put("super");
-            else
-                DumpParseTree(expr(), out, indent + 2);
-            out.printf(")");
-        }
         return;
     }
 
     const char* name = parseNodeNames[size_t(getKind())];
     out.printf("(%s ", name);
     indent += strlen(name) + 2;
     DumpParseTree(expr(), out, indent);
     out.printf(")");
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -50,16 +50,17 @@ class ObjectBox;
     F(Colon) \
     F(Shorthand) \
     F(Pos) \
     F(Neg) \
     F(PreIncrement) \
     F(PostIncrement) \
     F(PreDecrement) \
     F(PostDecrement) \
+    F(PropertyName) \
     F(Dot) \
     F(Elem) \
     F(Array) \
     F(Elision) \
     F(StatementList) \
     F(Label) \
     F(Object) \
     F(Call) \
@@ -377,18 +378,19 @@ IsTypeofKind(ParseNodeKind kind)
  * DeleteName unary     pn_kid: Name expr
  * DeleteProp unary     pn_kid: Dot expr
  * DeleteElem unary     pn_kid: Elem expr
  * 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
- * Dot      name        pn_expr: MEMBER expr to left of .
- *                          pn_atom: name to right of .
+ * PropertyName name    pn_atom: property name being accessed
+ * Dot      binary      pn_left: MEMBER expr to left of .
+ *                          pn_right: PropertyName to right of .
  * Elem     binary      pn_left: MEMBER expr to left of [
  *                          pn_right: expr between [ and ]
  * Call     binary      pn_left: callee expression on the left of the (
  *                          pn_right: Arguments
  * Arguments list       pn_head: list of arg1, arg2, ... argN
  *                          pn_count: N (where N is number of args)
  * Array    list        pn_head: list of pn_count array element exprs
  *                          [,,] holes are represented by Elision nodes
@@ -568,18 +570,17 @@ class ParseNode
         } unary;
         struct {                        /* name, labeled statement, etc. */
             union {
                 JSAtom*      atom;      /* lexical name or label atom */
                 ObjectBox*   objbox;    /* regexp object */
                 FunctionBox* funbox;    /* function object */
             };
             ParseNode*  expr;           /* module or function body, var
-                                           initializer, argument default, or
-                                           base object of ParseNodeKind::Dot */
+                                           initializer, or argument default */
         } name;
         struct {
             LexicalScope::Data* bindings;
             ParseNode*          body;
         } scope;
         struct {
             double       value;         /* aligned numeric literal value */
             DecimalPoint decimalPoint;  /* Whether the number has a decimal point */
@@ -1173,40 +1174,43 @@ class RegExpLiteral : public NullaryNode
     static bool test(const ParseNode& node) {
         bool match = node.isKind(ParseNodeKind::RegExp);
         MOZ_ASSERT_IF(match, node.isArity(PN_NULLARY));
         MOZ_ASSERT_IF(match, node.isOp(JSOP_REGEXP));
         return match;
     }
 };
 
-class PropertyAccess : public ParseNode
+class PropertyAccess : public BinaryNode
 {
   public:
-    PropertyAccess(ParseNode* lhs, PropertyName* name, uint32_t begin, uint32_t end)
-      : ParseNode(ParseNodeKind::Dot, JSOP_NOP, PN_NAME, TokenPos(begin, end))
+    /*
+     * PropertyAccess nodes can have any expression/'super' as left-hand
+     * side, but the name must be a ParseNodeKind::PropertyName node.
+     */
+    PropertyAccess(ParseNode* lhs, ParseNode* name, uint32_t begin, uint32_t end)
+      : BinaryNode(ParseNodeKind::Dot, JSOP_NOP, TokenPos(begin, end), lhs, name)
     {
         MOZ_ASSERT(lhs != nullptr);
         MOZ_ASSERT(name != nullptr);
-        pn_u.name.expr = lhs;
-        pn_u.name.atom = name;
     }
 
     static bool test(const ParseNode& node) {
         bool match = node.isKind(ParseNodeKind::Dot);
-        MOZ_ASSERT_IF(match, node.isArity(PN_NAME));
+        MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
+        MOZ_ASSERT_IF(match, node.pn_right->isKind(ParseNodeKind::PropertyName));
         return match;
     }
 
     ParseNode& expression() const {
-        return *pn_u.name.expr;
+        return *pn_u.binary.left;
     }
 
     PropertyName& name() const {
-        return *pn_u.name.atom->asPropertyName();
+        return *pn_u.binary.right->pn_atom->asPropertyName();
     }
 
     bool isSuper() const {
         // ParseNodeKind::SuperBase cannot result from any expression syntax.
         return expression().isKind(ParseNodeKind::SuperBase);
     }
 };
 
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -8795,17 +8795,22 @@ GeneralParser<ParseHandler, CharT>::memb
             if (!tokenStream.getToken(&tt))
                 return null();
             if (TokenKindIsPossibleIdentifierName(tt)) {
                 PropertyName* field = anyChars.currentName();
                 if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
                     error(JSMSG_BAD_SUPERPROP, "property");
                     return null();
                 }
-                nextMember = handler.newPropertyAccess(lhs, field, pos().end);
+
+                Node name = handler.newPropertyName(field, pos());
+                if (!name)
+                    return null();
+
+                nextMember = handler.newPropertyAccess(lhs, name);
                 if (!nextMember)
                     return null();
             } else {
                 error(JSMSG_NAME_AFTER_DOT);
                 return null();
             }
         } else if (tt == TokenKind::Lb) {
             Node propExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -564,18 +564,22 @@ class MOZ_STACK_CLASS PerHandlerParser
     // If ParseHandler is FullParseHandler:
     //   Do nothing.
     inline void clearAbortedSyntaxParse();
 
   public:
     bool isValidSimpleAssignmentTarget(Node node,
                                        FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls);
 
-    Node newPropertyAccess(Node expr, PropertyName* key, uint32_t end) {
-        return handler.newPropertyAccess(expr, key, end);
+    Node newPropertyName(PropertyName* key, const TokenPos& pos) {
+        return handler.newPropertyName(key, pos);
+    }
+
+    Node newPropertyAccess(Node expr, Node key) {
+        return handler.newPropertyAccess(expr, key);
     }
 
     FunctionBox* newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart,
                                 Directives directives, GeneratorKind generatorKind,
                                 FunctionAsyncKind asyncKind);
 };
 
 #define ABORTED_SYNTAX_PARSE_SENTINEL reinterpret_cast<void*>(0x1)
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -329,18 +329,22 @@ class SyntaxParseHandler
     }
 
     Node newThrowStatement(Node expr, const TokenPos& pos) { return NodeThrow; }
     Node newTryStatement(uint32_t begin, Node body, Node catchScope, Node finallyBlock) {
         return NodeGeneric;
     }
     Node newDebuggerStatement(const TokenPos& pos) { return NodeGeneric; }
 
-    Node newPropertyAccess(Node expr, PropertyName* key, uint32_t end) {
-        lastAtom = key;
+    Node newPropertyName(PropertyName* name, const TokenPos& pos) {
+        lastAtom = name;
+        return NodeGeneric;
+    }
+
+    Node newPropertyAccess(Node expr, Node key) {
         return NodeDottedProperty;
     }
 
     Node newPropertyByValue(Node pn, Node kid, uint32_t end) { return NodeElement; }
 
     MOZ_MUST_USE bool setupCatchScope(Node letBlock, Node catchName, Node catchBody) {
         return true;
     }
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -588,26 +588,26 @@ NumberNodeHasFrac(ParseNode* pn)
     MOZ_ASSERT(pn->isKind(ParseNodeKind::Number));
     return pn->pn_u.number.decimalPoint == HasDecimal;
 }
 
 static ParseNode*
 DotBase(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot));
-    MOZ_ASSERT(pn->isArity(PN_NAME));
-    return pn->expr();
+    MOZ_ASSERT(pn->isArity(PN_BINARY));
+    return pn->pn_left;
 }
 
 static PropertyName*
 DotMember(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot));
-    MOZ_ASSERT(pn->isArity(PN_NAME));
-    return pn->pn_atom->asPropertyName();
+    MOZ_ASSERT(pn->isArity(PN_BINARY));
+    return pn->pn_right->pn_atom->asPropertyName();
 }
 
 static ParseNode*
 ElemBase(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isKind(ParseNodeKind::Elem));
     return BinaryLeft(pn);
 }