-
Bug
-
Resolution: Fixed
-
P2
-
None
-
b98
-
generic
-
generic
-
Verified
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.
----
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();
----
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 ).
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.
----
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();
----
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 ).