Evolving the wrapper classes
daniel.smith at oracle.com
Wed Jun 17 21:38:39 UTC 2020
Here's a concrete proposal for how we'll evolve the wrapper classes (Byte, Short, Integer, Long, Float, Double, Character, and Boolean) to be inline classes whose ".val" representations are (in the Java language) the primitive types.
This has the effect of replacing boxing conversions in the Java language with lighter-weight reference conversions (no identity is imposed), and will facilitate specialization in the JVM by wrapping primitive values in lightweight inline class instances.
Important concepts in this approach:
- The wrapper classes are reference-default classes—'Integer' is a '.ref' type.
- In the language, 'int' is an alias for 'Integer.val'. These are the same type.
- In the JVM, there are three distinct types: 'Ljava/lang/Integer;', 'Qjava/lang/Integer$val;', and 'I'
The below outline feels pretty complete to me, as far as the core library/JVM/compiler components are concerned, and quite achievable. Please raise anything I'm overlooking (I'm sure there's something...).
Step 1: Warnings
In the near future, we implement a variety of warnings for clients of the wrapper classes who rely on features that will break when the wrapper classes are inline classes:
- The constructors, currently marked deprecated, are deprecated for removal. This should amplify warnings about their use.
Java compiler changes:
- Attempts to synchronize on or invoke wait/notify methods of expressions with wrapper class static types produce a new warning.
- Possibly, uses of '==', 'identityHashCode', or 'clone' on these expressions produce a warning.
- Possibly, any uses of 'getClass' that compare with '==' to wrapper class literals produce a warning.
- Possibly, runtime warnings occur mimicking some of the compiler warnings, but using runtime types.
(Note that all of these warnings may also apply to Value-based Classes. The wrapper classes happen to fall short of the value-based class requirements in their factories' guarantees about identity; these rules about factories and equality are probably unnecessary limitations, given the current deterministic behavior of acmp.)
Step 2: Preview Feature
When, or sometime after, we ship inline classes as a preview feature, we support treating the wrapper classes as inline.
JVM changes (when --enable-preview is set):
- References to java/lang/Integer and java/lang/Integer$val are hacked to load special class files corresponding to the .ref and .val types of inline class Integer.
- The type [I is considered by the verifier to be equivalent to [java/lang/Integer$val. Array operations (aaload, iaload, etc.) support this.
Java language/compiler changes (when --enable-preview is set):
- The class file reader knows how to find the special Integer.class and Integer$val.class.
- The type 'Integer.val' is equivalent to 'int'. Primitive types are inline types—they have members, support method invocation, etc.
- Where necessary (depending on the operations being performed), the compiler generates conversions between 'I' and 'java/lang/Integer$val'. 'I' is preferred wherever possible.
- Boxing can be specified with stronger guarantees about '=='.
Step 3: Standard Feature
When we're ready to leave preview, we'll need to raise the profile of "those things we warned about are going to blow up now!".
- The wrapper classes are declared in source as reference-default inline classes.
- The constructors are removed, replaced with private constructors.
- Wrapper classes are loaded using standard processes.
Java language/compiler changes:
- The wrapper classes have special permission to declare fields of their own type.
- Wrapper classes are read using standard processes.
More information about the valhalla-spec-observers