From 7992df7b931b76d64800d89890dd50bc079049da Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Mon, 26 Sep 2016 15:14:54 +0300 Subject: [PATCH] Change the way 'step over' over inline calls works (KT-13751) Previously it worked by invoking 'Run To Cursor' for the last position of inline function. As there's only one 'run to cursor' breakpoint available in Idea framework, it couldn't work when inline function call was was used in conditions of control flow statements. A new approach works through multiple step over operation and controlling stop position. In other words we try to "step over" inlined lines. Same thing is actually done in "Smart Step Into" action. (cherry picked from commit 2e8775d) #KT-13751 Fixed --- .../stepping/DebuggerSteppingHelper.java | 3 +- .../stepping/KotlinStepActionFactory.kt | 156 +++++++++ .../KotlinStepOverInlinedLinesHint.kt | 130 +++++++ .../stepping/KotlinSteppingCommandProvider.kt | 328 +++++++++--------- .../debugger/tinyApp/outs/inlineInIfFalse.out | 8 + .../debugger/tinyApp/outs/inlineInIfTrue.out | 8 + .../soInlineAnonymousFunctionArgument.out | 10 + .../soInlineCallInLastStatementInInline.out | 9 + ...ineCallInLastStatementInInlineInInline.out | 9 + ...ntOfInlineWithArgumentFromCalleeAndOwn.out | 9 + ...WithLastStatementMultilineArgumentCall.out | 8 + ...unWithLastStatementOneLineArgumentCall.out | 9 + .../outs/soInlineIfConditionLambdaFalse.out | 10 + .../outs/soInlineIfConditionLambdaTrue.out | 10 + .../tinyApp/outs/soInlineWhileCondition.out | 12 + .../outs/soReifiedInlineIfConditionFalse.out | 8 + .../outs/soSimpleInlineIfCondition.out | 8 + .../outs/stepOverDeclarationInInlineFun.out | 8 + .../tinyApp/outs/stepOverIfWithInline.out | 11 +- .../tinyApp/outs/stepOverInlinedLambda.out | 6 +- .../tinyApp/outs/stepOverInsideInlineFun.out | 1 + .../outs/stepOverNonLocalReturnInLambda.out | 2 +- .../outs/stepOverTryCatchWithInline.out | 6 - .../tinyApp/outs/stepOverWhenInReturn.out | 2 - .../tinyApp/outs/stepOverWhenWithInline.out | 14 - .../tinyApp/outs/stepOverWhileWithInline.out | 15 +- .../src/stepping/stepOver/inlineInIfFalse.kt | 16 + .../src/stepping/stepOver/inlineInIfTrue.kt | 15 + .../soInlineAnonymousFunctionArgument.kt | 20 ++ .../soInlineCallInLastStatementInInline.kt | 17 + ...lineCallInLastStatementInInlineInInline.kt | 18 + ...entOfInlineWithArgumentFromCalleeAndOwn.kt | 20 ++ ...nWithLastStatementMultilineArgumentCall.kt | 17 + ...FunWithLastStatementOneLineArgumentCall.kt | 14 + .../soInlineIfConditionLambdaFalse.kt | 23 ++ .../stepOver/soInlineIfConditionLambdaTrue.kt | 23 ++ .../stepOver/soInlineWhileCondition.kt | 23 ++ .../soReifiedInlineIfConditionFalse.kt | 15 + .../stepOver/soSimpleInlineIfCondition.kt | 18 + .../stepOverDeclarationInInlineFun.kt | 11 + .../stepping/stepOver/stepOverIfWithInline.kt | 36 +- .../stepOver/stepOverInlinedLambda.kt | 6 +- .../stepOver/stepOverInlinedLambdaStdlib.kt | 8 +- .../stepOver/stepOverInsideInlineFun.kt | 5 +- .../stepOver/stepOverTryCatchWithInline.kt | 32 +- .../stepping/stepOver/stepOverWhenInReturn.kt | 12 +- .../stepOver/stepOverWhenWithInline.kt | 52 +-- .../stepOver/stepOverWhileWithInline.kt | 41 +-- .../debugger/KotlinSteppingTestGenerated.java | 84 +++++ 49 files changed, 1004 insertions(+), 322 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinStepActionFactory.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinStepOverInlinedLinesHint.kt create mode 100644 idea/testData/debugger/tinyApp/outs/inlineInIfFalse.out create mode 100644 idea/testData/debugger/tinyApp/outs/inlineInIfTrue.out create mode 100644 idea/testData/debugger/tinyApp/outs/soInlineAnonymousFunctionArgument.out create mode 100644 idea/testData/debugger/tinyApp/outs/soInlineCallInLastStatementInInline.out create mode 100644 idea/testData/debugger/tinyApp/outs/soInlineCallInLastStatementInInlineInInline.out create mode 100644 idea/testData/debugger/tinyApp/outs/soInlineFunCallInLastStatementOfInlineWithArgumentFromCalleeAndOwn.out create mode 100644 idea/testData/debugger/tinyApp/outs/soInlineFunWithLastStatementMultilineArgumentCall.out create mode 100644 idea/testData/debugger/tinyApp/outs/soInlineFunWithLastStatementOneLineArgumentCall.out create mode 100644 idea/testData/debugger/tinyApp/outs/soInlineIfConditionLambdaFalse.out create mode 100644 idea/testData/debugger/tinyApp/outs/soInlineIfConditionLambdaTrue.out create mode 100644 idea/testData/debugger/tinyApp/outs/soInlineWhileCondition.out create mode 100644 idea/testData/debugger/tinyApp/outs/soReifiedInlineIfConditionFalse.out create mode 100644 idea/testData/debugger/tinyApp/outs/soSimpleInlineIfCondition.out create mode 100644 idea/testData/debugger/tinyApp/outs/stepOverDeclarationInInlineFun.out create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/inlineInIfFalse.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/inlineInIfTrue.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineAnonymousFunctionArgument.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineCallInLastStatementInInline.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineCallInLastStatementInInlineInInline.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunCallInLastStatementOfInlineWithArgumentFromCalleeAndOwn.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunWithLastStatementMultilineArgumentCall.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunWithLastStatementOneLineArgumentCall.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineIfConditionLambdaFalse.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineIfConditionLambdaTrue.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineWhileCondition.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soReifiedInlineIfConditionFalse.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soSimpleInlineIfCondition.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverDeclarationInInlineFun.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/DebuggerSteppingHelper.java b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/DebuggerSteppingHelper.java index e83f5bcda26..744bc73e35b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/DebuggerSteppingHelper.java +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/DebuggerSteppingHelper.java @@ -58,7 +58,8 @@ public class DebuggerSteppingHelper { if (frameProxy != null) { Action action = KotlinSteppingCommandProviderKt.getStepOverAction( frameProxy.location(), - kotlinSourcePosition + kotlinSourcePosition, + frameProxy ); createStepRequest( diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinStepActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinStepActionFactory.kt new file mode 100644 index 00000000000..b7408218897 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinStepActionFactory.kt @@ -0,0 +1,156 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.debugger.stepping + +import com.intellij.debugger.DebuggerManagerEx +import com.intellij.debugger.engine.* +import com.intellij.debugger.engine.evaluation.EvaluateException +import com.intellij.debugger.impl.DebuggerContextImpl +import com.intellij.debugger.impl.DebuggerSession +import com.intellij.debugger.jdi.ThreadReferenceProxyImpl +import com.intellij.debugger.settings.DebuggerSettings +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.project.Project +import com.intellij.util.EventDispatcher +import com.sun.jdi.request.EventRequest +import com.sun.jdi.request.StepRequest + +// Mass-copy-paste code for commands behaviour from com.intellij.debugger.engine.DebugProcessImpl +@SuppressWarnings("UnnecessaryFinalOnLocalVariableOrParameter") +class KotlinStepActionFactory(private val debuggerProcess: DebugProcessImpl) { + abstract class KotlinStepAction { + abstract fun contextAction(suspendContext: SuspendContextImpl) + } + + fun createKotlinStepOverInlineAction(smartStepFilter: KotlinMethodFilter): KotlinStepAction { + return StepOverInlineCommand(smartStepFilter, StepRequest.STEP_LINE) + } + + private val debuggerContext: DebuggerContextImpl get() = debuggerProcess.debuggerContext + private val suspendManager: SuspendManager get() = debuggerProcess.suspendManager + private val project: Project get() = debuggerProcess.project + private val session: DebuggerSession get() = debuggerProcess.session + + // TODO: ask for better API + private val debugProcessDispatcher: EventDispatcher + // Should be safe to use reflection as field is protected and not obfuscated + get() = getFromField("myDebugProcessDispatcher") + + // TODO: ask for better API + private val threadBlockedMonitor: ThreadBlockedMonitor + // FIXME: obfuscated in ULTIMATE. Absent in old AS + get() = getFromField("myThreadBlockedMonitor") + + private fun showStatusText(message: String) { + debuggerProcess.showStatusText(message) + } + + // TODO: ask for better API + private fun doStep( + suspendContext: SuspendContextImpl, + stepThread: ThreadReferenceProxyImpl, + size: Int, depth: Int, hint: RequestHint) { + // Should be safe to use reflection as field is protected and not obfuscated + val doStepMethod = DebugProcessImpl::class.java.getDeclaredMethod( + "doStep", + SuspendContextImpl::class.java, ThreadReferenceProxyImpl::class.java, + Integer.TYPE, Integer.TYPE, RequestHint::class.java) + + doStepMethod.isAccessible = true + + doStepMethod.invoke(debuggerProcess, suspendContext, stepThread, size, depth, hint) + } + + private fun getFromField(fieldName: String): T { + val field = DebugProcessImpl::class.java.getDeclaredField(fieldName) + field.isAccessible = true + + @Suppress("UNCHECKED_CAST") + return field.get(debuggerProcess) as T + } + + private inner class StepOverInlineCommand(private val mySmartStepFilter: KotlinMethodFilter, private val myStepSize: Int) : KotlinStepAction() { + private fun getContextThread(suspendContext: SuspendContextImpl): ThreadReferenceProxyImpl? { + val contextThread = debuggerContext.threadProxy + return contextThread ?: suspendContext.thread + } + + // See: ResumeCommand.applyThreadFilter() + private fun applyThreadFilter(suspendContext: SuspendContextImpl, thread: ThreadReferenceProxyImpl) { + if (suspendContext.suspendPolicy == EventRequest.SUSPEND_ALL) { + // there could be explicit resume as a result of call to voteSuspend() + // e.g. when breakpoint was considered invalid, in that case the filter will be applied _after_ + // resuming and all breakpoints in other threads will be ignored. + // As resume() implicitly cleares the filter, the filter must be always applied _before_ any resume() action happens + val breakpointManager = DebuggerManagerEx.getInstanceEx(project).breakpointManager + breakpointManager.applyThreadFilter(debuggerProcess, thread.threadReference) + } + } + + // See: StepCommand.resumeAction() + private fun resumeAction(suspendContext: SuspendContextImpl, thread: ThreadReferenceProxyImpl) { + if (suspendContext.suspendPolicy == EventRequest.SUSPEND_EVENT_THREAD || isResumeOnlyCurrentThread) { + threadBlockedMonitor.startWatching(thread) + } + if (isResumeOnlyCurrentThread && suspendContext.suspendPolicy == EventRequest.SUSPEND_ALL) { + suspendManager.resumeThread(suspendContext, thread) + } + else { + suspendManager.resume(suspendContext) + } + } + + // See: StepIntoCommand.contextAction() + override fun contextAction(suspendContext: SuspendContextImpl) { + showStatusText("Stepping over inline") + val stepThread = getContextThread(suspendContext) + + if (stepThread == null) { + // TODO: Intellij code doesn't bother to check thread for null, so probably it's not-null actually + debuggerProcess.createStepOverCommand(suspendContext, true).contextAction(suspendContext) + return + } + + val hint = KotlinStepOverInlinedLinesHint(stepThread, suspendContext, mySmartStepFilter) + hint.isResetIgnoreFilters = !session.shouldIgnoreSteppingFilters() + + try { + session.setIgnoreStepFiltersFlag(stepThread.frameCount()) + } + catch (e: EvaluateException) { + LOG.info(e) + } + + hint.isIgnoreFilters = true + applyThreadFilter(suspendContext, stepThread) + + doStep(suspendContext, stepThread, myStepSize, StepRequest.STEP_OVER, hint) + + showStatusText("Process resumed") + resumeAction(suspendContext, stepThread) + debugProcessDispatcher.multicaster.resumed(suspendContext) + } + + } + + companion object { + private val LOG = Logger.getInstance(KotlinStepActionFactory::class.java) + + private val isResumeOnlyCurrentThread: Boolean + get() = DebuggerSettings.getInstance().RESUME_ONLY_CURRENT_THREAD + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinStepOverInlinedLinesHint.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinStepOverInlinedLinesHint.kt new file mode 100644 index 00000000000..3693d28edf8 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinStepOverInlinedLinesHint.kt @@ -0,0 +1,130 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.debugger.stepping + +import com.intellij.debugger.SourcePosition +import com.intellij.debugger.engine.* +import com.intellij.debugger.engine.evaluation.EvaluateException +import com.intellij.debugger.engine.jdi.StackFrameProxy +import com.intellij.debugger.jdi.ThreadReferenceProxyImpl +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.util.Computable +import com.sun.jdi.VMDisconnectedException +import com.sun.jdi.request.StepRequest + +// Originally copied from RequestHint +class KotlinStepOverInlinedLinesHint( + stepThread: ThreadReferenceProxyImpl, + suspendContext: SuspendContextImpl, + methodFilter: KotlinMethodFilter) : RequestHint(stepThread, suspendContext, methodFilter) { + + private val LOG = Logger.getInstance(KotlinStepOverInlinedLinesHint::class.java) + + private var mySteppedOut = false + + private var myFrameCount: Int + private var myPosition: SourcePosition? + + // TODO: Copied from RequestHint constructor. But can't reused code because of private fields. + init { + var frameCount = 0 + var position: SourcePosition? = null + try { + frameCount = stepThread.frameCount() + + position = ApplicationManager.getApplication().runReadAction(Computable { + ContextUtil.getSourcePosition(object : StackFrameContext { + override fun getFrameProxy(): StackFrameProxy? { + try { + return stepThread.frame(0) + } + catch (e: EvaluateException) { + LOG.debug(e) + return null + } + + } + + override fun getDebugProcess(): DebugProcess { + return suspendContext.debugProcess + } + }) + }) + } + catch (e: Exception) { + LOG.info(e) + } + finally { + myFrameCount = frameCount + myPosition = position + } + } + + private val filter = methodFilter + + override fun getDepth(): Int = StepRequest.STEP_OVER + + // TODO: Copy of RequestHint.isTheSameFrame() + private fun isTheSameFrame(context: SuspendContextImpl): Boolean { + if (mySteppedOut) return false + + val contextThread = context.thread + if (contextThread != null) { + try { + val currentDepth = contextThread.frameCount() + if (currentDepth < myFrameCount) { + mySteppedOut = true + } + return currentDepth == myFrameCount + } + catch (ignored: EvaluateException) { + } + + } + return false + } + + override fun getNextStepDepth(context: SuspendContextImpl): Int { + try { + val frameProxy = context.frameProxy + if (frameProxy != null) { + if (isTheSameFrame(context)) { + if (filter.locationMatches(context, frameProxy.location())) { + return STOP + } + else { + return StepRequest.STEP_OVER + } + } + + if (mySteppedOut) { + return STOP + } + + return StepRequest.STEP_OUT + } + } + catch (ignored: VMDisconnectedException) { + } + catch (e: EvaluateException) { + LOG.error(e) + } + + return STOP + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt index 5df9c10c0dc..1fd68882cbf 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt @@ -19,15 +19,21 @@ package org.jetbrains.kotlin.idea.debugger.stepping import com.intellij.debugger.NoDataException import com.intellij.debugger.SourcePosition import com.intellij.debugger.engine.DebugProcessImpl +import com.intellij.debugger.engine.MethodFilter import com.intellij.debugger.engine.SuspendContextImpl import com.intellij.debugger.impl.DebuggerContextImpl import com.intellij.debugger.impl.JvmSteppingCommandProvider +import com.intellij.debugger.jdi.StackFrameProxyImpl import com.intellij.psi.PsiElement import com.intellij.xdebugger.impl.XSourcePositionImpl import com.sun.jdi.AbsentInformationException +import com.sun.jdi.LocalVariable import com.sun.jdi.Location import org.jetbrains.annotations.TestOnly +import org.jetbrains.kotlin.builtins.isFunctionType import org.jetbrains.kotlin.codegen.inline.KOTLIN_STRATA_NAME +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade @@ -38,12 +44,14 @@ import org.jetbrains.kotlin.idea.refactoring.getLineEndOffset import org.jetbrains.kotlin.idea.refactoring.getLineNumber import org.jetbrains.kotlin.idea.refactoring.getLineStartOffset import org.jetbrains.kotlin.idea.util.application.runReadAction +import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.psi.psiUtil.parents import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCallImpl import org.jetbrains.kotlin.resolve.inline.InlineUtil import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode @@ -102,7 +110,23 @@ class KotlinSteppingCommandProvider : JvmSteppingCommandProvider() { } private fun isSpecialStepOverNeeded(kotlinSourcePosition: KotlinSourcePosition): Boolean { - return getElementsToSkip(kotlinSourcePosition.function, kotlinSourcePosition.sourcePosition) != null + val sourcePosition = kotlinSourcePosition.sourcePosition + + val hasInlineCallsOnLine = getInlineFunctionCallsIfAny(sourcePosition).isNotEmpty() + if (hasInlineCallsOnLine) { + return true + } + + // Step over calls to lambda arguments in inline function while execution is already in that function + val containingFunctionDescriptor = kotlinSourcePosition.function.resolveToDescriptor() + if (InlineUtil.isInline(containingFunctionDescriptor)) { + val inlineArgumentsCallsIfAny = getInlineArgumentsCallsIfAny(sourcePosition, containingFunctionDescriptor) + if (inlineArgumentsCallsIfAny != null && inlineArgumentsCallsIfAny.isNotEmpty()) { + return true + } + } + + return false } @TestOnly @@ -132,123 +156,15 @@ class KotlinSteppingCommandProvider : JvmSteppingCommandProvider() { } } -private fun getElementsToSkip(containingFunction: KtNamedFunction, sourcePosition: SourcePosition): List? { - val inlineFunctionCalls = getInlineFunctionCallsIfAny(sourcePosition) - if (!inlineFunctionCalls.isEmpty()) { - val inlineArguments = getInlineArgumentsIfAny(inlineFunctionCalls) - if (inlineArguments.isNotEmpty() && inlineArguments.all { !it.shouldNotUseStepOver(sourcePosition.elementAt) }) { - return inlineArguments - } - - if (inlineArguments.isEmpty() && inlineFunctionCalls.all { !it.shouldNotUseStepOver(sourcePosition.elementAt) }) { - return emptyList() - } - } - - // Step over calls to lambda arguments in inline function while execution is already in that function - if (InlineUtil.isInline(containingFunction.resolveToDescriptor())) { - return emptyList() - } - - return null -} - -private fun PsiElement.getAdditionalElementsToSkip(): List { - val result = arrayListOf() - val ifParent = getParentOfType(false) - if (ifParent != null) { - if (ifParent.then.contains(this)) { - ifParent.elseKeyword?.let { result.add(it) } - ifParent.`else`?.let { result.add(it) } - } - } - val tryParent = getParentOfType(false) - if (tryParent != null) { - val catchClause = getParentOfType(false) - if (catchClause != null) { - result.addAll(tryParent.catchClauses.filter { it != catchClause }) - } - } - - val whenEntry = getParentOfType(false) - if (whenEntry != null) { - if (whenEntry.expression.contains(this)) { - val whenParent = whenEntry.getParentOfType(false) - if (whenParent != null) { - result.addAll(whenParent.entries.filter { it != whenEntry }) - } - } - } - - return result -} - -private fun PsiElement.shouldNotUseStepOver(elementAt: PsiElement): Boolean { - val ifParent = getParentOfType(false) - if (ifParent != null) { - // if (inlineFunCall()) {...} - if (ifParent.condition.contains(this)) { - return true - } - - /* - if (...) inlineFunCall() - else inlineFunCall() - */ - val ifParentElementAt = elementAt.getParentOfType(false) - if (ifParentElementAt == null) { - if (ifParent.then.contains(this)) { - return true - } - if (ifParent.`else`.contains(this)) { - return true - } - } - } - - val tryParent = getParentOfType(false) - if (tryParent != null) { - /* try { inlineFunCall() } - catch()... - */ - if (tryParent.tryBlock.contains(this)) { - return true - } - } - - val whenEntry = getParentOfType(false) - if (whenEntry != null) { - // inlineFunCall -> ... - if (whenEntry.conditions.any { it.contains(this) } ) { - return true - } - - // 1 == 2 -> inlineFunCall() - if (whenEntry.expression.contains(this)) { - val parentEntryElementAt = elementAt.getParentOfType(false) ?: return true - return parentEntryElementAt == whenEntry && - whenEntry.conditions.any { it.contains(elementAt) } - } - } - - val whileParent = getParentOfType(false) - if (whileParent != null) { - // while (inlineFunCall()) {...} - if (whileParent.condition.contains(this)) { - return true - } - - // last statement in while - return (whileParent.body as? KtBlockExpression)?.statements?.lastOrNull()?.getLineNumber() == elementAt.getLineNumber() - } - - return false -} - private fun PsiElement?.contains(element: PsiElement): Boolean { return this?.textRange?.contains(element.textRange) ?: false } +private fun getInlineCallFunctionArgumentsIfAny(sourcePosition: SourcePosition): List { + val inlineFunctionCalls = getInlineFunctionCallsIfAny(sourcePosition) + return getInlineArgumentsIfAny(inlineFunctionCalls) +} + private fun getInlineFunctionsIfAny(file: KtFile, offset: Int): List { val elementAt = file.findElementAt(offset) ?: return emptyList() val containingFunction = elementAt.getParentOfType(false) ?: return emptyList() @@ -269,14 +185,46 @@ private fun getInlineFunctionsIfAny(file: KtFile, offset: Int): List): List { return inlineFunctionCalls.flatMap { it.valueArguments - .map { getArgumentExpression(it) } + .map { getArgumentExpression(it) } .filterIsInstance() } } private fun getArgumentExpression(it: ValueArgument) = (it.getArgumentExpression() as? KtLambdaExpression)?.functionLiteral ?: it.getArgumentExpression() +private fun getInlineArgumentsCallsIfAny(sourcePosition: SourcePosition, declarationDescriptor: DeclarationDescriptor): List? { + if (declarationDescriptor !is CallableDescriptor) return null + + val valueParameters = declarationDescriptor.valueParameters.filter { it.type.isFunctionType }.toSet() + + if (valueParameters.isEmpty()) { + return null + } + + fun isCallOfArgument(ktCallExpression: KtCallExpression): Boolean { + val context = ktCallExpression.analyze(BodyResolveMode.PARTIAL) + val resolvedCall = ktCallExpression.getResolvedCall(context) ?: return false + + if (resolvedCall !is VariableAsFunctionResolvedCallImpl) return false + val candidateDescriptor = resolvedCall.variableCall.candidateDescriptor + + return candidateDescriptor in valueParameters + } + + return findCallsOnPosition(sourcePosition, ::isCallOfArgument) +} + private fun getInlineFunctionCallsIfAny(sourcePosition: SourcePosition): List { + fun isInlineCall(expr: KtCallExpression): Boolean { + val context = expr.analyze(BodyResolveMode.PARTIAL) + val resolvedCall = expr.getResolvedCall(context) ?: return false + return InlineUtil.isInline(resolvedCall.resultingDescriptor) + } + + return findCallsOnPosition(sourcePosition, ::isInlineCall) +} + +private fun findCallsOnPosition(sourcePosition: SourcePosition, filter: (KtCallExpression) -> Boolean): List { val file = sourcePosition.file as? KtFile ?: return emptyList() val lineNumber = sourcePosition.line var elementAt = sourcePosition.elementAt @@ -298,22 +246,16 @@ private fun getInlineFunctionCallsIfAny(sourcePosition: SourcePosition): List() - .filter { isInlineCall(it) } + .filter { filter(it) } .toSet() // It is necessary to check range because of multiline assign var linesRange = lineNumber..lineNumber - return allInlineFunctionCalls.filter { + return allFilteredCalls.filter { val shouldInclude = it.getLineNumber() in linesRange if (shouldInclude) { linesRange = Math.min(linesRange.start, it.getLineNumber())..Math.max(linesRange.endInclusive, it.getLineNumber(false)) @@ -322,10 +264,16 @@ private fun getInlineFunctionCallsIfAny(sourcePosition: SourcePosition): List? = null, + val inlineRangeVariables: List? = null) { + class STEP_OVER : Action() + class STEP_OUT : Action() + class RUN_TO_CURSOR(position: XSourcePositionImpl) : Action(position) + class STEP_OVER_INLINED(lineNumber: Int, stepOverLines: Set, inlineVariables: List) : Action( + lineNumber = lineNumber, stepOverLines = stepOverLines, inlineRangeVariables = inlineVariables) fun apply(debugProcess: DebugProcessImpl, suspendContext: SuspendContextImpl, @@ -338,30 +286,63 @@ sealed class Action(val position: XSourcePositionImpl?) { } is Action.STEP_OUT -> debugProcess.createStepOutCommand(suspendContext).contextAction(suspendContext) is Action.STEP_OVER -> debugProcess.createStepOverCommand(suspendContext, ignoreBreakpoints).contextAction(suspendContext) + is Action.STEP_OVER_INLINED -> KotlinStepActionFactory(debugProcess).createKotlinStepOverInlineAction( + KotlinStepOverInlineFilter(stepOverLines!!, lineNumber ?: -1, inlineRangeVariables!!)).contextAction(suspendContext) } } } +interface KotlinMethodFilter: MethodFilter { + fun locationMatches(context: SuspendContextImpl, location: Location): Boolean +} + +class KotlinStepOverInlineFilter(val stepOverLines: Set, val fromLine: Int, + val inlineFunRangeVariables: List) : KotlinMethodFilter { + override fun locationMatches(context: SuspendContextImpl, location: Location): Boolean { + val frameProxy = context.frameProxy + if (frameProxy == null) return true + + val currentLine = location.lineNumber() + + if (!(stepOverLines.contains(location.lineNumber()))) { + return currentLine != fromLine + } + + val visibleInlineVariables = getInlineRangeLocalVariables(frameProxy) + + // Our ranges check missed exit from inline function. This is when breakpoint was in last statement of inline functions. + // This can be observed by inline local range-variables. Absence of any means step out was done. + return inlineFunRangeVariables.any { !visibleInlineVariables.contains(it) } + } + + override fun locationMatches(process: DebugProcessImpl, location: Location): Boolean { + throw IllegalStateException() // Should not be called from Kotlin hint + } + + override fun getCallingExpressionLines(): Range? { + throw IllegalStateException() // Should not be called from Kotlin hint + } +} + fun getStepOverAction( location: Location, - kotlinSourcePosition: KotlinSteppingCommandProvider.KotlinSourcePosition + kotlinSourcePosition: KotlinSteppingCommandProvider.KotlinSourcePosition, + frameProxy: StackFrameProxyImpl ): Action { - val (inlineArgumentsToSkip, additionalElementsToSkip) = runReadAction { - val inlineArgumentsToSkip = getElementsToSkip(kotlinSourcePosition.function, kotlinSourcePosition.sourcePosition)!! - val additionalElementsToSkip = kotlinSourcePosition.sourcePosition.elementAt.getAdditionalElementsToSkip() - - Pair(inlineArgumentsToSkip, additionalElementsToSkip) + val inlineArgumentsToSkip = runReadAction { + getInlineCallFunctionArgumentsIfAny(kotlinSourcePosition.sourcePosition) } - return getStepOverAction(location, kotlinSourcePosition.file, kotlinSourcePosition.linesRange, inlineArgumentsToSkip, additionalElementsToSkip) + return getStepOverAction(location, kotlinSourcePosition.file, kotlinSourcePosition.linesRange, + inlineArgumentsToSkip, frameProxy) } fun getStepOverAction( location: Location, file: KtFile, range: IntRange, - inlinedArguments: List, - elementsToSkip: List + inlineFunctionArguments: List, + frameProxy: StackFrameProxyImpl ): Action { val computedReferenceType = location.declaringType() ?: return Action.STEP_OVER() @@ -389,38 +370,46 @@ fun getStepOverAction( return previousSuitableLocation != null && previousSuitableLocation.lineNumber() > location.lineNumber() } - if (isBackEdgeLocation()) { - return Action.STEP_OVER() + val patchedLocation = if (isBackEdgeLocation()) { + // Pretend we had already did a backing step + computedReferenceType.allLineLocations() + .filter(::isLocationSuitable) + .first { it.lineNumber() == location.lineNumber() } + } + else { + location } - // Predict step over location by assuming that it will be next valid location in the range of current function. - // This prediction doesn't work in branching position when there're several valid location after current position. - val locations = computedReferenceType.allLineLocations() - .dropWhile { it != location } + val lambdaArgumentRanges = runReadAction { + inlineFunctionArguments.filterIsInstance().map { + val startLineNumber = it.getLineNumber(true) + 1 + val endLineNumber = it.getLineNumber(false) + 1 + + startLineNumber..endLineNumber + } + } + + val inlineRangeVariables = getInlineRangeLocalVariables(frameProxy) + + // Try to find the range of inlined lines: + // - Lines from other files and from functions that are not in range of current one are definitely inlined + // - Lines in function arguments of inlined functions are inlined too as we found them starting from the position of inlined call. + // + // This heuristic doesn't work for DEX, because of missing strata information (https://code.google.com/p/android/issues/detail?id=82972) + // + // It also thinks that too many lines are inlined when there's a call of function argument or other + // inline function in last statement of inline function. The list of inlineRangeVariables is used to overcome it. + val probablyInlinedLocations = computedReferenceType.allLineLocations() + .dropWhile { it != patchedLocation } .drop(1) - .filter(::isLocationSuitable) - .dropWhile { it.lineNumber() == location.lineNumber() } + .dropWhile { it.lineNumber() == patchedLocation.lineNumber() } + .takeWhile { locationAtLine -> + !isLocationSuitable(locationAtLine) || lambdaArgumentRanges.any { locationAtLine.lineNumber() in it } + } + .dropWhile { it.lineNumber() == patchedLocation.lineNumber() } - for (locationAtLine in locations) { - val xPosition = runReadAction l@ { - val lineNumber = locationAtLine.lineNumber() - val lineStartOffset = file.getLineStartOffset(lineNumber - 1) ?: return@l null - if (inlinedArguments.any { it.textRange.contains(lineStartOffset) }) return@l null - if (elementsToSkip.any { it.textRange.contains(lineStartOffset) }) return@l null - - if (locationAtLine.lineNumber() == location.lineNumber()) return@l null - - val elementAt = file.findElementAt(lineStartOffset) ?: return@l null - XSourcePositionImpl.createByElement(elementAt) - } - - if (xPosition != null) { - return Action.RUN_TO_CURSOR(xPosition) - } - } - - if (locations.isNotEmpty()) { - return Action.STEP_OUT() + if (!probablyInlinedLocations.isEmpty()) { + return Action.STEP_OVER_INLINED(patchedLocation.lineNumber(), probablyInlinedLocations.map { it.lineNumber() }.toSet(), inlineRangeVariables) } return Action.STEP_OVER() @@ -507,6 +496,15 @@ private fun SuspendContextImpl.getNextPositionWithFilter( return null } +private fun getInlineRangeLocalVariables(stackFrame: StackFrameProxyImpl): List { + return stackFrame.visibleVariables() + .filter { + val name = it.name() + name.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION) + } + .map { it.variable } +} + private fun getInlineArgumentIfAny(elementAt: PsiElement?): KtFunctionLiteral? { val functionLiteralExpression = elementAt?.getParentOfType(false) ?: return null diff --git a/idea/testData/debugger/tinyApp/outs/inlineInIfFalse.out b/idea/testData/debugger/tinyApp/outs/inlineInIfFalse.out new file mode 100644 index 00000000000..6e86975ae8e --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/inlineInIfFalse.out @@ -0,0 +1,8 @@ +LineBreakpoint created at inlineInIfFalse.kt:6 +!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! inlineInIfFalse.InlineInIfFalseKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +inlineInIfFalse.kt:6 +inlineInIfFalse.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/inlineInIfTrue.out b/idea/testData/debugger/tinyApp/outs/inlineInIfTrue.out new file mode 100644 index 00000000000..1dfda07583f --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/inlineInIfTrue.out @@ -0,0 +1,8 @@ +LineBreakpoint created at inlineInIfTrue.kt:6 +!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! inlineInIfTrue.InlineInIfTrueKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +inlineInIfTrue.kt:6 +inlineInIfTrue.kt:7 +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/soInlineAnonymousFunctionArgument.out b/idea/testData/debugger/tinyApp/outs/soInlineAnonymousFunctionArgument.out new file mode 100644 index 00000000000..644fa726ca0 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/soInlineAnonymousFunctionArgument.out @@ -0,0 +1,10 @@ +LineBreakpoint created at soInlineAnonymousFunctionArgument.kt:5 +!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! soInlineAnonymousFunctionArgument.SoInlineAnonymousFunctionArgumentKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +soInlineAnonymousFunctionArgument.kt:5 +soInlineAnonymousFunctionArgument.kt:7 +soInlineAnonymousFunctionArgument.kt:11 +soInlineAnonymousFunctionArgument.kt:12 +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/soInlineCallInLastStatementInInline.out b/idea/testData/debugger/tinyApp/outs/soInlineCallInLastStatementInInline.out new file mode 100644 index 00000000000..db31454d764 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/soInlineCallInLastStatementInInline.out @@ -0,0 +1,9 @@ +LineBreakpoint created at soInlineCallInLastStatementInInline.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 !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! soInlineCallInLastStatementInInline.SoInlineCallInLastStatementInInlineKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +soInlineCallInLastStatementInInline.kt:9 +soInlineCallInLastStatementInInline.kt:10 +soInlineCallInLastStatementInInline.kt:5 +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/soInlineCallInLastStatementInInlineInInline.out b/idea/testData/debugger/tinyApp/outs/soInlineCallInLastStatementInInlineInInline.out new file mode 100644 index 00000000000..bdd3bd5772e --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/soInlineCallInLastStatementInInlineInInline.out @@ -0,0 +1,9 @@ +LineBreakpoint created at soInlineCallInLastStatementInInlineInInline.kt:15 +!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! soInlineCallInLastStatementInInlineInInline.SoInlineCallInLastStatementInInlineInInlineKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +soInlineCallInLastStatementInInlineInInline.kt:15 +soInlineCallInLastStatementInInlineInInline.kt:5 +soInlineCallInLastStatementInInlineInInline.kt:6 +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/soInlineFunCallInLastStatementOfInlineWithArgumentFromCalleeAndOwn.out b/idea/testData/debugger/tinyApp/outs/soInlineFunCallInLastStatementOfInlineWithArgumentFromCalleeAndOwn.out new file mode 100644 index 00000000000..57e87d9bd77 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/soInlineFunCallInLastStatementOfInlineWithArgumentFromCalleeAndOwn.out @@ -0,0 +1,9 @@ +LineBreakpoint created at soInlineFunCallInLastStatementOfInlineWithArgumentFromCalleeAndOwn.kt:11 lambdaOrdinal = -1 +!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! soInlineFunCallInLastStatementOfInlineWithArgumentFromCalleeAndOwn.SoInlineFunCallInLastStatementOfInlineWithArgumentFromCalleeAndOwnKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +soInlineFunCallInLastStatementOfInlineWithArgumentFromCalleeAndOwn.kt:11 +soInlineFunCallInLastStatementOfInlineWithArgumentFromCalleeAndOwn.kt:7 +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/soInlineFunWithLastStatementMultilineArgumentCall.out b/idea/testData/debugger/tinyApp/outs/soInlineFunWithLastStatementMultilineArgumentCall.out new file mode 100644 index 00000000000..bbc290db03e --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/soInlineFunWithLastStatementMultilineArgumentCall.out @@ -0,0 +1,8 @@ +LineBreakpoint created at soInlineFunWithLastStatementMultilineArgumentCall.kt:14 +!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! soInlineFunWithLastStatementMultilineArgumentCall.SoInlineFunWithLastStatementMultilineArgumentCallKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +soInlineFunWithLastStatementMultilineArgumentCall.kt:14 +soInlineFunWithLastStatementMultilineArgumentCall.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/soInlineFunWithLastStatementOneLineArgumentCall.out b/idea/testData/debugger/tinyApp/outs/soInlineFunWithLastStatementOneLineArgumentCall.out new file mode 100644 index 00000000000..a547ee6b1de --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/soInlineFunWithLastStatementOneLineArgumentCall.out @@ -0,0 +1,9 @@ +LineBreakpoint created at soInlineFunWithLastStatementOneLineArgumentCall.kt:11 +!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! soInlineFunWithLastStatementOneLineArgumentCall.SoInlineFunWithLastStatementOneLineArgumentCallKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +soInlineFunWithLastStatementOneLineArgumentCall.kt:11 +soInlineFunWithLastStatementOneLineArgumentCall.kt:6 +soInlineFunWithLastStatementOneLineArgumentCall.kt:7 +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/soInlineIfConditionLambdaFalse.out b/idea/testData/debugger/tinyApp/outs/soInlineIfConditionLambdaFalse.out new file mode 100644 index 00000000000..53a706cd02a --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/soInlineIfConditionLambdaFalse.out @@ -0,0 +1,10 @@ +LineBreakpoint created at soInlineIfConditionLambdaFalse.kt:11 +!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! soInlineIfConditionLambdaFalse.SoInlineIfConditionLambdaFalseKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +soInlineIfConditionLambdaFalse.kt:11 +soInlineIfConditionLambdaFalse.kt:15 +soInlineIfConditionLambdaFalse.kt:18 +soInlineIfConditionLambdaFalse.kt:7 +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/soInlineIfConditionLambdaTrue.out b/idea/testData/debugger/tinyApp/outs/soInlineIfConditionLambdaTrue.out new file mode 100644 index 00000000000..3228552d0a5 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/soInlineIfConditionLambdaTrue.out @@ -0,0 +1,10 @@ +LineBreakpoint created at soInlineIfConditionLambdaTrue.kt:11 +!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! soInlineIfConditionLambdaTrue.SoInlineIfConditionLambdaTrueKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +soInlineIfConditionLambdaTrue.kt:11 +soInlineIfConditionLambdaTrue.kt:12 +soInlineIfConditionLambdaTrue.kt:18 +soInlineIfConditionLambdaTrue.kt:7 +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/soInlineWhileCondition.out b/idea/testData/debugger/tinyApp/outs/soInlineWhileCondition.out new file mode 100644 index 00000000000..5244a0e2cb0 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/soInlineWhileCondition.out @@ -0,0 +1,12 @@ +LineBreakpoint created at soInlineWhileCondition.kt:5 +!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! soInlineWhileCondition.SoInlineWhileConditionKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +soInlineWhileCondition.kt:5 +soInlineWhileCondition.kt:7 +soInlineWhileCondition.kt:8 +soInlineWhileCondition.kt:7 +soInlineWhileCondition.kt:12 +soInlineWhileCondition.kt:15 +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/soReifiedInlineIfConditionFalse.out b/idea/testData/debugger/tinyApp/outs/soReifiedInlineIfConditionFalse.out new file mode 100644 index 00000000000..d96eab77aff --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/soReifiedInlineIfConditionFalse.out @@ -0,0 +1,8 @@ +LineBreakpoint created at soReifiedInlineIfConditionFalse.kt:6 +!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! soReifiedInlineIfConditionFalse.SoReifiedInlineIfConditionFalseKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +soReifiedInlineIfConditionFalse.kt:6 +soReifiedInlineIfConditionFalse.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/soSimpleInlineIfCondition.out b/idea/testData/debugger/tinyApp/outs/soSimpleInlineIfCondition.out new file mode 100644 index 00000000000..f312f1fdc02 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/soSimpleInlineIfCondition.out @@ -0,0 +1,8 @@ +LineBreakpoint created at soSimpleInlineIfCondition.kt:5 +!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! soSimpleInlineIfCondition.SoSimpleInlineIfConditionKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +soSimpleInlineIfCondition.kt:5 +soSimpleInlineIfCondition.kt:8 +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/stepOverDeclarationInInlineFun.out b/idea/testData/debugger/tinyApp/outs/stepOverDeclarationInInlineFun.out new file mode 100644 index 00000000000..75402ef1df6 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/stepOverDeclarationInInlineFun.out @@ -0,0 +1,8 @@ +LineBreakpoint created at stepOverDeclarationInInlineFun.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 !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! stepOverDeclarationInInlineFun.StepOverDeclarationInInlineFunKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +stepOverDeclarationInInlineFun.kt:9 +stepOverDeclarationInInlineFun.kt:10 +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/stepOverIfWithInline.out b/idea/testData/debugger/tinyApp/outs/stepOverIfWithInline.out index 1cde39be060..482fdbf23df 100644 --- a/idea/testData/debugger/tinyApp/outs/stepOverIfWithInline.out +++ b/idea/testData/debugger/tinyApp/outs/stepOverIfWithInline.out @@ -9,23 +9,14 @@ stepOverIfWithInline.kt:15 stepOverIfWithInline.kt:18 stepOverIfWithInline.kt:15 stepOverIfWithInline.kt:21 -stepOverIfWithInline.kt:46 -stepOverIfWithInline.kt:21 stepOverIfWithInline.kt:24 stepOverIfWithInline.kt:25 stepOverIfWithInline.kt:24 stepOverIfWithInline.kt:28 -stepOverIfWithInline.kt:46 -stepOverIfWithInline.kt:28 -stepOverIfWithInline.kt:32 -stepOverIfWithInline.kt:46 stepOverIfWithInline.kt:32 stepOverIfWithInline.kt:36 stepOverIfWithInline.kt:32 -stepOverIfWithInline.kt:40 -stepOverIfWithInline.kt:53 -stepOverIfWithInline.kt:54 -stepOverIfWithInline.kt:43 +stepOverIfWithInline.kt:38 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/stepOverInlinedLambda.out b/idea/testData/debugger/tinyApp/outs/stepOverInlinedLambda.out index 33950777dcd..1eed1bb42fa 100644 --- a/idea/testData/debugger/tinyApp/outs/stepOverInlinedLambda.out +++ b/idea/testData/debugger/tinyApp/outs/stepOverInlinedLambda.out @@ -1,6 +1,7 @@ -LineBreakpoint created at stepOverInlinedLambda.kt:6 +LineBreakpoint created at stepOverInlinedLambda.kt:5 !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! stepOverInlinedLambda.StepOverInlinedLambdaKt Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +stepOverInlinedLambda.kt:5 stepOverInlinedLambda.kt:6 stepOverInlinedLambda.kt:7 stepOverInlinedLambda.kt:10 @@ -11,8 +12,7 @@ stepOverInlinedLambda.kt:19 stepOverInlinedLambda.kt:20 stepOverInlinedLambda.kt:23 stepOverInlinedLambda.kt:29 -stepOverInlinedLambda.kt:31 -stepOverInlinedLambda.kt:32 +stepOverInlinedLambda.kt:30 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/stepOverInsideInlineFun.out b/idea/testData/debugger/tinyApp/outs/stepOverInsideInlineFun.out index 6f2b45f1156..adb1009c86f 100644 --- a/idea/testData/debugger/tinyApp/outs/stepOverInsideInlineFun.out +++ b/idea/testData/debugger/tinyApp/outs/stepOverInsideInlineFun.out @@ -4,6 +4,7 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke stepOverInsideInlineFun.kt:11 stepOverInsideInlineFun.kt:12 stepOverInsideInlineFun.kt:13 +stepOverInsideInlineFun.kt:14 stepOverInsideInlineFun.kt:7 Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' diff --git a/idea/testData/debugger/tinyApp/outs/stepOverNonLocalReturnInLambda.out b/idea/testData/debugger/tinyApp/outs/stepOverNonLocalReturnInLambda.out index 2aa89d67073..e0c9aa1028e 100644 --- a/idea/testData/debugger/tinyApp/outs/stepOverNonLocalReturnInLambda.out +++ b/idea/testData/debugger/tinyApp/outs/stepOverNonLocalReturnInLambda.out @@ -11,7 +11,7 @@ stepOverNonLocalReturnInLambda.kt:35 stepOverNonLocalReturnInLambda.kt:7 stepOverNonLocalReturnInLambda.kt:52 stepOverNonLocalReturnInLambda.kt:53 -stepOverNonLocalReturnInLambda.kt:10 +stepOverNonLocalReturnInLambda.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/stepOverTryCatchWithInline.out b/idea/testData/debugger/tinyApp/outs/stepOverTryCatchWithInline.out index ff8908033d0..9ae74534963 100644 --- a/idea/testData/debugger/tinyApp/outs/stepOverTryCatchWithInline.out +++ b/idea/testData/debugger/tinyApp/outs/stepOverTryCatchWithInline.out @@ -4,21 +4,15 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke stepOverTryCatchWithInline.kt:14 stepOverTryCatchWithInline.kt:16 stepOverTryCatchWithInline.kt:17 -stepOverTryCatchWithInline.kt:48 -stepOverTryCatchWithInline.kt:17 stepOverTryCatchWithInline.kt:24 stepOverTryCatchWithInline.kt:25 stepOverTryCatchWithInline.kt:27 stepOverTryCatchWithInline.kt:28 stepOverTryCatchWithInline.kt:35 stepOverTryCatchWithInline.kt:36 -stepOverTryCatchWithInline.kt:48 -stepOverTryCatchWithInline.kt:36 stepOverTryCatchWithInline.kt:38 stepOverTryCatchWithInline.kt:39 stepOverTryCatchWithInline.kt:43 -stepOverTryCatchWithInline.kt:48 -stepOverTryCatchWithInline.kt:43 stepOverTryCatchWithInline.kt:7 stepOverTryCatchWithInline.kt:8 stepOverTryCatchWithInline.kt:10 diff --git a/idea/testData/debugger/tinyApp/outs/stepOverWhenInReturn.out b/idea/testData/debugger/tinyApp/outs/stepOverWhenInReturn.out index b3d3375c3ee..53607a5bbc4 100644 --- a/idea/testData/debugger/tinyApp/outs/stepOverWhenInReturn.out +++ b/idea/testData/debugger/tinyApp/outs/stepOverWhenInReturn.out @@ -3,8 +3,6 @@ LineBreakpoint created at stepOverWhenInReturn.kt:10 Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' stepOverWhenInReturn.kt:10 stepOverWhenInReturn.kt:11 -stepOverWhenInReturn.kt:17 -stepOverWhenInReturn.kt:11 stepOverWhenInReturn.kt:10 stepOverWhenInReturn.kt:4 stepOverWhenInReturn.kt:5 diff --git a/idea/testData/debugger/tinyApp/outs/stepOverWhenWithInline.out b/idea/testData/debugger/tinyApp/outs/stepOverWhenWithInline.out index 7226e450cb5..db496e41d6a 100644 --- a/idea/testData/debugger/tinyApp/outs/stepOverWhenWithInline.out +++ b/idea/testData/debugger/tinyApp/outs/stepOverWhenWithInline.out @@ -4,43 +4,29 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke stepOverWhenWithInline.kt:5 stepOverWhenWithInline.kt:8 stepOverWhenWithInline.kt:9 -stepOverWhenWithInline.kt:83 -stepOverWhenWithInline.kt:9 stepOverWhenWithInline.kt:7 stepOverWhenWithInline.kt:14 stepOverWhenWithInline.kt:17 stepOverWhenWithInline.kt:18 stepOverWhenWithInline.kt:13 stepOverWhenWithInline.kt:26 -stepOverWhenWithInline.kt:83 -stepOverWhenWithInline.kt:26 -stepOverWhenWithInline.kt:27 -stepOverWhenWithInline.kt:83 stepOverWhenWithInline.kt:27 stepOverWhenWithInline.kt:25 stepOverWhenWithInline.kt:32 stepOverWhenWithInline.kt:34 -stepOverWhenWithInline.kt:83 -stepOverWhenWithInline.kt:34 stepOverWhenWithInline.kt:32 stepOverWhenWithInline.kt:38 stepOverWhenWithInline.kt:43 stepOverWhenWithInline.kt:38 stepOverWhenWithInline.kt:51 stepOverWhenWithInline.kt:52 -stepOverWhenWithInline.kt:83 -stepOverWhenWithInline.kt:52 stepOverWhenWithInline.kt:51 stepOverWhenWithInline.kt:58 -stepOverWhenWithInline.kt:83 -stepOverWhenWithInline.kt:58 stepOverWhenWithInline.kt:57 stepOverWhenWithInline.kt:64 stepOverWhenWithInline.kt:65 stepOverWhenWithInline.kt:63 stepOverWhenWithInline.kt:76 -stepOverWhenWithInline.kt:83 -stepOverWhenWithInline.kt:76 stepOverWhenWithInline.kt:75 stepOverWhenWithInline.kt:80 Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' diff --git a/idea/testData/debugger/tinyApp/outs/stepOverWhileWithInline.out b/idea/testData/debugger/tinyApp/outs/stepOverWhileWithInline.out index 03ac05a7328..0a095a36dca 100644 --- a/idea/testData/debugger/tinyApp/outs/stepOverWhileWithInline.out +++ b/idea/testData/debugger/tinyApp/outs/stepOverWhileWithInline.out @@ -5,8 +5,6 @@ stepOverWhileWithInline.kt:5 stepOverWhileWithInline.kt:7 stepOverWhileWithInline.kt:8 stepOverWhileWithInline.kt:9 -stepOverWhileWithInline.kt:41 -stepOverWhileWithInline.kt:9 stepOverWhileWithInline.kt:7 stepOverWhileWithInline.kt:13 stepOverWhileWithInline.kt:14 @@ -18,18 +16,7 @@ stepOverWhileWithInline.kt:21 stepOverWhileWithInline.kt:24 stepOverWhileWithInline.kt:25 stepOverWhileWithInline.kt:26 -stepOverWhileWithInline.kt:28 -stepOverWhileWithInline.kt:30 -stepOverWhileWithInline.kt:41 -stepOverWhileWithInline.kt:30 -stepOverWhileWithInline.kt:31 -stepOverWhileWithInline.kt:30 -stepOverWhileWithInline.kt:41 -stepOverWhileWithInline.kt:30 -stepOverWhileWithInline.kt:35 -stepOverWhileWithInline.kt:41 -stepOverWhileWithInline.kt:35 -stepOverWhileWithInline.kt:38 +stepOverWhileWithInline.kt:27 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/stepping/stepOver/inlineInIfFalse.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/inlineInIfFalse.kt new file mode 100644 index 00000000000..246509a7f41 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/inlineInIfFalse.kt @@ -0,0 +1,16 @@ +package inlineInIfFalse + +fun main(args: Array) { + val bar = "" + //Breakpoint! + if (inlineCall { true }) { + foo() + } + foo() +} + +fun foo() {} + +inline fun inlineCall(predicate: (String?) -> Boolean): Boolean { + return false +} \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/inlineInIfTrue.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/inlineInIfTrue.kt new file mode 100644 index 00000000000..0bd958578d4 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/inlineInIfTrue.kt @@ -0,0 +1,15 @@ +package inlineInIfTrue + +fun main(args: Array) { + val bar = "" + //Breakpoint! + if (inlineCall { true }) { + foo() + } +} + +fun foo() {} + +inline fun inlineCall(predicate: (String?) -> Boolean): Boolean { + return true +} \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineAnonymousFunctionArgument.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineAnonymousFunctionArgument.kt new file mode 100644 index 00000000000..d7355a680e2 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineAnonymousFunctionArgument.kt @@ -0,0 +1,20 @@ +package soInlineAnonymousFunctionArgument + +fun main(args: Array) { + //Breakpoint! + val b = 1 // 1 + + foo( // 2 + fun (){ test(1) } + ) + + foo(fun (){ test(1) }) // 3 +} // 4 + +inline fun foo(f: () -> Unit) { + f() +} + +fun test(i: Int) = 1 + +// STEP_OVER: 6 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineCallInLastStatementInInline.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineCallInLastStatementInInline.kt new file mode 100644 index 00000000000..39e2774ee76 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineCallInLastStatementInInline.kt @@ -0,0 +1,17 @@ +package soInlineCallInLastStatementInInline + +fun main(args: Array) { + bar() +} + +inline fun bar() { + //Breakpoint! + val a = 1 + foo { 42 } +} + +inline fun foo(f: () -> Unit) { + f() +} + +// STEP_OVER: 2 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineCallInLastStatementInInlineInInline.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineCallInLastStatementInInlineInInline.kt new file mode 100644 index 00000000000..ed9de9c95cd --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineCallInLastStatementInInlineInInline.kt @@ -0,0 +1,18 @@ +package soInlineCallInLastStatementInInlineInInline + +fun main(args: Array) { + bar() + val i = 45 +} + +inline fun bar() { + val a = 1 + foo { 42 } +} + +inline fun foo(f: () -> Unit) { + //Breakpoint! + f() +} + +// STEP_OVER: 4 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunCallInLastStatementOfInlineWithArgumentFromCalleeAndOwn.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunCallInLastStatementOfInlineWithArgumentFromCalleeAndOwn.kt new file mode 100644 index 00000000000..da548320530 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunCallInLastStatementOfInlineWithArgumentFromCalleeAndOwn.kt @@ -0,0 +1,20 @@ +package soInlineFunCallInLastStatementOfInlineWithArgumentFromCalleeAndOwn + +fun main(args: Array) { + bar { + println("") + } +} + +inline fun bar(f2: () -> Unit) { + //Breakpoint! (lambdaOrdinal = -1) + foo({ 42 }, + f2) +} + +inline fun foo(f1: () -> Unit, f2: () -> Unit) { + f1() + f2() +} + +// STEP_OVER 5 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunWithLastStatementMultilineArgumentCall.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunWithLastStatementMultilineArgumentCall.kt new file mode 100644 index 00000000000..d2a551cab0b --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunWithLastStatementMultilineArgumentCall.kt @@ -0,0 +1,17 @@ +package soInlineFunWithLastStatementMultilineArgumentCall + +fun main(args: Array) { + var k = 444 + bar { + val b = 1 + } + + k++ +} + +inline fun bar(f: (Int) -> Unit) { + //Breakpoint! + f(1) +} + +// STEP_OVER: 1 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunWithLastStatementOneLineArgumentCall.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunWithLastStatementOneLineArgumentCall.kt new file mode 100644 index 00000000000..81a19fcea3b --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunWithLastStatementOneLineArgumentCall.kt @@ -0,0 +1,14 @@ +package soInlineFunWithLastStatementOneLineArgumentCall + +fun main(args: Array) { + var k = 444 + bar { val b = 1 } + k++ +} + +inline fun bar(f: (Int) -> Unit) { + //Breakpoint! + f(1) +} + +// STEP_OVER: 4 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineIfConditionLambdaFalse.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineIfConditionLambdaFalse.kt new file mode 100644 index 00000000000..bce74a3506e --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineIfConditionLambdaFalse.kt @@ -0,0 +1,23 @@ +package soInlineIfConditionLambdaFalse + +fun main(args: Array) { + bar { + false + } +} + +inline fun bar(f: (Int) -> Boolean) { + //Breakpoint! + if (f(42)) { + foo() + } + else { + foo() + } + + foo() +} + +fun foo() {} + +// STEP_OVER: 4 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineIfConditionLambdaTrue.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineIfConditionLambdaTrue.kt new file mode 100644 index 00000000000..9610acbb4c5 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineIfConditionLambdaTrue.kt @@ -0,0 +1,23 @@ +package soInlineIfConditionLambdaTrue + +fun main(args: Array) { + bar { + true + } +} // 4 + +inline fun bar(f: (Int) -> Boolean) { + //Breakpoint! + if (f(42)) { // 1 + foo() // 2 + } + else { + foo() + } + + foo() // 3 +} + +fun foo() {} + +// STEP_OVER: 4 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineWhileCondition.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineWhileCondition.kt new file mode 100644 index 00000000000..a60e036241d --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineWhileCondition.kt @@ -0,0 +1,23 @@ +package soInlineWhileCondition + +fun main(args: Array) { + //Breakpoint! + var i = 1 // 1 + // inline in while condition (true) + while (id { i < 2 }) { // 2 4 + i++ // 3 + } + + // inline in while condition (false) + while (id { false }) { // 5 + bar() + } +} // 6 + +inline fun id(f: () -> Boolean): Boolean { + return f() +} + +fun bar() {} + +// STEP_OVER: 6 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soReifiedInlineIfConditionFalse.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soReifiedInlineIfConditionFalse.kt new file mode 100644 index 00000000000..7d633e2773e --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soReifiedInlineIfConditionFalse.kt @@ -0,0 +1,15 @@ +package soReifiedInlineIfConditionFalse + +fun main(args: Array) { + // Reified function call in if condition + //Breakpoint! + if (reified(11) != 11) { // 1 + val a = 22 + } +} // 2 + +inline fun reified(f: T): T { + val a = 33 + return f +} + diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSimpleInlineIfCondition.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSimpleInlineIfCondition.kt new file mode 100644 index 00000000000..2b83b63ba34 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSimpleInlineIfCondition.kt @@ -0,0 +1,18 @@ +package soSimpleInlineIfCondition + +fun main(args: Array) { + //Breakpoint! + if (foo { + test(2) + }) { + bar() + } + + bar() +} + +inline fun foo(f: () -> Boolean): Boolean = f() + +fun test(i: Int): Boolean = true + +fun bar() {} diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverDeclarationInInlineFun.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverDeclarationInInlineFun.kt new file mode 100644 index 00000000000..d3343e28151 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverDeclarationInInlineFun.kt @@ -0,0 +1,11 @@ +package stepOverDeclarationInInlineFun + +fun main(args: Array) { + foo { 1 } +} + +inline fun foo(f: () -> Int): Int { + //Breakpoint! + val a = 15 + return f() +} \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverIfWithInline.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverIfWithInline.kt index 563a65a98d1..a415d02521b 100644 --- a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverIfWithInline.kt +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverIfWithInline.kt @@ -2,45 +2,40 @@ package stepOverIfWithInline fun main(args: Array) { //Breakpoint! - val prop = 1 + val prop = 1 // 1 // False with braces - val a = if (1 > 2) { + val a = if (1 > 2) { // 2 4(?) foo { test(1) } } else { - foo { test(1) } + foo { test(1) } // 3 } // False without braces - val b = if (1 > 2) + val b = if (1 > 2) // 5 7(?) foo { test(1) } else - foo { test(1) } + foo { test(1) } // 6 // One line - val c = if (1 > 2) foo { test(1) } else foo { test(1) } + val c = if (1 > 2) foo { test(1) } else foo { test(1) } // 8 // Else on next line, false - val d = if (1 > 2) foo { test(1) } - else foo { test(1) } + val d = if (1 > 2) foo { test(1) } // 9 11 + else foo { test(1) } // 10 // Else on next line, true - val e = if (1 < 2) foo { test(1) } + val e = if (1 < 2) foo { test(1) } // 12 else foo { test(1) } // Inline function call in condition - val f = if (foo { test(1) } > 2) { + val f = if (foo { test(1) } > 2) { // 13 15(?) foo { test(1) } } else { - foo { test(1) } + foo { test(1) } // 14 } - - // Reified function call in if condition - if (reified(1) != 1) { - val a = 1 - } -} +} // 16 inline fun foo(f: () -> Int): Int { val a = 1 @@ -49,9 +44,4 @@ inline fun foo(f: () -> Int): Int { fun test(i: Int) = 1 -inline fun reified(f: T): Int { - val a = 1 - return 1 -} - -// STEP_OVER: 25 \ No newline at end of file +// STEP_OVER: 17 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverInlinedLambda.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverInlinedLambda.kt index 9e988752d92..5e027834915 100644 --- a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverInlinedLambda.kt +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverInlinedLambda.kt @@ -1,8 +1,8 @@ package stepOverInlinedLambda fun main(args: Array) { - val a = A() //Breakpoint! + val a = A() foo { test(1) } foo { test(2) @@ -27,8 +27,6 @@ fun main(args: Array) { }) val b = foo { test(1) } - - foo(fun (){ test(1) }) } inline fun foo(f: () -> Unit) { @@ -49,4 +47,4 @@ class A { fun test(i: Int) = 1 -// STEP_OVER: 11 \ No newline at end of file +// STEP_OVER: 12 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverInlinedLambdaStdlib.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverInlinedLambdaStdlib.kt index de83ee6fad7..03637639e61 100644 --- a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverInlinedLambdaStdlib.kt +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverInlinedLambdaStdlib.kt @@ -3,16 +3,16 @@ package stepOverInlinedLambdaStdlib fun main(args: Array) { //Breakpoint! val a = listOf(1, 2, 3) - a.filter { it > 1 } + a.filter { it > 1 } /*!*/ - a.filter { it > 1 }.map { it * 2 } + a.filter { it > 1 }.map { it * 2 } /*!*/ - a.filter { + a.filter { /*!*/ it > 1 }.map { it * 2 } -} +} /*!*/ // TRACING_FILTERS_ENABLED: false // STEP_OVER: 4 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverInsideInlineFun.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverInsideInlineFun.kt index 3f7b90c60ca..7db0f135cd9 100644 --- a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverInsideInlineFun.kt +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverInsideInlineFun.kt @@ -9,8 +9,11 @@ fun main(args: Array) { inline fun bar(f: (Int) -> Unit) { //Breakpoint! val a = 1 + foo() val f = f(1) val c = 1 } -// STEP_OVER: 3 \ No newline at end of file +fun foo() {} + +// STEP_OVER: 4 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverTryCatchWithInline.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverTryCatchWithInline.kt index 9d5924c5b29..f0bc123987a 100644 --- a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverTryCatchWithInline.kt +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverTryCatchWithInline.kt @@ -4,43 +4,43 @@ fun main(args: Array) { try { bar() } - catch(e: Exception) { - val a = 1 + catch(e: Exception) { // 13 + val a = 1 // 14 } -} +} // 15 fun bar() { //Breakpoint! - val prop = 1 + val prop = 1 // 1 // Try - try { - foo { test(1) } + try { // 2 + foo { test(1) } // 3 } catch(e: Exception) { foo { test(1) } } // Many catch clauses - try { - throw IllegalStateException() + try { // 4 + throw IllegalStateException() // 5 } - catch(e: IllegalStateException) { - foo { test(1) } + catch(e: IllegalStateException) { // 6 + foo { test(1) } // 7 } catch(e: Exception) { foo { test(1) } } // exception in lambda - try { - foo { throw IllegalStateException() } + try { // 8 + foo { throw IllegalStateException() } // 9 } - catch(e: Exception) { - foo { test(1) } + catch(e: Exception) { // 10 + foo { test(1) } // 11 } // Exception without catch - foo { throw IllegalStateException() } + foo { throw IllegalStateException() } // 12 val prop2 = 1 } @@ -51,4 +51,4 @@ inline fun foo(f: () -> Int): Int { fun test(i: Int) = 1 -// STEP_OVER: 20 \ No newline at end of file +// STEP_OVER: 15 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverWhenInReturn.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverWhenInReturn.kt index 5104422d798..aec6e1f4d93 100644 --- a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverWhenInReturn.kt +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverWhenInReturn.kt @@ -1,23 +1,23 @@ package stepOverWhenInReturn fun main(args: Array) { - whenInReturn() -} + whenInReturn() // 4? +} // 5 fun whenInReturn(): Int { val a = 1 //Breakpoint! - return when(a) { - 1 -> foo { 1 } + return when(a) { // 1 3? + 1 -> foo { 1 } // 2 else -> 3 } } inline fun foo(f: () -> Int): Int { - val a = 1 + val a = 15 return f() } -fun test(i: Int) = 1 +fun test(i: Int) = 42 // STEP_OVER: 6 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverWhenWithInline.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverWhenWithInline.kt index e865acd5f00..953b12d64d0 100644 --- a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverWhenWithInline.kt +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverWhenWithInline.kt @@ -2,45 +2,45 @@ package stepOverWhenWithInline fun main(args: Array) { //Breakpoint! - val prop = 1 + val prop = 1 // 1 // Break after second - val a = when { - 1 > 2 -> foo { test(1) } - 2 > 1 -> foo { test(1) } + val a = when { // 4 + 1 > 2 -> foo { test(1) } // 2 + 2 > 1 -> foo { test(1) } // 3 else -> foo { test(1) } } - val b = when { - 1 > 2 -> { + val b = when { // 8 + 1 > 2 -> { // 5 foo { test(1) } } - 2 > 1 -> { - foo { test(1) } + 2 > 1 -> { // 6 + foo { test(1) } // 7 } else -> { foo { test(1) } } } - val c = when { - foo { test(1) } > 2 -> 1 - 2 > foo { test(1) } -> 2 + val c = when { // 11 + foo { test(1) } > 2 -> 1 // 9 + 2 > foo { test(1) } -> 2 // 10 else -> foo { test(1) } } // When with expression - val a1 = when(prop) { + val a1 = when(1) { // 12 14 2 -> foo { test(1) } - 1 -> foo { test(1) } + 1 -> foo { test(1) } // 13 else -> foo { test(1) } } - val b1 = when(prop) { + val b1 = when(1) { // 15 17 2 -> { foo { test(1) } } 1 -> { - foo { test(1) } + foo { test(1) } // 16 } else -> { foo { test(1) } @@ -48,21 +48,21 @@ fun main(args: Array) { } // Break after first - val c1 = when(prop) { - foo { test(1) } -> 1 + val c1 = when(1) { // 18 20 + foo { test(1) } -> 1 // 19 foo { test(2) } -> 2 else -> foo { test(1) } } - val a2 = when { - 2 > 1 -> foo { test(1) } + val a2 = when { // 22 + 2 > 1 -> foo { test(1) } // 21 1 > 2 -> foo { test(1) } else -> foo { test(1) } } - val b2 = when { - 2 > 1 -> { - foo { test(1) } + val b2 = when { // 25 + 2 > 1 -> { // 23 + foo { test(1) } // 24 } 1 > 2 -> { foo { test(1) } @@ -72,12 +72,12 @@ fun main(args: Array) { } } - val c2 = when { - 2 > foo { test(1) } -> 2 + val c2 = when { // 27 + 2 > foo { test(1) } -> 2 // 26 foo { test(1) } > 2 -> 1 else -> foo { test(1) } } -} +} // 28 inline fun foo(f: () -> Int): Int { val a = 1 @@ -86,4 +86,4 @@ inline fun foo(f: () -> Int): Int { fun test(i: Int) = i -// STEP_OVER: 41 \ No newline at end of file +// STEP_OVER: 30 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverWhileWithInline.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverWhileWithInline.kt index 21b0d0e54db..307ee77de25 100644 --- a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverWhileWithInline.kt +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverWhileWithInline.kt @@ -2,40 +2,29 @@ package stepOverWhileWithInline fun main(args: Array) { //Breakpoint! - var prop = 0 + var prop = 0 // 1 // inline on last line of while - while(prop < 1) { - prop++ - foo { test(1) } + while(prop < 1) { // 2 5 + prop++ // 3 + foo { test(1) } // 4 } // inline call inside while - while(prop < 2) { - foo { test(1) } - prop++ + while(prop < 2) { // 6 9 + foo { test(1) } // 7 + prop++ // 8 } do { - prop++ - foo { test(1) } - } while(prop < 3) + prop++ // 10 + foo { test(1) } // 11 + } while(prop < 3) // 12 do { - foo { test(1) } - prop++ - } while(prop < 4) - - var i = 1 - // inline in while condition (true) - while(foo { test(i++) } == 1) { - prop++ - } - - // inline in while condition (false) - while(foo { test(2) } == 1) { - prop++ - } -} + foo { test(1) } // 13 + prop++ // 14 + } while(prop < 4) // 15 +} // 16 inline fun foo(f: () -> Int): Int { val a = 1 @@ -44,4 +33,4 @@ inline fun foo(f: () -> Int): Int { fun test(i: Int) = i -// STEP_OVER: 41 \ No newline at end of file +// STEP_OVER: 16 \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java index f2e972b7a7b..aaae589a2d3 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java @@ -433,6 +433,18 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest { doStepOverTest(fileName); } + @TestMetadata("inlineInIfFalse.kt") + public void testInlineInIfFalse() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/inlineInIfFalse.kt"); + doStepOverTest(fileName); + } + + @TestMetadata("inlineInIfTrue.kt") + public void testInlineInIfTrue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/inlineInIfTrue.kt"); + doStepOverTest(fileName); + } + @TestMetadata("noParameterLambdaArgumentCallInInline.kt") public void testNoParameterLambdaArgumentCallInInline() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/noParameterLambdaArgumentCallInInline.kt"); @@ -445,12 +457,84 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest { doStepOverTest(fileName); } + @TestMetadata("soInlineAnonymousFunctionArgument.kt") + public void testSoInlineAnonymousFunctionArgument() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineAnonymousFunctionArgument.kt"); + doStepOverTest(fileName); + } + + @TestMetadata("soInlineCallInLastStatementInInline.kt") + public void testSoInlineCallInLastStatementInInline() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineCallInLastStatementInInline.kt"); + doStepOverTest(fileName); + } + + @TestMetadata("soInlineCallInLastStatementInInlineInInline.kt") + public void testSoInlineCallInLastStatementInInlineInInline() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineCallInLastStatementInInlineInInline.kt"); + doStepOverTest(fileName); + } + + @TestMetadata("soInlineFunCallInLastStatementOfInlineWithArgumentFromCalleeAndOwn.kt") + public void testSoInlineFunCallInLastStatementOfInlineWithArgumentFromCalleeAndOwn() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunCallInLastStatementOfInlineWithArgumentFromCalleeAndOwn.kt"); + doStepOverTest(fileName); + } + + @TestMetadata("soInlineFunWithLastStatementMultilineArgumentCall.kt") + public void testSoInlineFunWithLastStatementMultilineArgumentCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunWithLastStatementMultilineArgumentCall.kt"); + doStepOverTest(fileName); + } + + @TestMetadata("soInlineFunWithLastStatementOneLineArgumentCall.kt") + public void testSoInlineFunWithLastStatementOneLineArgumentCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunWithLastStatementOneLineArgumentCall.kt"); + doStepOverTest(fileName); + } + + @TestMetadata("soInlineIfConditionLambdaFalse.kt") + public void testSoInlineIfConditionLambdaFalse() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineIfConditionLambdaFalse.kt"); + doStepOverTest(fileName); + } + + @TestMetadata("soInlineIfConditionLambdaTrue.kt") + public void testSoInlineIfConditionLambdaTrue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineIfConditionLambdaTrue.kt"); + doStepOverTest(fileName); + } + + @TestMetadata("soInlineWhileCondition.kt") + public void testSoInlineWhileCondition() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineWhileCondition.kt"); + doStepOverTest(fileName); + } + + @TestMetadata("soReifiedInlineIfConditionFalse.kt") + public void testSoReifiedInlineIfConditionFalse() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soReifiedInlineIfConditionFalse.kt"); + doStepOverTest(fileName); + } + + @TestMetadata("soSimpleInlineIfCondition.kt") + public void testSoSimpleInlineIfCondition() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soSimpleInlineIfCondition.kt"); + doStepOverTest(fileName); + } + @TestMetadata("stepOverCatchClause.kt") public void testStepOverCatchClause() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverCatchClause.kt"); doStepOverTest(fileName); } + @TestMetadata("stepOverDeclarationInInlineFun.kt") + public void testStepOverDeclarationInInlineFun() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverDeclarationInInlineFun.kt"); + doStepOverTest(fileName); + } + @TestMetadata("stepOverFalseConditionInLastIfInWhile.kt") public void testStepOverFalseConditionInLastIfInWhile() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverFalseConditionInLastIfInWhile.kt");