Bug 1535471 - Use JSOP_INITPROP for field initializers. r=jorendorff
authorAshley Hauck <khyperia@mozilla.com>
Wed, 24 Apr 2019 19:41:37 +0000
changeset 530021 8457ce7cc442cf10ebe42c8299fdcd4ce259981e
parent 530020 8e4422d0040f1b1994be8f4d00ec2f1c7067a707
child 530022 70858ec04904f0b15fc718e67a092cf736e69a39
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1535471
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1535471 - Use JSOP_INITPROP for field initializers. r=jorendorff Differential Revision: https://phabricator.services.mozilla.com/D27543
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/ElemOpEmitter.cpp
js/src/frontend/ElemOpEmitter.h
js/src/frontend/FoldConstants.cpp
js/src/frontend/FullParseHandler.h
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/PropOpEmitter.cpp
js/src/frontend/PropOpEmitter.h
js/src/jit-test/tests/fields/initprop.js
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1195,16 +1195,20 @@ restart:
       ParseNode* expr = pn->as<UnaryNode>().kid();
       return checkSideEffects(expr, answer);
     }
 
     case ParseNodeKind::ExpressionStmt:
       return checkSideEffects(pn->as<UnaryNode>().kid(), answer);
 
     // Binary cases with obvious side effects.
+    case ParseNodeKind::InitExpr:
+      *answer = true;
+      return true;
+
     case ParseNodeKind::AssignExpr:
     case ParseNodeKind::AddAssignExpr:
     case ParseNodeKind::SubAssignExpr:
     case ParseNodeKind::BitOrAssignExpr:
     case ParseNodeKind::BitXorAssignExpr:
     case ParseNodeKind::BitAndAssignExpr:
     case ParseNodeKind::LshAssignExpr:
     case ParseNodeKind::RshAssignExpr:
@@ -4086,16 +4090,18 @@ static bool EmitAssignmentRhs(BytecodeEm
     return false;
   }
 
   return true;
 }
 
 static inline JSOp CompoundAssignmentParseNodeKindToJSOp(ParseNodeKind pnk) {
   switch (pnk) {
+    case ParseNodeKind::InitExpr:
+      return JSOP_NOP;
     case ParseNodeKind::AssignExpr:
       return JSOP_NOP;
     case ParseNodeKind::AddAssignExpr:
       return JSOP_ADD;
     case ParseNodeKind::SubAssignExpr:
       return JSOP_SUB;
     case ParseNodeKind::BitOrAssignExpr:
       return JSOP_BITOR;
@@ -4117,19 +4123,24 @@ static inline JSOp CompoundAssignmentPar
       return JSOP_MOD;
     case ParseNodeKind::PowAssignExpr:
       return JSOP_POW;
     default:
       MOZ_CRASH("unexpected compound assignment op");
   }
 }
 
-bool BytecodeEmitter::emitAssignment(ParseNode* lhs, JSOp compoundOp,
-                                     ParseNode* rhs) {
+bool BytecodeEmitter::emitAssignmentOrInit(ParseNodeKind kind, ParseNode* lhs,
+                                           ParseNode* rhs) {
+  JSOp compoundOp = CompoundAssignmentParseNodeKindToJSOp(kind);
   bool isCompound = compoundOp != JSOP_NOP;
+  bool isInit = kind == ParseNodeKind::InitExpr;
+
+  MOZ_ASSERT_IF(isInit, lhs->isKind(ParseNodeKind::DotExpr) ||
+                            lhs->isKind(ParseNodeKind::ElemExpr));
 
   // Name assignments are handled separately because choosing ops and when
   // to emit BINDNAME is involved and should avoid duplication.
   if (lhs->isKind(ParseNodeKind::Name)) {
     NameNode* nameNode = &lhs->as<NameNode>();
     RootedAtom name(cx, nameNode->name());
     NameOpEmitter noe(this, name,
                       isCompound ? NameOpEmitter::Kind::CompoundAssignment
@@ -4178,17 +4189,18 @@ bool BytecodeEmitter::emitAssignment(Par
   uint8_t offset = 1;
 
   switch (lhs->getKind()) {
     case ParseNodeKind::DotExpr: {
       PropertyAccess* prop = &lhs->as<PropertyAccess>();
       bool isSuper = prop->isSuper();
       poe.emplace(this,
                   isCompound ? PropOpEmitter::Kind::CompoundAssignment
-                             : PropOpEmitter::Kind::SimpleAssignment,
+                             : isInit ? PropOpEmitter::Kind::PropInit
+                                      : PropOpEmitter::Kind::SimpleAssignment,
                   isSuper ? PropOpEmitter::ObjKind::Super
                           : PropOpEmitter::ObjKind::Other);
       if (!poe->prepareForObj()) {
         return false;
       }
       if (isSuper) {
         UnaryNode* base = &prop->expression().as<UnaryNode>();
         if (!emitGetThisForSuperBase(base)) {
@@ -4206,17 +4218,18 @@ bool BytecodeEmitter::emitAssignment(Par
       }
       break;
     }
     case ParseNodeKind::ElemExpr: {
       PropertyByValue* elem = &lhs->as<PropertyByValue>();
       bool isSuper = elem->isSuper();
       eoe.emplace(this,
                   isCompound ? ElemOpEmitter::Kind::CompoundAssignment
-                             : ElemOpEmitter::Kind::SimpleAssignment,
+                             : isInit ? ElemOpEmitter::Kind::PropInit
+                                      : ElemOpEmitter::Kind::SimpleAssignment,
                   isSuper ? ElemOpEmitter::ObjKind::Super
                           : ElemOpEmitter::ObjKind::Other);
       if (!emitElemObjAndKey(elem, isSuper, *eoe)) {
         //          [stack] # if Super
         //          [stack] THIS KEY
         //          [stack] # otherwise
         //          [stack] OBJ KEY
         return false;
@@ -5257,17 +5270,17 @@ bool BytecodeEmitter::emitInitializeForI
 
   ParseNode* target = forHead->kid1();
   MOZ_ASSERT(!forHead->kid2());
 
   // If the for-in/of loop didn't have a variable declaration, per-loop
   // initialization is just assigning the iteration value to a target
   // expression.
   if (!parser->astGenerator().isDeclarationList(target)) {
-    return emitAssignment(target, JSOP_NOP, nullptr);
+    return emitAssignmentOrInit(ParseNodeKind::AssignExpr, target, nullptr);
     //              [stack] ... ITERVAL
   }
 
   // Otherwise, per-loop initialization is (possibly) declaration
   // initialization.  If the declaration is a lexical declaration, it must be
   // initialized.  If the declaration is a variable declaration, an
   // assignment to that name (which does *not* necessarily assign to the
   // variable!) must be generated.
@@ -5278,18 +5291,19 @@ bool BytecodeEmitter::emitInitializeForI
 
   MOZ_ASSERT(target->isForLoopDeclaration());
   target = parser->astGenerator().singleBindingFromDeclaration(
       &target->as<ListNode>());
 
   NameNode* nameNode = nullptr;
   if (target->isKind(ParseNodeKind::Name)) {
     nameNode = &target->as<NameNode>();
-  } else if (target->isKind(ParseNodeKind::AssignExpr)) {
-    AssignmentNode* assignNode = &target->as<AssignmentNode>();
+  } else if (target->isKind(ParseNodeKind::AssignExpr) ||
+             target->isKind(ParseNodeKind::InitExpr)) {
+    BinaryNode* assignNode = &target->as<BinaryNode>();
     if (assignNode->left()->is<NameNode>()) {
       nameNode = &assignNode->left()->as<NameNode>();
     }
   }
 
   if (nameNode) {
     NameOpEmitter noe(this, nameNode->name(), NameOpEmitter::Kind::Initialize);
     if (!noe.prepareForRhs()) {
@@ -5313,17 +5327,18 @@ bool BytecodeEmitter::emitInitializeForI
       return false;
     }
 
     // The caller handles removing the iteration value from the stack.
     return true;
   }
 
   MOZ_ASSERT(
-      !target->isKind(ParseNodeKind::AssignExpr),
+      !target->isKind(ParseNodeKind::AssignExpr) &&
+          !target->isKind(ParseNodeKind::InitExpr),
       "for-in/of loop destructuring declarations can't have initializers");
 
   MOZ_ASSERT(target->isKind(ParseNodeKind::ArrayExpr) ||
              target->isKind(ParseNodeKind::ObjectExpr));
   return emitDestructuringOps(&target->as<ListNode>(),
                               DestructuringFlavor::Declaration);
 }
 
@@ -5416,18 +5431,19 @@ bool BytecodeEmitter::emitForIn(ForNode*
   ForInEmitter forIn(this, headLexicalEmitterScope);
 
   // Annex B: Evaluate the var-initializer expression if present.
   // |for (var i = initializer in expr) { ... }|
   ParseNode* forInTarget = forInHead->kid1();
   if (parser->astGenerator().isDeclarationList(forInTarget)) {
     ParseNode* decl = parser->astGenerator().singleBindingFromDeclaration(
         &forInTarget->as<ListNode>());
-    if (decl->isKind(ParseNodeKind::AssignExpr)) {
-      AssignmentNode* assignNode = &decl->as<AssignmentNode>();
+    if (decl->isKind(ParseNodeKind::AssignExpr) ||
+        decl->isKind(ParseNodeKind::InitExpr)) {
+      BinaryNode* assignNode = &decl->as<BinaryNode>();
       if (assignNode->left()->is<NameNode>()) {
         NameNode* nameNode = &assignNode->left()->as<NameNode>();
         ParseNode* initializer = assignNode->right();
         MOZ_ASSERT(
             forInTarget->isKind(ParseNodeKind::VarStmt),
             "for-in initializers are only permitted for |var| declarations");
 
         if (!updateSourceCoordNotes(decl->pn_pos.begin)) {
@@ -8458,19 +8474,20 @@ bool BytecodeEmitter::emitFunctionFormal
 
   bool hasRest = funbox->hasRest();
 
   FunctionParamsEmitter fpe(this, funbox);
   for (ParseNode* arg = paramsBody->head(); arg != funBody;
        arg = arg->pn_next) {
     ParseNode* bindingElement = arg;
     ParseNode* initializer = nullptr;
-    if (arg->isKind(ParseNodeKind::AssignExpr)) {
-      bindingElement = arg->as<AssignmentNode>().left();
-      initializer = arg->as<AssignmentNode>().right();
+    if (arg->isKind(ParseNodeKind::AssignExpr) ||
+        arg->isKind(ParseNodeKind::InitExpr)) {
+      bindingElement = arg->as<BinaryNode>().left();
+      initializer = arg->as<BinaryNode>().right();
     }
     bool hasInitializer = !!initializer;
     bool isRest = hasRest && arg->pn_next == funBody;
     bool isDestructuring = !bindingElement->isKind(ParseNodeKind::Name);
 
     // Left-hand sides are either simple names or destructuring patterns.
     MOZ_ASSERT(bindingElement->isKind(ParseNodeKind::Name) ||
                bindingElement->isKind(ParseNodeKind::ArrayExpr) ||
@@ -9007,34 +9024,33 @@ bool BytecodeEmitter::emitTree(
       break;
 
     case ParseNodeKind::CommaExpr:
       if (!emitSequenceExpr(&pn->as<ListNode>(), valueUsage)) {
         return false;
       }
       break;
 
+    case ParseNodeKind::InitExpr:
     case ParseNodeKind::AssignExpr:
     case ParseNodeKind::AddAssignExpr:
     case ParseNodeKind::SubAssignExpr:
     case ParseNodeKind::BitOrAssignExpr:
     case ParseNodeKind::BitXorAssignExpr:
     case ParseNodeKind::BitAndAssignExpr:
     case ParseNodeKind::LshAssignExpr:
     case ParseNodeKind::RshAssignExpr:
     case ParseNodeKind::UrshAssignExpr:
     case ParseNodeKind::MulAssignExpr:
     case ParseNodeKind::DivAssignExpr:
     case ParseNodeKind::ModAssignExpr:
     case ParseNodeKind::PowAssignExpr: {
-      AssignmentNode* assignNode = &pn->as<AssignmentNode>();
-      if (!emitAssignment(
-              assignNode->left(),
-              CompoundAssignmentParseNodeKindToJSOp(assignNode->getKind()),
-              assignNode->right())) {
+      BinaryNode* assignNode = &pn->as<BinaryNode>();
+      if (!emitAssignmentOrInit(assignNode->getKind(), assignNode->left(),
+                                assignNode->right())) {
         return false;
       }
       break;
     }
 
     case ParseNodeKind::ConditionalExpr:
       if (!emitConditionalExpression(pn->as<ConditionalExpression>(),
                                      valueUsage)) {
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -945,18 +945,18 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
   MOZ_MUST_USE bool emitAnonymousFunctionWithComputedName(
       ParseNode* node, FunctionPrefixKind prefixKind);
 
   MOZ_MUST_USE bool setFunName(JSFunction* fun, 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 emitAssignmentOrInit(ParseNodeKind kind, ParseNode* lhs,
+                                         ParseNode* rhs);
 
   MOZ_MUST_USE bool emitReturn(UnaryNode* returnNode);
   MOZ_MUST_USE bool emitExpressionStatement(UnaryNode* exprStmt);
   MOZ_MUST_USE bool emitStatementList(ListNode* stmtList);
 
   MOZ_MUST_USE bool emitDeleteName(UnaryNode* deleteNode);
   MOZ_MUST_USE bool emitDeleteProperty(UnaryNode* deleteNode);
   MOZ_MUST_USE bool emitDeleteElement(UnaryNode* deleteNode);
--- a/js/src/frontend/ElemOpEmitter.cpp
+++ b/js/src/frontend/ElemOpEmitter.cpp
@@ -120,21 +120,21 @@ bool ElemOpEmitter::emitGet() {
 
 #ifdef DEBUG
   state_ = State::Get;
 #endif
   return true;
 }
 
 bool ElemOpEmitter::prepareForRhs() {
-  MOZ_ASSERT(isSimpleAssignment() || isCompoundAssignment());
-  MOZ_ASSERT_IF(isSimpleAssignment(), state_ == State::Key);
+  MOZ_ASSERT(isSimpleAssignment() || isPropInit() || isCompoundAssignment());
+  MOZ_ASSERT_IF(isSimpleAssignment() || isPropInit(), state_ == State::Key);
   MOZ_ASSERT_IF(isCompoundAssignment(), state_ == State::Get);
 
-  if (isSimpleAssignment()) {
+  if (isSimpleAssignment() || isPropInit()) {
     // For CompoundAssignment, SUPERBASE is already emitted by emitGet.
     if (isSuper()) {
       if (!bce_->emitSuperBase()) {
         //          [stack] THIS KEY SUPERBASE
         return false;
       }
     }
   }
@@ -142,17 +142,17 @@ bool ElemOpEmitter::prepareForRhs() {
 #ifdef DEBUG
   state_ = State::Rhs;
 #endif
   return true;
 }
 
 bool ElemOpEmitter::skipObjAndKeyAndRhs() {
   MOZ_ASSERT(state_ == State::Start);
-  MOZ_ASSERT(isSimpleAssignment());
+  MOZ_ASSERT(isSimpleAssignment() || isPropInit());
 
 #ifdef DEBUG
   state_ = State::Rhs;
 #endif
   return true;
 }
 
 bool ElemOpEmitter::emitDelete() {
@@ -191,23 +191,27 @@ bool ElemOpEmitter::emitDelete() {
 
 #ifdef DEBUG
   state_ = State::Delete;
 #endif
   return true;
 }
 
 bool ElemOpEmitter::emitAssignment() {
-  MOZ_ASSERT(isSimpleAssignment() || isCompoundAssignment());
+  MOZ_ASSERT(isSimpleAssignment() || isPropInit() || isCompoundAssignment());
   MOZ_ASSERT(state_ == State::Rhs);
 
+  MOZ_ASSERT_IF(isPropInit(), !isSuper());
+
   JSOp setOp =
-      isSuper()
-          ? bce_->sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER
-          : bce_->sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM;
+      isPropInit()
+          ? JSOP_INITELEM
+          : isSuper() ? bce_->sc->strict() ? JSOP_STRICTSETELEM_SUPER
+                                           : JSOP_SETELEM_SUPER
+                      : bce_->sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM;
   if (!bce_->emitElemOpBase(setOp)) {
     //              [stack] ELEM
     return false;
   }
 
 #ifdef DEBUG
   state_ = State::Assignment;
 #endif
--- a/js/src/frontend/ElemOpEmitter.h
+++ b/js/src/frontend/ElemOpEmitter.h
@@ -123,16 +123,17 @@ class MOZ_STACK_CLASS ElemOpEmitter {
     Call,
     Set,
     Delete,
     PostIncrement,
     PreIncrement,
     PostDecrement,
     PreDecrement,
     SimpleAssignment,
+    PropInit,
     CompoundAssignment
   };
   enum class ObjKind { Super, Other };
 
  private:
   BytecodeEmitter* bce_;
 
   Kind kind_;
@@ -167,16 +168,17 @@ class MOZ_STACK_CLASS ElemOpEmitter {
   // | [PreIncrement]                                           |
   // | [PostDecrement]                                          |
   // | [PreDecrement]                                           |
   // |   emitIncDec +--------+                                  |
   // +------------->| IncDec |                                  |
   // |              +--------+                                  |
   // |                                      +-------------------+
   // | [SimpleAssignment]                   |
+  // | [PropInit]                           |
   // |                        prepareForRhs v  +-----+
   // +--------------------->+-------------->+->| Rhs |-+
   // |                      ^                  +-----+ |
   // |                      |                          |
   // |                      |            +-------------+
   // | [CompoundAssignment] |            |
   // |   emitGet +-----+    |            | emitAssignment +------------+
   // +---------->| Get |----+            +--------------->| Assignment |
@@ -214,16 +216,18 @@ class MOZ_STACK_CLASS ElemOpEmitter {
 
  private:
   MOZ_MUST_USE bool isCall() const { return kind_ == Kind::Call; }
 
   MOZ_MUST_USE bool isSimpleAssignment() const {
     return kind_ == Kind::SimpleAssignment;
   }
 
+  MOZ_MUST_USE bool isPropInit() const { return kind_ == Kind::PropInit; }
+
   MOZ_MUST_USE bool isDelete() const { return kind_ == Kind::Delete; }
 
   MOZ_MUST_USE bool isCompoundAssignment() const {
     return kind_ == Kind::CompoundAssignment;
   }
 
   MOZ_MUST_USE bool isIncDec() const { return isPostIncDec() || isPreIncDec(); }
 
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -357,16 +357,17 @@ restart:
     case ParseNodeKind::RshExpr:
     case ParseNodeKind::UrshExpr:
     case ParseNodeKind::AddExpr:
     case ParseNodeKind::SubExpr:
     case ParseNodeKind::MulExpr:
     case ParseNodeKind::DivExpr:
     case ParseNodeKind::ModExpr:
     case ParseNodeKind::PowExpr:
+    case ParseNodeKind::InitExpr:
     case ParseNodeKind::AssignExpr:
     case ParseNodeKind::AddAssignExpr:
     case ParseNodeKind::SubAssignExpr:
     case ParseNodeKind::BitOrAssignExpr:
     case ParseNodeKind::BitXorAssignExpr:
     case ParseNodeKind::BitAndAssignExpr:
     case ParseNodeKind::LshAssignExpr:
     case ParseNodeKind::RshAssignExpr:
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -811,26 +811,27 @@ class FullParseHandler {
   CallNodeType newNewExpression(uint32_t begin, Node ctor, Node args,
                                 bool isSpread) {
     return new_<CallNode>(ParseNodeKind::NewExpr,
                           isSpread ? JSOP_SPREADNEW : JSOP_NEW,
                           TokenPos(begin, args->pn_pos.end), ctor, args);
   }
 
   AssignmentNodeType newAssignment(ParseNodeKind kind, Node lhs, Node rhs) {
-    if (kind == ParseNodeKind::AssignExpr && lhs->isKind(ParseNodeKind::Name) &&
-        !lhs->isInParens()) {
+    if ((kind == ParseNodeKind::AssignExpr ||
+         kind == ParseNodeKind::InitExpr) &&
+        lhs->isKind(ParseNodeKind::Name) && !lhs->isInParens()) {
       checkAndSetIsDirectRHSAnonFunction(rhs);
     }
 
     return new_<AssignmentNode>(kind, lhs, rhs);
   }
 
   bool isUnparenthesizedAssignment(Node node) {
-    if (node->isKind(ParseNodeKind::AssignExpr) && !node->isInParens()) {
+    if ((node->isKind(ParseNodeKind::AssignExpr)) && !node->isInParens()) {
       return true;
     }
 
     return false;
   }
 
   bool isUnparenthesizedUnaryExpression(Node node) {
     if (!node->isInParens()) {
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -140,16 +140,17 @@ class BigIntBox;
   F(ClassNames, ClassNames)                                                  \
   F(NewTargetExpr, BinaryNode)                                               \
   F(PosHolder, NullaryNode)                                                  \
   F(SuperBase, UnaryNode)                                                    \
   F(SuperCallExpr, BinaryNode)                                               \
   F(SetThis, BinaryNode)                                                     \
   F(ImportMetaExpr, BinaryNode)                                              \
   F(CallImportExpr, BinaryNode)                                              \
+  F(InitExpr, BinaryNode)                                                    \
                                                                              \
   /* Unary operators. */                                                     \
   F(TypeOfNameExpr, UnaryNode)                                               \
   F(TypeOfExpr, UnaryNode)                                                   \
   F(VoidExpr, UnaryNode)                                                     \
   F(NotExpr, UnaryNode)                                                      \
   F(BitNotExpr, UnaryNode)                                                   \
   F(AwaitExpr, UnaryNode)                                                    \
@@ -180,17 +181,17 @@ class BigIntBox;
   F(AddExpr, ListNode)                                                       \
   F(SubExpr, ListNode)                                                       \
   F(MulExpr, ListNode)                                                       \
   F(DivExpr, ListNode)                                                       \
   F(ModExpr, ListNode)                                                       \
   F(PowExpr, ListNode)                                                       \
                                                                              \
   /* Assignment operators (= += -= etc.). */                                 \
-  /* ParseNode::isAssignment assumes all these are consecutive. */           \
+  /* AssignmentNode::test assumes all these are consecutive. */              \
   F(AssignExpr, AssignmentNode)                                              \
   F(AddAssignExpr, AssignmentNode)                                           \
   F(SubAssignExpr, AssignmentNode)                                           \
   F(BitOrAssignExpr, AssignmentNode)                                         \
   F(BitXorAssignExpr, AssignmentNode)                                        \
   F(BitAndAssignExpr, AssignmentNode)                                        \
   F(LshAssignExpr, AssignmentNode)                                           \
   F(RshAssignExpr, AssignmentNode)                                           \
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -7437,30 +7437,30 @@ GeneralParser<ParseHandler, Unit>::field
 
     propAssignFieldAccess =
         handler_.newPropertyAccess(propAssignThis, propAssignName);
     if (!propAssignFieldAccess) {
       return null();
     }
   }
 
-  // Synthesize an assignment expression for the property.
-  AssignmentNodeType initializerAssignment = handler_.newAssignment(
-      ParseNodeKind::AssignExpr, propAssignFieldAccess, initializerExpr);
-  if (!initializerAssignment) {
+  // Synthesize an property init.
+  AssignmentNodeType initializerPropInit = handler_.newAssignment(
+      ParseNodeKind::InitExpr, propAssignFieldAccess, initializerExpr);
+  if (!initializerPropInit) {
     return null();
   }
 
   bool canSkipLazyClosedOverBindings = handler_.canSkipLazyClosedOverBindings();
   if (!pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings)) {
     return null();
   }
 
   UnaryNodeType exprStatement =
-      handler_.newExprStatement(initializerAssignment, wholeInitializerPos.end);
+      handler_.newExprStatement(initializerPropInit, wholeInitializerPos.end);
   if (!exprStatement) {
     return null();
   }
 
   ListNodeType statementList = handler_.newStatementList(wholeInitializerPos);
   if (!argsbody) {
     return null();
   }
--- a/js/src/frontend/PropOpEmitter.cpp
+++ b/js/src/frontend/PropOpEmitter.cpp
@@ -98,21 +98,21 @@ bool PropOpEmitter::emitGet(JSAtom* prop
 
 #ifdef DEBUG
   state_ = State::Get;
 #endif
   return true;
 }
 
 bool PropOpEmitter::prepareForRhs() {
-  MOZ_ASSERT(isSimpleAssignment() || isCompoundAssignment());
-  MOZ_ASSERT_IF(isSimpleAssignment(), state_ == State::Obj);
+  MOZ_ASSERT(isSimpleAssignment() || isPropInit() || isCompoundAssignment());
+  MOZ_ASSERT_IF(isSimpleAssignment() || isPropInit(), state_ == State::Obj);
   MOZ_ASSERT_IF(isCompoundAssignment(), state_ == State::Get);
 
-  if (isSimpleAssignment()) {
+  if (isSimpleAssignment() || isPropInit()) {
     // For CompoundAssignment, SUPERBASE is already emitted by emitGet.
     if (isSuper()) {
       if (!bce_->emitSuperBase()) {
         //          [stack] THIS SUPERBASE
         return false;
       }
     }
   }
@@ -120,17 +120,17 @@ bool PropOpEmitter::prepareForRhs() {
 #ifdef DEBUG
   state_ = State::Rhs;
 #endif
   return true;
 }
 
 bool PropOpEmitter::skipObjAndRhs() {
   MOZ_ASSERT(state_ == State::Start);
-  MOZ_ASSERT(isSimpleAssignment());
+  MOZ_ASSERT(isSimpleAssignment() || isPropInit());
 
 #ifdef DEBUG
   state_ = State::Rhs;
 #endif
   return true;
 }
 
 bool PropOpEmitter::emitDelete(JSAtom* prop) {
@@ -169,29 +169,32 @@ bool PropOpEmitter::emitDelete(JSAtom* p
 
 #ifdef DEBUG
   state_ = State::Delete;
 #endif
   return true;
 }
 
 bool PropOpEmitter::emitAssignment(JSAtom* prop) {
-  MOZ_ASSERT(isSimpleAssignment() || isCompoundAssignment());
+  MOZ_ASSERT(isSimpleAssignment() || isPropInit() || isCompoundAssignment());
   MOZ_ASSERT(state_ == State::Rhs);
 
-  if (isSimpleAssignment()) {
+  if (isSimpleAssignment() || isPropInit()) {
     if (!prepareAtomIndex(prop)) {
       return false;
     }
   }
 
+  MOZ_ASSERT_IF(isPropInit(), !isSuper());
   JSOp setOp =
-      isSuper()
-          ? bce_->sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER
-          : bce_->sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
+      isPropInit()
+          ? JSOP_INITPROP
+          : isSuper() ? bce_->sc->strict() ? JSOP_STRICTSETPROP_SUPER
+                                           : JSOP_SETPROP_SUPER
+                      : bce_->sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
   if (!bce_->emitAtomOp(propAtomIndex_, setOp)) {
     //              [stack] VAL
     return false;
   }
 
 #ifdef DEBUG
   state_ = State::Assignment;
 #endif
--- a/js/src/frontend/PropOpEmitter.h
+++ b/js/src/frontend/PropOpEmitter.h
@@ -107,16 +107,17 @@ class MOZ_STACK_CLASS PropOpEmitter {
     Call,
     Set,
     Delete,
     PostIncrement,
     PreIncrement,
     PostDecrement,
     PreDecrement,
     SimpleAssignment,
+    PropInit,
     CompoundAssignment
   };
   enum class ObjKind { Super, Other };
 
  private:
   BytecodeEmitter* bce_;
 
   Kind kind_;
@@ -156,16 +157,17 @@ class MOZ_STACK_CLASS PropOpEmitter {
   // | [PreIncrement]                       |
   // | [PostDecrement]                      |
   // | [PreDecrement]                       |
   // |   emitIncDec +--------+              |
   // +------------->| IncDec |              |
   // |              +--------+              |
   // |                                      |
   // | [SimpleAssignment]                   |
+  // | [PropInit]                           |
   // |                        prepareForRhs |  +-----+
   // +--------------------->+-------------->+->| Rhs |-+
   // |                      ^                  +-----+ |
   // |                      |                          |
   // |                      |                +---------+
   // | [CompoundAssignment] |                |
   // |   emitGet +-----+    |                | emitAssignment +------------+
   // +---------->| Get |----+                + -------------->| Assignment |
@@ -202,16 +204,18 @@ class MOZ_STACK_CLASS PropOpEmitter {
   MOZ_MUST_USE bool isCall() const { return kind_ == Kind::Call; }
 
   MOZ_MUST_USE bool isSuper() const { return objKind_ == ObjKind::Super; }
 
   MOZ_MUST_USE bool isSimpleAssignment() const {
     return kind_ == Kind::SimpleAssignment;
   }
 
+  MOZ_MUST_USE bool isPropInit() const { return kind_ == Kind::PropInit; }
+
   MOZ_MUST_USE bool isDelete() const { return kind_ == Kind::Delete; }
 
   MOZ_MUST_USE bool isCompoundAssignment() const {
     return kind_ == Kind::CompoundAssignment;
   }
 
   MOZ_MUST_USE bool isIncDec() const { return isPostIncDec() || isPreIncDec(); }
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/fields/initprop.js
@@ -0,0 +1,27 @@
+// |jit-test| --enable-experimental-fields
+
+let called = false
+class base {
+    set x(arg) {
+        called = true;
+    }
+    get x() {
+        called = true;
+        return 0;
+    }
+}
+
+class c extends base {
+    x = 2;
+}
+assertEq(new c().x, 2);
+
+class d extends base {
+    ["x"] = 2;
+}
+assertEq(new d().x, 2);
+
+assertEq(called, false);
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);