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 boolean isObject(Class<?> cls) {
return isObject(cls.getName());
}
private static ValueType parseImpl(String string) {
switch (string.charAt(0)) {
case 'Z':

View File

@ -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;
/**
* <p>Container of static methods to manipulate over {@link JSObject}s.</p>
*
* @author Alexey Andreev
*/
final class JS {
private JS() {
}
/**
* Gets global JavaScript object, that is similar to the <code>window</code> 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 <T extends JSObject> JSArray<T> wrap(T[] array) {
JSArray<T> result = JSArray.create(array.length);
@ -61,20 +86,20 @@ final class JS {
return result;
}
public static <T extends JSObject> JSArray<JSArray<T>> wrap(T[][] array) {
JSArray<JSArray<T>> result = JSArray.create(array.length);
public static <T extends JSObject> Function<T[], JSArray<T>> arrayWrapper() {
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) {
result.set(i, wrap(array[i]));
result.set(i, f.apply(array[i]));
}
return result;
}
public static <T extends JSObject> JSArray<JSArray<JSArray<T>>> wrap(T[][][] array) {
JSArray<JSArray<JSArray<T>>> result = JSArray.create(array.length);
for (int i = 0; i < array.length; ++i) {
result.set(i, wrap(array[i]));
}
return result;
public static <T extends JSObject, S> Function<S[], JSArray<T>> arrayMapper(Function<S, T> f) {
return array -> map(array, f);
}
public static JSArray<JSBoolean> wrap(boolean[] array) {
@ -85,20 +110,8 @@ final class JS {
return result;
}
public static JSArray<JSArray<JSBoolean>> wrap(boolean[][] array) {
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<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 Function<boolean[], JSArray<JSBoolean>> booleanArrayWrapper() {
return JS::wrap;
}
public static JSArray<JSNumber> wrap(byte[] array) {
@ -109,20 +122,8 @@ final class JS {
return result;
}
public static JSArray<JSArray<JSNumber>> wrap(byte[][] array) {
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<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 Function<byte[], JSArray<JSNumber>> byteArrayWrapper() {
return JS::wrap;
}
public static JSArray<JSNumber> wrap(short[] array) {
@ -133,20 +134,8 @@ final class JS {
return result;
}
public static JSArray<JSArray<JSNumber>> wrap(short[][] array) {
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<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 Function<short[], JSArray<JSNumber>> shortArrayWrapper() {
return JS::wrap;
}
public static JSArray<JSNumber> wrap(char[] array) {
@ -157,20 +146,8 @@ final class JS {
return result;
}
public static JSArray<JSArray<JSNumber>> wrap(char[][] array) {
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<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 Function<char[], JSArray<JSNumber>> charArrayWrapper() {
return JS::wrap;
}
public static JSArray<JSNumber> wrap(int[] array) {
@ -181,20 +158,8 @@ final class JS {
return result;
}
public static JSArray<JSArray<JSNumber>> wrap(int[][] array) {
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<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 Function<int[], JSArray<JSNumber>> intArrayWrapper() {
return JS::wrap;
}
public static JSArray<JSString> wrap(String[] array) {
@ -205,20 +170,8 @@ final class JS {
return result;
}
public static JSArray<JSArray<JSString>> wrap(String[][] array) {
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<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 Function<String[], JSArray<JSString>> stringArrayWrapper() {
return JS::wrap;
}
public static JSArray<JSNumber> wrap(float[] array) {
@ -229,20 +182,8 @@ final class JS {
return result;
}
public static JSArray<JSArray<JSNumber>> wrap(float[][] array) {
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<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 Function<float[], JSArray<JSNumber>> floatArrayWrapper() {
return JS::wrap;
}
public static JSArray<JSNumber> wrap(double[] array) {
@ -253,30 +194,11 @@ final class JS {
return result;
}
public static JSArray<JSArray<JSNumber>> wrap(double[][] array) {
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 Function<double[], JSArray<JSNumber>> doubleArrayWrapper() {
return JS::wrap;
}
public static JSArray<JSArray<JSArray<JSNumber>>> wrap(double[][][] 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) {
public static <T extends JSObject> T[] unwrapArray(Class<T> type, JSArrayReader<T> 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 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")
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 extends JSObject> T[][][] unwrapArray3(Class<T> type, JSArray<JSArray<JSArray<T>>> array) {
Class<?> baseType = Array.newInstance(type, 0).getClass();
@SuppressWarnings("unchecked")
T[][][] result = (T[][][]) Array.newInstance(Array.newInstance(baseType, 0).getClass(), array.getLength());
public static <T, S extends JSObject> Function<JSArray<S>, T[]> arrayUnmapper(Class<T> type, Function<S, T> f) {
return array -> unmapArray(type, array, f);
}
public static boolean[] unwrapBooleanArray(JSArrayReader<JSBoolean> 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<JSArrayReader<JSBoolean>, boolean[]> booleanArrayUnwrapper() {
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)
@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,

View File

@ -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) {

View File

@ -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);
}

View File

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