diff --git a/src/main/java/me/qmx/jitescript/AnnotationArrayValue.java b/src/main/java/me/qmx/jitescript/AnnotationArrayValue.java index 5ddc2a4..04f3e30 100644 --- a/src/main/java/me/qmx/jitescript/AnnotationArrayValue.java +++ b/src/main/java/me/qmx/jitescript/AnnotationArrayValue.java @@ -1,5 +1,7 @@ package me.qmx.jitescript; +import static me.qmx.jitescript.util.CodegenUtils.ci; + import org.objectweb.asm.AnnotationVisitor; public class AnnotationArrayValue { @@ -12,7 +14,22 @@ public AnnotationArrayValue(String name, AnnotationVisitor node) { this.node = node; } - public void add(Object value) { - node.visit(name, value); + public AnnotationArrayValue add(Object value) { + if (value instanceof AnnotationData) { + add(((AnnotationData) value).getNode()); + } else { + node.visit(name, value); + } + return this; + } + + public AnnotationArrayValue addEnum(Enum value) { + addEnum(ci(value.getDeclaringClass()), value.name()); + return this; + } + + public AnnotationArrayValue addEnum(String desc, String value) { + node.visitEnum(null, desc, value); + return this; } } diff --git a/src/main/java/me/qmx/jitescript/AnnotationData.java b/src/main/java/me/qmx/jitescript/AnnotationData.java new file mode 100644 index 0000000..a61386f --- /dev/null +++ b/src/main/java/me/qmx/jitescript/AnnotationData.java @@ -0,0 +1,62 @@ +package me.qmx.jitescript; + +import static me.qmx.jitescript.util.CodegenUtils.ci; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.tree.AnnotationNode; + +public class AnnotationData { + + private final AnnotationVisitor node; + + public AnnotationData(Class type) { + this(ci(type)); + } + + public AnnotationData(String desc) { + this.node = new AnnotationNode(desc); + } + + private AnnotationData(AnnotationVisitor node) { + this.node = node; + } + + public AnnotationNode getNode() { + return (AnnotationNode) node; + } + + public AnnotationData value(String name, Object value) { + node.visit(name, value); + return this; + } + + public AnnotationData enumValue(String name, Enum value) { + enumValue(name, ci(value.getClass()), value.name()); + return this; + } + + public AnnotationData enumValue(String name, String desc, String value) { + node.visitEnum(name, desc, value); + return this; + } + + public AnnotationData annotationValue(String name, Class type) { + return annotationValue(name, ci(type)); + } + + public AnnotationData annotationValue(String name, String desc) { + return new AnnotationData(node.visitAnnotation(name, desc)); + } + + public AnnotationArrayValue arrayValue(String name) { + return new AnnotationArrayValue(name, node.visitArray(name)); + } + + public AnnotationArrayValue arrayValue(String name, Object... values) { + AnnotationArrayValue array = arrayValue(name); + for (Object value : values) { + array.add(value); + } + return array; + } +} diff --git a/src/main/java/me/qmx/jitescript/CodeBlock.java b/src/main/java/me/qmx/jitescript/CodeBlock.java index e50b413..f1e3728 100644 --- a/src/main/java/me/qmx/jitescript/CodeBlock.java +++ b/src/main/java/me/qmx/jitescript/CodeBlock.java @@ -37,7 +37,7 @@ public class CodeBlock implements Opcodes { private InsnList instructionList = new InsnList(); private List tryCatchBlockList = new ArrayList(); private List localVariableList = new ArrayList(); - private List annotations = new ArrayList(); + private List annotations = new ArrayList(); private int arity = 0; private boolean returns = false; @@ -1084,7 +1084,7 @@ public List getLocalVariableList() { return localVariableList; } - public List getAnnotations() { + public List getAnnotations() { return annotations; } @@ -1125,13 +1125,13 @@ public CodeBlock append(CodeBlock codeBlock) { return this; } - public VisibleAnnotation annotation(Class type) { - VisibleAnnotation annotation = new VisibleAnnotation(ci(type)); + public AnnotationData annotate(Class type) { + AnnotationData annotation = new AnnotationData(ci(type)); addAnnotation(annotation); return annotation; } - public CodeBlock addAnnotation(VisibleAnnotation annotation) { + public CodeBlock addAnnotation(AnnotationData annotation) { annotations.add(annotation); return this; } diff --git a/src/main/java/me/qmx/jitescript/FieldDefinition.java b/src/main/java/me/qmx/jitescript/FieldDefinition.java index d9fee39..ab73127 100644 --- a/src/main/java/me/qmx/jitescript/FieldDefinition.java +++ b/src/main/java/me/qmx/jitescript/FieldDefinition.java @@ -10,26 +10,26 @@ public class FieldDefinition { private final int modifiers; private final String signature; private final Object value; - private final List annotations; + private final List annotations; public FieldDefinition(String fieldName, int modifiers, String signature, Object value) { this.fieldName = fieldName; this.modifiers = modifiers; this.signature = signature; this.value = value; - this.annotations = new ArrayList(); + this.annotations = new ArrayList(); } public FieldNode getFieldNode() { FieldNode node = new FieldNode(modifiers, fieldName, signature, null, value); - node.visibleAnnotations = new ArrayList(); - for (VisibleAnnotation annotation : annotations) { + node.visibleAnnotations = new ArrayList(); + for (AnnotationData annotation : annotations) { node.visibleAnnotations.add(annotation.getNode()); } return node; } - public FieldDefinition addAnnotation(VisibleAnnotation annotation) { + public FieldDefinition addAnnotation(AnnotationData annotation) { annotations.add(annotation); return this; } diff --git a/src/main/java/me/qmx/jitescript/JiteClass.java b/src/main/java/me/qmx/jitescript/JiteClass.java index a70e6a3..e66645f 100644 --- a/src/main/java/me/qmx/jitescript/JiteClass.java +++ b/src/main/java/me/qmx/jitescript/JiteClass.java @@ -16,6 +16,7 @@ package me.qmx.jitescript; import static me.qmx.jitescript.CodeBlock.newCodeBlock; +import static me.qmx.jitescript.util.CodegenUtils.ci; import static me.qmx.jitescript.util.CodegenUtils.p; import static me.qmx.jitescript.util.CodegenUtils.sig; @@ -37,7 +38,7 @@ public class JiteClass implements Opcodes { private final List methods = new ArrayList(); private final List fields = new ArrayList(); private final List interfaces = new ArrayList(); - private final List annotations = new ArrayList(); + private final List annotations = new ArrayList(); private final List childClasses = new ArrayList(); private final String className; private final String superClassName; @@ -80,6 +81,16 @@ public JiteClass(String className, String superClassName, String[] interfaces) { } } + public AnnotationData annotate(Class annotationType) { + return annotate(ci(annotationType)); + } + + private AnnotationData annotate(String typeName) { + AnnotationData annotation = new AnnotationData(typeName); + annotations.add(annotation); + return annotation; + } + public int getAccess() { return access; } @@ -183,7 +194,7 @@ public byte[] toBytes() { return toBytes(JDKVersion.V1_6); } - public void addAnnotation(VisibleAnnotation annotation) { + public void addAnnotation(AnnotationData annotation) { annotations.add(annotation); } @@ -226,7 +237,7 @@ public byte[] toBytes(JDKVersion version) { node.visibleAnnotations = new ArrayList(); } - for (VisibleAnnotation a : annotations) { + for (AnnotationData a : annotations) { node.visibleAnnotations.add(a.getNode()); } diff --git a/src/main/java/me/qmx/jitescript/MethodDefinition.java b/src/main/java/me/qmx/jitescript/MethodDefinition.java index e88fb61..95b12ab 100644 --- a/src/main/java/me/qmx/jitescript/MethodDefinition.java +++ b/src/main/java/me/qmx/jitescript/MethodDefinition.java @@ -63,7 +63,7 @@ public MethodNode getMethodNode() { for (LocalVariableNode localVariableNode : getMethodBody().getLocalVariableList()) { method.localVariables.add(localVariableNode); } - for (VisibleAnnotation annotation : methodBody.getAnnotations()) { + for (AnnotationData annotation : methodBody.getAnnotations()) { method.visibleAnnotations.add(annotation.getNode()); } return method; diff --git a/src/test/java/me/qmx/jitescript/AnnotationsTest.java b/src/test/java/me/qmx/jitescript/AnnotationsTest.java index 26194ce..ca7a79f 100644 --- a/src/test/java/me/qmx/jitescript/AnnotationsTest.java +++ b/src/test/java/me/qmx/jitescript/AnnotationsTest.java @@ -11,6 +11,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.lang.reflect.Field; @@ -25,7 +26,7 @@ public class AnnotationsTest { public void testAnnotationWithScalarValue() throws Exception { JiteClass jiteClass = new JiteClass("AnnotatedClass", p(Object.class), new String[0]) {{ defineDefaultConstructor(); - VisibleAnnotation annotation = new VisibleAnnotation(ScalarAnnotation.class); + AnnotationData annotation = new AnnotationData(ScalarAnnotation.class); annotation.value("breakfastItem", "Waffles!"); addAnnotation(annotation); }}; @@ -41,7 +42,7 @@ public void testAnnotationWithScalarValue() throws Exception { public void testAnnotationWithArrayValue() throws Exception { JiteClass jiteClass = new JiteClass("AnnotatedClass", p(Object.class), new String[0]) {{ defineDefaultConstructor(); - VisibleAnnotation annotation = new VisibleAnnotation(AnnotationWithArray.class); + AnnotationData annotation = new AnnotationData(AnnotationWithArray.class); annotation.arrayValue("favoriteColors", "pink", "purple", "green"); addAnnotation(annotation); }}; @@ -57,7 +58,7 @@ public void testAnnotationWithArrayValue() throws Exception { public void testAnnotationWithAnnotationValue() throws Exception { JiteClass jiteClass = new JiteClass("AnnotatedClass", p(Object.class), new String[0]) {{ defineDefaultConstructor(); - VisibleAnnotation annotation = new VisibleAnnotation(AnnotationWithAnnotation.class); + AnnotationData annotation = new AnnotationData(AnnotationWithAnnotation.class); annotation.annotationValue("element", ScalarAnnotation.class).value("breakfastItem", "Pancakes!"); addAnnotation(annotation); }}; @@ -73,7 +74,7 @@ public void testAnnotationWithAnnotationValue() throws Exception { public void testAnnotationWithEnumValue() throws Exception { JiteClass jiteClass = new JiteClass("AnnotatedClass", p(Object.class), new String[0]) {{ defineDefaultConstructor(); - VisibleAnnotation annotation = new VisibleAnnotation(AnnotationWithEnum.class); + AnnotationData annotation = new AnnotationData(AnnotationWithEnum.class); annotation.enumValue("color", Colors.PINK); addAnnotation(annotation); }}; @@ -92,7 +93,7 @@ public void testAnnotatedMethod() throws Exception { defineMethod("annotatedMethod", ACC_PUBLIC, sig(String.class), new CodeBlock() {{ ldc("Sausages!"); areturn(); - annotation(ScalarAnnotation.class).value("breakfastItem", "Sausages!"); + annotate(ScalarAnnotation.class).value("breakfastItem", "Sausages!"); }}); }}; @@ -109,7 +110,7 @@ public void testAnnotatedField() throws Exception { JiteClass jiteClass = new JiteClass("AnnotatedClass", p(Object.class), new String[0]) {{ defineDefaultConstructor(); defineField("annotatedField", ACC_PUBLIC, ci(String.class), null) - .addAnnotation(new VisibleAnnotation(ScalarAnnotation.class).value("breakfastItem", "Toast!")); + .addAnnotation(new AnnotationData(ScalarAnnotation.class).value("breakfastItem", "Toast!")); }}; Class clazz = new DynamicClassLoader().define(jiteClass); @@ -120,6 +121,21 @@ public void testAnnotatedField() throws Exception { assertEquals(annotation.breakfastItem(), "Toast!"); } + @Test + public void testAnnotationsArrayValue() throws Exception { + JiteClass jiteClass = new JiteClass("ClassWithRepeatedAnnotations"); + jiteClass.defineDefaultConstructor(); + jiteClass.annotate(Container.class).arrayValue("value", + new AnnotationData(Entry.class).value("name", "Apples"), + new AnnotationData(Entry.class).value("name", "Oranges") + ); + Class clazz = new DynamicClassLoader().define(jiteClass); + assertTrue(clazz.isAnnotationPresent(Container.class)); + assertEquals(clazz.getAnnotationsByType(Entry.class).length, 2); + assertEquals(clazz.getAnnotationsByType(Entry.class)[0].name(), "Apples"); + assertEquals(clazz.getAnnotationsByType(Entry.class)[1].name(), "Oranges"); + } + @Target({ TYPE, METHOD, FIELD }) @Retention(RUNTIME) public @interface ScalarAnnotation { @@ -148,6 +164,21 @@ public void testAnnotatedField() throws Exception { Colors color(); } + @Target(TYPE) + @Retention(RUNTIME) + @Repeatable(Container.class) + public @interface Entry { + + String name(); + } + + @Target(TYPE) + @Retention(RUNTIME) + public @interface Container { + + Entry[] value(); + } + public enum Colors { PINK, GREEN, PURPLE }