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