Skip to content

Commit 30ab6bf

Browse files
committed
wasm gc: support Class.getAnnotations
1 parent b609828 commit 30ab6bf

File tree

17 files changed

+619
-106
lines changed

17 files changed

+619
-106
lines changed

classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@
5252
import org.teavm.classlib.impl.unicode.NumberFormatMetadataGenerator;
5353
import org.teavm.classlib.impl.unicode.TimeZoneLocalizationGenerator;
5454
import org.teavm.classlib.java.lang.CharacterMetadataGenerator;
55-
import org.teavm.classlib.java.lang.reflect.AnnotationDependencyListener;
55+
import org.teavm.classlib.java.lang.reflect.JSAnnotationDependencyListener;
56+
import org.teavm.classlib.java.lang.reflect.WasmGCAnnotationDependencyListener;
5657
import org.teavm.interop.PlatformMarker;
5758
import org.teavm.model.MethodReference;
5859
import org.teavm.model.ValueType;
@@ -84,6 +85,7 @@ public void install(TeaVMHost host) {
8485
if (jsExtension != null) {
8586
jsExtension.add(loadServicesMethod, new ServiceLoaderJSSupport());
8687
jsExtension.addVirtualMethods(new AnnotationVirtualMethods());
88+
host.add(new JSAnnotationDependencyListener());
8789
}
8890

8991
TeaVMCHost cHost = host.getExtension(TeaVMCHost.class);
@@ -99,6 +101,7 @@ public void install(TeaVMHost host) {
99101
var wasmGCHost = host.getExtension(TeaVMWasmGCHost.class);
100102
if (wasmGCHost != null) {
101103
wasmGCHost.addGeneratorFactory(new ServiceLoaderWasmGCSupport());
104+
host.add(new WasmGCAnnotationDependencyListener());
102105
}
103106
}
104107

@@ -108,8 +111,6 @@ public void install(TeaVMHost host) {
108111
host.add(new ReflectionTransformer());
109112
}
110113

111-
host.add(new AnnotationDependencyListener());
112-
113114
LambdaMetafactorySubstitutor lms = new LambdaMetafactorySubstitutor();
114115
host.add(new MethodReference("java.lang.invoke.LambdaMetafactory", "metafactory",
115116
ValueType.object("java.lang.invoke.MethodHandles$Lookup"), ValueType.object("java.lang.String"),

classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -800,8 +800,7 @@ public TAnnotation[] getAnnotations() {
800800
var map = new LinkedHashMap<Class<?>, TAnnotation>();
801801
while (cls != null) {
802802
for (var annot : cls.getDeclaredAnnotations()) {
803-
var platformClass = ((TClass<?>) (Object) annot.annotationType()).platformClass;
804-
if (initial || (platformClass.getMetadata().getFlags() & Flags.INHERITED_ANNOTATION) != 0) {
803+
if (initial || isInherited(annot)) {
805804
map.putIfAbsent(annot.annotationType(), annot);
806805
}
807806
}
@@ -813,17 +812,33 @@ public TAnnotation[] getAnnotations() {
813812
return annotationsCache.clone();
814813
}
815814

815+
private static boolean isInherited(TAnnotation annot) {
816+
if (PlatformDetector.isWebAssemblyGC()) {
817+
var flags = ((TClass<?>) (Object) annot.annotationType()).getWasmGCFlags();
818+
return (flags & WasmGCClassFlags.INHERITED_ANNOTATIONS) != 0;
819+
} else {
820+
var platformClass = ((TClass<?>) (Object) annot.annotationType()).platformClass;
821+
return (platformClass.getMetadata().getFlags() & Flags.INHERITED_ANNOTATION) != 0;
822+
}
823+
}
824+
816825
@Override
817826
public TAnnotation[] getDeclaredAnnotations() {
818827
if (declaredAnnotationsCache == null) {
819-
declaredAnnotationsCache = (TAnnotation[]) Platform.getAnnotations(getPlatformClass());
828+
if (PlatformDetector.isWebAssemblyGC()) {
829+
declaredAnnotationsCache = getDeclaredAnnotationsImpl();
830+
} else {
831+
declaredAnnotationsCache = (TAnnotation[]) Platform.getAnnotations(getPlatformClass());
832+
}
820833
if (declaredAnnotationsCache == null) {
821834
declaredAnnotationsCache = new TAnnotation[0];
822835
}
823836
}
824837
return declaredAnnotationsCache.clone();
825838
}
826839

840+
private native TAnnotation[] getDeclaredAnnotationsImpl();
841+
827842
private void ensureAnnotationsByType() {
828843
if (annotationsByType != null) {
829844
return;
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/*
2+
* Copyright 2015 Alexey Andreev.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.teavm.classlib.java.lang.reflect;
17+
18+
import java.util.ArrayList;
19+
import java.util.List;
20+
import org.teavm.dependency.AbstractDependencyListener;
21+
import org.teavm.dependency.DependencyAgent;
22+
import org.teavm.dependency.MethodDependency;
23+
import org.teavm.model.AccessLevel;
24+
import org.teavm.model.ClassHierarchy;
25+
import org.teavm.model.ClassHolder;
26+
import org.teavm.model.ClassReader;
27+
import org.teavm.model.ElementModifier;
28+
import org.teavm.model.FieldHolder;
29+
import org.teavm.model.MethodHolder;
30+
import org.teavm.model.MethodReader;
31+
import org.teavm.model.ValueType;
32+
import org.teavm.model.emit.ProgramEmitter;
33+
import org.teavm.model.emit.ValueEmitter;
34+
35+
public abstract class BaseAnnotationDependencyListener extends AbstractDependencyListener {
36+
public static final String ANNOTATION_IMPLEMENTOR_SUFFIX = "$$_impl";
37+
private static final ValueType ENUM_TYPE = ValueType.parse(Enum.class);
38+
private boolean enumsAsInts;
39+
40+
public BaseAnnotationDependencyListener(boolean enumsAsInts) {
41+
this.enumsAsInts = enumsAsInts;
42+
}
43+
44+
@Override
45+
public void methodReached(DependencyAgent agent, MethodDependency method) {
46+
ValueType type = method.getMethod().getResultType();
47+
while (type instanceof ValueType.Array) {
48+
type = ((ValueType.Array) type).getItemType();
49+
}
50+
if (type instanceof ValueType.Object) {
51+
String className = ((ValueType.Object) type).getClassName();
52+
ClassReader cls = agent.getClassSource().get(className);
53+
if (cls != null && cls.hasModifier(ElementModifier.ANNOTATION)) {
54+
agent.linkClass(className);
55+
}
56+
}
57+
}
58+
59+
protected final String getAnnotationImplementor(DependencyAgent agent, String annotationType) {
60+
String implementorName = annotationType + ANNOTATION_IMPLEMENTOR_SUFFIX;
61+
if (agent.getClassSource().get(implementorName) == null) {
62+
ClassHolder implementor = createImplementor(agent.getClassHierarchy(), annotationType, implementorName);
63+
agent.submitClass(implementor);
64+
}
65+
return implementorName;
66+
}
67+
68+
private ClassHolder createImplementor(ClassHierarchy hierarchy, String annotationType, String implementorName) {
69+
ClassHolder implementor = new ClassHolder(implementorName);
70+
implementor.setParent("java.lang.Object");
71+
implementor.getInterfaces().add(annotationType);
72+
implementor.getModifiers().add(ElementModifier.FINAL);
73+
implementor.setLevel(AccessLevel.PUBLIC);
74+
75+
ClassReader annotation = hierarchy.getClassSource().get(annotationType);
76+
if (annotation == null) {
77+
return implementor;
78+
}
79+
80+
List<ValueType> ctorSignature = new ArrayList<>();
81+
for (MethodReader methodDecl : annotation.getMethods()) {
82+
if (methodDecl.hasModifier(ElementModifier.STATIC)) {
83+
continue;
84+
}
85+
FieldHolder field = new FieldHolder("$" + methodDecl.getName());
86+
var type = methodDecl.getResultType();
87+
var isEnum = false;
88+
if (enumsAsInts && hierarchy.isSuperType(ENUM_TYPE, type, false)) {
89+
type = ValueType.INTEGER;
90+
isEnum = true;
91+
}
92+
field.setType(type);
93+
field.setLevel(AccessLevel.PRIVATE);
94+
implementor.addField(field);
95+
96+
MethodHolder accessor = new MethodHolder(methodDecl.getDescriptor());
97+
ProgramEmitter pe = ProgramEmitter.create(accessor, hierarchy);
98+
ValueEmitter thisVal = pe.var(0, implementor);
99+
if (isEnum) {
100+
var cacheField = new FieldHolder("enumCache$" + field.getName());
101+
cacheField.setLevel(AccessLevel.PRIVATE);
102+
cacheField.getModifiers().add(ElementModifier.STATIC);
103+
implementor.addField(cacheField);
104+
105+
cacheField.setType(ValueType.arrayOf(methodDecl.getResultType()));
106+
var enumType = ((ValueType.Object) methodDecl.getResultType()).getClassName();
107+
pe.when(pe.getField(cacheField.getReference(), cacheField.getType()).isNull()).thenDo(() -> {
108+
pe.setField(cacheField.getReference(), pe.invoke(enumType, "values", cacheField.getType()));
109+
});
110+
var result = thisVal.getField(field.getName(), field.getType());
111+
pe.getField(cacheField.getReference(), cacheField.getType()).getElement(result).returnValue();
112+
} else {
113+
ValueEmitter result = thisVal.getField(field.getName(), field.getType());
114+
if (field.getType() instanceof ValueType.Array) {
115+
result = result.cloneArray();
116+
result = result.cast(field.getType());
117+
}
118+
result.returnValue();
119+
}
120+
implementor.addMethod(accessor);
121+
122+
ctorSignature.add(field.getType());
123+
}
124+
ctorSignature.add(ValueType.VOID);
125+
126+
MethodHolder ctor = new MethodHolder("<init>", ctorSignature.toArray(new ValueType[0]));
127+
ProgramEmitter pe = ProgramEmitter.create(ctor, hierarchy);
128+
ValueEmitter thisVar = pe.var(0, implementor);
129+
thisVar.invokeSpecial(Object.class, "<init>");
130+
int index = 1;
131+
for (MethodReader methodDecl : annotation.getMethods()) {
132+
if (methodDecl.hasModifier(ElementModifier.STATIC)) {
133+
continue;
134+
}
135+
ValueEmitter param = pe.var(index++, methodDecl.getResultType());
136+
thisVar.setField("$" + methodDecl.getName(), param);
137+
}
138+
pe.exit();
139+
implementor.addMethod(ctor);
140+
141+
MethodHolder annotTypeMethod = new MethodHolder("annotationType", ValueType.parse(Class.class));
142+
pe = ProgramEmitter.create(annotTypeMethod, hierarchy);
143+
pe.constant(ValueType.object(annotationType)).returnValue();
144+
implementor.addMethod(annotTypeMethod);
145+
146+
return implementor;
147+
}
148+
}

classlib/src/main/java/org/teavm/classlib/java/lang/reflect/AnnotationDependencyListener.java renamed to classlib/src/main/java/org/teavm/classlib/java/lang/reflect/JSAnnotationDependencyListener.java

Lines changed: 4 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,15 @@
1919
import java.lang.annotation.Retention;
2020
import java.util.ArrayList;
2121
import java.util.List;
22-
import org.teavm.dependency.AbstractDependencyListener;
2322
import org.teavm.dependency.DependencyAgent;
2423
import org.teavm.dependency.DependencyNode;
2524
import org.teavm.dependency.MethodDependency;
2625
import org.teavm.model.AccessLevel;
2726
import org.teavm.model.AnnotationReader;
2827
import org.teavm.model.AnnotationValue;
29-
import org.teavm.model.ClassHierarchy;
3028
import org.teavm.model.ClassHolder;
3129
import org.teavm.model.ClassReader;
3230
import org.teavm.model.ElementModifier;
33-
import org.teavm.model.FieldHolder;
3431
import org.teavm.model.MethodHolder;
3532
import org.teavm.model.MethodReader;
3633
import org.teavm.model.MethodReference;
@@ -41,93 +38,18 @@
4138
import org.teavm.platform.PlatformAnnotationProvider;
4239
import org.teavm.platform.PlatformClass;
4340

44-
public class AnnotationDependencyListener extends AbstractDependencyListener {
41+
public class JSAnnotationDependencyListener extends BaseAnnotationDependencyListener {
4542
private static final MethodReference GET_ANNOTATIONS_METHOD = new MethodReference(
4643
Platform.class, "getAnnotations", PlatformClass.class, Annotation[].class);
4744
private static final String ANNOTATIONS_READER_SUFFIX = "$$__annotations__$$";
4845

49-
private String getAnnotationImplementor(DependencyAgent agent, String annotationType) {
50-
String implementorName = annotationType + "$$_impl";
51-
if (agent.getClassSource().get(implementorName) == null) {
52-
ClassHolder implementor = createImplementor(agent.getClassHierarchy(), annotationType, implementorName);
53-
agent.submitClass(implementor);
54-
}
55-
return implementorName;
56-
}
57-
58-
private ClassHolder createImplementor(ClassHierarchy hierarchy, String annotationType,
59-
String implementorName) {
60-
ClassHolder implementor = new ClassHolder(implementorName);
61-
implementor.setParent("java.lang.Object");
62-
implementor.getInterfaces().add(annotationType);
63-
implementor.getModifiers().add(ElementModifier.FINAL);
64-
implementor.setLevel(AccessLevel.PUBLIC);
65-
66-
ClassReader annotation = hierarchy.getClassSource().get(annotationType);
67-
if (annotation == null) {
68-
return implementor;
69-
}
70-
71-
List<ValueType> ctorSignature = new ArrayList<>();
72-
for (MethodReader methodDecl : annotation.getMethods()) {
73-
if (methodDecl.hasModifier(ElementModifier.STATIC)) {
74-
continue;
75-
}
76-
FieldHolder field = new FieldHolder("$" + methodDecl.getName());
77-
field.setType(methodDecl.getResultType());
78-
field.setLevel(AccessLevel.PRIVATE);
79-
implementor.addField(field);
80-
81-
MethodHolder accessor = new MethodHolder(methodDecl.getDescriptor());
82-
ProgramEmitter pe = ProgramEmitter.create(accessor, hierarchy);
83-
ValueEmitter thisVal = pe.var(0, implementor);
84-
ValueEmitter result = thisVal.getField(field.getName(), field.getType());
85-
if (field.getType() instanceof ValueType.Array) {
86-
result = result.cloneArray();
87-
}
88-
result.returnValue();
89-
implementor.addMethod(accessor);
90-
91-
ctorSignature.add(field.getType());
92-
}
93-
ctorSignature.add(ValueType.VOID);
94-
95-
MethodHolder ctor = new MethodHolder("<init>", ctorSignature.toArray(new ValueType[0]));
96-
ProgramEmitter pe = ProgramEmitter.create(ctor, hierarchy);
97-
ValueEmitter thisVar = pe.var(0, implementor);
98-
thisVar.invokeSpecial(Object.class, "<init>");
99-
int index = 1;
100-
for (MethodReader methodDecl : annotation.getMethods()) {
101-
if (methodDecl.hasModifier(ElementModifier.STATIC)) {
102-
continue;
103-
}
104-
ValueEmitter param = pe.var(index++, methodDecl.getResultType());
105-
thisVar.setField("$" + methodDecl.getName(), param);
106-
}
107-
pe.exit();
108-
implementor.addMethod(ctor);
109-
110-
MethodHolder annotTypeMethod = new MethodHolder("annotationType", ValueType.parse(Class.class));
111-
pe = ProgramEmitter.create(annotTypeMethod, hierarchy);
112-
pe.constant(ValueType.object(annotationType)).returnValue();
113-
implementor.addMethod(annotTypeMethod);
114-
115-
return implementor;
46+
public JSAnnotationDependencyListener() {
47+
super(false);
11648
}
11749

11850
@Override
11951
public void methodReached(DependencyAgent agent, MethodDependency method) {
120-
ValueType type = method.getMethod().getResultType();
121-
while (type instanceof ValueType.Array) {
122-
type = ((ValueType.Array) type).getItemType();
123-
}
124-
if (type instanceof ValueType.Object) {
125-
String className = ((ValueType.Object) type).getClassName();
126-
ClassReader cls = agent.getClassSource().get(className);
127-
if (cls != null && cls.hasModifier(ElementModifier.ANNOTATION)) {
128-
agent.linkClass(className);
129-
}
130-
}
52+
super.methodReached(agent, method);
13153

13254
if (method.getMethod().hasModifier(ElementModifier.STATIC)
13355
&& method.getMethod().getName().equals("$$__readAnnotations__$$")) {

0 commit comments

Comments
 (0)