"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