Debugger: Rewrite step over action (KT-14296)
The main goal is to make behavior similar to what happens in Java. For instance, now we always skip lambdas. Also, we can reliably use '$i$f' and '$i$a' synthetic local variables. There is no need in complicated hacks any more.
This commit is contained in:
+2
-1
@@ -123,7 +123,8 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
|
|||||||
|
|
||||||
if (psiFile !is KtFile) throw NoDataException.INSTANCE
|
if (psiFile !is KtFile) throw NoDataException.INSTANCE
|
||||||
|
|
||||||
val sourceLineNumber = location.safeSourceLineNumber()
|
// Zero-based line-number for Document.getLineStartOffset()
|
||||||
|
val sourceLineNumber = location.safeLineNumber() - 1
|
||||||
if (sourceLineNumber < 0) {
|
if (sourceLineNumber < 0) {
|
||||||
throw NoDataException.INSTANCE
|
throw NoDataException.INSTANCE
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-3
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.debugger.stepping;
|
package org.jetbrains.kotlin.idea.debugger.stepping;
|
||||||
|
|
||||||
|
import com.intellij.debugger.SourcePosition;
|
||||||
import com.intellij.debugger.engine.DebugProcessImpl;
|
import com.intellij.debugger.engine.DebugProcessImpl;
|
||||||
import com.intellij.debugger.engine.RequestHint;
|
import com.intellij.debugger.engine.RequestHint;
|
||||||
import com.intellij.debugger.engine.SuspendContextImpl;
|
import com.intellij.debugger.engine.SuspendContextImpl;
|
||||||
@@ -48,7 +49,7 @@ public class DebuggerSteppingHelper {
|
|||||||
public static DebugProcessImpl.ResumeCommand createStepOverCommand(
|
public static DebugProcessImpl.ResumeCommand createStepOverCommand(
|
||||||
SuspendContextImpl suspendContext,
|
SuspendContextImpl suspendContext,
|
||||||
boolean ignoreBreakpoints,
|
boolean ignoreBreakpoints,
|
||||||
KotlinSourcePosition kotlinSourcePosition
|
SourcePosition sourcePosition
|
||||||
) {
|
) {
|
||||||
DebugProcessImpl debugProcess = suspendContext.getDebugProcess();
|
DebugProcessImpl debugProcess = suspendContext.getDebugProcess();
|
||||||
|
|
||||||
@@ -58,8 +59,9 @@ public class DebuggerSteppingHelper {
|
|||||||
try {
|
try {
|
||||||
StackFrameProxyImpl frameProxy = suspendContext.getFrameProxy();
|
StackFrameProxyImpl frameProxy = suspendContext.getFrameProxy();
|
||||||
if (frameProxy != null) {
|
if (frameProxy != null) {
|
||||||
KotlinStepAction action = KotlinSteppingCommandProviderKt
|
|
||||||
.getStepOverAction(frameProxy.location(), kotlinSourcePosition, frameProxy);
|
Location location = frameProxy.location();
|
||||||
|
KotlinStepAction action = KotlinSteppingCommandProviderKt.getStepOverAction(location, sourcePosition, frameProxy);
|
||||||
|
|
||||||
createStepRequest(
|
createStepRequest(
|
||||||
suspendContext, getContextThread(),
|
suspendContext, getContextThread(),
|
||||||
|
|||||||
-39
@@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
|
||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jetbrains.kotlin.idea.debugger.stepping
|
|
||||||
|
|
||||||
import com.intellij.debugger.SourcePosition
|
|
||||||
import org.jetbrains.kotlin.idea.core.util.getLineNumber
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parents
|
|
||||||
|
|
||||||
data class KotlinSourcePosition(
|
|
||||||
val file: KtFile, val declaration: KtDeclaration,
|
|
||||||
val linesRange: IntRange, val sourcePosition: SourcePosition
|
|
||||||
) {
|
|
||||||
companion object {
|
|
||||||
fun create(sourcePosition: SourcePosition): KotlinSourcePosition? {
|
|
||||||
val file = sourcePosition.file as? KtFile ?: return null
|
|
||||||
if (sourcePosition.line < 0) return null
|
|
||||||
|
|
||||||
val elementAt = sourcePosition.elementAt ?: return null
|
|
||||||
|
|
||||||
val containingDeclaration = elementAt.parents
|
|
||||||
.filterIsInstance<KtDeclaration>()
|
|
||||||
.filter { it is KtFunction || it is KtProperty || it is KtClassInitializer }
|
|
||||||
.firstOrNull { !KtPsiUtil.isLocal(it) }
|
|
||||||
?: return null
|
|
||||||
|
|
||||||
val startLineNumber = containingDeclaration.getLineNumber(true) + 1
|
|
||||||
val endLineNumber = containingDeclaration.getLineNumber(false) + 1
|
|
||||||
if (startLineNumber > endLineNumber) return null
|
|
||||||
|
|
||||||
val linesRange = startLineNumber..endLineNumber
|
|
||||||
|
|
||||||
return KotlinSourcePosition(file, containingDeclaration, linesRange, sourcePosition)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+14
-15
@@ -9,36 +9,35 @@ import com.intellij.debugger.engine.DebugProcessImpl
|
|||||||
import com.intellij.debugger.engine.SuspendContextImpl
|
import com.intellij.debugger.engine.SuspendContextImpl
|
||||||
import com.intellij.xdebugger.impl.XSourcePositionImpl
|
import com.intellij.xdebugger.impl.XSourcePositionImpl
|
||||||
import org.jetbrains.kotlin.idea.debugger.stepping.filter.KotlinStepOverInlineFilter
|
import org.jetbrains.kotlin.idea.debugger.stepping.filter.KotlinStepOverInlineFilter
|
||||||
import org.jetbrains.kotlin.idea.debugger.stepping.filter.StepOverFilterData
|
import org.jetbrains.kotlin.idea.debugger.stepping.filter.LocationToken
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.stepping.filter.StepOverCallerInfo
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
|
|
||||||
sealed class KotlinStepAction(
|
sealed class KotlinStepAction {
|
||||||
val position: XSourcePositionImpl? = null,
|
object StepOver : KotlinStepAction() {
|
||||||
val stepOverInlineData: StepOverFilterData? = null
|
override fun apply(debugProcess: DebugProcessImpl, suspendContext: SuspendContextImpl, ignoreBreakpoints: Boolean) {
|
||||||
) {
|
|
||||||
class StepOver : KotlinStepAction() {
|
|
||||||
override fun apply(debugProcess: DebugProcessImpl, suspendContext: SuspendContextImpl, ignoreBreakpoints: Boolean) =
|
|
||||||
debugProcess.createStepOverCommand(suspendContext, ignoreBreakpoints).contextAction(suspendContext)
|
debugProcess.createStepOverCommand(suspendContext, ignoreBreakpoints).contextAction(suspendContext)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StepOut : KotlinStepAction() {
|
object StepOut : KotlinStepAction() {
|
||||||
override fun apply(debugProcess: DebugProcessImpl, suspendContext: SuspendContextImpl, ignoreBreakpoints: Boolean) =
|
override fun apply(debugProcess: DebugProcessImpl, suspendContext: SuspendContextImpl, ignoreBreakpoints: Boolean) {
|
||||||
debugProcess.createStepOutCommand(suspendContext).contextAction(suspendContext)
|
debugProcess.createStepOutCommand(suspendContext).contextAction(suspendContext)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RunToCursor(position: XSourcePositionImpl) : KotlinStepAction(position) {
|
class RunToCursor(private val position: XSourcePositionImpl) : KotlinStepAction() {
|
||||||
override fun apply(debugProcess: DebugProcessImpl, suspendContext: SuspendContextImpl, ignoreBreakpoints: Boolean) {
|
override fun apply(debugProcess: DebugProcessImpl, suspendContext: SuspendContextImpl, ignoreBreakpoints: Boolean) {
|
||||||
return runReadAction {
|
return runReadAction {
|
||||||
debugProcess.createRunToCursorCommand(suspendContext, position!!, ignoreBreakpoints)
|
debugProcess.createRunToCursorCommand(suspendContext, position, ignoreBreakpoints)
|
||||||
}.contextAction(suspendContext)
|
}.contextAction(suspendContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StepOverInlined(stepOverInlineData: StepOverFilterData) : KotlinStepAction(stepOverInlineData = stepOverInlineData) {
|
class StepOverInlined(private val tokensToSkip: Set<LocationToken>, private val callerInfo: StepOverCallerInfo) : KotlinStepAction() {
|
||||||
override fun apply(debugProcess: DebugProcessImpl, suspendContext: SuspendContextImpl, ignoreBreakpoints: Boolean) {
|
override fun apply(debugProcess: DebugProcessImpl, suspendContext: SuspendContextImpl, ignoreBreakpoints: Boolean) {
|
||||||
return KotlinStepActionFactory(debugProcess).createKotlinStepOverInlineAction(
|
val filter = KotlinStepOverInlineFilter(debugProcess.project, tokensToSkip, callerInfo)
|
||||||
KotlinStepOverInlineFilter(debugProcess.project, stepOverInlineData!!)
|
return KotlinStepActionFactory(debugProcess).createKotlinStepOverInlineAction(filter).contextAction(suspendContext)
|
||||||
).contextAction(suspendContext)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-15
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.debugger.stepping
|
package org.jetbrains.kotlin.idea.debugger.stepping
|
||||||
|
|
||||||
|
import com.intellij.debugger.engine.DebugProcess.JAVA_STRATUM
|
||||||
import com.intellij.debugger.engine.RequestHint
|
import com.intellij.debugger.engine.RequestHint
|
||||||
import com.intellij.debugger.engine.SuspendContextImpl
|
import com.intellij.debugger.engine.SuspendContextImpl
|
||||||
import com.intellij.debugger.engine.evaluation.EvaluateException
|
import com.intellij.debugger.engine.evaluation.EvaluateException
|
||||||
@@ -23,35 +24,29 @@ import com.intellij.debugger.jdi.ThreadReferenceProxyImpl
|
|||||||
import com.intellij.openapi.diagnostic.Logger
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.sun.jdi.VMDisconnectedException
|
import com.sun.jdi.VMDisconnectedException
|
||||||
import com.sun.jdi.request.StepRequest
|
import com.sun.jdi.request.StepRequest
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.safeLineNumber
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.safeLocation
|
||||||
|
|
||||||
// Originally copied from RequestHint
|
// Originally copied from RequestHint
|
||||||
class KotlinStepOverInlinedLinesHint(
|
class KotlinStepOverInlinedLinesHint(
|
||||||
stepThread: ThreadReferenceProxyImpl,
|
stepThread: ThreadReferenceProxyImpl,
|
||||||
suspendContext: SuspendContextImpl,
|
suspendContext: SuspendContextImpl,
|
||||||
methodFilter: KotlinMethodFilter
|
private val filter: KotlinMethodFilter
|
||||||
) : RequestHint(stepThread, suspendContext, methodFilter) {
|
) : RequestHint(stepThread, suspendContext, StepRequest.STEP_LINE, StepRequest.STEP_OVER, filter) {
|
||||||
private companion object {
|
private companion object {
|
||||||
private val LOG = Logger.getInstance(KotlinStepOverInlinedLinesHint::class.java)
|
private val LOG = Logger.getInstance(KotlinStepOverInlinedLinesHint::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val filter = methodFilter
|
|
||||||
|
|
||||||
override fun getDepth(): Int = StepRequest.STEP_OVER
|
|
||||||
|
|
||||||
override fun getNextStepDepth(context: SuspendContextImpl): Int {
|
override fun getNextStepDepth(context: SuspendContextImpl): Int {
|
||||||
try {
|
try {
|
||||||
val frameProxy = context.frameProxy
|
val frameProxy = context.frameProxy
|
||||||
if (frameProxy != null) {
|
if (frameProxy != null) {
|
||||||
if (isTheSameFrame(context)) {
|
if (isTheSameFrame(context)) {
|
||||||
return if (filter.locationMatches(context, frameProxy.location())) {
|
val isAcceptable = (filter.locationMatches(context, frameProxy.location()))
|
||||||
STOP
|
return if (isAcceptable) STOP else StepRequest.STEP_OVER
|
||||||
} else {
|
} else if (isSteppedOut) {
|
||||||
StepRequest.STEP_OVER
|
val lineNumber = frameProxy.safeLocation()?.safeLineNumber(JAVA_STRATUM) ?: -1
|
||||||
}
|
return if (lineNumber >= 0) STOP else StepRequest.STEP_OVER
|
||||||
}
|
|
||||||
|
|
||||||
if (isSteppedOut) {
|
|
||||||
return STOP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return StepRequest.STEP_OUT
|
return StepRequest.STEP_OUT
|
||||||
|
|||||||
+71
-182
@@ -15,32 +15,23 @@ import com.intellij.debugger.impl.JvmSteppingCommandProvider
|
|||||||
import com.intellij.debugger.jdi.StackFrameProxyImpl
|
import com.intellij.debugger.jdi.StackFrameProxyImpl
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.xdebugger.impl.XSourcePositionImpl
|
import com.intellij.xdebugger.impl.XSourcePositionImpl
|
||||||
import com.sun.jdi.AbsentInformationException
|
import com.sun.jdi.*
|
||||||
import com.sun.jdi.LocalVariable
|
|
||||||
import com.sun.jdi.Location
|
|
||||||
import org.jetbrains.annotations.TestOnly
|
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.analyze
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
||||||
import org.jetbrains.kotlin.idea.core.util.CodeInsightUtils
|
import org.jetbrains.kotlin.idea.core.util.CodeInsightUtils
|
||||||
import org.jetbrains.kotlin.idea.debugger.*
|
import org.jetbrains.kotlin.idea.debugger.*
|
||||||
import org.jetbrains.kotlin.idea.core.util.getLineNumber
|
import org.jetbrains.kotlin.idea.core.util.getLineNumber
|
||||||
import org.jetbrains.kotlin.idea.core.util.getLineStartOffset
|
import org.jetbrains.kotlin.idea.core.util.getLineStartOffset
|
||||||
import org.jetbrains.kotlin.idea.debugger.stepping.filter.KotlinStepOverInlineFilter
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.stepping.filter.KotlinSuspendCallStepOverFilter
|
import org.jetbrains.kotlin.idea.debugger.stepping.filter.KotlinSuspendCallStepOverFilter
|
||||||
import org.jetbrains.kotlin.idea.debugger.stepping.filter.StepOverFilterData
|
import org.jetbrains.kotlin.idea.debugger.stepping.filter.LocationToken
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.stepping.filter.StepOverCallerInfo
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
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.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
|
|
||||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
@@ -72,12 +63,6 @@ class KotlinSteppingCommandProvider : JvmSteppingCommandProvider() {
|
|||||||
ignoreBreakpoints: Boolean,
|
ignoreBreakpoints: Boolean,
|
||||||
sourcePosition: SourcePosition
|
sourcePosition: SourcePosition
|
||||||
): DebugProcessImpl.ResumeCommand? {
|
): DebugProcessImpl.ResumeCommand? {
|
||||||
val kotlinSourcePosition = KotlinSourcePosition.create(sourcePosition) ?: return null
|
|
||||||
|
|
||||||
if (isSpecialStepOverNeeded(kotlinSourcePosition)) {
|
|
||||||
return DebuggerSteppingHelper.createStepOverCommand(suspendContext, ignoreBreakpoints, kotlinSourcePosition)
|
|
||||||
}
|
|
||||||
|
|
||||||
val file = sourcePosition.elementAt.containingFile
|
val file = sourcePosition.elementAt.containingFile
|
||||||
val location = suspendContext.debugProcess.invokeInManagerThread { suspendContext.frameProxy?.safeLocation() } ?: return null
|
val location = suspendContext.debugProcess.invokeInManagerThread { suspendContext.frameProxy?.safeLocation() } ?: return null
|
||||||
if (isInSuspendMethod(location) && !isOnSuspendReturnOrReenter(location) && !isLastLineLocationInMethod(location)) {
|
if (isInSuspendMethod(location) && !isOnSuspendReturnOrReenter(location) && !isLastLineLocationInMethod(location)) {
|
||||||
@@ -86,27 +71,7 @@ class KotlinSteppingCommandProvider : JvmSteppingCommandProvider() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return DebuggerSteppingHelper.createStepOverCommand(suspendContext, ignoreBreakpoints, sourcePosition)
|
||||||
}
|
|
||||||
|
|
||||||
private fun isSpecialStepOverNeeded(kotlinSourcePosition: KotlinSourcePosition): Boolean {
|
|
||||||
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 containingDescriptor = kotlinSourcePosition.declaration.resolveToDescriptorIfAny()
|
|
||||||
if (containingDescriptor != null && InlineUtil.isInline(containingDescriptor)) {
|
|
||||||
val inlineArgumentsCallsIfAny = getInlineArgumentsCallsIfAny(sourcePosition, containingDescriptor)
|
|
||||||
if (inlineArgumentsCallsIfAny != null && inlineArgumentsCallsIfAny.isNotEmpty()) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestOnly
|
@TestOnly
|
||||||
@@ -127,7 +92,7 @@ class KotlinSteppingCommandProvider : JvmSteppingCommandProvider() {
|
|||||||
|
|
||||||
val lineStartOffset = file.getLineStartOffset(sourcePosition.line) ?: return null
|
val lineStartOffset = file.getLineStartOffset(sourcePosition.line) ?: return null
|
||||||
|
|
||||||
val inlineFunctions = getInlineFunctionsIfAny(file, lineStartOffset)
|
val inlineFunctions = findInlineFunctions(file, lineStartOffset)
|
||||||
val inlinedArgument = getInlineArgumentIfAny(sourcePosition.elementAt)
|
val inlinedArgument = getInlineArgumentIfAny(sourcePosition.elementAt)
|
||||||
|
|
||||||
if (inlineFunctions.isEmpty() && inlinedArgument == null) return null
|
if (inlineFunctions.isEmpty() && inlinedArgument == null) return null
|
||||||
@@ -140,12 +105,21 @@ private operator fun PsiElement?.contains(element: PsiElement): Boolean {
|
|||||||
return this?.textRange?.contains(element.textRange) ?: false
|
return this?.textRange?.contains(element.textRange) ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getInlineCallFunctionArgumentsIfAny(sourcePosition: SourcePosition): List<KtFunction> {
|
private fun findInlinedFunctionArguments(sourcePosition: SourcePosition): List<KtFunction> {
|
||||||
val inlineFunctionCalls = getInlineFunctionCallsIfAny(sourcePosition)
|
val args = mutableListOf<KtFunction>()
|
||||||
return getInlineArgumentsIfAny(inlineFunctionCalls)
|
|
||||||
|
for (call in findInlineFunctionCalls(sourcePosition)) {
|
||||||
|
for (arg in call.valueArguments) {
|
||||||
|
val expression = arg.getArgumentExpression()
|
||||||
|
val functionExpression = (expression as? KtLambdaExpression)?.functionLiteral ?: expression
|
||||||
|
args += functionExpression as? KtFunction ?: continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getInlineFunctionsIfAny(file: KtFile, offset: Int): List<KtNamedFunction> {
|
private fun findInlineFunctions(file: KtFile, offset: Int): List<KtNamedFunction> {
|
||||||
val elementAt = file.findElementAt(offset) ?: return emptyList()
|
val elementAt = file.findElementAt(offset) ?: return emptyList()
|
||||||
val containingFunction = elementAt.getParentOfType<KtNamedFunction>(false) ?: return emptyList()
|
val containingFunction = elementAt.getParentOfType<KtNamedFunction>(false) ?: return emptyList()
|
||||||
|
|
||||||
@@ -155,41 +129,7 @@ private fun getInlineFunctionsIfAny(file: KtFile, offset: Int): List<KtNamedFunc
|
|||||||
return DebuggerUtils.analyzeElementWithInline(containingFunction, false).filterIsInstance<KtNamedFunction>()
|
return DebuggerUtils.analyzeElementWithInline(containingFunction, false).filterIsInstance<KtNamedFunction>()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getInlineArgumentsIfAny(inlineFunctionCalls: List<KtCallExpression>): List<KtFunction> {
|
private fun findInlineFunctionCalls(sourcePosition: SourcePosition): List<KtCallExpression> {
|
||||||
return inlineFunctionCalls.flatMap {
|
|
||||||
it.valueArguments
|
|
||||||
.map(::getArgumentExpression)
|
|
||||||
.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 resolvedCall = ktCallExpression.resolveToCall() as? VariableAsFunctionResolvedCall ?: 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 {
|
fun isInlineCall(expr: KtCallExpression): Boolean {
|
||||||
val resolvedCall = expr.resolveToCall() ?: return false
|
val resolvedCall = expr.resolveToCall() ?: return false
|
||||||
return InlineUtil.isInline(resolvedCall.resultingDescriptor)
|
return InlineUtil.isInline(resolvedCall.resultingDescriptor)
|
||||||
@@ -239,108 +179,66 @@ interface KotlinMethodFilter : MethodFilter {
|
|||||||
fun locationMatches(context: SuspendContextImpl, location: Location): Boolean
|
fun locationMatches(context: SuspendContextImpl, location: Location): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getStepOverAction(location: Location, kotlinSourcePosition: KotlinSourcePosition, frameProxy: StackFrameProxyImpl): KotlinStepAction {
|
fun getStepOverAction(location: Location, sourcePosition: SourcePosition, frameProxy: StackFrameProxyImpl): KotlinStepAction {
|
||||||
val inlineArgumentsToSkip = runReadAction {
|
val stackFrame = frameProxy.safeStackFrame() ?: return KotlinStepAction.StepOver
|
||||||
getInlineCallFunctionArgumentsIfAny(kotlinSourcePosition.sourcePosition)
|
val method = location.safeMethod() ?: return KotlinStepAction.StepOver
|
||||||
|
val token = LocationToken.from(stackFrame).takeIf { it.lineNumber > 0 } ?: return KotlinStepAction.StepOver
|
||||||
|
|
||||||
|
val inlinedFunctionArgumentRanges = sourcePosition.collectInlineFunctionArgumentRanges()
|
||||||
|
|
||||||
|
val tokensToSkip = mutableSetOf(token)
|
||||||
|
|
||||||
|
for (candidate in method.allLineLocations() ?: emptyList()) {
|
||||||
|
val candidateKotlinLineNumber = candidate.safeKotlinPreferredLineNumber()
|
||||||
|
val candidateStackFrame = StackFrameForLocation(frameProxy.stackFrame, candidate)
|
||||||
|
val candidateToken = LocationToken.from(candidateStackFrame)
|
||||||
|
|
||||||
|
val isAcceptable = candidateToken.lineNumber >= 0
|
||||||
|
&& candidateToken.lineNumber != token.lineNumber
|
||||||
|
&& inlinedFunctionArgumentRanges.none { range -> range.contains(candidateKotlinLineNumber) }
|
||||||
|
&& candidateToken.inlineVariables.none { it !in token.inlineVariables }
|
||||||
|
|
||||||
|
if (!isAcceptable) {
|
||||||
|
tokensToSkip += candidateToken
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return getStepOverAction(
|
return KotlinStepAction.StepOverInlined(tokensToSkip, StepOverCallerInfo.from(location))
|
||||||
location, kotlinSourcePosition.file, kotlinSourcePosition.linesRange,
|
|
||||||
inlineArgumentsToSkip, frameProxy
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getStepOverAction(
|
private fun SourcePosition.collectInlineFunctionArgumentRanges(): List<IntRange> {
|
||||||
location: Location,
|
return runReadAction {
|
||||||
sourceFile: KtFile,
|
val ranges = mutableListOf<IntRange>()
|
||||||
range: IntRange,
|
for (arg in findInlinedFunctionArguments(this)) {
|
||||||
inlineFunctionArguments: List<KtElement>,
|
val range = arg.getLineRange() ?: continue
|
||||||
frameProxy: StackFrameProxyImpl
|
if (range.count() > 1) {
|
||||||
): KotlinStepAction {
|
ranges += range
|
||||||
location.declaringType() ?: return KotlinStepAction.StepOver()
|
}
|
||||||
|
|
||||||
val methodLocations = location.method().safeAllLineLocations()
|
|
||||||
if (methodLocations.isEmpty()) {
|
|
||||||
return KotlinStepAction.StepOver()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isThisMethodLocation(nextLocation: Location): Boolean {
|
|
||||||
if (nextLocation.method() != location.method()) {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
return@runReadAction ranges
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val ktLineNumber = nextLocation.lineNumber()
|
private class StackFrameForLocation(private val original: StackFrame, private val location: Location) : StackFrame by original {
|
||||||
if (ktLineNumber !in range) {
|
override fun location() = location
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return try {
|
override fun visibleVariables(): List<LocalVariable> {
|
||||||
nextLocation.sourceName(KOTLIN_STRATA_NAME) == sourceFile.name
|
return location.method()?.variables()?.filter { it.isVisible(this) } ?: throw AbsentInformationException()
|
||||||
} catch (e: AbsentInformationException) {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isBackEdgeLocation(): Boolean {
|
override fun visibleVariableByName(name: String?): LocalVariable {
|
||||||
val previousSuitableLocation = methodLocations.reversed()
|
return location.method()?.variablesByName(name)?.firstOrNull { it.isVisible(this) } ?: throw AbsentInformationException()
|
||||||
.asSequence()
|
}
|
||||||
.dropWhile { it != location }
|
}
|
||||||
.drop(1)
|
|
||||||
.filter(::isThisMethodLocation)
|
|
||||||
.dropWhile { it.lineNumber() == location.lineNumber() }
|
|
||||||
.firstOrNull()
|
|
||||||
|
|
||||||
return previousSuitableLocation != null && previousSuitableLocation.lineNumber() > location.lineNumber()
|
private fun KtElement.getLineRange(): IntRange? {
|
||||||
|
val startLineNumber = getLineNumber(true)
|
||||||
|
val endLineNumber = getLineNumber(false)
|
||||||
|
if (startLineNumber > endLineNumber) {
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
val patchedLocation = if (isBackEdgeLocation()) {
|
return startLineNumber..endLineNumber
|
||||||
// Pretend we had already done a backing step
|
|
||||||
methodLocations
|
|
||||||
.filter(::isThisMethodLocation)
|
|
||||||
.firstOrNull { it.lineNumber() == location.lineNumber() } ?: location
|
|
||||||
} else {
|
|
||||||
location
|
|
||||||
}
|
|
||||||
|
|
||||||
val patchedLineNumber = patchedLocation.lineNumber()
|
|
||||||
|
|
||||||
val lambdaArgumentRanges = runReadAction {
|
|
||||||
inlineFunctionArguments.map {
|
|
||||||
val startLineNumber = it.getLineNumber(true) + 1
|
|
||||||
val endLineNumber = it.getLineNumber(false) + 1
|
|
||||||
|
|
||||||
startLineNumber..endLineNumber
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val inlineRangeVariables = getInlineRangeLocalVariables(frameProxy)
|
|
||||||
|
|
||||||
// Try to find the range for step over:
|
|
||||||
// - Lines from other files and from functions that are not in range of current one are definitely inlined and should be stepped over.
|
|
||||||
// - Lines in function arguments of inlined functions are inlined too as we found them starting from the position of inlined call.
|
|
||||||
// - Current line locations should also be stepped over.
|
|
||||||
//
|
|
||||||
// We might erroneously extend this range too much when there's a call of function argument or other
|
|
||||||
// inline function in last statement of inline function. The list of inlineRangeVariables will be used later to overcome it.
|
|
||||||
val stepOverLocations = methodLocations
|
|
||||||
.dropWhile { it != patchedLocation }
|
|
||||||
.drop(1)
|
|
||||||
.dropWhile { it.lineNumber() == patchedLineNumber }
|
|
||||||
.takeWhile { loc ->
|
|
||||||
!isThisMethodLocation(loc) || lambdaArgumentRanges.any { loc.lineNumber() in it } || loc.lineNumber() == patchedLineNumber
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stepOverLocations.isNotEmpty()) {
|
|
||||||
return KotlinStepAction.StepOverInlined(
|
|
||||||
StepOverFilterData(
|
|
||||||
patchedLineNumber,
|
|
||||||
stepOverLocations.map { it.lineNumber() }.toSet(),
|
|
||||||
inlineRangeVariables
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return KotlinStepAction.StepOver()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getStepOutAction(
|
fun getStepOutAction(
|
||||||
@@ -349,7 +247,7 @@ fun getStepOutAction(
|
|||||||
inlineFunctions: List<KtNamedFunction>,
|
inlineFunctions: List<KtNamedFunction>,
|
||||||
inlinedArgument: KtFunctionLiteral?
|
inlinedArgument: KtFunctionLiteral?
|
||||||
): KotlinStepAction {
|
): KotlinStepAction {
|
||||||
val computedReferenceType = location.declaringType() ?: return KotlinStepAction.StepOut()
|
val computedReferenceType = location.declaringType() ?: return KotlinStepAction.StepOut
|
||||||
|
|
||||||
val locations = computedReferenceType.safeAllLineLocations()
|
val locations = computedReferenceType.safeAllLineLocations()
|
||||||
val nextLineLocations = locations
|
val nextLineLocations = locations
|
||||||
@@ -360,15 +258,15 @@ fun getStepOutAction(
|
|||||||
|
|
||||||
if (inlineFunctions.isNotEmpty()) {
|
if (inlineFunctions.isNotEmpty()) {
|
||||||
val position = suspendContext.getXPositionForStepOutFromInlineFunction(nextLineLocations, inlineFunctions)
|
val position = suspendContext.getXPositionForStepOutFromInlineFunction(nextLineLocations, inlineFunctions)
|
||||||
return position?.let { KotlinStepAction.RunToCursor(it) } ?: KotlinStepAction.StepOver()
|
return position?.let { KotlinStepAction.RunToCursor(it) } ?: KotlinStepAction.StepOver
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inlinedArgument != null) {
|
if (inlinedArgument != null) {
|
||||||
val position = suspendContext.getXPositionForStepOutFromInlinedArgument(nextLineLocations, inlinedArgument)
|
val position = suspendContext.getXPositionForStepOutFromInlinedArgument(nextLineLocations, inlinedArgument)
|
||||||
return position?.let { KotlinStepAction.RunToCursor(it) } ?: KotlinStepAction.StepOver()
|
return position?.let { KotlinStepAction.RunToCursor(it) } ?: KotlinStepAction.StepOver
|
||||||
}
|
}
|
||||||
|
|
||||||
return KotlinStepAction.StepOver()
|
return KotlinStepAction.StepOver
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun SuspendContextImpl.getXPositionForStepOutFromInlineFunction(
|
private fun SuspendContextImpl.getXPositionForStepOutFromInlineFunction(
|
||||||
@@ -421,15 +319,6 @@ private fun SuspendContextImpl.getNextPositionWithFilter(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getInlineRangeLocalVariables(stackFrame: StackFrameProxyImpl): List<LocalVariable> {
|
|
||||||
return stackFrame.safeVisibleVariables()
|
|
||||||
.filter {
|
|
||||||
val name = it.name()
|
|
||||||
name.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION)
|
|
||||||
}
|
|
||||||
.map { it.variable }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getInlineArgumentIfAny(elementAt: PsiElement?): KtFunctionLiteral? {
|
private fun getInlineArgumentIfAny(elementAt: PsiElement?): KtFunctionLiteral? {
|
||||||
val functionLiteralExpression = elementAt?.getParentOfType<KtLambdaExpression>(false) ?: return null
|
val functionLiteralExpression = elementAt?.getParentOfType<KtLambdaExpression>(false) ?: return null
|
||||||
|
|
||||||
|
|||||||
+51
-20
@@ -16,39 +16,70 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.debugger.stepping.filter
|
package org.jetbrains.kotlin.idea.debugger.stepping.filter
|
||||||
|
|
||||||
|
import com.intellij.debugger.engine.DebugProcess.JAVA_STRATUM
|
||||||
import com.intellij.debugger.engine.DebugProcessImpl
|
import com.intellij.debugger.engine.DebugProcessImpl
|
||||||
import com.intellij.debugger.engine.SuspendContextImpl
|
import com.intellij.debugger.engine.SuspendContextImpl
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.util.Range
|
import com.intellij.util.Range
|
||||||
import com.sun.jdi.LocalVariable
|
|
||||||
import com.sun.jdi.Location
|
import com.sun.jdi.Location
|
||||||
|
import com.sun.jdi.StackFrame
|
||||||
|
import org.jetbrains.kotlin.codegen.inline.isFakeLocalVariableForInline
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.safeLineNumber
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.safeMethod
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.safeVariables
|
||||||
import org.jetbrains.kotlin.idea.debugger.stepping.KotlinMethodFilter
|
import org.jetbrains.kotlin.idea.debugger.stepping.KotlinMethodFilter
|
||||||
import org.jetbrains.kotlin.idea.debugger.stepping.getInlineRangeLocalVariables
|
|
||||||
|
|
||||||
class StepOverFilterData(
|
@Suppress("EqualsOrHashCode")
|
||||||
val lineNumber: Int,
|
data class StepOverCallerInfo(val declaringType: String, val methodName: String?, val methodSignature: String?) {
|
||||||
val stepOverLines: Set<Int>,
|
companion object {
|
||||||
val inlineRangeVariables: List<LocalVariable>
|
fun from(location: Location): StepOverCallerInfo {
|
||||||
)
|
val method = location.safeMethod()
|
||||||
|
val declaringType = location.declaringType().name()
|
||||||
class KotlinStepOverInlineFilter(val project: Project, val data: StepOverFilterData) : KotlinMethodFilter {
|
val methodName = method?.name()
|
||||||
override fun locationMatches(context: SuspendContextImpl, location: Location): Boolean {
|
val methodSignature = method?.signature()
|
||||||
val frameProxy = context.frameProxy ?: return true
|
return StepOverCallerInfo(declaringType, methodName, methodSignature)
|
||||||
|
|
||||||
val currentLine = location.lineNumber()
|
|
||||||
if (!(data.stepOverLines.contains(currentLine))) {
|
|
||||||
return currentLine != data.lineNumber
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val visibleInlineVariables = getInlineRangeLocalVariables(frameProxy)
|
data class LocationToken(val lineNumber: Int, val inlineVariables: List<String>) {
|
||||||
|
companion object {
|
||||||
|
fun from(stackFrame: StackFrame): LocationToken {
|
||||||
|
val location = stackFrame.location()
|
||||||
|
val lineNumber = location.safeLineNumber(JAVA_STRATUM)
|
||||||
|
val methodVariables = ArrayList<String>(0)
|
||||||
|
|
||||||
// Our ranges check missed exit from inline function. This is when breakpoint was in last statement of inline functions.
|
for (variable in location.safeMethod()?.safeVariables() ?: emptyList()) {
|
||||||
// This can be observed by inline local range-variables. Absence of any means step out was done.
|
val name = variable.name()
|
||||||
return data.inlineRangeVariables.any { !visibleInlineVariables.contains(it) }
|
if (variable.isVisible(stackFrame) && isFakeLocalVariableForInline(name)) {
|
||||||
|
methodVariables += name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return LocationToken(lineNumber, methodVariables)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class KotlinStepOverInlineFilter(
|
||||||
|
val project: Project,
|
||||||
|
private val tokensToSkip: Set<LocationToken>,
|
||||||
|
private val callerInfo: StepOverCallerInfo
|
||||||
|
) : KotlinMethodFilter {
|
||||||
|
override fun locationMatches(context: SuspendContextImpl, location: Location): Boolean {
|
||||||
|
val stackFrame = context.frameProxy?.stackFrame ?: return true
|
||||||
|
val token = LocationToken.from(stackFrame)
|
||||||
|
val callerInfo = StepOverCallerInfo.from(location)
|
||||||
|
|
||||||
|
if (callerInfo.methodName != null && callerInfo.methodSignature != null && this.callerInfo == callerInfo) {
|
||||||
|
return token.lineNumber >= 0 && token !in tokensToSkip
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun locationMatches(process: DebugProcessImpl, location: Location): Boolean {
|
override fun locationMatches(process: DebugProcessImpl, location: Location): Boolean {
|
||||||
throw IllegalStateException() // Should not be called from Kotlin hint
|
throw IllegalStateException("Should not be called from Kotlin hint")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getCallingExpressionLines(): Range<Int>? = null
|
override fun getCallingExpressionLines(): Range<Int>? = null
|
||||||
|
|||||||
-1
@@ -173,4 +173,3 @@ class CoroutineBuilder(val suspendContext: XSuspendContext) {
|
|||||||
frame.safeLocation()?.safeMethod()?.signature() == "(Ljava/lang/Object;)Ljava/lang/Object;" &&
|
frame.safeLocation()?.safeMethod()?.signature() == "(Ljava/lang/Object;)Ljava/lang/Object;" &&
|
||||||
frame.safeLocation()?.safeLineNumber() ?: 0 < 0
|
frame.safeLocation()?.safeLineNumber() ?: 0 < 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+1
-1
@@ -11,7 +11,7 @@ functionCallStoredToVariable.kt:43
|
|||||||
functionCallStoredToVariable.kt:11
|
functionCallStoredToVariable.kt:11
|
||||||
functionCallStoredToVariable.kt:21
|
functionCallStoredToVariable.kt:21
|
||||||
functionCallStoredToVariable.kt:22
|
functionCallStoredToVariable.kt:22
|
||||||
functionCallStoredToVariable.kt:18
|
functionCallStoredToVariable.kt:25
|
||||||
functionCallStoredToVariable.kt:28
|
functionCallStoredToVariable.kt:28
|
||||||
functionCallStoredToVariable.kt:29
|
functionCallStoredToVariable.kt:29
|
||||||
functionCallStoredToVariable.kt:47
|
functionCallStoredToVariable.kt:47
|
||||||
|
|||||||
+1
@@ -5,6 +5,7 @@ continueLabel.kt:5
|
|||||||
continueLabel.kt:6
|
continueLabel.kt:6
|
||||||
continueLabel.kt:7
|
continueLabel.kt:7
|
||||||
continueLabel.kt:8
|
continueLabel.kt:8
|
||||||
|
continueLabel.kt:10
|
||||||
continueLabel.kt:5
|
continueLabel.kt:5
|
||||||
continueLabel.kt:6
|
continueLabel.kt:6
|
||||||
continueLabel.kt:7
|
continueLabel.kt:7
|
||||||
|
|||||||
Vendored
-1
@@ -6,7 +6,6 @@ inlineCallInForRangeExpression.kt:8
|
|||||||
inlineCallInForRangeExpression.kt:9
|
inlineCallInForRangeExpression.kt:9
|
||||||
inlineCallInForRangeExpression.kt:8
|
inlineCallInForRangeExpression.kt:8
|
||||||
inlineCallInForRangeExpression.kt:9
|
inlineCallInForRangeExpression.kt:9
|
||||||
inlineCallInForRangeExpression.kt:8
|
|
||||||
inlineCallInForRangeExpression.kt:11
|
inlineCallInForRangeExpression.kt:11
|
||||||
Disconnected from the target VM
|
Disconnected from the target VM
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ LineBreakpoint created at noParameterExtensionLambdaArgumentCallInInline3.kt:12
|
|||||||
Run Java
|
Run Java
|
||||||
Connected to the target VM
|
Connected to the target VM
|
||||||
noParameterExtensionLambdaArgumentCallInInline3.kt:12
|
noParameterExtensionLambdaArgumentCallInInline3.kt:12
|
||||||
noParameterExtensionLambdaArgumentCallInInline3.kt:5
|
noParameterExtensionLambdaArgumentCallInInline3.kt:14
|
||||||
Disconnected from the target VM
|
Disconnected from the target VM
|
||||||
|
|
||||||
Process finished with exit code 0
|
Process finished with exit code 0
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ LineBreakpoint created at noParameterLambdaArgumentCallInLambda.kt:5 lambdaOrdin
|
|||||||
Run Java
|
Run Java
|
||||||
Connected to the target VM
|
Connected to the target VM
|
||||||
noParameterLambdaArgumentCallInLambda.kt:5
|
noParameterLambdaArgumentCallInLambda.kt:5
|
||||||
noParameterLambdaArgumentCallInLambda.kt:6
|
noParameterLambdaArgumentCallInLambda.kt:11
|
||||||
Disconnected from the target VM
|
Disconnected from the target VM
|
||||||
|
|
||||||
Process finished with exit code 0
|
Process finished with exit code 0
|
||||||
|
|||||||
+1
@@ -3,6 +3,7 @@ Run Java
|
|||||||
Connected to the target VM
|
Connected to the target VM
|
||||||
soInlineCallInLastStatementInInlineFunctionArgument.kt:7
|
soInlineCallInLastStatementInInlineFunctionArgument.kt:7
|
||||||
soInlineCallInLastStatementInInlineFunctionArgument.kt:8
|
soInlineCallInLastStatementInInlineFunctionArgument.kt:8
|
||||||
|
soInlineCallInLastStatementInInlineFunctionArgument.kt:14
|
||||||
soInlineCallInLastStatementInInlineFunctionArgument.kt:9
|
soInlineCallInLastStatementInInlineFunctionArgument.kt:9
|
||||||
Disconnected from the target VM
|
Disconnected from the target VM
|
||||||
|
|
||||||
|
|||||||
-1
@@ -2,7 +2,6 @@ LineBreakpoint created at soInlineFunOnOneLineFor.kt:17
|
|||||||
Run Java
|
Run Java
|
||||||
Connected to the target VM
|
Connected to the target VM
|
||||||
soInlineFunOnOneLineFor.kt:17
|
soInlineFunOnOneLineFor.kt:17
|
||||||
soInlineFunOnOneLineFor.kt:18
|
|
||||||
soInlineFunOnOneLineFor.kt:8
|
soInlineFunOnOneLineFor.kt:8
|
||||||
soInlineFunOnOneLineFor.kt:9
|
soInlineFunOnOneLineFor.kt:9
|
||||||
Disconnected from the target VM
|
Disconnected from the target VM
|
||||||
|
|||||||
-2
@@ -1,7 +1,5 @@
|
|||||||
package soInlineOperatorIterator
|
package soInlineOperatorIterator
|
||||||
|
|
||||||
// TODO: Test with current bad behaviour of KT-14296
|
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
//Breakpoint!
|
//Breakpoint!
|
||||||
val list = listOf("a", "b", "c")
|
val list = listOf("a", "b", "c")
|
||||||
|
|||||||
Vendored
+8
-15
@@ -1,22 +1,15 @@
|
|||||||
LineBreakpoint created at soInlineOperatorIterator.kt:7
|
LineBreakpoint created at soInlineOperatorIterator.kt:5
|
||||||
Run Java
|
Run Java
|
||||||
Connected to the target VM
|
Connected to the target VM
|
||||||
|
soInlineOperatorIterator.kt:5
|
||||||
|
soInlineOperatorIterator.kt:6
|
||||||
soInlineOperatorIterator.kt:7
|
soInlineOperatorIterator.kt:7
|
||||||
soInlineOperatorIterator.kt:8
|
soInlineOperatorIterator.kt:6
|
||||||
soInlineOperatorIterator.kt:23
|
soInlineOperatorIterator.kt:7
|
||||||
soInlineOperatorIterator.kt:27
|
soInlineOperatorIterator.kt:6
|
||||||
|
soInlineOperatorIterator.kt:7
|
||||||
|
soInlineOperatorIterator.kt:6
|
||||||
soInlineOperatorIterator.kt:9
|
soInlineOperatorIterator.kt:9
|
||||||
soInlineOperatorIterator.kt:8
|
|
||||||
soInlineOperatorIterator.kt:23
|
|
||||||
soInlineOperatorIterator.kt:27
|
|
||||||
soInlineOperatorIterator.kt:9
|
|
||||||
soInlineOperatorIterator.kt:8
|
|
||||||
soInlineOperatorIterator.kt:23
|
|
||||||
soInlineOperatorIterator.kt:27
|
|
||||||
soInlineOperatorIterator.kt:9
|
|
||||||
soInlineOperatorIterator.kt:8
|
|
||||||
soInlineOperatorIterator.kt:23
|
|
||||||
soInlineOperatorIterator.kt:11
|
|
||||||
Disconnected from the target VM
|
Disconnected from the target VM
|
||||||
|
|
||||||
Process finished with exit code 0
|
Process finished with exit code 0
|
||||||
|
|||||||
+1
-1
@@ -14,4 +14,4 @@ inline fun bar(f: () -> Unit) {
|
|||||||
|
|
||||||
fun nop() {}
|
fun nop() {}
|
||||||
|
|
||||||
// STEP_OVER: 2
|
// STEP_OVER: 3
|
||||||
+1
@@ -3,6 +3,7 @@ Run Java
|
|||||||
Connected to the target VM
|
Connected to the target VM
|
||||||
soLastStatementInInlineFunctionArgumentAsAnonymous.kt:6
|
soLastStatementInInlineFunctionArgumentAsAnonymous.kt:6
|
||||||
soLastStatementInInlineFunctionArgumentAsAnonymous.kt:7
|
soLastStatementInInlineFunctionArgumentAsAnonymous.kt:7
|
||||||
|
soLastStatementInInlineFunctionArgumentAsAnonymous.kt:13
|
||||||
soLastStatementInInlineFunctionArgumentAsAnonymous.kt:8
|
soLastStatementInInlineFunctionArgumentAsAnonymous.kt:8
|
||||||
Disconnected from the target VM
|
Disconnected from the target VM
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -15,4 +15,4 @@ inline fun bar(f: () -> Unit) {
|
|||||||
|
|
||||||
fun nop() {}
|
fun nop() {}
|
||||||
|
|
||||||
// STEP_OVER: 2
|
// STEP_OVER: 3
|
||||||
+1
@@ -3,6 +3,7 @@ Run Java
|
|||||||
Connected to the target VM
|
Connected to the target VM
|
||||||
soLastStatementInInlineFunctionArgumentAsAnonymousParNextLine.kt:6
|
soLastStatementInInlineFunctionArgumentAsAnonymousParNextLine.kt:6
|
||||||
soLastStatementInInlineFunctionArgumentAsAnonymousParNextLine.kt:7
|
soLastStatementInInlineFunctionArgumentAsAnonymousParNextLine.kt:7
|
||||||
|
soLastStatementInInlineFunctionArgumentAsAnonymousParNextLine.kt:14
|
||||||
soLastStatementInInlineFunctionArgumentAsAnonymousParNextLine.kt:9
|
soLastStatementInInlineFunctionArgumentAsAnonymousParNextLine.kt:9
|
||||||
Disconnected from the target VM
|
Disconnected from the target VM
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -14,4 +14,4 @@ inline fun bar(f: () -> Unit) {
|
|||||||
|
|
||||||
fun nop() {}
|
fun nop() {}
|
||||||
|
|
||||||
// STEP_OVER: 2
|
// STEP_OVER: 3
|
||||||
+1
@@ -3,6 +3,7 @@ Run Java
|
|||||||
Connected to the target VM
|
Connected to the target VM
|
||||||
soLastStatementInInlineFunctionArgumentInPars.kt:6
|
soLastStatementInInlineFunctionArgumentInPars.kt:6
|
||||||
soLastStatementInInlineFunctionArgumentInPars.kt:7
|
soLastStatementInInlineFunctionArgumentInPars.kt:7
|
||||||
|
soLastStatementInInlineFunctionArgumentInPars.kt:13
|
||||||
soLastStatementInInlineFunctionArgumentInPars.kt:8
|
soLastStatementInInlineFunctionArgumentInPars.kt:8
|
||||||
Disconnected from the target VM
|
Disconnected from the target VM
|
||||||
|
|
||||||
|
|||||||
idea/jvm-debugger/jvm-debugger-test/testData/stepping/stepOver/stepOverInlineFunWithRecursionCall.kt
Vendored
+1
-1
@@ -18,4 +18,4 @@ inline fun inlineCall(l: () -> Unit) {
|
|||||||
l()
|
l()
|
||||||
}
|
}
|
||||||
|
|
||||||
// STEP_OVER: 4
|
// STEP_OVER: 5
|
||||||
+1
@@ -3,6 +3,7 @@ Run Java
|
|||||||
Connected to the target VM
|
Connected to the target VM
|
||||||
stepOverInlineFunWithRecursionCall.kt:6
|
stepOverInlineFunWithRecursionCall.kt:6
|
||||||
stepOverInlineFunWithRecursionCall.kt:6
|
stepOverInlineFunWithRecursionCall.kt:6
|
||||||
|
stepOverInlineFunWithRecursionCall.kt:19
|
||||||
stepOverInlineFunWithRecursionCall.kt:9
|
stepOverInlineFunWithRecursionCall.kt:9
|
||||||
stepOverInlineFunWithRecursionCall.kt:14
|
stepOverInlineFunWithRecursionCall.kt:14
|
||||||
stepOverInlineFunWithRecursionCall.kt:15
|
stepOverInlineFunWithRecursionCall.kt:15
|
||||||
|
|||||||
+31
-2
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.debugger
|
package org.jetbrains.kotlin.idea.debugger
|
||||||
|
|
||||||
|
import com.intellij.debugger.engine.DebugProcess.JAVA_STRATUM
|
||||||
import com.intellij.debugger.engine.evaluation.AbsentInformationEvaluateException
|
import com.intellij.debugger.engine.evaluation.AbsentInformationEvaluateException
|
||||||
import com.intellij.debugger.engine.evaluation.EvaluateException
|
import com.intellij.debugger.engine.evaluation.EvaluateException
|
||||||
import com.intellij.debugger.engine.jdi.StackFrameProxy
|
import com.intellij.debugger.engine.jdi.StackFrameProxy
|
||||||
@@ -12,6 +13,7 @@ import com.intellij.debugger.impl.DebuggerUtilsEx
|
|||||||
import com.intellij.debugger.jdi.LocalVariableProxyImpl
|
import com.intellij.debugger.jdi.LocalVariableProxyImpl
|
||||||
import com.intellij.debugger.jdi.StackFrameProxyImpl
|
import com.intellij.debugger.jdi.StackFrameProxyImpl
|
||||||
import com.sun.jdi.*
|
import com.sun.jdi.*
|
||||||
|
import org.jetbrains.kotlin.codegen.inline.KOTLIN_STRATA_NAME
|
||||||
|
|
||||||
fun StackFrameProxyImpl.safeVisibleVariables(): List<LocalVariableProxyImpl> {
|
fun StackFrameProxyImpl.safeVisibleVariables(): List<LocalVariableProxyImpl> {
|
||||||
return wrapAbsentInformationException { visibleVariables() } ?: emptyList()
|
return wrapAbsentInformationException { visibleVariables() } ?: emptyList()
|
||||||
@@ -21,6 +23,10 @@ fun StackFrameProxyImpl.safeVisibleVariableByName(name: String): LocalVariablePr
|
|||||||
return wrapAbsentInformationException { visibleVariableByName(name) }
|
return wrapAbsentInformationException { visibleVariableByName(name) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun StackFrame.safeVisibleVariables(): List<LocalVariable> {
|
||||||
|
return wrapAbsentInformationException { visibleVariables() } ?: emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
fun Method.safeAllLineLocations(): List<Location> {
|
fun Method.safeAllLineLocations(): List<Location> {
|
||||||
return DebuggerUtilsEx.allLineLocations(this) ?: emptyList()
|
return DebuggerUtilsEx.allLineLocations(this) ?: emptyList()
|
||||||
}
|
}
|
||||||
@@ -61,6 +67,14 @@ fun StackFrameProxy.safeLocation(): Location? {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun StackFrameProxy.safeStackFrame(): StackFrame? {
|
||||||
|
return try {
|
||||||
|
this.stackFrame
|
||||||
|
} catch (e: EvaluateException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun Location.safeSourceName(): String? {
|
fun Location.safeSourceName(): String? {
|
||||||
return wrapAbsentInformationException { this.sourceName() }
|
return wrapAbsentInformationException { this.sourceName() }
|
||||||
}
|
}
|
||||||
@@ -73,8 +87,23 @@ fun Location.safeLineNumber(): Int {
|
|||||||
return DebuggerUtilsEx.getLineNumber(this, false)
|
return DebuggerUtilsEx.getLineNumber(this, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Location.safeSourceLineNumber(): Int {
|
fun Location.safeLineNumber(stratum: String): Int {
|
||||||
return DebuggerUtilsEx.getLineNumber(this, true)
|
return try {
|
||||||
|
lineNumber(stratum)
|
||||||
|
} catch (e: InternalError) {
|
||||||
|
-1
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Location.safeKotlinPreferredLineNumber(): Int {
|
||||||
|
val kotlinLineNumber = safeLineNumber(KOTLIN_STRATA_NAME)
|
||||||
|
if (kotlinLineNumber > 0) {
|
||||||
|
return kotlinLineNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
return safeLineNumber(JAVA_STRATUM)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Location.safeMethod(): Method? {
|
fun Location.safeMethod(): Method? {
|
||||||
|
|||||||
Reference in New Issue
Block a user