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
This commit is contained in:
Nikolay Krasko
2016-09-26 15:14:54 +03:00
committed by Nikolay Krasko
parent 9594316b0a
commit 7992df7b93
49 changed files with 1004 additions and 322 deletions
@@ -58,7 +58,8 @@ public class DebuggerSteppingHelper {
if (frameProxy != null) {
Action action = KotlinSteppingCommandProviderKt.getStepOverAction(
frameProxy.location(),
kotlinSourcePosition
kotlinSourcePosition,
frameProxy
);
createStepRequest(
@@ -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<DebugProcessListener>
// 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 <T> 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
}
}
@@ -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<com.intellij.debugger.SourcePosition> {
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
}
}
@@ -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<KtFunction>? {
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<PsiElement> {
val result = arrayListOf<PsiElement>()
val ifParent = getParentOfType<KtIfExpression>(false)
if (ifParent != null) {
if (ifParent.then.contains(this)) {
ifParent.elseKeyword?.let { result.add(it) }
ifParent.`else`?.let { result.add(it) }
}
}
val tryParent = getParentOfType<KtTryExpression>(false)
if (tryParent != null) {
val catchClause = getParentOfType<KtCatchClause>(false)
if (catchClause != null) {
result.addAll(tryParent.catchClauses.filter { it != catchClause })
}
}
val whenEntry = getParentOfType<KtWhenEntry>(false)
if (whenEntry != null) {
if (whenEntry.expression.contains(this)) {
val whenParent = whenEntry.getParentOfType<KtWhenExpression>(false)
if (whenParent != null) {
result.addAll(whenParent.entries.filter { it != whenEntry })
}
}
}
return result
}
private fun PsiElement.shouldNotUseStepOver(elementAt: PsiElement): Boolean {
val ifParent = getParentOfType<KtIfExpression>(false)
if (ifParent != null) {
// if (inlineFunCall()) {...}
if (ifParent.condition.contains(this)) {
return true
}
/*
<caret>if (...) inlineFunCall()
else inlineFunCall()
*/
val ifParentElementAt = elementAt.getParentOfType<KtIfExpression>(false)
if (ifParentElementAt == null) {
if (ifParent.then.contains(this)) {
return true
}
if (ifParent.`else`.contains(this)) {
return true
}
}
}
val tryParent = getParentOfType<KtTryExpression>(false)
if (tryParent != null) {
/* try { inlineFunCall() }
catch()...
*/
if (tryParent.tryBlock.contains(this)) {
return true
}
}
val whenEntry = getParentOfType<KtWhenEntry>(false)
if (whenEntry != null) {
// <caret>inlineFunCall -> ...
if (whenEntry.conditions.any { it.contains(this) } ) {
return true
}
// <caret>1 == 2 -> inlineFunCall()
if (whenEntry.expression.contains(this)) {
val parentEntryElementAt = elementAt.getParentOfType<KtWhenEntry>(false) ?: return true
return parentEntryElementAt == whenEntry &&
whenEntry.conditions.any { it.contains(elementAt) }
}
}
val whileParent = getParentOfType<KtWhileExpression>(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<KtFunction> {
val inlineFunctionCalls = getInlineFunctionCallsIfAny(sourcePosition)
return getInlineArgumentsIfAny(inlineFunctionCalls)
}
private fun getInlineFunctionsIfAny(file: KtFile, offset: Int): List<KtNamedFunction> {
val elementAt = file.findElementAt(offset) ?: return emptyList()
val containingFunction = elementAt.getParentOfType<KtNamedFunction>(false) ?: return emptyList()
@@ -269,14 +185,46 @@ private fun getInlineFunctionsIfAny(file: KtFile, offset: Int): List<KtNamedFunc
private fun getInlineArgumentsIfAny(inlineFunctionCalls: List<KtCallExpression>): List<KtFunction> {
return inlineFunctionCalls.flatMap {
it.valueArguments
.map { getArgumentExpression(it) }
.map { getArgumentExpression(it) }
.filterIsInstance<KtFunction>()
}
}
private fun getArgumentExpression(it: ValueArgument) = (it.getArgumentExpression() as? KtLambdaExpression)?.functionLiteral ?: it.getArgumentExpression()
private fun getInlineArgumentsCallsIfAny(sourcePosition: SourcePosition, declarationDescriptor: DeclarationDescriptor): List<KtCallExpression>? {
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<KtCallExpression> {
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<KtCallExpression> {
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<Kt
val start = topMostElement.startOffset
val end = topMostElement.endOffset
fun isInlineCall(expr: KtExpression): Boolean {
val context = expr.analyze(BodyResolveMode.PARTIAL)
val resolvedCall = expr.getResolvedCall(context) ?: return false
return InlineUtil.isInline(resolvedCall.resultingDescriptor)
}
val allInlineFunctionCalls = CodeInsightUtils.
val allFilteredCalls = CodeInsightUtils.
findElementsOfClassInRange(file, start, end, KtExpression::class.java)
.map { KtPsiUtil.getParentCallIfPresent(it as KtExpression) }
.filterIsInstance<KtCallExpression>()
.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<Kt
}
}
sealed class Action(val position: XSourcePositionImpl?) {
class STEP_OVER: Action(null)
class STEP_OUT: Action(null)
class RUN_TO_CURSOR(position: XSourcePositionImpl): Action(position)
sealed class Action(val position: XSourcePositionImpl? = null,
val lineNumber: Int? = null,
val stepOverLines: Set<Int>? = null,
val inlineRangeVariables: List<LocalVariable>? = 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<Int>, inlineVariables: List<LocalVariable>) : 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<Int>, val fromLine: Int,
val inlineFunRangeVariables: List<LocalVariable>) : 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<Int>? {
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<KtElement>,
elementsToSkip: List<PsiElement>
inlineFunctionArguments: List<KtElement>,
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<KtElement>().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<LocalVariable> {
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<KtLambdaExpression>(false) ?: return null
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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'
@@ -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
@@ -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
@@ -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
@@ -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'
@@ -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
@@ -0,0 +1,16 @@
package inlineInIfFalse
fun main(args: Array<String>) {
val bar = ""
//Breakpoint!
if (inlineCall { true }) {
foo()
}
foo()
}
fun foo() {}
inline fun inlineCall(predicate: (String?) -> Boolean): Boolean {
return false
}
@@ -0,0 +1,15 @@
package inlineInIfTrue
fun main(args: Array<String>) {
val bar = ""
//Breakpoint!
if (inlineCall { true }) {
foo()
}
}
fun foo() {}
inline fun inlineCall(predicate: (String?) -> Boolean): Boolean {
return true
}
@@ -0,0 +1,20 @@
package soInlineAnonymousFunctionArgument
fun main(args: Array<String>) {
//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
@@ -0,0 +1,17 @@
package soInlineCallInLastStatementInInline
fun main(args: Array<String>) {
bar()
}
inline fun bar() {
//Breakpoint!
val a = 1
foo { 42 }
}
inline fun foo(f: () -> Unit) {
f()
}
// STEP_OVER: 2
@@ -0,0 +1,18 @@
package soInlineCallInLastStatementInInlineInInline
fun main(args: Array<String>) {
bar()
val i = 45
}
inline fun bar() {
val a = 1
foo { 42 }
}
inline fun foo(f: () -> Unit) {
//Breakpoint!
f()
}
// STEP_OVER: 4
@@ -0,0 +1,20 @@
package soInlineFunCallInLastStatementOfInlineWithArgumentFromCalleeAndOwn
fun main(args: Array<String>) {
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
@@ -0,0 +1,17 @@
package soInlineFunWithLastStatementMultilineArgumentCall
fun main(args: Array<String>) {
var k = 444
bar {
val b = 1
}
k++
}
inline fun bar(f: (Int) -> Unit) {
//Breakpoint!
f(1)
}
// STEP_OVER: 1
@@ -0,0 +1,14 @@
package soInlineFunWithLastStatementOneLineArgumentCall
fun main(args: Array<String>) {
var k = 444
bar { val b = 1 }
k++
}
inline fun bar(f: (Int) -> Unit) {
//Breakpoint!
f(1)
}
// STEP_OVER: 4
@@ -0,0 +1,23 @@
package soInlineIfConditionLambdaFalse
fun main(args: Array<String>) {
bar {
false
}
}
inline fun bar(f: (Int) -> Boolean) {
//Breakpoint!
if (f(42)) {
foo()
}
else {
foo()
}
foo()
}
fun foo() {}
// STEP_OVER: 4
@@ -0,0 +1,23 @@
package soInlineIfConditionLambdaTrue
fun main(args: Array<String>) {
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
@@ -0,0 +1,23 @@
package soInlineWhileCondition
fun main(args: Array<String>) {
//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
@@ -0,0 +1,15 @@
package soReifiedInlineIfConditionFalse
fun main(args: Array<String>) {
// Reified function call in if condition
//Breakpoint!
if (reified(11) != 11) { // 1
val a = 22
}
} // 2
inline fun <reified T> reified(f: T): T {
val a = 33
return f
}
@@ -0,0 +1,18 @@
package soSimpleInlineIfCondition
fun main(args: Array<String>) {
//Breakpoint!
if (foo {
test(2)
}) {
bar()
}
bar()
}
inline fun foo(f: () -> Boolean): Boolean = f()
fun test(i: Int): Boolean = true
fun bar() {}
@@ -0,0 +1,11 @@
package stepOverDeclarationInInlineFun
fun main(args: Array<String>) {
foo { 1 }
}
inline fun foo(f: () -> Int): Int {
//Breakpoint!
val a = 15
return f()
}
@@ -2,45 +2,40 @@ package stepOverIfWithInline
fun main(args: Array<String>) {
//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 T> reified(f: T): Int {
val a = 1
return 1
}
// STEP_OVER: 25
// STEP_OVER: 17
@@ -1,8 +1,8 @@
package stepOverInlinedLambda
fun main(args: Array<String>) {
val a = A()
//Breakpoint!
val a = A()
foo { test(1) }
foo {
test(2)
@@ -27,8 +27,6 @@ fun main(args: Array<String>) {
})
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
// STEP_OVER: 12
@@ -3,16 +3,16 @@ package stepOverInlinedLambdaStdlib
fun main(args: Array<String>) {
//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
@@ -9,8 +9,11 @@ fun main(args: Array<String>) {
inline fun bar(f: (Int) -> Unit) {
//Breakpoint!
val a = 1
foo()
val f = f(1)
val c = 1
}
// STEP_OVER: 3
fun foo() {}
// STEP_OVER: 4
@@ -4,43 +4,43 @@ fun main(args: Array<String>) {
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
// STEP_OVER: 15
@@ -1,23 +1,23 @@
package stepOverWhenInReturn
fun main(args: Array<String>) {
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
@@ -2,45 +2,45 @@ package stepOverWhenWithInline
fun main(args: Array<String>) {
//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<String>) {
}
// 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<String>) {
}
}
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
// STEP_OVER: 30
@@ -2,40 +2,29 @@ package stepOverWhileWithInline
fun main(args: Array<String>) {
//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
// STEP_OVER: 16
@@ -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");