[ULC] Move UltraLightSupport to service provided bridge

This commit is contained in:
Igor Yakovlev
2020-05-21 17:08:26 +03:00
parent 7cb2631991
commit 55bb8f24e3
5 changed files with 172 additions and 52 deletions
@@ -27,10 +27,26 @@ import org.jetbrains.kotlin.asJava.builder.LightClassDataHolderImpl
import org.jetbrains.kotlin.asJava.classes.KtUltraLightClass
import org.jetbrains.kotlin.asJava.classes.KtUltraLightClassForFacade
import org.jetbrains.kotlin.asJava.classes.KtUltraLightClassForScript
import org.jetbrains.kotlin.asJava.classes.*
import org.jetbrains.kotlin.codegen.ClassBuilderMode
import org.jetbrains.kotlin.codegen.JvmCodegenUtil
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.components.JavaDeprecationSettings
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.deprecation.CoroutineCompatibilitySupport
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.types.KotlinType
/**
* This class solves the problem of interdependency between analyzing Kotlin code and generating JetLightClasses
@@ -46,16 +62,85 @@ import org.jetbrains.kotlin.resolve.BindingContext
*/
class CliLightClassGenerationSupport(private val traceHolder: CliTraceHolder) : LightClassGenerationSupport() {
private val ultraLightSupport = object : KtUltraLightSupport {
private val languageVersionSettings: LanguageVersionSettings
get() = getContext().languageVersionSettings ?: LanguageVersionSettingsImpl.DEFAULT
override val isReleasedCoroutine
get() = languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines)
override val moduleDescriptor get() = traceHolder.module
override val moduleName: String get() = JvmCodegenUtil.getModuleName(moduleDescriptor)
override val deprecationResolver: DeprecationResolver
get() = DeprecationResolver(
LockBasedStorageManager.NO_LOCKS,
languageVersionSettings,
CoroutineCompatibilitySupport.ENABLED,
JavaDeprecationSettings
)
override val typeMapper: KotlinTypeMapper by lazyPub {
KotlinTypeMapper(
BindingContext.EMPTY,
ClassBuilderMode.LIGHT_CLASSES,
moduleName,
languageVersionSettings,
jvmTarget = JvmTarget.JVM_1_8,
typePreprocessor = KotlinType::cleanFromAnonymousTypes,
namePreprocessor = ::tryGetPredefinedName
)
}
}
override fun createUltraLightClassForFacade(
manager: PsiManager,
facadeClassFqName: FqName,
lightClassDataCache: CachedValue<LightClassDataHolder.ForFacade>,
files: Collection<KtFile>
): KtUltraLightClassForFacade? = null
): KtUltraLightClassForFacade? {
override fun createUltraLightClass(element: KtClassOrObject): KtUltraLightClass? = null
if (files.any { it.isScript() }) return null
override fun createUltraLightClassForScript(script: KtScript): KtUltraLightClassForScript? = null
val filesToSupports: List<Pair<KtFile, KtUltraLightSupport>> = files.map {
it to UltraLightSupportViaService(it)
}
return KtUltraLightClassForFacade(
manager,
facadeClassFqName,
lightClassDataCache,
files,
filesToSupports
)
}
override fun createUltraLightClass(element: KtClassOrObject): KtUltraLightClass? {
if (element.shouldNotBeVisibleAsLightClass()) {
return null
}
return UltraLightSupportViaService(element).let { support ->
when {
element is KtObjectDeclaration && element.isObjectLiteral() ->
KtUltraLightClassForAnonymousDeclaration(element, support)
element.safeIsLocal() ->
KtUltraLightClassForLocalDeclaration(element, support)
(element.hasModifier(KtTokens.INLINE_KEYWORD)) ->
KtUltraLightInlineClass(element, support)
else -> KtUltraLightClass(element, support)
}
}
}
override fun createUltraLightClassForScript(script: KtScript): KtUltraLightClassForScript? =
KtUltraLightClassForScript(script, support = UltraLightSupportViaService(script))
override fun getUltraLightClassSupport(element: KtElement): KtUltraLightSupport = ultraLightSupport
override fun createDataHolderForClass(classOrObject: KtClassOrObject, builder: LightClassBuilder): LightClassDataHolder.ForClass {
//force resolve companion for light class generation
@@ -89,4 +174,4 @@ class CliLightClassGenerationSupport(private val traceHolder: CliTraceHolder) :
override fun analyzeAnnotation(element: KtAnnotationEntry) = traceHolder.bindingContext.get(BindingContext.ANNOTATION, element)
override fun analyzeWithContent(element: KtClassOrObject) = traceHolder.bindingContext
}
}
@@ -51,6 +51,8 @@ abstract class LightClassGenerationSupport {
abstract fun createUltraLightClassForScript(script: KtScript): KtUltraLightClassForScript?
abstract fun getUltraLightClassSupport(element: KtElement): KtUltraLightSupport
abstract fun createUltraLightClassForFacade(
manager: PsiManager,
facadeClassFqName: FqName,
@@ -6,22 +6,23 @@
package org.jetbrains.kotlin.asJava.classes
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtAnnotated
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
import java.util.concurrent.ConcurrentMap
interface KtUltraLightSupport {
val moduleName: String
fun findAnnotation(owner: KtAnnotated, fqName: FqName): Pair<KtAnnotationEntry, AnnotationDescriptor>?
val deprecationResolver: DeprecationResolver
val typeMapper: KotlinTypeMapper
val moduleDescriptor: ModuleDescriptor
val isReleasedCoroutine: Boolean
fun hasAlias(file: KtFile, shortName: Name?): Boolean = false
companion object {
// This property may be removed once IntelliJ versions earlier than 2018.3 become unsupported
@@ -31,3 +32,24 @@ interface KtUltraLightSupport {
var forceUsingOldLightClasses = false
}
}
class UltraLightSupportViaService(private val ktElement: KtElement) : KtUltraLightSupport {
private val serviceProvidedSupport: KtUltraLightSupport
get() = LightClassGenerationSupport.getInstance(ktElement.project).getUltraLightClassSupport(ktElement)
override val moduleName: String
get() = serviceProvidedSupport.moduleName
override val deprecationResolver: DeprecationResolver
get() = serviceProvidedSupport.deprecationResolver
override val typeMapper: KotlinTypeMapper
get() = serviceProvidedSupport.typeMapper
override val moduleDescriptor: ModuleDescriptor
get() = serviceProvidedSupport.moduleDescriptor
override val isReleasedCoroutine: Boolean
get() = serviceProvidedSupport.isReleasedCoroutine
}
@@ -16,9 +16,13 @@ import com.intellij.psi.impl.compiled.ClsTypeElementImpl
import com.intellij.psi.impl.compiled.SignatureParsing
import com.intellij.psi.impl.compiled.StubBuildingVisitor
import com.intellij.psi.impl.light.*
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.PsiModificationTracker
import com.intellij.psi.util.TypeConversionUtil
import com.intellij.util.BitUtil.isSet
import com.intellij.util.IncorrectOperationException
import com.intellij.util.containers.ConcurrentFactoryMap
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.UltraLightClassModifierExtension
@@ -36,11 +40,14 @@ import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter
import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME
@@ -50,12 +57,14 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.jetbrains.kotlin.types.replace
import org.jetbrains.kotlin.types.typeUtil.supertypes
import org.jetbrains.org.objectweb.asm.Opcodes
import java.text.StringCharacterIterator
import java.util.concurrent.ConcurrentMap
private interface TypeParametersSupport<D, T> {
fun parameters(declaration: D): List<T>
@@ -72,7 +81,7 @@ private val supportForDescriptor = object : TypeParametersSupport<CallableMember
override fun hasNonTrivialBounds(
declaration: CallableMemberDescriptor,
typeParameter: TypeParameterDescriptor
typeParameter: TypeParameterDescriptor,
) = typeParameter.upperBounds.any { !KotlinBuiltIns.isDefaultBound(it) }
override fun asDescriptor(typeParameter: TypeParameterDescriptor) = typeParameter
@@ -86,7 +95,7 @@ private val supportForSourceDeclaration = object : TypeParametersSupport<KtTypeP
override fun hasNonTrivialBounds(
declaration: KtTypeParameterListOwner,
typeParameter: KtTypeParameter
typeParameter: KtTypeParameter,
) = typeParameter.extendsBound != null || declaration.typeConstraints.isNotEmpty()
override fun asDescriptor(typeParameter: KtTypeParameter) = typeParameter.resolve() as? TypeParameterDescriptor
@@ -95,21 +104,21 @@ private val supportForSourceDeclaration = object : TypeParametersSupport<KtTypeP
internal fun buildTypeParameterListForDescriptor(
declaration: CallableMemberDescriptor,
owner: PsiTypeParameterListOwner,
support: KtUltraLightSupport
support: KtUltraLightSupport,
): PsiTypeParameterList = buildTypeParameterList(declaration, owner, support, supportForDescriptor)
internal fun buildTypeParameterListForSourceDeclaration(
declaration: KtTypeParameterListOwner,
owner: PsiTypeParameterListOwner,
support: KtUltraLightSupport
support: KtUltraLightSupport,
): PsiTypeParameterList = buildTypeParameterList(declaration, owner, support, supportForSourceDeclaration)
private fun <D, T> buildTypeParameterList(
declaration: D,
owner: PsiTypeParameterListOwner,
support: KtUltraLightSupport,
typeParametersSupport: TypeParametersSupport<D, T>
typeParametersSupport: TypeParametersSupport<D, T>,
): PsiTypeParameterList {
val tpList = KotlinLightTypeParameterListBuilder(owner)
@@ -152,19 +161,20 @@ internal fun KtDeclaration.getKotlinType(): KotlinType? {
internal fun KtDeclaration.resolve() = LightClassGenerationSupport.getInstance(project).resolveToDescriptor(this)
internal fun KtElement.analyze() = LightClassGenerationSupport.getInstance(project).analyze(this)
internal fun KtAnnotationEntry.analyzeAnnotation() = LightClassGenerationSupport.getInstance(project).analyzeAnnotation(this)
// copy-pasted from kotlinInternalUastUtils.kt and post-processed
internal fun KotlinType.asPsiType(
support: KtUltraLightSupport,
mode: TypeMappingMode,
psiContext: PsiElement
psiContext: PsiElement,
): PsiType = support.mapType(psiContext) { typeMapper, signatureWriter ->
typeMapper.mapType(this, signatureWriter, mode)
}
internal fun KtUltraLightSupport.mapType(
psiContext: PsiElement,
mapTypeToSignatureWriter: (KotlinTypeMapper, JvmSignatureWriter) -> Unit
mapTypeToSignatureWriter: (KotlinTypeMapper, JvmSignatureWriter) -> Unit,
): PsiType {
val signatureWriter = BothSignatureWriter(BothSignatureWriter.Mode.SKIP_CHECKS)
mapTypeToSignatureWriter(typeMapper, signatureWriter)
@@ -174,7 +184,7 @@ internal fun KtUltraLightSupport.mapType(
fun createTypeFromCanonicalText(
canonicalSignature: String,
psiContext: PsiElement
psiContext: PsiElement,
): PsiType {
val signature = StringCharacterIterator(canonicalSignature)
@@ -231,7 +241,7 @@ fun KotlinType.cleanFromAnonymousTypes(): KotlinType? {
fun KtUltraLightClass.createGeneratedMethodFromDescriptor(
descriptor: FunctionDescriptor,
declarationForOrigin: KtDeclaration? = null
declarationForOrigin: KtDeclaration? = null,
): KtLightMethod {
val kotlinOrigin =
@@ -245,7 +255,7 @@ fun KtUltraLightClass.createGeneratedMethodFromDescriptor(
}
private fun KtUltraLightClass.lightMethod(
descriptor: FunctionDescriptor
descriptor: FunctionDescriptor,
): LightMethodBuilder {
val name = if (descriptor is ConstructorDescriptor) name else support.typeMapper.mapFunctionName(descriptor, OwnerKind.IMPLEMENTATION)
@@ -264,7 +274,7 @@ private fun KtUltraLightClass.lightMethod(
LightParameterListBuilder(manager, language),
object : LightModifierList(manager, language) {
override fun hasModifierProperty(name: String) = ModifierFlags.hasModifierProperty(name, accessFlags)
}
},
)
}
@@ -425,7 +435,7 @@ private fun ConstantValue<*>.asStringForPsiLiteral(parent: PsiElement): String =
val arrayPart = "[]".repeat(value.arrayNestedness)
val fqName = value.classId.asSingleFqName()
val canonicalText = psiType(
fqName.asString(), parent, boxPrimitiveType = value.arrayNestedness > 0
fqName.asString(), parent, boxPrimitiveType = value.arrayNestedness > 0,
).let(TypeConversionUtil::erasure).getCanonicalText(false)
"$canonicalText$arrayPart.class"
@@ -479,3 +489,32 @@ inline fun KtFile.safeIsScript() = runReadAction { this.isScript() }
@Suppress("NOTHING_TO_INLINE")
inline fun KtFile.safeScript() = runReadAction { this.script }
internal fun KtUltraLightSupport.findAnnotation(owner: KtAnnotated, fqName: FqName): Pair<KtAnnotationEntry, AnnotationDescriptor>? {
val candidates = owner.annotationEntries.filter {
it.shortName == fqName.shortName() || hasAlias(owner.containingKtFile, it.shortName)
}
for (entry in candidates) {
val descriptor = entry.analyzeAnnotation()
if (descriptor?.fqName == fqName) {
return Pair(entry, descriptor)
}
}
if (owner is KtPropertyAccessor) {
// We might have from the beginning just resolve the descriptor of the accessor
// But we trying to avoid analysis in case property doesn't have any relevant annotations at all
// (in case of `findAnnotation` returns null)
if (findAnnotation(owner.property, fqName) == null) return null
val accessorDescriptor = owner.resolve() ?: return null
// Just reuse the logic of use-site targeted annotation from the compiler
val annotationDescriptor = accessorDescriptor.annotations.findAnnotation(fqName) ?: return null
val entry = annotationDescriptor.source.getPsi() as? KtAnnotationEntry ?: return null
return entry to annotationDescriptor
}
return null
}
@@ -41,7 +41,6 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.lazy.NoDescriptorForDeclarationException
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.KotlinType
import java.util.concurrent.ConcurrentMap
@@ -62,40 +61,10 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
JvmCodegenUtil.getModuleName(moduleDescriptor)
}
override fun findAnnotation(owner: KtAnnotated, fqName: FqName): Pair<KtAnnotationEntry, AnnotationDescriptor>? {
val candidates = owner.annotationEntries.filter {
it.shortName == fqName.shortName() || owner.containingKtFile.hasAlias(it.shortName)
}
for (entry in candidates) {
val descriptor = analyzeAnnotation(entry)
if (descriptor?.fqName == fqName) {
return Pair(entry, descriptor)
}
}
if (owner is KtPropertyAccessor) {
// We might have from the beginning just resolve the descriptor of the accessor
// But we trying to avoid analysis in case property doesn't have any relevant annotations at all
// (in case of `findAnnotation` returns null)
if (findAnnotation(owner.property, fqName) == null) return null
val accessorDescriptor = owner.resolveToDescriptorIfAny() ?: return null
// Just reuse the logic of use-site targeted annotation from the compiler
val annotationDescriptor = accessorDescriptor.annotations.findAnnotation(fqName) ?: return null
val entry = annotationDescriptor.source.getPsi() as? KtAnnotationEntry ?: return null
return entry to annotationDescriptor
}
return null
}
@OptIn(FrontendInternals::class)
override val deprecationResolver: DeprecationResolver
get() = resolutionFacade.getFrontendService(DeprecationResolver::class.java)
override val typeMapper: KotlinTypeMapper by lazyPub {
KotlinTypeMapper(
BindingContext.EMPTY, ClassBuilderMode.LIGHT_CLASSES,
@@ -153,6 +122,9 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
override fun createUltraLightClassForScript(script: KtScript): KtUltraLightClassForScript? =
KtUltraLightClassForScript(script, support = KtUltraLightSupportImpl(script))
override fun getUltraLightClassSupport(element: KtElement): KtUltraLightSupport =
KtUltraLightSupportImpl(element)
private fun KtFile.hasAlias(shortName: Name?): Boolean {
if (shortName == null) return false
return allAliases(this)[shortName.asString()] == true