Bug 1535804 - Part 6: Add 'isStatic' code paths to various functions. r=arai! draft
authorAndré Bargull <andre.bargull@gmail.com>
Mon, 18 Nov 2019 00:23:53 -0800
changeset 2648926 e95a6c42be0dfcc9f04eb1531210bbf22986ed38
parent 2648925 949815967ded2f183f9f5eb97a6381b009ad8524
child 2648927 8375b2ca043bd5e8f76b258b1f518c0350531020
push id490869
push userandre.bargull@gmail.com
push dateSat, 22 Feb 2020 09:32:51 +0000
treeherdertry@ca279eef8570 [default view] [failures only]
reviewersarai
bugs1535804
milestone75.0a1
Bug 1535804 - Part 6: Add 'isStatic' code paths to various functions. r=arai! Parser.{h,cpp}: - Add a new `ClassFields` struct to count class fields to avoid passing another two `size_t` parameters pair to various functions. BytecodeEmitter.{h,cpp}: - Use a separate enum class instead of a plain `bool isStatic` for readability. ObjectEmitter.{h,cpp}: - Add a separate state for class field initialisation to avoid duplicating multiple states for instance vs. static class fields. - Also add an additional state to track when the initialiser expression was emitted to ensure `emitFieldInitializerHomeObject` won't be called out of order. Differential Revision: https://phabricator.services.mozilla.com/D53639
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/FullParseHandler.h
js/src/frontend/ObjectEmitter.cpp
js/src/frontend/ObjectEmitter.h
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/frontend/SyntaxParseHandler.h
js/src/vm/CommonPropertyNames.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -5505,17 +5505,18 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::e
     if (funbox->isInterpretedLazy()) {
       if (!fe.emitLazy()) {
         //          [stack] FUN?
         return false;
       }
 
       if (classContentsIfConstructor) {
         mozilla::Maybe<FieldInitializers> fieldInitializers =
-            setupFieldInitializers(classContentsIfConstructor);
+            setupFieldInitializers(classContentsIfConstructor,
+                                   FieldPlacement::Instance);
         if (!fieldInitializers) {
           ReportAllocationOverflow(cx);
           return false;
         }
         funbox->setFieldInitializers(*fieldInitializers);
       }
 
       return true;
@@ -5543,17 +5544,18 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::e
     EmitterMode nestedMode = emitterMode;
     if (nestedMode == BytecodeEmitter::LazyFunction) {
       MOZ_ASSERT(lazyScript->isBinAST());
       nestedMode = BytecodeEmitter::Normal;
     }
 
     mozilla::Maybe<FieldInitializers> fieldInitializers;
     if (classContentsIfConstructor) {
-      fieldInitializers = setupFieldInitializers(classContentsIfConstructor);
+      fieldInitializers = setupFieldInitializers(classContentsIfConstructor,
+                                                 FieldPlacement::Instance);
       if (!fieldInitializers) {
         ReportAllocationOverflow(cx);
         return false;
       }
     } else {
       // The BCE requires passing some value even if not used.
       fieldInitializers = Some(FieldInitializers::Invalid());
     }
@@ -8199,24 +8201,29 @@ bool BytecodeEmitter::isArrayObjLiteralC
   return true;
 }
 
 bool BytecodeEmitter::emitPropertyList(ListNode* obj, PropertyEmitter& pe,
                                        PropListType type, bool isInner) {
   //                [stack] CTOR? OBJ
 
   size_t curFieldKeyIndex = 0;
+  size_t curStaticFieldKeyIndex = 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)) {
+        auto fieldKeysName = field->isStatic()
+                                 ? &JSAtomState::dotStaticFieldKeys
+                                 : &JSAtomState::dotFieldKeys;
+        HandlePropertyName fieldKeys = cx->names().*fieldKeysName;
+        if (!emitGetName(fieldKeys)) {
           //        [stack] CTOR? OBJ ARRAY
           return false;
         }
 
         ParseNode* nameExpr = field->name().as<UnaryNode>().kid();
 
         if (!emitTree(nameExpr, ValueUsage::WantValue, EMIT_LINENOTE,
                       /* isInner = */ isInner)) {
@@ -8224,27 +8231,32 @@ bool BytecodeEmitter::emitPropertyList(L
           return false;
         }
 
         if (!emit1(JSOp::ToId)) {
           //        [stack] CTOR? OBJ ARRAY KEY
           return false;
         }
 
-        if (!emitUint32Operand(JSOp::InitElemArray, curFieldKeyIndex)) {
+        size_t fieldKeysIndex;
+        if (field->isStatic()) {
+          fieldKeysIndex = curStaticFieldKeyIndex++;
+        } else {
+          fieldKeysIndex = curFieldKeyIndex++;
+        }
+
+        if (!emitUint32Operand(JSOp::InitElemArray, fieldKeysIndex)) {
           //        [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(
@@ -8670,19 +8682,21 @@ bool BytecodeEmitter::emitObjLiteralValu
     }
   } else {
     MOZ_CRASH("Unexpected parse node");
   }
   return true;
 }
 
 mozilla::Maybe<FieldInitializers> BytecodeEmitter::setupFieldInitializers(
-    ListNode* classMembers) {
-  auto isClassField = [](ParseNode* propdef) {
-    return propdef->is<ClassField>();
+    ListNode* classMembers, FieldPlacement placement) {
+  bool isStatic = placement == FieldPlacement::Static;
+  auto isClassField = [isStatic](ParseNode* propdef) {
+    return propdef->is<ClassField>() &&
+           propdef->as<ClassField>().isStatic() == isStatic;
   };
 
   size_t numFields =
       std::count_if(classMembers->contents().begin(),
                     classMembers->contents().end(), isClassField);
   // If there are more initializers than can be represented, return invalid.
   if (numFields > FieldInitializers::MaxInitializers) {
     return Nothing();
@@ -8709,31 +8723,36 @@ mozilla::Maybe<FieldInitializers> Byteco
 //   constructor() {
 //     .initializers[0]();
 //   }
 // }
 //
 // BytecodeEmitter::emitCreateFieldKeys does `let .fieldKeys = [...];`
 // BytecodeEmitter::emitPropertyList fills in the elements of the array.
 // See GeneralParser::fieldInitializer for the `this[.fieldKeys[0]]` part.
-bool BytecodeEmitter::emitCreateFieldKeys(ListNode* obj) {
-  auto isFieldWithComputedName = [](ParseNode* propdef) {
+bool BytecodeEmitter::emitCreateFieldKeys(ListNode* obj,
+                                          FieldPlacement placement) {
+  bool isStatic = placement == FieldPlacement::Static;
+  auto isFieldWithComputedName = [isStatic](ParseNode* propdef) {
     return propdef->is<ClassField>() &&
+           propdef->as<ClassField>().isStatic() == isStatic &&
            propdef->as<ClassField>().name().getKind() ==
                ParseNodeKind::ComputedName;
   };
 
   size_t numFieldKeys = std::count_if(
       obj->contents().begin(), obj->contents().end(), isFieldWithComputedName);
   if (numFieldKeys == 0) {
     return true;
   }
 
-  NameOpEmitter noe(this, cx->names().dotFieldKeys.toHandle(),
-                    NameOpEmitter::Kind::Initialize);
+  auto fieldKeysName =
+      isStatic ? &JSAtomState::dotStaticFieldKeys : &JSAtomState::dotFieldKeys;
+  HandlePropertyName fieldKeys = cx->names().*fieldKeysName;
+  NameOpEmitter noe(this, fieldKeys, NameOpEmitter::Kind::Initialize);
   if (!noe.prepareForRhs()) {
     return false;
   }
 
   if (!emitUint32Operand(JSOp::NewArray, numFieldKeys)) {
     //              [stack] ARRAY
     return false;
   }
@@ -8747,60 +8766,82 @@ bool BytecodeEmitter::emitCreateFieldKey
     //              [stack]
     return false;
   }
 
   return true;
 }
 
 bool BytecodeEmitter::emitCreateFieldInitializers(ClassEmitter& ce,
-                                                  ListNode* obj) {
-  //          [stack] HOMEOBJ HERITAGE?
+                                                  ListNode* obj,
+                                                  FieldPlacement placement) {
+  // FieldPlacement::Instance
+  //                [stack] HOMEOBJ HERITAGE?
+  //
+  // FieldPlacement::Static
+  //                [stack] CTOR HOMEOBJ
   mozilla::Maybe<FieldInitializers> fieldInitializers =
-      setupFieldInitializers(obj);
+      setupFieldInitializers(obj, placement);
   if (!fieldInitializers) {
     ReportAllocationOverflow(cx);
     return false;
   }
   size_t numFields = fieldInitializers->numFieldInitializers;
 
   if (numFields == 0) {
     return true;
   }
 
-  if (!ce.prepareForFieldInitializers(numFields)) {
-    //          [stack] HOMEOBJ HERITAGE? ARRAY
+  bool isStatic = placement == FieldPlacement::Static;
+  if (!ce.prepareForFieldInitializers(numFields, isStatic)) {
+    //              [stack] HOMEOBJ HERITAGE? ARRAY
+    // or:
+    //              [stack] CTOR HOMEOBJ ARRAY
     return false;
   }
 
   for (ParseNode* propdef : obj->contents()) {
     if (!propdef->is<ClassField>()) {
       continue;
     }
+    if (propdef->as<ClassField>().isStatic() != isStatic) {
+      continue;
+    }
 
     FunctionNode* initializer = propdef->as<ClassField>().initializer();
+    if (!ce.prepareForFieldInitializer()) {
+      return false;
+    }
     if (!emitTree(initializer)) {
       //            [stack] HOMEOBJ HERITAGE? ARRAY LAMBDA
+      // or:
+      //            [stack] CTOR HOMEOBJ ARRAY LAMBDA
       return false;
     }
     if (initializer->funbox()->needsHomeObject()) {
       MOZ_ASSERT(initializer->funbox()->allowSuperProperty());
-      if (!ce.emitFieldInitializerHomeObject()) {
+      if (!ce.emitFieldInitializerHomeObject(isStatic)) {
         //          [stack] HOMEOBJ HERITAGE? ARRAY LAMBDA
+        // or:
+        //          [stack] CTOR HOMEOBJ ARRAY LAMBDA
         return false;
       }
     }
     if (!ce.emitStoreFieldInitializer()) {
       //            [stack] HOMEOBJ HERITAGE? ARRAY
+      // or:
+      //            [stack] CTOR HOMEOBJ ARRAY
       return false;
     }
   }
 
   if (!ce.emitFieldInitializersEnd()) {
     //              [stack] HOMEOBJ HERITAGE?
+    // or:
+    //              [stack] CTOR HOMEOBJ
     return false;
   }
 
   return true;
 }
 
 const FieldInitializers& BytecodeEmitter::findFieldInitializersForCall() {
   for (BytecodeEmitter* current = this; current; current = current->parent) {
@@ -9538,33 +9579,35 @@ bool BytecodeEmitter::emitClass(
 
       // The constructor scope should only contain the |.initializers| binding.
       MOZ_ASSERT(!constructorScope->isEmptyScope());
       MOZ_ASSERT(constructorScope->scopeBindings()->length == 1);
       MOZ_ASSERT(constructorScope->scopeBindings()->trailingNames[0].name() ==
                  cx->names().dotInitializers);
 
       auto isInstanceField = [](ParseNode* propdef) {
-        return propdef->is<ClassField>();
+        return propdef->is<ClassField>() &&
+               !propdef->as<ClassField>().isStatic();
       };
 
       // As an optimization omit the |.initializers| binding when no instance
       // fields are present.
       bool hasInstanceFields =
           std::any_of(classMembers->contents().begin(),
                       classMembers->contents().end(), isInstanceField);
       if (hasInstanceFields) {
         lse.emplace(this);
         if (!lse->emitScope(ScopeKind::Lexical,
                             constructorScope->scopeBindings())) {
           return false;
         }
 
         // Any class with field initializers will have a constructor
-        if (!emitCreateFieldInitializers(ce, classMembers)) {
+        if (!emitCreateFieldInitializers(ce, classMembers,
+                                         FieldPlacement::Instance)) {
           return false;
         }
       }
 
       ctor = &constructorScope->scopeBody()->as<ClassMethod>().method();
     } else {
       // The |.initializers| binding is never emitted when in self-hosting mode.
       MOZ_ASSERT(emitterMode == BytecodeEmitter::SelfHosting);
@@ -9595,17 +9638,17 @@ bool BytecodeEmitter::emitClass(
   } else {
     if (!ce.emitInitDefaultConstructor(classNode->pn_pos.begin,
                                        classNode->pn_pos.end)) {
       //            [stack] CTOR HOMEOBJ
       return false;
     }
   }
 
-  if (!emitCreateFieldKeys(classMembers)) {
+  if (!emitCreateFieldKeys(classMembers, FieldPlacement::Instance)) {
     return false;
   }
 
   if (!emitPropertyList(classMembers, ce, ClassBody)) {
     //              [stack] CTOR HOMEOBJ
     return false;
   }
 
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -512,21 +512,23 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
   MOZ_MUST_USE bool emitObjLiteralArray(ParseNode* arrayHead, bool isCow);
 
   // Is a field value OBJLITERAL-compatible?
   MOZ_MUST_USE bool isRHSObjLiteralCompatible(ParseNode* value);
 
   MOZ_MUST_USE bool emitObjLiteralValue(ObjLiteralCreationData* data,
                                         ParseNode* value);
 
+  enum class FieldPlacement { Instance, Static };
   mozilla::Maybe<FieldInitializers> setupFieldInitializers(
-      ListNode* classMembers);
-  MOZ_MUST_USE bool emitCreateFieldKeys(ListNode* obj);
-  MOZ_MUST_USE bool emitCreateFieldInitializers(ClassEmitter& ce,
-                                                ListNode* obj);
+      ListNode* classMembers, FieldPlacement placement);
+  MOZ_MUST_USE bool emitCreateFieldKeys(ListNode* obj,
+                                        FieldPlacement placement);
+  MOZ_MUST_USE bool emitCreateFieldInitializers(ClassEmitter& ce, ListNode* obj,
+                                                FieldPlacement placement);
   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.
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -492,21 +492,22 @@ class FullParseHandler {
                                                      bool isStatic) {
     MOZ_ASSERT(isUsableAsObjectPropertyName(key));
 
     checkAndSetIsDirectRHSAnonFunction(funNode);
 
     return new_<ClassMethod>(key, funNode, atype, isStatic);
   }
 
-  MOZ_MUST_USE ClassField* newClassFieldDefinition(
-      Node name, FunctionNodeType initializer) {
+  MOZ_MUST_USE ClassField* newClassFieldDefinition(Node name,
+                                                   FunctionNodeType initializer,
+                                                   bool isStatic) {
     MOZ_ASSERT(isUsableAsObjectPropertyName(name));
 
-    return new_<ClassField>(name, initializer);
+    return new_<ClassField>(name, initializer, isStatic);
   }
 
   MOZ_MUST_USE bool addClassMemberDefinition(ListNodeType memberList,
                                              Node member) {
     MOZ_ASSERT(memberList->isKind(ParseNodeKind::ClassMemberList));
     // Constructors can be surrounded by LexicalScopes.
     MOZ_ASSERT(member->isKind(ParseNodeKind::ClassMethod) ||
                member->isKind(ParseNodeKind::ClassField) ||
--- a/js/src/frontend/ObjectEmitter.cpp
+++ b/js/src/frontend/ObjectEmitter.cpp
@@ -574,17 +574,17 @@ bool ClassEmitter::emitDerivedClass(JS::
   classState_ = ClassState::Class;
 #endif
   return true;
 }
 
 bool ClassEmitter::emitInitConstructor(bool needsHomeObject) {
   MOZ_ASSERT(propertyState_ == PropertyState::Start);
   MOZ_ASSERT(classState_ == ClassState::Class ||
-             classState_ == ClassState::FieldInitializersEnd);
+             classState_ == ClassState::InstanceFieldInitializersEnd);
 
   //                [stack] HOMEOBJ CTOR
 
   if (needsHomeObject) {
     if (!bce_->emitDupAt(1)) {
       //            [stack] HOMEOBJ CTOR HOMEOBJ
       return false;
     }
@@ -603,18 +603,17 @@ bool ClassEmitter::emitInitConstructor(b
   classState_ = ClassState::InitConstructor;
 #endif
   return true;
 }
 
 bool ClassEmitter::emitInitDefaultConstructor(uint32_t classStart,
                                               uint32_t classEnd) {
   MOZ_ASSERT(propertyState_ == PropertyState::Start);
-  MOZ_ASSERT(classState_ == ClassState::Class ||
-             classState_ == ClassState::FieldInitializersEnd);
+  MOZ_ASSERT(classState_ == ClassState::Class);
 
   RootedAtom className(bce_->cx, name_);
   if (!className) {
     if (nameForAnonymousClass_) {
       className = nameForAnonymousClass_;
     } else {
       className = bce_->cx->names().empty;
     }
@@ -686,108 +685,145 @@ bool ClassEmitter::initProtoAndCtor() {
   if (!bce_->emitAtomOp(JSOp::InitHiddenProp, bce_->cx->names().constructor)) {
     //              [stack] NAME? CTOR HOMEOBJ
     return false;
   }
 
   return true;
 }
 
-bool ClassEmitter::prepareForFieldInitializers(size_t numFields) {
-  MOZ_ASSERT(classState_ == ClassState::Class);
+bool ClassEmitter::prepareForFieldInitializers(size_t numFields,
+                                               bool isStatic) {
+  MOZ_ASSERT_IF(!isStatic, classState_ == ClassState::Class);
+  MOZ_ASSERT_IF(isStatic, classState_ == ClassState::InitConstructor);
+  MOZ_ASSERT(fieldState_ == FieldState::Start);
 
   // .initializers is a variable that stores an array of lambdas containing
   // code (the initializer) for each field. Upon an object's construction,
   // these lambdas will be called, defining the values.
-  initializersAssignment_.emplace(bce_,
-                                  bce_->cx->names().dotInitializers.toHandle(),
+  auto initializersName = isStatic ? &JSAtomState::dotStaticInitializers
+                                   : &JSAtomState::dotInitializers;
+  HandlePropertyName initializers = bce_->cx->names().*initializersName;
+  initializersAssignment_.emplace(bce_, initializers,
                                   NameOpEmitter::Kind::Initialize);
   if (!initializersAssignment_->prepareForRhs()) {
     return false;
   }
 
   if (!bce_->emitUint32Operand(JSOp::NewArray, numFields)) {
-    //              [stack] HOMEOBJ HERITAGE? ARRAY
+    //              [stack] ARRAY
     return false;
   }
 
-  MOZ_ASSERT(fieldIndex_ == 0);
+  fieldIndex_ = 0;
 #ifdef DEBUG
-  classState_ = ClassState::FieldInitializers;
+  if (isStatic) {
+    classState_ = ClassState::StaticFieldInitializers;
+  } else {
+    classState_ = ClassState::InstanceFieldInitializers;
+  }
   numFields_ = numFields;
 #endif
   return true;
 }
 
-bool ClassEmitter::emitFieldInitializerHomeObject() {
-  MOZ_ASSERT(classState_ == ClassState::FieldInitializers);
-  //          [stack] OBJ HERITAGE? ARRAY METHOD
+bool ClassEmitter::prepareForFieldInitializer() {
+  MOZ_ASSERT(classState_ == ClassState::InstanceFieldInitializers ||
+             classState_ == ClassState::StaticFieldInitializers);
+  MOZ_ASSERT(fieldState_ == FieldState::Start);
+
+#ifdef DEBUG
+  fieldState_ = FieldState::Initializer;
+#endif
+  return true;
+}
 
-  if (!bce_->emitDupAt(isDerived_ ? 3 : 2)) {
-    //              [stack] OBJ HERITAGE? ARRAY METHOD OBJ
-    return false;
+bool ClassEmitter::emitFieldInitializerHomeObject(bool isStatic) {
+  MOZ_ASSERT(fieldState_ == FieldState::Initializer);
+  //                [stack] OBJ HERITAGE? ARRAY METHOD
+  // or:
+  //                [stack] CTOR HOMEOBJ ARRAY METHOD
+
+  if (isStatic) {
+    if (!bce_->emitDupAt(3)) {
+      //            [stack] CTOR HOMEOBJ ARRAY METHOD CTOR
+      return false;
+    }
+  } else {
+    if (!bce_->emitDupAt(isDerived_ ? 3 : 2)) {
+      //            [stack] OBJ HERITAGE? ARRAY METHOD OBJ
+      return false;
+    }
   }
   if (!bce_->emit1(JSOp::InitHomeObject)) {
     //              [stack] OBJ HERITAGE? ARRAY METHOD
+    // or:
+    //              [stack] CTOR HOMEOBJ ARRAY METHOD
     return false;
   }
 
 #ifdef DEBUG
-  classState_ = ClassState::FieldInitializerWithHomeObject;
+  fieldState_ = FieldState::InitializerWithHomeObject;
 #endif
   return true;
 }
 
 bool ClassEmitter::emitStoreFieldInitializer() {
-  MOZ_ASSERT(classState_ == ClassState::FieldInitializers ||
-             classState_ == ClassState::FieldInitializerWithHomeObject);
+  MOZ_ASSERT(fieldState_ == FieldState::Initializer ||
+             fieldState_ == FieldState::InitializerWithHomeObject);
   MOZ_ASSERT(fieldIndex_ < numFields_);
   //          [stack] HOMEOBJ HERITAGE? ARRAY METHOD
 
   if (!bce_->emitUint32Operand(JSOp::InitElemArray, fieldIndex_)) {
     //          [stack] HOMEOBJ HERITAGE? ARRAY
     return false;
   }
 
   fieldIndex_++;
 #ifdef DEBUG
-  classState_ = ClassState::FieldInitializers;
+  fieldState_ = FieldState::Start;
 #endif
   return true;
 }
 
 bool ClassEmitter::emitFieldInitializersEnd() {
   MOZ_ASSERT(propertyState_ == PropertyState::Start ||
              propertyState_ == PropertyState::Init);
-  MOZ_ASSERT(classState_ == ClassState::FieldInitializers ||
-             classState_ == ClassState::FieldInitializerWithHomeObject);
+  MOZ_ASSERT(classState_ == ClassState::InstanceFieldInitializers ||
+             classState_ == ClassState::StaticFieldInitializers);
+  MOZ_ASSERT(fieldState_ == FieldState::Start);
   MOZ_ASSERT(fieldIndex_ == numFields_);
 
   if (!initializersAssignment_->emitAssignment()) {
     //              [stack] HOMEOBJ HERITAGE? ARRAY
     return false;
   }
   initializersAssignment_.reset();
 
   if (!bce_->emit1(JSOp::Pop)) {
     //              [stack] HOMEOBJ HERITAGE?
     return false;
   }
 
 #ifdef DEBUG
-  classState_ = ClassState::FieldInitializersEnd;
+  if (classState_ == ClassState::InstanceFieldInitializers) {
+    classState_ = ClassState::InstanceFieldInitializersEnd;
+  } else {
+    classState_ = ClassState::StaticFieldInitializersEnd;
+  }
 #endif
   return true;
 }
 
 bool ClassEmitter::emitEnd(Kind kind) {
   MOZ_ASSERT(propertyState_ == PropertyState::Start ||
              propertyState_ == PropertyState::Init);
   MOZ_ASSERT(classState_ == ClassState::InitConstructor ||
-             classState_ == ClassState::FieldInitializersEnd);
+             classState_ == ClassState::InstanceFieldInitializersEnd ||
+             classState_ == ClassState::StaticFieldInitializersEnd);
   //                [stack] CTOR HOMEOBJ
 
   if (!bce_->emit1(JSOp::Pop)) {
     //              [stack] CTOR
     return false;
   }
 
   if (name_) {
--- a/js/src/frontend/ObjectEmitter.h
+++ b/js/src/frontend/ObjectEmitter.h
@@ -637,81 +637,123 @@ class MOZ_STACK_CLASS ClassEmitter : pub
   //   |   emitClass           +-------+
   //   +-+----------------->+->| Class |-+
   //     |                  ^  +-------+ |
   //     | emitDerivedClass |            |
   //     +------------------+            |
   //                                     |
   //     +-------------------------------+
   //     |
-  //     | prepareForFieldInitializers
-  //     +-----------------------------+
-  //     |                             |
-  //     |                             |    +-------------------+
-  //     |      +--------------------->+--->| FieldInitializers |-+
-  //     |      |                           +-------------------+ |
-  //     |      |                                                 |
-  //     |      |      (emit initializer method)                  |
-  //     |      |   +<--------------------------------------------+
-  //     |      |   |
-  //     |      |   | emitFieldInitializerHomeObject  +--------------------------------+
-  //     |      |   +-------------------------------->| FieldInitializerWithHomeObject |-+
-  //     |      |   |                                 +--------------------------------+ |
-  //     |      |   |                                                                    |
-  //     |      |   +------------------------------------------------------------------->+
-  //     |      |                                                                        |
-  //     |      |     emitStoreFieldInitializer                                          |
-  //     |  +<--+<-----------------------------------------------------------------------+
-  //     |  |
-  //     |  | emitFieldInitializersEnd  +----------------------+
-  //     |  +-------------------------->| FieldInitializersEnd |-+
-  //     |                              +----------------------+ |
-  //     |                                                       |
-  //     |<------------------------------------------------------+
   //     |
+  //     |  prepareForFieldInitializers(isStatic = false)
+  //     +---------------+
+  //     |               |
+  //     |      +--------v------------------+
+  //     |      | InstanceFieldInitializers |
+  //     |      +---------------------------+
+  //     |               |
+  //                     | emitFieldInitializersEnd
+  //     |               |
+  //     |      +--------v---------------------+
+  //     |      | InstanceFieldInitializersEnd |
+  //     |      +------------------------------+
+  //     |               |
+  //     +<--------------+
   //     |
   //     |   emitInitConstructor           +-----------------+
   //     +-+--------------------------->+->| InitConstructor |-+
   //       |                            ^  +-----------------+ |
   //       | emitInitDefaultConstructor |                      |
   //       +----------------------------+                      |
   //                                                           |
-  //       +---------------------------------------------------+
-  //       |
-  //       | (do PropertyEmitter operation)  emitEnd  +-----+
-  //       +-------------------------------+--------->| End |
-  //                                                  +-----+
+  //     +-----------------------------------------------------+
+  //     |
+  //     |  prepareForFieldInitializers(isStatic = true)
+  //     +---------------+
+  //     |               |
+  //     |      +--------v----------------+
+  //     |      | StaticFieldInitializers |
+  //     |      +-------------------------+
+  //     |               |
+  //     |               | emitFieldInitializersEnd
+  //     |               |
+  //     |      +--------v-------------------+
+  //     |      | StaticFieldInitializersEnd |
+  //     |      +----------------------------+
+  //     |               |
+  //     +<--------------+
+  //     |
+  //     | (do PropertyEmitter operation)  emitEnd  +-----+
+  //     +-------------------------------+--------->| End |
+  //                                                +-----+
   // clang-format on
   enum class ClassState {
     // The initial state.
     Start,
 
     // After calling emitScope.
     Scope,
 
     // After calling emitClass or emitDerivedClass.
     Class,
 
     // After calling emitInitConstructor or emitInitDefaultConstructor.
     InitConstructor,
 
-    // After calling prepareForFieldInitializers
-    // and 0 or more calls to emitStoreFieldInitializer.
-    FieldInitializers,
-
-    // After calling emitFieldInitializerHomeObject
-    FieldInitializerWithHomeObject,
+    // After calling prepareForFieldInitializers(isStatic = false).
+    InstanceFieldInitializers,
 
     // After calling emitFieldInitializersEnd.
-    FieldInitializersEnd,
+    InstanceFieldInitializersEnd,
+
+    // After calling prepareForFieldInitializers(isStatic = true).
+    StaticFieldInitializers,
+
+    // After calling emitFieldInitializersEnd.
+    StaticFieldInitializersEnd,
 
     // After calling emitEnd.
     End,
   };
   ClassState classState_ = ClassState::Start;
+
+  // The state of the fields emitter.
+  //
+  // clang-format off
+  //
+  //   +-------+
+  //   | Start +<-----------------------------+
+  //   +-------+                              |
+  //       |                                  |
+  //       | prepareForFieldInitializer       | emitStoreFieldInitializer
+  //       v                                  |
+  // +-------------+                          |
+  // | Initializer +------------------------->+
+  // +-------------+                          |
+  //       |                                  |
+  //       | emitFieldInitializerHomeObject   |
+  //       v                                  |
+  // +---------------------------+            |
+  // | InitializerWithHomeObject +------------+
+  // +---------------------------+
+  //
+  // clang-format on
+  enum class FieldState {
+    // After calling prepareForFieldInitializers
+    // and 0 or more calls to emitStoreFieldInitializer.
+    Start,
+
+    // After calling prepareForFieldInitializer
+    Initializer,
+
+    // After calling emitFieldInitializerHomeObject
+    InitializerWithHomeObject,
+  };
+  FieldState fieldState_ = FieldState::Start;
+
   size_t numFields_ = 0;
 #endif
 
   JS::Rooted<JSAtom*> name_;
   JS::Rooted<JSAtom*> nameForAnonymousClass_;
   bool hasNameOnStack_ = false;
   mozilla::Maybe<NameOpEmitter> initializersAssignment_;
   size_t fieldIndex_ = 0;
@@ -745,18 +787,20 @@ class MOZ_STACK_CLASS ClassEmitter : pub
   //   |                  |
   //   |                  classEnd
   //   |
   //   classStart
   //
   MOZ_MUST_USE bool emitInitDefaultConstructor(uint32_t classStart,
                                                uint32_t classEnd);
 
-  MOZ_MUST_USE bool prepareForFieldInitializers(size_t numFields);
-  MOZ_MUST_USE bool emitFieldInitializerHomeObject();
+  MOZ_MUST_USE bool prepareForFieldInitializers(size_t numFields,
+                                                bool isStatic);
+  MOZ_MUST_USE bool prepareForFieldInitializer();
+  MOZ_MUST_USE bool emitFieldInitializerHomeObject(bool isStatic);
   MOZ_MUST_USE bool emitStoreFieldInitializer();
   MOZ_MUST_USE bool emitFieldInitializersEnd();
 
   MOZ_MUST_USE bool emitEnd(Kind kind);
 
  private:
   MOZ_MUST_USE bool initProtoAndCtor();
 };
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -2125,31 +2125,36 @@ class ClassMethod : public BinaryNode {
   FunctionNode& method() const { return right()->as<FunctionNode>(); }
 
   bool isStatic() const { return isStatic_; }
 
   AccessorType accessorType() const { return accessorType_; }
 };
 
 class ClassField : public BinaryNode {
+  bool isStatic_;
+
  public:
-  ClassField(ParseNode* name, ParseNode* initializer)
+  ClassField(ParseNode* name, ParseNode* initializer, bool isStatic)
       : BinaryNode(ParseNodeKind::ClassField,
                    TokenPos::box(name->pn_pos, initializer->pn_pos), name,
-                   initializer) {}
+                   initializer),
+        isStatic_(isStatic) {}
 
   static bool test(const ParseNode& node) {
     bool match = node.isKind(ParseNodeKind::ClassField);
     MOZ_ASSERT_IF(match, node.is<BinaryNode>());
     return match;
   }
 
   ParseNode& name() const { return *left(); }
 
   FunctionNode* initializer() const { return &right()->as<FunctionNode>(); }
+
+  bool isStatic() const { return isStatic_; }
 };
 
 class PropertyDefinition : public BinaryNode {
   AccessorType accessorType_;
 
  public:
   PropertyDefinition(ParseNode* name, ParseNode* value,
                      AccessorType accessorType)
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -6944,17 +6944,17 @@ static AccessorType ToAccessorType(Prope
       MOZ_CRASH("unexpected property type");
   }
 }
 
 template <class ParseHandler, typename Unit>
 bool GeneralParser<ParseHandler, Unit>::classMember(
     YieldHandling yieldHandling, const ParseContext::ClassStatement& classStmt,
     HandlePropertyName className, uint32_t classStartOffset,
-    HasHeritage hasHeritage, size_t& numFields, size_t& numFieldKeys,
+    HasHeritage hasHeritage, ClassFields& classFields,
     ListNodeType& classMembers, bool* done) {
   *done = false;
 
   TokenKind tt;
   if (!tokenStream.getToken(&tt, TokenStream::SlashIsInvalid)) {
     return false;
   }
   if (tt == TokenKind::RightCurly) {
@@ -7015,30 +7015,34 @@ bool GeneralParser<ParseHandler, Unit>::
       errorAt(propNameOffset, JSMSG_BAD_METHOD_DEF);
       return false;
     }
 
     if (!abortIfSyntaxParser()) {
       return false;
     }
 
-    numFields++;
+    if (isStatic) {
+      classFields.staticFields++;
+    } else {
+      classFields.instanceFields++;
+    }
 
     FunctionNodeType initializer =
-        fieldInitializerOpt(propName, propAtom, numFieldKeys);
+        fieldInitializerOpt(propName, propAtom, classFields, isStatic);
     if (!initializer) {
       return false;
     }
 
     if (!matchOrInsertSemicolon(TokenStream::SlashIsInvalid)) {
       return false;
     }
 
     ClassFieldType field =
-        handler_.newClassFieldDefinition(propName, initializer);
+        handler_.newClassFieldDefinition(propName, initializer, isStatic);
     if (!field) {
       return false;
     }
 
     return handler_.addClassMemberDefinition(classMembers, field);
   }
 
   if (propType != PropertyType::Getter && propType != PropertyType::Setter &&
@@ -7148,20 +7152,21 @@ bool GeneralParser<ParseHandler, Unit>::
 
   return handler_.addClassMemberDefinition(classMembers, method);
 }
 
 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) {
+    const ClassFields& classFields, 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.
+  size_t numFields = classFields.instanceFields;
   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 false;
     }
@@ -7307,40 +7312,52 @@ GeneralParser<ParseHandler, Unit>::class
       return null();
     }
 
     ListNodeType classMembers = handler_.newClassMemberList(pos().begin);
     if (!classMembers) {
       return null();
     }
 
-    size_t numFields = 0;
-    size_t numFieldKeys = 0;
+    ClassFields classFields{};
     for (;;) {
       bool done;
       if (!classMember(yieldHandling, classStmt, className, classStartOffset,
-                       hasHeritage, numFields, numFieldKeys, classMembers,
-                       &done)) {
+                       hasHeritage, classFields, classMembers, &done)) {
         return null();
       }
       if (done) {
         break;
       }
     }
 
-    if (numFieldKeys > 0) {
+    if (classFields.instanceFieldKeys > 0) {
       if (!noteDeclaredName(cx_->names().dotFieldKeys, DeclarationKind::Let,
                             namePos)) {
         return null();
       }
     }
 
+    if (classFields.staticFields > 0) {
+      if (!noteDeclaredName(cx_->names().dotStaticInitializers,
+                            DeclarationKind::Let, namePos)) {
+        return null();
+      }
+    }
+
+    if (classFields.staticFieldKeys > 0) {
+      if (!noteDeclaredName(cx_->names().dotStaticFieldKeys,
+                            DeclarationKind::Let, namePos)) {
+        return null();
+      }
+    }
+
     classEndOffset = pos().end;
     if (!finishClassConstructor(classStmt, className, hasHeritage,
-                                classStartOffset, classEndOffset, numFields,
+                                classStartOffset, classEndOffset, classFields,
                                 classMembers)) {
       return null();
     }
 
     if (className) {
       // The inner name is immutable.
       if (!noteDeclaredName(className, DeclarationKind::Const, namePos)) {
         return null();
@@ -7540,17 +7557,18 @@ GeneralParser<ParseHandler, Unit>::synth
 
   return funNode;
 }
 
 template <class ParseHandler, typename Unit>
 typename ParseHandler::FunctionNodeType
 GeneralParser<ParseHandler, Unit>::fieldInitializerOpt(Node propName,
                                                        HandleAtom propAtom,
-                                                       size_t& numFieldKeys) {
+                                                       ClassFields& classFields,
+                                                       bool isStatic) {
   bool hasInitializer = false;
   if (!tokenStream.matchToken(&hasInitializer, TokenKind::Assign,
                               TokenStream::SlashIsDiv)) {
     return null();
   }
 
   TokenPos firstTokenPos;
   if (hasInitializer) {
@@ -7654,31 +7672,40 @@ GeneralParser<ParseHandler, Unit>::field
     return null();
   }
 
   Node propAssignFieldAccess;
   uint32_t indexValue;
   if (!propAtom) {
     // See BytecodeEmitter::emitCreateFieldKeys for an explanation of what
     // .fieldKeys means and its purpose.
-    Node dotFieldKeys = newInternalDotName(cx_->names().dotFieldKeys);
-    if (!dotFieldKeys) {
-      return null();
-    }
-
-    double fieldKeyIndex = numFieldKeys;
-    numFieldKeys++;
+    NameNodeType fieldKeysName;
+    if (isStatic) {
+      fieldKeysName = newInternalDotName(cx_->names().dotStaticFieldKeys);
+    } else {
+      fieldKeysName = newInternalDotName(cx_->names().dotFieldKeys);
+    }
+    if (!fieldKeysName) {
+      return null();
+    }
+
+    double fieldKeyIndex;
+    if (isStatic) {
+      fieldKeyIndex = classFields.staticFieldKeys++;
+    } else {
+      fieldKeyIndex = classFields.instanceFieldKeys++;
+    }
     Node fieldKeyIndexNode = handler_.newNumber(
         fieldKeyIndex, DecimalPoint::NoDecimal, wholeInitializerPos);
     if (!fieldKeyIndexNode) {
       return null();
     }
 
     Node fieldKeyValue = handler_.newPropertyByValue(
-        dotFieldKeys, fieldKeyIndexNode, wholeInitializerPos.end);
+        fieldKeysName, fieldKeyIndexNode, wholeInitializerPos.end);
     if (!fieldKeyValue) {
       return null();
     }
 
     propAssignFieldAccess = handler_.newPropertyByValue(
         propAssignThis, fieldKeyValue, wholeInitializerPos.end);
     if (!propAssignFieldAccess) {
       return null();
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -1307,32 +1307,44 @@ class MOZ_STACK_CLASS GeneralParser : pu
   inline bool checkExportedNameForFunction(FunctionNodeType funNode);
   inline bool checkExportedNameForClass(ClassNodeType classNode);
   inline bool checkExportedNameForClause(NameNodeType nameNode);
 
   enum ClassContext { ClassStatement, ClassExpression };
   ClassNodeType classDefinition(YieldHandling yieldHandling,
                                 ClassContext classContext,
                                 DefaultHandling defaultHandling);
+  struct ClassFields {
+    // The number of instance class fields.
+    size_t instanceFields = 0;
+
+    // The number of instance class fields with computed property names.
+    size_t instanceFieldKeys = 0;
+
+    // The number of static class fields.
+    size_t staticFields = 0;
+
+    // The number of static class fields with computed property names.
+    size_t staticFieldKeys = 0;
+  };
   MOZ_MUST_USE bool classMember(YieldHandling yieldHandling,
                                 const ParseContext::ClassStatement& classStmt,
                                 HandlePropertyName className,
                                 uint32_t classStartOffset,
                                 HasHeritage hasHeritage,
-                                size_t& numFieldsWithInitializers,
-                                size_t& numFieldKeys,
+                                ClassFields& classFields,
                                 ListNodeType& classMembers, bool* done);
   MOZ_MUST_USE bool finishClassConstructor(
       const ParseContext::ClassStatement& classStmt,
       HandlePropertyName className, HasHeritage hasHeritage,
       uint32_t classStartOffset, uint32_t classEndOffset,
-      size_t numFieldsWithInitializers, ListNodeType& classMembers);
+      const ClassFields& classFields, ListNodeType& classMembers);
 
   FunctionNodeType fieldInitializerOpt(Node name, HandleAtom atom,
-                                       size_t& numFieldKeys);
+                                       ClassFields& classFields, bool isStatic);
   FunctionNodeType synthesizeConstructor(HandleAtom className,
                                          uint32_t classNameOffset,
                                          HasHeritage hasHeritage);
 
   bool checkBindingIdentifier(PropertyName* ident, uint32_t offset,
                               YieldHandling yieldHandling,
                               TokenKind hint = TokenKind::Limit);
 
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -360,17 +360,18 @@ class SyntaxParseHandler {
     return true;
   }
   MOZ_MUST_USE Node newClassMethodDefinition(Node key, FunctionNodeType funNode,
                                              AccessorType atype,
                                              bool isStatic) {
     return NodeGeneric;
   }
   MOZ_MUST_USE Node newClassFieldDefinition(Node name,
-                                            FunctionNodeType initializer) {
+                                            FunctionNodeType initializer,
+                                            bool isStatic) {
     return NodeGeneric;
   }
   MOZ_MUST_USE bool addClassMemberDefinition(ListNodeType memberList,
                                              Node member) {
     return true;
   }
   UnaryNodeType newYieldExpression(uint32_t begin, Node value) {
     return NodeGeneric;
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -119,16 +119,18 @@
   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(dotFieldKeys, dotFieldKeys, ".fieldKeys")                              \
+  MACRO(dotStaticInitializers, dotStaticInitializers, ".staticInitializers")   \
+  MACRO(dotStaticFieldKeys, dotStaticFieldKeys, ".staticFieldKeys")            \
   MACRO(each, each, "each")                                                    \
   MACRO(element, element, "element")                                           \
   MACRO(elementType, elementType, "elementType")                               \
   MACRO(else, else_, "else")                                                   \
   MACRO(empty, empty, "")                                                      \
   MACRO(emptyRegExp, emptyRegExp, "(?:)")                                      \
   MACRO(encodeURI, encodeURI, "encodeURI")                                     \
   MACRO(encodeURIComponent, encodeURIComponent, "encodeURIComponent")          \