Fix irrelevant additional stops on breakpoint on line with inlines (KT-21945)
#KT-21945 Fixed
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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
|
||||
|
||||
import com.intellij.debugger.SourcePosition
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
|
||||
abstract class DelegateSourcePosition(private var delegate: SourcePosition) : SourcePosition() {
|
||||
override fun getFile(): PsiFile = delegate.file
|
||||
override fun getElementAt(): PsiElement = delegate.elementAt
|
||||
override fun getLine(): Int = delegate.line
|
||||
override fun getOffset(): Int = delegate.offset
|
||||
|
||||
override fun openEditor(requestFocus: Boolean): Editor = delegate.openEditor(requestFocus)
|
||||
|
||||
override fun canNavigate() = delegate.canNavigate()
|
||||
override fun canNavigateToSource() = delegate.canNavigateToSource()
|
||||
|
||||
override fun navigate(requestFocus: Boolean) {
|
||||
delegate.navigate(requestFocus)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = delegate.hashCode()
|
||||
override fun equals(other: Any?) = delegate == other
|
||||
|
||||
override fun toString() = "DSP($delegate)"
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import com.intellij.debugger.engine.DebugProcess
|
||||
import com.intellij.debugger.engine.DebugProcessImpl
|
||||
import com.intellij.debugger.engine.PositionManagerEx
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContext
|
||||
import com.intellij.debugger.impl.DebuggerUtilsEx
|
||||
import com.intellij.debugger.jdi.StackFrameProxyImpl
|
||||
import com.intellij.debugger.requests.ClassPrepareRequestor
|
||||
import com.intellij.psi.PsiFile
|
||||
@@ -32,6 +33,7 @@ import com.intellij.util.ThreeState
|
||||
import com.intellij.xdebugger.frame.XStackFrame
|
||||
import com.sun.jdi.AbsentInformationException
|
||||
import com.sun.jdi.Location
|
||||
import com.sun.jdi.Method
|
||||
import com.sun.jdi.ReferenceType
|
||||
import com.sun.jdi.request.ClassPrepareRequest
|
||||
import org.jetbrains.kotlin.codegen.inline.KOTLIN_STRATA_NAME
|
||||
@@ -78,6 +80,11 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
|
||||
if (location == null) throw NoDataException.INSTANCE
|
||||
|
||||
val fileName = location.safeSourceName ?: throw NoDataException.INSTANCE
|
||||
val lineNumber = location.safeLineNumber
|
||||
if (lineNumber < 0) {
|
||||
throw NoDataException.INSTANCE
|
||||
}
|
||||
|
||||
if (!DebuggerUtils.isKotlinSourceFile(fileName)) throw NoDataException.INSTANCE
|
||||
|
||||
val psiFile = getPsiFileByLocation(location)
|
||||
@@ -102,21 +109,18 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
|
||||
throw NoDataException.INSTANCE
|
||||
}
|
||||
|
||||
val sourceLineNumber = try {
|
||||
location.lineNumber() - 1
|
||||
}
|
||||
catch (e: InternalError) {
|
||||
-1
|
||||
}
|
||||
if (psiFile !is KtFile) throw NoDataException.INSTANCE
|
||||
|
||||
val sourceLineNumber = location.safeSourceLineNumber
|
||||
if (sourceLineNumber < 0) {
|
||||
throw NoDataException.INSTANCE
|
||||
}
|
||||
|
||||
val lambdaOrFunIfInside = getLambdaOrFunIfInside(location, psiFile as KtFile, sourceLineNumber)
|
||||
val lambdaOrFunIfInside = getLambdaOrFunIfInside(location, psiFile, sourceLineNumber)
|
||||
if (lambdaOrFunIfInside != null) {
|
||||
return SourcePosition.createFromElement(lambdaOrFunIfInside.bodyExpression!!)
|
||||
}
|
||||
|
||||
val elementInDeclaration = getElementForDeclarationLine(location, psiFile, sourceLineNumber)
|
||||
if (elementInDeclaration != null) {
|
||||
return SourcePosition.createFromElement(elementInDeclaration)
|
||||
@@ -127,9 +131,23 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
|
||||
return SourcePosition.createFromLine(ktFile ?: psiFile, line - 1)
|
||||
}
|
||||
|
||||
val sameLineLocations = location.safeMethod?.safeAllLineLocations?.filter { it.safeLineNumber == lineNumber && it.safeSourceName == fileName }
|
||||
if (sameLineLocations != null) {
|
||||
// There're several locations for same source line. If same source position would be created for all of them,
|
||||
// breakpoints at this line will stop on every location.
|
||||
// Each location is probably some code in arguments between inlined invocations (otherwise same line locations would
|
||||
// have been merged into one), but it's impossible to correctly map locations to actual source expressions now.
|
||||
val locationIndex = sameLineLocations.indexOf(location)
|
||||
if (locationIndex > 0) {
|
||||
return KotlinReentrantSourcePosition(SourcePosition.createFromLine(psiFile, sourceLineNumber))
|
||||
}
|
||||
}
|
||||
|
||||
return SourcePosition.createFromLine(psiFile, sourceLineNumber)
|
||||
}
|
||||
|
||||
class KotlinReentrantSourcePosition(delegate: SourcePosition) : DelegateSourcePosition(delegate)
|
||||
|
||||
// Returns a property or a constructor if debugger stops at class declaration
|
||||
private fun getElementForDeclarationLine(location: Location, file: KtFile, lineNumber: Int): KtElement? {
|
||||
val lineStartOffset = file.getLineStartOffset(lineNumber) ?: return null
|
||||
@@ -199,6 +217,11 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
|
||||
}
|
||||
}
|
||||
|
||||
private val Location.safeLineNumber: Int get() = DebuggerUtilsEx.getLineNumber(this, false)
|
||||
private val Location.safeSourceLineNumber: Int get() = DebuggerUtilsEx.getLineNumber(this, true)
|
||||
private val Location.safeMethod: Method? get() = DebuggerUtilsEx.getMethod(this)
|
||||
private val Method.safeAllLineLocations: MutableList<Location>? get() = DebuggerUtilsEx.allLineLocations(this)
|
||||
|
||||
private fun getPsiFileByLocation(location: Location): PsiFile? {
|
||||
val sourceName = location.safeSourceName ?: return null
|
||||
|
||||
|
||||
+6
-1
@@ -36,6 +36,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.java.debugger.breakpoints.properties.JavaBreakpointProperties;
|
||||
import org.jetbrains.java.debugger.breakpoints.properties.JavaLineBreakpointProperties;
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons;
|
||||
import org.jetbrains.kotlin.idea.debugger.KotlinPositionManager;
|
||||
import org.jetbrains.kotlin.psi.KtClassInitializer;
|
||||
import org.jetbrains.kotlin.psi.KtFunction;
|
||||
|
||||
@@ -51,7 +52,11 @@ public class KotlinLineBreakpointType extends JavaLineBreakpointType {
|
||||
public boolean matchesPosition(@NotNull LineBreakpoint<?> breakpoint, @NotNull SourcePosition position) {
|
||||
JavaBreakpointProperties properties = getProperties(breakpoint);
|
||||
if (properties == null || properties instanceof JavaLineBreakpointProperties) {
|
||||
if (properties != null && ((JavaLineBreakpointProperties)properties).getLambdaOrdinal() == null) return true;
|
||||
if (position instanceof KotlinPositionManager.KotlinReentrantSourcePosition) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (properties != null && ((JavaLineBreakpointProperties) properties).getLambdaOrdinal() == null) return true;
|
||||
|
||||
PsiElement containingMethod = getContainingMethod(breakpoint);
|
||||
if (containingMethod == null) return false;
|
||||
|
||||
Vendored
-6
@@ -29,9 +29,6 @@ fun main(args: Array<String>) {
|
||||
//Breakpoint!
|
||||
a.foo(1) { 1 }.foo(2) { 1 }
|
||||
|
||||
// EXPRESSION: it + 7
|
||||
// RESULT: Unresolved reference: it
|
||||
|
||||
// EXPRESSION: it + 8
|
||||
// RESULT: Unresolved reference: it
|
||||
//Breakpoint! (lambdaOrdinal = -1)
|
||||
@@ -53,9 +50,6 @@ fun main(args: Array<String>) {
|
||||
// EXPRESSION: it + 12
|
||||
// RESULT: 13: I
|
||||
|
||||
// EXPRESSION: it + 13
|
||||
// RESULT: Unresolved reference: it
|
||||
|
||||
// EXPRESSION: it + 14
|
||||
// RESULT: 16: I
|
||||
//Breakpoint!
|
||||
|
||||
Vendored
+12
-14
@@ -2,11 +2,11 @@ LineBreakpoint created at multipleBreakpointsAtLine.kt:9 lambdaOrdinal = -1
|
||||
LineBreakpoint created at multipleBreakpointsAtLine.kt:14 lambdaOrdinal = 1
|
||||
LineBreakpoint created at multipleBreakpointsAtLine.kt:19 lambdaOrdinal = 2
|
||||
LineBreakpoint created at multipleBreakpointsAtLine.kt:30
|
||||
LineBreakpoint created at multipleBreakpointsAtLine.kt:38 lambdaOrdinal = -1
|
||||
LineBreakpoint created at multipleBreakpointsAtLine.kt:43 lambdaOrdinal = 1
|
||||
LineBreakpoint created at multipleBreakpointsAtLine.kt:48 lambdaOrdinal = 2
|
||||
LineBreakpoint created at multipleBreakpointsAtLine.kt:62
|
||||
LineBreakpoint created at multipleBreakpointsAtLine.kt:67 lambdaOrdinal = 2
|
||||
LineBreakpoint created at multipleBreakpointsAtLine.kt:35 lambdaOrdinal = -1
|
||||
LineBreakpoint created at multipleBreakpointsAtLine.kt:40 lambdaOrdinal = 1
|
||||
LineBreakpoint created at multipleBreakpointsAtLine.kt:45 lambdaOrdinal = 2
|
||||
LineBreakpoint created at multipleBreakpointsAtLine.kt:56
|
||||
LineBreakpoint created at multipleBreakpointsAtLine.kt:61 lambdaOrdinal = 2
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
multipleBreakpointsAtLine.kt:9
|
||||
@@ -19,19 +19,17 @@ multipleBreakpointsAtLine.kt:30
|
||||
Compile bytecode for it + 5
|
||||
multipleBreakpointsAtLine.kt:30
|
||||
Compile bytecode for it + 6
|
||||
multipleBreakpointsAtLine.kt:38
|
||||
multipleBreakpointsAtLine.kt:38
|
||||
multipleBreakpointsAtLine.kt:43
|
||||
multipleBreakpointsAtLine.kt:35
|
||||
multipleBreakpointsAtLine.kt:40
|
||||
Compile bytecode for it + 9
|
||||
multipleBreakpointsAtLine.kt:48
|
||||
multipleBreakpointsAtLine.kt:45
|
||||
Compile bytecode for it + 10
|
||||
multipleBreakpointsAtLine.kt:62
|
||||
multipleBreakpointsAtLine.kt:62
|
||||
multipleBreakpointsAtLine.kt:56
|
||||
multipleBreakpointsAtLine.kt:56
|
||||
Compile bytecode for it + 12
|
||||
multipleBreakpointsAtLine.kt:62
|
||||
multipleBreakpointsAtLine.kt:62
|
||||
multipleBreakpointsAtLine.kt:56
|
||||
Compile bytecode for it + 14
|
||||
multipleBreakpointsAtLine.kt:67
|
||||
multipleBreakpointsAtLine.kt:61
|
||||
Compile bytecode for it + 15
|
||||
Disconnected from the target VM
|
||||
|
||||
|
||||
Vendored
-1
@@ -3,7 +3,6 @@ package inlineFunctionBreakpointVariants
|
||||
import inlineFunctionOtherPackage.*
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
// Breakpoint is outside of function literal, but we stop at it twice: before and after foo1 invocation
|
||||
//Breakpoint! (lambdaOrdinal = -1)
|
||||
foo1 { foo2() }
|
||||
}
|
||||
|
||||
Vendored
+2
-3
@@ -1,8 +1,7 @@
|
||||
LineBreakpoint created at inlineFunctionBreakpointVariants.kt:8 lambdaOrdinal = -1
|
||||
LineBreakpoint created at inlineFunctionBreakpointVariants.kt:7 lambdaOrdinal = -1
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
inlineFunctionBreakpointVariants.kt:8
|
||||
inlineFunctionBreakpointVariants.kt:8
|
||||
inlineFunctionBreakpointVariants.kt:7
|
||||
Disconnected from the target VM
|
||||
|
||||
Process finished with exit code 0
|
||||
|
||||
Vendored
-2
@@ -4,8 +4,6 @@ Connected to the target VM
|
||||
parametersOfInlineFunSeveralOnLine.kt:8
|
||||
parametersOfInlineFunSeveralOnLine.kt:8
|
||||
parametersOfInlineFunSeveralOnLine.kt:8
|
||||
parametersOfInlineFunSeveralOnLine.kt:8
|
||||
Compile bytecode for it
|
||||
Disconnected from the target VM
|
||||
|
||||
Process finished with exit code 0
|
||||
|
||||
@@ -24,7 +24,6 @@ Run Java
|
||||
Connected to the target VM
|
||||
crossinlineLiteral.kt:75
|
||||
crossinlineLiteral.kt:13
|
||||
crossinlineLiteral.kt:75
|
||||
crossinlineLiteral.kt:18
|
||||
crossinlineLiteral.kt:81
|
||||
crossinlineLiteral.kt:23
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package soBreakpointWithInline
|
||||
|
||||
fun foo() {}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
//Breakpoint!
|
||||
12.inlineExt().normalExt() // Try to step over this line. Debugger will stop on the same line twice
|
||||
foo()
|
||||
}
|
||||
|
||||
inline fun Any.inlineExt(): Any = this
|
||||
fun Any.normalExt(): Any = this
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
LineBreakpoint created at soBreakpointWithInline.kt:7
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
soBreakpointWithInline.kt:7
|
||||
soBreakpointWithInline.kt:8
|
||||
Disconnected from the target VM
|
||||
|
||||
Process finished with exit code 0
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package soBreakpointWithOrdinalOnInlineCallsInOneLine
|
||||
|
||||
fun test(i: Int): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
fun foo() {}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val listOf = listOf(1)
|
||||
//Breakpoint! (lambdaOrdinal = -1)
|
||||
val testSome = listOf.filterNot({test(it)}).get(0)
|
||||
foo()
|
||||
}
|
||||
|
||||
|
||||
// STEP_OVER: 4
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
LineBreakpoint created at soBreakpointWithOrdinalOnInlineCallsInOneLine.kt:12 lambdaOrdinal = -1
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
soBreakpointWithOrdinalOnInlineCallsInOneLine.kt:12
|
||||
soBreakpointWithOrdinalOnInlineCallsInOneLine.kt:13
|
||||
soBreakpointWithOrdinalOnInlineCallsInOneLine.kt:14
|
||||
Disconnected from the target VM
|
||||
|
||||
Process finished with exit code 0
|
||||
@@ -524,6 +524,18 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
|
||||
doStepOverTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("soBreakpointWithInline.kt")
|
||||
public void testSoBreakpointWithInline() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soBreakpointWithInline.kt");
|
||||
doStepOverTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("soBreakpointWithOrdinalOnInlineCallsInOneLine.kt")
|
||||
public void testSoBreakpointWithOrdinalOnInlineCallsInOneLine() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soBreakpointWithOrdinalOnInlineCallsInOneLine.kt");
|
||||
doStepOverTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("soInlineAnonymousFunctionArgument.kt")
|
||||
public void testSoInlineAnonymousFunctionArgument() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soInlineAnonymousFunctionArgument.kt");
|
||||
|
||||
@@ -16,10 +16,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger
|
||||
|
||||
import com.sun.jdi.Location
|
||||
import com.sun.jdi.Method
|
||||
|
||||
class MockMethod : Method {
|
||||
override fun name() = ""
|
||||
override fun allLineLocations() = emptyList<Location>()
|
||||
|
||||
override fun isSynthetic() = throw UnsupportedOperationException()
|
||||
override fun isFinal() = throw UnsupportedOperationException()
|
||||
@@ -33,7 +35,6 @@ class MockMethod : Method {
|
||||
override fun isBridge() = throw UnsupportedOperationException()
|
||||
override fun isObsolete() = throw UnsupportedOperationException()
|
||||
override fun isSynchronized() = throw UnsupportedOperationException()
|
||||
override fun allLineLocations() = throw UnsupportedOperationException()
|
||||
override fun allLineLocations(stratum: String?, sourceName: String?) = throw UnsupportedOperationException()
|
||||
override fun isNative() = throw UnsupportedOperationException()
|
||||
override fun locationOfCodeIndex(codeIndex: Long) = throw UnsupportedOperationException()
|
||||
|
||||
Reference in New Issue
Block a user