Make breakpoints work in suspend functions
This commit is contained in:
@@ -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<String> {
|
||||
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*")
|
||||
}
|
||||
|
||||
+8
@@ -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
|
||||
+8
@@ -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
|
||||
Vendored
+8
@@ -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
|
||||
+8
@@ -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
|
||||
+40
@@ -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<String>) {
|
||||
builder {
|
||||
bar.first()
|
||||
}
|
||||
}
|
||||
Vendored
+23
@@ -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<String>) {
|
||||
builder {
|
||||
first()
|
||||
}
|
||||
}
|
||||
+28
@@ -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<String>) {
|
||||
builder {
|
||||
bar.first()
|
||||
}
|
||||
}
|
||||
+37
@@ -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<String>) {
|
||||
val inClosure = "first"
|
||||
call {
|
||||
{
|
||||
val some = object : F {
|
||||
override suspend fun test() {
|
||||
foo(inClosure)
|
||||
second()
|
||||
//Breakpoint!
|
||||
foo("other")
|
||||
}
|
||||
}
|
||||
|
||||
builder {
|
||||
some.test()
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user