From 19e8c9edb095fd04c4959f652949faed8e47b050 Mon Sep 17 00:00:00 2001 From: Natalia Ukhorskaya Date: Tue, 25 Aug 2015 13:52:15 +0300 Subject: [PATCH] Debugger: Support parameters of inlined lambda in evaluate expression --- .../jetbrains/kotlin/psi/JetCodeFragment.kt | 12 +- .../evaluate/KotlinCodeFragmentFactory.kt | 130 +++++++++++++++--- .../outs/inlineFunctionalExpression.out | 1 + .../debugger/tinyApp/outs/inlineLambda.out | 1 + .../tinyApp/outs/parametersOfInlineFun.out | 14 ++ .../parametersOfInlineFunSeveralOnLine.out | 11 ++ .../lambdas/inlineFunctionalExpression.kt | 4 +- .../singleBreakpoint/lambdas/inlineLambda.kt | 4 +- .../singleBreakpoint/parametersOfInlineFun.kt | 37 +++++ .../parametersOfInlineFunSeveralOnLine.kt | 24 ++++ .../AbstractKotlinEvaluateExpressionTest.kt | 2 + ...KotlinEvaluateExpressionTestGenerated.java | 12 ++ 12 files changed, 224 insertions(+), 28 deletions(-) create mode 100644 idea/testData/debugger/tinyApp/outs/parametersOfInlineFun.out create mode 100644 idea/testData/debugger/tinyApp/outs/parametersOfInlineFunSeveralOnLine.out create mode 100644 idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/parametersOfInlineFun.kt create mode 100644 idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/parametersOfInlineFunSeveralOnLine.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetCodeFragment.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetCodeFragment.kt index 648c559e2dd..563bd83cf40 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetCodeFragment.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetCodeFragment.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.psi import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Key import com.intellij.psi.* import com.intellij.psi.impl.PsiManagerEx import com.intellij.psi.impl.source.tree.FileElement @@ -24,14 +25,9 @@ import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.tree.IElementType import com.intellij.testFramework.LightVirtualFile import org.jetbrains.kotlin.idea.JetFileType -import java.util.HashSet -import com.intellij.openapi.util.Key -import com.intellij.psi.impl.PsiModificationTrackerImpl -import com.intellij.psi.util.PsiModificationTracker import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.utils.addToStdlib.check import java.util.LinkedHashSet -import java.util.regex.Pattern public abstract class JetCodeFragment( private val _project: Project, @@ -44,6 +40,9 @@ public abstract class JetCodeFragment( private var viewProvider = super.getViewProvider() as SingleRootFileViewProvider private var myImports = LinkedHashSet() + private val additionalContextForLambda: PsiElement? by lazy { + this.getCopyableUserData(ADDITIONAL_CONTEXT_FOR_LAMBDA)?.invoke() + } init { getViewProvider().forceCachedPsi(this) @@ -70,7 +69,7 @@ public abstract class JetCodeFragment( override fun isValid() = true - override fun getContext() = context + override fun getContext() = additionalContextForLambda ?: context override fun getResolveScope() = context?.getResolveScope() ?: super.getResolveScope() @@ -146,5 +145,6 @@ public abstract class JetCodeFragment( companion object { public val IMPORT_SEPARATOR: String = "," public val RUNTIME_TYPE_EVALUATOR: Key> = Key.create("RUNTIME_TYPE_EVALUATOR") + public val ADDITIONAL_CONTEXT_FOR_LAMBDA: Key> = Key.create("ADDITIONAL_CONTEXT_FOR_LAMBDA") } } diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinCodeFragmentFactory.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinCodeFragmentFactory.kt index 9468083efaf..284a38d1859 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinCodeFragmentFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinCodeFragmentFactory.kt @@ -20,6 +20,10 @@ import com.intellij.debugger.DebuggerManagerEx import com.intellij.debugger.engine.evaluation.CodeFragmentFactory import com.intellij.debugger.engine.evaluation.CodeFragmentKind import com.intellij.debugger.engine.evaluation.TextWithImports +import com.intellij.debugger.engine.events.DebuggerCommandImpl +import com.intellij.debugger.jdi.LocalVariableProxyImpl +import com.intellij.debugger.jdi.StackFrameProxyImpl +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.project.Project @@ -32,20 +36,34 @@ import com.intellij.xdebugger.XDebuggerManager import com.intellij.xdebugger.impl.XDebugSessionImpl import com.intellij.xdebugger.impl.ui.tree.ValueMarkup import com.sun.jdi.ArrayReference -import com.sun.jdi.ObjectReference import com.sun.jdi.PrimitiveValue import com.sun.jdi.Value +import org.jetbrains.annotations.TestOnly +import org.jetbrains.eval4j.jdi.asValue import org.jetbrains.kotlin.asJava.KotlinLightClass +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.idea.JetFileType +import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils import org.jetbrains.kotlin.idea.core.refactoring.j2kText +import org.jetbrains.kotlin.idea.core.refactoring.quoteIfNeeded import org.jetbrains.kotlin.idea.debugger.KotlinEditorTextProvider +import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers +import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext +import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.inline.InlineUtil +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.types.JetType +import org.jetbrains.kotlin.types.typeUtil.makeNullable import org.jetbrains.kotlin.utils.addToStdlib.check -import java.util.HashMap +import java.util.* import java.util.concurrent.atomic.AtomicReference import kotlin.reflect.jvm.java @@ -106,6 +124,65 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() { } }) + if (contextElement != null) { + val lambdas = getInlinedLambdasInside(contextElement) + if (lambdas.isNotEmpty()) { + codeFragment.putCopyableUserData(JetCodeFragment.ADDITIONAL_CONTEXT_FOR_LAMBDA, lamdba@ { + val debuggerContext = DebuggerManagerEx.getInstanceEx(project).context + + val semaphore = Semaphore() + semaphore.down() + + var visibleVariables: List>? = null + + val worker = object : DebuggerCommandImpl() { + override fun action() { + try { + val frameProxy = if (ApplicationManager.getApplication().isUnitTestMode) + context?.getCopyableUserData(DEBUG_FRAME_FOR_TESTS) + else + debuggerContext.frameProxy + + visibleVariables = frameProxy?.let { f -> f.visibleVariables().map { it to f.getValue(it) } } ?: emptyList() + } + finally { + semaphore.up() + } + } + } + + debuggerContext.debugProcess?.managerThread?.invoke(worker) + + for (i in 0..50) { + if (semaphore.waitFor(20)) break + } + + fun isLocalVariableForParameterPresent(p: ValueParameterDescriptor): Boolean { + return visibleVariables?.firstOrNull { + if (it.first.name() != p.name.asString()) return@firstOrNull false + + val parameterClassDescriptor = p.type.constructor.declarationDescriptor as? ClassDescriptor ?: return@firstOrNull true + val actualClassDescriptor = it.second.asValue().asmType.getClassDescriptor(debuggerContext.project) ?: return@firstOrNull true + return@firstOrNull runReadAction { DescriptorUtils.isSubclass(actualClassDescriptor, parameterClassDescriptor) } + } != null + } + + for (lambda in lambdas) { + val function = lambda.analyze(BodyResolveMode.PARTIAL).get(BindingContext.FUNCTION, lambda) + if (function != null && function.valueParameters.all { isLocalVariableForParameterPresent(it) }) { + val fragmentForVisibleVariables = createCodeFragmentForVisibleVariables(lambda.project, visibleVariables!!) + return@lamdba createWrappingContext( + fragmentForVisibleVariables.first, + fragmentForVisibleVariables.second, + lambda.bodyExpression, + lambda.project) + } + } + return@lamdba null + }) + } + } + return codeFragment } @@ -131,6 +208,7 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() { companion object { public val LABEL_VARIABLE_VALUE_KEY: Key = Key.create("_label_variable_value_key_") public val DEBUG_LABEL_SUFFIX: String = "_DebugLabel" + @TestOnly val DEBUG_FRAME_FOR_TESTS: Key = Key.create("DEBUG_FRAME_FOR_TESTS") fun getContextElement(elementAt: PsiElement?): JetElement? { if (elementAt == null) return null @@ -161,6 +239,16 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() { private fun JetElement?.check(): Boolean = this != null && this.check { KotlinEditorTextProvider.isAcceptedAsCodeFragmentContext(it) } != null + private fun getInlinedLambdasInside(contextElement: JetElement): List { + val start = contextElement.startOffset + val end = contextElement.endOffset + + val bindingContext = contextElement.analyze(BodyResolveMode.PARTIAL) + return CodeInsightUtils.findElementsOfClassInRange(contextElement.getContainingJetFile(), start, end, javaClass(), javaClass()) + .filterIsInstance() + .filter { JetPsiUtil.getParentCallIfPresent(it as JetExpression) != null && InlineUtil.isInlinedArgument(it, bindingContext, false) } + } + //internal for tests fun createCodeFragmentForLabeledObjects(project: Project, markupMap: Map<*, ValueMarkup>): Pair> { val sb = StringBuilder() @@ -172,7 +260,7 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() { val objectRef = value as? Value ?: continue val labelNameWithSuffix = "$labelName$DEBUG_LABEL_SUFFIX" - sb.append("${createKotlinProperty(project, labelNameWithSuffix, objectRef.type().name(), TypeKind.getTypeKind(objectRef))}\n") + sb.append("${createKotlinProperty(project, labelNameWithSuffix, objectRef.type().name(), objectRef)}\n") labeledObjects.put(labelNameWithSuffix, objectRef) } @@ -180,28 +268,34 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() { return sb.toString() to labeledObjects } - private enum class TypeKind { - PRIMITIVE, - ARRAY, - OBJECT; + private fun createCodeFragmentForVisibleVariables(project: Project, visibleVariables: List>): Pair> { + val sb = StringBuilder() + val labeledObjects = HashMap() + for ((variable, value) in visibleVariables) { + val variableName = variable.name() + if (!Name.isValidIdentifier(variableName)) continue - companion object { - fun getTypeKind(value: Value): TypeKind { - return when(value) { - is ArrayReference -> ARRAY - is PrimitiveValue -> PRIMITIVE - else -> OBJECT - } - } + val kotlinProperty = createKotlinProperty(project, variableName, variable.typeName(), value) ?: continue + sb.append("$kotlinProperty\n") + + labeledObjects.put(variableName, value) } + sb.append("val _debug_context_val = 1") + return sb.toString() to labeledObjects } - private fun createKotlinProperty(project: Project, variableName: String, variableTypeName: String, typeKind: TypeKind): String? { - fun String.addArraySuffix() = if (typeKind == TypeKind.ARRAY) this + "[]" else this + private fun createKotlinProperty(project: Project, variableName: String, variableTypeName: String, value: Value): String? { + val actualClassDescriptor = value.asValue().asmType.getClassDescriptor(project) + if (actualClassDescriptor != null && actualClassDescriptor.defaultType.arguments.isEmpty()) { + val renderedType = IdeDescriptorRenderers.SOURCE_CODE.renderType(actualClassDescriptor.defaultType.makeNullable()) + return "val ${variableName.quoteIfNeeded()}: $renderedType = null" + } + + fun String.addArraySuffix() = if (value is ArrayReference) this + "[]" else this val className = variableTypeName.replace("$", ".").substringBefore("[]") val classType = PsiType.getTypeByName(className, project, GlobalSearchScope.allScope(project)) - val type = (if (typeKind != TypeKind.PRIMITIVE && classType.resolve() == null) + val type = (if (value !is PrimitiveValue && classType.resolve() == null) CommonClassNames.JAVA_LANG_OBJECT else className).addArraySuffix() diff --git a/idea/testData/debugger/tinyApp/outs/inlineFunctionalExpression.out b/idea/testData/debugger/tinyApp/outs/inlineFunctionalExpression.out index 06f13028217..ed3b77b37f5 100644 --- a/idea/testData/debugger/tinyApp/outs/inlineFunctionalExpression.out +++ b/idea/testData/debugger/tinyApp/outs/inlineFunctionalExpression.out @@ -3,6 +3,7 @@ LineBreakpoint created at inlineFunctionalExpression.kt:9 Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' inlineFunctionalExpression.kt:9 inlineFunctionalExpression.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/outs/inlineLambda.out b/idea/testData/debugger/tinyApp/outs/inlineLambda.out index ffd6e2a6dd7..3b6dc528560 100644 --- a/idea/testData/debugger/tinyApp/outs/inlineLambda.out +++ b/idea/testData/debugger/tinyApp/outs/inlineLambda.out @@ -3,6 +3,7 @@ LineBreakpoint created at inlineLambda.kt:9 Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' inlineLambda.kt:9 inlineLambda.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/outs/parametersOfInlineFun.out b/idea/testData/debugger/tinyApp/outs/parametersOfInlineFun.out new file mode 100644 index 00000000000..fe6656cbe10 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/parametersOfInlineFun.out @@ -0,0 +1,14 @@ +LineBreakpoint created at parametersOfInlineFun.kt:8 +!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! parametersOfInlineFun.ParametersOfInlineFunPackage +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +parametersOfInlineFun.kt:8 +parametersOfInlineFun.kt:8 +Compile bytecode for primitive +Compile bytecode for it +Compile bytecode for array +Compile bytecode for str +Compile bytecode for list +Compile bytecode for `$receiver`.prop +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/parametersOfInlineFunSeveralOnLine.out b/idea/testData/debugger/tinyApp/outs/parametersOfInlineFunSeveralOnLine.out new file mode 100644 index 00000000000..dfe78b9bd9d --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/parametersOfInlineFunSeveralOnLine.out @@ -0,0 +1,11 @@ +LineBreakpoint created at parametersOfInlineFunSeveralOnLine.kt:8 +!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! parametersOfInlineFunSeveralOnLine.ParametersOfInlineFunSeveralOnLinePackage +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +parametersOfInlineFunSeveralOnLine.kt:8 +parametersOfInlineFunSeveralOnLine.kt:8 +parametersOfInlineFunSeveralOnLine.kt:8 +parametersOfInlineFunSeveralOnLine.kt:8 +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 index 6aeefeabdc9..a34f3187d1c 100644 --- a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/inlineFunctionalExpression.kt +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/inlineFunctionalExpression.kt @@ -3,8 +3,8 @@ package inlineFunctionalExpression fun main(args: Array) { val a = array(1) // EXPRESSION: it - // RESULT: Unresolved reference: it - // STEP_INTO: 1 + // RESULT: 1: I + // RESUME: 1 //Breakpoint! a.map(fun (it): Int { return it * 1 }) } diff --git a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/inlineLambda.kt b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/inlineLambda.kt index b937ea8b53f..5b78438bd2b 100644 --- a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/inlineLambda.kt +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/inlineLambda.kt @@ -3,8 +3,8 @@ package inlineLambda fun main(args: Array) { val a = array(1) // EXPRESSION: it - // RESULT: Unresolved reference: it - // STEP_INTO: 1 + // RESULT: 1: I + // RESUME: 1 //Breakpoint! a.map { it * 1 } } diff --git a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/parametersOfInlineFun.kt b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/parametersOfInlineFun.kt new file mode 100644 index 00000000000..973d89fbb17 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/parametersOfInlineFun.kt @@ -0,0 +1,37 @@ +// Check that evaluate expression works inside inline function +package parametersOfInlineFun + +fun main(args: Array) { + val a = A(1) + // RESUME: 1 + //Breakpoint! + a.foo { 1 + 1 } +} + +inline fun A.foo(f: (i: Int) -> Unit) { + val primitive = 1 + val array = arrayOf(1) + val str = "str" + val list = listOf("str") + f(1) +} + +class A(val prop: Int) + +// EXPRESSION: primitive +// RESULT: 1: I + +// EXPRESSION: it +// RESULT: 1: I + +// EXPRESSION: array +// RESULT: instance of java.lang.Integer[1] (id=ID): [Ljava/lang/Integer; + +// EXPRESSION: str +// RESULT: "str": Ljava/lang/String; + +// EXPRESSION: list +// RESULT: instance of java.util.Collections$SingletonList(id=ID): Ljava/util/Collections$SingletonList; + +// EXPRESSION: `$receiver`.prop +// RESULT: 1: I \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/parametersOfInlineFunSeveralOnLine.kt b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/parametersOfInlineFunSeveralOnLine.kt new file mode 100644 index 00000000000..9cb75bf8516 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/parametersOfInlineFunSeveralOnLine.kt @@ -0,0 +1,24 @@ +// Check that evaluate expression works inside inline function +package parametersOfInlineFunSeveralOnLine + +fun main(args: Array) { + val a = A() + // RESUME: 3 + //Breakpoint! + a.foo { 1 + 1 }.bar { 1 + 1 } +} + +inline fun A.foo(f: (i: Int) -> Unit): A { + f(1) + return this +} + +inline fun A.bar(f: (s: String) -> Unit): A { + f("str") + return this +} + +class A() + +// EXPRESSION: it +// RESULT: "str": Ljava/lang/String; \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt index 4c439c52a65..14543210191 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt @@ -311,6 +311,8 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestB val sourcePosition = ContextUtil.getSourcePosition(this) val contextElement = createContextElement(this) + contextElement.putCopyableUserData(KotlinCodeFragmentFactory.DEBUG_FRAME_FOR_TESTS, evaluationContext?.frameProxy) + try { val evaluator = 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 99e9c779431..a45b7a497fb 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java @@ -211,6 +211,18 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat doSingleBreakpointTest(fileName); } + @TestMetadata("parametersOfInlineFun.kt") + public void testParametersOfInlineFun() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/parametersOfInlineFun.kt"); + doSingleBreakpointTest(fileName); + } + + @TestMetadata("parametersOfInlineFunSeveralOnLine.kt") + public void testParametersOfInlineFunSeveralOnLine() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/parametersOfInlineFunSeveralOnLine.kt"); + doSingleBreakpointTest(fileName); + } + @TestMetadata("privateMember.kt") public void testPrivateMember() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/privateMember.kt");