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");