jso: trying to improve optimization of JSWrapper

This commit is contained in:
Alexey Andreev 2023-09-26 17:48:29 +02:00
parent 9c0e3b5e59
commit eed44998f0
5 changed files with 109 additions and 30 deletions

View File

@ -553,13 +553,15 @@ class JSClassProcessor {
newInvoke.setLocation(invoke.getLocation()); newInvoke.setLocation(invoke.getLocation());
List<Variable> newArgs = new ArrayList<>(); List<Variable> newArgs = new ArrayList<>();
if (invoke.getInstance() != null) { if (invoke.getInstance() != null) {
Variable arg = marshaller.wrapArgument(callLocation, invoke.getInstance(), var arg = invoke.getInstance();
ValueType.object(method.getOwnerName()), false); arg = marshaller.wrapArgument(callLocation, arg,
ValueType.object(method.getOwnerName()), types.typeOf(arg), false);
newArgs.add(arg); newArgs.add(arg);
} }
for (int i = 0; i < invoke.getArguments().size(); ++i) { for (int i = 0; i < invoke.getArguments().size(); ++i) {
Variable arg = marshaller.wrapArgument(callLocation, invoke.getArguments().get(i), var arg = invoke.getArguments().get(i);
method.parameterType(i), byRefParams[i]); arg = marshaller.wrapArgument(callLocation, invoke.getArguments().get(i),
method.parameterType(i), types.typeOf(arg), byRefParams[i]);
newArgs.add(arg); newArgs.add(arg);
} }
newInvoke.setArguments(newArgs.toArray(new Variable[0])); newInvoke.setArguments(newArgs.toArray(new Variable[0]));
@ -597,9 +599,10 @@ class JSClassProcessor {
if (propertyName == null) { if (propertyName == null) {
propertyName = cutPrefix(method.getName(), 3); propertyName = cutPrefix(method.getName(), 3);
} }
Variable wrapped = marshaller.wrapArgument(callLocation, invoke.getArguments().get(0), var value = invoke.getArguments().get(0);
method.parameterType(0), false); value = marshaller.wrapArgument(callLocation, value,
addPropertySet(propertyName, invoke.getInstance(), wrapped, invoke.getLocation(), pure); method.parameterType(0), types.typeOf(value), false);
addPropertySet(propertyName, invoke.getInstance(), value, invoke.getLocation(), pure);
return true; return true;
} }
diagnostics.error(callLocation, "Method {{m0}} is not a proper native JavaScript property " diagnostics.error(callLocation, "Method {{m0}} is not a proper native JavaScript property "
@ -616,8 +619,9 @@ class JSClassProcessor {
private boolean processIndexer(MethodReader method, CallLocation callLocation, InvokeInstruction invoke) { private boolean processIndexer(MethodReader method, CallLocation callLocation, InvokeInstruction invoke) {
if (isProperGetIndexer(method.getDescriptor())) { if (isProperGetIndexer(method.getDescriptor())) {
Variable result = invoke.getReceiver() != null ? program.createVariable() : null; Variable result = invoke.getReceiver() != null ? program.createVariable() : null;
addIndexerGet(invoke.getInstance(), marshaller.wrapArgument(callLocation, invoke.getArguments().get(0), var index = invoke.getArguments().get(0);
method.parameterType(0), false), result, invoke.getLocation()); addIndexerGet(invoke.getInstance(), marshaller.wrapArgument(callLocation, index,
method.parameterType(0), types.typeOf(index), false), result, invoke.getLocation());
if (result != null) { if (result != null) {
result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType(), false, result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType(), false,
canBeOnlyJava(invoke.getReceiver())); canBeOnlyJava(invoke.getReceiver()));
@ -626,10 +630,11 @@ class JSClassProcessor {
return true; return true;
} }
if (isProperSetIndexer(method.getDescriptor())) { if (isProperSetIndexer(method.getDescriptor())) {
Variable index = marshaller.wrapArgument(callLocation, invoke.getArguments().get(0), var index = invoke.getArguments().get(0);
method.parameterType(0), false); marshaller.wrapArgument(callLocation, index, method.parameterType(0), types.typeOf(index), false);
Variable value = marshaller.wrapArgument(callLocation, invoke.getArguments().get(1), var value = invoke.getArguments().get(1);
method.parameterType(1), false); value = marshaller.wrapArgument(callLocation, value, method.parameterType(1),
types.typeOf(value), false);
addIndexerSet(invoke.getInstance(), index, value, invoke.getLocation()); addIndexerSet(invoke.getInstance(), index, value, invoke.getLocation());
return true; return true;
} }
@ -700,8 +705,9 @@ class JSClassProcessor {
invoke.getLocation())); invoke.getLocation()));
newInvoke.setLocation(invoke.getLocation()); newInvoke.setLocation(invoke.getLocation());
for (int i = 0; i < invoke.getArguments().size(); ++i) { for (int i = 0; i < invoke.getArguments().size(); ++i) {
Variable arg = marshaller.wrapArgument(callLocation, invoke.getArguments().get(i), var arg = invoke.getArguments().get(i);
method.parameterType(i), byRefParams[i]); arg = marshaller.wrapArgument(callLocation, arg,
method.parameterType(i), types.typeOf(arg), byRefParams[i]);
newArguments.add(arg); newArguments.add(arg);
} }
newInvoke.setArguments(newArguments.toArray(new Variable[0])); newInvoke.setArguments(newArguments.toArray(new Variable[0]));
@ -906,7 +912,8 @@ class JSClassProcessor {
ExitInstruction exit = new ExitInstruction(); ExitInstruction exit = new ExitInstruction();
if (insn.getReceiver() != null) { if (insn.getReceiver() != null) {
replacement.clear(); replacement.clear();
exit.setValueToReturn(marshaller.wrap(insn.getReceiver(), callee.getResultType(), null, false)); exit.setValueToReturn(marshaller.wrap(insn.getReceiver(), callee.getResultType(), JSType.MIXED,
null, false));
block.addAll(replacement); block.addAll(replacement);
} }
block.add(exit); block.add(exit);

View File

@ -147,7 +147,7 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
if (method.getResultType() != ValueType.VOID) { if (method.getResultType() != ValueType.VOID) {
invocation.setReceiver(program.createVariable()); invocation.setReceiver(program.createVariable());
exit.setValueToReturn(marshaller.wrapArgument(callLocation, invocation.getReceiver(), exit.setValueToReturn(marshaller.wrapArgument(callLocation, invocation.getReceiver(),
method.getResultType(), false)); method.getResultType(), JSType.MIXED, false));
basicBlock.addAll(marshallInstructions); basicBlock.addAll(marshallInstructions);
marshallInstructions.clear(); marshallInstructions.clear();
} }

View File

@ -58,7 +58,7 @@ class JSValueMarshaller {
this.replacement = replacement; this.replacement = replacement;
} }
Variable wrapArgument(CallLocation location, Variable var, ValueType type, boolean byRef) { Variable wrapArgument(CallLocation location, Variable var, ValueType type, JSType jsType, boolean byRef) {
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);
@ -66,7 +66,7 @@ class JSValueMarshaller {
return wrapFunctor(location, var, cls); return wrapFunctor(location, var, cls);
} }
} }
return wrap(var, type, location.getSourceLocation(), byRef); return wrap(var, type, jsType, location.getSourceLocation(), byRef);
} }
boolean isProperFunctor(ClassReader type) { boolean isProperFunctor(ClassReader type) {
@ -98,7 +98,7 @@ class JSValueMarshaller {
return functor; return functor;
} }
Variable wrap(Variable var, ValueType type, TextLocation location, boolean byRef) { Variable wrap(Variable var, ValueType type, JSType jsType, TextLocation location, boolean byRef) {
if (byRef) { if (byRef) {
InvokeInstruction insn = new InvokeInstruction(); InvokeInstruction insn = new InvokeInstruction();
insn.setMethod(JSMethods.ARRAY_DATA); insn.setMethod(JSMethods.ARRAY_DATA);
@ -112,14 +112,19 @@ class JSValueMarshaller {
if (type instanceof ValueType.Object) { if (type instanceof ValueType.Object) {
String className = ((ValueType.Object) type).getClassName(); String className = ((ValueType.Object) type).getClassName();
if (className.equals("java.lang.Object")) { if (className.equals("java.lang.Object")) {
if (jsType != JSType.NULL && jsType != JSType.JS) {
var unwrapNative = new InvokeInstruction(); var unwrapNative = new InvokeInstruction();
unwrapNative.setLocation(location); unwrapNative.setLocation(location);
unwrapNative.setType(InvocationType.SPECIAL); unwrapNative.setType(InvocationType.SPECIAL);
unwrapNative.setMethod(new MethodReference(JSWrapper.class, "javaToJs", Object.class, JSObject.class)); unwrapNative.setMethod(new MethodReference(JSWrapper.class,
"javaToJs", Object.class, JSObject.class));
unwrapNative.setArguments(var); unwrapNative.setArguments(var);
unwrapNative.setReceiver(program.createVariable()); unwrapNative.setReceiver(program.createVariable());
replacement.add(unwrapNative); replacement.add(unwrapNative);
return unwrapNative.getReceiver(); return unwrapNative.getReceiver();
} else {
return var;
}
} }
if (!className.equals("java.lang.String")) { if (!className.equals("java.lang.String")) {
return var; return var;
@ -516,7 +521,7 @@ class JSValueMarshaller {
} }
Variable addStringWrap(Variable var, TextLocation location) { Variable addStringWrap(Variable var, TextLocation location) {
return wrap(var, stringType, location, false); return wrap(var, stringType, JSType.MIXED, location, false);
} }
Variable addString(String str, TextLocation location) { Variable addString(String str, TextLocation location) {

View File

@ -244,6 +244,6 @@ public final class JSWrapper {
@Override @Override
public String toString() { public String toString() {
return JSObjects.toString(js); return JSObjects.isUndefined(js) ? "undefined" : JSObjects.toString(js);
} }
} }

View File

@ -244,6 +244,53 @@ public class JSWrapperTest {
a = processObject(JSNumber.valueOf(23)); a = processObject(JSNumber.valueOf(23));
assertTrue(a instanceof JSString); assertTrue(a instanceof JSString);
assertEquals("number", ((JSString) a).stringValue()); assertEquals("number", ((JSString) a).stringValue());
a = processObject(processObject(new A(24)));
assertEquals("A(24)", a.toString());
assertTrue(a instanceof A);
assertEquals(24, ((A) a).getX());
a = processObject(identity(processObject(new A(25))));
assertEquals("A(25)", a.toString());
assertTrue(a instanceof A);
assertEquals(25, ((A) a).getX());
a = processObject(identity(processObject(JSString.valueOf("asd"))));
assertEquals("asd", a.toString());
assertTrue(a instanceof JSString);
assertEquals("asd", ((JSString) a).stringValue());
a = processObject(processObject(identity(JSString.valueOf("zxc"))));
assertEquals("zxc", a.toString());
assertTrue(a instanceof JSString);
assertEquals("zxc", ((JSString) a).stringValue());
}
@Test
public void exportedObject() {
var r = new ReturningObject() {
@Override
public Object get() {
var o = createEmpty();
setProperty(o, "foo", JSNumber.valueOf(23));
return o;
}
};
var o = extract(r);
var foo = getProperty(o, "foo");
assertTrue(foo instanceof JSNumber);
assertEquals(JSNumber.valueOf(23), foo);
}
@Test
public void setProperty() {
var o = createEmpty();
callSetProperty(o, JSNumber.valueOf(23));
assertEquals(JSNumber.valueOf(23), getProperty(o, "foo"));
}
private void callSetProperty(Object instance, Object o) {
setProperty(instance, "foo", o);
} }
@JSBody(script = "return null;") @JSBody(script = "return null;")
@ -255,6 +302,22 @@ public class JSWrapperTest {
@JSBody(params = "o", script = "return typeof o === 'number' ? 'number' : o;") @JSBody(params = "o", script = "return typeof o === 'number' ? 'number' : o;")
private static native Object processObject(Object o); private static native Object processObject(Object o);
private Object identity(Object o) {
return o;
}
@JSBody(params = { "o", "name" }, script = "return o[name];")
private static native Object getProperty(Object o, String name);
@JSBody(params = { "o", "name", "value" }, script = "o[name] = value;")
private static native void setProperty(Object o, String name, Object value);
@JSBody(script = "return {};")
private static native Object createEmpty();
@JSBody(params = "o", script = "return o.get();")
private static native Object extract(ReturningObject o);
static class A { static class A {
private int x; private int x;
@ -271,4 +334,8 @@ public class JSWrapperTest {
return "A(" + x + ")"; return "A(" + x + ")";
} }
} }
interface ReturningObject extends JSObject {
Object get();
}
} }