jso: properly support JSWrapper generation when java.lang.Object method is called with receiver type of JSObject interface; add optimization for JSObject.toString call.

Fix #898
This commit is contained in:
Alexey Andreev 2024-04-01 17:52:28 +02:00
parent 005765aa6e
commit f668e27daa
2 changed files with 69 additions and 6 deletions

View File

@ -100,6 +100,7 @@ class JSClassProcessor {
private JSValueMarshaller marshaller;
private IncrementalDependencyRegistration incrementalCache;
private JSImportAnnotationCache annotationCache;
private ClassReader objectClass;
JSClassProcessor(ClassReaderSource classSource, JSTypeHelper typeHelper, JSBodyRepository repository,
Diagnostics diagnostics, IncrementalDependencyRegistration incrementalCache) {
@ -286,13 +287,16 @@ class JSClassProcessor {
processIsInstance((IsInstanceInstruction) insn);
} else if (insn instanceof InvokeInstruction) {
var invoke = (InvokeInstruction) insn;
var callLocation = new CallLocation(methodToProcess.getReference(), insn.getLocation());
if (processToString(invoke, callLocation)) {
continue;
}
var method = getMethod(invoke.getMethod().getClassName(), invoke.getMethod().getDescriptor());
processInvokeArgs(invoke, method);
if (method == null) {
continue;
}
processInvokeArgs(invoke, method);
var callLocation = new CallLocation(methodToProcess.getReference(), insn.getLocation());
replacement.clear();
if (processInvocation(method, callLocation, invoke, methodToProcess)) {
insn.insertNextAll(replacement);
@ -337,10 +341,18 @@ class JSClassProcessor {
}
private void processInvokeArgs(InvokeInstruction invoke, MethodReader methodToInvoke) {
if (typeHelper.isJavaScriptClass(invoke.getMethod().getClassName())
|| methodToInvoke.getAnnotations().get(JSBody.class.getName()) != null) {
if (methodToInvoke != null && methodToInvoke.getAnnotations().get(JSBody.class.getName()) != null) {
return;
}
var className = invoke.getMethod().getClassName();
if (typeHelper.isJavaScriptClass(invoke.getMethod().getClassName())) {
if (invoke.getMethod().getName().equals("<init>")
|| getObjectClass().getMethod(invoke.getMethod().getDescriptor()) == null) {
return;
} else {
className = "java.lang.Object";
}
}
Variable[] newArgs = null;
for (var i = 0; i < invoke.getArguments().size(); ++i) {
var type = invoke.getMethod().parameterType(i);
@ -358,8 +370,7 @@ class JSClassProcessor {
}
if (invoke.getInstance() != null) {
invoke.setInstance(wrapJsAsJava(invoke, invoke.getInstance(),
ValueType.object(invoke.getMethod().getClassName())));
invoke.setInstance(wrapJsAsJava(invoke, invoke.getInstance(), ValueType.object(className)));
}
}
@ -546,6 +557,39 @@ class JSClassProcessor {
return unwrap.getReceiver();
}
private boolean processToString(InvokeInstruction invoke, CallLocation location) {
if (!invoke.getMethod().getName().equals("toString") || !invoke.getArguments().isEmpty()
|| invoke.getInstance() == null || !invoke.getMethod().getReturnType().isObject(String.class)
|| types.typeOf(invoke.getInstance()) != JSType.JS) {
return false;
}
replacement.clear();
var methodName = marshaller.addStringWrap(marshaller.addString("toString", invoke.getLocation()),
invoke.getLocation());
var jsInvoke = new InvokeInstruction();
jsInvoke.setType(InvocationType.SPECIAL);
jsInvoke.setMethod(JSMethods.invoke(0));
jsInvoke.setReceiver(program.createVariable());
jsInvoke.setLocation(invoke.getLocation());
jsInvoke.setArguments(invoke.getInstance(), methodName);
replacement.add(jsInvoke);
var assign = new AssignInstruction();
assign.setAssignee(marshaller.unwrapReturnValue(location, jsInvoke.getReceiver(),
invoke.getMethod().getReturnType(), false, canBeOnlyJava(invoke.getReceiver())));
assign.setReceiver(invoke.getReceiver());
assign.setLocation(invoke.getLocation());
replacement.add(assign);
invoke.insertNextAll(replacement);
replacement.clear();
invoke.delete();
return true;
}
private boolean processInvocation(MethodReader method, CallLocation callLocation, InvokeInstruction invoke,
MethodHolder methodToProcess) {
if (method.getAnnotations().get(JSBody.class.getName()) != null) {
@ -837,6 +881,13 @@ class JSClassProcessor {
return true;
}
private ClassReader getObjectClass() {
if (objectClass == null) {
objectClass = classSource.get("java.lang.Object");
}
return objectClass;
}
private Variable getCallTarget(InvokeInstruction invoke) {
return invoke.getInstance() != null
? invoke.getInstance()

View File

@ -321,6 +321,15 @@ public class JSWrapperTest {
assertTrue(JSObjects.isUndefined(field1));
}
@Test
public void jsToString() {
var a = createWithToString("foo");
assertEquals("foo", a.toString());
Object b = createWithToString("bar");
assertEquals("bar", b.toString());
}
private void callSetProperty(Object instance, Object o) {
setProperty(instance, "foo", o);
}
@ -401,4 +410,7 @@ public class JSWrapperTest {
interface JArraySupplier extends JSObject {
J[] get();
}
@JSBody(params = "s", script = "return { toString: () => s };")
private static native JSObject createWithToString(String s);
}