From 2314ca7a94bf54e06ab587faa0a8ddb847ec71ce Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Mon, 8 Apr 2019 23:46:59 +0300 Subject: [PATCH] Debugger: Fix evaluation of lambda arguments (KT-10636) --- .../CodeFragmentParameterAnalyzer.kt | 35 ++++++++++++++----- .../multipleBreakpoints/lambdaParameters.kt | 22 ++++++++++++ .../multipleBreakpoints/lambdaParameters.out | 10 ++++++ ...KotlinEvaluateExpressionTestGenerated.java | 5 +++ 4 files changed, 63 insertions(+), 9 deletions(-) create mode 100644 idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/lambdaParameters.kt create mode 100644 idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/lambdaParameters.out diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentParameterAnalyzer.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentParameterAnalyzer.kt index abdd491303b..18ded2d0c2f 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentParameterAnalyzer.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentParameterAnalyzer.kt @@ -26,6 +26,8 @@ import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.checkers.COROUTINE_CONTEXT_1_3_FQ_NAME +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver @@ -87,10 +89,15 @@ class CodeFragmentParameterAnalyzer( codeFragment.accept(object : KtTreeVisitor() { override fun visitSimpleNameExpression(expression: KtSimpleNameExpression, data: Unit?): Void? { val resolvedCall = expression.getResolvedCall(bindingContext) ?: return null + processResolvedCall(resolvedCall, expression) + return null + } + + private fun processResolvedCall(resolvedCall: ResolvedCall<*>, expression: KtSimpleNameExpression) { // Capture dispatch receiver for the extension callable run { - val descriptor = resolvedCall.resultingDescriptor as? CallableDescriptor + val descriptor = resolvedCall.resultingDescriptor val containingClass = descriptor?.containingDeclaration as? ClassDescriptor val extensionParameter = descriptor?.extensionReceiverParameter if (descriptor != null && descriptor !is DebuggerFieldPropertyDescriptor @@ -104,12 +111,12 @@ class CodeFragmentParameterAnalyzer( if (runReadAction { expression.isDotSelector() }) { // The receiver expression is already captured for this reference - return null + return } if (isCodeFragmentDeclaration(resolvedCall.resultingDescriptor)) { // The reference is from the code fragment we analyze, no need to capture - return null + return } var processed = false @@ -143,14 +150,20 @@ class CodeFragmentParameterAnalyzer( // If a reference has receivers, we can calculate its value using them, no need to capture if (!processed) { - val descriptor = resolvedCall.resultingDescriptor - val parameter = processDebugLabel(descriptor) - ?: processCoroutineContextCall(descriptor) - ?: processSimpleNameExpression(descriptor) - checkBounds(descriptor, expression, parameter) + if (resolvedCall is VariableAsFunctionResolvedCall) { + processResolvedCall(resolvedCall.functionCall, expression) + processResolvedCall(resolvedCall.variableCall, expression) + } else { + processDescriptor(resolvedCall.resultingDescriptor, expression) + } } + } - return null + private fun processDescriptor(descriptor: DeclarationDescriptor, expression: KtSimpleNameExpression) { + val parameter = processDebugLabel(descriptor) + ?: processCoroutineContextCall(descriptor) + ?: processSimpleNameExpression(descriptor) + checkBounds(descriptor, expression, parameter) } override fun visitThisExpression(expression: KtThisExpression, data: Unit?): Void? { @@ -257,6 +270,10 @@ class CodeFragmentParameterAnalyzer( } private fun processSimpleNameExpression(target: DeclarationDescriptor): Smart? { + if (target is ValueParameterDescriptor && target.isCrossinline) { + throw EvaluateExceptionUtil.createEvaluateException("Evaluation of 'crossinline' lambdas is not supported") + } + val isLocalTarget = (target as? DeclarationDescriptorWithVisibility)?.visibility == Visibilities.LOCAL val isPrimaryConstructorParameter = !isLocalTarget diff --git a/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/lambdaParameters.kt b/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/lambdaParameters.kt new file mode 100644 index 00000000000..417b15557cd --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/lambdaParameters.kt @@ -0,0 +1,22 @@ +package lambdaParameters + +fun fun1(p: String, f: (String) -> Int) { + //Breakpoint! + f(p) +} + +inline fun fun2(p: String, crossinline f: (String) -> Int) { + //Breakpoint! + f(p) +} + +fun main(args: Array) { + fun1("abc", { x -> x.length }) + fun2("abc", { x -> x.length }) +} + +// EXPRESSION: f("abc") +// RESULT: 3: I + +// EXPRESSION: f("abc") +// RESULT: Evaluation of 'crossinline' lambdas is not supported \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/lambdaParameters.out b/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/lambdaParameters.out new file mode 100644 index 00000000000..837d30be504 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/lambdaParameters.out @@ -0,0 +1,10 @@ +LineBreakpoint created at lambdaParameters.kt:5 +LineBreakpoint created at lambdaParameters.kt:10 +Run Java +Connected to the target VM +lambdaParameters.kt:5 +Compile bytecode for f("abc") +lambdaParameters.kt:10 +Disconnected from the target VM + +Process finished with exit code 0 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 a1ea5035acd..7d8767bf66c 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java @@ -1086,6 +1086,11 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat runTest("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/isInsideInlineLambda.kt"); } + @TestMetadata("lambdaParameters.kt") + public void testLambdaParameters() throws Exception { + runTest("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/lambdaParameters.kt"); + } + @TestMetadata("localFun.kt") public void testLocalFun() throws Exception { runTest("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/localFun.kt");