@@ -10,14 +10,16 @@ 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 ._
22+ import dotty .tools .dotc .transform .SymUtils ._
2123
2224/*
2325 *
@@ -92,12 +94,12 @@ trait BCodeSkelBuilder extends BCodeHelpers {
9294
9395 /* ---------------- helper utils for generating classes and fields ---------------- */
9496
95- def genPlainClass (cd : TypeDef ) = cd match {
96- case TypeDef (_, impl) =>
97+ def genPlainClass (cd0 : TypeDef ) = cd0 match {
98+ case TypeDef (_, impl : Template ) =>
9799 assert(cnode == null , " GenBCode detected nested methods." )
98100 innerClassBufferASM.clear()
99101
100- claszSymbol = cd .symbol
102+ claszSymbol = cd0 .symbol
101103 isCZParcelable = isAndroidParcelableClass(claszSymbol)
102104 isCZStaticModule = claszSymbol.isStaticModuleClass
103105 thisName = internalName(claszSymbol)
@@ -106,14 +108,70 @@ trait BCodeSkelBuilder extends BCodeHelpers {
106108
107109 initJClass(cnode)
108110
111+ val cd = if (isCZStaticModule) {
112+ // Move statements from the primary constructor following the superclass constructor call to
113+ // a newly synthesised tree representing the "<clinit>", which also assigns the MODULE$ field.
114+ // Because the assigments to both the module instance fields, and the fields of the module itself
115+ // are in the <clinit>, these fields can be static + final.
116+
117+ // TODO should we do this transformation earlier, say in Constructors? Or would that just cause
118+ // pain for scala-{js, native}?
119+
120+ for (f <- claszSymbol.info.decls.filter(_.isField))
121+ f.setFlag(JavaStatic )
122+
123+ val (uptoSuperStats, remainingConstrStats) = splitAtSuper(impl.constr.rhs.asInstanceOf [Block ].stats)
124+ val clInitSymbol = ctx.newSymbol(
125+ claszSymbol,
126+ nme.STATIC_CONSTRUCTOR ,
127+ JavaStatic | Method ,
128+ MethodType (Nil )(_ => Nil , _ => defn.UnitType ),
129+ privateWithin = NoSymbol ,
130+ coord = claszSymbol.coord
131+ )
132+
133+ // We don't need to enter this field into the decls of claszSymbol.info as this is added manually to the generated class
134+ // in addModuleInstanceField. TODO: try adding it to the decls and making the usual field generation do the right thing.
135+ val moduleField = ctx.newSymbol(
136+ claszSymbol,
137+ str.MODULE_INSTANCE_FIELD .toTermName,
138+ JavaStatic | Private ,
139+ claszSymbol.typeRef,
140+ privateWithin = NoSymbol ,
141+ coord = claszSymbol.coord
142+ )
143+
144+ val thisMap = new TreeTypeMap (
145+ treeMap = {
146+ case tree : This if tree.symbol == claszSymbol =>
147+ ref(claszSymbol.sourceModule)
148+ case tree =>
149+ tree
150+ },
151+ oldOwners = claszSymbol.primaryConstructor :: Nil ,
152+ newOwners = clInitSymbol :: Nil
153+ )
154+
155+ val callConstructor = New (claszSymbol.typeRef).select(claszSymbol.primaryConstructor).appliedToArgs(Nil )
156+ val assignModuleField = Assign (ref(moduleField), callConstructor)
157+ val remainingConstrStatsSubst = remainingConstrStats.map(thisMap(_))
158+ val clinit = DefDef (
159+ clInitSymbol,
160+ Block (assignModuleField :: remainingConstrStatsSubst, unitLiteral)
161+ )
162+
163+ val constr2 = {
164+ val rhs = Block (uptoSuperStats, impl.constr.rhs.asInstanceOf [Block ].expr)
165+ cpy.DefDef (impl.constr)(rhs = rhs)
166+ }
167+
168+ val impl2 = cpy.Template (impl)(constr = constr2, body = clinit :: impl.body)
169+ cpy.TypeDef (cd0)(rhs = impl2)
170+ } else cd0
171+
109172 val methodSymbols = for (f <- cd.symbol.info.decls.toList if f.is(Method ) && f.isTerm && ! f.is(Module )) yield f
110173 val hasStaticCtor = methodSymbols exists (_.isStaticConstructor)
111- if (! hasStaticCtor) {
112- // but needs one ...
113- if (isCZStaticModule || isCZParcelable) {
114- fabricateStaticInit()
115- }
116- }
174+ if (! hasStaticCtor && isCZParcelable) fabricateStaticInitAndroid()
117175
118176 val optSerial : Option [Long ] =
119177 claszSymbol.getAnnotation(defn.SerialVersionUIDAnnot ).flatMap { annot =>
@@ -222,7 +280,7 @@ trait BCodeSkelBuilder extends BCodeHelpers {
222280 /*
223281 * must-single-thread
224282 */
225- private def fabricateStaticInit (): Unit = {
283+ private def fabricateStaticInitAndroid (): Unit = {
226284
227285 val clinit : asm.MethodVisitor = cnode.visitMethod(
228286 GenBCodeOps .PublicStatic , // TODO confirm whether we really don't want ACC_SYNTHETIC nor ACC_DEPRECATED
@@ -233,15 +291,9 @@ trait BCodeSkelBuilder extends BCodeHelpers {
233291 )
234292 clinit.visitCode()
235293
236- /* "legacy static initialization" */
237- if (isCZStaticModule) {
238- clinit.visitTypeInsn(asm.Opcodes .NEW , thisName)
239- clinit.visitMethodInsn(asm.Opcodes .INVOKESPECIAL ,
240- thisName, INSTANCE_CONSTRUCTOR_NAME , " ()V" , false )
241- }
242294 if (isCZParcelable) { legacyAddCreatorCode(clinit, cnode, thisName) }
243- clinit.visitInsn(asm.Opcodes .RETURN )
244295
296+ clinit.visitInsn(asm.Opcodes .RETURN )
245297 clinit.visitMaxs(0 , 0 ) // just to follow protocol, dummy arguments
246298 clinit.visitEnd()
247299 }
@@ -631,7 +683,7 @@ trait BCodeSkelBuilder extends BCodeHelpers {
631683 /*
632684 * must-single-thread
633685 *
634- * TODO document, explain interplay with `fabricateStaticInit ()`
686+ * TODO document, explain interplay with `fabricateStaticInitAndroid ()`
635687 */
636688 private def appendToStaticCtor (dd : DefDef ): Unit = {
637689
0 commit comments