KotlinPositionManager: add progress when computing classnames for inline function

This commit is contained in:
Natalia Ukhorskaya
2016-02-10 12:08:57 +03:00
parent 9ac2da8c96
commit 9070eff6ad
2 changed files with 112 additions and 78 deletions
@@ -25,8 +25,12 @@ import com.intellij.debugger.engine.PositionManagerEx
import com.intellij.debugger.engine.evaluation.EvaluationContext
import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.intellij.debugger.requests.ClassPrepareRequestor
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.roots.libraries.LibraryUtil
import com.intellij.openapi.ui.MessageType
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.impl.compiled.ClsFileImpl
@@ -34,6 +38,7 @@ import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.ThreeState
import com.intellij.xdebugger.frame.XStackFrame
import com.intellij.xdebugger.impl.XDebugSessionImpl
import com.sun.jdi.AbsentInformationException
import com.sun.jdi.Location
import com.sun.jdi.ReferenceType
@@ -238,7 +243,7 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
}
if (psiFile is ClsFileImpl) {
val decompiledPsiFile = runReadAction { psiFile.decompiledPsiFile }
val decompiledPsiFile = psiFile.readAction { it.decompiledPsiFile }
if (decompiledPsiFile is KtClsFile && sourcePosition.line == -1) {
val className =
JvmFileClassUtil.getFileClassInfoNoResolve(decompiledPsiFile).fileClassFqName.internalNameWithoutInnerClasses
@@ -254,7 +259,7 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
}
private fun classNamesForPosition(sourcePosition: SourcePosition, withInlines: Boolean): List<String> {
val element = runReadAction { sourcePosition.elementAt } ?: return emptyList()
val element = sourcePosition.readAction { it.elementAt } ?: return emptyList()
val names = classNamesForPosition(element, withInlines)
val lambdas = findLambdas(sourcePosition)
@@ -265,27 +270,25 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
return names + lambdas
}
private fun classNamesForPosition(element: PsiElement, withInlines: Boolean): List<String> {
return runReadAction {
if (DumbService.getInstance(element.project).isDumb) {
emptyList()
}
else {
val baseElement = getElementToCalculateClassName(element) ?: return@runReadAction emptyList()
getOrComputeClassNames(baseElement) {
element ->
val file = element.containingFile as KtFile
val isInLibrary = LibraryUtil.findLibraryEntry(file.virtualFile, file.project) != null
val typeMapper = KotlinDebuggerCaches.getOrCreateTypeMapper(element)
private fun classNamesForPosition(element: PsiElement?, withInlines: Boolean): List<String> {
if (DumbService.getInstance(myDebugProcess.project).isDumb) {
return emptyList()
}
else {
val baseElement = getElementToCalculateClassName(element) ?: return emptyList()
return getOrComputeClassNames(baseElement) {
element ->
val file = element.readAction { it.containingFile as KtFile }
val isInLibrary = LibraryUtil.findLibraryEntry(file.virtualFile, file.project) != null
val typeMapper = KotlinDebuggerCaches.getOrCreateTypeMapper(element)
getInternalClassNameForElement(element, typeMapper, file, isInLibrary, withInlines)
}
getInternalClassNameForElement(element, typeMapper, file, isInLibrary, withInlines)
}
}
}
private fun findLambdas(sourcePosition: SourcePosition): Collection<String> {
val lambdas = runReadAction { getLambdasAtLineIfAny(sourcePosition) }
val lambdas = sourcePosition.readAction { getLambdasAtLineIfAny(it) }
return lambdas.flatMap { classNamesForPosition(it, true) }
}
@@ -330,12 +333,13 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
isInLibrary: Boolean,
withInlines: Boolean
): KotlinDebuggerCaches.ComputedClassNames {
val parent = element.readAction { getElementToCalculateClassName(it.parent) }
when (element) {
is KtClassOrObject -> return CachedClassNames(getClassNameForClass(element, typeMapper))
is KtFunction -> {
val descriptor = InlineUtil.getInlineArgumentDescriptor(element, typeMapper.bindingContext)
val descriptor = element.readAction { InlineUtil.getInlineArgumentDescriptor(it, typeMapper.bindingContext) }
if (descriptor != null) {
val classNamesForParent = classNamesForPosition(element.parent, withInlines)
val classNamesForParent = classNamesForPosition(parent, withInlines)
if (descriptor.isCrossinline) {
return CachedClassNames(classNamesForParent + findCrossInlineArguments(element, descriptor, typeMapper.bindingContext))
}
@@ -344,7 +348,7 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
}
}
val crossInlineParameterUsages = element.containsCrossInlineParameterUsages(typeMapper.bindingContext)
val crossInlineParameterUsages = element.readAction { it.containsCrossInlineParameterUsages(typeMapper.bindingContext) }
if (crossInlineParameterUsages.isNotEmpty()) {
return CachedClassNames(classNamesForCrossInlineParameters(crossInlineParameterUsages, typeMapper.bindingContext).toList())
}
@@ -355,38 +359,36 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
return CachedClassNames(asmType.internalName)
}
element is KtAnonymousInitializer -> {
val parent = getElementToCalculateClassName(element.parent)
// Class-object initializer
if (parent is KtObjectDeclaration && parent.isCompanion()) {
return CachedClassNames(classNamesForPosition(parent.parent, withInlines))
}
return CachedClassNames(classNamesForPosition(element.parent, withInlines))
return CachedClassNames(classNamesForPosition(parent, withInlines))
}
element is KtPropertyAccessor && (!element.property.isTopLevel || !isInLibrary)-> {
val classOrObject = PsiTreeUtil.getParentOfType(element, KtClassOrObject::class.java)
element is KtPropertyAccessor && (!element.readAction { it.property.isTopLevel } || !isInLibrary) -> {
val classOrObject = element.readAction { PsiTreeUtil.getParentOfType(it, KtClassOrObject::class.java) }
if (classOrObject != null) {
return CachedClassNames(getClassNameForClass(classOrObject, typeMapper))
}
}
element is KtProperty && (!element.isTopLevel || !isInLibrary) -> {
element is KtProperty && (!element.readAction { it.isTopLevel } || !isInLibrary) -> {
val descriptor = typeMapper.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element)
if (descriptor !is PropertyDescriptor) {
return CachedClassNames(classNamesForPosition(element.parent, withInlines))
return CachedClassNames(classNamesForPosition(parent, withInlines))
}
return CachedClassNames(getJvmInternalNameForPropertyOwner(typeMapper, descriptor))
}
element is KtNamedFunction -> {
val parent = getElementToCalculateClassName(element.parent)
val parentInternalName = if (parent is KtClassOrObject) {
getJvmInternalNameForImpl(typeMapper, parent)
getClassNameForClass(parent, typeMapper)
}
else if (parent != null) {
val asmType = CodegenBinding.asmTypeForAnonymousClass(typeMapper.bindingContext, element)
asmType.internalName
}
else {
NoResolveFileClassesProvider.getFileClassInternalName(file)
getClassNameForFile(file)
}
if (!withInlines) return NonCachedClassNames(parentInternalName)
@@ -399,29 +401,34 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
}
}
return CachedClassNames(NoResolveFileClassesProvider.getFileClassInternalName(file))
return CachedClassNames(getClassNameForFile(file))
}
private fun getClassNameForClass(klass: KtClassOrObject, typeMapper: JetTypeMapper) = getJvmInternalNameForImpl(typeMapper, klass)
private fun getClassNameForClass(klass: KtClassOrObject, typeMapper: JetTypeMapper) = klass.readAction { getJvmInternalNameForImpl(typeMapper, it) }
private fun getClassNameForFile(file: KtFile) = file.readAction { NoResolveFileClassesProvider.getFileClassInternalName(it) }
private val TYPES_TO_CALCULATE_CLASSNAME: Array<Class<out KtElement>> =
arrayOf(KtClassOrObject::class.java,
arrayOf(KtClass::class.java,
KtObjectDeclaration::class.java,
KtEnumEntry::class.java,
KtFunctionLiteral::class.java,
KtNamedFunction::class.java,
KtPropertyAccessor::class.java,
KtProperty::class.java,
KtAnonymousInitializer::class.java)
KtClassInitializer::class.java)
private fun getElementToCalculateClassName(notPositionedElement: PsiElement?): KtElement? {
if (notPositionedElement?.javaClass as Class<*> in TYPES_TO_CALCULATE_CLASSNAME) return notPositionedElement as KtElement
return PsiTreeUtil.getParentOfType(notPositionedElement, *TYPES_TO_CALCULATE_CLASSNAME)
return readAction { PsiTreeUtil.getParentOfType(notPositionedElement, *TYPES_TO_CALCULATE_CLASSNAME) }
}
fun getJvmInternalNameForPropertyOwner(typeMapper: JetTypeMapper, descriptor: PropertyDescriptor): String {
return typeMapper.mapOwner(
if (JvmAbi.isPropertyWithBackingFieldInOuterClass(descriptor)) descriptor.containingDeclaration else descriptor
).internalName
return descriptor.readAction {
typeMapper.mapOwner(
if (JvmAbi.isPropertyWithBackingFieldInOuterClass(it)) it.containingDeclaration else it
).internalName
}
}
private fun getJvmInternalNameForImpl(typeMapper: JetTypeMapper, ktClass: KtClassOrObject): String? {
@@ -439,21 +446,38 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
return emptyList()
}
else {
return runReadAction {
val result = hashSetOf<String>()
val searchResult = hashSetOf<KtElement>()
val functionName = function.readAction { it.name }
val task = Runnable {
ReferencesSearch.search(function, myDebugProcess.searchScope).forEach {
if (!it.isImportUsage()) {
val usage = it.element
if (usage is KtElement) {
//TODO recursive search
val names = classNamesForPosition(usage, true)
result.addAll(names)
if (!it.readAction { it.isImportUsage() }) {
val usage = (it.element as? KtElement)?.let { getElementToCalculateClassName(it) }
if (usage != null) {
searchResult.add(usage)
}
}
}
result.toList()
}
var isSuccess = true
ApplicationManager.getApplication().invokeAndWait(
{
isSuccess = ProgressManager.getInstance().runProcessWithProgressSynchronously(
task,
"Compute class names for function $functionName",
true,
myDebugProcess.project)
}, ModalityState.NON_MODAL)
if (!isSuccess) {
XDebugSessionImpl.NOTIFICATION_GROUP.createNotification(
"Debugger can skip some executions of $functionName method, because the computation of class names was interrupted", MessageType.WARNING
).notify(myDebugProcess.project)
}
// TODO recursive search
return searchResult.flatMap { classNamesForPosition(it, true) }
}
}
@@ -504,15 +528,17 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
val inlineFunction = parameter.containingDeclaration.source.getPsi() as? KtNamedFunction ?: return emptySet()
ReferencesSearch.search(inlineFunction, myDebugProcess.searchScope).forEach {
if (!it.isImportUsage()) {
val call = (it.element as? KtExpression)?.let { KtPsiUtil.getParentCallIfPresent(it) }
if (call != null) {
val resolvedCall = call.getResolvedCall(context)
val argument = resolvedCall?.valueArguments?.entries?.firstOrNull { it.key.original == parameter }?.value
if (argument != null) {
val argumentExpression = getArgumentExpression(argument.arguments.first())
if (argumentExpression is KtFunction) {
result.add(getCrossInlineArgumentClassName(argumentExpression, inlineFunction.name!!, context))
runReadAction {
if (!it.isImportUsage()) {
val call = (it.element as? KtExpression)?.let { KtPsiUtil.getParentCallIfPresent(it) }
if (call != null) {
val resolvedCall = call.getResolvedCall(context)
val argument = resolvedCall?.valueArguments?.entries?.firstOrNull { it.key.original == parameter }?.value
if (argument != null) {
val argumentExpression = getArgumentExpression(argument.arguments.first())
if (argumentExpression is KtFunction) {
result.add(getCrossInlineArgumentClassName(argumentExpression, inlineFunction.name!!, context))
}
}
}
}
@@ -536,3 +562,7 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
private fun ReferenceType.containsKotlinStrata() = availableStrata().contains("Kotlin")
}
private inline fun <U, V> U.readAction(crossinline f: (U) -> V): V {
return runReadAction { f(this) }
}
@@ -95,7 +95,7 @@ class KotlinDebuggerCaches(private val project: Project) {
}
fun <T: PsiElement> getOrComputeClassNames(psiElement: T, create: (T) -> ComputedClassNames): List<String> {
val cache = getInstance(psiElement.project)
val cache = getInstance(runReadAction { psiElement.project })
synchronized(cache.cachedClassNames) {
val classNamesCache = cache.cachedClassNames.value
@@ -112,11 +112,11 @@ class KotlinDebuggerCaches(private val project: Project) {
}
fun getOrCreateTypeMapper(psiElement: PsiElement): JetTypeMapper {
val cache = getInstance(psiElement.project)
val cache = getInstance(runReadAction { psiElement.project })
synchronized(cache.cachedTypeMappers) {
val typeMappersCache = cache.cachedTypeMappers.value
val file = psiElement.containingFile as KtFile
val file = runReadAction { psiElement.containingFile as KtFile }
val isInLibrary = LibraryUtil.findLibraryEntry(file.virtualFile, file.project) != null
if (!isInLibrary) {
@@ -142,33 +142,37 @@ class KotlinDebuggerCaches(private val project: Project) {
}
private fun createTypeMapperForLibraryFile(element: KtElement, file: KtFile): JetTypeMapper {
val analysisResult = element.analyzeAndGetResult()
return runReadAction {
val analysisResult = element.analyzeAndGetResult()
val state = GenerationState(
file.project,
ClassBuilderFactories.THROW_EXCEPTION,
analysisResult.moduleDescriptor,
analysisResult.bindingContext,
listOf(file))
state.beforeCompile()
return state.typeMapper
val state = GenerationState(
file.project,
ClassBuilderFactories.THROW_EXCEPTION,
analysisResult.moduleDescriptor,
analysisResult.bindingContext,
listOf(file))
state.beforeCompile()
state.typeMapper
}
}
private fun getElementToCreateTypeMapperForLibraryFile(element: PsiElement?) =
if (element is KtElement) element else PsiTreeUtil.getParentOfType(element, KtElement::class.java)!!
runReadAction { if (element is KtElement) element else PsiTreeUtil.getParentOfType(element, KtElement::class.java)!! }
private fun createTypeMapperForSourceFile(file: KtFile): JetTypeMapper {
val analysisResult = file.analyzeFullyAndGetResult()
analysisResult.throwIfError()
return runReadAction {
val analysisResult = file.analyzeFullyAndGetResult()
analysisResult.throwIfError()
val state = GenerationState(
file.project,
ClassBuilderFactories.THROW_EXCEPTION,
analysisResult.moduleDescriptor,
analysisResult.bindingContext,
listOf(file))
state.beforeCompile()
return state.typeMapper
val state = GenerationState(
file.project,
ClassBuilderFactories.THROW_EXCEPTION,
analysisResult.moduleDescriptor,
analysisResult.bindingContext,
listOf(file))
state.beforeCompile()
state.typeMapper
}
}
@TestOnly fun addTypeMapper(file: KtFile, typeMapper: JetTypeMapper) {