From dff3e2f32843787cfbc98fd73544ebdcc063f4a5 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 23 Oct 2024 18:34:56 +0200 Subject: [PATCH] wasm gc: fix with passing JS objects to non-JS methods --- .../org/teavm/jso/impl/JSClassProcessor.java | 31 +++++++++++++++---- .../org/teavm/jso/test/ExportClassTest.java | 10 ++++++ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java index fe76d4405..a8ff0b206 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java @@ -324,7 +324,7 @@ class JSClassProcessor { processConstructArray((ConstructArrayInstruction) insn); } else if (insn instanceof ExitInstruction) { var exit = (ExitInstruction) insn; - exit.setValueToReturn(wrapJsAsJava(insn, exit.getValueToReturn(), + exit.setValueToReturn(convertValue(insn, exit.getValueToReturn(), methodToProcess.getResultType())); } else if (insn instanceof ClassConstantInstruction) { processClassConstant((ClassConstantInstruction) insn); @@ -399,7 +399,7 @@ class JSClassProcessor { for (var i = 0; i < invoke.getArguments().size(); ++i) { var type = invoke.getMethod().parameterType(i); var arg = invoke.getArguments().get(i); - var newArg = wrapJsAsJava(invoke, arg, type); + var newArg = convertValue(invoke, arg, type); if (newArg != arg) { if (newArgs == null) { newArgs = invoke.getArguments().toArray(new Variable[0]); @@ -412,12 +412,12 @@ class JSClassProcessor { } if (invoke.getInstance() != null) { - invoke.setInstance(wrapJsAsJava(invoke, invoke.getInstance(), ValueType.object(className))); + invoke.setInstance(convertValue(invoke, invoke.getInstance(), ValueType.object(className))); } } private void processPutField(PutFieldInstruction putField) { - putField.setValue(wrapJsAsJava(putField, putField.getValue(), putField.getFieldType())); + putField.setValue(convertValue(putField, putField.getValue(), putField.getFieldType())); } private void processGetFromArray(GetElementInstruction insn) { @@ -772,16 +772,20 @@ class JSClassProcessor { return null; } - private Variable wrapJsAsJava(Instruction instruction, Variable var, ValueType type) { + private Variable convertValue(Instruction instruction, Variable var, ValueType type) { if (!(type instanceof ValueType.Object)) { return var; } var cls = ((ValueType.Object) type).getClassName(); if (typeHelper.isJavaScriptClass(cls)) { - return var; + return convertJavaValueToJs(instruction, var); + } else { + return convertJsValueToJava(instruction, var); } + } + private Variable convertJsValueToJava(Instruction instruction, Variable var) { var varType = types.typeOf(var); if (varType != JSType.JS && varType != JSType.MIXED) { return var; @@ -796,6 +800,21 @@ class JSClassProcessor { return wrap.getReceiver(); } + private Variable convertJavaValueToJs(Instruction instruction, Variable var) { + var varType = types.typeOf(var); + if (varType == JSType.JS || varType == JSType.MIXED || varType == JSType.NULL) { + return var; + } + var wrap = new InvokeInstruction(); + wrap.setType(InvocationType.SPECIAL); + wrap.setMethod(JSMethods.UNWRAP); + wrap.setArguments(var); + wrap.setReceiver(program.createVariable()); + wrap.setLocation(instruction.getLocation()); + instruction.insertPrevious(wrap); + return wrap.getReceiver(); + } + private Variable unwrapJavaToJs(Instruction instruction, Variable var) { var varType = types.typeOf(var); if (varType != JSType.JAVA && varType != JSType.MIXED) { diff --git a/tests/src/test/java/org/teavm/jso/test/ExportClassTest.java b/tests/src/test/java/org/teavm/jso/test/ExportClassTest.java index fb5911405..c7e739c72 100644 --- a/tests/src/test/java/org/teavm/jso/test/ExportClassTest.java +++ b/tests/src/test/java/org/teavm/jso/test/ExportClassTest.java @@ -47,6 +47,16 @@ public class ExportClassTest { assertEquals("w", o.fooValue); } + @Test + public void simpleClassExportedViaStaticHelper() { + assertEquals("(OK)", callNativeJSMethod(new SimpleClass())); + assertEquals("[OK]", callNativeJSMethod(new DerivedSimpleClass())); + } + + private static String callNativeJSMethod(I a) { + return callIFromJs(a); + } + @JSBody(params = "a", script = "return a.foo('OK');") private static native String callIFromJs(I a);