diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/annotation/TElementType.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/annotation/TElementType.java new file mode 100644 index 000000000..fd4188358 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/annotation/TElementType.java @@ -0,0 +1,31 @@ +/* + * 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.annotation; + +/** + * + * @author Alexey Andreev + */ +public enum TElementType { + ANNOTATION_TYPE, + CONSTRUCTOR, + FIELD, + LOCAL_VARIABLE, + METHOD, + PACKAGE, + PARAMETER, + TYPE +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/annotation/TRetention.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/annotation/TRetention.java new file mode 100644 index 000000000..10756f66c --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/annotation/TRetention.java @@ -0,0 +1,31 @@ +/* + * 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.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author Alexey Andreev + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.ANNOTATION_TYPE) +public @interface TRetention { + TRetentionPolicy value(); +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/annotation/TRetentionPolicy.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/annotation/TRetentionPolicy.java new file mode 100644 index 000000000..311d4eac6 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/annotation/TRetentionPolicy.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.classlib.java.lang.annotation; + +/** + * + * @author Alexey Andreev + */ +public enum TRetentionPolicy { + CLASS, + RUNTIME, + SOURCE +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/annotation/TTarget.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/annotation/TTarget.java new file mode 100644 index 000000000..8ee6414f5 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/annotation/TTarget.java @@ -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.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author Alexey Andreev + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.ANNOTATION_TYPE) +public @interface TTarget { + TElementType[] value(); +} 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 index f83417120..6573434fa 100644 --- 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 @@ -128,6 +128,7 @@ public class AnnotationClassTransformer implements ClassHolderTransformer { return array; } case AnnotationValue.ENUM: + pe.initClass(value.getEnumValue().getClassName()); return pe.getField(value.getEnumValue(), type); case AnnotationValue.CLASS: return pe.constant(value.getJavaClass()); diff --git a/teavm-core/src/main/java/org/teavm/callgraph/DefaultClassAccessSite.java b/teavm-core/src/main/java/org/teavm/callgraph/DefaultClassAccessSite.java index 66ddc1bb4..766e36801 100644 --- a/teavm-core/src/main/java/org/teavm/callgraph/DefaultClassAccessSite.java +++ b/teavm-core/src/main/java/org/teavm/callgraph/DefaultClassAccessSite.java @@ -15,6 +15,7 @@ */ package org.teavm.callgraph; +import java.util.Objects; import org.teavm.model.InstructionLocation; /** @@ -46,4 +47,23 @@ public class DefaultClassAccessSite implements ClassAccessSite { public String getClassName() { return className; } + + @Override + public int hashCode() { + return Objects.hash(location, callee, className); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof DefaultClassAccessSite)) { + return false; + } + DefaultClassAccessSite other = (DefaultClassAccessSite)obj; + return Objects.equals(location, other.location) && + Objects.equals(callee, other.callee) && + Objects.equals(className, other.className); + } } diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java index d0a1c9ddd..bc0ebb378 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java @@ -203,7 +203,7 @@ public class DependencyChecker implements DependencyInfo { private Set classesAddedByRoot = new HashSet<>(); - public ClassDependency linkClass(String className, CallLocation callLocation) { + public ClassDependency linkClass(final String className, final CallLocation callLocation) { ClassDependency dep = classCache.map(className); boolean added = true; if (callLocation != null && callLocation.getMethod() != null) { @@ -215,9 +215,14 @@ public class DependencyChecker implements DependencyInfo { added = classesAddedByRoot.add(className); } if (!dep.isMissing() && added) { - for (DependencyListener listener : listeners) { - listener.classAchieved(agent, className, callLocation); - } + tasks.add(new Runnable() { + @Override + public void run() { + for (DependencyListener listener : listeners) { + listener.classAchieved(agent, className, callLocation); + } + } + }); } return dep; } diff --git a/teavm-core/src/main/java/org/teavm/model/emit/ProgramEmitter.java b/teavm-core/src/main/java/org/teavm/model/emit/ProgramEmitter.java index e97769bf7..13ea4c8a1 100644 --- a/teavm-core/src/main/java/org/teavm/model/emit/ProgramEmitter.java +++ b/teavm-core/src/main/java/org/teavm/model/emit/ProgramEmitter.java @@ -23,6 +23,7 @@ import org.teavm.model.instructions.DoubleConstantInstruction; import org.teavm.model.instructions.ExitInstruction; import org.teavm.model.instructions.FloatConstantInstruction; import org.teavm.model.instructions.GetFieldInstruction; +import org.teavm.model.instructions.InitClassInstruction; import org.teavm.model.instructions.IntegerConstantInstruction; import org.teavm.model.instructions.InvocationType; import org.teavm.model.instructions.InvokeInstruction; @@ -203,6 +204,12 @@ public final class ProgramEmitter { return constructArray(ValueType.parse(type), size); } + public void initClass(String className) { + InitClassInstruction insn = new InitClassInstruction(); + insn.setClassName(className); + addInstruction(insn); + } + public ProgramEmitter jump(BasicBlock block) { JumpInstruction insn = new JumpInstruction(); insn.setTarget(block); diff --git a/teavm-core/src/main/java/org/teavm/parsing/Parser.java b/teavm-core/src/main/java/org/teavm/parsing/Parser.java index e43eb2f2d..4a876d1f5 100644 --- a/teavm-core/src/main/java/org/teavm/parsing/Parser.java +++ b/teavm-core/src/main/java/org/teavm/parsing/Parser.java @@ -15,6 +15,7 @@ */ package org.teavm.parsing; +import java.lang.reflect.Array; import java.util.ArrayList; import java.util.List; import org.objectweb.asm.Opcodes; @@ -197,7 +198,8 @@ public final class Parser { private static AnnotationValue parseAnnotationValue(Object value) { if (value instanceof String[]) { String[] enumInfo = (String[])value; - return new AnnotationValue(new FieldReference(enumInfo[0], enumInfo[1])); + ValueType.Object object = (ValueType.Object)ValueType.parse(enumInfo[0]); + return new AnnotationValue(new FieldReference(object.getClassName(), enumInfo[1])); } else if (value instanceof Type) { Type cls = (Type)value; return new AnnotationValue(ValueType.parse(cls.getDescriptor())); @@ -210,7 +212,8 @@ public final class Parser { return new AnnotationValue(resultList); } else if (value instanceof AnnotationNode) { AnnotationNode annotNode = (AnnotationNode)value; - AnnotationHolder annotation = new AnnotationHolder(annotNode.desc.replace('.', '/')); + ValueType.Object object = (ValueType.Object)ValueType.parse(annotNode.desc); + AnnotationHolder annotation = new AnnotationHolder(object.getClassName()); parseAnnotationValues(annotation, annotNode.values); return new AnnotationValue(annotation); } else if (value instanceof String) { @@ -229,6 +232,14 @@ public final class Parser { return new AnnotationValue((Float)value); } else if (value instanceof Double) { return new AnnotationValue((Double)value); + } else if (value.getClass().isArray()) { + List resultList = new ArrayList<>(); + int size = Array.getLength(value); + for (int i = 0; i < size; ++i) { + Object item = Array.get(value, i); + resultList.add(parseAnnotationValue(item)); + } + return new AnnotationValue(resultList); } else { throw new AssertionError(); } diff --git a/teavm-tests/src/test/java/org/teavm/classlib/java/lang/ClassTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/lang/ClassTest.java index 29abcf039..8442bd37f 100644 --- a/teavm-tests/src/test/java/org/teavm/classlib/java/lang/ClassTest.java +++ b/teavm-tests/src/test/java/org/teavm/classlib/java/lang/ClassTest.java @@ -15,6 +15,7 @@ */ package org.teavm.classlib.java.lang; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; @@ -127,6 +128,26 @@ public class ClassTest { assertEquals(3, annot.x()); } + @Test + public void annotationFieldTypesSupported() { + AnnotWithVariousFields annot = D.class.getAnnotation(AnnotWithVariousFields.class); + assertEquals(true, annot.a()); + assertEquals((byte)2, annot.b()); + assertEquals((short)3, annot.c()); + assertEquals(4, annot.d()); + assertEquals(5L, annot.e()); + assertEquals(6.5, annot.f(), 0.01); + assertEquals(7.2, annot.g(), 0.01); + assertArrayEquals(new int[] { 2, 3 }, annot.h()); + assertEquals(RetentionPolicy.CLASS, annot.i()); + assertEquals(Retention.class, annot.j().annotationType()); + assertEquals(1, annot.k().length); + assertEquals(RetentionPolicy.RUNTIME, annot.k()[0].value()); + assertEquals("foo", annot.l()); + assertArrayEquals(new String[] { "bar" }, annot.m()); + assertEquals(Integer.class, annot.n()); + } + @TestAnnot private static class A { } @@ -139,6 +160,12 @@ public class ClassTest { private static class C { } + @AnnotWithVariousFields(a = true, b = 2, c = 3, d = 4, e = 5, f = 6.5f, g = 7.2, h = { 2, 3 }, + i = RetentionPolicy.CLASS, j = @Retention(RetentionPolicy.SOURCE), + k = { @Retention(RetentionPolicy.RUNTIME) }, l = "foo", m = "bar", n = Integer.class) + private static class D { + } + @Retention(RetentionPolicy.RUNTIME) static @interface TestAnnot { } @@ -147,4 +174,35 @@ public class ClassTest { static @interface AnnotWithDefaultField { int x() default 2; } + + @Retention(RetentionPolicy.RUNTIME) + static @interface AnnotWithVariousFields { + boolean a(); + + byte b(); + + short c(); + + int d(); + + long e(); + + float f(); + + double g(); + + int[] h(); + + RetentionPolicy i(); + + Retention j(); + + Retention[] k(); + + String l(); + + String[] m(); + + Class n(); + } }