"Model 2" prototype implementation
Brian Goetz
brian.goetz at oracle.com
Fri Aug 7 15:49:31 UTC 2015
Great question. We're working on a writeup of the translation details;
we announced the prototype before the writeup was ready, because we
didn't want to keep early adopters from trying it out any further, but
there's a writeup coming.
Here's a very brief taste (but you'll have to wait for the writeup, or
use javap on the output of javac, for more). Imagine the function
R : Type -> Class
which maps a compile-time type to a runtime class. In Java 5 generics,
for a class Foo<T>, the R mapping is very simple -- everything is erased:
R(Foo<String>) = Foo
R(Foo<?>) = Foo
R(Foo) = Foo
In Valhalla, for a class Foo<any T>, the mapping is more complex:
Compatibility demands we adopt the above mappings:
R(Foo<String>) = Foo
R(Foo<ref>) = Foo
R(Foo<raw>) = Foo
And we get some new ones:
R(Foo<int>) = Foo${0=I} *
R(Foo<any>) = Foo$any **
*Name mangling is temporary; something better is in the works.
**Foo$any is a synthetic *interface* generated by javac, which can be
implemented by Foo<int> and Foo<String> alike (using boxing in the API
where necessary).
Because Foo$any is an interface, it can't have fields. So we lift the
fields of Foo to (possibly boxing) accessors on Foo$any (VM help is
needed for nonpublic members here), and we convert field accesses
(through receivers of type Foo<any> only!) to invocations of those
accessors. (Field access through concretely-typed receivers
(Foo<String> or Foo<int>) are still translated with straight
getfield/putfield, so the boxing penalty is only paid by wildcard users.)
Here's your example:
public static void main(String[] args) {
Box<int> box = new Box<int>(3);
Box<any> box_any = box;
System.out.println(box_any.get());
System.out.println(box_any.t);
}
And the bytecode (which works as expected):
public static void main(java.lang.String[]);
Code:
0: new #2 // class "Box${0=I}"
3: dup
4: iconst_3
5: invokespecial #3 // Method
"Box${0=I}"."<init>":(I)V
8: astore_1
9: aload_1
10: astore_2
11: getstatic #4 // Field
java/lang/System.out:Ljava/io/PrintStream;
14: aload_2
15: invokedynamic #5, 0 // InvokeDynamic
#0:get:(LBox$$any;)Ljava/lang/Object;
20: invokevirtual #6 // Method
java/io/PrintStream.println:(Ljava/lang/Object;)V
23: getstatic #4 // Field
java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: invokedynamic #7, 0 // InvokeDynamic
#1:t$get:(LBox$$any;)Ljava/lang/Object;
32: invokevirtual #6 // Method
java/io/PrintStream.println:(Ljava/lang/Object;)V
35: return
BootstrapMethods:
0: #26 invokestatic
java/lang/invoke/VirtualAccess.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
Method arguments:
#27 invokeinterface Box$$any.get:()Ljava/lang/Object;
1: #26 invokestatic
java/lang/invoke/VirtualAccess.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
Method arguments:
#31 invokeinterface Box$$any.t$get:()Ljava/lang/Object;
The invokedynamic at bci=15 and 27 invoke the get() and t$get() methods
of the Foo$any interface. We use invokedynamic instead of
invokeinterface because it allows us to do some boxing adaptations more
easily than statically generating yet more bridge methods. (The
VirtualAccess bootstrap simply does a MethodHandle.asType adaptation of
the provided MH; in the current program, there is no difference in the
signatures so it's as if we did an invokeinterface of Box$$any.get() or
.t$get() directly.)
> I’m wondering how will the implementation work?
> Specifically, in this example, which assumes a specialized |Box| class
> defined here
> <web.archive.org/web/20150801070114/http://cr.openjdk.java.net/~briangoetz/valhalla/specialization.html#erasure-example-a-simple-box-class>:
>
> |Box<int> box = new Box<int>(4); Box<any> box_any = box; // what does
> get() return? and how does it return it?
> System.out.println(box_any.get()); // even more interesting, how does
> this work: System.out.println(box_any.t /* assuming the box value is
> visible */); |
>
> How do you translate the method calls and field accesses? Do they return
> boxed values?
>
> Thanks,
> Vlad
>
> PS: Shameless advertising — I recently worked on a paper describing the
> pitfalls I’ve encountered in miniboxing <http://scala-miniboxing.org>
> when using erased views (and specialized views) and how to mitigate
> them, which has been accepted for PPPJ 2015. In case you’re interested,
> an earlier draft is available here
> <http://infoscience.epfl.ch/record/208797?ln=en>.
>
>
More information about the valhalla-dev
mailing list