@@ -1067,7 +1067,8 @@ class JSCodeGen()(using genCtx: Context) {
10671067 val jsClassCaptures = List .newBuilder[js.ParamDef ]
10681068
10691069 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)
10711072 exports += e
10721073 jsClassCaptures ++= c
10731074 tree.subCtors.foreach(add(_))
@@ -1133,9 +1134,7 @@ class JSCodeGen()(using genCtx: Context) {
11331134 assert(jsSuperCall.isDefined,
11341135 s " Did not find Super call in primary JS construtor at ${dd.sourcePos}" )
11351136
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())
11391138 }
11401139
11411140 private def genSecondaryJSClassCtor (dd : DefDef ): SplitSecondaryJSCtor = {
@@ -1176,19 +1175,24 @@ class JSCodeGen()(using genCtx: Context) {
11761175 i " could not find the this() call in secondary JS constructor at ${dd.sourcePos}: \n ${stats.map(_.show).mkString(" \n " )}" )
11771176 val Some ((targetCtor, ctorArgs)) = thisCall
11781177
1179- val params = dd.paramss.flatten.map(_.symbol)
1178+ new SplitSecondaryJSCtor (sym, genParamsAndInfo(sym, dd.paramss),
1179+ beforeThisCall.result(), targetCtor, ctorArgs, afterThisCall.result())
1180+ }
11801181
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)
11831188 }
11841189
1185- private def genJSClassCtorDispatch (ctorSym : Symbol , allParamSyms : List [Symbol ],
1190+ private def genJSClassCtorDispatch (ctorSym : Symbol ,
1191+ allParamsAndInfos : List [(Symbol , JSParamInfo )],
11861192 overloadNum : Int ): (jsExportsGen.Exported , List [js.ParamDef ]) = {
11871193
11881194 implicit val pos : SourcePosition = ctorSym.sourcePos
11891195
1190- val allParamsAndInfos = allParamSyms.zip(ctorSym.jsParamInfos)
1191-
11921196 /* `allParams` are the parameters as seen from inside the constructor body,
11931197 * i.e., the ones generated by the trees in the constructor body.
11941198 */
@@ -1266,40 +1270,58 @@ class JSCodeGen()(using genCtx: Context) {
12661270 */
12671271
12681272 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) )
12711275
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)
12731278
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)
12861281
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)
12931286 }
12941287
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
12981290 } 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)
13001321 }
13011322
1302- ifOverload(tree, js.Block (inner ++ tree.ctor.beforeCall ++ assignments))
1323+ ifOverload(tree, js.Block (
1324+ inner ++ tree.ctor.beforeCall ++ captureAssigns ++ normalAssigns))
13031325 }
13041326
13051327 def postStats (tree : ConstructorTree [SplitSecondaryJSCtor ]): js.Tree = {
@@ -1311,22 +1333,24 @@ class JSCodeGen()(using genCtx: Context) {
13111333 val secondaryCtorTrees = ctorTree.subCtors
13121334
13131335 js.Block (
1314- secondaryCtorTrees.map(preStats(_, primaryCtor.params )) ++
1336+ secondaryCtorTrees.map(preStats(_, primaryCtor.paramsAndInfo )) ++
13151337 primaryCtor.body ++
13161338 secondaryCtorTrees.map(postStats(_))
13171339 )
13181340 }
13191341
13201342 private sealed trait JSCtor {
13211343 val sym : Symbol
1322- val params : List [Symbol ]
1344+ val paramsAndInfo : List [( Symbol , JSParamInfo ) ]
13231345 }
13241346
13251347 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
13271350
13281351 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 ],
13301354 val targetCtor : Symbol , val ctorArgs : List [js.Tree ],
13311355 val afterCall : List [js.Tree ]) extends JSCtor
13321356
0 commit comments