-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Implement @static annotation on singleton object fields. #894
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
Conversation
Copying from the closed pull request: I remember that there was an earlier hack included to fix an issue with Android compatibility, I think it would make sense to remove that hack now and check that "static" also works for this "real world use case". Look for |
Thinking about it again ... I guess it would make sense to check for |
/** | ||
* An annotation that marks a member in the companion object as static | ||
* and ensures that the compiler generates static fields/methods for it. | ||
* This is important for Java interoperability and performance reasons. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
performance reasons
As much I this can be true, I fear that people will start sprinkling it over their code to get "magically" faster performance. Maybe it would be better to leave that part of the documentation out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should. In fact, maybe we should also put it behind a language import.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea! Yes, seems to be reasonable. I fear the day were beginners start to write code like @specialized @inline @static def foo ...
. :-)
One question: why move the initialization out of the object’s constructor? The object is initialized from the static initializer anyway (at least in 2.10.0-M5 as I verified looking at some byte code), so which problems would ensue by just having the constructor happen “in sequence”? It would for sure make understanding the code flow easier, since the non-static parts are intermingled lexically with the static ones. And you could remove some code, which is always good ;-) |
That's exactly the first thing I've intended to do. The problem is that AtomicReferenceUpdaters will (on creation) check to see if the caller is the owner of the field they're supposed to modify, and throw an exception if it's not. This is why it's imperative that the creation of the arfu occurs in the static initializer of the class itself. |
What a pity that this very special and extremely quirky use-case should force such a questionable design choice. If someone goes to the trouble of putting an ARFU into a static field, why don’t they go the full nine yards and use Unsafe? The whole j.u.c. package has been moved this way for JDK7 and onward. |
I don't think this should go into 2.10.x since it's not a critical bugfix AFAICT. |
I think Martin was very interested to get this into 2.10. It was his idea to do this. |
I can vouch for that ^ |
I see. I didn't know, sorry. It still feels like an improvement instead of a critical bug fix. Let's discuss it at the meeting tomorrow. |
Sure! |
@axel22 could you please rebase on 2.10.x and push -f? |
Yes, I'll do this now. |
This commit introduces the `@static` annotation on fields of static singleton objects. A static singleton object is a top-level singleton object or an object nested within a static singleton object. The field annotated with `@static` is generated as a true static field in the companion class of the object. The initialization of the field is done in the static initializer of the companion class, instead of the object itself. Here's an example. This: object Foo { @static val FIELD = 123 } class Foo generates : object Foo { def FIELD(): Int = Foo.FIELD } class Foo { <static> val FIELD = 123 } The accessor in `object Foo` is changed to return the static field in `class Foo` (the companion class is generated if it is not already present, and the same is done for getters if `FIELD` is a `var`). Furthermore, all the callsites to accessor `Foo.FIELD` are rewritten to access the static field directly. It is illegal to annotate a field in the singleton object as `@static` if there is already a member of the same name in `class Foo`. This allows better Java interoperability with frameworks which rely on static fields being present in the class. The `AtomicReferenceFieldUpdater`s are one such example. Instead of having a Java base class holding the `volatile` mutable field `f` and a static field containing a reference to the `AtomicReferenceFieldUpdater` that mutates `f` atomically, and extending this Java base class from Scala, we can now simply do: object Foo { @static val arfu = AtomicReferenceUpdater.newUpdater( classOf[Foo], classOf[String], "f" ) } class Foo { @volatile var f = null import Foo._ def CAS(ov: String, nv: String) = arfu.compareAndSet(this, null, "") } In summary, this commit introduces the following: - adds the `@static` annotation - for objects without a companion class and `@static` members, creates a companion class (this is done in `CleanUp`) - checks for conflicting names and if `@static` is used on static singleton object member - creates the `static` field in the companion class for `@static` members - moves the field initialization from the companion object to the static initializer of the class (the initialization of `@static` members is bypassed in the `Constructors` phase, and added to static ctors in `CleanUp`) - all callsites to the accessors of `@static` are rewritten to access the static fields directly (this is done in `GenICode`) - getters and setters to `@static` fields access the static field directly, and the field in the singleton object is not emitted (this is done in `GenICode`) - changes in `GenJVM`, `GenASM` - now computing local var indices in static initializers as well - this was an oversight before, now is necessary Future work: allow the `@static` annotation on methods as well - this will be added in a future commit. Review by @odersky, @dragos, @paulp, @heathermiller.
I did a PLS REBUILD ALL |
Started jenkins job pr-scala-testsuite-linux-opt at https://scala-webapps.epfl.ch/jenkins/job/pr-scala-testsuite-linux-opt/520/ |
jenkins job pr-scala-testsuite-linux-opt: Success - https://scala-webapps.epfl.ch/jenkins/job/pr-scala-testsuite-linux-opt/520/ |
Ok, so now we wait for some one to sign off :-) |
if (m.symbol.isAccessor && m.symbol.accessed.hasStaticAnnotation) { | ||
// in companion object accessors to @static fields, we access the static field directly | ||
val hostClass = m.symbol.owner.companionClass | ||
val staticfield = hostClass.info.decls.find(_.name.toString.trim == m.symbol.accessed.name.toString.trim) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't you store the static symbol somewhere? I'm worried linear search might be too slow, plus it's comparing strings, which isn't fast either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could store it somewhere during cleanup
, but I'm not sure where so that it's visible later during icode
. Will look into it.
Also, maybe findMember
could help.
I will address these issues and add a commit once I do. |
Started jenkins job pr-scala-testsuite-linux-opt at https://scala-webapps.epfl.ch/jenkins/job/pr-scala-testsuite-linux-opt/607/ |
jenkins job pr-scala-testsuite-linux-opt: Success - https://scala-webapps.epfl.ch/jenkins/job/pr-scala-testsuite-linux-opt/607/ |
The upcoming `findMember` optimizations should ensure that this is fast enough.
Thanks @adriaanm :)) |
PLS REBUILD ALL |
Started jenkins job pr-scala-testsuite-linux-opt at https://scala-webapps.epfl.ch/jenkins/job/pr-scala-testsuite-linux-opt/623/ |
jenkins job pr-scala-testsuite-linux-opt: Success - https://scala-webapps.epfl.ch/jenkins/job/pr-scala-testsuite-linux-opt/623/ |
Implement @static annotation on singleton object fields.
This commit introduces the
@static
annotation on fields of static singleton objects.A static singleton object is a top-level singleton object or an object nested within
a static singleton object.
The field annotated with
@static
is generated as a true static field in the companionclass of the object. The initialization of the field is done in the static initializer of the
companion class, instead of the object itself.
Here's an example. This:
generates :
The accessor in
object Foo
is changed to return the staticfield in
class Foo
(the companion class is generated if it isnot already present, and the same is done for getters if
FIELD
is a
var
).Furthermore, all the callsites to accessor
Foo.FIELD
are rewrittento access the static field directly.
It is illegal to annotate a field in the singleton object as
@static
if there isalready a member of the same name in
class Foo
.This allows better Java interoperability with frameworks which rely on static fields being
present in the class.
The
AtomicReferenceFieldUpdater
s are one such example. Instead of havinga Java base class holding the
volatile
mutable fieldf
and a static field containinga reference to the
AtomicReferenceFieldUpdater
that mutatesf
atomically,and extending this Java base class from Scala, we can now simply do:
In summary, this commit introduces the following:
@static
annotation@static
members, creates a companion class(this is done in
CleanUp
)@static
is used on static singleton object memberstatic
field in the companion class for@static
membersthe class (the initialization of
@static
members is bypassed in theConstructors
phase,and added to static ctors in
CleanUp
)@static
are rewritten to access the static fields directly(this is done in
GenICode
)@static
fields access the static field directly, and the field in thesingleton object is not emitted (this is done in
GenICode
)GenJVM
,GenASM
- now computing local var indices in static initializersas well - this was an oversight before, now is necessary
Future work: allow the
@static
annotation on methods as well - this willbe added next.
Review by @odersky
Review by @dragos