Do not use visitor for counting lambda ordinal for debugger

This commit is contained in:
Natalia Ukhorskaya
2015-10-27 18:46:38 +03:00
parent eac6fdbaa0
commit de5c3733e9
8 changed files with 208 additions and 67 deletions
@@ -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> {
@@ -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
@@ -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
@@ -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())
@@ -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");