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.engine.DebugProcess
import com.intellij.debugger.engine.DebuggerUtils
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.PsiTreeUtil
import com.sun.jdi.AbsentInformationException
import com.sun.jdi.ObjectCollectedException
@@ -47,7 +49,7 @@ import org.jetbrains.org.objectweb.asm.Type
import java.util.*
class DebuggerClassNameProvider(
private val debugProcess: DebugProcess,
val project: Project, val searchScope: GlobalSearchScope,
val findInlineUseSites: 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.
*/
fun getClassesForPosition(position: SourcePosition): List<ReferenceType> = with(debugProcess) {
val lineNumber = runReadAction { position.line }
fun getClassesForPosition(position: SourcePosition): Set<String> {
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 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.progress.EmptyProgressIndicator
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.MessageType
import com.intellij.psi.PsiElement
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.inline.InlineUtil
class InlineCallableUsagesSearcher(private val myDebugProcess: DebugProcess) {
class InlineCallableUsagesSearcher(val project: Project, val searchScope: GlobalSearchScope) {
fun findInlinedCalls(
declaration: KtDeclaration,
alreadyVisited: Set<PsiElement>,
@@ -56,7 +57,7 @@ class InlineCallableUsagesSearcher(private val myDebugProcess: DebugProcess) {
task,
KotlinDebuggerCoreBundle.message("find.inline.calls.task.compute.names", declarationName),
true,
myDebugProcess.project
project
)
} else {
try {
@@ -70,7 +71,7 @@ class InlineCallableUsagesSearcher(private val myDebugProcess: DebugProcess) {
XDebuggerManagerImpl.NOTIFICATION_GROUP.createNotification(
KotlinDebuggerCoreBundle.message("find.inline.calls.task.cancelled", declarationName),
MessageType.WARNING
).notify(myDebugProcess.project)
).notify(project)
}
val newAlreadyVisited = HashSet<PsiElement>().apply {
@@ -105,12 +106,12 @@ class InlineCallableUsagesSearcher(private val myDebugProcess: DebugProcess) {
private fun getScopeForInlineDeclarationUsages(inlineDeclaration: KtDeclaration): GlobalSearchScope {
val virtualFile = runReadAction { inlineDeclaration.containingFile.virtualFile }
return if (virtualFile != null && ProjectRootsUtil.isLibraryFile(myDebugProcess.project, virtualFile)) {
myDebugProcess.searchScope.uniteWith(
KotlinSourceFilterScope.librarySources(GlobalSearchScope.allScope(myDebugProcess.project), myDebugProcess.project)
return if (virtualFile != null && ProjectRootsUtil.isLibraryFile(project, virtualFile)) {
searchScope.uniteWith(
KotlinSourceFilterScope.librarySources(GlobalSearchScope.allScope(project), project)
)
} else {
myDebugProcess.searchScope
searchScope
}
}
}
@@ -30,6 +30,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.ObjectCollectedException
import com.sun.jdi.ReferenceType
import com.sun.jdi.request.ClassPrepareRequest
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.resolve.inline.InlineUtil
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import java.util.ArrayList
class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiRequestPositionManager, PositionManagerEx() {
private val stackFrameInterceptor: StackFrameInterceptor = myDebugProcess.project.getService()
@@ -221,7 +223,8 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
}
val internalClassNames = DebuggerClassNameProvider(
myDebugProcess,
myDebugProcess.project,
myDebugProcess.searchScope,
alwaysReturnLambdaParentClass = false
).getOuterClassNamesForElement(literal.firstChild, emptySet()).classNames
@@ -265,7 +268,10 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
val psiFile = sourcePosition.file
if (psiFile is KtFile) {
if (!ProjectRootsUtil.isInProjectOrLibSource(psiFile)) return emptyList()
return hopelessAware { DebuggerClassNameProvider(myDebugProcess).getClassesForPosition(sourcePosition) } ?: emptyList()
return hopelessAware {
getReferenceTypesForPositionInKtFile(sourcePosition)
} ?: emptyList()
}
if (psiFile is ClsFileImpl) {
@@ -279,8 +285,17 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
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> {
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> {
@@ -319,7 +334,8 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
}
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 ->
listOfNotNull(
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 {
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
}