diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/JetPositionManager.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/JetPositionManager.kt index 17077eaf2a2..9636d062922 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/JetPositionManager.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/JetPositionManager.kt @@ -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()) - if (literals == null || literals.size() == 0) return null + val functionLiterals: Array? = CodeInsightUtils.findElementsOfClassInRange(file, start, end, javaClass()) + val functionalExpression: Array? = CodeInsightUtils.findElementsOfClassInRange(file, start, end, javaClass()) + 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) diff --git a/idea/testData/debugger/tinyApp/outs/inlineFunctionalExpression.out b/idea/testData/debugger/tinyApp/outs/inlineFunctionalExpression.out new file mode 100644 index 00000000000..fc654dc8472 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/inlineFunctionalExpression.out @@ -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 diff --git a/idea/testData/debugger/tinyApp/outs/oneLineFunctionalExpression.out b/idea/testData/debugger/tinyApp/outs/oneLineFunctionalExpression.out new file mode 100644 index 00000000000..1c691ebf518 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/oneLineFunctionalExpression.out @@ -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 diff --git a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/inlineFunctionalExpression.kt b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/inlineFunctionalExpression.kt new file mode 100644 index 00000000000..6aeefeabdc9 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/inlineFunctionalExpression.kt @@ -0,0 +1,11 @@ +package inlineFunctionalExpression + +fun main(args: Array) { + val a = array(1) + // EXPRESSION: it + // RESULT: Unresolved reference: it + // STEP_INTO: 1 + //Breakpoint! + a.map(fun (it): Int { return it * 1 }) +} + diff --git a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/oneLineFunctionalExpression.kt b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/oneLineFunctionalExpression.kt new file mode 100644 index 00000000000..a80630a3721 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/oneLineFunctionalExpression.kt @@ -0,0 +1,16 @@ +package oneLineFunctionalExpression + +fun main(args: Array) { + 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) + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java index cebba7a2719..b894fac4944 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java @@ -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");