Updating JSO implementation

This commit is contained in:
Alexey Andreev 2015-09-09 18:16:37 +03:00
parent e717adcd84
commit e1f6bfdaeb
6 changed files with 379 additions and 271 deletions

View File

@ -265,6 +265,10 @@ public abstract class ValueType {
public abstract boolean isObject(String cls); public abstract boolean isObject(String cls);
public boolean isObject(Class<?> cls) {
return isObject(cls.getName());
}
private static ValueType parseImpl(String string) { private static ValueType parseImpl(String string) {
switch (string.charAt(0)) { switch (string.charAt(0)) {
case 'Z': case 'Z':

View File

@ -17,6 +17,7 @@ package org.teavm.jso.plugin;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.util.Iterator; import java.util.Iterator;
import java.util.function.Function;
import org.teavm.dependency.PluggableDependency; import org.teavm.dependency.PluggableDependency;
import org.teavm.javascript.spi.GeneratedBy; import org.teavm.javascript.spi.GeneratedBy;
import org.teavm.javascript.spi.InjectedBy; 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.JSNumber;
import org.teavm.jso.core.JSString; import org.teavm.jso.core.JSString;
/**
* <p>Container of static methods to manipulate over {@link JSObject}s.</p>
*
* @author Alexey Andreev
*/
final class JS { final class JS {
private JS() { private JS() {
} }
/** @InjectedBy(JSNativeGenerator.class)
* Gets global JavaScript object, that is similar to the <code>window</code> object in the browser. public static native JSObject wrap(byte value);
* @return global object.
*/
@JSBody(params = {}, script = "return window;")
public static native JSObject getGlobal();
@InjectedBy(JSNativeGenerator.class) @InjectedBy(JSNativeGenerator.class)
public static native JSObject wrap(String str); public static native JSObject wrap(short value);
@InjectedBy(JSNativeGenerator.class) @InjectedBy(JSNativeGenerator.class)
public static native JSObject wrap(char c); public static native JSObject wrap(int value);
@InjectedBy(JSNativeGenerator.class) @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 <T extends JSObject> JSArray<T> wrap(T[] array) { public static <T extends JSObject> JSArray<T> wrap(T[] array) {
JSArray<T> result = JSArray.create(array.length); JSArray<T> result = JSArray.create(array.length);
@ -61,20 +86,20 @@ final class JS {
return result; return result;
} }
public static <T extends JSObject> JSArray<JSArray<T>> wrap(T[][] array) { public static <T extends JSObject> Function<T[], JSArray<T>> arrayWrapper() {
JSArray<JSArray<T>> result = JSArray.create(array.length); return JS::wrap;
}
public static <T extends JSObject, S> JSArray<T> map(S[] array, Function<S, T> f) {
JSArray<T> result = JSArray.create(array.length);
for (int i = 0; i < array.length; ++i) { for (int i = 0; i < array.length; ++i) {
result.set(i, wrap(array[i])); result.set(i, f.apply(array[i]));
} }
return result; return result;
} }
public static <T extends JSObject> JSArray<JSArray<JSArray<T>>> wrap(T[][][] array) { public static <T extends JSObject, S> Function<S[], JSArray<T>> arrayMapper(Function<S, T> f) {
JSArray<JSArray<JSArray<T>>> result = JSArray.create(array.length); return array -> map(array, f);
for (int i = 0; i < array.length; ++i) {
result.set(i, wrap(array[i]));
}
return result;
} }
public static JSArray<JSBoolean> wrap(boolean[] array) { public static JSArray<JSBoolean> wrap(boolean[] array) {
@ -85,20 +110,8 @@ final class JS {
return result; return result;
} }
public static JSArray<JSArray<JSBoolean>> wrap(boolean[][] array) { public static Function<boolean[], JSArray<JSBoolean>> booleanArrayWrapper() {
JSArray<JSArray<JSBoolean>> result = JSArray.create(array.length); return JS::wrap;
for (int i = 0; i < array.length; ++i) {
result.set(i, wrap(array[i]));
}
return result;
}
public static JSArray<JSArray<JSArray<JSBoolean>>> wrap(boolean[][][] array) {
JSArray<JSArray<JSArray<JSBoolean>>> result = JSArray.create(array.length);
for (int i = 0; i < array.length; ++i) {
result.set(i, wrap(array[i]));
}
return result;
} }
public static JSArray<JSNumber> wrap(byte[] array) { public static JSArray<JSNumber> wrap(byte[] array) {
@ -109,20 +122,8 @@ final class JS {
return result; return result;
} }
public static JSArray<JSArray<JSNumber>> wrap(byte[][] array) { public static Function<byte[], JSArray<JSNumber>> byteArrayWrapper() {
JSArray<JSArray<JSNumber>> result = JSArray.create(array.length); return JS::wrap;
for (int i = 0; i < array.length; ++i) {
result.set(i, wrap(array[i]));
}
return result;
}
public static JSArray<JSArray<JSArray<JSNumber>>> wrap(byte[][][] array) {
JSArray<JSArray<JSArray<JSNumber>>> result = JSArray.create(array.length);
for (int i = 0; i < array.length; ++i) {
result.set(i, wrap(array[i]));
}
return result;
} }
public static JSArray<JSNumber> wrap(short[] array) { public static JSArray<JSNumber> wrap(short[] array) {
@ -133,20 +134,8 @@ final class JS {
return result; return result;
} }
public static JSArray<JSArray<JSNumber>> wrap(short[][] array) { public static Function<short[], JSArray<JSNumber>> shortArrayWrapper() {
JSArray<JSArray<JSNumber>> result = JSArray.create(array.length); return JS::wrap;
for (int i = 0; i < array.length; ++i) {
result.set(i, wrap(array[i]));
}
return result;
}
public static JSArray<JSArray<JSArray<JSNumber>>> wrap(short[][][] array) {
JSArray<JSArray<JSArray<JSNumber>>> result = JSArray.create(array.length);
for (int i = 0; i < array.length; ++i) {
result.set(i, wrap(array[i]));
}
return result;
} }
public static JSArray<JSNumber> wrap(char[] array) { public static JSArray<JSNumber> wrap(char[] array) {
@ -157,20 +146,8 @@ final class JS {
return result; return result;
} }
public static JSArray<JSArray<JSNumber>> wrap(char[][] array) { public static Function<char[], JSArray<JSNumber>> charArrayWrapper() {
JSArray<JSArray<JSNumber>> result = JSArray.create(array.length); return JS::wrap;
for (int i = 0; i < array.length; ++i) {
result.set(i, wrap(array[i]));
}
return result;
}
public static JSArray<JSArray<JSArray<JSNumber>>> wrap(char[][][] array) {
JSArray<JSArray<JSArray<JSNumber>>> result = JSArray.create(array.length);
for (int i = 0; i < array.length; ++i) {
result.set(i, wrap(array[i]));
}
return result;
} }
public static JSArray<JSNumber> wrap(int[] array) { public static JSArray<JSNumber> wrap(int[] array) {
@ -181,20 +158,8 @@ final class JS {
return result; return result;
} }
public static JSArray<JSArray<JSNumber>> wrap(int[][] array) { public static Function<int[], JSArray<JSNumber>> intArrayWrapper() {
JSArray<JSArray<JSNumber>> result = JSArray.create(array.length); return JS::wrap;
for (int i = 0; i < array.length; ++i) {
result.set(i, wrap(array[i]));
}
return result;
}
public static JSArray<JSArray<JSArray<JSNumber>>> wrap(int[][][] array) {
JSArray<JSArray<JSArray<JSNumber>>> result = JSArray.create(array.length);
for (int i = 0; i < array.length; ++i) {
result.set(i, wrap(array[i]));
}
return result;
} }
public static JSArray<JSString> wrap(String[] array) { public static JSArray<JSString> wrap(String[] array) {
@ -205,20 +170,8 @@ final class JS {
return result; return result;
} }
public static JSArray<JSArray<JSString>> wrap(String[][] array) { public static Function<String[], JSArray<JSString>> stringArrayWrapper() {
JSArray<JSArray<JSString>> result = JSArray.create(array.length); return JS::wrap;
for (int i = 0; i < array.length; ++i) {
result.set(i, wrap(array[i]));
}
return result;
}
public static JSArray<JSArray<JSArray<JSString>>> wrap(String[][][] array) {
JSArray<JSArray<JSArray<JSString>>> result = JSArray.create(array.length);
for (int i = 0; i < array.length; ++i) {
result.set(i, wrap(array[i]));
}
return result;
} }
public static JSArray<JSNumber> wrap(float[] array) { public static JSArray<JSNumber> wrap(float[] array) {
@ -229,20 +182,8 @@ final class JS {
return result; return result;
} }
public static JSArray<JSArray<JSNumber>> wrap(float[][] array) { public static Function<float[], JSArray<JSNumber>> floatArrayWrapper() {
JSArray<JSArray<JSNumber>> result = JSArray.create(array.length); return JS::wrap;
for (int i = 0; i < array.length; ++i) {
result.set(i, wrap(array[i]));
}
return result;
}
public static JSArray<JSArray<JSArray<JSNumber>>> wrap(float[][][] array) {
JSArray<JSArray<JSArray<JSNumber>>> result = JSArray.create(array.length);
for (int i = 0; i < array.length; ++i) {
result.set(i, wrap(array[i]));
}
return result;
} }
public static JSArray<JSNumber> wrap(double[] array) { public static JSArray<JSNumber> wrap(double[] array) {
@ -253,30 +194,11 @@ final class JS {
return result; return result;
} }
public static JSArray<JSArray<JSNumber>> wrap(double[][] array) { public static Function<double[], JSArray<JSNumber>> doubleArrayWrapper() {
JSArray<JSArray<JSNumber>> result = JSArray.create(array.length); return JS::wrap;
for (int i = 0; i < array.length; ++i) {
result.set(i, wrap(array[i]));
}
return result;
} }
public static JSArray<JSArray<JSArray<JSNumber>>> wrap(double[][][] array) { public static <T extends JSObject> T[] unwrapArray(Class<T> type, JSArrayReader<T> array) {
JSArray<JSArray<JSArray<JSNumber>>> 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 extends JSObject> T[] unwrapArray(Class<T> type, JSArray<T> array) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
T[] result = (T[]) Array.newInstance(type, array.getLength()); T[] result = (T[]) Array.newInstance(type, array.getLength());
for (int i = 0; i < result.length; ++i) { for (int i = 0; i < result.length; ++i) {
@ -285,27 +207,118 @@ final class JS {
return result; return result;
} }
public static <T extends JSObject> T[][] unwrapArray2(Class<T> type, JSArray<JSArray<T>> array) { public static <T extends JSObject> Function<JSArrayReader<T>, T[]> arrayUnwrapper(Class<T> type) {
return array -> unwrapArray(type, array);
}
public static <S extends JSObject, T> T[] unmapArray(Class<T> type, JSArrayReader<S> array, Function<S, T> f) {
@SuppressWarnings("unchecked") @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) { for (int i = 0; i < result.length; ++i) {
result[i] = unwrapArray(type, array.get(i)); result[i] = f.apply(array.get(i));
} }
return result; return result;
} }
public static <T extends JSObject> T[][][] unwrapArray3(Class<T> type, JSArray<JSArray<JSArray<T>>> array) { public static <T, S extends JSObject> Function<JSArray<S>, T[]> arrayUnmapper(Class<T> type, Function<S, T> f) {
Class<?> baseType = Array.newInstance(type, 0).getClass(); return array -> unmapArray(type, array, f);
@SuppressWarnings("unchecked") }
T[][][] result = (T[][][]) Array.newInstance(Array.newInstance(baseType, 0).getClass(), array.getLength());
public static boolean[] unwrapBooleanArray(JSArrayReader<JSBoolean> array) {
boolean[] result = new boolean[array.getLength()];
for (int i = 0; i < result.length; ++i) { for (int i = 0; i < result.length; ++i) {
result[i] = unwrapArray2(type, array.get(i)); result[i] = array.get(i).booleanValue();
} }
return result; return result;
} }
@JSBody(params = "obj", script = "return typeof(obj) === 'undefined';") public static Function<JSArrayReader<JSBoolean>, boolean[]> booleanArrayUnwrapper() {
public static native boolean isUndefined(JSObject obj); return JS::unwrapBooleanArray;
}
public static byte[] unwrapByteArray(JSArrayReader<JSNumber> 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<JSArrayReader<JSNumber>, byte[]> byteArrayUnwrapper() {
return JS::unwrapByteArray;
}
public static short[] unwrapShortArray(JSArrayReader<JSNumber> 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<JSArrayReader<JSNumber>, short[]> shortArrayUnwrapper() {
return JS::unwrapShortArray;
}
public static int[] unwrapIntArray(JSArrayReader<JSNumber> 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<JSArrayReader<JSNumber>, int[]> intArrayUnwrapper() {
return JS::unwrapIntArray;
}
public static char[] unwrapCharArray(JSArrayReader<JSNumber> 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<JSArrayReader<JSNumber>, char[]> charArrayUnwrapper() {
return JS::unwrapCharArray;
}
public static float[] unwrapFloatArray(JSArrayReader<JSNumber> 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<JSArrayReader<JSNumber>, float[]> floatArrayUnwrapper() {
return JS::unwrapFloatArray;
}
public static double[] unwrapDoubleArray(JSArrayReader<JSNumber> 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<JSArrayReader<JSNumber>, double[]> doubleArrayUnwrapper() {
return JS::unwrapDoubleArray;
}
public static String[] unwrapStringArray(JSArrayReader<JSString> 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<JSArrayReader<JSString>, String[]> stringArrayUnwrapper() {
return JS::unwrapStringArray;
}
@InjectedBy(JSNativeGenerator.class) @InjectedBy(JSNativeGenerator.class)
@PluggableDependency(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, public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
JSObject d, JSObject e); JSObject d, JSObject e);
@InjectedBy(JSNativeGenerator.class) @InjectedBy(JSNativeGenerator.class)
@PluggableDependency(JSNativeGenerator.class) @PluggableDependency(JSNativeGenerator.class)
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c, public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,

View File

@ -17,7 +17,9 @@ package org.teavm.jso.plugin;
import java.io.IOException; import java.io.IOException;
import org.teavm.codegen.SourceWriter; 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.Renderer;
import org.teavm.javascript.ast.ConstantExpr; import org.teavm.javascript.ast.ConstantExpr;
import org.teavm.javascript.ast.Expr; 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 { public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
SourceWriter writer = context.getWriter(); SourceWriter writer = context.getWriter();
switch (methodRef.getName()) { 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": case "get":
context.writeExpr(context.getArgument(0)); context.writeExpr(context.getArgument(0));
renderProperty(context.getArgument(1), context); renderProperty(context.getArgument(1), context);
@ -122,9 +111,6 @@ public class JSNativeGenerator implements Injector, DependencyPlugin, Generator
} }
writer.append("))"); writer.append("))");
break; break;
case "marshall":
context.writeExpr(context.getArgument(0));
break;
case "wrap": case "wrap":
if (methodRef.getDescriptor().parameterType(0).isObject("java.lang.String")) { if (methodRef.getDescriptor().parameterType(0).isObject("java.lang.String")) {
if (context.getArgument(0) instanceof ConstantExpr) { if (context.getArgument(0) instanceof ConstantExpr) {

View File

@ -22,6 +22,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import org.teavm.diagnostics.Diagnostics; import org.teavm.diagnostics.Diagnostics;
import org.teavm.javascript.spi.GeneratedBy; import org.teavm.javascript.spi.GeneratedBy;
import org.teavm.javascript.spi.Sync; import org.teavm.javascript.spi.Sync;
@ -33,6 +34,7 @@ import org.teavm.jso.JSMethod;
import org.teavm.jso.JSObject; import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty; import org.teavm.jso.JSProperty;
import org.teavm.jso.core.JSArray; import org.teavm.jso.core.JSArray;
import org.teavm.jso.core.JSArrayReader;
import org.teavm.model.AccessLevel; import org.teavm.model.AccessLevel;
import org.teavm.model.AnnotationHolder; import org.teavm.model.AnnotationHolder;
import org.teavm.model.AnnotationReader; import org.teavm.model.AnnotationReader;
@ -667,28 +669,136 @@ class JavascriptNativeProcessor {
++degree; ++degree;
itemType = ((ValueType.Array) itemType).getItemType(); itemType = ((ValueType.Array) itemType).getItemType();
} }
if (degree > 3) {
diagnostics.error(location, "Unsupported type: {{t0}}", type); CastInstruction castInsn = new CastInstruction();
return var; 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) { insn.getArguments().add(var);
String className = ((ValueType.Object) itemType).getClassName(); insn.setReceiver(result);
if (className.equals("java.lang.String")) { return result;
String methodName = "unwrapStringArray"; }
if (degree > 1) {
methodName += degree; private Variable unwrapMultiDimensionArray(CallLocation location, Variable var, ValueType type, int degree) {
} Variable function = program.createVariable();
ValueType argType = degree == 1
? ValueType.parse(JSStringArray.class) InvokeInstruction insn = new InvokeInstruction();
: ValueType.parse(JSArray.class); insn.setMethod(multipleDimensionArrayUnwrapper(type));
return unwrap(var, methodName, argType, type, location.getSourceLocation()); insn.setType(InvocationType.SPECIAL);
} else if (isNative(className)) {
return unwrapObjectArray(location, var, degree, itemType, type); 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, private Variable unwrap(Variable var, String methodName, ValueType argType, ValueType resultType,
@ -714,57 +824,6 @@ class JavascriptNativeProcessor {
return result; 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) { private Variable wrapArgument(CallLocation location, Variable var, ValueType type) {
if (type instanceof ValueType.Object) { if (type instanceof ValueType.Object) {
String className = ((ValueType.Object) type).getClassName(); String className = ((ValueType.Object) type).getClassName();
@ -807,22 +866,89 @@ class JavascriptNativeProcessor {
} }
} }
Variable result = program.createVariable(); Variable result = program.createVariable();
InvokeInstruction insn = new InvokeInstruction();
insn.setMethod(new MethodReference(JS.class.getName(), "wrap", getWrappedType(type), getWrapperType(type))); ValueType itemType = type;
insn.getArguments().add(var); int degree = 0;
insn.setReceiver(result); while (itemType instanceof ValueType.Array) {
insn.setType(InvocationType.SPECIAL); itemType = ((ValueType.Array) itemType).getItemType();
insn.setLocation(location); ++degree;
replacement.add(insn); }
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; 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) { private ValueType getWrappedType(ValueType type) {
if (type instanceof ValueType.Array) { if (type instanceof ValueType.Array) {
ValueType itemType = ((ValueType.Array) type).getItemType(); ValueType itemType = ((ValueType.Array) type).getItemType();
return ValueType.arrayOf(getWrappedType(itemType)); return ValueType.arrayOf(getWrappedType(itemType));
} else if (type instanceof ValueType.Object) { } else if (type instanceof ValueType.Object) {
if (type.isObject("java.lang.String")) { if (type.isObject(String.class)) {
return type; return type;
} else { } else {
return ValueType.parse(JSObject.class); return ValueType.parse(JSObject.class);
@ -834,28 +960,7 @@ class JavascriptNativeProcessor {
private ValueType getWrapperType(ValueType type) { private ValueType getWrapperType(ValueType type) {
if (type instanceof ValueType.Array) { if (type instanceof ValueType.Array) {
ValueType itemType = ((ValueType.Array) type).getItemType(); return ValueType.parse(JSArray.class);
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);
}
} else { } else {
return ValueType.parse(JSObject.class); return ValueType.parse(JSObject.class);
} }

View File

@ -24,6 +24,7 @@ import org.teavm.jso.JSObject;
* @param <T> * @param <T>
*/ */
@JSFunctor @JSFunctor
@FunctionalInterface
public interface JSSortFunction<T extends JSObject> { public interface JSSortFunction<T extends JSObject> {
int compare(T a, T b); int compare(T a, T b);
} }