mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -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.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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
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