Support inlined functional expressions in debugger
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger
|
||||
|
||||
import com.google.common.collect.ObjectArrays
|
||||
import com.intellij.debugger.MultiRequestPositionManager
|
||||
import com.intellij.debugger.NoDataException
|
||||
import com.intellij.debugger.SourcePosition
|
||||
@@ -57,8 +58,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineAnalyzerExtension
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import java.util.ArrayList
|
||||
import java.util.WeakHashMap
|
||||
import java.util.*
|
||||
|
||||
class PositionedElement(val className: String?, val element: PsiElement?)
|
||||
|
||||
@@ -83,9 +83,9 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Mult
|
||||
|
||||
|
||||
if (lineNumber >= 0) {
|
||||
val lambdaIfInside = getLambdaIfInside(location, psiFile as JetFile, lineNumber)
|
||||
val lambdaIfInside = getLambdaOrFunIfInside(location, psiFile as JetFile, lineNumber)
|
||||
if (lambdaIfInside != null) {
|
||||
return SourcePosition.createFromElement(lambdaIfInside.getBodyExpression().getStatements().get(0))
|
||||
return SourcePosition.createFromElement(lambdaIfInside.getBodyExpression())
|
||||
}
|
||||
return SourcePosition.createFromLine(psiFile, lineNumber)
|
||||
}
|
||||
@@ -93,7 +93,7 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Mult
|
||||
throw NoDataException.INSTANCE
|
||||
}
|
||||
|
||||
private fun getLambdaIfInside(location: Location, file: JetFile, lineNumber: Int): JetFunctionLiteral? {
|
||||
private fun getLambdaOrFunIfInside(location: Location, file: JetFile, lineNumber: Int): JetFunction? {
|
||||
val currentLocationFqName = location.declaringType().name()
|
||||
if (currentLocationFqName == null) return null
|
||||
|
||||
@@ -101,8 +101,15 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Mult
|
||||
val end = CodeInsightUtils.getEndLineOffset(file, lineNumber)
|
||||
if (start == null || end == null) return null
|
||||
|
||||
val literals = CodeInsightUtils.findElementsOfClassInRange(file, start, end, javaClass<JetFunctionLiteral>())
|
||||
if (literals == null || literals.size() == 0) return null
|
||||
val functionLiterals: Array<out PsiElement>? = CodeInsightUtils.findElementsOfClassInRange(file, start, end, javaClass<JetFunctionLiteral>())
|
||||
val functionalExpression: Array<out PsiElement>? = CodeInsightUtils.findElementsOfClassInRange(file, start, end, javaClass<JetNamedFunction>())
|
||||
val literals =
|
||||
if (functionLiterals == null) functionalExpression
|
||||
else if (functionalExpression == null) functionLiterals
|
||||
else functionLiterals.plus(functionalExpression).toTypedArray()
|
||||
|
||||
|
||||
if (literals == null || literals.size() == 0) return null;
|
||||
|
||||
val isInLibrary = LibraryUtil.findLibraryEntry(file.getVirtualFile(), file.getProject()) != null
|
||||
val typeMapper = if (!isInLibrary)
|
||||
@@ -112,7 +119,7 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Mult
|
||||
|
||||
val currentLocationClassName = JvmClassName.byFqNameWithoutInnerClasses(FqName(currentLocationFqName)).getInternalName()
|
||||
for (literal in literals) {
|
||||
val functionLiteral = literal as JetFunctionLiteral
|
||||
val functionLiteral = literal as JetFunction
|
||||
if (isInlinedLambda(functionLiteral, typeMapper.getBindingContext())) {
|
||||
continue
|
||||
}
|
||||
@@ -321,6 +328,10 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Mult
|
||||
return PositionedElement(getJvmInternalNameForPropertyOwner(typeMapper, descriptor), element)
|
||||
}
|
||||
element is JetNamedFunction -> {
|
||||
if (isInlinedLambda(element, typeMapper.getBindingContext())) {
|
||||
return getInternalClassNameForElement(element.getParent(), typeMapper, file, isInLibrary)
|
||||
}
|
||||
|
||||
val parent = getElementToCalculateClassName(element)
|
||||
if (parent is JetClassOrObject) {
|
||||
return PositionedElement(getJvmInternalNameForImpl(typeMapper, parent), element)
|
||||
@@ -388,40 +399,8 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Mult
|
||||
return state.getTypeMapper()
|
||||
}
|
||||
|
||||
public fun isInlinedLambda(functionLiteral: JetFunctionLiteral, context: BindingContext): Boolean {
|
||||
val functionLiteralExpression = functionLiteral.getParent()
|
||||
if (functionLiteralExpression == null) return false
|
||||
|
||||
var parent = functionLiteralExpression.getParent()
|
||||
|
||||
var valueArgument: PsiElement = functionLiteralExpression
|
||||
while (parent is JetParenthesizedExpression || parent is JetBinaryExpressionWithTypeRHS || parent is JetLabeledExpression) {
|
||||
valueArgument = parent
|
||||
parent = parent.getParent()
|
||||
}
|
||||
|
||||
while (parent is ValueArgument || parent is JetValueArgumentList) {
|
||||
parent = parent.getParent()
|
||||
}
|
||||
|
||||
if (parent !is JetElement) return false
|
||||
|
||||
val call = (parent as JetElement).getResolvedCall(context)
|
||||
if (call == null) return false
|
||||
|
||||
if (!InlineUtil.isInline(call.getResultingDescriptor())) return false
|
||||
|
||||
for ((valueParameterDescriptor, resolvedValueArgument) in call.getValueArguments()) {
|
||||
for (next in resolvedValueArgument.getArguments()) {
|
||||
val expression = next.getArgumentExpression()
|
||||
if (valueArgument == expression) {
|
||||
return InlineAnalyzerExtension.checkInlinableParameter(
|
||||
valueParameterDescriptor, expression, call.getResultingDescriptor(), null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
public fun isInlinedLambda(functionLiteral: JetFunction, context: BindingContext): Boolean {
|
||||
return InlineUtil.isInlineLambda(functionLiteral, context, false)
|
||||
}
|
||||
|
||||
private fun createKeyForTypeMapper(file: JetFile) = PackagePartClassUtils.getPackagePartInternalName(file)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
LineBreakpoint created at inlineFunctionalExpression.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 !APP_PATH!\classes;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! inlineFunctionalExpression.InlineFunctionalExpressionPackage
|
||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
inlineFunctionalExpression.kt:9
|
||||
inlineFunctionalExpression.kt:9
|
||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
|
||||
Process finished with exit code 0
|
||||
@@ -0,0 +1,10 @@
|
||||
LineBreakpoint created at oneLineFunctionalExpression.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 !APP_PATH!\classes;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! oneLineFunctionalExpression.OneLineFunctionalExpressionPackage
|
||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
oneLineFunctionalExpression.kt:9
|
||||
oneLineFunctionalExpression.kt:14
|
||||
oneLineFunctionalExpression.kt:9
|
||||
Compile bytecode for it
|
||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
|
||||
Process finished with exit code 0
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package inlineFunctionalExpression
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val a = array(1)
|
||||
// EXPRESSION: it
|
||||
// RESULT: Unresolved reference: it
|
||||
// STEP_INTO: 1
|
||||
//Breakpoint!
|
||||
a.map(fun (it): Int { return it * 1 })
|
||||
}
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package oneLineFunctionalExpression
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val a = A()
|
||||
// EXPRESSION: it
|
||||
// RESULT: 1: I
|
||||
// STEP_INTO: 2
|
||||
//Breakpoint!
|
||||
a.foo (fun b(it): A { return a })
|
||||
}
|
||||
|
||||
class A {
|
||||
fun foo(f: (Int) -> A): A {
|
||||
return f(1)
|
||||
}
|
||||
}
|
||||
+12
@@ -522,6 +522,12 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("inlineFunctionalExpression.kt")
|
||||
public void testInlineFunctionalExpression() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/inlineFunctionalExpression.kt");
|
||||
doSingleBreakpointTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inlineLambda.kt")
|
||||
public void testInlineLambda() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/inlineLambda.kt");
|
||||
@@ -534,6 +540,12 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
|
||||
doSingleBreakpointTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("oneLineFunctionalExpression.kt")
|
||||
public void testOneLineFunctionalExpression() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/oneLineFunctionalExpression.kt");
|
||||
doSingleBreakpointTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("oneLineLambda.kt")
|
||||
public void testOneLineLambda() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/oneLineLambda.kt");
|
||||
|
||||
Reference in New Issue
Block a user