@@ -70,7 +70,6 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
70
70
var claszSymbol : Symbol = null
71
71
var isCZParcelable = false
72
72
var isCZStaticModule = false
73
- var initModuleInClinit = false
74
73
75
74
/* ---------------- idiomatic way to ask questions to typer ---------------- */
76
75
@@ -102,26 +101,51 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
102
101
103
102
/* ---------------- helper utils for generating classes and fields ---------------- */
104
103
105
- def genPlainClass (cd : ClassDef ): Unit = {
104
+ def genPlainClass (cd0 : ClassDef ): Unit = {
106
105
assert(cnode == null , " GenBCode detected nested methods." )
107
106
108
- claszSymbol = cd .symbol
107
+ claszSymbol = cd0 .symbol
109
108
isCZParcelable = isAndroidParcelableClass(claszSymbol)
110
109
isCZStaticModule = isStaticModuleClass(claszSymbol)
111
110
thisBType = classBTypeFromSymbol(claszSymbol)
112
- initModuleInClinit = isCZStaticModule && canAssignModuleInClinit(cd, claszSymbol)
113
111
114
112
cnode = new ClassNode1 ()
115
113
116
114
initJClass(cnode)
115
+ val cd = if (isCZStaticModule) {
116
+ // Move statements from the primary constructor following the superclass constructor call to
117
+ // a newly synthesised tree representing the "<clinit>", which also assigns the MODULE$ field.
118
+ // Because the assigments to both the module instance fields, and the fields of the module itself
119
+ // are in the <clinit>, these fields can be static + final.
117
120
118
- val hasStaticCtor = methodSymbols(cd) exists (_.isStaticConstructor)
119
- if ( ! hasStaticCtor) {
120
- // but needs one ...
121
- if (isCZStaticModule || isCZParcelable ) {
122
- fabricateStaticInit( )
121
+ // TODO should we do this transformation earlier, say in Constructors? Or would that just cause
122
+ // pain for scala-{js, native}?
123
+
124
+ for (f <- fieldSymbols(claszSymbol) ) {
125
+ f.setFlag( Flags . STATIC )
123
126
}
124
- }
127
+ val constructorDefDef = treeInfo.firstConstructor(cd0.impl.body).asInstanceOf [DefDef ]
128
+ val (uptoSuperStats, remainingConstrStats) = treeInfo.splitAtSuper(constructorDefDef.rhs.asInstanceOf [Block ].stats, classOnly = true )
129
+ val clInitSymbol = claszSymbol.newMethod(nme.CLASS_CONSTRUCTOR , claszSymbol.pos, Flags .STATIC ).setInfo(NullaryMethodType (definitions.UnitTpe ))
130
+
131
+ // We don't need to enter this field into the decls of claszSymbol.info as this is added manually to the generated class
132
+ // in addModuleInstanceField. TODO: try adding it to the decls and making the usual field generation do the right thing.
133
+ val moduleField = claszSymbol.newValue(nme.MODULE_INSTANCE_FIELD , claszSymbol.pos, Flags .STATIC | Flags .PRIVATE ).setInfo(claszSymbol.tpeHK)
134
+
135
+ val callConstructor = NewFromConstructor (claszSymbol.primaryConstructor).setType(claszSymbol.tpeHK)
136
+ val assignModuleField = Assign (global.gen.mkAttributedRef(moduleField).setType(claszSymbol.tpeHK), callConstructor).setType(definitions.UnitTpe )
137
+ val remainingConstrStatsSubst = remainingConstrStats.map(_.substituteThis(claszSymbol, global.gen.mkAttributedRef(claszSymbol.sourceModule)).changeOwner(claszSymbol.primaryConstructor -> clInitSymbol))
138
+ val clinit = DefDef (clInitSymbol, Block (assignModuleField :: remainingConstrStatsSubst, Literal (Constant (())).setType(definitions.UnitTpe )).setType(definitions.UnitTpe ))
139
+ deriveClassDef(cd0)(tmpl => deriveTemplate(tmpl)(body =>
140
+ clinit :: body.map {
141
+ case `constructorDefDef` => copyDefDef(constructorDefDef)(rhs = Block (uptoSuperStats, constructorDefDef.rhs.asInstanceOf [Block ].expr))
142
+ case tree => tree
143
+ }
144
+ ))
145
+ } else cd0
146
+
147
+ val hasStaticCtor = methodSymbols(cd) exists (_.isStaticConstructor)
148
+ if (! hasStaticCtor && isCZParcelable) fabricateStaticInitAndroid()
125
149
126
150
val optSerial : Option [Long ] = serialVUID(claszSymbol)
127
151
/* serialVersionUID can't be put on interfaces (it's a private field).
@@ -204,17 +228,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
204
228
*/
205
229
private def addModuleInstanceField (): Unit = {
206
230
// TODO confirm whether we really don't want ACC_SYNTHETIC nor ACC_DEPRECATED
207
- // scala/scala-dev#194:
208
- // This can't be FINAL on JVM 1.9+ because we assign it from within the
209
- // instance constructor, not from <clinit> directly. Assignment from <clinit>,
210
- // after the constructor has completely finished, seems like the principled
211
- // thing to do, but it would change behaviour when "benign" cyclic references
212
- // between modules exist.
213
- //
214
- // We special case modules with parents that we know don't (and won't ever) refer to
215
- // the module during their construction. These can use a final field, and defer the assigment
216
- // to <clinit>.
217
- val mods = if (initModuleInClinit) GenBCode .PublicStaticFinal else GenBCode .PublicStatic
231
+ val mods = GenBCode .PublicStaticFinal
218
232
val fv =
219
233
cnode.visitField(mods,
220
234
strMODULE_INSTANCE_FIELD,
@@ -232,7 +246,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
232
246
/*
233
247
* must-single-thread
234
248
*/
235
- private def fabricateStaticInit (): Unit = {
249
+ private def fabricateStaticInitAndroid (): Unit = {
236
250
237
251
val clinit : asm.MethodVisitor = cnode.visitMethod(
238
252
GenBCode .PublicStatic , // TODO confirm whether we really don't want ACC_SYNTHETIC nor ACC_DEPRECATED
@@ -243,20 +257,9 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
243
257
)
244
258
clinit.visitCode()
245
259
246
- /* "legacy static initialization" */
247
- if (isCZStaticModule) {
248
- clinit.visitTypeInsn(asm.Opcodes .NEW , thisBType.internalName)
249
- if (initModuleInClinit) clinit.visitInsn(asm.Opcodes .DUP )
250
-
251
- clinit.visitMethodInsn(asm.Opcodes .INVOKESPECIAL ,
252
- thisBType.internalName, INSTANCE_CONSTRUCTOR_NAME , " ()V" , false )
253
- if (initModuleInClinit) {
254
- assignModuleInstanceField(clinit)
255
- }
256
- }
257
260
if (isCZParcelable) { legacyAddCreatorCode(clinit, cnode, thisBType.internalName) }
258
- clinit.visitInsn(asm.Opcodes .RETURN )
259
261
262
+ clinit.visitInsn(asm.Opcodes .RETURN )
260
263
clinit.visitMaxs(0 , 0 ) // just to follow protocol, dummy arguments
261
264
clinit.visitEnd()
262
265
}
0 commit comments