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
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.asJava.LightClassBuilder
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.builder.InvalidLightClassDataHolder
import org.jetbrains.kotlin.asJava.builder.LightClassConstructionContext
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolderImpl
import org.jetbrains.kotlin.asJava.classes.KtUltraLightClassForFacade
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
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
*/
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 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.project.Project
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.asJava.builder.LightClassBuilderResult
import org.jetbrains.kotlin.asJava.builder.LightClassConstructionContext
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.KtUltraLightClassForFacade
import org.jetbrains.kotlin.asJava.classes.KtUltraLightSupport
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
@@ -43,6 +48,12 @@ abstract class LightClassGenerationSupport {
abstract fun createUltraLightClass(element: KtClassOrObject): KtUltraLightClass?
abstract fun createUltraLightClassForFacade(
facadeClassFqName: FqName,
searchScope: GlobalSearchScope,
facadeFile: KtFile
): KtUltraLightClassForFacade?
companion object {
@JvmStatic
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.project.Project
import com.intellij.openapi.util.Comparing
import com.intellij.openapi.util.registry.Registry
import com.intellij.psi.*
import com.intellij.psi.impl.light.LightEmptyImplementsList
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.util.containers.SLRUCache
import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder
import org.jetbrains.kotlin.asJava.builder.LightClassDataProviderForFileFacade
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 javax.swing.Icon
class KtLightClassForFacade private constructor(
manager: PsiManager,
private val facadeClassFqName: FqName,
private val lightClassDataCache: CachedValue<LightClassDataHolder.ForFacade>,
files: Collection<KtFile>
open class KtLightClassForFacade protected constructor(
manager: PsiManager,
protected val facadeClassFqName: FqName,
protected open val lightClassDataCache: CachedValue<LightClassDataHolder.ForFacade>,
files: Collection<KtFile>
) : KtLazyLightClass(manager) {
private data class StubCacheKey(val fqName: FqName, val searchScope: GlobalSearchScope)
class FacadeStubCache(private val project: Project) {
@@ -58,17 +61,18 @@ class KtLightClassForFacade private constructor(
val cache = object : SLRUCache<StubCacheKey, CachedValue<LightClassDataHolder.ForFacade>>(20, 30) {
override fun createValue(key: StubCacheKey): CachedValue<LightClassDataHolder.ForFacade> {
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>(
{ CachedValueProvider.Result.create(FacadeCacheData(), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT) },
/*trackValue = */ false)
{ CachedValueProvider.Result.create(FacadeCacheData(), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT) }, false
)
operator fun get(qualifiedName: FqName, searchScope: GlobalSearchScope): CachedValue<LightClassDataHolder.ForFacade> {
synchronized (cachedValue) {
synchronized(cachedValue) {
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
private val hashCode: Int =
computeHashCode()
computeHashCode()
private val packageFqName: FqName =
facadeClassFqName.parent()
facadeClassFqName.parent()
private val modifierList: PsiModifierList =
LightModifierList(manager, KotlinLanguage.INSTANCE, PsiModifier.PUBLIC, PsiModifier.FINAL)
LightModifierList(manager, KotlinLanguage.INSTANCE, PsiModifier.PUBLIC, PsiModifier.FINAL)
private val implementsList: LightEmptyImplementsList =
LightEmptyImplementsList(manager)
LightEmptyImplementsList(manager)
private val packageClsFile = FakeFileForLightClass(
files.first(),
lightClass = { this },
stub = { lightClassDataCache.value.javaFileStub },
packageFqName = packageFqName
files.first(),
lightClass = { this },
stub = { lightClassDataCache.value.javaFileStub },
packageFqName = packageFqName
)
override val kotlinOrigin: KtClassOrObject? get() = null
@@ -171,8 +175,7 @@ class KtLightClassForFacade private constructor(
val annotationList = file.fileAnnotationList
if (annotationList != null) {
annotationList.add(newFileAnnotationList.annotationEntries.first())
}
else {
} else {
val anchor = file.firstChild.siblings().firstOrNull { it !is PsiWhiteSpace && it !is PsiComment }
file.addBefore(newFileAnnotationList, anchor)
}
@@ -180,7 +183,7 @@ class KtLightClassForFacade private constructor(
}
val jvmNameExpression = jvmNameEntry.valueArguments.firstOrNull()?.getArgumentExpression() as? KtStringTemplateExpression
?: continue
?: continue
ElementManipulators.handleContentChange(jvmNameExpression, name)
}
@@ -247,29 +250,37 @@ class KtLightClassForFacade private constructor(
override fun toString() = "${KtLightClassForFacade::class.java.simpleName}:$facadeClassFqName"
companion object Factory {
companion object {
fun createForFacade(
manager: PsiManager,
facadeClassFqName: FqName,
searchScope: GlobalSearchScope,
files: Collection<KtFile>
manager: PsiManager,
facadeClassFqName: FqName,
searchScope: GlobalSearchScope,
files: Collection<KtFile>
): KtLightClassForFacade {
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)
return KtLightClassForFacade(manager, facadeClassFqName, lightClassDataCache, files)
}
fun createForSyntheticFile(
manager: PsiManager,
facadeClassFqName: FqName,
file: KtFile
manager: PsiManager,
facadeClassFqName: FqName,
file: KtFile
): KtLightClassForFacade {
// TODO: refactor, using cached value doesn't make sense for this case
val cachedValue = CachedValuesManager.getManager(manager.project).
createCachedValue<LightClassDataHolder.ForFacade>(
LightClassDataProviderForFileFacade.ByFile(manager.project, facadeClassFqName, file), /*trackValue = */false
)
val cachedValue = CachedValuesManager.getManager(manager.project).createCachedValue<LightClassDataHolder.ForFacade>(
LightClassDataProviderForFileFacade.ByFile(manager.project, facadeClassFqName, file), /*trackValue = */false
)
return KtLightClassForFacade(manager, facadeClassFqName, cachedValue, listOf(file))
}
}
@@ -339,7 +339,10 @@ abstract class KtLightClassForSourceDeclaration(
fun create(classOrObject: KtClassOrObject): KtLightClassForSourceDeclaration? =
CachedValuesManager.getCachedValue(classOrObject) {
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? {
@@ -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.PsiSuperMethodImplUtil
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.elements.KtLightField
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.codegen.JvmCodegenUtil
import org.jetbrains.kotlin.codegen.kotlinType
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.load.java.JvmAbi
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.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DelegationResolver
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.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.typeUtil.isAnyOrNullableAny
open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val support: KtUltraLightSupport) :
KtLightClassImpl(classOrObject) {
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
private val methodsBuilder by lazyPub {
UltraLightMembersCreator(this, isNamedObject(), classOrObject.hasModifier(SEALED_KEYWORD), support)
}
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 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()) {
propertyField(parameter, ::generateUniqueName, forceStatic = false)?.let(result::add)
methodsBuilder.createPropertyField(parameter, usedNames, forceStatic = false)?.let(result::add)
}
this.classOrObject.companionObjects.firstOrNull()?.let { companion ->
result.add(
KtUltraLightField(
companion,
generateUniqueName(companion.name.orEmpty()),
methodsBuilder.generateUniqueFieldName(companion.name.orEmpty(), usedNames),
this,
support,
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>()) {
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)
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 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
private fun propertyParameters() = classOrObject.primaryConstructorParameters.filter { it.hasValOrVar() }
@@ -273,16 +197,16 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
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
when (declaration) {
is KtNamedFunction -> result.addAll(asJavaMethods(declaration, false))
is KtProperty -> result.addAll(propertyAccessors(declaration, declaration.isVar, false))
is KtNamedFunction -> result.addAll(methodsBuilder.createMethods(declaration, false))
is KtProperty -> result.addAll(methodsBuilder.propertyAccessors(declaration, declaration.isVar, false,false))
}
}
for (parameter in propertyParameters()) {
result.addAll(propertyAccessors(parameter, parameter.isMutable, false))
result.addAll(methodsBuilder.propertyAccessors(parameter, parameter.isMutable, false,false))
}
if (!isInterface) {
@@ -292,8 +216,8 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
this.classOrObject.companionObjects.firstOrNull()?.let { companion ->
for (declaration in companion.declarations.filterNot { isHiddenByDeprecation(it) }) {
when (declaration) {
is KtNamedFunction -> if (isJvmStatic(declaration)) result.addAll(asJavaMethods(declaration, true))
is KtProperty -> result.addAll(propertyAccessors(declaration, declaration.isVar, true))
is KtNamedFunction -> if (isJvmStatic(declaration)) result.addAll(methodsBuilder.createMethods(declaration, 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())
}
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
if (primary != null && shouldGenerateNoArgOverload(primary)) {
result.add(noArgConstructor(simpleVisibility(primary), primary))
result.add(noArgConstructor(primary.simpleVisibility(), primary))
}
return result
}
@@ -403,12 +327,6 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
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 =
KtUltraLightMethodForSourceDeclaration(
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
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 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() =
hasModifier(CONST_KEYWORD) || isJvmField()
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 getContainingClass(): PsiClass? =
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.psi.KtEnumEntry
internal class KtUltraLightEnumEntry(
declaration: KtEnumEntry,
name: String,
containingClass: KtUltraLightClass,
containingClass: KtLightClass,
support: KtUltraLightSupport,
modifiers: Set<String>
) : KtUltraLightField(declaration, name, containingClass, support, modifiers), PsiEnumConstant {
@@ -19,7 +19,7 @@ internal class KtUltraLightEnumEntry(
private val enumEntry get() = declaration as KtEnumEntry
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
@@ -31,7 +31,7 @@ import org.jetbrains.kotlin.types.KotlinType
internal open class KtUltraLightField(
protected val declaration: KtNamedDeclaration,
name: String,
private val containingClass: KtUltraLightClass,
private val containingClass: KtLightClass,
private val support: KtUltraLightSupport,
modifiers: Set<String>
) : LightFieldBuilder(name, PsiType.NULL, declaration), KtLightField,
@@ -75,7 +75,7 @@ internal open class KtUltraLightField(
declaration is KtObjectDeclaration ->
(declaration.resolve() as? ClassDescriptor)?.defaultType
declaration is KtEnumEntry -> {
(containingClass.kotlinOrigin.resolve() as? ClassDescriptor)?.defaultType
(containingClass.kotlinOrigin?.resolve() as? ClassDescriptor)?.defaultType
}
else -> {
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,
closestDeclarationForOrigin: KtDeclaration?,
protected val support: KtUltraLightSupport,
containingClass: KtUltraLightClass
containingClass: KtLightClass
) : KtLightMethodImpl(
{ delegate },
closestDeclarationForOrigin?.let {
@@ -101,7 +101,7 @@ internal class KtUltraLightMethodForSourceDeclaration(
delegate: LightMethodBuilder,
declaration: KtDeclaration,
support: KtUltraLightSupport,
containingClass: KtUltraLightClass
containingClass: KtLightClass
) : KtUltraLightMethod(
delegate,
declaration,
@@ -5,20 +5,29 @@
package org.jetbrains.kotlin.asJava.classes
import org.jetbrains.annotations.TestOnly
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.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
interface KtUltraLightSupport {
val moduleName: String
fun findAnnotation(owner: KtAnnotated, fqName: FqName): Pair<KtAnnotationEntry, AnnotationDescriptor>?
fun isTooComplexForUltraLightGeneration(element: KtClassOrObject): Boolean
fun isTooComplexForUltraLightGeneration(element: KtDeclaration): Boolean
val deprecationResolver: DeprecationResolver
val typeMapper: KotlinTypeMapper
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.state.KotlinTypeMapper
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.name.FqName
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.psi.*
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.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.TypeProjectionImpl
import org.jetbrains.kotlin.types.replace
@@ -286,3 +300,16 @@ private fun packMethodFlags(access: Int, isInterface: Boolean): Int {
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
import com.intellij.openapi.diagnostic.debug
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiManager
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
@@ -60,6 +62,142 @@ import org.jetbrains.kotlin.types.KotlinType
import java.util.concurrent.ConcurrentMap
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? {
if (element.shouldNotBeVisibleAsLightClass() ||
element is KtObjectDeclaration && element.isObjectLiteral() ||
@@ -71,113 +209,8 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
}
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 {