diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/cfg/JcInstListBuilder.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/cfg/JcInstListBuilder.kt index 1c3dd68d6..dd72cdc3c 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/cfg/JcInstListBuilder.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/cfg/JcInstListBuilder.kt @@ -276,7 +276,8 @@ class JcInstListBuilder(val method: JcMethod,val instList: JcInstList private fun tryResolveJcLambdaExpr(expr: JcRawDynamicCallExpr): JcLambdaExpr? { if (expr.bsmArgs.size != 3) return null - val (interfaceMethodType, implementation, dynamicMethodType) = expr.bsmArgs + // RawInstListBuilder stores bootstrap arguments in reverse class-file order. + val (dynamicMethodType, implementation, interfaceMethodType) = expr.bsmArgs if (interfaceMethodType !is BsmMethodTypeArg) return null if (dynamicMethodType !is BsmMethodTypeArg) return null diff --git a/jacodb-core/src/test/kotlin/org/jacodb/testing/cfg/InvokeDynamicTest.kt b/jacodb-core/src/test/kotlin/org/jacodb/testing/cfg/InvokeDynamicTest.kt index 5868c8dd5..10571c54a 100644 --- a/jacodb-core/src/test/kotlin/org/jacodb/testing/cfg/InvokeDynamicTest.kt +++ b/jacodb-core/src/test/kotlin/org/jacodb/testing/cfg/InvokeDynamicTest.kt @@ -61,6 +61,27 @@ class InvokeDynamicTest : BaseInstructionsTest() { @Test fun `invoke dynamic constructor`() = runStaticMethod("testInvokeDynamicConstructor") + @Test + fun `erased and instantiated method types remain distinct for an unboxing reference`() { + assertLambdaMethodTypes(methodName = "makeWhitespacePredicate", implementationParameter = "int") + } + + @Test + fun `instantiated method type retains the wrapper before implementation widening`() { + assertLambdaMethodTypes(methodName = "makeWideningPredicate", implementationParameter = "long") + } + + private fun assertLambdaMethodTypes(methodName: String, implementationParameter: String) { + val clazz = cp.findClass() + val method = clazz.declaredMethods.single { it.name == methodName } + val lambda = method.instList.filterIsInstance() + .map { it.rhv }.filterIsInstance().single() + + assertEquals(listOf("java.lang.Object"), lambda.interfaceMethodType.argumentTypes.map { it.typeName }) + assertEquals(listOf("java.lang.Integer"), lambda.dynamicMethodType.argumentTypes.map { it.typeName }) + assertEquals(listOf(implementationParameter), lambda.actualMethod.method.parameters.map { it.type.typeName }) + } + private inline fun runStaticMethod(name: String) { val clazz = cp.findClass() diff --git a/jacodb-core/src/testFixtures/java/org/jacodb/testing/cfg/InvokeDynamicExamples.java b/jacodb-core/src/testFixtures/java/org/jacodb/testing/cfg/InvokeDynamicExamples.java index ac9ab4bce..4025ebd25 100644 --- a/jacodb-core/src/testFixtures/java/org/jacodb/testing/cfg/InvokeDynamicExamples.java +++ b/jacodb-core/src/testFixtures/java/org/jacodb/testing/cfg/InvokeDynamicExamples.java @@ -19,6 +19,7 @@ import java.util.HashMap; import java.util.Map; import java.util.function.Function; +import java.util.function.Predicate; public class InvokeDynamicExamples { @@ -161,4 +162,16 @@ public static String testNonStaticLambda() { String actual = collection.innerMap.get("deadabc"); return expected.equals(actual) ? "OK" : "BAD"; } + + public static Predicate makeWhitespacePredicate() { + return Character::isWhitespace; + } + + public static Predicate makeWideningPredicate() { + return InvokeDynamicExamples::positiveLong; + } + + private static boolean positiveLong(long value) { + return value > 0; + } }