diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt index 1020340e79e..04d302765f8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt @@ -31,18 +31,15 @@ import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.impl.compiled.ClsFileImpl import com.intellij.psi.search.searches.ReferencesSearch -import com.intellij.psi.util.* +import com.intellij.psi.util.PsiTreeUtil 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.ReferenceType import com.sun.jdi.request.ClassPrepareRequest -import org.jetbrains.annotations.TestOnly -import org.jetbrains.kotlin.codegen.ClassBuilderFactories import org.jetbrains.kotlin.codegen.binding.CodegenBinding import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil -import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.JetTypeMapper import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor @@ -51,8 +48,6 @@ import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil import org.jetbrains.kotlin.fileClasses.NoResolveFileClassesProvider import org.jetbrains.kotlin.fileClasses.getFileClassInternalName import org.jetbrains.kotlin.fileClasses.internalNameWithoutInnerClasses -import org.jetbrains.kotlin.idea.caches.resolve.analyzeAndGetResult -import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils import org.jetbrains.kotlin.idea.debugger.breakpoints.getLambdasAtLineIfAny import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinCodeFragmentFactory @@ -78,8 +73,6 @@ import com.intellij.debugger.engine.DebuggerUtils as JDebuggerUtils class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiRequestPositionManager, PositionManagerEx() { - private val myTypeMappers = WeakHashMap>() - override fun evaluateCondition(context: EvaluationContext, frame: StackFrameProxyImpl, location: Location, expression: String): ThreeState? { return ThreeState.UNSURE } @@ -167,10 +160,8 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq if (literalsOrFunctions.isEmpty()) return null; val isInLibrary = LibraryUtil.findLibraryEntry(file.virtualFile, file.project) != null - val typeMapper = if (!isInLibrary) - prepareTypeMapper(file) - else - createTypeMapperForLibraryFile(file.findElementAt(start), file) + val elementAt = file.findElementAt(start) ?: return null + val typeMapper = KotlinPositionManagerCache.getOrCreateTypeMapper(elementAt) val currentLocationClassName = JvmClassName.byFqNameWithoutInnerClasses(FqName(currentLocationFqName)).internalName for (literal in literalsOrFunctions) { @@ -274,7 +265,7 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq val file = sourcePosition.file.containingFile as KtFile val isInLibrary = LibraryUtil.findLibraryEntry(file.virtualFile, file.project) != null lambdas.flatMap { - val typeMapper = if (!isInLibrary) prepareTypeMapper(file) else createTypeMapperForLibraryFile(it, file) + val typeMapper = KotlinPositionManagerCache.getOrCreateTypeMapper(it) getInternalClassNameForElement(it, typeMapper, file, isInLibrary) } } @@ -295,30 +286,14 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq element -> val file = element.containingFile as KtFile val isInLibrary = LibraryUtil.findLibraryEntry(file.virtualFile, file.project) != null - val typeMapper = if (!isInLibrary) prepareTypeMapper(file) else createTypeMapperForLibraryFile(element, file) + val typeMapper = KotlinPositionManagerCache.getOrCreateTypeMapper(element) + getInternalClassNameForElement(element, typeMapper, file, isInLibrary) } } } } - private fun prepareTypeMapper(file: KtFile): JetTypeMapper { - val key = createKeyForTypeMapper(file) - - var value: CachedValue? = myTypeMappers.get(key) - if (value == null) { - value = CachedValuesManager.getManager(file.project).createCachedValue( - { - val typeMapper = createTypeMapper(file) - CachedValueProvider.Result(typeMapper, PsiModificationTracker.MODIFICATION_COUNT) - }, false) - - myTypeMappers.put(key, value) - } - - return value.value - } - override fun locationsOfLine(type: ReferenceType, position: SourcePosition): List { if (position.file !is KtFile) { throw NoDataException.INSTANCE @@ -352,13 +327,6 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq } } - @TestOnly fun addTypeMapper(file: KtFile, typeMapper: JetTypeMapper) { - val value = CachedValuesManager.getManager(file.project).createCachedValue( - { CachedValueProvider.Result(typeMapper, PsiModificationTracker.MODIFICATION_COUNT) }, false) - val key = createKeyForTypeMapper(file) - myTypeMappers.put(key, value) - } - private fun getInternalClassNameForElement( notPositionedElement: PsiElement?, typeMapper: JetTypeMapper, @@ -458,9 +426,6 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq element is KtPropertyAccessor || PsiTreeUtil.getParentOfType(element, KtProperty::class.java, KtPropertyAccessor::class.java) is KtPropertyAccessor - private fun getElementToCreateTypeMapperForLibraryFile(element: PsiElement?) = - if (element is KtElement) element else PsiTreeUtil.getParentOfType(element, KtElement::class.java) - private fun getJvmInternalNameForImpl(typeMapper: JetTypeMapper, ktClass: KtClassOrObject): String? { val classDescriptor = typeMapper.bindingContext.get(BindingContext.CLASS, ktClass) ?: return null @@ -471,22 +436,6 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq return typeMapper.mapClass(classDescriptor).internalName } - private fun createTypeMapperForLibraryFile(notPositionedElement: PsiElement?, file: KtFile): JetTypeMapper { - val element = getElementToCreateTypeMapperForLibraryFile(notPositionedElement) - val analysisResult = element!!.analyzeAndGetResult() - - val state = GenerationState(file.project, ClassBuilderFactories.THROW_EXCEPTION, - analysisResult.moduleDescriptor, analysisResult.bindingContext, listOf(file)) - state.beforeCompile() - return state.typeMapper - } - - fun isInlinedLambda(functionLiteral: KtFunction, context: BindingContext): Boolean { - return InlineUtil.isInlinedArgument(functionLiteral, context, false) - } - - private fun createKeyForTypeMapper(file: KtFile) = NoResolveFileClassesProvider.getFileClassInternalName(file) - private fun findInlinedCalls(function: KtNamedFunction, context: BindingContext): Set { return runReadAction { val result = hashSetOf() @@ -589,22 +538,4 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq private fun ReferenceType.containsKotlinStrata() = availableStrata().contains("Kotlin") private fun String?.toSet() = if (this == null) emptySet() else setOf(this) - - companion object { - fun createTypeMapper(file: KtFile): JetTypeMapper { - val project = file.project - - val analysisResult = file.analyzeFullyAndGetResult() - analysisResult.throwIfError() - - val state = GenerationState( - project, - ClassBuilderFactories.THROW_EXCEPTION, - analysisResult.moduleDescriptor, - analysisResult.bindingContext, - listOf(file)) - state.beforeCompile() - return state.typeMapper - } - } } diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManagerCache.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManagerCache.kt index d555d06307b..72ec7b11272 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManagerCache.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManagerCache.kt @@ -18,10 +18,22 @@ package org.jetbrains.kotlin.idea.debugger import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.project.Project +import com.intellij.openapi.roots.libraries.LibraryUtil import com.intellij.psi.PsiElement import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiModificationTracker +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.annotations.TestOnly +import org.jetbrains.kotlin.codegen.ClassBuilderFactories +import org.jetbrains.kotlin.codegen.state.GenerationState +import org.jetbrains.kotlin.codegen.state.JetTypeMapper +import org.jetbrains.kotlin.fileClasses.NoResolveFileClassesProvider +import org.jetbrains.kotlin.fileClasses.getFileClassInternalName +import org.jetbrains.kotlin.idea.caches.resolve.analyzeAndGetResult +import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.psi.KtFile import java.util.* class KotlinPositionManagerCache(private val project: Project) { @@ -32,6 +44,12 @@ class KotlinPositionManagerCache(private val project: Project) { hashMapOf(), PsiModificationTracker.MODIFICATION_COUNT) }, false) + private val cachedTypeMappers = CachedValuesManager.getManager(project).createCachedValue( + { + CachedValueProvider.Result>( + hashMapOf(), PsiModificationTracker.MODIFICATION_COUNT) + }, false) + companion object { fun getOrComputeClassNames(psiElement: PsiElement, create: (PsiElement) -> Collection): Collection { val cache = getInstance(psiElement.project) @@ -48,6 +66,68 @@ class KotlinPositionManagerCache(private val project: Project) { } } + fun getOrCreateTypeMapper(psiElement: PsiElement): JetTypeMapper { + val cache = getInstance(psiElement.project) + synchronized(cache.cachedTypeMappers) { + val typeMappersCache = cache.cachedTypeMappers.value + + val file = psiElement.containingFile as KtFile + val key = createKeyForTypeMapper(file) + + val cachedValue = typeMappersCache[key] + if (cachedValue != null) return cachedValue + + val isInLibrary = LibraryUtil.findLibraryEntry(file.virtualFile, file.project) != null + if (!isInLibrary) { + val newValue = createTypeMapperForSourceFile(file) + typeMappersCache[key] = newValue + return newValue + } + else { + return createTypeMapperForLibraryFile(psiElement, file) + } + } + } + private fun getInstance(project: Project) = ServiceManager.getService(project, KotlinPositionManagerCache::class.java)!! + + private fun createKeyForTypeMapper(file: KtFile) = NoResolveFileClassesProvider.getFileClassInternalName(file) + + private fun createTypeMapperForLibraryFile(notPositionedElement: PsiElement?, file: KtFile): JetTypeMapper { + val element = getElementToCreateTypeMapperForLibraryFile(notPositionedElement) + val analysisResult = element.analyzeAndGetResult() + + val state = GenerationState( + file.project, + ClassBuilderFactories.THROW_EXCEPTION, + analysisResult.moduleDescriptor, + analysisResult.bindingContext, + listOf(file)) + state.beforeCompile() + return state.typeMapper + } + + private fun getElementToCreateTypeMapperForLibraryFile(element: PsiElement?) = + if (element is KtElement) element else PsiTreeUtil.getParentOfType(element, KtElement::class.java)!! + + private fun createTypeMapperForSourceFile(file: KtFile): JetTypeMapper { + 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 + } + + @TestOnly fun addTypeMapper(file: KtFile, typeMapper: JetTypeMapper) { + val cache = getInstance(file.project) + val key = createKeyForTypeMapper(file) + cache.cachedTypeMappers.value[key] = typeMapper + } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt index 8244add44ef..1cd8f2e00b9 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt @@ -41,7 +41,7 @@ fun isInsideInlineArgument(inlineArgument: KtFunction, location: Location, debug } private fun lambdaOrdinalIndex(elementAt: KtFunction): Int { - val typeMapper = KotlinPositionManager.createTypeMapper(elementAt.getContainingKtFile()) + val typeMapper = KotlinPositionManagerCache.getOrCreateTypeMapper(elementAt) val type = CodegenBinding.asmTypeForAnonymousClass(typeMapper.bindingContext, elementAt) return type.className.substringAfterLast("$").toInt() diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractPositionManagerTest.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractPositionManagerTest.java index c2c05ef019e..bac6ff681a3 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractPositionManagerTest.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractPositionManagerTest.java @@ -93,7 +93,7 @@ public abstract class AbstractPositionManagerTest extends KotlinLightCodeInsight assertNotNull(positionManager); for (KtFile file : files) { - positionManager.addTypeMapper(file, state.getTypeMapper()); + KotlinPositionManagerCache.Companion.addTypeMapper(file, state.getTypeMapper()); } return positionManager;