Simplify implementation of JvmField property in interface companion

Move parts of the logic to the only places where they're needed:
checking for public/final/val is only needed in
JvmFieldApplicabilityChecker, checking the proto flag is only needed in
reflection, checking the JvmField annotation presence is only needed in
backend
This commit is contained in:
Alexander Udalov
2018-07-16 19:24:17 +02:00
parent 79c2aa4acf
commit 1e675bdc01
7 changed files with 64 additions and 70 deletions
@@ -47,6 +47,8 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmClassSignature;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter;
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver; import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver;
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver; import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver;
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
@@ -928,9 +930,13 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
boolean isNonCompanionObject = isNonCompanionObject(descriptor); boolean isNonCompanionObject = isNonCompanionObject(descriptor);
boolean isInterfaceCompanion = isCompanionObjectInInterfaceNotIntrinsic(descriptor); boolean isInterfaceCompanion = isCompanionObjectInInterfaceNotIntrinsic(descriptor);
boolean isInterfaceCompanionWithBackingFieldsInOuter = isInterfaceCompanionWithBackingFieldsInOuter(descriptor);
boolean isMappedIntrinsicCompanionObject = isMappedIntrinsicCompanionObject(descriptor); boolean isMappedIntrinsicCompanionObject = isMappedIntrinsicCompanionObject(descriptor);
boolean objectWithBackingFieldsInOuter = isCompanionObjectWithBackingFieldsInOuter(descriptor); boolean isClassCompanionWithBackingFieldsInOuter = isClassCompanionObjectWithBackingFieldsInOuter(descriptor);
if (isNonCompanionObject || (isInterfaceCompanion && !objectWithBackingFieldsInOuter) || isMappedIntrinsicCompanionObject) { if (isNonCompanionObject ||
(isInterfaceCompanion && !isInterfaceCompanionWithBackingFieldsInOuter) ||
isMappedIntrinsicCompanionObject
) {
ExpressionCodegen clInitCodegen = createOrGetClInitCodegen(); ExpressionCodegen clInitCodegen = createOrGetClInitCodegen();
InstructionAdapter v = clInitCodegen.v; InstructionAdapter v = clInitCodegen.v;
markLineNumberForElement(element.getPsiOrParent(), v); markLineNumberForElement(element.getPsiOrParent(), v);
@@ -958,7 +964,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
); );
} }
} }
else if (objectWithBackingFieldsInOuter) { else if (isClassCompanionWithBackingFieldsInOuter || isInterfaceCompanionWithBackingFieldsInOuter) {
ImplementationBodyCodegen parentCodegen = (ImplementationBodyCodegen) getParentCodegen(); ImplementationBodyCodegen parentCodegen = (ImplementationBodyCodegen) getParentCodegen();
ExpressionCodegen parentClInitCodegen = parentCodegen.createOrGetClInitCodegen(); ExpressionCodegen parentClInitCodegen = parentCodegen.createOrGetClInitCodegen();
InstructionAdapter parentVisitor = parentClInitCodegen.v; InstructionAdapter parentVisitor = parentClInitCodegen.v;
@@ -977,6 +983,15 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
} }
} }
private static boolean isInterfaceCompanionWithBackingFieldsInOuter(@NotNull DeclarationDescriptor declarationDescriptor) {
DeclarationDescriptor interfaceClass = declarationDescriptor.getContainingDeclaration();
if (!isCompanionObject(declarationDescriptor) || !isJvmInterface(interfaceClass)) return false;
Collection<DeclarationDescriptor> descriptors = ((ClassDescriptor) declarationDescriptor).getUnsubstitutedMemberScope()
.getContributedDescriptors(DescriptorKindFilter.ALL, MemberScope.Companion.getALL_NAME_FILTER());
return CollectionsKt.any(descriptors, d -> d instanceof PropertyDescriptor && hasJvmFieldAnnotation((PropertyDescriptor) d));
}
private void generateCompanionObjectBackingFieldCopies() { private void generateCompanionObjectBackingFieldCopies() {
if (companionObjectPropertiesToCopy == null) return; if (companionObjectPropertiesToCopy == null) return;
@@ -57,9 +57,9 @@ import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isConstOrHasJvmFieldAn
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isJvmInterface; import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isJvmInterface;
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.DELEGATED_PROPERTIES; import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.DELEGATED_PROPERTIES;
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.DELEGATED_PROPERTY_METADATA_OWNER; import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.DELEGATED_PROPERTY_METADATA_OWNER;
import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.*; import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.FIELD_FOR_PROPERTY;
import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.SYNTHETIC_METHOD_FOR_PROPERTY;
import static org.jetbrains.kotlin.diagnostics.Errors.EXPECTED_FUNCTION_SOURCE_WITH_DEFAULT_ARGUMENTS_NOT_FOUND; import static org.jetbrains.kotlin.diagnostics.Errors.EXPECTED_FUNCTION_SOURCE_WITH_DEFAULT_ARGUMENTS_NOT_FOUND;
import static org.jetbrains.kotlin.load.java.JvmAbi.isInterfaceCompanionWithBackingFieldsInOuter;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isCompanionObject; import static org.jetbrains.kotlin.resolve.DescriptorUtils.isCompanionObject;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isInterface; import static org.jetbrains.kotlin.resolve.DescriptorUtils.isInterface;
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.K_PROPERTY_TYPE; import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.K_PROPERTY_TYPE;
@@ -420,10 +420,6 @@ public class PropertyCodegen {
String name = backingFieldContext.getFieldName(propertyDescriptor, isDelegate); String name = backingFieldContext.getFieldName(propertyDescriptor, isDelegate);
v.getSerializationBindings().put(FIELD_FOR_PROPERTY, propertyDescriptor, Pair.create(type, name)); v.getSerializationBindings().put(FIELD_FOR_PROPERTY, propertyDescriptor, Pair.create(type, name));
if (hasJvmFieldAnnotation(propertyDescriptor) &&
isInterfaceCompanionWithBackingFieldsInOuter(propertyDescriptor.getContainingDeclaration())) {
v.getSerializationBindings().put(FIELD_MOVED_FROM_INTERFACE_COMPANION, propertyDescriptor, true);
}
FieldVisitor fv = builder.newField( FieldVisitor fv = builder.newField(
JvmDeclarationOriginKt.OtherOrigin(element, propertyDescriptor), modifiers, name, type.getDescriptor(), JvmDeclarationOriginKt.OtherOrigin(element, propertyDescriptor), modifiers, name, type.getDescriptor(),
@@ -140,7 +140,7 @@ fun populateCompanionBackingFieldNamesToOuterContextIfNeeded(
return return
} }
if (!JvmAbi.isCompanionObjectWithBackingFieldsInOuter(descriptor)) { if (!JvmAbi.isClassCompanionObjectWithBackingFieldsInOuter(descriptor)) {
return return
} }
val properties = companion.declarations.filterIsInstance<KtProperty>() val properties = companion.declarations.filterIsInstance<KtProperty>()
@@ -35,8 +35,6 @@ public final class JvmSerializationBindings {
SerializationMappingSlice.create(); SerializationMappingSlice.create();
public static final SerializationMappingSlice<PropertyDescriptor, Method> SYNTHETIC_METHOD_FOR_PROPERTY = public static final SerializationMappingSlice<PropertyDescriptor, Method> SYNTHETIC_METHOD_FOR_PROPERTY =
SerializationMappingSlice.create(); SerializationMappingSlice.create();
public static final SerializationMappingSlice<PropertyDescriptor, Boolean> FIELD_MOVED_FROM_INTERFACE_COMPANION =
SerializationMappingSlice.create();
static final class SerializationMappingSlice<K, V> extends BasicWritableSlice<K, V> { static final class SerializationMappingSlice<K, V> extends BasicWritableSlice<K, V> {
public SerializationMappingSlice() { public SerializationMappingSlice() {
@@ -181,18 +181,26 @@ class JvmSerializerExtension(private val bindings: JvmSerializationBindings, sta
) )
proto.setExtension(JvmProtoBuf.propertySignature, signature) proto.setExtension(JvmProtoBuf.propertySignature, signature)
val flags = JvmFlags.getPropertyFlags(bindings.get(FIELD_MOVED_FROM_INTERFACE_COMPANION, descriptor) == true)
if (flags != 0) {
proto.setExtension(JvmProtoBuf.flags, flags)
}
if (JvmAbi.isInterfaceCompanionWithBackingFieldsInOuter(descriptor.containingDeclaration)) { if (descriptor.isJvmFieldPropertyInInterfaceCompanion()) {
proto.setExtension(JvmProtoBuf.flags, JvmFlags.getPropertyFlags(true))
assert(!proto.hasVersionRequirement()) { "VersionRequirement should be empty for $descriptor" } assert(!proto.hasVersionRequirement()) { "VersionRequirement should be empty for $descriptor" }
proto.versionRequirement = proto.versionRequirement =
writeVersionRequirement(1, 2, 70, ProtoBuf.VersionRequirement.VersionKind.COMPILER_VERSION, versionRequirementTable) writeVersionRequirement(1, 2, 70, ProtoBuf.VersionRequirement.VersionKind.COMPILER_VERSION, versionRequirementTable)
} }
} }
private fun PropertyDescriptor.isJvmFieldPropertyInInterfaceCompanion(): Boolean {
if (!JvmAbi.hasJvmFieldAnnotation(this)) return false
val container = containingDeclaration
if (!DescriptorUtils.isCompanionObject(container)) return false
val grandParent = (container as ClassDescriptor).containingDeclaration
return DescriptorUtils.isInterface(grandParent) || DescriptorUtils.isAnnotationClass(grandParent)
}
override fun serializeErrorType(type: KotlinType, builder: ProtoBuf.Type.Builder) { override fun serializeErrorType(type: KotlinType, builder: ProtoBuf.Type.Builder) {
if (classBuilderMode === ClassBuilderMode.KAPT3) { if (classBuilderMode === ClassBuilderMode.KAPT3) {
builder.className = stringTable.getStringIndex(NON_EXISTENT_CLASS_NAME) builder.className = stringTable.getStringIndex(NON_EXISTENT_CLASS_NAME)
@@ -31,6 +31,8 @@ import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
import org.jetbrains.kotlin.resolve.jvm.annotations.findJvmFieldAnnotation import org.jetbrains.kotlin.resolve.jvm.annotations.findJvmFieldAnnotation
import org.jetbrains.kotlin.resolve.jvm.checkers.JvmFieldApplicabilityChecker.Problem.* import org.jetbrains.kotlin.resolve.jvm.checkers.JvmFieldApplicabilityChecker.Problem.*
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.MemberScope
class JvmFieldApplicabilityChecker : DeclarationChecker { class JvmFieldApplicabilityChecker : DeclarationChecker {
@@ -64,7 +66,7 @@ class JvmFieldApplicabilityChecker : DeclarationChecker {
if (!context.languageVersionSettings.supportsFeature(LanguageFeature.JvmFieldInInterface)) if (!context.languageVersionSettings.supportsFeature(LanguageFeature.JvmFieldInInterface))
INSIDE_COMPANION_OF_INTERFACE INSIDE_COMPANION_OF_INTERFACE
else { else {
if (!JvmAbi.isInterfaceCompanionWithBackingFieldsInOuter(descriptor.containingDeclaration)) { if (!isInterfaceCompanionWithPublicJvmFieldProperties(descriptor.containingDeclaration as ClassDescriptor)) {
NOT_PUBLIC_VAL_WITH_JVMFIELD NOT_PUBLIC_VAL_WITH_JVMFIELD
} else return } else return
} }
@@ -77,6 +79,19 @@ class JvmFieldApplicabilityChecker : DeclarationChecker {
context.trace.report(ErrorsJvm.INAPPLICABLE_JVM_FIELD.on(annotationEntry, problem.errorMessage)) context.trace.report(ErrorsJvm.INAPPLICABLE_JVM_FIELD.on(annotationEntry, problem.errorMessage))
} }
private fun isInterfaceCompanionWithPublicJvmFieldProperties(companionObject: ClassDescriptor): Boolean {
for (next in companionObject.unsubstitutedMemberScope.getContributedDescriptors(
DescriptorKindFilter.VARIABLES, MemberScope.ALL_NAME_FILTER
)) {
if (next !is PropertyDescriptor) continue
if (next.visibility != Visibilities.PUBLIC || next.isVar || next.modality != Modality.FINAL) return false
if (!JvmAbi.hasJvmFieldAnnotation(next)) return false
}
return true
}
private fun PropertyDescriptor.hasCustomAccessor() = !(getter?.isDefault ?: true) || !(setter?.isDefault ?: true) private fun PropertyDescriptor.hasCustomAccessor() = !(getter?.isDefault ?: true) || !(setter?.isDefault ?: true)
private fun PropertyDescriptor.hasBackingField(bindingContext: BindingContext) = private fun PropertyDescriptor.hasBackingField(bindingContext: BindingContext) =
@@ -89,5 +104,4 @@ class JvmFieldApplicabilityChecker : DeclarationChecker {
val outerClassKind = (containingClass.containingDeclaration as? ClassDescriptor)?.kind val outerClassKind = (containingClass.containingDeclaration as? ClassDescriptor)?.kind
return outerClassKind == ClassKind.INTERFACE || outerClassKind == ClassKind.ANNOTATION_CLASS return outerClassKind == ClassKind.INTERFACE || outerClassKind == ClassKind.ANNOTATION_CLASS
} }
} }
@@ -7,23 +7,21 @@ package org.jetbrains.kotlin.load.java;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.builtins.CompanionObjectMapping; import org.jetbrains.kotlin.builtins.CompanionObjectMapping;
import org.jetbrains.kotlin.descriptors.*; 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.annotations.AnnotationUseSiteTarget; import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget; import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget;
import org.jetbrains.kotlin.metadata.ProtoBuf;
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil;
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.kotlin.resolve.scopes.DescriptorKindFilter;
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor;
import org.jetbrains.kotlin.util.capitalizeDecapitalize.CapitalizeDecapitalizeKt; import org.jetbrains.kotlin.util.capitalizeDecapitalize.CapitalizeDecapitalizeKt;
import java.util.Collection;
import java.util.List; import java.util.List;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.*; import static org.jetbrains.kotlin.resolve.DescriptorUtils.isClassOrEnumClass;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isCompanionObject;
public final class JvmAbi { public final class JvmAbi {
public static final String DEFAULT_IMPLS_CLASS_NAME = "DefaultImpls"; public static final String DEFAULT_IMPLS_CLASS_NAME = "DefaultImpls";
@@ -103,59 +101,24 @@ public final class JvmAbi {
} }
public static boolean isPropertyWithBackingFieldInOuterClass(@NotNull PropertyDescriptor propertyDescriptor) { public static boolean isPropertyWithBackingFieldInOuterClass(@NotNull PropertyDescriptor propertyDescriptor) {
return propertyDescriptor.getKind() != CallableMemberDescriptor.Kind.FAKE_OVERRIDE && if (propertyDescriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) return false;
(isCompanionObjectWithBackingFieldsInOuter(propertyDescriptor.getContainingDeclaration())
|| if (isClassCompanionObjectWithBackingFieldsInOuter(propertyDescriptor.getContainingDeclaration())) return true;
(isCompanionObject(propertyDescriptor.getContainingDeclaration()) &&
hasJvmFieldAnnotation(propertyDescriptor))); return isCompanionObject(propertyDescriptor.getContainingDeclaration()) && hasJvmFieldAnnotation(propertyDescriptor);
} }
public static boolean isCompanionObjectWithBackingFieldsInOuter(@NotNull DeclarationDescriptor companionObject) { public static boolean isClassCompanionObjectWithBackingFieldsInOuter(@NotNull DeclarationDescriptor companionObject) {
return isCompanionObject(companionObject) && return isCompanionObject(companionObject) &&
(isClassOrEnumClass(companionObject.getContainingDeclaration()) || isClassOrEnumClass(companionObject.getContainingDeclaration()) &&
isInterfaceCompanionWithBackingFieldsInOuter(companionObject)) &&
!isMappedIntrinsicCompanionObject((ClassDescriptor) companionObject); !isMappedIntrinsicCompanionObject((ClassDescriptor) companionObject);
}
public static boolean isInterfaceCompanionWithBackingFieldsInOuter(@NotNull DeclarationDescriptor declarationDescriptor) {
DeclarationDescriptor interfaceClass = declarationDescriptor.getContainingDeclaration();
if (!isCompanionObject(declarationDescriptor) ||
(!isInterface(interfaceClass) && !isAnnotationClass(interfaceClass))) {
return false;
}
Collection<DeclarationDescriptor> descriptors = ((ClassDescriptor) declarationDescriptor).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 (!isMovedFromInterfaceCompanion(propertyDescriptor) && !hasJvmFieldAnnotation(propertyDescriptor)) return false;
hasJvmFieldAnnotation = true;
}
return hasJvmFieldAnnotation;
}
private static boolean isMovedFromInterfaceCompanion(@NotNull PropertyDescriptor propertyDescriptor) {
if (propertyDescriptor instanceof DeserializedPropertyDescriptor) {
ProtoBuf.Property proto = ((DeserializedPropertyDescriptor) propertyDescriptor).getProto();
return JvmProtoBufUtil.isMovedFromInterfaceCompanion(proto);
}
return false;
} }
public static boolean isMappedIntrinsicCompanionObject(@NotNull ClassDescriptor companionObject) { public static boolean isMappedIntrinsicCompanionObject(@NotNull ClassDescriptor companionObject) {
return CompanionObjectMapping.INSTANCE.isMappedIntrinsicCompanionObject(companionObject); return CompanionObjectMapping.INSTANCE.isMappedIntrinsicCompanionObject(companionObject);
} }
private static boolean hasJvmFieldAnnotation(@NotNull CallableMemberDescriptor memberDescriptor) { public static boolean hasJvmFieldAnnotation(@NotNull CallableMemberDescriptor memberDescriptor) {
List<AnnotationWithTarget> annotations = memberDescriptor.getAnnotations().getUseSiteTargetedAnnotations(); List<AnnotationWithTarget> annotations = memberDescriptor.getAnnotations().getUseSiteTargetedAnnotations();
for (AnnotationWithTarget annotationWithTarget : annotations) { for (AnnotationWithTarget annotationWithTarget : annotations) {
if (AnnotationUseSiteTarget.FIELD.equals(annotationWithTarget.getTarget()) && if (AnnotationUseSiteTarget.FIELD.equals(annotationWithTarget.getTarget()) &&