@@ -1067,7 +1067,8 @@ class JSCodeGen()(using genCtx: Context) {
1067
1067
val jsClassCaptures = List .newBuilder[js.ParamDef ]
1068
1068
1069
1069
def add (tree : ConstructorTree [_ <: JSCtor ]): Unit = {
1070
- val (e, c) = genJSClassCtorDispatch(tree.ctor.sym, tree.ctor.params, tree.overloadNum)
1070
+ val (e, c) = genJSClassCtorDispatch(tree.ctor.sym,
1071
+ tree.ctor.paramsAndInfo, tree.overloadNum)
1071
1072
exports += e
1072
1073
jsClassCaptures ++= c
1073
1074
tree.subCtors.foreach(add(_))
@@ -1133,9 +1134,7 @@ class JSCodeGen()(using genCtx: Context) {
1133
1134
assert(jsSuperCall.isDefined,
1134
1135
s " Did not find Super call in primary JS construtor at ${dd.sourcePos}" )
1135
1136
1136
- val params = dd.paramss.flatten.map(_.symbol)
1137
-
1138
- new PrimaryJSCtor (sym, params, jsSuperCall.get :: jsStats.result())
1137
+ new PrimaryJSCtor (sym, genParamsAndInfo(sym, dd.paramss), jsSuperCall.get :: jsStats.result())
1139
1138
}
1140
1139
1141
1140
private def genSecondaryJSClassCtor (dd : DefDef ): SplitSecondaryJSCtor = {
@@ -1176,19 +1175,24 @@ class JSCodeGen()(using genCtx: Context) {
1176
1175
i " could not find the this() call in secondary JS constructor at ${dd.sourcePos}: \n ${stats.map(_.show).mkString(" \n " )}" )
1177
1176
val Some ((targetCtor, ctorArgs)) = thisCall
1178
1177
1179
- val params = dd.paramss.flatten.map(_.symbol)
1178
+ new SplitSecondaryJSCtor (sym, genParamsAndInfo(sym, dd.paramss),
1179
+ beforeThisCall.result(), targetCtor, ctorArgs, afterThisCall.result())
1180
+ }
1180
1181
1181
- new SplitSecondaryJSCtor (sym, params, beforeThisCall.result(), targetCtor,
1182
- ctorArgs, afterThisCall.result())
1182
+ private def genParamsAndInfo (ctorSym : Symbol ,
1183
+ vparamss : List [ParamClause ]): List [(Symbol , JSParamInfo )] = {
1184
+ implicit val pos : SourcePosition = ctorSym.sourcePos
1185
+
1186
+ val paramSyms = if (vparamss.isEmpty) Nil else vparamss.head.map(_.symbol)
1187
+ paramSyms.zip(ctorSym.jsParamInfos)
1183
1188
}
1184
1189
1185
- private def genJSClassCtorDispatch (ctorSym : Symbol , allParamSyms : List [Symbol ],
1190
+ private def genJSClassCtorDispatch (ctorSym : Symbol ,
1191
+ allParamsAndInfos : List [(Symbol , JSParamInfo )],
1186
1192
overloadNum : Int ): (jsExportsGen.Exported , List [js.ParamDef ]) = {
1187
1193
1188
1194
implicit val pos : SourcePosition = ctorSym.sourcePos
1189
1195
1190
- val allParamsAndInfos = allParamSyms.zip(ctorSym.jsParamInfos)
1191
-
1192
1196
/* `allParams` are the parameters as seen from inside the constructor body,
1193
1197
* i.e., the ones generated by the trees in the constructor body.
1194
1198
*/
@@ -1266,40 +1270,58 @@ class JSCodeGen()(using genCtx: Context) {
1266
1270
*/
1267
1271
1268
1272
def preStats (tree : ConstructorTree [SplitSecondaryJSCtor ],
1269
- nextParams : List [Symbol ]): js.Tree = {
1270
- assert(tree.ctor.ctorArgs.size == nextParams.size, " param count mismatch " )
1273
+ nextParamsAndInfo : List [( Symbol , JSParamInfo ) ]): js.Tree = {
1274
+ val inner = tree.subCtors.map(preStats(_, tree.ctor.paramsAndInfo) )
1271
1275
1272
- val inner = tree.subCtors.map(preStats(_, tree.ctor.params))
1276
+ assert(tree.ctor.ctorArgs.size == nextParamsAndInfo.size, " param count mismatch" )
1277
+ val paramsInfosAndArgs = nextParamsAndInfo.zip(tree.ctor.ctorArgs)
1273
1278
1274
- /* Reject undefined params (i.e. using a default value of another
1275
- * constructor) via implementation restriction.
1276
- *
1277
- * This is mostly for historical reasons. The ideal solution here would
1278
- * be to recognize calls to default param getters of JS class
1279
- * constructors and not even translate them to UndefinedParam in the
1280
- * first place.
1281
- */
1282
- def isUndefinedParam (tree : js.Tree ): Boolean = tree match {
1283
- case js.Transient (UndefinedParam ) => true
1284
- case _ => false
1285
- }
1279
+ val (captureParamsInfosAndArgs, normalParamsInfosAndArgs) =
1280
+ paramsInfosAndArgs.partition(_._1._2.capture)
1286
1281
1287
- if (tree.ctor.ctorArgs.exists(isUndefinedParam)) {
1288
- report.error(
1289
- " Implementation restriction: " +
1290
- " in a JS class, a secondary constructor calling another constructor " +
1291
- " with default parameters must provide the values of all parameters." ,
1292
- tree.ctor.sym.sourcePos)
1282
+ val captureAssigns = for {
1283
+ ((param, _), arg) <- captureParamsInfosAndArgs
1284
+ } yield {
1285
+ js.Assign (genVarRef(param), arg)
1293
1286
}
1294
1287
1295
- val assignments = for {
1296
- (param, arg) <- nextParams.zip(tree.ctor.ctorArgs)
1297
- if ! isUndefinedParam(arg)
1288
+ val normalAssigns = for {
1289
+ (((param, info), arg), i) <- normalParamsInfosAndArgs.zipWithIndex
1298
1290
} yield {
1299
- js.Assign (genVarRef(param), arg)
1291
+ val newArg = arg match {
1292
+ case js.Transient (UndefinedParam ) =>
1293
+ /* Go full circle: We have ignored the default param getter for
1294
+ * this, we'll create it again.
1295
+ *
1296
+ * This seems not optimal: We could simply not ignore the calls to
1297
+ * default param getters in the first place.
1298
+ *
1299
+ * However, this proves to be difficult: Because of translations in
1300
+ * earlier phases, calls to default param getters may be assigned
1301
+ * to temporary variables first (see the undefinedDefaultParams
1302
+ * ScopedVar). If this happens, it becomes increasingly difficult
1303
+ * to distinguish a default param getter call for a constructor
1304
+ * call of *this* instance (in which case we would want to keep
1305
+ * the default param getter call) from one for a *different*
1306
+ * instance (in which case we would want to discard the default
1307
+ * param getter call)
1308
+ *
1309
+ * Because of this, it ends up being easier to just re-create the
1310
+ * default param getter call if necessary.
1311
+ */
1312
+ implicit val pos : SourcePosition = tree.ctor.sym.sourcePos
1313
+ jsExportsGen.genCallDefaultGetter(tree.ctor.sym, i, static = false ,
1314
+ captures = captureParamsInfosAndArgs.map(p => genVarRef(p._1._1)))(
1315
+ prevArgsCount => normalParamsInfosAndArgs.take(prevArgsCount).map(p => genVarRef(p._1._1)))
1316
+
1317
+ case arg => arg
1318
+ }
1319
+
1320
+ js.Assign (genVarRef(param), newArg)
1300
1321
}
1301
1322
1302
- ifOverload(tree, js.Block (inner ++ tree.ctor.beforeCall ++ assignments))
1323
+ ifOverload(tree, js.Block (
1324
+ inner ++ tree.ctor.beforeCall ++ captureAssigns ++ normalAssigns))
1303
1325
}
1304
1326
1305
1327
def postStats (tree : ConstructorTree [SplitSecondaryJSCtor ]): js.Tree = {
@@ -1311,22 +1333,24 @@ class JSCodeGen()(using genCtx: Context) {
1311
1333
val secondaryCtorTrees = ctorTree.subCtors
1312
1334
1313
1335
js.Block (
1314
- secondaryCtorTrees.map(preStats(_, primaryCtor.params )) ++
1336
+ secondaryCtorTrees.map(preStats(_, primaryCtor.paramsAndInfo )) ++
1315
1337
primaryCtor.body ++
1316
1338
secondaryCtorTrees.map(postStats(_))
1317
1339
)
1318
1340
}
1319
1341
1320
1342
private sealed trait JSCtor {
1321
1343
val sym : Symbol
1322
- val params : List [Symbol ]
1344
+ val paramsAndInfo : List [( Symbol , JSParamInfo ) ]
1323
1345
}
1324
1346
1325
1347
private class PrimaryJSCtor (val sym : Symbol ,
1326
- val params : List [Symbol ], val body : List [js.Tree ]) extends JSCtor
1348
+ val paramsAndInfo : List [(Symbol , JSParamInfo )],
1349
+ val body : List [js.Tree ]) extends JSCtor
1327
1350
1328
1351
private class SplitSecondaryJSCtor (val sym : Symbol ,
1329
- val params : List [Symbol ], val beforeCall : List [js.Tree ],
1352
+ val paramsAndInfo : List [(Symbol , JSParamInfo )],
1353
+ val beforeCall : List [js.Tree ],
1330
1354
val targetCtor : Symbol , val ctorArgs : List [js.Tree ],
1331
1355
val afterCall : List [js.Tree ]) extends JSCtor
1332
1356
0 commit comments