Bug 1523791: Set "name" property as part of ClassDefinitionEvaluation. r=jorendorff
authorAndré Bargull <andre.bargull@gmail.com>
Tue, 12 Feb 2019 05:10:11 -0800
changeset 458706 d09e3e887cdf
parent 458705 cbbdfdbbe0ab
child 458707 9aca1805a96a
push id35546
push userrmaries@mozilla.com
push dateWed, 13 Feb 2019 04:27:59 +0000
treeherdermozilla-central@636d2c00234d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1523791
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 1523791: Set "name" property as part of ClassDefinitionEvaluation. r=jorendorff
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/ObjectEmitter.cpp
js/src/frontend/ObjectEmitter.h
js/src/jit-test/tests/auto-regress/bug1448582-6.js
js/src/jit/BaselineCompiler.cpp
js/src/jit/CodeGenerator.cpp
js/src/tests/non262/Function/function-toString-discard-source-name.js
js/src/vm/Interpreter.cpp
js/src/vm/JSFunction.cpp
js/src/vm/JSFunction.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -3074,29 +3074,59 @@ bool BytecodeEmitter::emitDefault(ParseN
 
   if (!ifUndefined.emitEnd()) {
     //              [stack] VALUE/DEFAULTVALUE
     return false;
   }
   return true;
 }
 
-bool BytecodeEmitter::setOrEmitSetFunName(ParseNode* maybeFun,
-                                          HandleAtom name) {
-  MOZ_ASSERT(maybeFun->isDirectRHSAnonFunction());
-
-  if (maybeFun->is<FunctionNode>()) {
+bool BytecodeEmitter::emitAnonymousFunctionWithName(ParseNode* node,
+                                                    HandleAtom name) {
+  MOZ_ASSERT(node->isDirectRHSAnonFunction());
+
+  if (node->is<FunctionNode>()) {
+    if (!emitTree(node)) {
+      return false;
+    }
+
     // Function doesn't have 'name' property at this point.
     // Set function's name at compile time.
-    return setFunName(maybeFun->as<FunctionNode>().funbox()->function(), name);
-  }
-
-  MOZ_ASSERT(maybeFun->isKind(ParseNodeKind::ClassDecl));
-
-  return emitSetClassConstructorName(name);
+    return setFunName(node->as<FunctionNode>().funbox()->function(), name);
+  }
+
+  MOZ_ASSERT(node->is<ClassNode>());
+
+  return emitClass(&node->as<ClassNode>(), ClassNameKind::InferredName, name);
+}
+
+bool BytecodeEmitter::emitAnonymousFunctionWithComputedName(
+    ParseNode* node, FunctionPrefixKind prefixKind) {
+  MOZ_ASSERT(node->isDirectRHSAnonFunction());
+
+  if (node->is<FunctionNode>()) {
+    if (!emitTree(node)) {
+      //            [stack] NAME FUN
+      return false;
+    }
+    if (!emitDupAt(1)) {
+      //            [stack] NAME FUN NAME
+      return false;
+    }
+    if (!emit2(JSOP_SETFUNNAME, uint8_t(prefixKind))) {
+      //            [stack] NAME FUN
+      return false;
+    }
+    return true;
+  }
+
+  MOZ_ASSERT(node->is<ClassNode>());
+  MOZ_ASSERT(prefixKind == FunctionPrefixKind::None);
+
+  return emitClass(&node->as<ClassNode>(), ClassNameKind::ComputedName);
 }
 
 bool BytecodeEmitter::setFunName(JSFunction* fun, JSAtom* name) {
   // The inferred name may already be set if this function is an interpreted
   // lazy function and we OOM'ed after we set the inferred name the first
   // time.
   if (fun->hasInferredName()) {
     MOZ_ASSERT(fun->isInterpretedLazy());
@@ -3104,43 +3134,26 @@ bool BytecodeEmitter::setFunName(JSFunct
 
     return true;
   }
 
   fun->setInferredName(name);
   return true;
 }
 
-bool BytecodeEmitter::emitSetClassConstructorName(JSAtom* name) {
-  uint32_t nameIndex;
-  if (!makeAtomIndex(name, &nameIndex)) {
-    return false;
-  }
-  if (!emitIndexOp(JSOP_STRING, nameIndex)) {
-    //              [stack] FUN NAME
-    return false;
-  }
-  uint8_t kind = uint8_t(FunctionPrefixKind::None);
-  if (!emit2(JSOP_SETFUNNAME, kind)) {
-    //              [stack] FUN
-    return false;
-  }
-  return true;
-}
-
 bool BytecodeEmitter::emitInitializer(ParseNode* initializer,
                                       ParseNode* pattern) {
-  if (!emitTree(initializer)) {
-    return false;
-  }
-
   if (initializer->isDirectRHSAnonFunction()) {
     MOZ_ASSERT(!pattern->isInParens());
     RootedAtom name(cx, pattern->as<NameNode>().name());
-    if (!setOrEmitSetFunName(initializer, name)) {
+    if (!emitAnonymousFunctionWithName(initializer, name)) {
+      return false;
+    }
+  } else {
+    if (!emitTree(initializer)) {
       return false;
     }
   }
 
   return true;
 }
 
 bool BytecodeEmitter::emitDestructuringOpsArray(ListNode* pattern,
@@ -4056,27 +4069,28 @@ bool BytecodeEmitter::emitAssignment(Par
     NameOpEmitter noe(this, name,
                       isCompound ? NameOpEmitter::Kind::CompoundAssignment
                                  : NameOpEmitter::Kind::SimpleAssignment);
     if (!noe.prepareForRhs()) {
       //            [stack] ENV? VAL?
       return false;
     }
 
-    // Emit the RHS. If we emitted a BIND[G]NAME, then the scope is on
-    // the top of the stack and we need to pick the right RHS value.
-    uint8_t offset = noe.emittedBindOp() ? 2 : 1;
-    if (!EmitAssignmentRhs(this, rhs, offset)) {
-      //            [stack] ENV? VAL? RHS
-      return false;
-    }
     if (rhs && rhs->isDirectRHSAnonFunction()) {
       MOZ_ASSERT(!nameNode->isInParens());
       MOZ_ASSERT(!isCompound);
-      if (!setOrEmitSetFunName(rhs, name)) {
+      if (!emitAnonymousFunctionWithName(rhs, name)) {
+        //          [stack] ENV? VAL? RHS
+        return false;
+      }
+    } else {
+      // Emit the RHS. If we emitted a BIND[G]NAME, then the scope is on
+      // the top of the stack and we need to pick the right RHS value.
+      uint8_t offset = noe.emittedBindOp() ? 2 : 1;
+      if (!EmitAssignmentRhs(this, rhs, offset)) {
         //          [stack] ENV? VAL? RHS
         return false;
       }
     }
 
     // Emit the compound assignment op if there is one.
     if (isCompound) {
       if (!emit1(compoundOp)) {
@@ -7656,27 +7670,64 @@ bool BytecodeEmitter::emitPropertyList(L
       }
       continue;
     }
 
     BinaryNode* prop = &propdef->as<BinaryNode>();
 
     ParseNode* key = prop->left();
     ParseNode* propVal = prop->right();
-    bool isPropertyAnonFunctionOrClass = propVal->isDirectRHSAnonFunction();
     JSOp op = propdef->getOp();
     MOZ_ASSERT(op == JSOP_INITPROP || op == JSOP_INITPROP_GETTER ||
                op == JSOP_INITPROP_SETTER);
 
-    auto emitValue = [this, &propVal, &pe]() {
+    auto emitValue = [this, &key, &propVal, op, &pe]() {
       //            [stack] CTOR? OBJ CTOR? KEY?
 
-      if (!emitTree(propVal)) {
-        //          [stack] CTOR? OBJ CTOR? KEY? VAL
-        return false;
+      if (propVal->isDirectRHSAnonFunction()) {
+        if (key->isKind(ParseNodeKind::NumberExpr)) {
+          MOZ_ASSERT(op == JSOP_INITPROP);
+
+          NumericLiteral* literal = &key->as<NumericLiteral>();
+          RootedAtom keyAtom(cx, NumberToAtom(cx, literal->value()));
+          if (!keyAtom) {
+            return false;
+          }
+          if (!emitAnonymousFunctionWithName(propVal, keyAtom)) {
+            //      [stack] CTOR? OBJ CTOR? KEY VAL
+            return false;
+          }
+        } else if (key->isKind(ParseNodeKind::ObjectPropertyName) ||
+                   key->isKind(ParseNodeKind::StringExpr)) {
+          MOZ_ASSERT(op == JSOP_INITPROP);
+
+          RootedAtom keyAtom(cx, key->as<NameNode>().atom());
+          if (!emitAnonymousFunctionWithName(propVal, keyAtom)) {
+            //      [stack] CTOR? OBJ CTOR? VAL
+            return false;
+          }
+        } else {
+          MOZ_ASSERT(key->isKind(ParseNodeKind::ComputedName));
+
+          FunctionPrefixKind prefix = op == JSOP_INITPROP
+                                          ? FunctionPrefixKind::None
+                                          : op == JSOP_INITPROP_GETTER
+                                                ? FunctionPrefixKind::Get
+                                                : FunctionPrefixKind::Set;
+
+          if (!emitAnonymousFunctionWithComputedName(propVal, prefix)) {
+            //      [stack] CTOR? OBJ CTOR? KEY VAL
+            return false;
+          }
+        }
+      } else {
+        if (!emitTree(propVal)) {
+          //        [stack] CTOR? OBJ CTOR? KEY? VAL
+          return false;
+        }
       }
 
       if (propVal->is<FunctionNode>() &&
           propVal->as<FunctionNode>().funbox()->needsHomeObject()) {
         FunctionBox* funbox = propVal->as<FunctionNode>().funbox();
         MOZ_ASSERT(funbox->function()->allowSuperProperty());
 
         if (!pe.emitInitHomeObject(funbox->asyncKind())) {
@@ -7707,30 +7758,28 @@ bool BytecodeEmitter::emitPropertyList(L
       }
       if (!emitValue()) {
         //          [stack] CTOR? OBJ CTOR? KEY VAL
         return false;
       }
 
       switch (op) {
         case JSOP_INITPROP:
-          if (!pe.emitInitIndexProp(isPropertyAnonFunctionOrClass)) {
+          if (!pe.emitInitIndexProp()) {
             //      [stack] CTOR? OBJ
             return false;
           }
           break;
         case JSOP_INITPROP_GETTER:
-          MOZ_ASSERT(!isPropertyAnonFunctionOrClass);
           if (!pe.emitInitIndexGetter()) {
             //      [stack] CTOR? OBJ
             return false;
           }
           break;
         case JSOP_INITPROP_SETTER:
-          MOZ_ASSERT(!isPropertyAnonFunctionOrClass);
           if (!pe.emitInitIndexSetter()) {
             //      [stack] CTOR? OBJ
             return false;
           }
           break;
         default:
           MOZ_CRASH("Invalid op");
       }
@@ -7753,53 +7802,31 @@ bool BytecodeEmitter::emitPropertyList(L
         //          [stack] CTOR? OBJ CTOR?
         return false;
       }
       if (!emitValue()) {
         //          [stack] CTOR? OBJ CTOR? VAL
         return false;
       }
 
-      RootedFunction anonFunction(cx);
-      if (isPropertyAnonFunctionOrClass) {
-        MOZ_ASSERT(op == JSOP_INITPROP);
-
-        if (propVal->is<FunctionNode>()) {
-          // When the value is function, we set the function's name
-          // at the compile-time, instead of emitting SETFUNNAME.
-          FunctionBox* funbox = propVal->as<FunctionNode>().funbox();
-          anonFunction = funbox->function();
-        } else {
-          // Only object literal can have a property where key is
-          // name and value is an anonymous class.
-          //
-          //   ({ foo: class {} });
-          MOZ_ASSERT(type == ObjectLiteral);
-          MOZ_ASSERT(propVal->isKind(ParseNodeKind::ClassDecl));
-        }
-      }
-
       RootedAtom keyAtom(cx, key->as<NameNode>().atom());
       switch (op) {
         case JSOP_INITPROP:
-          if (!pe.emitInitProp(keyAtom, isPropertyAnonFunctionOrClass,
-                               anonFunction)) {
+          if (!pe.emitInitProp(keyAtom)) {
             //      [stack] CTOR? OBJ
             return false;
           }
           break;
         case JSOP_INITPROP_GETTER:
-          MOZ_ASSERT(!isPropertyAnonFunctionOrClass);
           if (!pe.emitInitGetter(keyAtom)) {
             //      [stack] CTOR? OBJ
             return false;
           }
           break;
         case JSOP_INITPROP_SETTER:
-          MOZ_ASSERT(!isPropertyAnonFunctionOrClass);
           if (!pe.emitInitSetter(keyAtom)) {
             //      [stack] CTOR? OBJ
             return false;
           }
           break;
         default:
           MOZ_CRASH("Invalid op");
       }
@@ -7825,30 +7852,28 @@ bool BytecodeEmitter::emitPropertyList(L
     }
     if (!emitValue()) {
       //            [stack] CTOR? OBJ CTOR? KEY VAL
       return false;
     }
 
     switch (op) {
       case JSOP_INITPROP:
-        if (!pe.emitInitComputedProp(isPropertyAnonFunctionOrClass)) {
+        if (!pe.emitInitComputedProp()) {
           //        [stack] CTOR? OBJ
           return false;
         }
         break;
       case JSOP_INITPROP_GETTER:
-        MOZ_ASSERT(isPropertyAnonFunctionOrClass);
         if (!pe.emitInitComputedGetter()) {
           //        [stack] CTOR? OBJ
           return false;
         }
         break;
       case JSOP_INITPROP_SETTER:
-        MOZ_ASSERT(isPropertyAnonFunctionOrClass);
         if (!pe.emitInitComputedSetter()) {
           //        [stack] CTOR? OBJ
           return false;
         }
         break;
       default:
         MOZ_CRASH("Invalid op");
     }
@@ -8495,28 +8520,37 @@ static MOZ_ALWAYS_INLINE FunctionNode* F
     }
   }
 
   return nullptr;
 }
 
 // This follows ES6 14.5.14 (ClassDefinitionEvaluation) and ES6 14.5.15
 // (BindingClassDeclarationEvaluation).
-bool BytecodeEmitter::emitClass(ClassNode* classNode) {
-  ClassNames* names = classNode->names();
+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);
 
+  // If |nameKind != ClassNameKind::ComputedName|
   //                [stack]
+  // Else
+  //                [stack] NAME
 
   ClassEmitter ce(this);
   RootedAtom innerName(cx);
   ClassEmitter::Kind kind = ClassEmitter::Kind::Expression;
-  if (names) {
+  if (ClassNames* names = classNode->names()) {
+    MOZ_ASSERT(nameKind == ClassNameKind::BindingName);
     innerName = names->innerBinding()->name();
     MOZ_ASSERT(innerName);
 
     if (names->outerBinding()) {
       MOZ_ASSERT(names->outerBinding()->name());
       MOZ_ASSERT(names->outerBinding()->name() == innerName);
       kind = ClassEmitter::Kind::Declaration;
     }
@@ -8527,41 +8561,49 @@ bool BytecodeEmitter::emitClass(ClassNod
     }
   }
 
   // This is kind of silly. In order to the get the home object defined on
   // the constructor, we have to make it second, but we want the prototype
   // on top for EmitPropertyList, because we expect static properties to be
   // rarer. The result is a few more swaps than we would like. Such is life.
   bool isDerived = !!heritageExpression;
+  bool hasNameOnStack = nameKind == ClassNameKind::ComputedName;
   if (isDerived) {
     if (!emitTree(heritageExpression)) {
       //            [stack] HERITAGE
       return false;
     }
-    if (!ce.emitDerivedClass(innerName)) {
+    if (!ce.emitDerivedClass(innerName, nameForAnonymousClass,
+                             hasNameOnStack)) {
       //            [stack] HERITAGE HOMEOBJ
       return false;
     }
   } else {
-    if (!ce.emitClass(innerName)) {
+    if (!ce.emitClass(innerName, nameForAnonymousClass, hasNameOnStack)) {
       //            [stack] HOMEOBJ
       return false;
     }
   }
 
   // Stack currently has HOMEOBJ followed by optional HERITAGE. When HERITAGE
   // is not used, an implicit value of %FunctionPrototype% is implied.
   if (constructor) {
     bool needsHomeObject = constructor->funbox()->needsHomeObject();
     // HERITAGE is consumed inside emitFunction.
     if (!emitFunction(constructor, isDerived)) {
       //            [stack] HOMEOBJ CTOR
       return false;
     }
+    if (nameKind == ClassNameKind::InferredName) {
+      if (!setFunName(constructor->funbox()->function(),
+                      nameForAnonymousClass)) {
+        return false;
+      }
+    }
     if (!ce.emitInitConstructor(needsHomeObject)) {
       //            [stack] CTOR HOMEOBJ
       return false;
     }
   } else {
     if (!ce.emitInitDefaultConstructor(Some(classNode->pn_pos.begin),
                                        Some(classNode->pn_pos.end))) {
       //            [stack] CTOR HOMEOBJ
@@ -8581,33 +8623,35 @@ bool BytecodeEmitter::emitClass(ClassNod
   }
 
   return true;
 }
 
 bool BytecodeEmitter::emitExportDefault(BinaryNode* exportNode) {
   MOZ_ASSERT(exportNode->isKind(ParseNodeKind::ExportDefaultStmt));
 
-  ParseNode* nameNode = exportNode->left();
-  if (!emitTree(nameNode)) {
-    return false;
+  ParseNode* valueNode = exportNode->left();
+  if (valueNode->isDirectRHSAnonFunction()) {
+    MOZ_ASSERT(exportNode->right());
+
+    HandlePropertyName name = cx->names().default_;
+    if (!emitAnonymousFunctionWithName(valueNode, name)) {
+      return false;
+    }
+  } else {
+    if (!emitTree(valueNode)) {
+      return false;
+    }
   }
 
   if (ParseNode* binding = exportNode->right()) {
     if (!emitLexicalInitialization(&binding->as<NameNode>())) {
       return false;
     }
 
-    if (nameNode->isDirectRHSAnonFunction()) {
-      HandlePropertyName name = cx->names().default_;
-      if (!setOrEmitSetFunName(nameNode, name)) {
-        return false;
-      }
-    }
-
     if (!emit1(JSOP_POP)) {
       return false;
     }
   }
 
   return true;
 }
 
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -762,20 +762,23 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
 
   // Check if the value on top of the stack is "undefined". If so, replace
   // that value on the stack with the value defined by |defaultExpr|.
   // |pattern| is a lhs node of the default expression.  If it's an
   // identifier and |defaultExpr| is an anonymous function, |SetFunctionName|
   // is called at compile time.
   MOZ_MUST_USE bool emitDefault(ParseNode* defaultExpr, ParseNode* pattern);
 
-  MOZ_MUST_USE bool setOrEmitSetFunName(ParseNode* maybeFun, HandleAtom name);
+  MOZ_MUST_USE bool emitAnonymousFunctionWithName(ParseNode* node,
+                                                  HandleAtom name);
+
+  MOZ_MUST_USE bool emitAnonymousFunctionWithComputedName(
+      ParseNode* node, FunctionPrefixKind prefixKind);
 
   MOZ_MUST_USE bool setFunName(JSFunction* fun, JSAtom* name);
-  MOZ_MUST_USE bool emitSetClassConstructorName(JSAtom* name);
   MOZ_MUST_USE bool emitInitializer(ParseNode* initializer, ParseNode* pattern);
 
   MOZ_MUST_USE bool emitCallSiteObject(CallSiteNode* callSiteObj);
   MOZ_MUST_USE bool emitTemplateString(ListNode* templateString);
   MOZ_MUST_USE bool emitAssignment(ParseNode* lhs, JSOp compoundOp,
                                    ParseNode* rhs);
 
   MOZ_MUST_USE bool emitReturn(UnaryNode* returnNode);
@@ -846,17 +849,30 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
   // emitSpread expects the current index (I) of the array, the array itself
   // and the iterator to be on the stack in that order (iterator on the bottom).
   // It will pop the iterator and I, then iterate over the iterator by calling
   // |.next()| and put the results into the I-th element of array with
   // incrementing I, then push the result I (it will be original I +
   // iteration count). The stack after iteration will look like |ARRAY INDEX|.
   MOZ_MUST_USE bool emitSpread(bool allowSelfHosted = false);
 
-  MOZ_MUST_USE bool emitClass(ClassNode* classNode);
+  enum class ClassNameKind {
+    // The class name is defined through its BindingIdentifier, if present.
+    BindingName,
+
+    // The class is anonymous and has a statically inferred name.
+    InferredName,
+
+    // The class is anonymous and has a dynamically computed name.
+    ComputedName
+  };
+
+  MOZ_MUST_USE bool emitClass(
+      ClassNode* classNode, ClassNameKind nameKind = ClassNameKind::BindingName,
+      HandleAtom nameForAnonymousClass = nullptr);
   MOZ_MUST_USE bool emitSuperElemOperands(
       PropertyByValue* elem, EmitElemOption opts = EmitElemOption::Get);
   MOZ_MUST_USE bool emitSuperGetElem(PropertyByValue* elem,
                                      bool isCall = false);
 
   MOZ_MUST_USE bool emitCalleeAndThis(ParseNode* callee, ParseNode* call,
                                       CallOrNewEmitter& cone);
 
--- a/js/src/frontend/ObjectEmitter.cpp
+++ b/js/src/frontend/ObjectEmitter.cpp
@@ -280,80 +280,67 @@ bool PropertyEmitter::emitInitHomeObject
     propertyState_ = PropertyState::InitHomeObjForIndex;
   } else {
     propertyState_ = PropertyState::InitHomeObjForComputed;
   }
 #endif
   return true;
 }
 
-bool PropertyEmitter::emitInitProp(
-    JS::Handle<JSAtom*> key, bool isPropertyAnonFunctionOrClass /* = false */,
-    JS::Handle<JSFunction*> anonFunction /* = nullptr */) {
-  return emitInit(isClass_ ? JSOP_INITHIDDENPROP : JSOP_INITPROP, key,
-                  isPropertyAnonFunctionOrClass, anonFunction);
+bool PropertyEmitter::emitInitProp(JS::Handle<JSAtom*> key) {
+  return emitInit(isClass_ ? JSOP_INITHIDDENPROP : JSOP_INITPROP, key);
 }
 
 bool PropertyEmitter::emitInitGetter(JS::Handle<JSAtom*> key) {
   obj_ = nullptr;
   return emitInit(isClass_ ? JSOP_INITHIDDENPROP_GETTER : JSOP_INITPROP_GETTER,
-                  key, false, nullptr);
+                  key);
 }
 
 bool PropertyEmitter::emitInitSetter(JS::Handle<JSAtom*> key) {
   obj_ = nullptr;
   return emitInit(isClass_ ? JSOP_INITHIDDENPROP_SETTER : JSOP_INITPROP_SETTER,
-                  key, false, nullptr);
+                  key);
 }
 
-bool PropertyEmitter::emitInitIndexProp(
-    bool isPropertyAnonFunctionOrClass /* = false */) {
-  return emitInitIndexOrComputed(isClass_ ? JSOP_INITHIDDENELEM : JSOP_INITELEM,
-                                 FunctionPrefixKind::None,
-                                 isPropertyAnonFunctionOrClass);
+bool PropertyEmitter::emitInitIndexProp() {
+  return emitInitIndexOrComputed(isClass_ ? JSOP_INITHIDDENELEM
+                                          : JSOP_INITELEM);
 }
 
 bool PropertyEmitter::emitInitIndexGetter() {
   obj_ = nullptr;
-  return emitInitIndexOrComputed(
-      isClass_ ? JSOP_INITHIDDENELEM_GETTER : JSOP_INITELEM_GETTER,
-      FunctionPrefixKind::Get, false);
+  return emitInitIndexOrComputed(isClass_ ? JSOP_INITHIDDENELEM_GETTER
+                                          : JSOP_INITELEM_GETTER);
 }
 
 bool PropertyEmitter::emitInitIndexSetter() {
   obj_ = nullptr;
-  return emitInitIndexOrComputed(
-      isClass_ ? JSOP_INITHIDDENELEM_SETTER : JSOP_INITELEM_SETTER,
-      FunctionPrefixKind::Set, false);
+  return emitInitIndexOrComputed(isClass_ ? JSOP_INITHIDDENELEM_SETTER
+                                          : JSOP_INITELEM_SETTER);
 }
 
-bool PropertyEmitter::emitInitComputedProp(
-    bool isPropertyAnonFunctionOrClass /* = false */) {
-  return emitInitIndexOrComputed(isClass_ ? JSOP_INITHIDDENELEM : JSOP_INITELEM,
-                                 FunctionPrefixKind::None,
-                                 isPropertyAnonFunctionOrClass);
+bool PropertyEmitter::emitInitComputedProp() {
+  return emitInitIndexOrComputed(isClass_ ? JSOP_INITHIDDENELEM
+                                          : JSOP_INITELEM);
 }
 
 bool PropertyEmitter::emitInitComputedGetter() {
   obj_ = nullptr;
-  return emitInitIndexOrComputed(
-      isClass_ ? JSOP_INITHIDDENELEM_GETTER : JSOP_INITELEM_GETTER,
-      FunctionPrefixKind::Get, true);
+  return emitInitIndexOrComputed(isClass_ ? JSOP_INITHIDDENELEM_GETTER
+                                          : JSOP_INITELEM_GETTER);
 }
 
 bool PropertyEmitter::emitInitComputedSetter() {
   obj_ = nullptr;
-  return emitInitIndexOrComputed(
-      isClass_ ? JSOP_INITHIDDENELEM_SETTER : JSOP_INITELEM_SETTER,
-      FunctionPrefixKind::Set, true);
+  return emitInitIndexOrComputed(isClass_ ? JSOP_INITHIDDENELEM_SETTER
+                                          : JSOP_INITELEM_SETTER);
 }
 
-bool PropertyEmitter::emitInit(JSOp op, JS::Handle<JSAtom*> key,
-                               bool isPropertyAnonFunctionOrClass,
-                               JS::Handle<JSFunction*> anonFunction) {
+bool PropertyEmitter::emitInit(JSOp op, JS::Handle<JSAtom*> key) {
   MOZ_ASSERT(propertyState_ == PropertyState::PropValue ||
              propertyState_ == PropertyState::InitHomeObj);
 
   MOZ_ASSERT(op == JSOP_INITPROP || op == JSOP_INITHIDDENPROP ||
              op == JSOP_INITPROP_GETTER || op == JSOP_INITHIDDENPROP_GETTER ||
              op == JSOP_INITPROP_SETTER || op == JSOP_INITHIDDENPROP_SETTER);
 
   //                [stack] CTOR? OBJ CTOR? VAL
@@ -371,73 +358,43 @@ bool PropertyEmitter::emitInit(JSOp op, 
                                   JSPROP_ENUMERATE)) {
       return false;
     }
     if (obj_->inDictionaryMode()) {
       obj_ = nullptr;
     }
   }
 
-  if (isPropertyAnonFunctionOrClass) {
-    MOZ_ASSERT(op == JSOP_INITPROP || op == JSOP_INITHIDDENPROP);
-
-    if (anonFunction) {
-      if (!bce_->setFunName(anonFunction, key)) {
-        return false;
-      }
-    } else {
-      // NOTE: This is setting the constructor's name of the class which is
-      //       the property value.  Not of the enclosing class.
-      if (!bce_->emitSetClassConstructorName(key)) {
-        //          [stack] CTOR? OBJ CTOR? FUN
-        return false;
-      }
-    }
-  }
-
   if (!bce_->emitIndex32(op, index)) {
     //              [stack] CTOR? OBJ CTOR?
     return false;
   }
 
   if (!emitPopClassConstructor()) {
     return false;
   }
 
 #ifdef DEBUG
   propertyState_ = PropertyState::Init;
 #endif
   return true;
 }
 
-bool PropertyEmitter::emitInitIndexOrComputed(
-    JSOp op, FunctionPrefixKind prefixKind,
-    bool isPropertyAnonFunctionOrClass) {
+bool PropertyEmitter::emitInitIndexOrComputed(JSOp op) {
   MOZ_ASSERT(propertyState_ == PropertyState::IndexValue ||
              propertyState_ == PropertyState::InitHomeObjForIndex ||
              propertyState_ == PropertyState::ComputedValue ||
              propertyState_ == PropertyState::InitHomeObjForComputed);
 
   MOZ_ASSERT(op == JSOP_INITELEM || op == JSOP_INITHIDDENELEM ||
              op == JSOP_INITELEM_GETTER || op == JSOP_INITHIDDENELEM_GETTER ||
              op == JSOP_INITELEM_SETTER || op == JSOP_INITHIDDENELEM_SETTER);
 
   //                [stack] CTOR? OBJ CTOR? KEY VAL
 
-  if (isPropertyAnonFunctionOrClass) {
-    if (!bce_->emitDupAt(1)) {
-      //            [stack] CTOR? OBJ CTOR? KEY FUN FUN
-      return false;
-    }
-    if (!bce_->emit2(JSOP_SETFUNNAME, uint8_t(prefixKind))) {
-      //            [stack] CTOR? OBJ CTOR? KEY FUN
-      return false;
-    }
-  }
-
   if (!bce_->emit1(op)) {
     //              [stack] CTOR? OBJ CTOR?
     return false;
   }
 
   if (!emitPopClassConstructor()) {
     return false;
   }
@@ -531,17 +488,20 @@ AutoSaveLocalStrictMode::~AutoSaveLocalS
 }
 
 void AutoSaveLocalStrictMode::restore() {
   MOZ_ALWAYS_TRUE(sc_->setLocalStrictMode(savedStrictness_));
   sc_ = nullptr;
 }
 
 ClassEmitter::ClassEmitter(BytecodeEmitter* bce)
-    : PropertyEmitter(bce), strictMode_(bce->sc), name_(bce->cx) {
+    : PropertyEmitter(bce),
+      strictMode_(bce->sc),
+      name_(bce->cx),
+      nameForAnonymousClass_(bce->cx) {
   isClass_ = true;
 }
 
 bool ClassEmitter::emitScopeForNamedClass(
     JS::Handle<LexicalScope::Data*> scopeBindings) {
   MOZ_ASSERT(propertyState_ == PropertyState::Start);
   MOZ_ASSERT(classState_ == ClassState::Start);
 
@@ -552,45 +512,57 @@ bool ClassEmitter::emitScopeForNamedClas
   }
 
 #ifdef DEBUG
   classState_ = ClassState::Scope;
 #endif
   return true;
 }
 
-bool ClassEmitter::emitClass(JS::Handle<JSAtom*> name) {
+bool ClassEmitter::emitClass(JS::Handle<JSAtom*> name,
+                             JS::Handle<JSAtom*> nameForAnonymousClass,
+                             bool hasNameOnStack) {
   MOZ_ASSERT(propertyState_ == PropertyState::Start);
   MOZ_ASSERT(classState_ == ClassState::Start ||
              classState_ == ClassState::Scope);
+  MOZ_ASSERT_IF(nameForAnonymousClass || hasNameOnStack, !name);
+  MOZ_ASSERT(!(nameForAnonymousClass && hasNameOnStack));
 
   //                [stack]
 
-  setName(name);
+  name_ = name;
+  nameForAnonymousClass_ = nameForAnonymousClass;
+  hasNameOnStack_ = hasNameOnStack;
   isDerived_ = false;
 
   if (!bce_->emitNewInit()) {
     //              [stack] HOMEOBJ
     return false;
   }
 
 #ifdef DEBUG
   classState_ = ClassState::Class;
 #endif
   return true;
 }
 
-bool ClassEmitter::emitDerivedClass(JS::Handle<JSAtom*> name) {
+bool ClassEmitter::emitDerivedClass(JS::Handle<JSAtom*> name,
+                                    JS::Handle<JSAtom*> nameForAnonymousClass,
+                                    bool hasNameOnStack) {
   MOZ_ASSERT(propertyState_ == PropertyState::Start);
   MOZ_ASSERT(classState_ == ClassState::Start ||
              classState_ == ClassState::Scope);
+  MOZ_ASSERT_IF(nameForAnonymousClass || hasNameOnStack, !name);
+  MOZ_ASSERT(!nameForAnonymousClass || !hasNameOnStack);
 
   //                [stack]
 
-  setName(name);
+  name_ = name;
+  nameForAnonymousClass_ = nameForAnonymousClass;
+  hasNameOnStack_ = hasNameOnStack;
   isDerived_ = true;
 
   InternalIfEmitter ifThenElse(bce_);
 
   // Heritage must be null or a non-generator constructor
   if (!bce_->emit1(JSOP_CHECKCLASSHERITAGE)) {
     //              [stack] HERITAGE
     return false;
@@ -655,23 +627,16 @@ bool ClassEmitter::emitDerivedClass(JS::
   }
 
 #ifdef DEBUG
   classState_ = ClassState::Class;
 #endif
   return true;
 }
 
-void ClassEmitter::setName(JS::Handle<JSAtom*> name) {
-  name_ = name;
-  if (!name_) {
-    name_ = bce_->cx->names().empty;
-  }
-}
-
 bool ClassEmitter::emitInitConstructor(bool needsHomeObject) {
   MOZ_ASSERT(propertyState_ == PropertyState::Start);
   MOZ_ASSERT(classState_ == ClassState::Class);
 
   //                [stack] HOMEOBJ CTOR
 
   if (needsHomeObject) {
     if (!bce_->emitDupAt(1)) {
@@ -706,58 +671,103 @@ bool ClassEmitter::emitInitDefaultConstr
     // actually make the constructor during execution, we can give it the
     // correct toString output.
     if (!bce_->newSrcNote3(SRC_CLASS_SPAN, ptrdiff_t(*classStart),
                            ptrdiff_t(*classEnd))) {
       return false;
     }
   }
 
+  RootedAtom className(bce_->cx, name_);
+  if (!className) {
+    if (nameForAnonymousClass_) {
+      className = nameForAnonymousClass_;
+    } else {
+      className = bce_->cx->names().empty;
+    }
+  }
+
   if (isDerived_) {
     //              [stack] HERITAGE PROTO
-    if (!bce_->emitAtomOp(name_, JSOP_DERIVEDCONSTRUCTOR)) {
+    if (!bce_->emitAtomOp(className, JSOP_DERIVEDCONSTRUCTOR)) {
       //            [stack] HOMEOBJ CTOR
       return false;
     }
   } else {
     //              [stack] HOMEOBJ
-    if (!bce_->emitAtomOp(name_, JSOP_CLASSCONSTRUCTOR)) {
+    if (!bce_->emitAtomOp(className, JSOP_CLASSCONSTRUCTOR)) {
       //            [stack] HOMEOBJ CTOR
       return false;
     }
   }
 
+  // The empty string is used as a placeholder, so if the inferred name for this
+  // anonymous class expression is also the empty string, we need to set it
+  // explicitly here.
+  if (nameForAnonymousClass_ == bce_->cx->names().empty) {
+    if (!emitSetEmptyClassConstructorNameForDefaultCtor()) {
+      return false;
+    }
+  }
+
   if (!initProtoAndCtor()) {
     //              [stack] CTOR HOMEOBJ
     return false;
   }
 
 #ifdef DEBUG
   classState_ = ClassState::InitConstructor;
 #endif
   return true;
 }
 
+bool ClassEmitter::emitSetEmptyClassConstructorNameForDefaultCtor() {
+  uint32_t nameIndex;
+  if (!bce_->makeAtomIndex(bce_->cx->names().empty, &nameIndex)) {
+    return false;
+  }
+  if (!bce_->emitIndexOp(JSOP_STRING, nameIndex)) {
+    //              [stack] CTOR NAME
+    return false;
+  }
+  if (!bce_->emit2(JSOP_SETFUNNAME, uint8_t(FunctionPrefixKind::None))) {
+    //              [stack] CTOR
+    return false;
+  }
+  return true;
+}
+
 bool ClassEmitter::initProtoAndCtor() {
-  //                [stack] HOMEOBJ CTOR
+  //                [stack] NAME? HOMEOBJ CTOR
+
+  if (hasNameOnStack_) {
+    if (!bce_->emitDupAt(2)) {
+      //            [stack] NAME HOMEOBJ CTOR NAME
+      return false;
+    }
+    if (!bce_->emit2(JSOP_SETFUNNAME, uint8_t(FunctionPrefixKind::None))) {
+      //            [stack] NAME HOMEOBJ CTOR
+      return false;
+    }
+  }
 
   if (!bce_->emit1(JSOP_SWAP)) {
-    //              [stack] CTOR HOMEOBJ
+    //              [stack] NAME? CTOR HOMEOBJ
     return false;
   }
   if (!bce_->emit1(JSOP_DUP2)) {
-    //              [stack] CTOR HOMEOBJ CTOR HOMEOBJ
+    //              [stack] NAME? CTOR HOMEOBJ CTOR HOMEOBJ
     return false;
   }
   if (!bce_->emitAtomOp(bce_->cx->names().prototype, JSOP_INITLOCKEDPROP)) {
-    //              [stack] CTOR HOMEOBJ CTOR
+    //              [stack] NAME? CTOR HOMEOBJ CTOR
     return false;
   }
   if (!bce_->emitAtomOp(bce_->cx->names().constructor, JSOP_INITHIDDENPROP)) {
-    //              [stack] CTOR HOMEOBJ
+    //              [stack] NAME? CTOR HOMEOBJ
     return false;
   }
 
   return true;
 }
 
 bool ClassEmitter::emitEnd(Kind kind) {
   MOZ_ASSERT(propertyState_ == PropertyState::Start ||
@@ -766,17 +776,17 @@ bool ClassEmitter::emitEnd(Kind kind) {
 
   //                [stack] CTOR HOMEOBJ
 
   if (!bce_->emit1(JSOP_POP)) {
     //              [stack] CTOR
     return false;
   }
 
-  if (name_ != bce_->cx->names().empty) {
+  if (name_) {
     MOZ_ASSERT(tdzCacheForInnerName_.isSome());
     MOZ_ASSERT(innerNameScope_.isSome());
 
     if (!bce_->emitLexicalInitialization(name_)) {
       //            [stack] CTOR
       return false;
     }
 
--- a/js/src/frontend/ObjectEmitter.h
+++ b/js/src/frontend/ObjectEmitter.h
@@ -244,58 +244,38 @@ class MOZ_STACK_CLASS PropertyEmitter {
       const mozilla::Maybe<uint32_t>& keyPos, Kind kind = Kind::Prototype);
   MOZ_MUST_USE bool prepareForComputedPropValue();
 
   MOZ_MUST_USE bool emitInitHomeObject(
       FunctionAsyncKind kind = FunctionAsyncKind::SyncFunction);
 
   // @param key
   //        Property key
-  // @param isPropertyAnonFunctionOrClass
-  //        True if the property value is an anonymous function or
-  //        an anonymous class
-  // @param anonFunction
-  //        The anonymous function object for property value
-  MOZ_MUST_USE bool emitInitProp(
-      JS::Handle<JSAtom*> key, bool isPropertyAnonFunctionOrClass = false,
-      JS::Handle<JSFunction*> anonFunction = nullptr);
+  MOZ_MUST_USE bool emitInitProp(JS::Handle<JSAtom*> key);
   MOZ_MUST_USE bool emitInitGetter(JS::Handle<JSAtom*> key);
   MOZ_MUST_USE bool emitInitSetter(JS::Handle<JSAtom*> key);
 
-  MOZ_MUST_USE bool emitInitIndexProp(
-      bool isPropertyAnonFunctionOrClass = false);
+  MOZ_MUST_USE bool emitInitIndexProp();
   MOZ_MUST_USE bool emitInitIndexGetter();
   MOZ_MUST_USE bool emitInitIndexSetter();
 
-  MOZ_MUST_USE bool emitInitComputedProp(
-      bool isPropertyAnonFunctionOrClass = false);
+  MOZ_MUST_USE bool emitInitComputedProp();
   MOZ_MUST_USE bool emitInitComputedGetter();
   MOZ_MUST_USE bool emitInitComputedSetter();
 
  private:
   MOZ_MUST_USE MOZ_ALWAYS_INLINE bool prepareForProp(
       const mozilla::Maybe<uint32_t>& keyPos, bool isStatic, bool isComputed);
 
   // @param op
   //        Opcode for initializing property
-  // @param prefixKind
-  //        None, Get, or Set
   // @param key
   //        Atom of the property if the property key is not computed
-  // @param isPropertyAnonFunctionOrClass
-  //        True if the property is either an anonymous function or an
-  //        anonymous class
-  // @param anonFunction
-  //        Anonymous function object for the property
-  MOZ_MUST_USE bool emitInit(JSOp op, JS::Handle<JSAtom*> key,
-                             bool isPropertyAnonFunctionOrClass,
-                             JS::Handle<JSFunction*> anonFunction);
-  MOZ_MUST_USE bool emitInitIndexOrComputed(JSOp op,
-                                            FunctionPrefixKind prefixKind,
-                                            bool isPropertyAnonFunctionOrClass);
+  MOZ_MUST_USE bool emitInit(JSOp op, JS::Handle<JSAtom*> key);
+  MOZ_MUST_USE bool emitInitIndexOrComputed(JSOp op);
 
   MOZ_MUST_USE bool emitPopClassConstructor();
 };
 
 // Class for emitting bytecode for object literal.
 //
 // Usage: (check for the return value is omitted for simplicity)
 //
@@ -315,17 +295,17 @@ class MOZ_STACK_CLASS PropertyEmitter {
 //     oe.emitEnd();
 //
 //   `{ prop: function() {} }`, when property value is anonymous function
 //     ObjectEmitter oe(this);
 //     oe.emitObject(1);
 //
 //     oe.prepareForPropValue(Some(offset_of_prop));
 //     emit(function);
-//     oe.emitInitProp(atom_of_prop, true, function_object);
+//     oe.emitInitProp(atom_of_prop);
 //
 //     oe.emitEnd();
 //
 //   `{ get prop() { ... }, set prop(v) { ... } }`
 //     ObjectEmitter oe(this);
 //     oe.emitObject(2);
 //
 //     oe.prepareForPropValue(Some(offset_of_prop));
@@ -341,17 +321,17 @@ class MOZ_STACK_CLASS PropertyEmitter {
 //   `{ 1: 10, get 2() { ... }, set 3(v) { ... } }`
 //     ObjectEmitter oe(this);
 //     oe.emitObject(3);
 //
 //     oe.prepareForIndexPropKey(Some(offset_of_prop));
 //     emit(1);
 //     oe.prepareForIndexPropValue();
 //     emit(10);
-//     oe.emitInitIndexedProp(atom_of_prop);
+//     oe.emitInitIndexedProp();
 //
 //     oe.prepareForIndexPropKey(Some(offset_of_opening_bracket));
 //     emit(2);
 //     oe.prepareForIndexPropValue();
 //     emit(function_for_getter);
 //     oe.emitInitIndexGetter();
 //
 //     oe.prepareForIndexPropKey(Some(offset_of_opening_bracket));
@@ -455,70 +435,70 @@ class MOZ_RAII AutoSaveLocalStrictMode {
 };
 
 // Class for emitting bytecode for JS class.
 //
 // Usage: (check for the return value is omitted for simplicity)
 //
 //   `class {}`
 //     ClassEmitter ce(this);
-//     ce.emitClass();
+//     ce.emitClass(nullptr, nullptr, false);
 //
 //     ce.emitInitDefaultConstructor(Some(offset_of_class),
 //                                   Some(offset_of_closing_bracket));
 //
 //     ce.emitEnd(ClassEmitter::Kind::Expression);
 //
 //   `class { constructor() { ... } }`
 //     ClassEmitter ce(this);
-//     ce.emitClass();
+//     ce.emitClass(nullptr, nullptr, false);
 //
 //     emit(function_for_constructor);
 //     ce.emitInitConstructor(/* needsHomeObject = */ false);
 //
 //     ce.emitEnd(ClassEmitter::Kind::Expression);
 //
 //   `class X { constructor() { ... } }`
 //     ClassEmitter ce(this);
 //     ce.emitScopeForNamedClass(scopeBindingForName);
-//     ce.emitClass(atom_of_X);
+//     ce.emitClass(atom_of_X, nullptr, false);
 //
 //     ce.emitInitDefaultConstructor(Some(offset_of_class),
 //                                   Some(offset_of_closing_bracket));
 //
 //     ce.emitEnd(ClassEmitter::Kind::Expression);
 //
 //   `class X { constructor() { ... } }`
 //     ClassEmitter ce(this);
 //     ce.emitScopeForNamedClass(scopeBindingForName);
-//     ce.emitClass(atom_of_X);
+//     ce.emitClass(atom_of_X, nullptr, false);
 //
 //     emit(function_for_constructor);
 //     ce.emitInitConstructor(/* needsHomeObject = */ false);
 //
 //     ce.emitEnd(ClassEmitter::Kind::Expression);
 //
 //   `class X extends Y { constructor() { ... } }`
 //     ClassEmitter ce(this);
 //     ce.emitScopeForNamedClass(scopeBindingForName);
 //
 //     emit(Y);
-//     ce.emitDerivedClass(atom_of_X);
+//     ce.emitDerivedClass(atom_of_X, nullptr, false);
 //
 //     emit(function_for_constructor);
 //     ce.emitInitConstructor(/* needsHomeObject = */ false);
 //
 //     ce.emitEnd(ClassEmitter::Kind::Expression);
 //
 //   `class X extends Y { constructor() { ... super.f(); ... } }`
 //     ClassEmitter ce(this);
 //     ce.emitScopeForNamedClass(scopeBindingForName);
 //
 //     emit(Y);
-//     ce.emitDerivedClass(atom_of_X);
+//     ce.emitDerivedClass(atom_of_X, nullptr, false);
 //
 //     emit(function_for_constructor);
 //     // pass true if constructor contains super.prop access
 //     ce.emitInitConstructor(/* needsHomeObject = */ true);
 //
 //     ce.emitEnd(ClassEmitter::Kind::Expression);
 //
 //   `m() {}` in class
@@ -675,27 +655,37 @@ class MOZ_STACK_CLASS ClassEmitter : pub
 
     // After calling emitEnd.
     End,
   };
   ClassState classState_ = ClassState::Start;
 #endif
 
   JS::Rooted<JSAtom*> name_;
+  JS::Rooted<JSAtom*> nameForAnonymousClass_;
+  bool hasNameOnStack_ = false;
 
  public:
   explicit ClassEmitter(BytecodeEmitter* bce);
 
   MOZ_MUST_USE bool emitScopeForNamedClass(
       JS::Handle<LexicalScope::Data*> scopeBindings);
 
   // @param name
   //        Name of the class (nullptr if this is anonymous class)
-  MOZ_MUST_USE bool emitClass(JS::Handle<JSAtom*> name);
-  MOZ_MUST_USE bool emitDerivedClass(JS::Handle<JSAtom*> name);
+  // @param nameForAnonymousClass
+  //        Statically inferred name of the class (only for anonymous classes)
+  // @param hasNameOnStack
+  //        If true the name is on the stack (only for anonymous classes)
+  MOZ_MUST_USE bool emitClass(JS::Handle<JSAtom*> name,
+                              JS::Handle<JSAtom*> nameForAnonymousClass,
+                              bool hasNameOnStack);
+  MOZ_MUST_USE bool emitDerivedClass(JS::Handle<JSAtom*> name,
+                                     JS::Handle<JSAtom*> nameForAnonymousClass,
+                                     bool hasNameOnStack);
 
   // @param needsHomeObject
   //        True if the constructor contains `super.foo`
   MOZ_MUST_USE bool emitInitConstructor(bool needsHomeObject);
 
   // Parameters are the offset in the source code for each character below:
   //
   //   class X { foo() {} }
@@ -707,16 +697,16 @@ class MOZ_STACK_CLASS ClassEmitter : pub
   //
   MOZ_MUST_USE bool emitInitDefaultConstructor(
       const mozilla::Maybe<uint32_t>& classStart,
       const mozilla::Maybe<uint32_t>& classEnd);
 
   MOZ_MUST_USE bool emitEnd(Kind kind);
 
  private:
-  void setName(JS::Handle<JSAtom*> name);
+  MOZ_MUST_USE bool emitSetEmptyClassConstructorNameForDefaultCtor();
   MOZ_MUST_USE bool initProtoAndCtor();
 };
 
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* frontend_ObjectEmitter_h */
--- a/js/src/jit-test/tests/auto-regress/bug1448582-6.js
+++ b/js/src/jit-test/tests/auto-regress/bug1448582-6.js
@@ -4,24 +4,23 @@
 // - The |o[index]| inner function has a dynamic name from a computed property name.
 // - The |self| inner function uses |Function.prototype.caller| to reinvoke the outer function.
 
 (function(index) {
     var o = {
         [index]: class {
             constructor() {}
 
-            // Prevent adding an inferred name at index = 1 by creating a
-            // static method named "name".
+            // The static method named "name" is added after assigning the
+            // inferred name.
             static [(index === 0 ? "not-name" : "name")]() {}
         }
     }
 
-    // At index = 0 the class will get the inferred name "0".
-    // At index = 1 the class should have no inferred name.
-    assertEq(displayName(o[index]), index === 0 ? "0" : "");
+    // The inferred name matches the current index.
+    assertEq(displayName(o[index]), String(index));
 
     if (index === 0) {
         (function self() {
             self.caller(1);
         })();
     }
 })(0);
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -2193,17 +2193,17 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
   frame.push(R0);
   return true;
 }
 
 typedef bool (*SetFunNameFn)(JSContext*, HandleFunction, HandleValue,
                              FunctionPrefixKind);
 static const VMFunction SetFunNameInfo =
-    FunctionInfo<SetFunNameFn>(js::SetFunctionNameIfNoOwnName, "SetFunName");
+    FunctionInfo<SetFunNameFn>(js::SetFunctionName, "SetFunName");
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_SETFUNNAME() {
   frame.popRegsAndSync(2);
 
   frame.push(R0);
   frame.syncStack(0);
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3180,17 +3180,17 @@ void CodeGenerator::emitLambdaInit(Regis
   // the nursery.
   masm.storePtr(ImmGCPtr(info.funUnsafe()->displayAtom()),
                 Address(output, JSFunction::offsetOfAtom()));
 }
 
 typedef bool (*SetFunNameFn)(JSContext*, HandleFunction, HandleValue,
                              FunctionPrefixKind);
 static const VMFunction SetFunNameInfo =
-    FunctionInfo<SetFunNameFn>(js::SetFunctionNameIfNoOwnName, "SetFunName");
+    FunctionInfo<SetFunNameFn>(js::SetFunctionName, "SetFunName");
 
 void CodeGenerator::visitSetFunName(LSetFunName* lir) {
   pushArg(Imm32(lir->mir()->prefixKind()));
   pushArg(ToValue(lir, LSetFunName::NameValue));
   pushArg(ToRegister(lir->fun()));
 
   callVM(SetFunNameInfo, lir);
 }
--- a/js/src/tests/non262/Function/function-toString-discard-source-name.js
+++ b/js/src/tests/non262/Function/function-toString-discard-source-name.js
@@ -87,17 +87,17 @@ class classDecl {}
 var classExpr = class C {};
 var classExprAnon = class {};
 
 this.classDecl = classDecl;
 `);
 
 assertFunctionName(classDecl, "classDecl");
 assertFunctionName(classExpr, "C");
-assertFunctionName(classExprAnon, undefined);
+assertFunctionName(classExprAnon, "classExprAnon");
 
 
 // Class declarations and expressions (explicit constructor).
 eval(`
 class classDecl { constructor() {} }
 var classExpr = class C { constructor() {} };
 var classExprAnon = class { constructor() {} };
 
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -3652,17 +3652,17 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_
     END_CASE(JSOP_TRYSKIPAWAIT)
 
     CASE(JSOP_SETFUNNAME) {
       MOZ_ASSERT(REGS.stackDepth() >= 2);
       FunctionPrefixKind prefixKind = FunctionPrefixKind(GET_UINT8(REGS.pc));
       ReservedRooted<Value> name(&rootValue0, REGS.sp[-1]);
       ReservedRooted<JSFunction*> fun(&rootFunction0,
                                       &REGS.sp[-2].toObject().as<JSFunction>());
-      if (!SetFunctionNameIfNoOwnName(cx, fun, name, prefixKind)) {
+      if (!SetFunctionName(cx, fun, name, prefixKind)) {
         goto error;
       }
 
       REGS.sp--;
     }
     END_CASE(JSOP_SETFUNNAME)
 
     CASE(JSOP_CALLEE) {
--- a/js/src/vm/JSFunction.cpp
+++ b/js/src/vm/JSFunction.cpp
@@ -2211,18 +2211,18 @@ static inline JSFunction* NewFunctionClo
   if (!clone) {
     return nullptr;
   }
 
   // JSFunction::HAS_INFERRED_NAME can be set at compile-time and at
   // runtime. In the latter case we should actually clear the flag before
   // cloning the function, but since we can't differentiate between both
   // cases here, we'll end up with a momentarily incorrect function name.
-  // This will be fixed up in SetFunctionNameIfNoOwnName(), which should
-  // happen through JSOP_SETFUNNAME directly after JSOP_LAMBDA.
+  // This will be fixed up in SetFunctionName(), which should happen through
+  // JSOP_SETFUNNAME directly after JSOP_LAMBDA.
   constexpr uint16_t NonCloneableFlags = JSFunction::EXTENDED |
                                          JSFunction::RESOLVED_LENGTH |
                                          JSFunction::RESOLVED_NAME;
 
   uint16_t flags = fun->flags() & ~NonCloneableFlags;
   if (allocKind == gc::AllocKind::FUNCTION_EXTENDED) {
     flags |= JSFunction::EXTENDED;
   }
@@ -2469,53 +2469,41 @@ JSAtom* js::IdToFunctionName(
     return SymbolToFunctionName(cx, JSID_TO_SYMBOL(id), prefixKind);
   }
 
   // Step 5.
   RootedValue idv(cx, IdToValue(id));
   return NameToFunctionName(cx, idv, prefixKind);
 }
 
-bool js::SetFunctionNameIfNoOwnName(JSContext* cx, HandleFunction fun,
-                                    HandleValue name,
-                                    FunctionPrefixKind prefixKind) {
+bool js::SetFunctionName(JSContext* cx, HandleFunction fun, HandleValue name,
+                         FunctionPrefixKind prefixKind) {
   MOZ_ASSERT(name.isString() || name.isSymbol() || name.isNumber());
 
-  // An inferred name may already be set if this function is a clone of a
-  // singleton function. Clear the inferred name in all cases, even if we
-  // end up not adding a new inferred name if |fun| is a class constructor.
+  // `fun` is a newly created function, so normally it can't already have an
+  // inferred name. The rare exception is when `fun` was created by cloning
+  // a singleton function; see the comment in NewFunctionClone. In that case,
+  // the inferred name is bogus, so clear it out.
   if (fun->hasInferredName()) {
     MOZ_ASSERT(fun->isSingleton());
     fun->clearInferredName();
   }
 
-  if (fun->isClassConstructor()) {
-    // A class may have static 'name' method or accessor.
-    if (fun->contains(cx, cx->names().name)) {
-      return true;
-    }
-  } else {
-    // Anonymous function shouldn't have own 'name' property at this point.
-    MOZ_ASSERT(!fun->containsPure(cx->names().name));
-  }
+  // Anonymous functions should neither have an own 'name' property nor a
+  // resolved name at this point.
+  MOZ_ASSERT(!fun->containsPure(cx->names().name));
+  MOZ_ASSERT(!fun->hasResolvedName());
 
   JSAtom* funName = name.isSymbol()
                         ? SymbolToFunctionName(cx, name.toSymbol(), prefixKind)
                         : NameToFunctionName(cx, name, prefixKind);
   if (!funName) {
     return false;
   }
 
-  // RESOLVED_NAME shouldn't yet be set, at least as long as we don't
-  // support the "static public fields" or "decorators" proposal.
-  // These two proposals allow to access class constructors before
-  // JSOP_SETFUNNAME is executed, which means user code may have set the
-  // RESOLVED_NAME flag when we reach this point.
-  MOZ_ASSERT(!fun->hasResolvedName());
-
   fun->setInferredName(funName);
 
   return true;
 }
 
 JSFunction* js::DefineFunction(
     JSContext* cx, HandleObject obj, HandleId id, Native native, unsigned nargs,
     unsigned flags, gc::AllocKind allocKind /* = AllocKind::FUNCTION */) {
--- a/js/src/vm/JSFunction.h
+++ b/js/src/vm/JSFunction.h
@@ -67,18 +67,18 @@ class JSFunction : public js::NativeObje
                         nonstandard function-statement) */
     SELF_HOSTED =
         0x0080, /* On an interpreted function, indicates a self-hosted builtin,
                    which must not be decompilable nor constructible. On a native
                    function, indicates an 'intrinsic', intended for use from
                    self-hosted code only. */
     HAS_INFERRED_NAME = 0x0100, /* function had no explicit name, but a name was
                                    set by SetFunctionName at compile time or
-                                   SetFunctionNameIfNoOwnName at runtime. See
-                                   atom_ for more info about this flag. */
+                                   SetFunctionName at runtime. See atom_ for
+                                   more info about this flag. */
     INTERPRETED_LAZY =
         0x0200, /* function is interpreted but doesn't have a script yet */
     RESOLVED_LENGTH =
         0x0400,             /* f.length has been resolved (see fun_resolve). */
     RESOLVED_NAME = 0x0800, /* f.name has been resolved (see fun_resolve). */
     NEW_SCRIPT_CLEARED =
         0x1000, /* For a function used as an interpreted constructor, whether
                    a 'new' type had constructor information cleared. */
@@ -179,18 +179,17 @@ class JSFunction : public js::NativeObje
   //      compile-time, the HAS_INFERRED_NAME is set directly in the
   //      bytecode emitter, when it happens at runtime, the flag is set when
   //      evaluating the JSOP_SETFUNNAME bytecode.
   //   d. HAS_GUESSED_ATOM and HAS_INFERRED_NAME cannot both be set.
   //   e. |atom_| can be null if neither an explicit, nor inferred, nor a
   //      guessed name was set.
   //   f. HAS_INFERRED_NAME can be set for cloned singleton function, even
   //      though the clone shouldn't receive an inferred name. See the
-  //      comments in NewFunctionClone() and SetFunctionNameIfNoOwnName()
-  //      for details.
+  //      comments in NewFunctionClone() and SetFunctionName() for details.
   //
   // 2. If the function is a bound function:
   //   a. To store the initial value of the "name" property.
   //   b. If HAS_BOUND_FUNCTION_NAME_PREFIX is not set, |atom_| doesn't
   //      contain the "bound " prefix which is prepended to the "name"
   //      property of bound functions per ECMAScript.
   //   c. Bound functions can never have an inferred or guessed name.
   //   d. |atom_| is never null for bound functions.
@@ -893,19 +892,18 @@ extern JSFunction* NewScriptedFunction(
     JSContext* cx, unsigned nargs, JSFunction::Flags flags, HandleAtom atom,
     HandleObject proto = nullptr,
     gc::AllocKind allocKind = gc::AllocKind::FUNCTION,
     NewObjectKind newKind = GenericObject, HandleObject enclosingEnv = nullptr);
 extern JSAtom* IdToFunctionName(
     JSContext* cx, HandleId id,
     FunctionPrefixKind prefixKind = FunctionPrefixKind::None);
 
-extern bool SetFunctionNameIfNoOwnName(JSContext* cx, HandleFunction fun,
-                                       HandleValue name,
-                                       FunctionPrefixKind prefixKind);
+extern bool SetFunctionName(JSContext* cx, HandleFunction fun, HandleValue name,
+                            FunctionPrefixKind prefixKind);
 
 extern JSFunction* DefineFunction(
     JSContext* cx, HandleObject obj, HandleId id, JSNative native,
     unsigned nargs, unsigned flags,
     gc::AllocKind allocKind = gc::AllocKind::FUNCTION);
 
 extern bool fun_toString(JSContext* cx, unsigned argc, Value* vp);