Backed out 2 changesets (bug 1552229, bug 1547467) for causing build bustages. CLOSED TREE
authorDorel Luca <dluca@mozilla.com>
Tue, 21 May 2019 00:32:14 +0300
changeset 474608 b702646a08e392ca61867731e4c2cac0160a9c38
parent 474607 1efcba0d56db1d3e12f0fecf3211af6d12a3a646
child 474609 2d5a9140e5591972a77fd575f599b8d3c5a87cc4
push id36042
push userdvarga@mozilla.com
push dateTue, 21 May 2019 04:19:40 +0000
treeherdermozilla-central@ca560ff55451 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1552229, 1547467
milestone69.0a1
backs outa8f5dec91d72d4032ced008f847ead37f350193f
6004e7f60bb6ae49de462c658b137da437d216cd
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
Backed out 2 changesets (bug 1552229, bug 1547467) for causing build bustages. CLOSED TREE Backed out changeset a8f5dec91d72 (bug 1552229) Backed out changeset 6004e7f60bb6 (bug 1547467)
js/src/builtin/ReflectParse.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/FullParseHandler.h
js/src/frontend/FunctionEmitter.cpp
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/SyntaxParseHandler.h
js/src/jit-test/tests/fields/bug1547467.js
js/src/jit-test/tests/fields/bug1552229.js
js/src/jit-test/tests/fields/super.js
js/src/vm/CommonPropertyNames.h
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -2464,19 +2464,16 @@ bool ASTSerializer::statement(ParseNode*
     case ParseNodeKind::ClassMemberList: {
       ListNode* memberList = &pn->as<ListNode>();
       NodeVector members(cx);
       if (!members.reserve(memberList->count())) {
         return false;
       }
 
       for (ParseNode* item : memberList->contents()) {
-        if (item->is<LexicalScopeNode>()) {
-          item = item->as<LexicalScopeNode>().scopeBody();
-        }
         if (item->is<ClassField>()) {
           ClassField* field = &item->as<ClassField>();
           MOZ_ASSERT(memberList->pn_pos.encloses(field->pn_pos));
 
           RootedValue prop(cx);
           if (!classField(field, &prop)) {
             return false;
           }
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2337,17 +2337,17 @@ bool BytecodeEmitter::emitSetThis(Binary
     //              [stack] NEWTHIS
     return false;
   }
   if (!noe.emitAssignment()) {
     //              [stack] NEWTHIS
     return false;
   }
 
-  if (!emitInitializeInstanceFields()) {
+  if (!emitInitializeInstanceFields(IsSuperCall::Yes)) {
     return false;
   }
 
   return true;
 }
 
 bool BytecodeEmitter::defineHoistedTopLevelFunctions(ParseNode* body) {
   MOZ_ASSERT(inPrologue());
@@ -7631,61 +7631,21 @@ bool BytecodeEmitter::emitConditionalExp
 
   return true;
 }
 
 bool BytecodeEmitter::emitPropertyList(ListNode* obj, PropertyEmitter& pe,
                                        PropListType type) {
   //                [stack] CTOR? OBJ
 
-  size_t curFieldKeyIndex = 0;
   for (ParseNode* propdef : obj->contents()) {
     if (propdef->is<ClassField>()) {
-      MOZ_ASSERT(type == ClassBody);
-      // Only handle computing field keys here: the .initializers lambda array
-      // is created elsewhere.
-      ClassField* field = &propdef->as<ClassField>();
-      if (field->name().getKind() == ParseNodeKind::ComputedName) {
-        if (!emitGetName(cx->names().dotFieldKeys)) {
-          //        [stack] CTOR? OBJ ARRAY
-          return false;
-        }
-
-        ParseNode* nameExpr = field->name().as<UnaryNode>().kid();
-
-        if (!emitTree(nameExpr)) {
-          //        [stack] CTOR? OBJ ARRAY KEY
-          return false;
-        }
-
-        if (!emit1(JSOP_TOID)) {
-          //        [stack] CTOR? OBJ ARRAY KEY
-          return false;
-        }
-
-        if (!emitUint32Operand(JSOP_INITELEM_ARRAY, curFieldKeyIndex)) {
-          //        [stack] CTOR? OBJ ARRAY
-          return false;
-        }
-
-        if (!emit1(JSOP_POP)) {
-          //        [stack] CTOR? OBJ
-          return false;
-        }
-
-        curFieldKeyIndex++;
-      }
-      continue;
-    }
-
-    if (propdef->is<LexicalScopeNode>()) {
-      // Constructors are sometimes wrapped in LexicalScopeNodes. As we already
-      // handled emitting the constructor, skip it.
-      MOZ_ASSERT(propdef->as<LexicalScopeNode>().scopeBody()->isKind(
-          ParseNodeKind::ClassMethod));
+      // Skip over class fields and emit them at the end.  This is needed
+      // because they're all emitted into a single array, which is then stored
+      // into a local variable.
       continue;
     }
 
     // Handle __proto__: v specially because *only* this form, and no other
     // involving "__proto__", performs [[Prototype]] mutation.
     if (propdef->isKind(ParseNodeKind::MutateProto)) {
       //            [stack] OBJ
       MOZ_ASSERT(type == ObjectLiteral);
@@ -7932,16 +7892,25 @@ bool BytecodeEmitter::emitPropertyList(L
           return false;
         }
         break;
       default:
         MOZ_CRASH("Invalid op");
     }
   }
 
+  if (obj->getKind() == ParseNodeKind::ClassMemberList) {
+    if (!emitCreateFieldKeys(obj)) {
+      return false;
+    }
+    if (!emitCreateFieldInitializers(obj)) {
+      return false;
+    }
+  }
+
   return true;
 }
 
 FieldInitializers BytecodeEmitter::setupFieldInitializers(
     ListNode* classMembers) {
   size_t numFields = 0;
   for (ParseNode* propdef : classMembers->contents()) {
     if (propdef->is<ClassField>()) {
@@ -7970,18 +7939,17 @@ FieldInitializers BytecodeEmitter::setup
 //   }
 // ];
 // class C {
 //   constructor() {
 //     .initializers[0]();
 //   }
 // }
 //
-// BytecodeEmitter::emitCreateFieldKeys does `let .fieldKeys = [...];`
-// BytecodeEmitter::emitPropertyList fills in the elements of the array.
+// BytecodeEmitter::emitCreateFieldKeys does `let .fieldKeys = [keyExpr, ...];`
 // See GeneralParser::fieldInitializer for the `this[.fieldKeys[0]]` part.
 bool BytecodeEmitter::emitCreateFieldKeys(ListNode* obj) {
   size_t numFieldKeys = 0;
   for (ParseNode* propdef : obj->contents()) {
     if (propdef->is<ClassField>()) {
       ClassField* field = &propdef->as<ClassField>();
       if (field->name().getKind() == ParseNodeKind::ComputedName) {
         numFieldKeys++;
@@ -7999,16 +7967,44 @@ bool BytecodeEmitter::emitCreateFieldKey
     return false;
   }
 
   if (!emitUint32Operand(JSOP_NEWARRAY, numFieldKeys)) {
     //              [stack] ARRAY
     return false;
   }
 
+  size_t curFieldKeyIndex = 0;
+  for (ParseNode* propdef : obj->contents()) {
+    if (propdef->is<ClassField>()) {
+      ClassField* field = &propdef->as<ClassField>();
+      if (field->name().getKind() == ParseNodeKind::ComputedName) {
+        ParseNode* nameExpr = field->name().as<UnaryNode>().kid();
+
+        if (!emitTree(nameExpr)) {
+          //        [stack] ARRAY KEY
+          return false;
+        }
+
+        if (!emit1(JSOP_TOID)) {
+          //        [stack] ARRAY KEY
+          return false;
+        }
+
+        if (!emitUint32Operand(JSOP_INITELEM_ARRAY, curFieldKeyIndex)) {
+          //        [stack] ARRAY
+          return false;
+        }
+
+        curFieldKeyIndex++;
+      }
+    }
+  }
+  MOZ_ASSERT(curFieldKeyIndex == numFieldKeys);
+
   if (!noe.emitAssignment()) {
     //              [stack] ARRAY
     return false;
   }
 
   if (!emit1(JSOP_POP)) {
     //              [stack]
     return false;
@@ -8102,27 +8098,65 @@ const FieldInitializers& BytecodeEmitter
         return fieldInitializers;
       }
     }
   }
 
   MOZ_CRASH("Constructor for field initializers not found.");
 }
 
-bool BytecodeEmitter::emitInitializeInstanceFields() {
+bool BytecodeEmitter::emitCopyInitializersToLocalInitializers() {
+  MOZ_ASSERT(sc->asFunctionBox()->isDerivedClassConstructor());
+  if (getFieldInitializers().numFieldInitializers == 0) {
+    return true;
+  }
+
+  NameOpEmitter noe(this, cx->names().dotLocalInitializers,
+                    NameOpEmitter::Kind::Initialize);
+  if (!noe.prepareForRhs()) {
+    //              [stack]
+    return false;
+  }
+
+  if (!emitGetName(cx->names().dotInitializers)) {
+    //              [stack] .initializers
+    return false;
+  }
+
+  if (!noe.emitAssignment()) {
+    //              [stack] .initializers
+    return false;
+  }
+
+  if (!emit1(JSOP_POP)) {
+    //              [stack]
+    return false;
+  }
+
+  return true;
+}
+
+bool BytecodeEmitter::emitInitializeInstanceFields(IsSuperCall isSuperCall) {
   const FieldInitializers& fieldInitializers = findFieldInitializersForCall();
   size_t numFields = fieldInitializers.numFieldInitializers;
 
   if (numFields == 0) {
     return true;
   }
 
-  if (!emitGetName(cx->names().dotInitializers)) {
-    //              [stack] ARRAY
-    return false;
+  if (isSuperCall == IsSuperCall::Yes) {
+    if (!emitGetName(cx->names().dotLocalInitializers)) {
+      //            [stack] ARRAY
+      return false;
+    }
+  } else {
+    if (!emitGetName(cx->names().dotInitializers)) {
+      //            [stack] ARRAY
+      return false;
+    }
   }
 
   for (size_t fieldIndex = 0; fieldIndex < numFields; fieldIndex++) {
     if (fieldIndex < numFields - 1) {
       // We DUP to keep the array around (it is consumed in the bytecode below)
       // for next iterations of this loop, except for the last iteration, which
       // avoids an extra POP at the end of the loop.
       if (!emit1(JSOP_DUP)) {
@@ -8632,49 +8666,46 @@ bool BytecodeEmitter::emitLexicalInitial
 
   if (!noe.emitAssignment()) {
     return false;
   }
 
   return true;
 }
 
-static MOZ_ALWAYS_INLINE ParseNode* FindConstructor(JSContext* cx,
-                                                    ListNode* classMethods) {
-  for (ParseNode* classElement : classMethods->contents()) {
-    ParseNode* unwrappedElement = classElement;
-    if (unwrappedElement->is<LexicalScopeNode>()) {
-      unwrappedElement = unwrappedElement->as<LexicalScopeNode>().scopeBody();
-    }
-    if (unwrappedElement->is<ClassMethod>()) {
-      ClassMethod& method = unwrappedElement->as<ClassMethod>();
+static MOZ_ALWAYS_INLINE FunctionNode* FindConstructor(JSContext* cx,
+                                                       ListNode* classMethods) {
+  for (ParseNode* mn : classMethods->contents()) {
+    if (mn->is<ClassMethod>()) {
+      ClassMethod& method = mn->as<ClassMethod>();
       ParseNode& methodName = method.name();
       if (!method.isStatic() &&
           (methodName.isKind(ParseNodeKind::ObjectPropertyName) ||
            methodName.isKind(ParseNodeKind::StringExpr)) &&
           methodName.as<NameNode>().atom() == cx->names().constructor) {
-        return classElement;
-      }
-    }
-  }
+        return &method.method();
+      }
+    }
+  }
+
   return nullptr;
 }
 
 // This follows ES6 14.5.14 (ClassDefinitionEvaluation) and ES6 14.5.15
 // (BindingClassDeclarationEvaluation).
 bool BytecodeEmitter::emitClass(
     ClassNode* classNode,
     ClassNameKind nameKind /* = ClassNameKind::BindingName */,
     HandleAtom nameForAnonymousClass /* = nullptr */) {
   MOZ_ASSERT((nameKind == ClassNameKind::InferredName) ==
              (nameForAnonymousClass != nullptr));
 
   ParseNode* heritageExpression = classNode->heritage();
   ListNode* classMembers = classNode->memberList();
-  ParseNode* constructor = FindConstructor(cx, classMembers);
+  FunctionNode* constructor = FindConstructor(cx, classMembers);
 
   // If |nameKind != ClassNameKind::ComputedName|
   //                [stack]
   // Else
   //                [stack] NAME
 
   ClassEmitter ce(this);
   RootedAtom innerName(cx);
@@ -8686,18 +8717,18 @@ bool BytecodeEmitter::emitClass(
 
     if (names->outerBinding()) {
       MOZ_ASSERT(names->outerBinding()->name());
       MOZ_ASSERT(names->outerBinding()->name() == innerName);
       kind = ClassEmitter::Kind::Declaration;
     }
   }
 
-  if (LexicalScopeNode* scopeBindings = classNode->scopeBindings()) {
-    if (!ce.emitScope(scopeBindings->scopeBindings())) {
+  if (!classNode->isEmptyScope()) {
+    if (!ce.emitScope(classNode->scopeBindings())) {
       //            [stack]
       return false;
     }
   }
 
   // This is kind of silly. In order to the get the home object defined on
   // the constructor, we have to make it second, but we want the prototype
   // on top for EmitPropertyList, because we expect static properties to be
@@ -8725,78 +8756,43 @@ bool BytecodeEmitter::emitClass(
       //            [stack] HOMEOBJ
       return false;
     }
   }
 
   // Stack currently has HOMEOBJ followed by optional HERITAGE. When HERITAGE
   // is not used, an implicit value of %FunctionPrototype% is implied.
   if (constructor) {
-    FunctionNode* ctor;
-    // .fieldKeys must be declared outside the scope .initializers is declared
-    // in, hence this extra scope.
-    Maybe<LexicalScopeEmitter> lse;
-    if (constructor->is<LexicalScopeNode>()) {
-      lse.emplace(this);
-      if (!lse->emitScope(
-              ScopeKind::Lexical,
-              constructor->as<LexicalScopeNode>().scopeBindings())) {
-        return false;
-      }
-
-      // Any class with field initializers will have a constructor
-      if (!emitCreateFieldInitializers(classMembers)) {
-        return false;
-      }
-      ctor = &constructor->as<LexicalScopeNode>()
-                  .scopeBody()
-                  ->as<ClassMethod>()
-                  .method();
-    } else {
-      ctor = &constructor->as<ClassMethod>().method();
-    }
-
-    bool needsHomeObject = ctor->funbox()->needsHomeObject();
+    bool needsHomeObject = constructor->funbox()->needsHomeObject();
     // HERITAGE is consumed inside emitFunction.
-    if (!emitFunction(ctor, isDerived, classMembers)) {
+    if (!emitFunction(constructor, isDerived, classMembers)) {
       //            [stack] HOMEOBJ CTOR
       return false;
     }
     if (nameKind == ClassNameKind::InferredName) {
-      if (!setFunName(ctor->funbox()->function(), nameForAnonymousClass)) {
-        return false;
-      }
-    }
-    if (lse.isSome()) {
-      if (!lse->emitEnd()) {
-        return false;
-      }
-      lse.reset();
+      if (!setFunName(constructor->funbox()->function(),
+                      nameForAnonymousClass)) {
+        return false;
+      }
     }
     if (!ce.emitInitConstructor(needsHomeObject)) {
       //            [stack] CTOR HOMEOBJ
       return false;
     }
   } else {
     if (!ce.emitInitDefaultConstructor(Some(classNode->pn_pos.begin),
                                        Some(classNode->pn_pos.end))) {
       //            [stack] CTOR HOMEOBJ
       return false;
     }
   }
-
-  if (!emitCreateFieldKeys(classMembers)) {
-    return false;
-  }
-
   if (!emitPropertyList(classMembers, ce, ClassBody)) {
     //              [stack] CTOR HOMEOBJ
     return false;
   }
-
   if (!ce.emitEnd(kind)) {
     //              [stack] # class declaration
     //              [stack]
     //              [stack] # class expression
     //              [stack] CTOR
     return false;
   }
 
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -475,17 +475,19 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
 
   MOZ_MUST_USE bool emitPropertyList(ListNode* obj, PropertyEmitter& pe,
                                      PropListType type);
 
   FieldInitializers setupFieldInitializers(ListNode* classMembers);
   MOZ_MUST_USE bool emitCreateFieldKeys(ListNode* obj);
   MOZ_MUST_USE bool emitCreateFieldInitializers(ListNode* obj);
   const FieldInitializers& findFieldInitializersForCall();
-  MOZ_MUST_USE bool emitInitializeInstanceFields();
+  MOZ_MUST_USE bool emitCopyInitializersToLocalInitializers();
+  enum class IsSuperCall : bool { No, Yes };
+  MOZ_MUST_USE bool emitInitializeInstanceFields(IsSuperCall isSuperCall);
 
   // To catch accidental misuse, emitUint16Operand/emit3 assert that they are
   // not used to unconditionally emit JSOP_GETLOCAL. Variable access should
   // instead be emitted using EmitVarOp. In special cases, when the caller
   // definitely knows that a given local slot is unaliased, this function may be
   // used as a non-asserting version of emitUint16Operand.
   MOZ_MUST_USE bool emitLocalOp(JSOp op, uint32_t slot);
 
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -435,45 +435,44 @@ class FullParseHandler {
     if (!propdef) {
       return false;
     }
 
     addList(/* list = */ literal, /* kid = */ propdef);
     return true;
   }
 
-  MOZ_MUST_USE ClassMethod* newClassMethodDefinition(Node key,
-                                                     FunctionNodeType funNode,
-                                                     AccessorType atype,
-                                                     bool isStatic) {
+  MOZ_MUST_USE bool addClassMethodDefinition(ListNodeType memberList, Node key,
+                                             FunctionNodeType funNode,
+                                             AccessorType atype,
+                                             bool isStatic) {
+    MOZ_ASSERT(memberList->isKind(ParseNodeKind::ClassMemberList));
     MOZ_ASSERT(isUsableAsObjectPropertyName(key));
 
     checkAndSetIsDirectRHSAnonFunction(funNode);
 
-    return new_<ClassMethod>(key, funNode, atype, isStatic);
-  }
-
-  MOZ_MUST_USE ClassField* newClassFieldDefinition(
-      Node name, FunctionNodeType initializer) {
-    MOZ_ASSERT(isUsableAsObjectPropertyName(name));
-
-    return new_<ClassField>(name, initializer);
+    ClassMethod* classMethod = new_<ClassMethod>(key, funNode, atype, isStatic);
+    if (!classMethod) {
+      return false;
+    }
+    addList(/* list = */ memberList, /* kid = */ classMethod);
+    return true;
   }
 
-  MOZ_MUST_USE bool addClassMemberDefinition(ListNodeType memberList,
-                                             Node member) {
+  MOZ_MUST_USE bool addClassFieldDefinition(ListNodeType memberList, Node name,
+                                            FunctionNodeType initializer) {
     MOZ_ASSERT(memberList->isKind(ParseNodeKind::ClassMemberList));
-    // Constructors can be surrounded by LexicalScopes.
-    MOZ_ASSERT(member->isKind(ParseNodeKind::ClassMethod) ||
-               member->isKind(ParseNodeKind::ClassField) ||
-               member->isKind(ParseNodeKind::LexicalScope) &&
-                   member->as<LexicalScopeNode>().scopeBody()->isKind(
-                       ParseNodeKind::ClassMethod));
+    MOZ_ASSERT(isUsableAsObjectPropertyName(name));
+
+    ClassField* classField = new_<ClassField>(name, initializer);
 
-    addList(/* list = */ memberList, /* kid = */ member);
+    if (!classField) {
+      return false;
+    }
+    addList(/* list = */ memberList, /* kid = */ classField);
     return true;
   }
 
   UnaryNodeType newInitialYieldExpression(uint32_t begin, Node gen) {
     TokenPos pos(begin, begin + 1);
     return new_<UnaryNode>(ParseNodeKind::InitialYield, pos, gen);
   }
 
--- a/js/src/frontend/FunctionEmitter.cpp
+++ b/js/src/frontend/FunctionEmitter.cpp
@@ -472,18 +472,24 @@ bool FunctionScriptEmitter::prepareForBo
   if (funbox_->needsPromiseResult()) {
     if (!emitAsyncFunctionRejectPrologue()) {
       return false;
     }
   }
 
   if (funbox_->function()->kind() ==
       JSFunction::FunctionKind::ClassConstructor) {
-    if (!funbox_->isDerivedClassConstructor()) {
-      if (!bce_->emitInitializeInstanceFields()) {
+    if (funbox_->isDerivedClassConstructor()) {
+      if (!bce_->emitCopyInitializersToLocalInitializers()) {
+        //          [stack]
+        return false;
+      }
+    } else {
+      if (!bce_->emitInitializeInstanceFields(
+              BytecodeEmitter::IsSuperCall::No)) {
         //          [stack]
         return false;
       }
     }
   }
 
 #ifdef DEBUG
   state_ = State::Body;
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -2076,29 +2076,30 @@ class ClassNode : public TernaryNode {
     bool match = node.isKind(ParseNodeKind::ClassDecl);
     MOZ_ASSERT_IF(match, node.is<TernaryNode>());
     return match;
   }
 
   ClassNames* names() const {
     return kid1() ? &kid1()->as<ClassNames>() : nullptr;
   }
-
   ParseNode* heritage() const { return kid2(); }
-
   ListNode* memberList() const {
     ListNode* list =
         &kid3()->as<LexicalScopeNode>().scopeBody()->as<ListNode>();
     MOZ_ASSERT(list->isKind(ParseNodeKind::ClassMemberList));
     return list;
   }
-
-  LexicalScopeNode* scopeBindings() const {
-    LexicalScopeNode* scope = &kid3()->as<LexicalScopeNode>();
-    return scope->isEmptyScope() ? nullptr : scope;
+  bool isEmptyScope() const {
+    ParseNode* scope = kid3();
+    return scope->as<LexicalScopeNode>().isEmptyScope();
+  }
+  Handle<LexicalScope::Data*> scopeBindings() const {
+    ParseNode* scope = kid3();
+    return scope->as<LexicalScopeNode>().scopeBindings();
   }
 };
 
 #ifdef DEBUG
 void DumpParseTree(ParseNode* pn, GenericPrinter& out, int indent = 0);
 #endif
 
 class ParseNodeAllocator {
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1934,16 +1934,23 @@ GeneralParser<ParseHandler, Unit>::funct
                                              canSkipLazyClosedOverBindings)) {
       return null();
     }
     if (!pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings)) {
       return null();
     }
   }
 
+  if (kind == FunctionSyntaxKind::DerivedClassConstructor) {
+    if (!noteDeclaredName(cx_->names().dotLocalInitializers,
+                          DeclarationKind::Var, pos())) {
+      return null();
+    }
+  }
+
   return finishLexicalScope(pc_->varScope(), body);
 }
 
 JSFunction* AllocNewFunction(JSContext* cx, HandleAtom atom,
                              FunctionSyntaxKind kind,
                              GeneratorKind generatorKind,
                              FunctionAsyncKind asyncKind, HandleObject proto,
                              bool isSelfHosting /* = false */,
@@ -2918,23 +2925,16 @@ bool GeneralParser<ParseHandler, Unit>::
     bool isStandaloneFunction /* = false */) {
   // Given a properly initialized parse context, try to parse an actual
   // function without concern for conversion to strict mode, use of lazy
   // parsing and such.
 
   FunctionBox* funbox = pc_->functionBox();
   RootedFunction fun(cx_, funbox->function());
 
-  if (kind == FunctionSyntaxKind::ClassConstructor ||
-      kind == FunctionSyntaxKind::DerivedClassConstructor) {
-    if (!noteUsedName(cx_->names().dotInitializers)) {
-      return null();
-    }
-  }
-
   // See below for an explanation why arrow function parameters and arrow
   // function bodies are parsed with different yield/await settings.
   {
     AwaitHandling awaitHandling =
         (funbox->isAsync() ||
          (kind == FunctionSyntaxKind::Arrow && awaitIsKeyword()))
             ? AwaitIsKeyword
             : AwaitIsName;
@@ -6818,23 +6818,18 @@ bool GeneralParser<ParseHandler, Unit>::
     if (!initializer) {
       return false;
     }
 
     if (!matchOrInsertSemicolon(TokenStream::SlashIsInvalid)) {
       return false;
     }
 
-    ClassFieldType field =
-        handler_.newClassFieldDefinition(propName, initializer);
-    if (!field) {
-      return false;
-    }
-
-    return handler_.addClassMemberDefinition(classMembers, field);
+    return handler_.addClassFieldDefinition(classMembers, propName,
+                                            initializer);
   }
 
   if (propType != PropertyType::Getter && propType != PropertyType::Setter &&
       propType != PropertyType::Method &&
       propType != PropertyType::GeneratorMethod &&
       propType != PropertyType::AsyncMethod &&
       propType != PropertyType::AsyncGeneratorMethod) {
     errorAt(propNameOffset, JSMSG_BAD_METHOD_DEF);
@@ -6875,106 +6870,59 @@ bool GeneralParser<ParseHandler, Unit>::
       funName = className;
       break;
     default:
       if (!anyChars.isCurrentTokenType(TokenKind::RightBracket)) {
         funName = propAtom;
       }
   }
 
-  // .fieldKeys must be declared outside the scope .initializers is declared in,
-  // hence this extra scope.
-  Maybe<ParseContext::Scope> dotInitializersScope;
-  if (isConstructor && !options().selfHostingMode) {
-    dotInitializersScope.emplace(this);
-    if (!dotInitializersScope->init(pc_)) {
-      return null();
-    }
-
-    if (!noteDeclaredName(cx_->names().dotInitializers, DeclarationKind::Let,
-                          pos())) {
-      return null();
-    }
-  }
-
   // Calling toString on constructors need to return the source text for
   // the entire class. The end offset is unknown at this point in
   // parsing and will be amended when class parsing finishes below.
   FunctionNodeType funNode = methodDefinition(
       isConstructor ? classStartOffset : propNameOffset, propType, funName);
   if (!funNode) {
     return false;
   }
 
   AccessorType atype = ToAccessorType(propType);
-
-  Node method =
-      handler_.newClassMethodDefinition(propName, funNode, atype, isStatic);
-  if (!method) {
-    return false;
-  }
-
-  if (dotInitializersScope.isSome()) {
-    method = finishLexicalScope(*dotInitializersScope, method);
-    if (!method) {
-      return null();
-    }
-    dotInitializersScope.reset();
-  }
-
-  return handler_.addClassMemberDefinition(classMembers, method);
+  return handler_.addClassMethodDefinition(classMembers, propName, funNode,
+                                           atype, isStatic);
 }
 
 template <class ParseHandler, typename Unit>
 bool GeneralParser<ParseHandler, Unit>::finishClassConstructor(
     const ParseContext::ClassStatement& classStmt, HandlePropertyName className,
     HasHeritage hasHeritage, uint32_t classStartOffset, uint32_t classEndOffset,
     size_t numFields, ListNodeType& classMembers) {
   // Fields cannot re-use the constructor obtained via JSOP_CLASSCONSTRUCTOR or
   // JSOP_DERIVEDCONSTRUCTOR due to needing to emit calls to the field
   // initializers in the constructor. So, synthesize a new one.
   if (classStmt.constructorBox == nullptr && numFields > 0) {
-    MOZ_ASSERT(!options().selfHostingMode);
-    // Unconditionally create the scope here, because it's always the
-    // constructor.
-    ParseContext::Scope dotInitializersScope(this);
-    if (!dotInitializersScope.init(pc_)) {
-      return null();
-    }
-
-    if (!noteDeclaredName(cx_->names().dotInitializers, DeclarationKind::Let,
-                          pos())) {
-      return null();
-    }
-
     // synthesizeConstructor assigns to classStmt.constructorBox
     FunctionNodeType synthesizedCtor =
         synthesizeConstructor(className, classStartOffset, hasHeritage);
     if (!synthesizedCtor) {
       return false;
     }
 
     MOZ_ASSERT(classStmt.constructorBox != nullptr);
 
     // Note: the *function* has the name of the class, but the *property*
     // containing the function has the name "constructor"
     Node constructorNameNode =
         handler_.newObjectLiteralPropertyName(cx_->names().constructor, pos());
     if (!constructorNameNode) {
       return false;
     }
-    ClassMethodType method = handler_.newClassMethodDefinition(
-        constructorNameNode, synthesizedCtor, AccessorType::None,
-        /* isStatic = */ false);
-    if (!method) {
-      return false;
-    }
-    LexicalScopeNodeType scope =
-        finishLexicalScope(dotInitializersScope, method);
-    if (!handler_.addClassMemberDefinition(classMembers, scope)) {
+
+    if (!handler_.addClassMethodDefinition(classMembers, constructorNameNode,
+                                           synthesizedCtor, AccessorType::None,
+                                           /* isStatic = */ false)) {
       return false;
     }
   }
 
   if (FunctionBox* ctorbox = classStmt.constructorBox) {
     // Amend the toStringEnd offset for the constructor now that we've
     // finished parsing the class.
     ctorbox->toStringEnd = classEndOffset;
@@ -7089,16 +7037,47 @@ GeneralParser<ParseHandler, Unit>::class
                        classMembers, &done)) {
         return null();
       }
       if (done) {
         break;
       }
     }
 
+    if (numFields > 0) {
+      // .initializers is always closed over by the constructor when there are
+      // fields with initializers. However, there's some strange circumstances
+      // which prevents us from using the normal noteUsedName() system. We
+      // cannot call noteUsedName(".initializers") when parsing the constructor,
+      // because .initializers should be marked as used *only if* there are
+      // fields with initializers. Even if we haven't seen any fields yet,
+      // there may be fields after the constructor.
+      // Consider the following class:
+      //
+      //  class C {
+      //    constructor() {
+      //      // do we noteUsedName(".initializers") here?
+      //    }
+      //    // ... because there might be some fields down here.
+      //  }
+      //
+      // So, instead, at the end of class parsing (where we are now), we do some
+      // tricks to pretend that noteUsedName(".initializers") was called in the
+      // constructor.
+      if (!usedNames_.markAsAlwaysClosedOver(cx_, cx_->names().dotInitializers,
+                                             pc_->scriptId(),
+                                             pc_->innermostScope()->id())) {
+        return null();
+      }
+      if (!noteDeclaredName(cx_->names().dotInitializers, DeclarationKind::Let,
+                            namePos)) {
+        return null();
+      }
+    }
+
     if (numFieldKeys > 0) {
       if (!noteDeclaredName(cx_->names().dotFieldKeys, DeclarationKind::Let,
                             namePos)) {
         return null();
       }
     }
 
     classEndOffset = pos().end;
@@ -7225,18 +7204,24 @@ GeneralParser<ParseHandler, Unit>::synth
   if (!stmtList) {
     return null();
   }
 
   if (!noteUsedName(cx_->names().dotThis)) {
     return null();
   }
 
-  if (!noteUsedName(cx_->names().dotInitializers)) {
-    return null();
+  // One might expect a noteUsedName(".initializers") here. See comment in
+  // GeneralParser<ParseHandler, Unit>::classDefinition on why it's not here.
+
+  if (hasHeritage == HasHeritage::Yes) {
+    if (!noteDeclaredName(cx_->names().dotLocalInitializers,
+                          DeclarationKind::Var, synthesizedBodyPos)) {
+      return null();
+    }
   }
 
   bool canSkipLazyClosedOverBindings = handler_.canSkipLazyClosedOverBindings();
   if (!pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings)) {
     return null();
   }
 
   if (hasHeritage == HasHeritage::Yes) {
@@ -7277,16 +7262,20 @@ GeneralParser<ParseHandler, Unit>::synth
       return null();
     }
 
     BinaryNodeType setThis = handler_.newSetThis(thisName, superCall);
     if (!setThis) {
       return null();
     }
 
+    if (!noteUsedName(cx_->names().dotLocalInitializers)) {
+      return null();
+    }
+
     UnaryNodeType exprStatement =
         handler_.newExprStatement(setThis, synthesizedBodyPos.end);
     if (!exprStatement) {
       return null();
     }
 
     handler_.addStatementToList(stmtList, exprStatement);
   }
@@ -9034,17 +9023,17 @@ typename ParseHandler::Node GeneralParse
           return null();
         }
 
         nextMember = handler_.newSetThis(thisName, nextMember);
         if (!nextMember) {
           return null();
         }
 
-        if (!noteUsedName(cx_->names().dotInitializers)) {
+        if (!noteUsedName(cx_->names().dotLocalInitializers)) {
           return null();
         }
       } else {
         if (options().selfHostingMode && handler_.isPropertyAccess(lhs)) {
           error(JSMSG_SELFHOSTED_METHOD_CALL);
           return null();
         }
 
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -343,27 +343,24 @@ class SyntaxParseHandler {
                                       Node inner) {
     return true;
   }
   MOZ_MUST_USE bool addObjectMethodDefinition(ListNodeType literal, Node key,
                                               FunctionNodeType funNode,
                                               AccessorType atype) {
     return true;
   }
-  MOZ_MUST_USE Node newClassMethodDefinition(Node key, FunctionNodeType funNode,
+  MOZ_MUST_USE bool addClassMethodDefinition(ListNodeType memberList, Node key,
+                                             FunctionNodeType funNode,
                                              AccessorType atype,
                                              bool isStatic) {
-    return NodeGeneric;
+    return true;
   }
-  MOZ_MUST_USE Node newClassFieldDefinition(Node name,
+  MOZ_MUST_USE bool addClassFieldDefinition(ListNodeType memberList, Node name,
                                             FunctionNodeType initializer) {
-    return NodeGeneric;
-  }
-  MOZ_MUST_USE bool addClassMemberDefinition(ListNodeType memberList,
-                                             Node member) {
     return true;
   }
   UnaryNodeType newYieldExpression(uint32_t begin, Node value) {
     return NodeGeneric;
   }
   UnaryNodeType newYieldStarExpression(uint32_t begin, Node value) {
     return NodeGeneric;
   }
deleted file mode 100644
--- a/js/src/jit-test/tests/fields/bug1547467.js
+++ /dev/null
@@ -1,19 +0,0 @@
-// |jit-test| --enable-experimental-fields
-
-load(libdir + "asserts.js");
-
-assertThrowsInstanceOf(() => {
-        class foo extends null {
-            constructor(a = class bar extends bar {}) {}
-        }
-        new foo();
-    },
-    ReferenceError
-)
-
-class B { }
-class C extends B {
-    constructor(a = class D { [super()] = 5; }) {
-    }
-}
-new C()
deleted file mode 100644
--- a/js/src/jit-test/tests/fields/bug1552229.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// |jit-test| --enable-experimental-fields
-
-let i = 0;
-function f(x) {
-    assertEq(++i, x);
-    return x;
-}
-class C{
-    [f(1)](){}
-    [f(2)] = "hi";
-    [f(3)](){}
-    [f(4)] = "hi";
-}
-new C();
-
-if (typeof reportCompare === "function")
-  reportCompare(true, true);
--- a/js/src/jit-test/tests/fields/super.js
+++ b/js/src/jit-test/tests/fields/super.js
@@ -1,78 +1,57 @@
 // |jit-test| --enable-experimental-fields
 
 class Base {
 }
 
 class C extends Base {
   field;
 }
-let c = new C();
-assertEq(true, "field" in c);
+new C();
 
 var D = class extends Base {
   field;
 };
-let d = new D();
-assertEq(true, "field" in d);
+new D();
 
 class E extends Base {
   field;
   constructor() {
     super();
   }
 };
-let e = new E();
-assertEq(true, "field" in e);
+new E();
 
 class F extends Base {
   constructor() {
     super();
   }
   field;
 };
-let f = new F();
-assertEq(true, "field" in f);
+new F();
 
 class G extends Base {
   field2 = 2;
   constructor() {
     super();
   }
   field3 = 3;
 };
-let g = new G();
-assertEq(2, g.field2);
-assertEq(3, g.field3);
+new G();
 
 class H extends Base {
   field = 2;
   constructor() {
     eval("super()");
   }
 };
-let h = new H();
-assertEq(2, h.field);
+new H();
 
 class I extends Base {
   field = 2;
   constructor() {
       class Tmp {
-          field = 10;
           [super()];
       }
   }
 };
-let i = new I();
-assertEq(2, i.field);
-
-class J extends Base {
-  field = 2;
-  constructor() {
-      class Tmp {
-          field = 10;
-          [super()](){}
-      }
-  }
-};
-let j = new J();
-assertEq(2, j.field);
+new I();
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -117,16 +117,17 @@
   MACRO(direction, direction, "direction")                                     \
   MACRO(displayURL, displayURL, "displayURL")                                  \
   MACRO(do, do_, "do")                                                         \
   MACRO(domNode, domNode, "domNode")                                           \
   MACRO(done, done, "done")                                                    \
   MACRO(dotGenerator, dotGenerator, ".generator")                              \
   MACRO(dotThis, dotThis, ".this")                                             \
   MACRO(dotInitializers, dotInitializers, ".initializers")                     \
+  MACRO(dotLocalInitializers, dotLocalInitializers, ".localInitializers")      \
   MACRO(dotFieldKeys, dotFieldKeys, ".fieldKeys")                              \
   MACRO(each, each, "each")                                                    \
   MACRO(elementType, elementType, "elementType")                               \
   MACRO(else, else_, "else")                                                   \
   MACRO(empty, empty, "")                                                      \
   MACRO(emptyRegExp, emptyRegExp, "(?:)")                                      \
   MACRO(encodeURI, encodeURI, "encodeURI")                                     \
   MACRO(encodeURIComponent, encodeURIComponent, "encodeURIComponent")          \