-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make MODULE$ Final Again? #537
Comments
One incompatibility is that reflectively instantiating the module's private constructor (either nefariously or accidentally with help from a misconfigured framework like kryo) would no longer get a fully initialized instance. |
If we put the actual module initialization in a private method, would that not break the definite assignment rules for final fields in the object? I believe that's the case for Java the language, but I am not sure to what degree the JVM enforces it. |
@odersky You're right. It was too good to be true! The update to the verifier in question here, that prevents static final assignment outside of It is worth noting the class-file version dependent treatment of non-conformant classfiles in JDK9+:
@mkeskells showed the effect that JIT optimizations can make: rorygraves/scalac_perf#32 (comment) That suggests |
@retronym Is there a difference CONDY would make here? |
It isn't exactly a constant because the null value can be observed from the superclass. class C {
println("O = " + O)
}
object O extends C
object Main extends App {
O.toString
new C
}
We could, however, use That's worth trying, but does require some care as we'd be using the technique very widely and we need a high degree of certainty that it behaves and performs as we'd like in all environments. |
@retronym I'd be extremely interested in hearing about how INDY usage for this would work out. If you do pursue it, please let me know. |
@viktorklang FYI - I had a couple of experements with Reme exotics library https://github.com/forax/exotic and the performance is very different in different VMs (tested JDK 8,9,10). Depends on the technique though |
FIY the fact that the modules are not final anymore also breaks constant folding in Graal (see oracle/graal#637). @dougxc mentioned that in Java 11 Dynamic Constants might be an alternative to solve this problem. |
From oracle/graal#637 (comment)
|
This seems like a good solution to me. The bonus benefit is that fields of objects would themselves be constant foldable, without relying on We're too late in the Scala 2.13 cycle to introduce this encoding. But it would be great for the next version. |
What does "assign to their ACC_FINAL" mean ? Could you sketch what the bytecode would look like with this proposal ? |
object ModuleStatic extends C() with T {
val x = 42
System.out.println("ModuleStatic")
var y = ""
}
class C; trait T { System.out.println("T") } public final class ModuleStatic$ extends C implements T {
public static final ModuleStatic$ MODULE$;
private static final int x;
private static String y;
private ModuleStatic$() {
super();
}
static {
MODULE$ = new ModuleStatic$();
T.$init$(MODULE$);
x = 42;
System.out.println("ModuleStatic");
y = "";
}
public int x() {
return x;
}
public String y() {
return y;
}
public void y_$eq(String value) {
y = value;
}
}
class C {
}
interface T {
static void $init$(T t) {
System.out.println("T");
}
} |
That looks like an excellent solution! I wonder why we did not think before
about making all object fields static.
- Martin
…On Wed, Sep 12, 2018 at 3:04 PM Jason Zaugg ***@***.***> wrote:
object ModuleStatic extends C() with T {
val x = 42
System.out.println("ModuleStatic")
val y = ""
}
class C; trait T { System.out.println("T") }
public class ModuleStatic$ extends C implements T {
public static final ModuleStatic$ MODULE$;
private static final int x;
private static String y;
private ModuleStatic$() {
super();
}
static {
MODULE$ = new ModuleStatic$();
T.$init$(MODULE$);
x = 42;
System.out.println("ModuleStatic");
y = "";
}
public int x() {
return x;
}
public String y() {
return y;
}
public void y_$eq(String value) {
y = value;
}
}class C {
}interface T {
static void $init$(T t) {
System.out.println("T");
}
}
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#537 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAwlVhI-qbgvueX7-nAaDcnr6Q641c6Nks5uaQWNgaJpZM4V7t7y>
.
--
Martin Odersky
EPFL and Lightbend
|
Are there any risks w.r.t. partial initialization? (i.e. if there's an exception somewhere after starting to initialize fields.) |
@viktorklang I think the situation is the same as before. An exception thrown by the constructor of the object still bubble out of The stack trace of the exception would start at |
The one behaviour that changes is what happens when someone reflectively calls the private constructor. The status quo is already broken: the If we're changing things, I'd suggest that we assert that |
Another nice property about this encoding is that it doesn't necessarily break reflective access to fields.
|
This is what we discussed in rorygraves/scalac_perf#19 such a long time ago. Strange how these things go in cycles. Are we really too late in 2.13 for this? This change would give us faster execution, and (if we can use the static fields directly in bytecode) smaller classfiles as well for some access patterns, including the common ones. Should we not consider the benefit with a small benchmark before dismissing the option? |
@retronym Good, thanks for elaborating on that. |
I can't commit to including this change in what is primarily a library release, with the dev cycle in feature freeze, but I'm open to being convinced by substantial gains in relevant benchmarks and due diligence in avoiding regressions. I'd like to make a decision by the end of October, which means we need to have a reviewable PR with a passing community build and benchmarks by then. |
We had to (#194) make the module field non-final to prepare for JDK9/Classfile 53 verifier, which enforces the (long standing) rule that static final field assignments must occur in
<clinit>
(not<init>
).However, this prevents the JIT from eliding the access of this field in calling code. For example, in
scala.math.max(1, 2)
, the JIT can't rule out thatscala.math.package$.MODULE$
hasn't been nulled out, so it must be checked.For some special cases, like above, I've managed to restore finality of that field in scala/scala#6523. But important modules don't qualify for the optimization, e.g
Predef
andNil
.The JDK has an internal annotation,
@Stable
, that would tell the JIT that this field is 'effectively final', ie it is assign once. That's not available for our purposes. It can be simulated with invokedynamic, which might be worth trying, but we need to ensure it is really zero-overhead across different JDK versions.Another idea came to mind yesterday.
Given:
We currently emit:
But why not:
ie, split the constructor up just after the super-class constructor call and move the rest into a private method that would be called after
<clinit>
assigns the module field.The text was updated successfully, but these errors were encountered: