From e1f6bfdaeb7dc75b9d4fef61cfc232cf9aaa038f Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 9 Sep 2015 18:16:37 +0300 Subject: [PATCH] Updating JSO implementation --- .../main/java/org/teavm/model/ValueType.java | 4 + .../main/java/org/teavm/jso/plugin/JS.java | 320 +++++++++--------- .../teavm/jso/plugin/JSNativeGenerator.java | 20 +- .../jso/plugin/JavascriptNativeProcessor.java | 305 +++++++++++------ .../services/org.teavm.vm.spi.TeaVMPlugin | 0 .../org/teavm/jso/core/JSSortFunction.java | 1 + 6 files changed, 379 insertions(+), 271 deletions(-) rename {teavm-jso => teavm-jso-impl}/src/main/resources/META-INF/services/org.teavm.vm.spi.TeaVMPlugin (100%) diff --git a/teavm-core/src/main/java/org/teavm/model/ValueType.java b/teavm-core/src/main/java/org/teavm/model/ValueType.java index 9cc6adacc..5e1dc9982 100644 --- a/teavm-core/src/main/java/org/teavm/model/ValueType.java +++ b/teavm-core/src/main/java/org/teavm/model/ValueType.java @@ -265,6 +265,10 @@ public abstract class ValueType { public abstract boolean isObject(String cls); + public boolean isObject(Class cls) { + return isObject(cls.getName()); + } + private static ValueType parseImpl(String string) { switch (string.charAt(0)) { case 'Z': diff --git a/teavm-jso-impl/src/main/java/org/teavm/jso/plugin/JS.java b/teavm-jso-impl/src/main/java/org/teavm/jso/plugin/JS.java index 8e3bd8901..690385ba9 100644 --- a/teavm-jso-impl/src/main/java/org/teavm/jso/plugin/JS.java +++ b/teavm-jso-impl/src/main/java/org/teavm/jso/plugin/JS.java @@ -17,6 +17,7 @@ package org.teavm.jso.plugin; import java.lang.reflect.Array; import java.util.Iterator; +import java.util.function.Function; import org.teavm.dependency.PluggableDependency; import org.teavm.javascript.spi.GeneratedBy; import org.teavm.javascript.spi.InjectedBy; @@ -28,30 +29,54 @@ import org.teavm.jso.core.JSBoolean; import org.teavm.jso.core.JSNumber; import org.teavm.jso.core.JSString; -/** - *

Container of static methods to manipulate over {@link JSObject}s.

- * - * @author Alexey Andreev - */ final class JS { private JS() { } - /** - * Gets global JavaScript object, that is similar to the window object in the browser. - * @return global object. - */ - @JSBody(params = {}, script = "return window;") - public static native JSObject getGlobal(); + @InjectedBy(JSNativeGenerator.class) + public static native JSObject wrap(byte value); @InjectedBy(JSNativeGenerator.class) - public static native JSObject wrap(String str); + public static native JSObject wrap(short value); @InjectedBy(JSNativeGenerator.class) - public static native JSObject wrap(char c); + public static native JSObject wrap(int value); @InjectedBy(JSNativeGenerator.class) - public static native JSObject marshall(Object obj); + public static native JSObject wrap(char value); + + @InjectedBy(JSNativeGenerator.class) + public static native JSObject wrap(float value); + + @InjectedBy(JSNativeGenerator.class) + public static native JSObject wrap(double value); + + @InjectedBy(JSNativeGenerator.class) + public static native JSObject wrap(boolean value); + + @InjectedBy(JSNativeGenerator.class) + public static native JSObject wrap(String value); + + @InjectedBy(JSNativeGenerator.class) + public static native byte unwrapByte(JSObject value); + + @InjectedBy(JSNativeGenerator.class) + public static native short unwrapShort(JSObject value); + + @InjectedBy(JSNativeGenerator.class) + public static native int unwrapInt(JSObject value); + + @InjectedBy(JSNativeGenerator.class) + public static native float unwrapFloat(JSObject value); + + @InjectedBy(JSNativeGenerator.class) + public static native double unwrapDouble(JSObject value); + + @InjectedBy(JSNativeGenerator.class) + public static native boolean unwrapBoolean(JSObject value); + + @InjectedBy(JSNativeGenerator.class) + public static native String unwrapString(JSObject value); public static JSArray wrap(T[] array) { JSArray result = JSArray.create(array.length); @@ -61,20 +86,20 @@ final class JS { return result; } - public static JSArray> wrap(T[][] array) { - JSArray> result = JSArray.create(array.length); + public static Function> arrayWrapper() { + return JS::wrap; + } + + public static JSArray map(S[] array, Function f) { + JSArray result = JSArray.create(array.length); for (int i = 0; i < array.length; ++i) { - result.set(i, wrap(array[i])); + result.set(i, f.apply(array[i])); } return result; } - public static JSArray>> wrap(T[][][] array) { - JSArray>> result = JSArray.create(array.length); - for (int i = 0; i < array.length; ++i) { - result.set(i, wrap(array[i])); - } - return result; + public static Function> arrayMapper(Function f) { + return array -> map(array, f); } public static JSArray wrap(boolean[] array) { @@ -85,20 +110,8 @@ final class JS { return result; } - public static JSArray> wrap(boolean[][] array) { - JSArray> result = JSArray.create(array.length); - for (int i = 0; i < array.length; ++i) { - result.set(i, wrap(array[i])); - } - return result; - } - - public static JSArray>> wrap(boolean[][][] array) { - JSArray>> result = JSArray.create(array.length); - for (int i = 0; i < array.length; ++i) { - result.set(i, wrap(array[i])); - } - return result; + public static Function> booleanArrayWrapper() { + return JS::wrap; } public static JSArray wrap(byte[] array) { @@ -109,20 +122,8 @@ final class JS { return result; } - public static JSArray> wrap(byte[][] array) { - JSArray> result = JSArray.create(array.length); - for (int i = 0; i < array.length; ++i) { - result.set(i, wrap(array[i])); - } - return result; - } - - public static JSArray>> wrap(byte[][][] array) { - JSArray>> result = JSArray.create(array.length); - for (int i = 0; i < array.length; ++i) { - result.set(i, wrap(array[i])); - } - return result; + public static Function> byteArrayWrapper() { + return JS::wrap; } public static JSArray wrap(short[] array) { @@ -133,20 +134,8 @@ final class JS { return result; } - public static JSArray> wrap(short[][] array) { - JSArray> result = JSArray.create(array.length); - for (int i = 0; i < array.length; ++i) { - result.set(i, wrap(array[i])); - } - return result; - } - - public static JSArray>> wrap(short[][][] array) { - JSArray>> result = JSArray.create(array.length); - for (int i = 0; i < array.length; ++i) { - result.set(i, wrap(array[i])); - } - return result; + public static Function> shortArrayWrapper() { + return JS::wrap; } public static JSArray wrap(char[] array) { @@ -157,20 +146,8 @@ final class JS { return result; } - public static JSArray> wrap(char[][] array) { - JSArray> result = JSArray.create(array.length); - for (int i = 0; i < array.length; ++i) { - result.set(i, wrap(array[i])); - } - return result; - } - - public static JSArray>> wrap(char[][][] array) { - JSArray>> result = JSArray.create(array.length); - for (int i = 0; i < array.length; ++i) { - result.set(i, wrap(array[i])); - } - return result; + public static Function> charArrayWrapper() { + return JS::wrap; } public static JSArray wrap(int[] array) { @@ -181,20 +158,8 @@ final class JS { return result; } - public static JSArray> wrap(int[][] array) { - JSArray> result = JSArray.create(array.length); - for (int i = 0; i < array.length; ++i) { - result.set(i, wrap(array[i])); - } - return result; - } - - public static JSArray>> wrap(int[][][] array) { - JSArray>> result = JSArray.create(array.length); - for (int i = 0; i < array.length; ++i) { - result.set(i, wrap(array[i])); - } - return result; + public static Function> intArrayWrapper() { + return JS::wrap; } public static JSArray wrap(String[] array) { @@ -205,20 +170,8 @@ final class JS { return result; } - public static JSArray> wrap(String[][] array) { - JSArray> result = JSArray.create(array.length); - for (int i = 0; i < array.length; ++i) { - result.set(i, wrap(array[i])); - } - return result; - } - - public static JSArray>> wrap(String[][][] array) { - JSArray>> result = JSArray.create(array.length); - for (int i = 0; i < array.length; ++i) { - result.set(i, wrap(array[i])); - } - return result; + public static Function> stringArrayWrapper() { + return JS::wrap; } public static JSArray wrap(float[] array) { @@ -229,20 +182,8 @@ final class JS { return result; } - public static JSArray> wrap(float[][] array) { - JSArray> result = JSArray.create(array.length); - for (int i = 0; i < array.length; ++i) { - result.set(i, wrap(array[i])); - } - return result; - } - - public static JSArray>> wrap(float[][][] array) { - JSArray>> result = JSArray.create(array.length); - for (int i = 0; i < array.length; ++i) { - result.set(i, wrap(array[i])); - } - return result; + public static Function> floatArrayWrapper() { + return JS::wrap; } public static JSArray wrap(double[] array) { @@ -253,30 +194,11 @@ final class JS { return result; } - public static JSArray> wrap(double[][] array) { - JSArray> result = JSArray.create(array.length); - for (int i = 0; i < array.length; ++i) { - result.set(i, wrap(array[i])); - } - return result; + public static Function> doubleArrayWrapper() { + return JS::wrap; } - public static JSArray>> wrap(double[][][] array) { - JSArray>> result = JSArray.create(array.length); - for (int i = 0; i < array.length; ++i) { - result.set(i, wrap(array[i])); - } - return result; - } - - @InjectedBy(JSNativeGenerator.class) - @PluggableDependency(JSNativeGenerator.class) - public static native String unwrapString(JSObject obj); - - @InjectedBy(JSNativeGenerator.class) - public static native char unwrapCharacter(JSObject obj); - - public static T[] unwrapArray(Class type, JSArray array) { + public static T[] unwrapArray(Class type, JSArrayReader array) { @SuppressWarnings("unchecked") T[] result = (T[]) Array.newInstance(type, array.getLength()); for (int i = 0; i < result.length; ++i) { @@ -285,27 +207,118 @@ final class JS { return result; } - public static T[][] unwrapArray2(Class type, JSArray> array) { + public static Function, T[]> arrayUnwrapper(Class type) { + return array -> unwrapArray(type, array); + } + + public static T[] unmapArray(Class type, JSArrayReader array, Function f) { @SuppressWarnings("unchecked") - T[][] result = (T[][]) Array.newInstance(Array.newInstance(type, 0).getClass(), array.getLength()); + T[] result = (T[]) Array.newInstance(type, array.getLength()); for (int i = 0; i < result.length; ++i) { - result[i] = unwrapArray(type, array.get(i)); + result[i] = f.apply(array.get(i)); } return result; } - public static T[][][] unwrapArray3(Class type, JSArray>> array) { - Class baseType = Array.newInstance(type, 0).getClass(); - @SuppressWarnings("unchecked") - T[][][] result = (T[][][]) Array.newInstance(Array.newInstance(baseType, 0).getClass(), array.getLength()); + public static Function, T[]> arrayUnmapper(Class type, Function f) { + return array -> unmapArray(type, array, f); + } + + public static boolean[] unwrapBooleanArray(JSArrayReader array) { + boolean[] result = new boolean[array.getLength()]; for (int i = 0; i < result.length; ++i) { - result[i] = unwrapArray2(type, array.get(i)); + result[i] = array.get(i).booleanValue(); } return result; } - @JSBody(params = "obj", script = "return typeof(obj) === 'undefined';") - public static native boolean isUndefined(JSObject obj); + public static Function, boolean[]> booleanArrayUnwrapper() { + return JS::unwrapBooleanArray; + } + + public static byte[] unwrapByteArray(JSArrayReader array) { + byte[] result = new byte[array.getLength()]; + for (int i = 0; i < result.length; ++i) { + result[i] = array.get(i).byteValue(); + } + return result; + } + + public static Function, byte[]> byteArrayUnwrapper() { + return JS::unwrapByteArray; + } + + public static short[] unwrapShortArray(JSArrayReader array) { + short[] result = new short[array.getLength()]; + for (int i = 0; i < result.length; ++i) { + result[i] = array.get(i).shortValue(); + } + return result; + } + + public static Function, short[]> shortArrayUnwrapper() { + return JS::unwrapShortArray; + } + + public static int[] unwrapIntArray(JSArrayReader array) { + int[] result = new int[array.getLength()]; + for (int i = 0; i < result.length; ++i) { + result[i] = array.get(i).intValue(); + } + return result; + } + + public static Function, int[]> intArrayUnwrapper() { + return JS::unwrapIntArray; + } + + public static char[] unwrapCharArray(JSArrayReader array) { + char[] result = new char[array.getLength()]; + for (int i = 0; i < result.length; ++i) { + result[i] = array.get(i).charValue(); + } + return result; + } + + public static Function, char[]> charArrayUnwrapper() { + return JS::unwrapCharArray; + } + + public static float[] unwrapFloatArray(JSArrayReader array) { + float[] result = new float[array.getLength()]; + for (int i = 0; i < result.length; ++i) { + result[i] = array.get(i).floatValue(); + } + return result; + } + + public static Function, float[]> floatArrayUnwrapper() { + return JS::unwrapFloatArray; + } + + public static double[] unwrapDoubleArray(JSArrayReader array) { + double[] result = new double[array.getLength()]; + for (int i = 0; i < result.length; ++i) { + result[i] = array.get(i).doubleValue(); + } + return result; + } + + public static Function, double[]> doubleArrayUnwrapper() { + return JS::unwrapDoubleArray; + } + + public static String[] unwrapStringArray(JSArrayReader array) { + String[] result = new String[array.getLength()]; + for (int i = 0; i < result.length; ++i) { + result[i] = array.get(i).stringValue(); + } + return result; + } + + public static Function, String[]> stringArrayUnwrapper() { + return JS::unwrapStringArray; + } @InjectedBy(JSNativeGenerator.class) @PluggableDependency(JSNativeGenerator.class) @@ -333,7 +346,6 @@ final class JS { public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e); - @InjectedBy(JSNativeGenerator.class) @PluggableDependency(JSNativeGenerator.class) public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c, diff --git a/teavm-jso-impl/src/main/java/org/teavm/jso/plugin/JSNativeGenerator.java b/teavm-jso-impl/src/main/java/org/teavm/jso/plugin/JSNativeGenerator.java index 85c4b123f..9406bfb08 100644 --- a/teavm-jso-impl/src/main/java/org/teavm/jso/plugin/JSNativeGenerator.java +++ b/teavm-jso-impl/src/main/java/org/teavm/jso/plugin/JSNativeGenerator.java @@ -17,7 +17,9 @@ package org.teavm.jso.plugin; import java.io.IOException; import org.teavm.codegen.SourceWriter; -import org.teavm.dependency.*; +import org.teavm.dependency.DependencyAgent; +import org.teavm.dependency.DependencyPlugin; +import org.teavm.dependency.MethodDependency; import org.teavm.javascript.Renderer; import org.teavm.javascript.ast.ConstantExpr; import org.teavm.javascript.ast.Expr; @@ -72,19 +74,6 @@ public class JSNativeGenerator implements Injector, DependencyPlugin, Generator public void generate(InjectorContext context, MethodReference methodRef) throws IOException { SourceWriter writer = context.getWriter(); switch (methodRef.getName()) { - case "getGlobal": - writer.append("window"); - break; - case "isUndefined": - writer.append("("); - context.writeExpr(context.getArgument(0)); - writer.ws().append("===").ws().append("undefined)"); - break; - case "getTypeName": - writer.append("(typeof "); - context.writeExpr(context.getArgument(0)); - writer.append(")"); - break; case "get": context.writeExpr(context.getArgument(0)); renderProperty(context.getArgument(1), context); @@ -122,9 +111,6 @@ public class JSNativeGenerator implements Injector, DependencyPlugin, Generator } writer.append("))"); break; - case "marshall": - context.writeExpr(context.getArgument(0)); - break; case "wrap": if (methodRef.getDescriptor().parameterType(0).isObject("java.lang.String")) { if (context.getArgument(0) instanceof ConstantExpr) { diff --git a/teavm-jso-impl/src/main/java/org/teavm/jso/plugin/JavascriptNativeProcessor.java b/teavm-jso-impl/src/main/java/org/teavm/jso/plugin/JavascriptNativeProcessor.java index 7505162f8..e4a822f27 100644 --- a/teavm-jso-impl/src/main/java/org/teavm/jso/plugin/JavascriptNativeProcessor.java +++ b/teavm-jso-impl/src/main/java/org/teavm/jso/plugin/JavascriptNativeProcessor.java @@ -22,6 +22,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Function; import org.teavm.diagnostics.Diagnostics; import org.teavm.javascript.spi.GeneratedBy; import org.teavm.javascript.spi.Sync; @@ -33,6 +34,7 @@ import org.teavm.jso.JSMethod; import org.teavm.jso.JSObject; import org.teavm.jso.JSProperty; import org.teavm.jso.core.JSArray; +import org.teavm.jso.core.JSArrayReader; import org.teavm.model.AccessLevel; import org.teavm.model.AnnotationHolder; import org.teavm.model.AnnotationReader; @@ -667,28 +669,136 @@ class JavascriptNativeProcessor { ++degree; itemType = ((ValueType.Array) itemType).getItemType(); } - if (degree > 3) { - diagnostics.error(location, "Unsupported type: {{t0}}", type); - return var; + + CastInstruction castInsn = new CastInstruction(); + castInsn.setValue(var); + castInsn.setTargetType(ValueType.parse(JSArrayReader.class)); + var = program.createVariable(); + castInsn.setReceiver(var); + castInsn.setLocation(location.getSourceLocation()); + replacement.add(castInsn); + + var = degree == 1 + ? unwrapSingleDimensionArray(location, var, itemType) + : unwrapMultiDimensionArray(location, var, itemType, degree); + + return var; + } + + private Variable unwrapSingleDimensionArray(CallLocation location, Variable var, ValueType type) { + Variable result = program.createVariable(); + + InvokeInstruction insn = new InvokeInstruction(); + insn.setMethod(singleDimensionArrayUnwrapper(type)); + insn.setType(InvocationType.SPECIAL); + + if (insn.getMethod().parameterCount() == 2) { + Variable cls = program.createVariable(); + ClassConstantInstruction clsInsn = new ClassConstantInstruction(); + clsInsn.setConstant(type); + clsInsn.setLocation(location.getSourceLocation()); + clsInsn.setReceiver(cls); + replacement.add(clsInsn); + insn.getArguments().add(cls); } - if (itemType instanceof ValueType.Object) { - String className = ((ValueType.Object) itemType).getClassName(); - if (className.equals("java.lang.String")) { - String methodName = "unwrapStringArray"; - if (degree > 1) { - methodName += degree; - } - ValueType argType = degree == 1 - ? ValueType.parse(JSStringArray.class) - : ValueType.parse(JSArray.class); - return unwrap(var, methodName, argType, type, location.getSourceLocation()); - } else if (isNative(className)) { - return unwrapObjectArray(location, var, degree, itemType, type); - } + insn.getArguments().add(var); + insn.setReceiver(result); + return result; + } + + private Variable unwrapMultiDimensionArray(CallLocation location, Variable var, ValueType type, int degree) { + Variable function = program.createVariable(); + + InvokeInstruction insn = new InvokeInstruction(); + insn.setMethod(multipleDimensionArrayUnwrapper(type)); + insn.setType(InvocationType.SPECIAL); + + if (insn.getMethod().parameterCount() == 1) { + Variable cls = program.createVariable(); + ClassConstantInstruction clsInsn = new ClassConstantInstruction(); + clsInsn.setConstant(type); + clsInsn.setLocation(location.getSourceLocation()); + clsInsn.setReceiver(cls); + replacement.add(clsInsn); + insn.getArguments().add(cls); } - diagnostics.error(location, "Unsupported type: {{t0}}", type); - return var; + + insn.setReceiver(function); + + while (--degree > 1) { + type = ValueType.arrayOf(type); + Variable cls = program.createVariable(); + + ClassConstantInstruction clsInsn = new ClassConstantInstruction(); + clsInsn.setConstant(type); + clsInsn.setLocation(location.getSourceLocation()); + clsInsn.setReceiver(cls); + replacement.add(clsInsn); + + insn = new InvokeInstruction(); + insn.setMethod(new MethodReference(JS.class, "arrayUnmapper", Class.class, Function.class, + Function.class)); + insn.setType(InvocationType.SPECIAL); + insn.getArguments().add(cls); + insn.getArguments().add(function); + function = program.createVariable(); + insn.setReceiver(function); + } + + return null; + } + + private MethodReference singleDimensionArrayUnwrapper(ValueType itemType) { + if (itemType instanceof ValueType.Primitive) { + switch (((ValueType.Primitive) itemType).getKind()) { + case BOOLEAN: + return new MethodReference(JS.class, "unwrapBooleanArray", JSArrayReader.class, boolean[].class); + case BYTE: + return new MethodReference(JS.class, "unwrapByteArray", JSArrayReader.class, byte[].class); + case SHORT: + return new MethodReference(JS.class, "unwrapShortArray", JSArrayReader.class, short[].class); + case CHARACTER: + return new MethodReference(JS.class, "unwrapCharArray", JSArrayReader.class, char[].class); + case INTEGER: + return new MethodReference(JS.class, "unwrapIntArray", JSArrayReader.class, int[].class); + case FLOAT: + return new MethodReference(JS.class, "unwrapFloatArray", JSArrayReader.class, float[].class); + case DOUBLE: + return new MethodReference(JS.class, "unwrapDoubleArray", JSArrayReader.class, double[].class); + default: + break; + } + } else if (itemType.isObject(String.class)) { + return new MethodReference(JS.class, "unwrapStringArray", JSArrayReader.class, String[].class); + } + return new MethodReference(JS.class, "unwrapArray", Class.class, JSArrayReader.class, byte[].class); + } + + private MethodReference multipleDimensionArrayUnwrapper(ValueType itemType) { + if (itemType instanceof ValueType.Primitive) { + switch (((ValueType.Primitive) itemType).getKind()) { + case BOOLEAN: + return new MethodReference(JS.class, "booleanArrayUnwrapper", Function.class, Function.class); + case BYTE: + return new MethodReference(JS.class, "byteArrayUnwrapper", Function.class, Function.class); + case SHORT: + return new MethodReference(JS.class, "shortArrayUnwrapper", Function.class, Function.class); + case CHARACTER: + return new MethodReference(JS.class, "charArrayUnwrapper", Function.class, Function.class); + case INTEGER: + return new MethodReference(JS.class, "intArrayUnwrapper", Function.class, Function.class); + case FLOAT: + return new MethodReference(JS.class, "floatArrayUnwrapper", Function.class, Function.class); + case DOUBLE: + return new MethodReference(JS.class, "doubleArrayUnwrapper", Function.class, Function.class); + default: + break; + } + } else if (itemType.isObject(String.class)) { + return new MethodReference(JS.class, "stringArrayUnwrapper", Function.class, Function.class); + } + return new MethodReference(JS.class, "arrayUnwrapper", Class.class, Function.class, Function.class); } private Variable unwrap(Variable var, String methodName, ValueType argType, ValueType resultType, @@ -714,57 +824,6 @@ class JavascriptNativeProcessor { return result; } - private Variable unwrapObjectArray(CallLocation location, Variable var, int degree, ValueType itemType, - ValueType expectedType) { - String methodName = "unwrapArray"; - if (degree > 1) { - methodName += degree; - } - ValueType resultType = ValueType.parse(JSObject.class); - for (int i = 0; i < degree; ++i) { - resultType = ValueType.arrayOf(resultType); - } - - Variable classVar = program.createVariable(); - ClassConstantInstruction classInsn = new ClassConstantInstruction(); - classInsn.setConstant(itemType); - classInsn.setReceiver(classVar); - classInsn.setLocation(location.getSourceLocation()); - replacement.add(classInsn); - - Variable castValue = program.createVariable(); - CastInstruction castInsn = new CastInstruction(); - castInsn.setValue(var); - castInsn.setReceiver(castValue); - castInsn.setLocation(location.getSourceLocation()); - castInsn.setTargetType(ValueType.parse(JSArray.class)); - replacement.add(castInsn); - var = castValue; - - Variable result = program.createVariable(); - InvokeInstruction insn = new InvokeInstruction(); - insn.setMethod(new MethodReference(JS.class.getName(), methodName, ValueType.parse(Class.class), - ValueType.parse(JSArray.class), resultType)); - insn.getArguments().add(classVar); - insn.getArguments().add(var); - insn.setReceiver(result); - insn.setType(InvocationType.SPECIAL); - insn.setLocation(location.getSourceLocation()); - replacement.add(insn); - var = result; - - Variable castResult = program.createVariable(); - castInsn = new CastInstruction(); - castInsn.setValue(var); - castInsn.setReceiver(castResult); - castInsn.setLocation(location.getSourceLocation()); - castInsn.setTargetType(expectedType); - replacement.add(castInsn); - var = castResult; - - return var; - } - private Variable wrapArgument(CallLocation location, Variable var, ValueType type) { if (type instanceof ValueType.Object) { String className = ((ValueType.Object) type).getClassName(); @@ -807,22 +866,89 @@ class JavascriptNativeProcessor { } } Variable result = program.createVariable(); - InvokeInstruction insn = new InvokeInstruction(); - insn.setMethod(new MethodReference(JS.class.getName(), "wrap", getWrappedType(type), getWrapperType(type))); - insn.getArguments().add(var); - insn.setReceiver(result); - insn.setType(InvocationType.SPECIAL); - insn.setLocation(location); - replacement.add(insn); + + ValueType itemType = type; + int degree = 0; + while (itemType instanceof ValueType.Array) { + itemType = ((ValueType.Array) itemType).getItemType(); + ++degree; + } + + if (degree <= 1) { + InvokeInstruction insn = new InvokeInstruction(); + insn.setMethod(new MethodReference(JS.class.getName(), "wrap", getWrappedType(type), + getWrapperType(type))); + insn.getArguments().add(var); + insn.setReceiver(result); + insn.setType(InvocationType.SPECIAL); + insn.setLocation(location); + replacement.add(insn); + } else { + Variable function = program.createVariable(); + + InvokeInstruction insn = new InvokeInstruction(); + insn.setMethod(getWrapperFunction(itemType)); + insn.setReceiver(function); + insn.setType(InvocationType.SPECIAL); + insn.setLocation(location); + replacement.add(insn); + + while (--degree > 1) { + insn = new InvokeInstruction(); + insn.setMethod(new MethodReference(JS.class, "arrayMapper", Function.class, Function.class)); + insn.getArguments().add(function); + function = program.createVariable(); + insn.setReceiver(function); + insn.setType(InvocationType.SPECIAL); + insn.setLocation(location); + replacement.add(insn); + } + + insn = new InvokeInstruction(); + insn.setMethod(new MethodReference(JS.class.getName(), "map", getWrappedType(type), + ValueType.parse(Function.class), getWrapperType(type))); + insn.getArguments().add(var); + insn.getArguments().add(function); + insn.setReceiver(result); + insn.setType(InvocationType.SPECIAL); + insn.setLocation(location); + replacement.add(insn); + } return result; } + private MethodReference getWrapperFunction(ValueType type) { + if (type instanceof ValueType.Primitive) { + switch (((ValueType.Primitive) type).getKind()) { + case BOOLEAN: + return new MethodReference(JS.class, "booleanArrayWrapper", Function.class); + case BYTE: + return new MethodReference(JS.class, "byteArrayWrapper", Function.class); + case SHORT: + return new MethodReference(JS.class, "shortArrayWrapper", Function.class); + case CHARACTER: + return new MethodReference(JS.class, "charArrayWrapper", Function.class); + case INTEGER: + return new MethodReference(JS.class, "intArrayWrapper", Function.class); + case FLOAT: + return new MethodReference(JS.class, "floatArrayWrapper", Function.class); + case DOUBLE: + return new MethodReference(JS.class, "doubleArrayWrapper", Function.class); + default: + break; + } + } else if (type.isObject(String.class)) { + return new MethodReference(JS.class, "stringArrayWrapper", Function.class); + } + return new MethodReference(JS.class, "arrayWrapper", Function.class); + } + private ValueType getWrappedType(ValueType type) { if (type instanceof ValueType.Array) { ValueType itemType = ((ValueType.Array) type).getItemType(); return ValueType.arrayOf(getWrappedType(itemType)); } else if (type instanceof ValueType.Object) { - if (type.isObject("java.lang.String")) { + if (type.isObject(String.class)) { return type; } else { return ValueType.parse(JSObject.class); @@ -834,28 +960,7 @@ class JavascriptNativeProcessor { private ValueType getWrapperType(ValueType type) { if (type instanceof ValueType.Array) { - ValueType itemType = ((ValueType.Array) type).getItemType(); - if (itemType instanceof ValueType.Primitive) { - switch (((ValueType.Primitive) itemType).getKind()) { - case BOOLEAN: - return ValueType.parse(JSBooleanArray.class); - case BYTE: - case SHORT: - case INTEGER: - case CHARACTER: - return ValueType.parse(JSIntArray.class); - case FLOAT: - case DOUBLE: - return ValueType.parse(JSDoubleArray.class); - case LONG: - default: - return ValueType.parse(JSArray.class); - } - } else if (itemType.isObject("java.lang.String")) { - return ValueType.parse(JSStringArray.class); - } else { - return ValueType.parse(JSArray.class); - } + return ValueType.parse(JSArray.class); } else { return ValueType.parse(JSObject.class); } diff --git a/teavm-jso/src/main/resources/META-INF/services/org.teavm.vm.spi.TeaVMPlugin b/teavm-jso-impl/src/main/resources/META-INF/services/org.teavm.vm.spi.TeaVMPlugin similarity index 100% rename from teavm-jso/src/main/resources/META-INF/services/org.teavm.vm.spi.TeaVMPlugin rename to teavm-jso-impl/src/main/resources/META-INF/services/org.teavm.vm.spi.TeaVMPlugin diff --git a/teavm-jso/src/main/java/org/teavm/jso/core/JSSortFunction.java b/teavm-jso/src/main/java/org/teavm/jso/core/JSSortFunction.java index 816d3fd55..09cefca0f 100644 --- a/teavm-jso/src/main/java/org/teavm/jso/core/JSSortFunction.java +++ b/teavm-jso/src/main/java/org/teavm/jso/core/JSSortFunction.java @@ -24,6 +24,7 @@ import org.teavm.jso.JSObject; * @param */ @JSFunctor +@FunctionalInterface public interface JSSortFunction { int compare(T a, T b); }