Debugger: Fix evaluation of lambda arguments (KT-10636)
This commit is contained in:
+26
-9
@@ -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
|
||||||
|
|||||||
+22
@@ -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
|
||||||
+10
@@ -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
|
||||||
Generated
+5
@@ -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");
|
||||||
|
|||||||
Reference in New Issue
Block a user