bug 1526031 - remove initOrStmt field from NameNode. r=jorendorff
☠☠ backed out by f959cbeb6c01 ☠ ☠
authorAshley Hauck <khyperia@mozilla.com>
Sat, 16 Feb 2019 00:20:22 +0000
changeset 517510 94a9c4cabe376cc22c9cd43199ee672bd32f0eae
parent 517509 27280189b6faf5b8353e39f2f429ccc853f2d851
child 517511 f959cbeb6c01620819738c5313074e596ccc7363
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
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 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
@@ -4256,19 +4256,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(factory_.finishInitializerAssignment(bindingNameNode, init));
+      BINJS_TRY_VAR(
+          result, factory_.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
@@ -1613,19 +1613,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(factory_.finishInitializerAssignment(bindingNameNode, init));
+        BINJS_TRY_VAR(result,
+                      factory_.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
@@ -4006,46 +4006,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));
@@ -5287,18 +5300,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.
@@ -5421,33 +5443,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
@@ -870,18 +870,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);
   }
@@ -1018,27 +1025,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
@@ -443,16 +443,17 @@ class NameResolver {
       case ParseNodeKind::BreakStmt:
         MOZ_ASSERT(cur->is<BreakStatement>());
         break;
 
       case ParseNodeKind::ContinueStmt:
         MOZ_ASSERT(cur->is<ContinueStatement>());
         break;
 
+      case ParseNodeKind::Name:
       case ParseNodeKind::ObjectPropertyName:
       case ParseNodeKind::PrivateName:  // TODO(khyperia): Implement private
                                         // field access.
       case ParseNodeKind::StringExpr:
       case ParseNodeKind::TemplateStringExpr:
         MOZ_ASSERT(cur->is<NameNode>());
         break;
 
@@ -466,17 +467,16 @@ class NameResolver {
 
       case ParseNodeKind::BigIntExpr:
         MOZ_ASSERT(cur->is<BigIntLiteral>());
         break;
 
       case ParseNodeKind::TypeOfNameExpr:
       case ParseNodeKind::SuperBase:
         MOZ_ASSERT(cur->as<UnaryNode>().kid()->isKind(ParseNodeKind::Name));
-        MOZ_ASSERT(!cur->as<UnaryNode>().kid()->as<NameNode>().initializer());
         break;
 
       case ParseNodeKind::NewTargetExpr:
       case ParseNodeKind::ImportMetaExpr: {
         MOZ_ASSERT(
             cur->as<BinaryNode>().left()->isKind(ParseNodeKind::PosHolder));
         MOZ_ASSERT(
             cur->as<BinaryNode>().right()->isKind(ParseNodeKind::PosHolder));
@@ -709,22 +709,20 @@ class NameResolver {
       // if any.  The third is the class body.
       case ParseNodeKind::ClassDecl: {
         ClassNode* classNode = &cur->as<ClassNode>();
 #ifdef DEBUG
         if (classNode->names()) {
           ClassNames* names = classNode->names();
           if (NameNode* outerBinding = names->outerBinding()) {
             MOZ_ASSERT(outerBinding->isKind(ParseNodeKind::Name));
-            MOZ_ASSERT(!outerBinding->initializer());
           }
 
           NameNode* innerBinding = names->innerBinding();
           MOZ_ASSERT(innerBinding->isKind(ParseNodeKind::Name));
-          MOZ_ASSERT(!innerBinding->initializer());
         }
 #endif
         if (ParseNode* heritage = classNode->heritage()) {
           if (!resolve(heritage, prefix)) {
             return false;
           }
         }
         if (!resolve(classNode->memberList(), prefix)) {
@@ -895,19 +893,17 @@ class NameResolver {
           MOZ_ASSERT(item->is<NullaryNode>());
           break;
         }
         for (ParseNode* item : list->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
         break;
       }
 
       case ParseNodeKind::CallImportExpr: {
         BinaryNode* node = &cur->as<BinaryNode>();
         if (!resolve(node->right(), prefix)) {
@@ -929,24 +925,16 @@ class NameResolver {
       }
 
       case ParseNodeKind::LabelStmt:
         if (!resolve(cur->as<LabeledStatement>().statement(), prefix)) {
           return false;
         }
         break;
 
-      case ParseNodeKind::Name:
-        if (ParseNode* init = cur->as<NameNode>().initializer()) {
-          if (!resolve(init, prefix)) {
-            return false;
-          }
-        }
-        break;
-
       case ParseNodeKind::LexicalScope:
         if (!resolve(cur->as<LexicalScopeNode>().scopeBody(), prefix)) {
           return false;
         }
         break;
 
       case ParseNodeKind::Function:
         if (ParseNode* body = cur->as<FunctionNode>().body()) {
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -341,53 +341,51 @@ void NameNode::dump(GenericPrinter& out,
     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 ClassField::dump(GenericPrinter& out, int indent) {
   out.printf("(");
   if (hasInitializer()) {
     indent += 2;
   }
   DumpParseTree(&name(), out, indent);
   if (hasInitializer()) {
     IndentNewLine(out, indent);
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -787,63 +787,45 @@ 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), 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.isArity(PN_NAME); }
 
   static constexpr ParseNodeArity arity() { return PN_NAME; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
-    if (initOrStmt) {
-      if (!visitor.visit(initOrStmt)) {
-        return false;
-      }
-    }
     return true;
   }
 
 #ifdef DEBUG
   void dump(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_;
@@ -1587,31 +1569,48 @@ 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) {
     bool match = node.isKind(ParseNodeKind::LabelStmt);
     MOZ_ASSERT_IF(match, node.isArity(PN_NAME));
     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
 };
 
 // Inside a switch statement, a CaseClause is a case-label and the subsequent
 // statements. The same node type is used for DefaultClauses. The only
 // difference is that their caseExpression() is null.
 class CaseClause : public BinaryNode {
  public:
   CaseClause(ParseNode* expr, ParseNode* stmts, uint32_t begin)
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -4202,85 +4202,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(context, bindingIdentifier(yieldHandling));
   if (!name) {
@@ -4302,23 +4300,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;
@@ -4346,17 +4348,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
@@ -1183,28 +1183,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,11 @@
+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));
+}
--- 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,