Bug 1528038 - Split field-related BytecodeEmitter behavior into separate methods. r=jorendorff
authorAshley Hauck <khyperia@mozilla.com>
Fri, 08 Mar 2019 13:00:47 +0000
changeset 521029 d689ce995f0a
parent 521028 fa25d5068254
child 521030 4e646efc209a
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1528038
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1528038 - Split field-related BytecodeEmitter behavior into separate methods. r=jorendorff Differential Revision: https://phabricator.services.mozilla.com/D21523
js/src/builtin/ReflectParse.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/ParseNode.h
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -2525,19 +2525,19 @@ bool ASTSerializer::classMethod(ClassMet
          expression(&classMethod->method(), &val) &&
          builder.classMethod(key, val, kind, isStatic, &classMethod->pn_pos,
                              dst);
 }
 
 bool ASTSerializer::classField(ClassField* classField, MutableHandleValue dst) {
   RootedValue key(cx), val(cx);
   // Dig through the lambda and get to the actual expression
-  if (classField->hasInitializer()) {
+  if (classField->initializer()) {
     ParseNode* value = classField->initializer()
-                           .body()
+                           ->body()
                            ->head()
                            ->as<LexicalScopeNode>()
                            .scopeBody()
                            ->as<ListNode>()
                            .head()
                            ->as<UnaryNode>()
                            .kid()
                            ->as<AssignmentNode>()
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2447,19 +2447,18 @@ bool BytecodeEmitter::emitScript(ParseNo
   }
 
   tellDebuggerAboutCompiledScript(cx);
 
   return true;
 }
 
 bool BytecodeEmitter::emitInitializeInstanceFields() {
-  FieldInitializers fieldInfo = this->fieldInitializers_;
-  MOZ_ASSERT(fieldInfo.valid);
-  size_t numFields = fieldInfo.numFieldInitializers;
+  MOZ_ASSERT(fieldInitializers_.valid);
+  size_t numFields = fieldInitializers_.numFieldInitializers;
 
   if (numFields == 0) {
     return true;
   }
 
   if (!emitGetName(cx->names().dotInitializers)) {
     //              [stack] ARRAY
     return false;
@@ -7736,27 +7735,21 @@ bool BytecodeEmitter::emitConditionalExp
 
   return true;
 }
 
 bool BytecodeEmitter::emitPropertyList(ListNode* obj, PropertyEmitter& pe,
                                        PropListType type) {
   //                [stack] CTOR? OBJ
 
-  size_t numFields = 0;
   for (ParseNode* propdef : obj->contents()) {
     if (propdef->is<ClassField>()) {
       // 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 private slot.
-      FunctionNode* initializer = &propdef->as<ClassField>().initializer();
-      // Don't include fields without initializers.
-      if (initializer != nullptr) {
-        numFields++;
-      }
+      // 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);
@@ -7998,64 +7991,96 @@ bool BytecodeEmitter::emitPropertyList(L
           return false;
         }
         break;
       default:
         MOZ_CRASH("Invalid op");
     }
   }
 
-  if (numFields > 0) {
-    // .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.
-
-    NameOpEmitter noe(this, cx->names().dotInitializers,
-                      NameOpEmitter::Kind::Initialize);
-    if (!noe.prepareForRhs()) {
-      return false;
-    }
-
-    if (!emitUint32Operand(JSOP_NEWARRAY, numFields)) {
-      //            [stack] CTOR? OBJ ARRAY
-      return false;
-    }
-
-    size_t curFieldIndex = 0;
-    for (ParseNode* propdef : obj->contents()) {
-      if (propdef->is<ClassField>()) {
-        FunctionNode* initializer = &propdef->as<ClassField>().initializer();
-        if (initializer == nullptr) {
-          continue;
-        }
-
-        if (!emitTree(initializer)) {
-          //        [stack] CTOR? OBJ ARRAY LAMBDA
-          return false;
-        }
-
-        if (!emitUint32Operand(JSOP_INITELEM_ARRAY, curFieldIndex)) {
-          //        [stack] CTOR? OBJ ARRAY
-          return false;
-        }
-
-        curFieldIndex++;
-      }
-    }
-
-    if (!noe.emitAssignment()) {
-      //            [stack] CTOR? OBJ ARRAY
-      return false;
-    }
-
-    if (!emit1(JSOP_POP)) {
-      //            [stack] CTOR? OBJ
-      return false;
-    }
-  }
+  if (obj->getKind() == ParseNodeKind::ClassMemberList) {
+    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>()) {
+      FunctionNode* initializer = propdef->as<ClassField>().initializer();
+      // Don't include fields without initializers.
+      if (initializer != nullptr) {
+        numFields++;
+      }
+    }
+  }
+  return FieldInitializers(numFields);
+}
+
+bool BytecodeEmitter::emitCreateFieldInitializers(ListNode* obj) {
+  const FieldInitializers& fieldInitializers = fieldInitializers_;
+  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
+  // code (the initializer) for each field. Upon an object's construction,
+  // these lambdas will be called, defining the values.
+
+  NameOpEmitter noe(this, cx->names().dotInitializers,
+                    NameOpEmitter::Kind::Initialize);
+  if (!noe.prepareForRhs()) {
+    return false;
+  }
+
+  if (!emitUint32Operand(JSOP_NEWARRAY, numFields)) {
+    //            [stack] CTOR? OBJ ARRAY
+    return false;
+  }
+
+  size_t curFieldIndex = 0;
+  for (ParseNode* propdef : obj->contents()) {
+    if (propdef->is<ClassField>()) {
+      FunctionNode* initializer = propdef->as<ClassField>().initializer();
+      if (initializer == nullptr) {
+        continue;
+      }
+
+      if (!emitTree(initializer)) {
+        //        [stack] CTOR? OBJ ARRAY LAMBDA
+        return false;
+      }
+
+      if (!emitUint32Operand(JSOP_INITELEM_ARRAY, curFieldIndex)) {
+        //        [stack] CTOR? OBJ ARRAY
+        return false;
+      }
+
+      curFieldIndex++;
+    }
+  }
+
+  if (!noe.emitAssignment()) {
+    //            [stack] CTOR? OBJ ARRAY
+    return false;
+  }
+
+  if (!emit1(JSOP_POP)) {
+    //            [stack] CTOR? OBJ
+    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()) {
@@ -8815,28 +8840,18 @@ bool BytecodeEmitter::emitClass(
   MOZ_ASSERT((nameKind == ClassNameKind::InferredName) ==
              (nameForAnonymousClass != nullptr));
 
   ParseNode* heritageExpression = classNode->heritage();
   ListNode* classMembers = classNode->memberList();
   FunctionNode* constructor = FindConstructor(cx, classMembers);
 
   // set this->fieldInitializers_
-  size_t numFields = 0;
-  for (ParseNode* propdef : classMembers->contents()) {
-    if (propdef->is<ClassField>()) {
-      FunctionNode* initializer = &propdef->as<ClassField>().initializer();
-      // Don't include fields without initializers.
-      if (initializer != nullptr) {
-        numFields++;
-      }
-    }
-  }
-  FieldInitializers fieldInfo(numFields);
-  AutoResetFieldInitializers _innermostClassAutoReset(this, fieldInfo);
+  AutoResetFieldInitializers _innermostClassAutoReset(
+      this, setupFieldInitializers(classMembers));
 
   // If |nameKind != ClassNameKind::ComputedName|
   //                [stack]
   // Else
   //                [stack] NAME
 
   ClassEmitter ce(this);
   RootedAtom innerName(cx);
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -600,16 +600,19 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
   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 emitCreateFieldInitializers(ListNode* obj);
+
   // 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);
 
   MOZ_MUST_USE bool emitArgOp(JSOp op, uint16_t slot);
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -1960,19 +1960,19 @@ class ClassField : public BinaryNode {
   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(); }
 
-  bool hasInitializer() const { return right() != nullptr; }
-
-  FunctionNode& initializer() const { return right()->as<FunctionNode>(); }
+  FunctionNode* initializer() const {
+    return right() ? &right()->as<FunctionNode>() : nullptr;
+  }
 };
 
 class SwitchStatement : public BinaryNode {
   bool hasDefault_; /* only for ParseNodeKind::Switch */
 
  public:
   SwitchStatement(uint32_t begin, ParseNode* discriminant,
                   LexicalScopeNode* lexicalForCaseList, bool hasDefault)