DebuggerClassNameProvider refactoring
This commit is contained in:
+5
-58
@@ -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
|
||||
}
|
||||
|
||||
+8
-7
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+72
-4
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user