Class verifier issues with unboxing method handles
Robert Field
Robert.Field at oracle.com
Mon Jun 17 08:50:36 PDT 2013
Thanks Mark, John, and Paul.
I've created: JDK-8016761 - Lambda metafactory: incorrect type
conversion of constructor method handle
<https://jbs.oracle.com/bugs/browse/JDK-8016761>
And assigned it to myself.
-Robert
On 6/17/13 7:35 AM, Paul Sandoz wrote:
> I tried playing around with that fix. It pushes the error further along to somewhere else.
>
> I don't know the exact terms here... but:
>
> implMethodReturnType = Type.getType(implMethodClassName)
>
> creates a type whose description is "java/lang/Integer" rather than "Ljava/lang/Integer" i.e. the input is not a type descriptor is just a class name with '.' replaced with '/'. (Perhaps that could be a bug in ASM not throwing an exception on an illegal description?)
>
> If i do the following then it works:
>
> if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
> implMethodReturnType = Type.getObjectType(implMethodClassName);
> } else {
> implMethodReturnType = implMethodAsmType.getReturnType();
> }
>
> Plus it does not cause additional failures in the lambda lang/invoke tests (same number of tests fail before and after the fix, but i dunno if they are exactly the same tests that fail :-) ).
>
> Paul.
>
> On Jun 14, 2013, at 3:55 AM, John Rose <john.r.rose at oracle.com> wrote:
>
>> A REF_newInvokeSpecial method handle constant refers to a void-returning method named <init>, but its bytecode behavior returns a reference to the constructed object.
>>
>> This may require special checks, such as with 'actualReturnType' in AbstractValidatingLambdaMetafactory.java.
>>
>> There is a missing check in InnerClassLambdaMetafactory.java. Here is a suggested fix (which I have not tested).
>>
>> — John
>>
>> diff --git a/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java b/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java
>> --- a/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java
>> +++ b/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java
>> @@ -112,7 +112,10 @@
>> implMethodDesc = implMethodType.toMethodDescriptorString();
>> Type implMethodAsmType = Type.getMethodType(implMethodDesc);
>> implMethodArgumentTypes = implMethodAsmType.getArgumentTypes();
>> - implMethodReturnType = implMethodAsmType.getReturnType();
>> + if (implKind == MethodHandleInfo.REF_newInvokeSpecial)
>> + implMethodReturnType = Type.getType(implMethodClassName);
>> + else
>> + implMethodReturnType = implMethodAsmType.getReturnType();
>> constructorType = invokedType.changeReturnType(Void.TYPE);
>> constructorDesc = constructorType.toMethodDescriptorString();
>> lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
>>
>>
>> On May 29, 2013, at 4:25 AM, Maurizio Cimadamore <maurizio.cimadamore at oracle.com> 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