@@ -10,14 +10,16 @@ import scala.tools.asm.util.{TraceMethodVisitor, ASMifier}
10
10
import java .io .PrintWriter
11
11
12
12
import dotty .tools .dotc .ast .tpd
13
+ import dotty .tools .dotc .ast .TreeTypeMap
13
14
import dotty .tools .dotc .CompilationUnit
14
15
import dotty .tools .dotc .core .Annotations .Annotation
15
16
import dotty .tools .dotc .core .Decorators ._
16
17
import dotty .tools .dotc .core .Flags ._
17
- import dotty .tools .dotc .core .StdNames .str
18
+ import dotty .tools .dotc .core .StdNames ._
18
19
import dotty .tools .dotc .core .Symbols ._
19
- import dotty .tools .dotc .core .Types .Type
20
+ import dotty .tools .dotc .core .Types ._
20
21
import dotty .tools .dotc .util .Spans ._
22
+ import dotty .tools .dotc .transform .SymUtils ._
21
23
22
24
/*
23
25
*
@@ -92,12 +94,12 @@ trait BCodeSkelBuilder extends BCodeHelpers {
92
94
93
95
/* ---------------- helper utils for generating classes and fields ---------------- */
94
96
95
- def genPlainClass (cd : TypeDef ) = cd match {
96
- case TypeDef (_, impl) =>
97
+ def genPlainClass (cd0 : TypeDef ) = cd0 match {
98
+ case TypeDef (_, impl : Template ) =>
97
99
assert(cnode == null , " GenBCode detected nested methods." )
98
100
innerClassBufferASM.clear()
99
101
100
- claszSymbol = cd .symbol
102
+ claszSymbol = cd0 .symbol
101
103
isCZParcelable = isAndroidParcelableClass(claszSymbol)
102
104
isCZStaticModule = claszSymbol.isStaticModuleClass
103
105
thisName = internalName(claszSymbol)
@@ -106,14 +108,70 @@ trait BCodeSkelBuilder extends BCodeHelpers {
106
108
107
109
initJClass(cnode)
108
110
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
+
109
172
val methodSymbols = for (f <- cd.symbol.info.decls.toList if f.is(Method ) && f.isTerm && ! f.is(Module )) yield f
110
173
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()
117
175
118
176
val optSerial : Option [Long ] =
119
177
claszSymbol.getAnnotation(defn.SerialVersionUIDAnnot ).flatMap { annot =>
@@ -222,7 +280,7 @@ trait BCodeSkelBuilder extends BCodeHelpers {
222
280
/*
223
281
* must-single-thread
224
282
*/
225
- private def fabricateStaticInit (): Unit = {
283
+ private def fabricateStaticInitAndroid (): Unit = {
226
284
227
285
val clinit : asm.MethodVisitor = cnode.visitMethod(
228
286
GenBCodeOps .PublicStatic , // TODO confirm whether we really don't want ACC_SYNTHETIC nor ACC_DEPRECATED
@@ -233,15 +291,9 @@ trait BCodeSkelBuilder extends BCodeHelpers {
233
291
)
234
292
clinit.visitCode()
235
293
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
- }
242
294
if (isCZParcelable) { legacyAddCreatorCode(clinit, cnode, thisName) }
243
- clinit.visitInsn(asm.Opcodes .RETURN )
244
295
296
+ clinit.visitInsn(asm.Opcodes .RETURN )
245
297
clinit.visitMaxs(0 , 0 ) // just to follow protocol, dummy arguments
246
298
clinit.visitEnd()
247
299
}
@@ -631,7 +683,7 @@ trait BCodeSkelBuilder extends BCodeHelpers {
631
683
/*
632
684
* must-single-thread
633
685
*
634
- * TODO document, explain interplay with `fabricateStaticInit ()`
686
+ * TODO document, explain interplay with `fabricateStaticInitAndroid ()`
635
687
*/
636
688
private def appendToStaticCtor (dd : DefDef ): Unit = {
637
689
0 commit comments