Support @JvmField on interface properties

#KT-15807 Fixed
This commit is contained in:
Mikhael Bogdanov
2018-03-13 15:18:01 +01:00
parent 719c1882e0
commit 1d283d243e
36 changed files with 718 additions and 34 deletions
@@ -7,21 +7,26 @@ package org.jetbrains.kotlin.load.java;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.builtins.CompanionObjectMapping;
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget;
import org.jetbrains.kotlin.name.ClassId;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter;
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
import org.jetbrains.kotlin.util.capitalizeDecapitalize.CapitalizeDecapitalizeKt;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isClassOrEnumClass;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isCompanionObject;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
public final class JvmAbi {
public static final String DEFAULT_IMPLS_CLASS_NAME = "DefaultImpls";
public static final String ERASED_INLINE_CLASS_NAME = "Erased";
public static final FqName JVM_FIELD_ANNOTATION_FQ_NAME = new FqName("kotlin.jvm.JvmField");
/**
* Warning: use DEFAULT_IMPLS_CLASS_NAME and TypeMappingConfiguration.innerClassNameFactory when possible.
@@ -97,16 +102,54 @@ public final class JvmAbi {
public static boolean isPropertyWithBackingFieldInOuterClass(@NotNull PropertyDescriptor propertyDescriptor) {
return propertyDescriptor.getKind() != CallableMemberDescriptor.Kind.FAKE_OVERRIDE &&
isCompanionObjectWithBackingFieldsInOuter(propertyDescriptor.getContainingDeclaration());
(isCompanionObjectWithBackingFieldsInOuter(propertyDescriptor.getContainingDeclaration())
||
(isCompanionObject(propertyDescriptor.getContainingDeclaration()) &&
hasJvmFieldAnnotation(propertyDescriptor)));
}
public static boolean isCompanionObjectWithBackingFieldsInOuter(@NotNull DeclarationDescriptor companionObject) {
return isCompanionObject(companionObject) &&
isClassOrEnumClass(companionObject.getContainingDeclaration()) &&
(isClassOrEnumClass(companionObject.getContainingDeclaration()) ||
isInterfaceCompanionWithBackingFieldsInOuter(companionObject)) &&
!isMappedIntrinsicCompanionObject((ClassDescriptor) companionObject);
}
public static boolean isInterfaceCompanionWithBackingFieldsInOuter(@NotNull DeclarationDescriptor companionObject) {
DeclarationDescriptor interfaceClass = companionObject.getContainingDeclaration();
if (!isInterface(interfaceClass) && !isAnnotationClass(interfaceClass)) return false;
Collection<DeclarationDescriptor> descriptors = ((ClassDescriptor) companionObject).getUnsubstitutedMemberScope()
.getContributedDescriptors(DescriptorKindFilter.ALL, MemberScope.Companion.getALL_NAME_FILTER());
boolean hasJvmFieldAnnotation = false;
for (DeclarationDescriptor next : descriptors) {
if (!(next instanceof PropertyDescriptor)) continue;
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) next;
if (propertyDescriptor.getVisibility() != Visibilities.PUBLIC ||
propertyDescriptor.isVar() ||
propertyDescriptor.getModality() != Modality.FINAL) {
return false;
}
if (!hasJvmFieldAnnotation(propertyDescriptor)) return false;
hasJvmFieldAnnotation = true;
}
return hasJvmFieldAnnotation;
}
public static boolean isMappedIntrinsicCompanionObject(@NotNull ClassDescriptor companionObject) {
return CompanionObjectMapping.INSTANCE.isMappedIntrinsicCompanionObject(companionObject);
}
private static boolean hasJvmFieldAnnotation(@NotNull CallableMemberDescriptor memberDescriptor) {
List<AnnotationWithTarget> annotations = memberDescriptor.getAnnotations().getUseSiteTargetedAnnotations();
for (AnnotationWithTarget annotationWithTarget : annotations) {
if (AnnotationUseSiteTarget.FIELD.equals(annotationWithTarget.getTarget()) &&
JVM_FIELD_ANNOTATION_FQ_NAME.equals(annotationWithTarget.getAnnotation().getFqName())) {
return true;
}
}
return memberDescriptor.getAnnotations().hasAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME);
}
}
@@ -99,13 +99,26 @@ abstract class AbstractBinaryClassAnnotationAndConstantLoader<A : Any, C : Any,
val fieldSignature = getPropertySignature(proto, container.nameResolver, container.typeTable, field = true)
val isConst = Flags.IS_CONST.get(proto.flags)
val isMovedFromInterfaceCompanion = JvmProtoBufUtil.isMovedFromInterfaceCompanion(proto)
val propertyAnnotations = syntheticFunctionSignature?.let { sig ->
findClassAndLoadMemberAnnotations(container, sig, property = true, isConst = isConst)
findClassAndLoadMemberAnnotations(
container,
sig,
property = true,
isConst = isConst,
isMovedFromInterfaceCompanion = isMovedFromInterfaceCompanion
)
}.orEmpty()
val fieldAnnotations = fieldSignature?.let { sig ->
findClassAndLoadMemberAnnotations(container, sig, property = true, field = true, isConst = isConst)
findClassAndLoadMemberAnnotations(
container,
sig,
property = true,
field = true,
isConst = isConst,
isMovedFromInterfaceCompanion = isMovedFromInterfaceCompanion
)
}.orEmpty()
// TODO: check delegate presence in some other way
@@ -136,11 +149,24 @@ abstract class AbstractBinaryClassAnnotationAndConstantLoader<A : Any, C : Any,
protected abstract fun transformAnnotations(annotations: List<A>): List<T>
private fun findClassAndLoadMemberAnnotations(
container: ProtoContainer, signature: MemberSignature,
property: Boolean = false, field: Boolean = false, isConst: Boolean? = null
container: ProtoContainer,
signature: MemberSignature,
property: Boolean = false,
field: Boolean = false,
isConst: Boolean? = null,
isMovedFromInterfaceCompanion: Boolean = false
): List<A> {
val kotlinClass =
findClassWithAnnotationsAndInitializers(container, getSpecialCaseContainerClass(container, property, field, isConst))
findClassWithAnnotationsAndInitializers(
container,
getSpecialCaseContainerClass(
container,
property,
field,
isConst,
isMovedFromInterfaceCompanion
)
)
?: return listOf()
return storage(kotlinClass).memberAnnotations[signature] ?: listOf()
@@ -202,7 +228,13 @@ abstract class AbstractBinaryClassAnnotationAndConstantLoader<A : Any, C : Any,
val signature = getCallableSignature(proto, container.nameResolver, container.typeTable, AnnotatedCallableKind.PROPERTY)
?: return null
val specialCase = getSpecialCaseContainerClass(container, property = true, field = true, isConst = Flags.IS_CONST.get(proto.flags))
val specialCase = getSpecialCaseContainerClass(
container,
property = true,
field = true,
isConst = Flags.IS_CONST.get(proto.flags),
isMovedFromInterfaceCompanion = JvmProtoBufUtil.isMovedFromInterfaceCompanion(proto)
)
val kotlinClass = findClassWithAnnotationsAndInitializers(container, specialCase) ?: return null
val constant = storage(kotlinClass).propertyConstants[signature] ?: return null
@@ -222,7 +254,11 @@ abstract class AbstractBinaryClassAnnotationAndConstantLoader<A : Any, C : Any,
// TODO: do not use KotlinClassFinder#findKotlinClass here because it traverses the file system in the compiler
// Introduce an API in KotlinJvmBinaryClass to find a class nearby instead
private fun getSpecialCaseContainerClass(
container: ProtoContainer, property: Boolean, field: Boolean, isConst: Boolean?
container: ProtoContainer,
property: Boolean,
field: Boolean,
isConst: Boolean?,
isMovedFromInterfaceCompanion: Boolean
): KotlinJvmBinaryClass? {
if (property) {
checkNotNull(isConst) { "isConst should not be null for property (container=$container)" }
@@ -242,7 +278,12 @@ abstract class AbstractBinaryClassAnnotationAndConstantLoader<A : Any, C : Any,
}
if (field && container is ProtoContainer.Class && container.kind == ProtoBuf.Class.Kind.COMPANION_OBJECT) {
val outerClass = container.outerClass
if (outerClass != null && (outerClass.kind == ProtoBuf.Class.Kind.CLASS || outerClass.kind == ProtoBuf.Class.Kind.ENUM_CLASS)) {
if (outerClass != null &&
(outerClass.kind == ProtoBuf.Class.Kind.CLASS || outerClass.kind == ProtoBuf.Class.Kind.ENUM_CLASS ||
(isMovedFromInterfaceCompanion &&
(outerClass.kind == ProtoBuf.Class.Kind.INTERFACE ||
outerClass.kind == ProtoBuf.Class.Kind.ANNOTATION_CLASS)))
) {
// Backing fields of properties of a companion object in a class are generated in the outer class
return outerClass.toBinaryClass()
}
@@ -119,4 +119,7 @@ object JvmProtoBufUtil {
private fun mapTypeDefault(type: ProtoBuf.Type, nameResolver: NameResolver): String? {
return if (type.hasClassName()) ClassMapperLite.mapClass(nameResolver.getQualifiedClassName(type.className)) else null
}
fun isMovedFromInterfaceCompanion(proto: ProtoBuf.Property) =
proto.getExtension(JvmProtoBuf.isMovedFromInterfaceCompanion).toInt().and(1) != 0
}
@@ -19,6 +19,8 @@ package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.metadata.deserialization.getExtensionOrNull
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.resolve.DescriptorFactory
import org.jetbrains.kotlin.resolve.DescriptorUtils
@@ -59,7 +61,9 @@ internal abstract class KPropertyImpl<out R> private constructor(
is KotlinProperty -> {
val descriptor = jvmSignature.descriptor
JvmProtoBufUtil.getJvmFieldSignature(jvmSignature.proto, jvmSignature.nameResolver, jvmSignature.typeTable)?.let {
val owner = if (JvmAbi.isCompanionObjectWithBackingFieldsInOuter(descriptor.containingDeclaration)) {
val owner = if (JvmAbi.isPropertyWithBackingFieldInOuterClass(descriptor) ||
JvmProtoBufUtil.isMovedFromInterfaceCompanion(jvmSignature.proto)
) {
container.jClass.enclosingClass
} else descriptor.containingDeclaration.let { containingDeclaration ->
if (containingDeclaration is ClassDescriptor) containingDeclaration.toJavaClass()
@@ -188,6 +192,11 @@ private fun KPropertyImpl.Accessor<*, *>.computeCallerForAccessor(isGetter: Bool
!DescriptorUtils.isInterface(possibleCompanionObject.containingDeclaration)
}
fun isInsideInterfaceCompanionObjectWithJvmField(): Boolean {
val possibleCompanionObject = property.descriptor.containingDeclaration
return JvmAbi.isInterfaceCompanionWithBackingFieldsInOuter(possibleCompanionObject)
}
fun isJvmStaticProperty() =
property.descriptor.annotations.findAnnotation(JVM_STATIC) != null
@@ -195,7 +204,7 @@ private fun KPropertyImpl.Accessor<*, *>.computeCallerForAccessor(isGetter: Bool
!TypeUtils.isNullableType(property.descriptor.type)
fun computeFieldCaller(field: Field): FunctionCaller<Field> = when {
isInsideClassCompanionObject() -> {
isInsideClassCompanionObject() || isInsideInterfaceCompanionObjectWithJvmField() -> {
val klass = (descriptor.containingDeclaration as ClassDescriptor).toJavaClass()!!
if (isGetter)
if (isBound) FunctionCaller.BoundClassCompanionFieldGetter(field, klass)