From f61d893b6d8f00b3036323863d4615c30923b36d Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 9 Oct 2024 19:28:45 +0200 Subject: [PATCH] wasm gc: fully support exporting classes to JS --- .../org/teavm/jso/impl/JSAliasRenderer.java | 12 +--- .../java/org/teavm/jso/impl/JSOPlugin.java | 2 - .../jso/impl/JSObjectClassTransformer.java | 58 ++++++++++--------- .../java/org/teavm/jso/impl/JSTypeHelper.java | 3 +- .../java/org/teavm/jso/impl/JSWrapper.java | 5 +- .../teavm/jso/impl/JSWrapperGenerator.java | 11 ---- .../wasmgc/WasmGCJSWrapperTransformer.java | 10 ---- .../java/org/teavm/jso/export/ExportTest.java | 12 ++-- 8 files changed, 39 insertions(+), 74 deletions(-) diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSAliasRenderer.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSAliasRenderer.java index deb2a8ade..ee65df448 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSAliasRenderer.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSAliasRenderer.java @@ -93,17 +93,12 @@ class JSAliasRenderer implements RendererListener, MethodContributor { private boolean exportClassInstanceMembers(ClassReader classReader) { var members = collectMembers(classReader, AliasCollector::isInstanceMember); - var isJsClassImpl = typeHelper.isJavaScriptImplementation(classReader.getName()); - if (members.methods.isEmpty() && members.properties.isEmpty() && !isJsClassImpl) { + if (members.methods.isEmpty() && members.properties.isEmpty()) { return false; } writer.append("c").ws().append("=").ws().appendClass(classReader.getName()).append(".prototype;") .softNewLine(); - if (isJsClassImpl) { - writer.append("c[").appendFunction("$rt_jso_marker").append("]").ws().append("=").ws().append("true;") - .softNewLine(); - } for (var aliasEntry : members.methods.entrySet()) { if (classReader.getMethod(aliasEntry.getValue().getDescriptor()) == null) { @@ -253,9 +248,6 @@ class JSAliasRenderer implements RendererListener, MethodContributor { private boolean hasClassesToExpose() { for (String className : classSource.getClassNames()) { ClassReader cls = classSource.get(className); - if (typeHelper.isJavaScriptImplementation(className)) { - return true; - } for (var method : cls.getMethods()) { if (getPublicAlias(method) != null) { return true; @@ -348,6 +340,4 @@ class JSAliasRenderer implements RendererListener, MethodContributor { MethodReader methodReader = classReader.getMethod(methodRef.getDescriptor()); return methodReader != null && getPublicAlias(methodReader) != null; } - - } diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSOPlugin.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSOPlugin.java index 1c1a2ce69..36cb170f1 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSOPlugin.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSOPlugin.java @@ -98,7 +98,5 @@ public class JSOPlugin implements TeaVMPlugin { wrapperGenerator); jsHost.add(new MethodReference(JSWrapper.class, "jsToWrapper", JSObject.class, JSWrapper.class), wrapperGenerator); - jsHost.add(new MethodReference(JSWrapper.class, "isJSImplementation", Object.class, boolean.class), - wrapperGenerator); } } diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSObjectClassTransformer.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSObjectClassTransformer.java index 731e9f237..c1812b000 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSObjectClassTransformer.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSObjectClassTransformer.java @@ -88,7 +88,7 @@ class JSObjectClassTransformer implements ClassHolderTransformer { processor.setClassFilter(classFilter); } processor.processClass(cls); - if (isJavaScriptClass(cls)) { + if (isJavaScriptClass(cls) && !isJavaScriptImplementation(cls)) { processor.processMemberMethods(cls); } @@ -99,44 +99,48 @@ class JSObjectClassTransformer implements ClassHolderTransformer { } processor.createJSMethods(cls); - if (cls.hasModifier(ElementModifier.ABSTRACT) - || cls.getAnnotations().get(JSClass.class.getName()) != null && isJavaScriptClass(cls)) { + if (isJavaScriptClass(cls) && !isJavaScriptImplementation(cls)) { return; } - MethodReference functorMethod = processor.isFunctor(cls.getName()); - if (functorMethod != null) { - if (processor.isFunctor(cls.getParent()) != null) { - functorMethod = null; + var hasStaticMethods = false; + var hasMemberMethods = false; + + if (!cls.hasModifier(ElementModifier.ABSTRACT)) { + MethodReference functorMethod = processor.isFunctor(cls.getName()); + if (functorMethod != null) { + if (processor.isFunctor(cls.getParent()) != null) { + functorMethod = null; + } + } + + ClassReader originalClass = hierarchy.getClassSource().get(cls.getName()); + ExposedClass exposedClass; + if (originalClass != null) { + exposedClass = getExposedClass(cls.getName()); + } else { + exposedClass = new ExposedClass(); + createExposedClass(cls, exposedClass); + } + + exposeMethods(cls, exposedClass, context.getDiagnostics(), functorMethod); + if (!exposedClass.methods.isEmpty()) { + hasMemberMethods = true; + cls.getAnnotations().add(new AnnotationHolder(JSClassToExpose.class.getName())); } } - - ClassReader originalClass = hierarchy.getClassSource().get(cls.getName()); - ExposedClass exposedClass; - if (originalClass != null) { - exposedClass = getExposedClass(cls.getName()); - } else { - exposedClass = new ExposedClass(); - createExposedClass(cls, exposedClass); - } - - exposeMethods(cls, exposedClass, context.getDiagnostics(), functorMethod); - var hasStaticMethods = exportStaticMethods(cls, context.getDiagnostics()); - - if (isJavaScriptImplementation(cls) || !exposedClass.methods.isEmpty()) { - cls.getAnnotations().add(new AnnotationHolder(JSClassToExpose.class.getName())); - } - if (isJavaScriptImplementation(cls) || !exposedClass.methods.isEmpty() || hasStaticMethods) { + hasStaticMethods = exportStaticMethods(cls, context.getDiagnostics()); + if (hasMemberMethods || hasStaticMethods) { cls.getAnnotations().add(new AnnotationHolder(JSClassObjectToExpose.class.getName())); } - if (wasmGC && (!exposedClass.methods.isEmpty() || isJavaScriptClass(cls))) { + if (wasmGC && hasMemberMethods) { var createWrapperMethod = new MethodHolder(JSMethods.MARSHALL_TO_JS); createWrapperMethod.setLevel(AccessLevel.PUBLIC); createWrapperMethod.getModifiers().add(ElementModifier.NATIVE); cls.addMethod(createWrapperMethod); - if (!isJavaScriptClass(cls) || isJavaScriptImplementation(cls)) { + if (!isJavaScriptClass(cls)) { cls.getInterfaces().add(JSMethods.JS_MARSHALLABLE); } } @@ -429,7 +433,7 @@ class JSObjectClassTransformer implements ClassHolderTransformer { if (typeHelper.isJavaScriptImplementation(cls.getName())) { return true; } - if (cls.getAnnotations().get(JSClass.class.getName()) != null) { + if (cls.getAnnotations().get(JSClass.class.getName()) != null || cls.hasModifier(ElementModifier.ABSTRACT)) { return false; } if (cls.getParent() != null) { diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSTypeHelper.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSTypeHelper.java index af6005bb6..01893ae20 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSTypeHelper.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSTypeHelper.java @@ -84,7 +84,8 @@ public class JSTypeHelper { return false; } ClassReader cls = classSource.get(className); - if (cls == null || cls.getAnnotations().get(JSClass.class.getName()) != null) { + if (cls == null || cls.getAnnotations().get(JSClass.class.getName()) != null + || cls.hasModifier(ElementModifier.ABSTRACT)) { return false; } if (cls.getParent() != null) { diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSWrapper.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSWrapper.java index 940c335c1..c83ae217a 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSWrapper.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSWrapper.java @@ -188,14 +188,11 @@ public final class JSWrapper { @NoSideEffects public static native boolean isJava(JSObject obj); - @NoSideEffects - private static native boolean isJSImplementation(Object obj); - public static JSObject unwrap(Object o) { if (o == null) { return null; } - return isJSImplementation(o) ? marshallJavaToJs(o) : ((JSWrapper) o).js; + return (!(o instanceof JSWrapper)) ? marshallJavaToJs(o) : ((JSWrapper) o).js; } public static JSObject maybeUnwrap(Object o) { diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSWrapperGenerator.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSWrapperGenerator.java index 283971f3c..0f06f24d5 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSWrapperGenerator.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSWrapperGenerator.java @@ -50,17 +50,6 @@ public class JSWrapperGenerator implements Injector, DependencyPlugin { context.getWriter().append(")"); } break; - case "isJSImplementation": - if (context.getPrecedence().ordinal() >= Precedence.EQUALITY.ordinal()) { - context.getWriter().append("("); - } - context.writeExpr(context.getArgument(0), Precedence.MEMBER_ACCESS); - context.getWriter().append("[").appendFunction("$rt_jso_marker").append("]") - .ws().append("===").ws().append("true"); - if (context.getPrecedence().ordinal() >= Precedence.EQUALITY.ordinal()) { - context.getWriter().append(")"); - } - break; } } diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJSWrapperTransformer.java b/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJSWrapperTransformer.java index 3c80c0471..1d46e3ab5 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJSWrapperTransformer.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJSWrapperTransformer.java @@ -25,7 +25,6 @@ import org.teavm.model.ClassHolderTransformerContext; import org.teavm.model.ElementModifier; import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodHolder; -import org.teavm.model.ValueType; import org.teavm.model.emit.ProgramEmitter; class WasmGCJSWrapperTransformer implements ClassHolderTransformer { @@ -35,8 +34,6 @@ class WasmGCJSWrapperTransformer implements ClassHolderTransformer { transformMarshallMethod(cls.getMethod(new MethodDescriptor("marshallJavaToJs", Object.class, JSObject.class)), context); transformWrapMethod(cls.getMethod(new MethodDescriptor("wrap", JSObject.class, Object.class))); - transformIsJsImplementation(cls.getMethod(new MethodDescriptor("isJSImplementation", - Object.class, boolean.class)), context); transformIsJava(cls.getMethod(new MethodDescriptor("isJava", Object.class, boolean.class)), context); addCreateWrapperMethod(cls, context); } @@ -54,13 +51,6 @@ class WasmGCJSWrapperTransformer implements ClassHolderTransformer { method.setProgram(null); } - private void transformIsJsImplementation(MethodHolder method, ClassHolderTransformerContext context) { - method.getModifiers().remove(ElementModifier.NATIVE); - var pe = ProgramEmitter.create(method, context.getHierarchy()); - var obj = pe.var(1, JSObject.class); - obj.instanceOf(ValueType.parse(JSMarshallable.class)).returnValue(); - } - private void addCreateWrapperMethod(ClassHolder cls, ClassHolderTransformerContext context) { var method = new MethodHolder(new MethodDescriptor("createWrapper", JSObject.class, Object.class)); method.getModifiers().add(ElementModifier.STATIC); diff --git a/tests/src/test/java/org/teavm/jso/export/ExportTest.java b/tests/src/test/java/org/teavm/jso/export/ExportTest.java index 4daf2748a..b15a23b0b 100644 --- a/tests/src/test/java/org/teavm/jso/export/ExportTest.java +++ b/tests/src/test/java/org/teavm/jso/export/ExportTest.java @@ -97,7 +97,7 @@ public class ExportTest { @Test public void exportClassMembers() { - testExport("exportClassMembers", ModuleWithExportedClassMembers.class, true); + testExport("exportClassMembers", ModuleWithExportedClassMembers.class); } @Test @@ -107,23 +107,19 @@ public class ExportTest { @Test public void exportClasses() { - testExport("exportClasses", ModuleWithExportedClasses.class, true); + testExport("exportClasses", ModuleWithExportedClasses.class); } @Test public void varargs() { - testExport("varargs", ModuleWithVararg.class, true); + testExport("varargs", ModuleWithVararg.class); } private void testExport(String name, Class moduleClass) { - testExport(name, moduleClass, false); - } - - private void testExport(String name, Class moduleClass, boolean skipWasm) { if (jsNeeded) { testExportJs(name, moduleClass); } - if (wasmGCNeeded && !skipWasm) { + if (wasmGCNeeded) { testExportWasmGC(name, moduleClass); } }