From 2d08d29dac388f4810ef7c1f35943df2990e02b7 Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Mon, 8 May 2023 17:51:39 +0200 Subject: [PATCH] [Java Resolution] use a smart psi pointer to store PSI in JavaElement for IDE `JavaElement`s are reused between read actions, so underlying PSI elements might be invalidated when using hard PSI references ^KT-58194 fixed --- .../analysis-api-impl-base/build.gradle.kts | 1 + .../JavaElementPsiSourceWithSmartPointer.kt | 28 ++++++++++++ ...avaElementSourceWithSmartPointerFactory.kt | 28 ++++++++++++ .../JavaElementTypeSourceWithSmartPointer.kt | 29 +++++++++++++ .../build.gradle.kts | 1 + .../structure/StandaloneProjectFactory.kt | 43 ++++++++++++++++--- 6 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/java/source/JavaElementPsiSourceWithSmartPointer.kt create mode 100644 analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/java/source/JavaElementSourceWithSmartPointerFactory.kt create mode 100644 analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/java/source/JavaElementTypeSourceWithSmartPointer.kt diff --git a/analysis/analysis-api-impl-base/build.gradle.kts b/analysis/analysis-api-impl-base/build.gradle.kts index 5ab9c5e90f5..a9564eb3745 100644 --- a/analysis/analysis-api-impl-base/build.gradle.kts +++ b/analysis/analysis-api-impl-base/build.gradle.kts @@ -8,6 +8,7 @@ dependencies { api(project(":analysis:analysis-api")) api(project(":analysis:analysis-api-impl-barebone")) api(project(":analysis:kt-references")) + api(project(":compiler:resolution.common.jvm")) api(intellijCore()) implementation(project(":analysis:analysis-internal-utils")) diff --git a/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/java/source/JavaElementPsiSourceWithSmartPointer.kt b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/java/source/JavaElementPsiSourceWithSmartPointer.kt new file mode 100644 index 00000000000..82c05d58032 --- /dev/null +++ b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/java/source/JavaElementPsiSourceWithSmartPointer.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.analysis.api.impl.base.java.source + +import com.intellij.psi.PsiElement +import com.intellij.psi.SmartPsiElementPointer +import org.jetbrains.kotlin.load.java.structure.impl.source.JavaElementPsiSource +import org.jetbrains.kotlin.load.java.structure.impl.source.JavaElementSourceFactory + +internal class JavaElementPsiSourceWithSmartPointer( + private val pointer: SmartPsiElementPointer, + override val factory: JavaElementSourceFactory, +) : JavaElementPsiSource() { + + override val psi: PSI + get() { + return pointer.element + ?: error("Cannot restore a PsiElement from $pointer") + } + + override fun toString(): String { + return pointer.element?.toString() ?: "Cannot restore a PsiElement from $pointer" + } +} + diff --git a/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/java/source/JavaElementSourceWithSmartPointerFactory.kt b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/java/source/JavaElementSourceWithSmartPointerFactory.kt new file mode 100644 index 00000000000..3cfb561e2fe --- /dev/null +++ b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/java/source/JavaElementSourceWithSmartPointerFactory.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.analysis.api.impl.base.java.source + +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiType +import com.intellij.psi.SmartPointerManager +import com.intellij.psi.SmartTypePointerManager +import org.jetbrains.kotlin.load.java.structure.impl.source.JavaElementPsiSource +import org.jetbrains.kotlin.load.java.structure.impl.source.JavaElementSourceFactory +import org.jetbrains.kotlin.load.java.structure.impl.source.JavaElementTypeSource + + class JavaElementSourceWithSmartPointerFactory(project: Project) : JavaElementSourceFactory() { + private val smartTypePointerManager = SmartTypePointerManager.getInstance(project) + private val smartPsiPointerManager = SmartPointerManager.getInstance(project) + + override fun createPsiSource(psi: PSI): JavaElementPsiSource { + return JavaElementPsiSourceWithSmartPointer(smartPsiPointerManager.createSmartPsiElementPointer(psi), this) + } + + override fun createTypeSource(type: TYPE): JavaElementTypeSource { + return JavaElementTypeSourceWithSmartPointer(smartTypePointerManager.createSmartTypePointer(type), this) + } +} \ No newline at end of file diff --git a/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/java/source/JavaElementTypeSourceWithSmartPointer.kt b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/java/source/JavaElementTypeSourceWithSmartPointer.kt new file mode 100644 index 00000000000..8163aa54893 --- /dev/null +++ b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/java/source/JavaElementTypeSourceWithSmartPointer.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.analysis.api.impl.base.java.source + +import com.intellij.psi.PsiType +import com.intellij.psi.SmartTypePointer +import org.jetbrains.kotlin.load.java.structure.impl.source.JavaElementSourceFactory +import org.jetbrains.kotlin.load.java.structure.impl.source.JavaElementTypeSource + +internal class JavaElementTypeSourceWithSmartPointer( + private val pointer: SmartTypePointer, + override val factory: JavaElementSourceFactory, +) : JavaElementTypeSource() { + override val type: TYPE + get() { + val type = pointer.type + ?: error("Cannot restore a PsiType from $pointer") + + @Suppress("UNCHECKED_CAST") + return type as TYPE + } + + override fun toString(): String { + return pointer.type?.toString() ?: "Cannot restore a PsiType from $pointer" + } +} \ No newline at end of file diff --git a/analysis/analysis-api-standalone/analysis-api-standalone-base/build.gradle.kts b/analysis/analysis-api-standalone/analysis-api-standalone-base/build.gradle.kts index b7e826a0c5e..c476d1d8486 100644 --- a/analysis/analysis-api-standalone/analysis-api-standalone-base/build.gradle.kts +++ b/analysis/analysis-api-standalone/analysis-api-standalone-base/build.gradle.kts @@ -7,6 +7,7 @@ dependencies { implementation(intellijCore()) implementation(kotlinStdlib()) implementation(project(":compiler:psi")) + implementation(project(":analysis:analysis-api-impl-base")) api(project(":compiler:cli-base")) api(project(":analysis:analysis-api")) api(project(":analysis:analysis-api-providers")) diff --git a/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/StandaloneProjectFactory.kt b/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/StandaloneProjectFactory.kt index 5f476f7f900..adbc32255d2 100644 --- a/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/StandaloneProjectFactory.kt +++ b/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/StandaloneProjectFactory.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.analysis.api.standalone.base.project.structure import com.intellij.codeInsight.ExternalAnnotationsManager import com.intellij.codeInsight.InferredAnnotationsManager +import com.intellij.core.CoreApplicationEnvironment import com.intellij.core.CoreJavaFileManager import com.intellij.core.CorePackageIndex import com.intellij.ide.highlighter.JavaFileType @@ -15,14 +16,16 @@ import com.intellij.openapi.Disposable import com.intellij.openapi.roots.PackageIndex import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFileManager -import com.intellij.psi.PsiDirectory -import com.intellij.psi.PsiFileSystemItem -import com.intellij.psi.PsiJavaFile +import com.intellij.psi.* import com.intellij.psi.impl.file.impl.JavaFileManager +import com.intellij.psi.impl.smartPointers.PsiClassReferenceTypePointerFactory +import com.intellij.psi.impl.smartPointers.SmartPointerManagerImpl +import com.intellij.psi.impl.smartPointers.SmartTypePointerManagerImpl import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.ProjectScope import com.intellij.util.io.URLUtil.JAR_PROTOCOL import com.intellij.util.io.URLUtil.JAR_SEPARATOR +import org.jetbrains.kotlin.analysis.api.impl.base.java.source.JavaElementSourceWithSmartPointerFactory import org.jetbrains.kotlin.analysis.project.structure.* import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.jvm.compiler.* @@ -35,9 +38,11 @@ import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleResolver import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem import org.jetbrains.kotlin.cli.jvm.modules.JavaModuleGraph import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.load.java.structure.impl.source.JavaElementSourceFactory import org.jetbrains.kotlin.load.kotlin.MetadataFinderFactory import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory import org.jetbrains.kotlin.resolve.ModuleAnnotationsResolver +import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addToStdlib.popLast @@ -51,12 +56,31 @@ object StandaloneProjectFactory { ): KotlinCoreProjectEnvironment { val applicationEnvironment = KotlinCoreEnvironment.getOrCreateApplicationEnvironmentForTests(applicationDisposable, compilerConfiguration) + registerApplicationExtensionPoints(applicationEnvironment, applicationDisposable) return KotlinCoreProjectEnvironment(projectDisposable, applicationEnvironment).apply { registerJavaPsiFacade(project) } } + private fun registerApplicationExtensionPoints( + applicationEnvironment: KotlinCoreApplicationEnvironment, + applicationDisposable: Disposable, + ) { + val applicationArea = applicationEnvironment.application.extensionArea + + if (applicationArea.hasExtensionPoint(ClassTypePointerFactory.EP_NAME)) return + KotlinCoreEnvironment.underApplicationLock { + if (applicationArea.hasExtensionPoint(ClassTypePointerFactory.EP_NAME)) return@underApplicationLock + CoreApplicationEnvironment.registerApplicationExtensionPoint( + ClassTypePointerFactory.EP_NAME, + ClassTypePointerFactory::class.java + ) + applicationArea.getExtensionPoint(ClassTypePointerFactory.EP_NAME) + .registerExtension(PsiClassReferenceTypePointerFactory(), applicationDisposable) + } + } + private fun registerJavaPsiFacade(project: MockProject) { with(project) { registerService( @@ -83,7 +107,14 @@ object StandaloneProjectFactory { val project = environment.project KotlinCoreEnvironment.registerProjectExtensionPoints(project.extensionArea) - KotlinCoreEnvironment.registerProjectServices(project) + with(project) { + registerService(SmartTypePointerManager::class.java, SmartTypePointerManagerImpl::class.java) + registerService(SmartPointerManager::class.java, SmartPointerManagerImpl::class.java) + registerService(JavaElementSourceFactory::class.java, JavaElementSourceWithSmartPointerFactory::class.java) + + registerService(KotlinJavaPsiFacade::class.java, KotlinJavaPsiFacade(this)) + registerService(ModuleAnnotationsResolver::class.java, CliModuleAnnotationsResolver()) + } project.registerService(ProjectStructureProvider::class.java, projectStructureProvider) initialiseVirtualFileFinderServices(environment, modules, sourceFiles, languageVersionSettings, jdkHome) @@ -202,14 +233,14 @@ object StandaloneProjectFactory { fun getAllBinaryRoots( modules: List, - environment: KotlinCoreProjectEnvironment + environment: KotlinCoreProjectEnvironment, ): List = withAllTransitiveDependencies(modules) .filterIsInstance() .flatMap { it.getJavaRoots(environment) } fun getVirtualFilesForLibraryRoots( roots: Collection, - environment: KotlinCoreProjectEnvironment + environment: KotlinCoreProjectEnvironment, ): List { return roots.mapNotNull { path -> val pathString = path.toAbsolutePath().toString()