Class verifier issues with unboxing method handles

Mark Derricutt mark at talios.com
Fri Jun 7 16:07:56 PDT 2013


Maurizio / David / others...

Does anyone know if anything has come of this? I don't see any further 
posts on this thread on either hotspot-dev or lambda-dev.

Thanks,
Mark


Maurizio Cimadamore wrote:
>
> David, Mark,
> a minimal test case to reproduce the issue is this:
>
> interface IntFunction<X> {
> int m(X x);
> }
>
> class Test {
> public static void main(String[] args) {
> IntFunction<String> s = Integer::new;
> }
> }
>
>
> Looking at the javap output, in particular at the indy call details,
> we find this:
>
> BootstrapMethods:
> 0: #15 invokestatic
> java/lang/invoke/LambdaMetafactory.metaFactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; 
>
>
> Method arguments:
> #16 invokeinterface IntFunction.m:(Ljava/lang/Object;)I
> #17 newinvokespecial java/lang/Integer."<init>":(Ljava/lang/String;)V
> #18 (Ljava/lang/String;)I
>
>
> This seems correct; however, judging from the runtime error, it seems
> like the metafactory is not unboxing the Integer instance back to int
> (which is the expected return value of the implemented method). Hence
> the 292 link failure. Robert, Brian can you confirm?
>
> Maurizio
>
>
> On 29/05/13 02:48, David Holmes wrote:
>>
>> Hi Mark,
>>
>> cc'ing lambda-dev. This may be a bug, a version mismatch, or something
>> else. The lambda-dev folk will know.
>>
>> David
>>
>> On 29/05/2013 11:04 AM, Mark Derricutt wrote:
>>>
>>> Hi all,
>>>
>>> Mark Reinhold suggested I posted this question/bug report to
>>> hotspot-dev
>>> rather than jdk8-dev so here goes.
>>>
>>> I have a fairly simple test case using the new streams API:
>>>
>>> public static void main(String[] args) {
>>> List<String> strings = Arrays.asList("1", "2", "3", "4", "5");
>>> strings.stream()
>>> .mapToInt(s -> new Integer(s))
>>> .forEach(i -> System.out.println(String.format("int %d", i)));
>>> }
>>>
>>>
>>> which compiles, runs, and prints out what I expect:
>>>
>>> int 1
>>> int 2
>>> int 3
>>> int 4
>>> int 5
>>>
>>>
>>> Given that mapToInt() is returning an IntegerStream wrapping primitive
>>> ints, the result of my closure is successfully autoboxed down to an int
>>> and all is happy with the world.
>>>
>>> If I change this code to be:
>>>
>>> strings.stream()
>>> .mapToInt(Integer::parseInt)
>>> .forEach(i -> System.out.println(String.format("int %d", i)));
>>>
>>> Again, everything works as expected as Integer::parseInt returns an
>>> int,
>>> so there's no boxing.
>>>
>>> Changing this once again to:
>>>
>>> strings.stream()
>>> .mapToInt(Integer::valueOf)
>>> .forEach(i -> System.out.println(String.format("int %d", i)));
>>>
>>> I also see the expected result, Integer::valueOf returns an Integer
>>> which is then being boxed down to an int.
>>>
>>> However, if I change the code to:
>>>
>>> strings.stream()
>>> .mapToInt(Integer::new)
>>> .forEach(i -> System.out.println(String.format("int %d", i)));
>>>
>>> which, if I understand correctly - Integer::new should be returning a
>>> method handle to the constructor. IntelliJ IDEA 13 autocompletes this
>>> for me, and hyperlinks to the Integer(String s) constructor, javac
>>> successfully compiles the code so my -assumption- is that the
>>> constructor would be called, returning an Integer which is then boxed
>>> down to an int, however, this is what I get:
>>>
>>> Exception in thread "main" java.lang.BootstrapMethodError: call site
>>> initialization exception
>>> at java.lang.invoke.CallSite.makeSite(CallSite.java:298)
>>> at
>>> java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:294) 
>>>
>>>
>>>
>>> at com.talios.test.TestJdk.main(TestJdk.java:12)
>>> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>>> at
>>> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
>>>
>>>
>>>
>>> at
>>> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
>>>
>>>
>>>
>>> at java.lang.reflect.Method.invoke(Method.java:491)
>>> at
>>> com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
>>> Caused by: java.lang.VerifyError: Bad type on operand stack
>>> Exception Details:
>>> Location:
>>> com/talios/test/TestJdk$$Lambda$1.applyAsInt(Ljava/lang/Object;)I
>>> @11: ireturn
>>> Reason:
>>> Type 'java/lang/Integer' (current frame, stack[0]) is not
>>> assignable to integer
>>> Current Frame:
>>> bci: @11
>>> flags: { }
>>> locals: { 'com/talios/test/TestJdk$$Lambda$1', 'java/lang/Object' }
>>> stack: { 'java/lang/Integer' }
>>> Bytecode:
>>> 0000000: bb00 0e59 2bc0 0010 b700 13ac
>>>
>>> at java.lang.Class.getDeclaredConstructors0(Native Method)
>>> at java.lang.Class.privateGetDeclaredConstructors(Class.java:2536)
>>> at java.lang.Class.getDeclaredConstructors(Class.java:1928)
>>> at
>>> java.lang.invoke.InnerClassLambdaMetafactory$1.run(InnerClassLambdaMetafactory.java:147) 
>>>
>>>
>>>
>>> at
>>> java.lang.invoke.InnerClassLambdaMetafactory$1.run(InnerClassLambdaMetafactory.java:144) 
>>>
>>>
>>>
>>> at java.security.AccessController.doPrivileged(Native Method)
>>> at
>>> java.lang.invoke.InnerClassLambdaMetafactory.buildCallSite(InnerClassLambdaMetafactory.java:143) 
>>>
>>>
>>>
>>> at
>>> java.lang.invoke.LambdaMetafactory.metaFactory(LambdaMetafactory.java:191) 
>>>
>>>
>>> at java.lang.invoke.CallSite.makeSite(CallSite.java:283)
>>> ... 7 more
>>>
>>> This is on OSX Mountain Lion, with JDK 8 Build 91.
>>>
>>> Have I walked in an obscure corner case of method handle breakage and
>>> found something new, or is this a new problem?
>>>
>>> Cheers,
>>> Mark
>>>
>>
>


More information about the lambda-dev mailing list