Class verifier issues with unboxing method handles
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Wed May 29 04:25:19 PDT 2013
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