Initial support for annotations

This commit is contained in:
Alex Tkachman
2012-02-05 11:29:04 +02:00
parent 1ed825e2cb
commit 3578d51cb1
7 changed files with 180 additions and 1 deletions
@@ -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<AnnotationDescriptor> annotations = annotated.getAnnotations();
if(annotations == null)
return;
for (AnnotationDescriptor annotationDescriptor : annotations) {
List<CompileTimeConstant<?>> 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);
}
};
}
}
@@ -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);
}
}
@@ -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();
@@ -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;
@@ -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);
@@ -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);
}
}
@@ -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<String, Class> classMap = loadAllClasses(state);