Skip to content

Improved annotations DSL to support simple array creation, enums, annotation-type values #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions src/main/java/me/qmx/jitescript/AnnotationArrayValue.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package me.qmx.jitescript;

import static me.qmx.jitescript.util.CodegenUtils.ci;

import org.objectweb.asm.AnnotationVisitor;

public class AnnotationArrayValue {
Expand All @@ -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;
}
}
62 changes: 62 additions & 0 deletions src/main/java/me/qmx/jitescript/AnnotationData.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
10 changes: 5 additions & 5 deletions src/main/java/me/qmx/jitescript/CodeBlock.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class CodeBlock implements Opcodes {
private InsnList instructionList = new InsnList();
private List<TryCatchBlockNode> tryCatchBlockList = new ArrayList<TryCatchBlockNode>();
private List<LocalVariableNode> localVariableList = new ArrayList<LocalVariableNode>();
private List<VisibleAnnotation> annotations = new ArrayList<VisibleAnnotation>();
private List<AnnotationData> annotations = new ArrayList<AnnotationData>();
private int arity = 0;
private boolean returns = false;

Expand Down Expand Up @@ -1084,7 +1084,7 @@ public List<LocalVariableNode> getLocalVariableList() {
return localVariableList;
}

public List<VisibleAnnotation> getAnnotations() {
public List<AnnotationData> getAnnotations() {
return annotations;
}

Expand Down Expand Up @@ -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;
}
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/me/qmx/jitescript/FieldDefinition.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,26 @@ public class FieldDefinition {
private final int modifiers;
private final String signature;
private final Object value;
private final List<VisibleAnnotation> annotations;
private final List<AnnotationData> 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<VisibleAnnotation>();
this.annotations = new ArrayList<AnnotationData>();
}

public FieldNode getFieldNode() {
FieldNode node = new FieldNode(modifiers, fieldName, signature, null, value);
node.visibleAnnotations = new ArrayList<VisibleAnnotation>();
for (VisibleAnnotation annotation : annotations) {
node.visibleAnnotations = new ArrayList<AnnotationData>();
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;
}
Expand Down
17 changes: 14 additions & 3 deletions src/main/java/me/qmx/jitescript/JiteClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -37,7 +38,7 @@ public class JiteClass implements Opcodes {
private final List<MethodDefinition> methods = new ArrayList<MethodDefinition>();
private final List<FieldDefinition> fields = new ArrayList<FieldDefinition>();
private final List<String> interfaces = new ArrayList<String>();
private final List<VisibleAnnotation> annotations = new ArrayList<VisibleAnnotation>();
private final List<AnnotationData> annotations = new ArrayList<AnnotationData>();
private final List<ChildEntry> childClasses = new ArrayList<ChildEntry>();
private final String className;
private final String superClassName;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -226,7 +237,7 @@ public byte[] toBytes(JDKVersion version) {
node.visibleAnnotations = new ArrayList<AnnotationNode>();
}

for (VisibleAnnotation a : annotations) {
for (AnnotationData a : annotations) {
node.visibleAnnotations.add(a.getNode());
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/me/qmx/jitescript/MethodDefinition.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
43 changes: 37 additions & 6 deletions src/test/java/me/qmx/jitescript/AnnotationsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}};
Expand All @@ -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);
}};
Expand All @@ -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);
}};
Expand All @@ -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);
}};
Expand All @@ -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!");
}});
}};

Expand All @@ -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);
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
Expand Down