[light classes] extract KotlinLightClassFactory to service

^KT-53097
This commit is contained in:
Dmitry Gridin
2022-07-12 17:20:03 +02:00
committed by Space
parent 81a5b4070d
commit 0e17d9b452
6 changed files with 147 additions and 109 deletions
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.cli.jvm.compiler
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiManager
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.PsiSearchScopeUtil
import com.intellij.util.SmartList
@@ -27,16 +26,14 @@ import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.MemberScope
class CliKotlinAsJavaSupport(
project: Project,
private val project: Project,
private val traceHolder: CliTraceHolder
) : KotlinAsJavaSupport() {
private val psiManager = PsiManager.getInstance(project)
override fun getFacadeClassesInPackage(packageFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
return findFacadeFilesInPackage(packageFqName, scope)
.groupBy { it.javaFileFacadeFqName }
.mapNotNull { (facadeClassFqName, _) ->
KotlinLightClassFactory.createFacade(psiManager, facadeClassFqName, scope)
KotlinLightClassFactory.createFacade(project, facadeClassFqName, scope)
}
}
@@ -53,7 +50,7 @@ class CliKotlinAsJavaSupport(
.orEmpty()
override fun getFacadeClasses(facadeFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
return listOfNotNull(KotlinLightClassFactory.createFacade(psiManager, facadeFqName, scope))
return listOfNotNull(KotlinLightClassFactory.createFacade(project, facadeFqName, scope))
}
override fun getScriptClasses(scriptFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
@@ -43,6 +43,8 @@ import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.classes.FacadeCache
import org.jetbrains.kotlin.asJava.classes.KotlinK1LightClassFactory
import org.jetbrains.kotlin.asJava.classes.KotlinLightClassFactory
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.cli.common.*
@@ -63,8 +65,8 @@ import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleResolver
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
import org.jetbrains.kotlin.codegen.extensions.ClassFileFactoryFinalizerExtension
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.jetbrains.kotlin.compiler.plugin.registerInProject
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.extensions.*
@@ -673,6 +675,7 @@ class KotlinCoreEnvironment private constructor(
// ability to get text from annotations xml files
applicationEnvironment.registerFileType(PlainTextFileType.INSTANCE, "xml")
applicationEnvironment.registerParserDefinition(JavaParserDefinition())
applicationEnvironment.registerApplicationService(KotlinLightClassFactory::class.java, KotlinK1LightClassFactory())
}
// made public for Upsource
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2022 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.
*/
@file:Suppress("unused")
package org.jetbrains.kotlin.asJava.classes
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtScript
interface KotlinLightClassFactory {
fun createClass(classOrObject: KtClassOrObject): KtLightClassForSourceDeclaration?
fun createFacade(project: Project, facadeClassFqName: FqName, searchScope: GlobalSearchScope): KtLightClassForFacade?
fun createFacadeForSyntheticFile(facadeClassFqName: FqName, file: KtFile): KtLightClassForFacade
fun createScript(script: KtScript): KtLightClassForScript?
companion object {
private val instance: KotlinLightClassFactory
get() = ServiceManager.getService(KotlinLightClassFactory::class.java)
fun createClass(classOrObject: KtClassOrObject): KtLightClassForSourceDeclaration? {
return instance.createClass(classOrObject)
}
fun createFacade(project: Project, facadeClassFqName: FqName, searchScope: GlobalSearchScope): KtLightClassForFacade? {
return instance.createFacade(project, facadeClassFqName, searchScope)
}
fun createFacadeForSyntheticFile(facadeClassFqName: FqName, file: KtFile): KtLightClassForFacade {
return instance.createFacadeForSyntheticFile(facadeClassFqName, file)
}
fun createScript(script: KtScript): KtLightClassForScript? {
return instance.createScript(script)
}
}
}
@@ -27,7 +27,7 @@ class FacadeCache(private val project: Project) {
private inner class FacadeCacheData {
val cache = object : SLRUCache<FacadeCacheKey, ValueWrapper>(20, 30) {
override fun createValue(key: FacadeCacheKey): ValueWrapper =
KotlinLightClassFactory.createFacadeNoCache(key.fqName, key.searchScope, project)
KotlinK1LightClassFactory.createFacadeNoCache(key.fqName, key.searchScope, project)
?.let { ValueWrapper(it) }
?: ValueWrapper.Null
}
@@ -0,0 +1,95 @@
/*
* Copyright 2010-2022 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.asJava.classes
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiManager
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtCodeFragment
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtScript
class KotlinK1LightClassFactory : KotlinLightClassFactory {
override fun createClass(classOrObject: KtClassOrObject): KtLightClassForSourceDeclaration? =
CachedValuesManager.getCachedValue(classOrObject) {
CachedValueProvider.Result.create(
createClassNoCache(classOrObject),
KotlinModificationTrackerService.getInstance(classOrObject.project).outOfBlockModificationTracker,
)
}
override fun createFacade(
project: Project,
facadeClassFqName: FqName,
searchScope: GlobalSearchScope,
): KtLightClassForFacade? = FacadeCache.getInstance(project)[facadeClassFqName, searchScope]
override fun createFacadeForSyntheticFile(facadeClassFqName: FqName, file: KtFile): KtLightClassForFacade {
val project = file.project
val manager = PsiManager.getInstance(project)
return LightClassGenerationSupport.getInstance(project).run {
createUltraLightClassForFacade(manager, facadeClassFqName, listOf(file)) ?: error { "Unable to create UL class for facade" }
}
}
override fun createScript(script: KtScript): KtLightClassForScript? = CachedValuesManager.getCachedValue(script) {
CachedValueProvider.Result.create(
createScriptNoCache(script),
KotlinModificationTrackerService.getInstance(script.project).outOfBlockModificationTracker,
)
}
companion object {
fun createScriptNoCache(script: KtScript): KtLightClassForScript? {
val containingFile = script.containingFile
if (containingFile is KtCodeFragment) {
// Avoid building light classes for code fragments
return null
}
return LightClassGenerationSupport.getInstance(script.project).run {
createUltraLightClassForScript(script)
}
}
fun createClassNoCache(classOrObject: KtClassOrObject): KtLightClassForSourceDeclaration? {
val containingFile = classOrObject.containingFile
if (containingFile is KtCodeFragment) {
// Avoid building light classes for code fragments
return null
}
if (classOrObject.shouldNotBeVisibleAsLightClass()) {
return null
}
return LightClassGenerationSupport.getInstance(classOrObject.project).run {
createUltraLightClass(classOrObject)
}
}
fun createFacadeNoCache(fqName: FqName, searchScope: GlobalSearchScope, project: Project): KtLightClassForFacade? {
val sources = KotlinAsJavaSupport.getInstance(project)
.findFilesForFacade(fqName, searchScope)
.filterNot { it.isCompiled || it.isScript() }
if (sources.isEmpty()) return null
val manager = PsiManager.getInstance(project)
return LightClassGenerationSupport.getInstance(project).run {
if (!canCreateUltraLightClassForFacade(sources)) return null
createUltraLightClassForFacade(manager, fqName, sources)
?: error("Unable to create UL class for facade: $fqName for ${sources.joinToString { it.virtualFilePath }}")
}
}
}
}
@@ -1,101 +0,0 @@
/*
* Copyright 2010-2022 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.
*/
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
package org.jetbrains.kotlin.asJava.classes
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiManager
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtCodeFragment
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtScript
object KotlinLightClassFactory {
fun createClass(classOrObject: KtClassOrObject): KtLightClassForSourceDeclaration? =
CachedValuesManager.getCachedValue(classOrObject) {
CachedValueProvider.Result.create(
createClassNoCache(classOrObject),
KotlinModificationTrackerService.getInstance(classOrObject.project).outOfBlockModificationTracker,
)
}
fun createClassNoCache(classOrObject: KtClassOrObject): KtLightClassForSourceDeclaration? {
val containingFile = classOrObject.containingFile
if (containingFile is KtCodeFragment) {
// Avoid building light classes for code fragments
return null
}
if (classOrObject.shouldNotBeVisibleAsLightClass()) {
return null
}
return LightClassGenerationSupport.getInstance(classOrObject.project).run {
createUltraLightClass(classOrObject)
}
}
fun createFacadeNoCache(fqName: FqName, searchScope: GlobalSearchScope, project: Project): KtLightClassForFacade? {
val sources = KotlinAsJavaSupport.getInstance(project)
.findFilesForFacade(fqName, searchScope)
.filterNot { it.isCompiled || it.isScript() }
if (sources.isEmpty()) return null
val manager = PsiManager.getInstance(project)
return LightClassGenerationSupport.getInstance(project).run {
if (!canCreateUltraLightClassForFacade(sources)) return null
createUltraLightClassForFacade(manager, fqName, sources)
?: error("Unable to create UL class for facade: $fqName for ${sources.joinToString { it.virtualFilePath }}")
}
}
fun createFacade(
manager: PsiManager,
facadeClassFqName: FqName,
searchScope: GlobalSearchScope
): KtLightClassForFacade? {
return FacadeCache.getInstance(manager.project)[facadeClassFqName, searchScope]
}
fun createFacadeForSyntheticFile(
facadeClassFqName: FqName,
file: KtFile
): KtLightClassForFacade {
val project = file.project
val manager = PsiManager.getInstance(project)
return LightClassGenerationSupport.getInstance(project).run {
createUltraLightClassForFacade(manager, facadeClassFqName, listOf(file)) ?: error { "Unable to create UL class for facade" }
}
}
fun createScript(script: KtScript): KtLightClassForScript? =
CachedValuesManager.getCachedValue(script) {
CachedValueProvider.Result.create(
createScriptNoCache(script),
KotlinModificationTrackerService.getInstance(script.project).outOfBlockModificationTracker,
)
}
fun createScriptNoCache(script: KtScript): KtLightClassForScript? {
val containingFile = script.containingFile
if (containingFile is KtCodeFragment) {
// Avoid building light classes for code fragments
return null
}
return LightClassGenerationSupport.getInstance(script.project).run {
createUltraLightClassForScript(script)
}
}
}