From 83f4a9752ba3237685eb5c03e2f883ff5843e76b Mon Sep 17 00:00:00 2001 From: Dmitry Gridin Date: Fri, 8 Jul 2022 19:44:18 +0200 Subject: [PATCH] [light classes] move lightClassUtils to light-classes-base module ^KT-53097 --- .../asJava/{utils.kt => lightClassUtils.kt} | 113 ++++++++++++- .../kotlin/asJava/lightClassUtils.kt | 151 ------------------ .../src/org/jetbrains/kotlin/asJava/utils.kt | 49 ++++++ 3 files changed, 156 insertions(+), 157 deletions(-) rename compiler/light-classes-base/src/org/jetbrains/kotlin/asJava/{utils.kt => lightClassUtils.kt} (65%) delete mode 100644 compiler/light-classes/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt create mode 100644 compiler/light-classes/src/org/jetbrains/kotlin/asJava/utils.kt diff --git a/compiler/light-classes-base/src/org/jetbrains/kotlin/asJava/utils.kt b/compiler/light-classes-base/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt similarity index 65% rename from compiler/light-classes-base/src/org/jetbrains/kotlin/asJava/utils.kt rename to compiler/light-classes-base/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt index cd49e8e285e..e0616f7778a 100644 --- a/compiler/light-classes-base/src/org/jetbrains/kotlin/asJava/utils.kt +++ b/compiler/light-classes-base/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt @@ -21,10 +21,7 @@ import org.jetbrains.kotlin.load.java.propertyNamesBySetMethodName import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject -import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType -import org.jetbrains.kotlin.psi.psiUtil.isPrivate -import org.jetbrains.kotlin.psi.psiUtil.isTopLevelKtOrJavaMember +import org.jetbrains.kotlin.psi.psiUtil.* /** * Can be null in scripts and for elements from non-jvm modules. @@ -48,6 +45,83 @@ fun KtFile.findFacadeClass(): KtLightClass? = KotlinAsJavaSupport.getInstance(pr fun KtScript.toLightClass(): KtLightClass? = KotlinAsJavaSupport.getInstance(project).getLightClassForScript(this) +fun KtElement.toLightElements(): List = when (this) { + is KtClassOrObject -> listOfNotNull(toLightClass()) + is KtNamedFunction, + is KtConstructor<*> -> LightClassUtil.getLightClassMethods(this as KtFunction) + is KtProperty -> LightClassUtil.getLightClassPropertyMethods(this).allDeclarations + is KtPropertyAccessor -> listOfNotNull(LightClassUtil.getLightClassAccessorMethod(this)) + is KtParameter -> mutableListOf().also { elements -> + toPsiParameters().toCollection(elements) + LightClassUtil.getLightClassPropertyMethods(this).toCollection(elements) + toAnnotationLightMethod()?.let(elements::add) + } + + is KtTypeParameter -> toPsiTypeParameters() + is KtFile -> listOfNotNull(findFacadeClass()) + else -> listOf() +} + +fun PsiElement.toLightMethods(): List = when (this) { + is KtFunction -> LightClassUtil.getLightClassMethods(this) + is KtProperty -> LightClassUtil.getLightClassPropertyMethods(this).toList() + is KtParameter -> LightClassUtil.getLightClassPropertyMethods(this).toList() + is KtPropertyAccessor -> LightClassUtil.getLightClassAccessorMethods(this) + is KtClass -> listOfNotNull(toLightClass()?.constructors?.firstOrNull()) + is PsiMethod -> listOf(this) + else -> listOf() +} + +fun PsiElement.getRepresentativeLightMethod(): PsiMethod? = when (this) { + is KtFunction -> LightClassUtil.getLightClassMethod(this) + is KtProperty -> LightClassUtil.getLightClassPropertyMethods(this).getter + is KtParameter -> LightClassUtil.getLightClassPropertyMethods(this).getter + is KtPropertyAccessor -> LightClassUtil.getLightClassAccessorMethod(this) + is PsiMethod -> this + else -> null +} + +fun KtParameter.toPsiParameters(): Collection { + val paramList = getNonStrictParentOfType() ?: return emptyList() + + val paramIndex = paramList.parameters.indexOf(this) + if (paramIndex < 0) return emptyList() + val owner = paramList.parent + val lightParamIndex = if (owner is KtDeclaration && owner.isExtensionDeclaration()) paramIndex + 1 else paramIndex + + val methods: Collection = when (owner) { + is KtFunction -> LightClassUtil.getLightClassMethods(owner) + is KtPropertyAccessor -> LightClassUtil.getLightClassAccessorMethods(owner) + else -> null + } ?: return emptyList() + + return methods.mapNotNull { it.parameterList.parameters.getOrNull(lightParamIndex) } +} + +private fun KtParameter.toAnnotationLightMethod(): PsiMethod? { + val parent = ownerFunction as? KtPrimaryConstructor ?: return null + val containingClass = parent.getContainingClassOrObject() + if (!containingClass.isAnnotation()) return null + + return LightClassUtil.getLightClassMethod(this) +} + +fun KtParameter.toLightGetter(): PsiMethod? = LightClassUtil.getLightClassPropertyMethods(this).getter + +fun KtParameter.toLightSetter(): PsiMethod? = LightClassUtil.getLightClassPropertyMethods(this).setter + +fun KtTypeParameter.toPsiTypeParameters(): List { + val paramList = getNonStrictParentOfType() ?: return listOf() + + val paramIndex = paramList.parameters.indexOf(this) + val ktDeclaration = paramList.getNonStrictParentOfType() ?: return listOf() + val lightOwners = ktDeclaration.toLightElements() + + return lightOwners.mapNotNull { lightOwner -> + (lightOwner as? PsiTypeParameterListOwner)?.typeParameters?.getOrNull(paramIndex) + } +} + // Returns original declaration if given PsiElement is a Kotlin light element, and element itself otherwise val PsiElement.unwrapped: PsiElement? get() = when (this) { @@ -60,11 +134,10 @@ val PsiElement.unwrapped: PsiElement? val PsiElement.namedUnwrappedElement: PsiNamedElement? get() = unwrapped?.getNonStrictParentOfType() + val KtClassOrObject.hasInterfaceDefaultImpls: Boolean get() = this is KtClass && isInterface() && hasNonAbstractMembers(this) -private fun hasNonAbstractMembers(ktInterface: KtClass): Boolean = ktInterface.declarations.any(::isNonAbstractMember) - val KtClassOrObject.hasRepeatableAnnotationContainer: Boolean get() = this is KtClass && isAnnotation() && @@ -81,6 +154,8 @@ val KtClassOrObject.hasRepeatableAnnotationContainer: Boolean return hasRepeatableAnnotation } +private fun hasNonAbstractMembers(ktInterface: KtClass): Boolean = ktInterface.declarations.any(::isNonAbstractMember) + private fun isNonAbstractMember(member: KtDeclaration?): Boolean = (member is KtNamedFunction && member.hasBody()) || (member is KtProperty && (member.hasDelegateExpressionOrInitializer() || member.getter?.hasBody() ?: false || member.setter?.hasBody() ?: false)) @@ -91,6 +166,31 @@ fun FqName.defaultImplsChild() = child(DEFAULT_IMPLS_CLASS_NAME) private val REPEATABLE_ANNOTATION_CONTAINER_NAME = Name.identifier(JvmAbi.REPEATABLE_ANNOTATION_CONTAINER_NAME) fun FqName.repeatableAnnotationContainerChild() = child(REPEATABLE_ANNOTATION_CONTAINER_NAME) +@Suppress("unused") +fun KtElement.toLightAnnotation(): PsiAnnotation? { + val ktDeclaration = getStrictParentOfType()?.parent as? KtDeclaration ?: return null + for (lightElement in ktDeclaration.toLightElements()) { + if (lightElement !is PsiModifierListOwner) continue + for (rootAnnotation in lightElement.modifierList?.annotations ?: continue) { + for (annotation in rootAnnotation.withNestedAnnotations()) { + if (annotation is KtLightElement<*, *> && annotation.kotlinOrigin == this) + return annotation + } + } + } + return null +} + +private fun PsiAnnotation.withNestedAnnotations(): Sequence { + fun handleValue(memberValue: PsiAnnotationMemberValue?): Sequence = when (memberValue) { + is PsiArrayInitializerMemberValue -> memberValue.initializers.asSequence().flatMap { handleValue(it) } + is PsiAnnotation -> memberValue.withNestedAnnotations() + else -> emptySequence() + } + + return sequenceOf(this) + parameterList.attributes.asSequence().flatMap { handleValue(it.value) } +} + fun propertyNameByAccessor(name: String, accessor: KtLightMethod): String? { val toRename = accessor.kotlinOrigin ?: return null if (toRename !is KtProperty && toRename !is KtParameter) return null @@ -148,6 +248,7 @@ fun fastCheckIsNullabilityApplied(lightElement: KtLightElement<*, PsiModifierLis return true } + private val PsiMethod.canBeGetter: Boolean get() = JvmAbi.isGetterName(name) && parameters.isEmpty() && returnTypeElement?.textMatches("void") != true diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt deleted file mode 100644 index f1cf542fb61..00000000000 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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 - -import com.intellij.psi.* -import org.jetbrains.kotlin.asJava.classes.runReadAction -import org.jetbrains.kotlin.asJava.elements.* -import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.* -import org.jetbrains.kotlin.resolve.DelegatingBindingTrace -import org.jetbrains.kotlin.resolve.constants.ArrayValue -import org.jetbrains.kotlin.resolve.constants.ConstantValue -import org.jetbrains.kotlin.types.TypeUtils - -fun KtElement.toLightElements(): List = when (this) { - is KtClassOrObject -> listOfNotNull(toLightClass()) - is KtNamedFunction, - is KtConstructor<*> -> LightClassUtil.getLightClassMethods(this as KtFunction) - is KtProperty -> LightClassUtil.getLightClassPropertyMethods(this).allDeclarations - is KtPropertyAccessor -> listOfNotNull(LightClassUtil.getLightClassAccessorMethod(this)) - is KtParameter -> mutableListOf().also { elements -> - toPsiParameters().toCollection(elements) - LightClassUtil.getLightClassPropertyMethods(this).toCollection(elements) - toAnnotationLightMethod()?.let(elements::add) - } - - is KtTypeParameter -> toPsiTypeParameters() - is KtFile -> listOfNotNull(findFacadeClass()) - else -> listOf() -} - -fun PsiElement.toLightMethods(): List = when (this) { - is KtFunction -> LightClassUtil.getLightClassMethods(this) - is KtProperty -> LightClassUtil.getLightClassPropertyMethods(this).toList() - is KtParameter -> LightClassUtil.getLightClassPropertyMethods(this).toList() - is KtPropertyAccessor -> LightClassUtil.getLightClassAccessorMethods(this) - is KtClass -> listOfNotNull(toLightClass()?.constructors?.firstOrNull()) - is PsiMethod -> listOf(this) - else -> listOf() -} - -fun PsiElement.getRepresentativeLightMethod(): PsiMethod? = when (this) { - is KtFunction -> LightClassUtil.getLightClassMethod(this) - is KtProperty -> LightClassUtil.getLightClassPropertyMethods(this).getter - is KtParameter -> LightClassUtil.getLightClassPropertyMethods(this).getter - is KtPropertyAccessor -> LightClassUtil.getLightClassAccessorMethod(this) - is PsiMethod -> this - else -> null -} - -fun KtParameter.toPsiParameters(): Collection { - val paramList = getNonStrictParentOfType() ?: return emptyList() - - val paramIndex = paramList.parameters.indexOf(this) - if (paramIndex < 0) return emptyList() - val owner = paramList.parent - val lightParamIndex = if (owner is KtDeclaration && owner.isExtensionDeclaration()) paramIndex + 1 else paramIndex - - val methods: Collection = when (owner) { - is KtFunction -> LightClassUtil.getLightClassMethods(owner) - is KtPropertyAccessor -> LightClassUtil.getLightClassAccessorMethods(owner) - else -> null - } ?: return emptyList() - - return methods.mapNotNull { it.parameterList.parameters.getOrNull(lightParamIndex) } -} - -private fun KtParameter.toAnnotationLightMethod(): PsiMethod? { - val parent = ownerFunction as? KtPrimaryConstructor ?: return null - val containingClass = parent.getContainingClassOrObject() - if (!containingClass.isAnnotation()) return null - - return LightClassUtil.getLightClassMethod(this) -} - -fun KtParameter.toLightGetter(): PsiMethod? = LightClassUtil.getLightClassPropertyMethods(this).getter - -fun KtParameter.toLightSetter(): PsiMethod? = LightClassUtil.getLightClassPropertyMethods(this).setter - -fun KtTypeParameter.toPsiTypeParameters(): List { - val paramList = getNonStrictParentOfType() ?: return listOf() - - val paramIndex = paramList.parameters.indexOf(this) - val ktDeclaration = paramList.getNonStrictParentOfType() ?: return listOf() - val lightOwners = ktDeclaration.toLightElements() - - return lightOwners.mapNotNull { lightOwner -> - (lightOwner as? PsiTypeParameterListOwner)?.typeParameters?.getOrNull(paramIndex) - } -} - -@Suppress("unused") -fun KtElement.toLightAnnotation(): PsiAnnotation? { - val ktDeclaration = getStrictParentOfType()?.parent as? KtDeclaration ?: return null - for (lightElement in ktDeclaration.toLightElements()) { - if (lightElement !is PsiModifierListOwner) continue - for (rootAnnotation in lightElement.modifierList?.annotations ?: continue) { - for (annotation in rootAnnotation.withNestedAnnotations()) { - if (annotation is KtLightElement<*, *> && annotation.kotlinOrigin == this) - return annotation - } - } - } - return null -} - -private fun PsiAnnotation.withNestedAnnotations(): Sequence { - fun handleValue(memberValue: PsiAnnotationMemberValue?): Sequence = when (memberValue) { - is PsiArrayInitializerMemberValue -> memberValue.initializers.asSequence().flatMap { handleValue(it) } - is PsiAnnotation -> memberValue.withNestedAnnotations() - else -> emptySequence() - } - - return sequenceOf(this) + parameterList.attributes.asSequence().flatMap { handleValue(it.value) } -} - -fun KtLightMethod.checkIsMangled(): Boolean { - val demangledName = KotlinTypeMapper.InternalNameMapper.demangleInternalName(name) ?: return false - val originalName = propertyNameByAccessor(demangledName, this) ?: demangledName - return originalName == kotlinOrigin?.name -} - -fun computeExpression(expression: PsiElement): Any? { - fun evalConstantValue(constantValue: ConstantValue<*>): Any? = - if (constantValue is ArrayValue) { - val items = constantValue.value.map { evalConstantValue(it) } - items.singleOrNull() ?: items - } else { - constantValue.value - } - - val expressionToCompute = when (expression) { - is KtLightElementBase -> expression.kotlinOrigin as? KtExpression ?: return null - else -> return null - } - - val generationSupport = LightClassGenerationSupport.getInstance(expressionToCompute.project) - val evaluator = generationSupport.createConstantEvaluator(expressionToCompute) - - val constant = runReadAction { - val evaluatorTrace = DelegatingBindingTrace(generationSupport.analyze(expressionToCompute), "Evaluating annotation argument") - evaluator.evaluateExpression(expressionToCompute, evaluatorTrace) - } ?: return null - - if (constant.isError) return null - return evalConstantValue(constant.toConstantValue(TypeUtils.NO_EXPECTED_TYPE)) -} diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/utils.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/utils.kt new file mode 100644 index 00000000000..6730c027cd1 --- /dev/null +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/utils.kt @@ -0,0 +1,49 @@ +/* + * 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 + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.asJava.classes.runReadAction +import org.jetbrains.kotlin.asJava.elements.KtLightElementBase +import org.jetbrains.kotlin.asJava.elements.KtLightMethod +import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.resolve.DelegatingBindingTrace +import org.jetbrains.kotlin.resolve.constants.ArrayValue +import org.jetbrains.kotlin.resolve.constants.ConstantValue +import org.jetbrains.kotlin.types.TypeUtils + +fun KtLightMethod.checkIsMangled(): Boolean { + val demangledName = KotlinTypeMapper.InternalNameMapper.demangleInternalName(name) ?: return false + val originalName = propertyNameByAccessor(demangledName, this) ?: demangledName + return originalName == kotlinOrigin?.name +} + +fun computeExpression(expression: PsiElement): Any? { + fun evalConstantValue(constantValue: ConstantValue<*>): Any? = + if (constantValue is ArrayValue) { + val items = constantValue.value.map { evalConstantValue(it) } + items.singleOrNull() ?: items + } else { + constantValue.value + } + + val expressionToCompute = when (expression) { + is KtLightElementBase -> expression.kotlinOrigin as? KtExpression ?: return null + else -> return null + } + + val generationSupport = LightClassGenerationSupport.getInstance(expressionToCompute.project) + val evaluator = generationSupport.createConstantEvaluator(expressionToCompute) + + val constant = runReadAction { + val evaluatorTrace = DelegatingBindingTrace(generationSupport.analyze(expressionToCompute), "Evaluating annotation argument") + evaluator.evaluateExpression(expressionToCompute, evaluatorTrace) + } ?: return null + + if (constant.isError) return null + return evalConstantValue(constant.toConstantValue(TypeUtils.NO_EXPECTED_TYPE)) +}