Rewrite AnnotationCodegen to generate annotation arguments by AnnotationDescriptor

This commit is contained in:
Natalia.Ukhorskaya
2013-07-04 18:57:01 +04:00
parent b6bdcb303d
commit 728e08cc49
3 changed files with 152 additions and 104 deletions
@@ -17,25 +17,26 @@
package org.jetbrains.jet.codegen;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.AnnotationVisitor;
import org.jetbrains.asm4.ClassVisitor;
import org.jetbrains.asm4.FieldVisitor;
import org.jetbrains.asm4.MethodVisitor;
import org.jetbrains.jet.codegen.intrinsics.IntrinsicMethods;
import org.jetbrains.jet.codegen.state.JetTypeMapper;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.annotations.Annotated;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationArgumentVisitor;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.psi.JetAnnotationEntry;
import org.jetbrains.jet.lang.psi.JetClass;
import org.jetbrains.jet.lang.psi.JetModifierList;
import org.jetbrains.jet.lang.psi.JetModifierListOwner;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.calls.model.DefaultValueArgument;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument;
import org.jetbrains.jet.lang.resolve.calls.model.VarargValueArgument;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.resolve.constants.*;
import org.jetbrains.jet.lang.resolve.constants.StringValue;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
@@ -84,124 +85,136 @@ public abstract class AnnotationCodegen {
AnnotationDescriptor annotationDescriptor = bindingContext.get(BindingContext.ANNOTATION, annotationEntry);
if (annotationDescriptor == null) continue; // Skipping annotations if they are not resolved. Needed for JetLightClass generation
JetType type = annotationDescriptor.getType();
genAnnotation(resolvedCall, type);
genAnnotation(annotationDescriptor);
}
}
private void genAnnotation(
ResolvedCall<? extends CallableDescriptor> resolvedCall,
JetType type
) {
ClassifierDescriptor classifierDescriptor = type.getConstructor().getDeclarationDescriptor();
private void genAnnotation(AnnotationDescriptor annotationDescriptor) {
ClassifierDescriptor classifierDescriptor = annotationDescriptor.getType().getConstructor().getDeclarationDescriptor();
RetentionPolicy rp = getRetentionPolicy(classifierDescriptor, typeMapper);
if (rp == RetentionPolicy.SOURCE) {
return;
}
String internalName = typeMapper.mapType(type).getDescriptor();
String internalName = typeMapper.mapType(annotationDescriptor.getType()).getDescriptor();
AnnotationVisitor annotationVisitor = visitAnnotation(internalName, rp == RetentionPolicy.RUNTIME);
getAnnotation(resolvedCall, annotationVisitor);
genAnnotationArguments(annotationDescriptor, annotationVisitor);
annotationVisitor.visitEnd();
}
private void getAnnotation(ResolvedCall<? extends CallableDescriptor> resolvedCall, AnnotationVisitor annotationVisitor) {
for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> entry : resolvedCall.getValueArguments().entrySet()) {
ResolvedValueArgument valueArgument = entry.getValue();
if (valueArgument instanceof DefaultValueArgument) {
continue;
}
Name keyName = entry.getKey().getName();
genAnnotationValueArgument(annotationVisitor, valueArgument, keyName.asString());
private void genAnnotationArguments(AnnotationDescriptor annotationDescriptor, AnnotationVisitor annotationVisitor) {
for (Map.Entry<ValueParameterDescriptor, CompileTimeConstant<?>> entry : annotationDescriptor.getAllValueArguments().entrySet()) {
ValueParameterDescriptor descriptor = entry.getKey();
String name = descriptor.getName().asString();
genAnnotationArgument(name, entry.getValue(), annotationVisitor);
}
}
private void genAnnotationValueArgument(AnnotationVisitor annotationVisitor, ResolvedValueArgument valueArgument, String keyName) {
List<ValueArgument> valueArguments = valueArgument.getArguments();
if (valueArgument instanceof VarargValueArgument) {
AnnotationVisitor visitor = annotationVisitor.visitArray(keyName);
for (ValueArgument argument : valueArguments) {
genAnnotationExpressionValue(visitor, null, argument.getArgumentExpression());
private void genAnnotationArgument(
@Nullable final String name,
@NotNull CompileTimeConstant<?> value,
@NotNull final AnnotationVisitor annotationVisitor
) {
AnnotationArgumentVisitor argumentVisitor = new AnnotationArgumentVisitor<Void, Void>() {
@Override
public Void visitLongValue(@NotNull LongValue value, Void data) {
return visitSimpleValue(value);
}
visitor.visitEnd();
}
else {
assert valueArguments.size() == 1 : "Number of arguments on " + keyName + " = " + valueArguments.size(); // todo
JetExpression expression = valueArguments.get(0).getArgumentExpression();
genAnnotationExpressionValue(annotationVisitor, keyName, expression);
}
}
private void genAnnotationExpressionValue(AnnotationVisitor annotationVisitor, @Nullable String keyName, JetExpression expression) {
CompileTimeConstant<?> compileTimeConstant =
bindingContext.get(BindingContext.COMPILE_TIME_VALUE, expression);
@Override
public Void visitIntValue(IntValue value, Void data) {
return visitSimpleValue(value);
}
if (compileTimeConstant != null) {
Object value = compileTimeConstant.getValue();
annotationVisitor.visit(keyName, value);
return;
}
@Override
public Void visitShortValue(ShortValue value, Void data) {
return visitSimpleValue(value);
}
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).getDescriptor(), descriptor.getName().asString());
return;
@Override
public Void visitByteValue(ByteValue value, Void data) {
return visitSimpleValue(value);
}
@Override
public Void visitDoubleValue(DoubleValue value, Void data) {
return visitSimpleValue(value);
}
@Override
public Void visitFloatValue(FloatValue value, Void data) {
return visitSimpleValue(value);
}
@Override
public Void visitBooleanValue(BooleanValue value, Void data) {
return visitSimpleValue(value);
}
@Override
public Void visitCharValue(CharValue value, Void data) {
return visitSimpleValue(value);
}
@Override
public Void visitStringValue(StringValue value, Void data) {
return visitSimpleValue(value);
}
@Override
public Void visitEnumValue(EnumValue value, Void data) {
String propertyName = value.getValue().getName().asString();
annotationVisitor.visitEnum(name, typeMapper.mapType(value.getType(KotlinBuiltIns.getInstance())).getDescriptor(), propertyName);
return null;
}
@Override
public Void visitArrayValue(ArrayValue value, Void data) {
AnnotationVisitor visitor = annotationVisitor.visitArray(name);
for (CompileTimeConstant<?> argument : value.getValue()) {
genAnnotationArgument(null, argument, visitor);
}
visitor.visitEnd();
return null;
}
}
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) {
//noinspection ConstantConditions
if ("Intrinsic".equals(annotation.getType().getConstructor().getDeclarationDescriptor().getName().asString())) {
value = (String) annotation.getAllValueArguments().values().iterator().next().getValue();
break;
}
}
}
if (IntrinsicMethods.KOTLIN_JAVA_CLASS_FUNCTION.equals(value)) {
//noinspection ConstantConditions
annotationVisitor.visit(keyName, typeMapper
.mapType(call.getResultingDescriptor().getReturnType().getArguments().get(0).getType()));
return;
}
else 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()) {
genAnnotationExpressionValue(visitor, null, argument.getArgumentExpression());
}
visitor.visitEnd();
return;
}
else if (call.getResultingDescriptor() instanceof ConstructorDescriptor) {
ConstructorDescriptor descriptor = (ConstructorDescriptor) call.getResultingDescriptor();
AnnotationVisitor visitor = annotationVisitor.visitAnnotation(keyName, typeMapper
.mapType(descriptor.getContainingDeclaration()).getDescriptor());
getAnnotation(call, visitor);
visitor.visitEnd();
return;
}
}
}
}
throw new IllegalStateException("Don't know how to compile annotation value");
@Override
public Void visitAnnotationValue(AnnotationValue value, Void data) {
String internalAnnName = typeMapper.mapType(value.getValue().getType()).getDescriptor();
AnnotationVisitor visitor = annotationVisitor.visitAnnotation(name, internalAnnName);
genAnnotationArguments(value.getValue(), visitor);
visitor.visitEnd();
return null;
}
@Override
public Void visitJavaClassValue(JavaClassValue value, Void data) {
annotationVisitor.visit(name, typeMapper.mapType(value.getValue()));
return null;
}
private Void visitSimpleValue(CompileTimeConstant value) {
annotationVisitor.visit(name, value.getValue());
return null;
}
@Override
public Void visitErrorValue(ErrorValue value, Void data) {
return visitUnsupportedValue(value);
}
@Override
public Void visitNullValue(NullValue value, Void data) {
return visitUnsupportedValue(value);
}
private Void visitUnsupportedValue(CompileTimeConstant value) {
throw new IllegalStateException("Don't know how to compile annotation value " + value);
}
};
value.accept(argumentVisitor, null);
}
private static RetentionPolicy getRetentionPolicy(ClassifierDescriptor descriptor, JetTypeMapper typeMapper) {
@@ -0,0 +1,21 @@
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
Retention(RetentionPolicy.RUNTIME)
annotation class Ann(val s: String, vararg val p: Int)
Ann("str", 1, 2, 3) class MyClass
fun box(): String {
val ann = javaClass<MyClass>().getAnnotation(javaClass<Ann>())
if (ann == null) return "fail: cannot find Ann on MyClass"
if (ann.s != "str") return "fail: annotation parameter s should be \"str\""
if (ann.p[0] != 1) return "fail: annotation parameter p[0] should be 1"
if (ann.p[1] != 2) return "fail: annotation parameter p[1] should be 2"
if (ann.p[2] != 3) return "fail: annotation parameter p[2] should be 3"
return "OK"
}
fun main(args: Array<String>) {
println(box())
}
@@ -31,12 +31,25 @@ import org.jetbrains.jet.codegen.generated.AbstractBlackBoxCodegenTest;
/** This class is generated by {@link org.jetbrains.jet.generators.tests.GenerateTests}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("compiler/testData/codegen/boxWithStdlib")
@InnerTestClasses({BlackBoxWithStdlibCodegenTestGenerated.Casts.class, BlackBoxWithStdlibCodegenTestGenerated.DataClasses.class, BlackBoxWithStdlibCodegenTestGenerated.FullJdk.class, BlackBoxWithStdlibCodegenTestGenerated.JdkAnnotations.class, BlackBoxWithStdlibCodegenTestGenerated.Ranges.class, BlackBoxWithStdlibCodegenTestGenerated.Regressions.class, BlackBoxWithStdlibCodegenTestGenerated.Strings.class})
@InnerTestClasses({BlackBoxWithStdlibCodegenTestGenerated.Annotations.class, BlackBoxWithStdlibCodegenTestGenerated.Casts.class, BlackBoxWithStdlibCodegenTestGenerated.DataClasses.class, BlackBoxWithStdlibCodegenTestGenerated.FullJdk.class, BlackBoxWithStdlibCodegenTestGenerated.JdkAnnotations.class, BlackBoxWithStdlibCodegenTestGenerated.Ranges.class, BlackBoxWithStdlibCodegenTestGenerated.Regressions.class, BlackBoxWithStdlibCodegenTestGenerated.Strings.class})
public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInBoxWithStdlib() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/codegen/boxWithStdlib"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("compiler/testData/codegen/boxWithStdlib/annotations")
public static class Annotations extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInAnnotations() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/codegen/boxWithStdlib/annotations"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("varargInAnnotationParameter.kt")
public void testVarargInAnnotationParameter() throws Exception {
doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/annotations/varargInAnnotationParameter.kt");
}
}
@TestMetadata("compiler/testData/codegen/boxWithStdlib/casts")
public static class Casts extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInCasts() throws Exception {
@@ -882,6 +895,7 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
public static Test suite() {
TestSuite suite = new TestSuite("BlackBoxWithStdlibCodegenTestGenerated");
suite.addTestSuite(BlackBoxWithStdlibCodegenTestGenerated.class);
suite.addTestSuite(Annotations.class);
suite.addTestSuite(Casts.class);
suite.addTest(DataClasses.innerSuite());
suite.addTestSuite(FullJdk.class);