From 378d0a757a293707be41ad29d2d5fdc0b0db067f Mon Sep 17 00:00:00 2001 From: Vladimir Ilmov Date: Thu, 2 Jul 2020 10:24:29 +0200 Subject: [PATCH] DebuggerClassNameProvider refactoring --- .../debugger/DebuggerClassNameProvider.kt | 63 ++------------- .../debugger/InlineCallableUsagesSearcher.kt | 15 ++-- .../idea/debugger/KotlinPositionManager.kt | 76 ++++++++++++++++++- 3 files changed, 85 insertions(+), 69 deletions(-) diff --git a/idea/jvm-debugger/jvm-debugger-core/src/org/jetbrains/kotlin/idea/debugger/DebuggerClassNameProvider.kt b/idea/jvm-debugger/jvm-debugger-core/src/org/jetbrains/kotlin/idea/debugger/DebuggerClassNameProvider.kt index af52af15fcf..6f10ea115d0 100644 --- a/idea/jvm-debugger/jvm-debugger-core/src/org/jetbrains/kotlin/idea/debugger/DebuggerClassNameProvider.kt +++ b/idea/jvm-debugger/jvm-debugger-core/src/org/jetbrains/kotlin/idea/debugger/DebuggerClassNameProvider.kt @@ -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 = with(debugProcess) { - val lineNumber = runReadAction { position.line } - + fun getClassesForPosition(position: SourcePosition): Set { 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 { - val vmProxy = virtualMachineProxy - - try { - if (!outerClass.isPrepared) { - return emptyList() - } - } catch (e: ObjectCollectedException) { - return emptyList() - } - - val targetClasses = ArrayList(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 -} diff --git a/idea/jvm-debugger/jvm-debugger-core/src/org/jetbrains/kotlin/idea/debugger/InlineCallableUsagesSearcher.kt b/idea/jvm-debugger/jvm-debugger-core/src/org/jetbrains/kotlin/idea/debugger/InlineCallableUsagesSearcher.kt index b6cfd60dbf0..7adb2f651db 100644 --- a/idea/jvm-debugger/jvm-debugger-core/src/org/jetbrains/kotlin/idea/debugger/InlineCallableUsagesSearcher.kt +++ b/idea/jvm-debugger/jvm-debugger-core/src/org/jetbrains/kotlin/idea/debugger/InlineCallableUsagesSearcher.kt @@ -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, @@ -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().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 } } } \ No newline at end of file diff --git a/idea/jvm-debugger/jvm-debugger-core/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt b/idea/jvm-debugger/jvm-debugger-core/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt index a45d0e1593a..593e790ba77 100644 --- a/idea/jvm-debugger/jvm-debugger-core/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt +++ b/idea/jvm-debugger/jvm-debugger-core/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt @@ -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 { + 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 { - 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 { @@ -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.readAction(crossinline f: (U) -> V): V { return runReadAction { f(this) } } + +private fun DebugProcess.findTargetClasses(outerClass: ReferenceType, lineAt: Int): List { + val vmProxy = virtualMachineProxy + + try { + if (!outerClass.isPrepared) { + return emptyList() + } + } catch (e: ObjectCollectedException) { + return emptyList() + } + + val targetClasses = ArrayList(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 +}