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;
import java.util.HashMap;
import java.util.Map;
import org.teavm.classlib.impl.DeclaringClassMetadataGenerator;
import org.teavm.classlib.java.lang.annotation.TAnnotation;
import org.teavm.classlib.java.lang.reflect.TAnnotatedElement;
@ -33,6 +35,8 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
private TClass<?> componentType;
private boolean componentTypeDirty = true;
private PlatformClass platformClass;
private TAnnotation[] annotationsCache;
private Map<TClass<?>, TAnnotation> annotationsByType;
private TClass(PlatformClass platformClass) {
this.platformClass = platformClass;
@ -203,21 +207,37 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
@Override
public boolean isAnnotationPresent(TClass<? extends TAnnotation> annotationClass) {
return false;
ensureAnnotationsByType();
return annotationsByType.containsKey(annotationClass);
}
@SuppressWarnings("unchecked")
@Override
public <S extends TAnnotation> S getAnnotation(TClass<S> annotationClass) {
return null;
ensureAnnotationsByType();
return (S)annotationsByType.get(annotationClass);
}
@Override
public TAnnotation[] getAnnotations() {
return null;
if (annotationsCache == null) {
annotationsCache = (TAnnotation[])Platform.getAnnotations(getPlatformClass());
}
return annotationsCache.clone();
}
@Override
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.emit.ProgramEmitter;
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 {
@Override
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.getModifiers().add(ElementModifier.STATIC);
ProgramEmitter pe = ProgramEmitter.create(readerMethod);
@ -63,10 +64,13 @@ public class AnnotationClassTransformer implements ClassHolderTransformer {
ValueEmitter array = pe.constructArray(Annotation.class, annotations.size());
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();
cls.addMethod(readerMethod);
}
private ValueEmitter generateAnnotationInstance(ClassReaderSource classSource, ProgramEmitter pe,
@ -116,7 +120,8 @@ public class AnnotationClassTransformer implements ClassHolderTransformer {
ValueType itemType = ((ValueType.Array)type).getItemType();
ValueEmitter array = pe.constructArray(itemType, list.size());
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;
}

View File

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

View File

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

View File

@ -17,13 +17,16 @@ package org.teavm.dependency;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.Map;
import org.teavm.common.CachedMapper;
import org.teavm.common.Mapper;
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;
/**
@ -33,10 +36,9 @@ import org.teavm.model.util.ModelUtils;
class DependencyClassSource implements ClassReaderSource {
private ClassReaderSource innerSource;
private Diagnostics diagnostics;
private ConcurrentMap<String, ClassHolder> generatedClasses = new ConcurrentHashMap<>();
private Map<String, ClassHolder> generatedClasses = new HashMap<>();
private List<ClassHolderTransformer> transformers = new ArrayList<>();
private CachedMapper<String, ClassReader> cache = new CachedMapper<>(
new Mapper<String, ClassReader>() {
private CachedMapper<String, ClassReader> cache = new CachedMapper<>(new Mapper<String, ClassReader>() {
@Override public ClassReader map(String preimage) {
return findAndTransformClass(preimage);
}
@ -53,12 +55,11 @@ class DependencyClassSource implements ClassReaderSource {
}
public void submit(ClassHolder cls) {
if (innerSource.get(cls.getName()) != null) {
throw new IllegalArgumentException("Class " + cls.getName() + " is already defined");
}
if (generatedClasses.putIfAbsent(cls.getName(), cls) != null) {
if (innerSource.get(cls.getName()) != null || generatedClasses.containsKey(cls.getName())) {
throw new IllegalArgumentException("Class " + cls.getName() + " is already defined");
}
generatedClasses.put(cls.getName(), cls);
cache.invalidate(cls.getName());
}
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.ValueType;
import org.teavm.model.Variable;
import org.teavm.model.instructions.ArrayElementType;
import org.teavm.model.instructions.ArrayLengthInstruction;
import org.teavm.model.instructions.BinaryBranchingCondition;
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.PutElementInstruction;
import org.teavm.model.instructions.PutFieldInstruction;
import org.teavm.model.instructions.UnwrapArrayInstruction;
/**
*
@ -277,6 +279,15 @@ public class ValueEmitter {
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() {
Variable result = pe.getProgram().createVariable();
ArrayLengthInstruction insn = new ArrayLengthInstruction();

View File

@ -17,12 +17,15 @@ package org.teavm.platform.plugin;
import java.lang.annotation.Annotation;
import org.teavm.dependency.DependencyAgent;
import org.teavm.dependency.DependencyConsumer;
import org.teavm.dependency.DependencyListener;
import org.teavm.dependency.DependencyNode;
import org.teavm.dependency.DependencyType;
import org.teavm.dependency.FieldDependency;
import org.teavm.dependency.MethodDependency;
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;
/**
@ -30,26 +33,32 @@ import org.teavm.platform.Platform;
* @author Alexey Andreev
*/
public class AnnotationDependencySupport implements DependencyListener {
private DependencyNode allAnnotations;
private DependencyNode allClasses;
@Override
public void started(DependencyAgent agent) {
allAnnotations = agent.createNode();
allClasses = agent.createNode();
}
@Override
public void classAchieved(DependencyAgent agent, String className, CallLocation location) {
allClasses.propagate(agent.getType(className));
}
@Override
public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) {
if (method.getMethod().getName().equals("$$_readAnnotations_$$") &&
method.getMethod().hasModifier(ElementModifier.STATIC)) {
method.getResult().getArrayItem().connect(allAnnotations);
} else if (method.getReference().getClassName().equals(Platform.class.getName()) &&
public void methodAchieved(final DependencyAgent agent, final MethodDependency method,
final CallLocation location) {
if (method.getReference().getClassName().equals(Platform.class.getName()) &&
method.getReference().getName().equals("getAnnotations")) {
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();
for (String clsName : context.getClassSource().getClassNames()) {
ClassReader cls = context.getClassSource().get(clsName);
MethodReader method = cls.getMethod(new MethodDescriptor("$$_readAnnotations_$$",
MethodReader method = cls.getMethod(new MethodDescriptor("$$__readAnnotations__$$",
ValueType.parse(Annotation[].class)));
if (method != null) {
writer.appendClass(clsName).append("[c]").ws().append("=").ws();

View File

@ -15,7 +15,13 @@
*/
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;
/**
@ -106,6 +112,18 @@ public class ClassTest {
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 {
}
@Retention(RetentionPolicy.RUNTIME)
static @interface TestAnnot {
}
}