Debugger: Fix evaluation of lambda arguments (KT-10636)

This commit is contained in:
Yan Zhulanow
2019-04-08 23:46:59 +03:00
parent fa5705cc68
commit 2314ca7a94
4 changed files with 63 additions and 9 deletions
@@ -26,6 +26,8 @@ import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall 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.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.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
@@ -87,10 +89,15 @@ class CodeFragmentParameterAnalyzer(
codeFragment.accept(object : KtTreeVisitor<Unit>() { codeFragment.accept(object : KtTreeVisitor<Unit>() {
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression, data: Unit?): Void? { override fun visitSimpleNameExpression(expression: KtSimpleNameExpression, data: Unit?): Void? {
val resolvedCall = expression.getResolvedCall(bindingContext) ?: return null 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 // Capture dispatch receiver for the extension callable
run { run {
val descriptor = resolvedCall.resultingDescriptor as? CallableDescriptor val descriptor = resolvedCall.resultingDescriptor
val containingClass = descriptor?.containingDeclaration as? ClassDescriptor val containingClass = descriptor?.containingDeclaration as? ClassDescriptor
val extensionParameter = descriptor?.extensionReceiverParameter val extensionParameter = descriptor?.extensionReceiverParameter
if (descriptor != null && descriptor !is DebuggerFieldPropertyDescriptor if (descriptor != null && descriptor !is DebuggerFieldPropertyDescriptor
@@ -104,12 +111,12 @@ class CodeFragmentParameterAnalyzer(
if (runReadAction { expression.isDotSelector() }) { if (runReadAction { expression.isDotSelector() }) {
// The receiver expression is already captured for this reference // The receiver expression is already captured for this reference
return null return
} }
if (isCodeFragmentDeclaration(resolvedCall.resultingDescriptor)) { if (isCodeFragmentDeclaration(resolvedCall.resultingDescriptor)) {
// The reference is from the code fragment we analyze, no need to capture // The reference is from the code fragment we analyze, no need to capture
return null return
} }
var processed = false 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 a reference has receivers, we can calculate its value using them, no need to capture
if (!processed) { if (!processed) {
val descriptor = resolvedCall.resultingDescriptor if (resolvedCall is VariableAsFunctionResolvedCall) {
val parameter = processDebugLabel(descriptor) processResolvedCall(resolvedCall.functionCall, expression)
?: processCoroutineContextCall(descriptor) processResolvedCall(resolvedCall.variableCall, expression)
?: processSimpleNameExpression(descriptor) } else {
checkBounds(descriptor, expression, parameter) 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? { override fun visitThisExpression(expression: KtThisExpression, data: Unit?): Void? {
@@ -257,6 +270,10 @@ class CodeFragmentParameterAnalyzer(
} }
private fun processSimpleNameExpression(target: DeclarationDescriptor): Smart? { 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 isLocalTarget = (target as? DeclarationDescriptorWithVisibility)?.visibility == Visibilities.LOCAL
val isPrimaryConstructorParameter = !isLocalTarget val isPrimaryConstructorParameter = !isLocalTarget
@@ -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<String>) {
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
@@ -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
@@ -1086,6 +1086,11 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
runTest("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/isInsideInlineLambda.kt"); 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") @TestMetadata("localFun.kt")
public void testLocalFun() throws Exception { public void testLocalFun() throws Exception {
runTest("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/localFun.kt"); runTest("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/localFun.kt");