Debugger: fix breakpoints inside inline functions in libraries sources
This commit is contained in:
@@ -191,6 +191,7 @@ Issues fixed:
|
||||
- Avoid 1s delay in completion in debugger fields if session is not stopped on a breakpoint
|
||||
- Avoid cast to runtime type unavailable in current scope
|
||||
- Fix text with line breaks in popup with line breakpoint variants
|
||||
- Fix breakpoints inside inline functions in libraries sources
|
||||
|
||||
### Java to Kotlin converter
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ import com.intellij.openapi.ui.MessageType
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.impl.compiled.ClsFileImpl
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.util.ThreeState
|
||||
@@ -63,6 +64,7 @@ import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches.Computed
|
||||
import org.jetbrains.kotlin.idea.decompiler.classFile.KtClsFile
|
||||
import org.jetbrains.kotlin.idea.refactoring.getLineStartOffset
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.isImportUsage
|
||||
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
|
||||
import org.jetbrains.kotlin.idea.util.DebuggerUtils
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
@@ -81,6 +83,11 @@ import com.intellij.debugger.engine.DebuggerUtils as JDebuggerUtils
|
||||
|
||||
class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiRequestPositionManager, PositionManagerEx() {
|
||||
|
||||
private val scopes = listOf(
|
||||
myDebugProcess.searchScope,
|
||||
KotlinSourceFilterScope.librarySources(GlobalSearchScope.allScope(myDebugProcess.project), myDebugProcess.project)
|
||||
)
|
||||
|
||||
override fun evaluateCondition(context: EvaluationContext, frame: StackFrameProxyImpl, location: Location, expression: String): ThreeState? {
|
||||
return ThreeState.UNSURE
|
||||
}
|
||||
@@ -106,7 +113,7 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
|
||||
val javaClassName = JvmClassName.byInternalName(defaultInternalName(location))
|
||||
val project = myDebugProcess.project
|
||||
|
||||
val defaultPsiFile = DebuggerUtils.findSourceFileForClass(project, myDebugProcess.searchScope, javaClassName, javaSourceFileName)
|
||||
val defaultPsiFile = DebuggerUtils.findSourceFileForClass(project, scopes, javaClassName, javaSourceFileName)
|
||||
if (defaultPsiFile != null) {
|
||||
return SourcePosition.createFromLine(defaultPsiFile, 0)
|
||||
}
|
||||
@@ -218,7 +225,7 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
|
||||
|
||||
val project = myDebugProcess.project
|
||||
|
||||
return DebuggerUtils.findSourceFileForClass(project, myDebugProcess.searchScope, className, sourceName)
|
||||
return DebuggerUtils.findSourceFileForClass(project, scopes, className, sourceName)
|
||||
}
|
||||
|
||||
private fun defaultInternalName(location: Location): String {
|
||||
@@ -450,7 +457,7 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
|
||||
val functionName = function.readAction { it.name }
|
||||
|
||||
val task = Runnable {
|
||||
ReferencesSearch.search(function, myDebugProcess.searchScope).forEach {
|
||||
ReferencesSearch.search(function, getScopeForInlineFunctionUsages(function)).forEach {
|
||||
if (!it.readAction { it.isImportUsage() }) {
|
||||
val usage = (it.element as? KtElement)?.let { getElementToCalculateClassName(it) }
|
||||
if (usage != null) {
|
||||
@@ -527,7 +534,7 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
|
||||
val result = hashSetOf<String>()
|
||||
val inlineFunction = parameter.containingDeclaration.source.getPsi() as? KtNamedFunction ?: return emptySet()
|
||||
|
||||
ReferencesSearch.search(inlineFunction, myDebugProcess.searchScope).forEach {
|
||||
ReferencesSearch.search(inlineFunction, getScopeForInlineFunctionUsages(inlineFunction)).forEach {
|
||||
runReadAction {
|
||||
if (!it.isImportUsage()) {
|
||||
val call = (it.element as? KtExpression)?.let { KtPsiUtil.getParentCallIfPresent(it) }
|
||||
@@ -548,6 +555,16 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
|
||||
return result
|
||||
}
|
||||
|
||||
private fun getScopeForInlineFunctionUsages(inlineFunction: KtNamedFunction): GlobalSearchScope {
|
||||
val virtualFile = runReadAction { inlineFunction.containingFile.virtualFile }
|
||||
if (virtualFile != null && ProjectRootsUtil.isLibraryFile(myDebugProcess.project, virtualFile)) {
|
||||
return GlobalSearchScope.union(scopes.toTypedArray())
|
||||
}
|
||||
else {
|
||||
return myDebugProcess.searchScope
|
||||
}
|
||||
}
|
||||
|
||||
private fun getArgumentExpression(it: ValueArgument) = (it.getArgumentExpression() as? KtLambdaExpression)?.functionLiteral ?: it.getArgumentExpression()
|
||||
|
||||
private fun String.substringIndex(): String {
|
||||
|
||||
@@ -63,7 +63,7 @@ class KotlinExceptionFilter(private val searchScope: GlobalSearchScope) : Filter
|
||||
val internalName = fullyQualifiedName.replace('.', '/')
|
||||
val jvmClassName = JvmClassName.byInternalName(internalName)
|
||||
|
||||
val file = DebuggerUtils.findSourceFileForClass(project, searchScope, jvmClassName, fileName) ?: return null
|
||||
val file = DebuggerUtils.findSourceFileForClassIncludeLibrarySources(project, searchScope, jvmClassName, fileName) ?: return null
|
||||
|
||||
val virtualFile = file.virtualFile ?: return null
|
||||
|
||||
@@ -121,7 +121,7 @@ class KotlinExceptionFilter(private val searchScope: GlobalSearchScope) : Filter
|
||||
} ?: return null
|
||||
|
||||
val newJvmName = JvmClassName.byInternalName(mappingInfo.path)
|
||||
val newSourceFile = DebuggerUtils.findSourceFileForClass(project, searchScope, newJvmName, mappingInfo.name) ?: return null
|
||||
val newSourceFile = DebuggerUtils.findSourceFileForClassIncludeLibrarySources(project, searchScope, newJvmName, mappingInfo.name) ?: return null
|
||||
return OpenFileHyperlinkInfo(project, newSourceFile.virtualFile, mappingInfo.getIntervalIfContains(line)!!.map(line) - 1)
|
||||
}
|
||||
|
||||
|
||||
@@ -42,20 +42,28 @@ object DebuggerUtils {
|
||||
|
||||
private val KOTLIN_EXTENSIONS = Sets.newHashSet("kt", "kts")
|
||||
|
||||
fun findSourceFileForClassIncludeLibrarySources(
|
||||
project: Project,
|
||||
scope: GlobalSearchScope,
|
||||
className: JvmClassName,
|
||||
fileName: String): KtFile? {
|
||||
return findSourceFileForClass(
|
||||
project,
|
||||
listOf(scope, KotlinSourceFilterScope.librarySources(GlobalSearchScope.allScope(project), project)),
|
||||
className,
|
||||
fileName)
|
||||
}
|
||||
|
||||
fun findSourceFileForClass(
|
||||
project: Project,
|
||||
searchScope: GlobalSearchScope,
|
||||
scopes: List<GlobalSearchScope>,
|
||||
className: JvmClassName,
|
||||
fileName: String): KtFile? {
|
||||
val extension = FileUtilRt.getExtension(fileName)
|
||||
if (!KOTLIN_EXTENSIONS.contains(extension)) return null
|
||||
if (DumbService.getInstance(project).isDumb) return null
|
||||
|
||||
val filesWithExactName = findFilesByNameInPackage(className, fileName, project, searchScope).check { it.isNotEmpty() }
|
||||
// Source files for libraries aren't included into ModuleWithDependencies scope
|
||||
?: findFilesByNameInPackage(
|
||||
className, fileName, project,
|
||||
KotlinSourceFilterScope.librarySources(GlobalSearchScope.allScope(project), project))
|
||||
val filesWithExactName = scopes.findFirstNotEmpty { findFilesByNameInPackage(className, fileName, project, it) } ?: return null
|
||||
|
||||
if (filesWithExactName.isEmpty()) return null
|
||||
|
||||
@@ -65,7 +73,7 @@ object DebuggerUtils {
|
||||
|
||||
// Static facade or inner class of such facade?
|
||||
val partFqName = className.fqNameForClassNameWithoutDollars
|
||||
val filesForPart = StaticFacadeIndexUtil.findFilesForFilePart(partFqName, searchScope, project)
|
||||
val filesForPart = scopes.findFirstNotEmpty { StaticFacadeIndexUtil.findFilesForFilePart(partFqName, it, project) } ?: return null
|
||||
if (!filesForPart.isEmpty()) {
|
||||
for (file in filesForPart) {
|
||||
if (file.name == fileName) {
|
||||
@@ -79,6 +87,15 @@ object DebuggerUtils {
|
||||
return filesWithExactName.first()
|
||||
}
|
||||
|
||||
private fun <T, R> Collection<T>.findFirstNotEmpty(predicate: (T) -> Collection<R>): Collection<R>? {
|
||||
var result: Collection<R> = emptyList()
|
||||
for (e in this) {
|
||||
result = predicate(e)
|
||||
if (result.isNotEmpty()) break
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun findFilesByNameInPackage(className: JvmClassName, fileName: String, project: Project, searchScope: GlobalSearchScope)
|
||||
= findFilesWithExactPackage(className.packageFqName, searchScope, project).filter { it.name == fileName }
|
||||
|
||||
|
||||
+8
@@ -2,4 +2,12 @@ package customLib.inlineFunInLibrary
|
||||
|
||||
public inline fun inlineFun(f: () -> Unit) {
|
||||
1 + 1
|
||||
inlineFunInner {
|
||||
1 + 1
|
||||
}
|
||||
}
|
||||
|
||||
public inline fun inlineFunInner(f: () -> Unit) {
|
||||
// Breakpoint 2
|
||||
1 + 1
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
LineBreakpoint created at inlineFunInLibrary.kt:4
|
||||
LineBreakpoint created at inlineFunInLibrary.kt:12
|
||||
!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! breakpointInInlineFun.BreakpointInInlineFunKt
|
||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
inlineFunInLibrary.kt:4
|
||||
inlineFunInLibrary.kt:12
|
||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
|
||||
Process finished with exit code 0
|
||||
|
||||
+4
-1
@@ -6,4 +6,7 @@ fun main(args: Array<String>) {
|
||||
inlineFun { }
|
||||
}
|
||||
|
||||
// ADDITIONAL_BREAKPOINT: inlineFunInLibrary.kt:public inline fun inlineFun
|
||||
// RESUME: 2
|
||||
|
||||
// ADDITIONAL_BREAKPOINT: inlineFunInLibrary.kt:public inline fun inlineFun
|
||||
// ADDITIONAL_BREAKPOINT: inlineFunInLibrary.kt: Breakpoint 2
|
||||
Reference in New Issue
Block a user