mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-23 00:24:11 -08:00
Add very first draft of annotation support
This commit is contained in:
parent
3cb39b9507
commit
fd689d2efa
|
@ -17,6 +17,8 @@ package org.teavm.classlib.impl;
|
|||
|
||||
import java.util.ServiceLoader;
|
||||
import org.teavm.classlib.impl.unicode.CLDRReader;
|
||||
import org.teavm.classlib.java.lang.reflect.AnnotationClassTransformer;
|
||||
import org.teavm.classlib.java.lang.reflect.AnnotationDependencyListener;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.platform.PlatformClass;
|
||||
import org.teavm.vm.spi.TeaVMHost;
|
||||
|
@ -38,5 +40,8 @@ public class JCLPlugin implements TeaVMPlugin {
|
|||
host.add(javacSupport);
|
||||
|
||||
host.registerService(CLDRReader.class, new CLDRReader(host.getProperties(), host.getClassLoader()));
|
||||
|
||||
host.add(new AnnotationDependencyListener());
|
||||
host.add(new AnnotationClassTransformer());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* 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.diagnostics.Diagnostics;
|
||||
import org.teavm.model.AccessLevel;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.AnnotationValue;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
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 AnnotationClassTransformer implements ClassHolderTransformer {
|
||||
@Override
|
||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
||||
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 = innerSource.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(innerSource, pe, annotations.get(i)));
|
||||
}
|
||||
|
||||
array.returnValue();
|
||||
}
|
||||
|
||||
private ValueEmitter generateAnnotationInstance(ClassReaderSource classSource, ProgramEmitter pe,
|
||||
AnnotationReader annotation) {
|
||||
ClassReader annotationClass = classSource.get(annotation.getType());
|
||||
if (annotationClass == null) {
|
||||
return pe.constantNull();
|
||||
}
|
||||
|
||||
String className = annotation.getType() + "$$_impl";
|
||||
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(classSource, 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(ClassReaderSource classSource, 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(classSource, 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(classSource, pe, value.getAnnotation());
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown annotation value type: " + value.getType());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
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;
|
||||
|
@ -25,7 +23,6 @@ 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;
|
||||
|
@ -56,92 +53,11 @@ public class AnnotationDependencyListener implements DependencyListener {
|
|||
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);
|
||||
}
|
||||
if (cls.hasModifier(ElementModifier.ANNOTATION)) {
|
||||
getAnnotationImplementor(agent, className);
|
||||
}
|
||||
|
||||
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());
|
||||
for (AnnotationReader annotation : cls.getAnnotations().all()) {
|
||||
agent.linkClass(annotation.getType(), location);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,6 +121,17 @@ public class AnnotationDependencyListener implements DependencyListener {
|
|||
|
||||
@Override
|
||||
public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
||||
ValueType type = method.getMethod().getResultType();
|
||||
while (type instanceof ValueType.Array) {
|
||||
type = ((ValueType.Array)type).getItemType();
|
||||
}
|
||||
if (type instanceof ValueType.Object) {
|
||||
String className = ((ValueType.Object)type).getClassName();
|
||||
ClassReader cls = agent.getClassSource().get(className);
|
||||
if (cls != null && cls.hasModifier(ElementModifier.ANNOTATION)) {
|
||||
agent.linkClass(className, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.teavm.platform;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import org.teavm.dependency.PluggableDependency;
|
||||
import org.teavm.javascript.spi.GeneratedBy;
|
||||
import org.teavm.javascript.spi.InjectedBy;
|
||||
|
@ -105,6 +106,10 @@ public final class Platform {
|
|||
@PluggableDependency(PlatformGenerator.class)
|
||||
public static native Enum<?>[] getEnumConstants(PlatformClass cls);
|
||||
|
||||
@GeneratedBy(PlatformGenerator.class)
|
||||
@PluggableDependency(PlatformGenerator.class)
|
||||
public static native Annotation[] getAnnotations(PlatformClass cls);
|
||||
|
||||
@GeneratedBy(PlatformGenerator.class)
|
||||
@PluggableDependency(PlatformGenerator.class)
|
||||
public static native void startThread(PlatformRunnable runnable);
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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.platform.plugin;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.DependencyListener;
|
||||
import org.teavm.dependency.DependencyNode;
|
||||
import org.teavm.dependency.FieldDependency;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.platform.Platform;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class AnnotationDependencySupport implements DependencyListener {
|
||||
private DependencyNode allAnnotations;
|
||||
|
||||
@Override
|
||||
public void started(DependencyAgent agent) {
|
||||
allAnnotations = agent.createNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void classAchieved(DependencyAgent agent, String className, CallLocation location) {
|
||||
}
|
||||
|
||||
@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()) &&
|
||||
method.getReference().getName().equals("getAnnotations")) {
|
||||
method.getResult().propagate(agent.getType("[" + Annotation.class.getName()));
|
||||
allAnnotations.connect(method.getResult().getArrayItem());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fieldAchieved(DependencyAgent agent, FieldDependency field, CallLocation location) {
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
package org.teavm.platform.plugin;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import org.teavm.codegen.SourceWriter;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.DependencyPlugin;
|
||||
|
@ -24,7 +25,12 @@ import org.teavm.javascript.spi.Generator;
|
|||
import org.teavm.javascript.spi.GeneratorContext;
|
||||
import org.teavm.javascript.spi.Injector;
|
||||
import org.teavm.javascript.spi.InjectorContext;
|
||||
import org.teavm.model.*;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.platform.Platform;
|
||||
import org.teavm.platform.PlatformClass;
|
||||
import org.teavm.platform.PlatformRunnable;
|
||||
|
@ -54,6 +60,8 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
|
|||
case "getCurrentThread":
|
||||
method.getResult().propagate(agent.getType("java.lang.Thread"));
|
||||
break;
|
||||
case "getAnnotations":
|
||||
method.getResult();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,6 +100,9 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
|
|||
case "getEnumConstants":
|
||||
generateEnumConstants(context, writer);
|
||||
break;
|
||||
case "getAnnotations":
|
||||
generateAnnotations(context, writer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -196,4 +207,30 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
|
|||
writer.append("return ").append(selfName).append("(").append(context.getParameterName(1))
|
||||
.append(");").softNewLine();
|
||||
}
|
||||
|
||||
private void generateAnnotations(GeneratorContext context, SourceWriter writer) throws IOException {
|
||||
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_$$",
|
||||
ValueType.parse(Annotation[].class)));
|
||||
if (method != null) {
|
||||
writer.appendClass(clsName).append("[c]").ws().append("=").ws();
|
||||
writer.appendMethodBody(method.getReference());
|
||||
writer.append(";").softNewLine();
|
||||
}
|
||||
}
|
||||
|
||||
String selfName = writer.getNaming().getFullNameFor(new MethodReference(Platform.class, "getAnnotations",
|
||||
PlatformClass.class, Annotation[].class));
|
||||
writer.append(selfName).ws().append("=").ws().append("function(cls)").ws().append("{").softNewLine().indent();
|
||||
writer.append("if").ws().append("(!cls.hasOwnProperty(c))").ws().append("{").indent().softNewLine();
|
||||
writer.append("return null;").softNewLine();
|
||||
writer.outdent().append("}").softNewLine();
|
||||
writer.append("return cls[c]();").softNewLine();
|
||||
writer.outdent().append("};").softNewLine();
|
||||
|
||||
writer.append("return ").append(selfName).append("(").append(context.getParameterName(1))
|
||||
.append(");").softNewLine();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ public class PlatformPlugin implements TeaVMPlugin {
|
|||
host.add(new NewInstanceDependencySupport());
|
||||
host.add(new ClassLookupDependencySupport());
|
||||
host.add(new EnumDependencySupport());
|
||||
host.add(new AnnotationDependencySupport());
|
||||
host.add(new PlatformDependencyListener());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user