diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java index c144ee85621..a1454300aab 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java @@ -79,8 +79,7 @@ public abstract class ClassBodyCodegen extends MemberCodegen { protected void generateKotlinAnnotation() { } - protected void generateSyntheticParts() { - } + protected abstract void generateSyntheticParts(); private void generateClassBody() { FunctionCodegen functionCodegen = new FunctionCodegen(context, v, state, this); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java index 2c18adf449a..a44f0b9bd1a 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java @@ -27,7 +27,6 @@ import org.jetbrains.asm4.commons.InstructionAdapter; import org.jetbrains.asm4.commons.Method; import org.jetbrains.jet.codegen.context.CodegenContext; import org.jetbrains.jet.codegen.context.FieldOwnerContext; -import org.jetbrains.jet.codegen.context.NamespaceContext; import org.jetbrains.jet.codegen.context.NamespaceFacadeContext; import org.jetbrains.jet.codegen.signature.JvmMethodSignature; import org.jetbrains.jet.codegen.state.GenerationState; @@ -49,6 +48,7 @@ import static org.jetbrains.asm4.Opcodes.*; import static org.jetbrains.jet.codegen.AsmUtil.*; import static org.jetbrains.jet.codegen.CodegenUtil.getParentBodyCodegen; import static org.jetbrains.jet.codegen.CodegenUtil.isInterface; +import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isTrait; import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE; public class PropertyCodegen extends GenerationStateAware { @@ -142,26 +142,41 @@ public class PropertyCodegen extends GenerationStateAware { AnnotationCodegen.forField(fieldVisitor, typeMapper).genAnnotations(propertyDescriptor); } else if (!propertyDescriptor.getAnnotations().isEmpty()) { - // Annotations on properties without backing fields 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 - ReceiverParameterDescriptor receiver = propertyDescriptor.getReceiverParameter(); - Type receiverAsmType = receiver == null ? null : typeMapper.mapType(receiver.getType()); - Method method = JvmAbi.getSyntheticMethodSignatureForAnnotatedProperty(propertyDescriptor.getName(), receiverAsmType); - - MethodVisitor mv = v.newMethod(null, - ACC_DEPRECATED | ACC_FINAL | ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC, - method.getName(), - method.getDescriptor(), - null, - null); - v.getMemberMap().recordSyntheticMethodOfProperty(propertyDescriptor, method); - AnnotationCodegen.forMethod(mv, typeMapper).genAnnotations(propertyDescriptor); - mv.visitCode(); - mv.visitInsn(Opcodes.RETURN); - mv.visitEnd(); + if (!isTrait(context.getContextDescriptor())) { + Method method = getSyntheticMethodSignature(typeMapper, propertyDescriptor); + generateSyntheticMethodForAnnotatedProperty(v, typeMapper, propertyDescriptor, method); + v.getMemberMap().recordSyntheticMethodOfProperty(propertyDescriptor, method); + } } } + // Annotations on properties without backing fields 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 + public static void generateSyntheticMethodForAnnotatedProperty( + @NotNull ClassBuilder v, + @NotNull JetTypeMapper typeMapper, + @NotNull PropertyDescriptor propertyDescriptor, + @NotNull Method method + ) { + MethodVisitor mv = v.newMethod(null, + ACC_DEPRECATED | ACC_FINAL | ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC, + method.getName(), + method.getDescriptor(), + null, + null); + AnnotationCodegen.forMethod(mv, typeMapper).genAnnotations(propertyDescriptor); + mv.visitCode(); + mv.visitInsn(Opcodes.RETURN); + mv.visitEnd(); + } + + @NotNull + public static Method getSyntheticMethodSignature(@NotNull JetTypeMapper typeMapper, @NotNull PropertyDescriptor propertyDescriptor) { + ReceiverParameterDescriptor receiver = propertyDescriptor.getReceiverParameter(); + Type receiverAsmType = receiver == null ? null : typeMapper.mapType(receiver.getType()); + return JvmAbi.getSyntheticMethodSignatureForAnnotatedProperty(propertyDescriptor.getName(), receiverAsmType); + } + private FieldVisitor generateBackingField(JetNamedDeclaration element, PropertyDescriptor propertyDescriptor, boolean isDelegate, JetType jetType, Object defaultValue) { int modifiers = getDeprecatedAccessFlag(propertyDescriptor); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/TraitImplBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/TraitImplBodyCodegen.java index 35be1ec7f65..117880f14b6 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/TraitImplBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/TraitImplBodyCodegen.java @@ -18,10 +18,16 @@ package org.jetbrains.jet.codegen; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.asm4.commons.Method; import org.jetbrains.jet.codegen.context.ClassContext; import org.jetbrains.jet.codegen.state.GenerationState; import org.jetbrains.jet.codegen.state.JetTypeMapperMode; +import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; +import org.jetbrains.jet.lang.descriptors.VariableDescriptor; 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 static org.jetbrains.asm4.Opcodes.*; @@ -49,6 +55,25 @@ public class TraitImplBodyCodegen extends ClassBodyCodegen { v.visitSource(myClass.getContainingFile().getName(), null); } + @Override + protected void generateSyntheticParts() { + generateSyntheticMethodsForAnnotatedProperties(); + } + + private void generateSyntheticMethodsForAnnotatedProperties() { + for (JetDeclaration declaration : myClass.getDeclarations()) { + if (declaration instanceof JetProperty) { + VariableDescriptor variable = bindingContext.get(BindingContext.VARIABLE, declaration); + assert variable instanceof PropertyDescriptor : "Variable in trait should be a property: " + variable; + PropertyDescriptor property = (PropertyDescriptor) variable; + if (!property.getAnnotations().isEmpty()) { + Method method = PropertyCodegen.getSyntheticMethodSignature(typeMapper, property); + PropertyCodegen.generateSyntheticMethodForAnnotatedProperty(v, typeMapper, property, method); + } + } + } + } + @NotNull private String jvmName() { return typeMapper.mapType(descriptor.getDefaultType(), JetTypeMapperMode.TRAIT_IMPL).getInternalName(); diff --git a/compiler/testData/codegen/properties/syntheticMethod/inTrait.kt b/compiler/testData/codegen/properties/syntheticMethod/inTrait.kt new file mode 100644 index 00000000000..4baa7786d3b --- /dev/null +++ b/compiler/testData/codegen/properties/syntheticMethod/inTrait.kt @@ -0,0 +1,7 @@ +import java.lang.annotation.* + +Retention(RetentionPolicy.RUNTIME) annotation class SomeAnnotation(val value: String) + +trait T { + [SomeAnnotation("OK")] val property: Int +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/SyntheticMethodForAnnotatedPropertyGenTest.java b/compiler/tests/org/jetbrains/jet/codegen/SyntheticMethodForAnnotatedPropertyGenTest.java index 11c441c87f6..d7f01c83e31 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/SyntheticMethodForAnnotatedPropertyGenTest.java +++ b/compiler/tests/org/jetbrains/jet/codegen/SyntheticMethodForAnnotatedPropertyGenTest.java @@ -45,7 +45,7 @@ public class SyntheticMethodForAnnotatedPropertyGenTest extends CodegenTestCase public void testInClass() { loadFile(); - assertClassHasAnnotatedSyntheticMethod(generateClass("A")); + assertAnnotatedSyntheticMethodExistence(true, generateClass("A")); } public void testTopLevel() { @@ -55,14 +55,24 @@ public class SyntheticMethodForAnnotatedPropertyGenTest extends CodegenTestCase if (fileName.startsWith(packageClassName) && !fileName.equals(packageClassName + ".class")) { // This should be package$src class Class a = generateClass(fileName.substring(0, fileName.length() - ".class".length())); - assertClassHasAnnotatedSyntheticMethod(a); + assertAnnotatedSyntheticMethodExistence(true, a); } } } - private static void assertClassHasAnnotatedSyntheticMethod(@NotNull Class a) { - for (Method method : a.getDeclaredMethods()) { + public void testInTrait() throws ClassNotFoundException { + loadFile(); + GeneratedClassLoader loader = generateAndCreateClassLoader(); + assertAnnotatedSyntheticMethodExistence(false, loader.loadClass("T")); + assertAnnotatedSyntheticMethodExistence(true, loader.loadClass("T" + JvmAbi.TRAIT_IMPL_SUFFIX)); + } + + private static void assertAnnotatedSyntheticMethodExistence(boolean expected, @NotNull Class clazz) { + for (Method method : clazz.getDeclaredMethods()) { if (TEST_SYNTHETIC_METHOD_NAME.equals(method.getName())) { + if (!expected) { + fail("Synthetic method for annotated property found, but not expected: " + method); + } assertTrue(method.isSynthetic()); int modifiers = method.getModifiers(); assertTrue(Modifier.isFinal(modifiers)); @@ -75,6 +85,8 @@ public class SyntheticMethodForAnnotatedPropertyGenTest extends CodegenTestCase return; } } - fail("Synthetic method for annotated property not found: " + TEST_SYNTHETIC_METHOD_NAME); + if (expected) { + fail("Synthetic method for annotated property expected, but not found: " + TEST_SYNTHETIC_METHOD_NAME); + } } }