First working version of annotation support

This commit is contained in:
Alexey Andreev 2015-06-18 13:47:16 +04:00
parent fd689d2efa
commit 32feb24d0f
9 changed files with 103 additions and 29 deletions

View File

@ -15,6 +15,8 @@
*/ */
package org.teavm.classlib.java.lang; package org.teavm.classlib.java.lang;
import java.util.HashMap;
import java.util.Map;
import org.teavm.classlib.impl.DeclaringClassMetadataGenerator; import org.teavm.classlib.impl.DeclaringClassMetadataGenerator;
import org.teavm.classlib.java.lang.annotation.TAnnotation; import org.teavm.classlib.java.lang.annotation.TAnnotation;
import org.teavm.classlib.java.lang.reflect.TAnnotatedElement; import org.teavm.classlib.java.lang.reflect.TAnnotatedElement;
@ -33,6 +35,8 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
private TClass<?> componentType; private TClass<?> componentType;
private boolean componentTypeDirty = true; private boolean componentTypeDirty = true;
private PlatformClass platformClass; private PlatformClass platformClass;
private TAnnotation[] annotationsCache;
private Map<TClass<?>, TAnnotation> annotationsByType;
private TClass(PlatformClass platformClass) { private TClass(PlatformClass platformClass) {
this.platformClass = platformClass; this.platformClass = platformClass;
@ -203,21 +207,37 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
@Override @Override
public boolean isAnnotationPresent(TClass<? extends TAnnotation> annotationClass) { public boolean isAnnotationPresent(TClass<? extends TAnnotation> annotationClass) {
return false; ensureAnnotationsByType();
return annotationsByType.containsKey(annotationClass);
} }
@SuppressWarnings("unchecked")
@Override @Override
public <S extends TAnnotation> S getAnnotation(TClass<S> annotationClass) { public <S extends TAnnotation> S getAnnotation(TClass<S> annotationClass) {
return null; ensureAnnotationsByType();
return (S)annotationsByType.get(annotationClass);
} }
@Override @Override
public TAnnotation[] getAnnotations() { public TAnnotation[] getAnnotations() {
return null; if (annotationsCache == null) {
annotationsCache = (TAnnotation[])Platform.getAnnotations(getPlatformClass());
}
return annotationsCache.clone();
} }
@Override @Override
public TAnnotation[] getDeclaredAnnotations() { public TAnnotation[] getDeclaredAnnotations() {
return null; return getAnnotations();
}
private void ensureAnnotationsByType() {
if (annotationsByType != null) {
return;
}
annotationsByType = new HashMap<>();
for (TAnnotation annot : getAnnotations()) {
annotationsByType.put((TClass<?>)(Object)annot.getClass(), annot);
}
} }
} }

View File

@ -34,6 +34,7 @@ import org.teavm.model.MethodReference;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.model.emit.ProgramEmitter; import org.teavm.model.emit.ProgramEmitter;
import org.teavm.model.emit.ValueEmitter; import org.teavm.model.emit.ValueEmitter;
import org.teavm.model.instructions.ArrayElementType;
/** /**
* *
@ -42,7 +43,7 @@ import org.teavm.model.emit.ValueEmitter;
public class AnnotationClassTransformer implements ClassHolderTransformer { public class AnnotationClassTransformer implements ClassHolderTransformer {
@Override @Override
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) { public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
MethodHolder readerMethod = new MethodHolder("$$_readAnnotations_$$", ValueType.parse(Annotation[].class)); MethodHolder readerMethod = new MethodHolder("$$__readAnnotations__$$", ValueType.parse(Annotation[].class));
readerMethod.setLevel(AccessLevel.PUBLIC); readerMethod.setLevel(AccessLevel.PUBLIC);
readerMethod.getModifiers().add(ElementModifier.STATIC); readerMethod.getModifiers().add(ElementModifier.STATIC);
ProgramEmitter pe = ProgramEmitter.create(readerMethod); ProgramEmitter pe = ProgramEmitter.create(readerMethod);
@ -63,10 +64,13 @@ public class AnnotationClassTransformer implements ClassHolderTransformer {
ValueEmitter array = pe.constructArray(Annotation.class, annotations.size()); ValueEmitter array = pe.constructArray(Annotation.class, annotations.size());
for (int i = 0; i < annotations.size(); ++i) { for (int i = 0; i < annotations.size(); ++i) {
array.setElement(i, generateAnnotationInstance(innerSource, pe, annotations.get(i))); array.unwrapArray(ArrayElementType.OBJECT).setElement(i,
generateAnnotationInstance(innerSource, pe, annotations.get(i)));
} }
array.returnValue(); array.returnValue();
cls.addMethod(readerMethod);
} }
private ValueEmitter generateAnnotationInstance(ClassReaderSource classSource, ProgramEmitter pe, private ValueEmitter generateAnnotationInstance(ClassReaderSource classSource, ProgramEmitter pe,
@ -116,7 +120,8 @@ public class AnnotationClassTransformer implements ClassHolderTransformer {
ValueType itemType = ((ValueType.Array)type).getItemType(); ValueType itemType = ((ValueType.Array)type).getItemType();
ValueEmitter array = pe.constructArray(itemType, list.size()); ValueEmitter array = pe.constructArray(itemType, list.size());
for (int i = 0; i < list.size(); ++i) { for (int i = 0; i < list.size(); ++i) {
array.setElement(i, generateAnnotationValue(classSource, pe, itemType, list.get(i))); array.unwrapArray(ArrayElementType.OBJECT).setElement(i,
generateAnnotationValue(classSource, pe, itemType, list.get(i)));
} }
return array; return array;
} }

View File

@ -43,7 +43,7 @@ import org.teavm.model.emit.ValueEmitter;
*/ */
public class AnnotationDependencyListener implements DependencyListener { public class AnnotationDependencyListener implements DependencyListener {
@Override @Override
public void started(DependencyAgent agent) { public void started(final DependencyAgent agent) {
} }
@Override @Override
@ -85,6 +85,9 @@ public class AnnotationDependencyListener implements DependencyListener {
List<ValueType> ctorSignature = new ArrayList<>(); List<ValueType> ctorSignature = new ArrayList<>();
for (MethodReader methodDecl : annotation.getMethods()) { for (MethodReader methodDecl : annotation.getMethods()) {
if (methodDecl.hasModifier(ElementModifier.STATIC)) {
continue;
}
FieldHolder field = new FieldHolder("$" + methodDecl.getName()); FieldHolder field = new FieldHolder("$" + methodDecl.getName());
field.setType(methodDecl.getResultType()); field.setType(methodDecl.getResultType());
field.setLevel(AccessLevel.PRIVATE); field.setLevel(AccessLevel.PRIVATE);
@ -109,6 +112,9 @@ public class AnnotationDependencyListener implements DependencyListener {
ValueEmitter thisVal = pe.wrapNew(); ValueEmitter thisVal = pe.wrapNew();
thisVal.invokeSpecial(new MethodReference(Object.class, "<init>", void.class)); thisVal.invokeSpecial(new MethodReference(Object.class, "<init>", void.class));
for (MethodReader methodDecl : annotation.getMethods()) { for (MethodReader methodDecl : annotation.getMethods()) {
if (methodDecl.hasModifier(ElementModifier.STATIC)) {
continue;
}
ValueEmitter param = pe.wrapNew(); ValueEmitter param = pe.wrapNew();
FieldReference field = new FieldReference(implementorName, "$" + methodDecl.getName()); FieldReference field = new FieldReference(implementorName, "$" + methodDecl.getName());
thisVal.setField(field, methodDecl.getResultType(), param); thisVal.setField(field, methodDecl.getResultType(), param);

View File

@ -61,6 +61,10 @@ public class CachedMapper<T, R> implements Mapper<T, R> {
return wrapper.value; return wrapper.value;
} }
public void invalidate(T preimage) {
cache.remove(preimage);
}
public boolean caches(T preimage) { public boolean caches(T preimage) {
return cache.get(preimage) != null; return cache.get(preimage) != null;
} }

View File

@ -17,13 +17,16 @@ package org.teavm.dependency;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import org.teavm.common.CachedMapper; import org.teavm.common.CachedMapper;
import org.teavm.common.Mapper; import org.teavm.common.Mapper;
import org.teavm.diagnostics.Diagnostics; import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.*; import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.util.ModelUtils; import org.teavm.model.util.ModelUtils;
/** /**
@ -33,10 +36,9 @@ import org.teavm.model.util.ModelUtils;
class DependencyClassSource implements ClassReaderSource { class DependencyClassSource implements ClassReaderSource {
private ClassReaderSource innerSource; private ClassReaderSource innerSource;
private Diagnostics diagnostics; private Diagnostics diagnostics;
private ConcurrentMap<String, ClassHolder> generatedClasses = new ConcurrentHashMap<>(); private Map<String, ClassHolder> generatedClasses = new HashMap<>();
private List<ClassHolderTransformer> transformers = new ArrayList<>(); private List<ClassHolderTransformer> transformers = new ArrayList<>();
private CachedMapper<String, ClassReader> cache = new CachedMapper<>( private CachedMapper<String, ClassReader> cache = new CachedMapper<>(new Mapper<String, ClassReader>() {
new Mapper<String, ClassReader>() {
@Override public ClassReader map(String preimage) { @Override public ClassReader map(String preimage) {
return findAndTransformClass(preimage); return findAndTransformClass(preimage);
} }
@ -53,12 +55,11 @@ class DependencyClassSource implements ClassReaderSource {
} }
public void submit(ClassHolder cls) { public void submit(ClassHolder cls) {
if (innerSource.get(cls.getName()) != null) { if (innerSource.get(cls.getName()) != null || generatedClasses.containsKey(cls.getName())) {
throw new IllegalArgumentException("Class " + cls.getName() + " is already defined");
}
if (generatedClasses.putIfAbsent(cls.getName(), cls) != null) {
throw new IllegalArgumentException("Class " + cls.getName() + " is already defined"); throw new IllegalArgumentException("Class " + cls.getName() + " is already defined");
} }
generatedClasses.put(cls.getName(), cls);
cache.invalidate(cls.getName());
} }
private ClassReader findAndTransformClass(String name) { private ClassReader findAndTransformClass(String name) {

View File

@ -22,6 +22,7 @@ import org.teavm.model.MethodReference;
import org.teavm.model.Phi; import org.teavm.model.Phi;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.model.Variable; import org.teavm.model.Variable;
import org.teavm.model.instructions.ArrayElementType;
import org.teavm.model.instructions.ArrayLengthInstruction; import org.teavm.model.instructions.ArrayLengthInstruction;
import org.teavm.model.instructions.BinaryBranchingCondition; import org.teavm.model.instructions.BinaryBranchingCondition;
import org.teavm.model.instructions.BinaryBranchingInstruction; import org.teavm.model.instructions.BinaryBranchingInstruction;
@ -45,6 +46,7 @@ import org.teavm.model.instructions.NegateInstruction;
import org.teavm.model.instructions.NumericOperandType; import org.teavm.model.instructions.NumericOperandType;
import org.teavm.model.instructions.PutElementInstruction; import org.teavm.model.instructions.PutElementInstruction;
import org.teavm.model.instructions.PutFieldInstruction; import org.teavm.model.instructions.PutFieldInstruction;
import org.teavm.model.instructions.UnwrapArrayInstruction;
/** /**
* *
@ -277,6 +279,15 @@ public class ValueEmitter {
setElement(pe.constant(index), value); setElement(pe.constant(index), value);
} }
public ValueEmitter unwrapArray(ArrayElementType elementType) {
Variable result = pe.getProgram().createVariable();
UnwrapArrayInstruction insn = new UnwrapArrayInstruction(elementType);
insn.setArray(variable);
insn.setReceiver(result);
pe.addInstruction(insn);
return pe.wrap(result);
}
public ValueEmitter arrayLength() { public ValueEmitter arrayLength() {
Variable result = pe.getProgram().createVariable(); Variable result = pe.getProgram().createVariable();
ArrayLengthInstruction insn = new ArrayLengthInstruction(); ArrayLengthInstruction insn = new ArrayLengthInstruction();

View File

@ -17,12 +17,15 @@ package org.teavm.platform.plugin;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import org.teavm.dependency.DependencyAgent; import org.teavm.dependency.DependencyAgent;
import org.teavm.dependency.DependencyConsumer;
import org.teavm.dependency.DependencyListener; import org.teavm.dependency.DependencyListener;
import org.teavm.dependency.DependencyNode; import org.teavm.dependency.DependencyNode;
import org.teavm.dependency.DependencyType;
import org.teavm.dependency.FieldDependency; import org.teavm.dependency.FieldDependency;
import org.teavm.dependency.MethodDependency; import org.teavm.dependency.MethodDependency;
import org.teavm.model.CallLocation; import org.teavm.model.CallLocation;
import org.teavm.model.ElementModifier; import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
import org.teavm.platform.Platform; import org.teavm.platform.Platform;
/** /**
@ -30,26 +33,32 @@ import org.teavm.platform.Platform;
* @author Alexey Andreev * @author Alexey Andreev
*/ */
public class AnnotationDependencySupport implements DependencyListener { public class AnnotationDependencySupport implements DependencyListener {
private DependencyNode allAnnotations; private DependencyNode allClasses;
@Override @Override
public void started(DependencyAgent agent) { public void started(DependencyAgent agent) {
allAnnotations = agent.createNode(); allClasses = agent.createNode();
} }
@Override @Override
public void classAchieved(DependencyAgent agent, String className, CallLocation location) { public void classAchieved(DependencyAgent agent, String className, CallLocation location) {
allClasses.propagate(agent.getType(className));
} }
@Override @Override
public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) { public void methodAchieved(final DependencyAgent agent, final MethodDependency method,
if (method.getMethod().getName().equals("$$_readAnnotations_$$") && final CallLocation location) {
method.getMethod().hasModifier(ElementModifier.STATIC)) { if (method.getReference().getClassName().equals(Platform.class.getName()) &&
method.getResult().getArrayItem().connect(allAnnotations);
} else if (method.getReference().getClassName().equals(Platform.class.getName()) &&
method.getReference().getName().equals("getAnnotations")) { method.getReference().getName().equals("getAnnotations")) {
method.getResult().propagate(agent.getType("[" + Annotation.class.getName())); method.getResult().propagate(agent.getType("[" + Annotation.class.getName()));
allAnnotations.connect(method.getResult().getArrayItem()); allClasses.addConsumer(new DependencyConsumer() {
@Override public void consume(DependencyType type) {
MethodDependency readMethod = agent.linkMethod(new MethodReference(type.getName(),
"$$__readAnnotations__$$", ValueType.parse(Annotation[].class)), location);
readMethod.getResult().getArrayItem().connect(method.getResult().getArrayItem());
readMethod.use();
}
});
} }
} }

View File

@ -212,7 +212,7 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
writer.append("var c").ws().append("=").ws().append("'$$annotations$$';").softNewLine(); writer.append("var c").ws().append("=").ws().append("'$$annotations$$';").softNewLine();
for (String clsName : context.getClassSource().getClassNames()) { for (String clsName : context.getClassSource().getClassNames()) {
ClassReader cls = context.getClassSource().get(clsName); ClassReader cls = context.getClassSource().get(clsName);
MethodReader method = cls.getMethod(new MethodDescriptor("$$_readAnnotations_$$", MethodReader method = cls.getMethod(new MethodDescriptor("$$__readAnnotations__$$",
ValueType.parse(Annotation[].class))); ValueType.parse(Annotation[].class)));
if (method != null) { if (method != null) {
writer.appendClass(clsName).append("[c]").ws().append("=").ws(); writer.appendClass(clsName).append("[c]").ws().append("=").ws();

View File

@ -15,7 +15,13 @@
*/ */
package org.teavm.classlib.java.lang; package org.teavm.classlib.java.lang;
import static org.junit.Assert.*; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.junit.Test; import org.junit.Test;
/** /**
@ -106,6 +112,18 @@ public class ClassTest {
assertEquals(ClassTest.class, new A().getClass().getDeclaringClass()); assertEquals(ClassTest.class, new A().getClass().getDeclaringClass());
} }
@Test
public void annotationsExposed() {
Annotation[] annotations = A.class.getAnnotations();
assertEquals(1, annotations.length);
assertTrue(TestAnnot.class.isAssignableFrom(annotations[0].getClass()));
}
@TestAnnot
private static class A { private static class A {
} }
@Retention(RetentionPolicy.RUNTIME)
static @interface TestAnnot {
}
} }