Move Wasmtime for .NET to the Wasmtime repo.

This moves the Wasmtime for .NET implementation to the Wasmtime repo.

Wasmtime for .NET is a binding of the Wasmtime API for use in .NET.
This commit is contained in:
Peter Huene
2019-11-22 17:11:00 -08:00
parent bbe2a797ba
commit 9fdf5bce8e
100 changed files with 6391 additions and 0 deletions

View File

@@ -0,0 +1,43 @@
using System;
using System.IO;
using Wasmtime;
namespace Wasmtime.Tests
{
public abstract class ModuleFixture : IDisposable
{
public ModuleFixture()
{
Engine = new Engine();
Store = Engine.CreateStore();
Module = Store.CreateModule(Path.Combine("Modules", ModuleFileName));
}
public void Dispose()
{
if (Module != null)
{
Module.Dispose();
Module = null;
}
if (Store != null)
{
Store.Dispose();
Store = null;
}
if (Engine != null)
{
Engine.Dispose();
Engine = null;
}
}
public Engine Engine { get; set; }
public Store Store { get; set; }
public Module Module { get; set; }
protected abstract string ModuleFileName { get; }
}
}

View File

@@ -0,0 +1,150 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class FunctionExportsFixture : ModuleFixture
{
protected override string ModuleFileName => "FunctionExports.wasm";
}
public class FunctionExportsTests : IClassFixture<FunctionExportsFixture>
{
public FunctionExportsTests(FunctionExportsFixture fixture)
{
Fixture = fixture;
}
private FunctionExportsFixture Fixture { get; set; }
[Theory]
[MemberData(nameof(GetFunctionExports))]
public void ItHasTheExpectedFunctionExports(string exportName, ValueKind[] expectedParameters, ValueKind[] expectedResults)
{
var export = Fixture.Module.Exports.Functions.Where(f => f.Name == exportName).FirstOrDefault();
export.Should().NotBeNull();
export.Parameters.Should().Equal(expectedParameters);
export.Results.Should().Equal(expectedResults);
}
[Fact]
public void ItHasTheExpectedNumberOfExportedFunctions()
{
GetFunctionExports().Count().Should().Be(Fixture.Module.Exports.Functions.Count);
}
public static IEnumerable<object[]> GetFunctionExports()
{
yield return new object[] {
"no_params_no_results",
Array.Empty<ValueKind>(),
Array.Empty<ValueKind>()
};
yield return new object[] {
"one_i32_param_no_results",
new ValueKind[] {
ValueKind.Int32
},
Array.Empty<ValueKind>()
};
yield return new object[] {
"one_i64_param_no_results",
new ValueKind[] {
ValueKind.Int64
},
Array.Empty<ValueKind>()
};
yield return new object[] {
"one_f32_param_no_results",
new ValueKind[] {
ValueKind.Float32
},
Array.Empty<ValueKind>()
};
yield return new object[] {
"one_f64_param_no_results",
new ValueKind[] {
ValueKind.Float64
},
Array.Empty<ValueKind>()
};
yield return new object[] {
"one_param_of_each_type",
new ValueKind[] {
ValueKind.Int32,
ValueKind.Int64,
ValueKind.Float32,
ValueKind.Float64
},
Array.Empty<ValueKind>()
};
yield return new object[] {
"no_params_one_i32_result",
Array.Empty<ValueKind>(),
new ValueKind[] {
ValueKind.Int32,
}
};
yield return new object[] {
"no_params_one_i64_result",
Array.Empty<ValueKind>(),
new ValueKind[] {
ValueKind.Int64,
}
};
yield return new object[] {
"no_params_one_f32_result",
Array.Empty<ValueKind>(),
new ValueKind[] {
ValueKind.Float32,
}
};
yield return new object[] {
"no_params_one_f64_result",
Array.Empty<ValueKind>(),
new ValueKind[] {
ValueKind.Float64,
}
};
yield return new object[] {
"one_result_of_each_type",
Array.Empty<ValueKind>(),
new ValueKind[] {
ValueKind.Int32,
ValueKind.Int64,
ValueKind.Float32,
ValueKind.Float64,
}
};
yield return new object[] {
"one_param_and_result_of_each_type",
new ValueKind[] {
ValueKind.Int32,
ValueKind.Int64,
ValueKind.Float32,
ValueKind.Float64,
},
new ValueKind[] {
ValueKind.Int32,
ValueKind.Int64,
ValueKind.Float32,
ValueKind.Float64,
}
};
}
}
}

View File

@@ -0,0 +1,169 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class FunctionImportsFixture : ModuleFixture
{
protected override string ModuleFileName => "FunctionImports.wasm";
}
public class FunctionImportsTests : IClassFixture<FunctionImportsFixture>
{
public FunctionImportsTests(FunctionImportsFixture fixture)
{
Fixture = fixture;
}
private FunctionImportsFixture Fixture { get; set; }
[Theory]
[MemberData(nameof(GetFunctionImports))]
public void ItHasTheExpectedFunctionImports(string importModule, string importName, ValueKind[] expectedParameters, ValueKind[] expectedResults)
{
var import = Fixture.Module.Imports.Functions.Where(f => f.ModuleName == importModule && f.Name == importName).FirstOrDefault();
import.Should().NotBeNull();
import.Parameters.Should().Equal(expectedParameters);
import.Results.Should().Equal(expectedResults);
}
[Fact]
public void ItHasTheExpectedNumberOfExportedFunctions()
{
GetFunctionImports().Count().Should().Be(Fixture.Module.Imports.Functions.Count);
}
public static IEnumerable<object[]> GetFunctionImports()
{
yield return new object[] {
"",
"no_params_no_results",
Array.Empty<ValueKind>(),
Array.Empty<ValueKind>()
};
yield return new object[] {
"",
"one_i32_param_no_results",
new ValueKind[] {
ValueKind.Int32
},
Array.Empty<ValueKind>()
};
yield return new object[] {
"",
"one_i64_param_no_results",
new ValueKind[] {
ValueKind.Int64
},
Array.Empty<ValueKind>()
};
yield return new object[] {
"",
"one_f32_param_no_results",
new ValueKind[] {
ValueKind.Float32
},
Array.Empty<ValueKind>()
};
yield return new object[] {
"",
"one_f64_param_no_results",
new ValueKind[] {
ValueKind.Float64
},
Array.Empty<ValueKind>()
};
yield return new object[] {
"",
"one_param_of_each_type",
new ValueKind[] {
ValueKind.Int32,
ValueKind.Int64,
ValueKind.Float32,
ValueKind.Float64
},
Array.Empty<ValueKind>()
};
yield return new object[] {
"",
"no_params_one_i32_result",
Array.Empty<ValueKind>(),
new ValueKind[] {
ValueKind.Int32,
}
};
yield return new object[] {
"",
"no_params_one_i64_result",
Array.Empty<ValueKind>(),
new ValueKind[] {
ValueKind.Int64,
}
};
yield return new object[] {
"",
"no_params_one_f32_result",
Array.Empty<ValueKind>(),
new ValueKind[] {
ValueKind.Float32,
}
};
yield return new object[] {
"",
"no_params_one_f64_result",
Array.Empty<ValueKind>(),
new ValueKind[] {
ValueKind.Float64,
}
};
yield return new object[] {
"",
"one_result_of_each_type",
Array.Empty<ValueKind>(),
new ValueKind[] {
ValueKind.Int32,
ValueKind.Int64,
ValueKind.Float32,
ValueKind.Float64,
}
};
yield return new object[] {
"",
"one_param_and_result_of_each_type",
new ValueKind[] {
ValueKind.Int32,
ValueKind.Int64,
ValueKind.Float32,
ValueKind.Float64,
},
new ValueKind[] {
ValueKind.Int32,
ValueKind.Int64,
ValueKind.Float32,
ValueKind.Float64,
}
};
yield return new object[] {
"other",
"function_from_module",
Array.Empty<ValueKind>(),
Array.Empty<ValueKind>(),
};
}
}
}

View File

@@ -0,0 +1,220 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FluentAssertions;
using Wasmtime;
using Xunit;
namespace Wasmtime.Tests
{
public class GlobalExportsFixture : ModuleFixture
{
protected override string ModuleFileName => "GlobalExports.wasm";
}
public class GlobalExportsTests : IClassFixture<GlobalExportsFixture>
{
public class Host : IHost
{
public Instance Instance { get; set; }
}
public GlobalExportsTests(GlobalExportsFixture fixture)
{
Fixture = fixture;
}
private GlobalExportsFixture Fixture { get; set; }
[Theory]
[MemberData(nameof(GetGlobalExports))]
public void ItHasTheExpectedGlobalExports(string exportName, ValueKind expectedKind, bool expectedMutable)
{
var export = Fixture.Module.Exports.Globals.Where(f => f.Name == exportName).FirstOrDefault();
export.Should().NotBeNull();
export.Kind.Should().Be(expectedKind);
export.IsMutable.Should().Be(expectedMutable);
}
[Fact]
public void ItHasTheExpectedNumberOfExportedGlobals()
{
GetGlobalExports().Count().Should().Be(Fixture.Module.Exports.Globals.Count);
}
[Fact]
public void ItCreatesExternsForTheGlobals()
{
using (var instance = Fixture.Module.Instantiate(new Host()))
{
dynamic dyn = instance;
var globals = instance.Externs.Globals;
globals.Count.Should().Be(8);
var i32 = globals[0];
i32.Name.Should().Be("global_i32");
i32.Kind.Should().Be(ValueKind.Int32);
i32.IsMutable.Should().Be(false);
i32.Value.Should().Be(0);
var i32Mut = globals[1];
i32Mut.Name.Should().Be("global_i32_mut");
i32Mut.Kind.Should().Be(ValueKind.Int32);
i32Mut.IsMutable.Should().Be(true);
i32Mut.Value.Should().Be(1);
i32Mut.Value = 11;
i32Mut.Value.Should().Be(11);
dyn.global_i32_mut = 12;
((int)dyn.global_i32_mut).Should().Be(12);
i32Mut.Value.Should().Be(12);
var i64 = globals[2];
i64.Name.Should().Be("global_i64");
i64.Kind.Should().Be(ValueKind.Int64);
i64.IsMutable.Should().Be(false);
i64.Value.Should().Be(2);
var i64Mut = globals[3];
i64Mut.Name.Should().Be("global_i64_mut");
i64Mut.Kind.Should().Be(ValueKind.Int64);
i64Mut.IsMutable.Should().Be(true);
i64Mut.Value.Should().Be(3);
i64Mut.Value = 13;
i64Mut.Value.Should().Be(13);
dyn.global_i64_mut = 14;
((long)dyn.global_i64_mut).Should().Be(14);
i64Mut.Value.Should().Be(14);
var f32 = globals[4];
f32.Name.Should().Be("global_f32");
f32.Kind.Should().Be(ValueKind.Float32);
f32.IsMutable.Should().Be(false);
f32.Value.Should().Be(4);
var f32Mut = globals[5];
f32Mut.Name.Should().Be("global_f32_mut");
f32Mut.Kind.Should().Be(ValueKind.Float32);
f32Mut.IsMutable.Should().Be(true);
f32Mut.Value.Should().Be(5);
f32Mut.Value = 15;
f32Mut.Value.Should().Be(15);
dyn.global_f32_mut = 16;
((float)dyn.global_f32_mut).Should().Be(16);
f32Mut.Value.Should().Be(16);
var f64 = globals[6];
f64.Name.Should().Be("global_f64");
f64.Kind.Should().Be(ValueKind.Float64);
f64.IsMutable.Should().Be(false);
f64.Value.Should().Be(6);
var f64Mut = globals[7];
f64Mut.Name.Should().Be("global_f64_mut");
f64Mut.Kind.Should().Be(ValueKind.Float64);
f64Mut.IsMutable.Should().Be(true);
f64Mut.Value.Should().Be(7);
f64Mut.Value = 17;
f64Mut.Value.Should().Be(17);
dyn.global_f64_mut = 17;
((double)dyn.global_f64_mut).Should().Be(17);
f64Mut.Value.Should().Be(17);
Action action = () => i32.Value = 0;
action
.Should()
.Throw<InvalidOperationException>()
.WithMessage("The value of global 'global_i32' cannot be modified.");
action = () => dyn.global_i32 = 0;
action
.Should()
.Throw<InvalidOperationException>()
.WithMessage("The value of global 'global_i32' cannot be modified.");
action = () => i64.Value = 0;
action
.Should()
.Throw<InvalidOperationException>()
.WithMessage("The value of global 'global_i64' cannot be modified.");
action = () => dyn.global_i64 = 0;
action
.Should()
.Throw<InvalidOperationException>()
.WithMessage("The value of global 'global_i64' cannot be modified.");
action = () => f32.Value = 0;
action
.Should()
.Throw<InvalidOperationException>()
.WithMessage("The value of global 'global_f32' cannot be modified.");
action = () => dyn.global_f32 = 0;
action
.Should()
.Throw<InvalidOperationException>()
.WithMessage("The value of global 'global_f32' cannot be modified.");
action = () => f64.Value = 0;
action
.Should()
.Throw<InvalidOperationException>()
.WithMessage("The value of global 'global_f64' cannot be modified.");
action = () => dyn.global_f64 = 0;
action
.Should()
.Throw<InvalidOperationException>()
.WithMessage("The value of global 'global_f64' cannot be modified.");
}
}
public static IEnumerable<object[]> GetGlobalExports()
{
yield return new object[] {
"global_i32",
ValueKind.Int32,
false
};
yield return new object[] {
"global_i32_mut",
ValueKind.Int32,
true
};
yield return new object[] {
"global_i64",
ValueKind.Int64,
false
};
yield return new object[] {
"global_i64_mut",
ValueKind.Int64,
true
};
yield return new object[] {
"global_f32",
ValueKind.Float32,
false
};
yield return new object[] {
"global_f32_mut",
ValueKind.Float32,
true
};
yield return new object[] {
"global_f64",
ValueKind.Float64,
false
};
yield return new object[] {
"global_f64_mut",
ValueKind.Float64,
true
};
}
}
}

View File

@@ -0,0 +1,259 @@
using System;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class GlobalImportBindingFixture : ModuleFixture
{
protected override string ModuleFileName => "GlobalImportBindings.wasm";
}
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;
}
private GlobalImportBindingFixture Fixture { get; set; }
[Fact]
public void ItFailsToInstantiateWithMissingImport()
{
Action action = () => { using (var instance = Fixture.Module.Instantiate(new NoImportsHost())) { } };
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.");
}
[Fact]
public void ItFailsToInstantiateWithStaticField()
{
Action action = () => { using (var instance = Fixture.Module.Instantiate(new GlobalIsStaticHost())) { } };
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.");
}
[Fact]
public void ItFailsToInstantiateWithGlobalTypeMismatch()
{
Action action = () => { using (var instance = Fixture.Module.Instantiate(new TypeMismatchHost())) { } };
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'.");
}
[Fact]
public void ItFailsToInstantiateWhenGlobalIsNotMut()
{
Action action = () => { using (var instance = Fixture.Module.Instantiate(new NotMutHost())) { } };
action
.Should()
.Throw<WasmtimeException>()
.WithMessage("Unable to bind 'NotMutHost.Int32Mut' to WebAssembly import 'global_i32_mut': the import is mutable (use the 'MutableGlobal' type).");
}
[Fact]
public void ItFailsToInstantiateWhenGlobalIsMut()
{
Action action = () => { using (var instance = Fixture.Module.Instantiate(new MutHost())) { } };
action
.Should()
.Throw<WasmtimeException>()
.WithMessage("Unable to bind 'MutHost.Int32' to WebAssembly import 'global_i32': the import is constant (use the 'Global' type).");
}
[Fact]
public void ItBindsTheGlobalsCorrectly()
{
var host = new ValidHost();
using (dynamic instance = Fixture.Module.Instantiate(host))
{
host.Int32Mut.Value.Should().Be(0);
((int)instance.get_global_i32_mut()).Should().Be(0);
host.Int32.Value.Should().Be(1);
((int)instance.get_global_i32()).Should().Be(1);
host.Int64Mut.Value.Should().Be(2);
((long)instance.get_global_i64_mut()).Should().Be(2);
host.Int64.Value.Should().Be(3);
((long)instance.get_global_i64()).Should().Be(3);
host.Float32Mut.Value.Should().Be(4);
((float)instance.get_global_f32_mut()).Should().Be(4);
host.Float32.Value.Should().Be(5);
((float)instance.get_global_f32()).Should().Be(5);
host.Float64Mut.Value.Should().Be(6);
((double)instance.get_global_f64_mut()).Should().Be(6);
host.Float64.Value.Should().Be(7);
((double)instance.get_global_f64()).Should().Be(7);
host.Int32Mut.Value = 10;
host.Int32Mut.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);
((int)instance.get_global_i32_mut()).Should().Be(11);
host.Int64Mut.Value = 12;
host.Int64Mut.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);
((long)instance.get_global_i64_mut()).Should().Be(13);
host.Float32Mut.Value = 14;
host.Float32Mut.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);
((float)instance.get_global_f32_mut()).Should().Be(15);
host.Float64Mut.Value = 16;
host.Float64Mut.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);
((double)instance.get_global_f64_mut()).Should().Be(17);
}
}
}
}

View File

@@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class GlobalImportsFixture : ModuleFixture
{
protected override string ModuleFileName => "GlobalImports.wasm";
}
public class GlobalImportsTests : IClassFixture<GlobalImportsFixture>
{
public GlobalImportsTests(GlobalImportsFixture fixture)
{
Fixture = fixture;
}
private GlobalImportsFixture Fixture { get; set; }
[Theory]
[MemberData(nameof(GetGlobalImports))]
public void ItHasTheExpectedGlobalImports(string importModule, string importName, ValueKind expectedKind, bool expectedMutable)
{
var import = Fixture.Module.Imports.Globals.Where(f => f.ModuleName == importModule && f.Name == importName).FirstOrDefault();
import.Should().NotBeNull();
import.Kind.Should().Be(expectedKind);
import.IsMutable.Should().Be(expectedMutable);
}
[Fact]
public void ItHasTheExpectedNumberOfExportedGlobals()
{
GetGlobalImports().Count().Should().Be(Fixture.Module.Imports.Globals.Count);
}
public static IEnumerable<object[]> GetGlobalImports()
{
yield return new object[] {
"",
"global_i32",
ValueKind.Int32,
false
};
yield return new object[] {
"",
"global_i32_mut",
ValueKind.Int32,
true
};
yield return new object[] {
"",
"global_i64",
ValueKind.Int64,
false
};
yield return new object[] {
"",
"global_i64_mut",
ValueKind.Int64,
true
};
yield return new object[] {
"",
"global_f32",
ValueKind.Float32,
false
};
yield return new object[] {
"",
"global_f32_mut",
ValueKind.Float32,
true
};
yield return new object[] {
"",
"global_f64",
ValueKind.Float64,
false
};
yield return new object[] {
"",
"global_f64_mut",
ValueKind.Float64,
true
};
yield return new object[] {
"other",
"global_from_module",
ValueKind.Int32,
false
};
}
}
}

View File

@@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class MemoryExportsFixture : ModuleFixture
{
protected override string ModuleFileName => "MemoryExports.wasm";
}
public class MemoryExportsTests : IClassFixture<MemoryExportsFixture>
{
public class Host : IHost
{
public Instance Instance { get; set; }
}
public MemoryExportsTests(MemoryExportsFixture fixture)
{
Fixture = fixture;
}
private MemoryExportsFixture Fixture { get; set; }
[Theory]
[MemberData(nameof(GetMemoryExports))]
public void ItHasTheExpectedMemoryExports(string exportName, uint expectedMinimum, uint expectedMaximum)
{
var export = Fixture.Module.Exports.Memories.Where(m => m.Name == exportName).FirstOrDefault();
export.Should().NotBeNull();
export.Minimum.Should().Be(expectedMinimum);
export.Maximum.Should().Be(expectedMaximum);
}
[Fact]
public void ItHasTheExpectedNumberOfExportedTables()
{
GetMemoryExports().Count().Should().Be(Fixture.Module.Exports.Memories.Count);
}
[Fact]
public void ItCreatesExternsForTheMemories()
{
var host = new Host();
using (var instance = Fixture.Module.Instantiate(host))
{
instance.Externs.Memories.Count.Should().Be(1);
var memory = instance.Externs.Memories[0];
memory.ReadString(0, 11).Should().Be("Hello World");
int written = memory.WriteString(0, "WebAssembly Rocks!");
memory.ReadString(0, written).Should().Be("WebAssembly Rocks!");
memory.ReadByte(20).Should().Be(1);
memory.WriteByte(20, 11);
memory.ReadByte(20).Should().Be(11);
memory.ReadInt16(21).Should().Be(2);
memory.WriteInt16(21, 12);
memory.ReadInt16(21).Should().Be(12);
memory.ReadInt32(23).Should().Be(3);
memory.WriteInt32(23, 13);
memory.ReadInt32(23).Should().Be(13);
memory.ReadInt64(27).Should().Be(4);
memory.WriteInt64(27, 14);
memory.ReadInt64(27).Should().Be(14);
memory.ReadSingle(35).Should().Be(5);
memory.WriteSingle(35, 15);
memory.ReadSingle(35).Should().Be(15);
memory.ReadDouble(39).Should().Be(6);
memory.WriteDouble(39, 16);
memory.ReadDouble(39).Should().Be(16);
memory.ReadIntPtr(48).Should().Be((IntPtr)7);
memory.WriteIntPtr(48, (IntPtr)17);
memory.ReadIntPtr(48).Should().Be((IntPtr)17);
}
}
public static IEnumerable<object[]> GetMemoryExports()
{
yield return new object[] {
"mem",
1,
2
};
}
}
}

View File

@@ -0,0 +1,187 @@
using System;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class MemoryImportBindingFixture : ModuleFixture
{
protected override string ModuleFileName => "MemoryImportBinding.wasm";
}
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;
}
private MemoryImportBindingFixture Fixture { get; set; }
[Fact]
public void ItFailsToInstantiateWithMissingImport()
{
Action action = () => { using (var instance = Fixture.Module.Instantiate(new MissingImportsHost())) { } };
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).");
}
[Fact]
public void ItBindsTheGlobalsCorrectly()
{
var host = new ValidHost();
using (dynamic instance = Fixture.Module.Instantiate(host))
{
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!");
host.Mem.ReadByte(20).Should().Be(1);
host.Mem.WriteByte(20, 11);
host.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);
((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);
((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);
((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);
((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);
((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);
((IntPtr)instance.ReadIntPtr()).Should().Be((IntPtr)17);
}
}
}
}

View File

@@ -0,0 +1,34 @@
using System;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class MemoryImportFromModuleFixture : ModuleFixture
{
protected override string ModuleFileName => "MemoryImportFromModule.wasm";
}
public class MemoryImportFromModuleTests : IClassFixture<MemoryImportFromModuleFixture>
{
public MemoryImportFromModuleTests(MemoryImportFromModuleFixture fixture)
{
Fixture = fixture;
}
private MemoryImportFromModuleFixture Fixture { get; set; }
[Fact]
public void ItHasTheExpectedImport()
{
Fixture.Module.Imports.Memories.Count.Should().Be(1);
var memory = Fixture.Module.Imports.Memories[0];
memory.ModuleName.Should().Be("js");
memory.Name.Should().Be("mem");
memory.Minimum.Should().Be(1);
memory.Maximum.Should().Be(2);
}
}
}

View File

@@ -0,0 +1,34 @@
using System;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class MemoryImportNoUpperBoundFixture : ModuleFixture
{
protected override string ModuleFileName => "MemoryImportNoUpperBound.wasm";
}
public class MemoryImportNoUpperBoundTests : IClassFixture<MemoryImportNoUpperBoundFixture>
{
public MemoryImportNoUpperBoundTests(MemoryImportNoUpperBoundFixture fixture)
{
Fixture = fixture;
}
private MemoryImportNoUpperBoundFixture Fixture { get; set; }
[Fact]
public void ItHasTheExpectedImport()
{
Fixture.Module.Imports.Memories.Count.Should().Be(1);
var memory = Fixture.Module.Imports.Memories[0];
memory.ModuleName.Should().Be("");
memory.Name.Should().Be("mem");
memory.Minimum.Should().Be(1);
memory.Maximum.Should().Be(uint.MaxValue);
}
}
}

View File

@@ -0,0 +1,34 @@
using System;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class MemoryImportWithUpperBoundFixture : ModuleFixture
{
protected override string ModuleFileName => "MemoryImportWithUpperBound.wasm";
}
public class MemoryImportWithUpperBoundTests : IClassFixture<MemoryImportWithUpperBoundFixture>
{
public MemoryImportWithUpperBoundTests(MemoryImportWithUpperBoundFixture fixture)
{
Fixture = fixture;
}
private MemoryImportWithUpperBoundFixture Fixture { get; set; }
[Fact]
public void ItHasTheExpectedImport()
{
Fixture.Module.Imports.Memories.Count.Should().Be(1);
var memory = Fixture.Module.Imports.Memories[0];
memory.ModuleName.Should().Be("");
memory.Name.Should().Be("mem");
memory.Minimum.Should().Be(10);
memory.Maximum.Should().Be(100);
}
}
}

Binary file not shown.

View File

@@ -0,0 +1,26 @@
(module
(func $no_params_no_results)
(func $one_i32_param_no_results (param i32))
(func $one_i64_param_no_results (param i64))
(func $one_f32_param_no_results (param f32))
(func $one_f64_param_no_results (param f64))
(func $one_param_of_each_type (param i32 i64 f32 f64))
(func $no_params_one_i32_result (result i32) i32.const 0)
(func $no_params_one_i64_result (result i64) i64.const 0)
(func $no_params_one_f32_result (result f32) f32.const 0)
(func $no_params_one_f64_result (result f64) f64.const 0)
(func $one_result_of_each_type (result i32 i64 f32 f64) i32.const 0 i64.const 0 f32.const 0 f64.const 0)
(func $one_param_and_result_of_each_type (param i32 i64 f32 f64) (result i32 i64 f32 f64) i32.const 0 i64.const 0 f32.const 0 f64.const 0)
(export "no_params_no_results" (func $no_params_no_results))
(export "one_i32_param_no_results" (func $one_i32_param_no_results))
(export "one_i64_param_no_results" (func $one_i64_param_no_results))
(export "one_f32_param_no_results" (func $one_f32_param_no_results))
(export "one_f64_param_no_results" (func $one_f64_param_no_results))
(export "one_param_of_each_type" (func $one_param_of_each_type))
(export "no_params_one_i32_result" (func $no_params_one_i32_result))
(export "no_params_one_i64_result" (func $no_params_one_i64_result))
(export "no_params_one_f32_result" (func $no_params_one_f32_result))
(export "no_params_one_f64_result" (func $no_params_one_f64_result))
(export "one_result_of_each_type" (func $one_result_of_each_type))
(export "one_param_and_result_of_each_type" (func $one_param_and_result_of_each_type))
)

Binary file not shown.

View File

@@ -0,0 +1,27 @@
(module
(type $t0 (func))
(type $t1 (func (param i32)))
(type $t2 (func (param i64)))
(type $t3 (func (param f32)))
(type $t4 (func (param f64)))
(type $t5 (func (param i32 i64 f32 f64)))
(type $t6 (func (result i32)))
(type $t7 (func (result i64)))
(type $t8 (func (result f32)))
(type $t9 (func (result f64)))
(type $t10 (func (result i32 i64 f32 f64)))
(type $t11 (func (param i32 i64 f32 f64) (result i32 i64 f32 f64)))
(import "" "no_params_no_results" (func $.f0 (type $t0)))
(import "" "one_i32_param_no_results" (func $.f1 (type $t1)))
(import "" "one_i64_param_no_results" (func $.f2 (type $t2)))
(import "" "one_f32_param_no_results" (func $.f3 (type $t3)))
(import "" "one_f64_param_no_results" (func $.f4 (type $t4)))
(import "" "one_param_of_each_type" (func $.f5 (type $t5)))
(import "" "no_params_one_i32_result" (func $.f6 (type $t6)))
(import "" "no_params_one_i64_result" (func $.f7 (type $t7)))
(import "" "no_params_one_f32_result" (func $.f8 (type $t8)))
(import "" "no_params_one_f64_result" (func $.f9 (type $t9)))
(import "" "one_result_of_each_type" (func $.f10 (type $t10)))
(import "" "one_param_and_result_of_each_type" (func $.f11 (type $t11)))
(import "other" "function_from_module" (func $.f12 (type $t0)))
)

Binary file not shown.

View File

@@ -0,0 +1,18 @@
(module
(global $g1 i32 (i32.const 0))
(global $g2 (mut i32) (i32.const 1))
(global $g3 i64 (i64.const 2))
(global $g4 (mut i64) (i64.const 3))
(global $g5 f32 (f32.const 4))
(global $g6 (mut f32) (f32.const 5))
(global $g7 f64 (f64.const 6))
(global $g8 (mut f64) (f64.const 7))
(export "global_i32" (global $g1))
(export "global_i32_mut" (global $g2))
(export "global_i64" (global $g3))
(export "global_i64_mut" (global $g4))
(export "global_f32" (global $g5))
(export "global_f32_mut" (global $g6))
(export "global_f64" (global $g7))
(export "global_f64_mut" (global $g8))
)

View File

@@ -0,0 +1,22 @@
(module
(import "" "global_i32_mut" (global $global_i32_mut (mut i32)))
(import "" "global_i32" (global $global_i32 i32))
(import "" "global_i64_mut" (global $global_i64_mut (mut i64)))
(import "" "global_i64" (global $global_i64 i64))
(import "" "global_f32_mut" (global $global_f32_mut (mut f32)))
(import "" "global_f32" (global $global_f32 f32))
(import "" "global_f64_mut" (global $global_f64_mut (mut f64)))
(import "" "global_f64" (global $global_f64 f64))
(func (export "get_global_i32_mut") (result i32) (global.get $global_i32_mut))
(func (export "get_global_i32") (result i32) (global.get $global_i32))
(func (export "set_global_i32_mut") (param i32) (global.set $global_i32_mut (local.get 0)))
(func (export "get_global_i64_mut") (result i64) (global.get $global_i64_mut))
(func (export "get_global_i64") (result i64) (global.get $global_i64))
(func (export "set_global_i64_mut") (param i64) (global.set $global_i64_mut (local.get 0)))
(func (export "get_global_f32_mut") (result f32) (global.get $global_f32_mut))
(func (export "get_global_f32") (result f32) (global.get $global_f32))
(func (export "set_global_f32_mut") (param f32) (global.set $global_f32_mut (local.get 0)))
(func (export "get_global_f64_mut") (result f64) (global.get $global_f64_mut))
(func (export "get_global_f64") (result f64) (global.get $global_f64))
(func (export "set_global_f64_mut") (param f64) (global.set $global_f64_mut (local.get 0)))
)

Binary file not shown.

View File

@@ -0,0 +1,11 @@
(module
(global $g1 (import "" "global_i32") i32)
(global $g2 (import "" "global_i32_mut") (mut i32))
(global $g3 (import "" "global_i64") i64)
(global $g4 (import "" "global_i64_mut") (mut i64))
(global $g5 (import "" "global_f32") f32)
(global $g6 (import "" "global_f32_mut") (mut f32))
(global $g7 (import "" "global_f64") f64)
(global $g8 (import "" "global_f64_mut") (mut f64))
(global $g9 (import "other" "global_from_module") i32)
)

Binary file not shown.

View File

@@ -0,0 +1,11 @@
(module
(memory (export "mem") 1 2)
(data (i32.const 0) "Hello World")
(data (i32.const 20) "\01")
(data (i32.const 21) "\02\00")
(data (i32.const 23) "\03\00\00\00")
(data (i32.const 27) "\04\00\00\00\00\00\00\00")
(data (i32.const 35) "\00\00\a0\40")
(data (i32.const 39) "\00\00\00\00\00\00\18\40")
(data (i32.const 48) "\07\00\00\00\00\00\00\00")
)

View File

@@ -0,0 +1,39 @@
(module
(import "" "mem" (memory 1))
(data (i32.const 0) "Hello World")
(data (i32.const 20) "\01")
(data (i32.const 21) "\02\00")
(data (i32.const 23) "\03\00\00\00")
(data (i32.const 27) "\04\00\00\00\00\00\00\00")
(data (i32.const 35) "\00\00\a0\40")
(data (i32.const 39) "\00\00\00\00\00\00\18\40")
(data (i32.const 48) "\07\00\00\00\00\00\00\00")
(func (export "ReadByte") (result i32)
i32.const 20
i32.load8_s
)
(func (export "ReadInt16") (result i32)
i32.const 21
i32.load16_s
)
(func (export "ReadInt32") (result i32)
i32.const 23
i32.load
)
(func (export "ReadInt64") (result i64)
i32.const 27
i64.load
)
(func (export "ReadFloat32") (result f32)
i32.const 35
f32.load
)
(func (export "ReadFloat64") (result f64)
i32.const 39
f64.load
)
(func (export "ReadIntPtr") (result i64)
i32.const 48
i64.load
)
)

View File

@@ -0,0 +1,3 @@
(module
(import "js" "mem" (memory 1 2))
)

View File

@@ -0,0 +1,3 @@
(module
(import "" "mem" (memory 1))
)

View File

@@ -0,0 +1,3 @@
(module
(import "" "mem" (memory 10 100))
)

Binary file not shown.

View File

@@ -0,0 +1,8 @@
(module
(table $t0 1 10 funcref)
(table $t1 10 anyref)
(table $t2 100 1000 funcref)
(export "table1" (table $t0))
(export "table2" (table $t1))
(export "table3" (table $t2))
)

Binary file not shown.

View File

@@ -0,0 +1,5 @@
(module
(import "" "table1" (table $t1 10 funcref))
(import "" "table2" (table $t2 15 anyref))
(import "other" "table3" (table $t3 1 funcref))
)

View File

@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class TableExportsFixture : ModuleFixture
{
protected override string ModuleFileName => "TableExports.wasm";
}
public class TableExportsTests : IClassFixture<TableExportsFixture>
{
public TableExportsTests(TableExportsFixture fixture)
{
Fixture = fixture;
}
private TableExportsFixture Fixture { get; set; }
[Theory]
[MemberData(nameof(GetTableExports))]
public void ItHasTheExpectedTableExports(string exportName, ValueKind expectedKind, uint expectedMinimum, uint expectedMaximum)
{
var export = Fixture.Module.Exports.Tables.Where(f => f.Name == exportName).FirstOrDefault();
export.Should().NotBeNull();
export.Kind.Should().Be(expectedKind);
export.Minimum.Should().Be(expectedMinimum);
export.Maximum.Should().Be(expectedMaximum);
}
[Fact]
public void ItHasTheExpectedNumberOfExportedTables()
{
GetTableExports().Count().Should().Be(Fixture.Module.Exports.Tables.Count);
}
public static IEnumerable<object[]> GetTableExports()
{
yield return new object[] {
"table1",
ValueKind.FuncRef,
1,
10
};
yield return new object[] {
"table2",
ValueKind.AnyRef,
10,
uint.MaxValue
};
yield return new object[] {
"table3",
ValueKind.FuncRef,
100,
1000
};
}
}
}

View File

@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class TableImportsFixture : ModuleFixture
{
protected override string ModuleFileName => "TableImports.wasm";
}
public class TableImportsTests : IClassFixture<TableImportsFixture>
{
public TableImportsTests(TableImportsFixture fixture)
{
Fixture = fixture;
}
private TableImportsFixture Fixture { get; set; }
[Theory]
[MemberData(nameof(GetTableImports))]
public void ItHasTheExpectedTableImports(string importModule, string importName, ValueKind expectedKind, uint expectedMinimum, uint expectedMaximum)
{
var import = Fixture.Module.Imports.Tables.Where(f => f.ModuleName == importModule && f.Name == importName).FirstOrDefault();
import.Should().NotBeNull();
import.Kind.Should().Be(expectedKind);
import.Minimum.Should().Be(expectedMinimum);
import.Maximum.Should().Be(expectedMaximum);
}
[Fact]
public void ItHasTheExpectedNumberOfExportedTables()
{
GetTableImports().Count().Should().Be(Fixture.Module.Imports.Tables.Count);
}
public static IEnumerable<object[]> GetTableImports()
{
yield return new object[] {
"",
"table1",
ValueKind.FuncRef,
10,
uint.MaxValue
};
yield return new object[] {
"",
"table2",
ValueKind.AnyRef,
15,
uint.MaxValue
};
yield return new object[] {
"other",
"table3",
ValueKind.FuncRef,
1,
uint.MaxValue
};
}
}
}

View File

@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="5.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="coverlet.collector" Version="1.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\src\Wasmtime.csproj" />
</ItemGroup>
<!-- This is needed as we're not referencing Wasmtime as a package. -->
<Target Name="BuildWasmtime" BeforeTargets="AssignTargetPaths">
<Message Text="Building Wasmtime from source." Importance="High" />
<Exec Command="$(BuildWasmtimeCommand)" StandardOutputImportance="Low" StandardErrorImportance="Low" />
<ItemGroup>
<None Include="$(WasmtimeOutputPath)/$(WasmtimeLibraryFilename)" Link="$(WasmtimeLibraryFilename)" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Target>
<ItemGroup>
<None Update="Modules/*.wasm" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>