mirror of
https://github.com/konsoletyper/teavm.git
synced 2024-11-27 01:30:35 +08:00
JS: Support JSByRef annotation on method return types
This commit is contained in:
parent
f664d94d74
commit
ac627580c6
@ -26,6 +26,6 @@ import java.lang.annotation.Target;
|
||||
* to: byte[], short[], char[], int[], float[], double[] or T[], where T is JSObject.</p>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Target({ ElementType.PARAMETER, ElementType.METHOD })
|
||||
public @interface JSByRef {
|
||||
}
|
||||
|
@ -35,6 +35,34 @@ final class JS {
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
public static native JSObject arrayData(Object array);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
public static native byte[] dataToByteArray(JSObject obj);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
public static native char[] dataToCharArray(JSObject obj);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
public static native short[] dataToShortArray(JSObject obj);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
public static native int[] dataToIntArray(JSObject obj);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
public static native float[] dataToFloatArray(JSObject obj);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
public static native double[] dataToDoubleArray(JSObject obj);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
public static native JSObject[] dataToArray(JSObject obj);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
public static native JSObject wrap(byte value);
|
||||
|
||||
|
@ -361,6 +361,13 @@ class JSClassProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
boolean returnByRef = method.getAnnotations().get(JSByRef.class.getName()) != null;
|
||||
if (returnByRef && !typeHelper.isSupportedByRefType(method.getResultType())) {
|
||||
diagnostics.error(callLocation, "Method {{m0}} is marked with @JSByRef, but does not return valid "
|
||||
+ "array type");
|
||||
return false;
|
||||
}
|
||||
|
||||
requireJSBody(diagnostics, method);
|
||||
MethodReference delegate = repository.methodMap.get(method.getReference());
|
||||
if (delegate == null) {
|
||||
@ -387,7 +394,7 @@ class JSClassProcessor {
|
||||
newInvoke.setArguments(newArgs.toArray(new Variable[0]));
|
||||
replacement.add(newInvoke);
|
||||
if (result != null) {
|
||||
result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType());
|
||||
result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType(), returnByRef);
|
||||
copyVar(result, invoke.getReceiver(), invoke.getLocation());
|
||||
}
|
||||
|
||||
@ -406,7 +413,7 @@ class JSClassProcessor {
|
||||
Variable result = invoke.getReceiver() != null ? program.createVariable() : null;
|
||||
addPropertyGet(propertyName, invoke.getInstance(), result, invoke.getLocation());
|
||||
if (result != null) {
|
||||
result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType());
|
||||
result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType(), false);
|
||||
copyVar(result, invoke.getReceiver(), invoke.getLocation());
|
||||
}
|
||||
return true;
|
||||
@ -438,7 +445,7 @@ class JSClassProcessor {
|
||||
addIndexerGet(invoke.getInstance(), marshaller.wrapArgument(callLocation, invoke.getArguments().get(0),
|
||||
method.parameterType(0), false), result, invoke.getLocation());
|
||||
if (result != null) {
|
||||
result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType());
|
||||
result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType(), false);
|
||||
copyVar(result, invoke.getReceiver(), invoke.getLocation());
|
||||
}
|
||||
return true;
|
||||
@ -520,7 +527,7 @@ class JSClassProcessor {
|
||||
newInvoke.setArguments(newArguments.toArray(new Variable[0]));
|
||||
replacement.add(newInvoke);
|
||||
if (result != null) {
|
||||
result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType());
|
||||
result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType(), false);
|
||||
copyVar(result, invoke.getReceiver(), invoke.getLocation());
|
||||
}
|
||||
|
||||
@ -681,12 +688,12 @@ class JSClassProcessor {
|
||||
replacement.clear();
|
||||
if (!callee.hasModifier(ElementModifier.STATIC)) {
|
||||
insn.setInstance(marshaller.unwrapReturnValue(location, program.variableAt(paramIndex++),
|
||||
ValueType.object(calleeRef.getClassName())));
|
||||
ValueType.object(calleeRef.getClassName()), false));
|
||||
}
|
||||
Variable[] args = new Variable[callee.parameterCount()];
|
||||
for (int i = 0; i < callee.parameterCount(); ++i) {
|
||||
args[i] = marshaller.unwrapReturnValue(location, program.variableAt(paramIndex++),
|
||||
callee.parameterType(i));
|
||||
callee.parameterType(i), false);
|
||||
}
|
||||
insn.setArguments(args);
|
||||
if (callee.getResultType() != ValueType.VOID) {
|
||||
|
@ -23,7 +23,7 @@ import org.teavm.jso.core.JSArrayReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
public final class JSMethods {
|
||||
final class JSMethods {
|
||||
public static final MethodReference GET = new MethodReference(JS.class, "get", JSObject.class,
|
||||
JSObject.class, JSObject.class);
|
||||
public static final MethodReference SET = new MethodReference(JS.class, "set", JSObject.class, JSObject.class,
|
||||
@ -93,6 +93,21 @@ public final class JSMethods {
|
||||
public static final MethodReference ARRAY_UNWRAPPER = new MethodReference(JS.class,
|
||||
"arrayUnwrapper", Class.class, Function.class);
|
||||
|
||||
public static final MethodReference DATA_TO_BYTE_ARRAY = new MethodReference(JS.class,
|
||||
"dataToByteArray", JSObject.class, byte[].class);
|
||||
public static final MethodReference DATA_TO_SHORT_ARRAY = new MethodReference(JS.class,
|
||||
"dataToShortArray", JSObject.class, short[].class);
|
||||
public static final MethodReference DATA_TO_CHAR_ARRAY = new MethodReference(JS.class,
|
||||
"dataToCharArray", JSObject.class, char[].class);
|
||||
public static final MethodReference DATA_TO_INT_ARRAY = new MethodReference(JS.class,
|
||||
"dataToIntArray", JSObject.class, int[].class);
|
||||
public static final MethodReference DATA_TO_FLOAT_ARRAY = new MethodReference(JS.class,
|
||||
"dataToFloatArray", JSObject.class, float[].class);
|
||||
public static final MethodReference DATA_TO_DOUBLE_ARRAY = new MethodReference(JS.class,
|
||||
"dataToDoubleArray", JSObject.class, double[].class);
|
||||
public static final MethodReference DATA_TO_ARRAY = new MethodReference(JS.class,
|
||||
"dataToArray", JSObject.class, JSObject[].class);
|
||||
|
||||
public static final MethodReference FUNCTION_AS_OBJECT = new MethodReference(JS.class, "functionAsObject",
|
||||
JSObject.class, JSObject.class, JSObject.class);
|
||||
|
||||
|
@ -178,6 +178,43 @@ public class JSNativeGenerator implements Injector, DependencyPlugin, Generator
|
||||
writer.append(")");
|
||||
}
|
||||
break;
|
||||
|
||||
case "dataToByteArray":
|
||||
writer.append("$rt_wrapArray($rt_bytecls(),").ws();
|
||||
context.writeExpr(context.getArgument(0), Precedence.min());
|
||||
writer.append(")");
|
||||
break;
|
||||
case "dataToShortArray":
|
||||
writer.append("$rt_wrapArray($rt_shortcls(),").ws();
|
||||
context.writeExpr(context.getArgument(0), Precedence.min());
|
||||
writer.append(")");
|
||||
break;
|
||||
case "dataToCharArray":
|
||||
writer.append("$rt_wrapArray($rt_charcls(),").ws();
|
||||
context.writeExpr(context.getArgument(0), Precedence.min());
|
||||
writer.append(")");
|
||||
break;
|
||||
case "dataToIntArray":
|
||||
writer.append("$rt_wrapArray($rt_intcls(),").ws();
|
||||
context.writeExpr(context.getArgument(0), Precedence.min());
|
||||
writer.append(")");
|
||||
break;
|
||||
case "dataToFloatArray":
|
||||
writer.append("$rt_wrapArray($rt_floatcls(),").ws();
|
||||
context.writeExpr(context.getArgument(0), Precedence.min());
|
||||
writer.append(")");
|
||||
break;
|
||||
case "dataToDoubleArray":
|
||||
writer.append("$rt_wrapArray($rt_doublecls(),").ws();
|
||||
context.writeExpr(context.getArgument(0), Precedence.min());
|
||||
writer.append(")");
|
||||
break;
|
||||
case "dataToArray":
|
||||
writer.append("$rt_wrapArray($rt_objcls(),").ws();
|
||||
context.writeExpr(context.getArgument(0), Precedence.min());
|
||||
writer.append(")");
|
||||
break;
|
||||
|
||||
default:
|
||||
if (methodRef.getName().startsWith("unwrap")) {
|
||||
context.writeExpr(context.getArgument(0), context.getPrecedence());
|
||||
@ -209,6 +246,28 @@ public class JSNativeGenerator implements Injector, DependencyPlugin, Generator
|
||||
case "unwrapString":
|
||||
method.getResult().propagate(agent.getType("java.lang.String"));
|
||||
break;
|
||||
|
||||
case "dataToByteArray":
|
||||
method.getResult().propagate(agent.getType("[B"));
|
||||
break;
|
||||
case "dataToShortArray":
|
||||
method.getResult().propagate(agent.getType("[S"));
|
||||
break;
|
||||
case "dataToCharArray":
|
||||
method.getResult().propagate(agent.getType("[C"));
|
||||
break;
|
||||
case "dataToIntArray":
|
||||
method.getResult().propagate(agent.getType("[I"));
|
||||
break;
|
||||
case "dataToFloatArray":
|
||||
method.getResult().propagate(agent.getType("[F"));
|
||||
break;
|
||||
case "dataToDoubleArray":
|
||||
method.getResult().propagate(agent.getType("[D"));
|
||||
break;
|
||||
case "dataToArray":
|
||||
method.getResult().propagate(agent.getType("[Ljava/lang/Object;"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,7 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
||||
|
||||
for (int i = 0; i < method.parameterCount(); ++i) {
|
||||
variablesToPass[i] = marshaller.unwrapReturnValue(callLocation, variablesToPass[i],
|
||||
method.parameterType(i));
|
||||
method.parameterType(i), false);
|
||||
}
|
||||
|
||||
basicBlock.addAll(marshallInstructions);
|
||||
|
@ -218,7 +218,11 @@ class JSValueMarshaller {
|
||||
return JSMethods.ARRAY_WRAPPER;
|
||||
}
|
||||
|
||||
Variable unwrapReturnValue(CallLocation location, Variable var, ValueType type) {
|
||||
Variable unwrapReturnValue(CallLocation location, Variable var, ValueType type, boolean byRef) {
|
||||
if (byRef) {
|
||||
return unwrapByRef(location, var, type);
|
||||
}
|
||||
|
||||
if (type instanceof ValueType.Object) {
|
||||
String className = ((ValueType.Object) type).getClassName();
|
||||
ClassReader cls = classSource.get(className);
|
||||
@ -229,6 +233,29 @@ class JSValueMarshaller {
|
||||
return unwrap(location, var, type);
|
||||
}
|
||||
|
||||
private Variable unwrapByRef(CallLocation location, Variable var, ValueType type) {
|
||||
type = ((ValueType.Array) type).getItemType();
|
||||
if (type instanceof ValueType.Primitive) {
|
||||
switch (((ValueType.Primitive) type).getKind()) {
|
||||
case BYTE:
|
||||
return invokeMethod(location, JSMethods.DATA_TO_BYTE_ARRAY, var);
|
||||
case SHORT:
|
||||
return invokeMethod(location, JSMethods.DATA_TO_SHORT_ARRAY, var);
|
||||
case CHARACTER:
|
||||
return invokeMethod(location, JSMethods.DATA_TO_CHAR_ARRAY, var);
|
||||
case INTEGER:
|
||||
return invokeMethod(location, JSMethods.DATA_TO_INT_ARRAY, var);
|
||||
case FLOAT:
|
||||
return invokeMethod(location, JSMethods.DATA_TO_FLOAT_ARRAY, var);
|
||||
case DOUBLE:
|
||||
return invokeMethod(location, JSMethods.DATA_TO_DOUBLE_ARRAY, var);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return invokeMethod(location, JSMethods.DATA_TO_ARRAY, var);
|
||||
}
|
||||
|
||||
Variable unwrap(CallLocation location, Variable var, ValueType type) {
|
||||
if (type instanceof ValueType.Primitive) {
|
||||
switch (((ValueType.Primitive) type).getKind()) {
|
||||
@ -302,6 +329,18 @@ class JSValueMarshaller {
|
||||
return var;
|
||||
}
|
||||
|
||||
private Variable invokeMethod(CallLocation location, MethodReference method, Variable var) {
|
||||
InvokeInstruction invoke = new InvokeInstruction();
|
||||
invoke.setArguments(var);
|
||||
invoke.setMethod(method);
|
||||
invoke.setReceiver(program.createVariable());
|
||||
invoke.setType(InvocationType.SPECIAL);
|
||||
invoke.setLocation(location.getSourceLocation());
|
||||
replacement.add(invoke);
|
||||
|
||||
return invoke.getReceiver();
|
||||
}
|
||||
|
||||
private Variable unwrapSingleDimensionArray(CallLocation location, Variable var, ValueType type) {
|
||||
Variable result = program.createVariable();
|
||||
|
||||
|
@ -17,6 +17,7 @@ package org.teavm.jso.test;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@ -158,6 +159,15 @@ public class ConversionTest {
|
||||
assertEquals(44, array[1]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnsArrayByRef() {
|
||||
int[] first = { 23, 42 };
|
||||
int[] second = rewrap(first);
|
||||
assertNotSame(first, second);
|
||||
second[0] = 99;
|
||||
assertEquals(99, first[0]);
|
||||
}
|
||||
|
||||
@JSBody(params = { "a", "b", "c", "d", "e", "f", "g", "h" }, script = ""
|
||||
+ "return '' + a + ':' + b + ':' + c + ':' + d + ':' + e + ':' + f.toFixed(1) + ':'"
|
||||
+ "+ g.toFixed(1) + ':' + h;")
|
||||
@ -336,4 +346,8 @@ public class ConversionTest {
|
||||
+ "}"
|
||||
+ "};")
|
||||
private static native ByRefMutator createByRefMutator();
|
||||
|
||||
@JSByRef
|
||||
@JSBody(params = "array", script = "return array;")
|
||||
private static native int[] rewrap(@JSByRef int[] array);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user