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
This commit is contained in:
Alexander Udalov
2017-12-29 16:21:58 +01:00
parent 907f53e539
commit 899002b681
12 changed files with 76 additions and 77 deletions
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.load.java.structure.impl
import com.intellij.psi.* import com.intellij.psi.*
import org.jetbrains.kotlin.load.java.structure.* 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 import org.jetbrains.kotlin.name.Name
abstract class JavaAnnotationArgumentImpl( abstract class JavaAnnotationArgumentImpl(
@@ -64,14 +66,17 @@ class JavaEnumValueAnnotationArgumentImpl(
private val psiReference: PsiReferenceExpression, private val psiReference: PsiReferenceExpression,
name: Name? name: Name?
) : JavaAnnotationArgumentImpl(name), JavaEnumValueAnnotationArgument { ) : JavaAnnotationArgumentImpl(name), JavaEnumValueAnnotationArgument {
override fun resolve(): JavaField? { override val enumClassId: ClassId?
val element = psiReference.resolve() get() {
return when (element) { val element = psiReference.resolve()
null -> null if (element is PsiEnumConstant) {
is PsiEnumConstant -> JavaFieldImpl(element) return JavaFieldImpl(element).containingClass.classId
else -> throw IllegalStateException("Reference argument should be an enum value, but was $element: ${element.text}") }
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? override val entryName: Name?
get() = psiReference.referenceName?.let(Name::identifier) get() = psiReference.referenceName?.let(Name::identifier)
@@ -127,7 +127,7 @@ class BinaryJavaAnnotation private constructor(
} }
override val classId: ClassId? override val classId: ClassId?
get() = classifierResolutionResult.classifier.safeAs<JavaClass>()?.classId() get() = classifierResolutionResult.classifier.safeAs<JavaClass>()?.classId
?: ClassId.topLevel(FqName(classifierResolutionResult.qualifiedName)) ?: ClassId.topLevel(FqName(classifierResolutionResult.qualifiedName))
override fun resolve() = classifierResolutionResult.classifier as? JavaClass override fun resolve() = classifierResolutionResult.classifier as? JavaClass
@@ -152,7 +152,7 @@ class BinaryJavaAnnotationVisitor(
} }
override fun visitEnum(name: String?, desc: String, value: String) { 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?) { override fun visit(name: String?, value: Any?) {
@@ -216,23 +216,8 @@ class PlainJavaAnnotationAsAnnotationArgument(
class PlainJavaEnumValueAnnotationArgument( class PlainJavaEnumValueAnnotationArgument(
name: String?, name: String?,
private val desc: String, override val enumClassId: ClassId,
entryName: String, entryName: String
private val context: ClassifierResolutionContext
) : PlainJavaAnnotationArgument(name), JavaEnumValueAnnotationArgument { ) : PlainJavaAnnotationArgument(name), JavaEnumValueAnnotationArgument {
override val entryName = Name.identifier(entryName) 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)
} }
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.org.objectweb.asm.Type
typealias ClassIdToJavaClass = (ClassId) -> JavaClass? typealias ClassIdToJavaClass = (ClassId) -> JavaClass?
@@ -117,4 +118,6 @@ class ClassifierResolutionContext private constructor(
private fun createClassIdForTopLevel(internalName: String) = ClassId.topLevel(FqName(internalName.replace('/', '.'))) private fun createClassIdForTopLevel(internalName: String) = ClassId.topLevel(FqName(internalName.replace('/', '.')))
internal fun resolveByInternalName(c: String) = resolveClass(mapInternalNameToClassId(c)) internal fun resolveByInternalName(c: String) = resolveClass(mapInternalNameToClassId(c))
internal fun mapDescToClassId(desc: String): ClassId = mapInternalNameToClassId(Type.getType(desc).internalName)
} }
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.javac.wrappers.symbols
import com.sun.tools.javac.code.Symbol import com.sun.tools.javac.code.Symbol
import org.jetbrains.kotlin.javac.JavacWrapper import org.jetbrains.kotlin.javac.JavacWrapper
import org.jetbrains.kotlin.load.java.structure.* import org.jetbrains.kotlin.load.java.structure.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import javax.lang.model.element.AnnotationMirror import javax.lang.model.element.AnnotationMirror
import javax.lang.model.element.AnnotationValue import javax.lang.model.element.AnnotationValue
@@ -63,16 +64,17 @@ class SymbolBasedReferenceAnnotationArgument(
name: Name, name: Name,
javac: JavacWrapper javac: JavacWrapper
) : SymbolBasedAnnotationArgument(name, javac), JavaEnumValueAnnotationArgument { ) : SymbolBasedAnnotationArgument(name, javac), JavaEnumValueAnnotationArgument {
override val entryName: Name? // TODO: do not create extra objects here
get() = name private val javaField: JavaField? by lazy(LazyThreadSafetyMode.PUBLICATION) {
val containingClass = element.enclosingElement as Symbol.ClassSymbol
override fun resolve(): SymbolBasedField { SymbolBasedField(element, SymbolBasedClass(containingClass, javac, null, containingClass.classfile), javac)
val containingClass = (element.enclosingElement as Symbol.ClassSymbol).let {
SymbolBasedClass(it, javac, null, it.classfile)
}
return SymbolBasedField(element, containingClass, javac)
} }
override val enumClassId: ClassId?
get() = javaField?.containingClass?.classId
override val entryName: Name?
get() = javaField?.name
} }
class SymbolBasedClassObjectAnnotationArgument( class SymbolBasedClassObjectAnnotationArgument(
@@ -50,21 +50,26 @@ class TreeBasedLiteralAnnotationArgument(name: Name,
override val value: Any?, override val value: Any?,
javac: JavacWrapper) : TreeBasedAnnotationArgument(name, javac), JavaLiteralAnnotationArgument javac: JavacWrapper) : TreeBasedAnnotationArgument(name, javac), JavaLiteralAnnotationArgument
class TreeBasedReferenceAnnotationArgument(name: Name, class TreeBasedReferenceAnnotationArgument(
private val compilationUnit: CompilationUnitTree, name: Name,
private val field: JCTree.JCFieldAccess, private val compilationUnit: CompilationUnitTree,
javac: JavacWrapper, private val field: JCTree.JCFieldAccess,
private val onElement: JavaElement) : TreeBasedAnnotationArgument(name, javac), JavaEnumValueAnnotationArgument { javac: JavacWrapper,
override val entryName: Name? private val onElement: JavaElement
get() = name ) : 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? { javaClass?.fields?.find { it.name == fieldName }
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 }
} }
override val enumClassId: ClassId?
get() = javaField?.containingClass?.classId
override val entryName: Name?
get() = javaField?.name
} }
class TreeBasedArrayAnnotationArgument(val args: List<JavaAnnotationArgument>, class TreeBasedArrayAnnotationArgument(val args: List<JavaAnnotationArgument>,
@@ -20,7 +20,6 @@ import com.sun.tools.javac.tree.JCTree
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.javac.wrappers.symbols.SymbolBasedArrayAnnotationArgument 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.javac.wrappers.symbols.SymbolBasedReferenceAnnotationArgument
import org.jetbrains.kotlin.load.java.JavaVisibilities import org.jetbrains.kotlin.load.java.JavaVisibilities
import org.jetbrains.kotlin.load.java.structure.JavaAnnotation import org.jetbrains.kotlin.load.java.structure.JavaAnnotation
@@ -81,18 +80,16 @@ fun Collection<JavaAnnotation>.filterTypeAnnotations(): Collection<JavaAnnotatio
} }
is TreeBasedArrayAnnotationArgument -> { is TreeBasedArrayAnnotationArgument -> {
elementTypeArg.args.find { elementTypeArg.args.find {
val field = (it as? TreeBasedReferenceAnnotationArgument)?.resolve() as? SymbolBasedField (it as? TreeBasedReferenceAnnotationArgument)?.entryName?.asString() == "TYPE_USE"
field?.element?.simpleName?.contentEquals("TYPE_USE") ?: false
}?.let { filteredAnnotations.add(annotation) } }?.let { filteredAnnotations.add(annotation) }
} }
is TreeBasedReferenceAnnotationArgument -> { is TreeBasedReferenceAnnotationArgument -> {
(elementTypeArg.resolve() as? SymbolBasedField)?.let { field -> if (elementTypeArg.entryName?.asString() == "TYPE_USE") {
field.element.simpleName.takeIf { it.contentEquals("TYPE_USE") } filteredAnnotations.add(annotation)
?.let { filteredAnnotations.add(annotation) }
} }
} }
} }
} }
return filteredAnnotations return filteredAnnotations
} }
@@ -162,7 +162,7 @@ object JavaAnnotationTargetMapper {
internal fun mapJavaTargetArguments(arguments: List<JavaAnnotationArgument>, builtIns: KotlinBuiltIns): ConstantValue<*> { internal fun mapJavaTargetArguments(arguments: List<JavaAnnotationArgument>, builtIns: KotlinBuiltIns): ConstantValue<*> {
// Map arguments: java.lang.annotation.Target -> kotlin.annotation.Target // Map arguments: java.lang.annotation.Target -> kotlin.annotation.Target
val kotlinTargets = arguments.filterIsInstance<JavaEnumValueAnnotationArgument>() val kotlinTargets = arguments.filterIsInstance<JavaEnumValueAnnotationArgument>()
.flatMap { mapJavaTargetArgumentByName(it.resolve()?.name?.asString()) } .flatMap { mapJavaTargetArgumentByName(it.entryName?.asString()) }
.mapNotNull { builtIns.getAnnotationTargetEnumEntry(it) } .mapNotNull { builtIns.getAnnotationTargetEnumEntry(it) }
.map(::EnumValue) .map(::EnumValue)
val parameterDescriptor = DescriptorResolverUtils.getAnnotationParameterByName( val parameterDescriptor = DescriptorResolverUtils.getAnnotationParameterByName(
@@ -181,7 +181,7 @@ object JavaAnnotationTargetMapper {
internal fun mapJavaRetentionArgument(element: JavaAnnotationArgument?, builtIns: KotlinBuiltIns): ConstantValue<*>? { internal fun mapJavaRetentionArgument(element: JavaAnnotationArgument?, builtIns: KotlinBuiltIns): ConstantValue<*>? {
// Map argument: java.lang.annotation.Retention -> kotlin.annotation.annotation // Map argument: java.lang.annotation.Retention -> kotlin.annotation.annotation
return (element as? JavaEnumValueAnnotationArgument)?.let { return (element as? JavaEnumValueAnnotationArgument)?.let {
retentionNameList[it.resolve()?.name?.asString()]?.let { retentionNameList[it.entryName?.asString()]?.let {
builtIns.getAnnotationRetentionEnumEntry(it)?.let(::EnumValue) builtIns.getAnnotationRetentionEnumEntry(it)?.let(::EnumValue)
} }
} }
@@ -67,7 +67,7 @@ class LazyJavaAnnotationDescriptor(
private fun resolveAnnotationArgument(argument: JavaAnnotationArgument?): ConstantValue<*>? { private fun resolveAnnotationArgument(argument: JavaAnnotationArgument?): ConstantValue<*>? {
return when (argument) { return when (argument) {
is JavaLiteralAnnotationArgument -> ConstantValueFactory.createConstantValue(argument.value) 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 JavaArrayAnnotationArgument -> resolveFromArray(argument.name ?: DEFAULT_ANNOTATION_MEMBER_NAME, argument.getElements())
is JavaAnnotationAsAnnotationArgument -> resolveFromAnnotation(argument.getAnnotation()) is JavaAnnotationAsAnnotationArgument -> resolveFromAnnotation(argument.getAnnotation())
is JavaClassObjectAnnotationArgument -> resolveFromJavaClassObjectType(argument.getReferencedType()) is JavaClassObjectAnnotationArgument -> resolveFromJavaClassObjectType(argument.getReferencedType())
@@ -97,17 +97,19 @@ class LazyJavaAnnotationDescriptor(
return ConstantValueFactory.createArrayValue(values, arrayType) return ConstantValueFactory.createArrayValue(values, arrayType)
} }
private fun resolveFromEnumValue(element: JavaField?, entryName: Name?): ConstantValue<*>? { private fun resolveFromEnumValue(enumClassId: ClassId?, entryName: Name?): ConstantValue<*>? {
if (element == null || !element.isEnumEntry) { if (entryName == null) return null
if (entryName == null) return null
if (enumClassId == null) {
return ConstantValueFactory.createEnumValue(ErrorUtils.createErrorClassWithExactName(entryName)) 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(entryName, NoLookupLocation.FROM_JAVA_LOADER)
val classifier = enumClass.unsubstitutedInnerClassesScope.getContributedClassifier(element.name, NoLookupLocation.FROM_JAVA_LOADER)
as? ClassDescriptor ?: return null as? ClassDescriptor ?: return null
return ConstantValueFactory.createEnumValue(classifier) return ConstantValueFactory.createEnumValue(classifier)
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.load.java.structure package org.jetbrains.kotlin.load.java.structure
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
interface JavaAnnotationArgument { interface JavaAnnotationArgument {
@@ -31,8 +32,8 @@ interface JavaArrayAnnotationArgument : JavaAnnotationArgument {
} }
interface JavaEnumValueAnnotationArgument : JavaAnnotationArgument { interface JavaEnumValueAnnotationArgument : JavaAnnotationArgument {
val enumClassId: ClassId?
val entryName: Name? val entryName: Name?
fun resolve(): JavaField?
} }
interface JavaClassObjectAnnotationArgument : JavaAnnotationArgument { interface JavaClassObjectAnnotationArgument : JavaAnnotationArgument {
@@ -90,6 +90,9 @@ interface JavaClass : JavaClassifier, JavaTypeParameterListOwner, JavaModifierLi
val constructors: Collection<JavaConstructor> val constructors: Collection<JavaConstructor>
} }
val JavaClass.classId: ClassId?
get() = outerClass?.classId?.createNestedClassId(name) ?: fqName?.let(ClassId::topLevel)
enum class LightClassOriginKind { enum class LightClassOriginKind {
SOURCE, BINARY SOURCE, BINARY
} }
@@ -17,6 +17,7 @@
package kotlin.reflect.jvm.internal.structure package kotlin.reflect.jvm.internal.structure
import org.jetbrains.kotlin.load.java.structure.* import org.jetbrains.kotlin.load.java.structure.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
abstract class ReflectJavaAnnotationArgument( abstract class ReflectJavaAnnotationArgument(
@@ -51,11 +52,12 @@ class ReflectJavaEnumValueAnnotationArgument(
name: Name?, name: Name?,
private val value: Enum<*> private val value: Enum<*>
) : ReflectJavaAnnotationArgument(name), JavaEnumValueAnnotationArgument { ) : ReflectJavaAnnotationArgument(name), JavaEnumValueAnnotationArgument {
override fun resolve(): ReflectJavaField { override val enumClassId: ClassId?
val clazz = value::class.java get() {
val enumClass = if (clazz.isEnum) clazz else clazz.enclosingClass val clazz = value::class.java
return ReflectJavaField(enumClass.getDeclaredField(value.name)) val enumClass = if (clazz.isEnum) clazz else clazz.enclosingClass
} return enumClass.classId
}
override val entryName: Name? override val entryName: Name?
get() = Name.identifier(value.name) get() = Name.identifier(value.name)
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.idea.decompiler.textBuilder.LoggingErrorReporter
import org.jetbrains.kotlin.idea.decompiler.textBuilder.ResolveEverythingToKotlinAnyLocalClassifierResolver import org.jetbrains.kotlin.idea.decompiler.textBuilder.ResolveEverythingToKotlinAnyLocalClassifierResolver
import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.load.java.structure.JavaClass 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.load.kotlin.*
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
@@ -104,7 +105,7 @@ class DirectoryBasedClassFinder(
val packageDirectory: VirtualFile, val packageDirectory: VirtualFile,
val directoryPackageFqName: FqName val directoryPackageFqName: FqName
) : KotlinClassFinder { ) : KotlinClassFinder {
override fun findKotlinClass(javaClass: JavaClass) = findKotlinClass(javaClass.classId) override fun findKotlinClass(javaClass: JavaClass) = findKotlinClass(javaClass.classId!!)
override fun findKotlinClass(classId: ClassId): KotlinJvmBinaryClass? { override fun findKotlinClass(classId: ClassId): KotlinJvmBinaryClass? {
if (classId.packageFqName != directoryPackageFqName) { if (classId.packageFqName != directoryPackageFqName) {
@@ -149,10 +150,3 @@ class DirectoryBasedDataFinder(
return ClassDataWithSource(JvmProtoBufUtil.readClassDataFrom(data, strings), KotlinJvmBinarySourceElement(binaryClass)) 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)
}