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.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
|
|
||||||
}
|
|
||||||
|
|||||||
+8
-7
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+72
-4
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user