wasm gc: add support for importing modules in more cases

This commit is contained in:
Alexey Andreev 2024-10-17 20:18:56 +02:00
parent e4a2550cc6
commit f3e035148d
11 changed files with 116 additions and 16 deletions

View File

@ -89,7 +89,7 @@ public class DisassemblyImportSectionListener extends BaseDisassemblyListener im
} }
@Override @Override
public void global(WasmHollowType type) { public void global(WasmHollowType type, boolean mutable) {
writer.address().write("(import \"").write(currentModule).write("\" \"") writer.address().write("(import \"").write(currentModule).write("\" \"")
.write(currentName).write("\" "); .write(currentName).write("\" ");
writer.write("(global "); writer.write("(global ");
@ -99,9 +99,15 @@ public class DisassemblyImportSectionListener extends BaseDisassemblyListener im
writer.write(" $").write(name); writer.write(" $").write(name);
} }
writer.endLinkTarget(); writer.endLinkTarget();
writer.write(" (type "); writer.write(" ");
if (mutable) {
writer.write("(mut ");
}
writeType(type); writeType(type);
writer.write("))").eol(); if (mutable) {
writer.write(")");
}
writer.write(")").eol();
++globalIndex; ++globalIndex;
} }
} }

View File

@ -37,6 +37,7 @@ import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.WasmTag; import org.teavm.backend.wasm.model.WasmTag;
import org.teavm.backend.wasm.runtime.gc.WasmGCSupport; import org.teavm.backend.wasm.runtime.gc.WasmGCSupport;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassHierarchy;
import org.teavm.model.ClassReaderSource; import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
@ -67,6 +68,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
private boolean strict; private boolean strict;
private String entryPoint; private String entryPoint;
private Consumer<WasmGCInitializerContributor> initializerContributors; private Consumer<WasmGCInitializerContributor> initializerContributors;
private Diagnostics diagnostics;
public WasmGCGenerationContext(WasmModule module, WasmGCVirtualTableProvider virtualTables, public WasmGCGenerationContext(WasmModule module, WasmGCVirtualTableProvider virtualTables,
WasmGCTypeMapper typeMapper, WasmFunctionTypes functionTypes, ListableClassReaderSource classes, WasmGCTypeMapper typeMapper, WasmFunctionTypes functionTypes, ListableClassReaderSource classes,
@ -75,7 +77,8 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
WasmGCStandardClasses standardClasses, WasmGCStringProvider strings, WasmGCStandardClasses standardClasses, WasmGCStringProvider strings,
WasmGCCustomGeneratorProvider customGenerators, WasmGCIntrinsicProvider intrinsics, WasmGCCustomGeneratorProvider customGenerators, WasmGCIntrinsicProvider intrinsics,
WasmGCNameProvider names, boolean strict, String entryPoint, WasmGCNameProvider names, boolean strict, String entryPoint,
Consumer<WasmGCInitializerContributor> initializerContributors) { Consumer<WasmGCInitializerContributor> initializerContributors,
Diagnostics diagnostics) {
this.module = module; this.module = module;
this.virtualTables = virtualTables; this.virtualTables = virtualTables;
this.typeMapper = typeMapper; this.typeMapper = typeMapper;
@ -94,6 +97,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
this.strict = strict; this.strict = strict;
this.entryPoint = entryPoint; this.entryPoint = entryPoint;
this.initializerContributors = initializerContributors; this.initializerContributors = initializerContributors;
this.diagnostics = diagnostics;
} }
public WasmGCClassInfoProvider classInfoProvider() { public WasmGCClassInfoProvider classInfoProvider() {
@ -202,6 +206,10 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
return intrinsics; return intrinsics;
} }
public Diagnostics diagnostics() {
return diagnostics;
}
public Collection<String> getInterfaceImplementors(String className) { public Collection<String> getInterfaceImplementors(String className) {
if (interfaceImplementors == null) { if (interfaceImplementors == null) {
fillInterfaceImplementors(); fillInterfaceImplementors();

View File

@ -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.WasmThrow;
import org.teavm.backend.wasm.model.expression.WasmUnreachable; import org.teavm.backend.wasm.model.expression.WasmUnreachable;
import org.teavm.backend.wasm.runtime.StringInternPool; import org.teavm.backend.wasm.runtime.StringInternPool;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassHierarchy;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
@ -849,6 +850,16 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
return context.entryPoint(); return context.entryPoint();
} }
@Override
public Diagnostics diagnostics() {
return context.diagnostics();
}
@Override
public MethodReference currentMethod() {
return currentMethod;
}
@Override @Override
public void addToInitializer(Consumer<WasmFunction> initializerContributor) { public void addToInitializer(Consumer<WasmFunction> initializerContributor) {
context.addToInitializer(initializerContributor); context.addToInitializer(initializerContributor);

View File

@ -366,7 +366,8 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
names, names,
strict, strict,
entryPoint, entryPoint,
initializerContributors initializerContributors,
diagnostics
); );
} }
return context; return context;

View File

@ -30,7 +30,9 @@ import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.WasmTag; import org.teavm.backend.wasm.model.WasmTag;
import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassHierarchy;
import org.teavm.model.MethodReference;
public interface WasmGCIntrinsicContext { public interface WasmGCIntrinsicContext {
WasmExpression generate(Expr expr); WasmExpression generate(Expr expr);
@ -63,5 +65,9 @@ public interface WasmGCIntrinsicContext {
String entryPoint(); String entryPoint();
Diagnostics diagnostics();
MethodReference currentMethod();
void addToInitializer(Consumer<WasmFunction> initializerContributor); void addToInitializer(Consumer<WasmFunction> initializerContributor);
} }

View File

@ -22,7 +22,7 @@ public interface ImportSectionListener {
default void function(int typeIndex) { default void function(int typeIndex) {
} }
default void global(WasmHollowType type) { default void global(WasmHollowType type, boolean mutable) {
} }
default void endEntry() { default void endEntry() {

View File

@ -45,7 +45,7 @@ public class ImportSectionParser extends BaseSectionParser {
} }
case 3: { case 3: {
var valueType = reader.readType(); var valueType = reader.readType();
listener.global(valueType); listener.global(valueType, reader.readLEB() != 0);
break; break;
} }
default: default:

View File

@ -619,7 +619,7 @@ async function wrapImports(wasmModule, imports) {
if (names === void 0) { if (names === void 0) {
let namesByModule = []; let namesByModule = [];
names = namesByModule; names = namesByModule;
propertiesToAdd[name] = names; propertiesToAdd[module] = names;
promises.push((async () => { promises.push((async () => {
let moduleInstance = await import(module); let moduleInstance = await import(module);
let importsByModule = {}; let importsByModule = {};

View File

@ -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.WasmBranch;
import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmExpression; 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.WasmIsNull;
import org.teavm.backend.wasm.model.expression.WasmThrow; import org.teavm.backend.wasm.model.expression.WasmThrow;
import org.teavm.backend.wasm.runtime.gc.WasmGCSupport; import org.teavm.backend.wasm.runtime.gc.WasmGCSupport;
import org.teavm.jso.JSObject; import org.teavm.jso.JSObject;
import org.teavm.jso.impl.JS; 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.MethodReference;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
@ -49,7 +52,6 @@ class WasmGCJSIntrinsic implements WasmGCIntrinsic {
@Override @Override
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) { public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) {
var jsoContext = WasmGCJsoContext.wrap(context);
switch (invocation.getMethod().getName()) { switch (invocation.getMethod().getName()) {
case "wrap": case "wrap":
return wrapString(invocation.getArguments().get(0), context); return wrapString(invocation.getArguments().get(0), context);
@ -69,13 +71,79 @@ class WasmGCJSIntrinsic implements WasmGCIntrinsic {
return arrayItem(invocation, context); return arrayItem(invocation, context);
case "get": case "get":
case "getPure": case "getPure":
return new WasmCall(functions.getGet(jsoContext), context.generate(invocation.getArguments().get(0)), return getProperty(invocation, context);
context.generate(invocation.getArguments().get(1))); case "importModule":
return importModule(invocation, context);
default: default:
throw new IllegalArgumentException(); 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) { private WasmExpression wrapString(Expr stringExpr, WasmGCIntrinsicContext context) {
if (stringExpr instanceof ConstantExpr) { if (stringExpr instanceof ConstantExpr) {
var constantExpr = (ConstantExpr) stringExpr; var constantExpr = (ConstantExpr) stringExpr;

View File

@ -57,6 +57,8 @@ public final class WasmGCJso {
jsIntrinsic); jsIntrinsic);
wasmGCHost.addIntrinsic(new MethodReference(JS.class, "getPure", JSObject.class, JSObject.class, wasmGCHost.addIntrinsic(new MethodReference(JS.class, "getPure", JSObject.class, JSObject.class,
JSObject.class), jsIntrinsic); JSObject.class), jsIntrinsic);
wasmGCHost.addIntrinsic(new MethodReference(JS.class, "importModule", String.class, JSObject.class),
jsIntrinsic);
var wrapperIntrinsic = new WasmGCJSWrapperIntrinsic(); var wrapperIntrinsic = new WasmGCJSWrapperIntrinsic();
wasmGCHost.addIntrinsic(new MethodReference(JSWrapper.class, "wrap", JSObject.class, Object.class), wasmGCHost.addIntrinsic(new MethodReference(JSWrapper.class, "wrap", JSObject.class, Object.class),

View File

@ -26,6 +26,7 @@ import org.teavm.junit.JsModuleTest;
import org.teavm.junit.OnlyPlatform; import org.teavm.junit.OnlyPlatform;
import org.teavm.junit.ServeJS; import org.teavm.junit.ServeJS;
import org.teavm.junit.SkipJVM; import org.teavm.junit.SkipJVM;
import org.teavm.junit.SkipPlatform;
import org.teavm.junit.TeaVMTestRunner; import org.teavm.junit.TeaVMTestRunner;
import org.teavm.junit.TestPlatform; import org.teavm.junit.TestPlatform;
@ -39,14 +40,14 @@ public class ImportModuleTest {
"org/teavm/jso/test/amd.js", "org/teavm/jso/test/amd.js",
"org/teavm/jso/test/amdModule.js" "org/teavm/jso/test/amdModule.js"
}) })
@OnlyPlatform(TestPlatform.JAVASCRIPT) @SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
public void amd() { public void amd() {
assertEquals(23, runTestFunction()); assertEquals(23, runTestFunction());
} }
@Test @Test
@AttachJavaScript("org/teavm/jso/test/commonjs.js") @AttachJavaScript("org/teavm/jso/test/commonjs.js")
@OnlyPlatform(TestPlatform.JAVASCRIPT) @SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
public void commonjs() { public void commonjs() {
assertEquals(23, runTestFunction()); assertEquals(23, runTestFunction());
} }
@ -54,7 +55,6 @@ public class ImportModuleTest {
@Test @Test
@JsModuleTest @JsModuleTest
@ServeJS(from = "org/teavm/jso/test/es2015.js", as = "testModule.js") @ServeJS(from = "org/teavm/jso/test/es2015.js", as = "testModule.js")
@OnlyPlatform({TestPlatform.JAVASCRIPT, TestPlatform.WEBASSEMBLY_GC})
public void es2015() { public void es2015() {
assertEquals(23, runTestFunction()); assertEquals(23, runTestFunction());
} }
@ -62,7 +62,6 @@ public class ImportModuleTest {
@Test @Test
@JsModuleTest @JsModuleTest
@ServeJS(from = "org/teavm/jso/test/classWithConstructorInModule.js", as = "testModule.js") @ServeJS(from = "org/teavm/jso/test/classWithConstructorInModule.js", as = "testModule.js")
@OnlyPlatform(TestPlatform.JAVASCRIPT)
public void classConstructor() { public void classConstructor() {
var o = new ClassWithConstructorInModule(); var o = new ClassWithConstructorInModule();
assertEquals(99, o.getFoo()); assertEquals(99, o.getFoo());
@ -75,7 +74,6 @@ public class ImportModuleTest {
@Test @Test
@JsModuleTest @JsModuleTest
@ServeJS(from = "org/teavm/jso/test/classWithConstructorInModule.js", as = "testModule.js") @ServeJS(from = "org/teavm/jso/test/classWithConstructorInModule.js", as = "testModule.js")
@OnlyPlatform(TestPlatform.JAVASCRIPT)
public void topLevel() { public void topLevel() {
assertEquals("top level", ClassWithConstructorInModule.topLevelFunction()); assertEquals("top level", ClassWithConstructorInModule.topLevelFunction());
assertEquals("top level prop", ClassWithConstructorInModule.getTopLevelProperty()); assertEquals("top level prop", ClassWithConstructorInModule.getTopLevelProperty());