From f3e035148d2a527d6726dc4b250b440a12cb06ce Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 17 Oct 2024 20:18:56 +0200 Subject: [PATCH] wasm gc: add support for importing modules in more cases --- .../DisassemblyImportSectionListener.java | 12 ++- .../gc/methods/WasmGCGenerationContext.java | 10 ++- .../gc/methods/WasmGCGenerationVisitor.java | 11 +++ .../gc/methods/WasmGCMethodGenerator.java | 3 +- .../intrinsics/gc/WasmGCIntrinsicContext.java | 6 ++ .../wasm/parser/ImportSectionListener.java | 2 +- .../wasm/parser/ImportSectionParser.java | 2 +- core/src/main/js/wasm-gc-runtime/runtime.js | 2 +- .../jso/impl/wasmgc/WasmGCJSIntrinsic.java | 74 ++++++++++++++++++- .../org/teavm/jso/impl/wasmgc/WasmGCJso.java | 2 + .../org/teavm/jso/test/ImportModuleTest.java | 8 +- 11 files changed, 116 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyImportSectionListener.java b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyImportSectionListener.java index d6e08b5ca..7b2a27074 100644 --- a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyImportSectionListener.java +++ b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyImportSectionListener.java @@ -89,7 +89,7 @@ public class DisassemblyImportSectionListener extends BaseDisassemblyListener im } @Override - public void global(WasmHollowType type) { + public void global(WasmHollowType type, boolean mutable) { writer.address().write("(import \"").write(currentModule).write("\" \"") .write(currentName).write("\" "); writer.write("(global "); @@ -99,9 +99,15 @@ public class DisassemblyImportSectionListener extends BaseDisassemblyListener im writer.write(" $").write(name); } writer.endLinkTarget(); - writer.write(" (type "); + writer.write(" "); + if (mutable) { + writer.write("(mut "); + } writeType(type); - writer.write("))").eol(); + if (mutable) { + writer.write(")"); + } + writer.write(")").eol(); ++globalIndex; } } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationContext.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationContext.java index c01f54891..2a1c9395d 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationContext.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationContext.java @@ -37,6 +37,7 @@ import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmTag; import org.teavm.backend.wasm.runtime.gc.WasmGCSupport; +import org.teavm.diagnostics.Diagnostics; import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassReaderSource; import org.teavm.model.ElementModifier; @@ -67,6 +68,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext { private boolean strict; private String entryPoint; private Consumer initializerContributors; + private Diagnostics diagnostics; public WasmGCGenerationContext(WasmModule module, WasmGCVirtualTableProvider virtualTables, WasmGCTypeMapper typeMapper, WasmFunctionTypes functionTypes, ListableClassReaderSource classes, @@ -75,7 +77,8 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext { WasmGCStandardClasses standardClasses, WasmGCStringProvider strings, WasmGCCustomGeneratorProvider customGenerators, WasmGCIntrinsicProvider intrinsics, WasmGCNameProvider names, boolean strict, String entryPoint, - Consumer initializerContributors) { + Consumer initializerContributors, + Diagnostics diagnostics) { this.module = module; this.virtualTables = virtualTables; this.typeMapper = typeMapper; @@ -94,6 +97,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext { this.strict = strict; this.entryPoint = entryPoint; this.initializerContributors = initializerContributors; + this.diagnostics = diagnostics; } public WasmGCClassInfoProvider classInfoProvider() { @@ -202,6 +206,10 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext { return intrinsics; } + public Diagnostics diagnostics() { + return diagnostics; + } + public Collection getInterfaceImplementors(String className) { if (interfaceImplementors == null) { fillInterfaceImplementors(); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java index 3da315056..64457d4e3 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java @@ -84,6 +84,7 @@ import org.teavm.backend.wasm.model.expression.WasmTest; import org.teavm.backend.wasm.model.expression.WasmThrow; import org.teavm.backend.wasm.model.expression.WasmUnreachable; import org.teavm.backend.wasm.runtime.StringInternPool; +import org.teavm.diagnostics.Diagnostics; import org.teavm.model.ClassHierarchy; import org.teavm.model.ElementModifier; import org.teavm.model.FieldReference; @@ -849,6 +850,16 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { return context.entryPoint(); } + @Override + public Diagnostics diagnostics() { + return context.diagnostics(); + } + + @Override + public MethodReference currentMethod() { + return currentMethod; + } + @Override public void addToInitializer(Consumer initializerContributor) { context.addToInitializer(initializerContributor); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java index 6e2b7f5a1..6365d7cd1 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java @@ -366,7 +366,8 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository { names, strict, entryPoint, - initializerContributors + initializerContributors, + diagnostics ); } return context; diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicContext.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicContext.java index 32e4f8b68..b362dc9c6 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicContext.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicContext.java @@ -30,7 +30,9 @@ import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmTag; import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.diagnostics.Diagnostics; import org.teavm.model.ClassHierarchy; +import org.teavm.model.MethodReference; public interface WasmGCIntrinsicContext { WasmExpression generate(Expr expr); @@ -63,5 +65,9 @@ public interface WasmGCIntrinsicContext { String entryPoint(); + Diagnostics diagnostics(); + + MethodReference currentMethod(); + void addToInitializer(Consumer initializerContributor); } diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/ImportSectionListener.java b/core/src/main/java/org/teavm/backend/wasm/parser/ImportSectionListener.java index cc997d8f0..dbe910ef5 100644 --- a/core/src/main/java/org/teavm/backend/wasm/parser/ImportSectionListener.java +++ b/core/src/main/java/org/teavm/backend/wasm/parser/ImportSectionListener.java @@ -22,7 +22,7 @@ public interface ImportSectionListener { default void function(int typeIndex) { } - default void global(WasmHollowType type) { + default void global(WasmHollowType type, boolean mutable) { } default void endEntry() { diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/ImportSectionParser.java b/core/src/main/java/org/teavm/backend/wasm/parser/ImportSectionParser.java index 4b2e2fbf4..fd0bf0c59 100644 --- a/core/src/main/java/org/teavm/backend/wasm/parser/ImportSectionParser.java +++ b/core/src/main/java/org/teavm/backend/wasm/parser/ImportSectionParser.java @@ -45,7 +45,7 @@ public class ImportSectionParser extends BaseSectionParser { } case 3: { var valueType = reader.readType(); - listener.global(valueType); + listener.global(valueType, reader.readLEB() != 0); break; } default: diff --git a/core/src/main/js/wasm-gc-runtime/runtime.js b/core/src/main/js/wasm-gc-runtime/runtime.js index 5aee92941..40f287f4c 100644 --- a/core/src/main/js/wasm-gc-runtime/runtime.js +++ b/core/src/main/js/wasm-gc-runtime/runtime.js @@ -619,7 +619,7 @@ async function wrapImports(wasmModule, imports) { if (names === void 0) { let namesByModule = []; names = namesByModule; - propertiesToAdd[name] = names; + propertiesToAdd[module] = names; promises.push((async () => { let moduleInstance = await import(module); let importsByModule = {}; diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJSIntrinsic.java b/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJSIntrinsic.java index bc5badff3..41ad1995c 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJSIntrinsic.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJSIntrinsic.java @@ -29,11 +29,14 @@ import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBranch; import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmGetGlobal; import org.teavm.backend.wasm.model.expression.WasmIsNull; import org.teavm.backend.wasm.model.expression.WasmThrow; import org.teavm.backend.wasm.runtime.gc.WasmGCSupport; import org.teavm.jso.JSObject; import org.teavm.jso.impl.JS; +import org.teavm.jso.impl.JSMethods; +import org.teavm.model.CallLocation; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; @@ -49,7 +52,6 @@ class WasmGCJSIntrinsic implements WasmGCIntrinsic { @Override public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) { - var jsoContext = WasmGCJsoContext.wrap(context); switch (invocation.getMethod().getName()) { case "wrap": return wrapString(invocation.getArguments().get(0), context); @@ -69,13 +71,79 @@ class WasmGCJSIntrinsic implements WasmGCIntrinsic { return arrayItem(invocation, context); case "get": case "getPure": - return new WasmCall(functions.getGet(jsoContext), context.generate(invocation.getArguments().get(0)), - context.generate(invocation.getArguments().get(1))); + return getProperty(invocation, context); + case "importModule": + return importModule(invocation, context); default: throw new IllegalArgumentException(); } } + private WasmExpression getProperty(InvocationExpr invocation, WasmGCIntrinsicContext context) { + var result = tryGetFromModule(invocation, context); + if (result != null) { + return result; + } + var jsoContext = WasmGCJsoContext.wrap(context); + return new WasmCall(functions.getGet(jsoContext), context.generate(invocation.getArguments().get(0)), + context.generate(invocation.getArguments().get(1))); + } + + private WasmExpression tryGetFromModule(InvocationExpr invocation, WasmGCIntrinsicContext context) { + var target = invocation.getArguments().get(0); + if (!(target instanceof InvocationExpr)) { + return null; + } + var targetCall = (InvocationExpr) target; + if (!targetCall.getMethod().equals(JSMethods.IMPORT_MODULE)) { + return null; + } + var moduleName = extractString(targetCall.getArguments().get(0)); + if (moduleName == null) { + return null; + } + + var property = invocation.getArguments().get(1); + if (!(property instanceof InvocationExpr)) { + return null; + } + var propertyCall = (InvocationExpr) property; + if (!propertyCall.getMethod().equals(JSMethods.WRAP_STRING)) { + return null; + } + var name = extractString(propertyCall.getArguments().get(0)); + if (name == null) { + return null; + } + + var jsoContext = WasmGCJsoContext.wrap(context); + var global = commonGen.getImportGlobal(jsoContext, moduleName, name); + return new WasmGetGlobal(global); + } + + private WasmExpression importModule(InvocationExpr invocation, WasmGCIntrinsicContext context) { + var jsoContext = WasmGCJsoContext.wrap(context); + var nameArg = invocation.getArguments().get(0); + var name = extractString(nameArg); + if (name == null) { + context.diagnostics().error(new CallLocation(context.currentMethod(), invocation.getLocation()), + "Invalid JS module import call"); + } + var global = commonGen.getImportGlobal(jsoContext, name, "__self__"); + return new WasmGetGlobal(global); + } + + private String extractString(Expr expr) { + if (!(expr instanceof ConstantExpr)) { + return null; + } + var constant = ((ConstantExpr) expr).getValue(); + if (!(constant instanceof String)) { + return null; + } + return (String) constant; + } + private WasmExpression wrapString(Expr stringExpr, WasmGCIntrinsicContext context) { if (stringExpr instanceof ConstantExpr) { var constantExpr = (ConstantExpr) stringExpr; diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJso.java b/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJso.java index 2072e64a3..d160e5238 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJso.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJso.java @@ -57,6 +57,8 @@ public final class WasmGCJso { jsIntrinsic); wasmGCHost.addIntrinsic(new MethodReference(JS.class, "getPure", JSObject.class, JSObject.class, JSObject.class), jsIntrinsic); + wasmGCHost.addIntrinsic(new MethodReference(JS.class, "importModule", String.class, JSObject.class), + jsIntrinsic); var wrapperIntrinsic = new WasmGCJSWrapperIntrinsic(); wasmGCHost.addIntrinsic(new MethodReference(JSWrapper.class, "wrap", JSObject.class, Object.class), diff --git a/tests/src/test/java/org/teavm/jso/test/ImportModuleTest.java b/tests/src/test/java/org/teavm/jso/test/ImportModuleTest.java index fa4fbf98a..bf776c86f 100644 --- a/tests/src/test/java/org/teavm/jso/test/ImportModuleTest.java +++ b/tests/src/test/java/org/teavm/jso/test/ImportModuleTest.java @@ -26,6 +26,7 @@ import org.teavm.junit.JsModuleTest; import org.teavm.junit.OnlyPlatform; import org.teavm.junit.ServeJS; import org.teavm.junit.SkipJVM; +import org.teavm.junit.SkipPlatform; import org.teavm.junit.TeaVMTestRunner; import org.teavm.junit.TestPlatform; @@ -39,14 +40,14 @@ public class ImportModuleTest { "org/teavm/jso/test/amd.js", "org/teavm/jso/test/amdModule.js" }) - @OnlyPlatform(TestPlatform.JAVASCRIPT) + @SkipPlatform(TestPlatform.WEBASSEMBLY_GC) public void amd() { assertEquals(23, runTestFunction()); } @Test @AttachJavaScript("org/teavm/jso/test/commonjs.js") - @OnlyPlatform(TestPlatform.JAVASCRIPT) + @SkipPlatform(TestPlatform.WEBASSEMBLY_GC) public void commonjs() { assertEquals(23, runTestFunction()); } @@ -54,7 +55,6 @@ public class ImportModuleTest { @Test @JsModuleTest @ServeJS(from = "org/teavm/jso/test/es2015.js", as = "testModule.js") - @OnlyPlatform({TestPlatform.JAVASCRIPT, TestPlatform.WEBASSEMBLY_GC}) public void es2015() { assertEquals(23, runTestFunction()); } @@ -62,7 +62,6 @@ public class ImportModuleTest { @Test @JsModuleTest @ServeJS(from = "org/teavm/jso/test/classWithConstructorInModule.js", as = "testModule.js") - @OnlyPlatform(TestPlatform.JAVASCRIPT) public void classConstructor() { var o = new ClassWithConstructorInModule(); assertEquals(99, o.getFoo()); @@ -75,7 +74,6 @@ public class ImportModuleTest { @Test @JsModuleTest @ServeJS(from = "org/teavm/jso/test/classWithConstructorInModule.js", as = "testModule.js") - @OnlyPlatform(TestPlatform.JAVASCRIPT) public void topLevel() { assertEquals("top level", ClassWithConstructorInModule.topLevelFunction()); assertEquals("top level prop", ClassWithConstructorInModule.getTopLevelProperty());