Alexey Andreev 2015-06-30 15:41:49 +04:00
parent d5da4964c4
commit 6ab53cbf6a
6 changed files with 175 additions and 155 deletions

View File

@ -17,7 +17,6 @@ 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.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;
@ -42,6 +41,5 @@ public class JCLPlugin implements TeaVMPlugin {
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 AnnotationDependencyListener());
host.add(new AnnotationClassTransformer());
} }
} }

View File

@ -1,141 +0,0 @@
/*
* 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;
import org.teavm.model.instructions.ArrayElementType;
/**
*
* @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.unwrapArray(ArrayElementType.OBJECT).setElement(i,
generateAnnotationInstance(innerSource, pe, annotations.get(i)));
}
array.returnValue();
cls.addMethod(readerMethod);
}
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(methodDecl.getName());
if (value == null) {
value = methodDecl.getAnnotationDefault();
}
params.add(generateAnnotationValue(classSource, pe, methodDecl.getResultType(), value));
}
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.unwrapArray(ArrayElementType.OBJECT).setElement(i,
generateAnnotationValue(classSource, pe, itemType, list.get(i)));
}
return array;
}
case AnnotationValue.ENUM:
pe.initClass(value.getEnumValue().getClassName());
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,6 +15,8 @@
*/ */
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.AbstractDependencyListener; import org.teavm.dependency.AbstractDependencyListener;
@ -22,6 +24,7 @@ import org.teavm.dependency.DependencyAgent;
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;
@ -35,6 +38,8 @@ 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;
import org.teavm.platform.PlatformAnnotationProvider;
/** /**
* *
@ -48,12 +53,11 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
return; return;
} }
if (cls.hasModifier(ElementModifier.ANNOTATION)) {
getAnnotationImplementor(agent, className);
}
for (AnnotationReader annotation : cls.getAnnotations().all()) { for (AnnotationReader annotation : cls.getAnnotations().all()) {
agent.linkClass(annotation.getType(), location); agent.linkClass(annotation.getType(), location);
} }
createAnnotationClass(agent, className);
} }
private String getAnnotationImplementor(DependencyAgent agent, String annotationType) { private String getAnnotationImplementor(DependencyAgent agent, String annotationType) {
@ -150,4 +154,128 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
} }
} }
} }
private void createAnnotationClass(DependencyAgent agent, String className) {
String readerClassName = className + "$$__annotations__$$";
if (agent.getClassSource().get(readerClassName) != null) {
return;
}
ClassHolder cls = new ClassHolder(className + "$$__annotations__$$");
cls.setLevel(AccessLevel.PUBLIC);
cls.setOwnerName("java.lang.Object");
cls.getInterfaces().add(PlatformAnnotationProvider.class.getName());
MethodHolder ctor = new MethodHolder("<init>", ValueType.VOID);
ctor.setLevel(AccessLevel.PUBLIC);
ProgramEmitter pe = ProgramEmitter.create(ctor);
ValueEmitter thisVar = pe.newVar();
thisVar.invokeSpecial(new MethodReference(Object.class, "<init>", void.class));
pe.exit();
ClassReader annotatedClass = agent.getClassSource().get(className);
cls.addMethod(ctor);
cls.addMethod(addReader(agent, annotatedClass));
agent.submitClass(cls);
}
private MethodHolder addReader(DependencyAgent agent, ClassReader cls) {
MethodHolder readerMethod = new MethodHolder("getAnnotations", ValueType.parse(Annotation[].class));
readerMethod.setLevel(AccessLevel.PUBLIC);
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());
if (retention != null) {
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.unwrapArray(ArrayElementType.OBJECT).setElement(i,
generateAnnotationInstance(agent, pe, annotations.get(i)));
}
array.returnValue();
return readerMethod;
}
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(methodDecl.getName());
if (value == null) {
value = methodDecl.getAnnotationDefault();
}
params.add(generateAnnotationValue(agent, pe, methodDecl.getResultType(), value));
}
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.unwrapArray(ArrayElementType.OBJECT).setElement(i,
generateAnnotationValue(agent, pe, itemType, list.get(i)));
}
return array;
}
case AnnotationValue.ENUM:
pe.initClass(value.getEnumValue().getClassName());
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());
}
}
} }

View File

@ -0,0 +1,26 @@
/*
* 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;
import java.lang.annotation.Annotation;
/**
*
* @author Alexey Andreev
*/
public interface PlatformAnnotationProvider {
Annotation[] getAnnotations();
}

View File

@ -26,6 +26,7 @@ import org.teavm.model.CallLocation;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.platform.Platform; import org.teavm.platform.Platform;
import org.teavm.platform.PlatformAnnotationProvider;
/** /**
* *
@ -50,10 +51,19 @@ public class AnnotationDependencySupport extends AbstractDependencyListener {
if (method.getReference().getClassName().equals(Platform.class.getName()) && 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()));
agent.linkMethod(new MethodReference(PlatformAnnotationProvider.class, "getAnnotations",
Annotation[].class), location);
allClasses.addConsumer(new DependencyConsumer() { allClasses.addConsumer(new DependencyConsumer() {
@Override public void consume(DependencyType type) { @Override public void consume(DependencyType type) {
MethodDependency readMethod = agent.linkMethod(new MethodReference(type.getName(), if (type.getName().endsWith("$$__annotations__$$")) {
"$$__readAnnotations__$$", ValueType.parse(Annotation[].class)), location); return;
}
String className = type.getName() + "$$__annotations__$$";
agent.linkMethod(new MethodReference(className, "<init>", ValueType.VOID), location)
.propagate(0, className)
.use();
MethodDependency readMethod = agent.linkMethod(new MethodReference(className,
"getAnnotations", ValueType.parse(Annotation[].class)), location);
readMethod.getResult().getArrayItem().connect(method.getResult().getArrayItem()); readMethod.getResult().getArrayItem().connect(method.getResult().getArrayItem());
readMethod.use(); readMethod.use();
} }

View File

@ -209,13 +209,12 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
private void generateAnnotations(GeneratorContext context, SourceWriter writer) throws IOException { private void generateAnnotations(GeneratorContext context, SourceWriter writer) throws IOException {
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 annotCls = context.getClassSource().get(clsName + "$$__annotations__$$");
MethodReader method = cls.getMethod(new MethodDescriptor("$$__readAnnotations__$$", if (annotCls != null) {
ValueType.parse(Annotation[].class)));
if (method != null) {
writer.appendClass(clsName).append("[c]").ws().append("=").ws(); writer.appendClass(clsName).append("[c]").ws().append("=").ws();
writer.appendMethodBody(method.getReference()); MethodReference ctor = new MethodReference(annotCls.getName(), "<init>", ValueType.VOID);
writer.append(";").softNewLine(); writer.append(writer.getNaming().getNameForInit(ctor));
writer.append("();").softNewLine();
} }
} }
@ -225,7 +224,7 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
writer.append("if").ws().append("(!cls.hasOwnProperty(c))").ws().append("{").indent().softNewLine(); writer.append("if").ws().append("(!cls.hasOwnProperty(c))").ws().append("{").indent().softNewLine();
writer.append("return null;").softNewLine(); writer.append("return null;").softNewLine();
writer.outdent().append("}").softNewLine(); writer.outdent().append("}").softNewLine();
writer.append("return cls[c]();").softNewLine(); writer.append("return cls[c].").appendMethod("getAnnotations", Annotation[].class).append("();").softNewLine();
writer.outdent().append("};").softNewLine(); writer.outdent().append("};").softNewLine();
writer.append("return ").append(selfName).append("(").append(context.getParameterName(1)) writer.append("return ").append(selfName).append("(").append(context.getParameterName(1))