Fix breakpoints in function literals in inline calls (KT-11521, KT-12734, KT-12470)

#KT-11521 Fixed
 #KT-12734 Fixed
 #KT-12470 Fixed
This commit is contained in:
Nikolay Krasko
2016-11-16 16:28:22 +03:00
committed by Nikolay Krasko
parent 59a349a4ae
commit 1889f4f7b1
21 changed files with 394 additions and 10 deletions
@@ -221,7 +221,7 @@ public class MethodInliner {
if (invokeCall.lambdaInfo.getFunctionDescriptor().getValueParameters().isEmpty()) {
// There won't be no parameters processing and line call can be left without actual instructions.
// Note: if function is called on the line with other instructions like 1 + foo() no will still be generated.
// Note: if function is called on the line with other instructions like 1 + foo(), 'nop' will still be generated.
visitInsn(Opcodes.NOP);
}
@@ -47,6 +47,7 @@ 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.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
@@ -119,7 +120,12 @@ class DebuggerClassNameProvider(val myDebugProcess: DebugProcess, val scopes: Li
when {
element is KtFunctionLiteral -> {
val asmType = CodegenBinding.asmTypeForAnonymousClass(typeMapper.bindingContext, element)
return CachedClassNames(asmType.internalName)
val className = asmType.internalName
val inlineCallClassPatterns = inlineCallClassPatterns(typeMapper, element)
val names = listOf(className) + inlineCallClassPatterns
return CachedClassNames(names)
}
element is KtAnonymousInitializer -> {
// Class-object initializer
@@ -159,14 +165,46 @@ class DebuggerClassNameProvider(val myDebugProcess: DebugProcess, val scopes: Li
val inlinedCalls = findInlinedCalls(element, typeMapper.bindingContext)
if (parentInternalName == null) return CachedClassNames(inlinedCalls)
return CachedClassNames(listOf(parentInternalName) + inlinedCalls)
val inlineCallPatterns = if (parent != null) inlineCallClassPatterns(typeMapper, parent) else emptyList()
return CachedClassNames((listOf(parentInternalName) + inlinedCalls + inlineCallPatterns).filterNotNull())
}
}
return CachedClassNames(getClassNameForFile(file))
}
private fun inlineCallClassPatterns(typeMapper: KotlinTypeMapper, element: KtElement): List<String> {
val context = typeMapper.bindingContext
val (inlineCall, functionLiteral) = runReadAction {
element.parents.filterIsInstance<KtFunctionLiteral>().map {
val lambdaExpression = it.parent as? KtLambdaExpression ?: return@map null
val ktCallExpression =
lambdaExpression.typedParent<KtValueArgument>()?.typedParent<KtValueArgumentList>()?.typedParent<KtCallExpression>() ?:
lambdaExpression.typedParent<KtLambdaArgument>()?.typedParent<KtCallExpression>() ?:
return@map null
ktCallExpression to it
}.lastOrNull {
it != null && isInlineCall(context, it.component1())
}
} ?: return emptyList()
val lexicalScope = context[BindingContext.LEXICAL_SCOPE, inlineCall] ?: return emptyList()
val resolvedCall = runReadAction { inlineCall.getResolvedCall(context) } ?: return emptyList()
val inlineFunctionName = resolvedCall.resultingDescriptor.name
val originalInternalClassName = CodegenBinding.asmTypeForAnonymousClass(typeMapper.bindingContext, functionLiteral).internalName
val ownerDescriptorName = lexicalScope.ownerDescriptor.name
val mangledInternalClassName = originalInternalClassName.funPrefix() + (if (ownerDescriptorName.isSpecial) "\$\$special\$" else "$") +
InlineCodegenUtil.INLINE_TRANSFORMATION_SUFFIX + "$" + inlineFunctionName
return listOf("$mangledInternalClassName*")
}
private fun getClassNameForClass(klass: KtClassOrObject, typeMapper: KotlinTypeMapper) = klass.readAction { getJvmInternalNameForImpl(typeMapper, it) }
private fun getClassNameForFile(file: KtFile) = file.readAction { NoResolveFileClassesProvider.getFileClassInternalName(it) }
@@ -329,14 +367,32 @@ class DebuggerClassNameProvider(val myDebugProcess: DebugProcess, val scopes: Li
}
private fun getArgumentExpression(it: ValueArgument) = (it.getArgumentExpression() as? KtLambdaExpression)?.functionLiteral ?: it.getArgumentExpression()
}
private fun String.substringIndex(): String {
if (lastIndexOf("$") < 0) return this
private fun isInlineCall(context: BindingContext, expr: KtCallExpression): Boolean {
val resolvedCall = expr.getResolvedCall(context) ?: return false
return InlineUtil.isInline(resolvedCall.resultingDescriptor)
}
val suffix = substringAfterLast("$")
if (suffix.all(Char::isDigit)) {
return substringBeforeLast("$") + "$"
}
return this
private inline fun <reified T : PsiElement> 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
val suffix = substringAfterLast("$")
if (suffix.all(Char::isDigit)) {
return substringBeforeLast("$") + "$"
}
return this
}
@@ -0,0 +1,8 @@
LineBreakpoint created at stopInInlineCallLocalFunLambda.kt:9
!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
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
stopInInlineCallLocalFunLambda.kt:9
stopInInlineCallLocalFunLambda.kt:10
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,8 @@
LineBreakpoint created at stopInInlinedInSpecialNamedFun.kt:10
!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
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
stopInInlinedInSpecialNamedFun.kt:10
stopInInlinedInSpecialNamedFun.kt:11
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,8 @@
LineBreakpoint created at stopInInlinedInSpecialNamedFunWithGet.kt:10
!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
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
stopInInlinedInSpecialNamedFunWithGet.kt:10
stopInInlinedInSpecialNamedFunWithGet.kt:11
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,8 @@
LineBreakpoint created at stopInLambdaInlineCallLambda.kt:9
!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
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
stopInLambdaInlineCallLambda.kt:9
stopInLambdaInlineCallLambda.kt:10
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,8 @@
LineBreakpoint created at stopInLocalFunInlineCallLambda.kt:9
!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
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
stopInLocalFunInlineCallLambda.kt:9
stopInLocalFunInlineCallLambda.kt:10
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,8 @@
LineBreakpoint created at stopInNonInlinedLambdaInInlineCallWithClosure.kt:8
!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
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
stopInNonInlinedLambdaInInlineCallWithClosure.kt:8
stopInNonInlinedLambdaInInlineCallWithClosure.kt:9
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,8 @@
LineBreakpoint created at stopInNonInlinedLambdaInInlineCallWithoutClosure.kt:8
!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
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
stopInNonInlinedLambdaInInlineCallWithoutClosure.kt:8
stopInNonInlinedLambdaInInlineCallWithoutClosure.kt:9
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,8 @@
LineBreakpoint created at stopInObjectLiteralInInlineCallNoClosure.kt:12
!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
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
stopInObjectLiteralInInlineCallNoClosure.kt:12
stopInObjectLiteralInInlineCallNoClosure.kt:13
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,8 @@
LineBreakpoint created at stopInObjectLiteralInInlineCallWithClosure.kt:12
!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
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
stopInObjectLiteralInInlineCallWithClosure.kt:12
stopInObjectLiteralInInlineCallWithClosure.kt:13
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,21 @@
package stopInInlineCallLocalFunLambda
fun main(args: Array<String>) {
var prop = 1
inlineFun {
fun local() {
{
//Breakpoint!
foo(12)
}()
}
local()
}
}
inline fun inlineFun(f: () -> Unit) {
f()
}
fun foo(a: Any) {}
@@ -0,0 +1,24 @@
package stopInInlinedInSpecialNamedFun
// KT-12470
class Foo(bar: Bar) {
init {
with(bar) {
{
//Breakpoint!
nop()
nop()
}()
}
}
fun nop() {}
}
fun main(args: Array<String>) {
Foo(Bar())
}
class Bar
@@ -0,0 +1,28 @@
package stopInInlinedInSpecialNamedFunWithGet
// KT-12470
class Foo(bar: Bar) {
init {
with(bar) {
get<String> {
//Breakpoint!
nop()
nop()
}
}
}
private fun nop() {}
}
fun main(args: Array<String>) {
Foo(Bar())
}
class Bar
class BarContext
inline fun <reified T : Any> Bar.get(noinline body: BarContext.() -> Unit) {
BarContext().body()
}
@@ -0,0 +1,19 @@
package stopInLambdaInlineCallLambda
fun main(args: Array<String>) {
var prop = 1
{
inlineFun {
{
//Breakpoint!
foo(12)
}()
}
}()
}
inline fun inlineFun(f: () -> Unit) {
f()
}
fun foo(a: Any) {}
@@ -0,0 +1,21 @@
package stopInLocalFunInlineCallLambda
fun main(args: Array<String>) {
var prop = 1
fun local() {
inlineFun {
{
//Breakpoint!
foo(12)
}()
}
}
local()
}
inline fun inlineFun(f: () -> Unit) {
f()
}
fun foo(a: Any) {}
@@ -0,0 +1,22 @@
package stopInNonInlinedLambdaInInlineCallWithClosure
fun main(args: Array<String>) {
var prop = 1
inlineFun {
notInlineFun {
//Breakpoint!
foo(prop)
foo(prop)
}
}
}
inline fun inlineFun(f: () -> Unit) {
f()
}
fun notInlineFun(f: () -> Unit) {
f()
}
fun foo(a: Any) {}
@@ -0,0 +1,21 @@
package stopInNonInlinedLambdaInInlineCallWithoutClosure
fun main(args: Array<String>) {
var prop = 1
inlineFun {
notInlineFun {
//Breakpoint!
foo(12)
}
}
}
inline fun inlineFun(f: () -> Unit) {
f()
}
fun notInlineFun(f: () -> Unit) {
f()
}
fun foo(a: Any) {}
@@ -0,0 +1,23 @@
package stopInObjectLiteralInInlineCallNoClosure
// KT-12734
fun main(args: Array<String>) {
val a = 12
inlineF {
val s = object: () -> Unit {
override fun invoke() {
//Breakpoint!
nop()
nop()
}
}
s()
}
}
inline fun <R> inlineF(block: () -> R): R = block()
fun nop() {}
@@ -0,0 +1,23 @@
package stopInObjectLiteralInInlineCallWithClosure
// KT-12734
fun main(args: Array<String>) {
val a = 12
inlineF {
val s = object: () -> Unit {
override fun invoke() {
//Breakpoint!
nop(a)
nop(a)
}
}
s()
}
}
inline fun <R> inlineF(block: () -> R): R = block()
fun nop(a: Any) {}
@@ -704,6 +704,12 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
doStepOverTest(fileName);
}
@TestMetadata("stopInInlineCallLocalFunLambda.kt")
public void testStopInInlineCallLocalFunLambda() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInInlineCallLocalFunLambda.kt");
doStepOverTest(fileName);
}
@TestMetadata("stopInInlineFunDex.kt")
public void testStopInInlineFunDex() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInInlineFunDex.kt");
@@ -722,11 +728,59 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
doStepOverTest(fileName);
}
@TestMetadata("stopInInlinedInSpecialNamedFun.kt")
public void testStopInInlinedInSpecialNamedFun() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInInlinedInSpecialNamedFun.kt");
doStepOverTest(fileName);
}
@TestMetadata("stopInInlinedInSpecialNamedFunWithGet.kt")
public void testStopInInlinedInSpecialNamedFunWithGet() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInInlinedInSpecialNamedFunWithGet.kt");
doStepOverTest(fileName);
}
@TestMetadata("stopInLabdaOfCrossinlineCalledInAnonymous.kt")
public void testStopInLabdaOfCrossinlineCalledInAnonymous() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInLabdaOfCrossinlineCalledInAnonymous.kt");
doStepOverTest(fileName);
}
@TestMetadata("stopInLambdaInlineCallLambda.kt")
public void testStopInLambdaInlineCallLambda() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInLambdaInlineCallLambda.kt");
doStepOverTest(fileName);
}
@TestMetadata("stopInLocalFunInlineCallLambda.kt")
public void testStopInLocalFunInlineCallLambda() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInLocalFunInlineCallLambda.kt");
doStepOverTest(fileName);
}
@TestMetadata("stopInNonInlinedLambdaInInlineCallWithClosure.kt")
public void testStopInNonInlinedLambdaInInlineCallWithClosure() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInNonInlinedLambdaInInlineCallWithClosure.kt");
doStepOverTest(fileName);
}
@TestMetadata("stopInNonInlinedLambdaInInlineCallWithoutClosure.kt")
public void testStopInNonInlinedLambdaInInlineCallWithoutClosure() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInNonInlinedLambdaInInlineCallWithoutClosure.kt");
doStepOverTest(fileName);
}
@TestMetadata("stopInObjectLiteralInInlineCallNoClosure.kt")
public void testStopInObjectLiteralInInlineCallNoClosure() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInObjectLiteralInInlineCallNoClosure.kt");
doStepOverTest(fileName);
}
@TestMetadata("stopInObjectLiteralInInlineCallWithClosure.kt")
public void testStopInObjectLiteralInInlineCallWithClosure() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInObjectLiteralInInlineCallWithClosure.kt");
doStepOverTest(fileName);
}
}
@TestMetadata("idea/testData/debugger/tinyApp/src/stepping/filters")