Bug 1542448 - Copy .initializers to .localInitializers for derived classes. r=jorendorff
authorAshley Hauck <khyperia@mozilla.com>
Thu, 11 Apr 2019 23:07:06 +0000
changeset 469150 d11fc84ce16f489bac63f4055ddd484aa9435589
parent 469149 83ab9bf5138e6e4a4d1ab32fa5ae672593141dfc
child 469151 53309e75d9a0ab4016281765d655fccc29662add
push id35856
push usercsabou@mozilla.com
push dateFri, 12 Apr 2019 03:19:48 +0000
treeherdermozilla-central@940684cd1065 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1542448
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 1542448 - Copy .initializers to .localInitializers for derived classes. r=jorendorff Differential Revision: https://phabricator.services.mozilla.com/D26967
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/FunctionEmitter.cpp
js/src/frontend/Parser.cpp
js/src/jit-test/tests/fields/super.js
js/src/vm/CommonPropertyNames.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2362,17 +2362,17 @@ bool BytecodeEmitter::emitSetThis(Binary
     //              [stack] NEWTHIS
     return false;
   }
   if (!noe.emitAssignment()) {
     //              [stack] NEWTHIS
     return false;
   }
 
-  if (!emitInitializeInstanceFields()) {
+  if (!emitInitializeInstanceFields(IsSuperCall::Yes)) {
     return false;
   }
 
   return true;
 }
 
 bool BytecodeEmitter::defineHoistedTopLevelFunctions(ParseNode* body) {
   MOZ_ASSERT(inPrologue());
@@ -8100,27 +8100,65 @@ const FieldInitializers& BytecodeEmitter
         return fieldInitializers;
       }
     }
   }
 
   MOZ_CRASH("Constructor for field initializers not found.");
 }
 
-bool BytecodeEmitter::emitInitializeInstanceFields() {
+bool BytecodeEmitter::emitCopyInitializersToLocalInitializers() {
+  MOZ_ASSERT(sc->asFunctionBox()->isDerivedClassConstructor());
+  if (getFieldInitializers().numFieldInitializers == 0) {
+    return true;
+  }
+
+  NameOpEmitter noe(this, cx->names().dotLocalInitializers,
+                    NameOpEmitter::Kind::Initialize);
+  if (!noe.prepareForRhs()) {
+    //              [stack]
+    return false;
+  }
+
+  if (!emitGetName(cx->names().dotInitializers)) {
+    //              [stack] .initializers
+    return false;
+  }
+
+  if (!noe.emitAssignment()) {
+    //              [stack] .initializers
+    return false;
+  }
+
+  if (!emit1(JSOP_POP)) {
+    //              [stack]
+    return false;
+  }
+
+  return true;
+}
+
+bool BytecodeEmitter::emitInitializeInstanceFields(IsSuperCall isSuperCall) {
   const FieldInitializers& fieldInitializers = findFieldInitializersForCall();
   size_t numFields = fieldInitializers.numFieldInitializers;
 
   if (numFields == 0) {
     return true;
   }
 
-  if (!emitGetName(cx->names().dotInitializers)) {
-    //              [stack] ARRAY
-    return false;
+  if (isSuperCall == IsSuperCall::Yes) {
+    if (!emitGetName(cx->names().dotLocalInitializers)) {
+      //            [stack] ARRAY
+      return false;
+    }
+  } else {
+    if (!emitGetName(cx->names().dotInitializers)) {
+      //            [stack] ARRAY
+      return false;
+    }
   }
 
   for (size_t fieldIndex = 0; fieldIndex < numFields; fieldIndex++) {
     if (fieldIndex < numFields - 1) {
       // We DUP to keep the array around (it is consumed in the bytecode below)
       // for next iterations of this loop, except for the last iteration, which
       // avoids an extra POP at the end of the loop.
       if (!emit1(JSOP_DUP)) {
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -774,17 +774,19 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
 
   MOZ_MUST_USE bool emitPropertyList(ListNode* obj, PropertyEmitter& pe,
                                      PropListType type);
 
   FieldInitializers setupFieldInitializers(ListNode* classMembers);
   MOZ_MUST_USE bool emitCreateFieldKeys(ListNode* obj);
   MOZ_MUST_USE bool emitCreateFieldInitializers(ListNode* obj);
   const FieldInitializers& findFieldInitializersForCall();
-  MOZ_MUST_USE bool emitInitializeInstanceFields();
+  MOZ_MUST_USE bool emitCopyInitializersToLocalInitializers();
+  enum class IsSuperCall : bool { No, Yes };
+  MOZ_MUST_USE bool emitInitializeInstanceFields(IsSuperCall isSuperCall);
 
   // To catch accidental misuse, emitUint16Operand/emit3 assert that they are
   // not used to unconditionally emit JSOP_GETLOCAL. Variable access should
   // instead be emitted using EmitVarOp. In special cases, when the caller
   // definitely knows that a given local slot is unaliased, this function may be
   // used as a non-asserting version of emitUint16Operand.
   MOZ_MUST_USE bool emitLocalOp(JSOp op, uint32_t slot);
 
--- a/js/src/frontend/FunctionEmitter.cpp
+++ b/js/src/frontend/FunctionEmitter.cpp
@@ -471,21 +471,28 @@ bool FunctionScriptEmitter::prepareForBo
 
   if (funbox_->needsPromiseResult()) {
     if (!emitAsyncFunctionRejectPrologue()) {
       return false;
     }
   }
 
   if (funbox_->function()->kind() ==
-          JSFunction::FunctionKind::ClassConstructor &&
-      !funbox_->isDerivedClassConstructor()) {
-    if (!bce_->emitInitializeInstanceFields()) {
-      //            [stack]
-      return false;
+      JSFunction::FunctionKind::ClassConstructor) {
+    if (funbox_->isDerivedClassConstructor()) {
+      if (!bce_->emitCopyInitializersToLocalInitializers()) {
+        //          [stack]
+        return false;
+      }
+    } else {
+      if (!bce_->emitInitializeInstanceFields(
+              BytecodeEmitter::IsSuperCall::No)) {
+        //          [stack]
+        return false;
+      }
     }
   }
 
 #ifdef DEBUG
   state_ = State::Body;
 #endif
   return true;
 }
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1929,16 +1929,23 @@ GeneralParser<ParseHandler, Unit>::funct
                                              canSkipLazyClosedOverBindings)) {
       return null();
     }
     if (!pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings)) {
       return null();
     }
   }
 
+  if (kind == FunctionSyntaxKind::DerivedClassConstructor) {
+    if (!noteDeclaredName(cx_->names().dotLocalInitializers,
+                          DeclarationKind::Var, pos())) {
+      return null();
+    }
+  }
+
   return finishLexicalScope(pc_->varScope(), body);
 }
 
 JSFunction* AllocNewFunction(JSContext* cx, HandleAtom atom,
                              FunctionSyntaxKind kind,
                              GeneratorKind generatorKind,
                              FunctionAsyncKind asyncKind, HandleObject proto,
                              bool isSelfHosting /* = false */,
@@ -7052,17 +7059,17 @@ GeneralParser<ParseHandler, Unit>::class
       // So, instead, at the end of class parsing (where we are now), we do some
       // tricks to pretend that noteUsedName(".initializers") was called in the
       // constructor.
       if (!usedNames_.markAsAlwaysClosedOver(cx_, cx_->names().dotInitializers,
                                              pc_->scriptId(),
                                              pc_->innermostScope()->id())) {
         return null();
       }
-      if (!noteDeclaredName(cx_->names().dotInitializers, DeclarationKind::Var,
+      if (!noteDeclaredName(cx_->names().dotInitializers, DeclarationKind::Let,
                             namePos)) {
         return null();
       }
     }
 
     if (numFieldKeys > 0) {
       if (!noteDeclaredName(cx_->names().dotFieldKeys, DeclarationKind::Var,
                             namePos)) {
@@ -7190,16 +7197,23 @@ GeneralParser<ParseHandler, Unit>::synth
 
   if (!noteUsedName(cx_->names().dotThis)) {
     return null();
   }
 
   // One might expect a noteUsedName(".initializers") here. See comment in
   // GeneralParser<ParseHandler, Unit>::classDefinition on why it's not here.
 
+  if (hasHeritage == HasHeritage::Yes) {
+    if (!noteDeclaredName(cx_->names().dotLocalInitializers,
+                          DeclarationKind::Var, synthesizedBodyPos)) {
+      return null();
+    }
+  }
+
   bool canSkipLazyClosedOverBindings = handler_.canSkipLazyClosedOverBindings();
   if (!pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings)) {
     return null();
   }
 
   if (hasHeritage == HasHeritage::Yes) {
     NameNodeType thisName = newThisName();
     if (!thisName) {
@@ -7222,16 +7236,20 @@ GeneralParser<ParseHandler, Unit>::synth
       return null();
     }
 
     BinaryNodeType setThis = handler_.newSetThis(thisName, superCall);
     if (!setThis) {
       return null();
     }
 
+    if (!noteUsedName(cx_->names().dotLocalInitializers)) {
+      return null();
+    }
+
     UnaryNodeType exprStatement =
         handler_.newExprStatement(setThis, synthesizedBodyPos.end);
     if (!exprStatement) {
       return null();
     }
 
     handler_.addStatementToList(stmtList, exprStatement);
   }
@@ -8987,16 +9005,20 @@ typename ParseHandler::Node GeneralParse
         if (!thisName) {
           return null();
         }
 
         nextMember = handler_.newSetThis(thisName, nextMember);
         if (!nextMember) {
           return null();
         }
+
+        if (!noteUsedName(cx_->names().dotLocalInitializers)) {
+          return null();
+        }
       } else {
         if (options().selfHostingMode && handler_.isPropertyAccess(lhs)) {
           error(JSMSG_SELFHOSTED_METHOD_CALL);
           return null();
         }
 
         JSOp op = JSOP_CALL;
         bool maybeAsyncArrow = false;
--- a/js/src/jit-test/tests/fields/super.js
+++ b/js/src/jit-test/tests/fields/super.js
@@ -40,8 +40,18 @@ new G();
 
 class H extends Base {
   field = 2;
   constructor() {
     eval("super()");
   }
 };
 new H();
+
+class I extends Base {
+  field = 2;
+  constructor() {
+      class Tmp {
+          [super()];
+      }
+  }
+};
+new I();
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -116,16 +116,17 @@
   MACRO(direction, direction, "direction")                                     \
   MACRO(displayURL, displayURL, "displayURL")                                  \
   MACRO(do, do_, "do")                                                         \
   MACRO(domNode, domNode, "domNode")                                           \
   MACRO(done, done, "done")                                                    \
   MACRO(dotGenerator, dotGenerator, ".generator")                              \
   MACRO(dotThis, dotThis, ".this")                                             \
   MACRO(dotInitializers, dotInitializers, ".initializers")                     \
+  MACRO(dotLocalInitializers, dotLocalInitializers, ".localInitializers")      \
   MACRO(dotFieldKeys, dotFieldKeys, ".fieldKeys")                              \
   MACRO(each, each, "each")                                                    \
   MACRO(elementType, elementType, "elementType")                               \
   MACRO(else, else_, "else")                                                   \
   MACRO(empty, empty, "")                                                      \
   MACRO(emptyRegExp, emptyRegExp, "(?:)")                                      \
   MACRO(encodeURI, encodeURI, "encodeURI")                                     \
   MACRO(encodeURIComponent, encodeURIComponent, "encodeURIComponent")          \