From c4c5635f880847cc30a489be8ed5c31f68b8797b Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 18 Oct 2016 23:51:31 +0300 Subject: [PATCH] Add reflection support for constructors --- .../teavm/classlib/ReflectionSupplier.java | 3 + .../impl/ReflectionDependencyListener.java | 84 ++++++++++-- .../classlib/impl/reflection/Converter.java | 10 +- .../classlib/impl/reflection/JSCallable.java | 26 ++++ .../impl/reflection/JSConstructor.java | 28 ++++ .../classlib/impl/reflection/JSField.java | 12 +- .../impl/reflection/JSFieldGetter.java | 2 +- .../impl/reflection/JSFieldSetter.java | 2 +- .../classlib/impl/reflection/JSMember.java | 30 +++++ .../classlib/java/lang/ClassGenerator.java | 93 ++++++++----- .../org/teavm/classlib/java/lang/TClass.java | 14 +- .../java/lang/reflect/TConstructor.java | 122 ++++++++++++++++++ .../classlib/java/lang/reflect/TField.java | 4 +- .../reflect/TInvocationTargetException.java | 37 ++++++ .../org/teavm/platform/PlatformSequence.java | 5 - .../support/ReflectionSupplierImpl.java | 14 ++ 16 files changed, 417 insertions(+), 69 deletions(-) create mode 100644 classlib/src/main/java/org/teavm/classlib/impl/reflection/JSCallable.java create mode 100644 classlib/src/main/java/org/teavm/classlib/impl/reflection/JSConstructor.java create mode 100644 classlib/src/main/java/org/teavm/classlib/impl/reflection/JSMember.java create mode 100644 classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TConstructor.java create mode 100644 classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TInvocationTargetException.java diff --git a/classlib/src/main/java/org/teavm/classlib/ReflectionSupplier.java b/classlib/src/main/java/org/teavm/classlib/ReflectionSupplier.java index 29fa290ba..99a913068 100644 --- a/classlib/src/main/java/org/teavm/classlib/ReflectionSupplier.java +++ b/classlib/src/main/java/org/teavm/classlib/ReflectionSupplier.java @@ -16,7 +16,10 @@ package org.teavm.classlib; import java.util.Collection; +import org.teavm.model.MethodDescriptor; public interface ReflectionSupplier { Collection getAccessibleFields(ReflectionContext context, String className); + + Collection getAccessibleMethods(ReflectionContext context, String className); } diff --git a/classlib/src/main/java/org/teavm/classlib/impl/ReflectionDependencyListener.java b/classlib/src/main/java/org/teavm/classlib/impl/ReflectionDependencyListener.java index 68ae14aae..8dbdf1f3d 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/ReflectionDependencyListener.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/ReflectionDependencyListener.java @@ -15,9 +15,10 @@ */ package org.teavm.classlib.impl; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; -import java.util.HashMap; -import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -33,6 +34,9 @@ import org.teavm.model.ClassReader; import org.teavm.model.ClassReaderSource; import org.teavm.model.ElementModifier; import org.teavm.model.FieldReader; +import org.teavm.model.MemberReader; +import org.teavm.model.MethodDescriptor; +import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; @@ -40,11 +44,18 @@ public class ReflectionDependencyListener extends AbstractDependencyListener { private List 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 newInstance = new MethodReference(Constructor.class, "newInstance", Object[].class, + Object.class); private MethodReference getFields = new MethodReference(Class.class, "getDeclaredFields", Field[].class); + private MethodReference getConstructors = new MethodReference(Class.class, "getDeclaredConstructors", + Constructor[].class); private boolean fieldGetHandled; private boolean fieldSetHandled; - private Map> accessibleFieldCache = new HashMap<>(); - private Set classesWithReflectableFields = new HashSet<>(); + private boolean newInstanceHandled; + private Map> accessibleFieldCache = new LinkedHashMap<>(); + private Map> accessibleMethodCache = new LinkedHashMap<>(); + private Set classesWithReflectableFields = new LinkedHashSet<>(); + private Set classesWithReflectableMethods = new LinkedHashSet<>(); public ReflectionDependencyListener(List reflectionSuppliers) { this.reflectionSuppliers = reflectionSuppliers; @@ -54,22 +65,38 @@ public class ReflectionDependencyListener extends AbstractDependencyListener { return classesWithReflectableFields; } + public Set getClassesWithReflectableMethods() { + return classesWithReflectableMethods; + } + public Set getAccessibleFields(String className) { return accessibleFieldCache.get(className); } + public Set getAccessibleMethods(String className) { + return accessibleMethodCache.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(newInstance)) { + handleNewInstance(agent, method, location); } else if (method.getReference().equals(getFields)) { method.getVariable(0).getClassValueNode().addConsumer(type -> { if (!type.getName().startsWith("[")) { classesWithReflectableFields.add(type.getName()); } }); + } else if (method.getReference().equals(getConstructors)) { + method.getVariable(0).getClassValueNode().addConsumer(type -> { + if (!type.getName().startsWith("[")) { + classesWithReflectableMethods.add(type.getName()); + } + }); } } @@ -118,9 +145,37 @@ public class ReflectionDependencyListener extends AbstractDependencyListener { }); } - private void linkClassIfNecessary(DependencyAgent agent, FieldReader field, CallLocation location) { - if (field.hasModifier(ElementModifier.STATIC)) { - agent.linkClass(field.getOwnerName(), location).initClass(location); + private void handleNewInstance(DependencyAgent agent, MethodDependency method, CallLocation location) { + if (newInstanceHandled) { + return; + } + newInstanceHandled = true; + + DependencyNode classValueNode = agent.linkMethod(getConstructors, location).getVariable(0).getClassValueNode(); + classValueNode.addConsumer(reflectedType -> { + if (reflectedType.getName().startsWith("[")) { + return; + } + + Set accessibleMethods = getAccessibleMethods(agent, reflectedType.getName()); + ClassReader cls = agent.getClassSource().get(reflectedType.getName()); + for (MethodDescriptor methodDescriptor : accessibleMethods) { + MethodReader calledMethod = cls.getMethod(methodDescriptor); + MethodDependency calledMethodDep = agent.linkMethod(method.getReference(), location); + calledMethodDep.use(); + for (int i = 0; i < methodDescriptor.parameterCount(); ++i) { + propagateSet(agent, methodDescriptor.parameterType(i), method.getVariable(1).getArrayItem(), + calledMethodDep.getVariable(i + 1), location); + } + calledMethodDep.getVariable(0).propagate(reflectedType); + linkClassIfNecessary(agent, calledMethod, location); + } + }); + } + + private void linkClassIfNecessary(DependencyAgent agent, MemberReader member, CallLocation location) { + if (member.hasModifier(ElementModifier.STATIC)) { + agent.linkClass(member.getOwnerName(), location).initClass(location); } } @@ -128,15 +183,28 @@ public class ReflectionDependencyListener extends AbstractDependencyListener { return accessibleFieldCache.computeIfAbsent(className, key -> gatherAccessibleFields(agent, key)); } + private Set getAccessibleMethods(DependencyAgent agent, String className) { + return accessibleMethodCache.computeIfAbsent(className, key -> gatherAccessibleMethods(agent, key)); + } + private Set gatherAccessibleFields(DependencyAgent agent, String className) { ReflectionContextImpl context = new ReflectionContextImpl(agent); - Set fields = new HashSet<>(); + Set fields = new LinkedHashSet<>(); for (ReflectionSupplier supplier : reflectionSuppliers) { fields.addAll(supplier.getAccessibleFields(context, className)); } return fields; } + private Set gatherAccessibleMethods(DependencyAgent agent, String className) { + ReflectionContextImpl context = new ReflectionContextImpl(agent); + Set methods = new LinkedHashSet<>(); + for (ReflectionSupplier supplier : reflectionSuppliers) { + methods.addAll(supplier.getAccessibleMethods(context, className)); + } + return methods; + } + private void propagateGet(DependencyAgent agent, ValueType type, DependencyNode sourceNode, DependencyNode targetNode, CallLocation location) { if (type instanceof ValueType.Primitive) { diff --git a/classlib/src/main/java/org/teavm/classlib/impl/reflection/Converter.java b/classlib/src/main/java/org/teavm/classlib/impl/reflection/Converter.java index fce4a58b6..1c3bd695e 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/reflection/Converter.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/reflection/Converter.java @@ -16,15 +16,19 @@ package org.teavm.classlib.impl.reflection; import org.teavm.backend.javascript.spi.InjectedBy; -import org.teavm.jso.JSObject; +import org.teavm.platform.PlatformObject; +import org.teavm.platform.PlatformSequence; public final class Converter { private Converter() { } @InjectedBy(ConverterInjector.class) - public static native Object toJava(JSObject jsObject); + public static native Object toJava(PlatformObject jsObject); @InjectedBy(ConverterInjector.class) - public static native JSObject fromJava(Object object); + public static native PlatformObject fromJava(Object object); + + @InjectedBy(ConverterInjector.class) + public static native PlatformSequence arrayFromJava(Object[] objects); } diff --git a/classlib/src/main/java/org/teavm/classlib/impl/reflection/JSCallable.java b/classlib/src/main/java/org/teavm/classlib/impl/reflection/JSCallable.java new file mode 100644 index 000000000..420e2afd9 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/impl/reflection/JSCallable.java @@ -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.impl.reflection; + +import org.teavm.jso.JSFunctor; +import org.teavm.jso.JSObject; +import org.teavm.platform.PlatformObject; +import org.teavm.platform.PlatformSequence; + +@JSFunctor +public interface JSCallable extends JSObject { + PlatformObject call(PlatformObject object, PlatformSequence arguments); +} diff --git a/classlib/src/main/java/org/teavm/classlib/impl/reflection/JSConstructor.java b/classlib/src/main/java/org/teavm/classlib/impl/reflection/JSConstructor.java new file mode 100644 index 000000000..a85c5f92a --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/impl/reflection/JSConstructor.java @@ -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.platform.PlatformClass; +import org.teavm.platform.PlatformSequence; + +public interface JSConstructor extends JSMember { + @JSProperty + PlatformSequence getParameterTypes(); + + @JSProperty + JSCallable getCallable(); +} diff --git a/classlib/src/main/java/org/teavm/classlib/impl/reflection/JSField.java b/classlib/src/main/java/org/teavm/classlib/impl/reflection/JSField.java index 004cd21ea..390130ee6 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/reflection/JSField.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/reflection/JSField.java @@ -15,20 +15,10 @@ */ 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(); - +public interface JSField extends JSMember { @JSProperty PlatformClass getType(); diff --git a/classlib/src/main/java/org/teavm/classlib/impl/reflection/JSFieldGetter.java b/classlib/src/main/java/org/teavm/classlib/impl/reflection/JSFieldGetter.java index f712a7160..529d80ed4 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/reflection/JSFieldGetter.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/reflection/JSFieldGetter.java @@ -21,5 +21,5 @@ import org.teavm.platform.PlatformObject; @JSFunctor public interface JSFieldGetter extends JSObject { - JSObject get(PlatformObject instance); + PlatformObject get(PlatformObject instance); } diff --git a/classlib/src/main/java/org/teavm/classlib/impl/reflection/JSFieldSetter.java b/classlib/src/main/java/org/teavm/classlib/impl/reflection/JSFieldSetter.java index 5dc01e367..98ce9f597 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/reflection/JSFieldSetter.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/reflection/JSFieldSetter.java @@ -21,5 +21,5 @@ import org.teavm.platform.PlatformObject; @JSFunctor public interface JSFieldSetter extends JSObject { - void set(PlatformObject instance, JSObject value); + void set(PlatformObject instance, PlatformObject value); } diff --git a/classlib/src/main/java/org/teavm/classlib/impl/reflection/JSMember.java b/classlib/src/main/java/org/teavm/classlib/impl/reflection/JSMember.java new file mode 100644 index 000000000..de4b611f1 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/impl/reflection/JSMember.java @@ -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.jso.JSObject; +import org.teavm.jso.JSProperty; + +public interface JSMember extends JSObject { + @JSProperty + String getName(); + + @JSProperty + int getModifiers(); + + @JSProperty + int getAccessLevel(); +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java b/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java index e348d37ac..21274678a 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java @@ -25,6 +25,7 @@ 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.MemberReader; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; @@ -32,20 +33,23 @@ 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); + case "createMetadata": + generateCreateMetadata(context, writer); break; } } - private void generateCreateJsFields(GeneratorContext context, SourceWriter writer) throws IOException { + private void generateCreateMetadata(GeneratorContext context, SourceWriter writer) throws IOException { ReflectionDependencyListener reflection = context.getService(ReflectionDependencyListener.class); for (String className : reflection.getClassesWithReflectableFields()) { - generateCreateJsFieldsForClass(context, writer, className); + generateCreateFieldsForClass(context, writer, className); + } + for (String className : reflection.getClassesWithReflectableMethods()) { + generateCreateMethodsForClass(context, writer, className); } } - private void generateCreateJsFieldsForClass(GeneratorContext context, SourceWriter writer, String className) + private void generateCreateFieldsForClass(GeneratorContext context, SourceWriter writer, String className) throws IOException { ReflectionDependencyListener reflection = context.getService(ReflectionDependencyListener.class); Set accessibleFields = reflection.getAccessibleFields(className); @@ -53,8 +57,43 @@ public class ClassGenerator implements Generator { ClassReader cls = context.getClassSource().get(className); writer.appendClass(className).append(".$meta.fields").ws().append('=').ws().append('[').indent(); + generateCreateMembers(writer, cls.getFields(), field -> { + appendProperty(writer, "type", false, () -> writer.append(context.typeToClassString(field.getType()))); + + appendProperty(writer, "getter", false, () -> { + if (accessibleFields != null && accessibleFields.contains(field.getName())) { + renderGetter(writer, field); + } else { + writer.append("null"); + } + }); + + appendProperty(writer, "setter", false, () -> { + if (accessibleFields != null && accessibleFields.contains(field.getName())) { + renderSetter(writer, field); + } else { + writer.append("null"); + } + }); + }); + + writer.outdent().append("];").softNewLine(); + } + + private void generateCreateMethodsForClass(GeneratorContext context, SourceWriter writer, String className) + throws IOException { + ReflectionDependencyListener reflection = context.getService(ReflectionDependencyListener.class); + + ClassReader cls = context.getClassSource().get(className); + writer.appendClass(className).append(".$meta.methods").ws().append('=').ws().append('[').indent(); + + writer.outdent().append("];").softNewLine(); + } + + private void generateCreateMembers(SourceWriter writer, Iterable members, + MemberRenderer renderer) throws IOException { boolean first = true; - for (FieldReader field : cls.getFields()) { + for (T member : members) { if (!first) { writer.append(",").ws(); } else { @@ -63,37 +102,25 @@ public class ClassGenerator implements Generator { 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"); - } - + appendProperty(writer, "name", true, () -> writer.append('"') + .append(RenderingUtil.escapeString(member.getName())).append('"')); + appendProperty(writer, "modifiers", false, () -> writer.append(packModifiers(member.readModifiers()))); + appendProperty(writer, "accessLevel", false, () -> writer.append(member.getLevel().ordinal())); + renderer.render(member); writer.outdent().softNewLine().append("}"); } writer.outdent().append("];").softNewLine(); } + private void appendProperty(SourceWriter writer, String name, boolean first, Fragment value) throws IOException { + if (!first) { + writer.append(",").softNewLine(); + } + writer.append(name).ws().append(':').ws(); + value.render(); + } + private void renderGetter(SourceWriter writer, FieldReader field) throws IOException { writer.append("function(obj)").ws().append("{").indent().softNewLine(); initClass(writer, field); @@ -209,6 +236,10 @@ public class ClassGenerator implements Generator { void render() throws IOException; } + private interface MemberRenderer { + void render(T member) throws IOException; + } + private int packModifiers(Set elementModifiers) { ElementModifier[] knownModifiers = ElementModifier.values(); int value = 0; diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java index e52015efa..8511ddba6 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java @@ -53,7 +53,7 @@ public class TClass extends TObject implements TAnnotatedElement { private Map, TAnnotation> annotationsByType; private TField[] declaredFields; private TField[] fields; - private static boolean jsFieldsInitialized; + private static boolean reflectionInitialized; private TClass(PlatformClass platformClass) { this.platformClass = platformClass; @@ -142,7 +142,7 @@ public class TClass extends TObject implements TAnnotatedElement { public TField[] getDeclaredFields() throws TSecurityException { if (declaredFields == null) { - initJsFields(); + initReflection(); JSClass jsClass = (JSClass) getPlatformClass().getMetadata(); JSArray jsFields = jsClass.getFields(); declaredFields = new TField[jsFields.getLength()]; @@ -156,15 +156,15 @@ public class TClass extends TObject implements TAnnotatedElement { return declaredFields; } - private static void initJsFields() { - if (!jsFieldsInitialized) { - jsFieldsInitialized = true; - createJsFields(); + private static void initReflection() { + if (!reflectionInitialized) { + reflectionInitialized = true; + createMetadata(); } } @GeneratedBy(ClassGenerator.class) - private static native void createJsFields(); + private static native void createMetadata(); public TField[] getFields() throws TSecurityException { if (fields == null) { diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TConstructor.java b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TConstructor.java new file mode 100644 index 000000000..ccaa674ac --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TConstructor.java @@ -0,0 +1,122 @@ +/* + * 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.JSCallable; +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.TInstantiationException; +import org.teavm.classlib.java.lang.TObject; +import org.teavm.platform.PlatformObject; +import org.teavm.platform.PlatformSequence; + +public class TConstructor extends TAccessibleObject implements TMember { + private TClass declaringClass; + private String name; + private int modifiers; + private int accessLevel; + private TClass[] parameterTypes; + private JSCallable callable; + + public TConstructor(TClass declaringClass, String name, int modifiers, int accessLevel, + TClass[] parameterTypes, JSCallable callable) { + this.declaringClass = declaringClass; + this.name = name; + this.modifiers = modifiers; + this.accessLevel = accessLevel; + this.parameterTypes = parameterTypes; + this.callable = callable; + } + + @Override + public TClass getDeclaringClass() { + return declaringClass; + } + + @Override + public String getName() { + return name; + } + + @Override + public int getModifiers() { + return Flags.getModifiers(modifiers, accessLevel); + } + + @Override + public boolean isSynthetic() { + return (modifiers & Flags.SYNTHETIC) != 0; + } + + public TClass[] getParameterTypes() { + return parameterTypes.clone(); + } + + public int getParameterCount() { + return parameterTypes.length; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(TModifier.toString(getModifiers())); + if (sb.length() > 0) { + sb.append(' '); + } + sb.append(declaringClass.getName().toString()).append('('); + for (int i = 0; i < parameterTypes.length; ++i) { + if (i > 0) { + sb.append(','); + } + sb.append(parameterTypes[i].getName()); + } + return sb.append(')').toString(); + } + + @SuppressWarnings("unchecked") + public T newInstance(Object... initargs) throws TInstantiationException, TIllegalAccessException, + TIllegalArgumentException, TInvocationTargetException { + if ((modifiers & Flags.ABSTRACT) != 0) { + throw new TInstantiationException(); + } + if (callable == null) { + throw new TIllegalAccessException(); + } + + if (initargs.length != parameterTypes.length) { + throw new TIllegalArgumentException(); + } + for (int i = 0; i < initargs.length; ++i) { + if (initargs[i] != null && !parameterTypes[i].isInstance((TObject) initargs[i])) { + throw new TIllegalArgumentException(); + } + if (parameterTypes[i].isPrimitive() && initargs[i] == null) { + throw new TIllegalArgumentException(); + } + } + + PlatformSequence jsArgs = Converter.arrayFromJava(initargs); + PlatformObject instance = callable.call(null, jsArgs); + return (T) Converter.toJava(instance); + } + + public boolean isVarArgs() { + return (modifiers & Flags.VARARGS) != 0; + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TField.java b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TField.java index e9104567b..c57025bd3 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TField.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TField.java @@ -23,8 +23,8 @@ 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; +import org.teavm.platform.PlatformObject; public class TField extends TAccessibleObject implements TMember { private TClass declaringClass; @@ -90,7 +90,7 @@ public class TField extends TAccessibleObject implements TMember { throw new TIllegalAccessException(); } checkInstance(obj); - JSObject result = getter.get(Platform.getPlatformObject(obj)); + PlatformObject result = getter.get(Platform.getPlatformObject(obj)); return Converter.toJava(result); } diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TInvocationTargetException.java b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TInvocationTargetException.java new file mode 100644 index 000000000..064c3e667 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TInvocationTargetException.java @@ -0,0 +1,37 @@ +/* + * 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.TReflectiveOperationException; +import org.teavm.classlib.java.lang.TString; +import org.teavm.classlib.java.lang.TThrowable; + +public class TInvocationTargetException extends TReflectiveOperationException { + protected TInvocationTargetException() { + } + + public TInvocationTargetException(TThrowable target) { + super(null, target); + } + + public TInvocationTargetException(TThrowable target, TString s) { + super(s, target); + } + + public TThrowable getTargetException() { + return getCause(); + } +} diff --git a/platform/src/main/java/org/teavm/platform/PlatformSequence.java b/platform/src/main/java/org/teavm/platform/PlatformSequence.java index e7faeac85..e62129637 100644 --- a/platform/src/main/java/org/teavm/platform/PlatformSequence.java +++ b/platform/src/main/java/org/teavm/platform/PlatformSequence.java @@ -19,11 +19,6 @@ import org.teavm.jso.JSIndexer; import org.teavm.jso.JSObject; import org.teavm.jso.JSProperty; -/** - * - * @author Alexey Andreev - * @param - */ public interface PlatformSequence extends JSObject { @JSProperty int getLength(); diff --git a/tests/src/test/java/org/teavm/classlib/support/ReflectionSupplierImpl.java b/tests/src/test/java/org/teavm/classlib/support/ReflectionSupplierImpl.java index 406588a4f..bade95073 100644 --- a/tests/src/test/java/org/teavm/classlib/support/ReflectionSupplierImpl.java +++ b/tests/src/test/java/org/teavm/classlib/support/ReflectionSupplierImpl.java @@ -22,6 +22,8 @@ import org.teavm.classlib.ReflectionContext; import org.teavm.classlib.ReflectionSupplier; import org.teavm.model.ClassReader; import org.teavm.model.FieldReader; +import org.teavm.model.MethodDescriptor; +import org.teavm.model.MethodReader; public class ReflectionSupplierImpl implements ReflectionSupplier { @Override @@ -35,4 +37,16 @@ public class ReflectionSupplierImpl implements ReflectionSupplier { } return fields; } + + @Override + public Collection getAccessibleMethods(ReflectionContext context, String className) { + ClassReader cls = context.getClassSource().get(className); + Set methods = new HashSet<>(); + for (MethodReader method : cls.getMethods()) { + if (method.getAnnotations().get(Reflectable.class.getName()) != null) { + methods.add(method.getDescriptor()); + } + } + return methods; + } }