From 9ae8a8abf141045516221c0f8b67b079e8d6d775 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Wed, 17 Apr 2019 01:24:15 +0300 Subject: [PATCH] Debugger: Fix method evaluation on arrays (KT-11706) --- .../src/org/jetbrains/eval4j/jdi/jdiEval.kt | 36 ++++++++++++++----- .../evaluate/singleBreakpoint/arrayMethods.kt | 15 ++++++++ .../singleBreakpoint/arrayMethods.out | 10 ++++++ ...KotlinEvaluateExpressionTestGenerated.java | 5 +++ 4 files changed, 58 insertions(+), 8 deletions(-) create mode 100644 idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/arrayMethods.kt create mode 100644 idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/arrayMethods.out diff --git a/idea/jvm-debugger/eval4j/src/org/jetbrains/eval4j/jdi/jdiEval.kt b/idea/jvm-debugger/eval4j/src/org/jetbrains/eval4j/jdi/jdiEval.kt index c5bf9aee51b..ba42329d82f 100644 --- a/idea/jvm-debugger/eval4j/src/org/jetbrains/eval4j/jdi/jdiEval.kt +++ b/idea/jvm-debugger/eval4j/src/org/jetbrains/eval4j/jdi/jdiEval.kt @@ -219,13 +219,33 @@ class JDIEval( mayThrow { _class.setValue(field, jdiValue) }.ifFail(field) } - private fun findMethod(methodDesc: MethodDescription, _class: ReferenceType = methodDesc.ownerType.asReferenceType()): Method { + private fun findMethod(methodDesc: MethodDescription, clazz: ReferenceType = methodDesc.ownerType.asReferenceType()): Method { + val method = findMethodOrNull(methodDesc, clazz) + if (method != null) { + return method + } + + throwBrokenCodeException(NoSuchMethodError("Method not found: $methodDesc")) + } + + private fun findMethodOrNull(methodDesc: MethodDescription, clazz: ReferenceType): Method? { val methodName = methodDesc.name - val method = when (_class) { - is ClassType -> - _class.concreteMethodByName(methodName, methodDesc.desc) - else -> - _class.methodsByName(methodName, methodDesc.desc).firstOrNull() + + var method: Method? + when (clazz) { + is ClassType -> { + method = clazz.concreteMethodByName(methodName, methodDesc.desc) + } + is ArrayType -> { // Copied from com.intellij.debugger.engine.DebuggerUtils.findMethod + val objectType = OBJECT.asReferenceType() + method = findMethodOrNull(methodDesc, objectType) + if (method == null && methodDesc.name == "clone" && methodDesc.desc == "()[Ljava/lang/Object;") { + method = findMethodOrNull(MethodDescription(OBJECT.internalName, "clone", "()[Ljava/lang/Object;", false), objectType) + } + } + else -> { + method = clazz.methodsByName(methodName, methodDesc.desc).firstOrNull() + } } if (method != null) { @@ -235,7 +255,7 @@ class JDIEval( // Module name can be different for internal functions during evaluation and compilation val internalNameWithoutSuffix = internalNameWithoutModuleSuffix(methodName) if (internalNameWithoutSuffix != null) { - val internalMethods = _class.visibleMethods().filter { + val internalMethods = clazz.visibleMethods().filter { val name = it.name() name.startsWith(internalNameWithoutSuffix) && canBeMangledInternalName(name) && it.signature() == methodDesc.desc } @@ -246,7 +266,7 @@ class JDIEval( } } - throwBrokenCodeException(NoSuchMethodError("Method not found: $methodDesc")) + return null } override fun invokeStaticMethod(methodDesc: MethodDescription, arguments: List): Value { diff --git a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/arrayMethods.kt b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/arrayMethods.kt new file mode 100644 index 00000000000..2c0cfc994d0 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/arrayMethods.kt @@ -0,0 +1,15 @@ +package arrayMethods + +fun main(args: Array) { + //Breakpoint! + val a = 5 +} + +// EXPRESSION: args.toString().length > 0 +// RESULT: 1: Z + +// EXPRESSION: args.hashCode() and 0 +// RESULT: 0: I + +// EXPRESSION: args.clone() +// RESULT: instance of java.lang.String[0] (id=ID): [Ljava/lang/String; \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/arrayMethods.out b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/arrayMethods.out new file mode 100644 index 00000000000..749fa60f0dc --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/arrayMethods.out @@ -0,0 +1,10 @@ +LineBreakpoint created at arrayMethods.kt:5 +Run Java +Connected to the target VM +arrayMethods.kt:5 +Compile bytecode for args.toString().length > 0 +Compile bytecode for args.hashCode() and 0 +Compile bytecode for args.clone() +Disconnected from the target VM + +Process finished with exit code 0 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 cafd7087553..2234b6a41c8 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java @@ -51,6 +51,11 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat runTest("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/anonymousObjects.kt"); } + @TestMetadata("arrayMethods.kt") + public void testArrayMethods() throws Exception { + runTest("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/arrayMethods.kt"); + } + @TestMetadata("arrays.kt") public void testArrays() throws Exception { runTest("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/arrays.kt");