Bug 1534721 - Handle fields in derived classes. r=jorendorff
authorAshley Hauck <khyperia@mozilla.com>
Mon, 08 Apr 2019 22:31:19 +0000
changeset 468437 ed722cf1b2d96bd3b511b04993cd52d4d528d679
parent 468436 3fb311555933b9df68642f533201d3822c243a22
child 468438 57538e81d06b0cb09b086b15c88c826d27124b34
push id35837
push userrmaries@mozilla.com
push dateTue, 09 Apr 2019 03:43:40 +0000
treeherdermozilla-central@9eb55c9bf557 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1534721
milestone68.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 1534721 - Handle fields in derived classes. r=jorendorff Differential Revision: https://phabricator.services.mozilla.com/D26008
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/FunctionEmitter.cpp
js/src/frontend/FunctionEmitter.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/jit-test/tests/fields/super.js
js/src/tests/jstests.list
js/src/vm/JSScript.h
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -981,18 +981,24 @@ static bool CompileLazyFunctionImpl(JSCo
 
   if (lazy->isLikelyConstructorWrapper()) {
     script->setLikelyConstructorWrapper();
   }
   if (lazy->hasBeenCloned()) {
     script->setHasBeenCloned();
   }
 
+  FieldInitializers fieldInitializers = FieldInitializers::Invalid();
+  if (fun->kind() == JSFunction::FunctionKind::ClassConstructor) {
+    fieldInitializers = lazy->getFieldInitializers();
+  }
+
   BytecodeEmitter bce(/* parent = */ nullptr, &parser, pn->funbox(), script,
-                      lazy, pn->pn_pos, BytecodeEmitter::LazyFunction);
+                      lazy, pn->pn_pos, BytecodeEmitter::LazyFunction,
+                      fieldInitializers);
   if (!bce.init()) {
     return false;
   }
 
   if (!bce.emitFunctionScript(pn, BytecodeEmitter::TopLevelFunction::Yes)) {
     return false;
   }
 
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -86,34 +86,31 @@ static bool ParseNodeRequiresSpecialLine
   // handling to avoid strange stepping behavior.
   // Functions usually shouldn't have location information (bug 1431202).
 
   ParseNodeKind kind = pn->getKind();
   return kind == ParseNodeKind::WhileStmt || kind == ParseNodeKind::ForStmt ||
          kind == ParseNodeKind::Function;
 }
 
-BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent, SharedContext* sc,
-                                 HandleScript script,
-                                 Handle<LazyScript*> lazyScript,
-                                 uint32_t lineNum, EmitterMode emitterMode)
+BytecodeEmitter::BytecodeEmitter(
+    BytecodeEmitter* parent, SharedContext* sc, HandleScript script,
+    Handle<LazyScript*> lazyScript, uint32_t lineNum, EmitterMode emitterMode,
+    FieldInitializers fieldInitializers /* = FieldInitializers::Invalid() */)
     : sc(sc),
       cx(sc->cx_),
       parent(parent),
       script(cx, script),
       lazyScript(cx, lazyScript),
       code_(cx),
       notes_(cx),
       currentLine_(lineNum),
+      fieldInitializers_(fieldInitializers),
       atomIndices(cx->frontendCollectionPool()),
       firstLine(lineNum),
-      fieldInitializers_(parent
-                             ? parent->fieldInitializers_
-                             : lazyScript ? lazyScript->getFieldInitializers()
-                                          : FieldInitializers::Invalid()),
       numberList(cx),
       scopeList(cx),
       tryNoteList(cx),
       scopeNoteList(cx),
       resumeOffsetList(cx),
       emitterMode(emitterMode) {
   MOZ_ASSERT_IF(emitterMode == LazyFunction, lazyScript);
 
@@ -122,27 +119,31 @@ BytecodeEmitter::BytecodeEmitter(Bytecod
     numICEntries = sc->asFunctionBox()->function()->nargs() + 1;
   }
 }
 
 BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
                                  BCEParserHandle* handle, SharedContext* sc,
                                  HandleScript script,
                                  Handle<LazyScript*> lazyScript,
-                                 uint32_t lineNum, EmitterMode emitterMode)
-    : BytecodeEmitter(parent, sc, script, lazyScript, lineNum, emitterMode) {
+                                 uint32_t lineNum, EmitterMode emitterMode,
+                                 FieldInitializers fieldInitializers)
+    : BytecodeEmitter(parent, sc, script, lazyScript, lineNum, emitterMode,
+                      fieldInitializers) {
   parser = handle;
 }
 
 BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
                                  const EitherParser& parser, SharedContext* sc,
                                  HandleScript script,
                                  Handle<LazyScript*> lazyScript,
-                                 uint32_t lineNum, EmitterMode emitterMode)
-    : BytecodeEmitter(parent, sc, script, lazyScript, lineNum, emitterMode) {
+                                 uint32_t lineNum, EmitterMode emitterMode,
+                                 FieldInitializers fieldInitializers)
+    : BytecodeEmitter(parent, sc, script, lazyScript, lineNum, emitterMode,
+                      fieldInitializers) {
   ep_.emplace(parser);
   this->parser = ep_.ptr();
 }
 
 void BytecodeEmitter::initFromBodyPosition(TokenPos bodyPosition) {
   setScriptStartOffsetIfUnset(bodyPosition.begin);
   setFunctionBodyEndPos(bodyPosition.end);
 }
@@ -2348,16 +2349,20 @@ bool BytecodeEmitter::emitSetThis(Binary
     //              [stack] NEWTHIS
     return false;
   }
   if (!noe.emitAssignment()) {
     //              [stack] NEWTHIS
     return false;
   }
 
+  if (!emitInitializeInstanceFields()) {
+    return false;
+  }
+
   return true;
 }
 
 bool BytecodeEmitter::defineHoistedTopLevelFunctions(ParseNode* body) {
   MOZ_ASSERT(inPrologue());
   MOZ_ASSERT(sc->isGlobalContext() || (sc->isEvalContext() && !sc->strict()));
   MOZ_ASSERT(body->is<LexicalScopeNode>() || body->is<ListNode>());
 
@@ -2479,16 +2484,20 @@ bool BytecodeEmitter::emitFunctionScript
                                          TopLevelFunction isTopLevel) {
   MOZ_ASSERT(inPrologue());
   ListNode* paramsBody = &funNode->body()->as<ListNode>();
   MOZ_ASSERT(paramsBody->isKind(ParseNodeKind::ParamsBody));
   FunctionBox* funbox = sc->asFunctionBox();
   AutoFrontendTraceLog traceLog(cx, TraceLogger_BytecodeEmission,
                                 parser->errorReporter(), funbox);
 
+  MOZ_ASSERT((fieldInitializers_.valid) ==
+             (funbox->function()->kind() ==
+              JSFunction::FunctionKind::ClassConstructor));
+
   setScriptStartOffsetIfUnset(paramsBody->pn_pos.begin);
 
   //                [stack]
 
   FunctionScriptEmitter fse(this, funbox, Some(paramsBody->pn_pos.begin),
                             Some(paramsBody->pn_pos.end));
   if (!fse.prepareForParameters()) {
     //              [stack]
@@ -2520,16 +2529,18 @@ bool BytecodeEmitter::emitFunctionScript
       return false;
     }
   }
 
   if (!fse.initScript()) {
     return false;
   }
 
+  script->setFieldInitializers(fieldInitializers_);
+
   return true;
 }
 
 bool BytecodeEmitter::emitDestructuringLHSRef(ParseNode* target,
                                               size_t* emitted) {
   *emitted = 0;
 
   if (target->isKind(ParseNodeKind::Spread)) {
@@ -5596,21 +5607,26 @@ bool BytecodeEmitter::emitFor(ForNode* f
   if (forNode->head()->isKind(ParseNodeKind::ForIn)) {
     return emitForIn(forNode, headLexicalEmitterScope);
   }
 
   MOZ_ASSERT(forNode->head()->isKind(ParseNodeKind::ForOf));
   return emitForOf(forNode, headLexicalEmitterScope);
 }
 
-MOZ_NEVER_INLINE bool BytecodeEmitter::emitFunction(FunctionNode* funNode,
-                                                    bool needsProto) {
+MOZ_NEVER_INLINE bool BytecodeEmitter::emitFunction(
+    FunctionNode* funNode, bool needsProto /* = false */,
+    ListNode* classContentsIfConstructor /* = nullptr */) {
   FunctionBox* funbox = funNode->funbox();
   RootedFunction fun(cx, funbox->function());
 
+  MOZ_ASSERT((classContentsIfConstructor != nullptr) ==
+             (funbox->function()->kind() ==
+              JSFunction::FunctionKind::ClassConstructor));
+
   //                [stack]
 
   FunctionEmitter fe(this, funbox, funNode->syntaxKind(),
                      funNode->functionIsHoisted()
                          ? FunctionEmitter::IsHoisted::Yes
                          : FunctionEmitter::IsHoisted::No);
 
   // Set the |wasEmitted| flag in the funbox once the function has been
@@ -5627,16 +5643,21 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::e
 
   if (fun->isInterpreted()) {
     if (fun->isInterpretedLazy()) {
       if (!fe.emitLazy()) {
         //          [stack] FUN?
         return false;
       }
 
+      if (classContentsIfConstructor) {
+        fun->lazyScript()->setFieldInitializers(
+            setupFieldInitializers(classContentsIfConstructor));
+      }
+
       return true;
     }
 
     if (!fe.prepareForNonLazy()) {
       //            [stack]
       return false;
     }
 
@@ -5657,28 +5678,35 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::e
     }
 
     EmitterMode nestedMode = emitterMode;
     if (nestedMode == BytecodeEmitter::LazyFunction) {
       MOZ_ASSERT(lazyScript->isBinAST());
       nestedMode = BytecodeEmitter::Normal;
     }
 
+    FieldInitializers fieldInitializers = FieldInitializers::Invalid();
+    if (classContentsIfConstructor) {
+      fieldInitializers = setupFieldInitializers(classContentsIfConstructor);
+    }
+
     BytecodeEmitter bce2(this, parser, funbox, script,
                          /* lazyScript = */ nullptr, funNode->pn_pos,
-                         nestedMode);
+                         nestedMode, fieldInitializers);
     if (!bce2.init()) {
       return false;
     }
 
     /* We measured the max scope depth when we parsed the function. */
     if (!bce2.emitFunctionScript(funNode, TopLevelFunction::No)) {
       return false;
     }
 
+    // fieldInitializers are copied to the JSScript inside BytecodeEmitter
+
     if (funbox->isLikelyConstructorWrapper()) {
       script->setLikelyConstructorWrapper();
     }
 
     if (!fe.emitNonLazyEnd()) {
       //            [stack] FUN?
       return false;
     }
@@ -7956,17 +7984,17 @@ bool BytecodeEmitter::emitCreateFieldKey
     //            [stack]
     return false;
   }
 
   return true;
 }
 
 bool BytecodeEmitter::emitCreateFieldInitializers(ListNode* obj) {
-  const FieldInitializers& fieldInitializers = fieldInitializers_;
+  FieldInitializers fieldInitializers = setupFieldInitializers(obj);
   MOZ_ASSERT(fieldInitializers.valid);
   size_t numFields = fieldInitializers.numFieldInitializers;
 
   if (numFields == 0) {
     return true;
   }
 
   // .initializers is a variable that stores an array of lambdas containing
@@ -8014,16 +8042,104 @@ bool BytecodeEmitter::emitCreateFieldIni
   if (!emit1(JSOP_POP)) {
     //            [stack] CTOR? OBJ
     return false;
   }
 
   return true;
 }
 
+const FieldInitializers& BytecodeEmitter::findFieldInitializersForCall() {
+  for (BytecodeEmitter* current = this; current; current = current->parent) {
+    if (current->sc->isFunctionBox()) {
+      FunctionBox* box = current->sc->asFunctionBox();
+      if (box->function()->kind() ==
+          JSFunction::FunctionKind::ClassConstructor) {
+        const FieldInitializers& fieldInitializers =
+            current->getFieldInitializers();
+        MOZ_ASSERT(fieldInitializers.valid);
+        return fieldInitializers;
+      }
+    }
+  }
+
+  for (ScopeIter si(innermostScope()); si; si++) {
+    if (si.scope()->is<FunctionScope>()) {
+      JSFunction* fun = si.scope()->as<FunctionScope>().canonicalFunction();
+      if (fun->kind() == JSFunction::FunctionKind::ClassConstructor) {
+        const FieldInitializers& fieldInitializers =
+            fun->isInterpretedLazy()
+                ? fun->lazyScript()->getFieldInitializers()
+                : fun->nonLazyScript()->getFieldInitializers();
+        MOZ_ASSERT(fieldInitializers.valid);
+        return fieldInitializers;
+      }
+    }
+  }
+
+  MOZ_CRASH("Constructor for field initializers not found.");
+}
+
+bool BytecodeEmitter::emitInitializeInstanceFields() {
+  const FieldInitializers& fieldInitializers = findFieldInitializersForCall();
+  size_t numFields = fieldInitializers.numFieldInitializers;
+
+  if (numFields == 0) {
+    return true;
+  }
+
+  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)) {
+        //          [stack] ARRAY ARRAY
+        return false;
+      }
+    }
+
+    if (!emitNumberOp(fieldIndex)) {
+      //            [stack] ARRAY? ARRAY INDEX
+      return false;
+    }
+
+    // Don't use CALLELEM here, because the receiver of the call != the receiver
+    // of this getelem. (Specifically, the call receiver is `this`, and the
+    // receiver of this getelem is `.initializers`)
+    if (!emit1(JSOP_GETELEM)) {
+      //            [stack] ARRAY? FUNC
+      return false;
+    }
+
+    // This is guaranteed to run after super(), so we don't need TDZ checks.
+    if (!emitGetName(cx->names().dotThis)) {
+      //            [stack] ARRAY? FUNC THIS
+      return false;
+    }
+
+    if (!emitCall(JSOP_CALL_IGNORES_RV, 0)) {
+      //            [stack] ARRAY? RVAL
+      return false;
+    }
+
+    if (!emit1(JSOP_POP)) {
+      //            [stack] ARRAY?
+      return false;
+    }
+  }
+
+  return true;
+}
+
 // Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
 // the comment on emitSwitch.
 MOZ_NEVER_INLINE bool BytecodeEmitter::emitObject(ListNode* objNode) {
   if (!objNode->hasNonConstInitializer() && objNode->head() &&
       checkSingletonContext()) {
     return emitSingletonInitialiser(objNode);
   }
 
@@ -8504,47 +8620,29 @@ static MOZ_ALWAYS_INLINE FunctionNode* F
         return &method.method();
       }
     }
   }
 
   return nullptr;
 }
 
-class AutoResetFieldInitializers {
-  BytecodeEmitter* bce;
-  FieldInitializers oldFieldInfo;
-
- public:
-  AutoResetFieldInitializers(BytecodeEmitter* bce,
-                             FieldInitializers newFieldInfo)
-      : bce(bce), oldFieldInfo(bce->fieldInitializers_) {
-    bce->fieldInitializers_ = newFieldInfo;
-  }
-
-  ~AutoResetFieldInitializers() { bce->fieldInitializers_ = oldFieldInfo; }
-};
-
 // 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();
   FunctionNode* constructor = FindConstructor(cx, classMembers);
 
-  // set this->fieldInitializers_
-  AutoResetFieldInitializers _innermostClassAutoReset(
-      this, setupFieldInitializers(classMembers));
-
   // If |nameKind != ClassNameKind::ComputedName|
   //                [stack]
   // Else
   //                [stack] NAME
 
   ClassEmitter ce(this);
   RootedAtom innerName(cx);
   ClassEmitter::Kind kind = ClassEmitter::Kind::Expression;
@@ -8594,17 +8692,17 @@ bool BytecodeEmitter::emitClass(
     }
   }
 
   // Stack currently has HOMEOBJ followed by optional HERITAGE. When HERITAGE
   // is not used, an implicit value of %FunctionPrototype% is implied.
   if (constructor) {
     bool needsHomeObject = constructor->funbox()->needsHomeObject();
     // HERITAGE is consumed inside emitFunction.
-    if (!emitFunction(constructor, isDerived)) {
+    if (!emitFunction(constructor, isDerived, classMembers)) {
       //            [stack] HOMEOBJ CTOR
       return false;
     }
     if (nameKind == ClassNameKind::InferredName) {
       if (!setFunName(constructor->funbox()->function(),
                       nameForAnonymousClass)) {
         return false;
       }
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -145,16 +145,19 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
 
   uint32_t lastSeparatorOffet_ = 0;
   uint32_t lastSeparatorLine_ = 0;
   uint32_t lastSeparatorColumn_ = 0;
 
   // switchToMain sets this to the bytecode offset of the main section.
   mozilla::Maybe<uint32_t> mainOffset_ = {};
 
+  /* field info for enclosing class */
+  const FieldInitializers fieldInitializers_;
+
  public:
   // Last jump target emitted.
   JumpTarget lastTarget = {-1 - ptrdiff_t(JSOP_JUMPTARGET_LENGTH)};
 
   // Private storage for parser wrapper. DO NOT REFERENCE INTERNALLY. May not be
   // initialized. Use |parser| instead.
   mozilla::Maybe<EitherParser> ep_ = {};
   BCEParserHandle* parser = nullptr;
@@ -171,18 +174,17 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
   uint32_t bodyScopeIndex =
       UINT32_MAX; /* index into scopeList of the body scope */
 
   EmitterScope* varEmitterScope = nullptr;
   NestableControl* innermostNestableControl = nullptr;
   EmitterScope* innermostEmitterScope_ = nullptr;
   TDZCheckCache* innermostTDZCheckCache = nullptr;
 
-  /* field info for enclosing class */
-  FieldInitializers fieldInitializers_;
+  const FieldInitializers& getFieldInitializers() { return fieldInitializers_; }
 
 #ifdef DEBUG
   bool unstableEmitterScope = false;
 
   friend class AutoCheckUnstableEmitterScope;
 #endif
 
   EmitterScope* innermostEmitterScope() const {
@@ -249,79 +251,86 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
   /*
    * Note that BytecodeEmitters are magic: they own the arena "top-of-stack"
    * space above their tempMark points. This means that you cannot alloc from
    * tempLifoAlloc and save the pointer beyond the next BytecodeEmitter
    * destruction.
    */
  private:
   // Internal constructor, for delegation use only.
-  BytecodeEmitter(BytecodeEmitter* parent, SharedContext* sc,
-                  HandleScript script, Handle<LazyScript*> lazyScript,
-                  uint32_t lineNum, EmitterMode emitterMode);
+  BytecodeEmitter(
+      BytecodeEmitter* parent, SharedContext* sc, HandleScript script,
+      Handle<LazyScript*> lazyScript, uint32_t lineNum, EmitterMode emitterMode,
+      FieldInitializers fieldInitializers = FieldInitializers::Invalid());
 
   void initFromBodyPosition(TokenPos bodyPosition);
 
   /*
    * Helper for reporting that we have insufficient args.  pluralizer
    * should be "s" if requiredArgs is anything other than "1" and ""
    * if requiredArgs is "1".
    */
   void reportNeedMoreArgsError(ParseNode* pn, const char* errorName,
                                const char* requiredArgs, const char* pluralizer,
                                const ListNode* argsList);
 
  public:
-  BytecodeEmitter(BytecodeEmitter* parent, BCEParserHandle* parser,
-                  SharedContext* sc, HandleScript script,
-                  Handle<LazyScript*> lazyScript, uint32_t lineNum,
-                  EmitterMode emitterMode = Normal);
+  BytecodeEmitter(
+      BytecodeEmitter* parent, BCEParserHandle* parser, SharedContext* sc,
+      HandleScript script, Handle<LazyScript*> lazyScript, uint32_t lineNum,
+      EmitterMode emitterMode = Normal,
+      FieldInitializers fieldInitializers = FieldInitializers::Invalid());
 
-  BytecodeEmitter(BytecodeEmitter* parent, const EitherParser& parser,
-                  SharedContext* sc, HandleScript script,
-                  Handle<LazyScript*> lazyScript, uint32_t lineNum,
-                  EmitterMode emitterMode = Normal);
+  BytecodeEmitter(
+      BytecodeEmitter* parent, const EitherParser& parser, SharedContext* sc,
+      HandleScript script, Handle<LazyScript*> lazyScript, uint32_t lineNum,
+      EmitterMode emitterMode = Normal,
+      FieldInitializers fieldInitializers = FieldInitializers::Invalid());
 
   template <typename Unit>
-  BytecodeEmitter(BytecodeEmitter* parent,
-                  Parser<FullParseHandler, Unit>* parser, SharedContext* sc,
-                  HandleScript script, Handle<LazyScript*> lazyScript,
-                  uint32_t lineNum, EmitterMode emitterMode = Normal)
+  BytecodeEmitter(
+      BytecodeEmitter* parent, Parser<FullParseHandler, Unit>* parser,
+      SharedContext* sc, HandleScript script, Handle<LazyScript*> lazyScript,
+      uint32_t lineNum, EmitterMode emitterMode = Normal,
+      FieldInitializers fieldInitializers = FieldInitializers::Invalid())
       : BytecodeEmitter(parent, EitherParser(parser), sc, script, lazyScript,
-                        lineNum, emitterMode) {}
+                        lineNum, emitterMode, fieldInitializers) {}
 
   // An alternate constructor that uses a TokenPos for the starting
   // line and that sets functionBodyEndPos as well.
-  BytecodeEmitter(BytecodeEmitter* parent, BCEParserHandle* parser,
-                  SharedContext* sc, HandleScript script,
-                  Handle<LazyScript*> lazyScript, TokenPos bodyPosition,
-                  EmitterMode emitterMode = Normal)
+  BytecodeEmitter(
+      BytecodeEmitter* parent, BCEParserHandle* parser, SharedContext* sc,
+      HandleScript script, Handle<LazyScript*> lazyScript,
+      TokenPos bodyPosition, EmitterMode emitterMode = Normal,
+      FieldInitializers fieldInitializers = FieldInitializers::Invalid())
       : BytecodeEmitter(parent, parser, sc, script, lazyScript,
                         parser->errorReporter().lineAt(bodyPosition.begin),
-                        emitterMode) {
+                        emitterMode, fieldInitializers) {
     initFromBodyPosition(bodyPosition);
   }
 
-  BytecodeEmitter(BytecodeEmitter* parent, const EitherParser& parser,
-                  SharedContext* sc, HandleScript script,
-                  Handle<LazyScript*> lazyScript, TokenPos bodyPosition,
-                  EmitterMode emitterMode = Normal)
+  BytecodeEmitter(
+      BytecodeEmitter* parent, const EitherParser& parser, SharedContext* sc,
+      HandleScript script, Handle<LazyScript*> lazyScript,
+      TokenPos bodyPosition, EmitterMode emitterMode = Normal,
+      FieldInitializers fieldInitializers = FieldInitializers::Invalid())
       : BytecodeEmitter(parent, parser, sc, script, lazyScript,
                         parser.errorReporter().lineAt(bodyPosition.begin),
-                        emitterMode) {
+                        emitterMode, fieldInitializers) {
     initFromBodyPosition(bodyPosition);
   }
 
   template <typename Unit>
-  BytecodeEmitter(BytecodeEmitter* parent,
-                  Parser<FullParseHandler, Unit>* parser, SharedContext* sc,
-                  HandleScript script, Handle<LazyScript*> lazyScript,
-                  TokenPos bodyPosition, EmitterMode emitterMode = Normal)
+  BytecodeEmitter(
+      BytecodeEmitter* parent, Parser<FullParseHandler, Unit>* parser,
+      SharedContext* sc, HandleScript script, Handle<LazyScript*> lazyScript,
+      TokenPos bodyPosition, EmitterMode emitterMode = Normal,
+      FieldInitializers fieldInitializers = FieldInitializers::Invalid())
       : BytecodeEmitter(parent, EitherParser(parser), sc, script, lazyScript,
-                        bodyPosition, emitterMode) {}
+                        bodyPosition, emitterMode, fieldInitializers) {}
 
   MOZ_MUST_USE bool init();
 
   template <typename T>
   T* findInnermostNestableControl() const;
 
   template <typename T, typename Predicate /* (T*) -> bool */>
   T* findInnermostNestableControl(Predicate predicate) const;
@@ -589,31 +598,34 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
 
   MOZ_MUST_USE bool emitInternedScopeOp(uint32_t index, JSOp op);
   MOZ_MUST_USE bool emitInternedObjectOp(uint32_t index, JSOp op);
   MOZ_MUST_USE bool emitObjectOp(ObjectBox* objbox, JSOp op);
   MOZ_MUST_USE bool emitObjectPairOp(ObjectBox* objbox1, ObjectBox* objbox2,
                                      JSOp op);
   MOZ_MUST_USE bool emitRegExp(uint32_t index);
 
-  MOZ_NEVER_INLINE MOZ_MUST_USE bool emitFunction(FunctionNode* funNode,
-                                                  bool needsProto = false);
+  MOZ_NEVER_INLINE MOZ_MUST_USE bool emitFunction(
+      FunctionNode* funNode, bool needsProto = false,
+      ListNode* classContentsIfConstructor = nullptr);
   MOZ_NEVER_INLINE MOZ_MUST_USE bool emitObject(ListNode* objNode);
 
   MOZ_MUST_USE bool replaceNewInitWithNewObject(JSObject* obj,
                                                 ptrdiff_t offset);
 
   MOZ_MUST_USE bool emitHoistedFunctionsInList(ListNode* stmtList);
 
   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();
 
   // 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/FunctionEmitter.cpp
+++ b/js/src/frontend/FunctionEmitter.cpp
@@ -471,18 +471,19 @@ bool FunctionScriptEmitter::prepareForBo
 
   if (funbox_->needsPromiseResult()) {
     if (!emitAsyncFunctionRejectPrologue()) {
       return false;
     }
   }
 
   if (funbox_->function()->kind() ==
-      JSFunction::FunctionKind::ClassConstructor) {
-    if (!emitInitializeInstanceFields()) {
+          JSFunction::FunctionKind::ClassConstructor &&
+      !funbox_->isDerivedClassConstructor()) {
+    if (!bce_->emitInitializeInstanceFields()) {
       //            [stack]
       return false;
     }
   }
 
 #ifdef DEBUG
   state_ = State::Body;
 #endif
@@ -593,73 +594,16 @@ bool FunctionScriptEmitter::emitExtraBod
       //            [stack]
       return false;
     }
   }
 
   return true;
 }
 
-bool FunctionScriptEmitter::emitInitializeInstanceFields() {
-  MOZ_ASSERT(bce_->fieldInitializers_.valid);
-  size_t numFields = bce_->fieldInitializers_.numFieldInitializers;
-
-  if (numFields == 0) {
-    return true;
-  }
-
-  if (!bce_->emitGetName(bce_->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 (!bce_->emit1(JSOP_DUP)) {
-        //          [stack] ARRAY ARRAY
-        return false;
-      }
-    }
-
-    if (!bce_->emitNumberOp(fieldIndex)) {
-      //            [stack] ARRAY? ARRAY INDEX
-      return false;
-    }
-
-    // Don't use CALLELEM here, because the receiver of the call != the receiver
-    // of this getelem. (Specifically, the call receiver is `this`, and the
-    // receiver of this getelem is `.initializers`)
-    if (!bce_->emit1(JSOP_GETELEM)) {
-      //            [stack] ARRAY? FUNC
-      return false;
-    }
-
-    // This is guaranteed to run after super(), so we don't need TDZ checks.
-    if (!bce_->emitGetName(bce_->cx->names().dotThis)) {
-      //            [stack] ARRAY? FUNC THIS
-      return false;
-    }
-
-    if (!bce_->emitCall(JSOP_CALL_IGNORES_RV, 0)) {
-      //            [stack] ARRAY? RVAL
-      return false;
-    }
-
-    if (!bce_->emit1(JSOP_POP)) {
-      //            [stack] ARRAY?
-      return false;
-    }
-  }
-
-  return true;
-}
-
 bool FunctionScriptEmitter::emitEndBody() {
   MOZ_ASSERT(state_ == State::Body);
 
   //                [stack]
 
   if (funbox_->needsFinalYield()) {
     // If we fall off the end of a generator, do a final yield.
     bool needsIteratorResult = funbox_->needsIteratorResult();
--- a/js/src/frontend/FunctionEmitter.h
+++ b/js/src/frontend/FunctionEmitter.h
@@ -258,17 +258,16 @@ class MOZ_STACK_CLASS FunctionScriptEmit
   // WARNING: There shouldn't be any fallible operation for the function
   //          compilation after `initScript` call.
   //          See the comment inside JSScript::fullyInitFromEmitter for
   //          more details.
   MOZ_MUST_USE bool initScript();
 
  private:
   MOZ_MUST_USE bool emitExtraBodyVarScope();
-  MOZ_MUST_USE bool emitInitializeInstanceFields();
 
   // Async functions have implicit try-catch blocks to convert exceptions
   // into promise rejections.
   MOZ_MUST_USE bool emitAsyncFunctionRejectPrologue();
   MOZ_MUST_USE bool emitAsyncFunctionRejectEpilogue();
 };
 
 // Class for emitting function parameters.
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -6886,25 +6886,25 @@ bool GeneralParser<ParseHandler, Unit>::
   AccessorType atype = ToAccessorType(propType);
   return handler_.addClassMethodDefinition(classMembers, propName, funNode,
                                            atype, isStatic);
 }
 
 template <class ParseHandler, typename Unit>
 bool GeneralParser<ParseHandler, Unit>::finishClassConstructor(
     const ParseContext::ClassStatement& classStmt, HandlePropertyName className,
-    uint32_t classStartOffset, uint32_t classEndOffset, size_t numFields,
-    ListNodeType& classMembers) {
+    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) {
     // synthesizeConstructor assigns to classStmt.constructorBox
     FunctionNodeType synthesizedCtor =
-        synthesizeConstructor(className, classStartOffset);
+        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"
@@ -6933,22 +6933,16 @@ bool GeneralParser<ParseHandler, Unit>::
 
     // Set the same information, but on the lazyScript.
     if (ctorbox->function()->isInterpretedLazy()) {
       ctorbox->function()->lazyScript()->setToStringEnd(classEndOffset);
 
       if (numFields > 0) {
         ctorbox->function()->lazyScript()->setHasThisBinding();
       }
-
-      // Field initializers can be retrieved if the class and constructor are
-      // being compiled at the same time, but we need to stash the field
-      // information if the constructor is being compiled lazily.
-      FieldInitializers fieldInfo(numFields);
-      ctorbox->function()->lazyScript()->setFieldInitializers(fieldInfo);
     }
   }
 
   return true;
 }
 
 template <class ParseHandler, typename Unit>
 typename ParseHandler::ClassNodeType
@@ -7081,18 +7075,19 @@ GeneralParser<ParseHandler, Unit>::class
     if (numFieldKeys > 0) {
       if (!noteDeclaredName(cx_->names().dotFieldKeys, DeclarationKind::Var,
                             namePos)) {
         return null();
       }
     }
 
     classEndOffset = pos().end;
-    if (!finishClassConstructor(classStmt, className, classStartOffset,
-                                classEndOffset, numFields, classMembers)) {
+    if (!finishClassConstructor(classStmt, className, hasHeritage,
+                                classStartOffset, classEndOffset, numFields,
+                                classMembers)) {
       return null();
     }
 
     if (className) {
       // The inner name is immutable.
       if (!noteDeclaredName(className, DeclarationKind::Const, namePos)) {
         return null();
       }
@@ -7135,18 +7130,21 @@ GeneralParser<ParseHandler, Unit>::class
 
   return handler_.newClass(nameNode, classHeritage, classBlock,
                            TokenPos(classStartOffset, classEndOffset));
 }
 
 template <class ParseHandler, typename Unit>
 typename ParseHandler::FunctionNodeType
 GeneralParser<ParseHandler, Unit>::synthesizeConstructor(
-    HandleAtom className, uint32_t classNameOffset) {
-  FunctionSyntaxKind functionSyntaxKind = FunctionSyntaxKind::ClassConstructor;
+    HandleAtom className, uint32_t classNameOffset, HasHeritage hasHeritage) {
+  FunctionSyntaxKind functionSyntaxKind =
+      hasHeritage == HasHeritage::Yes
+          ? FunctionSyntaxKind::DerivedClassConstructor
+          : FunctionSyntaxKind::ClassConstructor;
 
   // Create the function object.
   RootedFunction fun(cx_, newFunction(className, functionSyntaxKind,
                                       GeneratorKind::NotGenerator,
                                       FunctionAsyncKind::SyncFunction));
   if (!fun) {
     return null();
   }
@@ -7204,16 +7202,52 @@ GeneralParser<ParseHandler, Unit>::synth
   // One might expect a noteUsedName(".initializers") here. See comment in
   // GeneralParser<ParseHandler, Unit>::classDefinition on why it's not here.
 
   bool canSkipLazyClosedOverBindings = handler_.canSkipLazyClosedOverBindings();
   if (!pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings)) {
     return null();
   }
 
+  if (hasHeritage == HasHeritage::Yes) {
+    NameNodeType thisName = newThisName();
+    if (!thisName) {
+      return null();
+    }
+
+    UnaryNodeType superBase =
+        handler_.newSuperBase(thisName, synthesizedBodyPos);
+    if (!superBase) {
+      return null();
+    }
+
+    ListNodeType arguments = handler_.newArguments(synthesizedBodyPos);
+    if (!arguments) {
+      return null();
+    }
+
+    CallNodeType superCall = handler_.newSuperCall(superBase, arguments, false);
+    if (!superCall) {
+      return null();
+    }
+
+    BinaryNodeType setThis = handler_.newSetThis(thisName, superCall);
+    if (!setThis) {
+      return null();
+    }
+
+    UnaryNodeType exprStatement =
+        handler_.newExprStatement(setThis, synthesizedBodyPos.end);
+    if (!exprStatement) {
+      return null();
+    }
+
+    handler_.addStatementToList(stmtList, exprStatement);
+  }
+
   auto initializerBody = finishLexicalScope(lexicalScope, stmtList);
   if (!initializerBody) {
     return null();
   }
   handler_.setBeginPosition(initializerBody, stmtList);
   handler_.setEndPosition(initializerBody, stmtList);
 
   handler_.setFunctionBody(funNode, initializerBody);
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -1308,25 +1308,26 @@ class MOZ_STACK_CLASS GeneralParser : pu
   MOZ_MUST_USE bool classMember(
       YieldHandling yieldHandling, DefaultHandling defaultHandling,
       const ParseContext::ClassStatement& classStmt,
       HandlePropertyName className, uint32_t classStartOffset,
       HasHeritage hasHeritage, size_t& numFieldsWithInitializers,
       size_t& numFieldKeys, ListNodeType& classMembers, bool* done);
   MOZ_MUST_USE bool finishClassConstructor(
       const ParseContext::ClassStatement& classStmt,
-      HandlePropertyName className, uint32_t classStartOffset,
-      uint32_t classEndOffset, size_t numFieldsWithInitializers,
-      ListNodeType& classMembers);
+      HandlePropertyName className, HasHeritage hasHeritage,
+      uint32_t classStartOffset, uint32_t classEndOffset,
+      size_t numFieldsWithInitializers, ListNodeType& classMembers);
 
   FunctionNodeType fieldInitializerOpt(YieldHandling yieldHandling,
                                        HasHeritage hasHeritage, Node name,
                                        HandleAtom atom, size_t& numFieldKeys);
   FunctionNodeType synthesizeConstructor(HandleAtom className,
-                                         uint32_t classNameOffset);
+                                         uint32_t classNameOffset,
+                                         HasHeritage hasHeritage);
 
   bool checkBindingIdentifier(PropertyName* ident, uint32_t offset,
                               YieldHandling yieldHandling,
                               TokenKind hint = TokenKind::Limit);
 
   PropertyName* labelOrIdentifierReference(YieldHandling yieldHandling);
 
   PropertyName* labelIdentifier(YieldHandling yieldHandling) {
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/fields/super.js
@@ -0,0 +1,47 @@
+// |jit-test| --enable-experimental-fields
+
+class Base {
+}
+
+class C extends Base {
+  field;
+}
+new C();
+
+var D = class extends Base {
+  field;
+};
+new D();
+
+class E extends Base {
+  field;
+  constructor() {
+    super();
+  }
+};
+new E();
+
+class F extends Base {
+  constructor() {
+    super();
+  }
+  field;
+};
+new F();
+
+class G extends Base {
+  field2 = 2;
+  constructor() {
+    super();
+  }
+  field3 = 3;
+};
+new G();
+
+class H extends Base {
+  field = 2;
+  constructor() {
+    eval("super()");
+  }
+};
+new H();
--- a/js/src/tests/jstests.list
+++ b/js/src/tests/jstests.list
@@ -646,20 +646,16 @@ skip script test262/language/statements/
 skip script test262/language/statements/class/elements/regular-definitions-string-literal-names.js
 skip script test262/language/statements/class/elements/same-line-async-gen-literal-names-asi.js
 skip script test262/language/statements/class/elements/same-line-async-method-literal-names-asi.js
 skip script test262/language/statements/class/elements/same-line-gen-literal-names-asi.js
 skip script test262/language/statements/class/elements/same-line-method-literal-names-asi.js
 skip script test262/language/statements/class/elements/syntax/valid/grammar-fields-multi-line.js
 skip script test262/language/statements/class/elements/wrapped-in-sc-literal-names-asi.js
 
-# https://bugzilla.mozilla.org/show_bug.cgi?id=1534721
-skip script test262/language/expressions/class/elements/fields-run-once-on-double-super.js
-skip script test262/language/expressions/class/constructor-this-tdz-during-initializers.js
-
 # https://bugzilla.mozilla.org/show_bug.cgi?id=1542406
 skip script test262/language/expressions/class/elements/derived-cls-direct-eval-err-contains-supercall-1.js
 skip script test262/language/expressions/class/elements/derived-cls-direct-eval-err-contains-supercall-2.js
 skip script test262/language/expressions/class/elements/derived-cls-direct-eval-err-contains-supercall.js
 skip script test262/language/expressions/class/elements/derived-cls-direct-eval-err-contains-superproperty-1.js
 skip script test262/language/expressions/class/elements/derived-cls-direct-eval-err-contains-superproperty-2.js
 skip script test262/language/expressions/class/elements/derived-cls-indirect-eval-err-contains-supercall-1.js
 skip script test262/language/expressions/class/elements/derived-cls-indirect-eval-err-contains-supercall-2.js
--- a/js/src/vm/JSScript.h
+++ b/js/src/vm/JSScript.h
@@ -1242,16 +1242,44 @@ class ScriptSourceObject : public Native
     PRIVATE_SLOT,
     RESERVED_SLOTS
   };
 };
 
 enum class GeneratorKind : bool { NotGenerator, Generator };
 enum class FunctionAsyncKind : bool { SyncFunction, AsyncFunction };
 
+struct FieldInitializers {
+#ifdef DEBUG
+  bool valid;
+#endif
+  // This struct will eventually have a vector of constant values for optimizing
+  // field initializers.
+  size_t numFieldInitializers;
+
+  explicit FieldInitializers(size_t numFieldInitializers)
+      :
+#ifdef DEBUG
+        valid(true),
+#endif
+        numFieldInitializers(numFieldInitializers) {
+  }
+
+  static FieldInitializers Invalid() { return FieldInitializers(); }
+
+ private:
+  FieldInitializers()
+      :
+#ifdef DEBUG
+        valid(false),
+#endif
+        numFieldInitializers(0) {
+  }
+};
+
 /*
  * NB: after a successful XDR_DECODE, XDRScript callers must do any required
  * subsequent set-up of owning function or script object and then call
  * CallNewScriptHook.
  */
 template <XDRMode mode>
 XDRResult XDRScript(XDRState<mode>* xdr, HandleScope enclosingScope,
                     HandleScriptSourceObject sourceObject, HandleFunction fun,
@@ -1355,16 +1383,18 @@ class alignas(JS::Value) PrivateScriptDa
     uint32_t offset;
     uint32_t length;
   };
 
   // Concrete Fields
   PackedOffsets packedOffsets = {};  // zeroes
   uint32_t nscopes = 0;
 
+  js::FieldInitializers fieldInitializers_ = js::FieldInitializers::Invalid();
+
   // Translate an offset into a concrete pointer.
   template <typename T>
   T* offsetToPointer(size_t offset) {
     uintptr_t base = reinterpret_cast<uintptr_t>(this);
     uintptr_t elem = base + offset;
     return reinterpret_cast<T*>(elem);
   }
 
@@ -1426,16 +1456,20 @@ class alignas(JS::Value) PrivateScriptDa
   // Fast tests for if array exists
   bool hasConsts() const { return packedOffsets.constsSpanOffset != 0; }
   bool hasObjects() const { return packedOffsets.objectsSpanOffset != 0; }
   bool hasTryNotes() const { return packedOffsets.tryNotesSpanOffset != 0; }
   bool hasScopeNotes() const { return packedOffsets.scopeNotesSpanOffset != 0; }
   bool hasResumeOffsets() const {
     return packedOffsets.resumeOffsetsSpanOffset != 0;
   }
+  void setFieldInitializers(FieldInitializers fieldInitializers) {
+    fieldInitializers_ = fieldInitializers;
+  }
+  const FieldInitializers& getFieldInitializers() { return fieldInitializers_; }
 
   // Allocate a new PrivateScriptData. Headers and GCPtrs are initialized.
   // The size of allocation is returned as an out parameter.
   static PrivateScriptData* new_(JSContext* cx, uint32_t nscopes,
                                  uint32_t nconsts, uint32_t nobjects,
                                  uint32_t ntrynotes, uint32_t nscopenotes,
                                  uint32_t nresumeoffsets, uint32_t* dataSize);
 
@@ -2278,16 +2312,26 @@ class JSScript : public js::gc::TenuredC
   bool hasMappedArgsObj() const {
     return hasFlag(ImmutableFlags::HasMappedArgsObj);
   }
 
   bool functionHasThisBinding() const {
     return hasFlag(ImmutableFlags::FunctionHasThisBinding);
   }
 
+  void setFieldInitializers(js::FieldInitializers fieldInitializers) {
+    MOZ_ASSERT(data_);
+    data_->setFieldInitializers(fieldInitializers);
+  }
+
+  const js::FieldInitializers& getFieldInitializers() const {
+    MOZ_ASSERT(data_);
+    return data_->getFieldInitializers();
+  }
+
   /*
    * Arguments access (via JSOP_*ARG* opcodes) must access the canonical
    * location for the argument. If an arguments object exists AND it's mapped
    * ('arguments' aliases formals), then all access must go through the
    * arguments object. Otherwise, the local slot is the canonical location for
    * the arguments. Note: if a formal is aliased through the scope chain, then
    * script->formalIsAliased and JSOP_*ARG* opcodes won't be emitted at all.
    */
@@ -2853,44 +2897,16 @@ class JSScript : public js::gc::TenuredC
 
 /* If this fails, add/remove padding within JSScript. */
 static_assert(
     sizeof(JSScript) % js::gc::CellAlignBytes == 0,
     "Size of JSScript must be an integral multiple of js::gc::CellAlignBytes");
 
 namespace js {
 
-struct FieldInitializers {
-#ifdef DEBUG
-  bool valid;
-#endif
-  // This struct will eventually have a vector of constant values for optimizing
-  // field initializers.
-  size_t numFieldInitializers;
-
-  explicit FieldInitializers(size_t numFieldInitializers)
-      :
-#ifdef DEBUG
-        valid(true),
-#endif
-        numFieldInitializers(numFieldInitializers) {
-  }
-
-  static FieldInitializers Invalid() { return FieldInitializers(); }
-
- private:
-  FieldInitializers()
-      :
-#ifdef DEBUG
-        valid(false),
-#endif
-        numFieldInitializers(0) {
-  }
-};
-
 // Variable-length data for LazyScripts. Contains vector of inner functions and
 // vector of captured property ids.
 class alignas(uintptr_t) LazyScriptData final {
  private:
   uint32_t numClosedOverBindings_ = 0;
   uint32_t numInnerFunctions_ = 0;
 
   FieldInitializers fieldInitializers_ = FieldInitializers::Invalid();
@@ -3265,19 +3281,19 @@ class LazyScript : public gc::TenuredCel
   }
   void setHasThisBinding() { setFlag(ImmutableFlags::FunctionHasThisBinding); }
 
   void setFieldInitializers(FieldInitializers fieldInitializers) {
     MOZ_ASSERT(lazyData_);
     lazyData_->fieldInitializers_ = fieldInitializers;
   }
 
-  FieldInitializers getFieldInitializers() const {
-    return lazyData_ ? lazyData_->fieldInitializers_
-                     : FieldInitializers::Invalid();
+  const FieldInitializers& getFieldInitializers() const {
+    MOZ_ASSERT(lazyData_);
+    return lazyData_->fieldInitializers_;
   }
 
   const char* filename() const { return scriptSource()->filename(); }
   uint32_t sourceStart() const { return sourceStart_; }
   uint32_t sourceEnd() const { return sourceEnd_; }
   uint32_t sourceLength() const { return sourceEnd_ - sourceStart_; }
   uint32_t toStringStart() const { return toStringStart_; }
   uint32_t toStringEnd() const { return toStringEnd_; }