@@ -1146,42 +1146,72 @@ class JSCodeGen()(using genCtx: Context) {
1146
1146
1147
1147
private def genPrimaryJSClassCtor (dd : DefDef ): PrimaryJSCtor = {
1148
1148
val sym = dd.symbol
1149
- val Block (stats, _) = dd.rhs: @ unchecked
1150
1149
assert(sym.isPrimaryConstructor, s " called with non-primary ctor: $sym" )
1151
1150
1151
+ var preSuperStats = List .newBuilder[js.Tree ]
1152
1152
var jsSuperCall : Option [js.JSSuperConstructorCall ] = None
1153
- val jsStats = List .newBuilder[js.Tree ]
1153
+ val postSuperStats = List .newBuilder[js.Tree ]
1154
1154
1155
- /* Move all statements after the super constructor call since JS
1156
- * cannot access `this` before the super constructor call.
1155
+ /* Move param accessor initializers after the super constructor call since
1156
+ * JS cannot access `this` before the super constructor call.
1157
1157
*
1158
1158
* dotc inserts statements before the super constructor call for param
1159
1159
* accessor initializers (including val's and var's declared in the
1160
- * params). We move those after the super constructor call, and are
1161
- * therefore executed later than for a Scala class.
1160
+ * params). Those statements are assignments whose rhs'es are always simple
1161
+ * Idents (the constructor params).
1162
+ *
1163
+ * There can also be local `val`s before the super constructor call for
1164
+ * default arguments to the super constructor. These must remain before.
1165
+ *
1166
+ * Our strategy is therefore to move only the field assignments after the
1167
+ * super constructor call. They are therefore executed later than for a
1168
+ * Scala class (as specified for non-native JS classes semantics).
1169
+ * However, side effects and evaluation order of all the other
1170
+ * computations remains unchanged.
1162
1171
*/
1163
1172
withPerMethodBodyState(sym) {
1164
- stats.foreach {
1165
- case tree @ Apply (fun @ Select (Super (This (_), _), _), args)
1166
- if fun.symbol.isClassConstructor =>
1167
- assert(jsSuperCall.isEmpty, s " Found 2 JS Super calls at ${dd.sourcePos}" )
1168
- implicit val pos : Position = tree.span
1169
- jsSuperCall = Some (js.JSSuperConstructorCall (genActualJSArgs(fun.symbol, args)))
1173
+ def isThisField (tree : Tree ): Boolean = tree match {
1174
+ case Select (ths : This , _) => ths.symbol == currentClassSym.get
1175
+ case tree : Ident => desugarIdent(tree).exists(isThisField(_))
1176
+ case _ => false
1177
+ }
1170
1178
1171
- case stat =>
1172
- val jsStat = genStat(stat)
1173
- assert(jsSuperCall.isDefined || ! jsStat.isInstanceOf [js.VarDef ],
1174
- " Trying to move a local VarDef after the super constructor call of a non-native JS class at " +
1175
- dd.sourcePos)
1176
- jsStats += jsStat
1179
+ def rec (tree : Tree ): Unit = {
1180
+ tree match {
1181
+ case Block (stats, expr) =>
1182
+ stats.foreach(rec(_))
1183
+ rec(expr)
1184
+
1185
+ case tree @ Apply (fun @ Select (Super (This (_), _), _), args)
1186
+ if fun.symbol.isClassConstructor =>
1187
+ assert(jsSuperCall.isEmpty, s " Found 2 JS Super calls at ${dd.sourcePos}" )
1188
+ implicit val pos : Position = tree.span
1189
+ jsSuperCall = Some (js.JSSuperConstructorCall (genActualJSArgs(fun.symbol, args)))
1190
+
1191
+ case tree if jsSuperCall.isDefined =>
1192
+ // Once we're past the super constructor call, everything goes after.
1193
+ postSuperStats += genStat(tree)
1194
+
1195
+ case Assign (lhs, Ident (_)) if isThisField(lhs) =>
1196
+ /* If that shape appears before the jsSuperCall, it is a param
1197
+ * accessor initializer. We move it.
1198
+ */
1199
+ postSuperStats += genStat(tree)
1200
+
1201
+ case stat =>
1202
+ // Other statements are left before.
1203
+ preSuperStats += genStat(stat)
1204
+ }
1177
1205
}
1206
+
1207
+ rec(dd.rhs)
1178
1208
}
1179
1209
1180
1210
assert(jsSuperCall.isDefined,
1181
1211
s " Did not find Super call in primary JS construtor at ${dd.sourcePos}" )
1182
1212
1183
1213
new PrimaryJSCtor (sym, genParamsAndInfo(sym, dd.paramss),
1184
- js.JSConstructorBody (Nil , jsSuperCall.get, jsStats .result())(dd.span))
1214
+ js.JSConstructorBody (preSuperStats.result() , jsSuperCall.get, postSuperStats .result())(dd.span))
1185
1215
}
1186
1216
1187
1217
private def genSecondaryJSClassCtor (dd : DefDef ): SplitSecondaryJSCtor = {
0 commit comments