Reimplement the C# API.
This commit reimplements the C# API in terms of a Wasmtime linker. It removes the custom binding implementation that was based on reflection in favor of the linker's implementation. This should make the C# API a little closer to the Rust API. The `Engine` and `Store` types have been hidden behind the `Host` type which is responsible for hosting WebAssembly module instances. Documentation and tests have been updated.
This commit is contained in:
@@ -8,14 +8,12 @@ namespace Wasmtime.Tests
|
||||
{
|
||||
public ModuleFixture()
|
||||
{
|
||||
Engine = new EngineBuilder()
|
||||
Host = new HostBuilder()
|
||||
.WithMultiValue(true)
|
||||
.WithReferenceTypes(true)
|
||||
.Build();
|
||||
Store = Engine.CreateStore();
|
||||
var wat = Path.Combine("Modules", ModuleFileName);
|
||||
var wasm = Engine.WatToWasm(File.ReadAllText(wat));
|
||||
Module = Store.CreateModule(wat, wasm);
|
||||
|
||||
Module = Host.LoadModuleText(Path.Combine("Modules", ModuleFileName));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -26,21 +24,14 @@ namespace Wasmtime.Tests
|
||||
Module = null;
|
||||
}
|
||||
|
||||
if (!(Store is null))
|
||||
if (!(Host is null))
|
||||
{
|
||||
Store.Dispose();
|
||||
Store = null;
|
||||
}
|
||||
|
||||
if (!(Engine is null))
|
||||
{
|
||||
Engine.Dispose();
|
||||
Engine = null;
|
||||
Host.Dispose();
|
||||
Host = null;
|
||||
}
|
||||
}
|
||||
|
||||
public Engine Engine { get; set; }
|
||||
public Store Store { get; set; }
|
||||
public Host Host { get; set; }
|
||||
public Module Module { get; set; }
|
||||
|
||||
protected abstract string ModuleFileName { get; }
|
||||
|
||||
@@ -14,20 +14,16 @@ namespace Wasmtime.Tests
|
||||
{
|
||||
const string THROW_MESSAGE = "Test error message for wasmtime dotnet unit tests.";
|
||||
|
||||
class MyHost : IHost
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
[Import("add", Module = "env")]
|
||||
public int Add(int x, int y) => x + y;
|
||||
|
||||
[Import("do_throw", Module = "env")]
|
||||
public void Throw() => throw new Exception(THROW_MESSAGE);
|
||||
}
|
||||
|
||||
public FunctionThunkingTests(FunctionThunkingFixture fixture)
|
||||
{
|
||||
Fixture = fixture;
|
||||
|
||||
Fixture.Host.DefineFunction("env", "add", (int x, int y) => x + y);
|
||||
Fixture.Host.DefineFunction("env", "swap", (int x, int y) => (y, x));
|
||||
Fixture.Host.DefineFunction("env", "do_throw", () => throw new Exception(THROW_MESSAGE));
|
||||
Fixture.Host.DefineFunction("env", "check_string", (Caller caller, int address, int length) => {
|
||||
caller.GetMemory("mem").ReadString(address, length).Should().Be("Hello World");
|
||||
});
|
||||
}
|
||||
|
||||
private FunctionThunkingFixture Fixture { get; }
|
||||
@@ -35,30 +31,37 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItBindsImportMethodsAndCallsThemCorrectly()
|
||||
{
|
||||
var host = new MyHost();
|
||||
using var instance = Fixture.Module.Instantiate(host);
|
||||
using dynamic instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
|
||||
var add_func = instance.Externs.Functions.Where(f => f.Name == "add_wrapper").Single();
|
||||
int invoke_add(int x, int y) => (int)add_func.Invoke(new object[] { x, y });
|
||||
int x = instance.add(40, 2);
|
||||
x.Should().Be(42);
|
||||
x = instance.add(22, 5);
|
||||
x.Should().Be(27);
|
||||
|
||||
invoke_add(40, 2).Should().Be(42);
|
||||
invoke_add(22, 5).Should().Be(27);
|
||||
object[] results = instance.swap(10, 100);
|
||||
results.Should().Equal(new object[] { 100, 10 });
|
||||
|
||||
//Collect garbage to make sure delegate function pointers pasted to wasmtime are rooted.
|
||||
instance.check_string();
|
||||
|
||||
// Collect garbage to make sure delegate function pointers pasted to wasmtime are rooted.
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
|
||||
invoke_add(1970, 50).Should().Be(2020);
|
||||
x = instance.add(1970, 50);
|
||||
x.Should().Be(2020);
|
||||
|
||||
results = instance.swap(2020, 1970);
|
||||
results.Should().Equal(new object[] { 1970, 2020 });
|
||||
|
||||
instance.check_string();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItPropagatesExceptionsToCallersViaTraps()
|
||||
{
|
||||
var host = new MyHost();
|
||||
using var instance = Fixture.Module.Instantiate(host);
|
||||
using dynamic instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
|
||||
var throw_func = instance.Externs.Functions.Where(f => f.Name == "do_throw_wrapper").Single();
|
||||
Action action = () => throw_func.Invoke();
|
||||
Action action = () => instance.do_throw();
|
||||
|
||||
action
|
||||
.Should()
|
||||
|
||||
@@ -15,11 +15,6 @@ namespace Wasmtime.Tests
|
||||
|
||||
public class GlobalExportsTests : IClassFixture<GlobalExportsFixture>
|
||||
{
|
||||
public class Host : IHost
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
}
|
||||
|
||||
public GlobalExportsTests(GlobalExportsFixture fixture)
|
||||
{
|
||||
Fixture = fixture;
|
||||
@@ -46,7 +41,7 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItCreatesExternsForTheGlobals()
|
||||
{
|
||||
using var instance = Fixture.Module.Instantiate(new Host());
|
||||
using var instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
|
||||
dynamic dyn = instance;
|
||||
var globals = instance.Externs.Globals;
|
||||
|
||||
@@ -11,106 +11,11 @@ namespace Wasmtime.Tests
|
||||
|
||||
public class GlobalImportBindingTests : IClassFixture<GlobalImportBindingFixture>
|
||||
{
|
||||
class NoImportsHost : IHost
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
}
|
||||
|
||||
class GlobalIsStaticHost : IHost
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
[Import("global_i32_mut")]
|
||||
public static int x = 0;
|
||||
}
|
||||
|
||||
class GlobalIsNotReadOnlyHost : IHost
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
[Import("global_i32_mut")]
|
||||
public int x = 0;
|
||||
}
|
||||
|
||||
class NotAGlobalHost : IHost
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
[Import("global_i32_mut")]
|
||||
public readonly int x = 0;
|
||||
}
|
||||
|
||||
class NotAValidGlobalTypeHost : IHost
|
||||
{
|
||||
public struct NotAValue
|
||||
{
|
||||
}
|
||||
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
[Import("global_i32_mut")]
|
||||
public readonly MutableGlobal<NotAValue> x = new MutableGlobal<NotAValue>(new NotAValue());
|
||||
}
|
||||
|
||||
class TypeMismatchHost : IHost
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
[Import("global_i32_mut")]
|
||||
public readonly MutableGlobal<long> x = new MutableGlobal<long>(0);
|
||||
}
|
||||
|
||||
class NotMutHost : IHost
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
[Import("global_i32_mut")]
|
||||
public readonly Global<int> Int32Mut = new Global<int>(0);
|
||||
}
|
||||
|
||||
class MutHost : IHost
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
[Import("global_i32_mut")]
|
||||
public readonly MutableGlobal<int> Int32Mut = new MutableGlobal<int>(0);
|
||||
|
||||
[Import("global_i32")]
|
||||
public readonly MutableGlobal<int> Int32 = new MutableGlobal<int>(0);
|
||||
}
|
||||
|
||||
class ValidHost : IHost
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
[Import("global_i32_mut")]
|
||||
public readonly MutableGlobal<int> Int32Mut = new MutableGlobal<int>(0);
|
||||
|
||||
[Import("global_i32")]
|
||||
public readonly Global<int> Int32 = new Global<int>(1);
|
||||
|
||||
[Import("global_i64_mut")]
|
||||
public readonly MutableGlobal<long> Int64Mut = new MutableGlobal<long>(2);
|
||||
|
||||
[Import("global_i64")]
|
||||
public readonly Global<long> Int64 = new Global<long>(3);
|
||||
|
||||
[Import("global_f32_mut")]
|
||||
public readonly MutableGlobal<float> Float32Mut = new MutableGlobal<float>(4);
|
||||
|
||||
[Import("global_f32")]
|
||||
public readonly Global<float> Float32 = new Global<float>(5);
|
||||
|
||||
[Import("global_f64_mut")]
|
||||
public readonly MutableGlobal<double> Float64Mut = new MutableGlobal<double>(6);
|
||||
|
||||
[Import("global_f64")]
|
||||
public readonly Global<double> Float64 = new Global<double>(7);
|
||||
}
|
||||
|
||||
public GlobalImportBindingTests(GlobalImportBindingFixture fixture)
|
||||
{
|
||||
Fixture = fixture;
|
||||
|
||||
Fixture.Host.ClearDefinitions();
|
||||
}
|
||||
|
||||
private GlobalImportBindingFixture Fixture { get; set; }
|
||||
@@ -118,140 +23,119 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWithMissingImport()
|
||||
{
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new NoImportsHost()); };
|
||||
Action action = () => { using var instance = Fixture.Host.Instantiate(Fixture.Module); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
.Throw<WasmtimeException>()
|
||||
.WithMessage("Failed to bind global import 'global_i32_mut': the host does not contain a global field with a matching 'Import' attribute.");
|
||||
.WithMessage("unknown import: `::global_i32_mut` has not been defined");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWithStaticField()
|
||||
public void ItFailsToDefineAGlobalWithInvalidType()
|
||||
{
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new GlobalIsStaticHost()); };
|
||||
Action action = () => { Fixture.Host.DefineGlobal("", "global_i32_mut", "invalid"); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
.Throw<WasmtimeException>()
|
||||
.WithMessage("Unable to bind 'GlobalIsStaticHost.x' to WebAssembly import 'global_i32_mut': field cannot be static.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWithNonReadOnlyField()
|
||||
{
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new GlobalIsNotReadOnlyHost()); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
.Throw<WasmtimeException>()
|
||||
.WithMessage("Unable to bind 'GlobalIsNotReadOnlyHost.x' to WebAssembly import 'global_i32_mut': field must be readonly.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWithInvalidType()
|
||||
{
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new NotAGlobalHost()); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
.Throw<WasmtimeException>()
|
||||
.WithMessage("Unable to bind 'NotAGlobalHost.x' to WebAssembly import 'global_i32_mut': field is expected to be of type 'Global<T>'.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWithInvalidGlobalType()
|
||||
{
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new NotAValidGlobalTypeHost()); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
.Throw<NotSupportedException>()
|
||||
.WithMessage("Type 'Wasmtime.Tests.GlobalImportBindingTests+NotAValidGlobalTypeHost+NotAValue' is not a supported WebAssembly value type.");
|
||||
.WithMessage("Global variables cannot be of type 'System.String'.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWithGlobalTypeMismatch()
|
||||
{
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new TypeMismatchHost()); };
|
||||
Fixture.Host.DefineGlobal("", "global_i32_mut", 0L);
|
||||
Action action = () => { using var instance = Fixture.Host.Instantiate(Fixture.Module); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
.Throw<WasmtimeException>()
|
||||
.WithMessage("Unable to bind 'TypeMismatchHost.x' to WebAssembly import 'global_i32_mut': global type argument is expected to be of type 'int'.");
|
||||
.WithMessage("incompatible import type for `::global_i32_mut` specified*");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWhenGlobalIsNotMut()
|
||||
{
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new NotMutHost()); };
|
||||
Fixture.Host.DefineGlobal("", "global_i32_mut", 1);
|
||||
Action action = () => { using var instance = Fixture.Host.Instantiate(Fixture.Module); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
.Throw<WasmtimeException>()
|
||||
.WithMessage("Unable to bind 'NotMutHost.Int32Mut' to WebAssembly import 'global_i32_mut': the import is mutable (use the 'MutableGlobal' type).");
|
||||
.WithMessage("incompatible import type for `::global_i32_mut` specified*");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWhenGlobalIsMut()
|
||||
{
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new MutHost()); };
|
||||
Fixture.Host.DefineMutableGlobal("", "global_i32_mut", 0);
|
||||
Fixture.Host.DefineMutableGlobal("", "global_i32", 0);
|
||||
Action action = () => { using var instance = Fixture.Host.Instantiate(Fixture.Module); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
.Throw<WasmtimeException>()
|
||||
.WithMessage("Unable to bind 'MutHost.Int32' to WebAssembly import 'global_i32': the import is constant (use the 'Global' type).");
|
||||
.WithMessage("incompatible import type for `::global_i32` specified*");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItBindsTheGlobalsCorrectly()
|
||||
{
|
||||
var host = new ValidHost();
|
||||
using dynamic instance = Fixture.Module.Instantiate(host);
|
||||
var global_i32_mut = Fixture.Host.DefineMutableGlobal("", "global_i32_mut", 0);
|
||||
var global_i32 = Fixture.Host.DefineGlobal("", "global_i32", 1);
|
||||
var global_i64_mut = Fixture.Host.DefineMutableGlobal("", "global_i64_mut", 2L);
|
||||
var global_i64 = Fixture.Host.DefineGlobal("", "global_i64", 3L);
|
||||
var global_f32_mut = Fixture.Host.DefineMutableGlobal("", "global_f32_mut", 4f);
|
||||
var global_f32 = Fixture.Host.DefineGlobal("", "global_f32", 5f);
|
||||
var global_f64_mut = Fixture.Host.DefineMutableGlobal("", "global_f64_mut", 6.0);
|
||||
var global_f64 = Fixture.Host.DefineGlobal("", "global_f64", 7.0);
|
||||
|
||||
host.Int32Mut.Value.Should().Be(0);
|
||||
using dynamic instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
|
||||
global_i32_mut.Value.Should().Be(0);
|
||||
((int)instance.get_global_i32_mut()).Should().Be(0);
|
||||
host.Int32.Value.Should().Be(1);
|
||||
global_i32.Value.Should().Be(1);
|
||||
((int)instance.get_global_i32()).Should().Be(1);
|
||||
host.Int64Mut.Value.Should().Be(2);
|
||||
global_i64_mut.Value.Should().Be(2);
|
||||
((long)instance.get_global_i64_mut()).Should().Be(2);
|
||||
host.Int64.Value.Should().Be(3);
|
||||
global_i64.Value.Should().Be(3);
|
||||
((long)instance.get_global_i64()).Should().Be(3);
|
||||
host.Float32Mut.Value.Should().Be(4);
|
||||
global_f32_mut.Value.Should().Be(4);
|
||||
((float)instance.get_global_f32_mut()).Should().Be(4);
|
||||
host.Float32.Value.Should().Be(5);
|
||||
global_f32.Value.Should().Be(5);
|
||||
((float)instance.get_global_f32()).Should().Be(5);
|
||||
host.Float64Mut.Value.Should().Be(6);
|
||||
global_f64_mut.Value.Should().Be(6);
|
||||
((double)instance.get_global_f64_mut()).Should().Be(6);
|
||||
host.Float64.Value.Should().Be(7);
|
||||
global_f64.Value.Should().Be(7);
|
||||
((double)instance.get_global_f64()).Should().Be(7);
|
||||
|
||||
host.Int32Mut.Value = 10;
|
||||
host.Int32Mut.Value.Should().Be(10);
|
||||
global_i32_mut.Value = 10;
|
||||
global_i32_mut.Value.Should().Be(10);
|
||||
((int)instance.get_global_i32_mut()).Should().Be(10);
|
||||
instance.set_global_i32_mut(11);
|
||||
host.Int32Mut.Value.Should().Be(11);
|
||||
global_i32_mut.Value.Should().Be(11);
|
||||
((int)instance.get_global_i32_mut()).Should().Be(11);
|
||||
|
||||
host.Int64Mut.Value = 12;
|
||||
host.Int64Mut.Value.Should().Be(12);
|
||||
global_i64_mut.Value = 12;
|
||||
global_i64_mut.Value.Should().Be(12);
|
||||
((long)instance.get_global_i64_mut()).Should().Be(12);
|
||||
instance.set_global_i64_mut(13);
|
||||
host.Int64Mut.Value.Should().Be(13);
|
||||
global_i64_mut.Value.Should().Be(13);
|
||||
((long)instance.get_global_i64_mut()).Should().Be(13);
|
||||
|
||||
host.Float32Mut.Value = 14;
|
||||
host.Float32Mut.Value.Should().Be(14);
|
||||
global_f32_mut.Value = 14;
|
||||
global_f32_mut.Value.Should().Be(14);
|
||||
((float)instance.get_global_f32_mut()).Should().Be(14);
|
||||
instance.set_global_f32_mut(15);
|
||||
host.Float32Mut.Value.Should().Be(15);
|
||||
global_f32_mut.Value.Should().Be(15);
|
||||
((float)instance.get_global_f32_mut()).Should().Be(15);
|
||||
|
||||
host.Float64Mut.Value = 16;
|
||||
host.Float64Mut.Value.Should().Be(16);
|
||||
global_f64_mut.Value = 16;
|
||||
global_f64_mut.Value.Should().Be(16);
|
||||
((double)instance.get_global_f64_mut()).Should().Be(16);
|
||||
instance.set_global_f64_mut(17);
|
||||
host.Float64Mut.Value.Should().Be(17);
|
||||
global_f64_mut.Value.Should().Be(17);
|
||||
((double)instance.get_global_f64_mut()).Should().Be(17);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,11 +13,6 @@ namespace Wasmtime.Tests
|
||||
|
||||
public class MemoryExportsTests : IClassFixture<MemoryExportsFixture>
|
||||
{
|
||||
public class Host : IHost
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
}
|
||||
|
||||
public MemoryExportsTests(MemoryExportsFixture fixture)
|
||||
{
|
||||
Fixture = fixture;
|
||||
@@ -44,8 +39,7 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItCreatesExternsForTheMemories()
|
||||
{
|
||||
var host = new Host();
|
||||
using var instance = Fixture.Module.Instantiate(host);
|
||||
using var instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
|
||||
instance.Externs.Memories.Count.Should().Be(1);
|
||||
|
||||
|
||||
@@ -11,62 +11,11 @@ namespace Wasmtime.Tests
|
||||
|
||||
public class MemoryImportBindingTests : IClassFixture<MemoryImportBindingFixture>
|
||||
{
|
||||
class MissingImportsHost : IHost
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
}
|
||||
|
||||
class MemoryIsStaticHost : IHost
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
[Import("mem")]
|
||||
public static Memory x = new Memory(minimum: 1);
|
||||
}
|
||||
|
||||
class MemoryIsNotReadOnlyHost : IHost
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
[Import("mem")]
|
||||
public Memory x = new Memory(minimum: 1);
|
||||
}
|
||||
|
||||
class NotAMemoryHost : IHost
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
[Import("mem")]
|
||||
public readonly int x = 0;
|
||||
}
|
||||
|
||||
class InvalidMinimumHost : IHost
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
[Import("mem")]
|
||||
public readonly Memory Mem = new Memory(minimum: 2);
|
||||
}
|
||||
|
||||
class InvalidMaximumHost : IHost
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
[Import("mem")]
|
||||
public readonly Memory Mem = new Memory(maximum: 2);
|
||||
}
|
||||
|
||||
class ValidHost : IHost
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
[Import("mem")]
|
||||
public readonly Memory Mem = new Memory(minimum: 1);
|
||||
}
|
||||
|
||||
public MemoryImportBindingTests(MemoryImportBindingFixture fixture)
|
||||
{
|
||||
Fixture = fixture;
|
||||
|
||||
Fixture.Host.ClearDefinitions();
|
||||
}
|
||||
|
||||
private MemoryImportBindingFixture Fixture { get; set; }
|
||||
@@ -74,112 +23,58 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWithMissingImport()
|
||||
{
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new MissingImportsHost()); };
|
||||
Action action = () => { using var instance = Fixture.Host.Instantiate(Fixture.Module); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
.Throw<WasmtimeException>()
|
||||
.WithMessage("Failed to bind memory import 'mem': the host does not contain a memory field with a matching 'Import' attribute.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWithStaticField()
|
||||
{
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new MemoryIsStaticHost()); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
.Throw<WasmtimeException>()
|
||||
.WithMessage("Unable to bind 'MemoryIsStaticHost.x' to WebAssembly import 'mem': field cannot be static.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWithNonReadOnlyField()
|
||||
{
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new MemoryIsNotReadOnlyHost()); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
.Throw<WasmtimeException>()
|
||||
.WithMessage("Unable to bind 'MemoryIsNotReadOnlyHost.x' to WebAssembly import 'mem': field must be readonly.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWithInvalidType()
|
||||
{
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new NotAMemoryHost()); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
.Throw<WasmtimeException>()
|
||||
.WithMessage("Unable to bind 'NotAMemoryHost.x' to WebAssembly import 'mem': field is expected to be of type 'Memory'.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWhenMemoryHasInvalidMinimum()
|
||||
{
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new InvalidMinimumHost()); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
.Throw<WasmtimeException>()
|
||||
.WithMessage("Unable to bind 'InvalidMinimumHost.Mem' to WebAssembly import 'mem': Memory does not have the expected minimum of 1 page(s).");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWhenMemoryHasInvalidMaximum()
|
||||
{
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new InvalidMaximumHost()); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
.Throw<WasmtimeException>()
|
||||
.WithMessage("Unable to bind 'InvalidMaximumHost.Mem' to WebAssembly import 'mem': Memory does not have the expected maximum of 4294967295 page(s).");
|
||||
.WithMessage("unknown import: `::mem` has not been defined");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItBindsTheGlobalsCorrectly()
|
||||
{
|
||||
var host = new ValidHost();
|
||||
using dynamic instance = Fixture.Module.Instantiate(host);
|
||||
var mem = Fixture.Host.DefineMemory("", "mem");
|
||||
|
||||
host.Mem.ReadString(0, 11).Should().Be("Hello World");
|
||||
int written = host.Mem.WriteString(0, "WebAssembly Rocks!");
|
||||
host.Mem.ReadString(0, written).Should().Be("WebAssembly Rocks!");
|
||||
using dynamic instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
|
||||
host.Mem.ReadByte(20).Should().Be(1);
|
||||
host.Mem.WriteByte(20, 11);
|
||||
host.Mem.ReadByte(20).Should().Be(11);
|
||||
mem.ReadString(0, 11).Should().Be("Hello World");
|
||||
int written = mem.WriteString(0, "WebAssembly Rocks!");
|
||||
mem.ReadString(0, written).Should().Be("WebAssembly Rocks!");
|
||||
|
||||
mem.ReadByte(20).Should().Be(1);
|
||||
mem.WriteByte(20, 11);
|
||||
mem.ReadByte(20).Should().Be(11);
|
||||
((byte)instance.ReadByte()).Should().Be(11);
|
||||
|
||||
host.Mem.ReadInt16(21).Should().Be(2);
|
||||
host.Mem.WriteInt16(21, 12);
|
||||
host.Mem.ReadInt16(21).Should().Be(12);
|
||||
mem.ReadInt16(21).Should().Be(2);
|
||||
mem.WriteInt16(21, 12);
|
||||
mem.ReadInt16(21).Should().Be(12);
|
||||
((short)instance.ReadInt16()).Should().Be(12);
|
||||
|
||||
host.Mem.ReadInt32(23).Should().Be(3);
|
||||
host.Mem.WriteInt32(23, 13);
|
||||
host.Mem.ReadInt32(23).Should().Be(13);
|
||||
mem.ReadInt32(23).Should().Be(3);
|
||||
mem.WriteInt32(23, 13);
|
||||
mem.ReadInt32(23).Should().Be(13);
|
||||
((int)instance.ReadInt32()).Should().Be(13);
|
||||
|
||||
host.Mem.ReadInt64(27).Should().Be(4);
|
||||
host.Mem.WriteInt64(27, 14);
|
||||
host.Mem.ReadInt64(27).Should().Be(14);
|
||||
mem.ReadInt64(27).Should().Be(4);
|
||||
mem.WriteInt64(27, 14);
|
||||
mem.ReadInt64(27).Should().Be(14);
|
||||
((long)instance.ReadInt64()).Should().Be(14);
|
||||
|
||||
host.Mem.ReadSingle(35).Should().Be(5);
|
||||
host.Mem.WriteSingle(35, 15);
|
||||
host.Mem.ReadSingle(35).Should().Be(15);
|
||||
mem.ReadSingle(35).Should().Be(5);
|
||||
mem.WriteSingle(35, 15);
|
||||
mem.ReadSingle(35).Should().Be(15);
|
||||
((float)instance.ReadFloat32()).Should().Be(15);
|
||||
|
||||
host.Mem.ReadDouble(39).Should().Be(6);
|
||||
host.Mem.WriteDouble(39, 16);
|
||||
host.Mem.ReadDouble(39).Should().Be(16);
|
||||
mem.ReadDouble(39).Should().Be(6);
|
||||
mem.WriteDouble(39, 16);
|
||||
mem.ReadDouble(39).Should().Be(16);
|
||||
((double)instance.ReadFloat64()).Should().Be(16);
|
||||
|
||||
host.Mem.ReadIntPtr(48).Should().Be((IntPtr)7);
|
||||
host.Mem.WriteIntPtr(48, (IntPtr)17);
|
||||
host.Mem.ReadIntPtr(48).Should().Be((IntPtr)17);
|
||||
mem.ReadIntPtr(48).Should().Be((IntPtr)7);
|
||||
mem.WriteIntPtr(48, (IntPtr)17);
|
||||
mem.ReadIntPtr(48).Should().Be((IntPtr)17);
|
||||
((IntPtr)instance.ReadIntPtr()).Should().Be((IntPtr)17);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
(module
|
||||
(type $FUNCSIG$iii (func (param i32 i32) (result i32)))
|
||||
(type $FUNCSIG$v (func))
|
||||
(import "env" "add" (func $add (param i32 i32) (result i32)))
|
||||
(import "env" "do_throw" (func $do_throw))
|
||||
(table 0 anyfunc)
|
||||
(memory $0 1)
|
||||
(export "memory" (memory $0))
|
||||
(export "add_wrapper" (func $add_wrapper))
|
||||
(export "do_throw_wrapper" (func $do_throw_wrapper))
|
||||
(func $add_wrapper (; 2 ;) (param $0 i32) (param $1 i32) (result i32)
|
||||
(call $add
|
||||
(get_local $0)
|
||||
(get_local $1)
|
||||
)
|
||||
(import "env" "add" (func $env.add (param i32 i32) (result i32)))
|
||||
(import "env" "swap" (func $env.swap (param i32 i32) (result i32 i32)))
|
||||
(import "env" "do_throw" (func $env.do_throw))
|
||||
(import "env" "check_string" (func $env.check_string (param i32 i32)))
|
||||
(memory (export "mem") 1)
|
||||
(export "add" (func $add))
|
||||
(export "swap" (func $swap))
|
||||
(export "do_throw" (func $do_throw))
|
||||
(export "check_string" (func $check_string))
|
||||
(func $add (param i32 i32) (result i32)
|
||||
(call $env.add (local.get 0) (local.get 1))
|
||||
)
|
||||
(func $do_throw_wrapper (; 3 ;)
|
||||
(call $do_throw)
|
||||
(func $swap (param i32 i32) (result i32 i32)
|
||||
(call $env.swap (local.get 0) (local.get 1))
|
||||
)
|
||||
(func $do_throw
|
||||
(call $env.do_throw)
|
||||
)
|
||||
(func $check_string
|
||||
(call $env.check_string (i32.const 0) (i32.const 11))
|
||||
)
|
||||
(data (i32.const 0) "Hello World")
|
||||
)
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace Wasmtime.Tests
|
||||
public WasiSnapshot0Tests(WasiSnapshot0Fixture fixture)
|
||||
{
|
||||
Fixture = fixture;
|
||||
|
||||
Fixture.Host.ClearDefinitions();
|
||||
}
|
||||
|
||||
private WasiSnapshot0Fixture Fixture { get; set; }
|
||||
@@ -24,7 +26,8 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItHasNoEnvironmentByDefault()
|
||||
{
|
||||
using var instance = Fixture.Module.Instantiate(new Wasi(Fixture.Module.Store, "wasi_unstable"));
|
||||
Fixture.Host.DefineWasi("wasi_unstable");
|
||||
using var instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
@@ -43,11 +46,12 @@ namespace Wasmtime.Tests
|
||||
{"VERY", "COOL"},
|
||||
};
|
||||
|
||||
var wasi = new WasiBuilder("wasi_unstable")
|
||||
.WithEnvironmentVariables(env.Select(kvp => (kvp.Key, kvp.Value)))
|
||||
.Build(Fixture.Module.Store);
|
||||
var config = new WasiConfiguration()
|
||||
.WithEnvironmentVariables(env.Select(kvp => (kvp.Key, kvp.Value)));
|
||||
|
||||
using var instance = Fixture.Module.Instantiate(wasi);
|
||||
Fixture.Host.DefineWasi("wasi_unstable", config);
|
||||
|
||||
using var instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
@@ -67,11 +71,12 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItInheritsEnvironment()
|
||||
{
|
||||
var wasi = new WasiBuilder("wasi_unstable")
|
||||
.WithInheritedEnvironment()
|
||||
.Build(Fixture.Module.Store);
|
||||
var config = new WasiConfiguration()
|
||||
.WithInheritedEnvironment();
|
||||
|
||||
using var instance = Fixture.Module.Instantiate(wasi);
|
||||
Fixture.Host.DefineWasi("wasi_unstable", config);
|
||||
|
||||
using var instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
@@ -83,7 +88,9 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItHasNoArgumentsByDefault()
|
||||
{
|
||||
using var instance = Fixture.Module.Instantiate(new Wasi(Fixture.Module.Store, "wasi_unstable"));
|
||||
Fixture.Host.DefineWasi("wasi_unstable");
|
||||
|
||||
using var instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
@@ -103,11 +110,12 @@ namespace Wasmtime.Tests
|
||||
"COOL"
|
||||
};
|
||||
|
||||
var wasi = new WasiBuilder("wasi_unstable")
|
||||
.WithArgs(args)
|
||||
.Build(Fixture.Module.Store);
|
||||
var config = new WasiConfiguration()
|
||||
.WithArgs(args);
|
||||
|
||||
using var instance = Fixture.Module.Instantiate(wasi);
|
||||
Fixture.Host.DefineWasi("wasi_unstable", config);
|
||||
|
||||
using var instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
@@ -127,11 +135,12 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItInheritsArguments()
|
||||
{
|
||||
var wasi = new WasiBuilder("wasi_unstable")
|
||||
.WithInheritedArgs()
|
||||
.Build(Fixture.Module.Store);
|
||||
var config = new WasiConfiguration()
|
||||
.WithInheritedArgs();
|
||||
|
||||
using var instance = Fixture.Module.Instantiate(wasi);
|
||||
Fixture.Host.DefineWasi("wasi_unstable", config);
|
||||
|
||||
using var instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
@@ -148,11 +157,12 @@ namespace Wasmtime.Tests
|
||||
using var file = new TempFile();
|
||||
File.WriteAllText(file.Path, MESSAGE);
|
||||
|
||||
var wasi = new WasiBuilder("wasi_unstable")
|
||||
.WithStandardInput(file.Path)
|
||||
.Build(Fixture.Module.Store);
|
||||
var config = new WasiConfiguration()
|
||||
.WithStandardInput(file.Path);
|
||||
|
||||
using var instance = Fixture.Module.Instantiate(wasi);
|
||||
Fixture.Host.DefineWasi("wasi_unstable", config);
|
||||
|
||||
using var instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
@@ -173,19 +183,19 @@ namespace Wasmtime.Tests
|
||||
|
||||
using var file = new TempFile();
|
||||
|
||||
var builder = new WasiBuilder("wasi_unstable");
|
||||
var config = new WasiConfiguration();
|
||||
if (fd == 1)
|
||||
{
|
||||
builder.WithStandardOutput(file.Path);
|
||||
config.WithStandardOutput(file.Path);
|
||||
}
|
||||
else if (fd == 2)
|
||||
{
|
||||
builder.WithStandardError(file.Path);
|
||||
config.WithStandardError(file.Path);
|
||||
}
|
||||
|
||||
var wasi = builder.Build(Fixture.Module.Store);
|
||||
Fixture.Host.DefineWasi("wasi_unstable", config);
|
||||
|
||||
using var instance = Fixture.Module.Instantiate(wasi);
|
||||
using var instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
@@ -206,11 +216,12 @@ namespace Wasmtime.Tests
|
||||
|
||||
using var file = new TempFile();
|
||||
|
||||
var wasi = new WasiBuilder("wasi_unstable")
|
||||
.WithPreopenedDirectory(Path.GetDirectoryName(file.Path), "/foo")
|
||||
.Build(Fixture.Module.Store);
|
||||
var config = new WasiConfiguration()
|
||||
.WithPreopenedDirectory(Path.GetDirectoryName(file.Path), "/foo");
|
||||
|
||||
using var instance = Fixture.Module.Instantiate(wasi);
|
||||
Fixture.Host.DefineWasi("wasi_unstable", config);
|
||||
|
||||
using var instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
|
||||
@@ -24,7 +24,9 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItHasNoEnvironmentByDefault()
|
||||
{
|
||||
using var instance = Fixture.Module.Instantiate(new Wasi(Fixture.Module.Store, "wasi_snapshot_preview1"));
|
||||
Fixture.Host.DefineWasi("wasi_snapshot_preview1");
|
||||
|
||||
using var instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
@@ -43,11 +45,12 @@ namespace Wasmtime.Tests
|
||||
{"VERY", "COOL"},
|
||||
};
|
||||
|
||||
var wasi = new WasiBuilder("wasi_snapshot_preview1")
|
||||
.WithEnvironmentVariables(env.Select(kvp => (kvp.Key, kvp.Value)))
|
||||
.Build(Fixture.Module.Store);
|
||||
var config = new WasiConfiguration()
|
||||
.WithEnvironmentVariables(env.Select(kvp => (kvp.Key, kvp.Value)));
|
||||
|
||||
using var instance = Fixture.Module.Instantiate(wasi);
|
||||
Fixture.Host.DefineWasi("wasi_snapshot_preview1", config);
|
||||
|
||||
using var instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
@@ -67,11 +70,12 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItInheritsEnvironment()
|
||||
{
|
||||
var wasi = new WasiBuilder("wasi_snapshot_preview1")
|
||||
.WithInheritedEnvironment()
|
||||
.Build(Fixture.Module.Store);
|
||||
var config = new WasiConfiguration()
|
||||
.WithInheritedEnvironment();
|
||||
|
||||
using var instance = Fixture.Module.Instantiate(wasi);
|
||||
Fixture.Host.DefineWasi("wasi_snapshot_preview1", config);
|
||||
|
||||
using var instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
@@ -83,7 +87,9 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItHasNoArgumentsByDefault()
|
||||
{
|
||||
using var instance = Fixture.Module.Instantiate(new Wasi(Fixture.Module.Store, "wasi_snapshot_preview1"));
|
||||
Fixture.Host.DefineWasi("wasi_snapshot_preview1");
|
||||
|
||||
using var instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
@@ -103,11 +109,12 @@ namespace Wasmtime.Tests
|
||||
"COOL"
|
||||
};
|
||||
|
||||
var wasi = new WasiBuilder("wasi_snapshot_preview1")
|
||||
.WithArgs(args)
|
||||
.Build(Fixture.Module.Store);
|
||||
var config = new WasiConfiguration()
|
||||
.WithArgs(args);
|
||||
|
||||
using var instance = Fixture.Module.Instantiate(wasi);
|
||||
Fixture.Host.DefineWasi("wasi_snapshot_preview1", config);
|
||||
|
||||
using var instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
@@ -127,11 +134,12 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItInheritsArguments()
|
||||
{
|
||||
var wasi = new WasiBuilder("wasi_snapshot_preview1")
|
||||
.WithInheritedArgs()
|
||||
.Build(Fixture.Module.Store);
|
||||
var config = new WasiConfiguration()
|
||||
.WithInheritedArgs();
|
||||
|
||||
using var instance = Fixture.Module.Instantiate(wasi);
|
||||
Fixture.Host.DefineWasi("wasi_snapshot_preview1", config);
|
||||
|
||||
using var instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
@@ -148,11 +156,12 @@ namespace Wasmtime.Tests
|
||||
using var file = new TempFile();
|
||||
File.WriteAllText(file.Path, MESSAGE);
|
||||
|
||||
var wasi = new WasiBuilder("wasi_snapshot_preview1")
|
||||
.WithStandardInput(file.Path)
|
||||
.Build(Fixture.Module.Store);
|
||||
var config = new WasiConfiguration()
|
||||
.WithStandardInput(file.Path);
|
||||
|
||||
using var instance = Fixture.Module.Instantiate(wasi);
|
||||
Fixture.Host.DefineWasi("wasi_snapshot_preview1", config);
|
||||
|
||||
using var instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
@@ -173,19 +182,19 @@ namespace Wasmtime.Tests
|
||||
|
||||
using var file = new TempFile();
|
||||
|
||||
var builder = new WasiBuilder("wasi_snapshot_preview1");
|
||||
var config = new WasiConfiguration();
|
||||
if (fd == 1)
|
||||
{
|
||||
builder.WithStandardOutput(file.Path);
|
||||
config.WithStandardOutput(file.Path);
|
||||
}
|
||||
else if (fd == 2)
|
||||
{
|
||||
builder.WithStandardError(file.Path);
|
||||
config.WithStandardError(file.Path);
|
||||
}
|
||||
|
||||
var wasi = builder.Build(Fixture.Module.Store);
|
||||
Fixture.Host.DefineWasi("wasi_snapshot_preview1", config);
|
||||
|
||||
using var instance = Fixture.Module.Instantiate(wasi);
|
||||
using var instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
@@ -206,11 +215,12 @@ namespace Wasmtime.Tests
|
||||
|
||||
using var file = new TempFile();
|
||||
|
||||
var wasi = new WasiBuilder("wasi_snapshot_preview1")
|
||||
.WithPreopenedDirectory(Path.GetDirectoryName(file.Path), "/foo")
|
||||
.Build(Fixture.Module.Store);
|
||||
var config = new WasiConfiguration()
|
||||
.WithPreopenedDirectory(Path.GetDirectoryName(file.Path), "/foo");
|
||||
|
||||
using var instance = Fixture.Module.Instantiate(wasi);
|
||||
Fixture.Host.DefineWasi("wasi_snapshot_preview1", config);
|
||||
|
||||
using var instance = Fixture.Host.Instantiate(Fixture.Module);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
|
||||
Reference in New Issue
Block a user