diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java index b5aac4631..cb2776abd 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java @@ -17,7 +17,6 @@ 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; @@ -42,6 +41,5 @@ public class JCLPlugin implements TeaVMPlugin { host.registerService(CLDRReader.class, new CLDRReader(host.getProperties(), host.getClassLoader())); host.add(new AnnotationDependencyListener()); - host.add(new AnnotationClassTransformer()); } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/AnnotationClassTransformer.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/AnnotationClassTransformer.java deleted file mode 100644 index 6573434fa..000000000 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/AnnotationClassTransformer.java +++ /dev/null @@ -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 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 ctorSignature = new ArrayList<>(); - List 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, "", 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 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()); - } - } -} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/AnnotationDependencyListener.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/AnnotationDependencyListener.java index 856377276..9d201ef1b 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/AnnotationDependencyListener.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/AnnotationDependencyListener.java @@ -15,6 +15,8 @@ */ 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.AbstractDependencyListener; @@ -22,6 +24,7 @@ import org.teavm.dependency.DependencyAgent; 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; @@ -35,6 +38,8 @@ 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; +import org.teavm.platform.PlatformAnnotationProvider; /** * @@ -48,12 +53,11 @@ public class AnnotationDependencyListener extends AbstractDependencyListener { return; } - if (cls.hasModifier(ElementModifier.ANNOTATION)) { - getAnnotationImplementor(agent, className); - } for (AnnotationReader annotation : cls.getAnnotations().all()) { agent.linkClass(annotation.getType(), location); } + + createAnnotationClass(agent, className); } 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("", ValueType.VOID); + ctor.setLevel(AccessLevel.PUBLIC); + ProgramEmitter pe = ProgramEmitter.create(ctor); + ValueEmitter thisVar = pe.newVar(); + + thisVar.invokeSpecial(new MethodReference(Object.class, "", 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 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 ctorSignature = new ArrayList<>(); + List 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, "", 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 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()); + } + } } diff --git a/teavm-platform/src/main/java/org/teavm/platform/PlatformAnnotationProvider.java b/teavm-platform/src/main/java/org/teavm/platform/PlatformAnnotationProvider.java new file mode 100644 index 000000000..b38041bfb --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/PlatformAnnotationProvider.java @@ -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(); +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/AnnotationDependencySupport.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/AnnotationDependencySupport.java index 361e645bf..e16b8b48e 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/AnnotationDependencySupport.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/AnnotationDependencySupport.java @@ -26,6 +26,7 @@ import org.teavm.model.CallLocation; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; 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()) && method.getReference().getName().equals("getAnnotations")) { method.getResult().propagate(agent.getType("[" + Annotation.class.getName())); + agent.linkMethod(new MethodReference(PlatformAnnotationProvider.class, "getAnnotations", + Annotation[].class), location); allClasses.addConsumer(new DependencyConsumer() { @Override public void consume(DependencyType type) { - MethodDependency readMethod = agent.linkMethod(new MethodReference(type.getName(), - "$$__readAnnotations__$$", ValueType.parse(Annotation[].class)), location); + if (type.getName().endsWith("$$__annotations__$$")) { + return; + } + String className = type.getName() + "$$__annotations__$$"; + agent.linkMethod(new MethodReference(className, "", 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.use(); } diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java index 300e0f66f..833bf2b48 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java @@ -209,13 +209,12 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin 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) { + ClassReader annotCls = context.getClassSource().get(clsName + "$$__annotations__$$"); + if (annotCls != null) { writer.appendClass(clsName).append("[c]").ws().append("=").ws(); - writer.appendMethodBody(method.getReference()); - writer.append(";").softNewLine(); + MethodReference ctor = new MethodReference(annotCls.getName(), "", ValueType.VOID); + 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("return null;").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.append("return ").append(selfName).append("(").append(context.getParameterName(1))