mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-23 23:04:50 -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;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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 {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user