diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/cfg/TypedMethodRefImpl.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/cfg/TypedMethodRefImpl.kt index 8ac72b6b5..488d96f8d 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/cfg/TypedMethodRefImpl.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/cfg/TypedMethodRefImpl.kt @@ -38,6 +38,10 @@ abstract class MethodSignatureRef( private val alwaysTrue: (JcTypedMethod) -> Boolean = { true } } + private fun predicate(additionalFilter: (JcTypedMethod) -> Boolean = alwaysTrue): (JcTypedMethod) -> Boolean = { + it.name == name && additionalFilter(it) && it.method.description == description + } + protected val description: String = buildString { append("(") argTypes.forEach { @@ -48,17 +52,16 @@ abstract class MethodSignatureRef( } private fun List.findMethod(filter: (JcTypedMethod) -> Boolean = alwaysTrue): JcTypedMethod? { - return firstOrNull { it.name == name && filter(it) && it.method.description == description } + return firstOrNull(predicate(filter)) } protected fun JcClassType.findTypedMethod(filter: (JcTypedMethod) -> Boolean = alwaysTrue): JcTypedMethod { - return findMethodOrNull(filter) ?: throw IllegalStateException(this.methodNotFoundMessage) + return findMethodOrNull(predicate(filter)) ?: throw IllegalStateException(this.methodNotFoundMessage) } protected fun JcClassType.findTypedMethodOrNull(filter: (JcTypedMethod) -> Boolean = alwaysTrue): JcTypedMethod? { - var methodOrNull = findMethodOrNull { - it.name == name && filter(it) && it.method.description == description - } + var methodOrNull = findMethodOrNull(predicate(filter)) + if (methodOrNull == null && jcClass.packageName == "java.lang.invoke") { methodOrNull = findMethodOrNull { val method = it.method @@ -194,11 +197,11 @@ class VirtualMethodRefImpl( } override val method: JcTypedMethod by softLazy { - actualType.findTypedMethodOrNull { !it.isPrivate } ?: declaredMethod + actualType.findTypedMethodOrNull() ?: declaredMethod } override val declaredMethod: JcTypedMethod by softLazy { - type.findTypedMethod { !it.isPrivate } + type.findTypedMethod() } } diff --git a/jacodb-core/src/test/kotlin/org/jacodb/testing/cfg/InstructionsTest.kt b/jacodb-core/src/test/kotlin/org/jacodb/testing/cfg/InstructionsTest.kt index 88a0b2ff3..dae1057fb 100644 --- a/jacodb-core/src/test/kotlin/org/jacodb/testing/cfg/InstructionsTest.kt +++ b/jacodb-core/src/test/kotlin/org/jacodb/testing/cfg/InstructionsTest.kt @@ -29,12 +29,12 @@ import org.jacodb.api.ext.cfg.callExpr import org.jacodb.api.ext.cfg.locals import org.jacodb.api.ext.cfg.values import org.jacodb.testing.BaseTest +import org.jacodb.testing.Common import org.jacodb.testing.WithDB import org.jacodb.testing.cfg.RealMethodResolution.Virtual import org.jacodb.testing.cfg.RealMethodResolution.VirtualImpl -import org.jacodb.testing.structure.FieldsAndMethods import org.jacodb.testing.primitives.Primitives -import org.jacodb.testing.Common +import org.jacodb.testing.structure.FieldsAndMethods import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test import org.junit.jupiter.api.condition.DisabledOnJre @@ -244,6 +244,15 @@ class InstructionsTest : BaseTest() { assertEquals(cp.boolean, fieldBoolean.fieldType) } + @Test + fun `private call with invokevirtual instruction`() { + val clazz = cp.findClass("VirtualInstructions") + val instList = clazz.declaredMethods.first { it.name == "run" }.instList + val callDoSmth = instList.mapNotNull { it.callExpr }. first { + it.toString().contains("doSmth") + } + assertEquals("doSmth", callDoSmth.method.method.name) + } } fun JcMethod.dumpInstructions(): String { diff --git a/jacodb-core/src/test/resources/samples/VirtualInstructions.class b/jacodb-core/src/test/resources/samples/VirtualInstructions.class new file mode 100644 index 000000000..621947ef4 Binary files /dev/null and b/jacodb-core/src/test/resources/samples/VirtualInstructions.class differ