Load annotations of const properties from multifile classes

Rework backing field generation logic in PropertyCodegen to switch the
ClassBuilder instance for a multifile part to that of the corresponding facade
class. This became needed because multifile parts, and their metadata, are
generated _before_ the multifile facade class and otherwise we would never
record that there's a synthetic '$annotations' method for a const val and would
not write that to the protobuf message for the property.

See also bad83200

 #KT-10892 Fixed
This commit is contained in:
Alexander Udalov
2016-03-23 18:55:40 +03:00
parent 6924d883eb
commit a8bebeb48d
14 changed files with 150 additions and 61 deletions
@@ -73,7 +73,7 @@ class MultifileClassCodegen(
private fun getDeserializedCallables(compiledPackageFragment: PackageFragmentDescriptor) =
compiledPackageFragment.getMemberScope().getContributedDescriptors(DescriptorKindFilter.CALLABLES, MemberScope.ALL_NAME_FILTER).filterIsInstance<DeserializedCallableMemberDescriptor>()
private val classBuilder = ClassBuilderOnDemand {
val classBuilder = ClassBuilderOnDemand {
val originFile = files.firstOrNull()
val actualPackageFragment = packageFragment ?:
compiledPackageFragment ?:
@@ -163,7 +163,7 @@ class MultifileClassCodegen(
var generatePart = false
val partClassInfo = state.fileClassesProvider.getFileClassInfo(file)
val partType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(partClassInfo.fileClassFqName)
val partContext = state.rootContext.intoMultifileClassPart(packageFragment, facadeClassType, partType, file)
val partContext = state.rootContext.intoMultifileClassPart(this, packageFragment, facadeClassType, partType, file)
for (declaration in file.declarations) {
when (declaration) {
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.codegen;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
@@ -69,9 +68,6 @@ import static org.jetbrains.kotlin.resolve.jvm.annotations.AnnotationUtilKt.hasJ
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
public class PropertyCodegen {
private static Logger LOG = Logger.getInstance(PropertyCodegen.class);
private final GenerationState state;
private final ClassBuilder v;
private final FunctionCodegen functionCodegen;
@@ -123,7 +119,7 @@ public class PropertyCodegen {
assert kind == OwnerKind.PACKAGE || kind == OwnerKind.IMPLEMENTATION || kind == OwnerKind.DEFAULT_IMPLS
: "Generating property with a wrong kind (" + kind + "): " + descriptor;
if (isBackingFieldOwner(descriptor)) {
if (CodegenContextUtil.isImplClassOwner(context)) {
assert declaration != null : "Declaration is null for different context: " + context;
genBackingFieldAndAnnotations(declaration, descriptor, false);
@@ -137,14 +133,10 @@ public class PropertyCodegen {
}
}
private boolean isBackingFieldOwner(@NotNull PropertyDescriptor descriptor) {
if (descriptor.isConst()) {
return !(context instanceof MultifileClassPartContext);
}
return CodegenContextUtil.isImplClassOwner(context);
}
private void genBackingFieldAndAnnotations(@NotNull KtNamedDeclaration declaration, @NotNull PropertyDescriptor descriptor, boolean isParameter) {
ClassBuilder builder = getCorrectClassBuilder(descriptor);
if (builder == null) return;
boolean hasBackingField = hasBackingField(declaration, descriptor);
boolean hasDelegate = declaration instanceof KtProperty && ((KtProperty) declaration).hasDelegate();
@@ -157,8 +149,17 @@ public class PropertyCodegen {
Annotations delegateAnnotations = annotationSplitter.getAnnotationsForTarget(AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD);
Annotations propertyAnnotations = annotationSplitter.getAnnotationsForTarget(AnnotationUseSiteTarget.PROPERTY);
generateBackingField(declaration, descriptor, fieldAnnotations, delegateAnnotations);
generateSyntheticMethodIfNeeded(descriptor, propertyAnnotations);
generateBackingField(builder, declaration, descriptor, fieldAnnotations, delegateAnnotations);
generateSyntheticMethodIfNeeded(builder, descriptor, propertyAnnotations);
}
@Nullable
private ClassBuilder getCorrectClassBuilder(@NotNull PropertyDescriptor descriptor) {
if (descriptor.isConst() && context instanceof MultifileClassPartContext) {
return ((MultifileClassPartContext) context).getMultifileClassCodegen().getClassBuilder();
}
return this.v;
}
/**
@@ -244,6 +245,7 @@ public class PropertyCodegen {
}
private boolean generateBackingField(
@NotNull ClassBuilder builder,
@NotNull KtNamedDeclaration p,
@NotNull PropertyDescriptor descriptor,
@NotNull Annotations backingFieldAnnotations,
@@ -254,10 +256,10 @@ public class PropertyCodegen {
}
if (p instanceof KtProperty && ((KtProperty) p).hasDelegate()) {
generatePropertyDelegateAccess((KtProperty) p, descriptor, delegateAnnotations);
generatePropertyDelegateAccess(builder, (KtProperty) p, descriptor, delegateAnnotations);
}
else if (Boolean.TRUE.equals(bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, descriptor))) {
generateBackingFieldAccess(p, descriptor, backingFieldAnnotations);
generateBackingFieldAccess(builder, p, descriptor, backingFieldAnnotations);
}
else {
return false;
@@ -267,16 +269,19 @@ public class PropertyCodegen {
// Annotations on properties are stored in bytecode on an empty synthetic method. This way they're still
// accessible via reflection, and 'deprecated' and 'private' flags prevent this method from being called accidentally
private void generateSyntheticMethodIfNeeded(@NotNull PropertyDescriptor descriptor, Annotations annotations) {
private void generateSyntheticMethodIfNeeded(
@NotNull ClassBuilder builder,
@NotNull PropertyDescriptor descriptor,
@NotNull Annotations annotations
) {
if (annotations.getAllAnnotations().isEmpty()) return;
ReceiverParameterDescriptor receiver = descriptor.getExtensionReceiverParameter();
String name = JvmAbi.getSyntheticMethodNameForAnnotatedProperty(descriptor.getName());
String desc = receiver == null ? "()V" : "(" + typeMapper.mapType(receiver.getType()) + ")V";
Method syntheticMethod = getSyntheticMethodSignature(descriptor);
if (!isInterface(context.getContextDescriptor()) || kind == OwnerKind.DEFAULT_IMPLS) {
int flags = ACC_DEPRECATED | ACC_FINAL | ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC;
MethodVisitor mv = v.newMethod(JvmDeclarationOriginKt.OtherOrigin(descriptor), flags, name, desc, null, null);
MethodVisitor mv = builder.newMethod(JvmDeclarationOriginKt.OtherOrigin(descriptor), flags, syntheticMethod.getName(),
syntheticMethod.getDescriptor(), null, null);
AnnotationCodegen.forMethod(mv, typeMapper)
.genAnnotations(new AnnotatedSimple(annotations), Type.VOID_TYPE, AnnotationUseSiteTarget.PROPERTY);
mv.visitCode();
@@ -285,15 +290,24 @@ public class PropertyCodegen {
}
if (kind != OwnerKind.DEFAULT_IMPLS) {
v.getSerializationBindings().put(SYNTHETIC_METHOD_FOR_PROPERTY, descriptor, new Method(name, desc));
v.getSerializationBindings().put(SYNTHETIC_METHOD_FOR_PROPERTY, descriptor, syntheticMethod);
}
}
@NotNull
private Method getSyntheticMethodSignature(@NotNull PropertyDescriptor descriptor) {
ReceiverParameterDescriptor receiver = descriptor.getExtensionReceiverParameter();
String name = JvmAbi.getSyntheticMethodNameForAnnotatedProperty(descriptor.getName());
String desc = receiver == null ? "()V" : "(" + typeMapper.mapType(receiver.getType()) + ")V";
return new Method(name, desc);
}
private void generateBackingField(
ClassBuilder builder,
KtNamedDeclaration element,
PropertyDescriptor propertyDescriptor,
boolean isDelegate,
KotlinType jetType,
KotlinType kotlinType,
Object defaultValue,
Annotations annotations
) {
@@ -317,9 +331,7 @@ public class PropertyCodegen {
modifiers |= ACC_SYNTHETIC;
}
Type type = typeMapper.mapType(jetType);
ClassBuilder builder = v;
Type type = typeMapper.mapType(kotlinType);
FieldOwnerContext backingFieldContext = context;
if (AsmUtil.isInstancePropertyWithStaticBackingField(propertyDescriptor) ) {
@@ -342,15 +354,22 @@ public class PropertyCodegen {
v.getSerializationBindings().put(FIELD_FOR_PROPERTY, propertyDescriptor, Pair.create(type, name));
FieldVisitor fv = builder.newField(JvmDeclarationOriginKt.OtherOrigin(element, propertyDescriptor), modifiers, name, type.getDescriptor(),
typeMapper.mapFieldSignature(jetType, propertyDescriptor), defaultValue);
FieldVisitor fv = builder.newField(
JvmDeclarationOriginKt.OtherOrigin(element, propertyDescriptor), modifiers, name, type.getDescriptor(),
typeMapper.mapFieldSignature(kotlinType, propertyDescriptor), defaultValue
);
Annotated fieldAnnotated = new AnnotatedWithFakeAnnotations(propertyDescriptor, annotations);
AnnotationCodegen.forField(fv, typeMapper).genAnnotations(
fieldAnnotated, type, isDelegate ? AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD : AnnotationUseSiteTarget.FIELD);
}
private void generatePropertyDelegateAccess(KtProperty p, PropertyDescriptor propertyDescriptor, Annotations annotations) {
private void generatePropertyDelegateAccess(
@NotNull ClassBuilder builder,
@NotNull KtProperty p,
@NotNull PropertyDescriptor propertyDescriptor,
@NotNull Annotations annotations
) {
KtExpression delegateExpression = p.getDelegateExpression();
KotlinType delegateType = delegateExpression != null ? bindingContext.getType(p.getDelegateExpression()) : null;
if (delegateType == null) {
@@ -358,10 +377,15 @@ public class PropertyCodegen {
delegateType = ErrorUtils.createErrorType("Delegate type");
}
generateBackingField(p, propertyDescriptor, true, delegateType, null, annotations);
generateBackingField(builder, p, propertyDescriptor, true, delegateType, null, annotations);
}
private void generateBackingFieldAccess(KtNamedDeclaration p, PropertyDescriptor propertyDescriptor, Annotations annotations) {
private void generateBackingFieldAccess(
@NotNull ClassBuilder builder,
@NotNull KtNamedDeclaration p,
@NotNull PropertyDescriptor propertyDescriptor,
@NotNull Annotations annotations
) {
Object value = null;
if (shouldWriteFieldInitializer(propertyDescriptor)) {
@@ -371,7 +395,7 @@ public class PropertyCodegen {
}
}
generateBackingField(p, propertyDescriptor, false, propertyDescriptor.getType(), value, annotations);
generateBackingField(builder, p, propertyDescriptor, false, propertyDescriptor.getType(), value, annotations);
}
private boolean shouldWriteFieldInitializer(@NotNull PropertyDescriptor descriptor) {
@@ -251,12 +251,13 @@ public abstract class CodegenContext<T extends DeclarationDescriptor> {
@NotNull
public FieldOwnerContext<PackageFragmentDescriptor> intoMultifileClassPart(
@NotNull MultifileClassCodegen codegen,
@NotNull PackageFragmentDescriptor descriptor,
@NotNull Type multifileClassType,
@NotNull Type filePartType,
@NotNull KtFile sourceFile
) {
return new MultifileClassPartContext(descriptor, this, multifileClassType, filePartType, sourceFile);
return new MultifileClassPartContext(codegen, descriptor, this, multifileClassType, filePartType, sourceFile);
}
@NotNull
@@ -18,14 +18,17 @@ package org.jetbrains.kotlin.codegen.context;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.codegen.MultifileClassCodegen;
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor;
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.org.objectweb.asm.Type;
public class MultifileClassPartContext extends MultifileClassContextBase implements DelegatingToPartContext, FacadePartWithSourceFile {
private final KtFile sourceFile;
private final MultifileClassCodegen codegen;
public MultifileClassPartContext(
@NotNull MultifileClassCodegen codegen,
PackageFragmentDescriptor descriptor,
CodegenContext parent,
Type multifileClassType,
@@ -34,6 +37,7 @@ public class MultifileClassPartContext extends MultifileClassContextBase impleme
) {
super(descriptor, parent, multifileClassType, filePartType);
this.sourceFile = sourceFile;
this.codegen = codegen;
}
@Nullable
@@ -47,4 +51,9 @@ public class MultifileClassPartContext extends MultifileClassContextBase impleme
public KtFile getSourceFile() {
return sourceFile;
}
@NotNull
public MultifileClassCodegen getMultifileClassCodegen() {
return codegen;
}
}