@@ -1132,10 +1132,32 @@ class JSCodeGen()(using genCtx: Context) {
11321132 * This is used for the primary constructor of a non-native JS class,
11331133 * because those cannot access `this` before the super constructor call.
11341134 *
1135- * dotc inserts statements before the super constructor call for param
1136- * accessor initializers (including val's and var's declared in the params).
1137- * We move those after the super constructor call, and are therefore
1138- * executed later than for a Scala class.
1135+ * Normally, in Scala, param accessors (i.e., fields declared directly in
1136+ * constructor parameters) are initialized *before* the super constructor
1137+ * call. This is important for cases like
1138+ *
1139+ * abstract class A {
1140+ * def a: Int
1141+ * println(a)
1142+ * }
1143+ * class B(val a: Int) extends A
1144+ *
1145+ * where `a` is supposed to be correctly initialized by the time `println`
1146+ * is executed.
1147+ *
1148+ * However, in a JavaScript class, this is forbidden: it is not allowed to
1149+ * read the `this` value in a constructor before the super constructor call.
1150+ *
1151+ * Therefore, for JavaScript classes, we specifically move all those early
1152+ * assignments after the super constructor call, to comply with JavaScript
1153+ * limitations. This clearly introduces a semantic difference in
1154+ * initialization order between Scala classes and JavaScript classes, but
1155+ * there is nothing we can do about it. That difference in behavior is
1156+ * basically spec'ed in Scala.js the language, since specifying it any other
1157+ * way would prevent JavaScript classes from ever having constructor
1158+ * parameters.
1159+ *
1160+ * We do the same thing in Scala 2, obviously.
11391161 */
11401162 private def moveAllStatementsAfterSuperConstructorCall (body : js.Tree ): js.Tree = {
11411163 val bodyStats = body match {
@@ -1447,7 +1469,7 @@ class JSCodeGen()(using genCtx: Context) {
14471469 val genBoxedRhs = box(genRhs, atPhase(elimErasedValueTypePhase)(sym.info))
14481470 js.Assign (field, genBoxedRhs)
14491471 } else {
1450- js.Assign (field,genRhs)
1472+ js.Assign (field, genRhs)
14511473 }
14521474 }
14531475
@@ -1773,28 +1795,28 @@ class JSCodeGen()(using genCtx: Context) {
17731795 s " but isInnerNonNativeJSClass = $nestedJSClass" )
17741796
17751797 def genArgs : List [js.TreeOrJSSpread ] = genActualJSArgs(ctor, args)
1776-
1777- if (cls == jsdefn.JSObjectClass && args.isEmpty)
1778- js.JSObjectConstr (Nil )
1779- else if (cls == jsdefn.JSArrayClass && args.isEmpty)
1780- js.JSArrayConstr (Nil )
1781- else if (cls.isAnonymousClass)
1782- genNewAnonJSClass(cls, jsClassValue.get, args.map(genExpr))(fun.span)
1783- else if (! nestedJSClass)
1784- js.JSNew (genLoadJSConstructor(cls), genArgs)
1785- else if (! atPhase(erasurePhase)(cls.is(ModuleClass ))) // LambdaLift removes the ModuleClass flag of lifted classes
1786- js.JSNew (jsClassValue.get, genArgs)
1787- else
1788- genCreateInnerJSModule(cls, jsClassValue.get, args.map(genExpr))
1798+ def genArgsAsClassCaptures : List [js.Tree ] = args.map(genExpr)
1799+
1800+ jsClassValue.fold {
1801+ // Static JS class (by construction, it cannot be a module class, as their News do not reach the back-end)
1802+ if (cls == jsdefn.JSObjectClass && args.isEmpty)
1803+ js.JSObjectConstr (Nil )
1804+ else if (cls == jsdefn.JSArrayClass && args.isEmpty)
1805+ js.JSArrayConstr (Nil )
1806+ else
1807+ js.JSNew (genLoadJSConstructor(cls), genArgs)
1808+ } { jsClassVal =>
1809+ // Nested JS class
1810+ if (cls.isAnonymousClass)
1811+ genNewAnonJSClass(cls, jsClassVal, genArgsAsClassCaptures)(fun.span)
1812+ else if (atPhase(erasurePhase)(cls.is(ModuleClass ))) // LambdaLift removes the ModuleClass flag of lifted classes
1813+ js.JSNew (js.CreateJSClass (encodeClassName(cls), jsClassVal :: genArgsAsClassCaptures), Nil )
1814+ else
1815+ js.JSNew (jsClassVal, genArgs)
1816+ }
17891817 }
17901818 }
17911819
1792- /** Gen JS code to create the JS class of an inner JS module class. */
1793- private def genCreateInnerJSModule (sym : Symbol , jsSuperClassValue : js.Tree , args : List [js.Tree ])(
1794- implicit pos : Position ): js.Tree = {
1795- js.JSNew (js.CreateJSClass (encodeClassName(sym), jsSuperClassValue :: args), Nil )
1796- }
1797-
17981820 /** Generate an instance of an anonymous (non-lambda) JS class inline
17991821 *
18001822 * @param sym Class to generate the instance of
0 commit comments