Bug 1526031 - remove initOrStmt field from NameNode. r=jorendorff,arai
☠☠ backed out by c4d3d4113621 ☠ ☠
authorAshley Hauck <khyperia@mozilla.com>
Thu, 28 Feb 2019 01:29:19 +0000
changeset 519583 01d931a9140b35f9ea90a6a5a45956510f5ef7f1
parent 519582 4024f3814d1831602d7df302498b6ec0196f4a75
child 519584 c4d3d41136213af2b11a74d5e2a70aa227bd5d1e
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, arai
bugs1526031
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 1526031 - remove initOrStmt field from NameNode. r=jorendorff,arai Differential Revision: https://phabricator.services.mozilla.com/D19054
js/src/builtin/ReflectParse.cpp
js/src/frontend/BinASTParser.cpp
js/src/frontend/BinSource.yaml
js/src/frontend/BytecodeEmitter.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/tests/non262/expressions/constant-folded-labeled-statement.js
js/src/wasm/AsmJS.cpp
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -1934,18 +1934,17 @@ bool ASTSerializer::variableDeclaration(
 }
 
 bool ASTSerializer::variableDeclarator(ParseNode* pn, MutableHandleValue dst) {
   ParseNode* patternNode;
   ParseNode* initNode;
 
   if (pn->isKind(ParseNodeKind::Name)) {
     patternNode = pn;
-    initNode = pn->as<NameNode>().initializer();
-    MOZ_ASSERT_IF(initNode, pn->pn_pos.encloses(initNode->pn_pos));
+    initNode = nullptr;
   } else if (pn->isKind(ParseNodeKind::AssignExpr)) {
     AssignmentNode* assignNode = &pn->as<AssignmentNode>();
     patternNode = assignNode->left();
     initNode = assignNode->right();
     MOZ_ASSERT(pn->pn_pos.encloses(patternNode->pn_pos));
     MOZ_ASSERT(pn->pn_pos.encloses(initNode->pn_pos));
   } else {
     /* This happens for a destructuring declarator in a for-in/of loop. */
--- a/js/src/frontend/BinASTParser.cpp
+++ b/js/src/frontend/BinASTParser.cpp
@@ -2624,17 +2624,21 @@ JS::Result<ParseNode*> BinASTParser<Tok>
   MOZ_ASSERT(pc_->isFunctionBox());
 
   ParseContext::Scope lexicalScope(cx_, pc_, usedNames_);
   BINJS_TRY(lexicalScope.init(pc_));
   ListNode* params;
   ListNode* body;
   MOZ_TRY(parseGetterContents(length, &params, &body));
   MOZ_TRY(prependDirectivesToBody(body, directives));
-  BINJS_MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body));
+  BINJS_TRY_DECL(lexicalScopeData,
+                 NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
+  BINJS_TRY_DECL(bodyScope, handler_.newLexicalScope(*lexicalScopeData, body));
+  BINJS_MOZ_TRY_DECL(method,
+                     buildFunction(start, kind, name, params, bodyScope));
   BINJS_TRY_DECL(result, handler_.newObjectMethodOrPropertyDefinition(
                              name, method, accessorType));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceEagerMethod(
     const size_t start, const BinKind kind, const BinFields& fields) {
@@ -2686,17 +2690,21 @@ JS::Result<ParseNode*> BinASTParser<Tok>
   MOZ_ASSERT(pc_->isFunctionBox());
 
   ParseContext::Scope lexicalScope(cx_, pc_, usedNames_);
   BINJS_TRY(lexicalScope.init(pc_));
   ListNode* params;
   ListNode* body;
   MOZ_TRY(parseFunctionOrMethodContents(length, &params, &body));
   MOZ_TRY(prependDirectivesToBody(body, directives));
-  BINJS_MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body));
+  BINJS_TRY_DECL(lexicalScopeData,
+                 NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
+  BINJS_TRY_DECL(bodyScope, handler_.newLexicalScope(*lexicalScopeData, body));
+  BINJS_MOZ_TRY_DECL(method,
+                     buildFunction(start, kind, name, params, bodyScope));
   BINJS_TRY_DECL(result, handler_.newObjectMethodOrPropertyDefinition(
                              name, method, accessorType));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceEagerSetter(
     const size_t start, const BinKind kind, const BinFields& fields) {
@@ -2741,17 +2749,21 @@ JS::Result<ParseNode*> BinASTParser<Tok>
   MOZ_ASSERT(pc_->isFunctionBox());
 
   ParseContext::Scope lexicalScope(cx_, pc_, usedNames_);
   BINJS_TRY(lexicalScope.init(pc_));
   ListNode* params;
   ListNode* body;
   MOZ_TRY(parseSetterContents(length, &params, &body));
   MOZ_TRY(prependDirectivesToBody(body, directives));
-  BINJS_MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body));
+  BINJS_TRY_DECL(lexicalScopeData,
+                 NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
+  BINJS_TRY_DECL(bodyScope, handler_.newLexicalScope(*lexicalScopeData, body));
+  BINJS_MOZ_TRY_DECL(method,
+                     buildFunction(start, kind, name, params, bodyScope));
   BINJS_TRY_DECL(result, handler_.newObjectMethodOrPropertyDefinition(
                              name, method, accessorType));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceEmptyStatement(
     const size_t start, const BinKind kind, const BinFields& fields) {
@@ -4239,19 +4251,21 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 
   BINJS_MOZ_TRY_DECL(init, parseOptionalExpression());
 
   ParseNode* result;
   if (binding->isKind(ParseNodeKind::Name)) {
     // `var foo [= bar]``
     NameNode* bindingNameNode = &binding->template as<NameNode>();
     MOZ_TRY(checkBinding(bindingNameNode->atom()->asPropertyName()));
-    result = bindingNameNode;
     if (init) {
-      BINJS_TRY(handler_.finishInitializerAssignment(bindingNameNode, init));
+      BINJS_TRY_VAR(
+          result, handler_.finishInitializerAssignment(bindingNameNode, init));
+    } else {
+      result = bindingNameNode;
     }
   } else {
     // `var pattern = bar`
     if (!init) {
       // Here, `init` is required.
       return raiseMissingField("VariableDeclarator (with non-trivial pattern)",
                                BinField::Init);
     }
--- a/js/src/frontend/BinSource.yaml
+++ b/js/src/frontend/BinSource.yaml
@@ -1001,18 +1001,22 @@ EagerGetter:
   inherits: EagerMethod
 
 EagerMethod:
   init: |
     const auto syntax = FunctionSyntaxKind::Method;
     const auto accessorType = AccessorType::None;
   inherits: EagerFunctionExpression
   build: |
+    BINJS_TRY_DECL(lexicalScopeData,
+                   NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
+    BINJS_TRY_DECL(bodyScope,
+                   handler_.newLexicalScope(*lexicalScopeData, body));
     BINJS_MOZ_TRY_DECL(method,
-                       buildFunction(start, kind, name, params, body));
+                       buildFunction(start, kind, name, params, bodyScope));
     BINJS_TRY_DECL(result,
                    handler_.newObjectMethodOrPropertyDefinition(name, method,
                                                                 accessorType));
 
 EagerSetter:
   init: |
     const auto syntax = FunctionSyntaxKind::Setter;
     const bool isGenerator = false;
@@ -1598,19 +1602,21 @@ VariableDeclaration:
 
 VariableDeclarator:
   build: |
     ParseNode* result;
     if (binding->isKind(ParseNodeKind::Name)) {
       // `var foo [= bar]``
       NameNode* bindingNameNode = &binding->template as<NameNode>();
       MOZ_TRY(checkBinding(bindingNameNode->atom()->asPropertyName()));
-      result = bindingNameNode;
       if (init) {
-        BINJS_TRY(handler_.finishInitializerAssignment(bindingNameNode, init));
+        BINJS_TRY_VAR(result,
+                      handler_.finishInitializerAssignment(bindingNameNode, init));
+      } else {
+        result = bindingNameNode;
       }
     } else {
       // `var pattern = bar`
       if (!init) {
         // Here, `init` is required.
         return raiseMissingField("VariableDeclarator (with non-trivial pattern)",
                                  BinField::Init);
       }
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -4034,46 +4034,59 @@ bool BytecodeEmitter::emitTemplateString
 
   return true;
 }
 
 bool BytecodeEmitter::emitDeclarationList(ListNode* declList) {
   MOZ_ASSERT(declList->isOp(JSOP_NOP));
 
   for (ParseNode* decl : declList->contents()) {
-    if (decl->isKind(ParseNodeKind::AssignExpr)) {
+    ParseNode* pattern;
+    ParseNode* initializer;
+    if (decl->isKind(ParseNodeKind::Name)) {
+      pattern = decl;
+      initializer = nullptr;
+    } else {
       MOZ_ASSERT(decl->isOp(JSOP_NOP));
 
       AssignmentNode* assignNode = &decl->as<AssignmentNode>();
-      ListNode* pattern = &assignNode->left()->as<ListNode>();
+      pattern = assignNode->left();
+      initializer = assignNode->right();
+    }
+
+    if (pattern->isKind(ParseNodeKind::Name)) {
+      // initializer can be null here.
+      if (!emitSingleDeclaration(declList, &pattern->as<NameNode>(),
+                                 initializer)) {
+        return false;
+      }
+    } else {
+      MOZ_ASSERT(decl->isOp(JSOP_NOP));
       MOZ_ASSERT(pattern->isKind(ParseNodeKind::ArrayExpr) ||
                  pattern->isKind(ParseNodeKind::ObjectExpr));
-
-      if (!updateSourceCoordNotes(assignNode->right()->pn_pos.begin)) {
+      MOZ_ASSERT(initializer != nullptr);
+
+      if (!updateSourceCoordNotes(initializer->pn_pos.begin)) {
         return false;
       }
       if (!markStepBreakpoint()) {
         return false;
       }
-      if (!emitTree(assignNode->right())) {
-        return false;
-      }
-
-      if (!emitDestructuringOps(pattern, DestructuringDeclaration)) {
+      if (!emitTree(initializer)) {
+        return false;
+      }
+
+      if (!emitDestructuringOps(&pattern->as<ListNode>(),
+                                DestructuringDeclaration)) {
         return false;
       }
 
       if (!emit1(JSOP_POP)) {
         return false;
       }
-    } else {
-      NameNode* name = &decl->as<NameNode>();
-      if (!emitSingleDeclaration(declList, name, name->initializer())) {
-        return false;
-      }
     }
   }
   return true;
 }
 
 bool BytecodeEmitter::emitSingleDeclaration(ListNode* declList, NameNode* decl,
                                             ParseNode* initializer) {
   MOZ_ASSERT(decl->isKind(ParseNodeKind::Name));
@@ -5315,18 +5328,27 @@ bool BytecodeEmitter::emitInitializeForI
   if (!updateSourceCoordNotes(target->pn_pos.begin)) {
     return false;
   }
 
   MOZ_ASSERT(target->isForLoopDeclaration());
   target = parser->astGenerator().singleBindingFromDeclaration(
       &target->as<ListNode>());
 
+  NameNode* nameNode = nullptr;
   if (target->isKind(ParseNodeKind::Name)) {
-    NameNode* nameNode = &target->as<NameNode>();
+    nameNode = &target->as<NameNode>();
+  } else if (target->isKind(ParseNodeKind::AssignExpr)) {
+    AssignmentNode* assignNode = &target->as<AssignmentNode>();
+    if (assignNode->left()->is<NameNode>()) {
+      nameNode = &assignNode->left()->as<NameNode>();
+    }
+  }
+
+  if (nameNode) {
     NameOpEmitter noe(this, nameNode->name(), NameOpEmitter::Kind::Initialize);
     if (!noe.prepareForRhs()) {
       return false;
     }
     if (noe.emittedBindOp()) {
       // Per-iteration initialization in for-in/of loops computes the
       // iteration value *before* initializing.  Thus the initializing
       // value may be buried under a bind-specific value on the stack.
@@ -5449,33 +5471,35 @@ bool BytecodeEmitter::emitForIn(ForNode*
   ForInEmitter forIn(this, headLexicalEmitterScope);
 
   // Annex B: Evaluate the var-initializer expression if present.
   // |for (var i = initializer in expr) { ... }|
   ParseNode* forInTarget = forInHead->kid1();
   if (parser->astGenerator().isDeclarationList(forInTarget)) {
     ParseNode* decl = parser->astGenerator().singleBindingFromDeclaration(
         &forInTarget->as<ListNode>());
-    if (decl->isKind(ParseNodeKind::Name)) {
-      if (ParseNode* initializer = decl->as<NameNode>().initializer()) {
+    if (decl->isKind(ParseNodeKind::AssignExpr)) {
+      AssignmentNode* assignNode = &decl->as<AssignmentNode>();
+      if (assignNode->left()->is<NameNode>()) {
+        NameNode* nameNode = &assignNode->left()->as<NameNode>();
+        ParseNode* initializer = assignNode->right();
         MOZ_ASSERT(
             forInTarget->isKind(ParseNodeKind::VarStmt),
             "for-in initializers are only permitted for |var| declarations");
 
         if (!updateSourceCoordNotes(decl->pn_pos.begin)) {
           return false;
         }
 
-        NameNode* nameNode = &decl->as<NameNode>();
         NameOpEmitter noe(this, nameNode->name(),
                           NameOpEmitter::Kind::Initialize);
         if (!noe.prepareForRhs()) {
           return false;
         }
-        if (!emitInitializer(initializer, decl)) {
+        if (!emitInitializer(initializer, nameNode)) {
           return false;
         }
         if (!noe.emitAssignment()) {
           return false;
         }
 
         // Pop the initializer.
         if (!emit1(JSOP_POP)) {
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -874,18 +874,25 @@ class FullParseHandler {
 
   bool isUsableAsObjectPropertyName(Node node) {
     return node->isKind(ParseNodeKind::NumberExpr) ||
            node->isKind(ParseNodeKind::ObjectPropertyName) ||
            node->isKind(ParseNodeKind::StringExpr) ||
            node->isKind(ParseNodeKind::ComputedName);
   }
 
-  inline MOZ_MUST_USE bool finishInitializerAssignment(NameNodeType nameNode,
-                                                       Node init);
+  AssignmentNodeType finishInitializerAssignment(NameNodeType nameNode,
+                                                 Node init) {
+    MOZ_ASSERT(nameNode->isKind(ParseNodeKind::Name));
+    MOZ_ASSERT(!nameNode->isInParens());
+
+    checkAndSetIsDirectRHSAnonFunction(init);
+
+    return newAssignment(ParseNodeKind::AssignExpr, nameNode, init);
+  }
 
   void setBeginPosition(Node pn, Node oth) {
     setBeginPosition(pn, oth->pn_pos.begin);
   }
   void setBeginPosition(Node pn, uint32_t begin) {
     pn->pn_pos.begin = begin;
     MOZ_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end);
   }
@@ -1022,27 +1029,12 @@ inline bool FullParseHandler::setLastFun
   if (!pn) {
     return false;
   }
 
   body->replaceLast(pn);
   return true;
 }
 
-inline bool FullParseHandler::finishInitializerAssignment(NameNodeType nameNode,
-                                                          Node init) {
-  MOZ_ASSERT(nameNode->isKind(ParseNodeKind::Name));
-  MOZ_ASSERT(!nameNode->isInParens());
-
-  checkAndSetIsDirectRHSAnonFunction(init);
-
-  nameNode->setInitializer(init);
-  nameNode->setOp(JSOP_SETNAME);
-
-  /* The declarator's position must include the initializer. */
-  nameNode->pn_pos.end = init->pn_pos.end;
-  return true;
-}
-
 }  // namespace frontend
 }  // namespace js
 
 #endif /* frontend_FullParseHandler_h */
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -414,19 +414,17 @@ class NameResolver : public ParseNodeVis
     if (!isImport && item && item->isKind(ParseNodeKind::ExportBatchSpecStmt)) {
       MOZ_ASSERT(item->is<NullaryNode>());
     } else {
       for (ParseNode* item : pn->contents()) {
         BinaryNode* spec = &item->as<BinaryNode>();
         MOZ_ASSERT(spec->isKind(isImport ? ParseNodeKind::ImportSpec
                                          : ParseNodeKind::ExportSpec));
         MOZ_ASSERT(spec->left()->isKind(ParseNodeKind::Name));
-        MOZ_ASSERT(!spec->left()->as<NameNode>().initializer());
         MOZ_ASSERT(spec->right()->isKind(ParseNodeKind::Name));
-        MOZ_ASSERT(!spec->right()->as<NameNode>().initializer());
       }
     }
 #endif
     return true;
   }
 
  public:
   MOZ_MUST_USE bool visitImportSpecList(ListNode* pn) {
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -306,53 +306,51 @@ void NameNode::dumpImpl(GenericPrinter& 
     case ParseNodeKind::Name:
     case ParseNodeKind::PrivateName:  // atom() already includes the '#', no
                                       // need to specially include it.
     case ParseNodeKind::PropertyNameExpr:
       if (!atom()) {
         out.put("#<null name>");
       } else if (getOp() == JSOP_GETARG && atom()->length() == 0) {
         // Dump destructuring parameter.
-        static const char ZeroLengthPrefix[] = "(#<zero-length name> ";
-        constexpr size_t ZeroLengthPrefixLength =
-            ArrayLength(ZeroLengthPrefix) - 1;
-        out.put(ZeroLengthPrefix);
-        DumpParseTree(initializer(), out, indent + ZeroLengthPrefixLength);
-        out.printf(")");
+        static const char ZeroLengthName[] = "(#<zero-length name>)";
+        out.put(ZeroLengthName);
       } else {
         JS::AutoCheckCannotGC nogc;
         if (atom()->hasLatin1Chars()) {
           DumpName(out, atom()->latin1Chars(nogc), atom()->length());
         } else {
           DumpName(out, atom()->twoByteChars(nogc), atom()->length());
         }
       }
       return;
 
     case ParseNodeKind::LabelStmt: {
-      const char* name = parseNodeNames[size_t(getKind())];
-      out.printf("(%s ", name);
-      atom()->dumpCharsNoNewline(out);
-      indent += strlen(name) + atom()->length() + 2;
-      DumpParseTree(initializer(), out, indent);
-      out.printf(")");
+      this->as<LabeledStatement>().dump(out, indent);
       return;
     }
 
     default: {
       const char* name = parseNodeNames[size_t(getKind())];
-      out.printf("(%s ", name);
-      indent += strlen(name) + 2;
-      DumpParseTree(initializer(), out, indent);
-      out.printf(")");
+      out.printf("(%s)", name);
       return;
     }
   }
 }
 
+void LabeledStatement::dump(GenericPrinter& out, int indent) {
+  const char* name = parseNodeNames[size_t(getKind())];
+  out.printf("(%s ", name);
+  atom()->dumpCharsNoNewline(out);
+  out.printf(" ");
+  indent += strlen(name) + atom()->length() + 3;
+  DumpParseTree(statement(), out, indent);
+  out.printf(")");
+}
+
 void LexicalScopeNode::dumpImpl(GenericPrinter& out, int indent) {
   const char* name = parseNodeNames[size_t(getKind())];
   out.printf("(%s [", name);
   int nameIndent = indent + strlen(name) + 3;
   if (!isEmptyScope()) {
     LexicalScope::Data* bindings = scopeBindings();
     for (uint32_t i = 0; i < bindings->length; i++) {
       JSAtom* name = bindings->trailingNames[i].name();
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -791,65 +791,47 @@ class NullaryNode : public ParseNode {
   }
 
 #ifdef DEBUG
   void dumpImpl(GenericPrinter& out, int indent);
 #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), atom_(atom), initOrStmt(initOrStmt) {
-    MOZ_ASSERT(is<NameNode>());
-  }
+  JSAtom* atom_; /* lexical name or label atom */
 
  public:
   NameNode(ParseNodeKind kind, JSOp op, JSAtom* atom, const TokenPos& pos)
-      : ParseNode(kind, op, pos), atom_(atom), initOrStmt(nullptr) {
+      : ParseNode(kind, op, pos), atom_(atom) {
     MOZ_ASSERT(is<NameNode>());
   }
 
   static bool test(const ParseNode& node) {
     return node.typeCode() == TypeCode::Name;
   }
 
   static constexpr TypeCode classTypeCode() { return TypeCode::Name; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
-    if (initOrStmt) {
-      if (!visitor.visit(initOrStmt)) {
-        return false;
-      }
-    }
     return true;
   }
 
 #ifdef DEBUG
   void dumpImpl(GenericPrinter& out, int indent);
 #endif
 
   JSAtom* atom() const { return atom_; }
 
   PropertyName* name() const {
     MOZ_ASSERT(isKind(ParseNodeKind::Name));
     return atom()->asPropertyName();
   }
 
-  ParseNode* initializer() const { return initOrStmt; }
-
   void setAtom(JSAtom* atom) { atom_ = atom; }
-
-  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_;
@@ -1591,24 +1573,27 @@ class LexicalScopeNode : public ParseNod
   ParseNode* scopeBody() const { return body; }
 
   void setScopeBody(ParseNode* body) { this->body = body; }
 
   bool isEmptyScope() const { return !bindings; }
 };
 
 class LabeledStatement : public NameNode {
+  ParseNode* statement_;
+
  public:
   LabeledStatement(PropertyName* label, ParseNode* stmt, uint32_t begin)
-      : NameNode(ParseNodeKind::LabelStmt, JSOP_NOP, label, stmt,
-                 TokenPos(begin, stmt->pn_pos.end)) {}
+      : NameNode(ParseNodeKind::LabelStmt, JSOP_NOP, label,
+                 TokenPos(begin, stmt->pn_pos.end)),
+        statement_(stmt) {}
 
   PropertyName* label() const { return atom()->asPropertyName(); }
 
-  ParseNode* statement() const { return initializer(); }
+  ParseNode* statement() const { return statement_; }
 
   static bool test(const ParseNode& node) {
     return node.isKind(ParseNodeKind::LabelStmt);
   }
 };
 
 // Inside a switch statement, a CaseClause is a case-label and the subsequent
 // statements. The same node type is used for DefaultClauses. The only
@@ -1626,16 +1611,30 @@ class CaseClause : public BinaryNode {
   ListNode* statementList() const { return &right()->as<ListNode>(); }
 
   static bool test(const ParseNode& node) {
     bool match = node.isKind(ParseNodeKind::Case);
     MOZ_ASSERT_IF(match, node.is<BinaryNode>());
     MOZ_ASSERT_IF(match, node.isOp(JSOP_NOP));
     return match;
   }
+
+  template <typename Visitor>
+  bool accept(Visitor& visitor) {
+    if (statement_) {
+      if (!visitor.visit(statement_)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+#ifdef DEBUG
+  void dump(GenericPrinter& out, int indent);
+#endif
 };
 
 class LoopControlStatement : public ParseNode {
   PropertyName* label_; /* target of break/continue statement */
 
  protected:
   LoopControlStatement(ParseNodeKind kind, PropertyName* label,
                        const TokenPos& pos)
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -4085,85 +4085,83 @@ GeneralParser<ParseHandler, Unit>::decla
   if (!init) {
     return null();
   }
 
   return handler_.newAssignment(ParseNodeKind::AssignExpr, pattern, init);
 }
 
 template <class ParseHandler, typename Unit>
-bool GeneralParser<ParseHandler, Unit>::initializerInNameDeclaration(
+typename ParseHandler::Node
+GeneralParser<ParseHandler, Unit>::initializerInNameDeclaration(
     NameNodeType binding, DeclarationKind declKind, bool initialDeclaration,
     YieldHandling yieldHandling, ParseNodeKind* forHeadKind,
     Node* forInOrOfExpression) {
   MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Assign));
 
   uint32_t initializerOffset;
   if (!tokenStream.peekOffset(&initializerOffset, TokenStream::Operand)) {
-    return false;
+    return null();
   }
 
   Node initializer = assignExpr(forHeadKind ? InProhibited : InAllowed,
                                 yieldHandling, TripledotProhibited);
   if (!initializer) {
-    return false;
+    return null();
   }
 
   if (forHeadKind && initialDeclaration) {
     bool isForIn, isForOf;
     if (!matchInOrOf(&isForIn, &isForOf)) {
-      return false;
+      return null();
     }
 
     // An initialized declaration can't appear in a for-of:
     //
     //   for (var/let/const x = ... of ...); // BAD
     if (isForOf) {
       errorAt(initializerOffset, JSMSG_OF_AFTER_FOR_LOOP_DECL);
-      return false;
+      return null();
     }
 
     if (isForIn) {
       // Lexical declarations in for-in loops can't be initialized:
       //
       //   for (let/const x = ... in ...); // BAD
       if (DeclarationKindIsLexical(declKind)) {
         errorAt(initializerOffset, JSMSG_IN_AFTER_LEXICAL_FOR_DECL);
-        return false;
+        return null();
       }
 
       // This leaves only initialized for-in |var| declarations.  ES6
       // forbids these; later ES un-forbids in non-strict mode code.
       *forHeadKind = ParseNodeKind::ForIn;
       if (!strictModeErrorAt(initializerOffset,
                              JSMSG_INVALID_FOR_IN_DECL_WITH_INIT)) {
-        return false;
+        return null();
       }
 
       *forInOrOfExpression =
           expressionAfterForInOrOf(ParseNodeKind::ForIn, yieldHandling);
       if (!*forInOrOfExpression) {
-        return false;
+        return null();
       }
     } else {
       *forHeadKind = ParseNodeKind::ForHead;
     }
   }
 
   return handler_.finishInitializerAssignment(binding, initializer);
 }
 
 template <class ParseHandler, typename Unit>
-typename ParseHandler::NameNodeType
-GeneralParser<ParseHandler, Unit>::declarationName(DeclarationKind declKind,
-                                                   TokenKind tt,
-                                                   bool initialDeclaration,
-                                                   YieldHandling yieldHandling,
-                                                   ParseNodeKind* forHeadKind,
-                                                   Node* forInOrOfExpression) {
+typename ParseHandler::Node GeneralParser<ParseHandler, Unit>::declarationName(
+    DeclarationKind declKind, TokenKind tt, bool initialDeclaration,
+    YieldHandling yieldHandling, ParseNodeKind* forHeadKind,
+    Node* forInOrOfExpression) {
   // Anything other than possible identifier is an error.
   if (!TokenKindIsPossibleIdentifier(tt)) {
     error(JSMSG_NO_VARIABLE_NAME);
     return null();
   }
 
   RootedPropertyName name(cx_, bindingIdentifier(yieldHandling));
   if (!name) {
@@ -4185,23 +4183,27 @@ GeneralParser<ParseHandler, Unit>::decla
   //
   // Therefore get the token here as Operand.
   bool matched;
   if (!tokenStream.matchToken(&matched, TokenKind::Assign,
                               TokenStream::Operand)) {
     return null();
   }
 
+  Node declaration;
   if (matched) {
-    if (!initializerInNameDeclaration(binding, declKind, initialDeclaration,
-                                      yieldHandling, forHeadKind,
-                                      forInOrOfExpression)) {
+    declaration = initializerInNameDeclaration(
+        binding, declKind, initialDeclaration, yieldHandling, forHeadKind,
+        forInOrOfExpression);
+    if (!declaration) {
       return null();
     }
   } else {
+    declaration = binding;
+
     if (initialDeclaration && forHeadKind) {
       bool isForIn, isForOf;
       if (!matchInOrOf(&isForIn, &isForOf)) {
         return null();
       }
 
       if (isForIn) {
         *forHeadKind = ParseNodeKind::ForIn;
@@ -4229,17 +4231,17 @@ GeneralParser<ParseHandler, Unit>::decla
   }
 
   // Note the declared name after knowing whether or not we are in a for-of
   // loop, due to special early error semantics in Annex B.3.5.
   if (!noteDeclaredName(name, declKind, namePos)) {
     return null();
   }
 
-  return binding;
+  return declaration;
 }
 
 template <class ParseHandler, typename Unit>
 typename ParseHandler::ListNodeType
 GeneralParser<ParseHandler, Unit>::declarationList(
     YieldHandling yieldHandling, ParseNodeKind kind,
     ParseNodeKind* forHeadKind /* = nullptr */,
     Node* forInOrOfExpression /* = nullptr */) {
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -1195,28 +1195,26 @@ class MOZ_STACK_CLASS GeneralParser : pu
   // for-in/of loop head, returning the iterated expression in
   // |*forInOrOfExpression|.  (An "initial declaration" is the first
   // declaration in a declaration list: |a| but not |b| in |var a, b|, |{c}|
   // but not |d| in |let {c} = 3, d|.)
   Node declarationPattern(DeclarationKind declKind, TokenKind tt,
                           bool initialDeclaration, YieldHandling yieldHandling,
                           ParseNodeKind* forHeadKind,
                           Node* forInOrOfExpression);
-  NameNodeType declarationName(DeclarationKind declKind, TokenKind tt,
-                               bool initialDeclaration,
-                               YieldHandling yieldHandling,
-                               ParseNodeKind* forHeadKind,
-                               Node* forInOrOfExpression);
+  Node declarationName(DeclarationKind declKind, TokenKind tt,
+                       bool initialDeclaration, YieldHandling yieldHandling,
+                       ParseNodeKind* forHeadKind, Node* forInOrOfExpression);
 
   // Having parsed a name (not found in a destructuring pattern) declared by
   // a declaration, with the current token being the '=' separating the name
   // from its initializer, parse and bind that initializer -- and possibly
   // consume trailing in/of and subsequent expression, if so directed by
   // |forHeadKind|.
-  bool initializerInNameDeclaration(NameNodeType binding,
+  Node initializerInNameDeclaration(NameNodeType binding,
                                     DeclarationKind declKind,
                                     bool initialDeclaration,
                                     YieldHandling yieldHandling,
                                     ParseNodeKind* forHeadKind,
                                     Node* forInOrOfExpression);
 
   Node expr(InHandling inHandling, YieldHandling yieldHandling,
             TripledotHandling tripledotHandling,
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -512,19 +512,19 @@ class SyntaxParseHandler {
     return NodeGeneric;
   }
 
   TernaryNodeType newForInOrOfHead(ParseNodeKind kind, Node target,
                                    Node iteratedExpr, const TokenPos& pos) {
     return NodeGeneric;
   }
 
-  MOZ_MUST_USE bool finishInitializerAssignment(NameNodeType nameNode,
-                                                Node init) {
-    return true;
+  AssignmentNodeType finishInitializerAssignment(NameNodeType nameNode,
+                                                 Node init) {
+    return NodeUnparenthesizedAssignment;
   }
 
   void setBeginPosition(Node pn, Node oth) {}
   void setBeginPosition(Node pn, uint32_t begin) {}
 
   void setEndPosition(Node pn, Node oth) {}
   void setEndPosition(Node pn, uint32_t end) {}
 
new file mode 100644
--- /dev/null
+++ b/js/src/tests/non262/expressions/constant-folded-labeled-statement.js
@@ -0,0 +1,14 @@
+var BUGNUMBER = 1499448;
+var summary = "Constant folder should fold labeled statements";
+
+print(BUGNUMBER + ": " + summary);
+
+if (typeof disassemble === "function") {
+    var code = disassemble(() => { x: 2+2; });
+
+    if (typeof reportCompare === "function")
+        reportCompare(true, /int8 4/.test(code));
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -604,20 +604,16 @@ static inline PropertyName* ObjectNormal
   return BinaryLeft(pn)->as<NameNode>().atom()->asPropertyName();
 }
 
 static inline ParseNode* ObjectNormalFieldInitializer(ParseNode* pn) {
   MOZ_ASSERT(IsNormalObjectField(pn));
   return BinaryRight(pn);
 }
 
-static inline ParseNode* MaybeInitializer(ParseNode* pn) {
-  return pn->as<NameNode>().initializer();
-}
-
 static inline bool IsUseOfName(ParseNode* pn, PropertyName* name) {
   return pn->isName(name);
 }
 
 static inline bool IsIgnoredDirectiveName(JSContext* cx, JSAtom* atom) {
   return atom != cx->names().useStrict;
 }
 
@@ -3035,31 +3031,35 @@ static bool CheckGlobalDotImport(ModuleV
 
   if (baseName != m.importArgumentName()) {
     return m.fail(base, "expected global or import name");
   }
 
   return m.addFFI(varName, field);
 }
 
-static bool CheckModuleGlobal(ModuleValidatorShared& m, ParseNode* var,
+static bool CheckModuleGlobal(ModuleValidatorShared& m, ParseNode* decl,
                               bool isConst) {
+  if (!decl->isKind(ParseNodeKind::AssignExpr)) {
+    return m.fail(decl, "module import needs initializer");
+  }
+  AssignmentNode* assignNode = &decl->as<AssignmentNode>();
+
+  ParseNode* var = assignNode->left();
+
   if (!var->isKind(ParseNodeKind::Name)) {
     return m.fail(var, "import variable is not a plain name");
   }
 
   PropertyName* varName = var->as<NameNode>().name();
   if (!CheckModuleLevelName(m, var, varName)) {
     return false;
   }
 
-  ParseNode* initNode = MaybeInitializer(var);
-  if (!initNode) {
-    return m.fail(var, "module import needs initializer");
-  }
+  ParseNode* initNode = assignNode->right();
 
   if (IsNumericLiteral(m, initNode)) {
     return CheckGlobalVariableInitConstant(m, varName, initNode, isConst);
   }
 
   if (initNode->isKind(ParseNodeKind::BitOrExpr) ||
       initNode->isKind(ParseNodeKind::PosExpr) ||
       initNode->isKind(ParseNodeKind::CallExpr)) {
@@ -3249,34 +3249,38 @@ static bool CheckFinalReturn(FunctionVal
       !IsVoid(f.returnedType())) {
     return f.fail(lastNonEmptyStmt,
                   "void incompatible with previous return type");
   }
 
   return true;
 }
 
-static bool CheckVariable(FunctionValidatorShared& f, ParseNode* var,
+static bool CheckVariable(FunctionValidatorShared& f, ParseNode* decl,
                           ValTypeVector* types, Vector<NumLit>* inits) {
+  if (!decl->isKind(ParseNodeKind::AssignExpr)) {
+    return f.failName(
+        decl, "var '%s' needs explicit type declaration via an initial value",
+        decl->as<NameNode>().name());
+  }
+  AssignmentNode* assignNode = &decl->as<AssignmentNode>();
+
+  ParseNode* var = assignNode->left();
+
   if (!var->isKind(ParseNodeKind::Name)) {
     return f.fail(var, "local variable is not a plain name");
   }
 
   PropertyName* name = var->as<NameNode>().name();
 
   if (!CheckIdentifier(f.m(), var, name)) {
     return false;
   }
 
-  ParseNode* initNode = MaybeInitializer(var);
-  if (!initNode) {
-    return f.failName(
-        var, "var '%s' needs explicit type declaration via an initial value",
-        name);
-  }
+  ParseNode* initNode = assignNode->right();
 
   NumLit lit;
   if (!IsLiteralOrConst(f, initNode, &lit)) {
     return f.failName(
         var, "var '%s' initializer must be literal or const literal", name);
   }
 
   if (!lit.valid()) {
@@ -6149,23 +6153,31 @@ static bool CheckFunctions(ModuleValidat
       return false;
     }
   }
 
   return CheckAllFunctionsDefined(m);
 }
 
 template <typename Unit>
-static bool CheckFuncPtrTable(ModuleValidator<Unit>& m, ParseNode* var) {
+static bool CheckFuncPtrTable(ModuleValidator<Unit>& m, ParseNode* decl) {
+  if (!decl->isKind(ParseNodeKind::AssignExpr)) {
+    return m.fail(decl, "function-pointer table must have initializer");
+  }
+  AssignmentNode* assignNode = &decl->as<AssignmentNode>();
+
+  ParseNode* var = assignNode->left();
+
   if (!var->isKind(ParseNodeKind::Name)) {
     return m.fail(var, "function-pointer table name is not a plain name");
   }
 
-  ParseNode* arrayLiteral = MaybeInitializer(var);
-  if (!arrayLiteral || !arrayLiteral->isKind(ParseNodeKind::ArrayExpr)) {
+  ParseNode* arrayLiteral = assignNode->right();
+
+  if (!arrayLiteral->isKind(ParseNodeKind::ArrayExpr)) {
     return m.fail(
         var, "function-pointer table's initializer must be an array literal");
   }
 
   unsigned length = ListLength(arrayLiteral);
 
   if (!IsPowerOfTwo(length)) {
     return m.failf(arrayLiteral,