Skip to content

Commit d51356b

Browse files
committed
Scala.js: Implement the @EnableReflectiveInstantiation treatment.
1 parent f79679f commit d51356b

File tree

3 files changed

+132
-1
lines changed

3 files changed

+132
-1
lines changed

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

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ class JSCodeGen()(implicit ctx: Context) {
6868
private val thisLocalVarIdent = new ScopedVar[Option[js.Ident]]
6969
private val undefinedDefaultParams = new ScopedVar[mutable.Set[Symbol]]
7070

71+
private def withNewLocalNameScope[A](body: => A): A = {
72+
withScopedVars(localNames := new LocalNameGenerator) {
73+
body
74+
}
75+
}
76+
7177
/** Implicitly materializes the current local name generator. */
7278
private implicit def implicitLocalNames: LocalNameGenerator = localNames.get
7379

@@ -86,6 +92,10 @@ class JSCodeGen()(implicit ctx: Context) {
8692
private def freshLocalIdent(base: String)(implicit pos: Position): js.Ident =
8793
localNames.get.freshLocalIdent(base)
8894

95+
/** Returns a new fresh local identifier. */
96+
private def freshLocalIdent(base: TermName)(implicit pos: Position): js.Ident =
97+
localNames.get.freshLocalIdent(base)
98+
8999
// Compilation unit --------------------------------------------------------
90100

91101
def run(): Unit = {
@@ -287,9 +297,31 @@ class JSCodeGen()(implicit ctx: Context) {
287297
Nil
288298
}
289299

300+
// Static initializer
301+
val optStaticInitializer = {
302+
// Initialization of reflection data, if required
303+
val reflectInit = {
304+
val enableReflectiveInstantiation = {
305+
sym.baseClasses.exists { ancestor =>
306+
ancestor.hasAnnotation(jsdefn.EnableReflectiveInstantiationAnnot)
307+
}
308+
}
309+
if (enableReflectiveInstantiation)
310+
genRegisterReflectiveInstantiation(sym)
311+
else
312+
None
313+
}
314+
315+
val staticInitializerStats = reflectInit.toList
316+
if (staticInitializerStats.nonEmpty)
317+
Some(genStaticInitializerWithStats(js.Block(staticInitializerStats)))
318+
else
319+
None
320+
}
321+
290322
// Hashed definitions of the class
291323
val hashedDefs =
292-
ir.Hashers.hashMemberDefs(generatedMembers ++ exports)
324+
ir.Hashers.hashMemberDefs(generatedMembers ++ exports ++ optStaticInitializer)
293325

294326
// The complete class definition
295327
val kind =
@@ -461,6 +493,92 @@ class JSCodeGen()(implicit ctx: Context) {
461493
}).toList
462494
}
463495

496+
// Static initializers -----------------------------------------------------
497+
498+
private def genStaticInitializerWithStats(stats: js.Tree)(
499+
implicit pos: Position): js.MethodDef = {
500+
js.MethodDef(
501+
js.MemberFlags.empty.withNamespace(js.MemberNamespace.StaticConstructor),
502+
js.Ident(ir.Definitions.StaticInitializerName),
503+
Nil,
504+
jstpe.NoType,
505+
Some(stats))(
506+
OptimizerHints.empty, None)
507+
}
508+
509+
private def genRegisterReflectiveInstantiation(sym: Symbol)(
510+
implicit pos: Position): Option[js.Tree] = {
511+
if (isStaticModule(sym))
512+
genRegisterReflectiveInstantiationForModuleClass(sym)
513+
else if (sym.is(ModuleClass))
514+
None // scala-js#3228
515+
else if (sym.is(Lifted) && !sym.originalOwner.isClass)
516+
None // scala-js#3227
517+
else
518+
genRegisterReflectiveInstantiationForNormalClass(sym)
519+
}
520+
521+
private def genRegisterReflectiveInstantiationForModuleClass(sym: Symbol)(
522+
implicit pos: Position): Option[js.Tree] = {
523+
val fqcnArg = js.StringLiteral(sym.fullName.toString)
524+
val runtimeClassArg = js.ClassOf(toTypeRef(sym.info))
525+
val loadModuleFunArg =
526+
js.Closure(arrow = true, Nil, Nil, genLoadModule(sym), Nil)
527+
528+
val stat = genApplyMethod(
529+
genLoadModule(jsdefn.ReflectModule),
530+
jsdefn.Reflect_registerLoadableModuleClass,
531+
List(fqcnArg, runtimeClassArg, loadModuleFunArg))
532+
533+
Some(stat)
534+
}
535+
536+
private def genRegisterReflectiveInstantiationForNormalClass(sym: Symbol)(
537+
implicit pos: Position): Option[js.Tree] = {
538+
val ctors =
539+
if (sym.is(Abstract)) Nil
540+
else sym.info.member(nme.CONSTRUCTOR).alternatives.map(_.symbol).filter(m => !m.is(Private | Protected))
541+
542+
if (ctors.isEmpty) {
543+
None
544+
} else {
545+
val constructorsInfos = for {
546+
ctor <- ctors
547+
} yield {
548+
withNewLocalNameScope {
549+
val (parameterTypes, formalParams, actualParams) = (for {
550+
(paramName, paramInfo) <- ctor.info.paramNamess.flatten.zip(ctor.info.paramInfoss.flatten)
551+
} yield {
552+
val paramType = js.ClassOf(toTypeRef(paramInfo))
553+
val paramDef = js.ParamDef(freshLocalIdent(paramName), jstpe.AnyType,
554+
mutable = false, rest = false)
555+
val actualParam = unbox(paramDef.ref, paramInfo)
556+
(paramType, paramDef, actualParam)
557+
}).unzip3
558+
559+
val paramTypesArray = js.JSArrayConstr(parameterTypes)
560+
561+
val newInstanceFun = js.Closure(arrow = true, Nil, formalParams, {
562+
js.New(encodeClassRef(sym), encodeMethodSym(ctor), actualParams)
563+
}, Nil)
564+
565+
js.JSArrayConstr(List(paramTypesArray, newInstanceFun))
566+
}
567+
}
568+
569+
val fqcnArg = js.StringLiteral(sym.fullName.toString)
570+
val runtimeClassArg = js.ClassOf(toTypeRef(sym.info))
571+
val ctorsInfosArg = js.JSArrayConstr(constructorsInfos)
572+
573+
val stat = genApplyMethod(
574+
genLoadModule(jsdefn.ReflectModule),
575+
jsdefn.Reflect_registerInstantiatableClass,
576+
List(fqcnArg, runtimeClassArg, ctorsInfosArg))
577+
578+
Some(stat)
579+
}
580+
}
581+
464582
// Generate a method -------------------------------------------------------
465583

466584
private def genMethod(dd: DefDef): Option[js.MethodDef] = {

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,16 @@ final class JSDefinitions()(implicit ctx: Context) {
158158
lazy val BoxesRunTime_unboxToCharR = defn.BoxesRunTimeModule.requiredMethodRef("unboxToChar")
159159
def BoxesRunTime_unboxToChar(implicit ctx: Context): Symbol = BoxesRunTime_unboxToCharR.symbol
160160

161+
lazy val EnableReflectiveInstantiationAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.reflect.annotation.EnableReflectiveInstantiation")
162+
def EnableReflectiveInstantiationAnnot(implicit ctx: Context) = EnableReflectiveInstantiationAnnotType.symbol.asClass
163+
164+
lazy val ReflectModuleRef = ctx.requiredModuleRef("scala.scalajs.reflect.Reflect")
165+
def ReflectModule(implicit ctx: Context) = ReflectModuleRef.symbol
166+
lazy val Reflect_registerLoadableModuleClassR = ReflectModule.requiredMethodRef("registerLoadableModuleClass")
167+
def Reflect_registerLoadableModuleClass(implicit ctx: Context) = Reflect_registerLoadableModuleClassR.symbol
168+
lazy val Reflect_registerInstantiatableClassR = ReflectModule.requiredMethodRef("registerInstantiatableClass")
169+
def Reflect_registerInstantiatableClass(implicit ctx: Context) = Reflect_registerInstantiatableClassR.symbol
170+
161171
/** If `cls` is a class in the scala package, its name, otherwise EmptyTypeName */
162172
private def scalajsClassName(cls: Symbol)(implicit ctx: Context): TypeName =
163173
if (cls.isClass && cls.owner == ScalaJSJSPackageClass) cls.asClass.name

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ object JSEncoding {
6666
def freshLocalIdent(base: String)(implicit pos: ir.Position): js.Ident =
6767
js.Ident(freshName(base), Some(base))
6868

69+
def freshLocalIdent(base: TermName)(implicit pos: ir.Position): js.Ident =
70+
js.Ident(freshName(base.toString), Some(base.unexpandedName.toString))
71+
6972
private def freshName(base: String = "x"): String = {
7073
var suffix = 1
7174
var longName = base

0 commit comments

Comments
 (0)