mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-03 05:44:10 -08:00
Add reflection support for fields
This commit is contained in:
parent
2aa9d5b24a
commit
31f9ca57ce
|
@ -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();
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -20,8 +20,11 @@ import java.lang.invoke.LambdaMetafactory;
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.invoke.MethodType;
|
import java.lang.invoke.MethodType;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.ServiceLoader;
|
import java.util.ServiceLoader;
|
||||||
import org.teavm.backend.javascript.TeaVMJavaScriptHost;
|
import org.teavm.backend.javascript.TeaVMJavaScriptHost;
|
||||||
|
import org.teavm.classlib.ReflectionSupplier;
|
||||||
import org.teavm.classlib.impl.lambda.LambdaMetafactorySubstitutor;
|
import org.teavm.classlib.impl.lambda.LambdaMetafactorySubstitutor;
|
||||||
import org.teavm.classlib.impl.unicode.CLDRReader;
|
import org.teavm.classlib.impl.unicode.CLDRReader;
|
||||||
import org.teavm.classlib.java.lang.reflect.AnnotationDependencyListener;
|
import org.teavm.classlib.java.lang.reflect.AnnotationDependencyListener;
|
||||||
|
@ -60,5 +63,13 @@ public class JCLPlugin implements TeaVMPlugin {
|
||||||
host.add(new ScalaHacks());
|
host.add(new ScalaHacks());
|
||||||
|
|
||||||
host.add(new NumericClassTransformer());
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,16 +16,30 @@
|
||||||
package org.teavm.classlib.java.lang;
|
package org.teavm.classlib.java.lang;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
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.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.annotation.TAnnotation;
|
||||||
import org.teavm.classlib.java.lang.reflect.TAnnotatedElement;
|
import org.teavm.classlib.java.lang.reflect.TAnnotatedElement;
|
||||||
import org.teavm.dependency.PluggableDependency;
|
import org.teavm.dependency.PluggableDependency;
|
||||||
import org.teavm.interop.Address;
|
import org.teavm.interop.Address;
|
||||||
import org.teavm.interop.DelegateTo;
|
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.Platform;
|
||||||
import org.teavm.platform.PlatformClass;
|
import org.teavm.platform.PlatformClass;
|
||||||
|
import org.teavm.platform.PlatformSequence;
|
||||||
import org.teavm.platform.metadata.ClassResource;
|
import org.teavm.platform.metadata.ClassResource;
|
||||||
import org.teavm.platform.metadata.ClassScopedMetadataProvider;
|
import org.teavm.platform.metadata.ClassScopedMetadataProvider;
|
||||||
import org.teavm.runtime.RuntimeClass;
|
import org.teavm.runtime.RuntimeClass;
|
||||||
|
@ -37,6 +51,9 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
|
||||||
private PlatformClass platformClass;
|
private PlatformClass platformClass;
|
||||||
private TAnnotation[] annotationsCache;
|
private TAnnotation[] annotationsCache;
|
||||||
private Map<TClass<?>, TAnnotation> annotationsByType;
|
private Map<TClass<?>, TAnnotation> annotationsByType;
|
||||||
|
private TField[] declaredFields;
|
||||||
|
private TField[] fields;
|
||||||
|
private static boolean jsFieldsInitialized;
|
||||||
|
|
||||||
private TClass(PlatformClass platformClass) {
|
private TClass(PlatformClass platformClass) {
|
||||||
this.platformClass = platformClass;
|
this.platformClass = platformClass;
|
||||||
|
@ -115,10 +132,123 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
|
||||||
return Platform.isEnum(platformClass);
|
return Platform.isEnum(platformClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isInterface() {
|
||||||
|
return (platformClass.getMetadata().getFlags() & Flags.INTERFACE) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
public TClass<?> getComponentType() {
|
public TClass<?> getComponentType() {
|
||||||
return getClass(Platform.getArrayItem(platformClass));
|
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() {
|
public boolean desiredAssertionStatus() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -128,6 +258,24 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
|
||||||
return (TClass<? super T>) getClass(platformClass.getMetadata().getSuperclass());
|
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")
|
@SuppressWarnings("unchecked")
|
||||||
public T[] getEnumConstants() {
|
public T[] getEnumConstants() {
|
||||||
return isEnum() ? (T[]) Platform.getEnumConstants(platformClass) : null;
|
return isEnum() ? (T[]) Platform.getEnumConstants(platformClass) : null;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,15 +15,56 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.classlib.java.lang.reflect;
|
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.TClass;
|
||||||
|
import org.teavm.classlib.java.lang.TObject;
|
||||||
import org.teavm.classlib.java.lang.annotation.TAnnotation;
|
import org.teavm.classlib.java.lang.annotation.TAnnotation;
|
||||||
|
|
||||||
public interface TAnnotatedElement {
|
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);
|
<T extends TAnnotation> T getAnnotation(TClass<T> annotationClass);
|
||||||
|
|
||||||
TAnnotation[] getAnnotations();
|
TAnnotation[] getAnnotations();
|
||||||
|
|
||||||
TAnnotation[] getDeclaredAnnotations();
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,4 +36,7 @@ public interface PlatformClassMetadata extends JSObject {
|
||||||
|
|
||||||
@JSProperty
|
@JSProperty
|
||||||
boolean isEnum();
|
boolean isEnum();
|
||||||
|
|
||||||
|
@JSProperty
|
||||||
|
int getFlags();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
org.teavm.classlib.support.ReflectionSupplierImpl
|
Loading…
Reference in New Issue
Block a user