Class verifier issues with unboxing method handles

Paul Sandoz paul.sandoz at oracle.com
Mon Jun 17 07:35:49 PDT 2013


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