diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/DebuggerClassNameProvider.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/DebuggerClassNameProvider.kt index c83248571ce..cca4547efef 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/DebuggerClassNameProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/DebuggerClassNameProvider.kt @@ -46,6 +46,7 @@ import org.jetbrains.kotlin.idea.search.usagesSearch.isImportUsage import org.jetbrains.kotlin.idea.util.ProjectRootsUtil import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.parents @@ -187,7 +188,7 @@ class DebuggerClassNameProvider(val myDebugProcess: DebugProcess, val scopes: Li private fun inlineCallClassPatterns(typeMapper: KotlinTypeMapper, element: KtElement): List { val context = typeMapper.bindingContext - val (inlineCall, ktAnonymousClassElementProducer) = runReadAction { + val inlineCall = runReadAction { element.parents.map { val ktCallExpression: KtCallExpression = when(it) { is KtFunctionLiteral -> { @@ -210,31 +211,26 @@ class DebuggerClassNameProvider(val myDebugProcess: DebugProcess, val scopes: Li ktCallExpression to (it as KtElement) }.lastOrNull { it != null && isInlineCall(context, it.component1()) - } + }?.first } ?: return emptyList() val lexicalScope = context[BindingContext.LEXICAL_SCOPE, inlineCall] ?: return emptyList() + val baseClassName = classNamesForPosition(inlineCall, false).firstOrNull() ?: return emptyList() val resolvedCall = runReadAction { inlineCall.getResolvedCall(context) } ?: return emptyList() val inlineFunctionName = resolvedCall.resultingDescriptor.name - val originalInternalClassName = CodegenBinding.asmTypeForAnonymousClass( - typeMapper.bindingContext, ktAnonymousClassElementProducer).internalName - val ownerDescriptor = lexicalScope.ownerDescriptor - - val className = if (isFunctionWithSuspendStateMachine(ownerDescriptor, typeMapper.bindingContext)) { - originalInternalClassName.replaceAfterLast("$", DO_RESUME_METHOD_NAME) + val ownerDescriptorName = if (isFunctionWithSuspendStateMachine(ownerDescriptor, typeMapper.bindingContext)) { + Name.identifier(DO_RESUME_METHOD_NAME) } else { - originalInternalClassName.funPrefix() + ownerDescriptor.name } + val ownerJvmName = if (ownerDescriptorName.isSpecial) InlineCodegenUtil.SPECIAL_TRANSFORMATION_NAME else ownerDescriptorName.asString() val mangledInternalClassName = - className + - (if (ownerDescriptor.name.isSpecial) "$" + InlineCodegenUtil.SPECIAL_TRANSFORMATION_NAME else "") + - InlineCodegenUtil.INLINE_CALL_TRANSFORMATION_SUFFIX + "$" + - inlineFunctionName + baseClassName + "$" + ownerJvmName + InlineCodegenUtil.INLINE_CALL_TRANSFORMATION_SUFFIX + "$" + inlineFunctionName return listOf("$mangledInternalClassName*") } @@ -254,7 +250,7 @@ class DebuggerClassNameProvider(val myDebugProcess: DebugProcess, val scopes: Li KtSecondaryConstructor::class.java) private fun getElementToCalculateClassName(notPositionedElement: PsiElement?): KtElement? { - if (notPositionedElement?.javaClass as Class<*> in TYPES_TO_CALCULATE_CLASSNAME) return notPositionedElement as KtElement + if (notPositionedElement?.let { it::class.java } as Class<*> in TYPES_TO_CALCULATE_CLASSNAME) return notPositionedElement as KtElement return readAction { PsiTreeUtil.getParentOfType(notPositionedElement, *TYPES_TO_CALCULATE_CLASSNAME) } } @@ -411,17 +407,6 @@ private fun isInlineCall(context: BindingContext, expr: KtCallExpression): Boole private inline fun PsiElement.typedParent(): T? = getStrictParentOfType() -private fun String.funPrefix(): String { - if (lastIndexOf("$") < 0) return this - - val trimmed = trimEnd { it == '$' || it.isDigit() } // Can accidentally trim end of function name if it ends with a number - val nextDollarIndex = indexOf('$', startIndex = trimmed.length - 1) - - if (nextDollarIndex < 0) return this - - return this.substring(0, nextDollarIndex) -} - private fun String.substringIndex(): String { if (lastIndexOf("$") < 0) return this diff --git a/idea/testData/debugger/tinyApp/outs/stopInObjectLiteralInInlineCallWithClosureInAnonymous.out b/idea/testData/debugger/tinyApp/outs/stopInObjectLiteralInInlineCallWithClosureInAnonymous.out new file mode 100644 index 00000000000..8628bec7679 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/stopInObjectLiteralInInlineCallWithClosureInAnonymous.out @@ -0,0 +1,8 @@ +LineBreakpoint created at stopInObjectLiteralInInlineCallWithClosureInAnonymous.kt:11 +!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! stopInObjectLiteralInInlineCallWithClosureInAnonymous.StopInObjectLiteralInInlineCallWithClosureInAnonymousKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +stopInObjectLiteralInInlineCallWithClosureInAnonymous.kt:11 +stopInObjectLiteralInInlineCallWithClosureInAnonymous.kt:12 +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/stopInObjectLiteralInInlineCallWithClosureInAnonymous.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInObjectLiteralInInlineCallWithClosureInAnonymous.kt new file mode 100644 index 00000000000..e0f38eb7c3b --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInObjectLiteralInInlineCallWithClosureInAnonymous.kt @@ -0,0 +1,23 @@ +package stopInObjectLiteralInInlineCallWithClosureInAnonymous + +fun main(args: Array) { + val a = 12 + + { + inlineF { + val s = object : () -> Unit { + override fun invoke() { + //Breakpoint! + nop(a) + nop(a) + } + } + + s() + } + }() +} + +inline fun inlineF(block: () -> R): R = block() + +fun nop(a: Any) {} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java index 107c82b9173..b53d7528008 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java @@ -824,6 +824,12 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest { doStepOverTest(fileName); } + @TestMetadata("stopInObjectLiteralInInlineCallWithClosureInAnonymous.kt") + public void testStopInObjectLiteralInInlineCallWithClosureInAnonymous() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInObjectLiteralInInlineCallWithClosureInAnonymous.kt"); + doStepOverTest(fileName); + } + @TestMetadata("stopInSuspendFunctionWithSuspendPoints.kt") public void testStopInSuspendFunctionWithSuspendPoints() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInSuspendFunctionWithSuspendPoints.kt");