org.jetbrains.kotlin.asJava.classes.ultraLightMembersCreator.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-embeddable Show documentation
Show all versions of kotlin-compiler-embeddable Show documentation
the Kotlin compiler embeddable
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.asJava.classes
import com.intellij.openapi.progress.ProgressManager
import com.intellij.psi.*
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.builder.LightMemberOriginForDeclaration
import org.jetbrains.kotlin.asJava.elements.KtLightField
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.elements.convertToLightAnnotationMemberValue
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.JvmStandardClassIds.JVM_OVERLOADS_FQ_NAME
import org.jetbrains.kotlin.name.JvmStandardClassIds.JVM_SYNTHETIC_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.name.JvmStandardClassIds.STRICTFP_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.name.JvmStandardClassIds.SYNCHRONIZED_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
import org.jetbrains.kotlin.psi.psiUtil.hasSuspendModifier
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.hasJvmSyntheticAnnotation
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
internal class UltraLightMembersCreator(
private val containingClass: KtLightClass,
private val containingClassIsNamedObject: Boolean,
private val containingClassIsSealed: Boolean,
private val mangleInternalFunctions: Boolean,
private val support: KtUltraLightSupport
) {
fun generateUniqueFieldName(base: String, usedNames: HashSet): 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,
forceStatic: Boolean
): KtLightField? {
ProgressManager.checkCanceled()
if (variable.nameAsSafeName.isSpecial) return null
if (!hasBackingField(variable)) return null
if (variable.hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME) || variable.hasExpectModifier()) return null
val property = variable as? KtProperty
val hasDelegate = property?.hasDelegate() == true
val fieldName = generateUniqueFieldName(
(variable.name ?: "") + (if (hasDelegate) JvmAbi.DELEGATED_PROPERTY_NAME_SUFFIX else ""),
usedPropertyNames
)
val visibility = when {
variable.hasModifier(PRIVATE_KEYWORD) -> PsiModifier.PRIVATE
variable.hasModifier(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(CONST_KEYWORD) || hasDelegate) {
modifiers.add(PsiModifier.FINAL)
}
if (forceStatic || containingClassIsNamedObject && variable.isJvmStatic(support)) {
modifiers.add(PsiModifier.STATIC)
}
return KtUltraLightFieldForSourceDeclaration(variable, fieldName, containingClass, support, modifiers)
}
private fun hasBackingField(property: KtCallableDeclaration): Boolean {
if (property.hasModifier(ABSTRACT_KEYWORD)) return false
if (property.hasModifier(LATEINIT_KEYWORD)) return true
if (property is KtParameter) return true
if (property !is KtProperty) return false
return property.hasInitializer() ||
property.getter?.takeIf { it.hasBody() } == null ||
property.setter?.takeIf { it.hasBody() } == null && property.isVar
}
fun createMethods(
ktFunction: KtFunction,
forceStatic: Boolean,
forcePrivate: Boolean = false,
forceNonFinal: Boolean = false,
additionalReceiverParameter: ((KtUltraLightMethod) -> KtUltraLightParameter)? = null,
): Collection {
ProgressManager.checkCanceled()
if (ktFunction.hasExpectModifier()
|| ktFunction.hasReifiedParameters()
|| ktFunction.hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME)
) return emptyList()
var methodIndex = METHOD_INDEX_BASE
val basicMethod = asJavaMethod(
ktFunction,
forceStatic,
forcePrivate,
methodIndex = methodIndex,
forceNonFinal = forceNonFinal,
additionalReceiverParameter = additionalReceiverParameter,
)
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) {
methodIndex++
result.add(
asJavaMethod(
ktFunction,
forceStatic,
forcePrivate,
numberOfDefaultParametersToAdd = numberOfDefaultParametersToAdd,
methodIndex = methodIndex,
forceNonFinal = forceNonFinal,
additionalReceiverParameter = additionalReceiverParameter,
)
)
}
}
return result
}
internal class KtUltraLightAnnotationMethod(
private val psiMethod: KtLightMethod,
private val expression: KtExpression
) : KtLightMethod by psiMethod, PsiAnnotationMethod {
private val value by lazyPub {
convertToLightAnnotationMemberValue(psiMethod, expression)
}
override fun equals(other: Any?): Boolean = other === this ||
other is KtUltraLightAnnotationMethod &&
other.psiMethod == psiMethod &&
other.expression == expression
override fun hashCode(): Int = psiMethod.hashCode()
override fun toString(): String = "KtUltraLightAnnotationMethod(method=$psiMethod, expression=$expression"
override fun getDefaultValue(): PsiAnnotationMemberValue = value
override fun getSourceElement(): PsiElement? = psiMethod.sourceElement
}
private fun asJavaMethod(
ktFunction: KtFunction,
forceStatic: Boolean,
forcePrivate: Boolean,
numberOfDefaultParametersToAdd: Int = -1,
methodIndex: Int,
forceNonFinal: Boolean = false,
additionalReceiverParameter: ((KtUltraLightMethod) -> KtUltraLightParameter)? = null,
): KtLightMethod {
ProgressManager.checkCanceled()
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, forceNonFinal)
val wrapper = KtUltraLightMethodForSourceDeclaration(method, ktFunction, support, containingClass, methodIndex)
additionalReceiverParameter?.let {
val receiver = it(wrapper)
method.addParameter(receiver)
}
addReceiverParameter(ktFunction, wrapper, method)
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 isSuspendFunction = ktFunction.modifierList?.hasSuspendModifier() == true
if (isSuspendFunction) {
method.addParameter(KtUltraLightSuspendContinuationParameter(ktFunction, support, wrapper))
}
val returnType: PsiType? by lazyPub {
when {
isConstructor -> null
else -> methodReturnType(ktFunction, wrapper, isSuspendFunction)
}
}
method.setMethodReturnType { returnType }
return wrapper
}
private fun addReceiverParameter(callable: KtCallableDeclaration, wrapper: KtUltraLightMethod, associatedBuilder: LightMethodBuilder) {
if (callable.receiverTypeReference == null) return
require(wrapper.delegate == associatedBuilder) {
"Invalid use. Wrapper does not wrap an associated method builder."
}
associatedBuilder.addParameter(KtUltraLightReceiverParameter(callable, support, wrapper))
}
private fun methodReturnType(ktDeclaration: KtDeclaration, wrapper: KtUltraLightMethod, isSuspendFunction: Boolean): PsiType {
if (isSuspendFunction) {
return support.moduleDescriptor
.builtIns
.nullableAnyType
.asPsiType(support, TypeMappingMode.DEFAULT, wrapper)
}
if (ktDeclaration is KtNamedFunction &&
ktDeclaration.hasBlockBody() &&
!ktDeclaration.hasDeclaredReturnType()
) return PsiTypes.voidType()
val desc =
ktDeclaration.resolve()?.getterIfProperty() as? CallableDescriptor
?: return PsiTypes.nullType()
return support.mapType(desc.returnType, wrapper) { typeMapper, signatureWriter ->
typeMapper.mapReturnType(desc, signatureWriter)
}
}
private fun DeclarationDescriptor.getterIfProperty() =
if (this@getterIfProperty is PropertyDescriptor) [email protected] else this@getterIfProperty
private inner class UltraLightModifierListForMember(
private val declaration: KtDeclaration,
private val accessedProperty: KtProperty?,
private val outerDeclaration: KtDeclaration,
private val forceStatic: Boolean,
private val forcePrivate: Boolean = false,
private val forceNonFinal: Boolean = false,
) : LightModifierList(declaration.manager, declaration.language) {
override fun hasModifierProperty(name: String): Boolean {
val hasModifierByDeclaration = hasModifier(name)
if (name != PsiModifier.FINAL) return hasModifierByDeclaration
if (!hasModifierByDeclaration) return false //AllOpen can't modify open to final
//AllOpen can affect on modality of the member. We ought to check if the extension could override the modality
val descriptor = lazy { declaration.resolve() }
var modifier = PsiModifier.FINAL
project.applyCompilerPlugins {
modifier = it.interceptModalityBuilding(declaration, descriptor, modifier)
}
return modifier == PsiModifier.FINAL
}
private fun hasModifier(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 ||
(declaration is KtConstructor<*> && containingClassIsSealed)
) {
return name == PsiModifier.PROTECTED
}
if (outerDeclaration.hasModifier(OVERRIDE_KEYWORD)) {
when ((outerDeclaration.resolve() as? CallableDescriptor)?.visibility) {
DescriptorVisibilities.PUBLIC -> return name == PsiModifier.PUBLIC
DescriptorVisibilities.PRIVATE -> return name == PsiModifier.PRIVATE
DescriptorVisibilities.PROTECTED -> return name == PsiModifier.PROTECTED
}
}
return name == PsiModifier.PUBLIC
}
return when (name) {
PsiModifier.FINAL ->
!forceNonFinal && !containingClass.isInterface && outerDeclaration !is KtConstructor<*> && isFinal(outerDeclaration)
PsiModifier.ABSTRACT -> containingClass.isInterface || outerDeclaration.hasModifier(ABSTRACT_KEYWORD)
PsiModifier.STATIC ->
forceStatic || containingClassIsNamedObject && (outerDeclaration.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)
PsiModifier.NATIVE -> declaration is KtFunction && declaration.hasModifier(EXTERNAL_KEYWORD)
else -> false
}
}
private fun KtDeclaration.isPrivate() =
hasModifier(PRIVATE_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()
}
}
private fun lightMethod(
name: String,
declaration: KtDeclaration,
forceStatic: Boolean,
forcePrivate: Boolean = false,
forceNonFinal: 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),
UltraLightModifierListForMember(declaration, accessedProperty, outer, forceStatic, forcePrivate, forceNonFinal)
).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
return if (mangleInternalFunctions && isInternalNonPublishedApi(declaration))
KotlinTypeMapper.InternalNameMapper.mangleInternalName(name, support.moduleName)
else 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)
}
fun propertyAccessors(
declaration: KtCallableDeclaration,
mutable: Boolean,
forceStatic: Boolean,
onlyJvmStatic: Boolean,
createAsAnnotationMethod: Boolean = false,
isJvmRecord: Boolean = false,
forceNonFinal: Boolean = false,
additionalReceiverParameter: ((KtUltraLightMethod) -> KtUltraLightParameter)? = null,
): List {
ProgressManager.checkCanceled()
val propertyName = declaration.name ?: return emptyList()
if (declaration.isConstOrJvmField() ||
declaration.hasReifiedParameters() ||
declaration.hasExpectModifier()
) return emptyList()
val ktGetter = (declaration as? KtProperty)?.getter
val ktSetter = (declaration as? KtProperty)?.setter
val isPrivate = !forceStatic && declaration.hasModifier(PRIVATE_KEYWORD)
if (isPrivate && declaration !is KtProperty) return emptyList()
fun needsAccessor(accessor: KtPropertyAccessor?, type: MethodType): Boolean {
if (onlyJvmStatic && !declaration.isJvmStatic(support) && !(accessor != null && accessor.isJvmStatic(support)))
return false
if (declaration is KtProperty && declaration.hasDelegate())
return true
if (accessor?.hasBody() != true &&
(accessor?.hasModifier(PRIVATE_KEYWORD) == true ||
accessor?.hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME) == true ||
isPrivate)
) return false
if (!declaration.hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME)) return true
val annotated = (declaration.resolve() as? PropertyDescriptor) ?: return false
val propertyAnnotated = when (type) {
MethodType.GETTER -> annotated.getter
MethodType.SETTER -> annotated.setter
else -> throw NotImplementedError()
}
return propertyAnnotated?.hasJvmSyntheticAnnotation() != true
}
val result = arrayListOf()
if (needsAccessor(ktGetter, MethodType.GETTER)) {
val auxiliaryOrigin = ktGetter ?: declaration
val lightMemberOrigin = LightMemberOriginForDeclaration(
originalElement = declaration,
originKind = JvmDeclarationOriginKind.OTHER,
auxiliaryOriginalElement = auxiliaryOrigin
)
val defaultGetterName = if (createAsAnnotationMethod || isJvmRecord) propertyName else JvmAbi.getterName(propertyName)
val getterName = computeMethodName(auxiliaryOrigin, defaultGetterName, MethodType.GETTER)
val getterPrototype = lightMethod(
getterName,
auxiliaryOrigin,
forceStatic = onlyJvmStatic || forceStatic,
forceNonFinal = forceNonFinal,
)
val getterWrapper = KtUltraLightMethodForSourceDeclaration(
getterPrototype,
lightMemberOrigin,
support,
containingClass,
forceToSkipNullabilityAnnotation = createAsAnnotationMethod,
methodIndex = METHOD_INDEX_FOR_GETTER,
)
val getterType: PsiType by lazyPub { methodReturnType(declaration, getterWrapper, isSuspendFunction = false) }
getterPrototype.setMethodReturnType { getterType }
additionalReceiverParameter?.invoke(getterWrapper)?.let {
getterPrototype.addParameter(it)
}
addReceiverParameter(declaration, getterWrapper, getterPrototype)
val defaultExpression = if (createAsAnnotationMethod && declaration is KtParameter) declaration.defaultValue else null
val getterMethodResult = defaultExpression?.let {
KtUltraLightAnnotationMethod(getterWrapper, it)
} ?: getterWrapper
result.add(getterMethodResult)
}
if (!createAsAnnotationMethod && mutable && needsAccessor(ktSetter, MethodType.SETTER)) {
val auxiliaryOrigin = ktSetter ?: declaration
val lightMemberOrigin = LightMemberOriginForDeclaration(
originalElement = declaration,
originKind = JvmDeclarationOriginKind.OTHER,
auxiliaryOriginalElement = auxiliaryOrigin
)
val setterName = computeMethodName(auxiliaryOrigin, JvmAbi.setterName(propertyName), MethodType.SETTER)
val setterPrototype = lightMethod(
setterName,
auxiliaryOrigin,
forceStatic = onlyJvmStatic || forceStatic,
forceNonFinal = forceNonFinal,
).setMethodReturnType(PsiTypes.voidType())
val setterWrapper = KtUltraLightMethodForSourceDeclaration(
setterPrototype,
lightMemberOrigin,
support,
containingClass,
methodIndex = METHOD_INDEX_FOR_SETTER,
)
additionalReceiverParameter?.invoke(setterWrapper)?.let {
setterPrototype.addParameter(it)
}
addReceiverParameter(declaration, setterWrapper, setterPrototype)
val setterParameter = ktSetter?.parameter
setterPrototype.addParameter(
if (setterParameter != null)
KtUltraLightParameterForSource(
name = setterParameter.name ?: propertyName,
kotlinOrigin = setterParameter,
support = support,
method = setterWrapper,
containingDeclaration = declaration
)
else
KtUltraLightParameterForSetterParameter(
name = SpecialNames.IMPLICIT_SET_PARAMETER.asString(),
property = declaration,
support = support,
method = setterWrapper,
containingDeclaration = declaration
)
)
result.add(setterWrapper)
}
return result
}
private fun KtCallableDeclaration.hasReifiedParameters(): Boolean =
typeParameters.any { it.hasModifier(REIFIED_KEYWORD) }
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)
}
}