more annotations

This commit is contained in:
Alex Tkachman
2012-03-31 16:14:32 +03:00
parent fc843eca8f
commit 0cb26b0566
7 changed files with 288 additions and 34 deletions
@@ -17,20 +17,17 @@
package org.jetbrains.jet.codegen;
import com.intellij.psi.PsiElement;
import org.jetbrains.jet.codegen.intrinsics.IntrinsicMethod;
import org.jetbrains.jet.codegen.intrinsics.IntrinsicMethods;
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.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.calls.DefaultValueArgument;
import org.jetbrains.jet.lang.resolve.calls.ResolvedCall;
import org.jetbrains.jet.lang.resolve.calls.ResolvedValueArgument;
import org.jetbrains.jet.lang.resolve.calls.*;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lang.types.JetType;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.*;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
@@ -80,12 +77,67 @@ public abstract class AnnotationCodegen {
ResolvedValueArgument valueArgument = entry.getValue();
if (!(valueArgument instanceof DefaultValueArgument)) {
List<ValueArgument> valueArguments = valueArgument.getArguments();
assert valueArguments.size() == 1 : "Number of assertions on " + resolvedCall.getResultingDescriptor() + " = " + valueArguments.size(); // todo
CompileTimeConstant<?> compileTimeConstant = bindingContext.get(BindingContext.COMPILE_TIME_VALUE, valueArguments.get(0).getArgumentExpression());
assert compileTimeConstant != null;
assert valueArguments.size() == 1 : "Number of arguments on " + resolvedCall.getResultingDescriptor() + " = " + valueArguments.size(); // todo
CompileTimeConstant<?> compileTimeConstant =
bindingContext.get(BindingContext.COMPILE_TIME_VALUE, valueArguments.get(0).getArgumentExpression());
Object value = compileTimeConstant.getValue();
annotationVisitor.visit(entry.getKey().getName(), value);
String keyName = entry.getKey().getName();
if(compileTimeConstant != null) {
Object value = compileTimeConstant.getValue();
annotationVisitor.visit(keyName, value);
continue;
}
ExpressionValueArgument expressionValueArgument = (ExpressionValueArgument)valueArgument;
JetExpression expression = expressionValueArgument.getArguments().get(0).getArgumentExpression();
if(expression instanceof JetDotQualifiedExpression) {
JetDotQualifiedExpression qualifiedExpression = (JetDotQualifiedExpression)expression;
ResolvedCall<? extends CallableDescriptor> call =
bindingContext.get(BindingContext.RESOLVED_CALL, qualifiedExpression.getSelectorExpression());
if(call != null) {
if(call.getResultingDescriptor() instanceof PropertyDescriptor) {
PropertyDescriptor descriptor = (PropertyDescriptor)call.getResultingDescriptor();
annotationVisitor.visitEnum(keyName, typeMapper.mapType(descriptor.getReturnType()).getDescriptor(),descriptor.getName());
continue;
}
}
}
else {
if(expression instanceof JetCallExpression) {
JetCallExpression callExpression = (JetCallExpression)expression;
ResolvedCall<? extends CallableDescriptor> call =
bindingContext.get(BindingContext.RESOLVED_CALL, callExpression.getCalleeExpression());
if(call != null) {
List<AnnotationDescriptor> annotations = call.getResultingDescriptor().getOriginal().getAnnotations();
String value = null;
if (annotations != null) {
for (AnnotationDescriptor annotation : annotations) {
if("Intrinsic".equals(annotation.getType().getConstructor().getDeclarationDescriptor().getName())) {
value = (String) annotation.getValueArguments().get(0).getValue();
break;
}
}
}
if(IntrinsicMethods.KOTLIN_JAVA_CLASS_FUNCTION.equals(value)) {
annotationVisitor.visit(keyName, typeMapper.mapType(call.getResultingDescriptor().getReturnType().getArguments().get(0).getType()));
continue;
}
if(IntrinsicMethods.KOTLIN_ARRAYS_ARRAY.equals(value)) {
AnnotationVisitor visitor = annotationVisitor.visitArray(keyName);
VarargValueArgument next = (VarargValueArgument)call.getValueArguments().values().iterator().next();
for (ValueArgument argument : next.getArguments()) {
CompileTimeConstant<?> constant =
bindingContext.get(BindingContext.COMPILE_TIME_VALUE, argument.getArgumentExpression());
visitor.visit(null, constant.getValue());
}
visitor.visitEnd();
continue;
}
}
}
}
throw new IllegalStateException("Don't know how to compile annotation value");
}
}
@@ -29,6 +29,9 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
/**
* @author max
* @author yole
@@ -99,27 +102,33 @@ public abstract class ClassBodyCodegen {
functionCodegen.gen(declaration);
}
private void generatePrimaryConstructorProperties(PropertyCodegen propertyCodegen, PsiElement origin) {
private void generatePrimaryConstructorProperties(PropertyCodegen propertyCodegen, JetClassOrObject origin) {
boolean isAnnotation = origin instanceof JetClass && ((JetClass)origin).isAnnotation();
OwnerKind kind = context.getContextKind();
for (JetParameter p : getPrimaryConstructorParameters()) {
if (p.getValOrVarNode() != null) {
PropertyDescriptor propertyDescriptor = state.getBindingContext().get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, p);
if (propertyDescriptor != null) {
propertyCodegen.generateDefaultGetter(propertyDescriptor, Opcodes.ACC_PUBLIC, p);
if (propertyDescriptor.isVar()) {
propertyCodegen.generateDefaultSetter(propertyDescriptor, Opcodes.ACC_PUBLIC, origin);
}
if(!isAnnotation) {
propertyCodegen.generateDefaultGetter(propertyDescriptor, ACC_PUBLIC, p);
if (propertyDescriptor.isVar()) {
propertyCodegen.generateDefaultSetter(propertyDescriptor, ACC_PUBLIC, origin);
}
//noinspection ConstantConditions
if (!(kind instanceof OwnerKind.DelegateKind) && state.getBindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor)) {
int modifiers = JetTypeMapper.getAccessModifiers(propertyDescriptor, 0);
if (!propertyDescriptor.isVar()) {
modifiers |= Opcodes.ACC_FINAL;
//noinspection ConstantConditions
if (!(kind instanceof OwnerKind.DelegateKind) && state.getBindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor)) {
int modifiers = JetTypeMapper.getAccessModifiers(propertyDescriptor, 0);
if (!propertyDescriptor.isVar()) {
modifiers |= Opcodes.ACC_FINAL;
}
if(state.getInjector().getJetStandardLibrary().isVolatile(propertyDescriptor)) {
modifiers |= Opcodes.ACC_VOLATILE;
}
v.newField(p, modifiers, p.getName(), state.getInjector().getJetTypeMapper().mapType(propertyDescriptor.getType()).getDescriptor(), null, null);
}
if(state.getInjector().getJetStandardLibrary().isVolatile(propertyDescriptor)) {
modifiers |= Opcodes.ACC_VOLATILE;
}
v.newField(p, modifiers, p.getName(), state.getInjector().getJetTypeMapper().mapType(propertyDescriptor.getType()).getDescriptor(), null, null);
}
else {
v.newMethod(p, ACC_PUBLIC|ACC_ABSTRACT, p.getName(), "()" + state.getInjector().getJetTypeMapper().mapType(propertyDescriptor.getType()).getDescriptor(), null, null);
}
}
}
@@ -135,7 +144,7 @@ public abstract class ClassBodyCodegen {
private void generateStaticInitializer() {
if (staticInitializerChunks.size() > 0) {
final MethodVisitor mv = v.newMethod(null, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,"<clinit>", "()V", null, null);
final MethodVisitor mv = v.newMethod(null, ACC_PUBLIC | Opcodes.ACC_STATIC,"<clinit>", "()V", null, null);
if (v.generateCode() == ClassBuilder.Mode.FULL) {
mv.visitCode();
@@ -72,7 +72,8 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
boolean isInterface = false;
boolean isFinal = false;
boolean isStatic = false;
boolean isAnnotation = false;
if(myClass instanceof JetClass) {
JetClass jetClass = (JetClass) myClass;
if (jetClass.hasModifier(JetTokens.ABSTRACT_KEYWORD))
@@ -81,6 +82,12 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
isAbstract = true;
isInterface = true;
}
if (jetClass.isAnnotation()) {
isAbstract = true;
isInterface = true;
isAnnotation = true;
signature.getInterfaces().add("java/lang/annotation/Annotation");
}
if (!jetClass.hasModifier(JetTokens.OPEN_KEYWORD) && !isAbstract) {
isFinal = true;
}
@@ -103,6 +110,9 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
if (isStatic) {
access |= ACC_STATIC;
}
if (isAnnotation) {
access |= ACC_ANNOTATION;
}
v.defineClass(myClass, V1_6,
access,
signature.getName(),
@@ -407,8 +417,14 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
}
protected void generatePrimaryConstructor() {
if(myClass instanceof JetClass && ((JetClass) myClass).isTrait())
return;
if(myClass instanceof JetClass) {
JetClass aClass = (JetClass)myClass;
if(aClass.isTrait())
return;
if(aClass.isAnnotation()) {
return;
}
}
ConstructorDescriptor constructorDescriptor = bindingContext.get(BindingContext.CONSTRUCTOR, myClass);
@@ -63,6 +63,9 @@ public class IntrinsicMethods {
public static final ArraySet ARRAY_SET = new ArraySet();
public static final ArrayGet ARRAY_GET = new ArrayGet();
public static final StringPlus STRING_PLUS = new StringPlus();
public static final String KOTLIN_JAVA_CLASS_FUNCTION = "kotlin.javaClass.function";
public static final String KOTLIN_ARRAYS_ARRAY = "kotlin.arrays.array";
public static final String KOTLIN_JAVA_CLASS_PROPERTY = "kotlin.javaClass.property";
private Project myProject;
private JetStandardLibrary myStdLib;
@@ -83,9 +86,9 @@ public class IntrinsicMethods {
@PostConstruct
public void init() {
namedMethods.put("kotlin.javaClass.function", new JavaClassFunction());
namedMethods.put("kotlin.javaClass.property", new JavaClassProperty());
namedMethods.put("kotlin.arrays.array", new JavaClassArray());
namedMethods.put(KOTLIN_JAVA_CLASS_FUNCTION, new JavaClassFunction());
namedMethods.put(KOTLIN_JAVA_CLASS_PROPERTY, new JavaClassProperty());
namedMethods.put(KOTLIN_ARRAYS_ARRAY, new JavaClassArray());
List<String> primitiveCastMethods = OperatorConventions.NUMBER_CONVERSIONS.asList();
for (String method : primitiveCastMethods) {
@@ -249,6 +252,21 @@ public class IntrinsicMethods {
return descriptor.getMemberScope(typeParameters);
}
public IntrinsicMethod isIntrinsicMethod(DeclarationDescriptor descriptor) {
List<AnnotationDescriptor> annotations = descriptor.getAnnotations();
if (annotations != null) {
for (AnnotationDescriptor annotation : annotations) {
if("Intrinsic".equals(annotation.getType().getConstructor().getDeclarationDescriptor().getName())) {
String value = (String) annotation.getValueArguments().get(0).getValue();
IntrinsicMethod intrinsicMethod = namedMethods.get(value);
if(intrinsicMethod != null)
return intrinsicMethod;
}
}
}
return null;
}
public IntrinsicMethod getIntrinsic(DeclarationDescriptor descriptor) {
IntrinsicMethod intrinsicMethod = myMethods.get(descriptor.getOriginal());
if(intrinsicMethod == null) {
@@ -143,6 +143,10 @@ public class JetClass extends JetTypeParameterListOwner
return findChildByType(JetTokens.TRAIT_KEYWORD) != null;
}
public boolean isAnnotation() {
return hasModifier(JetTokens.ANNOTATION_KEYWORD);
}
@Override
public IStubElementType getElementType() {
// TODO (stubs)
@@ -16,7 +16,13 @@
package org.jetbrains.jet.codegen;
import jet.JetObject;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class AnnotationGenTest extends CodegenTestCase {
@@ -70,4 +76,48 @@ public class AnnotationGenTest extends CodegenTestCase {
Deprecated annotation = (Deprecated) aClass.getAnnotation(Deprecated.class);
assertNotNull(annotation);
}
}
public void testSimplestAnnotationClass() throws NoSuchFieldException, NoSuchMethodException {
loadText("annotation class A");
Class aClass = generateClass("A");
Class[] interfaces = aClass.getInterfaces();
assertEquals(2, interfaces.length);
assertEquals(0, aClass.getDeclaredMethods().length);
assertTrue(Annotation.class == interfaces[0] || Annotation.class == interfaces[1]);
assertTrue(JetObject.class == interfaces[0] || JetObject.class == interfaces[1]);
assertTrue(aClass.isAnnotation());
}
public void testAnnotationClassWithStringProperty()
throws
NoSuchFieldException,
NoSuchMethodException,
ClassNotFoundException,
IllegalAccessException,
InstantiationException,
InvocationTargetException {
loadText("import java.lang.annotation.*\n" +
"" +
"Retention(RetentionPolicy.RUNTIME) annotation class A(val a: String)\n" +
"" +
"A(\"239\") class B()");
Class aClass = generateClass("A");
Retention annotation = (Retention)aClass.getAnnotation(Retention.class);
RetentionPolicy value = annotation.value();
assertEquals(RetentionPolicy.RUNTIME, value);
Method[] methods = aClass.getDeclaredMethods();
assertEquals(1, methods.length);
assertEquals("a", methods[0].getName());
assertEquals(String.class, methods[0].getReturnType());
assertEquals(0, methods[0].getParameterTypes().length);
assertTrue(aClass.isAnnotation());
Class<?> bClass = aClass.getClassLoader().loadClass("B");
Annotation bClassAnnotation = bClass.getAnnotation(aClass);
assertNotNull(bClassAnnotation);
assertEquals("239", methods[0].invoke(bClassAnnotation));
}
}
@@ -21,6 +21,8 @@ import org.jetbrains.jet.lang.psi.JetPsiUtil;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
@@ -164,4 +166,107 @@ public class StdlibTest extends CodegenTestCase {
loader.dispose();
}
}
public void testAnnotationClassWithClassProperty()
throws
NoSuchFieldException,
NoSuchMethodException,
ClassNotFoundException,
IllegalAccessException,
InstantiationException,
InvocationTargetException {
loadText("import java.lang.annotation.*\n" +
"" +
"Retention(RetentionPolicy.RUNTIME) annotation class A(val a: java.lang.Class<*>)\n" +
"" +
"A(javaClass<String>()) class B()");
Class aClass = generateClass("A");
Retention annotation = (Retention)aClass.getAnnotation(Retention.class);
RetentionPolicy value = annotation.value();
assertEquals(RetentionPolicy.RUNTIME, value);
Method[] methods = aClass.getDeclaredMethods();
assertEquals(1, methods.length);
assertEquals("a", methods[0].getName());
assertEquals(Class.class, methods[0].getReturnType());
assertEquals(0, methods[0].getParameterTypes().length);
assertTrue(aClass.isAnnotation());
Class<?> bClass = aClass.getClassLoader().loadClass("B");
Annotation bClassAnnotation = bClass.getAnnotation(aClass);
assertNotNull(bClassAnnotation);
assertEquals(String.class, methods[0].invoke(bClassAnnotation));
}
public void testAnnotationClassWithStringArrayProperty()
throws
NoSuchFieldException,
NoSuchMethodException,
ClassNotFoundException,
IllegalAccessException,
InstantiationException,
InvocationTargetException {
loadText("import java.lang.annotation.*\n" +
"" +
"Retention(RetentionPolicy.RUNTIME) annotation class A(val a: Array<String>)\n" +
"" +
"A(array(\"239\",\"932\")) class B()");
Class aClass = generateClass("A");
Retention annotation = (Retention)aClass.getAnnotation(Retention.class);
RetentionPolicy value = annotation.value();
assertEquals(RetentionPolicy.RUNTIME, value);
Method[] methods = aClass.getDeclaredMethods();
assertEquals(1, methods.length);
assertEquals("a", methods[0].getName());
assertEquals(String[].class, methods[0].getReturnType());
assertEquals(0, methods[0].getParameterTypes().length);
assertTrue(aClass.isAnnotation());
Class<?> bClass = aClass.getClassLoader().loadClass("B");
Annotation bClassAnnotation = bClass.getAnnotation(aClass);
assertNotNull(bClassAnnotation);
Object invoke = methods[0].invoke(bClassAnnotation);
assertEquals("239", ((String[])invoke)[0]);
assertEquals("932", ((String[])invoke)[1]);
}
public void testAnnotationClassWithIntArrayProperty()
throws
NoSuchFieldException,
NoSuchMethodException,
ClassNotFoundException,
IllegalAccessException,
InstantiationException,
InvocationTargetException {
loadText("import java.lang.annotation.*\n" +
"" +
"Retention(RetentionPolicy.RUNTIME) annotation class A(val a: IntArray)\n" +
"" +
"A(intArray(239,932)) class B()");
Class aClass = generateClass("A");
Retention annotation = (Retention)aClass.getAnnotation(Retention.class);
RetentionPolicy value = annotation.value();
assertEquals(RetentionPolicy.RUNTIME, value);
Method[] methods = aClass.getDeclaredMethods();
assertEquals(1, methods.length);
assertEquals("a", methods[0].getName());
assertEquals(int[].class, methods[0].getReturnType());
assertEquals(0, methods[0].getParameterTypes().length);
assertTrue(aClass.isAnnotation());
Class<?> bClass = aClass.getClassLoader().loadClass("B");
Annotation bClassAnnotation = bClass.getAnnotation(aClass);
assertNotNull(bClassAnnotation);
Object invoke = methods[0].invoke(bClassAnnotation);
assertEquals(239, ((int[])invoke)[0]);
assertEquals(932, ((int[])invoke)[1]);
}
}