diff --git a/usvm-jvm-instrumentation/src/main/kotlin/org/usvm/instrumentation/instrumentation/JcInstructionTracer.kt b/usvm-jvm-instrumentation/src/main/kotlin/org/usvm/instrumentation/instrumentation/JcInstructionTracer.kt index b1d0c25d3b..07c0af0bab 100644 --- a/usvm-jvm-instrumentation/src/main/kotlin/org/usvm/instrumentation/instrumentation/JcInstructionTracer.kt +++ b/usvm-jvm-instrumentation/src/main/kotlin/org/usvm/instrumentation/instrumentation/JcInstructionTracer.kt @@ -121,17 +121,24 @@ object JcInstructionTracer : Tracer { accessType: StaticFieldAccessType, jcClasspath: JcClasspath ): Long { - val jcClass = + var jcClass = jcRawFieldRef.declaringClass.toJcClassOrInterface(jcClasspath) ?: error("Can't find class in classpath") - val encodedClass = encodedClasses.getOrPut(jcClass) { EncodedClass(currentClassIndex++) } - val indexedJcField = - jcClass.declaredFields.withIndex() - .find { it.value.isStatic && it.value.name == jcRawFieldRef.fieldName } - ?: error("Field not found") - val accessTypeId = accessType.ordinal.toLong() - val instId = encodeStaticFieldAccessId(encodedClass.id, indexedJcField.index.toLong(), accessTypeId) - encodedJcStaticFieldRef[instId] = indexedJcField.value to accessType - return instId + while (true) { + val encodedClass = encodedClasses.getOrPut(jcClass) { EncodedClass(currentClassIndex++) } + val indexedJcField = + jcClass.declaredFields.withIndex() + .find { it.value.isStatic && it.value.name == jcRawFieldRef.fieldName } + if (indexedJcField == null) { + // static fields can be accessed via subclass of declaring class + jcClass = jcClass.superClass + ?: error("Field `${jcRawFieldRef.declaringClass.typeName}.${jcRawFieldRef.fieldName}` not found") + continue + } + val accessTypeId = accessType.ordinal.toLong() + val instId = encodeStaticFieldAccessId(encodedClass.id, indexedJcField.index.toLong(), accessTypeId) + encodedJcStaticFieldRef[instId] = indexedJcField.value to accessType + return instId + } } private fun decode(jcInstructionId: Long): JcInst = diff --git a/usvm-jvm-instrumentation/src/samples/java/example/ClassWithStaticField.java b/usvm-jvm-instrumentation/src/samples/java/example/ClassWithStaticField.java new file mode 100644 index 0000000000..30e0576730 --- /dev/null +++ b/usvm-jvm-instrumentation/src/samples/java/example/ClassWithStaticField.java @@ -0,0 +1,5 @@ +package example; + +public class ClassWithStaticField { + public static String STATIC_FIELD = "static field content"; +} diff --git a/usvm-jvm-instrumentation/src/samples/java/example/ParentStaticFieldUser.java b/usvm-jvm-instrumentation/src/samples/java/example/ParentStaticFieldUser.java new file mode 100644 index 0000000000..fdd70f465e --- /dev/null +++ b/usvm-jvm-instrumentation/src/samples/java/example/ParentStaticFieldUser.java @@ -0,0 +1,7 @@ +package example; + +public class ParentStaticFieldUser extends ClassWithStaticField { + public static String getParentStaticField() { + return ParentStaticFieldUser.STATIC_FIELD; + } +} diff --git a/usvm-jvm-instrumentation/src/test/kotlin/org/usvm/instrumentation/executor/ConcreteExecutorTests.kt b/usvm-jvm-instrumentation/src/test/kotlin/org/usvm/instrumentation/executor/ConcreteExecutorTests.kt index 8aa5af2c2a..8d37f6241e 100644 --- a/usvm-jvm-instrumentation/src/test/kotlin/org/usvm/instrumentation/executor/ConcreteExecutorTests.kt +++ b/usvm-jvm-instrumentation/src/test/kotlin/org/usvm/instrumentation/executor/ConcreteExecutorTests.kt @@ -9,6 +9,7 @@ import org.usvm.instrumentation.testcase.api.UTestExecutionExceptionResult import org.usvm.instrumentation.testcase.api.UTestExecutionSuccessResult import org.usvm.instrumentation.testcase.descriptor.UTestConstantDescriptor import org.usvm.instrumentation.util.UTestCreator +import kotlin.test.assertContains import kotlin.test.assertEquals import kotlin.test.assertIs import kotlin.test.assertNotNull @@ -248,4 +249,16 @@ class ConcreteExecutorTests: UTestConcreteExecutorTest() { assertIs(res) } + @Test + fun `get parent static field`() = executeTest { + val uTest = UTestCreator.ParentStaticFieldUser.getParentStaticField(jcClasspath) + val res = uTestConcreteExecutor.executeAsync(uTest) + assertIs(res) + val result = res.result + assertNotNull(result) + assertIs(result) + assertEquals("static field content", result.value) + assertContains(res.resultState.statics.keys.map { it.name }, "STATIC_FIELD") + } + } \ No newline at end of file diff --git a/usvm-jvm-instrumentation/src/test/kotlin/org/usvm/instrumentation/util/UTestCreator.kt b/usvm-jvm-instrumentation/src/test/kotlin/org/usvm/instrumentation/util/UTestCreator.kt index a7776b97c7..99b27530f6 100644 --- a/usvm-jvm-instrumentation/src/test/kotlin/org/usvm/instrumentation/util/UTestCreator.kt +++ b/usvm-jvm-instrumentation/src/test/kotlin/org/usvm/instrumentation/util/UTestCreator.kt @@ -498,4 +498,13 @@ object UTestCreator { return UTest(listOf(), methodCall) } } + + object ParentStaticFieldUser { + fun getParentStaticField(jcClasspath: JcClasspath): UTest { + val jcClass = jcClasspath.findClass("example.ParentStaticFieldUser") + val jcMethod = jcClass.declaredMethods.find { it.name == "getParentStaticField" }!! + + return UTest(listOf(), UTestStaticMethodCall(jcMethod, emptyList())) + } + } } \ No newline at end of file