Skip to content

Commit 1889f4f

Browse files
committed
Fix breakpoints in function literals in inline calls (KT-11521, KT-12734, KT-12470)
#KT-11521 Fixed #KT-12734 Fixed #KT-12470 Fixed
1 parent 59a349a commit 1889f4f

21 files changed

+394
-10
lines changed

compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ public void visitMethodInsn(int opcode, String owner, String name, String desc,
221221

222222
if (invokeCall.lambdaInfo.getFunctionDescriptor().getValueParameters().isEmpty()) {
223223
// There won't be no parameters processing and line call can be left without actual instructions.
224-
// Note: if function is called on the line with other instructions like 1 + foo() no will still be generated.
224+
// Note: if function is called on the line with other instructions like 1 + foo(), 'nop' will still be generated.
225225
visitInsn(Opcodes.NOP);
226226
}
227227

idea/src/org/jetbrains/kotlin/idea/debugger/DebuggerClassNameProvider.kt

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
4747
import org.jetbrains.kotlin.idea.util.application.runReadAction
4848
import org.jetbrains.kotlin.load.java.JvmAbi
4949
import org.jetbrains.kotlin.psi.*
50+
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
5051
import org.jetbrains.kotlin.psi.psiUtil.parents
5152
import org.jetbrains.kotlin.resolve.BindingContext
5253
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
@@ -119,7 +120,12 @@ class DebuggerClassNameProvider(val myDebugProcess: DebugProcess, val scopes: Li
119120
when {
120121
element is KtFunctionLiteral -> {
121122
val asmType = CodegenBinding.asmTypeForAnonymousClass(typeMapper.bindingContext, element)
122-
return CachedClassNames(asmType.internalName)
123+
val className = asmType.internalName
124+
125+
val inlineCallClassPatterns = inlineCallClassPatterns(typeMapper, element)
126+
val names = listOf(className) + inlineCallClassPatterns
127+
128+
return CachedClassNames(names)
123129
}
124130
element is KtAnonymousInitializer -> {
125131
// Class-object initializer
@@ -159,14 +165,46 @@ class DebuggerClassNameProvider(val myDebugProcess: DebugProcess, val scopes: Li
159165
val inlinedCalls = findInlinedCalls(element, typeMapper.bindingContext)
160166
if (parentInternalName == null) return CachedClassNames(inlinedCalls)
161167

162-
return CachedClassNames(listOf(parentInternalName) + inlinedCalls)
168+
val inlineCallPatterns = if (parent != null) inlineCallClassPatterns(typeMapper, parent) else emptyList()
163169

170+
return CachedClassNames((listOf(parentInternalName) + inlinedCalls + inlineCallPatterns).filterNotNull())
164171
}
165172
}
166173

167174
return CachedClassNames(getClassNameForFile(file))
168175
}
169176

177+
private fun inlineCallClassPatterns(typeMapper: KotlinTypeMapper, element: KtElement): List<String> {
178+
val context = typeMapper.bindingContext
179+
180+
val (inlineCall, functionLiteral) = runReadAction {
181+
element.parents.filterIsInstance<KtFunctionLiteral>().map {
182+
val lambdaExpression = it.parent as? KtLambdaExpression ?: return@map null
183+
val ktCallExpression =
184+
lambdaExpression.typedParent<KtValueArgument>()?.typedParent<KtValueArgumentList>()?.typedParent<KtCallExpression>() ?:
185+
lambdaExpression.typedParent<KtLambdaArgument>()?.typedParent<KtCallExpression>() ?:
186+
return@map null
187+
188+
ktCallExpression to it
189+
}.lastOrNull {
190+
it != null && isInlineCall(context, it.component1())
191+
}
192+
} ?: return emptyList()
193+
194+
val lexicalScope = context[BindingContext.LEXICAL_SCOPE, inlineCall] ?: return emptyList()
195+
196+
val resolvedCall = runReadAction { inlineCall.getResolvedCall(context) } ?: return emptyList()
197+
val inlineFunctionName = resolvedCall.resultingDescriptor.name
198+
199+
val originalInternalClassName = CodegenBinding.asmTypeForAnonymousClass(typeMapper.bindingContext, functionLiteral).internalName
200+
val ownerDescriptorName = lexicalScope.ownerDescriptor.name
201+
202+
val mangledInternalClassName = originalInternalClassName.funPrefix() + (if (ownerDescriptorName.isSpecial) "\$\$special\$" else "$") +
203+
InlineCodegenUtil.INLINE_TRANSFORMATION_SUFFIX + "$" + inlineFunctionName
204+
205+
return listOf("$mangledInternalClassName*")
206+
}
207+
170208
private fun getClassNameForClass(klass: KtClassOrObject, typeMapper: KotlinTypeMapper) = klass.readAction { getJvmInternalNameForImpl(typeMapper, it) }
171209
private fun getClassNameForFile(file: KtFile) = file.readAction { NoResolveFileClassesProvider.getFileClassInternalName(it) }
172210

@@ -329,14 +367,32 @@ class DebuggerClassNameProvider(val myDebugProcess: DebugProcess, val scopes: Li
329367
}
330368

331369
private fun getArgumentExpression(it: ValueArgument) = (it.getArgumentExpression() as? KtLambdaExpression)?.functionLiteral ?: it.getArgumentExpression()
370+
}
332371

333-
private fun String.substringIndex(): String {
334-
if (lastIndexOf("$") < 0) return this
372+
private fun isInlineCall(context: BindingContext, expr: KtCallExpression): Boolean {
373+
val resolvedCall = expr.getResolvedCall(context) ?: return false
374+
return InlineUtil.isInline(resolvedCall.resultingDescriptor)
375+
}
335376

336-
val suffix = substringAfterLast("$")
337-
if (suffix.all(Char::isDigit)) {
338-
return substringBeforeLast("$") + "$"
339-
}
340-
return this
377+
private inline fun <reified T : PsiElement> PsiElement.typedParent(): T? = getStrictParentOfType()
378+
379+
private fun String.funPrefix(): String {
380+
if (lastIndexOf("$") < 0) return this
381+
382+
val trimmed = trimEnd { it == '$' || it.isDigit() } // Can accidentally trim end of function name if it ends with a number
383+
val nextDollarIndex = indexOf('$', startIndex = trimmed.length - 1)
384+
385+
if (nextDollarIndex < 0) return this
386+
387+
return this.substring(0, nextDollarIndex)
388+
}
389+
390+
private fun String.substringIndex(): String {
391+
if (lastIndexOf("$") < 0) return this
392+
393+
val suffix = substringAfterLast("$")
394+
if (suffix.all(Char::isDigit)) {
395+
return substringBeforeLast("$") + "$"
341396
}
397+
return this
342398
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
LineBreakpoint created at stopInInlineCallLocalFunLambda.kt:9
2+
!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! stopInInlineCallLocalFunLambda.StopInInlineCallLocalFunLambdaKt
3+
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
4+
stopInInlineCallLocalFunLambda.kt:9
5+
stopInInlineCallLocalFunLambda.kt:10
6+
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
7+
8+
Process finished with exit code 0
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
LineBreakpoint created at stopInInlinedInSpecialNamedFun.kt:10
2+
!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! stopInInlinedInSpecialNamedFun.StopInInlinedInSpecialNamedFunKt
3+
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
4+
stopInInlinedInSpecialNamedFun.kt:10
5+
stopInInlinedInSpecialNamedFun.kt:11
6+
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
7+
8+
Process finished with exit code 0
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
LineBreakpoint created at stopInInlinedInSpecialNamedFunWithGet.kt:10
2+
!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! stopInInlinedInSpecialNamedFunWithGet.StopInInlinedInSpecialNamedFunWithGetKt
3+
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
4+
stopInInlinedInSpecialNamedFunWithGet.kt:10
5+
stopInInlinedInSpecialNamedFunWithGet.kt:11
6+
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
7+
8+
Process finished with exit code 0
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
LineBreakpoint created at stopInLambdaInlineCallLambda.kt:9
2+
!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! stopInLambdaInlineCallLambda.StopInLambdaInlineCallLambdaKt
3+
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
4+
stopInLambdaInlineCallLambda.kt:9
5+
stopInLambdaInlineCallLambda.kt:10
6+
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
7+
8+
Process finished with exit code 0
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
LineBreakpoint created at stopInLocalFunInlineCallLambda.kt:9
2+
!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! stopInLocalFunInlineCallLambda.StopInLocalFunInlineCallLambdaKt
3+
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
4+
stopInLocalFunInlineCallLambda.kt:9
5+
stopInLocalFunInlineCallLambda.kt:10
6+
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
7+
8+
Process finished with exit code 0
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
LineBreakpoint created at stopInNonInlinedLambdaInInlineCallWithClosure.kt:8
2+
!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! stopInNonInlinedLambdaInInlineCallWithClosure.StopInNonInlinedLambdaInInlineCallWithClosureKt
3+
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
4+
stopInNonInlinedLambdaInInlineCallWithClosure.kt:8
5+
stopInNonInlinedLambdaInInlineCallWithClosure.kt:9
6+
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
7+
8+
Process finished with exit code 0
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
LineBreakpoint created at stopInNonInlinedLambdaInInlineCallWithoutClosure.kt:8
2+
!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! stopInNonInlinedLambdaInInlineCallWithoutClosure.StopInNonInlinedLambdaInInlineCallWithoutClosureKt
3+
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
4+
stopInNonInlinedLambdaInInlineCallWithoutClosure.kt:8
5+
stopInNonInlinedLambdaInInlineCallWithoutClosure.kt:9
6+
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
7+
8+
Process finished with exit code 0
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
LineBreakpoint created at stopInObjectLiteralInInlineCallNoClosure.kt:12
2+
!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! stopInObjectLiteralInInlineCallNoClosure.StopInObjectLiteralInInlineCallNoClosureKt
3+
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
4+
stopInObjectLiteralInInlineCallNoClosure.kt:12
5+
stopInObjectLiteralInInlineCallNoClosure.kt:13
6+
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
7+
8+
Process finished with exit code 0
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
LineBreakpoint created at stopInObjectLiteralInInlineCallWithClosure.kt:12
2+
!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! stopInObjectLiteralInInlineCallWithClosure.StopInObjectLiteralInInlineCallWithClosureKt
3+
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
4+
stopInObjectLiteralInInlineCallWithClosure.kt:12
5+
stopInObjectLiteralInInlineCallWithClosure.kt:13
6+
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
7+
8+
Process finished with exit code 0
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package stopInInlineCallLocalFunLambda
2+
3+
fun main(args: Array<String>) {
4+
var prop = 1
5+
inlineFun {
6+
fun local() {
7+
{
8+
//Breakpoint!
9+
foo(12)
10+
}()
11+
}
12+
13+
local()
14+
}
15+
}
16+
17+
inline fun inlineFun(f: () -> Unit) {
18+
f()
19+
}
20+
21+
fun foo(a: Any) {}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package stopInInlinedInSpecialNamedFun
2+
3+
// KT-12470
4+
5+
class Foo(bar: Bar) {
6+
init {
7+
with(bar) {
8+
{
9+
//Breakpoint!
10+
nop()
11+
nop()
12+
}()
13+
}
14+
}
15+
16+
fun nop() {}
17+
}
18+
19+
fun main(args: Array<String>) {
20+
Foo(Bar())
21+
}
22+
23+
class Bar
24+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package stopInInlinedInSpecialNamedFunWithGet
2+
3+
// KT-12470
4+
5+
class Foo(bar: Bar) {
6+
init {
7+
with(bar) {
8+
get<String> {
9+
//Breakpoint!
10+
nop()
11+
nop()
12+
}
13+
}
14+
}
15+
16+
private fun nop() {}
17+
}
18+
19+
fun main(args: Array<String>) {
20+
Foo(Bar())
21+
}
22+
23+
class Bar
24+
class BarContext
25+
26+
inline fun <reified T : Any> Bar.get(noinline body: BarContext.() -> Unit) {
27+
BarContext().body()
28+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package stopInLambdaInlineCallLambda
2+
3+
fun main(args: Array<String>) {
4+
var prop = 1
5+
{
6+
inlineFun {
7+
{
8+
//Breakpoint!
9+
foo(12)
10+
}()
11+
}
12+
}()
13+
}
14+
15+
inline fun inlineFun(f: () -> Unit) {
16+
f()
17+
}
18+
19+
fun foo(a: Any) {}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package stopInLocalFunInlineCallLambda
2+
3+
fun main(args: Array<String>) {
4+
var prop = 1
5+
fun local() {
6+
inlineFun {
7+
{
8+
//Breakpoint!
9+
foo(12)
10+
}()
11+
}
12+
}
13+
14+
local()
15+
}
16+
17+
inline fun inlineFun(f: () -> Unit) {
18+
f()
19+
}
20+
21+
fun foo(a: Any) {}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package stopInNonInlinedLambdaInInlineCallWithClosure
2+
3+
fun main(args: Array<String>) {
4+
var prop = 1
5+
inlineFun {
6+
notInlineFun {
7+
//Breakpoint!
8+
foo(prop)
9+
foo(prop)
10+
}
11+
}
12+
}
13+
14+
inline fun inlineFun(f: () -> Unit) {
15+
f()
16+
}
17+
18+
fun notInlineFun(f: () -> Unit) {
19+
f()
20+
}
21+
22+
fun foo(a: Any) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package stopInNonInlinedLambdaInInlineCallWithoutClosure
2+
3+
fun main(args: Array<String>) {
4+
var prop = 1
5+
inlineFun {
6+
notInlineFun {
7+
//Breakpoint!
8+
foo(12)
9+
}
10+
}
11+
}
12+
13+
inline fun inlineFun(f: () -> Unit) {
14+
f()
15+
}
16+
17+
fun notInlineFun(f: () -> Unit) {
18+
f()
19+
}
20+
21+
fun foo(a: Any) {}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package stopInObjectLiteralInInlineCallNoClosure
2+
3+
// KT-12734
4+
5+
fun main(args: Array<String>) {
6+
val a = 12
7+
8+
inlineF {
9+
val s = object: () -> Unit {
10+
override fun invoke() {
11+
//Breakpoint!
12+
nop()
13+
nop()
14+
}
15+
}
16+
17+
s()
18+
}
19+
}
20+
21+
inline fun <R> inlineF(block: () -> R): R = block()
22+
23+
fun nop() {}

0 commit comments

Comments
 (0)