Debugger: Fix breakpoints and stepping for inline-only function lambda arguments (#KT-23064)

This commit is contained in:
Yan Zhulanow
2018-03-06 00:46:29 +03:00
parent ae37cc30a1
commit eec9c6bffa
16 changed files with 238 additions and 49 deletions
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.codegen.inline package org.jetbrains.kotlin.codegen.inline
import org.jetbrains.kotlin.backend.jvm.codegen.IrExpressionLambda import org.jetbrains.kotlin.backend.jvm.codegen.IrExpressionLambda
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.ClosureCodegen import org.jetbrains.kotlin.codegen.ClosureCodegen
import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.codegen.StackValue
@@ -368,7 +369,14 @@ class MethodInliner(
) )
val transformationVisitor = object : MethodVisitor(API, transformedNode) { val transformationVisitor = object : MethodVisitor(API, transformedNode) {
private val GENERATE_DEBUG_INFO = GENERATE_SMAP && inlineOnlySmapSkipper == null /*
Ignore simple @InlineOnly functions such as 'error()' or 'assert()' without lambda parameters,
as we likely to want to have a line number from the call site in a stack trace.
*/
private val GENERATE_LINE_NUMBERS = GENERATE_SMAP && (inlineOnlySmapSkipper == null || run {
val callableDescriptor = inliningContext.root.sourceCompilerForInline.callableDescriptor
callableDescriptor != null && callableDescriptor.valueParameters.any { it.type.isFunctionType }
})
private val isInliningLambda = nodeRemapper.isInsideInliningLambda private val isInliningLambda = nodeRemapper.isInsideInliningLambda
@@ -400,7 +408,7 @@ class MethodInliner(
} }
override fun visitLineNumber(line: Int, start: Label) { override fun visitLineNumber(line: Int, start: Label) {
if (isInliningLambda || GENERATE_DEBUG_INFO) { if (isInliningLambda || GENERATE_LINE_NUMBERS) {
super.visitLineNumber(line, start) super.visitLineNumber(line, start)
} }
} }
@@ -428,7 +436,7 @@ class MethodInliner(
override fun visitLocalVariable( override fun visitLocalVariable(
name: String, desc: String, signature: String?, start: Label, end: Label, index: Int name: String, desc: String, signature: String?, start: Label, end: Label, index: Int
) { ) {
if (isInliningLambda || GENERATE_DEBUG_INFO) { if (isInliningLambda || (GENERATE_SMAP && inlineOnlySmapSkipper == null)) {
val varSuffix = if (inliningContext.isRoot && !isFakeLocalVariableForInline(name)) INLINE_FUN_VAR_SUFFIX else "" val varSuffix = if (inliningContext.isRoot && !isFakeLocalVariableForInline(name)) INLINE_FUN_VAR_SUFFIX else ""
val varName = if (!varSuffix.isEmpty() && name == "this") name + "_" else name val varName = if (!varSuffix.isEmpty() && name == "this") name + "_" else name
super.visitLocalVariable(varName + varSuffix, desc, signature, start, end, getNewIndex(index)) super.visitLocalVariable(varName + varSuffix, desc, signature, start, end, getNewIndex(index))
@@ -46,6 +46,8 @@ interface SourceCompilerForInline {
val callElement: Any val callElement: Any
val callableDescriptor: CallableDescriptor?
val lookupLocation: LookupLocation val lookupLocation: LookupLocation
val callElementText: String val callElementText: String
@@ -99,6 +101,8 @@ class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, overrid
override val lookupLocation = KotlinLookupLocation(callElement) override val lookupLocation = KotlinLookupLocation(callElement)
override val callableDescriptor: CallableDescriptor?
get() = (this.context as? MethodContext)?.functionDescriptor
override val callElementText by lazy { override val callElementText by lazy {
callElement.text callElement.text
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.codegen.OwnerKind
import org.jetbrains.kotlin.codegen.SourceInfo import org.jetbrains.kotlin.codegen.SourceInfo
import org.jetbrains.kotlin.codegen.inline.* import org.jetbrains.kotlin.codegen.inline.*
import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.incremental.components.LookupLocation
@@ -50,6 +51,9 @@ class IrSourceCompilerForInline(
override val callElementText: String override val callElementText: String
get() = callElement.toString() get() = callElement.toString()
override val callableDescriptor: CallableDescriptor
get() = callElement.descriptor
override val callsiteFile: PsiFile? override val callsiteFile: PsiFile?
get() = TODO("not implemented") get() = TODO("not implemented")
@@ -87,22 +87,26 @@ Kotlin
*F *F
+ 1 2.kt + 1 2.kt
_2Kt _2Kt
+ 2 1.kt + 2 Standard.kt
test/_1Kt kotlin/StandardKt__StandardKt
+ 3 1.kt + 3 1.kt
test/_1Kt
+ 4 1.kt
test/_1Kt$lParams$1 test/_1Kt$lParams$1
*L *L
1#1,10:1 1#1,10:1
31#2,5:11 34#2,4:11
29#2:17 31#3,5:15
32#3:16 29#3:21
32#4:20
*E *E
*S KotlinDebug *S KotlinDebug
*F *F
+ 1 2.kt + 1 2.kt
_2Kt _2Kt
*L *L
5#1,5:11 5#1,4:11
5#1:17 5#1,5:15
5#1:16 5#1:21
5#1:20
*E *E
@@ -123,26 +123,30 @@ Kotlin
*F *F
+ 1 2.kt + 1 2.kt
_2Kt _2Kt
+ 2 1.kt + 2 Standard.kt
test/_1Kt kotlin/StandardKt__StandardKt
+ 3 1.kt + 3 1.kt
test/_1Kt
+ 4 1.kt
test/_1Kt$lParams$1 test/_1Kt$lParams$1
*L *L
1#1,10:1 1#1,10:1
31#2:11 34#2,4:11
70#2,2:12 31#3:15
29#2:15 70#3,2:16
50#3:14 29#3:19
68#3:16 50#4:18
68#4:20
*E *E
*S KotlinDebug *S KotlinDebug
*F *F
+ 1 2.kt + 1 2.kt
_2Kt _2Kt
*L *L
5#1:11 5#1,4:11
5#1,2:12
5#1:15 5#1:15
5#1:14 5#1,2:16
5#1:16 5#1:19
5#1:18
5#1:20
*E *E
@@ -112,24 +112,28 @@ Kotlin
*F *F
+ 1 2.kt + 1 2.kt
_2Kt _2Kt
+ 2 1.kt + 2 Standard.kt
test/_1Kt kotlin/StandardKt__StandardKt
+ 3 1.kt + 3 1.kt
test/_1Kt
+ 4 1.kt
test/_1Kt$lParams$1 test/_1Kt$lParams$1
*L *L
1#1,10:1 1#1,10:1
31#2,5:11 34#2,4:11
29#2:17 31#3,5:15
32#3:16 29#3:21
32#4:20
*E *E
*S KotlinDebug *S KotlinDebug
*F *F
+ 1 2.kt + 1 2.kt
_2Kt _2Kt
*L *L
5#1,5:11 5#1,4:11
5#1:17 5#1,5:15
5#1:16 5#1:21
5#1:20
*E *E
// FILE: 2.smap-separate-compilation // FILE: 2.smap-separate-compilation
@@ -140,24 +144,28 @@ Kotlin
*F *F
+ 1 2.kt + 1 2.kt
_2Kt _2Kt
+ 2 1.kt + 2 Standard.kt
test/_1Kt kotlin/StandardKt__StandardKt
+ 3 1.kt + 3 1.kt
test/_1Kt
+ 4 1.kt
test/_1Kt$lParams$1 test/_1Kt$lParams$1
*L *L
1#1,10:1 1#1,10:1
31#2,5:11 34#2,4:11
29#2:17 31#3,5:15
32#3:16 29#3:21
32#4:20
*E *E
*S KotlinDebug *S KotlinDebug
*F *F
+ 1 2.kt + 1 2.kt
_2Kt _2Kt
*L *L
5#1,5:11 5#1,4:11
5#1:17 5#1,5:15
5#1:16 5#1:21
5#1:20
*E *E
SMAP SMAP
@@ -76,20 +76,24 @@ Kotlin
*F *F
+ 1 2.kt + 1 2.kt
_2Kt _2Kt
+ 2 1.kt + 2 Standard.kt
test/_1Kt kotlin/StandardKt__StandardKt
+ 3 1.kt + 3 1.kt
test/_1Kt
+ 4 1.kt
test/_1Kt$lParams$1 test/_1Kt$lParams$1
*L *L
1#1,10:1 1#1,10:1
30#2,5:11 34#2,4:11
31#3:16 30#3,5:15
31#4:20
*E *E
*S KotlinDebug *S KotlinDebug
*F *F
+ 1 2.kt + 1 2.kt
_2Kt _2Kt
*L *L
5#1,5:11 5#1,4:11
5#1:16 5#1,5:15
5#1:20
*E *E
@@ -20,6 +20,16 @@ Kotlin
*F *F
+ 1 2.kt + 1 2.kt
_2Kt _2Kt
+ 2 _Strings.kt
kotlin/text/StringsKt___StringsKt
*L *L
1#1,7:1 1#1,7:1
498#2:8
*E
*S KotlinDebug
*F
+ 1 2.kt
_2Kt
*L
4#1:8
*E *E
@@ -25,6 +25,16 @@ Kotlin
*F *F
+ 1 2.kt + 1 2.kt
_2Kt _2Kt
+ 2 1.kt
test/_1Kt
*L *L
1#1,8:1 1#1,8:1
10#2:9
*E
*S KotlinDebug
*F
+ 1 2.kt
_2Kt
*L
5#1:9
*E *E
@@ -0,0 +1,49 @@
package inlineOnlyLambdas
class Test
fun test() {}
fun main(args: Array<String>) {
val data = Test()
//Breakpoint! (lambdaOrdinal = 1)
data.onelinerApply { test() }
//Breakpoint! (lambdaOrdinal = 1)
data.multiLineApply { test() }
//Breakpoint! (lambdaOrdinal = 1)
data.onelinerApply2 { test() }
//Breakpoint! (lambdaOrdinal = 1)
data.multiLineApply2 { test() }
//Breakpoint!
data.withoutLambdaParams()
}
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@kotlin.internal.InlineOnly
inline fun <T> T.withoutLambdaParams(): T {
return this
}
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@kotlin.internal.InlineOnly
inline fun <T> T.onelinerApply(block: T.() -> Unit): T { block(); return this }
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@kotlin.internal.InlineOnly
inline fun <T> T.multiLineApply(block: T.() -> Unit): T {
block();
return this
}
inline fun <T> T.onelinerApply2(block: T.() -> Unit): T { block(); return this }
inline fun <T> T.multiLineApply2(block: T.() -> Unit): T {
block();
return this
}
// RESUME: 5
@@ -0,0 +1,15 @@
LineBreakpoint created at inlineOnlyLambdas.kt:10 lambdaOrdinal = 1
LineBreakpoint created at inlineOnlyLambdas.kt:13 lambdaOrdinal = 1
LineBreakpoint created at inlineOnlyLambdas.kt:16 lambdaOrdinal = 1
LineBreakpoint created at inlineOnlyLambdas.kt:19 lambdaOrdinal = 1
LineBreakpoint created at inlineOnlyLambdas.kt:22
Run Java
Connected to the target VM
inlineOnlyLambdas.kt:10
inlineOnlyLambdas.kt:13
inlineOnlyLambdas.kt:16
inlineOnlyLambdas.kt:19
inlineOnlyLambdas.kt:22
Disconnected from the target VM
Process finished with exit code 0
@@ -0,0 +1,38 @@
package inlineOnlyLambdasStepping
class Test
fun test() {}
fun main(args: Array<String>) {
val data = Test()
// STEP_OVER: 2
//Breakpoint! (lambdaOrdinal = 1)
data.onelinerApply { test() }
data.multiLineApply { test() }
// STEP_OVER: 3
//Breakpoint! (lambdaOrdinal = 1)
data.onelinerApply2 { test() }
data.multiLineApply2 { test() }
}
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@kotlin.internal.InlineOnly
inline fun <T> T.onelinerApply(block: T.() -> Unit): T { block(); return this }
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@kotlin.internal.InlineOnly
inline fun <T> T.multiLineApply(block: T.() -> Unit): T {
block();
return this
}
inline fun <T> T.onelinerApply2(block: T.() -> Unit): T { block(); return this }
inline fun <T> T.multiLineApply2(block: T.() -> Unit): T {
block();
return this
}
@@ -0,0 +1,13 @@
LineBreakpoint created at inlineOnlyLambdasStepping.kt:11 lambdaOrdinal = 1
LineBreakpoint created at inlineOnlyLambdasStepping.kt:17 lambdaOrdinal = 1
Run Java
Connected to the target VM
inlineOnlyLambdasStepping.kt:11
inlineOnlyLambdasStepping.kt:13
inlineOnlyLambdasStepping.kt:17
inlineOnlyLambdasStepping.kt:17
inlineOnlyLambdasStepping.kt:19
inlineOnlyLambdasStepping.kt:20
Disconnected from the target VM
Process finished with exit code 0
@@ -27,5 +27,5 @@ inline fun forEach(s: () -> Unit) {
} }
} }
// STEP_INTO: 9 // STEP_INTO: 15
// TRACING_FILTERS_ENABLED: false // TRACING_FILTERS_ENABLED: false
@@ -3,14 +3,20 @@ Run Java
Connected to the target VM Connected to the target VM
inlineOnly.kt:5 inlineOnly.kt:5
inlineOnly.kt:7 inlineOnly.kt:7
inlineOnly.kt:13 inlineOnly.kt:25
inlineOnly.kt:14 inlineOnly.kt:26
inlineOnly.kt:7 inlineOnly.kt:7
inlineOnly.kt:13 inlineOnly.kt:13
inlineOnly.kt:14 inlineOnly.kt:14
inlineOnly.kt:7 inlineOnly.kt:7
inlineOnly.kt:9 inlineOnly.kt:25
PrintStream.!EXT! inlineOnly.kt:26
inlineOnly.kt:7
inlineOnly.kt:13
inlineOnly.kt:14
inlineOnly.kt:7
inlineOnly.kt:25
inlineOnly.kt:28
Disconnected from the target VM Disconnected from the target VM
Process finished with exit code 0 Process finished with exit code 0
@@ -1218,6 +1218,18 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
doCustomTest(fileName); doCustomTest(fileName);
} }
@TestMetadata("inlineOnlyLambdas.kt")
public void testInlineOnlyLambdas() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/inlineOnlyLambdas.kt");
doCustomTest(fileName);
}
@TestMetadata("inlineOnlyLambdasStepping.kt")
public void testInlineOnlyLambdasStepping() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/inlineOnlyLambdasStepping.kt");
doCustomTest(fileName);
}
@TestMetadata("inlineProperties.kt") @TestMetadata("inlineProperties.kt")
public void testInlineProperties() throws Exception { public void testInlineProperties() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/inlineProperties.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/inlineProperties.kt");