From 6542ea9ba1394b691e663b72ceae62382c6a7267 Mon Sep 17 00:00:00 2001 From: Igor Yakovlev Date: Tue, 14 Jul 2020 15:14:22 +0300 Subject: [PATCH] [ULC] Add constant evaluation to light classes service --- .../CliLightClassGenerationSupport.kt | 15 ++++---- .../asJava/LightClassGenerationSupport.kt | 2 ++ .../asJava/classes/ultraLightSupport.kt | 8 +++-- .../elements/KtLightAnnotationsValues.kt | 4 +-- .../kotlin/asJava/lightClassUtils.kt | 32 +++++++++++++++++ .../resolve/IDELightClassGenerationSupport.kt | 13 +++++-- .../KotlinLightConstantExpressionEvaluator.kt | 36 ++----------------- 7 files changed, 62 insertions(+), 48 deletions(-) diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.kt index 46ebd85b40c..da1874ca568 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.kt @@ -24,9 +24,6 @@ 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.KtUltraLightClass -import org.jetbrains.kotlin.asJava.classes.KtUltraLightClassForFacade -import org.jetbrains.kotlin.asJava.classes.KtUltraLightClassForScript import org.jetbrains.kotlin.asJava.classes.* import org.jetbrains.kotlin.codegen.ClassBuilderMode import org.jetbrains.kotlin.codegen.JvmCodegenUtil @@ -36,7 +33,6 @@ import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.load.java.components.JavaDeprecationSettings import org.jetbrains.kotlin.name.FqName @@ -44,9 +40,9 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.deprecation.CoroutineCompatibilitySupport import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver -import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator as ResolveEvaluator /** * This class solves the problem of interdependency between analyzing Kotlin code and generating JetLightClasses @@ -64,12 +60,17 @@ class CliLightClassGenerationSupport(private val traceHolder: CliTraceHolder) : private val ultraLightSupport = object : KtUltraLightSupport { + //TODO: languageVersionSettings is always default private val languageVersionSettings: LanguageVersionSettings - get() = getContext().languageVersionSettings ?: LanguageVersionSettingsImpl.DEFAULT + get() = getContext().languageVersionSettings + ?: LanguageVersionSettingsImpl.DEFAULT override val isReleasedCoroutine get() = languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines) + override fun getConstantEvaluator(expression: KtExpression): org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator = + ResolveEvaluator(moduleDescriptor, languageVersionSettings, expression.project) + override val moduleDescriptor get() = traceHolder.module override val moduleName: String get() = JvmCodegenUtil.getModuleName(moduleDescriptor) @@ -174,4 +175,6 @@ class CliLightClassGenerationSupport(private val traceHolder: CliTraceHolder) : override fun analyzeAnnotation(element: KtAnnotationEntry) = traceHolder.bindingContext.get(BindingContext.ANNOTATION, element) override fun analyzeWithContent(element: KtClassOrObject) = traceHolder.bindingContext + + } \ No newline at end of file diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.kt index 7c26896d7a4..4c2da1332b1 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.kt @@ -18,6 +18,8 @@ package org.jetbrains.kotlin.asJava import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.project.Project +import com.intellij.psi.PsiConstantEvaluationHelper +import com.intellij.psi.PsiElement import com.intellij.psi.PsiManager import com.intellij.psi.util.CachedValue import org.jetbrains.kotlin.asJava.builder.LightClassBuilderResult diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightSupport.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightSupport.kt index 5023cfbc66c..12ebcbf18c8 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightSupport.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightSupport.kt @@ -9,12 +9,10 @@ import org.jetbrains.annotations.TestOnly import org.jetbrains.kotlin.asJava.LightClassGenerationSupport import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper import org.jetbrains.kotlin.descriptors.ModuleDescriptor -import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor -import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver -import java.util.concurrent.ConcurrentMap interface KtUltraLightSupport { val moduleName: String @@ -23,6 +21,7 @@ interface KtUltraLightSupport { val moduleDescriptor: ModuleDescriptor val isReleasedCoroutine: Boolean fun hasAlias(file: KtFile, shortName: Name?): Boolean = false + fun getConstantEvaluator(expression: KtExpression): ConstantExpressionEvaluator companion object { // This property may be removed once IntelliJ versions earlier than 2018.3 become unsupported @@ -52,4 +51,7 @@ class UltraLightSupportViaService(private val ktElement: KtElement) : KtUltraLig override val isReleasedCoroutine: Boolean get() = serviceProvidedSupport.isReleasedCoroutine + + override fun getConstantEvaluator(expression: KtExpression): ConstantExpressionEvaluator = + serviceProvidedSupport.getConstantEvaluator(expression) } diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightAnnotationsValues.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightAnnotationsValues.kt index 92be3784293..5f314f7def0 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightAnnotationsValues.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightAnnotationsValues.kt @@ -13,6 +13,7 @@ import com.intellij.psi.impl.light.LightTypeElement import org.jetbrains.kotlin.asJava.LightClassGenerationSupport import org.jetbrains.kotlin.asJava.classes.cannotModify import org.jetbrains.kotlin.asJava.classes.lazyPub +import org.jetbrains.kotlin.asJava.computeExpression import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.psi.* @@ -39,8 +40,7 @@ open class KtLightPsiLiteral( private val lightParent: PsiElement ) : KtLightElementBase(lightParent), PsiLiteralExpression { - override fun getValue(): Any? = - LanguageConstantExpressionEvaluator.INSTANCE.forLanguage(kotlinOrigin.language)?.computeConstantExpression(this, false) + override fun getValue(): Any? = computeExpression(this) override fun getType(): PsiType? { val bindingContext = LightClassGenerationSupport.getInstance(this.project).analyze(kotlinOrigin) diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt index abd7058deaf..dd882824f6a 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt @@ -21,6 +21,7 @@ import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.asJava.classes.KtLightClass import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade import org.jetbrains.kotlin.asJava.classes.KtUltraLightElementWithNullabilityAnnotation +import org.jetbrains.kotlin.asJava.classes.runReadAction import org.jetbrains.kotlin.asJava.elements.PsiElementWithOrigin import org.jetbrains.kotlin.asJava.elements.* import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap @@ -32,6 +33,10 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name 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 /** * Can be null in scripts and for elements from non-jvm modules. @@ -248,4 +253,31 @@ fun fastCheckIsNullabilityApplied(lightElement: KtLightElement<*, PsiModifierLis } return true +} + +fun computeExpression(expression: PsiElement): Any? { + fun evalConstantValue(constantValue: ConstantValue<*>): Any? { + return 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 + .getUltraLightClassSupport(expressionToCompute) + .getConstantEvaluator(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)) } \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt index 4d670fb7fca..c046e24cdbf 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt @@ -38,6 +38,7 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.NoDescriptorForDeclarationException @@ -46,12 +47,18 @@ import java.util.concurrent.ConcurrentMap class IDELightClassGenerationSupport(private val project: Project) : LightClassGenerationSupport() { - private inner class KtUltraLightSupportImpl(private val element: KtElement) : KtUltraLightSupport { + private class KtUltraLightSupportImpl(private val element: KtElement) : KtUltraLightSupport { private val module = ModuleUtilCore.findModuleForPsiElement(element) + private val languageVersionSettings + get() = module?.languageVersionSettings ?: KotlinTypeMapper.LANGUAGE_VERSION_SETTINGS_DEFAULT + override val isReleasedCoroutine - get() = module?.languageVersionSettings?.supportsFeature(LanguageFeature.ReleaseCoroutines) ?: true + get() = languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines) ?: true + + override fun getConstantEvaluator(expression: KtExpression): ConstantExpressionEvaluator = + ConstantExpressionEvaluator(moduleDescriptor, KotlinTypeMapper.LANGUAGE_VERSION_SETTINGS_DEFAULT, expression.project) private val resolutionFacade get() = element.getResolutionFacade() @@ -68,7 +75,7 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG override val typeMapper: KotlinTypeMapper by lazyPub { KotlinTypeMapper( BindingContext.EMPTY, ClassBuilderMode.LIGHT_CLASSES, - moduleName, KotlinTypeMapper.LANGUAGE_VERSION_SETTINGS_DEFAULT, // TODO use proper LanguageVersionSettings + moduleName, languageVersionSettings, jvmTarget = JvmTarget.JVM_1_8, typePreprocessor = KotlinType::cleanFromAnonymousTypes, namePreprocessor = ::tryGetPredefinedName diff --git a/idea/src/org/jetbrains/kotlin/idea/KotlinLightConstantExpressionEvaluator.kt b/idea/src/org/jetbrains/kotlin/idea/KotlinLightConstantExpressionEvaluator.kt index 1ac22878c9b..8f9ca2380cf 100644 --- a/idea/src/org/jetbrains/kotlin/idea/KotlinLightConstantExpressionEvaluator.kt +++ b/idea/src/org/jetbrains/kotlin/idea/KotlinLightConstantExpressionEvaluator.kt @@ -19,24 +19,10 @@ package org.jetbrains.kotlin.idea import com.intellij.psi.PsiConstantEvaluationHelper import com.intellij.psi.PsiElement import com.intellij.psi.impl.ConstantExpressionEvaluator -import org.jetbrains.kotlin.asJava.elements.KtLightElementBase -import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade -import org.jetbrains.kotlin.idea.project.languageVersionSettings -import org.jetbrains.kotlin.idea.util.application.runReadAction +import org.jetbrains.kotlin.asJava.computeExpression 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 -import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator as FrontendConstantExpressionEvaluator class KotlinLightConstantExpressionEvaluator : ConstantExpressionEvaluator { - private fun evalConstantValue(constantValue: ConstantValue<*>): Any? { - return if (constantValue is ArrayValue) { - val items = constantValue.value.map { evalConstantValue(it) } - items.singleOrNull() ?: items - } else constantValue.value - } override fun computeConstantExpression(expression: PsiElement, throwExceptionOnOverflow: Boolean): Any? { return computeExpression(expression, throwExceptionOnOverflow, null) @@ -46,23 +32,5 @@ class KotlinLightConstantExpressionEvaluator : ConstantExpressionEvaluator { expression: PsiElement, throwExceptionOnOverflow: Boolean, auxEvaluator: PsiConstantEvaluationHelper.AuxEvaluator? - ): Any? { - val expressionToCompute = when (expression) { - is KtLightElementBase -> expression.kotlinOrigin as? KtExpression ?: return null - else -> return null - } - - val resolutionFacade = expressionToCompute.getResolutionFacade() - val evaluator = FrontendConstantExpressionEvaluator( - resolutionFacade.moduleDescriptor, expressionToCompute.languageVersionSettings, resolutionFacade.project - ) - - val constant = runReadAction { - val evaluatorTrace = DelegatingBindingTrace(resolutionFacade.analyze(expressionToCompute), "Evaluating annotation argument") - evaluator.evaluateExpression(expressionToCompute, evaluatorTrace) - } ?: return null - - if (constant.isError) return null - return evalConstantValue(constant.toConstantValue(TypeUtils.NO_EXPECTED_TYPE)) - } + ): Any? = computeExpression(expression) }