diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/AnnotationCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/AnnotationCodegen.java new file mode 100644 index 00000000000..17973fb972f --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/codegen/AnnotationCodegen.java @@ -0,0 +1,90 @@ +package org.jetbrains.jet.codegen; + +import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor; +import org.jetbrains.jet.lang.descriptors.annotations.Annotated; +import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; +import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.util.slicedmap.RewritePolicy; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; + +import java.lang.annotation.RetentionPolicy; +import java.util.List; + +/** + * @author alex.tkachman + */ +public abstract class AnnotationCodegen { + public void genAnnotations(Annotated annotated, JetTypeMapper typeMapper) { + if(annotated == null) + return; + + List annotations = annotated.getAnnotations(); + if(annotations == null) + return; + + for (AnnotationDescriptor annotationDescriptor : annotations) { + List> valueArguments = annotationDescriptor.getValueArguments(); + if(!valueArguments.isEmpty()) { + throw new UnsupportedOperationException("Only annotations without values are supported by backend so far"); + } + + JetType type = annotationDescriptor.getType(); + ClassifierDescriptor classifierDescriptor = type.getConstructor().getDeclarationDescriptor(); + RetentionPolicy rp = getRetentionPolicy(classifierDescriptor, typeMapper); + if(rp != RetentionPolicy.SOURCE) { + String internalName = typeMapper.mapType(type).getDescriptor(); + AnnotationVisitor annotationVisitor = visitAnnotation(internalName, rp == RetentionPolicy.RUNTIME); + annotationVisitor.visitEnd(); + } + } + } + + private static RetentionPolicy getRetentionPolicy(ClassifierDescriptor descriptor, JetTypeMapper typeMapper) { + RetentionPolicy rp = RetentionPolicy.RUNTIME; + /* + @todo : when JavaDescriptoResolver provides ennough info + for (AnnotationDescriptor annotationDescriptor : descriptor.getAnnotations()) { + String internalName = typeMapper.mapType(annotationDescriptor.getType()).getInternalName(); + if("java/lang/annotation/RetentionPolicy".equals(internalName)) { + CompileTimeConstant compileTimeConstant = annotationDescriptor.getValueArguments().get(0); + System.out.println(compileTimeConstant); + break; + } + } + */ + return rp; //To change body of created methods use File | Settings | File Templates. + } + + abstract AnnotationVisitor visitAnnotation(String descr, boolean visible); + + public static AnnotationCodegen forClass(final ClassVisitor cv) { + return new AnnotationCodegen() { + @Override + AnnotationVisitor visitAnnotation(String descr, boolean visible) { + return cv.visitAnnotation(descr, visible); + } + }; + } + + public static AnnotationCodegen forMethod(final MethodVisitor mv) { + return new AnnotationCodegen() { + @Override + AnnotationVisitor visitAnnotation(String descr, boolean visible) { + return mv.visitAnnotation(descr, visible); + } + }; + } + + public static AnnotationCodegen forField(final FieldVisitor mv) { + return new AnnotationCodegen() { + @Override + AnnotationVisitor visitAnnotation(String descr, boolean visible) { + return mv.visitAnnotation(descr, visible); + } + }; + } +} diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java b/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java index 2ce02718eb0..4799a503c12 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java @@ -2,13 +2,17 @@ package org.jetbrains.jet.codegen; import gnu.trove.THashSet; import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.descriptors.annotations.Annotated; import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.jet.lang.psi.JetClassObject; import org.jetbrains.jet.lang.psi.JetClassOrObject; import org.jetbrains.jet.lang.psi.JetObjectDeclaration; import org.jetbrains.jet.lang.resolve.calls.ExpressionAsFunctionDescriptor; +import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; import org.jetbrains.jet.lang.types.JetStandardClasses; import org.jetbrains.jet.lang.types.JetType; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.MethodVisitor; import java.util.Collections; import java.util.List; @@ -133,4 +137,5 @@ public class CodegenUtil { return myClass instanceof JetObjectDeclaration && !((JetObjectDeclaration) myClass).isObjectLiteral() && !(myClass.getParent() instanceof JetClassObject); } + } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java index 9e6f7ca2f53..f090510c5ce 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java @@ -98,6 +98,7 @@ public class FunctionCodegen { if (isAbstract) flags |= ACC_ABSTRACT; final MethodVisitor mv = v.newMethod(fun, flags, jvmSignature.getAsmMethod().getName(), jvmSignature.getAsmMethod().getDescriptor(), jvmSignature.getGenericsSignature(), null); + AnnotationCodegen.forMethod(mv).genAnnotations(functionDescriptor, state.getTypeMapper()); if(v.generateCode()) { int start = 0; if (needJetAnnotations) { @@ -154,6 +155,8 @@ public class FunctionCodegen { } } } + + if (!isAbstract && v.generateCode()) { mv.visitCode(); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index bdad028593b..3b41814699b 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -111,6 +111,8 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { v.visitInnerClass(innerClassInternalName, outerClassInernalName, innerClass.getName(), innerClassAccess); } + AnnotationCodegen.forClass(v.getVisitor()).genAnnotations(descriptor, typeMapper); + if(myClass instanceof JetClass && signature.getKotlinGenericSignature() != null) { AnnotationVisitor annotationVisitor = v.newAnnotation(myClass, JvmStdlibNames.JET_CLASS.getDescriptor(), true); annotationVisitor.visit(JvmStdlibNames.JET_CLASS_SIGNATURE, signature.getKotlinGenericSignature()); @@ -460,6 +462,8 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { } jetConstructorVisitor.visitEnd(); + AnnotationCodegen.forMethod(mv).genAnnotations(constructorDescriptor, typeMapper); + if (constructorDescriptor != null) { int i = 0; diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java index 45c648d9225..b805e960755 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java @@ -10,6 +10,7 @@ import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; import org.jetbrains.jet.lang.resolve.java.JvmAbi; import org.jetbrains.jet.lang.resolve.java.JvmStdlibNames; import org.jetbrains.jet.lexer.JetTokens; +import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; @@ -79,7 +80,8 @@ public class PropertyCodegen { if(state.getStandardLibrary().isVolatile(propertyDescriptor)) { modifiers |= Opcodes.ACC_VOLATILE; } - v.newField(p, modifiers, p.getName(), state.getTypeMapper().mapType(propertyDescriptor.getOutType()).getDescriptor(), null, value); + FieldVisitor fieldVisitor = v.newField(p, modifiers, p.getName(), state.getTypeMapper().mapType(propertyDescriptor.getOutType()).getDescriptor(), null, value); + AnnotationCodegen.forField(fieldVisitor).genAnnotations(propertyDescriptor, state.getTypeMapper()); } } @@ -146,6 +148,12 @@ public class PropertyCodegen { String getterName = getterName(propertyDescriptor.getName()); MethodVisitor mv = v.newMethod(origin, flags, getterName, descriptor, null, null); generateJetPropertyAnnotation(mv, signature.getPropertyTypeKotlinSignature(), signature.getJvmMethodSignature().getKotlinTypeParameter()); + + if(propertyDescriptor.getGetter() != null) { + assert !propertyDescriptor.getGetter().hasBody(); + AnnotationCodegen.forMethod(mv).genAnnotations(propertyDescriptor.getGetter(), state.getTypeMapper()); + } + if (v.generateCode() && (!isTrait || kind instanceof OwnerKind.DelegateKind)) { mv.visitCode(); InstructionAdapter iv = new InstructionAdapter(mv); @@ -204,6 +212,12 @@ public class PropertyCodegen { final String descriptor = signature.getJvmMethodSignature().getAsmMethod().getDescriptor(); MethodVisitor mv = v.newMethod(origin, flags, setterName(propertyDescriptor.getName()), descriptor, null, null); generateJetPropertyAnnotation(mv, signature.getPropertyTypeKotlinSignature(), signature.getJvmMethodSignature().getKotlinTypeParameter()); + + if(propertyDescriptor.getSetter() != null) { + assert !propertyDescriptor.getSetter().hasBody(); + AnnotationCodegen.forMethod(mv).genAnnotations(propertyDescriptor.getSetter(), state.getTypeMapper()); + } + if (v.generateCode() && (!isTrait || kind instanceof OwnerKind.DelegateKind)) { mv.visitCode(); InstructionAdapter iv = new InstructionAdapter(mv); diff --git a/compiler/tests/org/jetbrains/jet/codegen/AnnotationGenTest.java b/compiler/tests/org/jetbrains/jet/codegen/AnnotationGenTest.java new file mode 100644 index 00000000000..82df3fb7c86 --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/codegen/AnnotationGenTest.java @@ -0,0 +1,58 @@ +package org.jetbrains.jet.codegen; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public class AnnotationGenTest extends CodegenTestCase { + public void testPropField() throws NoSuchFieldException, NoSuchMethodException { + loadText("[Deprecated] var x = 0"); + Class aClass = generateNamespaceClass(); + assertNull(aClass.getDeclaredMethod("getX").getAnnotation(Deprecated.class)); + assertNull(aClass.getDeclaredMethod("setX", int.class).getAnnotation(Deprecated.class)); + assertNotNull(aClass.getDeclaredField("x").getAnnotation(Deprecated.class)); + } + + public void testPropGetter() throws NoSuchFieldException, NoSuchMethodException { + loadText("var x = 0\n" + + "[Deprecated] get"); + + Class aClass = generateNamespaceClass(); + assertNotNull(aClass.getDeclaredMethod("getX").getAnnotation(Deprecated.class)); + assertNull(aClass.getDeclaredMethod("setX", int.class).getAnnotation(Deprecated.class)); + assertNull(aClass.getDeclaredField("x").getAnnotation(Deprecated.class)); + } + + public void testPropSetter() throws NoSuchFieldException, NoSuchMethodException { + loadText("var x = 0\n" + + "[Deprecated] set"); + System.out.println(generateToText()); + Class aClass = generateNamespaceClass(); + assertNull(aClass.getDeclaredMethod("getX").getAnnotation(Deprecated.class)); + assertNotNull(aClass.getDeclaredMethod("setX", int.class).getAnnotation(Deprecated.class)); + assertNull(aClass.getDeclaredField("x").getAnnotation(Deprecated.class)); + } + + public void testConstructor() throws NoSuchFieldException, NoSuchMethodException { + loadText("class A [Deprecated] () {}"); + Class aClass = generateClass("A"); + Constructor x = aClass.getDeclaredConstructor(); + Deprecated annotation = (Deprecated) x.getAnnotation(Deprecated.class); + assertNotNull(annotation); + } + + public void testMethod() throws NoSuchFieldException, NoSuchMethodException { + loadText("[Deprecated] fun x () {}"); + Class aClass = generateNamespaceClass(); + Method x = aClass.getDeclaredMethod("x"); + Deprecated annotation = (Deprecated) x.getAnnotation(Deprecated.class); + assertNotNull(annotation); + } + + public void testClass() throws NoSuchFieldException, NoSuchMethodException { + loadText("[Deprecated] class A () {}"); + Class aClass = generateClass("A"); + Deprecated annotation = (Deprecated) aClass.getAnnotation(Deprecated.class); + assertNotNull(annotation); + } +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/CodegenTestCase.java b/compiler/tests/org/jetbrains/jet/codegen/CodegenTestCase.java index b905f030458..92f3340673f 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/CodegenTestCase.java +++ b/compiler/tests/org/jetbrains/jet/codegen/CodegenTestCase.java @@ -114,6 +114,11 @@ public abstract class CodegenTestCase extends JetLiteFixture { return loadRootNamespaceClass(state); } + protected Class generateClass(String name) { + ClassFileFactory state = generateClassesInFile(); + return loadClass(name, state); + } + protected Class loadRootNamespaceClass(@NotNull ClassFileFactory state) { String fqName = NamespaceCodegen.getJVMClassName(JetPsiUtil.getFQName(myFile), true).replace("/", "."); Map classMap = loadAllClasses(state);