Add reflection support for fields

This commit is contained in:
Alexey Andreev 2016-10-17 00:20:56 +03:00
parent 2aa9d5b24a
commit 31f9ca57ce
23 changed files with 1487 additions and 1 deletions

View File

@ -0,0 +1,24 @@
/*
* Copyright 2016 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;
import org.teavm.model.ClassReaderSource;
public interface ReflectionContext {
ClassLoader getClassLoader();
ClassReaderSource getClassSource();
}

View File

@ -0,0 +1,22 @@
/*
* Copyright 2016 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;
import java.util.Collection;
public interface ReflectionSupplier {
Collection<String> getAccessibleFields(ReflectionContext context, String className);
}

View File

@ -20,8 +20,11 @@ import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;
import org.teavm.backend.javascript.TeaVMJavaScriptHost;
import org.teavm.classlib.ReflectionSupplier;
import org.teavm.classlib.impl.lambda.LambdaMetafactorySubstitutor;
import org.teavm.classlib.impl.unicode.CLDRReader;
import org.teavm.classlib.java.lang.reflect.AnnotationDependencyListener;
@ -60,5 +63,13 @@ public class JCLPlugin implements TeaVMPlugin {
host.add(new ScalaHacks());
host.add(new NumericClassTransformer());
List<ReflectionSupplier> reflectionSuppliers = new ArrayList<>();
for (ReflectionSupplier supplier : ServiceLoader.load(ReflectionSupplier.class, host.getClassLoader())) {
reflectionSuppliers.add(supplier);
}
ReflectionDependencyListener reflection = new ReflectionDependencyListener(reflectionSuppliers);
host.registerService(ReflectionDependencyListener.class, reflection);
host.add(reflection);
}
}

View File

@ -0,0 +1,231 @@
/*
* Copyright 2016 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.impl;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.teavm.classlib.ReflectionContext;
import org.teavm.classlib.ReflectionSupplier;
import org.teavm.dependency.AbstractDependencyListener;
import org.teavm.dependency.DependencyAgent;
import org.teavm.dependency.DependencyNode;
import org.teavm.dependency.FieldDependency;
import org.teavm.dependency.MethodDependency;
import org.teavm.model.CallLocation;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldReader;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
public class ReflectionDependencyListener extends AbstractDependencyListener {
private List<ReflectionSupplier> reflectionSuppliers;
private MethodReference fieldGet = new MethodReference(Field.class, "get", Object.class, Object.class);
private MethodReference fieldSet = new MethodReference(Field.class, "set", Object.class, Object.class, void.class);
private MethodReference getFields = new MethodReference(Class.class, "getDeclaredFields", Field[].class);
private boolean fieldGetHandled;
private boolean fieldSetHandled;
private Map<String, Set<String>> accessibleFieldCache = new HashMap<>();
private Set<String> classesWithReflectableFields = new HashSet<>();
public ReflectionDependencyListener(List<ReflectionSupplier> reflectionSuppliers) {
this.reflectionSuppliers = reflectionSuppliers;
}
public Set<String> getClassesWithReflectableFields() {
return classesWithReflectableFields;
}
public Set<String> getAccessibleFields(String className) {
return accessibleFieldCache.get(className);
}
@Override
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
if (method.getReference().equals(fieldGet)) {
handleFieldGet(agent, method, location);
} else if (method.getReference().equals(fieldSet)) {
handleFieldSet(agent, method, location);
} else if (method.getReference().equals(getFields)) {
method.getVariable(0).getClassValueNode().addConsumer(type -> {
if (!type.getName().startsWith("[")) {
classesWithReflectableFields.add(type.getName());
}
});
}
}
private void handleFieldGet(DependencyAgent agent, MethodDependency method, CallLocation location) {
if (fieldGetHandled) {
return;
}
fieldGetHandled = true;
DependencyNode classValueNode = agent.linkMethod(getFields, location).getVariable(0).getClassValueNode();
classValueNode.addConsumer(reflectedType -> {
if (reflectedType.getName().startsWith("[")) {
return;
}
Set<String> accessibleFields = getAccessibleFields(agent, reflectedType.getName());
ClassReader cls = agent.getClassSource().get(reflectedType.getName());
for (String fieldName : accessibleFields) {
FieldReader field = cls.getField(fieldName);
FieldDependency fieldDep = agent.linkField(field.getReference(), location);
propagateGet(agent, field.getType(), fieldDep.getValue(), method.getResult(), location);
linkClassIfNecessary(agent, field, location);
}
});
}
private void handleFieldSet(DependencyAgent agent, MethodDependency method, CallLocation location) {
if (fieldSetHandled) {
return;
}
fieldSetHandled = true;
DependencyNode classValueNode = agent.linkMethod(getFields, location).getVariable(0).getClassValueNode();
classValueNode.addConsumer(reflectedType -> {
if (reflectedType.getName().startsWith("[")) {
return;
}
Set<String> accessibleFields = getAccessibleFields(agent, reflectedType.getName());
ClassReader cls = agent.getClassSource().get(reflectedType.getName());
for (String fieldName : accessibleFields) {
FieldReader field = cls.getField(fieldName);
FieldDependency fieldDep = agent.linkField(field.getReference(), location);
propagateSet(agent, field.getType(), method.getVariable(2), fieldDep.getValue(), location);
linkClassIfNecessary(agent, field, location);
}
});
}
private void linkClassIfNecessary(DependencyAgent agent, FieldReader field, CallLocation location) {
if (field.hasModifier(ElementModifier.STATIC)) {
agent.linkClass(field.getOwnerName(), location).initClass(location);
}
}
private Set<String> getAccessibleFields(DependencyAgent agent, String className) {
return accessibleFieldCache.computeIfAbsent(className, key -> gatherAccessibleFields(agent, key));
}
private Set<String> gatherAccessibleFields(DependencyAgent agent, String className) {
ReflectionContextImpl context = new ReflectionContextImpl(agent);
Set<String> fields = new HashSet<>();
for (ReflectionSupplier supplier : reflectionSuppliers) {
fields.addAll(supplier.getAccessibleFields(context, className));
}
return fields;
}
private void propagateGet(DependencyAgent agent, ValueType type, DependencyNode sourceNode,
DependencyNode targetNode, CallLocation location) {
if (type instanceof ValueType.Primitive) {
MethodReference boxMethod;
switch (((ValueType.Primitive) type).getKind()) {
case BOOLEAN:
boxMethod = new MethodReference(Boolean.class, "valueOf", boolean.class, Boolean.class);
break;
case BYTE:
boxMethod = new MethodReference(Byte.class, "valueOf", byte.class, Byte.class);
break;
case SHORT:
boxMethod = new MethodReference(Short.class, "valueOf", short.class, Short.class);
break;
case CHARACTER:
boxMethod = new MethodReference(Character.class, "valueOf", char.class, Character.class);
break;
case INTEGER:
boxMethod = new MethodReference(Integer.class, "valueOf", int.class, Integer.class);
break;
case FLOAT:
boxMethod = new MethodReference(Float.class, "valueOf", float.class, Float.class);
break;
case DOUBLE:
boxMethod = new MethodReference(Double.class, "valueOf", double.class, Double.class);
break;
default:
throw new AssertionError(type.toString());
}
MethodDependency boxMethodDep = agent.linkMethod(boxMethod, location);
boxMethodDep.use();
boxMethodDep.getResult().connect(targetNode);
} else if (type instanceof ValueType.Array || type instanceof ValueType.Object) {
sourceNode.connect(targetNode);
}
}
private void propagateSet(DependencyAgent agent, ValueType type, DependencyNode sourceNode,
DependencyNode targetNode, CallLocation location) {
if (type instanceof ValueType.Primitive) {
MethodReference unboxMethod;
switch (((ValueType.Primitive) type).getKind()) {
case BOOLEAN:
unboxMethod = new MethodReference(Boolean.class, "booleanValue", boolean.class);
break;
case BYTE:
unboxMethod = new MethodReference(Byte.class, "byteValue", byte.class);
break;
case SHORT:
unboxMethod = new MethodReference(Short.class, "shortValue", short.class);
break;
case CHARACTER:
unboxMethod = new MethodReference(Character.class, "charValue", char.class);
break;
case INTEGER:
unboxMethod = new MethodReference(Integer.class, "intValue", int.class);
break;
case FLOAT:
unboxMethod = new MethodReference(Float.class, "floatValue", float.class);
break;
case DOUBLE:
unboxMethod = new MethodReference(Double.class, "doubleOf", double.class);
break;
default:
throw new AssertionError(type.toString());
}
MethodDependency unboxMethodDep = agent.linkMethod(unboxMethod, location);
unboxMethodDep.use();
sourceNode.connect(unboxMethodDep.getResult());
} else if (type instanceof ValueType.Array || type instanceof ValueType.Object) {
sourceNode.connect(targetNode);
}
}
private static class ReflectionContextImpl implements ReflectionContext {
private DependencyAgent agent;
public ReflectionContextImpl(DependencyAgent agent) {
this.agent = agent;
}
@Override
public ClassLoader getClassLoader() {
return agent.getClassLoader();
}
@Override
public ClassReaderSource getClassSource() {
return agent.getClassSource();
}
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2016 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.impl.reflection;
import org.teavm.backend.javascript.spi.InjectedBy;
import org.teavm.jso.JSObject;
public final class Converter {
private Converter() {
}
@InjectedBy(ConverterInjector.class)
public static native Object toJava(JSObject jsObject);
@InjectedBy(ConverterInjector.class)
public static native JSObject fromJava(Object object);
}

View File

@ -0,0 +1,28 @@
/*
* Copyright 2016 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.impl.reflection;
import java.io.IOException;
import org.teavm.backend.javascript.spi.Injector;
import org.teavm.backend.javascript.spi.InjectorContext;
import org.teavm.model.MethodReference;
public class ConverterInjector implements Injector {
@Override
public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
context.writeExpr(context.getArgument(0));
}
}

View File

@ -0,0 +1,87 @@
/*
* Copyright 2016 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.impl.reflection;
public final class Flags {
private Flags() {
}
public static final int ABSTRACT = 1;
public static final int ANNOTATION = 2;
public static final int BRIDGE = 4;
public static final int DEPRECATED = 8;
public static final int ENUM = 16;
public static final int FINAL = 32;
public static final int INTERFACE = 64;
public static final int NATIVE = 128;
public static final int STATIC = 256;
public static final int STRICT = 512;
public static final int SUPER = 1024;
public static final int SYNCHRONIZED = 2048;
public static final int SYNTHETIC = 4096;
public static final int TRANSIENT = 8192;
public static final int VARARGS = 16384;
public static final int VOLATILE = 32768;
public static final int PACKAGE_PRIVATE = 0;
public static final int PRIVATE = 1;
public static final int PROTECTED = 2;
public static final int PUBLIC = 3;
public static int getModifiers(int flags, int access) {
int modifiers = 0;
switch (access) {
case PUBLIC:
modifiers |= 1;
break;
case PRIVATE:
modifiers |= 2;
break;
case PROTECTED:
modifiers |= 4;
break;
}
// static
modifiers |= (flags >>> 5) & 8;
// final
modifiers |= (flags >>> 1) & 16;
// synchronized
modifiers |= (flags >>> 5) & 32;
// volatile
modifiers |= (flags >>> 9) & 64;
// transient
modifiers |= (flags >>> 6) & 128;
// native
modifiers |= (flags << 1) & 256;
// interface
modifiers |= (flags << 3) & 512;
// abstract
modifiers |= (flags << 10) & 1024;
// strict
modifiers |= (flags << 2) & 2048;
return modifiers;
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright 2016 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.impl.reflection;
import org.teavm.jso.JSProperty;
import org.teavm.jso.core.JSArray;
import org.teavm.platform.PlatformClassMetadata;
public interface JSClass extends PlatformClassMetadata {
@JSProperty
JSArray<JSField> getFields();
@JSProperty
void setFields(JSArray<JSField> fields);
}

View File

@ -0,0 +1,40 @@
/*
* Copyright 2016 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.impl.reflection;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
import org.teavm.platform.PlatformClass;
public interface JSField extends JSObject {
@JSProperty
String getName();
@JSProperty
int getModifiers();
@JSProperty
int getAccessLevel();
@JSProperty
PlatformClass getType();
@JSProperty
JSFieldGetter getGetter();
@JSProperty
JSFieldSetter getSetter();
}

View File

@ -0,0 +1,25 @@
/*
* Copyright 2016 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.impl.reflection;
import org.teavm.jso.JSFunctor;
import org.teavm.jso.JSObject;
import org.teavm.platform.PlatformObject;
@JSFunctor
public interface JSFieldGetter extends JSObject {
JSObject get(PlatformObject instance);
}

View File

@ -0,0 +1,25 @@
/*
* Copyright 2016 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.impl.reflection;
import org.teavm.jso.JSFunctor;
import org.teavm.jso.JSObject;
import org.teavm.platform.PlatformObject;
@JSFunctor
public interface JSFieldSetter extends JSObject {
void set(PlatformObject instance, JSObject value);
}

View File

@ -0,0 +1,225 @@
/*
* Copyright 2016 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;
import java.io.IOException;
import java.util.Set;
import org.teavm.backend.javascript.codegen.SourceWriter;
import org.teavm.backend.javascript.rendering.RenderingUtil;
import org.teavm.backend.javascript.spi.Generator;
import org.teavm.backend.javascript.spi.GeneratorContext;
import org.teavm.classlib.impl.ReflectionDependencyListener;
import org.teavm.model.ClassReader;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldReader;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
public class ClassGenerator implements Generator {
@Override
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
switch (methodRef.getName()) {
case "createJsFields":
generateCreateJsFields(context, writer);
break;
}
}
private void generateCreateJsFields(GeneratorContext context, SourceWriter writer) throws IOException {
ReflectionDependencyListener reflection = context.getService(ReflectionDependencyListener.class);
for (String className : reflection.getClassesWithReflectableFields()) {
generateCreateJsFieldsForClass(context, writer, className);
}
}
private void generateCreateJsFieldsForClass(GeneratorContext context, SourceWriter writer, String className)
throws IOException {
ReflectionDependencyListener reflection = context.getService(ReflectionDependencyListener.class);
Set<String> accessibleFields = reflection.getAccessibleFields(className);
ClassReader cls = context.getClassSource().get(className);
writer.appendClass(className).append(".$meta.fields").ws().append('=').ws().append('[').indent();
boolean first = true;
for (FieldReader field : cls.getFields()) {
if (!first) {
writer.append(",").ws();
} else {
writer.softNewLine();
}
first = false;
writer.append("{").indent().softNewLine();
writer.append("name").ws().append(':').ws().append('"')
.append(RenderingUtil.escapeString(field.getName()))
.append("\",").softNewLine();
writer.append("modifiers").ws().append(':').ws().append(packModifiers(field.readModifiers()))
.append(",").softNewLine();
writer.append("accessLevel").ws().append(':').ws().append(field.getLevel().ordinal())
.append(",").softNewLine();
writer.append("type").ws().append(':').ws().append(context.typeToClassString(field.getType()))
.append(",").softNewLine();
writer.append("getter").ws().append(':').ws();
if (accessibleFields != null && accessibleFields.contains(field.getName())) {
renderGetter(writer, field);
} else {
writer.append("null");
}
writer.append(",").softNewLine();
writer.append("setter").ws().append(':').ws();
if (accessibleFields != null && accessibleFields.contains(field.getName())) {
renderSetter(writer, field);
} else {
writer.append("null");
}
writer.outdent().softNewLine().append("}");
}
writer.outdent().append("];").softNewLine();
}
private void renderGetter(SourceWriter writer, FieldReader field) throws IOException {
writer.append("function(obj)").ws().append("{").indent().softNewLine();
initClass(writer, field);
writer.append("return ");
boxIfNecessary(writer, field.getType(), () -> fieldAccess(writer, field));
writer.append(";").softNewLine();
writer.outdent().append("}");
}
private void renderSetter(SourceWriter writer, FieldReader field) throws IOException {
writer.append("function(obj,").ws().append("val)").ws().append("{").indent().softNewLine();
initClass(writer, field);
fieldAccess(writer, field);
writer.ws().append('=').ws();
unboxIfNecessary(writer, field.getType(), () -> writer.append("val"));
writer.append(";").softNewLine();
writer.outdent().append("}");
}
private void initClass(SourceWriter writer, FieldReader field) throws IOException {
if (field.hasModifier(ElementModifier.STATIC)) {
writer.appendClass(field.getOwnerName()).append("_$callClinit();").softNewLine();
}
}
private void fieldAccess(SourceWriter writer, FieldReader field) throws IOException {
if (field.hasModifier(ElementModifier.STATIC)) {
writer.appendStaticField(field.getReference());
} else {
writer.append("obj.").appendField(field.getReference());
}
}
private void boxIfNecessary(SourceWriter writer, ValueType type, Fragment fragment) throws IOException {
boolean boxed = false;
if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) type).getKind()) {
case BOOLEAN:
writer.appendMethodBody(new MethodReference(Boolean.class, "valueOf", boolean.class,
Boolean.class));
break;
case BYTE:
writer.appendMethodBody(new MethodReference(Byte.class, "valueOf", byte.class, Byte.class));
break;
case SHORT:
writer.appendMethodBody(new MethodReference(Short.class, "valueOf", short.class, Short.class));
break;
case CHARACTER:
writer.appendMethodBody(new MethodReference(Character.class, "valueOf", char.class,
Character.class));
break;
case INTEGER:
writer.appendMethodBody(new MethodReference(Integer.class, "valueOf", int.class, Integer.class));
break;
case LONG:
writer.appendMethodBody(new MethodReference(Long.class, "valueOf", long.class, Long.class));
break;
case FLOAT:
writer.appendMethodBody(new MethodReference(Float.class, "valueOf", float.class, Float.class));
break;
case DOUBLE:
writer.appendMethodBody(new MethodReference(Double.class, "valueOf", double.class, Double.class));
break;
}
writer.append('(');
boxed = true;
}
fragment.render();
if (boxed) {
writer.append(')');
}
}
private void unboxIfNecessary(SourceWriter writer, ValueType type, Fragment fragment) throws IOException {
boolean boxed = false;
if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) type).getKind()) {
case BOOLEAN:
writer.appendMethodBody(new MethodReference(Boolean.class, "booleanValue", boolean.class));
break;
case BYTE:
writer.appendMethodBody(new MethodReference(Byte.class, "byteValue", byte.class));
break;
case SHORT:
writer.appendMethodBody(new MethodReference(Short.class, "shortValue", short.class));
break;
case CHARACTER:
writer.appendMethodBody(new MethodReference(Character.class, "charValue", char.class));
break;
case INTEGER:
writer.appendMethodBody(new MethodReference(Integer.class, "intValue", int.class));
break;
case LONG:
writer.appendMethodBody(new MethodReference(Long.class, "longValue", long.class));
break;
case FLOAT:
writer.appendMethodBody(new MethodReference(Float.class, "floatValue", float.class));
break;
case DOUBLE:
writer.appendMethodBody(new MethodReference(Double.class, "doubleValue", double.class));
break;
}
writer.append('(');
boxed = true;
}
fragment.render();
if (boxed) {
writer.append(')');
}
}
private interface Fragment {
void render() throws IOException;
}
private int packModifiers(Set<ElementModifier> elementModifiers) {
ElementModifier[] knownModifiers = ElementModifier.values();
int value = 0;
int bit = 1;
for (int i = 0; i < knownModifiers.length; ++i) {
ElementModifier modifier = knownModifiers[i];
if (elementModifiers.contains(modifier)) {
value |= bit;
}
bit <<= 1;
}
return value;
}
}

View File

@ -16,16 +16,30 @@
package org.teavm.classlib.java.lang;
import java.io.InputStream;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.teavm.backend.javascript.spi.GeneratedBy;
import org.teavm.classlib.impl.DeclaringClassMetadataGenerator;
import org.teavm.classlib.impl.reflection.Flags;
import org.teavm.classlib.impl.reflection.JSClass;
import org.teavm.classlib.impl.reflection.JSField;
import org.teavm.classlib.java.lang.annotation.TAnnotation;
import org.teavm.classlib.java.lang.reflect.TAnnotatedElement;
import org.teavm.dependency.PluggableDependency;
import org.teavm.interop.Address;
import org.teavm.interop.DelegateTo;
import org.teavm.classlib.java.lang.reflect.TField;
import org.teavm.classlib.java.lang.reflect.TModifier;
import org.teavm.jso.core.JSArray;
import org.teavm.platform.Platform;
import org.teavm.platform.PlatformClass;
import org.teavm.platform.PlatformSequence;
import org.teavm.platform.metadata.ClassResource;
import org.teavm.platform.metadata.ClassScopedMetadataProvider;
import org.teavm.runtime.RuntimeClass;
@ -37,6 +51,9 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
private PlatformClass platformClass;
private TAnnotation[] annotationsCache;
private Map<TClass<?>, TAnnotation> annotationsByType;
private TField[] declaredFields;
private TField[] fields;
private static boolean jsFieldsInitialized;
private TClass(PlatformClass platformClass) {
this.platformClass = platformClass;
@ -115,10 +132,123 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
return Platform.isEnum(platformClass);
}
public boolean isInterface() {
return (platformClass.getMetadata().getFlags() & Flags.INTERFACE) != 0;
}
public TClass<?> getComponentType() {
return getClass(Platform.getArrayItem(platformClass));
}
public TField[] getDeclaredFields() throws TSecurityException {
if (declaredFields == null) {
initJsFields();
JSClass jsClass = (JSClass) getPlatformClass().getMetadata();
JSArray<JSField> jsFields = jsClass.getFields();
declaredFields = new TField[jsFields.getLength()];
for (int i = 0; i < jsFields.getLength(); ++i) {
JSField jsField = jsFields.get(i);
declaredFields[i] = new TField(this, jsField.getName(), jsField.getModifiers(),
jsField.getAccessLevel(), TClass.getClass(jsField.getType()), jsField.getGetter(),
jsField.getSetter());
}
}
return declaredFields;
}
private static void initJsFields() {
if (!jsFieldsInitialized) {
jsFieldsInitialized = true;
createJsFields();
}
}
@GeneratedBy(ClassGenerator.class)
private static native void createJsFields();
public TField[] getFields() throws TSecurityException {
if (fields == null) {
List<TField> fieldList = new ArrayList<>();
TClass<?> cls = this;
if (cls.isInterface()) {
getFieldsOfInterfaces(cls, fieldList, new HashSet<>());
} else {
while (cls != null) {
for (TField field : declaredFields) {
if (Modifier.isPublic(field.getModifiers())) {
fieldList.add(field);
}
}
cls = cls.getSuperclass();
}
}
fields = fieldList.toArray(new TField[fieldList.size()]);
}
return fields;
}
public TField getDeclaredField(String name) throws TNoSuchFieldError {
for (TField field : getDeclaredFields()) {
if (field.getName().equals(name)) {
return field;
}
}
throw new TNoSuchFieldError();
}
public TField getField(String name) throws TNoSuchFieldError {
TField result = findField(name, new HashSet<>());
if (result == null) {
throw new TNoSuchFieldError();
}
return result;
}
private TField findField(String name, Set<String> visited) {
if (!visited.add(name)) {
return null;
}
for (TField field : getDeclaredFields()) {
if (TModifier.isPublic(field.getModifiers()) && field.getName().equals(name)) {
return field;
}
}
for (TClass<?> iface : getInterfaces()) {
TField field = iface.findField(name, visited);
if (field != null) {
return field;
}
}
TClass<?> superclass = getSuperclass();
if (superclass != null) {
TField field = superclass.findField(name, visited);
if (field != null) {
return field;
}
}
return null;
}
private static void getFieldsOfInterfaces(TClass<?> iface, List<TField> fields, Set<TClass<?>> visited) {
if (!visited.add(iface)) {
return;
}
for (TField field : iface.getDeclaredFields()) {
if (Modifier.isPublic(field.getModifiers())) {
fields.add(field);
}
}
for (TClass<?> superInterface : iface.getInterfaces()) {
getFieldsOfInterfaces(superInterface, fields, visited);
}
}
public boolean desiredAssertionStatus() {
return true;
}
@ -128,6 +258,24 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
return (TClass<? super T>) getClass(platformClass.getMetadata().getSuperclass());
}
@SuppressWarnings("unchecked")
public TClass<? super T>[] getInterfaces() {
PlatformSequence<PlatformClass> supertypes = platformClass.getMetadata().getSupertypes();
TClass<? super T>[] filteredSupertypes = (TClass<? super T>[]) new TClass<?>[supertypes.getLength()];
int j = 0;
for (int i = 0; i < supertypes.getLength(); ++i) {
if (supertypes.get(i) != platformClass.getMetadata().getSuperclass()) {
filteredSupertypes[j++] = (TClass<? super T>) getClass(supertypes.get(j));
}
}
if (filteredSupertypes.length > j) {
filteredSupertypes = Arrays.copyOf(filteredSupertypes, j);
}
return filteredSupertypes;
}
@SuppressWarnings("unchecked")
public T[] getEnumConstants() {
return isEnum() ? (T[]) Platform.getEnumConstants(platformClass) : null;

View File

@ -0,0 +1,52 @@
/*
* Copyright 2016 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 org.teavm.classlib.java.lang.TClass;
import org.teavm.classlib.java.lang.TSecurityException;
import org.teavm.classlib.java.lang.annotation.TAnnotation;
public class TAccessibleObject implements TAnnotatedElement {
protected TAccessibleObject() {
}
public static void setAccessible(TAccessibleObject[] array, boolean flag) throws TSecurityException {
// Do nothing
}
public void setAccessible(boolean flag) throws TSecurityException {
// Do nothing
}
public boolean isAccessible() {
return true;
}
@Override
public <T extends TAnnotation> T getAnnotation(TClass<T> annotationClass) {
return null;
}
@Override
public TAnnotation[] getAnnotations() {
return null;
}
@Override
public TAnnotation[] getDeclaredAnnotations() {
return null;
}
}

View File

@ -15,15 +15,56 @@
*/
package org.teavm.classlib.java.lang.reflect;
import java.util.ArrayList;
import java.util.List;
import org.teavm.classlib.java.lang.TClass;
import org.teavm.classlib.java.lang.TObject;
import org.teavm.classlib.java.lang.annotation.TAnnotation;
public interface TAnnotatedElement {
boolean isAnnotationPresent(TClass<? extends TAnnotation> annotationClass);
default boolean isAnnotationPresent(TClass<? extends TAnnotation> annotationClass) {
return getAnnotation(annotationClass) != null;
}
<T extends TAnnotation> T getAnnotation(TClass<T> annotationClass);
TAnnotation[] getAnnotations();
TAnnotation[] getDeclaredAnnotations();
default <T extends TAnnotation> T[] getAnnotationsByType(TClass<T> annotationClass) {
List<T> result = new ArrayList<>();
Object classAsObject = annotationClass;
for (TAnnotation annot : getAnnotations()) {
if (annot.annotationType() == classAsObject) {
result.add(annotationClass.cast((TObject) annot));
}
}
@SuppressWarnings("unchecked")
T[] array = (T[]) (Object) TArray.newInstance(annotationClass, result.size());
return result.toArray(array);
}
default <T extends TAnnotation> T getDeclaredAnnotation(TClass<T> annotationClass) {
Object classAsObject = annotationClass;
for (TAnnotation annot : getDeclaredAnnotations()) {
if (annot.annotationType() == classAsObject) {
return annotationClass.cast((TObject) annot);
}
}
return null;
}
default <T extends TAnnotation> T[] getDeclaredAnnotationsByType(TClass<T> annotationClass) {
List<T> result = new ArrayList<>();
Object classAsObject = annotationClass;
for (TAnnotation annot : getDeclaredAnnotations()) {
if (annot.annotationType() == classAsObject) {
result.add(annotationClass.cast((TObject) annot));
}
}
@SuppressWarnings("unchecked")
T[] array = (T[]) (Object) TArray.newInstance(annotationClass, result.size());
return result.toArray(array);
}
}

View File

@ -0,0 +1,115 @@
/*
* Copyright 2016 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 org.teavm.classlib.impl.reflection.Converter;
import org.teavm.classlib.impl.reflection.Flags;
import org.teavm.classlib.impl.reflection.JSFieldGetter;
import org.teavm.classlib.impl.reflection.JSFieldSetter;
import org.teavm.classlib.java.lang.TClass;
import org.teavm.classlib.java.lang.TIllegalAccessException;
import org.teavm.classlib.java.lang.TIllegalArgumentException;
import org.teavm.classlib.java.lang.TObject;
import org.teavm.jso.JSObject;
import org.teavm.platform.Platform;
public class TField extends TAccessibleObject implements TMember {
private TClass<?> declaringClass;
private String name;
private int modifiers;
private int accessLevel;
private TClass<?> type;
private JSFieldGetter getter;
private JSFieldSetter setter;
public TField(TClass<?> declaringClass, String name, int modifiers, int accessLevel, TClass<?> type,
JSFieldGetter getter, JSFieldSetter setter) {
this.declaringClass = declaringClass;
this.name = name;
this.modifiers = modifiers;
this.accessLevel = accessLevel;
this.type = type;
this.getter = getter;
this.setter = setter;
}
@Override
public TClass<?> getDeclaringClass() {
return declaringClass;
}
@Override
public String getName() {
return name;
}
@Override
public int getModifiers() {
return Flags.getModifiers(modifiers, accessLevel);
}
public boolean isEnumConstant() {
return (modifiers & Flags.ENUM) != 0;
}
@Override
public boolean isSynthetic() {
return (modifiers & Flags.SYNTHETIC) != 0;
}
public TClass<?> getType() {
return type;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(TModifier.toString(getModifiers()));
if (sb.length() > 0) {
sb.append(' ');
}
sb.append(type.getName()).append(' ').append(declaringClass.getName()).append(".").append(name);
return sb.toString();
}
public Object get(Object obj) throws TIllegalArgumentException, TIllegalAccessException {
if (getter == null) {
throw new TIllegalAccessException();
}
checkInstance(obj);
JSObject result = getter.get(Platform.getPlatformObject(obj));
return Converter.toJava(result);
}
public void set(Object obj, Object value) throws TIllegalArgumentException, TIllegalAccessException {
if (setter == null) {
throw new TIllegalAccessException();
}
checkInstance(obj);
setter.set(Platform.getPlatformObject(obj), Converter.fromJava(value));
}
private void checkInstance(Object obj) {
if ((modifiers & Flags.STATIC) == 0) {
if (obj == null) {
throw new NullPointerException();
}
if (!declaringClass.isInstance((TObject) obj)) {
throw new TIllegalArgumentException();
}
}
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2016 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 org.teavm.classlib.java.lang.TClass;
public interface TMember {
int PUBLIC = 0;
int DECLARED = 1;
TClass<?> getDeclaringClass();
String getName();
int getModifiers();
boolean isSynthetic();
}

View File

@ -0,0 +1,109 @@
/*
* Copyright 2016 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;
public class TModifier {
private static String[] modifierNames;
public static final int PUBLIC = 1;
public static final int PRIVATE = 2;
public static final int PROTECTED = 4;
public static final int STATIC = 8;
public static final int FINAL = 16;
public static final int SYNCHRONIZED = 32;
public static final int VOLATILE = 64;
public static final int TRANSIENT = 128;
public static final int NATIVE = 256;
public static final int INTERFACE = 512;
public static final int ABSTRACT = 1024;
public static final int STRICT = 2048;
private static final int[] canonicalOrder = { PUBLIC, PROTECTED, PRIVATE, ABSTRACT, STATIC, FINAL, TRANSIENT,
VOLATILE, SYNCHRONIZED, NATIVE, STRICT, INTERFACE };
private TModifier() {
}
public static boolean isPublic(int mod) {
return (mod & PUBLIC) != 0;
}
public static boolean isPrivate(int mod) {
return (mod & PRIVATE) != 0;
}
public static boolean isProtected(int mod) {
return (mod & PROTECTED) != 0;
}
public static boolean isStatic(int mod) {
return (mod & STATIC) != 0;
}
public static boolean isFinal(int mod) {
return (mod & FINAL) != 0;
}
public static boolean isSynchronized(int mod) {
return (mod & SYNCHRONIZED) != 0;
}
public static boolean isVolatile(int mod) {
return (mod & VOLATILE) != 0;
}
public static boolean isTransient(int mod) {
return (mod & TRANSIENT) != 0;
}
public static boolean isNative(int mod) {
return (mod & NATIVE) != 0;
}
public static boolean isInterface(int mod) {
return (mod & INTERFACE) != 0;
}
public static boolean isAbstract(int mod) {
return (mod & ABSTRACT) != 0;
}
public static boolean isStrict(int mod) {
return (mod & STRICT) != 0;
}
public static String toString(int mod) {
StringBuilder sb = new StringBuilder();
String[] modifierNames = getModifierNames();
int index = 0;
for (int modifier : canonicalOrder) {
if ((mod & modifier) != 0) {
if (sb.length() > 0) {
sb.append(' ');
}
sb.append(modifierNames[index]);
}
++index;
}
return sb.toString();
}
private static String[] getModifierNames() {
if (modifierNames == null) {
modifierNames = new String[] { "public", "protected", "private", "abstract", "static", "final",
"transient", "volatile", "synchronized", "native", "strictfp", "interface" };
}
return modifierNames;
}
}

View File

@ -36,4 +36,7 @@ public interface PlatformClassMetadata extends JSObject {
@JSProperty
boolean isEnum();
@JSProperty
int getFlags();
}

View File

@ -0,0 +1,146 @@
/*
* Copyright 2016 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.lang.reflect.Field;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.teavm.classlib.support.Reflectable;
import org.teavm.junit.SkipJVM;
import org.teavm.junit.TeaVMTestRunner;
@RunWith(TeaVMTestRunner.class)
public class FieldTest {
@Test
public void fieldsEnumerated() {
new ReflectableType();
StringBuilder sb = new StringBuilder();
for (Field field : ReflectableType.class.getDeclaredFields()) {
sb.append(field).append(";");
}
assertEquals(""
+ "public int org.teavm.classlib.java.lang.reflect.FieldTest$ReflectableType.a;"
+ "private boolean org.teavm.classlib.java.lang.reflect.FieldTest$ReflectableType.b;"
+ "java.lang.Object org.teavm.classlib.java.lang.reflect.FieldTest$ReflectableType.c;"
+ "java.lang.String org.teavm.classlib.java.lang.reflect.FieldTest$ReflectableType.d;"
+ "long org.teavm.classlib.java.lang.reflect.FieldTest$ReflectableType.e;"
+ "private static short org.teavm.classlib.java.lang.reflect.FieldTest$ReflectableType.f;"
+ "static boolean org.teavm.classlib.java.lang.reflect.FieldTest$ReflectableType.initialized;",
sb.toString());
}
@Test
public void fieldRead() throws NoSuchFieldException, IllegalAccessException {
ReflectableType instance = new ReflectableType();
Field field = instance.getClass().getDeclaredField("a");
Object result = field.get(instance);
assertEquals(23, result);
}
@Test
public void fieldWritten() throws NoSuchFieldException, IllegalAccessException {
ReflectableType instance = new ReflectableType();
Field field = instance.getClass().getDeclaredField("a");
field.set(instance, 234);
assertEquals(234, instance.a);
}
@Test(expected = IllegalAccessException.class)
@SkipJVM
public void fieldCannotBeRead() throws NoSuchFieldException, IllegalAccessException {
ReflectableType instance = new ReflectableType();
Field field = instance.getClass().getDeclaredField("e");
field.get(instance);
}
@Test(expected = IllegalAccessException.class)
@SkipJVM
public void fieldCannotBeWritten() throws NoSuchFieldException, IllegalAccessException {
ReflectableType instance = new ReflectableType();
Field field = instance.getClass().getDeclaredField("e");
field.set(instance, 1L);
}
@Test
public void staticFieldRead() throws NoSuchFieldException, IllegalAccessException {
Field field = ReflectableType.class.getDeclaredField("f");
field.setAccessible(true);
Object result = field.get(null);
assertTrue(ReflectableType.initialized);
assertEquals(ReflectableType.f, result);
}
@Test
public void staticFieldWritten() throws NoSuchFieldException, IllegalAccessException {
Field field = ReflectableType.class.getDeclaredField("f");
field.setAccessible(true);
field.set(null, (short) 999);
assertTrue(ReflectableType.initialized);
assertEquals((short) 999, ReflectableType.f);
}
@Test
public void dependencyMaintainedForGet() throws NoSuchFieldException, IllegalAccessException {
ReflectableType instance = new ReflectableType();
instance.c = new Foo(123);
Field field = ReflectableType.class.getDeclaredField("c");
Foo result = (Foo) field.get(instance);
assertEquals(123, result.getValue());
}
@Test
public void dependencyMaintainedForSet() throws NoSuchFieldException, IllegalAccessException {
ReflectableType instance = new ReflectableType();
Field field = ReflectableType.class.getDeclaredField("c");
field.set(instance, new Foo(123));
assertEquals(123, ((Foo) instance.c).getValue());
}
static class ReflectableType {
@Reflectable public int a;
@Reflectable private boolean b;
@Reflectable Object c;
@Reflectable String d;
long e;
@Reflectable private static short f = 99;
static boolean initialized = true;
public ReflectableType() {
a = 23;
b = true;
c = "foo";
d = "bar";
e = 42;
}
}
static class Foo {
int value;
public Foo(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright 2016 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.support;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR })
public @interface Reflectable {
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2016 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.support;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.teavm.classlib.ReflectionContext;
import org.teavm.classlib.ReflectionSupplier;
import org.teavm.model.ClassReader;
import org.teavm.model.FieldReader;
public class ReflectionSupplierImpl implements ReflectionSupplier {
@Override
public Collection<String> getAccessibleFields(ReflectionContext context, String className) {
ClassReader cls = context.getClassSource().get(className);
Set<String> fields = new HashSet<>();
for (FieldReader field : cls.getFields()) {
if (field.getAnnotations().get(Reflectable.class.getName()) != null) {
fields.add(field.getName());
}
}
return fields;
}
}

View File

@ -0,0 +1 @@
org.teavm.classlib.support.ReflectionSupplierImpl