From 3f4cde3d2502f25cd42d9376f9cd17c0befbe0b2 Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Fri, 27 May 2016 21:26:19 +0300 Subject: [PATCH] NoSuchFieldError in Evaluate Expression on a property of a derived class (KT-12206) #KT-12206 Fixed --- ChangeLog.md | 1 + .../kotlin/codegen/JvmCodegenUtil.java | 11 +++++++--- .../bytecodeText/accessorForOverridenVal.kt | 20 +++++++++++++++++++ .../codegen/BytecodeTextTestGenerated.java | 6 ++++++ ...essToOverridenPropertyWithBackingField.out | 8 ++++++++ ...kt12206BasePropertyWithoutBackingField.out | 8 ++++++++ ...cessToOverridenPropertyWithBackingField.kt | 16 +++++++++++++++ .../kt12206BasePropertyWithoutBackingField.kt | 17 ++++++++++++++++ ...KotlinEvaluateExpressionTestGenerated.java | 12 +++++++++++ 9 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 compiler/testData/codegen/bytecodeText/accessorForOverridenVal.kt create mode 100644 idea/testData/debugger/tinyApp/outs/accessToOverridenPropertyWithBackingField.out create mode 100644 idea/testData/debugger/tinyApp/outs/kt12206BasePropertyWithoutBackingField.out create mode 100644 idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/accessToOverridenPropertyWithBackingField.kt create mode 100644 idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/kt12206BasePropertyWithoutBackingField.kt diff --git a/ChangeLog.md b/ChangeLog.md index 53674964566..0cfebe0f49b 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -250,6 +250,7 @@ - [`KT-11967`](https://youtrack.jetbrains.com/issue/KT-11967) Fix Find Usages/Rename for parameter references in XML files - [`KT-10770`](https://youtrack.jetbrains.com/issue/KT-10770) "Optimize imports" will not keep import if a type is only referenced by kdoc - [`KT-11955`](https://youtrack.jetbrains.com/issue/KT-11955) Copy/Paste inserts fully qualified name when copying function with overloads +- [`KT-12206`](https://youtrack.jetbrains.com/issue/KT-12206) Fix NoSuchFieldError on accessing base property without backing field in evaluate expression ### Reflection diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java index 9b885fe8460..b1ca978267e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java @@ -48,7 +48,6 @@ import org.jetbrains.kotlin.types.KotlinType; import java.io.File; import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.LOCAL_VARIABLE_DELEGATE; -import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.LOCAL_VARIABLE_PROPERTY_METADATA; import static org.jetbrains.kotlin.descriptors.Modality.ABSTRACT; import static org.jetbrains.kotlin.descriptors.Modality.FINAL; import static org.jetbrains.kotlin.resolve.BindingContext.DELEGATED_PROPERTY_CALL; @@ -151,8 +150,14 @@ public class JvmCodegenUtil { return false; } - // Only properties of the same class can be directly accessed, except when we are evaluating expressions in the debugger - if (!isCallInsideSameClassAsDeclared(property, context) && !isDebuggerContext(context)) return false; + if (!isDebuggerContext(context)) { + // Unless we are evaluating expression in debugger context, only properties of the same class can be directly accessed + if (!isCallInsideSameClassAsDeclared(property, context)) return false; + } + else { + // In debugger we want to access through accessors if they are present. If property overrides something accessors must be present. + if (!property.getOverriddenDescriptors().isEmpty()) return false; + } // Delegated and extension properties have no backing fields if (isDelegated || property.getExtensionReceiverParameter() != null) return false; diff --git a/compiler/testData/codegen/bytecodeText/accessorForOverridenVal.kt b/compiler/testData/codegen/bytecodeText/accessorForOverridenVal.kt new file mode 100644 index 00000000000..4a3016e4743 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/accessorForOverridenVal.kt @@ -0,0 +1,20 @@ +package b + +abstract class B { + open val propWithFinal: Int + get() = 1 + + open val propWithNonFinal: Int + get() = 2 +} + +abstract class Base: B() { + override final val propWithFinal: Int = 3 + override val propWithNonFinal: Int = 4 + + fun fooFinal() = this.propWithFinal + fun fooNonFinal() = this.propWithNonFinal +} + +// 2 GETFIELD b/Base.propWithFinal : I +// 1 INVOKEVIRTUAL b/Base.getPropWithNonFinal \(\)I \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 5b2bdd7c4db..0592ebee743 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -31,6 +31,12 @@ import java.util.regex.Pattern; @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { + @TestMetadata("accessorForOverridenVal.kt") + public void testAccessorForOverridenVal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/accessorForOverridenVal.kt"); + doTest(fileName); + } + @TestMetadata("accessorForProtected.kt") public void testAccessorForProtected() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/accessorForProtected.kt"); diff --git a/idea/testData/debugger/tinyApp/outs/accessToOverridenPropertyWithBackingField.out b/idea/testData/debugger/tinyApp/outs/accessToOverridenPropertyWithBackingField.out new file mode 100644 index 00000000000..70ebad5928a --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/accessToOverridenPropertyWithBackingField.out @@ -0,0 +1,8 @@ +LineBreakpoint created at accessToOverridenPropertyWithBackingField.kt:12 +!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! accessToOverridenPropertyWithBackingField.AccessToOverridenPropertyWithBackingFieldKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +accessToOverridenPropertyWithBackingField.kt:12 +Compile bytecode for d.prop +Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' + +Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/outs/kt12206BasePropertyWithoutBackingField.out b/idea/testData/debugger/tinyApp/outs/kt12206BasePropertyWithoutBackingField.out new file mode 100644 index 00000000000..111953ca158 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/kt12206BasePropertyWithoutBackingField.out @@ -0,0 +1,8 @@ +LineBreakpoint created at kt12206BasePropertyWithoutBackingField.kt:13 +!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! kt12206BasePropertyWithoutBackingField.Kt12206BasePropertyWithoutBackingFieldKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +kt12206BasePropertyWithoutBackingField.kt:13 +Compile bytecode for d.prop +Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' + +Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/accessToOverridenPropertyWithBackingField.kt b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/accessToOverridenPropertyWithBackingField.kt new file mode 100644 index 00000000000..2ffa4e0d960 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/accessToOverridenPropertyWithBackingField.kt @@ -0,0 +1,16 @@ +package accessToOverridenPropertyWithBackingField + +open class B1 { + val prop = "OK" +} + +class D1: B1() + +fun main(args: Array) { + val d = D1() + //Breakpoint! + d.prop // <-- breakpoint here +} + +// EXPRESSION: d.prop +// RESULT: "OK": Ljava/lang/String; \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/kt12206BasePropertyWithoutBackingField.kt b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/kt12206BasePropertyWithoutBackingField.kt new file mode 100644 index 00000000000..c0966e97c04 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/kt12206BasePropertyWithoutBackingField.kt @@ -0,0 +1,17 @@ +package kt12206BasePropertyWithoutBackingField + +abstract class Base { + val prop: String? + get() = "OK" +} + +class Derived : Base() + +fun main(args: Array) { + val d = Derived() + //Breakpoint! + d.prop // <-- breakpoint here +} + +// EXPRESSION: d.prop +// RESULT: "OK": Ljava/lang/String; \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java index 251ed4eb06a..57253a9a2eb 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java @@ -39,6 +39,12 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat doSingleBreakpointTest(fileName); } + @TestMetadata("accessToOverridenPropertyWithBackingField.kt") + public void testAccessToOverridenPropertyWithBackingField() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/accessToOverridenPropertyWithBackingField.kt"); + doSingleBreakpointTest(fileName); + } + public void testAllFilesPresentInSingleBreakpoint() throws Exception { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint"), Pattern.compile("^(.+)\\.kt$"), true); } @@ -205,6 +211,12 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat doSingleBreakpointTest(fileName); } + @TestMetadata("kt12206BasePropertyWithoutBackingField.kt") + public void testKt12206BasePropertyWithoutBackingField() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/kt12206BasePropertyWithoutBackingField.kt"); + doSingleBreakpointTest(fileName); + } + @TestMetadata("kt5554OnlyIntsShouldBeCoerced.kt") public void testKt5554OnlyIntsShouldBeCoerced() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/kt5554OnlyIntsShouldBeCoerced.kt");