From 7f05a2e6a5cc48b512221b5b022dae62ef027c9c Mon Sep 17 00:00:00 2001 From: Austin Wise Date: Thu, 5 Dec 2019 19:29:35 -0800 Subject: [PATCH 1/5] Use DoNotWrapExceptions to avoid exception wrapping. --- crates/misc/dotnet/src/Bindings/FunctionBinding.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/misc/dotnet/src/Bindings/FunctionBinding.cs b/crates/misc/dotnet/src/Bindings/FunctionBinding.cs index 1941b20cf8..632fa733da 100644 --- a/crates/misc/dotnet/src/Bindings/FunctionBinding.cs +++ b/crates/misc/dotnet/src/Bindings/FunctionBinding.cs @@ -240,7 +240,7 @@ namespace Wasmtime.Bindings { SetArgs(arguments, args); - var result = Method.Invoke(host, args); + var result = Method.Invoke(host, BindingFlags.DoNotWrapExceptions, null, args, null); if (hasReturn) { @@ -248,9 +248,9 @@ namespace Wasmtime.Bindings } return IntPtr.Zero; } - catch (TargetInvocationException ex) + catch (Exception ex) { - var bytes = Encoding.UTF8.GetBytes(ex.InnerException.Message + "\0" /* exception messages need a null */); + var bytes = Encoding.UTF8.GetBytes(ex.Message + "\0" /* exception messages need a null */); fixed (byte* ptr = bytes) { From 96d6a16ce951824c986766c280b3fd0281f8f262 Mon Sep 17 00:00:00 2001 From: Austin Wise Date: Fri, 13 Dec 2019 17:25:08 -0800 Subject: [PATCH 2/5] Remove unneed dynamic binding in MemoryBinding. The Validate function already checks that the field type is Memory. --- crates/misc/dotnet/src/Bindings/MemoryBinding.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/misc/dotnet/src/Bindings/MemoryBinding.cs b/crates/misc/dotnet/src/Bindings/MemoryBinding.cs index ea6c860f96..790f3c5ddb 100644 --- a/crates/misc/dotnet/src/Bindings/MemoryBinding.cs +++ b/crates/misc/dotnet/src/Bindings/MemoryBinding.cs @@ -45,7 +45,7 @@ namespace Wasmtime.Bindings internal override SafeHandle Bind(Store store, IHost host) { - dynamic memory = Field.GetValue(host); + Memory memory = (Memory)Field.GetValue(host); if (memory.Handle != null) { throw new InvalidOperationException("Cannot bind more than once."); From e11056345a206ece2b0782fbaf75ca9594e575f8 Mon Sep 17 00:00:00 2001 From: Austin Wise Date: Thu, 5 Dec 2019 19:26:37 -0800 Subject: [PATCH 3/5] Add a Visual Studio solution. --- crates/misc/dotnet/.gitignore | 1 + crates/misc/dotnet/Wasmtime.sln | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 crates/misc/dotnet/Wasmtime.sln diff --git a/crates/misc/dotnet/.gitignore b/crates/misc/dotnet/.gitignore index 1944291481..6ae5b272e6 100644 --- a/crates/misc/dotnet/.gitignore +++ b/crates/misc/dotnet/.gitignore @@ -2,6 +2,7 @@ *.*~ .DS_Store +.vs/ .vscode bin/ diff --git a/crates/misc/dotnet/Wasmtime.sln b/crates/misc/dotnet/Wasmtime.sln new file mode 100644 index 0000000000..03b1de28bb --- /dev/null +++ b/crates/misc/dotnet/Wasmtime.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29519.181 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wasmtime", "src\Wasmtime.csproj", "{5EB63C51-5286-4DDF-BF7F-4110CC6D80B8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wasmtime.Tests", "tests\Wasmtime.Tests.csproj", "{8A200114-1D0B-4F90-9F82-1FFE47C207DD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5EB63C51-5286-4DDF-BF7F-4110CC6D80B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5EB63C51-5286-4DDF-BF7F-4110CC6D80B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5EB63C51-5286-4DDF-BF7F-4110CC6D80B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5EB63C51-5286-4DDF-BF7F-4110CC6D80B8}.Release|Any CPU.Build.0 = Release|Any CPU + {8A200114-1D0B-4F90-9F82-1FFE47C207DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A200114-1D0B-4F90-9F82-1FFE47C207DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A200114-1D0B-4F90-9F82-1FFE47C207DD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A200114-1D0B-4F90-9F82-1FFE47C207DD}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F5AC35E5-1373-49E6-97DC-68CB5E0369E0} + EndGlobalSection +EndGlobal From df0f0e3c4423f8587c3ac040777f72432bd033ea Mon Sep 17 00:00:00 2001 From: Austin Wise Date: Fri, 13 Dec 2019 18:48:32 -0800 Subject: [PATCH 4/5] Remove trailing null bytes from trap messages. It appears there are two trailing null bytes at the end of the string. This does not seem right. But it might be a good idea generally to remove any null bytes that get into error messages. --- crates/misc/dotnet/src/TrapException.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/misc/dotnet/src/TrapException.cs b/crates/misc/dotnet/src/TrapException.cs index 795b4a397c..2ed7df8e76 100644 --- a/crates/misc/dotnet/src/TrapException.cs +++ b/crates/misc/dotnet/src/TrapException.cs @@ -1,6 +1,6 @@ using System; -using System.Runtime.InteropServices; using System.Runtime.Serialization; +using System.Text; namespace Wasmtime { @@ -27,7 +27,13 @@ namespace Wasmtime unsafe { Interop.wasm_trap_message(trap, out var bytes); - var message = Marshal.PtrToStringUTF8((IntPtr)bytes.data, (int)bytes.size - 1 /* remove null */); + var byteSpan = new ReadOnlySpan(bytes.data, checked((int)bytes.size)); + + int indexOfNull = byteSpan.IndexOf((byte)0); + if (indexOfNull != -1) + byteSpan = byteSpan.Slice(0, indexOfNull); + + var message = Encoding.UTF8.GetString(byteSpan); Interop.wasm_byte_vec_delete(ref bytes); Interop.wasm_trap_delete(trap); From 50d0aa939c4998b1851b8125ac2d2cbfd612ce9d Mon Sep 17 00:00:00 2001 From: Austin Wise Date: Fri, 13 Dec 2019 18:50:57 -0800 Subject: [PATCH 5/5] Add tests calling function imports. --- .../dotnet/tests/FunctionThunkingTests.cs | 71 ++++++++++++++++++ .../tests/Modules/FunctionThunking.wasm | Bin 0 -> 126 bytes .../dotnet/tests/Modules/FunctionThunking.wat | 20 +++++ 3 files changed, 91 insertions(+) create mode 100644 crates/misc/dotnet/tests/FunctionThunkingTests.cs create mode 100644 crates/misc/dotnet/tests/Modules/FunctionThunking.wasm create mode 100644 crates/misc/dotnet/tests/Modules/FunctionThunking.wat diff --git a/crates/misc/dotnet/tests/FunctionThunkingTests.cs b/crates/misc/dotnet/tests/FunctionThunkingTests.cs new file mode 100644 index 0000000000..dc7af0bd3b --- /dev/null +++ b/crates/misc/dotnet/tests/FunctionThunkingTests.cs @@ -0,0 +1,71 @@ +using FluentAssertions; +using System; +using System.Linq; +using Xunit; + +namespace Wasmtime.Tests +{ + public class FunctionThunkingFixture : ModuleFixture + { + protected override string ModuleFileName => "FunctionThunking.wasm"; + } + + public class FunctionThunkingTests : IClassFixture + { + const string THROW_MESSAGE = "Test error messages for wasmtime dotnet bidnings 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; + } + + private FunctionThunkingFixture Fixture { get; } + + [Fact] + public void ItBindsImportMethodsAndCallsThemCorrectly() + { + var host = new MyHost(); + using (var instance = Fixture.Module.Instantiate(host)) + { + 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 }); + + invoke_add(40, 2).Should().Be(42); + invoke_add(22, 5).Should().Be(27); + + //Collect garbage to make sure delegate function pointers pasted to wasmtime are rooted. + GC.Collect(); + GC.WaitForPendingFinalizers(); + + invoke_add(1970, 50).Should().Be(2020); + } + } + + [Fact] + public void ItPropegatesExceptionsToCallersViaTraps() + { + var host = new MyHost(); + using (var instance = Fixture.Module.Instantiate(host)) + { + var throw_func = instance.Externs.Functions.Where(f => f.Name == "do_throw_wrapper").Single(); + Action action = () => throw_func.Invoke(); + + action + .Should() + .Throw() + .WithMessage(THROW_MESSAGE); + } + } + } +} diff --git a/crates/misc/dotnet/tests/Modules/FunctionThunking.wasm b/crates/misc/dotnet/tests/Modules/FunctionThunking.wasm new file mode 100644 index 0000000000000000000000000000000000000000..a8a67acb1c8d95fc18ad155eedf7c7c196cc43bb GIT binary patch literal 126 zcmX}ju?~YE6ouh)?xn4fq0F7!eJCY?#Dzj=Q{%*|YX{x_{K*IZNd&-NW@(LW2IM6g z-#_&s0RQSjp1w=XE2t`rfHO4IJ$b DhqxM1 literal 0 HcmV?d00001 diff --git a/crates/misc/dotnet/tests/Modules/FunctionThunking.wat b/crates/misc/dotnet/tests/Modules/FunctionThunking.wat new file mode 100644 index 0000000000..fb487afaa8 --- /dev/null +++ b/crates/misc/dotnet/tests/Modules/FunctionThunking.wat @@ -0,0 +1,20 @@ +(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) + ) + ) + (func $do_throw_wrapper (; 3 ;) + (call $do_throw) + ) +)