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