bug 1525331 - Move fields from union to individual subclasses. r=jorendorff
authorAshley Hauck <khyperia@mozilla.com>
Wed, 06 Feb 2019 20:32:23 +0000
changeset 515339 a226aae4349fe5e23fbd453f7a965a090abca1fc
parent 515338 aba804ffb8e8410a1ff38cd1bdfe70de1107d23a
child 515340 a96986fa4704ebcced8dd5578c10aaf593ed7d7b
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1525331
milestone67.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 1525331 - Move fields from union to individual subclasses. r=jorendorff Differential Revision: https://phabricator.services.mozilla.com/D18857
js/src/frontend/BinASTParserBase.h
js/src/frontend/FullParseHandler.h
js/src/frontend/ParseNode.cpp
js/src/frontend/ParseNode.h
--- a/js/src/frontend/BinASTParserBase.h
+++ b/js/src/frontend/BinASTParserBase.h
@@ -41,18 +41,17 @@ class BinASTParserBase : private JS::Aut
 
   void trace(JSTracer* trc) {
     TraceListNode::TraceList(trc, traceListHead_);
     doTrace(trc);
   }
 
  public:
   ParseNode* allocParseNode(size_t size) {
-    MOZ_ASSERT(size == sizeof(ParseNode));
-    return static_cast<ParseNode*>(nodeAlloc_.allocNode());
+    return static_cast<ParseNode*>(nodeAlloc_.allocNode(size));
   }
 
   JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
 
   // Needs access to AutoGCRooter.
   friend void TraceBinParser(JSTracer* trc, JS::AutoGCRooter* parser);
 
  protected:
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -31,18 +31,17 @@ enum class SourceKind {
 };
 
 // Parse handler used when generating a full parse tree for all code which the
 // parser encounters.
 class FullParseHandler {
   ParseNodeAllocator allocator;
 
   ParseNode* allocParseNode(size_t size) {
-    MOZ_ASSERT(size == sizeof(ParseNode));
-    return static_cast<ParseNode*>(allocator.allocNode());
+    return static_cast<ParseNode*>(allocator.allocNode(size));
   }
 
   /*
    * If this is a full parse to construct the bytecode for a function that
    * was previously lazily parsed, that lazy function and the current index
    * into its inner functions. We do not want to reparse the inner functions.
    */
   const Rooted<LazyScript*> lazyOuterFunction_;
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -30,30 +30,30 @@ void ListNode::checkConsistency() const 
     while (pn) {
       last = pn;
       pn = pn->pn_next;
       actualCount++;
     }
 
     tailNode = &last->pn_next;
   } else {
-    tailNode = &pn_u.list.head;
+    tailNode = &head_;
   }
   MOZ_ASSERT(tail() == tailNode);
   MOZ_ASSERT(count() == actualCount);
 }
 #endif
 
 /*
  * Allocate a ParseNode from parser's node freelist or, failing that, from
  * cx's temporary arena.
  */
-void* ParseNodeAllocator::allocNode() {
+void* ParseNodeAllocator::allocNode(size_t size) {
   LifoAlloc::AutoFallibleScope fallibleAllocator(&alloc);
-  void* p = alloc.alloc(sizeof(ParseNode));
+  void* p = alloc.alloc(size);
   if (!p) {
     ReportOutOfMemory(cx);
   }
   return p;
 }
 
 ParseNode* ParseNode::appendOrCreateList(ParseNodeKind kind, ParseNode* left,
                                          ParseNode* right,
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -645,28 +645,26 @@ class ParseNode {
   ParseNode(ParseNodeKind kind, JSOp op)
       : pn_type(kind),
         pn_op(op),
         pn_parens(false),
         pn_rhs_anon_fun(false),
         pn_pos(0, 0),
         pn_next(nullptr) {
     MOZ_ASSERT(kind < ParseNodeKind::Limit);
-    memset(&pn_u, 0, sizeof pn_u);
   }
 
   ParseNode(ParseNodeKind kind, JSOp op, const TokenPos& pos)
       : pn_type(kind),
         pn_op(op),
         pn_parens(false),
         pn_rhs_anon_fun(false),
         pn_pos(pos),
         pn_next(nullptr) {
     MOZ_ASSERT(kind < ParseNodeKind::Limit);
-    memset(&pn_u, 0, sizeof pn_u);
   }
 
   JSOp getOp() const { return JSOp(pn_op); }
   void setOp(JSOp op) { pn_op = op; }
   bool isOp(JSOp op) const { return getOp() == op; }
 
   ParseNodeKind getKind() const {
     MOZ_ASSERT(pn_type < ParseNodeKind::Limit);
@@ -692,109 +690,16 @@ class ParseNode {
   void setInParens(bool enabled) { pn_parens = enabled; }
 
   bool isDirectRHSAnonFunction() const { return pn_rhs_anon_fun; }
   void setDirectRHSAnonFunction(bool enabled) { pn_rhs_anon_fun = enabled; }
 
   TokenPos pn_pos;    /* two 16-bit pairs here, for 64 bits */
   ParseNode* pn_next; /* intrinsic link in parent PN_LIST */
 
-  union {
-    struct { /* list of next-linked nodes */
-     private:
-      friend class ListNode;
-      ParseNode* head;  /* first node in list */
-      ParseNode** tail; /* ptr to last node's pn_next in list */
-      uint32_t count;   /* number of nodes in list */
-      uint32_t xflags;  /* see ListNode class */
-    } list;
-    struct { /* ternary: if, for(;;), ?: */
-     private:
-      friend class TernaryNode;
-      ParseNode* kid1; /* condition, discriminant, etc. */
-      ParseNode* kid2; /* then-part, case list, etc. */
-      ParseNode* kid3; /* else-part, default case, etc. */
-    } ternary;
-    struct { /* two kids if binary */
-     private:
-      friend class BinaryNode;
-      friend class ForNode;
-      friend class ClassMethod;
-      friend class PropertyAccess;
-      friend class SwitchStatement;
-      ParseNode* left;
-      ParseNode* right;
-      union {
-        unsigned iflags; /* JSITER_* flags for ParseNodeKind::For node */
-        bool isStatic;   /* only for ParseNodeKind::ClassMethod */
-        bool hasDefault; /* only for ParseNodeKind::Switch */
-      };
-    } binary;
-    struct { /* one kid if unary */
-     private:
-      friend class UnaryNode;
-      ParseNode* kid;
-      bool prologue; /* directive prologue member */
-    } unary;
-    struct { /* name, labeled statement, etc. */
-     private:
-      friend class NameNode;
-      JSAtom* atom;          /* lexical name or label atom */
-      ParseNode* initOrStmt; /* var initializer, argument default,
-                              * or label statement target */
-    } name;
-    struct {
-     private:
-      friend class ClassField;
-      ParseNode* name;
-      ParseNode* initializer; /* field initializer - optional */
-    } field;
-    struct {
-     private:
-      friend class RegExpLiteral;
-      ObjectBox* objbox;
-    } regexp;
-    struct {
-     private:
-      friend class FunctionNode;
-      FunctionBox* funbox;
-      ParseNode* body;
-      FunctionSyntaxKind syntaxKind;
-    } function;
-    struct {
-     private:
-      friend class ModuleNode;
-      ParseNode* body;
-    } module;
-    struct {
-     private:
-      friend class LexicalScopeNode;
-      LexicalScope::Data* bindings;
-      ParseNode* body;
-    } scope;
-    struct {
-     private:
-      friend class NumericLiteral;
-      double value;              /* aligned numeric literal value */
-      DecimalPoint decimalPoint; /* Whether the number has a decimal point */
-    } number;
-#ifdef ENABLE_BIGINT
-    struct {
-     private:
-      friend class BigIntLiteral;
-      BigIntBox* box;
-    } bigint;
-#endif
-    class {
-     private:
-      friend class LoopControlStatement;
-      PropertyName* label; /* target of break/continue statement */
-    } loopControl;
-  } pn_u;
-
  public:
   /*
    * If |left| is a list of the given kind/left-associative op, append
    * |right| to it and return |left|.  Otherwise return a [left, right] list.
    */
   static ParseNode* appendOrCreateList(ParseNodeKind kind, ParseNode* left,
                                        ParseNode* right,
                                        FullParseHandler* handler,
@@ -890,101 +795,103 @@ class NullaryNode : public ParseNode {
   }
 
 #ifdef DEBUG
   void dump(GenericPrinter& out);
 #endif
 };
 
 class NameNode : public ParseNode {
+  JSAtom* atom_;         /* lexical name or label atom */
+  ParseNode* initOrStmt; /* var initializer, argument default, or label
+                            statement target */
+
  protected:
   NameNode(ParseNodeKind kind, JSOp op, JSAtom* atom, ParseNode* initOrStmt,
            const TokenPos& pos)
-      : ParseNode(kind, op, pos) {
-    pn_u.name.atom = atom;
-    pn_u.name.initOrStmt = initOrStmt;
+      : ParseNode(kind, op, pos), atom_(atom), initOrStmt(initOrStmt) {
     MOZ_ASSERT(is<NameNode>());
   }
 
  public:
   NameNode(ParseNodeKind kind, JSOp op, JSAtom* atom, const TokenPos& pos)
-      : ParseNode(kind, op, pos) {
-    pn_u.name.atom = atom;
-    pn_u.name.initOrStmt = nullptr;
+      : ParseNode(kind, op, pos), atom_(atom), initOrStmt(nullptr) {
     MOZ_ASSERT(is<NameNode>());
   }
 
   static bool test(const ParseNode& node) { return node.isArity(PN_NAME); }
 
   static constexpr ParseNodeArity arity() { return PN_NAME; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
-    if (pn_u.name.initOrStmt) {
-      if (!visitor.visit(pn_u.name.initOrStmt)) {
+    if (initOrStmt) {
+      if (!visitor.visit(initOrStmt)) {
         return false;
       }
     }
     return true;
   }
 
 #ifdef DEBUG
   void dump(GenericPrinter& out, int indent);
 #endif
 
-  JSAtom* atom() const { return pn_u.name.atom; }
+  JSAtom* atom() const { return atom_; }
 
   PropertyName* name() const {
     MOZ_ASSERT(isKind(ParseNodeKind::Name));
     return atom()->asPropertyName();
   }
 
-  ParseNode* initializer() const { return pn_u.name.initOrStmt; }
+  ParseNode* initializer() const { return initOrStmt; }
 
-  void setAtom(JSAtom* atom) { pn_u.name.atom = atom; }
+  void setAtom(JSAtom* atom) { atom_ = atom; }
 
-  void setInitializer(ParseNode* init) { pn_u.name.initOrStmt = init; }
+  void setInitializer(ParseNode* init) { initOrStmt = init; }
 };
 
 inline bool ParseNode::isName(PropertyName* name) const {
   return getKind() == ParseNodeKind::Name && as<NameNode>().name() == name;
 }
 
 class UnaryNode : public ParseNode {
+  ParseNode* kid_;
+  bool prologue; /* directive prologue member */
+
  public:
   UnaryNode(ParseNodeKind kind, const TokenPos& pos, ParseNode* kid)
-      : ParseNode(kind, JSOP_NOP, pos) {
-    pn_u.unary.kid = kid;
+      : ParseNode(kind, JSOP_NOP, pos), kid_(kid), prologue(false) {
     MOZ_ASSERT(is<UnaryNode>());
   }
 
   static bool test(const ParseNode& node) { return node.isArity(PN_UNARY); }
 
   static constexpr ParseNodeArity arity() { return PN_UNARY; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
-    if (pn_u.unary.kid) {
-      if (!visitor.visit(pn_u.unary.kid)) {
+    if (kid_) {
+      if (!visitor.visit(kid_)) {
         return false;
       }
     }
     return true;
   }
 
 #ifdef DEBUG
   void dump(GenericPrinter& out, int indent);
 #endif
 
-  ParseNode* kid() const { return pn_u.unary.kid; }
+  ParseNode* kid() const { return kid_; }
 
   /* Return true if this node appears in a Directive Prologue. */
-  bool isDirectivePrologueMember() const { return pn_u.unary.prologue; }
+  bool isDirectivePrologueMember() const { return prologue; }
 
-  void setIsDirectivePrologueMember() { pn_u.unary.prologue = true; }
+  void setIsDirectivePrologueMember() { prologue = true; }
 
   /*
    * Non-null if this is a statement node which could be a member of a
    * Directive Prologue: an expression statement consisting of a single
    * string literal.
    *
    * This considers only the node and its children, not its context. After
    * parsing, check the node's prologue flag to see if it is indeed part of
@@ -1000,68 +907,69 @@ class UnaryNode : public ParseNode {
       if (kid()->isKind(ParseNodeKind::StringExpr) && !kid()->isInParens()) {
         return kid()->as<NameNode>().atom();
       }
     }
     return nullptr;
   }
 
   // Methods used by FoldConstants.cpp.
-  ParseNode** unsafeKidReference() { return &pn_u.unary.kid; }
+  ParseNode** unsafeKidReference() { return &kid_; }
 };
 
 class BinaryNode : public ParseNode {
+  ParseNode* left_;
+  ParseNode* right_;
+
  public:
   BinaryNode(ParseNodeKind kind, JSOp op, const TokenPos& pos, ParseNode* left,
              ParseNode* right)
-      : ParseNode(kind, op, pos) {
-    pn_u.binary.left = left;
-    pn_u.binary.right = right;
+      : ParseNode(kind, op, pos), left_(left), right_(right) {
     MOZ_ASSERT(is<BinaryNode>());
   }
 
   BinaryNode(ParseNodeKind kind, JSOp op, ParseNode* left, ParseNode* right)
-      : ParseNode(kind, op, TokenPos::box(left->pn_pos, right->pn_pos)) {
-    pn_u.binary.left = left;
-    pn_u.binary.right = right;
+      : ParseNode(kind, op, TokenPos::box(left->pn_pos, right->pn_pos)),
+        left_(left),
+        right_(right) {
     MOZ_ASSERT(is<BinaryNode>());
   }
 
   static bool test(const ParseNode& node) { return node.isArity(PN_BINARY); }
 
   static constexpr ParseNodeArity arity() { return PN_BINARY; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
-    if (pn_u.binary.left) {
-      if (!visitor.visit(pn_u.binary.left)) {
+    if (left_) {
+      if (!visitor.visit(left_)) {
         return false;
       }
     }
-    if (pn_u.binary.right) {
-      if (!visitor.visit(pn_u.binary.right)) {
+    if (right_) {
+      if (!visitor.visit(right_)) {
         return false;
       }
     }
     return true;
   }
 
 #ifdef DEBUG
   void dump(GenericPrinter& out, int indent);
 #endif
 
-  ParseNode* left() const { return pn_u.binary.left; }
+  ParseNode* left() const { return left_; }
 
-  ParseNode* right() const { return pn_u.binary.right; }
+  ParseNode* right() const { return right_; }
 
   // Methods used by FoldConstants.cpp.
   // callers are responsible for keeping the list consistent.
-  ParseNode** unsafeLeftReference() { return &pn_u.binary.left; }
+  ParseNode** unsafeLeftReference() { return &left_; }
 
-  ParseNode** unsafeRightReference() { return &pn_u.binary.right; }
+  ParseNode** unsafeRightReference() { return &right_; }
 };
 
 class AssignmentNode : public BinaryNode {
  public:
   AssignmentNode(ParseNodeKind kind, JSOp op, ParseNode* left, ParseNode* right)
       : BinaryNode(kind, op, TokenPos(left->pn_pos.begin, right->pn_pos.end),
                    left, right) {}
 
@@ -1070,101 +978,109 @@ class AssignmentNode : public BinaryNode
     bool match = ParseNodeKind::AssignmentStart <= kind &&
                  kind <= ParseNodeKind::AssignmentLast;
     MOZ_ASSERT_IF(match, node.is<BinaryNode>());
     return match;
   }
 };
 
 class ForNode : public BinaryNode {
+  unsigned iflags_; /* JSITER_* flags */
+
  public:
   ForNode(const TokenPos& pos, ParseNode* forHead, ParseNode* body,
           unsigned iflags)
       : BinaryNode(ParseNodeKind::ForStmt,
                    forHead->isKind(ParseNodeKind::ForIn) ? JSOP_ITER : JSOP_NOP,
-                   pos, forHead, body) {
+                   pos, forHead, body),
+        iflags_(iflags) {
     MOZ_ASSERT(forHead->isKind(ParseNodeKind::ForIn) ||
                forHead->isKind(ParseNodeKind::ForOf) ||
                forHead->isKind(ParseNodeKind::ForHead));
-    pn_u.binary.iflags = iflags;
   }
 
   static bool test(const ParseNode& node) {
     bool match = node.isKind(ParseNodeKind::ForStmt);
     MOZ_ASSERT_IF(match, node.is<BinaryNode>());
     return match;
   }
 
   TernaryNode* head() const { return &left()->as<TernaryNode>(); }
 
   ParseNode* body() const { return right(); }
 
-  unsigned iflags() const { return pn_u.binary.iflags; }
+  unsigned iflags() const { return iflags_; }
 };
 
 class TernaryNode : public ParseNode {
+  ParseNode* kid1_; /* condition, discriminant, etc. */
+  ParseNode* kid2_; /* then-part, case list, etc. */
+  ParseNode* kid3_; /* else-part, default case, etc. */
+
  public:
   TernaryNode(ParseNodeKind kind, ParseNode* kid1, ParseNode* kid2,
               ParseNode* kid3)
       : TernaryNode(kind, kid1, kid2, kid3,
                     TokenPos((kid1 ? kid1 : kid2 ? kid2 : kid3)->pn_pos.begin,
                              (kid3 ? kid3 : kid2 ? kid2 : kid1)->pn_pos.end)) {}
 
   TernaryNode(ParseNodeKind kind, ParseNode* kid1, ParseNode* kid2,
               ParseNode* kid3, const TokenPos& pos)
-      : ParseNode(kind, JSOP_NOP, pos) {
-    pn_u.ternary.kid1 = kid1;
-    pn_u.ternary.kid2 = kid2;
-    pn_u.ternary.kid3 = kid3;
+      : ParseNode(kind, JSOP_NOP, pos), kid1_(kid1), kid2_(kid2), kid3_(kid3) {
     MOZ_ASSERT(is<TernaryNode>());
   }
 
   static bool test(const ParseNode& node) { return node.isArity(PN_TERNARY); }
 
   static constexpr ParseNodeArity arity() { return PN_TERNARY; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
-    if (pn_u.ternary.kid1) {
-      if (!visitor.visit(pn_u.ternary.kid1)) {
+    if (kid1_) {
+      if (!visitor.visit(kid1_)) {
         return false;
       }
     }
-    if (pn_u.ternary.kid2) {
-      if (!visitor.visit(pn_u.ternary.kid2)) {
+    if (kid2_) {
+      if (!visitor.visit(kid2_)) {
         return false;
       }
     }
-    if (pn_u.ternary.kid3) {
-      if (!visitor.visit(pn_u.ternary.kid3)) {
+    if (kid3_) {
+      if (!visitor.visit(kid3_)) {
         return false;
       }
     }
     return true;
   }
 
 #ifdef DEBUG
   void dump(GenericPrinter& out, int indent);
 #endif
 
-  ParseNode* kid1() const { return pn_u.ternary.kid1; }
+  ParseNode* kid1() const { return kid1_; }
 
-  ParseNode* kid2() const { return pn_u.ternary.kid2; }
+  ParseNode* kid2() const { return kid2_; }
 
-  ParseNode* kid3() const { return pn_u.ternary.kid3; }
+  ParseNode* kid3() const { return kid3_; }
 
   // Methods used by FoldConstants.cpp.
-  ParseNode** unsafeKid1Reference() { return &pn_u.ternary.kid1; }
+  ParseNode** unsafeKid1Reference() { return &kid1_; }
 
-  ParseNode** unsafeKid2Reference() { return &pn_u.ternary.kid2; }
+  ParseNode** unsafeKid2Reference() { return &kid2_; }
 
-  ParseNode** unsafeKid3Reference() { return &pn_u.ternary.kid3; }
+  ParseNode** unsafeKid3Reference() { return &kid3_; }
 };
 
 class ListNode : public ParseNode {
+  ParseNode* head_;  /* first node in list */
+  ParseNode** tail_; /* ptr to last node's pn_next in list */
+  uint32_t count_;   /* number of nodes in list */
+  uint32_t xflags;
+
  private:
   // xflags bits.
 
   // Statement list has top-level function statements.
   static constexpr uint32_t hasTopLevelFunctionDeclarationsBit = 0x01;
 
   // One or more of
   //   * array has holes
@@ -1203,36 +1119,36 @@ class ListNode : public ParseNode {
 
   ListNode(ParseNodeKind kind, JSOp op, const TokenPos& pos)
       : ParseNode(kind, op, pos) {
     makeEmpty();
     MOZ_ASSERT(is<ListNode>());
   }
 
   ListNode(ParseNodeKind kind, JSOp op, ParseNode* kid)
-      : ParseNode(kind, op, kid->pn_pos) {
+      : ParseNode(kind, op, kid->pn_pos),
+        head_(kid),
+        tail_(&kid->pn_next),
+        count_(1),
+        xflags(0) {
     if (kid->pn_pos.begin < pn_pos.begin) {
       pn_pos.begin = kid->pn_pos.begin;
     }
     pn_pos.end = kid->pn_pos.end;
 
-    pn_u.list.head = kid;
-    pn_u.list.tail = &kid->pn_next;
-    pn_u.list.count = 1;
-    pn_u.list.xflags = 0;
     MOZ_ASSERT(is<ListNode>());
   }
 
   static bool test(const ParseNode& node) { return node.isArity(PN_LIST); }
 
   static constexpr ParseNodeArity arity() { return PN_LIST; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
-    ParseNode** listp = &pn_u.list.head;
+    ParseNode** listp = &head_;
     for (; *listp; listp = &(*listp)->pn_next) {
       // Don't use PN*& because we want to check if it changed, so we can use
       // ReplaceNode
       ParseNode* pn = *listp;
       if (!visitor.visit(pn)) {
         return false;
       }
       if (pn != *listp) {
@@ -1242,72 +1158,72 @@ class ListNode : public ParseNode {
     unsafeReplaceTail(listp);
     return true;
   }
 
 #ifdef DEBUG
   void dump(GenericPrinter& out, int indent);
 #endif
 
-  ParseNode* head() const { return pn_u.list.head; }
+  ParseNode* head() const { return head_; }
 
-  ParseNode** tail() const { return pn_u.list.tail; }
+  ParseNode** tail() const { return tail_; }
 
-  uint32_t count() const { return pn_u.list.count; }
+  uint32_t count() const { return count_; }
 
   bool empty() const { return count() == 0; }
 
   MOZ_MUST_USE bool hasTopLevelFunctionDeclarations() const {
     MOZ_ASSERT(isKind(ParseNodeKind::StatementList));
-    return pn_u.list.xflags & hasTopLevelFunctionDeclarationsBit;
+    return xflags & hasTopLevelFunctionDeclarationsBit;
   }
 
   MOZ_MUST_USE bool emittedTopLevelFunctionDeclarations() const {
     MOZ_ASSERT(isKind(ParseNodeKind::StatementList));
     MOZ_ASSERT(hasTopLevelFunctionDeclarations());
-    return pn_u.list.xflags & emittedTopLevelFunctionDeclarationsBit;
+    return xflags & emittedTopLevelFunctionDeclarationsBit;
   }
 
   MOZ_MUST_USE bool hasArrayHoleOrSpread() const {
     MOZ_ASSERT(isKind(ParseNodeKind::ArrayExpr));
-    return pn_u.list.xflags & hasArrayHoleOrSpreadBit;
+    return xflags & hasArrayHoleOrSpreadBit;
   }
 
   MOZ_MUST_USE bool hasNonConstInitializer() const {
     MOZ_ASSERT(isKind(ParseNodeKind::ArrayExpr) ||
                isKind(ParseNodeKind::ObjectExpr));
-    return pn_u.list.xflags & hasNonConstInitializerBit;
+    return xflags & hasNonConstInitializerBit;
   }
 
   void setHasTopLevelFunctionDeclarations() {
     MOZ_ASSERT(isKind(ParseNodeKind::StatementList));
-    pn_u.list.xflags |= hasTopLevelFunctionDeclarationsBit;
+    xflags |= hasTopLevelFunctionDeclarationsBit;
   }
 
   void setEmittedTopLevelFunctionDeclarations() {
     MOZ_ASSERT(isKind(ParseNodeKind::StatementList));
     MOZ_ASSERT(hasTopLevelFunctionDeclarations());
-    pn_u.list.xflags |= emittedTopLevelFunctionDeclarationsBit;
+    xflags |= emittedTopLevelFunctionDeclarationsBit;
   }
 
   void setHasArrayHoleOrSpread() {
     MOZ_ASSERT(isKind(ParseNodeKind::ArrayExpr));
-    pn_u.list.xflags |= hasArrayHoleOrSpreadBit;
+    xflags |= hasArrayHoleOrSpreadBit;
   }
 
   void setHasNonConstInitializer() {
     MOZ_ASSERT(isKind(ParseNodeKind::ArrayExpr) ||
                isKind(ParseNodeKind::ObjectExpr));
-    pn_u.list.xflags |= hasNonConstInitializerBit;
+    xflags |= hasNonConstInitializerBit;
   }
 
   void unsetHasNonConstInitializer() {
     MOZ_ASSERT(isKind(ParseNodeKind::ArrayExpr) ||
                isKind(ParseNodeKind::ObjectExpr));
-    pn_u.list.xflags &= ~hasNonConstInitializerBit;
+    xflags &= ~hasNonConstInitializerBit;
   }
 
   /*
    * Compute a pointer to the last element in a singly-linked list. NB: list
    * must be non-empty -- this is asserted!
    */
   ParseNode* last() const {
     MOZ_ASSERT(!empty());
@@ -1328,72 +1244,72 @@ class ListNode : public ParseNode {
   void replaceLast(ParseNode* node) {
     MOZ_ASSERT(!empty());
     pn_pos.end = node->pn_pos.end;
 
     ParseNode* item = head();
     ParseNode* lastNode = last();
     MOZ_ASSERT(item);
     if (item == lastNode) {
-      pn_u.list.head = node;
+      head_ = node;
     } else {
       while (item->pn_next != lastNode) {
         MOZ_ASSERT(item->pn_next);
         item = item->pn_next;
       }
       item->pn_next = node;
     }
-    pn_u.list.tail = &node->pn_next;
+    tail_ = &node->pn_next;
   }
 
   void makeEmpty() {
-    pn_u.list.head = nullptr;
-    pn_u.list.tail = &pn_u.list.head;
-    pn_u.list.count = 0;
-    pn_u.list.xflags = 0;
+    head_ = nullptr;
+    tail_ = &head_;
+    count_ = 0;
+    xflags = 0;
   }
 
   void append(ParseNode* item) {
     MOZ_ASSERT(item->pn_pos.begin >= pn_pos.begin);
     appendWithoutOrderAssumption(item);
   }
 
   void appendWithoutOrderAssumption(ParseNode* item) {
     pn_pos.end = item->pn_pos.end;
-    *pn_u.list.tail = item;
-    pn_u.list.tail = &item->pn_next;
-    pn_u.list.count++;
+    *tail_ = item;
+    tail_ = &item->pn_next;
+    count_++;
   }
 
   void prepend(ParseNode* item) {
-    item->pn_next = pn_u.list.head;
-    pn_u.list.head = item;
-    if (pn_u.list.tail == &pn_u.list.head) {
-      pn_u.list.tail = &item->pn_next;
+    item->pn_next = head_;
+    head_ = item;
+    if (tail_ == &head_) {
+      tail_ = &item->pn_next;
     }
-    pn_u.list.count++;
+    count_++;
   }
 
   void prependAndUpdatePos(ParseNode* item) {
     prepend(item);
     pn_pos.begin = item->pn_pos.begin;
   }
 
   // Methods used by FoldConstants.cpp.
   // Caller is responsible for keeping the list consistent.
-  ParseNode** unsafeHeadReference() { return &pn_u.list.head; }
+  ParseNode** unsafeHeadReference() { return &head_; }
 
   void unsafeReplaceTail(ParseNode** newTail) {
-    pn_u.list.tail = newTail;
+    tail_ = newTail;
     checkConsistency();
   }
 
   void unsafeDecrementCount() {
     MOZ_ASSERT(count() > 1);
-    pn_u.list.count--;
+    count_--;
   }
 
  private:
   // Classes to iterate over ListNode contents:
   //
   // Usage:
   //   ListNode* list;
   //   for (ParseNode* item : list->contents()) {
@@ -1489,102 +1405,110 @@ inline bool ParseNode::isForLoopDeclarat
     MOZ_ASSERT(!as<ListNode>().empty());
     return true;
   }
 
   return false;
 }
 
 class FunctionNode : public ParseNode {
+  FunctionBox* funbox_;
+  ParseNode* body_;
+  FunctionSyntaxKind syntaxKind_;
+
  public:
   FunctionNode(FunctionSyntaxKind syntaxKind, const TokenPos& pos)
-      : ParseNode(ParseNodeKind::Function, JSOP_NOP, pos) {
-    MOZ_ASSERT(!pn_u.function.body);
-    MOZ_ASSERT(!pn_u.function.funbox);
+      : ParseNode(ParseNodeKind::Function, JSOP_NOP, pos),
+        funbox_(nullptr),
+        body_(nullptr),
+        syntaxKind_(syntaxKind) {
+    MOZ_ASSERT(!body_);
+    MOZ_ASSERT(!funbox_);
     MOZ_ASSERT(is<FunctionNode>());
-    pn_u.function.syntaxKind = syntaxKind;
   }
 
   static bool test(const ParseNode& node) {
     bool match = node.isKind(ParseNodeKind::Function);
     MOZ_ASSERT_IF(match, node.isArity(PN_FUNCTION));
     return match;
   }
 
   static constexpr ParseNodeArity arity() { return PN_FUNCTION; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
     // Note: body is null for lazily-parsed functions.
-    if (pn_u.function.body) {
-      if (!visitor.visit(pn_u.function.body)) {
+    if (body_) {
+      if (!visitor.visit(body_)) {
         return false;
       }
     }
     return true;
   }
 
 #ifdef DEBUG
   void dump(GenericPrinter& out, int indent);
 #endif
 
-  FunctionBox* funbox() const { return pn_u.function.funbox; }
+  FunctionBox* funbox() const { return funbox_; }
 
-  ListNode* body() const {
-    return pn_u.function.body ? &pn_u.function.body->as<ListNode>() : nullptr;
-  }
+  ListNode* body() const { return body_ ? &body_->as<ListNode>() : nullptr; }
 
-  void setFunbox(FunctionBox* funbox) { pn_u.function.funbox = funbox; }
+  void setFunbox(FunctionBox* funbox) { funbox_ = funbox; }
 
-  void setBody(ListNode* body) { pn_u.function.body = body; }
+  void setBody(ListNode* body) { body_ = body; }
 
-  FunctionSyntaxKind syntaxKind() const { return pn_u.function.syntaxKind; }
+  FunctionSyntaxKind syntaxKind() const { return syntaxKind_; }
 
   bool functionIsHoisted() const {
     return syntaxKind() == FunctionSyntaxKind::Statement;
   }
 };
 
 class ModuleNode : public ParseNode {
+  ParseNode* body_;
+
  public:
   explicit ModuleNode(const TokenPos& pos)
-      : ParseNode(ParseNodeKind::Module, JSOP_NOP, pos) {
-    MOZ_ASSERT(!pn_u.module.body);
+      : ParseNode(ParseNodeKind::Module, JSOP_NOP, pos), body_(nullptr) {
+    MOZ_ASSERT(!body_);
     MOZ_ASSERT(is<ModuleNode>());
   }
 
   static bool test(const ParseNode& node) {
     bool match = node.isKind(ParseNodeKind::Module);
     MOZ_ASSERT_IF(match, node.isArity(PN_MODULE));
     return match;
   }
 
   static constexpr ParseNodeArity arity() { return PN_MODULE; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
-    return visitor.visit(pn_u.module.body);
+    return visitor.visit(body_);
   }
 
 #ifdef DEBUG
   void dump(GenericPrinter& out, int indent);
 #endif
 
-  ListNode* body() const { return &pn_u.module.body->as<ListNode>(); }
+  ListNode* body() const { return &body_->as<ListNode>(); }
 
-  void setBody(ListNode* body) { pn_u.module.body = body; }
+  void setBody(ListNode* body) { body_ = body; }
 };
 
 class NumericLiteral : public ParseNode {
+  double value_;              /* aligned numeric literal value */
+  DecimalPoint decimalPoint_; /* Whether the number has a decimal point */
+
  public:
   NumericLiteral(double value, DecimalPoint decimalPoint, const TokenPos& pos)
-      : ParseNode(ParseNodeKind::NumberExpr, JSOP_NOP, pos) {
-    pn_u.number.value = value;
-    pn_u.number.decimalPoint = decimalPoint;
-  }
+      : ParseNode(ParseNodeKind::NumberExpr, JSOP_NOP, pos),
+        value_(value),
+        decimalPoint_(decimalPoint) {}
 
   static bool test(const ParseNode& node) {
     bool match = node.isKind(ParseNodeKind::NumberExpr);
     MOZ_ASSERT_IF(match, node.isArity(PN_NUMBER));
     return match;
   }
 
   static constexpr ParseNodeArity arity() { return PN_NUMBER; }
@@ -1593,32 +1517,32 @@ class NumericLiteral : public ParseNode 
   bool accept(Visitor& visitor) {
     return true;
   }
 
 #ifdef DEBUG
   void dump(GenericPrinter& out, int indent);
 #endif
 
-  double value() const { return pn_u.number.value; }
+  double value() const { return value_; }
 
-  DecimalPoint decimalPoint() const { return pn_u.number.decimalPoint; }
+  DecimalPoint decimalPoint() const { return decimalPoint_; }
 
-  void setValue(double v) { pn_u.number.value = v; }
+  void setValue(double v) { value_ = v; }
 
-  void setDecimalPoint(DecimalPoint d) { pn_u.number.decimalPoint = d; }
+  void setDecimalPoint(DecimalPoint d) { decimalPoint_ = d; }
 };
 
 #ifdef ENABLE_BIGINT
 class BigIntLiteral : public ParseNode {
+  BigIntBox* box_;
+
  public:
   BigIntLiteral(BigIntBox* bibox, const TokenPos& pos)
-      : ParseNode(ParseNodeKind::BigIntExpr, JSOP_NOP, pos) {
-    pn_u.bigint.box = bibox;
-  }
+      : ParseNode(ParseNodeKind::BigIntExpr, JSOP_NOP, pos), box_(bibox) {}
 
   static bool test(const ParseNode& node) {
     bool match = node.isKind(ParseNodeKind::BigIntExpr);
     MOZ_ASSERT_IF(match, node.isArity(PN_BIGINT));
     return match;
   }
 
   static constexpr ParseNodeArity arity() { return PN_BIGINT; }
@@ -1627,58 +1551,59 @@ class BigIntLiteral : public ParseNode {
   bool accept(Visitor& visitor) {
     return true;
   }
 
 #  ifdef DEBUG
   void dump(GenericPrinter& out, int indent);
 #  endif
 
-  BigIntBox* box() const { return pn_u.bigint.box; }
+  BigIntBox* box() const { return box_; }
 };
 #endif
 
 class LexicalScopeNode : public ParseNode {
+  LexicalScope::Data* bindings;
+  ParseNode* body;
+
  public:
   LexicalScopeNode(LexicalScope::Data* bindings, ParseNode* body)
-      : ParseNode(ParseNodeKind::LexicalScope, JSOP_NOP, body->pn_pos) {
-    pn_u.scope.bindings = bindings;
-    pn_u.scope.body = body;
-  }
+      : ParseNode(ParseNodeKind::LexicalScope, JSOP_NOP, body->pn_pos),
+        bindings(bindings),
+        body(body) {}
 
   static bool test(const ParseNode& node) {
     bool match = node.isKind(ParseNodeKind::LexicalScope);
     MOZ_ASSERT_IF(match, node.isArity(PN_SCOPE));
     return match;
   }
 
   static constexpr ParseNodeArity arity() { return PN_SCOPE; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
-    return visitor.visit(pn_u.scope.body);
+    return visitor.visit(body);
   }
 
 #ifdef DEBUG
   void dump(GenericPrinter& out, int indent);
 #endif
 
   Handle<LexicalScope::Data*> scopeBindings() const {
     MOZ_ASSERT(!isEmptyScope());
     // Bindings' GC safety depend on the presence of an AutoKeepAtoms that
     // the rest of the frontend also depends on.
-    return Handle<LexicalScope::Data*>::fromMarkedLocation(
-        &pn_u.scope.bindings);
+    return Handle<LexicalScope::Data*>::fromMarkedLocation(&bindings);
   }
 
-  ParseNode* scopeBody() const { return pn_u.scope.body; }
+  ParseNode* scopeBody() const { return body; }
 
-  void setScopeBody(ParseNode* body) { pn_u.scope.body = body; }
+  void setScopeBody(ParseNode* body) { this->body = body; }
 
-  bool isEmptyScope() const { return !pn_u.scope.bindings; }
+  bool isEmptyScope() const { return !bindings; }
 };
 
 class LabeledStatement : public NameNode {
  public:
   LabeledStatement(PropertyName* label, ParseNode* stmt, uint32_t begin)
       : NameNode(ParseNodeKind::LabelStmt, JSOP_NOP, label, stmt,
                  TokenPos(begin, stmt->pn_pos.end)) {}
 
@@ -1713,29 +1638,30 @@ class CaseClause : public BinaryNode {
     bool match = node.isKind(ParseNodeKind::Case);
     MOZ_ASSERT_IF(match, node.is<BinaryNode>());
     MOZ_ASSERT_IF(match, node.isOp(JSOP_NOP));
     return match;
   }
 };
 
 class LoopControlStatement : public ParseNode {
+  PropertyName* label_; /* target of break/continue statement */
+
  protected:
   LoopControlStatement(ParseNodeKind kind, PropertyName* label,
                        const TokenPos& pos)
-      : ParseNode(kind, JSOP_NOP, pos) {
+      : ParseNode(kind, JSOP_NOP, pos), label_(label) {
     MOZ_ASSERT(kind == ParseNodeKind::BreakStmt ||
                kind == ParseNodeKind::ContinueStmt);
-    pn_u.loopControl.label = label;
     MOZ_ASSERT(is<LoopControlStatement>());
   }
 
  public:
   /* Label associated with this break/continue statement, if any. */
-  PropertyName* label() const { return pn_u.loopControl.label; }
+  PropertyName* label() const { return label_; }
 
 #ifdef DEBUG
   void dump(GenericPrinter& out, int indent);
 #endif
 
   static bool test(const ParseNode& node) {
     bool match = node.isKind(ParseNodeKind::BreakStmt) ||
                  node.isKind(ParseNodeKind::ContinueStmt);
@@ -1892,23 +1818,24 @@ class BooleanLiteral : public NullaryNod
     bool match = node.isKind(ParseNodeKind::TrueExpr) ||
                  node.isKind(ParseNodeKind::FalseExpr);
     MOZ_ASSERT_IF(match, node.is<NullaryNode>());
     return match;
   }
 };
 
 class RegExpLiteral : public ParseNode {
+  ObjectBox* objbox_;
+
  public:
   RegExpLiteral(ObjectBox* reobj, const TokenPos& pos)
-      : ParseNode(ParseNodeKind::RegExpExpr, JSOP_REGEXP, pos) {
-    pn_u.regexp.objbox = reobj;
-  }
+      : ParseNode(ParseNodeKind::RegExpExpr, JSOP_REGEXP, pos),
+        objbox_(reobj) {}
 
-  ObjectBox* objbox() const { return pn_u.regexp.objbox; }
+  ObjectBox* objbox() const { return objbox_; }
 
 #ifdef DEBUG
   void dump(GenericPrinter& out, int indent);
 #endif
 
   static bool test(const ParseNode& node) {
     bool match = node.isKind(ParseNodeKind::RegExpExpr);
     MOZ_ASSERT_IF(match, node.isArity(PN_REGEXP));
@@ -1949,17 +1876,17 @@ class PropertyAccess : public BinaryNode
 
   NameNode& key() const { return right()->as<NameNode>(); }
 
   // Method used by BytecodeEmitter::emitPropLHS for optimization.
   // Those methods allow expression to temporarily be nullptr for
   // optimization purpose.
   ParseNode* maybeExpression() const { return left(); }
 
-  void setExpression(ParseNode* pn) { pn_u.binary.left = pn; }
+  void setExpression(ParseNode* pn) { *unsafeLeftReference() = pn; }
 
   PropertyName& name() const {
     return *right()->as<NameNode>().atom()->asPropertyName();
   }
 
   bool isSuper() const {
     // ParseNodeKind::SuperBase cannot result from any expression syntax.
     return expression().isKind(ParseNodeKind::SuperBase);
@@ -2007,120 +1934,124 @@ class CallSiteNode : public ListNode {
 
   ListNode* rawNodes() const {
     MOZ_ASSERT(head());
     return &head()->as<ListNode>();
   }
 };
 
 class ClassMethod : public BinaryNode {
+  bool isStatic_;
+
  public:
   /*
    * Method definitions often keep a name and function body that overlap,
    * so explicitly define the beginning and end here.
    */
   ClassMethod(ParseNode* name, ParseNode* body, JSOp op, bool isStatic)
       : BinaryNode(ParseNodeKind::ClassMethod, op,
-                   TokenPos(name->pn_pos.begin, body->pn_pos.end), name, body) {
-    pn_u.binary.isStatic = isStatic;
-  }
+                   TokenPos(name->pn_pos.begin, body->pn_pos.end), name, body),
+        isStatic_(isStatic) {}
 
   static bool test(const ParseNode& node) {
     bool match = node.isKind(ParseNodeKind::ClassMethod);
     MOZ_ASSERT_IF(match, node.is<BinaryNode>());
     return match;
   }
 
   ParseNode& name() const { return *left(); }
 
   FunctionNode& method() const { return right()->as<FunctionNode>(); }
 
-  bool isStatic() const { return pn_u.binary.isStatic; }
+  bool isStatic() const { return isStatic_; }
 };
 
 class ClassField : public ParseNode {
+  ParseNode* name_;
+  ParseNode* initializer_; /* field initializer - optional */
+
  public:
   ClassField(ParseNode* name, ParseNode* initializer)
       : ParseNode(ParseNodeKind::ClassField, JSOP_NOP,
                   initializer == nullptr
                       ? name->pn_pos
-                      : TokenPos::box(name->pn_pos, initializer->pn_pos)) {
-    pn_u.field.name = name;
-    pn_u.field.initializer = initializer;
-  }
+                      : TokenPos::box(name->pn_pos, initializer->pn_pos)),
+        name_(name),
+        initializer_(initializer) {}
 
   static bool test(const ParseNode& node) {
     bool match = node.isKind(ParseNodeKind::ClassField);
     MOZ_ASSERT_IF(match, node.isArity(PN_FIELD));
     return match;
   }
 
   static constexpr ParseNodeArity arity() { return PN_FIELD; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
-    if (!visitor.visit(pn_u.field.name)) {
+    if (!visitor.visit(name_)) {
       return false;
     }
-    if (pn_u.field.initializer) {
-      if (!visitor.visit(pn_u.field.initializer)) {
+    if (initializer_) {
+      if (!visitor.visit(initializer_)) {
         return false;
       }
     }
     return true;
   }
 
-  ParseNode& name() const { return *pn_u.field.name; }
+  ParseNode& name() const { return *name_; }
 
-  bool hasInitializer() const { return pn_u.field.initializer != nullptr; }
+  bool hasInitializer() const { return initializer_ != nullptr; }
 
-  ParseNode& initializer() const { return *pn_u.field.initializer; }
+  ParseNode& initializer() const { return *initializer_; }
 
 #ifdef DEBUG
   void dump(GenericPrinter& out, int indent);
 #endif
 };
 
 class SwitchStatement : public BinaryNode {
+  bool hasDefault_; /* only for ParseNodeKind::Switch */
+
  public:
   SwitchStatement(uint32_t begin, ParseNode* discriminant,
                   LexicalScopeNode* lexicalForCaseList, bool hasDefault)
       : BinaryNode(ParseNodeKind::SwitchStmt, JSOP_NOP,
                    TokenPos(begin, lexicalForCaseList->pn_pos.end),
-                   discriminant, lexicalForCaseList) {
+                   discriminant, lexicalForCaseList),
+        hasDefault_(hasDefault) {
 #ifdef DEBUG
     ListNode* cases = &lexicalForCaseList->scopeBody()->as<ListNode>();
     MOZ_ASSERT(cases->isKind(ParseNodeKind::StatementList));
     bool found = false;
     for (ParseNode* item : cases->contents()) {
       CaseClause* caseNode = &item->as<CaseClause>();
       if (caseNode->isDefault()) {
         found = true;
         break;
       }
     }
     MOZ_ASSERT(found == hasDefault);
 #endif
-
-    pn_u.binary.hasDefault = hasDefault;
   }
 
   static bool test(const ParseNode& node) {
     bool match = node.isKind(ParseNodeKind::SwitchStmt);
     MOZ_ASSERT_IF(match, node.is<BinaryNode>());
     return match;
   }
 
   ParseNode& discriminant() const { return *left(); }
 
   LexicalScopeNode& lexicalForCaseList() const {
     return right()->as<LexicalScopeNode>();
   }
 
-  bool hasDefault() const { return pn_u.binary.hasDefault; }
+  bool hasDefault() const { return hasDefault_; }
 };
 
 class ClassNames : public BinaryNode {
  public:
   ClassNames(ParseNode* outerBinding, ParseNode* innerBinding,
              const TokenPos& pos)
       : BinaryNode(ParseNodeKind::ClassNames, JSOP_NOP, pos, outerBinding,
                    innerBinding) {
@@ -2196,17 +2127,17 @@ class ClassNode : public TernaryNode {
 void DumpParseTree(ParseNode* pn, GenericPrinter& out, int indent = 0);
 #endif
 
 class ParseNodeAllocator {
  public:
   explicit ParseNodeAllocator(JSContext* cx, LifoAlloc& alloc)
       : cx(cx), alloc(alloc) {}
 
-  void* allocNode();
+  void* allocNode(size_t size);
 
  private:
   JSContext* cx;
   LifoAlloc& alloc;
 };
 
 inline bool ParseNode::isConstant() {
   switch (pn_type) {