Add generator of method that reads annotations of a class

This commit is contained in:
Alexey Andreev 2015-06-17 21:48:48 +03:00
parent c38c07becc
commit 3cb39b9507
5 changed files with 333 additions and 1 deletions

View File

@ -16,6 +16,8 @@
package org.teavm.classlib.java.lang;
import org.teavm.classlib.impl.DeclaringClassMetadataGenerator;
import org.teavm.classlib.java.lang.annotation.TAnnotation;
import org.teavm.classlib.java.lang.reflect.TAnnotatedElement;
import org.teavm.platform.Platform;
import org.teavm.platform.PlatformClass;
import org.teavm.platform.metadata.ClassResource;
@ -26,7 +28,7 @@ import org.teavm.platform.metadata.ClassScopedMetadataProvider;
* @author Alexey Andreev
* @param <T> class type.
*/
public class TClass<T> extends TObject {
public class TClass<T> extends TObject implements TAnnotatedElement {
TString name;
private TClass<?> componentType;
private boolean componentTypeDirty = true;
@ -198,4 +200,24 @@ public class TClass<T> extends TObject {
}
return (TClass<? extends U>)this;
}
@Override
public boolean isAnnotationPresent(TClass<? extends TAnnotation> annotationClass) {
return false;
}
@Override
public <S extends TAnnotation> S getAnnotation(TClass<S> annotationClass) {
return null;
}
@Override
public TAnnotation[] getAnnotations() {
return null;
}
@Override
public TAnnotation[] getDeclaredAnnotations() {
return null;
}
}

View File

@ -0,0 +1,213 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.java.lang.reflect;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.List;
import org.teavm.dependency.DependencyAgent;
import org.teavm.dependency.DependencyListener;
import org.teavm.dependency.FieldDependency;
import org.teavm.dependency.MethodDependency;
import org.teavm.model.AccessLevel;
import org.teavm.model.AnnotationReader;
import org.teavm.model.AnnotationValue;
import org.teavm.model.CallLocation;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldHolder;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
import org.teavm.model.emit.ProgramEmitter;
import org.teavm.model.emit.ValueEmitter;
/**
*
* @author Alexey Andreev
*/
public class AnnotationDependencyListener implements DependencyListener {
@Override
public void started(DependencyAgent agent) {
}
@Override
public void classAchieved(DependencyAgent agent, String className, CallLocation location) {
ClassReader cls = agent.getClassSource().get(className);
if (cls == null) {
return;
}
MethodHolder readerMethod = new MethodHolder("$$_readAnnotations_$$", ValueType.parse(Annotation[].class));
readerMethod.setLevel(AccessLevel.PUBLIC);
readerMethod.getModifiers().add(ElementModifier.STATIC);
ProgramEmitter pe = ProgramEmitter.create(readerMethod);
List<AnnotationReader> annotations = new ArrayList<>();
for (AnnotationReader annot : cls.getAnnotations().all()) {
ClassReader annotType = agent.getClassSource().get(annot.getType());
if (annotType == null) {
continue;
}
AnnotationReader retention = annotType.getAnnotations().get(Retention.class.getName());
String retentionPolicy = retention.getValue("value").getEnumValue().getFieldName();
if (retentionPolicy.equals("RUNTIME")) {
annotations.add(annot);
}
}
ValueEmitter array = pe.constructArray(Annotation.class, annotations.size());
for (int i = 0; i < annotations.size(); ++i) {
array.setElement(i, generateAnnotationInstance(agent, pe, annotations.get(i)));
}
array.returnValue();
}
private ValueEmitter generateAnnotationInstance(DependencyAgent agent, ProgramEmitter pe,
AnnotationReader annotation) {
ClassReader annotationClass = agent.getClassSource().get(annotation.getType());
if (annotationClass == null) {
return pe.constantNull();
}
String className = getAnnotationImplementor(agent, annotation.getType());
List<ValueType> ctorSignature = new ArrayList<>();
List<ValueEmitter> params = new ArrayList<>();
for (MethodReader methodDecl : annotationClass.getMethods()) {
ctorSignature.add(methodDecl.getResultType());
AnnotationValue value = annotation.getValue(className);
params.add(value != null ? generateAnnotationValue(agent, pe, methodDecl.getResultType(), value) :
pe.constantNull());
}
ctorSignature.add(ValueType.VOID);
MethodReference ctor = new MethodReference(className, "<init>", ctorSignature.toArray(
new ValueType[ctorSignature.size()]));
return pe.construct(ctor, params.toArray(new ValueEmitter[params.size()]));
}
private ValueEmitter generateAnnotationValue(DependencyAgent agent, ProgramEmitter pe, ValueType type,
AnnotationValue value) {
switch (value.getType()) {
case AnnotationValue.BOOLEAN:
return pe.constant(value.getBoolean() ? 1 : 0);
case AnnotationValue.BYTE:
return pe.constant(value.getByte());
case AnnotationValue.SHORT:
return pe.constant(value.getShort());
case AnnotationValue.INT:
return pe.constant(value.getInt());
case AnnotationValue.LONG:
return pe.constant(value.getLong());
case AnnotationValue.FLOAT:
return pe.constant(value.getFloat());
case AnnotationValue.DOUBLE:
return pe.constant(value.getDouble());
case AnnotationValue.STRING:
return pe.constant(value.getString());
case AnnotationValue.LIST: {
List<AnnotationValue> list = value.getList();
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(agent, pe, itemType, list.get(i)));
}
return array;
}
case AnnotationValue.ENUM:
return pe.getField(value.getEnumValue(), type);
case AnnotationValue.CLASS:
return pe.constant(value.getJavaClass());
case AnnotationValue.ANNOTATION:
return generateAnnotationInstance(agent, pe, value.getAnnotation());
default:
throw new IllegalArgumentException("Unknown annotation value type: " + value.getType());
}
}
private String getAnnotationImplementor(DependencyAgent agent, String annotationType) {
String implementorName = annotationType + "$$_impl";
if (agent.getClassSource().get(implementorName) == null) {
ClassHolder implementor = createImplementor(agent.getClassSource(), annotationType, implementorName);
agent.submitClass(implementor);
}
return implementorName;
}
private ClassHolder createImplementor(ClassReaderSource classSource, String annotationType,
String implementorName) {
ClassHolder implementor = new ClassHolder(implementorName);
implementor.setParent("java.lang.Object");
implementor.getInterfaces().add(annotationType);
implementor.getModifiers().add(ElementModifier.FINAL);
implementor.setLevel(AccessLevel.PUBLIC);
ClassReader annotation = classSource.get(annotationType);
if (annotation == null) {
return implementor;
}
List<ValueType> ctorSignature = new ArrayList<>();
for (MethodReader methodDecl : annotation.getMethods()) {
FieldHolder field = new FieldHolder("$" + methodDecl.getName());
field.setType(methodDecl.getResultType());
field.setLevel(AccessLevel.PRIVATE);
implementor.addField(field);
MethodHolder accessor = new MethodHolder(methodDecl.getDescriptor());
ProgramEmitter pe = ProgramEmitter.create(accessor);
ValueEmitter thisVal = pe.wrapNew();
ValueEmitter result = thisVal.getField(field.getReference(), field.getType());
if (field.getType() instanceof ValueType.Array) {
result = result.cloneArray();
}
result.returnValue();
implementor.addMethod(accessor);
ctorSignature.add(field.getType());
}
ctorSignature.add(ValueType.VOID);
MethodHolder ctor = new MethodHolder("<init>", ctorSignature.toArray(new ValueType[ctorSignature.size()]));
ProgramEmitter pe = ProgramEmitter.create(ctor);
ValueEmitter thisVal = pe.wrapNew();
thisVal.invokeSpecial(new MethodReference(Object.class, "<init>", void.class));
for (MethodReader methodDecl : annotation.getMethods()) {
ValueEmitter param = pe.wrapNew();
FieldReference field = new FieldReference(implementorName, "$" + methodDecl.getName());
thisVal.setField(field, methodDecl.getResultType(), param);
}
pe.exit();
implementor.addMethod(ctor);
return implementor;
}
@Override
public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) {
}
@Override
public void fieldAchieved(DependencyAgent agent, FieldDependency field, CallLocation location) {
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.java.lang.reflect;
import org.teavm.classlib.java.lang.TClass;
import org.teavm.classlib.java.lang.annotation.TAnnotation;
/**
*
* @author Alexey Andreev
*/
public interface TAnnotatedElement {
boolean isAnnotationPresent(TClass<? extends TAnnotation> annotationClass);
<T extends TAnnotation> T getAnnotation(TClass<T> annotationClass);
TAnnotation[] getAnnotations();
TAnnotation[] getDeclaredAnnotations();
}

View File

@ -16,6 +16,8 @@
package org.teavm.model.emit;
import org.teavm.model.*;
import org.teavm.model.instructions.ClassConstantInstruction;
import org.teavm.model.instructions.ConstructArrayInstruction;
import org.teavm.model.instructions.ConstructInstruction;
import org.teavm.model.instructions.DoubleConstantInstruction;
import org.teavm.model.instructions.ExitInstruction;
@ -62,6 +64,19 @@ public final class ProgramEmitter {
return block;
}
public ValueEmitter constant(Class<?> cls) {
return constant(ValueType.parse(cls));
}
public ValueEmitter constant(ValueType value) {
Variable var = program.createVariable();
ClassConstantInstruction insn = new ClassConstantInstruction();
insn.setReceiver(var);
insn.setConstant(value);
addInstruction(insn);
return wrap(var);
}
public ValueEmitter constant(String value) {
Variable var = program.createVariable();
StringConstantInstruction insn = new StringConstantInstruction();
@ -166,6 +181,28 @@ public final class ProgramEmitter {
return instance;
}
public ValueEmitter constructArray(ValueType type, ValueEmitter size) {
Variable var = program.createVariable();
ConstructArrayInstruction insn = new ConstructArrayInstruction();
insn.setReceiver(var);
insn.setSize(size.getVariable());
insn.setItemType(type);
addInstruction(insn);
return wrap(var);
}
public ValueEmitter constructArray(ValueType type, int size) {
return constructArray(type, constant(size));
}
public ValueEmitter constructArray(Class<?> type, int size) {
return constructArray(ValueType.parse(type), size);
}
public ValueEmitter constructArray(Class<?> type, ValueEmitter size) {
return constructArray(ValueType.parse(type), size);
}
public ProgramEmitter jump(BasicBlock block) {
JumpInstruction insn = new JumpInstruction();
insn.setTarget(block);

View File

@ -33,6 +33,7 @@ import org.teavm.model.instructions.CastInstruction;
import org.teavm.model.instructions.CastIntegerDirection;
import org.teavm.model.instructions.CastIntegerInstruction;
import org.teavm.model.instructions.CastNumberInstruction;
import org.teavm.model.instructions.CloneArrayInstruction;
import org.teavm.model.instructions.ExitInstruction;
import org.teavm.model.instructions.GetElementInstruction;
import org.teavm.model.instructions.GetFieldInstruction;
@ -42,6 +43,7 @@ import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.IsInstanceInstruction;
import org.teavm.model.instructions.NegateInstruction;
import org.teavm.model.instructions.NumericOperandType;
import org.teavm.model.instructions.PutElementInstruction;
import org.teavm.model.instructions.PutFieldInstruction;
/**
@ -259,6 +261,22 @@ public class ValueEmitter {
return pe.wrap(result);
}
public ValueEmitter getElement(int index) {
return getElement(pe.constant(index));
}
public void setElement(ValueEmitter index, ValueEmitter value) {
PutElementInstruction insn = new PutElementInstruction();
insn.setArray(variable);
insn.setIndex(index.variable);
insn.setValue(value.variable);
pe.addInstruction(insn);
}
public void setElement(int index, ValueEmitter value) {
setElement(pe.constant(index), value);
}
public ValueEmitter arrayLength() {
Variable result = pe.getProgram().createVariable();
ArrayLengthInstruction insn = new ArrayLengthInstruction();
@ -277,4 +295,13 @@ public class ValueEmitter {
pe.addInstruction(insn);
return pe.wrap(result);
}
public ValueEmitter cloneArray() {
Variable result = pe.getProgram().createVariable();
CloneArrayInstruction insn = new CloneArrayInstruction();
insn.setArray(variable);
insn.setReceiver(result);
pe.addInstruction(insn);
return pe.wrap(result);
}
}