@@ -10,15 +10,17 @@ import scala.tools.asm.util.{TraceMethodVisitor, ASMifier}
1010import java .io .PrintWriter
1111
1212import dotty .tools .dotc .ast .tpd
13+ import dotty .tools .dotc .ast .TreeTypeMap
1314import dotty .tools .dotc .CompilationUnit
1415import dotty .tools .dotc .core .Annotations .Annotation
1516import dotty .tools .dotc .core .Decorators ._
1617import dotty .tools .dotc .core .Flags ._
17- import dotty .tools .dotc .core .StdNames .str
18+ import dotty .tools .dotc .core .StdNames ._
1819import dotty .tools .dotc .core .Symbols ._
19- import dotty .tools .dotc .core .Types .Type
20+ import dotty .tools .dotc .core .Types ._
2021import dotty .tools .dotc .util .Spans ._
2122import dotty .tools .dotc .report
23+ import dotty .tools .dotc .transform .SymUtils ._
2224
2325/*
2426 *
@@ -93,12 +95,12 @@ trait BCodeSkelBuilder extends BCodeHelpers {
9395
9496 /* ---------------- helper utils for generating classes and fields ---------------- */
9597
96- def genPlainClass (cd : TypeDef ) = cd match {
97- case TypeDef (_, impl) =>
98+ def genPlainClass (cd0 : TypeDef ) = cd0 match {
99+ case TypeDef (_, impl : Template ) =>
98100 assert(cnode == null , " GenBCode detected nested methods." )
99101 innerClassBufferASM.clear()
100102
101- claszSymbol = cd .symbol
103+ claszSymbol = cd0 .symbol
102104 isCZParcelable = isAndroidParcelableClass(claszSymbol)
103105 isCZStaticModule = claszSymbol.isStaticModuleClass
104106 thisName = internalName(claszSymbol)
@@ -107,14 +109,70 @@ trait BCodeSkelBuilder extends BCodeHelpers {
107109
108110 initJClass(cnode)
109111
112+ val cd = if (isCZStaticModule) {
113+ // Move statements from the primary constructor following the superclass constructor call to
114+ // a newly synthesised tree representing the "<clinit>", which also assigns the MODULE$ field.
115+ // Because the assigments to both the module instance fields, and the fields of the module itself
116+ // are in the <clinit>, these fields can be static + final.
117+
118+ // TODO should we do this transformation earlier, say in Constructors? Or would that just cause
119+ // pain for scala-{js, native}?
120+
121+ for (f <- claszSymbol.info.decls.filter(_.isField))
122+ f.setFlag(JavaStatic )
123+
124+ val (uptoSuperStats, remainingConstrStats) = splitAtSuper(impl.constr.rhs.asInstanceOf [Block ].stats)
125+ val clInitSymbol = ctx.newSymbol(
126+ claszSymbol,
127+ nme.STATIC_CONSTRUCTOR ,
128+ JavaStatic | Method ,
129+ MethodType (Nil )(_ => Nil , _ => defn.UnitType ),
130+ privateWithin = NoSymbol ,
131+ coord = claszSymbol.coord
132+ )
133+
134+ // We don't need to enter this field into the decls of claszSymbol.info as this is added manually to the generated class
135+ // in addModuleInstanceField. TODO: try adding it to the decls and making the usual field generation do the right thing.
136+ val moduleField = ctx.newSymbol(
137+ claszSymbol,
138+ str.MODULE_INSTANCE_FIELD .toTermName,
139+ JavaStatic | Private ,
140+ claszSymbol.typeRef,
141+ privateWithin = NoSymbol ,
142+ coord = claszSymbol.coord
143+ )
144+
145+ val thisMap = new TreeTypeMap (
146+ treeMap = {
147+ case tree : This if tree.symbol == claszSymbol =>
148+ ref(claszSymbol.sourceModule)
149+ case tree =>
150+ tree
151+ },
152+ oldOwners = claszSymbol.primaryConstructor :: Nil ,
153+ newOwners = clInitSymbol :: Nil
154+ )
155+
156+ val callConstructor = New (claszSymbol.typeRef).select(claszSymbol.primaryConstructor).appliedToArgs(Nil )
157+ val assignModuleField = Assign (ref(moduleField), callConstructor)
158+ val remainingConstrStatsSubst = remainingConstrStats.map(thisMap(_))
159+ val clinit = DefDef (
160+ clInitSymbol,
161+ Block (assignModuleField :: remainingConstrStatsSubst, unitLiteral)
162+ )
163+
164+ val constr2 = {
165+ val rhs = Block (uptoSuperStats, impl.constr.rhs.asInstanceOf [Block ].expr)
166+ cpy.DefDef (impl.constr)(rhs = rhs)
167+ }
168+
169+ val impl2 = cpy.Template (impl)(constr = constr2, body = clinit :: impl.body)
170+ cpy.TypeDef (cd0)(rhs = impl2)
171+ } else cd0
172+
110173 val methodSymbols = for (f <- cd.symbol.info.decls.toList if f.is(Method ) && f.isTerm && ! f.is(Module )) yield f
111174 val hasStaticCtor = methodSymbols exists (_.isStaticConstructor)
112- if (! hasStaticCtor) {
113- // but needs one ...
114- if (isCZStaticModule || isCZParcelable) {
115- fabricateStaticInit()
116- }
117- }
175+ if (! hasStaticCtor && isCZParcelable) fabricateStaticInitAndroid()
118176
119177 val optSerial : Option [Long ] =
120178 claszSymbol.getAnnotation(defn.SerialVersionUIDAnnot ).flatMap { annot =>
@@ -223,7 +281,7 @@ trait BCodeSkelBuilder extends BCodeHelpers {
223281 /*
224282 * must-single-thread
225283 */
226- private def fabricateStaticInit (): Unit = {
284+ private def fabricateStaticInitAndroid (): Unit = {
227285
228286 val clinit : asm.MethodVisitor = cnode.visitMethod(
229287 GenBCodeOps .PublicStatic , // TODO confirm whether we really don't want ACC_SYNTHETIC nor ACC_DEPRECATED
@@ -234,15 +292,9 @@ trait BCodeSkelBuilder extends BCodeHelpers {
234292 )
235293 clinit.visitCode()
236294
237- /* "legacy static initialization" */
238- if (isCZStaticModule) {
239- clinit.visitTypeInsn(asm.Opcodes .NEW , thisName)
240- clinit.visitMethodInsn(asm.Opcodes .INVOKESPECIAL ,
241- thisName, INSTANCE_CONSTRUCTOR_NAME , " ()V" , false )
242- }
243295 if (isCZParcelable) { legacyAddCreatorCode(clinit, cnode, thisName) }
244- clinit.visitInsn(asm.Opcodes .RETURN )
245296
297+ clinit.visitInsn(asm.Opcodes .RETURN )
246298 clinit.visitMaxs(0 , 0 ) // just to follow protocol, dummy arguments
247299 clinit.visitEnd()
248300 }
@@ -632,7 +684,7 @@ trait BCodeSkelBuilder extends BCodeHelpers {
632684 /*
633685 * must-single-thread
634686 *
635- * TODO document, explain interplay with `fabricateStaticInit ()`
687+ * TODO document, explain interplay with `fabricateStaticInitAndroid ()`
636688 */
637689 private def appendToStaticCtor (dd : DefDef ): Unit = {
638690
0 commit comments