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.psi.util.PsiTreeUtil
|
||||||
import com.intellij.xdebugger.impl.XDebugSessionImpl
|
import com.intellij.xdebugger.impl.XDebugSessionImpl
|
||||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
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.inline.InlineCodegenUtil
|
||||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
|
||||||
import org.jetbrains.kotlin.fileClasses.NoResolveFileClassesProvider
|
import org.jetbrains.kotlin.fileClasses.NoResolveFileClassesProvider
|
||||||
import org.jetbrains.kotlin.fileClasses.getFileClassInternalName
|
import org.jetbrains.kotlin.fileClasses.getFileClassInternalName
|
||||||
import org.jetbrains.kotlin.idea.debugger.breakpoints.getLambdasAtLineIfAny
|
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) -> {
|
element is KtProperty && (!element.readAction { it.isTopLevel } || !isInLibrary) -> {
|
||||||
val descriptor = typeMapper.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element)
|
val descriptor = typeMapper.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element) as? PropertyDescriptor ?:
|
||||||
if (descriptor !is PropertyDescriptor) {
|
return CachedClassNames(classNamesForPosition(elementOfClassName, withInlines))
|
||||||
return CachedClassNames(classNamesForPosition(elementOfClassName, withInlines))
|
|
||||||
}
|
|
||||||
|
|
||||||
return CachedClassNames(getJvmInternalNameForPropertyOwner(typeMapper, descriptor))
|
return CachedClassNames(getJvmInternalNameForPropertyOwner(typeMapper, descriptor))
|
||||||
}
|
}
|
||||||
element is KtNamedFunction -> {
|
element is KtNamedFunction -> {
|
||||||
val parentInternalName = if (elementOfClassName is KtClassOrObject) {
|
val descriptor = typeMapper.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element)
|
||||||
getClassNameForClass(elementOfClassName, typeMapper)
|
|
||||||
}
|
val parentInternalName = when {
|
||||||
else if (elementOfClassName != null) {
|
isSuspendDescriptor(descriptor, typeMapper.bindingContext) -> {
|
||||||
val asmType = CodegenBinding.asmTypeForAnonymousClass(typeMapper.bindingContext, element)
|
CodegenBinding.asmTypeForAnonymousClass(typeMapper.bindingContext, element).internalName
|
||||||
asmType.internalName
|
}
|
||||||
}
|
elementOfClassName is KtClassOrObject -> getClassNameForClass(elementOfClassName, typeMapper)
|
||||||
else {
|
elementOfClassName != null -> {
|
||||||
getClassNameForFile(file)
|
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)
|
if (!withInlines) return NonCachedClassNames(parentInternalName)
|
||||||
|
|
||||||
val inlinedCalls = findInlinedCalls(element, typeMapper.bindingContext)
|
val inlinedCalls = findInlinedCalls(element, typeMapper.bindingContext)
|
||||||
if (parentInternalName == null) return CachedClassNames(inlinedCalls)
|
if (parentInternalName == null) return CachedClassNames(inlinedCalls)
|
||||||
|
|
||||||
@@ -178,6 +186,10 @@ class DebuggerClassNameProvider(val myDebugProcess: DebugProcess, val scopes: Li
|
|||||||
return CachedClassNames(getClassNameForFile(file))
|
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> {
|
private fun inlineCallClassPatterns(typeMapper: KotlinTypeMapper, element: KtElement): List<String> {
|
||||||
val context = typeMapper.bindingContext
|
val context = typeMapper.bindingContext
|
||||||
|
|
||||||
@@ -215,10 +227,20 @@ class DebuggerClassNameProvider(val myDebugProcess: DebugProcess, val scopes: Li
|
|||||||
val originalInternalClassName = CodegenBinding.asmTypeForAnonymousClass(
|
val originalInternalClassName = CodegenBinding.asmTypeForAnonymousClass(
|
||||||
typeMapper.bindingContext, ktAnonymousClassElementProducer).internalName
|
typeMapper.bindingContext, ktAnonymousClassElementProducer).internalName
|
||||||
|
|
||||||
val ownerDescriptorName = lexicalScope.ownerDescriptor.name
|
val ownerDescriptor = lexicalScope.ownerDescriptor
|
||||||
|
|
||||||
val mangledInternalClassName = originalInternalClassName.funPrefix() + (if (ownerDescriptorName.isSpecial) "\$\$special\$" else "$") +
|
val className = if (isSuspendDescriptor(ownerDescriptor, typeMapper.bindingContext)) {
|
||||||
InlineCodegenUtil.INLINE_TRANSFORMATION_SUFFIX + "$" + inlineFunctionName
|
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*")
|
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);
|
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")
|
@TestMetadata("stopInLabdaOfCrossinlineCalledInAnonymous.kt")
|
||||||
public void testStopInLabdaOfCrossinlineCalledInAnonymous() throws Exception {
|
public void testStopInLabdaOfCrossinlineCalledInAnonymous() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInLabdaOfCrossinlineCalledInAnonymous.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInLabdaOfCrossinlineCalledInAnonymous.kt");
|
||||||
@@ -818,6 +824,24 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
|
|||||||
doStepOverTest(fileName);
|
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")
|
@TestMetadata("stopInSuspendFunctionWithoutSuspendPoints.kt")
|
||||||
public void testStopInSuspendFunctionWithoutSuspendPoints() throws Exception {
|
public void testStopInSuspendFunctionWithoutSuspendPoints() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInSuspendFunctionWithoutSuspendPoints.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInSuspendFunctionWithoutSuspendPoints.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user