"Model 2" prototype implementation
Brian Goetz
brian.goetz at oracle.com
Fri Aug 7 20:09:14 UTC 2015
Sure, we could have chosen that. But this is a more straightforward
translation, and the simpler the translation is, the fewer dark corners
there are. For one thing, it means that instanceof/cast (and even more
importantly, their reflective equivalents) *just work*. It means that
the subtyping is scrutable to the JIT and can feed information into
optimizations.
Most dispatch on Foo<any> receivers can proceed without indy; the
prototype happens to use it for dispatching all calls on Foo<any>
receivers, but this is neither necessary nor optimal.
On 8/7/2015 2:01 PM, Rémi Forax wrote:
> Hi Brian,
> why do you need the interface Box$any and not use Object and send Box.class as a bootstrap argument to the indy call ?
>
> Rémi
>
>
> Le 7 août 2015 17:49:31 CEST, Brian Goetz <brian.goetz at oracle.com> a écrit :
>> 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