From 899002b6817bd95f8d524e4277ce5ee863fd3d74 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Fri, 29 Dec 2017 16:21:58 +0100 Subject: [PATCH] Refactor JavaEnumValueAnnotationArgument and implementations Only require implementations to be able to compute the enum class name and the corresponding entry name. Only this should be necessary to correctly resolve the argument; the resolvers will find the corresponding class descriptor if necessary --- .../structure/impl/annotationArgumentsImpl.kt | 19 +++++++----- .../structure/impl/classFiles/Annotations.kt | 23 +++------------ .../classFiles/ClassifierResolutionContext.kt | 3 ++ .../symbols/symbolBasedAnnotationArguments.kt | 18 +++++++----- .../wrappers/trees/TreeBasedAnnotation.kt | 29 +++++++++++-------- .../kotlin/javac/wrappers/trees/utils.kt | 11 +++---- .../java/components/JavaAnnotationMapper.kt | 4 +-- .../LazyJavaAnnotationDescriptor.kt | 18 +++++++----- .../java/structure/annotationArguments.kt | 3 +- .../load/java/structure/javaElements.kt | 3 ++ .../ReflectJavaAnnotationArguments.kt | 12 ++++---- .../DeserializerForClassfileDecompiler.kt | 10 ++----- 12 files changed, 76 insertions(+), 77 deletions(-) diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/annotationArgumentsImpl.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/annotationArgumentsImpl.kt index 9d9ac93851d..91fb68e8b37 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/annotationArgumentsImpl.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/annotationArgumentsImpl.kt @@ -18,6 +18,8 @@ package org.jetbrains.kotlin.load.java.structure.impl import com.intellij.psi.* import org.jetbrains.kotlin.load.java.structure.* +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name abstract class JavaAnnotationArgumentImpl( @@ -64,14 +66,17 @@ class JavaEnumValueAnnotationArgumentImpl( private val psiReference: PsiReferenceExpression, name: Name? ) : JavaAnnotationArgumentImpl(name), JavaEnumValueAnnotationArgument { - override fun resolve(): JavaField? { - val element = psiReference.resolve() - return when (element) { - null -> null - is PsiEnumConstant -> JavaFieldImpl(element) - else -> throw IllegalStateException("Reference argument should be an enum value, but was $element: ${element.text}") + override val enumClassId: ClassId? + get() { + val element = psiReference.resolve() + if (element is PsiEnumConstant) { + return JavaFieldImpl(element).containingClass.classId + } + + val fqName = (psiReference.qualifier as? PsiReferenceExpression)?.qualifiedName ?: return null + // TODO: find a way to construct a correct name (with nested classes) for unresolved enums + return ClassId.topLevel(FqName(fqName)) } - } override val entryName: Name? get() = psiReference.referenceName?.let(Name::identifier) diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/Annotations.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/Annotations.kt index e35f34d070c..a60822e330f 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/Annotations.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/Annotations.kt @@ -127,7 +127,7 @@ class BinaryJavaAnnotation private constructor( } override val classId: ClassId? - get() = classifierResolutionResult.classifier.safeAs()?.classId() + get() = classifierResolutionResult.classifier.safeAs()?.classId ?: ClassId.topLevel(FqName(classifierResolutionResult.qualifiedName)) override fun resolve() = classifierResolutionResult.classifier as? JavaClass @@ -152,7 +152,7 @@ class BinaryJavaAnnotationVisitor( } override fun visitEnum(name: String?, desc: String, value: String) { - addArgument(PlainJavaEnumValueAnnotationArgument(name, desc, value, context)) + addArgument(PlainJavaEnumValueAnnotationArgument(name, context.mapDescToClassId(desc), value)) } override fun visit(name: String?, value: Any?) { @@ -216,23 +216,8 @@ class PlainJavaAnnotationAsAnnotationArgument( class PlainJavaEnumValueAnnotationArgument( name: String?, - private val desc: String, - entryName: String, - private val context: ClassifierResolutionContext + override val enumClassId: ClassId, + entryName: String ) : PlainJavaAnnotationArgument(name), JavaEnumValueAnnotationArgument { override val entryName = Name.identifier(entryName) - - override fun resolve(): JavaField? { - val javaClass = context.resolveByInternalName(Type.getType(desc).internalName).classifier as? JavaClass ?: return null - return javaClass.fields.singleOrNull { it.name == entryName } - } -} - -private fun JavaClass.classId(): ClassId? { - val fqName = fqName ?: return null - if (outerClass == null) return ClassId.topLevel(fqName) - - val outerClassId = outerClass!!.classId() ?: return null - - return ClassId(outerClassId.packageFqName, outerClassId.relativeClassName.child(name), false) } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/ClassifierResolutionContext.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/ClassifierResolutionContext.kt index c1ef24ac605..9a56272f985 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/ClassifierResolutionContext.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/ClassifierResolutionContext.kt @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name +import org.jetbrains.org.objectweb.asm.Type typealias ClassIdToJavaClass = (ClassId) -> JavaClass? @@ -117,4 +118,6 @@ class ClassifierResolutionContext private constructor( private fun createClassIdForTopLevel(internalName: String) = ClassId.topLevel(FqName(internalName.replace('/', '.'))) internal fun resolveByInternalName(c: String) = resolveClass(mapInternalNameToClassId(c)) + + internal fun mapDescToClassId(desc: String): ClassId = mapInternalNameToClassId(Type.getType(desc).internalName) } diff --git a/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/symbols/symbolBasedAnnotationArguments.kt b/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/symbols/symbolBasedAnnotationArguments.kt index d7eb60b3479..c4569217e6e 100644 --- a/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/symbols/symbolBasedAnnotationArguments.kt +++ b/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/symbols/symbolBasedAnnotationArguments.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.javac.wrappers.symbols import com.sun.tools.javac.code.Symbol import org.jetbrains.kotlin.javac.JavacWrapper import org.jetbrains.kotlin.load.java.structure.* +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name import javax.lang.model.element.AnnotationMirror import javax.lang.model.element.AnnotationValue @@ -63,16 +64,17 @@ class SymbolBasedReferenceAnnotationArgument( name: Name, javac: JavacWrapper ) : SymbolBasedAnnotationArgument(name, javac), JavaEnumValueAnnotationArgument { - override val entryName: Name? - get() = name - - override fun resolve(): SymbolBasedField { - val containingClass = (element.enclosingElement as Symbol.ClassSymbol).let { - SymbolBasedClass(it, javac, null, it.classfile) - } - return SymbolBasedField(element, containingClass, javac) + // TODO: do not create extra objects here + private val javaField: JavaField? by lazy(LazyThreadSafetyMode.PUBLICATION) { + val containingClass = element.enclosingElement as Symbol.ClassSymbol + SymbolBasedField(element, SymbolBasedClass(containingClass, javac, null, containingClass.classfile), javac) } + override val enumClassId: ClassId? + get() = javaField?.containingClass?.classId + + override val entryName: Name? + get() = javaField?.name } class SymbolBasedClassObjectAnnotationArgument( diff --git a/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/trees/TreeBasedAnnotation.kt b/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/trees/TreeBasedAnnotation.kt index fb5127312df..49b4f5ae4fe 100644 --- a/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/trees/TreeBasedAnnotation.kt +++ b/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/trees/TreeBasedAnnotation.kt @@ -50,21 +50,26 @@ class TreeBasedLiteralAnnotationArgument(name: Name, override val value: Any?, javac: JavacWrapper) : TreeBasedAnnotationArgument(name, javac), JavaLiteralAnnotationArgument -class TreeBasedReferenceAnnotationArgument(name: Name, - private val compilationUnit: CompilationUnitTree, - private val field: JCTree.JCFieldAccess, - javac: JavacWrapper, - private val onElement: JavaElement) : TreeBasedAnnotationArgument(name, javac), JavaEnumValueAnnotationArgument { - override val entryName: Name? - get() = name +class TreeBasedReferenceAnnotationArgument( + name: Name, + private val compilationUnit: CompilationUnitTree, + private val field: JCTree.JCFieldAccess, + javac: JavacWrapper, + private val onElement: JavaElement +) : TreeBasedAnnotationArgument(name, javac), JavaEnumValueAnnotationArgument { + // TODO: do not run resolve here + private val javaField: JavaField? by lazy(LazyThreadSafetyMode.PUBLICATION) { + val javaClass = javac.resolve(field.selected, compilationUnit, onElement) as? JavaClass + val fieldName = Name.identifier(field.name.toString()) - override fun resolve(): JavaField? { - val javaClass = javac.resolve(field.selected, compilationUnit, onElement) as? JavaClass ?: return null - val fieldName = field.name.toString().let { Name.identifier(it) } - - return javaClass.fields.find { it.name == fieldName } + javaClass?.fields?.find { it.name == fieldName } } + override val enumClassId: ClassId? + get() = javaField?.containingClass?.classId + + override val entryName: Name? + get() = javaField?.name } class TreeBasedArrayAnnotationArgument(val args: List, diff --git a/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/trees/utils.kt b/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/trees/utils.kt index 55fa2ca219e..27a63db6c71 100644 --- a/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/trees/utils.kt +++ b/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/trees/utils.kt @@ -20,7 +20,6 @@ import com.sun.tools.javac.tree.JCTree import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.javac.wrappers.symbols.SymbolBasedArrayAnnotationArgument -import org.jetbrains.kotlin.javac.wrappers.symbols.SymbolBasedField import org.jetbrains.kotlin.javac.wrappers.symbols.SymbolBasedReferenceAnnotationArgument import org.jetbrains.kotlin.load.java.JavaVisibilities import org.jetbrains.kotlin.load.java.structure.JavaAnnotation @@ -81,18 +80,16 @@ fun Collection.filterTypeAnnotations(): Collection { elementTypeArg.args.find { - val field = (it as? TreeBasedReferenceAnnotationArgument)?.resolve() as? SymbolBasedField - field?.element?.simpleName?.contentEquals("TYPE_USE") ?: false + (it as? TreeBasedReferenceAnnotationArgument)?.entryName?.asString() == "TYPE_USE" }?.let { filteredAnnotations.add(annotation) } } is TreeBasedReferenceAnnotationArgument -> { - (elementTypeArg.resolve() as? SymbolBasedField)?.let { field -> - field.element.simpleName.takeIf { it.contentEquals("TYPE_USE") } - ?.let { filteredAnnotations.add(annotation) } + if (elementTypeArg.entryName?.asString() == "TYPE_USE") { + filteredAnnotations.add(annotation) } } } } return filteredAnnotations -} \ No newline at end of file +} diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/components/JavaAnnotationMapper.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/components/JavaAnnotationMapper.kt index f56f9dbb875..2a956a7a932 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/components/JavaAnnotationMapper.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/components/JavaAnnotationMapper.kt @@ -162,7 +162,7 @@ object JavaAnnotationTargetMapper { internal fun mapJavaTargetArguments(arguments: List, builtIns: KotlinBuiltIns): ConstantValue<*> { // Map arguments: java.lang.annotation.Target -> kotlin.annotation.Target val kotlinTargets = arguments.filterIsInstance() - .flatMap { mapJavaTargetArgumentByName(it.resolve()?.name?.asString()) } + .flatMap { mapJavaTargetArgumentByName(it.entryName?.asString()) } .mapNotNull { builtIns.getAnnotationTargetEnumEntry(it) } .map(::EnumValue) val parameterDescriptor = DescriptorResolverUtils.getAnnotationParameterByName( @@ -181,7 +181,7 @@ object JavaAnnotationTargetMapper { internal fun mapJavaRetentionArgument(element: JavaAnnotationArgument?, builtIns: KotlinBuiltIns): ConstantValue<*>? { // Map argument: java.lang.annotation.Retention -> kotlin.annotation.annotation return (element as? JavaEnumValueAnnotationArgument)?.let { - retentionNameList[it.resolve()?.name?.asString()]?.let { + retentionNameList[it.entryName?.asString()]?.let { builtIns.getAnnotationRetentionEnumEntry(it)?.let(::EnumValue) } } diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaAnnotationDescriptor.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaAnnotationDescriptor.kt index 4b8b74ca683..f213a8fcf7e 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaAnnotationDescriptor.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaAnnotationDescriptor.kt @@ -67,7 +67,7 @@ class LazyJavaAnnotationDescriptor( private fun resolveAnnotationArgument(argument: JavaAnnotationArgument?): ConstantValue<*>? { return when (argument) { is JavaLiteralAnnotationArgument -> ConstantValueFactory.createConstantValue(argument.value) - is JavaEnumValueAnnotationArgument -> resolveFromEnumValue(argument.resolve(), argument.entryName) + is JavaEnumValueAnnotationArgument -> resolveFromEnumValue(argument.enumClassId, argument.entryName) is JavaArrayAnnotationArgument -> resolveFromArray(argument.name ?: DEFAULT_ANNOTATION_MEMBER_NAME, argument.getElements()) is JavaAnnotationAsAnnotationArgument -> resolveFromAnnotation(argument.getAnnotation()) is JavaClassObjectAnnotationArgument -> resolveFromJavaClassObjectType(argument.getReferencedType()) @@ -97,17 +97,19 @@ class LazyJavaAnnotationDescriptor( return ConstantValueFactory.createArrayValue(values, arrayType) } - private fun resolveFromEnumValue(element: JavaField?, entryName: Name?): ConstantValue<*>? { - if (element == null || !element.isEnumEntry) { - if (entryName == null) return null + private fun resolveFromEnumValue(enumClassId: ClassId?, entryName: Name?): ConstantValue<*>? { + if (entryName == null) return null + + if (enumClassId == null) { return ConstantValueFactory.createEnumValue(ErrorUtils.createErrorClassWithExactName(entryName)) } - val containingJavaClass = element.containingClass + val enumClass = c.module.findNonGenericClassAcrossDependencies( + enumClassId, + c.components.deserializedDescriptorResolver.components.notFoundClasses + ) - val enumClass = c.components.moduleClassResolver.resolveClass(containingJavaClass) ?: return null - - val classifier = enumClass.unsubstitutedInnerClassesScope.getContributedClassifier(element.name, NoLookupLocation.FROM_JAVA_LOADER) + val classifier = enumClass.unsubstitutedInnerClassesScope.getContributedClassifier(entryName, NoLookupLocation.FROM_JAVA_LOADER) as? ClassDescriptor ?: return null return ConstantValueFactory.createEnumValue(classifier) diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/structure/annotationArguments.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/structure/annotationArguments.kt index ce5c6db53b7..dcbc99b44cd 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/structure/annotationArguments.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/structure/annotationArguments.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.load.java.structure +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name interface JavaAnnotationArgument { @@ -31,8 +32,8 @@ interface JavaArrayAnnotationArgument : JavaAnnotationArgument { } interface JavaEnumValueAnnotationArgument : JavaAnnotationArgument { + val enumClassId: ClassId? val entryName: Name? - fun resolve(): JavaField? } interface JavaClassObjectAnnotationArgument : JavaAnnotationArgument { diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/structure/javaElements.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/structure/javaElements.kt index 1e9ecd88d6a..88c68f50f05 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/structure/javaElements.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/structure/javaElements.kt @@ -90,6 +90,9 @@ interface JavaClass : JavaClassifier, JavaTypeParameterListOwner, JavaModifierLi val constructors: Collection } +val JavaClass.classId: ClassId? + get() = outerClass?.classId?.createNestedClassId(name) ?: fqName?.let(ClassId::topLevel) + enum class LightClassOriginKind { SOURCE, BINARY } diff --git a/core/descriptors.runtime/src/kotlin/reflect/jvm/internal/structure/ReflectJavaAnnotationArguments.kt b/core/descriptors.runtime/src/kotlin/reflect/jvm/internal/structure/ReflectJavaAnnotationArguments.kt index 00101bc9ca7..633633668d1 100644 --- a/core/descriptors.runtime/src/kotlin/reflect/jvm/internal/structure/ReflectJavaAnnotationArguments.kt +++ b/core/descriptors.runtime/src/kotlin/reflect/jvm/internal/structure/ReflectJavaAnnotationArguments.kt @@ -17,6 +17,7 @@ package kotlin.reflect.jvm.internal.structure import org.jetbrains.kotlin.load.java.structure.* +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name abstract class ReflectJavaAnnotationArgument( @@ -51,11 +52,12 @@ class ReflectJavaEnumValueAnnotationArgument( name: Name?, private val value: Enum<*> ) : ReflectJavaAnnotationArgument(name), JavaEnumValueAnnotationArgument { - override fun resolve(): ReflectJavaField { - val clazz = value::class.java - val enumClass = if (clazz.isEnum) clazz else clazz.enclosingClass - return ReflectJavaField(enumClass.getDeclaredField(value.name)) - } + override val enumClassId: ClassId? + get() { + val clazz = value::class.java + val enumClass = if (clazz.isEnum) clazz else clazz.enclosingClass + return enumClass.classId + } override val entryName: Name? get() = Name.identifier(value.name) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/DeserializerForClassfileDecompiler.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/DeserializerForClassfileDecompiler.kt index 8c6436423f0..c0aee144d17 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/DeserializerForClassfileDecompiler.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/DeserializerForClassfileDecompiler.kt @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.idea.decompiler.textBuilder.LoggingErrorReporter import org.jetbrains.kotlin.idea.decompiler.textBuilder.ResolveEverythingToKotlinAnyLocalClassifierResolver import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.load.java.structure.JavaClass +import org.jetbrains.kotlin.load.java.structure.classId import org.jetbrains.kotlin.load.kotlin.* import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName @@ -104,7 +105,7 @@ class DirectoryBasedClassFinder( val packageDirectory: VirtualFile, val directoryPackageFqName: FqName ) : KotlinClassFinder { - override fun findKotlinClass(javaClass: JavaClass) = findKotlinClass(javaClass.classId) + override fun findKotlinClass(javaClass: JavaClass) = findKotlinClass(javaClass.classId!!) override fun findKotlinClass(classId: ClassId): KotlinJvmBinaryClass? { if (classId.packageFqName != directoryPackageFqName) { @@ -149,10 +150,3 @@ class DirectoryBasedDataFinder( return ClassDataWithSource(JvmProtoBufUtil.readClassDataFrom(data, strings), KotlinJvmBinarySourceElement(binaryClass)) } } - - -private val JavaClass.classId: ClassId - get() { - val outer = outerClass - return if (outer == null) ClassId.topLevel(fqName!!) else outer.classId.createNestedClassId(name) - }