Add very first draft of annotation support

This commit is contained in:
Alexey Andreev 2015-06-17 22:26:23 +03:00
parent 3cb39b9507
commit fd689d2efa
7 changed files with 256 additions and 89 deletions

View File

@ -17,6 +17,8 @@ package org.teavm.classlib.impl;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import org.teavm.classlib.impl.unicode.CLDRReader; 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.model.MethodReference;
import org.teavm.platform.PlatformClass; import org.teavm.platform.PlatformClass;
import org.teavm.vm.spi.TeaVMHost; import org.teavm.vm.spi.TeaVMHost;
@ -38,5 +40,8 @@ public class JCLPlugin implements TeaVMPlugin {
host.add(javacSupport); host.add(javacSupport);
host.registerService(CLDRReader.class, new CLDRReader(host.getProperties(), host.getClassLoader())); host.registerService(CLDRReader.class, new CLDRReader(host.getProperties(), host.getClassLoader()));
host.add(new AnnotationDependencyListener());
host.add(new AnnotationClassTransformer());
} }
} }

View File

@ -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());
}
}
}

View File

@ -15,8 +15,6 @@
*/ */
package org.teavm.classlib.java.lang.reflect; package org.teavm.classlib.java.lang.reflect;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.teavm.dependency.DependencyAgent; import org.teavm.dependency.DependencyAgent;
@ -25,7 +23,6 @@ import org.teavm.dependency.FieldDependency;
import org.teavm.dependency.MethodDependency; import org.teavm.dependency.MethodDependency;
import org.teavm.model.AccessLevel; import org.teavm.model.AccessLevel;
import org.teavm.model.AnnotationReader; import org.teavm.model.AnnotationReader;
import org.teavm.model.AnnotationValue;
import org.teavm.model.CallLocation; import org.teavm.model.CallLocation;
import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolder;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
@ -56,92 +53,11 @@ public class AnnotationDependencyListener implements DependencyListener {
return; return;
} }
MethodHolder readerMethod = new MethodHolder("$$_readAnnotations_$$", ValueType.parse(Annotation[].class)); if (cls.hasModifier(ElementModifier.ANNOTATION)) {
readerMethod.setLevel(AccessLevel.PUBLIC); getAnnotationImplementor(agent, className);
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);
}
} }
for (AnnotationReader annotation : cls.getAnnotations().all()) {
ValueEmitter array = pe.constructArray(Annotation.class, annotations.size()); agent.linkClass(annotation.getType(), location);
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());
} }
} }
@ -205,6 +121,17 @@ public class AnnotationDependencyListener implements DependencyListener {
@Override @Override
public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) { 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 @Override

View File

@ -15,6 +15,7 @@
*/ */
package org.teavm.platform; package org.teavm.platform;
import java.lang.annotation.Annotation;
import org.teavm.dependency.PluggableDependency; import org.teavm.dependency.PluggableDependency;
import org.teavm.javascript.spi.GeneratedBy; import org.teavm.javascript.spi.GeneratedBy;
import org.teavm.javascript.spi.InjectedBy; import org.teavm.javascript.spi.InjectedBy;
@ -105,6 +106,10 @@ public final class Platform {
@PluggableDependency(PlatformGenerator.class) @PluggableDependency(PlatformGenerator.class)
public static native Enum<?>[] getEnumConstants(PlatformClass cls); public static native Enum<?>[] getEnumConstants(PlatformClass cls);
@GeneratedBy(PlatformGenerator.class)
@PluggableDependency(PlatformGenerator.class)
public static native Annotation[] getAnnotations(PlatformClass cls);
@GeneratedBy(PlatformGenerator.class) @GeneratedBy(PlatformGenerator.class)
@PluggableDependency(PlatformGenerator.class) @PluggableDependency(PlatformGenerator.class)
public static native void startThread(PlatformRunnable runnable); public static native void startThread(PlatformRunnable runnable);

View File

@ -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) {
}
}

View File

@ -16,6 +16,7 @@
package org.teavm.platform.plugin; package org.teavm.platform.plugin;
import java.io.IOException; import java.io.IOException;
import java.lang.annotation.Annotation;
import org.teavm.codegen.SourceWriter; import org.teavm.codegen.SourceWriter;
import org.teavm.dependency.DependencyAgent; import org.teavm.dependency.DependencyAgent;
import org.teavm.dependency.DependencyPlugin; 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.GeneratorContext;
import org.teavm.javascript.spi.Injector; import org.teavm.javascript.spi.Injector;
import org.teavm.javascript.spi.InjectorContext; 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.Platform;
import org.teavm.platform.PlatformClass; import org.teavm.platform.PlatformClass;
import org.teavm.platform.PlatformRunnable; import org.teavm.platform.PlatformRunnable;
@ -54,6 +60,8 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
case "getCurrentThread": case "getCurrentThread":
method.getResult().propagate(agent.getType("java.lang.Thread")); method.getResult().propagate(agent.getType("java.lang.Thread"));
break; break;
case "getAnnotations":
method.getResult();
} }
} }
@ -92,6 +100,9 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
case "getEnumConstants": case "getEnumConstants":
generateEnumConstants(context, writer); generateEnumConstants(context, writer);
break; 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)) writer.append("return ").append(selfName).append("(").append(context.getParameterName(1))
.append(");").softNewLine(); .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();
}
} }

View File

@ -33,6 +33,7 @@ public class PlatformPlugin implements TeaVMPlugin {
host.add(new NewInstanceDependencySupport()); host.add(new NewInstanceDependencySupport());
host.add(new ClassLookupDependencySupport()); host.add(new ClassLookupDependencySupport());
host.add(new EnumDependencySupport()); host.add(new EnumDependencySupport());
host.add(new AnnotationDependencySupport());
host.add(new PlatformDependencyListener()); host.add(new PlatformDependencyListener());
} }
} }