Skip to content

Commit 77e17d8

Browse files
authored
Add a wasmtime-specific wasmtime_wat2wasm C API (#1206)
* Add a wasmtime-specific `wasmtime_wat2wasm` C API This commit implements a wasmtime-specific C API for converting the text format to the binary format. An upstream spec issue exists for adding this to the C API, but in the meantime we can experiment with our own version of this API and use it in the C# extension, for example! Closes #1000 * Reorder arguments * Use wasm_byte_vec_t for input `*.wat` * Mark wat input as const * Return an error message and use `fixed` * Actually include the error message * Use `fixed` in `Module.cs` as well
1 parent 732c646 commit 77e17d8

38 files changed

+118
-30
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/c-api/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ doctest = false
2020
wasmtime = { path = "../api" }
2121
wasi-common = { path = "../wasi-common" }
2222
wasmtime-wasi = { path = "../wasi" }
23+
wat = "1.0"

crates/c-api/include/wasmtime.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,26 @@ WASMTIME_CONFIG_PROP(cranelift_opt_level, wasmtime_opt_level_t)
3838

3939
///////////////////////////////////////////////////////////////////////////////
4040

41+
// Converts from the text format of WebAssembly to to the binary format.
42+
//
43+
// * `engine` - a previously created engine which will drive allocations and
44+
// such
45+
// * `wat` - this it the input buffer with the WebAssembly Text Format inside of
46+
// it. This will be parsed and converted to the binary format.
47+
// * `ret` - if the conversion is successful, this byte vector is filled in with
48+
// the WebAssembly binary format.
49+
// * `error_message` - if the conversion fails, this is filled in with a
50+
// descriptive error message of why parsing failed. This parameter is
51+
// optional.
52+
//
53+
// Returns `true` if conversion succeeded, or `false` if it failed.
54+
bool wasmtime_wat2wasm(
55+
wasm_engine_t *engine,
56+
const wasm_byte_vec_t *wat,
57+
own wasm_byte_vec_t *ret,
58+
own wasm_byte_vec_t *error_message,
59+
);
60+
4161
#ifdef __cplusplus
4262
} // extern "C"
4363
#endif

crates/c-api/src/ext.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
//! This file defines the extern "C" API extension, which are specific
22
//! to the wasmtime implementation.
33
4-
use crate::wasm_config_t;
4+
use crate::{wasm_byte_vec_t, wasm_config_t, wasm_engine_t};
5+
use std::str;
56
use wasmtime::{OptLevel, Strategy};
67

78
#[repr(u8)]
@@ -86,3 +87,33 @@ pub unsafe extern "C" fn wasmtime_config_cranelift_opt_level_set(
8687
WASMTIME_OPT_LEVEL_SPEED_AND_SIZE => OptLevel::SpeedAndSize,
8788
});
8889
}
90+
91+
#[no_mangle]
92+
pub unsafe extern "C" fn wasmtime_wat2wasm(
93+
_engine: *mut wasm_engine_t,
94+
wat: *const wasm_byte_vec_t,
95+
ret: *mut wasm_byte_vec_t,
96+
error: *mut wasm_byte_vec_t,
97+
) -> bool {
98+
let wat = match str::from_utf8((*wat).as_slice()) {
99+
Ok(s) => s,
100+
Err(_) => {
101+
if !error.is_null() {
102+
(*error).set_from_slice(b"input was not valid utf-8");
103+
}
104+
return false;
105+
}
106+
};
107+
match wat::parse_str(wat) {
108+
Ok(bytes) => {
109+
(*ret).set_from_slice(&bytes);
110+
true
111+
}
112+
Err(e) => {
113+
if !error.is_null() {
114+
(*error).set_from_slice(e.to_string().as_bytes());
115+
}
116+
false
117+
}
118+
}
119+
}

crates/misc/dotnet/src/Engine.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System;
2+
using System.Text;
3+
using System.Runtime.InteropServices;
24

35
namespace Wasmtime
46
{
@@ -40,6 +42,34 @@ public Store CreateStore()
4042
return new Store(this);
4143
}
4244

45+
/// <summary>
46+
/// Converts the WebAssembly text format to the binary format
47+
/// </summary>
48+
/// <returns>Returns the binary-encoded wasm module.</returns>
49+
public byte[] WatToWasm(string wat)
50+
{
51+
var watBytes = Encoding.UTF8.GetBytes(wat);
52+
unsafe
53+
{
54+
fixed (byte *ptr = watBytes)
55+
{
56+
Interop.wasm_byte_vec_t watByteVec;
57+
watByteVec.size = (UIntPtr)watBytes.Length;
58+
watByteVec.data = ptr;
59+
if (!Interop.wasmtime_wat2wasm(Handle, ref watByteVec, out var bytes, out var error)) {
60+
var errorSpan = new ReadOnlySpan<byte>(error.data, checked((int)error.size));
61+
var message = Encoding.UTF8.GetString(errorSpan);
62+
Interop.wasm_byte_vec_delete(ref error);
63+
throw new WasmtimeException("failed to parse input wat: " + message);
64+
}
65+
var byteSpan = new ReadOnlySpan<byte>(bytes.data, checked((int)bytes.size));
66+
var ret = byteSpan.ToArray();
67+
Interop.wasm_byte_vec_delete(ref bytes);
68+
return ret;
69+
}
70+
}
71+
}
72+
4373
/// <inheritdoc/>
4474
public void Dispose()
4575
{

crates/misc/dotnet/src/Interop.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -899,7 +899,7 @@ public static extern unsafe void wasi_config_set_env(
899899
public static extern bool wasi_config_set_stdin_file(
900900
WasiConfigHandle config,
901901
[MarshalAs(UnmanagedType.LPUTF8Str)] string path
902-
);
902+
);
903903

904904
[DllImport(LibraryName)]
905905
public static extern void wasi_config_inherit_stdin(WasiConfigHandle config);
@@ -909,7 +909,7 @@ public static extern bool wasi_config_set_stdin_file(
909909
public static extern bool wasi_config_set_stdout_file(
910910
WasiConfigHandle config,
911911
[MarshalAs(UnmanagedType.LPUTF8Str)] string path
912-
);
912+
);
913913

914914
[DllImport(LibraryName)]
915915
public static extern void wasi_config_inherit_stdout(WasiConfigHandle config);
@@ -919,7 +919,7 @@ public static extern bool wasi_config_set_stdout_file(
919919
public static extern bool wasi_config_set_stderr_file(
920920
WasiConfigHandle config,
921921
[MarshalAs(UnmanagedType.LPUTF8Str)] string path
922-
);
922+
);
923923

924924
[DllImport(LibraryName)]
925925
public static extern void wasi_config_inherit_stderr(WasiConfigHandle config);
@@ -974,5 +974,14 @@ out IntPtr trap
974974

975975
[DllImport(LibraryName)]
976976
public static extern void wasmtime_config_cranelift_opt_level_set(WasmConfigHandle config, wasmtime_opt_level_t level);
977+
978+
[DllImport(LibraryName, CharSet=CharSet.Ansi)]
979+
[return: MarshalAs(UnmanagedType.I1)]
980+
public static extern bool wasmtime_wat2wasm(
981+
EngineHandle engine,
982+
ref wasm_byte_vec_t wat,
983+
out wasm_byte_vec_t vec,
984+
out wasm_byte_vec_t error_message
985+
);
977986
}
978987
}

crates/misc/dotnet/src/Module.cs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,13 @@ internal Module(Store store, string name, byte[] bytes)
1515
throw new ArgumentNullException(nameof(store));
1616
}
1717

18-
var bytesHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
19-
20-
try
18+
unsafe
2119
{
22-
unsafe
20+
fixed (byte *ptr = bytes)
2321
{
2422
Interop.wasm_byte_vec_t vec;
2523
vec.size = (UIntPtr)bytes.Length;
26-
vec.data = (byte*)bytesHandle.AddrOfPinnedObject();
24+
vec.data = ptr;
2725

2826
Handle = Interop.wasm_module_new(store.Handle, ref vec);
2927
}
@@ -33,10 +31,6 @@ internal Module(Store store, string name, byte[] bytes)
3331
throw new WasmtimeException($"WebAssembly module '{name}' is not valid.");
3432
}
3533
}
36-
finally
37-
{
38-
bytesHandle.Free();
39-
}
4034

4135
Store = store;
4236
Name = name;

crates/misc/dotnet/tests/Fixtures/ModuleFixture.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ public ModuleFixture()
1313
.WithReferenceTypes(true)
1414
.Build();
1515
Store = Engine.CreateStore();
16-
Module = Store.CreateModule(Path.Combine("Modules", ModuleFileName));
16+
var wat = Path.Combine("Modules", ModuleFileName);
17+
var wasm = Engine.WatToWasm(File.ReadAllText(wat));
18+
Module = Store.CreateModule(wat, wasm);
1719
}
1820

1921
public void Dispose()

crates/misc/dotnet/tests/FunctionExportsTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Wasmtime.Tests
88
{
99
public class FunctionExportsFixture : ModuleFixture
1010
{
11-
protected override string ModuleFileName => "FunctionExports.wasm";
11+
protected override string ModuleFileName => "FunctionExports.wat";
1212
}
1313

1414
public class FunctionExportsTests : IClassFixture<FunctionExportsFixture>

crates/misc/dotnet/tests/FunctionImportsTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Wasmtime.Tests
88
{
99
public class FunctionImportsFixture : ModuleFixture
1010
{
11-
protected override string ModuleFileName => "FunctionImports.wasm";
11+
protected override string ModuleFileName => "FunctionImports.wat";
1212
}
1313

1414
public class FunctionImportsTests : IClassFixture<FunctionImportsFixture>

crates/misc/dotnet/tests/FunctionThunkingTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace Wasmtime.Tests
77
{
88
public class FunctionThunkingFixture : ModuleFixture
99
{
10-
protected override string ModuleFileName => "FunctionThunking.wasm";
10+
protected override string ModuleFileName => "FunctionThunking.wat";
1111
}
1212

1313
public class FunctionThunkingTests : IClassFixture<FunctionThunkingFixture>

crates/misc/dotnet/tests/GlobalExportsTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace Wasmtime.Tests
1010
{
1111
public class GlobalExportsFixture : ModuleFixture
1212
{
13-
protected override string ModuleFileName => "GlobalExports.wasm";
13+
protected override string ModuleFileName => "GlobalExports.wat";
1414
}
1515

1616
public class GlobalExportsTests : IClassFixture<GlobalExportsFixture>

crates/misc/dotnet/tests/GlobalImportBindingTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace Wasmtime.Tests
66
{
77
public class GlobalImportBindingFixture : ModuleFixture
88
{
9-
protected override string ModuleFileName => "GlobalImportBindings.wasm";
9+
protected override string ModuleFileName => "GlobalImportBindings.wat";
1010
}
1111

1212
public class GlobalImportBindingTests : IClassFixture<GlobalImportBindingFixture>

crates/misc/dotnet/tests/GlobalImportsTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Wasmtime.Tests
88
{
99
public class GlobalImportsFixture : ModuleFixture
1010
{
11-
protected override string ModuleFileName => "GlobalImports.wasm";
11+
protected override string ModuleFileName => "GlobalImports.wat";
1212
}
1313

1414
public class GlobalImportsTests : IClassFixture<GlobalImportsFixture>

crates/misc/dotnet/tests/MemoryExportsTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Wasmtime.Tests
88
{
99
public class MemoryExportsFixture : ModuleFixture
1010
{
11-
protected override string ModuleFileName => "MemoryExports.wasm";
11+
protected override string ModuleFileName => "MemoryExports.wat";
1212
}
1313

1414
public class MemoryExportsTests : IClassFixture<MemoryExportsFixture>

crates/misc/dotnet/tests/MemoryImportBindingTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace Wasmtime.Tests
66
{
77
public class MemoryImportBindingFixture : ModuleFixture
88
{
9-
protected override string ModuleFileName => "MemoryImportBinding.wasm";
9+
protected override string ModuleFileName => "MemoryImportBinding.wat";
1010
}
1111

1212
public class MemoryImportBindingTests : IClassFixture<MemoryImportBindingFixture>

crates/misc/dotnet/tests/MemoryImportFromModuleTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace Wasmtime.Tests
66
{
77
public class MemoryImportFromModuleFixture : ModuleFixture
88
{
9-
protected override string ModuleFileName => "MemoryImportFromModule.wasm";
9+
protected override string ModuleFileName => "MemoryImportFromModule.wat";
1010
}
1111

1212
public class MemoryImportFromModuleTests : IClassFixture<MemoryImportFromModuleFixture>

crates/misc/dotnet/tests/MemoryImportNoUpperBoundTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace Wasmtime.Tests
66
{
77
public class MemoryImportNoUpperBoundFixture : ModuleFixture
88
{
9-
protected override string ModuleFileName => "MemoryImportNoUpperBound.wasm";
9+
protected override string ModuleFileName => "MemoryImportNoUpperBound.wat";
1010
}
1111

1212
public class MemoryImportNoUpperBoundTests : IClassFixture<MemoryImportNoUpperBoundFixture>

crates/misc/dotnet/tests/MemoryImportWithUpperBoundTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace Wasmtime.Tests
66
{
77
public class MemoryImportWithUpperBoundFixture : ModuleFixture
88
{
9-
protected override string ModuleFileName => "MemoryImportWithUpperBound.wasm";
9+
protected override string ModuleFileName => "MemoryImportWithUpperBound.wat";
1010
}
1111

1212
public class MemoryImportWithUpperBoundTests : IClassFixture<MemoryImportWithUpperBoundFixture>
Binary file not shown.
Binary file not shown.
Binary file not shown.
-194 Bytes
Binary file not shown.
Binary file not shown.
-176 Bytes
Binary file not shown.
-112 Bytes
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-53 Bytes
Binary file not shown.
-52 Bytes
Binary file not shown.
-607 Bytes
Binary file not shown.

crates/misc/dotnet/tests/TableExportsTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Wasmtime.Tests
88
{
99
public class TableExportsFixture : ModuleFixture
1010
{
11-
protected override string ModuleFileName => "TableExports.wasm";
11+
protected override string ModuleFileName => "TableExports.wat";
1212
}
1313

1414
public class TableExportsTests : IClassFixture<TableExportsFixture>

crates/misc/dotnet/tests/TableImportsTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Wasmtime.Tests
88
{
99
public class TableImportsFixture : ModuleFixture
1010
{
11-
protected override string ModuleFileName => "TableImports.wasm";
11+
protected override string ModuleFileName => "TableImports.wat";
1212
}
1313

1414
public class TableImportsTests : IClassFixture<TableImportsFixture>

crates/misc/dotnet/tests/TempFile.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@ public void Dispose()
2121

2222
public string Path { get; private set; }
2323
}
24-
}
24+
}

crates/misc/dotnet/tests/WasiTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace Wasmtime.Tests
99
{
1010
public class WasiFixture : ModuleFixture
1111
{
12-
protected override string ModuleFileName => "Wasi.wasm";
12+
protected override string ModuleFileName => "Wasi.wat";
1313
}
1414

1515
public class WasiTests : IClassFixture<WasiFixture>

crates/misc/dotnet/tests/Wasmtime.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
</Target>
2828

2929
<ItemGroup>
30-
<None Update="Modules/*.wasm" CopyToOutputDirectory="PreserveNewest" />
30+
<None Update="Modules/*.wat" CopyToOutputDirectory="PreserveNewest" />
3131
</ItemGroup>
3232

3333
</Project>

0 commit comments

Comments
 (0)