Add support UL for FileFacade

This commit is contained in:
Igor Yakovlev
2019-05-22 17:35:24 +03:00
parent e996705b45
commit 852a4bc0ca
13 changed files with 735 additions and 519 deletions
@@ -16,13 +16,16 @@
package org.jetbrains.kotlin.cli.jvm.compiler package org.jetbrains.kotlin.cli.jvm.compiler
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.asJava.LightClassBuilder import org.jetbrains.kotlin.asJava.LightClassBuilder
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.builder.InvalidLightClassDataHolder import org.jetbrains.kotlin.asJava.builder.InvalidLightClassDataHolder
import org.jetbrains.kotlin.asJava.builder.LightClassConstructionContext import org.jetbrains.kotlin.asJava.builder.LightClassConstructionContext
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolderImpl import org.jetbrains.kotlin.asJava.builder.LightClassDataHolderImpl
import org.jetbrains.kotlin.asJava.classes.KtUltraLightClassForFacade
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
@@ -39,6 +42,13 @@ import org.jetbrains.kotlin.resolve.BindingContext
* To mitigate this, CliLightClassGenerationSupport hold a trace that is shared between the analyzer and JetLightClasses * To mitigate this, CliLightClassGenerationSupport hold a trace that is shared between the analyzer and JetLightClasses
*/ */
class CliLightClassGenerationSupport(private val traceHolder: CliTraceHolder) : LightClassGenerationSupport() { class CliLightClassGenerationSupport(private val traceHolder: CliTraceHolder) : LightClassGenerationSupport() {
override fun createUltraLightClassForFacade(
facadeClassFqName: FqName,
searchScope: GlobalSearchScope,
facadeFile: KtFile
): KtUltraLightClassForFacade? = null
override fun createUltraLightClass(element: KtClassOrObject) = null override fun createUltraLightClass(element: KtClassOrObject) = null
override fun createDataHolderForClass(classOrObject: KtClassOrObject, builder: LightClassBuilder): LightClassDataHolder.ForClass { override fun createDataHolderForClass(classOrObject: KtClassOrObject, builder: LightClassBuilder): LightClassDataHolder.ForClass {
@@ -18,11 +18,16 @@ package org.jetbrains.kotlin.asJava
import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.asJava.builder.LightClassBuilderResult import org.jetbrains.kotlin.asJava.builder.LightClassBuilderResult
import org.jetbrains.kotlin.asJava.builder.LightClassConstructionContext import org.jetbrains.kotlin.asJava.builder.LightClassConstructionContext
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.classes.KtUltraLightClass import org.jetbrains.kotlin.asJava.classes.KtUltraLightClass
import org.jetbrains.kotlin.asJava.classes.KtUltraLightClassForFacade
import org.jetbrains.kotlin.asJava.classes.KtUltraLightSupport
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
@@ -43,6 +48,12 @@ abstract class LightClassGenerationSupport {
abstract fun createUltraLightClass(element: KtClassOrObject): KtUltraLightClass? abstract fun createUltraLightClass(element: KtClassOrObject): KtUltraLightClass?
abstract fun createUltraLightClassForFacade(
facadeClassFqName: FqName,
searchScope: GlobalSearchScope,
facadeFile: KtFile
): KtUltraLightClassForFacade?
companion object { companion object {
@JvmStatic @JvmStatic
fun getInstance(project: Project): LightClassGenerationSupport { fun getInstance(project: Project): LightClassGenerationSupport {
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.asJava.classes
import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Comparing import com.intellij.openapi.util.Comparing
import com.intellij.openapi.util.registry.Registry
import com.intellij.psi.* import com.intellij.psi.*
import com.intellij.psi.impl.light.LightEmptyImplementsList import com.intellij.psi.impl.light.LightEmptyImplementsList
import com.intellij.psi.impl.light.LightModifierList import com.intellij.psi.impl.light.LightModifierList
@@ -29,6 +30,7 @@ import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.PsiModificationTracker import com.intellij.psi.util.PsiModificationTracker
import com.intellij.util.containers.SLRUCache import com.intellij.util.containers.SLRUCache
import org.jetbrains.annotations.NonNls import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder
import org.jetbrains.kotlin.asJava.builder.LightClassDataProviderForFileFacade import org.jetbrains.kotlin.asJava.builder.LightClassDataProviderForFileFacade
import org.jetbrains.kotlin.asJava.elements.FakeFileForLightClass import org.jetbrains.kotlin.asJava.elements.FakeFileForLightClass
@@ -45,12 +47,13 @@ import org.jetbrains.kotlin.psi.KtStringTemplateExpression
import org.jetbrains.kotlin.psi.psiUtil.siblings import org.jetbrains.kotlin.psi.psiUtil.siblings
import javax.swing.Icon import javax.swing.Icon
class KtLightClassForFacade private constructor( open class KtLightClassForFacade protected constructor(
manager: PsiManager, manager: PsiManager,
private val facadeClassFqName: FqName, protected val facadeClassFqName: FqName,
private val lightClassDataCache: CachedValue<LightClassDataHolder.ForFacade>, protected open val lightClassDataCache: CachedValue<LightClassDataHolder.ForFacade>,
files: Collection<KtFile> files: Collection<KtFile>
) : KtLazyLightClass(manager) { ) : KtLazyLightClass(manager) {
private data class StubCacheKey(val fqName: FqName, val searchScope: GlobalSearchScope) private data class StubCacheKey(val fqName: FqName, val searchScope: GlobalSearchScope)
class FacadeStubCache(private val project: Project) { class FacadeStubCache(private val project: Project) {
@@ -58,17 +61,18 @@ class KtLightClassForFacade private constructor(
val cache = object : SLRUCache<StubCacheKey, CachedValue<LightClassDataHolder.ForFacade>>(20, 30) { val cache = object : SLRUCache<StubCacheKey, CachedValue<LightClassDataHolder.ForFacade>>(20, 30) {
override fun createValue(key: StubCacheKey): CachedValue<LightClassDataHolder.ForFacade> { override fun createValue(key: StubCacheKey): CachedValue<LightClassDataHolder.ForFacade> {
val stubProvider = LightClassDataProviderForFileFacade.ByProjectSource(project, key.fqName, key.searchScope) val stubProvider = LightClassDataProviderForFileFacade.ByProjectSource(project, key.fqName, key.searchScope)
return CachedValuesManager.getManager(project).createCachedValue<LightClassDataHolder.ForFacade>(stubProvider, /*trackValue = */false) return CachedValuesManager.getManager(project)
.createCachedValue<LightClassDataHolder.ForFacade>(stubProvider, /*trackValue = */false)
} }
} }
} }
private val cachedValue: CachedValue<FacadeCacheData> = CachedValuesManager.getManager(project).createCachedValue<FacadeCacheData>( private val cachedValue: CachedValue<FacadeCacheData> = CachedValuesManager.getManager(project).createCachedValue<FacadeCacheData>(
{ CachedValueProvider.Result.create(FacadeCacheData(), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT) }, { CachedValueProvider.Result.create(FacadeCacheData(), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT) }, false
/*trackValue = */ false) )
operator fun get(qualifiedName: FqName, searchScope: GlobalSearchScope): CachedValue<LightClassDataHolder.ForFacade> { operator fun get(qualifiedName: FqName, searchScope: GlobalSearchScope): CachedValue<LightClassDataHolder.ForFacade> {
synchronized (cachedValue) { synchronized(cachedValue) {
return cachedValue.value.cache.get(StubCacheKey(qualifiedName, searchScope)) return cachedValue.value.cache.get(StubCacheKey(qualifiedName, searchScope))
} }
} }
@@ -83,22 +87,22 @@ class KtLightClassForFacade private constructor(
val files: Collection<KtFile> = files.toSet() // needed for hashCode val files: Collection<KtFile> = files.toSet() // needed for hashCode
private val hashCode: Int = private val hashCode: Int =
computeHashCode() computeHashCode()
private val packageFqName: FqName = private val packageFqName: FqName =
facadeClassFqName.parent() facadeClassFqName.parent()
private val modifierList: PsiModifierList = private val modifierList: PsiModifierList =
LightModifierList(manager, KotlinLanguage.INSTANCE, PsiModifier.PUBLIC, PsiModifier.FINAL) LightModifierList(manager, KotlinLanguage.INSTANCE, PsiModifier.PUBLIC, PsiModifier.FINAL)
private val implementsList: LightEmptyImplementsList = private val implementsList: LightEmptyImplementsList =
LightEmptyImplementsList(manager) LightEmptyImplementsList(manager)
private val packageClsFile = FakeFileForLightClass( private val packageClsFile = FakeFileForLightClass(
files.first(), files.first(),
lightClass = { this }, lightClass = { this },
stub = { lightClassDataCache.value.javaFileStub }, stub = { lightClassDataCache.value.javaFileStub },
packageFqName = packageFqName packageFqName = packageFqName
) )
override val kotlinOrigin: KtClassOrObject? get() = null override val kotlinOrigin: KtClassOrObject? get() = null
@@ -171,8 +175,7 @@ class KtLightClassForFacade private constructor(
val annotationList = file.fileAnnotationList val annotationList = file.fileAnnotationList
if (annotationList != null) { if (annotationList != null) {
annotationList.add(newFileAnnotationList.annotationEntries.first()) annotationList.add(newFileAnnotationList.annotationEntries.first())
} } else {
else {
val anchor = file.firstChild.siblings().firstOrNull { it !is PsiWhiteSpace && it !is PsiComment } val anchor = file.firstChild.siblings().firstOrNull { it !is PsiWhiteSpace && it !is PsiComment }
file.addBefore(newFileAnnotationList, anchor) file.addBefore(newFileAnnotationList, anchor)
} }
@@ -180,7 +183,7 @@ class KtLightClassForFacade private constructor(
} }
val jvmNameExpression = jvmNameEntry.valueArguments.firstOrNull()?.getArgumentExpression() as? KtStringTemplateExpression val jvmNameExpression = jvmNameEntry.valueArguments.firstOrNull()?.getArgumentExpression() as? KtStringTemplateExpression
?: continue ?: continue
ElementManipulators.handleContentChange(jvmNameExpression, name) ElementManipulators.handleContentChange(jvmNameExpression, name)
} }
@@ -247,29 +250,37 @@ class KtLightClassForFacade private constructor(
override fun toString() = "${KtLightClassForFacade::class.java.simpleName}:$facadeClassFqName" override fun toString() = "${KtLightClassForFacade::class.java.simpleName}:$facadeClassFqName"
companion object Factory { companion object {
fun createForFacade( fun createForFacade(
manager: PsiManager, manager: PsiManager,
facadeClassFqName: FqName, facadeClassFqName: FqName,
searchScope: GlobalSearchScope, searchScope: GlobalSearchScope,
files: Collection<KtFile> files: Collection<KtFile>
): KtLightClassForFacade { ): KtLightClassForFacade {
assert(files.isNotEmpty()) { "No files for facade $facadeClassFqName" } assert(files.isNotEmpty()) { "No files for facade $facadeClassFqName" }
val ultraLightEnabled =
!KtUltraLightSupport.forceUsingOldLightClasses && Registry.`is`("kotlin.use.ultra.light.classes", true)
if (ultraLightEnabled && files.count() == 1) {
val facadeFile = files.first()
LightClassGenerationSupport.getInstance(facadeFile.project)
.createUltraLightClassForFacade(facadeClassFqName, searchScope, facadeFile)?.let { return it }
}
val lightClassDataCache = FacadeStubCache.getInstance(manager.project).get(facadeClassFqName, searchScope) val lightClassDataCache = FacadeStubCache.getInstance(manager.project).get(facadeClassFqName, searchScope)
return KtLightClassForFacade(manager, facadeClassFqName, lightClassDataCache, files) return KtLightClassForFacade(manager, facadeClassFqName, lightClassDataCache, files)
} }
fun createForSyntheticFile( fun createForSyntheticFile(
manager: PsiManager, manager: PsiManager,
facadeClassFqName: FqName, facadeClassFqName: FqName,
file: KtFile file: KtFile
): KtLightClassForFacade { ): KtLightClassForFacade {
// TODO: refactor, using cached value doesn't make sense for this case // TODO: refactor, using cached value doesn't make sense for this case
val cachedValue = CachedValuesManager.getManager(manager.project). val cachedValue = CachedValuesManager.getManager(manager.project).createCachedValue<LightClassDataHolder.ForFacade>(
createCachedValue<LightClassDataHolder.ForFacade>( LightClassDataProviderForFileFacade.ByFile(manager.project, facadeClassFqName, file), /*trackValue = */false
LightClassDataProviderForFileFacade.ByFile(manager.project, facadeClassFqName, file), /*trackValue = */false )
)
return KtLightClassForFacade(manager, facadeClassFqName, cachedValue, listOf(file)) return KtLightClassForFacade(manager, facadeClassFqName, cachedValue, listOf(file))
} }
} }
@@ -339,7 +339,10 @@ abstract class KtLightClassForSourceDeclaration(
fun create(classOrObject: KtClassOrObject): KtLightClassForSourceDeclaration? = fun create(classOrObject: KtClassOrObject): KtLightClassForSourceDeclaration? =
CachedValuesManager.getCachedValue(classOrObject) { CachedValuesManager.getCachedValue(classOrObject) {
CachedValueProvider.Result CachedValueProvider.Result
.create(createNoCache(classOrObject, KtUltraLightClass.forceUsingOldLightClasses), OUT_OF_CODE_BLOCK_MODIFICATION_COUNT) .create(
createNoCache(classOrObject, KtUltraLightSupport.forceUsingOldLightClasses),
OUT_OF_CODE_BLOCK_MODIFICATION_COUNT
)
} }
fun createNoCache(classOrObject: KtClassOrObject, forceUsingOldLightClasses: Boolean): KtLightClassForSourceDeclaration? { fun createNoCache(classOrObject: KtClassOrObject, forceUsingOldLightClasses: Boolean): KtLightClassForSourceDeclaration? {
@@ -0,0 +1,82 @@
/*
* Copyright 2010-2019 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.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiManager
import com.intellij.psi.util.CachedValue
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder
import org.jetbrains.kotlin.asJava.elements.KtLightField
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtProperty
class KtUltraLightClassForFacade(
manager: PsiManager,
facadeClassFqName: FqName,
lightClassDataCache: CachedValue<LightClassDataHolder.ForFacade>,
private val file: KtFile,
private val support: KtUltraLightSupport
) : KtLightClassForFacade(manager, facadeClassFqName, lightClassDataCache, listOf(file)) {
private val methodsBuilder by lazyPub { UltraLightMembersCreator(this, false, true, support) }
private inline fun <T> forTooComplex(getter: () -> T): T {
check(tooComplex) {
"Cls delegate shouldn't be loaded for not too complex ultra-light classes! Qualified name: $qualifiedName"
}
return getter()
}
override val lightClassDataCache: CachedValue<LightClassDataHolder.ForFacade>
get() = forTooComplex { super.lightClassDataCache }
override val clsDelegate: PsiClass
get() = forTooComplex { super.clsDelegate }
private val tooComplex: Boolean by lazyPub { file.declarations.any { support.isTooComplexForUltraLightGeneration(it) } }
private val ownMethodsForNotTooComplex: List<KtLightMethod> by lazyPub {
val result = arrayListOf<KtLightMethod>()
for (declaration in file.declarations.filterNot { it.isHiddenByDeprecation(support) }) {
if (declaration.hasModifier(KtTokens.PRIVATE_KEYWORD)) continue
when (declaration) {
is KtNamedFunction -> result.addAll(methodsBuilder.createMethods(declaration, true))
is KtProperty -> result.addAll(methodsBuilder.propertyAccessors(declaration, declaration.isVar, true, false))
}
}
result
}
private val ownFieldsForNotTooComplex: List<KtLightField> by lazyPub {
hashSetOf<String>().run {
file.declarations.filterIsInstance<KtProperty>().mapNotNull {
methodsBuilder.createPropertyField(it, this, forceStatic = true)
}
}
}
override fun getOwnFields() = if (!tooComplex) ownFieldsForNotTooComplex else super.getOwnFields()
override fun getOwnMethods() = if (!tooComplex) ownMethodsForNotTooComplex else super.getOwnMethods()
override fun hashCode(): Int = file.hashCode()
override fun toString(): String = "UltraLight class for file facade"
override fun equals(other: Any?): Boolean = this === other
override fun copy(): KtLightClassForFacade = KtUltraLightClassForFacade(manager, facadeClassFqName, lightClassDataCache, file, support)
override fun setName(name: String): PsiElement? = this
}
@@ -10,10 +10,6 @@ import com.intellij.psi.*
import com.intellij.psi.impl.PsiClassImplUtil import com.intellij.psi.impl.PsiClassImplUtil
import com.intellij.psi.impl.PsiSuperMethodImplUtil import com.intellij.psi.impl.PsiSuperMethodImplUtil
import com.intellij.psi.impl.light.LightMethodBuilder import com.intellij.psi.impl.light.LightMethodBuilder
import com.intellij.psi.impl.light.LightModifierList
import com.intellij.psi.impl.light.LightParameterListBuilder
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.builder.LightClassData import org.jetbrains.kotlin.asJava.builder.LightClassData
import org.jetbrains.kotlin.asJava.elements.KtLightField import org.jetbrains.kotlin.asJava.elements.KtLightField
import org.jetbrains.kotlin.asJava.elements.KtLightMethod import org.jetbrains.kotlin.asJava.elements.KtLightMethod
@@ -22,40 +18,29 @@ import org.jetbrains.kotlin.backend.common.DataClassMethodGenerator
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.codegen.JvmCodegenUtil import org.jetbrains.kotlin.codegen.JvmCodegenUtil
import org.jetbrains.kotlin.codegen.kotlinType import org.jetbrains.kotlin.codegen.kotlinType
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotated import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.lexer.KtTokens.* import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DelegationResolver import org.jetbrains.kotlin.resolve.DelegationResolver
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.resolve.annotations.argumentValue import org.jetbrains.kotlin.resolve.annotations.argumentValue
import org.jetbrains.kotlin.resolve.constants.EnumValue import org.jetbrains.kotlin.resolve.constants.EnumValue
import org.jetbrains.kotlin.resolve.descriptorUtil.isPublishedApi
import org.jetbrains.kotlin.resolve.inline.isInlineOnly
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_OVERLOADS_FQ_NAME import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_OVERLOADS_FQ_NAME
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SYNTHETIC_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.resolve.jvm.annotations.STRICTFP_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.resolve.jvm.annotations.SYNCHRONIZED_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny
open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val support: KtUltraLightSupport) : open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val support: KtUltraLightSupport) :
KtLightClassImpl(classOrObject) { KtLightClassImpl(classOrObject) {
companion object {
// This property may be removed once IntelliJ versions earlier than 2018.3 become unsupported private val methodsBuilder by lazyPub {
// And usages of that property may be replaced with relevant registry key UltraLightMembersCreator(this, isNamedObject(), classOrObject.hasModifier(SEALED_KEYWORD), support)
@Volatile
@get:TestOnly
var forceUsingOldLightClasses = false
} }
private val tooComplex: Boolean by lazyPub { support.isTooComplexForUltraLightGeneration(classOrObject) } private val tooComplex: Boolean by lazyPub { support.isTooComplexForUltraLightGeneration(classOrObject) }
@@ -142,26 +127,15 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
val result = arrayListOf<KtLightField>() val result = arrayListOf<KtLightField>()
val usedNames = hashSetOf<String>() val usedNames = hashSetOf<String>()
fun generateUniqueName(base: String): String {
if (usedNames.add(base)) return base
var i = 1
while (true) {
val suggestion = "$base$$i"
if (usedNames.add(suggestion)) return suggestion
i++
}
}
for (parameter in propertyParameters()) { for (parameter in propertyParameters()) {
propertyField(parameter, ::generateUniqueName, forceStatic = false)?.let(result::add) methodsBuilder.createPropertyField(parameter, usedNames, forceStatic = false)?.let(result::add)
} }
this.classOrObject.companionObjects.firstOrNull()?.let { companion -> this.classOrObject.companionObjects.firstOrNull()?.let { companion ->
result.add( result.add(
KtUltraLightField( KtUltraLightField(
companion, companion,
generateUniqueName(companion.name.orEmpty()), methodsBuilder.generateUniqueFieldName(companion.name.orEmpty(), usedNames),
this, this,
support, support,
setOf(PsiModifier.STATIC, PsiModifier.FINAL, PsiModifier.PUBLIC) setOf(PsiModifier.STATIC, PsiModifier.FINAL, PsiModifier.PUBLIC)
@@ -170,7 +144,7 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
for (property in companion.declarations.filterIsInstance<KtProperty>()) { for (property in companion.declarations.filterIsInstance<KtProperty>()) {
if (isInterface && !property.isConstOrJvmField()) continue if (isInterface && !property.isConstOrJvmField()) continue
propertyField(property, ::generateUniqueName, true)?.let(result::add) methodsBuilder.createPropertyField(property, usedNames, true)?.let(result::add)
} }
} }
@@ -182,7 +156,7 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
// Probably, the same should work for const vals but it doesn't at the moment (see KT-28294) // Probably, the same should work for const vals but it doesn't at the moment (see KT-28294)
if (isCompanion && (containingClass?.isInterface == false || property.isJvmField())) continue if (isCompanion && (containingClass?.isInterface == false || property.isJvmField())) continue
propertyField(property, ::generateUniqueName, forceStatic = this.classOrObject is KtObjectDeclaration)?.let(result::add) methodsBuilder.createPropertyField(property, usedNames, forceStatic = this.classOrObject is KtObjectDeclaration)?.let(result::add)
} }
} }
@@ -215,56 +189,6 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
private fun isNamedObject() = classOrObject is KtObjectDeclaration && !classOrObject.isCompanion() private fun isNamedObject() = classOrObject is KtObjectDeclaration && !classOrObject.isCompanion()
private fun propertyField(
// KtProperty | KtParameter
variable: KtCallableDeclaration,
generateUniqueName: (String) -> String,
forceStatic: Boolean
): KtLightField? {
val property = variable as? KtProperty
if (property != null && !hasBackingField(property)) return null
if (variable.hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME)) return null
val hasDelegate = property?.hasDelegate() == true
val fieldName = generateUniqueName((variable.name ?: "") + (if (hasDelegate) "\$delegate" else ""))
val visibility = when {
variable.hasModifier(PRIVATE_KEYWORD) -> PsiModifier.PRIVATE
variable.hasModifier(LATEINIT_KEYWORD) || variable.isConstOrJvmField() -> {
val declaration = property?.setter ?: variable
simpleVisibility(declaration)
}
else -> PsiModifier.PRIVATE
}
val modifiers = hashSetOf(visibility)
val isMutable = when (variable) {
is KtProperty -> variable.isVar
is KtParameter -> variable.isMutable
else -> error("Unexpected type of variable: ${variable::class.java}")
}
if (!isMutable || variable.hasModifier(CONST_KEYWORD) || hasDelegate) {
modifiers.add(PsiModifier.FINAL)
}
if (forceStatic || isNamedObject() && isJvmStatic(variable)) {
modifiers.add(PsiModifier.STATIC)
}
return KtUltraLightField(variable, fieldName, this, support, modifiers)
}
private fun hasBackingField(property: KtProperty): Boolean {
if (property.hasModifier(ABSTRACT_KEYWORD)) return false
if (property.hasModifier(LATEINIT_KEYWORD) || property.accessors.isEmpty()) return true
val context = LightClassGenerationSupport.getInstance(project).analyze(property)
val descriptor = context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, property)
return descriptor is PropertyDescriptor && context[BindingContext.BACKING_FIELD_REQUIRED, descriptor] == true
}
override fun getOwnFields(): List<KtLightField> = if (tooComplex) super.getOwnFields() else _ownFields override fun getOwnFields(): List<KtLightField> = if (tooComplex) super.getOwnFields() else _ownFields
private fun propertyParameters() = classOrObject.primaryConstructorParameters.filter { it.hasValOrVar() } private fun propertyParameters() = classOrObject.primaryConstructorParameters.filter { it.hasValOrVar() }
@@ -273,16 +197,16 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
val result = arrayListOf<KtLightMethod>() val result = arrayListOf<KtLightMethod>()
for (declaration in this.classOrObject.declarations.filterNot { isHiddenByDeprecation(it) }) { for (declaration in this.classOrObject.declarations.filterNot { it.isHiddenByDeprecation(support) }) {
if (declaration.hasModifier(PRIVATE_KEYWORD) && isInterface) continue if (declaration.hasModifier(PRIVATE_KEYWORD) && isInterface) continue
when (declaration) { when (declaration) {
is KtNamedFunction -> result.addAll(asJavaMethods(declaration, false)) is KtNamedFunction -> result.addAll(methodsBuilder.createMethods(declaration, false))
is KtProperty -> result.addAll(propertyAccessors(declaration, declaration.isVar, false)) is KtProperty -> result.addAll(methodsBuilder.propertyAccessors(declaration, declaration.isVar, false,false))
} }
} }
for (parameter in propertyParameters()) { for (parameter in propertyParameters()) {
result.addAll(propertyAccessors(parameter, parameter.isMutable, false)) result.addAll(methodsBuilder.propertyAccessors(parameter, parameter.isMutable, false,false))
} }
if (!isInterface) { if (!isInterface) {
@@ -292,8 +216,8 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
this.classOrObject.companionObjects.firstOrNull()?.let { companion -> this.classOrObject.companionObjects.firstOrNull()?.let { companion ->
for (declaration in companion.declarations.filterNot { isHiddenByDeprecation(it) }) { for (declaration in companion.declarations.filterNot { isHiddenByDeprecation(it) }) {
when (declaration) { when (declaration) {
is KtNamedFunction -> if (isJvmStatic(declaration)) result.addAll(asJavaMethods(declaration, true)) is KtNamedFunction -> if (isJvmStatic(declaration)) result.addAll(methodsBuilder.createMethods(declaration, true))
is KtProperty -> result.addAll(propertyAccessors(declaration, declaration.isVar, true)) is KtProperty -> result.addAll(methodsBuilder.propertyAccessors(declaration, declaration.isVar, false,true))
} }
} }
} }
@@ -374,11 +298,11 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
result.add(defaultConstructor()) result.add(defaultConstructor())
} }
for (constructor in constructors.filterNot { isHiddenByDeprecation(it) }) { for (constructor in constructors.filterNot { isHiddenByDeprecation(it) }) {
result.addAll(asJavaMethods(constructor, false, forcePrivate = isEnum)) result.addAll(methodsBuilder.createMethods(constructor, false, forcePrivate = isEnum))
} }
val primary = classOrObject.primaryConstructor val primary = classOrObject.primaryConstructor
if (primary != null && shouldGenerateNoArgOverload(primary)) { if (primary != null && shouldGenerateNoArgOverload(primary)) {
result.add(noArgConstructor(simpleVisibility(primary), primary)) result.add(noArgConstructor(primary.simpleVisibility(), primary))
} }
return result return result
} }
@@ -403,12 +327,6 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
return noArgConstructor(visibility, classOrObject) return noArgConstructor(visibility, classOrObject)
} }
private fun simpleVisibility(declaration: KtDeclaration): String = when {
declaration.hasModifier(PRIVATE_KEYWORD) -> PsiModifier.PRIVATE
declaration.hasModifier(PROTECTED_KEYWORD) -> PsiModifier.PROTECTED
else -> PsiModifier.PUBLIC
}
private fun noArgConstructor(visibility: String, declaration: KtDeclaration): KtUltraLightMethod = private fun noArgConstructor(visibility: String, declaration: KtDeclaration): KtUltraLightMethod =
KtUltraLightMethodForSourceDeclaration( KtUltraLightMethodForSourceDeclaration(
LightMethodBuilder(manager, language, name.orEmpty()).setConstructor(true).addModifier(visibility), LightMethodBuilder(manager, language, name.orEmpty()).setConstructor(true).addModifier(visibility),
@@ -426,285 +344,13 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
override fun getOwnMethods(): List<KtLightMethod> = if (tooComplex) super.getOwnMethods() else _ownMethods override fun getOwnMethods(): List<KtLightMethod> = if (tooComplex) super.getOwnMethods() else _ownMethods
private fun asJavaMethods(
ktFunction: KtFunction,
forceStatic: Boolean,
forcePrivate: Boolean = false
): Collection<KtLightMethod> {
if (ktFunction.hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME) || ktFunction.hasReifiedParameters()) return emptyList()
val basicMethod = asJavaMethod(ktFunction, forceStatic, forcePrivate)
val result = mutableListOf(basicMethod)
if (ktFunction.hasAnnotation(JVM_OVERLOADS_FQ_NAME)) {
val numberOfDefaultParameters = ktFunction.valueParameters.count(KtParameter::hasDefaultValue)
for (numberOfDefaultParametersToAdd in numberOfDefaultParameters - 1 downTo 0) {
result.add(
asJavaMethod(
ktFunction,
forceStatic,
forcePrivate,
numberOfDefaultParametersToAdd = numberOfDefaultParametersToAdd
)
)
}
}
return result
}
private fun asJavaMethod(
ktFunction: KtFunction,
forceStatic: Boolean,
forcePrivate: Boolean,
numberOfDefaultParametersToAdd: Int = -1
): KtLightMethod {
val isConstructor = ktFunction is KtConstructor<*>
val name =
if (isConstructor)
this.name
else computeMethodName(ktFunction, ktFunction.name ?: SpecialNames.NO_NAME_PROVIDED.asString(), MethodType.REGULAR)
val method = lightMethod(name.orEmpty(), ktFunction, forceStatic, forcePrivate)
val wrapper = KtUltraLightMethodForSourceDeclaration(method, ktFunction, support, this)
addReceiverParameter(ktFunction, wrapper)
var remainingNumberOfDefaultParametersToAdd =
if (numberOfDefaultParametersToAdd >= 0)
numberOfDefaultParametersToAdd
else
// Just to avoid computing the actual number of default parameters, we use an upper bound
ktFunction.valueParameters.size
for (parameter in ktFunction.valueParameters) {
if (parameter.hasDefaultValue()) {
if (remainingNumberOfDefaultParametersToAdd == 0) continue
remainingNumberOfDefaultParametersToAdd--
}
method.addParameter(KtUltraLightParameterForSource(parameter.name.orEmpty(), parameter, support, wrapper, ktFunction))
}
val returnType: PsiType? by lazyPub {
if (isConstructor) null
else methodReturnType(ktFunction, wrapper)
}
method.setMethodReturnType { returnType }
return wrapper
}
private fun addReceiverParameter(callable: KtCallableDeclaration, method: KtUltraLightMethod) {
if (callable.receiverTypeReference == null) return
method.delegate.addParameter(KtUltraLightReceiverParameter(callable, support, method))
}
private fun methodReturnType(ktDeclaration: KtDeclaration, wrapper: KtUltraLightMethod): PsiType {
val desc =
ktDeclaration.resolve()?.getterIfProperty() as? FunctionDescriptor
?: return PsiType.NULL
return support.mapType(wrapper) { typeMapper, signatureWriter ->
typeMapper.mapReturnType(desc, signatureWriter)
}
}
private fun DeclarationDescriptor.getterIfProperty() =
if (this@getterIfProperty is PropertyDescriptor) this@getterIfProperty.getter else this@getterIfProperty
private fun lightMethod(
name: String,
declaration: KtDeclaration,
forceStatic: Boolean,
forcePrivate: Boolean = false
): LightMethodBuilder {
val accessedProperty = if (declaration is KtPropertyAccessor) declaration.property else null
val outer = accessedProperty ?: declaration
return LightMethodBuilder(
manager, language, name,
LightParameterListBuilder(manager, language),
object : LightModifierList(manager, language) {
override fun hasModifierProperty(name: String): Boolean {
if (name == PsiModifier.PUBLIC || name == PsiModifier.PROTECTED || name == PsiModifier.PRIVATE) {
if (forcePrivate || declaration.isPrivate() || accessedProperty?.isPrivate() == true) {
return name == PsiModifier.PRIVATE
}
if (declaration.hasModifier(PROTECTED_KEYWORD) || accessedProperty?.hasModifier(PROTECTED_KEYWORD) == true) {
return name == PsiModifier.PROTECTED
}
if (outer.hasModifier(OVERRIDE_KEYWORD)) {
when ((outer.resolve() as? CallableDescriptor)?.visibility) {
Visibilities.PUBLIC -> return name == PsiModifier.PUBLIC
Visibilities.PRIVATE -> return name == PsiModifier.PRIVATE
Visibilities.PROTECTED -> return name == PsiModifier.PROTECTED
}
}
return name == PsiModifier.PUBLIC
}
return when (name) {
PsiModifier.FINAL -> !isInterface && outer !is KtConstructor<*> && isFinal(outer)
PsiModifier.ABSTRACT -> isInterface || outer.hasModifier(ABSTRACT_KEYWORD)
PsiModifier.STATIC -> forceStatic || isNamedObject() && (isJvmStatic(outer) || isJvmStatic(declaration))
PsiModifier.STRICTFP -> declaration is KtFunction && declaration.hasAnnotation(STRICTFP_ANNOTATION_FQ_NAME)
PsiModifier.SYNCHRONIZED -> declaration is KtFunction && declaration.hasAnnotation(SYNCHRONIZED_ANNOTATION_FQ_NAME)
else -> false
}
}
fun KtDeclaration.isPrivate() =
hasModifier(PRIVATE_KEYWORD) ||
this is KtConstructor<*> && classOrObject.hasModifier(SEALED_KEYWORD) ||
isInlineOnly()
private fun KtDeclaration.isInlineOnly(): Boolean {
if (this !is KtCallableDeclaration || !hasModifier(INLINE_KEYWORD)) return false
if (annotationEntries.isEmpty()) return false
val descriptor = resolve() as? CallableMemberDescriptor ?: return false
return descriptor.isInlineOnly()
}
}
).setConstructor(declaration is KtConstructor<*>)
}
private enum class MethodType {
REGULAR,
GETTER,
SETTER
}
private fun computeMethodName(declaration: KtDeclaration, name: String, type: MethodType): String {
fun tryCompute(declaration: KtDeclaration, type: MethodType): String? {
if (!declaration.hasAnnotation(DescriptorUtils.JVM_NAME)) return null
val annotated = (declaration.resolve() as? Annotated) ?: return null
val resultName = DescriptorUtils.getJvmName(annotated)
if (resultName !== null || type == MethodType.REGULAR) return resultName
val propertyAnnotated = when (type) {
MethodType.GETTER -> (annotated as? PropertyDescriptor)?.getter
MethodType.SETTER -> (annotated as? PropertyDescriptor)?.setter
else -> throw NotImplementedError()
}
return propertyAnnotated?.let(DescriptorUtils::getJvmName)
}
val computedName = tryCompute(declaration, type)
if (computedName !== null) return computedName
if (isInternalNonPublishedApi(declaration)) return KotlinTypeMapper.InternalNameMapper.mangleInternalName(name, support.moduleName)
return name
}
private tailrec fun isInternalNonPublishedApi(declaration: KtDeclaration): Boolean {
if (declaration.hasModifier(PRIVATE_KEYWORD) ||
declaration.hasModifier(PROTECTED_KEYWORD) ||
declaration.hasModifier(PUBLIC_KEYWORD)
) {
return false
}
if (isInternal(declaration) && declaration.resolve()?.isPublishedApi() != true) return true
val containingProperty = (declaration as? KtPropertyAccessor)?.property ?: return false
return isInternalNonPublishedApi(containingProperty)
}
private fun KtAnnotated.hasAnnotation(name: FqName) = support.findAnnotation(this, name) != null private fun KtAnnotated.hasAnnotation(name: FqName) = support.findAnnotation(this, name) != null
private fun isInternal(f: KtDeclaration): Boolean {
if (f.hasModifier(OVERRIDE_KEYWORD)) {
val desc = f.resolve()
return desc is CallableDescriptor &&
desc.visibility.effectiveVisibility(desc, false) == EffectiveVisibility.Internal
}
return f.hasModifier(INTERNAL_KEYWORD)
}
private fun propertyAccessors(
declaration: KtCallableDeclaration,
mutable: Boolean,
onlyJvmStatic: Boolean
): List<KtLightMethod> {
val propertyName = declaration.name ?: return emptyList()
if (declaration.isConstOrJvmField() || declaration.hasReifiedParameters()) return emptyList()
val ktGetter = (declaration as? KtProperty)?.getter
val ktSetter = (declaration as? KtProperty)?.setter
val isPrivate = declaration.hasModifier(PRIVATE_KEYWORD)
if (isPrivate && declaration !is KtProperty) return emptyList()
fun needsAccessor(accessor: KtPropertyAccessor?): Boolean {
if (!onlyJvmStatic || isJvmStatic(declaration) || accessor != null && isJvmStatic(accessor)) {
if (declaration is KtProperty && declaration.hasDelegate()) {
return true
}
if (accessor?.hasModifier(PRIVATE_KEYWORD) == true || accessor?.hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME) == true) {
return false
}
if (!isPrivate || accessor?.hasBody() == true) {
return true
}
}
return false
}
val result = arrayListOf<KtLightMethod>()
if (needsAccessor(ktGetter)) {
val getterName = computeMethodName(ktGetter ?: declaration, JvmAbi.getterName(propertyName), MethodType.GETTER)
val getterPrototype = lightMethod(getterName, ktGetter ?: declaration, onlyJvmStatic)
val getterWrapper = KtUltraLightMethodForSourceDeclaration(getterPrototype, declaration, support, this)
val getterType: PsiType by lazyPub { methodReturnType(declaration, getterWrapper) }
getterPrototype.setMethodReturnType { getterType }
addReceiverParameter(declaration, getterWrapper)
result.add(getterWrapper)
}
if (mutable && needsAccessor(ktSetter)) {
val setterName = computeMethodName(ktSetter ?: declaration, JvmAbi.setterName(propertyName), MethodType.SETTER)
val setterPrototype = lightMethod(setterName, ktSetter ?: declaration, onlyJvmStatic)
.setMethodReturnType(PsiType.VOID)
val setterWrapper = KtUltraLightMethodForSourceDeclaration(setterPrototype, declaration, support, this)
addReceiverParameter(declaration, setterWrapper)
val setterParameter = ktSetter?.parameter
setterPrototype.addParameter(
if (setterParameter != null)
KtUltraLightParameterForSource(propertyName, setterParameter, support, setterWrapper, declaration)
else
KtUltraLightParameterForSetterParameter(propertyName, declaration, support, setterWrapper, declaration)
)
result.add(setterWrapper)
}
return result
}
private fun KtCallableDeclaration.isConstOrJvmField() = private fun KtCallableDeclaration.isConstOrJvmField() =
hasModifier(CONST_KEYWORD) || isJvmField() hasModifier(CONST_KEYWORD) || isJvmField()
private fun KtCallableDeclaration.isJvmField() = hasAnnotation(JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME) private fun KtCallableDeclaration.isJvmField() = hasAnnotation(JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME)
private fun isFinal(declaration: KtDeclaration): Boolean {
if (declaration.hasModifier(FINAL_KEYWORD)) return true
return declaration !is KtPropertyAccessor &&
!declaration.hasModifier(OPEN_KEYWORD) &&
!declaration.hasModifier(OVERRIDE_KEYWORD) &&
!declaration.hasModifier(ABSTRACT_KEYWORD)
}
private fun KtCallableDeclaration.hasReifiedParameters(): Boolean =
typeParameters.any { it.hasModifier(REIFIED_KEYWORD) }
override fun getInitializers(): Array<PsiClassInitializer> = emptyArray() override fun getInitializers(): Array<PsiClassInitializer> = emptyArray()
override fun getContainingClass(): PsiClass? = override fun getContainingClass(): PsiClass? =
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.psi.KtEnumEntry
internal class KtUltraLightEnumEntry( internal class KtUltraLightEnumEntry(
declaration: KtEnumEntry, declaration: KtEnumEntry,
name: String, name: String,
containingClass: KtUltraLightClass, containingClass: KtLightClass,
support: KtUltraLightSupport, support: KtUltraLightSupport,
modifiers: Set<String> modifiers: Set<String>
) : KtUltraLightField(declaration, name, containingClass, support, modifiers), PsiEnumConstant { ) : KtUltraLightField(declaration, name, containingClass, support, modifiers), PsiEnumConstant {
@@ -19,7 +19,7 @@ internal class KtUltraLightEnumEntry(
private val enumEntry get() = declaration as KtEnumEntry private val enumEntry get() = declaration as KtEnumEntry
private val _initializingClass by lazyPub { private val _initializingClass by lazyPub {
enumEntry.body?.let { KtUltraLightClassForEnumEntry(enumEntry, containingClass.support, this) } enumEntry.body?.let { KtUltraLightClassForEnumEntry(enumEntry, support, this) }
} }
override fun getInitializingClass(): PsiEnumConstantInitializer? = _initializingClass override fun getInitializingClass(): PsiEnumConstantInitializer? = _initializingClass
@@ -31,7 +31,7 @@ import org.jetbrains.kotlin.types.KotlinType
internal open class KtUltraLightField( internal open class KtUltraLightField(
protected val declaration: KtNamedDeclaration, protected val declaration: KtNamedDeclaration,
name: String, name: String,
private val containingClass: KtUltraLightClass, private val containingClass: KtLightClass,
private val support: KtUltraLightSupport, private val support: KtUltraLightSupport,
modifiers: Set<String> modifiers: Set<String>
) : LightFieldBuilder(name, PsiType.NULL, declaration), KtLightField, ) : LightFieldBuilder(name, PsiType.NULL, declaration), KtLightField,
@@ -75,7 +75,7 @@ internal open class KtUltraLightField(
declaration is KtObjectDeclaration -> declaration is KtObjectDeclaration ->
(declaration.resolve() as? ClassDescriptor)?.defaultType (declaration.resolve() as? ClassDescriptor)?.defaultType
declaration is KtEnumEntry -> { declaration is KtEnumEntry -> {
(containingClass.kotlinOrigin.resolve() as? ClassDescriptor)?.defaultType (containingClass.kotlinOrigin?.resolve() as? ClassDescriptor)?.defaultType
} }
else -> { else -> {
declaration.getKotlinType() declaration.getKotlinType()
@@ -0,0 +1,384 @@
/*
* Copyright 2010-2019 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.psi.PsiModifier
import com.intellij.psi.PsiType
import com.intellij.psi.impl.light.LightMethodBuilder
import com.intellij.psi.impl.light.LightModifierList
import com.intellij.psi.impl.light.LightParameterListBuilder
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.elements.KtLightField
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.lexer.KtTokens.REIFIED_KEYWORD
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.isPublishedApi
import org.jetbrains.kotlin.resolve.inline.isInlineOnly
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_OVERLOADS_FQ_NAME
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SYNTHETIC_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.resolve.jvm.annotations.STRICTFP_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.resolve.jvm.annotations.SYNCHRONIZED_ANNOTATION_FQ_NAME
internal class UltraLightMembersCreator(
private val containingClass: KtLightClass,
private val containingClassIsNamedObject: Boolean,
private val containingClassIsSealed: Boolean,
private val support: KtUltraLightSupport
) {
fun generateUniqueFieldName(base: String, usedNames: HashSet<String>): String {
if (usedNames.add(base)) return base
var i = 1
while (true) {
val suggestion = "$base$$i"
if (usedNames.add(suggestion)) return suggestion
i++
}
}
fun createPropertyField(
// KtProperty | KtParameter
variable: KtCallableDeclaration,
usedPropertyNames: HashSet<String>,
forceStatic: Boolean
): KtLightField? {
val property = variable as? KtProperty
if (property != null && !hasBackingField(property)) return null
if (variable.hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME)) return null
val hasDelegate = property?.hasDelegate() == true
val fieldName = generateUniqueFieldName((variable.name ?: "") + (if (hasDelegate) "\$delegate" else ""), usedPropertyNames)
val visibility = when {
variable.hasModifier(KtTokens.PRIVATE_KEYWORD) -> PsiModifier.PRIVATE
variable.hasModifier(KtTokens.LATEINIT_KEYWORD) || variable.isConstOrJvmField() -> {
val declaration = property?.setter ?: variable
declaration.simpleVisibility()
}
else -> PsiModifier.PRIVATE
}
val modifiers = hashSetOf(visibility)
val isMutable = when (variable) {
is KtProperty -> variable.isVar
is KtParameter -> variable.isMutable
else -> error("Unexpected type of variable: ${variable::class.java}")
}
if (!isMutable || variable.hasModifier(KtTokens.CONST_KEYWORD) || hasDelegate) {
modifiers.add(PsiModifier.FINAL)
}
if (forceStatic || containingClassIsNamedObject && variable.isJvmStatic(support)) {
modifiers.add(PsiModifier.STATIC)
}
return KtUltraLightField(variable, fieldName, containingClass, support, modifiers)
}
private fun hasBackingField(property: KtProperty): Boolean {
if (property.hasModifier(KtTokens.ABSTRACT_KEYWORD)) return false
if (property.hasModifier(KtTokens.LATEINIT_KEYWORD) || property.accessors.isEmpty()) return true
val context = LightClassGenerationSupport.getInstance(containingClass.project).analyze(property)
val descriptor = context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, property)
return descriptor is PropertyDescriptor && context[BindingContext.BACKING_FIELD_REQUIRED, descriptor] == true
}
fun createMethods(
ktFunction: KtFunction,
forceStatic: Boolean,
forcePrivate: Boolean = false
): Collection<KtLightMethod> {
if (ktFunction.hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME) || ktFunction.hasReifiedParameters()) return emptyList()
val basicMethod = asJavaMethod(ktFunction, forceStatic, forcePrivate)
val result = mutableListOf(basicMethod)
if (ktFunction.hasAnnotation(JVM_OVERLOADS_FQ_NAME)) {
val numberOfDefaultParameters = ktFunction.valueParameters.count(KtParameter::hasDefaultValue)
for (numberOfDefaultParametersToAdd in numberOfDefaultParameters - 1 downTo 0) {
result.add(
asJavaMethod(
ktFunction,
forceStatic,
forcePrivate,
numberOfDefaultParametersToAdd = numberOfDefaultParametersToAdd
)
)
}
}
return result
}
private fun asJavaMethod(
ktFunction: KtFunction,
forceStatic: Boolean,
forcePrivate: Boolean,
numberOfDefaultParametersToAdd: Int = -1
): KtLightMethod {
val isConstructor = ktFunction is KtConstructor<*>
val name =
if (isConstructor)
containingClass.name
else computeMethodName(ktFunction, ktFunction.name ?: SpecialNames.NO_NAME_PROVIDED.asString(), MethodType.REGULAR)
val method = lightMethod(name.orEmpty(), ktFunction, forceStatic, forcePrivate)
val wrapper = KtUltraLightMethodForSourceDeclaration(method, ktFunction, support, containingClass)
addReceiverParameter(ktFunction, wrapper)
var remainingNumberOfDefaultParametersToAdd =
if (numberOfDefaultParametersToAdd >= 0)
numberOfDefaultParametersToAdd
else
// Just to avoid computing the actual number of default parameters, we use an upper bound
ktFunction.valueParameters.size
for (parameter in ktFunction.valueParameters) {
if (parameter.hasDefaultValue()) {
if (remainingNumberOfDefaultParametersToAdd == 0) continue
remainingNumberOfDefaultParametersToAdd--
}
method.addParameter(KtUltraLightParameterForSource(parameter.name.orEmpty(), parameter, support, wrapper, ktFunction))
}
val returnType: PsiType? by lazyPub {
if (isConstructor) null
else methodReturnType(ktFunction, wrapper)
}
method.setMethodReturnType { returnType }
return wrapper
}
private fun addReceiverParameter(callable: KtCallableDeclaration, method: KtUltraLightMethod) {
if (callable.receiverTypeReference == null) return
method.delegate.addParameter(KtUltraLightReceiverParameter(callable, support, method))
}
private fun methodReturnType(ktDeclaration: KtDeclaration, wrapper: KtUltraLightMethod): PsiType {
val desc =
ktDeclaration.resolve()?.getterIfProperty() as? FunctionDescriptor
?: return PsiType.NULL
return support.mapType(wrapper) { typeMapper, signatureWriter ->
typeMapper.mapReturnType(desc, signatureWriter)
}
}
private fun DeclarationDescriptor.getterIfProperty() =
if (this@getterIfProperty is PropertyDescriptor) this@getterIfProperty.getter else this@getterIfProperty
private fun lightMethod(
name: String,
declaration: KtDeclaration,
forceStatic: Boolean,
forcePrivate: Boolean = false
): LightMethodBuilder {
val accessedProperty = if (declaration is KtPropertyAccessor) declaration.property else null
val outer = accessedProperty ?: declaration
val manager = declaration.manager
val language = declaration.language
return LightMethodBuilder(
manager, language, name,
LightParameterListBuilder(manager, language),
object : LightModifierList(manager, language) {
override fun hasModifierProperty(name: String): Boolean {
if (name == PsiModifier.PUBLIC || name == PsiModifier.PROTECTED || name == PsiModifier.PRIVATE) {
if (forcePrivate || declaration.isPrivate() || accessedProperty?.isPrivate() == true) {
return name == PsiModifier.PRIVATE
}
if (declaration.hasModifier(KtTokens.PROTECTED_KEYWORD) || accessedProperty?.hasModifier(KtTokens.PROTECTED_KEYWORD) == true) {
return name == PsiModifier.PROTECTED
}
if (outer.hasModifier(KtTokens.OVERRIDE_KEYWORD)) {
when ((outer.resolve() as? CallableDescriptor)?.visibility) {
Visibilities.PUBLIC -> return name == PsiModifier.PUBLIC
Visibilities.PRIVATE -> return name == PsiModifier.PRIVATE
Visibilities.PROTECTED -> return name == PsiModifier.PROTECTED
}
}
return name == PsiModifier.PUBLIC
}
return when (name) {
PsiModifier.FINAL -> !containingClass.isInterface && outer !is KtConstructor<*> && isFinal(outer)
PsiModifier.ABSTRACT -> containingClass.isInterface || outer.hasModifier(KtTokens.ABSTRACT_KEYWORD)
PsiModifier.STATIC -> forceStatic || containingClassIsNamedObject && (outer.isJvmStatic(support) || declaration.isJvmStatic(support))
PsiModifier.STRICTFP -> declaration is KtFunction && declaration.hasAnnotation(STRICTFP_ANNOTATION_FQ_NAME)
PsiModifier.SYNCHRONIZED -> declaration is KtFunction && declaration.hasAnnotation(SYNCHRONIZED_ANNOTATION_FQ_NAME)
else -> false
}
}
fun KtDeclaration.isPrivate() =
hasModifier(KtTokens.PRIVATE_KEYWORD) ||
this is KtConstructor<*> && containingClassIsSealed || isInlineOnly()
private fun KtDeclaration.isInlineOnly(): Boolean {
if (this !is KtCallableDeclaration || !hasModifier(KtTokens.INLINE_KEYWORD)) return false
if (annotationEntries.isEmpty()) return false
val descriptor = resolve() as? CallableMemberDescriptor ?: return false
return descriptor.isInlineOnly()
}
}
).setConstructor(declaration is KtConstructor<*>)
}
private enum class MethodType {
REGULAR,
GETTER,
SETTER
}
private fun computeMethodName(declaration: KtDeclaration, name: String, type: MethodType): String {
fun tryCompute(declaration: KtDeclaration, type: MethodType): String? {
if (!declaration.hasAnnotation(DescriptorUtils.JVM_NAME)) return null
val annotated = (declaration.resolve() as? Annotated) ?: return null
val resultName = DescriptorUtils.getJvmName(annotated)
if (resultName !== null || type == MethodType.REGULAR) return resultName
val propertyAnnotated = when (type) {
MethodType.GETTER -> (annotated as? PropertyDescriptor)?.getter
MethodType.SETTER -> (annotated as? PropertyDescriptor)?.setter
else -> throw NotImplementedError()
}
return propertyAnnotated?.let(DescriptorUtils::getJvmName)
}
val computedName = tryCompute(declaration, type)
if (computedName !== null) return computedName
if (isInternalNonPublishedApi(declaration)) return KotlinTypeMapper.InternalNameMapper.mangleInternalName(name, support.moduleName)
return name
}
private tailrec fun isInternalNonPublishedApi(declaration: KtDeclaration): Boolean {
if (declaration.hasModifier(KtTokens.PRIVATE_KEYWORD) ||
declaration.hasModifier(KtTokens.PROTECTED_KEYWORD) ||
declaration.hasModifier(KtTokens.PUBLIC_KEYWORD)
) {
return false
}
if (isInternal(declaration) && declaration.resolve()?.isPublishedApi() != true) return true
val containingProperty = (declaration as? KtPropertyAccessor)?.property ?: return false
return isInternalNonPublishedApi(containingProperty)
}
private fun KtAnnotated.hasAnnotation(name: FqName) = support.findAnnotation(this, name) != null
private fun isInternal(f: KtDeclaration): Boolean {
if (f.hasModifier(KtTokens.OVERRIDE_KEYWORD)) {
val desc = f.resolve()
return desc is CallableDescriptor &&
desc.visibility.effectiveVisibility(desc, false) == EffectiveVisibility.Internal
}
return f.hasModifier(KtTokens.INTERNAL_KEYWORD)
}
fun propertyAccessors(
declaration: KtCallableDeclaration,
mutable: Boolean,
forceStatic: Boolean,
onlyJvmStatic: Boolean
): List<KtLightMethod> {
val propertyName = declaration.name ?: return emptyList()
if (declaration.isConstOrJvmField() || declaration.hasReifiedParameters()) return emptyList()
val ktGetter = (declaration as? KtProperty)?.getter
val ktSetter = (declaration as? KtProperty)?.setter
val isPrivate = !forceStatic && declaration.hasModifier(KtTokens.PRIVATE_KEYWORD)
if (isPrivate && declaration !is KtProperty) return emptyList()
fun needsAccessor(accessor: KtPropertyAccessor?): Boolean {
if (!onlyJvmStatic || declaration.isJvmStatic(support) || accessor != null && accessor.isJvmStatic(support)) {
if (declaration is KtProperty && declaration.hasDelegate()) {
return true
}
if (accessor?.hasModifier(KtTokens.PRIVATE_KEYWORD) == true || accessor?.hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME) == true) {
return false
}
if (!isPrivate || accessor?.hasBody() == true) {
return true
}
}
return false
}
val result = arrayListOf<KtLightMethod>()
if (needsAccessor(ktGetter)) {
val getterName = computeMethodName(ktGetter ?: declaration, JvmAbi.getterName(propertyName), MethodType.GETTER)
val getterPrototype = lightMethod(getterName, ktGetter ?: declaration, onlyJvmStatic || forceStatic)
val getterWrapper = KtUltraLightMethodForSourceDeclaration(getterPrototype, declaration, support, containingClass)
val getterType: PsiType by lazyPub { methodReturnType(declaration, getterWrapper) }
getterPrototype.setMethodReturnType { getterType }
addReceiverParameter(declaration, getterWrapper)
result.add(getterWrapper)
}
if (mutable && needsAccessor(ktSetter)) {
val setterName = computeMethodName(ktSetter ?: declaration, JvmAbi.setterName(propertyName), MethodType.SETTER)
val setterPrototype = lightMethod(setterName, ktSetter ?: declaration, onlyJvmStatic || forceStatic)
.setMethodReturnType(PsiType.VOID)
val setterWrapper = KtUltraLightMethodForSourceDeclaration(setterPrototype, declaration, support, containingClass)
addReceiverParameter(declaration, setterWrapper)
val setterParameter = ktSetter?.parameter
setterPrototype.addParameter(
if (setterParameter != null)
KtUltraLightParameterForSource(propertyName, setterParameter, support, setterWrapper, declaration)
else
KtUltraLightParameterForSetterParameter(propertyName, declaration, support, setterWrapper, declaration)
)
result.add(setterWrapper)
}
return result
}
private fun KtCallableDeclaration.hasReifiedParameters(): Boolean =
typeParameters.any { it.hasModifier(REIFIED_KEYWORD) }
private fun KtCallableDeclaration.isConstOrJvmField() =
hasModifier(KtTokens.CONST_KEYWORD) || isJvmField()
private fun KtCallableDeclaration.isJvmField() = hasAnnotation(JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME)
private fun isFinal(declaration: KtDeclaration): Boolean {
if (declaration.hasModifier(KtTokens.FINAL_KEYWORD)) return true
return declaration !is KtPropertyAccessor &&
!declaration.hasModifier(KtTokens.OPEN_KEYWORD) &&
!declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD) &&
!declaration.hasModifier(KtTokens.ABSTRACT_KEYWORD)
}
}
@@ -29,7 +29,7 @@ internal abstract class KtUltraLightMethod(
internal val delegate: LightMethodBuilder, internal val delegate: LightMethodBuilder,
closestDeclarationForOrigin: KtDeclaration?, closestDeclarationForOrigin: KtDeclaration?,
protected val support: KtUltraLightSupport, protected val support: KtUltraLightSupport,
containingClass: KtUltraLightClass containingClass: KtLightClass
) : KtLightMethodImpl( ) : KtLightMethodImpl(
{ delegate }, { delegate },
closestDeclarationForOrigin?.let { closestDeclarationForOrigin?.let {
@@ -101,7 +101,7 @@ internal class KtUltraLightMethodForSourceDeclaration(
delegate: LightMethodBuilder, delegate: LightMethodBuilder,
declaration: KtDeclaration, declaration: KtDeclaration,
support: KtUltraLightSupport, support: KtUltraLightSupport,
containingClass: KtUltraLightClass containingClass: KtLightClass
) : KtUltraLightMethod( ) : KtUltraLightMethod(
delegate, delegate,
declaration, declaration,
@@ -5,20 +5,29 @@
package org.jetbrains.kotlin.asJava.classes package org.jetbrains.kotlin.asJava.classes
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtAnnotated import org.jetbrains.kotlin.psi.KtAnnotated
import org.jetbrains.kotlin.psi.KtAnnotationEntry import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
interface KtUltraLightSupport { interface KtUltraLightSupport {
val moduleName: String val moduleName: String
fun findAnnotation(owner: KtAnnotated, fqName: FqName): Pair<KtAnnotationEntry, AnnotationDescriptor>? fun findAnnotation(owner: KtAnnotated, fqName: FqName): Pair<KtAnnotationEntry, AnnotationDescriptor>?
fun isTooComplexForUltraLightGeneration(element: KtClassOrObject): Boolean fun isTooComplexForUltraLightGeneration(element: KtDeclaration): Boolean
val deprecationResolver: DeprecationResolver val deprecationResolver: DeprecationResolver
val typeMapper: KotlinTypeMapper val typeMapper: KotlinTypeMapper
val moduleDescriptor: ModuleDescriptor val moduleDescriptor: ModuleDescriptor
companion object {
// This property may be removed once IntelliJ versions earlier than 2018.3 become unsupported
// And usages of that property may be replaced with relevant registry key
@Volatile
@get:TestOnly
var forceUsingOldLightClasses = false
}
} }
@@ -24,10 +24,24 @@ import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter
import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.resolve.annotations.argumentValue
import org.jetbrains.kotlin.resolve.constants.EnumValue
import org.jetbrains.kotlin.resolve.descriptorUtil.isPublishedApi
import org.jetbrains.kotlin.resolve.inline.isInlineOnly
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_OVERLOADS_FQ_NAME
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SYNTHETIC_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.resolve.jvm.annotations.STRICTFP_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.resolve.jvm.annotations.SYNCHRONIZED_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeProjectionImpl import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.jetbrains.kotlin.types.replace import org.jetbrains.kotlin.types.replace
@@ -286,3 +300,16 @@ private fun packMethodFlags(access: Int, isInterface: Boolean): Int {
return flags return flags
} }
internal fun KtDeclaration.isHiddenByDeprecation(support: KtUltraLightSupport): Boolean {
val deprecated = support.findAnnotation(this, FqName("kotlin.Deprecated"))?.second
return (deprecated?.argumentValue("level") as? EnumValue)?.enumEntryName?.asString() == "HIDDEN"
}
internal fun KtAnnotated.isJvmStatic(support: KtUltraLightSupport): Boolean = support.findAnnotation(this, JVM_STATIC_ANNOTATION_FQ_NAME) !== null
internal fun KtDeclaration.simpleVisibility(): String = when {
hasModifier(KtTokens.PRIVATE_KEYWORD) -> PsiModifier.PRIVATE
hasModifier(KtTokens.PROTECTED_KEYWORD) -> PsiModifier.PROTECTED
else -> PsiModifier.PUBLIC
}
@@ -17,10 +17,12 @@
package org.jetbrains.kotlin.idea.caches.resolve package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.openapi.diagnostic.debug import com.intellij.openapi.diagnostic.debug
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleUtilCore import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiManager
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.CachedValuesManager
@@ -60,6 +62,142 @@ import org.jetbrains.kotlin.types.KotlinType
import java.util.concurrent.ConcurrentMap import java.util.concurrent.ConcurrentMap
class IDELightClassGenerationSupport(private val project: Project) : LightClassGenerationSupport() { class IDELightClassGenerationSupport(private val project: Project) : LightClassGenerationSupport() {
private inner class KtUltraLightSupportImpl(private val element: KtElement, private val module: Module) : KtUltraLightSupport {
fun KtDeclaration.forLogString(): String? = when (this) {
is KtClassOrObject -> this.fqName?.asString()
is KtFile -> this.packageFqNameByTree.asString()
else -> this.text
}
override fun isTooComplexForUltraLightGeneration(element: KtDeclaration): Boolean {
val facet = KotlinFacet.get(module)
val pluginClasspaths = facet?.configuration?.settings?.compilerArguments?.pluginClasspaths
if (!pluginClasspaths.isNullOrEmpty()) {
val stringifiedClasspaths = pluginClasspaths.joinToString()
LOG.debug { "Using heavy light classes for ${element.forLogString()} because of compiler plugins $stringifiedClasspaths" }
return true
}
val problem = findTooComplexDeclaration(element)
if (problem != null) {
LOG.debug {
"Using heavy light classes for ${element.forLogString()} because of ${StringUtil.trimLog(problem.text, 100)}"
}
return true
}
return false
}
override val moduleDescriptor by lazyPub {
element.getResolutionFacade().moduleDescriptor
}
override val moduleName: String by lazyPub {
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 = analyze(entry).get(BindingContext.ANNOTATION, 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
}
override val deprecationResolver: DeprecationResolver by lazyPub {
element.getResolutionFacade().getFrontendService(DeprecationResolver::class.java)
}
override val typeMapper: KotlinTypeMapper by lazyPub {
KotlinTypeMapper(
BindingContext.EMPTY, ClassBuilderMode.LIGHT_CLASSES,
moduleName, KotlinTypeMapper.LANGUAGE_VERSION_SETTINGS_DEFAULT, // TODO use proper LanguageVersionSettings
jvmTarget = JvmTarget.JVM_1_8,
typePreprocessor = KotlinType::cleanFromAnonymousTypes
)
}
private fun findTooComplexDeclaration(declaration: KtDeclaration): PsiElement? {
if (declaration.hasExpectModifier() ||
declaration.hasModifier(KtTokens.ANNOTATION_KEYWORD) ||
declaration.hasModifier(KtTokens.INLINE_KEYWORD) && declaration is KtClassOrObject ||
declaration.hasModifier(KtTokens.SUSPEND_KEYWORD)
) {
return declaration
}
if (declaration is KtClassOrObject) {
declaration.primaryConstructor?.let { findTooComplexDeclaration(it) }?.let { return it }
for (d in declaration.declarations) {
if (d is KtClassOrObject && !(d is KtObjectDeclaration && d.isCompanion())) continue
findTooComplexDeclaration(d)?.let { return it }
}
if (implementsKotlinCollection(declaration)) {
return declaration.getSuperTypeList()
}
}
if (declaration is KtCallableDeclaration) {
declaration.valueParameters.mapNotNull { findTooComplexDeclaration(it) }.firstOrNull()?.let { return it }
if (declaration.typeReference?.hasModifier(KtTokens.SUSPEND_KEYWORD) == true) {
return declaration.typeReference
}
}
if (declaration is KtProperty) {
declaration.accessors.mapNotNull { findTooComplexDeclaration(it) }.firstOrNull()?.let { return it }
}
return null
}
}
override fun createUltraLightClassForFacade(
facadeClassFqName: FqName,
searchScope: GlobalSearchScope,
facadeFile: KtFile
): KtUltraLightClassForFacade? {
val lightClassDataCache =
KtLightClassForFacade.FacadeStubCache.getInstance(project).get(facadeClassFqName, searchScope)
if (facadeFile.isScript()) return null
val module = ModuleUtilCore.findModuleForPsiElement(facadeFile) ?: return null
return KtUltraLightClassForFacade(
facadeFile.manager,
facadeClassFqName,
lightClassDataCache,
facadeFile,
KtUltraLightSupportImpl(facadeFile, module)
)
}
override fun createUltraLightClass(element: KtClassOrObject): KtUltraLightClass? { override fun createUltraLightClass(element: KtClassOrObject): KtUltraLightClass? {
if (element.shouldNotBeVisibleAsLightClass() || if (element.shouldNotBeVisibleAsLightClass() ||
element is KtObjectDeclaration && element.isObjectLiteral() || element is KtObjectDeclaration && element.isObjectLiteral() ||
@@ -71,113 +209,8 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
} }
val module = ModuleUtilCore.findModuleForPsiElement(element) ?: return null val module = ModuleUtilCore.findModuleForPsiElement(element) ?: return null
return KtUltraLightClass(element, object : KtUltraLightSupport {
override fun isTooComplexForUltraLightGeneration(element: KtClassOrObject): Boolean {
val facet = KotlinFacet.get(module)
val pluginClasspaths = facet?.configuration?.settings?.compilerArguments?.pluginClasspaths
if (!pluginClasspaths.isNullOrEmpty()) {
val stringifiedClasspaths = pluginClasspaths.joinToString()
LOG.debug { "Using heavy light classes for ${element.fqName?.asString()} because of compiler plugins $stringifiedClasspaths" }
return true
}
val problem = findTooComplexDeclaration(element)
if (problem != null) {
LOG.debug {
"Using heavy light classes for ${element.fqName?.asString()} because of ${StringUtil.trimLog(problem.text, 100)}"
}
return true
}
return false
}
override val moduleDescriptor by lazyPub {
element.getResolutionFacade().moduleDescriptor
}
override val moduleName: String by lazyPub {
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 = analyze(entry).get(BindingContext.ANNOTATION, 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
}
override val deprecationResolver: DeprecationResolver by lazyPub {
element.getResolutionFacade().getFrontendService(DeprecationResolver::class.java)
}
override val typeMapper: KotlinTypeMapper by lazyPub {
KotlinTypeMapper(
BindingContext.EMPTY, ClassBuilderMode.LIGHT_CLASSES,
moduleName, KotlinTypeMapper.LANGUAGE_VERSION_SETTINGS_DEFAULT, // TODO use proper LanguageVersionSettings
jvmTarget = JvmTarget.JVM_1_8,
typePreprocessor = KotlinType::cleanFromAnonymousTypes
)
}
})
}
private fun findTooComplexDeclaration(declaration: KtDeclaration): PsiElement? {
if (declaration.hasExpectModifier() ||
declaration.hasModifier(KtTokens.ANNOTATION_KEYWORD) ||
declaration.hasModifier(KtTokens.INLINE_KEYWORD) && declaration is KtClassOrObject ||
declaration.hasModifier(KtTokens.SUSPEND_KEYWORD)
) {
return declaration
}
if (declaration is KtClassOrObject) {
declaration.primaryConstructor?.let { findTooComplexDeclaration(it) }?.let { return it }
for (d in declaration.declarations) {
if (d is KtClassOrObject && !(d is KtObjectDeclaration && d.isCompanion())) continue
findTooComplexDeclaration(d)?.let { return it }
}
if (implementsKotlinCollection(declaration)) {
return declaration.getSuperTypeList()
}
}
if (declaration is KtCallableDeclaration) {
declaration.valueParameters.mapNotNull { findTooComplexDeclaration(it) }.firstOrNull()?.let { return it }
if (declaration.typeReference?.hasModifier(KtTokens.SUSPEND_KEYWORD) == true) {
return declaration.typeReference
}
}
if (declaration is KtProperty) {
declaration.accessors.mapNotNull { findTooComplexDeclaration(it) }.firstOrNull()?.let { return it }
}
return null
return KtUltraLightClass(element, KtUltraLightSupportImpl(element, module))
} }
private fun implementsKotlinCollection(classOrObject: KtClassOrObject): Boolean { private fun implementsKotlinCollection(classOrObject: KtClassOrObject): Boolean {