diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/DebuggerClassNameProvider.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/DebuggerClassNameProvider.kt index 24ac1fbdb04..1ce1dfb3cdb 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/DebuggerClassNameProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/DebuggerClassNameProvider.kt @@ -30,11 +30,11 @@ import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.psi.util.PsiTreeUtil import com.intellij.xdebugger.impl.XDebugSessionImpl import org.jetbrains.kotlin.codegen.binding.CodegenBinding +import org.jetbrains.kotlin.codegen.coroutines.DO_RESUME_METHOD_NAME +import org.jetbrains.kotlin.codegen.coroutines.containsNonTailSuspensionCalls import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.fileClasses.NoResolveFileClassesProvider import org.jetbrains.kotlin.fileClasses.getFileClassInternalName import org.jetbrains.kotlin.idea.debugger.breakpoints.getLambdasAtLineIfAny @@ -145,27 +145,35 @@ class DebuggerClassNameProvider(val myDebugProcess: DebugProcess, val scopes: Li } } element is KtProperty && (!element.readAction { it.isTopLevel } || !isInLibrary) -> { - val descriptor = typeMapper.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element) - if (descriptor !is PropertyDescriptor) { - return CachedClassNames(classNamesForPosition(elementOfClassName, withInlines)) - } + val descriptor = typeMapper.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element) as? PropertyDescriptor ?: + return CachedClassNames(classNamesForPosition(elementOfClassName, withInlines)) return CachedClassNames(getJvmInternalNameForPropertyOwner(typeMapper, descriptor)) } element is KtNamedFunction -> { - val parentInternalName = if (elementOfClassName is KtClassOrObject) { - getClassNameForClass(elementOfClassName, typeMapper) - } - else if (elementOfClassName != null) { - val asmType = CodegenBinding.asmTypeForAnonymousClass(typeMapper.bindingContext, element) - asmType.internalName - } - else { - getClassNameForFile(file) + val descriptor = typeMapper.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element) + + val parentInternalName = when { + isSuspendDescriptor(descriptor, typeMapper.bindingContext) -> { + CodegenBinding.asmTypeForAnonymousClass(typeMapper.bindingContext, element).internalName + } + elementOfClassName is KtClassOrObject -> getClassNameForClass(elementOfClassName, typeMapper) + elementOfClassName != null -> { + val asmType = CodegenBinding.asmTypeForAnonymousClass(typeMapper.bindingContext, element) + asmType.internalName + } + else -> { + val descriptor = typeMapper.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element) + if (isSuspendDescriptor(descriptor, typeMapper.bindingContext)) { + CodegenBinding.asmTypeForAnonymousClass(typeMapper.bindingContext, element).internalName + } + else { + getClassNameForFile(file) + } + } } if (!withInlines) return NonCachedClassNames(parentInternalName) - val inlinedCalls = findInlinedCalls(element, typeMapper.bindingContext) if (parentInternalName == null) return CachedClassNames(inlinedCalls) @@ -178,6 +186,10 @@ class DebuggerClassNameProvider(val myDebugProcess: DebugProcess, val scopes: Li return CachedClassNames(getClassNameForFile(file)) } + private fun isSuspendDescriptor(descriptor: DeclarationDescriptor?, bindingContext: BindingContext): Boolean { + return descriptor is SimpleFunctionDescriptor && descriptor.isSuspend && descriptor.containsNonTailSuspensionCalls(bindingContext) + } + private fun inlineCallClassPatterns(typeMapper: KotlinTypeMapper, element: KtElement): List { val context = typeMapper.bindingContext @@ -215,10 +227,20 @@ class DebuggerClassNameProvider(val myDebugProcess: DebugProcess, val scopes: Li val originalInternalClassName = CodegenBinding.asmTypeForAnonymousClass( typeMapper.bindingContext, ktAnonymousClassElementProducer).internalName - val ownerDescriptorName = lexicalScope.ownerDescriptor.name + val ownerDescriptor = lexicalScope.ownerDescriptor - val mangledInternalClassName = originalInternalClassName.funPrefix() + (if (ownerDescriptorName.isSpecial) "\$\$special\$" else "$") + - InlineCodegenUtil.INLINE_TRANSFORMATION_SUFFIX + "$" + inlineFunctionName + val className = if (isSuspendDescriptor(ownerDescriptor, typeMapper.bindingContext)) { + originalInternalClassName.replaceAfterLast("$", DO_RESUME_METHOD_NAME) + } + else { + originalInternalClassName.funPrefix() + } + + val mangledInternalClassName = + className + + (if (ownerDescriptor.name.isSpecial) "\$\$special\$" else "$") + + InlineCodegenUtil.INLINE_TRANSFORMATION_SUFFIX + "$" + + inlineFunctionName return listOf("$mangledInternalClassName*") } diff --git a/idea/testData/debugger/tinyApp/outs/stopInInlinedLambdaInSuspendFunctionWithSuspendPointsInObjectLiteral.out b/idea/testData/debugger/tinyApp/outs/stopInInlinedLambdaInSuspendFunctionWithSuspendPointsInObjectLiteral.out new file mode 100644 index 00000000000..687f17a062d --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/stopInInlinedLambdaInSuspendFunctionWithSuspendPointsInObjectLiteral.out @@ -0,0 +1,8 @@ +LineBreakpoint created at stopInInlinedLambdaInSuspendFunctionWithSuspendPointsInObjectLiteral.kt:26 +!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! stopInInlinedLambdaInSuspendFunctionWithSuspendPointsInObjectLiteral.StopInInlinedLambdaInSuspendFunctionWithSuspendPointsInObjectLiteralKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +stopInInlinedLambdaInSuspendFunctionWithSuspendPointsInObjectLiteral.kt:26 +stopInInlinedLambdaInSuspendFunctionWithSuspendPointsInObjectLiteral.kt:27 +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/stopInSuspendFunctionWithSuspendPoints.out b/idea/testData/debugger/tinyApp/outs/stopInSuspendFunctionWithSuspendPoints.out new file mode 100644 index 00000000000..2b07dc2fd72 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/stopInSuspendFunctionWithSuspendPoints.out @@ -0,0 +1,8 @@ +LineBreakpoint created at stopInSuspendFunctionWithSuspendPoints.kt:16 +!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! stopInSuspendFunctionWithSuspendPoints.StopInSuspendFunctionWithSuspendPointsKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +stopInSuspendFunctionWithSuspendPoints.kt:16 +stopInSuspendFunctionWithSuspendPoints.kt:17 +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/stopInSuspendFunctionWithSuspendPointsInAnonymousObject.out b/idea/testData/debugger/tinyApp/outs/stopInSuspendFunctionWithSuspendPointsInAnonymousObject.out new file mode 100644 index 00000000000..686ea08fdfa --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/stopInSuspendFunctionWithSuspendPointsInAnonymousObject.out @@ -0,0 +1,8 @@ +LineBreakpoint created at stopInSuspendFunctionWithSuspendPointsInAnonymousObject.kt:20 +!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! stopInSuspendFunctionWithSuspendPointsInAnonymousObject.StopInSuspendFunctionWithSuspendPointsInAnonymousObjectKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +stopInSuspendFunctionWithSuspendPointsInAnonymousObject.kt:20 +stopInSuspendFunctionWithSuspendPointsInAnonymousObject.kt:21 +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/stopInSuspendFunctionWithSuspendPointsInObjectLiteralInInlineCallWithClosure.out b/idea/testData/debugger/tinyApp/outs/stopInSuspendFunctionWithSuspendPointsInObjectLiteralInInlineCallWithClosure.out new file mode 100644 index 00000000000..de37d48b287 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/stopInSuspendFunctionWithSuspendPointsInObjectLiteralInInlineCallWithClosure.out @@ -0,0 +1,8 @@ +LineBreakpoint created at stopInSuspendFunctionWithSuspendPointsInObjectLiteralInInlineCallWithClosure.kt:28 +!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! stopInSuspendFunctionWithSuspendPointsInObjectLiteralInInlineCallWithClosure.StopInSuspendFunctionWithSuspendPointsInObjectLiteralInInlineCallWithClosureKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +stopInSuspendFunctionWithSuspendPointsInObjectLiteralInInlineCallWithClosure.kt:28 +stopInSuspendFunctionWithSuspendPointsInObjectLiteralInInlineCallWithClosure.kt:29 +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/stepping/stepOver/stopInInlinedLambdaInSuspendFunctionWithSuspendPointsInObjectLiteral.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInInlinedLambdaInSuspendFunctionWithSuspendPointsInObjectLiteral.kt new file mode 100644 index 00000000000..cfcab1a480b --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInInlinedLambdaInSuspendFunctionWithSuspendPointsInObjectLiteral.kt @@ -0,0 +1,40 @@ +package stopInInlinedLambdaInSuspendFunctionWithSuspendPointsInObjectLiteral + +import forTests.builder + +fun foo(a: Any) {} + +suspend fun second() { } + +interface Bar { + suspend fun first() +} + +inline fun call(f: () -> Unit) { + f() +} + +val bar = object : Bar { + val t = 121 + + override suspend fun first() { + foo("first") + + call { + { + //Breakpoint! + foo(t) + }() + } + + second() + + foo("second") + } +} + +fun main(args: Array) { + builder { + bar.first() + } +} \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInSuspendFunctionWithSuspendPoints.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInSuspendFunctionWithSuspendPoints.kt new file mode 100644 index 00000000000..4c9e715b8be --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInSuspendFunctionWithSuspendPoints.kt @@ -0,0 +1,23 @@ +package stopInSuspendFunctionWithSuspendPoints + +import forTests.builder + +fun foo(a: Any) {} + +suspend fun second() { +} + +suspend fun first() { + foo("first") + + second() + + //Breakpoint! + foo("second") +} + +fun main(args: Array) { + builder { + first() + } +} \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInSuspendFunctionWithSuspendPointsInAnonymousObject.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInSuspendFunctionWithSuspendPointsInAnonymousObject.kt new file mode 100644 index 00000000000..a838ac86383 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInSuspendFunctionWithSuspendPointsInAnonymousObject.kt @@ -0,0 +1,28 @@ +package stopInSuspendFunctionWithSuspendPointsInAnonymousObject + +import forTests.builder + +fun foo(a: Any) {} + +suspend fun second() { } + +interface Bar { + suspend fun first() +} + +val bar = object : Bar { + override suspend fun first() { + foo("first") + + second() + + //Breakpoint! + foo("second") + } +} + +fun main(args: Array) { + builder { + bar.first() + } +} \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInSuspendFunctionWithSuspendPointsInObjectLiteralInInlineCallWithClosure.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInSuspendFunctionWithSuspendPointsInObjectLiteralInInlineCallWithClosure.kt new file mode 100644 index 00000000000..38331ab97c1 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInSuspendFunctionWithSuspendPointsInObjectLiteralInInlineCallWithClosure.kt @@ -0,0 +1,37 @@ +package stopInSuspendFunctionWithSuspendPointsInObjectLiteralInInlineCallWithClosure + +import forTests.builder + +fun foo(a: Any) {} + +suspend fun second() { + foo("call") +} + +interface F { + suspend fun test() {} +} + +inline fun call(f: () -> Unit) { + f() +} + +fun main(args: Array) { + val inClosure = "first" + call { + { + val some = object : F { + override suspend fun test() { + foo(inClosure) + second() + //Breakpoint! + foo("other") + } + } + + builder { + some.test() + } + }() + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java index ec77e1c36fa..d73319b025f 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java @@ -752,6 +752,12 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest { doStepOverTest(fileName); } + @TestMetadata("stopInInlinedLambdaInSuspendFunctionWithSuspendPointsInObjectLiteral.kt") + public void testStopInInlinedLambdaInSuspendFunctionWithSuspendPointsInObjectLiteral() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInInlinedLambdaInSuspendFunctionWithSuspendPointsInObjectLiteral.kt"); + doStepOverTest(fileName); + } + @TestMetadata("stopInLabdaOfCrossinlineCalledInAnonymous.kt") public void testStopInLabdaOfCrossinlineCalledInAnonymous() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInLabdaOfCrossinlineCalledInAnonymous.kt"); @@ -818,6 +824,24 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest { doStepOverTest(fileName); } + @TestMetadata("stopInSuspendFunctionWithSuspendPoints.kt") + public void testStopInSuspendFunctionWithSuspendPoints() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInSuspendFunctionWithSuspendPoints.kt"); + doStepOverTest(fileName); + } + + @TestMetadata("stopInSuspendFunctionWithSuspendPointsInAnonymousObject.kt") + public void testStopInSuspendFunctionWithSuspendPointsInAnonymousObject() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInSuspendFunctionWithSuspendPointsInAnonymousObject.kt"); + doStepOverTest(fileName); + } + + @TestMetadata("stopInSuspendFunctionWithSuspendPointsInObjectLiteralInInlineCallWithClosure.kt") + public void testStopInSuspendFunctionWithSuspendPointsInObjectLiteralInInlineCallWithClosure() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInSuspendFunctionWithSuspendPointsInObjectLiteralInInlineCallWithClosure.kt"); + doStepOverTest(fileName); + } + @TestMetadata("stopInSuspendFunctionWithoutSuspendPoints.kt") public void testStopInSuspendFunctionWithoutSuspendPoints() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInSuspendFunctionWithoutSuspendPoints.kt");