diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt index e2210d8d9c9..b2f0f12b06d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt @@ -101,37 +101,40 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq throw NoDataException.INSTANCE } - val lineNumber = try { + val sourceLineNumber = try { location.lineNumber() - 1 } catch (e: InternalError) { -1 } - if (lineNumber < 0) { + if (sourceLineNumber < 0) { throw NoDataException.INSTANCE } - val lambdaOrFunIfInside = getLambdaOrFunIfInside(location, psiFile as KtFile, lineNumber) + val lambdaOrFunIfInside = getLambdaOrFunIfInside(location, psiFile as KtFile, sourceLineNumber) if (lambdaOrFunIfInside != null) { return SourcePosition.createFromElement(lambdaOrFunIfInside.bodyExpression!!) } - val elementInDeclaration = getElementForDeclarationLine(location, psiFile, lineNumber) + val elementInDeclaration = getElementForDeclarationLine(location, psiFile, sourceLineNumber) if (elementInDeclaration != null) { return SourcePosition.createFromElement(elementInDeclaration) } - if (lineNumber > psiFile.getLineCount() && myDebugProcess.isDexDebug()) { - val inlinePosition = getOriginalPositionOfInlinedLine( - location.lineNumber(), FqName(location.declaringType().name()), location.sourceName(), - myDebugProcess.project, GlobalSearchScope.allScope(myDebugProcess.project)) + if (sourceLineNumber > psiFile.getLineCount() && myDebugProcess.isDexDebug()) { + val thisFunLine = getLastLineNumberForLocation(location, myDebugProcess.project) + if (thisFunLine != null && thisFunLine != location.lineNumber()) { + return SourcePosition.createFromLine(psiFile, thisFunLine - 1) + } + + val inlinePosition = getOriginalPositionOfInlinedLine(location, myDebugProcess.project) if (inlinePosition != null) { return SourcePosition.createFromLine(inlinePosition.first, inlinePosition.second) } } - return SourcePosition.createFromLine(psiFile, lineNumber) + return SourcePosition.createFromLine(psiFile, sourceLineNumber) } // Returns a property or a constructor if debugger stops at class declaration diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/NoStrataPositionManagerHelper.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/NoStrataPositionManagerHelper.kt index 3a95fe476db..620e536e743 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/NoStrataPositionManagerHelper.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/NoStrataPositionManagerHelper.kt @@ -21,9 +21,11 @@ import com.intellij.debugger.engine.DebugProcess import com.intellij.debugger.jdi.VirtualMachineProxyImpl import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.search.GlobalSearchScope import com.sun.jdi.Location import com.sun.jdi.ReferenceType +import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil import org.jetbrains.kotlin.idea.refactoring.getLineStartOffset import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.lexer.KtTokens @@ -32,9 +34,84 @@ import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.psiUtil.parents import org.jetbrains.kotlin.resolve.jvm.JvmClassName +import org.jetbrains.kotlin.utils.getOrPutNullable +import org.jetbrains.org.objectweb.asm.* +import java.util.* -internal fun getOriginalPositionOfInlinedLine( - lineNumber: Int, fqName: FqName, fileName: String, project: Project, searchScope: GlobalSearchScope): Pair? { +// TODO: Don't read same bytecode file again and again +// TODO: Build line mapping for the whole file +// TODO: Quick caching for the same location + +fun noStrataLineNumber(location: Location, isDexDebug: Boolean, project: Project, preferInlined: Boolean = false): Int { + if (isDexDebug) { + if (!preferInlined) { + val thisFunLine = runReadAction { getLastLineNumberForLocation(location, project) } + if (thisFunLine != null && thisFunLine != location.lineNumber()) { + // TODO: bad line because of inlining + return thisFunLine + } + } + + val inlinePosition = runReadAction { getOriginalPositionOfInlinedLine(location, project) } + + if (inlinePosition != null) { + return inlinePosition.second + 1 + } + } + + return location.lineNumber() +} + +fun getLastLineNumberForLocation(location: Location, project: Project, searchScope: GlobalSearchScope = GlobalSearchScope.allScope(project)): Int? { + val lineNumber = location.lineNumber() + val fqName = FqName(location.declaringType().name()) + val fileName = location.sourceName() + + val method = location.method() ?: return null + + val bytes = findAndReadClassFile(fqName, fileName, project, searchScope, { isInlineFunctionLineNumber(it, lineNumber, project) }) ?: return null + + fun readLineNumberTableMapping(bytes: ByteArray): Map> { + val labelsToAllStrings = HashMap>() + + ClassReader(bytes).accept(object : ClassVisitor(InlineCodegenUtil.API) { + override fun visitMethod(access: Int, name: String?, desc: String?, signature: String?, exceptions: Array?): MethodVisitor? { + if (!(name == method.name() && desc == method.signature())) { + return null + } + + return object : MethodVisitor(Opcodes.ASM5, null) { + override fun visitLineNumber(line: Int, start: Label?) { + if (start != null) { + labelsToAllStrings.getOrPutNullable(start.toString(), { LinkedHashSet() }).add(line) + } + } + } + } + }, ClassReader.SKIP_FRAMES and ClassReader.SKIP_CODE) + + return labelsToAllStrings + } + + val lineMapping = readLineNumberTableMapping(bytes) + return lineMapping.values.firstOrNull { it.contains(lineNumber) }?.last() +} + +internal fun getOriginalPositionOfInlinedLine(location: Location, project: Project): Pair? { + val lineNumber = location.lineNumber() + val fqName = FqName(location.declaringType().name()) + val fileName = location.sourceName() + val searchScope = GlobalSearchScope.allScope(project) + + val bytes = findAndReadClassFile(fqName, fileName, project, searchScope, { isInlineFunctionLineNumber(it, lineNumber, project) }) ?: return null + val smapData = readDebugInfo(bytes) ?: return null + return mapStacktraceLineToSource(smapData, lineNumber, project, SourceLineKind.EXECUTED_LINE, searchScope) +} + +internal fun findAndReadClassFile( + fqName: FqName, fileName: String, project: Project, searchScope: GlobalSearchScope, + sourceFileFilter: (VirtualFile) -> Boolean = { true }, + libFileFilter: (VirtualFile) -> Boolean = { true }): ByteArray? { val internalName = fqName.asString().replace('.', '/') val jvmClassName = JvmClassName.byInternalName(internalName) @@ -42,9 +119,7 @@ internal fun getOriginalPositionOfInlinedLine( val virtualFile = file.virtualFile ?: return null - val bytes = readClassFile(project, jvmClassName, virtualFile, { isInlineFunctionLineNumber(it, lineNumber, project) }) ?: return null - val smapData = readDebugInfo(bytes) ?: return null - return mapStacktraceLineToSource(smapData, lineNumber, project, SourceLineKind.EXECUTED_LINE, searchScope) + return readClassFile(project, jvmClassName, virtualFile, sourceFileFilter = sourceFileFilter, libFileFilter = libFileFilter) } internal fun getLocationsOfInlinedLine(type: ReferenceType, position: SourcePosition, sourceSearchScope: GlobalSearchScope): List { diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/DebuggerSteppingHelper.java b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/DebuggerSteppingHelper.java index 5e9ce58d1a4..d711af2b12f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/DebuggerSteppingHelper.java +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/DebuggerSteppingHelper.java @@ -55,12 +55,7 @@ public class DebuggerSteppingHelper { return debugProcess.new ResumeCommand(suspendContext) { @Override public void contextAction() { - // In DEX there's no strata for trying to understand what lines were inlined and no local variables attributes are - // preserved that can be used for fine tuning. So do an ordinal 'step over' instead. - if (NoStrataPositionManagerHelperKt.isDexDebug(suspendContext.getDebugProcess())) { - debugProcess.createStepOverCommand(suspendContext, true).contextAction(); - return; - } + boolean isDexDebug = NoStrataPositionManagerHelperKt.isDexDebug(suspendContext.getDebugProcess()); try { StackFrameProxyImpl frameProxy = suspendContext.getFrameProxy(); @@ -68,7 +63,8 @@ public class DebuggerSteppingHelper { Action action = KotlinSteppingCommandProviderKt.getStepOverAction( frameProxy.location(), kotlinSourcePosition, - frameProxy + frameProxy, + isDexDebug ); createStepRequest( diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinStepOverInlineFilter.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinStepOverInlineFilter.kt index 6ee092de721..e116e6b04d6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinStepOverInlineFilter.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinStepOverInlineFilter.kt @@ -18,19 +18,24 @@ package org.jetbrains.kotlin.idea.debugger.stepping import com.intellij.debugger.engine.DebugProcessImpl import com.intellij.debugger.engine.SuspendContextImpl +import com.intellij.openapi.project.Project import com.intellij.util.Range import com.sun.jdi.LocalVariable import com.sun.jdi.Location +import org.jetbrains.kotlin.idea.debugger.noStrataLineNumber + +class KotlinStepOverInlineFilter( + val isDexDebug: Boolean, + val project: Project, + val stepOverLines: Set, val fromLine: Int, + val inlineFunRangeVariables: List) : KotlinMethodFilter { + private fun Location.ktLineNumber() = noStrataLineNumber(this, isDexDebug, project) -class KotlinStepOverInlineFilter(val stepOverLines: Set, val fromLine: Int, - val inlineFunRangeVariables: List) : KotlinMethodFilter { override fun locationMatches(context: SuspendContextImpl, location: Location): Boolean { - val frameProxy = context.frameProxy - if (frameProxy == null) return true + val frameProxy = context.frameProxy ?: return true - val currentLine = location.lineNumber() - - if (!(stepOverLines.contains(location.lineNumber()))) { + val currentLine = location.ktLineNumber() + if (!(stepOverLines.contains(currentLine))) { return currentLine != fromLine } diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt index 6348630bdb6..4d156358162 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt @@ -40,6 +40,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils import org.jetbrains.kotlin.idea.debugger.DebuggerUtils +import org.jetbrains.kotlin.idea.debugger.noStrataLineNumber import org.jetbrains.kotlin.idea.refactoring.getLineEndOffset import org.jetbrains.kotlin.idea.refactoring.getLineNumber import org.jetbrains.kotlin.idea.refactoring.getLineStartOffset @@ -268,12 +269,13 @@ private fun findCallsOnPosition(sourcePosition: SourcePosition, filter: (KtCallE sealed class Action(val position: XSourcePositionImpl? = null, val lineNumber: Int? = null, val stepOverLines: Set? = null, - val inlineRangeVariables: List? = null) { + val inlineRangeVariables: List? = null, + val isDexDebug: Boolean = false) { class STEP_OVER : Action() class STEP_OUT : Action() class RUN_TO_CURSOR(position: XSourcePositionImpl) : Action(position) - class STEP_OVER_INLINED(lineNumber: Int, stepOverLines: Set, inlineVariables: List) : Action( - lineNumber = lineNumber, stepOverLines = stepOverLines, inlineRangeVariables = inlineVariables) + class STEP_OVER_INLINED(lineNumber: Int, stepOverLines: Set, inlineVariables: List, isDexDebug: Boolean) : Action( + lineNumber = lineNumber, stepOverLines = stepOverLines, inlineRangeVariables = inlineVariables, isDexDebug = isDexDebug) fun apply(debugProcess: DebugProcessImpl, suspendContext: SuspendContextImpl, @@ -287,26 +289,32 @@ sealed class Action(val position: XSourcePositionImpl? = null, 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) + KotlinStepOverInlineFilter( + isDexDebug, + debugProcess.project, + stepOverLines!!, + lineNumber ?: -1, + inlineRangeVariables!!)).contextAction(suspendContext) } } } -interface KotlinMethodFilter: MethodFilter { +interface KotlinMethodFilter : MethodFilter { fun locationMatches(context: SuspendContextImpl, location: Location): Boolean } fun getStepOverAction( location: Location, kotlinSourcePosition: KotlinSteppingCommandProvider.KotlinSourcePosition, - frameProxy: StackFrameProxyImpl + frameProxy: StackFrameProxyImpl, + isDexDebug: Boolean ): Action { val inlineArgumentsToSkip = runReadAction { getInlineCallFunctionArgumentsIfAny(kotlinSourcePosition.sourcePosition) } return getStepOverAction(location, kotlinSourcePosition.file, kotlinSourcePosition.linesRange, - inlineArgumentsToSkip, frameProxy) + inlineArgumentsToSkip, frameProxy, isDexDebug) } fun getStepOverAction( @@ -314,12 +322,21 @@ fun getStepOverAction( file: KtFile, range: IntRange, inlineFunctionArguments: List, - frameProxy: StackFrameProxyImpl + frameProxy: StackFrameProxyImpl, + isDexDebug: Boolean ): Action { val computedReferenceType = location.declaringType() ?: return Action.STEP_OVER() + val project = file.project + + fun Location.ktLineNumber() = noStrataLineNumber(this, isDexDebug, project, true) + fun isLocationSuitable(nextLocation: Location): Boolean { - if (nextLocation.method() != location.method() || nextLocation.lineNumber() !in range) { + if (nextLocation.method() != location.method()) { + return false + } + + if (nextLocation.ktLineNumber() !in range) { return false } @@ -336,22 +353,24 @@ fun getStepOverAction( .dropWhile { it != location } .drop(1) .filter(::isLocationSuitable) - .dropWhile { it.lineNumber() == location.lineNumber() } + .dropWhile { it.ktLineNumber() == location.ktLineNumber() } .firstOrNull() - return previousSuitableLocation != null && previousSuitableLocation.lineNumber() > location.lineNumber() + return previousSuitableLocation != null && previousSuitableLocation.ktLineNumber() > location.ktLineNumber() } val patchedLocation = if (isBackEdgeLocation()) { // Pretend we had already did a backing step computedReferenceType.allLineLocations() .filter(::isLocationSuitable) - .first { it.lineNumber() == location.lineNumber() } + .first { it.ktLineNumber() == location.ktLineNumber() } } else { location } + val patchedLineNumber = patchedLocation.ktLineNumber() + val lambdaArgumentRanges = runReadAction { inlineFunctionArguments.filterIsInstance().map { val startLineNumber = it.getLineNumber(true) + 1 @@ -367,21 +386,24 @@ fun getStepOverAction( // - 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) - .dropWhile { it.lineNumber() == patchedLocation.lineNumber() } - .takeWhile { locationAtLine -> - !isLocationSuitable(locationAtLine) || lambdaArgumentRanges.any { locationAtLine.lineNumber() in it } + .dropWhile { it.ktLineNumber() == patchedLineNumber } + .takeWhile { location -> + !isLocationSuitable(location) || lambdaArgumentRanges.any { location.ktLineNumber() in it } } - .dropWhile { it.lineNumber() == patchedLocation.lineNumber() } + .dropWhile { it.ktLineNumber() == patchedLineNumber } if (!probablyInlinedLocations.isEmpty()) { - return Action.STEP_OVER_INLINED(patchedLocation.lineNumber(), probablyInlinedLocations.map { it.lineNumber() }.toSet(), inlineRangeVariables) + return Action.STEP_OVER_INLINED( + patchedLineNumber, + probablyInlinedLocations.map { it.ktLineNumber() }.toSet(), + inlineRangeVariables, + isDexDebug + ) } return Action.STEP_OVER() diff --git a/idea/testData/debugger/tinyApp/outs/inlineInIfFalseDex.out b/idea/testData/debugger/tinyApp/outs/inlineInIfFalseDex.out index d5573edbe46..8600a9e8d36 100644 --- a/idea/testData/debugger/tinyApp/outs/inlineInIfFalseDex.out +++ b/idea/testData/debugger/tinyApp/outs/inlineInIfFalseDex.out @@ -2,7 +2,6 @@ LineBreakpoint created at inlineInIfFalseDex.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! inlineInIfFalseDex.InlineInIfFalseDexKt Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' inlineInIfFalseDex.kt:6 -inlineInIfFalseDex.kt:15 inlineInIfFalseDex.kt:9 inlineInIfFalseDex.kt:10 Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' diff --git a/idea/testData/debugger/tinyApp/outs/inlineInIfTrueDex.out b/idea/testData/debugger/tinyApp/outs/inlineInIfTrueDex.out index 818f260cb55..8e53db9c3dc 100644 --- a/idea/testData/debugger/tinyApp/outs/inlineInIfTrueDex.out +++ b/idea/testData/debugger/tinyApp/outs/inlineInIfTrueDex.out @@ -2,7 +2,6 @@ LineBreakpoint created at inlineInIfTrueDex.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! inlineInIfTrueDex.InlineInIfTrueDexKt Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' inlineInIfTrueDex.kt:6 -inlineInIfTrueDex.kt:14 inlineInIfTrueDex.kt:7 inlineInIfTrueDex.kt:9 Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' diff --git a/idea/testData/debugger/tinyApp/outs/soInlineAnonymousFunctionArgumentDex.out b/idea/testData/debugger/tinyApp/outs/soInlineAnonymousFunctionArgumentDex.out index 1e79e42ce9b..ee0367ec442 100644 --- a/idea/testData/debugger/tinyApp/outs/soInlineAnonymousFunctionArgumentDex.out +++ b/idea/testData/debugger/tinyApp/outs/soInlineAnonymousFunctionArgumentDex.out @@ -3,12 +3,8 @@ LineBreakpoint created at soInlineAnonymousFunctionArgumentDex.kt:5 Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' soInlineAnonymousFunctionArgumentDex.kt:5 soInlineAnonymousFunctionArgumentDex.kt:7 -soInlineAnonymousFunctionArgumentDex.kt:15 -soInlineAnonymousFunctionArgumentDex.kt:8 -soInlineAnonymousFunctionArgumentDex.kt:16 -soInlineAnonymousFunctionArgumentDex.kt:15 soInlineAnonymousFunctionArgumentDex.kt:11 -soInlineAnonymousFunctionArgumentDex.kt:16 +soInlineAnonymousFunctionArgumentDex.kt:12 Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/outs/soInlineCallInLastStatementInInlineDex.out b/idea/testData/debugger/tinyApp/outs/soInlineCallInLastStatementInInlineDex.out index 2172f82ed8f..0038d30c213 100644 --- a/idea/testData/debugger/tinyApp/outs/soInlineCallInLastStatementInInlineDex.out +++ b/idea/testData/debugger/tinyApp/outs/soInlineCallInLastStatementInInlineDex.out @@ -3,9 +3,7 @@ LineBreakpoint created at soInlineCallInLastStatementInInlineDex.kt:10 Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' soInlineCallInLastStatementInInlineDex.kt:10 soInlineCallInLastStatementInInlineDex.kt:11 -soInlineCallInLastStatementInInlineDex.kt:15 -soInlineCallInLastStatementInInlineDex.kt:11 -soInlineCallInLastStatementInInlineDex.kt:16 +soInlineCallInLastStatementInInlineDex.kt:5 soInlineCallInLastStatementInInlineDex.kt:6 Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' diff --git a/idea/testData/debugger/tinyApp/outs/soInlineCallInLastStatementInInlineFunctionArgumentDex.out b/idea/testData/debugger/tinyApp/outs/soInlineCallInLastStatementInInlineFunctionArgumentDex.out index a2244b58f55..7fbd0d59c0e 100644 --- a/idea/testData/debugger/tinyApp/outs/soInlineCallInLastStatementInInlineFunctionArgumentDex.out +++ b/idea/testData/debugger/tinyApp/outs/soInlineCallInLastStatementInInlineFunctionArgumentDex.out @@ -2,9 +2,8 @@ LineBreakpoint created at soInlineCallInLastStatementInInlineFunctionArgumentDex !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! soInlineCallInLastStatementInInlineFunctionArgumentDex.SoInlineCallInLastStatementInInlineFunctionArgumentDexKt Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' soInlineCallInLastStatementInInlineFunctionArgumentDex.kt:7 -soInlineCallInLastStatementInInlineFunctionArgumentDex.kt:17 -soInlineCallInLastStatementInInlineFunctionArgumentDex.kt:18 -soInlineCallInLastStatementInInlineFunctionArgumentDex.kt:14 +soInlineCallInLastStatementInInlineFunctionArgumentDex.kt:8 +soInlineCallInLastStatementInInlineFunctionArgumentDex.kt:9 Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/outs/soInlineFunDex.out b/idea/testData/debugger/tinyApp/outs/soInlineFunDex.out new file mode 100644 index 00000000000..a82bb12cad0 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/soInlineFunDex.out @@ -0,0 +1,10 @@ +LineBreakpoint created at soInlineFunDex.kt:7 +!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! soInlineFunDex.SoInlineFunDexKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +soInlineFunDex.kt:7 +soInlineFunDex.kt:9 +soInlineFunDex.kt:11 +soInlineFunDex.kt:12 +Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' + +Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/outs/soInlineFunOnOneLineFor.out b/idea/testData/debugger/tinyApp/outs/soInlineFunOnOneLineFor.out new file mode 100644 index 00000000000..45170392c3d --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/soInlineFunOnOneLineFor.out @@ -0,0 +1,10 @@ +LineBreakpoint created at soInlineFunOnOneLineFor.kt:17 +!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! soInlineFunOnOneLineFor.SoInlineFunOnOneLineForKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +soInlineFunOnOneLineFor.kt:17 +soInlineFunOnOneLineFor.kt:18 +soInlineFunOnOneLineFor.kt:8 +soInlineFunOnOneLineFor.kt:9 +Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' + +Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/outs/soInlineFunOnOneLineForDex.out b/idea/testData/debugger/tinyApp/outs/soInlineFunOnOneLineForDex.out new file mode 100644 index 00000000000..d02523426c6 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/soInlineFunOnOneLineForDex.out @@ -0,0 +1,10 @@ +LineBreakpoint created at soInlineFunOnOneLineForDex.kt:17 +!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! soInlineFunOnOneLineForDex.SoInlineFunOnOneLineForDexKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +soInlineFunOnOneLineForDex.kt:17 +soInlineFunOnOneLineForDex.kt:18 +soInlineFunOnOneLineForDex.kt:8 +soInlineFunOnOneLineForDex.kt:9 +Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' + +Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/outs/soInlineFunWithFor.out b/idea/testData/debugger/tinyApp/outs/soInlineFunWithFor.out new file mode 100644 index 00000000000..f8952b6b747 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/soInlineFunWithFor.out @@ -0,0 +1,10 @@ +LineBreakpoint created at soInlineFunWithFor.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! soInlineFunWithFor.SoInlineFunWithForKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +soInlineFunWithFor.kt:5 +soInlineFunWithFor.kt:7 +soInlineFunWithFor.kt:9 +soInlineFunWithFor.kt:10 +Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' + +Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/outs/soInlineIterableFunDex.out b/idea/testData/debugger/tinyApp/outs/soInlineIterableFunDex.out new file mode 100644 index 00000000000..5701ae61c29 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/soInlineIterableFunDex.out @@ -0,0 +1,10 @@ +LineBreakpoint created at soInlineIterableFunDex.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! soInlineIterableFunDex.SoInlineIterableFunDexKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +soInlineIterableFunDex.kt:5 +soInlineIterableFunDex.kt:7 +soInlineIterableFunDex.kt:9 +soInlineIterableFunDex.kt:10 +Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' + +Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/outs/soInlineLibFunDex.out b/idea/testData/debugger/tinyApp/outs/soInlineLibFunDex.out new file mode 100644 index 00000000000..7fb6456a7a1 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/soInlineLibFunDex.out @@ -0,0 +1,11 @@ +LineBreakpoint created at soInlineLibFunDex.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! soInlineLibFunDex.SoInlineLibFunDexKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +soInlineLibFunDex.kt:5 +soInlineLibFunDex.kt:7 +soInlineLibFunDex.kt:9 +soInlineLibFunDex.kt:10 +soInlineLibFunDex.kt:12 +Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' + +Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/outs/soInlineWhileConditionDex.out b/idea/testData/debugger/tinyApp/outs/soInlineWhileConditionDex.out index 677faa4083c..ff17de57eb6 100644 --- a/idea/testData/debugger/tinyApp/outs/soInlineWhileConditionDex.out +++ b/idea/testData/debugger/tinyApp/outs/soInlineWhileConditionDex.out @@ -3,14 +3,8 @@ LineBreakpoint created at soInlineWhileConditionDex.kt:5 Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' soInlineWhileConditionDex.kt:5 soInlineWhileConditionDex.kt:7 -soInlineWhileConditionDex.kt:18 -soInlineWhileConditionDex.kt:7 soInlineWhileConditionDex.kt:8 soInlineWhileConditionDex.kt:7 -soInlineWhileConditionDex.kt:18 -soInlineWhileConditionDex.kt:7 -soInlineWhileConditionDex.kt:12 -soInlineWhileConditionDex.kt:18 soInlineWhileConditionDex.kt:12 soInlineWhileConditionDex.kt:15 Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/inlineInIfTrueDex.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/inlineInIfTrueDex.kt index 0b6e6cd3367..1bdbb579941 100644 --- a/idea/testData/debugger/tinyApp/src/stepping/stepOver/inlineInIfTrueDex.kt +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/inlineInIfTrueDex.kt @@ -3,10 +3,10 @@ package inlineInIfTrueDex fun main(args: Array) { val bar = "" //Breakpoint! - if (inlineCall { true }) { - foo() + if (inlineCall { true }) { // 1 + foo() // 2 } -} +} // 3 fun foo() {} diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineAnonymousFunctionArgumentDex.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineAnonymousFunctionArgumentDex.kt index db8993d4365..f1c59e07941 100644 --- a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineAnonymousFunctionArgumentDex.kt +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineAnonymousFunctionArgumentDex.kt @@ -2,14 +2,14 @@ package soInlineAnonymousFunctionArgumentDex fun main(args: Array) { //Breakpoint! - val b = 1 + val b = 1 // 1 - foo( - fun (){ test(1) } + foo( // 2 + fun (){ test(1) } // ) - foo(fun (){ test(1) }) -} + foo(fun (){ test(1) }) // 3 +} // 4 inline fun foo(f: () -> Unit) { f() diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineCallInLastStatementInInlineDex.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineCallInLastStatementInInlineDex.kt index b5e76807100..1d87ea567e2 100644 --- a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineCallInLastStatementInInlineDex.kt +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineCallInLastStatementInInlineDex.kt @@ -2,13 +2,13 @@ package soInlineCallInLastStatementInInlineDex fun main(args: Array) { bar() - nop() -} + nop() // 3 +} // 4 inline fun bar() { //Breakpoint! - nop() - foo { 42 } + nop() // 1 + foo { 42 } // 2 } inline fun foo(f: () -> Unit) { diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunDex.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunDex.kt new file mode 100644 index 00000000000..c2d28f9671b --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunDex.kt @@ -0,0 +1,27 @@ +package soInlineFunDex + +fun main(args: Array) { + val a = 1 + + //Breakpoint! + simple() // 1 + + withParam(1 + a) // 2 + + withLambda { "hi" } // 3 +} // 4 + +inline fun simple() { + foo() +} + +inline fun withParam(i: Int) { +} + +inline fun withLambda(a: () -> Unit) { + a() +} + +fun foo() {} + +// STEP_OVER: 6 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunOnOneLineFor.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunOnOneLineFor.kt new file mode 100644 index 00000000000..554908540d2 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunOnOneLineFor.kt @@ -0,0 +1,21 @@ +package soInlineFunOnOneLineFor + +fun main(args: Array) { + val list = listOf(1, 2, 3) + + list.any1 { it > 2 } + + foo() +} + +fun foo() { + +} + +inline fun Iterable.any1(predicate: (T) -> Boolean): Boolean { + //Breakpoint! + for (element in this) if (predicate(element)) return true + return false +} + +// STEP_OVER: 3 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunOnOneLineForDex.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunOnOneLineForDex.kt new file mode 100644 index 00000000000..232bad5adc8 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunOnOneLineForDex.kt @@ -0,0 +1,21 @@ +package soInlineFunOnOneLineForDex + +fun main(args: Array) { + val list = listOf(1, 2, 3) + + list.any1 { it > 2 } + + foo() +} + +fun foo() { + +} + +inline fun Iterable.any1(predicate: (T) -> Boolean): Boolean { + //Breakpoint! + for (element in this) if (predicate(element)) return true + return false +} + +// STEP_OVER: 3 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunWithFor.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunWithFor.kt new file mode 100644 index 00000000000..d10fdff4653 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunWithFor.kt @@ -0,0 +1,22 @@ +package soInlineFunWithFor + +fun main(args: Array) { + //Breakpoint! + foo() + + val r = any1 { it > 2 } + + foo() +} + +fun foo() {} + +inline fun any1(predicate: (Int) -> Boolean) { + for (i in 1..2) { + if (predicate(i)) { + return + } + } +} + +// STEP_OVER: 5 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineIterableFunDex.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineIterableFunDex.kt new file mode 100644 index 00000000000..04622b23bb5 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineIterableFunDex.kt @@ -0,0 +1,21 @@ +package soInlineIterableFunDex + +fun main(args: Array) { + //Breakpoint! + val list = listOf(1, 2, 3) + + list.any1 { it > 2 } + + foo() +} + +fun foo() { + +} + +inline fun Iterable.any1(predicate: (T) -> Boolean): Boolean { + for (element in this) if (predicate(element)) return true + return false +} + +// STEP_OVER: 5 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineLibFunDex.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineLibFunDex.kt new file mode 100644 index 00000000000..06899b29402 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineLibFunDex.kt @@ -0,0 +1,18 @@ +package soInlineLibFunDex + +fun main(args: Array) { + //Breakpoint! + var i = 1 + + listOf(1, 2, 3).any { it > 2 } // Step over goes into 'any' (regression) + + if (listOf(1, 2, 3).any { it > 2 }) { // Step over goes into 'any' + i++ + } +} + +inline fun test(a: () -> Unit) { + a() +} + +// STEP_OVER: 7 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineWhileCondition.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineWhileCondition.kt index a60e036241d..ad90b5b241d 100644 --- a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineWhileCondition.kt +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineWhileCondition.kt @@ -9,7 +9,7 @@ fun main(args: Array) { } // inline in while condition (false) - while (id { false }) { // 5 + while (id { false }) { // 5 bar() } } // 6 diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java index 408710fcd41..e04a7270b95 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java @@ -518,6 +518,30 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest { doStepOverTest(fileName); } + @TestMetadata("soInlineFunDex.kt") + public void testSoInlineFunDex() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunDex.kt"); + doStepOverTest(fileName); + } + + @TestMetadata("soInlineFunOnOneLineFor.kt") + public void testSoInlineFunOnOneLineFor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunOnOneLineFor.kt"); + doStepOverTest(fileName); + } + + @TestMetadata("soInlineFunOnOneLineForDex.kt") + public void testSoInlineFunOnOneLineForDex() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunOnOneLineForDex.kt"); + doStepOverTest(fileName); + } + + @TestMetadata("soInlineFunWithFor.kt") + public void testSoInlineFunWithFor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunWithFor.kt"); + doStepOverTest(fileName); + } + @TestMetadata("soInlineFunWithLastStatementMultilineArgumentCall.kt") public void testSoInlineFunWithLastStatementMultilineArgumentCall() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineFunWithLastStatementMultilineArgumentCall.kt"); @@ -542,6 +566,18 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest { doStepOverTest(fileName); } + @TestMetadata("soInlineIterableFunDex.kt") + public void testSoInlineIterableFunDex() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineIterableFunDex.kt"); + doStepOverTest(fileName); + } + + @TestMetadata("soInlineLibFunDex.kt") + public void testSoInlineLibFunDex() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineLibFunDex.kt"); + doStepOverTest(fileName); + } + @TestMetadata("soInlineOperatorIterator.kt") public void testSoInlineOperatorIterator() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineOperatorIterator.kt");