Load annotations on properties declared in traits

This commit is contained in:
Alexander Udalov
2013-10-16 21:49:27 +04:00
parent 5a1daaa4b6
commit b7789da3b9
21 changed files with 204 additions and 53 deletions
@@ -35,6 +35,7 @@ import java.util.List;
import static org.jetbrains.asm4.Opcodes.ACC_STATIC;
import static org.jetbrains.asm4.Opcodes.RETURN;
import static org.jetbrains.jet.codegen.binding.CodegenBinding.enumEntryNeedSubclass;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isTopLevelOrInnerClass;
public abstract class ClassBodyCodegen extends MemberCodegen {
protected final JetClassOrObject myClass;
@@ -71,13 +72,14 @@ public abstract class ClassBodyCodegen extends MemberCodegen {
generateStaticInitializer();
generateKotlinAnnotation();
if (state.getClassBuilderMode() == ClassBuilderMode.FULL && isTopLevelOrInnerClass(descriptor)) {
generateKotlinAnnotation();
}
}
protected abstract void generateDeclaration();
protected void generateKotlinAnnotation() {
}
protected abstract void generateKotlinAnnotation();
protected abstract void generateSyntheticParts();
@@ -111,7 +111,7 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
if (owner instanceof NamespaceFacadeContext) {
Type ownerType = ((NamespaceFacadeContext) owner).getDelegateToClassType();
v.getMemberMap().recordSrcClassNameForCallable(functionDescriptor, shortNameByAsmType(ownerType));
v.getMemberMap().recordImplClassNameForCallable(functionDescriptor, shortNameByAsmType(ownerType));
}
else {
v.getMemberMap().recordMethodOfDescriptor(functionDescriptor, asmMethod);
@@ -213,12 +213,6 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
@Override
protected void generateKotlinAnnotation() {
if (state.getClassBuilderMode() != ClassBuilderMode.FULL) {
return;
}
if (!isTopLevelOrInnerClass(descriptor)) return;
DescriptorSerializer serializer = new DescriptorSerializer(new JavaSerializerExtension(v.getMemberMap()));
ProtoBuf.Class classProto = serializer.classProto(descriptor).build();
@@ -45,7 +45,7 @@ public class JavaSerializerExtension extends SerializerExtension {
@NotNull NameTable nameTable
) {
saveSignature(callable, proto, nameTable);
saveSrcClassName(callable, proto, nameTable);
saveImplClassName(callable, proto, nameTable);
}
private void saveSignature(
@@ -91,14 +91,14 @@ public class JavaSerializerExtension extends SerializerExtension {
}
}
private void saveSrcClassName(
private void saveImplClassName(
@NotNull CallableMemberDescriptor callable,
@NotNull ProtoBuf.Callable.Builder proto,
@NotNull NameTable nameTable
) {
String name = memberMap.getSrcClassNameOfCallable(callable);
String name = memberMap.getImplClassNameOfCallable(callable);
if (name != null) {
proto.setExtension(JavaProtoBuf.srcClassName, nameTable.getSimpleNameIndex(Name.identifier(name)));
proto.setExtension(JavaProtoBuf.implClassName, nameTable.getSimpleNameIndex(Name.identifier(name)));
}
}
@@ -31,7 +31,7 @@ public final class MemberMap {
private final Map<FunctionDescriptor, Method> methodForFunction = new HashMap<FunctionDescriptor, Method>();
private final Map<PropertyDescriptor, Pair<Type, String>> fieldForProperty = new HashMap<PropertyDescriptor, Pair<Type, String>>();
private final Map<PropertyDescriptor, Method> syntheticMethodForProperty = new HashMap<PropertyDescriptor, Method>();
private final Map<CallableMemberDescriptor, String> srcClassNameForCallable = new HashMap<CallableMemberDescriptor, String>();
private final Map<CallableMemberDescriptor, String> implClassNameForCallable = new HashMap<CallableMemberDescriptor, String>();
private final Set<PropertyDescriptor> staticFieldInOuterClass = new HashSet<PropertyDescriptor>();
@NotNull
@@ -50,8 +50,8 @@ public final class MemberMap {
result.recordSyntheticMethodOfProperty(entry.getKey(), entry.getValue());
}
for (Map.Entry<CallableMemberDescriptor, String> entry : map.srcClassNameForCallable.entrySet()) {
result.recordSrcClassNameForCallable(entry.getKey(), entry.getValue());
for (Map.Entry<CallableMemberDescriptor, String> entry : map.implClassNameForCallable.entrySet()) {
result.recordImplClassNameForCallable(entry.getKey(), entry.getValue());
}
for (PropertyDescriptor property : map.staticFieldInOuterClass) {
@@ -77,8 +77,8 @@ public final class MemberMap {
assert old == null : "Duplicate synthetic method for property: " + descriptor + "; " + old;
}
public void recordSrcClassNameForCallable(@NotNull CallableMemberDescriptor descriptor, @NotNull String name) {
String old = srcClassNameForCallable.put(descriptor, name);
public void recordImplClassNameForCallable(@NotNull CallableMemberDescriptor descriptor, @NotNull String name) {
String old = implClassNameForCallable.put(descriptor, name);
assert old == null : "Duplicate src class name for callable: " + descriptor + "; " + old;
}
@@ -103,8 +103,8 @@ public final class MemberMap {
}
@Nullable
public String getSrcClassNameOfCallable(@NotNull CallableMemberDescriptor descriptor) {
return srcClassNameForCallable.get(descriptor);
public String getImplClassNameOfCallable(@NotNull CallableMemberDescriptor descriptor) {
return implClassNameForCallable.get(descriptor);
}
public boolean isStaticFieldInOuterClass(@NotNull PropertyDescriptor property) {
@@ -116,6 +116,6 @@ public final class MemberMap {
return "Functions: " + methodForFunction.size() +
", fields: " + fieldForProperty.size() +
", synthetic methods: " + syntheticMethodForProperty.size() +
", src class names: " + srcClassNameForCallable.size();
", impl class names: " + implClassNameForCallable.size();
}
}
@@ -92,7 +92,7 @@ public class PropertyCodegen extends GenerationStateAware {
if (context instanceof NamespaceFacadeContext) {
Type ownerType = ((NamespaceFacadeContext) context).getDelegateToClassType();
v.getMemberMap().recordSrcClassNameForCallable(propertyDescriptor, shortNameByAsmType(ownerType));
v.getMemberMap().recordImplClassNameForCallable(propertyDescriptor, shortNameByAsmType(ownerType));
}
else if (kind != OwnerKind.TRAIT_IMPL) {
generateBackingField(p, propertyDescriptor);
@@ -142,11 +142,16 @@ public class PropertyCodegen extends GenerationStateAware {
AnnotationCodegen.forField(fieldVisitor, typeMapper).genAnnotations(propertyDescriptor);
}
else if (!propertyDescriptor.getAnnotations().isEmpty()) {
if (!isTrait(context.getContextDescriptor())) {
Method method = getSyntheticMethodSignature(typeMapper, propertyDescriptor);
generateSyntheticMethodForAnnotatedProperty(v, typeMapper, propertyDescriptor, method);
v.getMemberMap().recordSyntheticMethodOfProperty(propertyDescriptor, method);
Method method = getSyntheticMethodSignature(typeMapper, propertyDescriptor);
DeclarationDescriptor descriptor = context.getContextDescriptor();
if (isTrait(descriptor)) {
Type tImplType = typeMapper.mapTraitImpl((ClassDescriptor) descriptor);
v.getMemberMap().recordImplClassNameForCallable(propertyDescriptor, shortNameByAsmType(tImplType));
}
else {
generateSyntheticMethodForAnnotatedProperty(v, typeMapper, propertyDescriptor, method);
}
v.getMemberMap().recordSyntheticMethodOfProperty(propertyDescriptor, method);
}
}
@@ -18,6 +18,7 @@ package org.jetbrains.jet.codegen;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.AnnotationVisitor;
import org.jetbrains.asm4.commons.Method;
import org.jetbrains.jet.codegen.context.ClassContext;
import org.jetbrains.jet.codegen.state.GenerationState;
@@ -27,8 +28,11 @@ import org.jetbrains.jet.lang.psi.JetClassOrObject;
import org.jetbrains.jet.lang.psi.JetDeclaration;
import org.jetbrains.jet.lang.psi.JetProperty;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames;
import static org.jetbrains.asm4.Opcodes.*;
import static org.jetbrains.jet.codegen.AsmUtil.asmDescByFqNameWithoutInnerClasses;
public class TraitImplBodyCodegen extends ClassBodyCodegen {
@@ -54,6 +58,14 @@ public class TraitImplBodyCodegen extends ClassBodyCodegen {
v.visitSource(myClass.getContainingFile().getName(), null);
}
@Override
protected void generateKotlinAnnotation() {
AnnotationVisitor av =
v.getVisitor().visitAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_TRAIT_IMPL), true);
av.visit(JvmAnnotationNames.ABI_VERSION_FIELD_NAME, JvmAbi.VERSION);
av.visitEnd();
}
@Override
protected void generateSyntheticParts() {
generateSyntheticMethodsForAnnotatedProperties();
@@ -0,0 +1,9 @@
package test
annotation class Anno
class Class {
trait Trait {
[Anno] val property: Int
}
}
@@ -0,0 +1,14 @@
package test
internal final annotation class Anno : jet.Annotation {
/*primary*/ public constructor Anno()
}
internal final class Class {
/*primary*/ public constructor Class()
internal trait Trait {
test.Anno() internal abstract val property: jet.Int
internal abstract fun <get-property>(): jet.Int
}
}
@@ -0,0 +1,7 @@
package test
annotation class Anno
trait Trait {
[Anno] val property: Int
}
@@ -0,0 +1,10 @@
package test
internal final annotation class Anno : jet.Annotation {
/*primary*/ public constructor Anno()
}
internal trait Trait {
test.Anno() internal abstract val property: jet.Int
internal abstract fun <get-property>(): jet.Int
}
@@ -0,0 +1,10 @@
package test
annotation class Anno
trait Trait {
class object {
[Anno] val property: Int
get() = 42
}
}
@@ -0,0 +1,14 @@
package test
internal final annotation class Anno : jet.Annotation {
/*primary*/ public constructor Anno()
}
internal trait Trait {
internal class object <class-object-for-Trait> {
/*primary*/ private constructor <class-object-for-Trait>()
test.Anno() internal final val property: jet.Int
internal final fun <get-property>(): jet.Int
}
}
@@ -209,11 +209,26 @@ public class LoadCompiledKotlinTestGenerated extends AbstractLoadCompiledKotlinT
doTestWithAccessors("compiler/testData/loadKotlin/annotations/propertiesWithoutBackingFields/ExtensionsWithSameNamePackage.kt");
}
@TestMetadata("NestedTrait.kt")
public void testNestedTrait() throws Exception {
doTestWithAccessors("compiler/testData/loadKotlin/annotations/propertiesWithoutBackingFields/NestedTrait.kt");
}
@TestMetadata("TopLevel.kt")
public void testTopLevel() throws Exception {
doTestWithAccessors("compiler/testData/loadKotlin/annotations/propertiesWithoutBackingFields/TopLevel.kt");
}
@TestMetadata("Trait.kt")
public void testTrait() throws Exception {
doTestWithAccessors("compiler/testData/loadKotlin/annotations/propertiesWithoutBackingFields/Trait.kt");
}
@TestMetadata("TraitClassObject.kt")
public void testTraitClassObject() throws Exception {
doTestWithAccessors("compiler/testData/loadKotlin/annotations/propertiesWithoutBackingFields/TraitClassObject.kt");
}
}
public static Test innerSuite() {
@@ -211,11 +211,26 @@ public class LazyResolveNamespaceComparingTestGenerated extends AbstractLazyReso
doTestCheckingPrimaryConstructorsAndAccessors("compiler/testData/loadKotlin/annotations/propertiesWithoutBackingFields/ExtensionsWithSameNamePackage.kt");
}
@TestMetadata("NestedTrait.kt")
public void testNestedTrait() throws Exception {
doTestCheckingPrimaryConstructorsAndAccessors("compiler/testData/loadKotlin/annotations/propertiesWithoutBackingFields/NestedTrait.kt");
}
@TestMetadata("TopLevel.kt")
public void testTopLevel() throws Exception {
doTestCheckingPrimaryConstructorsAndAccessors("compiler/testData/loadKotlin/annotations/propertiesWithoutBackingFields/TopLevel.kt");
}
@TestMetadata("Trait.kt")
public void testTrait() throws Exception {
doTestCheckingPrimaryConstructorsAndAccessors("compiler/testData/loadKotlin/annotations/propertiesWithoutBackingFields/Trait.kt");
}
@TestMetadata("TraitClassObject.kt")
public void testTraitClassObject() throws Exception {
doTestCheckingPrimaryConstructorsAndAccessors("compiler/testData/loadKotlin/annotations/propertiesWithoutBackingFields/TraitClassObject.kt");
}
}
public static Test innerSuite() {
@@ -19,6 +19,7 @@ package org.jetbrains.jet.lang.resolve.java;
import jet.KotlinClass;
import jet.KotlinPackage;
import jet.KotlinPackageFragment;
import jet.KotlinTraitImpl;
import org.jetbrains.jet.lang.resolve.name.FqName;
import static org.jetbrains.jet.lang.resolve.java.resolver.DescriptorResolverUtils.fqNameByClass;
@@ -30,6 +31,8 @@ public final class JvmAnnotationNames {
public static final FqName KOTLIN_PACKAGE_FRAGMENT = fqNameByClass(KotlinPackageFragment.class);
public static final FqName KOTLIN_TRAIT_IMPL = fqNameByClass(KotlinTraitImpl.class);
public static final String ABI_VERSION_FIELD_NAME = "abiVersion";
public static final String DATA_FIELD_NAME = "data";
@@ -47,6 +47,8 @@ import javax.inject.Inject;
import java.io.IOException;
import java.util.*;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isClassObject;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isTrait;
import static org.jetbrains.jet.lang.resolve.java.DescriptorSearchRule.IGNORE_KOTLIN_SOURCES;
import static org.jetbrains.jet.lang.resolve.kotlin.DeserializedResolverUtils.kotlinFqNameToJavaFqName;
import static org.jetbrains.jet.lang.resolve.kotlin.DeserializedResolverUtils.naiveKotlinFqName;
@@ -231,7 +233,7 @@ public class AnnotationDescriptorDeserializer implements AnnotationDeserializer
MemberSignature signature = getCallableSignature(proto, nameResolver, kind);
if (signature == null) return Collections.emptyList();
KotlinJvmBinaryClass kotlinClass = findClassWithMemberAnnotations(container, proto, nameResolver);
KotlinJvmBinaryClass kotlinClass = findClassWithMemberAnnotations(container, proto, nameResolver, kind);
if (kotlinClass == null) {
errorReporter.reportAnnotationLoadingError("Kotlin class for loading member annotations is not found: " + container, null);
return Collections.emptyList();
@@ -245,33 +247,42 @@ public class AnnotationDescriptorDeserializer implements AnnotationDeserializer
private KotlinJvmBinaryClass findClassWithMemberAnnotations(
@NotNull ClassOrNamespaceDescriptor container,
@NotNull ProtoBuf.Callable proto,
@NotNull NameResolver nameResolver
@NotNull NameResolver nameResolver,
@NotNull AnnotatedCallableKind kind
) {
if (container instanceof NamespaceDescriptor) {
Name name = loadSrcClassName(proto, nameResolver);
if (name != null) {
return kotlinClassFinder.find(getSrcClassFqName((NamespaceDescriptor) container, name));
return loadPackageFragmentClassFqName((NamespaceDescriptor) container, proto, nameResolver);
}
else if (isClassObject(container) && isStaticFieldInOuter(proto)) {
// Backing fields of properties of a class object are generated in the outer class
return findKotlinClassByDescriptor((ClassOrNamespaceDescriptor) container.getContainingDeclaration());
}
else if (isTrait(container) && kind == AnnotatedCallableKind.PROPERTY) {
NamespaceDescriptor containingPackage = DescriptorUtils.getParentOfType(container, NamespaceDescriptor.class);
assert containingPackage != null : "Trait must have a namespace among his parents: " + container;
if (proto.hasExtension(JavaProtoBuf.implClassName)) {
Name tImplName = nameResolver.getName(proto.getExtension(JavaProtoBuf.implClassName));
return kotlinClassFinder.find(containingPackage.getFqName().child(tImplName));
}
return null;
}
else if (container instanceof ClassDescriptor && ((ClassDescriptor) container).getKind() == ClassKind.CLASS_OBJECT) {
// Backing fields of properties of a class object are generated in the outer class
if (isStaticFieldInOuter(proto)) {
return findKotlinClassByDescriptor((ClassOrNamespaceDescriptor) container.getContainingDeclaration());
}
}
return findKotlinClassByDescriptor(container);
}
@NotNull
private static FqName getSrcClassFqName(@NotNull NamespaceDescriptor container, @NotNull Name name) {
return PackageClassUtils.getPackageClassFqName(DescriptorUtils.getFQName(container).toSafe()).parent().child(name);
}
@Nullable
private static Name loadSrcClassName(@NotNull ProtoBuf.Callable proto, @NotNull NameResolver nameResolver) {
return proto.hasExtension(JavaProtoBuf.srcClassName) ? nameResolver.getName(proto.getExtension(JavaProtoBuf.srcClassName)) : null;
private KotlinJvmBinaryClass loadPackageFragmentClassFqName(
@NotNull NamespaceDescriptor container,
@NotNull ProtoBuf.Callable proto,
@NotNull NameResolver nameResolver
) {
if (proto.hasExtension(JavaProtoBuf.implClassName)) {
Name name = nameResolver.getName(proto.getExtension(JavaProtoBuf.implClassName));
FqName fqName = PackageClassUtils.getPackageClassFqName(container.getFqName()).parent().child(name);
return kotlinClassFinder.find(fqName);
}
return null;
}
private static boolean isStaticFieldInOuter(@NotNull ProtoBuf.Callable proto) {
@@ -38,6 +38,7 @@ import static org.jetbrains.jet.lang.resolve.kotlin.KotlinJvmBinaryClass.Annotat
CLASS(JvmAnnotationNames.KOTLIN_CLASS),
PACKAGE(JvmAnnotationNames.KOTLIN_PACKAGE),
PACKAGE_FRAGMENT(JvmAnnotationNames.KOTLIN_PACKAGE_FRAGMENT),
TRAIT_IMPL(JvmAnnotationNames.KOTLIN_TRAIT_IMPL),
OLD_CLASS(JvmAnnotationNames.OLD_JET_CLASS_ANNOTATION),
OLD_PACKAGE(JvmAnnotationNames.OLD_JET_PACKAGE_CLASS_ANNOTATION);
@@ -92,6 +93,8 @@ import static org.jetbrains.jet.lang.resolve.kotlin.KotlinJvmBinaryClass.Annotat
return serializedDataHeader(SerializedDataHeader.Kind.PACKAGE);
case PACKAGE_FRAGMENT:
return new PackageFragmentClassHeader(version);
case TRAIT_IMPL:
return new TraitImplClassHeader(version);
default:
throw new UnsupportedOperationException("Unknown compatible HeaderType: " + foundType);
}
@@ -123,8 +126,8 @@ import static org.jetbrains.jet.lang.resolve.kotlin.KotlinJvmBinaryClass.Annotat
if (newType == HeaderType.CLASS || newType == HeaderType.PACKAGE) {
return kotlinClassOrPackageVisitor(annotationClassName);
}
else if (newType == HeaderType.PACKAGE_FRAGMENT) {
return kotlinPackageFragmentVisitor(annotationClassName);
else if (newType == HeaderType.PACKAGE_FRAGMENT || newType == HeaderType.TRAIT_IMPL) {
return annotationWithAbiVersionVisitor(annotationClassName);
}
return null;
@@ -198,7 +201,7 @@ import static org.jetbrains.jet.lang.resolve.kotlin.KotlinJvmBinaryClass.Annotat
}
@NotNull
private AnnotationArgumentVisitor kotlinPackageFragmentVisitor(@NotNull final JvmClassName annotationClassName) {
private AnnotationArgumentVisitor annotationWithAbiVersionVisitor(@NotNull final JvmClassName annotationClassName) {
return new AnnotationArgumentVisitor() {
@Override
public void visit(@Nullable Name name, @Nullable Object value) {
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.lang.resolve.kotlin.header;
/* package */ class TraitImplClassHeader extends KotlinClassHeader {
protected TraitImplClassHeader(int version) {
super(version);
}
}
@@ -74,6 +74,10 @@ extend Callable {
optional JavaPropertySignature property_signature = 101;
// For top-level callables, short name of "...Package$src$..." class with the callable's body and annotations
optional int32 src_class_name = 102;
// Package-less name of the class with the callable's body and annotations, if it differs from the class it's serialized into.
// E.g. for a class "A/B/C$D" this would be "C$D".
// This is needed to find the class to load annotations from in the following cases:
// 1) annotations on top-level members are written to compiled package fragment classes
// 2) annotations on properties in traits are written to TImpl classes
optional int32 impl_class_name = 102;
}
@@ -9,7 +9,7 @@ public final class JavaProtoBuf {
com.google.protobuf.ExtensionRegistryLite registry) {
registry.add(org.jetbrains.jet.descriptors.serialization.JavaProtoBuf.methodSignature);
registry.add(org.jetbrains.jet.descriptors.serialization.JavaProtoBuf.propertySignature);
registry.add(org.jetbrains.jet.descriptors.serialization.JavaProtoBuf.srcClassName);
registry.add(org.jetbrains.jet.descriptors.serialization.JavaProtoBuf.implClassName);
}
public interface JavaTypeOrBuilder
extends com.google.protobuf.MessageLiteOrBuilder {
@@ -2825,14 +2825,14 @@ public final class JavaProtoBuf {
null,
101,
com.google.protobuf.WireFormat.FieldType.MESSAGE);
public static final int SRC_CLASS_NAME_FIELD_NUMBER = 102;
public static final int IMPL_CLASS_NAME_FIELD_NUMBER = 102;
/**
* <code>extend .org.jetbrains.jet.descriptors.serialization.Callable { ... }</code>
*/
public static final
com.google.protobuf.GeneratedMessageLite.GeneratedExtension<
org.jetbrains.jet.descriptors.serialization.ProtoBuf.Callable,
java.lang.Integer> srcClassName = com.google.protobuf.GeneratedMessageLite
java.lang.Integer> implClassName = com.google.protobuf.GeneratedMessageLite
.newSingularGeneratedExtension(
org.jetbrains.jet.descriptors.serialization.ProtoBuf.Callable.getDefaultInstance(),
0,