Do not use visitor for counting lambda ordinal for debugger
This commit is contained in:
@@ -17,53 +17,26 @@
|
||||
package org.jetbrains.kotlin.idea.debugger
|
||||
|
||||
import com.intellij.debugger.engine.DebugProcessImpl
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.sun.jdi.*
|
||||
import com.sun.tools.jdi.LocalVariableImpl
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.KtFunctionLiteralExpression
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parents
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import java.util.*
|
||||
|
||||
fun isInsideInlineArgument(inlineArgument: KtFunction, location: Location, debugProcess: DebugProcessImpl): Boolean {
|
||||
return isInsideInlineArgument(inlineArgument, location.visibleVariables(debugProcess))
|
||||
}
|
||||
|
||||
fun isInsideInlineArgument(inlineArgument: KtFunction, visibleVariables: List<LocalVariable>): Boolean {
|
||||
val visibleVariables = location.visibleVariables(debugProcess)
|
||||
val lambdaOrdinalIndex = runReadAction { lambdaOrdinalIndex(inlineArgument) }
|
||||
val markerLocalVariables = visibleVariables.filter { it.name().startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT) }
|
||||
for (variable in markerLocalVariables) {
|
||||
val lambdaOrdinal = lambdaOrdinal(variable.name())
|
||||
if (lambdaOrdinalIndex[inlineArgument] == lambdaOrdinal) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return markerLocalVariables.firstOrNull { lambdaOrdinal(it.name()) == lambdaOrdinalIndex } != null
|
||||
}
|
||||
|
||||
private fun lambdaOrdinalIndex(elementAt: PsiElement): HashMap<KtFunction, Int> {
|
||||
val parent = elementAt.parents.firstIsInstanceOrNull<KtNamedFunction>()
|
||||
private fun lambdaOrdinalIndex(elementAt: KtFunction): Int {
|
||||
val typeMapper = JetPositionManager.createTypeMapper(elementAt.getContainingJetFile())
|
||||
|
||||
val actualLambdaOrdinals = hashMapOf<KtFunction, Int>()
|
||||
parent?.accept(object : KtTreeVisitorVoid() {
|
||||
override fun visitNamedFunction(function: KtNamedFunction) {
|
||||
if (function != parent) {
|
||||
actualLambdaOrdinals[function] = actualLambdaOrdinals.size + 1
|
||||
}
|
||||
super.visitNamedFunction(function)
|
||||
}
|
||||
|
||||
override fun visitFunctionLiteralExpression(expression: KtFunctionLiteralExpression) {
|
||||
actualLambdaOrdinals[expression.functionLiteral] = actualLambdaOrdinals.size + 1
|
||||
super.visitFunctionLiteralExpression(expression)
|
||||
}
|
||||
})
|
||||
return actualLambdaOrdinals
|
||||
val type = CodegenBinding.asmTypeForAnonymousClass(typeMapper.bindingContext, elementAt)
|
||||
return type.className.substringAfterLast("$").toInt()
|
||||
}
|
||||
|
||||
private fun Location.visibleVariables(debugProcess: DebugProcessImpl): List<LocalVariable> {
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package isInsideInlineLambdaInLibrary
|
||||
|
||||
public fun test() {
|
||||
val a = A()
|
||||
//Breakpoint1
|
||||
a.foo(1) { 1 }
|
||||
|
||||
// inside other lambda
|
||||
a.foo(100) {
|
||||
//Breakpoint2
|
||||
a.foo(2) { 1 }
|
||||
1
|
||||
}
|
||||
|
||||
// inside variable declaration
|
||||
//Breakpoint3
|
||||
val x = a.foo(3) { 1 }
|
||||
|
||||
// inside object declaration
|
||||
val y = object {
|
||||
fun foo() {
|
||||
//Breakpoint4
|
||||
a.foo(4) { 1 }
|
||||
}
|
||||
}
|
||||
y.foo()
|
||||
|
||||
// inside local function
|
||||
fun local() {
|
||||
//Breakpoint5
|
||||
a.foo(5) { 1 }
|
||||
}
|
||||
local()
|
||||
}
|
||||
|
||||
class A {
|
||||
inline fun foo(i: Int, f: (i: Int) -> Int): A {
|
||||
f(i)
|
||||
return this
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
LineBreakpoint created at isInsideInlineLambdaInLibrary.kt:6 lambdaOrdinal = 0
|
||||
LineBreakpoint created at isInsideInlineLambdaInLibrary.kt:11 lambdaOrdinal = 0
|
||||
LineBreakpoint created at isInsideInlineLambdaInLibrary.kt:17 lambdaOrdinal = 0
|
||||
LineBreakpoint created at isInsideInlineLambdaInLibrary.kt:23 lambdaOrdinal = 0
|
||||
LineBreakpoint created at isInsideInlineLambdaInLibrary.kt:31 lambdaOrdinal = 0
|
||||
LineBreakpoint created at isInsideInlineLambda.kt:9 lambdaOrdinal = 0
|
||||
LineBreakpoint created at isInsideInlineLambda.kt:17 lambdaOrdinal = 0
|
||||
LineBreakpoint created at isInsideInlineLambda.kt:25 lambdaOrdinal = 0
|
||||
LineBreakpoint created at isInsideInlineLambda.kt:33 lambdaOrdinal = 0
|
||||
LineBreakpoint created at isInsideInlineLambda.kt:43 lambdaOrdinal = 0
|
||||
!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! isInsideInlineLambda.IsInsideInlineLambdaKt
|
||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
isInsideInlineLambda.kt:9
|
||||
Compile bytecode for it + 1
|
||||
isInsideInlineLambda.kt:17
|
||||
Compile bytecode for it + 2
|
||||
isInsideInlineLambda.kt:25
|
||||
Compile bytecode for it + 3
|
||||
isInsideInlineLambda.kt:33
|
||||
Compile bytecode for it + 4
|
||||
isInsideInlineLambda.kt:43
|
||||
Compile bytecode for it + 5
|
||||
isInsideInlineLambdaInLibrary.kt:6
|
||||
Compile bytecode for it + 11
|
||||
isInsideInlineLambdaInLibrary.kt:11
|
||||
Compile bytecode for it + 12
|
||||
isInsideInlineLambdaInLibrary.kt:17
|
||||
Compile bytecode for it + 13
|
||||
isInsideInlineLambdaInLibrary.kt:23
|
||||
Compile bytecode for it + 14
|
||||
isInsideInlineLambdaInLibrary.kt:31
|
||||
Compile bytecode for it + 15
|
||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
|
||||
Process finished with exit code 0
|
||||
@@ -17,6 +17,7 @@ stepOverIfWithInline.kt:24
|
||||
stepOverIfWithInline.kt:28
|
||||
stepOverIfWithInline.kt:41
|
||||
stepOverIfWithInline.kt:28
|
||||
stepOverIfWithInline.kt:28
|
||||
stepOverIfWithInline.kt:32
|
||||
stepOverIfWithInline.kt:41
|
||||
stepOverIfWithInline.kt:32
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package isInsideInlineLambda
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val a = A()
|
||||
|
||||
// EXPRESSION: it + 1
|
||||
// RESULT: 2: I
|
||||
//Breakpoint! (0)
|
||||
a.foo(1) { 1 }
|
||||
|
||||
|
||||
// inside other lambda
|
||||
a.foo(1) {
|
||||
// EXPRESSION: it + 2
|
||||
// RESULT: 3: I
|
||||
//Breakpoint! (0)
|
||||
a.foo(1) { 1 }
|
||||
1
|
||||
}
|
||||
|
||||
// inside variable declaration
|
||||
// EXPRESSION: it + 3
|
||||
// RESULT: 4: I
|
||||
//Breakpoint! (0)
|
||||
val x = a.foo(1) { 1 }
|
||||
|
||||
// inside object declaration
|
||||
val y = object {
|
||||
fun foo() {
|
||||
// EXPRESSION: it + 4
|
||||
// RESULT: 5: I
|
||||
//Breakpoint! (0)
|
||||
a.foo(1) { 1 }
|
||||
}
|
||||
}
|
||||
y.foo()
|
||||
|
||||
// inside local function
|
||||
fun local() {
|
||||
// EXPRESSION: it + 5
|
||||
// RESULT: 6: I
|
||||
//Breakpoint! (0)
|
||||
a.foo(1) { 1 }
|
||||
}
|
||||
local()
|
||||
|
||||
isInsideInlineLambdaInLibrary.test()
|
||||
}
|
||||
|
||||
class A {
|
||||
inline fun foo(i: Int, f: (i: Int) -> Int): A {
|
||||
f(i)
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
// ADDITIONAL_BREAKPOINT: isInsideInlineLambdaInLibrary.kt:Breakpoint1:(0)
|
||||
// EXPRESSION: it + 11
|
||||
// RESULT: 12: I
|
||||
|
||||
// ADDITIONAL_BREAKPOINT: isInsideInlineLambdaInLibrary.kt:Breakpoint2:(0)
|
||||
// EXPRESSION: it + 12
|
||||
// RESULT: 14: I
|
||||
|
||||
// ADDITIONAL_BREAKPOINT: isInsideInlineLambdaInLibrary.kt:Breakpoint3:(0)
|
||||
// EXPRESSION: it + 13
|
||||
// RESULT: 16: I
|
||||
|
||||
// ADDITIONAL_BREAKPOINT: isInsideInlineLambdaInLibrary.kt:Breakpoint4:(0)
|
||||
// EXPRESSION: it + 14
|
||||
// RESULT: 18: I
|
||||
|
||||
// ADDITIONAL_BREAKPOINT: isInsideInlineLambdaInLibrary.kt:Breakpoint5:(0)
|
||||
// EXPRESSION: it + 15
|
||||
// RESULT: 20: I
|
||||
+1
-1
@@ -44,4 +44,4 @@ inline fun foo(f: () -> Int): Int {
|
||||
|
||||
fun test(i: Int) = 1
|
||||
|
||||
// STEP_OVER: 21
|
||||
// STEP_OVER: 22
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.jetbrains.kotlin.idea.debugger
|
||||
|
||||
import com.intellij.debugger.DebuggerInvocationUtil
|
||||
import com.intellij.debugger.DebuggerManagerEx
|
||||
import com.intellij.debugger.SourcePosition
|
||||
import com.intellij.debugger.actions.MethodSmartStepTarget
|
||||
import com.intellij.debugger.actions.SmartStepTarget
|
||||
@@ -269,7 +268,6 @@ abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() {
|
||||
val document = runReadAction { PsiDocumentManager.getInstance(myProject).getDocument(file) } ?: return
|
||||
val breakpointManager = XDebuggerManager.getInstance(myProject).getBreakpointManager()
|
||||
val kotlinFieldBreakpointType = findBreakpointType(KotlinFieldBreakpointType::class.java)
|
||||
val kotlinLineBreakpointType = findBreakpointType(KotlinLineBreakpointType::class.java)
|
||||
val virtualFile = file.getVirtualFile()
|
||||
|
||||
val runnable = {
|
||||
@@ -302,27 +300,8 @@ abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() {
|
||||
}
|
||||
}
|
||||
else if (comment.startsWith("//Breakpoint!")) {
|
||||
val javaBreakpoint = createBreakpointOfType(
|
||||
breakpointManager,
|
||||
kotlinLineBreakpointType as XLineBreakpointType<XBreakpointProperties<*>>,
|
||||
lineIndex,
|
||||
virtualFile)
|
||||
if (javaBreakpoint is LineBreakpoint<*>) {
|
||||
|
||||
if (comment.contains("(")) {
|
||||
val ordinal = comment.substringAfter("//Breakpoint! (").substringBefore(")").toInt()
|
||||
|
||||
val properties = javaBreakpoint.xBreakpoint.properties as? JavaLineBreakpointProperties ?: continue
|
||||
properties.lambdaOrdinal = ordinal
|
||||
|
||||
BreakpointManager.addBreakpoint(javaBreakpoint)
|
||||
println("LineBreakpoint created at ${file.virtualFile.name}:${lineIndex + 1} lambdaOrdinal = $ordinal", ProcessOutputTypes.SYSTEM)
|
||||
}
|
||||
else {
|
||||
BreakpointManager.addBreakpoint(javaBreakpoint)
|
||||
println("LineBreakpoint created at ${file.virtualFile.name}:${lineIndex + 1}", ProcessOutputTypes.SYSTEM)
|
||||
}
|
||||
}
|
||||
val ordinal = if (comment.contains(" (")) comment.substringAfter("//Breakpoint! (").substringBefore(")").toInt() else null
|
||||
createLineBreakpoint(breakpointManager, file, lineIndex, ordinal)
|
||||
}
|
||||
else {
|
||||
throw AssertionError("Cannot create breakpoint at line ${lineIndex + 1}")
|
||||
@@ -338,6 +317,33 @@ abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun createLineBreakpoint(
|
||||
breakpointManager: XBreakpointManager,
|
||||
file: PsiFile,
|
||||
lineIndex: Int,
|
||||
lambdaOrdinal: Int?
|
||||
) {
|
||||
val kotlinLineBreakpointType = findBreakpointType(KotlinLineBreakpointType::class.java)
|
||||
val javaBreakpoint = createBreakpointOfType(
|
||||
breakpointManager,
|
||||
kotlinLineBreakpointType as XLineBreakpointType<XBreakpointProperties<*>>,
|
||||
lineIndex,
|
||||
file.virtualFile)
|
||||
if (javaBreakpoint is LineBreakpoint<*>) {
|
||||
if (lambdaOrdinal != null) {
|
||||
val properties = javaBreakpoint.xBreakpoint.properties as? JavaLineBreakpointProperties ?: return
|
||||
properties.lambdaOrdinal = lambdaOrdinal
|
||||
|
||||
BreakpointManager.addBreakpoint(javaBreakpoint)
|
||||
println("LineBreakpoint created at ${file.virtualFile.name}:${lineIndex + 1} lambdaOrdinal = $lambdaOrdinal", ProcessOutputTypes.SYSTEM)
|
||||
}
|
||||
else {
|
||||
BreakpointManager.addBreakpoint(javaBreakpoint)
|
||||
println("LineBreakpoint created at ${file.virtualFile.name}:${lineIndex + 1}", ProcessOutputTypes.SYSTEM)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createBreakpointOfType(
|
||||
breakpointManager: XBreakpointManager,
|
||||
breakpointType: XLineBreakpointType<XBreakpointProperties<*>>,
|
||||
@@ -365,12 +371,19 @@ abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() {
|
||||
val breakpoints = InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, "// ADDITIONAL_BREAKPOINT: ")
|
||||
for (breakpoint in breakpoints) {
|
||||
val position = breakpoint.split(".kt:")
|
||||
assert(position.size() == 2) { "Couldn't parse position from test directive: directive = $breakpoint" }
|
||||
createBreakpoint(position[0], position[1])
|
||||
assert(position.size == 2) { "Couldn't parse position from test directive: directive = $breakpoint" }
|
||||
var lineMarker = position[1]
|
||||
var ordinal: Int? = null
|
||||
if (lineMarker.contains(":(") && lineMarker.endsWith(")")) {
|
||||
val lineMarkerAndOrdinal = lineMarker.split(":(")
|
||||
lineMarker = lineMarkerAndOrdinal[0]
|
||||
ordinal = lineMarkerAndOrdinal[1].substringBefore(")").toInt()
|
||||
}
|
||||
createBreakpoint(position[0], lineMarker, ordinal)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createBreakpoint(fileName: String, lineMarker: String) {
|
||||
private fun createBreakpoint(fileName: String, lineMarker: String, ordinal: Int?) {
|
||||
val project = getProject()!!
|
||||
val sourceFiles = runReadAction {
|
||||
FilenameIndex.getAllFilesByExt(project, "kt").filter {
|
||||
@@ -384,16 +397,13 @@ abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() {
|
||||
val runnable = Runnable() {
|
||||
val psiSourceFile = PsiManager.getInstance(project).findFile(sourceFiles.first())!!
|
||||
|
||||
val breakpointManager = DebuggerManagerEx.getInstanceEx(project)?.getBreakpointManager()!!
|
||||
val breakpointManager = XDebuggerManager.getInstance(myProject).getBreakpointManager()
|
||||
val document = PsiDocumentManager.getInstance(project).getDocument(psiSourceFile)!!
|
||||
|
||||
val index = psiSourceFile.getText()!!.indexOf(lineMarker)
|
||||
val lineNumber = document.getLineNumber(index) + 1
|
||||
val lineNumber = document.getLineNumber(index) + 1 // lineMarker is for previous line
|
||||
|
||||
val breakpoint = breakpointManager.addLineBreakpoint(document, lineNumber)
|
||||
if (breakpoint != null) {
|
||||
println("LineBreakpoint created at " + psiSourceFile.getName() + ":" + lineNumber, ProcessOutputTypes.SYSTEM);
|
||||
}
|
||||
createLineBreakpoint(breakpointManager, psiSourceFile, lineNumber, ordinal)
|
||||
}
|
||||
|
||||
DebuggerInvocationUtil.invokeAndWait(project, runnable, ModalityState.defaultModalityState())
|
||||
|
||||
+6
@@ -682,6 +682,12 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
|
||||
doMultipleBreakpointsTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("isInsideInlineLambda.kt")
|
||||
public void testIsInsideInlineLambda() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/isInsideInlineLambda.kt");
|
||||
doMultipleBreakpointsTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("localFun.kt")
|
||||
public void testLocalFun() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/localFun.kt");
|
||||
|
||||
Reference in New Issue
Block a user