Fix look up for breakpoint in crossinline lambdas in android (KT-15282)

#KT-15282 Fixed
This commit is contained in:
Nikolay Krasko
2017-01-13 15:19:05 +03:00
parent b94c62649a
commit 5d08e82aaf
10 changed files with 156 additions and 5 deletions
+1
View File
@@ -2,6 +2,7 @@
<dictionary name="Nikolay.Krasko">
<words>
<w>accessors</w>
<w>crossinline</w>
<w>fqname</w>
<w>goto</w>
<w>gradle</w>
@@ -30,6 +30,7 @@ import com.intellij.util.containers.ContainerUtil
import com.sun.jdi.Location
import com.sun.jdi.ReferenceType
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches
import org.jetbrains.kotlin.idea.refactoring.getLineCount
import org.jetbrains.kotlin.idea.refactoring.getLineStartOffset
@@ -42,11 +43,13 @@ import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.tail
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.resolve.inline.InlineUtil
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.utils.addToStdlib.check
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.getOrPutNullable
import org.jetbrains.org.objectweb.asm.*
import java.io.File
@@ -254,9 +257,17 @@ internal fun getLocationsOfInlinedLine(type: ReferenceType, position: SourcePosi
val lineStartOffset = file.getLineStartOffset(line) ?: return listOf()
val element = file.findElementAt(lineStartOffset) ?: return listOf()
val ktElement = element.parents.firstIsInstanceOrNull<KtElement>() ?: return listOf()
val isInInline = runReadAction { element.parents.any { it is KtFunction && it.hasModifier(KtTokens.INLINE_KEYWORD) } }
if (!isInInline) return listOf()
if (!isInInline) {
// Lambdas passed to crossinline arguments are inlined when they are used in non-inlined lambdas
val isInCrossinlineArgument = isInCrossinlineArgument(ktElement)
if (!isInCrossinlineArgument) {
return listOf()
}
}
val lines = inlinedLinesNumbers(line + 1, position.file.name, FqName(type.name()), type.sourceName(), project, sourceSearchScope)
val inlineLocations = lines.flatMap { type.locationsOfLine(it) }
@@ -264,6 +275,25 @@ internal fun getLocationsOfInlinedLine(type: ReferenceType, position: SourcePosi
return inlineLocations
}
fun isInCrossinlineArgument(ktElement: KtElement): Boolean {
val argumentFunctions = runReadAction {
ktElement.parents.filter {
when (it) {
is KtFunctionLiteral -> it.parent is KtLambdaExpression && (it.parent.parent is KtValueArgument || it.parent.parent is KtLambdaArgument)
is KtFunction -> it.parent is KtValueArgument
else -> false
}
}.filterIsInstance<KtFunction>()
}
val bindingContext = ktElement.analyze(BodyResolveMode.PARTIAL)
return argumentFunctions.any {
val argumentDescriptor = InlineUtil.getInlineArgumentDescriptor(it, bindingContext)
argumentDescriptor?.isCrossinline ?: false
}
}
private fun inlinedLinesNumbers(
inlineLineNumber: Int, inlineFileName: String,
destinationTypeFqName: FqName, destinationFileName: String,
@@ -281,7 +311,7 @@ private fun inlinedLinesNumbers(
val smap = smapData.kotlinStrata ?: return listOf()
val mappingsToInlinedFile = smap.fileMappings.filter() { it.name == inlineFileName }
val mappingsToInlinedFile = smap.fileMappings.filter { it.name == inlineFileName }
val mappingIntervals = mappingsToInlinedFile.flatMap { it.lineMappings }
val mappedLines = mappingIntervals.asSequence().
@@ -188,7 +188,7 @@ private fun getInlineFunctionsIfAny(file: KtFile, offset: Int): List<KtNamedFunc
private fun getInlineArgumentsIfAny(inlineFunctionCalls: List<KtCallExpression>): List<KtFunction> {
return inlineFunctionCalls.flatMap {
it.valueArguments
.map { getArgumentExpression(it) }
.map(::getArgumentExpression)
.filterIsInstance<KtFunction>()
}
}
@@ -0,0 +1,8 @@
LineBreakpoint created at stopInLambdaInInlinedCallWithCrossInline.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! stopInLambdaInInlinedCallWithCrossInline.StopInLambdaInInlinedCallWithCrossInlineKt
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
stopInLambdaInInlinedCallWithCrossInline.kt:8
stopInLambdaInInlinedCallWithCrossInline.kt:18
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 stopInLambdaInInlinedCallWithCrossInlineDex.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! stopInLambdaInInlinedCallWithCrossInlineDex.StopInLambdaInInlinedCallWithCrossInlineDexKt
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
stopInLambdaInInlinedCallWithCrossInlineDex.kt:8
stopInLambdaInInlinedCallWithCrossInlineDex.kt:19
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,20 @@
package stopInAnonymousFunctionInInlinedCallWithCrossInline
// KT-15282
fun main(args: Array<String>) {
foo(fun () {
//Breakpoint!
12
})
}
fun bar(f: () -> Unit) {
f()
}
inline fun foo(crossinline func: () -> Int) {
bar {
func()
}
}
@@ -0,0 +1,20 @@
package stopInAnonymousFunctionInInlinedCallWithCrossInlineDex
// KT-15282
fun main(args: Array<String>) {
foo(fun () {
//Breakpoint!
12
})
}
fun bar(f: () -> Unit) {
f()
}
inline fun foo(crossinline func: () -> Int) {
bar {
func()
}
}
@@ -0,0 +1,20 @@
package stopInLambdaInInlinedCallWithCrossInline
// KT-15282
fun main(args: Array<String>) {
foo {
//Breakpoint!
12
}
}
fun bar(f: () -> Unit) {
f()
}
inline fun foo(crossinline func: () -> Int) {
bar {
func()
}
}
@@ -0,0 +1,20 @@
package stopInLambdaInInlinedCallWithCrossInlineDex
// KT-15282
fun main(args: Array<String>) {
foo {
//Breakpoint!
12
}
}
fun bar(f: () -> Unit) {
f()
}
inline fun foo(crossinline func: () -> Int) {
bar {
func()
}
}
@@ -704,6 +704,18 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
doStepOverTest(fileName);
}
@TestMetadata("stopInAnonymousFunctionInInlinedCallWithCrossInline.kt")
public void testStopInAnonymousFunctionInInlinedCallWithCrossInline() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInAnonymousFunctionInInlinedCallWithCrossInline.kt");
doStepOverTest(fileName);
}
@TestMetadata("stopInAnonymousFunctionInInlinedCallWithCrossInlineDex.kt")
public void testStopInAnonymousFunctionInInlinedCallWithCrossInlineDex() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInAnonymousFunctionInInlinedCallWithCrossInlineDex.kt");
doStepOverTest(fileName);
}
@TestMetadata("stopInInlineCallLocalFunLambda.kt")
public void testStopInInlineCallLocalFunLambda() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInInlineCallLocalFunLambda.kt");
@@ -746,6 +758,18 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
doStepOverTest(fileName);
}
@TestMetadata("stopInLambdaInInlinedCallWithCrossInline.kt")
public void testStopInLambdaInInlinedCallWithCrossInline() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInLambdaInInlinedCallWithCrossInline.kt");
doStepOverTest(fileName);
}
@TestMetadata("stopInLambdaInInlinedCallWithCrossInlineDex.kt")
public void testStopInLambdaInInlinedCallWithCrossInlineDex() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInLambdaInInlinedCallWithCrossInlineDex.kt");
doStepOverTest(fileName);
}
@TestMetadata("stopInLambdaInlineCallLambda.kt")
public void testStopInLambdaInlineCallLambda() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInLambdaInlineCallLambda.kt");