@@ -1132,10 +1132,32 @@ class JSCodeGen()(using genCtx: Context) {
1132
1132
* This is used for the primary constructor of a non-native JS class,
1133
1133
* because those cannot access `this` before the super constructor call.
1134
1134
*
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.
1139
1161
*/
1140
1162
private def moveAllStatementsAfterSuperConstructorCall (body : js.Tree ): js.Tree = {
1141
1163
val bodyStats = body match {
@@ -1447,7 +1469,7 @@ class JSCodeGen()(using genCtx: Context) {
1447
1469
val genBoxedRhs = box(genRhs, atPhase(elimErasedValueTypePhase)(sym.info))
1448
1470
js.Assign (field, genBoxedRhs)
1449
1471
} else {
1450
- js.Assign (field,genRhs)
1472
+ js.Assign (field, genRhs)
1451
1473
}
1452
1474
}
1453
1475
@@ -1773,28 +1795,28 @@ class JSCodeGen()(using genCtx: Context) {
1773
1795
s " but isInnerNonNativeJSClass = $nestedJSClass" )
1774
1796
1775
1797
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
+ }
1789
1817
}
1790
1818
}
1791
1819
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
-
1798
1820
/** Generate an instance of an anonymous (non-lambda) JS class inline
1799
1821
*
1800
1822
* @param sym Class to generate the instance of
0 commit comments