Initial support for annotations
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user