mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-08 07:54:11 -08:00
First working version of annotation support
This commit is contained in:
parent
fd689d2efa
commit
32feb24d0f
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 {
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user