Skip to content

Commit 5c91a61

Browse files
committed
Scala.js: Properly call default param getters for nested JS ctors
Forward port of scala-js/scala-js@491176f
1 parent a6097e3 commit 5c91a61

File tree

2 files changed

+48
-15
lines changed

2 files changed

+48
-15
lines changed

compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,9 +1140,14 @@ class JSCodeGen()(using genCtx: Context) {
11401140

11411141
private def genSecondaryJSClassCtor(dd: DefDef): SplitSecondaryJSCtor = {
11421142
val sym = dd.symbol
1143-
val Block(stats, _) = dd.rhs
11441143
assert(!sym.isPrimaryConstructor, s"called with primary ctor $sym")
11451144

1145+
def flattenBlocks(t: Tree): List[Tree] = t match {
1146+
case Block(stats, expr) => (stats :+ expr).flatMap(flattenBlocks)
1147+
case _ => t :: Nil
1148+
}
1149+
val stats = flattenBlocks(dd.rhs)
1150+
11461151
val beforeThisCall = List.newBuilder[js.Tree]
11471152
var thisCall: Option[(Symbol, List[js.Tree])] = None
11481153
val afterThisCall = List.newBuilder[js.Tree]
@@ -1167,6 +1172,8 @@ class JSCodeGen()(using genCtx: Context) {
11671172
}
11681173
}
11691174

1175+
assert(thisCall.isDefined,
1176+
i"could not find the this() call in secondary JS constructor at ${dd.sourcePos}:\n${stats.map(_.show).mkString("\n")}")
11701177
val Some((targetCtor, ctorArgs)) = thisCall
11711178

11721179
val params = dd.paramss.flatten.map(_.symbol)
@@ -1175,16 +1182,18 @@ class JSCodeGen()(using genCtx: Context) {
11751182
ctorArgs, afterThisCall.result())
11761183
}
11771184

1178-
private def genJSClassCtorDispatch(sym: Symbol, allParams: List[Symbol],
1185+
private def genJSClassCtorDispatch(ctorSym: Symbol, allParamSyms: List[Symbol],
11791186
overloadNum: Int): (jsExportsGen.Exported, List[js.ParamDef]) = {
11801187

1181-
implicit val pos: SourcePosition = sym.sourcePos
1188+
implicit val pos: SourcePosition = ctorSym.sourcePos
1189+
1190+
val allParamsAndInfos = allParamSyms.zip(ctorSym.jsParamInfos)
11821191

11831192
/* `allParams` are the parameters as seen from inside the constructor body,
11841193
* i.e., the ones generated by the trees in the constructor body.
11851194
*/
11861195
val (captureParamsAndInfos, normalParamsAndInfos) =
1187-
allParams.zip(sym.jsParamInfos).partition(_._2.capture)
1196+
allParamsAndInfos.partition(_._2.capture)
11881197

11891198
/* For class captures, we need to generate different names than the ones
11901199
* used by the constructor body. This is necessary so that we can forward
@@ -1203,13 +1212,14 @@ class JSCodeGen()(using genCtx: Context) {
12031212

12041213
val normalInfos = normalParamsAndInfos.map(_._2).toIndexedSeq
12051214

1206-
val jsExport = new jsExportsGen.Exported(sym, normalInfos) {
1215+
val jsExport = new jsExportsGen.Exported(ctorSym, normalInfos) {
12071216
def genBody(formalArgsRegistry: jsExportsGen.FormalArgsRegistry): js.Tree = {
12081217
val paramAssigns = for {
12091218
((param, info), i) <- normalParamsAndInfos.zipWithIndex
12101219
} yield {
1211-
val rhs = jsExportsGen.genScalaArg(this, i, formalArgsRegistry, info, static = true)(
1212-
prevArgsCount => allParams.take(prevArgsCount).map(genVarRef(_)))
1220+
val rhs = jsExportsGen.genScalaArg(this, i, formalArgsRegistry, info, static = true,
1221+
captures = captureParamsAndInfos.map(pi => genVarRef(pi._1)))(
1222+
prevArgsCount => normalParamsAndInfos.take(prevArgsCount).map(pi => genVarRef(pi._1)))
12131223

12141224
js.Assign(genVarRef(param), rhs)
12151225
}
@@ -3511,7 +3521,7 @@ class JSCodeGen()(using genCtx: Context) {
35113521
}
35123522

35133523
/** Gen a statically linked call to an instance method. */
3514-
private def genApplyMethodMaybeStatically(receiver: js.Tree, method: Symbol,
3524+
def genApplyMethodMaybeStatically(receiver: js.Tree, method: Symbol,
35153525
arguments: List[js.Tree])(implicit pos: Position): js.Tree = {
35163526
if (method.isPrivate || method.isClassConstructor)
35173527
genApplyMethodStatically(receiver, method, arguments)

compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import Denotations._
1313
import Flags._
1414
import Names._
1515
import NameKinds.DefaultGetterName
16+
import NameOps._
1617
import Periods._
1718
import Phases._
1819
import StdNames._
@@ -681,7 +682,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) {
681682
val varDefs = new mutable.ListBuffer[js.VarDef]
682683

683684
for ((param, i) <- exported.params.zipWithIndex) {
684-
val rhs = genScalaArg(exported, i, formalArgsRegistry, param, static)(
685+
val rhs = genScalaArg(exported, i, formalArgsRegistry, param, static, captures = Nil)(
685686
prevArgsCount => varDefs.take(prevArgsCount).toList.map(_.ref))
686687

687688
varDefs += js.VarDef(freshLocalIdent("prep" + i), NoOriginalName, rhs.tpe, mutable = false, rhs)
@@ -698,7 +699,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) {
698699
* (unboxing and default parameter handling).
699700
*/
700701
def genScalaArg(exported: Exported, paramIndex: Int, formalArgsRegistry: FormalArgsRegistry,
701-
param: JSParamInfo, static: Boolean)(
702+
param: JSParamInfo, static: Boolean, captures: List[js.Tree])(
702703
previousArgsValues: Int => List[js.Tree])(
703704
implicit pos: SourcePosition): js.Tree = {
704705

@@ -713,7 +714,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) {
713714
if (exported.hasDefaultAt(paramIndex)) {
714715
// If argument is undefined and there is a default getter, call it
715716
js.If(js.BinaryOp(js.BinaryOp.===, jsArg, js.Undefined()), {
716-
genCallDefaultGetter(exported.sym, paramIndex, static)(previousArgsValues)
717+
genCallDefaultGetter(exported.sym, paramIndex, static, captures)(previousArgsValues)
717718
}, {
718719
unboxedArg
719720
})(unboxedArg.tpe)
@@ -724,7 +725,8 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) {
724725
}
725726
}
726727

727-
private def genCallDefaultGetter(sym: Symbol, paramIndex: Int, static: Boolean)(
728+
private def genCallDefaultGetter(sym: Symbol, paramIndex: Int,
729+
static: Boolean, captures: List[js.Tree])(
728730
previousArgsValues: Int => List[js.Tree])(
729731
implicit pos: SourcePosition): js.Tree = {
730732

@@ -735,9 +737,30 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) {
735737
assert(!defaultGetterDenot.isOverloaded, i"found overloaded default getter $defaultGetterDenot")
736738
val defaultGetter = defaultGetterDenot.symbol
737739

738-
val targetTree =
739-
if (sym.isClassConstructor || static) genLoadModule(targetSym)
740-
else js.This()(encodeClassType(targetSym))
740+
val targetTree = {
741+
if (sym.isClassConstructor || static) {
742+
if (targetSym.isStatic) {
743+
assert(captures.isEmpty, i"expected empty captures for ${targetSym.fullName} at $pos")
744+
genLoadModule(targetSym)
745+
} else {
746+
assert(captures.sizeIs == 1, "expected exactly one capture")
747+
748+
// Find the module accessor.
749+
val outer = targetSym.originalOwner
750+
val name = atPhase(typerPhase)(targetSym.name.unexpandedName).sourceModuleName
751+
val modAccessor = outer.info.memberBasedOnFlags(name, required = Module).symbol
752+
assert(modAccessor.exists, i"could not find module accessor for ${targetSym.fullName} at $pos")
753+
754+
val receiver = captures.head
755+
if (outer.isJSType)
756+
genApplyJSClassMethod(receiver, modAccessor, Nil)
757+
else
758+
genApplyMethodMaybeStatically(receiver, modAccessor, Nil)
759+
}
760+
} else {
761+
js.This()(encodeClassType(targetSym))
762+
}
763+
}
741764

742765
// Pass previous arguments to defaultGetter
743766
val defaultGetterArgs = previousArgsValues(defaultGetter.info.paramInfoss.head.size)

0 commit comments

Comments
 (0)