You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Sep 1, 2020. It is now read-only.
SD-194 Tweak module initialization to comply with JVM spec
Top level modules in Scala currently desugar as:
```
class C; object O extends C { toString }
```
```
public final class O$ extends C {
public static final O$ MODULE$;
public static {};
Code:
0: new #2 // class O$
3: invokespecial #12 // Method "<init>":()V
6: return
private O$();
Code:
0: aload_0
1: invokespecial #13 // Method C."<init>":()V
4: aload_0
5: putstatic #15 // Field MODULE$:LO$;
8: aload_0
9: invokevirtual #21 // Method java/lang/Object.toString:()Ljava/lang/String;
12: pop
13: return
}
```
The static initalizer `<clinit>` calls the constructor `<init>`, which
invokes superclass constructor, assigns `MODULE$= this`, and then runs
the remainder of the object's constructor (`toString` in the example
above.)
It turns out that this relies on a bug in the JVM's verifier: assignment to a
static final must occur lexically within the <clinit>, not from within `<init>`
(even if the latter is happens to be called by the former).
I'd like to move the assignment to <clinit> but that would
change behaviour of "benign" cyclic references between modules.
Example:
```
package p1; class CC { def foo = O.bar}; object O {new CC().foo; def bar = println(1)};
// Exiting paste mode, now interpreting.
scala> p1.O
1
```
This relies on the way that we assign MODULE$ field after the super class constructors
are finished, but before the rest of the module constructor is called.
Instead, this commit removes the ACC_FINAL bit from the field. It actually wasn't
behaving as final at all, precisely the issue that the stricter verifier
now alerts us to.
```
scala> :paste -raw
// Entering paste mode (ctrl-D to finish)
package p1; object O
// Exiting paste mode, now interpreting.
scala> val O1 = p1.O
O1: p1.O.type = p1.O$@ee7d9f1
scala> scala.reflect.ensureAccessible(p1.O.getClass.getDeclaredConstructor()).newInstance()
res0: p1.O.type = p1.O$@64cee07
scala> O1 eq p1.O
res1: Boolean = false
```
We will still achieve safe publication of the assignment to other threads
by virtue of the fact that `<clinit>` is executed within the scope of
an initlization lock, as specified by:
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.5
Fixes scala/scala-dev#SD-194
0 commit comments