DebuggerClassNameProvider refactoring

This commit is contained in:
Vladimir Ilmov
2020-07-02 10:24:29 +02:00
parent f6a16c5203
commit 378d0a757a
3 changed files with 85 additions and 69 deletions
@@ -19,7 +19,9 @@ package org.jetbrains.kotlin.idea.debugger
import com.intellij.debugger.SourcePosition import com.intellij.debugger.SourcePosition
import com.intellij.debugger.engine.DebugProcess import com.intellij.debugger.engine.DebugProcess
import com.intellij.debugger.engine.DebuggerUtils import com.intellij.debugger.engine.DebuggerUtils
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
import com.sun.jdi.AbsentInformationException import com.sun.jdi.AbsentInformationException
import com.sun.jdi.ObjectCollectedException import com.sun.jdi.ObjectCollectedException
@@ -47,7 +49,7 @@ import org.jetbrains.org.objectweb.asm.Type
import java.util.* import java.util.*
class DebuggerClassNameProvider( class DebuggerClassNameProvider(
private val debugProcess: DebugProcess, val project: Project, val searchScope: GlobalSearchScope,
val findInlineUseSites: Boolean = true, val findInlineUseSites: Boolean = true,
val alwaysReturnLambdaParentClass: Boolean = true val alwaysReturnLambdaParentClass: Boolean = true
) { ) {
@@ -78,17 +80,13 @@ class DebuggerClassNameProvider(
} }
} }
private val inlineUsagesSearcher = InlineCallableUsagesSearcher(debugProcess) private val inlineUsagesSearcher = InlineCallableUsagesSearcher(project, searchScope)
/** /**
* Returns classes in which the given line number *is* present. * Returns classes in which the given line number *is* present.
*/ */
fun getClassesForPosition(position: SourcePosition): List<ReferenceType> = with(debugProcess) { fun getClassesForPosition(position: SourcePosition): Set<String> {
val lineNumber = runReadAction { position.line }
return doGetClassesForPosition(position) return doGetClassesForPosition(position)
.flatMap { className -> virtualMachineProxy.classesByName(className) }
.flatMap { referenceType -> findTargetClasses(referenceType, lineNumber) }
} }
/** /**
@@ -292,54 +290,3 @@ class DebuggerClassNameProvider(
private fun String.toJdiName() = replace('/', '.') private fun String.toJdiName() = replace('/', '.')
private fun DebugProcess.findTargetClasses(outerClass: ReferenceType, lineAt: Int): List<ReferenceType> {
val vmProxy = virtualMachineProxy
try {
if (!outerClass.isPrepared) {
return emptyList()
}
} catch (e: ObjectCollectedException) {
return emptyList()
}
val targetClasses = ArrayList<ReferenceType>(1)
try {
for (location in outerClass.safeAllLineLocations()) {
val locationLine = location.lineNumber() - 1
if (locationLine < 0) {
// such locations are not correspond to real lines in code
continue
}
if (lineAt == locationLine) {
val method = location.method()
if (method == null || DebuggerUtils.isSynthetic(method) || method.isBridge) {
// skip synthetic methods
continue
}
targetClasses += outerClass
break
}
}
// The same line number may appear in different classes so we have to scan nested classes as well.
// For example, in the next example line 3 appears in both Foo and Foo$Companion.
/* class Foo {
companion object {
val a = Foo() /* line 3 */
}
} */
val nestedTypes = vmProxy.nestedTypes(outerClass)
for (nested in nestedTypes) {
targetClasses += findTargetClasses(nested, lineAt)
}
} catch (_: AbsentInformationException) {
}
return targetClasses
}
@@ -9,6 +9,7 @@ import com.intellij.debugger.engine.DebugProcess
import com.intellij.openapi.application.ex.ApplicationManagerEx import com.intellij.openapi.application.ex.ApplicationManagerEx
import com.intellij.openapi.progress.EmptyProgressIndicator import com.intellij.openapi.progress.EmptyProgressIndicator
import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.MessageType import com.intellij.openapi.ui.MessageType
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference import com.intellij.psi.PsiReference
@@ -30,7 +31,7 @@ import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.inline.InlineUtil import org.jetbrains.kotlin.resolve.inline.InlineUtil
class InlineCallableUsagesSearcher(private val myDebugProcess: DebugProcess) { class InlineCallableUsagesSearcher(val project: Project, val searchScope: GlobalSearchScope) {
fun findInlinedCalls( fun findInlinedCalls(
declaration: KtDeclaration, declaration: KtDeclaration,
alreadyVisited: Set<PsiElement>, alreadyVisited: Set<PsiElement>,
@@ -56,7 +57,7 @@ class InlineCallableUsagesSearcher(private val myDebugProcess: DebugProcess) {
task, task,
KotlinDebuggerCoreBundle.message("find.inline.calls.task.compute.names", declarationName), KotlinDebuggerCoreBundle.message("find.inline.calls.task.compute.names", declarationName),
true, true,
myDebugProcess.project project
) )
} else { } else {
try { try {
@@ -70,7 +71,7 @@ class InlineCallableUsagesSearcher(private val myDebugProcess: DebugProcess) {
XDebuggerManagerImpl.NOTIFICATION_GROUP.createNotification( XDebuggerManagerImpl.NOTIFICATION_GROUP.createNotification(
KotlinDebuggerCoreBundle.message("find.inline.calls.task.cancelled", declarationName), KotlinDebuggerCoreBundle.message("find.inline.calls.task.cancelled", declarationName),
MessageType.WARNING MessageType.WARNING
).notify(myDebugProcess.project) ).notify(project)
} }
val newAlreadyVisited = HashSet<PsiElement>().apply { val newAlreadyVisited = HashSet<PsiElement>().apply {
@@ -105,12 +106,12 @@ class InlineCallableUsagesSearcher(private val myDebugProcess: DebugProcess) {
private fun getScopeForInlineDeclarationUsages(inlineDeclaration: KtDeclaration): GlobalSearchScope { private fun getScopeForInlineDeclarationUsages(inlineDeclaration: KtDeclaration): GlobalSearchScope {
val virtualFile = runReadAction { inlineDeclaration.containingFile.virtualFile } val virtualFile = runReadAction { inlineDeclaration.containingFile.virtualFile }
return if (virtualFile != null && ProjectRootsUtil.isLibraryFile(myDebugProcess.project, virtualFile)) { return if (virtualFile != null && ProjectRootsUtil.isLibraryFile(project, virtualFile)) {
myDebugProcess.searchScope.uniteWith( searchScope.uniteWith(
KotlinSourceFilterScope.librarySources(GlobalSearchScope.allScope(myDebugProcess.project), myDebugProcess.project) KotlinSourceFilterScope.librarySources(GlobalSearchScope.allScope(project), project)
) )
} else { } else {
myDebugProcess.searchScope searchScope
} }
} }
} }
@@ -30,6 +30,7 @@ import com.intellij.util.ThreeState
import com.intellij.xdebugger.frame.XStackFrame import com.intellij.xdebugger.frame.XStackFrame
import com.sun.jdi.AbsentInformationException import com.sun.jdi.AbsentInformationException
import com.sun.jdi.Location import com.sun.jdi.Location
import com.sun.jdi.ObjectCollectedException
import com.sun.jdi.ReferenceType import com.sun.jdi.ReferenceType
import com.sun.jdi.request.ClassPrepareRequest import com.sun.jdi.request.ClassPrepareRequest
import org.jetbrains.kotlin.codegen.inline.KOTLIN_STRATA_NAME import org.jetbrains.kotlin.codegen.inline.KOTLIN_STRATA_NAME
@@ -50,6 +51,7 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.inline.InlineUtil import org.jetbrains.kotlin.resolve.inline.InlineUtil
import org.jetbrains.kotlin.resolve.jvm.JvmClassName import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import java.util.ArrayList
class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiRequestPositionManager, PositionManagerEx() { class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiRequestPositionManager, PositionManagerEx() {
private val stackFrameInterceptor: StackFrameInterceptor = myDebugProcess.project.getService() private val stackFrameInterceptor: StackFrameInterceptor = myDebugProcess.project.getService()
@@ -221,7 +223,8 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
} }
val internalClassNames = DebuggerClassNameProvider( val internalClassNames = DebuggerClassNameProvider(
myDebugProcess, myDebugProcess.project,
myDebugProcess.searchScope,
alwaysReturnLambdaParentClass = false alwaysReturnLambdaParentClass = false
).getOuterClassNamesForElement(literal.firstChild, emptySet()).classNames ).getOuterClassNamesForElement(literal.firstChild, emptySet()).classNames
@@ -265,7 +268,10 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
val psiFile = sourcePosition.file val psiFile = sourcePosition.file
if (psiFile is KtFile) { if (psiFile is KtFile) {
if (!ProjectRootsUtil.isInProjectOrLibSource(psiFile)) return emptyList() if (!ProjectRootsUtil.isInProjectOrLibSource(psiFile)) return emptyList()
return hopelessAware { DebuggerClassNameProvider(myDebugProcess).getClassesForPosition(sourcePosition) } ?: emptyList()
return hopelessAware {
getReferenceTypesForPositionInKtFile(sourcePosition)
} ?: emptyList()
} }
if (psiFile is ClsFileImpl) { if (psiFile is ClsFileImpl) {
@@ -279,8 +285,17 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
throw NoDataException.INSTANCE throw NoDataException.INSTANCE
} }
private fun getReferenceTypesForPositionInKtFile(sourcePosition: SourcePosition): List<ReferenceType> {
val debuggerClassNameProvider = DebuggerClassNameProvider(myDebugProcess.project, myDebugProcess.searchScope)
val lineNumber = runReadAction { sourcePosition.line }
val classes = debuggerClassNameProvider.getClassesForPosition(sourcePosition)
return classes.flatMap { className -> myDebugProcess.virtualMachineProxy.classesByName(className) }
.flatMap { referenceType -> myDebugProcess.findTargetClasses(referenceType, lineNumber) }
}
fun originalClassNamesForPosition(position: SourcePosition): List<String> { fun originalClassNamesForPosition(position: SourcePosition): List<String> {
return DebuggerClassNameProvider(myDebugProcess, findInlineUseSites = false).getOuterClassNamesForPosition(position) val debuggerClassNameProvider = DebuggerClassNameProvider(myDebugProcess.project, myDebugProcess.searchScope, findInlineUseSites = false)
return debuggerClassNameProvider.getOuterClassNamesForPosition(position)
} }
override fun locationsOfLine(type: ReferenceType, position: SourcePosition): List<Location> { override fun locationsOfLine(type: ReferenceType, position: SourcePosition): List<Location> {
@@ -319,7 +334,8 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
} }
return DumbService.getInstance(myDebugProcess.project).runReadActionInSmartMode(Computable { return DumbService.getInstance(myDebugProcess.project).runReadActionInSmartMode(Computable {
val classNames = DebuggerClassNameProvider(myDebugProcess).getOuterClassNamesForPosition(position) val classNames =
DebuggerClassNameProvider(myDebugProcess.project, myDebugProcess.searchScope).getOuterClassNamesForPosition(position)
classNames.flatMap { name -> classNames.flatMap { name ->
listOfNotNull( listOfNotNull(
myDebugProcess.requestsManager.createClassPrepareRequest(requestor, name), myDebugProcess.requestsManager.createClassPrepareRequest(requestor, name),
@@ -333,3 +349,55 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
inline fun <U, V> U.readAction(crossinline f: (U) -> V): V { inline fun <U, V> U.readAction(crossinline f: (U) -> V): V {
return runReadAction { f(this) } return runReadAction { f(this) }
} }
private fun DebugProcess.findTargetClasses(outerClass: ReferenceType, lineAt: Int): List<ReferenceType> {
val vmProxy = virtualMachineProxy
try {
if (!outerClass.isPrepared) {
return emptyList()
}
} catch (e: ObjectCollectedException) {
return emptyList()
}
val targetClasses = ArrayList<ReferenceType>(1)
try {
for (location in outerClass.safeAllLineLocations()) {
val locationLine = location.lineNumber() - 1
if (locationLine < 0) {
// such locations are not correspond to real lines in code
continue
}
if (lineAt == locationLine) {
val method = location.method()
if (method == null || com.intellij.debugger.engine.DebuggerUtils.isSynthetic(method) || method.isBridge) {
// skip synthetic methods
continue
}
targetClasses += outerClass
break
}
}
// The same line number may appear in different classes so we have to scan nested classes as well.
// For example, in the next example line 3 appears in both Foo and Foo$Companion.
/* class Foo {
companion object {
val a = Foo() /* line 3 */
}
} */
val nestedTypes = vmProxy.nestedTypes(outerClass)
for (nested in nestedTypes) {
targetClasses += findTargetClasses(nested, lineAt)
}
} catch (_: AbsentInformationException) {
}
return targetClasses
}