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>
|
* to: byte[], short[], char[], int[], float[], double[] or T[], where T is JSObject.</p>
|
||||||
*/
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.PARAMETER)
|
@Target({ ElementType.PARAMETER, ElementType.METHOD })
|
||||||
public @interface JSByRef {
|
public @interface JSByRef {
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,34 @@ final class JS {
|
|||||||
@InjectedBy(JSNativeGenerator.class)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native JSObject arrayData(Object array);
|
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)
|
@InjectedBy(JSNativeGenerator.class)
|
||||||
public static native JSObject wrap(byte value);
|
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);
|
requireJSBody(diagnostics, method);
|
||||||
MethodReference delegate = repository.methodMap.get(method.getReference());
|
MethodReference delegate = repository.methodMap.get(method.getReference());
|
||||||
if (delegate == null) {
|
if (delegate == null) {
|
||||||
@ -387,7 +394,7 @@ class JSClassProcessor {
|
|||||||
newInvoke.setArguments(newArgs.toArray(new Variable[0]));
|
newInvoke.setArguments(newArgs.toArray(new Variable[0]));
|
||||||
replacement.add(newInvoke);
|
replacement.add(newInvoke);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType());
|
result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType(), returnByRef);
|
||||||
copyVar(result, invoke.getReceiver(), invoke.getLocation());
|
copyVar(result, invoke.getReceiver(), invoke.getLocation());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,7 +413,7 @@ class JSClassProcessor {
|
|||||||
Variable result = invoke.getReceiver() != null ? program.createVariable() : null;
|
Variable result = invoke.getReceiver() != null ? program.createVariable() : null;
|
||||||
addPropertyGet(propertyName, invoke.getInstance(), result, invoke.getLocation());
|
addPropertyGet(propertyName, invoke.getInstance(), result, invoke.getLocation());
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType());
|
result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType(), false);
|
||||||
copyVar(result, invoke.getReceiver(), invoke.getLocation());
|
copyVar(result, invoke.getReceiver(), invoke.getLocation());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -438,7 +445,7 @@ class JSClassProcessor {
|
|||||||
addIndexerGet(invoke.getInstance(), marshaller.wrapArgument(callLocation, invoke.getArguments().get(0),
|
addIndexerGet(invoke.getInstance(), marshaller.wrapArgument(callLocation, invoke.getArguments().get(0),
|
||||||
method.parameterType(0), false), result, invoke.getLocation());
|
method.parameterType(0), false), result, invoke.getLocation());
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType());
|
result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType(), false);
|
||||||
copyVar(result, invoke.getReceiver(), invoke.getLocation());
|
copyVar(result, invoke.getReceiver(), invoke.getLocation());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -520,7 +527,7 @@ class JSClassProcessor {
|
|||||||
newInvoke.setArguments(newArguments.toArray(new Variable[0]));
|
newInvoke.setArguments(newArguments.toArray(new Variable[0]));
|
||||||
replacement.add(newInvoke);
|
replacement.add(newInvoke);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType());
|
result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType(), false);
|
||||||
copyVar(result, invoke.getReceiver(), invoke.getLocation());
|
copyVar(result, invoke.getReceiver(), invoke.getLocation());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -681,12 +688,12 @@ class JSClassProcessor {
|
|||||||
replacement.clear();
|
replacement.clear();
|
||||||
if (!callee.hasModifier(ElementModifier.STATIC)) {
|
if (!callee.hasModifier(ElementModifier.STATIC)) {
|
||||||
insn.setInstance(marshaller.unwrapReturnValue(location, program.variableAt(paramIndex++),
|
insn.setInstance(marshaller.unwrapReturnValue(location, program.variableAt(paramIndex++),
|
||||||
ValueType.object(calleeRef.getClassName())));
|
ValueType.object(calleeRef.getClassName()), false));
|
||||||
}
|
}
|
||||||
Variable[] args = new Variable[callee.parameterCount()];
|
Variable[] args = new Variable[callee.parameterCount()];
|
||||||
for (int i = 0; i < callee.parameterCount(); ++i) {
|
for (int i = 0; i < callee.parameterCount(); ++i) {
|
||||||
args[i] = marshaller.unwrapReturnValue(location, program.variableAt(paramIndex++),
|
args[i] = marshaller.unwrapReturnValue(location, program.variableAt(paramIndex++),
|
||||||
callee.parameterType(i));
|
callee.parameterType(i), false);
|
||||||
}
|
}
|
||||||
insn.setArguments(args);
|
insn.setArguments(args);
|
||||||
if (callee.getResultType() != ValueType.VOID) {
|
if (callee.getResultType() != ValueType.VOID) {
|
||||||
|
@ -23,7 +23,7 @@ import org.teavm.jso.core.JSArrayReader;
|
|||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
|
||||||
public final class JSMethods {
|
final class JSMethods {
|
||||||
public static final MethodReference GET = new MethodReference(JS.class, "get", JSObject.class,
|
public static final MethodReference GET = new MethodReference(JS.class, "get", JSObject.class,
|
||||||
JSObject.class, JSObject.class);
|
JSObject.class, JSObject.class);
|
||||||
public static final MethodReference SET = new MethodReference(JS.class, "set", 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,
|
public static final MethodReference ARRAY_UNWRAPPER = new MethodReference(JS.class,
|
||||||
"arrayUnwrapper", Class.class, Function.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",
|
public static final MethodReference FUNCTION_AS_OBJECT = new MethodReference(JS.class, "functionAsObject",
|
||||||
JSObject.class, JSObject.class, JSObject.class);
|
JSObject.class, JSObject.class, JSObject.class);
|
||||||
|
|
||||||
|
@ -178,6 +178,43 @@ public class JSNativeGenerator implements Injector, DependencyPlugin, Generator
|
|||||||
writer.append(")");
|
writer.append(")");
|
||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
if (methodRef.getName().startsWith("unwrap")) {
|
if (methodRef.getName().startsWith("unwrap")) {
|
||||||
context.writeExpr(context.getArgument(0), context.getPrecedence());
|
context.writeExpr(context.getArgument(0), context.getPrecedence());
|
||||||
@ -209,6 +246,28 @@ public class JSNativeGenerator implements Injector, DependencyPlugin, Generator
|
|||||||
case "unwrapString":
|
case "unwrapString":
|
||||||
method.getResult().propagate(agent.getType("java.lang.String"));
|
method.getResult().propagate(agent.getType("java.lang.String"));
|
||||||
break;
|
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) {
|
for (int i = 0; i < method.parameterCount(); ++i) {
|
||||||
variablesToPass[i] = marshaller.unwrapReturnValue(callLocation, variablesToPass[i],
|
variablesToPass[i] = marshaller.unwrapReturnValue(callLocation, variablesToPass[i],
|
||||||
method.parameterType(i));
|
method.parameterType(i), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
basicBlock.addAll(marshallInstructions);
|
basicBlock.addAll(marshallInstructions);
|
||||||
|
@ -218,7 +218,11 @@ class JSValueMarshaller {
|
|||||||
return JSMethods.ARRAY_WRAPPER;
|
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) {
|
if (type instanceof ValueType.Object) {
|
||||||
String className = ((ValueType.Object) type).getClassName();
|
String className = ((ValueType.Object) type).getClassName();
|
||||||
ClassReader cls = classSource.get(className);
|
ClassReader cls = classSource.get(className);
|
||||||
@ -229,6 +233,29 @@ class JSValueMarshaller {
|
|||||||
return unwrap(location, var, type);
|
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) {
|
Variable unwrap(CallLocation location, Variable var, ValueType type) {
|
||||||
if (type instanceof ValueType.Primitive) {
|
if (type instanceof ValueType.Primitive) {
|
||||||
switch (((ValueType.Primitive) type).getKind()) {
|
switch (((ValueType.Primitive) type).getKind()) {
|
||||||
@ -302,6 +329,18 @@ class JSValueMarshaller {
|
|||||||
return var;
|
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) {
|
private Variable unwrapSingleDimensionArray(CallLocation location, Variable var, ValueType type) {
|
||||||
Variable result = program.createVariable();
|
Variable result = program.createVariable();
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ package org.teavm.jso.test;
|
|||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotSame;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@ -158,6 +159,15 @@ public class ConversionTest {
|
|||||||
assertEquals(44, array[1]);
|
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 = ""
|
@JSBody(params = { "a", "b", "c", "d", "e", "f", "g", "h" }, script = ""
|
||||||
+ "return '' + a + ':' + b + ':' + c + ':' + d + ':' + e + ':' + f.toFixed(1) + ':'"
|
+ "return '' + a + ':' + b + ':' + c + ':' + d + ':' + e + ':' + f.toFixed(1) + ':'"
|
||||||
+ "+ g.toFixed(1) + ':' + h;")
|
+ "+ g.toFixed(1) + ':' + h;")
|
||||||
@ -336,4 +346,8 @@ public class ConversionTest {
|
|||||||
+ "}"
|
+ "}"
|
||||||
+ "};")
|
+ "};")
|
||||||
private static native ByRefMutator createByRefMutator();
|
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