diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.kt index c999e24f9ac..46ebd85b40c 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.kt @@ -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, files: Collection - ): 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> = 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 -} +} \ No newline at end of file diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.kt index e7770593fc7..7c26896d7a4 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.kt @@ -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, diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightSupport.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightSupport.kt index 8b5ff408674..5023cfbc66c 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightSupport.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightSupport.kt @@ -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? 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 +} diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt index 187251cd769..8fdbefeb061 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt @@ -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 { fun parameters(declaration: D): List @@ -72,7 +81,7 @@ private val supportForDescriptor = object : TypeParametersSupport buildTypeParameterList( declaration: D, owner: PsiTypeParameterListOwner, support: KtUltraLightSupport, - typeParametersSupport: TypeParametersSupport + typeParametersSupport: TypeParametersSupport, ): 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? { + 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 +} diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt index 77610dbee8e..4d670fb7fca 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt @@ -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? { - 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