From 94d8bcba4ed68fd97461f8fd82c31ce9d8ce41b4 Mon Sep 17 00:00:00 2001 From: Christoph Voigt Date: Tue, 31 May 2022 22:33:31 +0200 Subject: [PATCH 01/17] update to zig master 0.10.0 and gyro v0.7.0 --- .github/workflows/ci.yml | 2 ++ .gitignore | 2 ++ README.md | 4 ++-- build.zig | 19 ++++++++++++++----- ci/install_gyro | 10 ++++++++++ ci/install_wasmtime | 10 ++++++++++ ci/linux_ci | 35 ++++++++++++++++++----------------- ci/macos_ci | 30 +++++++++++++++--------------- ci/run_gyro | 14 ++++++++++++++ ci/win_ci | 32 +++++++++++++++++--------------- examples/interrupt.zig | 4 ++-- examples/linking.zig | 2 +- gyro.zzz | 11 +++++------ src/main.zig | 15 ++++++++------- 14 files changed, 120 insertions(+), 70 deletions(-) create mode 100755 ci/install_gyro create mode 100755 ci/install_wasmtime create mode 100755 ci/run_gyro diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 98113a3..90081f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,8 @@ on: pull_request: branches: - main + schedule: + - cron: '0 0 * * *' # run at 00:00 UTC jobs: build_nix: diff --git a/.gitignore b/.gitignore index dc7010e..8ea72a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ zig-cache +zig-* +wasmtime* .DS_Store *.swp .gyro diff --git a/README.md b/README.md index 8ee0c29..49ba880 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # wasmtime-zig -[github](https://github.com/kubkon/wasmtime-zig) +[github](https://github.com/kubkon/wasmtime-zig) [build status](https://github.com/kubkon/wasmtime-zig/actions?query=branch%3Amaster) Zig embedding of [Wasmtime] @@ -13,7 +13,7 @@ but expected, and things might just not work as expected yet. ## Building -To build this library, you will need Zig nightly 0.8.0, as well as [`gyro`] package manager. +To build this library, you will need Zig nightly 0.10.0, as well as [`gyro`] package manager (`v0.7.0`). [`gyro`]: https://github.com/mattnite/gyro diff --git a/build.zig b/build.zig index b4755e4..9c5ab2b 100644 --- a/build.zig +++ b/build.zig @@ -30,13 +30,22 @@ pub fn build(b: *std.build.Builder) !void { simple_exe.setBuildMode(mode); simple_exe.addPackage(.{ .name = "wasmtime", - .path = "src/main.zig", + .source = .{ .path = "src/main.zig" }, .dependencies = &.{pkgs.wasm}, }); - if (builtin.os.tag == .windows) { - simple_exe.linkSystemLibrary("wasmtime.dll"); - } else { - simple_exe.linkSystemLibrary("wasmtime"); + switch (builtin.os.tag) { + .windows => { + simple_exe.linkSystemLibrary("wasmtime.dll"); + simple_exe.linkSystemLibrary("unwind"); + }, + .linux => { + simple_exe.linkSystemLibrary("wasmtime"); + simple_exe.linkSystemLibrary("unwind"); + }, + .macos => { + simple_exe.linkSystemLibrary("wasmtime"); + }, + else => unreachable, } simple_exe.linkLibC(); simple_exe.step.dependOn(b.getInstallStep()); diff --git a/ci/install_gyro b/ci/install_gyro new file mode 100755 index 0000000..7adedd4 --- /dev/null +++ b/ci/install_gyro @@ -0,0 +1,10 @@ +#!/bin/sh + +set -x +set -e + +GYRO_VERSION=$1 +GYRO=$2 + +curl -sL "https://github.com/mattnite/gyro/releases/download/$GYRO_VERSION/$GYRO.tar.gz" -o "$GYRO.tar.gz" +tar xf "$GYRO.tar.gz" diff --git a/ci/install_wasmtime b/ci/install_wasmtime new file mode 100755 index 0000000..7b1f174 --- /dev/null +++ b/ci/install_wasmtime @@ -0,0 +1,10 @@ +#!/bin/sh + +set -x +set -e + +WASMTIME_VERSION=$1 +WASMTIME=$2 + +curl -sL "https://github.com/bytecodealliance/wasmtime/releases/download/$WASMTIME_VERSION/$WASMTIME.tar.xz" -o "$WASMTIME.tar.xz" +tar xf "$WASMTIME.tar.xz" diff --git a/ci/linux_ci b/ci/linux_ci index 8f4246b..464ea98 100755 --- a/ci/linux_ci +++ b/ci/linux_ci @@ -3,28 +3,29 @@ set -x set -e -ZIG="zig-linux-x86_64-0.8.0-dev.2667+44de88498" +ARCH="x86_64" +OS="linux" +ARCH_OS="${ARCH}-${OS}" +OS_ARCH="${OS}-${ARCH}" + +ZIG_SRC="https://ziglang.org/download/index.json" +ZIG_VERSION=$(curl -s ${ZIG_SRC} | jq -r '.master.version') +ZIG="zig-${OS_ARCH}-$ZIG_VERSION" +ZIG_MASTER=$(curl -s ${ZIG_SRC} | jq -r --arg ARCH_OS "$ARCH_OS" '.master[$ARCH_OS].tarball') + WASMTIME_VERSION="v0.24.0" -WASMTIME="wasmtime-$WASMTIME_VERSION-x86_64-linux-c-api" -GYRO_VERSION="0.2.3" -GYRO="gyro-$GYRO_VERSION-linux-x86_64" +WASMTIME="wasmtime-$WASMTIME_VERSION-${ARCH_OS}-c-api" +GYRO_VERSION="0.7.0" +GYRO="gyro-$GYRO_VERSION-${OS_ARCH}" -wget -nv "https://ziglang.org/builds/$ZIG.tar.xz" +# TODO: make this nice +curl -sL "${ZIG_MASTER}" -o "$ZIG.tar.xz" tar xf "$ZIG.tar.xz" export PATH="$(pwd)/$ZIG:$PATH" -wget -nv "https://github.com/bytecodealliance/wasmtime/releases/download/$WASMTIME_VERSION/$WASMTIME.tar.xz" -tar xf "$WASMTIME.tar.xz" +./ci/install_wasmtime $WASMTIME_VERSION $WASMTIME -wget -nv "https://github.com/mattnite/gyro/releases/download/$GYRO_VERSION/$GYRO.tar.gz" -tar xf "$GYRO.tar.gz" +./ci/install_gyro $GYRO_VERSION $GYRO export PATH="$(pwd)/$GYRO/bin:$PATH" -gyro build -gyro build test -gyro build run -Dexample=simple --search-prefix "$(pwd)/$WASMTIME" -gyro build run -Dexample=gcd --search-prefix "$(pwd)/$WASMTIME" -gyro build run -Dexample=linking --search-prefix "$(pwd)/$WASMTIME" -gyro build run -Dexample=memory --search-prefix "$(pwd)/$WASMTIME" -gyro build run -Dexample=interrupt --search-prefix "$(pwd)/$WASMTIME" - +./ci/run_gyro $WASMTIME diff --git a/ci/macos_ci b/ci/macos_ci index e41c5fc..ca694e0 100755 --- a/ci/macos_ci +++ b/ci/macos_ci @@ -3,28 +3,28 @@ set -x set -e -ZIG="zig-macos-x86_64-0.8.0-dev.2670+d8d92dafe" +ARCH="x86_64" +OS="macos" +ARCH_OS="${ARCH}-${OS}" +OS_ARCH="${OS}-${ARCH}" + +ZIG_SRC="https://ziglang.org/download/index.json" +ZIG_VERSION=$(curl -s ${ZIG_SRC} | jq -r '.master.version') +ZIG="zig-${OS_ARCH}-$ZIG_VERSION" +ZIG_MASTER=$(curl -s ${ZIG_SRC} | jq -r --arg ARCH_OS "$ARCH_OS" '.master[$ARCH_OS].tarball') + WASMTIME_VERSION="v0.24.0" WASMTIME="wasmtime-$WASMTIME_VERSION-x86_64-macos-c-api" -GYRO_VERSION="0.2.3" +GYRO_VERSION="0.7.0" GYRO="gyro-$GYRO_VERSION-macos-x86_64" -curl -L "https://ziglang.org/builds/$ZIG.tar.xz" -o "$ZIG.tar.xz" +curl -sL "${ZIG_MASTER}" -o "$ZIG.tar.xz" tar xf "$ZIG.tar.xz" export PATH="$(pwd)/$ZIG:$PATH" -curl -L "https://github.com/bytecodealliance/wasmtime/releases/download/$WASMTIME_VERSION/$WASMTIME.tar.xz" -o "$WASMTIME.tar.xz" -tar xf "$WASMTIME.tar.xz" +./ci/install_wasmtime $WASMTIME_VERSION $WASMTIME -curl -L "https://github.com/mattnite/gyro/releases/download/$GYRO_VERSION/$GYRO.tar.gz" -o "$GYRO.tar.gz" -tar xf "$GYRO.tar.gz" +./ci/install_gyro $GYRO_VERSION $GYRO export PATH="$(pwd)/$GYRO/bin:$PATH" -gyro build -gyro build test -gyro build run -Dexample=simple --search-prefix "$(pwd)/$WASMTIME" -gyro build run -Dexample=gcd --search-prefix "$(pwd)/$WASMTIME" -gyro build run -Dexample=linking --search-prefix "$(pwd)/$WASMTIME" -gyro build run -Dexample=memory --search-prefix "$(pwd)/$WASMTIME" -gyro build run -Dexample=interrupt --search-prefix "$(pwd)/$WASMTIME" - +./ci/run_gyro $WASMTIME diff --git a/ci/run_gyro b/ci/run_gyro new file mode 100755 index 0000000..c01870f --- /dev/null +++ b/ci/run_gyro @@ -0,0 +1,14 @@ +#!/bin/sh + +set -x +set -e + +WASMTIME=$1 + +gyro build +gyro build test +gyro build run -Dexample=simple --search-prefix "$(pwd)/$WASMTIME" +gyro build run -Dexample=gcd --search-prefix "$(pwd)/$WASMTIME" +gyro build run -Dexample=linking --search-prefix "$(pwd)/$WASMTIME" +gyro build run -Dexample=memory --search-prefix "$(pwd)/$WASMTIME" +gyro build run -Dexample=interrupt --search-prefix "$(pwd)/$WASMTIME" diff --git a/ci/win_ci b/ci/win_ci index 531f81c..c5978fe 100755 --- a/ci/win_ci +++ b/ci/win_ci @@ -3,28 +3,30 @@ set -x set -e -ZIG="zig-windows-x86_64-0.8.0-dev.2667+44de88498" +ARCH="x86_64" +OS="windows" +ARCH_OS="${ARCH}-${OS}" +OS_ARCH="${OS}-${ARCH}" + +ZIG_SRC="https://ziglang.org/download/index.json" +ZIG_VERSION=$(curl -s ${ZIG_SRC} | jq -r '.master.version') +ZIG="zig-${OS_ARCH}-$ZIG_VERSION" +ZIG_MASTER=$(curl -s ${ZIG_SRC} | jq -r --arg ARCH_OS "$ARCH_OS" '.master[$ARCH_OS].tarball') + WASMTIME_VERSION="v0.24.0" -WASMTIME="wasmtime-$WASMTIME_VERSION-x86_64-windows-c-api" -GYRO_VERSION="0.2.3" -GYRO="gyro-$GYRO_VERSION-windows-x86_64" +WASMTIME="wasmtime-$WASMTIME_VERSION-${ARCH_OS}-c-api" +GYRO_VERSION="0.7.0" +GYRO="gyro-$GYRO_VERSION-${OS_ARCH}" -curl -L "https://ziglang.org/builds/$ZIG.zip" -o "$ZIG.zip" +curl -sL "${ZIG_MASTER}" -o "$ZIG.zip" 7z x "$ZIG.zip" export PATH="$(pwd)/$ZIG:$PATH" -curl -L "https://github.com/bytecodealliance/wasmtime/releases/download/$WASMTIME_VERSION/$WASMTIME.zip" -o "$WASMTIME.zip" +curl -sL "https://github.com/bytecodealliance/wasmtime/releases/download/$WASMTIME_VERSION/$WASMTIME.zip" -o "$WASMTIME.zip" 7z x "$WASMTIME.zip" -curl -L "https://github.com/mattnite/gyro/releases/download/$GYRO_VERSION/$GYRO.zip" -o "$GYRO.zip" +curl -sL "https://github.com/mattnite/gyro/releases/download/$GYRO_VERSION/$GYRO.zip" -o "$GYRO.zip" 7z x "$GYRO.zip" export PATH="$(pwd)/$GYRO/bin:$PATH" -gyro build -gyro build test -gyro build run -Dexample=simple --search-prefix "$(pwd)/$WASMTIME" -gyro build run -Dexample=gcd --search-prefix "$(pwd)/$WASMTIME" -gyro build run -Dexample=linking --search-prefix "$(pwd)/$WASMTIME" -gyro build run -Dexample=memory --search-prefix "$(pwd)/$WASMTIME" -gyro build run -Dexample=interrupt --search-prefix "$(pwd)/$WASMTIME" - +./ci/run_gyro $WASMTIME diff --git a/examples/interrupt.zig b/examples/interrupt.zig index b96bf0b..db0ff9c 100644 --- a/examples/interrupt.zig +++ b/examples/interrupt.zig @@ -38,7 +38,7 @@ pub fn main() !void { defer instance.deinit(); std.debug.print("Instance initialized...\n", .{}); - const thread = try std.Thread.spawn(interrupt, handle); + const thread = try std.Thread.spawn(.{}, interrupt, .{ handle }); if (instance.getExportFunc(module, "run")) |f| { std.debug.print("Calling export...\n", .{}); @@ -50,5 +50,5 @@ pub fn main() !void { std.debug.print("Export not found...\n", .{}); } - thread.wait(); + thread.join(); } diff --git a/examples/linking.zig b/examples/linking.zig index 9ebf0ad..2ae1ea4 100644 --- a/examples/linking.zig +++ b/examples/linking.zig @@ -1,6 +1,6 @@ const std = @import("std"); const wasmtime = @import("wasmtime"); -const builtin = std.builtin; +const builtin = @import("builtin"); const fs = std.fs; const ga = std.heap.c_allocator; const Allocator = std.mem.Allocator; diff --git a/gyro.zzz b/gyro.zzz index f2b7f48..297b6ca 100644 --- a/gyro.zzz +++ b/gyro.zzz @@ -1,7 +1,6 @@ pkgs: wasmtime: version: 0.0.0 - root: src/main.zig description: Zig embedding of Wasmtime license: Apache-2.0 source_url: "https://github.com/zigwasm/wasmtime-zig" @@ -9,10 +8,10 @@ pkgs: wasmtime wasm wasi + root: src/main.zig deps: wasm: - src: - github: - user: zigwasm - repo: wasm-zig - ref: main + git: + url: "https://github.com/zigwasm/wasm-zig.git" + ref: main + root: src/main.zig diff --git a/src/main.zig b/src/main.zig index 996f265..b523a4e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -69,14 +69,14 @@ pub const Module = struct { inner: *wasm.Module, /// Initializes a new `Module` using the supplied engine and wasm bytecode - pub fn initFromWasm(engine: *Engine, wasm: []const u8) !Module { - var wasm_bytes = ByteVec.initWithCapacity(wasm.len); + pub fn initFromWasm(engine: *Engine, _wasm: []const u8) !Module { + var wasm_bytes = ByteVec.initWithCapacity(_wasm.len); defer wasm_bytes.deinit(); var i: usize = 0; var ptr = wasm_bytes.data; - while (i < wasm.len) : (i += 1) { - ptr.* = wasm[i]; + while (i < _wasm.len) : (i += 1) { + ptr.* = _wasm[i]; ptr += 1; } @@ -163,8 +163,8 @@ pub const Func = struct { comptime var wasm_args: [args_len]Value = undefined; inline for (wasm_args) |*arg, i| { arg.* = switch (@TypeOf(args[i])) { - i32, u32 => .{ .kind = .i32, .of = .{ .i32 = @intCast(i32, args[i]) } }, - i64, u64 => .{ .kind = .i64, .of = .{ .i64 = @intCast(i64, args[i]) } }, + i32, u32 => .{ .kind = .i32, .of = .{ .i32 = @bitCast(i32, args[i]) } }, + i64, u64 => .{ .kind = .i64, .of = .{ .i64 = @bitCast(i64, args[i]) } }, f32 => .{ .kind = .f32, .of = .{ .f32 = args[i] } }, f64 => .{ .kind = .f64, .of = .{ .f64 = args[i] } }, *Func => .{ .kind = .funcref, .of = .{ .ref = args[i] } }, @@ -216,7 +216,7 @@ pub const Func = struct { f32 => result_ty.of.f32, f64 => result_ty.of.f64, *Func => @ptrCast(?*Func, result_ty.of.ref).?, - *Extern => @ptrCast(?*c.Extern, result_ty.of.ref).?, + *Extern => @ptrCast(?*Extern, result_ty.of.ref).?, else => |ty| @compileError("Unsupported result type '" ++ @typeName(ty) ++ "'"), }; } @@ -296,6 +296,7 @@ pub const InterruptHandle = opaque { } /// Invokes an interrupt in the current wasm module pub fn interrupt(self: *InterruptHandle) void { + std.debug.print("> INTERRUPTING! \n", .{}); wasmtime_interrupt_handle_interrupt(self); } From a05c8402fc206795c16fb94d893aa86b42cd41f0 Mon Sep 17 00:00:00 2001 From: Christoph Voigt Date: Thu, 16 Jun 2022 22:12:53 +0200 Subject: [PATCH 02/17] update wasmtime to 0.36.0 --- ci/linux_ci | 2 +- ci/macos_ci | 2 +- ci/win_ci | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/linux_ci b/ci/linux_ci index 464ea98..e79387b 100755 --- a/ci/linux_ci +++ b/ci/linux_ci @@ -13,7 +13,7 @@ ZIG_VERSION=$(curl -s ${ZIG_SRC} | jq -r '.master.version') ZIG="zig-${OS_ARCH}-$ZIG_VERSION" ZIG_MASTER=$(curl -s ${ZIG_SRC} | jq -r --arg ARCH_OS "$ARCH_OS" '.master[$ARCH_OS].tarball') -WASMTIME_VERSION="v0.24.0" +WASMTIME_VERSION="v0.37.0" WASMTIME="wasmtime-$WASMTIME_VERSION-${ARCH_OS}-c-api" GYRO_VERSION="0.7.0" GYRO="gyro-$GYRO_VERSION-${OS_ARCH}" diff --git a/ci/macos_ci b/ci/macos_ci index ca694e0..b3abf30 100755 --- a/ci/macos_ci +++ b/ci/macos_ci @@ -13,7 +13,7 @@ ZIG_VERSION=$(curl -s ${ZIG_SRC} | jq -r '.master.version') ZIG="zig-${OS_ARCH}-$ZIG_VERSION" ZIG_MASTER=$(curl -s ${ZIG_SRC} | jq -r --arg ARCH_OS "$ARCH_OS" '.master[$ARCH_OS].tarball') -WASMTIME_VERSION="v0.24.0" +WASMTIME_VERSION="v0.37.0" WASMTIME="wasmtime-$WASMTIME_VERSION-x86_64-macos-c-api" GYRO_VERSION="0.7.0" GYRO="gyro-$GYRO_VERSION-macos-x86_64" diff --git a/ci/win_ci b/ci/win_ci index c5978fe..33fd355 100755 --- a/ci/win_ci +++ b/ci/win_ci @@ -13,7 +13,7 @@ ZIG_VERSION=$(curl -s ${ZIG_SRC} | jq -r '.master.version') ZIG="zig-${OS_ARCH}-$ZIG_VERSION" ZIG_MASTER=$(curl -s ${ZIG_SRC} | jq -r --arg ARCH_OS "$ARCH_OS" '.master[$ARCH_OS].tarball') -WASMTIME_VERSION="v0.24.0" +WASMTIME_VERSION="v0.37.0" WASMTIME="wasmtime-$WASMTIME_VERSION-${ARCH_OS}-c-api" GYRO_VERSION="0.7.0" GYRO="gyro-$GYRO_VERSION-${OS_ARCH}" From ec18cc539239b543a59f6a841b39bfa240ed1f59 Mon Sep 17 00:00:00 2001 From: Christoph Voigt Date: Sat, 18 Jun 2022 00:17:16 +0200 Subject: [PATCH 03/17] add config.zig --- src/config.zig | 198 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 src/config.zig diff --git a/src/config.zig b/src/config.zig new file mode 100644 index 0000000..6cc3e80 --- /dev/null +++ b/src/config.zig @@ -0,0 +1,198 @@ +const std = @import("std"); +const error = @import("error.zig").Error; + +pub const wasm = @import("wasm"); + +// wasmtime_strategy_t +// Strategy is the compilation strategies for wasmtime +pub const Strategy = enum(u8) { + // WASMTIME_STRATEGY_AUTO + // StrategyAuto will let wasmtime automatically pick an appropriate compilation strategy + strategyAuto = 0, + // WASMTIME_STRATEGY_CRANELIFT + // StrategyCranelift will force wasmtime to use the Cranelift backend + strategyCranelift = 1, +} + +// wasmtime_opt_level_t +// OptLevel decides what degree of optimization wasmtime will perform on generated machine code +pub const OptLevel = enum(u8) { + // WASMTIME_OPT_LEVEL_NONE + // OptLevelNone will perform no optimizations + optLevelNone = 0, + // WASMTIME_OPT_LEVEL_SPEED + // OptLevelSpeed will optimize machine code to be as fast as possible + optLevelSpeed = 1, + // WASMTIME_OPT_LEVEL_SPEED_AND_SIZE + // OptLevelSpeedAndSize will optimize machine code for speed, but also optimize + // to be small, sometimes at the cost of speed. + optLevelSpeedAndSize = 2, +} + +// wasmtime_profiling_strategy_t +// ProfilingStrategy decides what sort of profiling to enable, if any. +pub const ProfilerStrategy = enum(u8) { + // WASMTIME_PROFILING_STRATEGY_NONE + // profilingStrategyNone means no profiler will be used + profilingStrategyNone = 0, + // WASMTIME_PROFILING_STRATEGY_JITDUMP + // profilingStrategyJitdump will use the "jitdump" linux support + profilingStrategyJitdump = 1, + // WASMTIME_PROFILING_STRATEGY_VTUNE + // Support for VTune will be enabled and the VTune runtime will be informed, + // at runtime, about JIT code. + // + // Note that this isn't always enabled at build time. + profileingStrategyVTune = 2, +} + +pub const Config = struct { + inner: *wasm.Config, + + const Options = struct { + debugInfo: bool = false, + wasmThreads: bool = false, + wasmReferenceTypes: bool = false, + wasmSIMD: bool = false, + wasmBulkMemory: bool = false, + wasmMultiValue: bool = false, + wasmMultiMemory: bool = false, + wasmMemory64: bool = false, + consumeFuel: bool = false, + craneliftDebugVerifier: bool = false, + craneLiftOptLevel: OptLevel = optLevelSpeed, + epochInterruption: bool = false, + }; + + pub fn init(options: Options) !Config { + var config = Config{ + .inner = try wasm.Config.init(), + }; + + if (options.debugInfo) { config.setDebugInfo(true); } + if (options.wasmThreads) { config.setWasmThreads(true); } + if (options.wasmReferenceTypes) { config.setWasmReferenceTypes(true); } + if (options.wasmSIMD) { config.setWasmSIMD(true); } + if (options.wasmBulkMemory) { config.setWasmBulkMemory(true); } + if (options.wasmMultiValue) { config.setWasmMultiValue(true); } + if (options.wasmMultiMemory) { config.setWasmMultiMemory(true); } + if (options.wasmMemory64) { config.setWasmMemory64(true); } + if (options.consumeFuel) { config.setConsumeFuel(true); } + if (options.craneliftDebugVerifier) { config.setCraneliftDebugVerifier(true); } + if (options.craneLiftOptLevel) { config.setCraneLiftOptLevel(options.craneLiftOptLevel); } + if (options.epochInterruption) { config.setEpochInterruption(true);} + + return config; + } + + pub fn deinit(self: *Config) void { + wasm_config_delete(*self.inner); + } + + // setDebugInfo configures whether dwarf debug information for JIT code is enabled + pub fn setDebugInfo(self: *Config, opt: bool) void { + wasmtime_config_debug_info_set(self.inner, opt) + } + + // setWasmThreads configures whether the wasm threads proposal is enabled + pub fn setWasmThreads(self: *Config, opt: bool) void { + wasmtime_config_wasm_threads_set(self.inner, opt) + } + + // setWasmReferenceTypes configures whether the wasm reference types proposal is enabled + pub fn setWasmReferenceTypes(self: *Config, opt: bool) void { + wasmtime_config_wasm_reference_types_set(self.inner, opt) + } + + // setWasmSIMD configures whether the wasm SIMD proposal is enabled + pub fn setWasmSIMD(self: *Config, opt: bool) void { + wasmtime_config_wasm_simd_set(self.inner, opt) + } + + // setWasmBulkMemory configures whether the wasm bulk memory proposal is enabled + pub fn setWasmBulkMemory(self: *Config, opt: bool) void { + wasmtime_config_wasm_bulk_memory_set(self.inner, opt) + } + + // setWasmMultiValue configures whether the wasm multi value proposal is enabled + pub fn setWasmMultiValue(self: *Config, opt: bool) void { + wasmtime_config_wasm_multi_value_set(self.inner, opt) + } + + // setWasmMultiMemory configures whether the wasm multi memory proposal is enabled + pub fn setWasmMultiMemory(self: *Config, opt: bool) void { + wasmtime_config_wasm_multi_memory_set(self.inner, opt) + } + + // setWasmMemory64 configures whether the wasm memory64 proposal is enabled + pub fn setWasmMemory64(self: *Config, opt: bool) void { + wasmtime_config_wasm_memory64_set(self.inner, opt) + } + + // setConsumFuel configures whether fuel is enabled + pub fn setConsumeFuel(self: *Config, opt: bool) void { + wasmtime_config_consume_fuel_set(self.inner, opt) + } + + // setStrategy configures what compilation strategy is used to compile wasm code + pub fn setStrategy(self: *Config, strat: *Strategy) !void { + return wasmtime_config_strategy_set(self.inner, strat) orelse error.ConfigStrategySet + } + + // setCraneliftDebugVerifier configures whether the cranelift debug verifier will be active when + // cranelift is used to compile wasm code. + pub fn setCraneliftDebugVerifier(self: *Config, opt: bool) void { + wasmtime_config_cranelift_debug_verifier_set(self.inner, opt) + } + + // setCraneliftOptLevel configures the cranelift optimization level for generated code + pub fn setCraneLiftOptLevel(self: *Config, level: *OptLevel) void { + wasmtime_config_cranelift_opt_level_set(self.inner, level) orelse error.ConfigOptLevelSet + } + + // setProfiler configures what profiler strategy to use for generated code + pub fn setProfiler(self: *Config, profiler: *ProfilerStrategy) !void { + return wasmtime_config_profiler_set(self.inner, profiler) orelse error.ConfigProfilerStrategySet + } + + // cacheConfigLoadDefault enables compiled code caching for this `Config` using the default settings + // configuration can be found. + // + // For more information about caching see + // https://bytecodealliance.github.io/wasmtime/cli-cache.html + pub fn cacheConfigLoadDefault() !void { + return wasmtime_config_cache_config_load(self.inner, null) orelse error.ConfigLoadDefault; + } + + // cacheConfigLoad enables compiled code caching for this `Config` using the settings specified + // in the configuration file `path`. + // + // For more information about caching and configuration options see + // https://bytecodealliance.github.io/wasmtime/cli-cache.html + pub fn cacheConfigLoad(path: []const u8) !void { + return wasmtime_config_cache_config_load(self.inner, path); + } + + // setEpochInterruption enables epoch-based instrumentation of generated code to + // interrupt WebAssembly execution when the current engine epoch exceeds a + // defined threshold. + pub fn setEpochInterruption(self: *Config, opt: bool) !void { + wasmtime_config_epoch_interruption_set(self.inner, opt); + } + + extern "c" fn wasmtime_config_debug_info_set(*wasm.Config, bool) void; + extern "c" fn wasmtime_config_wasm_threads_set(*wasm.Config, bool) void; + extern "c" fn wasmtime_config_wasm_reference_types_set(*wasm.Config, bool) void; + extern "c" fn wasmtime_config_wasm_simd_set(*wasm.Config, bool) void; + extern "c" fn wasmtime_config_wasm_bulk_memory_set(*wasm.Config, bool) void; + extern "c" fn wasmtime_config_wasm_multi_value_set(*wasm.Config, bool) void; + extern "c" fn wasmtime_config_wasm_multi_memory_set(*wasm.Config, bool) void; + extern "c" fn wasmtime_config_wasm_memory64_set(*wasm.Config, bool) void; + extern "c" fn wasmtime_config_consume_fuel_set(*wasm.Config, bool) void; + extern "c" fn wasmtime_config_strategy_set(*wasm.Config, *Strategy) void; + extern "c" fn wasmtime_config_cranelift_debug_verifier_set(*wasm.Config, bool) void; + extern "c" fn wasmtime_config_cranelift_opt_level_set(*wasm.Config, *OptLevel) void; + extern "c" fn wasmtime_config_profiler_set(*wasm.Config, *ProfilerStrategy) void; + extern "c" fn wasmtime_config_cache_config_load(*wasm.Config, []const u8) void; // CString?? + extern "c" fn wasmtime_config_epoch_interruption_set(*wasm.Config, bool) void; +}; \ No newline at end of file From 0fab8aa44f5471d81f4692b2d057b87e3d2e74ca Mon Sep 17 00:00:00 2001 From: Christoph Voigt Date: Sat, 18 Jun 2022 00:26:25 +0200 Subject: [PATCH 04/17] add engine --- src/engine.zig | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/engine.zig diff --git a/src/engine.zig b/src/engine.zig new file mode 100644 index 0000000..d9fbe38 --- /dev/null +++ b/src/engine.zig @@ -0,0 +1,44 @@ +const std = @import("std"); + +pub const config = @import("./config.zig"); + +pub const wasm = @import("wasm"); + +// Engine is an instance of a wasmtime engine which is used to create a `Store`. +// +// Engines are a form of global configuration for wasm compilations and modules +// and such. +pub const Engine = struct { + inner: *wasm.Engine, + + /// TODO: decide: call function "init" or "new"? + /// init creates a new `Engine` with default configuration. + pub fn init() !*Engine { + return Engine { + .inner = try wasm.Engine.init(); + } + } + + // withConfig creates a new `Engine` with the `Config` provided + // + // Note that once a `Config` is passed to this method it cannot be used again. + pub fn withConfig(config: *config.Config) !*Engine { + return Engine { + .inner = try wasm.Engine.withConfig(config); + } + } + + /// Frees the resources of the `Engine` + pub fn deinit(self: *Engine) void { + self.inner.deinit(self.inner); + } + + /// IncrementEpoch will increase the current epoch number by 1 within the + /// current engine which will cause any connected stores with their epoch + /// deadline exceeded to now be interrupted. + pub fn incrementEpoch(self: *Engine) void { + wasmtime_engine_increment_epoch(self.inner); + } + + extern "c" fn wasmtime_engine_increment_epoch(*wasm.Engine) void; +}; \ No newline at end of file From 3b6cb7253e3fac816406bb228e923f3f199fd4ac Mon Sep 17 00:00:00 2001 From: Christoph Voigt Date: Mon, 20 Jun 2022 00:11:05 +0200 Subject: [PATCH 05/17] add slab --- src/slab.zig | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/store.zig | 48 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 src/slab.zig create mode 100644 src/store.zig diff --git a/src/slab.zig b/src/slab.zig new file mode 100644 index 0000000..327a9ee --- /dev/null +++ b/src/slab.zig @@ -0,0 +1,56 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const ArrayList = std.ArrayList; +const testing = std.testing; + +pub const Slab = struct { + list: ArrayList(u64), + next: u64, + + pub fn init(allocator: Allocator) Slab { + + const s = Slab { + .list = ArrayList(u64).init(allocator), + .next = 0, + }; + + return s; + } + + pub fn allocate(self: *Slab) !u64 { + if (self.next == self.list.items.len) { + try self.list.append(self.next+1); + } + const ret = self.next; + self.next = self.list.items[ret]; + return ret; + } + + pub fn deallocate(self: *Slab, slot: u64) void { + self.list.items[slot] = self.next; + self.next = slot; + } +}; + + +test "wasmtime.Slab" { + var slab = Slab.init(std.heap.c_allocator); + + var al = try slab.allocate(); + if (al != 0) { + @panic("bad alloc"); + } + al = try slab.allocate(); + if (al != 1) { + @panic("bad alloc"); + } + slab.deallocate(0); + al = try slab.allocate(); + if (al != 0) { + @panic("bad alloc"); + } + al = try slab.allocate(); + if (al != 2) { + @panic("bad alloc"); + } +} diff --git a/src/store.zig b/src/store.zig new file mode 100644 index 0000000..52fef1a --- /dev/null +++ b/src/store.zig @@ -0,0 +1,48 @@ +const std = @import("std"); + +pub const config = @import("./config.zig"); +pub const error = @import("./error.zig").Error; + +pub const wasm = @import("wasm"); + +pub const Context = opaque {} + +// Store is a general group of wasm instances, and many objects +// must all be created with and reference the same `Store` +pub const Store = struct { + inner: *wasm.Store, + + engine: *wasmtime.Engine, + + // init creates a new `Store` from the configuration provided in `engine` + pub fn init(engine: *wasmtime.Engine) !*Store { + return Store { + .inner = try wasm.Store.init(); + } + } + + pub fn deinit(self: *Store) void { + self.inner.deinit(self.inner); + } + + pub fn context() *Context{ + return wasmtime_store_context(self.inner) orelse error.StoreContext + } + + pub fn setEpochDeadline(self: *Store, deadline: u64) { + wasmtime_context_set_epoch_deadline(self.context(), deadline) + } + + // not imlemented + pub fn fuelConsumed() (uint64, bool) // zig multiple return values? + pub fn addFuel(fuel: u64) !void {} + pub fn consumeFuel(fuel: u64) !u64 {} + + extern "c" fn wasmtime_store_context(*Store) *Context; + extern "c" fn wasmtime_context_set_epoch_deadline(*Context, u64); + + // not imlemented + extern "c" fn wasmtime_context_fuel_consumed(*Context, u64) // ?? + extern "c" fn wasmtime_context_add_fuel(*Context, u64) void + extern "c" fn wasmtime_context_consume_fuel(*Context, u64, u64) u64 +} From daa30e41d2283b76e6659da6eb9051158b902c10 Mon Sep 17 00:00:00 2001 From: Christoph Voigt Date: Mon, 20 Jun 2022 22:29:55 +0200 Subject: [PATCH 06/17] engine initialized --- src/config.zig | 57 ++++---- src/engine.zig | 31 +++-- src/error.zig | 21 +++ src/main.zig | 343 +------------------------------------------------ 4 files changed, 73 insertions(+), 379 deletions(-) create mode 100644 src/error.zig diff --git a/src/config.zig b/src/config.zig index 6cc3e80..a3fc232 100644 --- a/src/config.zig +++ b/src/config.zig @@ -1,6 +1,5 @@ const std = @import("std"); -const error = @import("error.zig").Error; - +const Error = @import("error.zig").Error; pub const wasm = @import("wasm"); // wasmtime_strategy_t @@ -12,7 +11,7 @@ pub const Strategy = enum(u8) { // WASMTIME_STRATEGY_CRANELIFT // StrategyCranelift will force wasmtime to use the Cranelift backend strategyCranelift = 1, -} +}; // wasmtime_opt_level_t // OptLevel decides what degree of optimization wasmtime will perform on generated machine code @@ -23,11 +22,11 @@ pub const OptLevel = enum(u8) { // WASMTIME_OPT_LEVEL_SPEED // OptLevelSpeed will optimize machine code to be as fast as possible optLevelSpeed = 1, - // WASMTIME_OPT_LEVEL_SPEED_AND_SIZE + // WASMTIME_OPT_LEVEL_SPEED_AND_SIZE // OptLevelSpeedAndSize will optimize machine code for speed, but also optimize // to be small, sometimes at the cost of speed. optLevelSpeedAndSize = 2, -} +}; // wasmtime_profiling_strategy_t // ProfilingStrategy decides what sort of profiling to enable, if any. @@ -44,12 +43,12 @@ pub const ProfilerStrategy = enum(u8) { // // Note that this isn't always enabled at build time. profileingStrategyVTune = 2, -} +}; pub const Config = struct { inner: *wasm.Config, - const Options = struct { + pub const Options = struct { debugInfo: bool = false, wasmThreads: bool = false, wasmReferenceTypes: bool = false, @@ -60,7 +59,7 @@ pub const Config = struct { wasmMemory64: bool = false, consumeFuel: bool = false, craneliftDebugVerifier: bool = false, - craneLiftOptLevel: OptLevel = optLevelSpeed, + // craneLiftOptLevel: OptLevel = OptLevel.optLevelNone, epochInterruption: bool = false, }; @@ -79,80 +78,80 @@ pub const Config = struct { if (options.wasmMemory64) { config.setWasmMemory64(true); } if (options.consumeFuel) { config.setConsumeFuel(true); } if (options.craneliftDebugVerifier) { config.setCraneliftDebugVerifier(true); } - if (options.craneLiftOptLevel) { config.setCraneLiftOptLevel(options.craneLiftOptLevel); } - if (options.epochInterruption) { config.setEpochInterruption(true);} + // if (options.craneLiftOptLevel != undefined) { config.setCraneLiftOptLevel(options.craneLiftOptLevel); } + if (options.epochInterruption) { try config.setEpochInterruption(true);} return config; } - pub fn deinit(self: *Config) void { - wasm_config_delete(*self.inner); - } + // pub fn deinit(self: *Config) void { + // wasm_config_delete(*self.inner); + // } // setDebugInfo configures whether dwarf debug information for JIT code is enabled pub fn setDebugInfo(self: *Config, opt: bool) void { - wasmtime_config_debug_info_set(self.inner, opt) + wasmtime_config_debug_info_set(self.inner, opt); } // setWasmThreads configures whether the wasm threads proposal is enabled pub fn setWasmThreads(self: *Config, opt: bool) void { - wasmtime_config_wasm_threads_set(self.inner, opt) + wasmtime_config_wasm_threads_set(self.inner, opt); } // setWasmReferenceTypes configures whether the wasm reference types proposal is enabled pub fn setWasmReferenceTypes(self: *Config, opt: bool) void { - wasmtime_config_wasm_reference_types_set(self.inner, opt) + wasmtime_config_wasm_reference_types_set(self.inner, opt); } // setWasmSIMD configures whether the wasm SIMD proposal is enabled pub fn setWasmSIMD(self: *Config, opt: bool) void { - wasmtime_config_wasm_simd_set(self.inner, opt) + wasmtime_config_wasm_simd_set(self.inner, opt); } // setWasmBulkMemory configures whether the wasm bulk memory proposal is enabled pub fn setWasmBulkMemory(self: *Config, opt: bool) void { - wasmtime_config_wasm_bulk_memory_set(self.inner, opt) + wasmtime_config_wasm_bulk_memory_set(self.inner, opt); } // setWasmMultiValue configures whether the wasm multi value proposal is enabled pub fn setWasmMultiValue(self: *Config, opt: bool) void { - wasmtime_config_wasm_multi_value_set(self.inner, opt) + wasmtime_config_wasm_multi_value_set(self.inner, opt); } // setWasmMultiMemory configures whether the wasm multi memory proposal is enabled pub fn setWasmMultiMemory(self: *Config, opt: bool) void { - wasmtime_config_wasm_multi_memory_set(self.inner, opt) + wasmtime_config_wasm_multi_memory_set(self.inner, opt); } // setWasmMemory64 configures whether the wasm memory64 proposal is enabled pub fn setWasmMemory64(self: *Config, opt: bool) void { - wasmtime_config_wasm_memory64_set(self.inner, opt) + wasmtime_config_wasm_memory64_set(self.inner, opt); } // setConsumFuel configures whether fuel is enabled pub fn setConsumeFuel(self: *Config, opt: bool) void { - wasmtime_config_consume_fuel_set(self.inner, opt) + wasmtime_config_consume_fuel_set(self.inner, opt); } // setStrategy configures what compilation strategy is used to compile wasm code pub fn setStrategy(self: *Config, strat: *Strategy) !void { - return wasmtime_config_strategy_set(self.inner, strat) orelse error.ConfigStrategySet + return wasmtime_config_strategy_set(self.inner, strat) orelse Error.ConfigStrategySet; } // setCraneliftDebugVerifier configures whether the cranelift debug verifier will be active when // cranelift is used to compile wasm code. pub fn setCraneliftDebugVerifier(self: *Config, opt: bool) void { - wasmtime_config_cranelift_debug_verifier_set(self.inner, opt) + wasmtime_config_cranelift_debug_verifier_set(self.inner, opt); } // setCraneliftOptLevel configures the cranelift optimization level for generated code pub fn setCraneLiftOptLevel(self: *Config, level: *OptLevel) void { - wasmtime_config_cranelift_opt_level_set(self.inner, level) orelse error.ConfigOptLevelSet + wasmtime_config_cranelift_opt_level_set(self.inner, level) orelse Error.ConfigOptLevelSet; } // setProfiler configures what profiler strategy to use for generated code pub fn setProfiler(self: *Config, profiler: *ProfilerStrategy) !void { - return wasmtime_config_profiler_set(self.inner, profiler) orelse error.ConfigProfilerStrategySet + return wasmtime_config_profiler_set(self.inner, profiler) orelse Error.ConfigProfilerStrategySet; } // cacheConfigLoadDefault enables compiled code caching for this `Config` using the default settings @@ -160,8 +159,8 @@ pub const Config = struct { // // For more information about caching see // https://bytecodealliance.github.io/wasmtime/cli-cache.html - pub fn cacheConfigLoadDefault() !void { - return wasmtime_config_cache_config_load(self.inner, null) orelse error.ConfigLoadDefault; + pub fn cacheConfigLoadDefault(self: *Config) !void { + return wasmtime_config_cache_config_load(self.inner, null) orelse Error.ConfigLoadDefault; } // cacheConfigLoad enables compiled code caching for this `Config` using the settings specified @@ -169,7 +168,7 @@ pub const Config = struct { // // For more information about caching and configuration options see // https://bytecodealliance.github.io/wasmtime/cli-cache.html - pub fn cacheConfigLoad(path: []const u8) !void { + pub fn cacheConfigLoad(self: *Config, path: []const u8) !void { return wasmtime_config_cache_config_load(self.inner, path); } diff --git a/src/engine.zig b/src/engine.zig index d9fbe38..fc93591 100644 --- a/src/engine.zig +++ b/src/engine.zig @@ -1,6 +1,6 @@ const std = @import("std"); -pub const config = @import("./config.zig"); +pub const Config = @import("./config.zig").Config; pub const wasm = @import("wasm"); @@ -13,24 +13,24 @@ pub const Engine = struct { /// TODO: decide: call function "init" or "new"? /// init creates a new `Engine` with default configuration. - pub fn init() !*Engine { + pub fn init() !Engine { return Engine { - .inner = try wasm.Engine.init(); - } + .inner = try wasm.Engine.init(), + }; } // withConfig creates a new `Engine` with the `Config` provided // // Note that once a `Config` is passed to this method it cannot be used again. - pub fn withConfig(config: *config.Config) !*Engine { + pub fn withConfig(config: *Config) !Engine { return Engine { - .inner = try wasm.Engine.withConfig(config); - } + .inner = try wasm.Engine.withConfig(config.inner), + }; } /// Frees the resources of the `Engine` pub fn deinit(self: *Engine) void { - self.inner.deinit(self.inner); + self.inner.deinit(); } /// IncrementEpoch will increase the current epoch number by 1 within the @@ -41,4 +41,17 @@ pub const Engine = struct { } extern "c" fn wasmtime_engine_increment_epoch(*wasm.Engine) void; -}; \ No newline at end of file +}; + +test "withConfig" { + + const o = Config.Options { + .debugInfo = true, + }; + + var c: Config = try Config.init(o); + + var engine = try Engine.withConfig(&c); + defer engine.deinit(); + +} \ No newline at end of file diff --git a/src/error.zig b/src/error.zig new file mode 100644 index 0000000..dbc2928 --- /dev/null +++ b/src/error.zig @@ -0,0 +1,21 @@ +// @TODO: Split these up into own error sets +pub const Error = error{ + /// Failed to initialize a `Config` + ConfigInit, + ConfigStrategySet, + ConfigOptLevelSet, + ConfigProfilerStrategySet, + ConfigLoadDefault, + /// Failed to initialize an `Engine` (i.e. invalid config) + EngineInit, + /// Failed to initialize a `Store` + StoreInit, + StoreContext, + /// Failed to initialize a `Module` + ModuleInit, + /// Failed to create a wasm function based on + /// the given `Store` and functype + FuncInit, + /// Failed to initialize a new `Instance` + InstanceInit, +}; \ No newline at end of file diff --git a/src/main.zig b/src/main.zig index b523a4e..7799f55 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,350 +1,11 @@ const std = @import("std"); const testing = std.testing; -const meta = std.meta; -const trait = std.meta.trait; const log = std.log.scoped(.wasmtime_zig); pub const wasm = @import("wasm"); -// Re-exports -pub const ByteVec = wasm.ByteVec; -pub const NameVec = wasm.NameVec; -pub const ValVec = wasm.ValVec; -pub const Value = wasm.Value; -pub const Extern = wasm.Extern; -pub const ExternVec = wasm.ExternVec; -pub const Engine = wasm.Engine; -pub const Store = wasm.Store; -pub const Error = wasm.Error; -pub const Trap = wasm.Trap; -pub const Memory = wasm.Memory; -pub const MemoryType = wasm.MemoryType; -pub const WasiInstance = wasm.WasiInstance; -pub const WasiConfig = wasm.WasiConfig; - -// Helpers -extern "c" fn wasmtime_wat2wasm(wat: *ByteVec, wasm: *ByteVec) ?*WasmError; - -pub const WasmError = opaque { - /// Gets the error message - pub fn getMessage(self: *WasmError) *ByteVec { - var bytes: ?*ByteVec = null; - wasmtime_error_message(self, &bytes); - return bytes.?; - } - - pub fn deinit(self: *WasmError) void { - wasmtime_error_delete(self); - } - - extern "c" fn wasmtime_error_message(*const WasmError, *?*ByteVec) void; - extern "c" fn wasmtime_error_delete(*WasmError) void; -}; - -pub const Config = struct { - inner: *wasm.Config, - - const Options = struct { - interruptable: bool = false, - }; - - pub fn init(options: Options) !Config { - var config = Config{ - .inner = try wasm.Config.init(), - }; - if (options.interruptable) { - config.setInterruptable(true); - } - return config; - } - - pub fn setInterruptable(self: Config, opt: bool) void { - wasmtime_config_interruptable_set(self.inner, opt); - } - - extern "c" fn wasmtime_config_interruptable_set(*wasm.Config, bool) void; -}; - -pub const Module = struct { - inner: *wasm.Module, - - /// Initializes a new `Module` using the supplied engine and wasm bytecode - pub fn initFromWasm(engine: *Engine, _wasm: []const u8) !Module { - var wasm_bytes = ByteVec.initWithCapacity(_wasm.len); - defer wasm_bytes.deinit(); - - var i: usize = 0; - var ptr = wasm_bytes.data; - while (i < _wasm.len) : (i += 1) { - ptr.* = _wasm[i]; - ptr += 1; - } - - var module = Module{ - .inner = try Module.initInner(engine, &wasm_bytes), - }; - return module; - } - - /// Initializes a new `Module` by first converting the given wat format - /// into wasm bytecode. - pub fn initFromWat(engine: *Engine, wat: []const u8) !Module { - var wat_bytes = ByteVec.initWithCapacity(wat.len); - defer wat_bytes.deinit(); - - var i: usize = 0; - var ptr = wat_bytes.data; - while (i < wat.len) : (i += 1) { - ptr.* = wat[i]; - ptr += 1; - } - - var wasm_bytes: ByteVec = undefined; - const err = wasmtime_wat2wasm(&wat_bytes, &wasm_bytes); - defer if (err == null) wasm_bytes.deinit(); - - if (err) |e| { - defer e.deinit(); - var msg = e.getMessage(); - defer msg.deinit(); - - log.err("unexpected error occurred: '{s}'", .{msg.toSlice()}); - return Error.ModuleInit; - } - - var module = Module{ - .inner = try Module.initInner(engine, &wasm_bytes), - }; - return module; - } - - fn initInner(engine: *Engine, wasm_bytes: *ByteVec) !*wasm.Module { - var inner: ?*wasm.Module = undefined; - const err = wasmtime_module_new(engine, wasm_bytes, &inner); - - if (err) |e| { - defer e.deinit(); - var msg = e.getMessage(); - defer msg.deinit(); - - log.err("unexpected error occurred: '{s}'", .{msg.toSlice()}); - return Error.ModuleInit; - } - - return inner.?; - } - - pub fn deinit(self: Module) void { - self.inner.deinit(); - } - - extern "c" fn wasmtime_module_new(*Engine, *ByteVec, *?*wasm.Module) ?*WasmError; -}; - -pub const Func = struct { - inner: *wasm.Func, - - pub const CallError = wasm.Func.CallError; - - pub fn init(store: *Store, callback: anytype) !Func { - return Func{ - .inner = try wasm.Func.init(store, callback), - }; - } - - /// Tries to call the wasm function - /// expects `args` to be tuple of arguments - /// TODO this is a hard-copy of wasm.Func.call implementation. Refactor. - pub fn call(self: Func, comptime ResultType: type, args: anytype) CallError!ResultType { - if (!comptime trait.isTuple(@TypeOf(args))) - @compileError("Expected 'args' to be a tuple, but found type '" ++ @typeName(@TypeOf(args)) ++ "'"); - - const args_len = args.len; - comptime var wasm_args: [args_len]Value = undefined; - inline for (wasm_args) |*arg, i| { - arg.* = switch (@TypeOf(args[i])) { - i32, u32 => .{ .kind = .i32, .of = .{ .i32 = @bitCast(i32, args[i]) } }, - i64, u64 => .{ .kind = .i64, .of = .{ .i64 = @bitCast(i64, args[i]) } }, - f32 => .{ .kind = .f32, .of = .{ .f32 = args[i] } }, - f64 => .{ .kind = .f64, .of = .{ .f64 = args[i] } }, - *Func => .{ .kind = .funcref, .of = .{ .ref = args[i] } }, - *Extern => .{ .kind = .anyref, .of = .{ .ref = args[i] } }, - else => |ty| @compileError("Unsupported argument type '" ++ @typeName(ty) + "'"), - }; - } - - // TODO multiple return values - const result_len: usize = if (ResultType == void) 0 else 1; - if (result_len != wasm_func_result_arity(self.inner)) return CallError.InvalidResultCount; - if (args_len != wasm_func_param_arity(self.inner)) return CallError.InvalidParamCount; - - const final_args = ValVec{ - .size = args_len, - .data = if (args_len == 0) undefined else @ptrCast([*]Value, &wasm_args), - }; - - var trap: ?*Trap = null; - var result_list = ValVec.initWithCapacity(result_len); - defer result_list.deinit(); - const err = wasmtime_func_call(self.inner, &final_args, &result_list, &trap); - - if (err) |e| { - defer e.deinit(); - var msg = e.getMessage(); - defer msg.deinit(); - - log.err("Unable to call function: '{s}'", .{msg.toSlice()}); - return CallError.InnerError; - } - - if (trap) |t| { - t.deinit(); - // TODO handle trap message - log.err("code unexpectedly trapped", .{}); - return CallError.Trap; - } - - if (ResultType == void) return; - - // TODO: Handle multiple returns - const result_ty = result_list.data[0]; - if (!wasm.Func.matchesKind(ResultType, result_ty.kind)) return CallError.InvalidResultType; - - return switch (ResultType) { - i32, u32 => @intCast(ResultType, result_ty.of.i32), - i64, u64 => @intCast(ResultType, result_ty.of.i64), - f32 => result_ty.of.f32, - f64 => result_ty.of.f64, - *Func => @ptrCast(?*Func, result_ty.of.ref).?, - *Extern => @ptrCast(?*Extern, result_ty.of.ref).?, - else => |ty| @compileError("Unsupported result type '" ++ @typeName(ty) ++ "'"), - }; - } - - pub fn deinit(self: Func) void { - self.inner.deinit(); - } - - extern "c" fn wasmtime_func_call(*wasm.Func, *const ValVec, *ValVec, *?*Trap) ?*WasmError; - extern "c" fn wasm_func_result_arity(*const wasm.Func) usize; - extern "c" fn wasm_func_param_arity(*const wasm.Func) usize; -}; - -pub const Instance = struct { - inner: *wasm.Instance, - - /// Initializes a new `Instance` using the given `store` and `mode`. - /// The given slice defined in `import` must match what was initialized - /// using the same `Store` as given. - pub fn init(store: *Store, module: Module, import: []const Func) !Instance { - var trap: ?*Trap = null; - var inner: ?*wasm.Instance = null; - - var imports = ExternVec.initWithCapacity(import.len); - defer imports.deinit(); - - var ptr = imports.data; - for (import) |func| { - ptr.* = func.inner.asExtern(); - ptr += 1; - } - - const err = wasmtime_instance_new(store, module.inner, &imports, &inner, &trap); - - if (err) |e| { - defer e.deinit(); - var msg = e.getMessage(); - defer msg.deinit(); - - log.err("unexpected error occurred: '{s}'", .{msg.toSlice()}); - return Error.InstanceInit; - } - - if (trap) |t| { - defer t.deinit(); - // TODO handle trap message - log.err("code unexpectedly trapped", .{}); - return Error.InstanceInit; - } - - return Instance{ - .inner = inner.?, - }; - } - - pub fn getExportFunc(self: Instance, module: Module, name: []const u8) ?Func { - var inner = self.inner.getExportFunc(module.inner, name) orelse return null; - return Func{ .inner = inner }; - } - - pub fn getExportMem(self: Instance, module: Module, name: []const u8) ?*Memory { - return self.inner.getExportMem(module.inner, name); - } - - pub fn deinit(self: Instance) void { - self.inner.deinit(); - } - - extern "c" fn wasmtime_instance_new(*Store, *const wasm.Module, *const ExternVec, *?*wasm.Instance, *?*Trap) ?*WasmError; -}; - -pub const InterruptHandle = opaque { - /// Creates a new interrupt handle. - /// Must be freed by calling `deinit()` - pub fn init(store: *Store) !*InterruptHandle { - return wasmtime_interrupt_handle_new(store) orelse error.InterruptsNotEnabled; - } - /// Invokes an interrupt in the current wasm module - pub fn interrupt(self: *InterruptHandle) void { - std.debug.print("> INTERRUPTING! \n", .{}); - wasmtime_interrupt_handle_interrupt(self); - } - - pub fn deinit(self: *InterruptHandle) void { - wasmtime_interrupt_handle_delete(self); - } - - extern "c" fn wasmtime_interrupt_handle_interrupt(*InterruptHandle) void; - extern "c" fn wasmtime_interrupt_handle_delete(*InterruptHandle) void; - extern "c" fn wasmtime_interrupt_handle_new(*Store) ?*InterruptHandle; -}; - -pub const Linker = opaque { - pub fn init(store: *Store) !*Linker { - return wasmtime_linker_new(store) orelse error.LinkerInit; - } - - pub fn deinit(self: *Linker) void { - wasmtime_linker_delete(self); - } - - /// Defines a `WasiInstance` for the current `Linker` - pub fn defineWasi(self: *Linker, wasi: *const WasiInstance) ?*WasmError { - return wasmtime_linker_define_wasi(self, wasi); - } - - /// Defines an `Instance` for the current `Linker` object using the given `name` - pub fn defineInstance(self: *Linker, name: *const NameVec, instance: *const wasm.Instance) ?*WasmError { - return wasmtime_linker_define_instance(self, name, instance); - } - - /// Instantiates the `Linker` for the given `Module` and creates a new `Instance` for it - /// Returns a `WasmError` when failed to instantiate - pub fn instantiate( - self: *const Linker, - module: Module, - instance: *?*wasm.Instance, - trap: *?*Trap, - ) ?*WasmError { - return wasmtime_linker_instantiate(self, module.inner, instance, trap); - } - - extern "c" fn wasmtime_linker_new(*Store) ?*Linker; - extern "c" fn wasmtime_linker_delete(*Linker) void; - extern "c" fn wasmtime_linker_define_wasi(*Linker, *const WasiInstance) ?*WasmError; - extern "c" fn wasmtime_linker_define_instance(*Linker, *const NameVec, *const wasm.Instance) ?*WasmError; - extern "c" fn wasmtime_linker_instantiate(*const Linker, *const wasm.Module, *?*wasm.Instance, *?*Trap) ?*WasmError; -}; +pub const Config = @import("config.zig").Config; +pub const Engine = @import("engine.zig").Engine; test "" { testing.refAllDecls(@This()); From c9ba92c925255ab80f374c60bcde25b3318dd7db Mon Sep 17 00:00:00 2001 From: Christoph Voigt Date: Tue, 21 Jun 2022 00:01:11 +0200 Subject: [PATCH 07/17] store --- src/main.zig | 4 ++++ src/store.zig | 40 ++++++++++++++++++---------------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/main.zig b/src/main.zig index 7799f55..981cc17 100644 --- a/src/main.zig +++ b/src/main.zig @@ -6,6 +6,10 @@ pub const wasm = @import("wasm"); pub const Config = @import("config.zig").Config; pub const Engine = @import("engine.zig").Engine; +pub const Store = @import("store.zig").Store; +pub const Module = @import("module.zig").Module; +// pub const Func = @import("func.zig").Func; +// pub const Instance = @import("instance.zig").Instance; test "" { testing.refAllDecls(@This()); diff --git a/src/store.zig b/src/store.zig index 52fef1a..a6e5c8a 100644 --- a/src/store.zig +++ b/src/store.zig @@ -1,48 +1,44 @@ const std = @import("std"); -pub const config = @import("./config.zig"); -pub const error = @import("./error.zig").Error; +pub const Error = @import("./error.zig").Error; +pub const Engine = @import("./engine.zig").Engine; pub const wasm = @import("wasm"); -pub const Context = opaque {} +pub const Context = opaque {}; // Store is a general group of wasm instances, and many objects // must all be created with and reference the same `Store` pub const Store = struct { inner: *wasm.Store, - engine: *wasmtime.Engine, + engine: *Engine, // init creates a new `Store` from the configuration provided in `engine` - pub fn init(engine: *wasmtime.Engine) !*Store { + pub fn init(engine: *Engine) !Store { return Store { - .inner = try wasm.Store.init(); - } + .inner = try wasm.Store.init(engine.inner), + .engine = engine, + }; } pub fn deinit(self: *Store) void { - self.inner.deinit(self.inner); + self.inner.deinit(); } - pub fn context() *Context{ - return wasmtime_store_context(self.inner) orelse error.StoreContext + pub fn context(self: *Store) *Context{ + return wasmtime_store_context(self.inner) orelse Error.StoreContext; } - pub fn setEpochDeadline(self: *Store, deadline: u64) { - wasmtime_context_set_epoch_deadline(self.context(), deadline) + pub fn setEpochDeadline(self: *Store, deadline: u64) void { + wasmtime_context_set_epoch_deadline(self.context(), deadline); } - // not imlemented - pub fn fuelConsumed() (uint64, bool) // zig multiple return values? - pub fn addFuel(fuel: u64) !void {} - pub fn consumeFuel(fuel: u64) !u64 {} - extern "c" fn wasmtime_store_context(*Store) *Context; - extern "c" fn wasmtime_context_set_epoch_deadline(*Context, u64); + extern "c" fn wasmtime_context_set_epoch_deadline(*Context, u64) void; // not imlemented - extern "c" fn wasmtime_context_fuel_consumed(*Context, u64) // ?? - extern "c" fn wasmtime_context_add_fuel(*Context, u64) void - extern "c" fn wasmtime_context_consume_fuel(*Context, u64, u64) u64 -} + // extern "c" fn wasmtime_context_fuel_consumed(*Context, u64) c_int // ?? + // extern "c" fn wasmtime_context_add_fuel(*Context, u64) void + // extern "c" fn wasmtime_context_consume_fuel(*Context, u64, u64) u64 +}; From 68984d9d9bd96e6560b2a8ba8e3aac0e14bfecc1 Mon Sep 17 00:00:00 2001 From: Christoph Voigt Date: Fri, 24 Jun 2022 13:37:24 +0200 Subject: [PATCH 08/17] wat2wasm --- src/error.zig | 3 +- src/wat2wasm.zig | 109 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 src/wat2wasm.zig diff --git a/src/error.zig b/src/error.zig index dbc2928..e7fcc83 100644 --- a/src/error.zig +++ b/src/error.zig @@ -13,9 +13,10 @@ pub const Error = error{ StoreContext, /// Failed to initialize a `Module` ModuleInit, + ModuleWat2Wasm, /// Failed to create a wasm function based on /// the given `Store` and functype FuncInit, /// Failed to initialize a new `Instance` InstanceInit, -}; \ No newline at end of file +}; diff --git a/src/wat2wasm.zig b/src/wat2wasm.zig new file mode 100644 index 0000000..2ec7b53 --- /dev/null +++ b/src/wat2wasm.zig @@ -0,0 +1,109 @@ +const std = @import("std"); +const testing = std.testing; +const log = std.log.scoped(.wasmtime_zig); + +pub const er = @import("./error.zig"); +pub const Error = er.Error; + +// pub const wasm = @import(".gyro/wasm-zig-zigwasm-github.com-3a96556d/pkg/src/main.zig"); +pub const wasm = @import(".gyro/wasm-zig-zigwasm-github.com-3a96556d/pkg/src/main.zig"); + +pub const ByteVec = wasm.ByteVec; + +pub const WasmError = opaque { + /// Gets the error message + pub fn getMessage(self: *WasmError) ByteVec { + + // log.info("self (*WasmError): {s}", .{ self }); + + // log.info("> Initializing bytes...", .{ }); + var bytes: ByteVec = ByteVec.initWithCapacity(0); + // log.info("bytes: {s}", .{ bytes }); + // log.info("&bytes: {s}", .{ &bytes }); + // log.info("> Calling wasmtime_error_message(self, &bytes)...", .{ }); + wasmtime_error_message(self, &bytes); + // log.info("self (*WasmError): {s}", .{ self }); + // log.info("bytes: {s}", .{ bytes }); + // log.info("&bytes: {s}", .{ &bytes }); + // log.info("bytes.toSlice(): {s}", .{ bytes.toSlice() }); + // log.info("&bytes.toSlice(): {s}", .{ &bytes.toSlice() }); + // std.debug.print("\nbytes WasmError: {s}\n", .{ bytes.?.toSlice() }); + // std.debug.print("\nbytes WasmError: {s}\n", .{ &bytes.?.toSlice() }); + + // var ret: ByteVec = bytes.?; + + // log.info("\nreturning bytes WasmError: {s}\n", .{ bytes }); + + + // log.info("-------------------------------------------", .{ }); + return bytes; + // return @ptrCast(*ByteVec, bytes.?); + } + + pub fn deinit(self: *WasmError) void { + // std.debug.print("\ndeinit() WasmError: {s}\n", .{ self }); + wasmtime_error_delete(self); + } + + extern "c" fn wasmtime_error_message(*const WasmError, *ByteVec) void; + extern "c" fn wasmtime_error_delete(*WasmError) void; +}; + +pub const Convert = struct { + // Wat2Wasm converts the text format of WebAssembly to the binary format. + // + // Takes the text format in-memory as input, and returns either the binary + // encoding of the text format or an error if parsing fails. + pub fn wat2wasm(wat: []const u8) !ByteVec { + + var wat_bytes = ByteVec.initWithCapacity(wat.len); + defer wat_bytes.deinit(); + + var retVec: ByteVec = undefined; + + // log.info("\nwat: {s}\n", .{wat}); + // log.info("wat.len: {d}\n", .{wat.len}); + // log.info("&wat: {s}\n", .{&wat}); + // log.info("wat_bytes.size: {d}\n", .{wat_bytes.size}); + + const err = wasmtime_wat2wasm(wat.ptr, wat.len, &retVec); + defer if (err == null) retVec.deinit(); + + if (err) |e| { + defer e.deinit(); + var msg = e.getMessage(); + // log.info("Message &msg.toSlice(): {s}\n", .{ &msg.toSlice() }); + // log.info("Message msg.toSlice(): {s}\n", .{ msg.toSlice() }); + // defer msg.?.deinit(); + defer msg.deinit(); + + log.err("unexpected error occurred: '{s}'", .{msg.toSlice()}); + + return Error.ModuleWat2Wasm; + } + + return retVec; + } + + extern "c" fn wasmtime_wat2wasm(wat: [*]const u8, wat_len: usize, retVec: *ByteVec) ?*WasmError; +}; + +test "wat2wasm" { + + const wasm_data1 = try Convert.wat2wasm("(module)"); + // Return value should be of type ByteVec + try testing.expectEqual(@TypeOf(wasm_data1), @TypeOf(ByteVec.initWithCapacity(0))); + + // Return value should be len == 8 + try testing.expectEqual(wasm_data1.toSlice().len, 8); + + // Converting wat "____" to wasm should fail + var v = Convert.wat2wasm("asd__") catch |err| { + // log.info("Am I called?: {s}\n", .{ err }); + try testing.expect(@TypeOf(err) != @TypeOf(ByteVec.initWithCapacity(0))); + // log.info("Am I called?: {s}\n", .{ err }); + return; + }; + _ = v; + +} \ No newline at end of file From a1f0619714748b576cd660b738cc5a134eda6953 Mon Sep 17 00:00:00 2001 From: Christoph Voigt Date: Sun, 3 Jul 2022 11:46:40 +0200 Subject: [PATCH 09/17] update to 0.38.1 --- ci/linux_ci | 2 +- ci/macos_ci | 2 +- ci/win_ci | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/linux_ci b/ci/linux_ci index e79387b..aeaf6ec 100755 --- a/ci/linux_ci +++ b/ci/linux_ci @@ -13,7 +13,7 @@ ZIG_VERSION=$(curl -s ${ZIG_SRC} | jq -r '.master.version') ZIG="zig-${OS_ARCH}-$ZIG_VERSION" ZIG_MASTER=$(curl -s ${ZIG_SRC} | jq -r --arg ARCH_OS "$ARCH_OS" '.master[$ARCH_OS].tarball') -WASMTIME_VERSION="v0.37.0" +WASMTIME_VERSION="v0.38.1" WASMTIME="wasmtime-$WASMTIME_VERSION-${ARCH_OS}-c-api" GYRO_VERSION="0.7.0" GYRO="gyro-$GYRO_VERSION-${OS_ARCH}" diff --git a/ci/macos_ci b/ci/macos_ci index b3abf30..dbac8c8 100755 --- a/ci/macos_ci +++ b/ci/macos_ci @@ -13,7 +13,7 @@ ZIG_VERSION=$(curl -s ${ZIG_SRC} | jq -r '.master.version') ZIG="zig-${OS_ARCH}-$ZIG_VERSION" ZIG_MASTER=$(curl -s ${ZIG_SRC} | jq -r --arg ARCH_OS "$ARCH_OS" '.master[$ARCH_OS].tarball') -WASMTIME_VERSION="v0.37.0" +WASMTIME_VERSION="v0.38.1" WASMTIME="wasmtime-$WASMTIME_VERSION-x86_64-macos-c-api" GYRO_VERSION="0.7.0" GYRO="gyro-$GYRO_VERSION-macos-x86_64" diff --git a/ci/win_ci b/ci/win_ci index 33fd355..00ab3b5 100755 --- a/ci/win_ci +++ b/ci/win_ci @@ -13,7 +13,7 @@ ZIG_VERSION=$(curl -s ${ZIG_SRC} | jq -r '.master.version') ZIG="zig-${OS_ARCH}-$ZIG_VERSION" ZIG_MASTER=$(curl -s ${ZIG_SRC} | jq -r --arg ARCH_OS "$ARCH_OS" '.master[$ARCH_OS].tarball') -WASMTIME_VERSION="v0.37.0" +WASMTIME_VERSION="v0.38.1" WASMTIME="wasmtime-$WASMTIME_VERSION-${ARCH_OS}-c-api" GYRO_VERSION="0.7.0" GYRO="gyro-$GYRO_VERSION-${OS_ARCH}" From 0a9f6af0235e08ce1651b81adc85e7bee06d1d75 Mon Sep 17 00:00:00 2001 From: Christoph Voigt Date: Sun, 3 Jul 2022 11:49:01 +0200 Subject: [PATCH 10/17] add wat2wasm --- examples/wat2wasm.zig | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 examples/wat2wasm.zig diff --git a/examples/wat2wasm.zig b/examples/wat2wasm.zig new file mode 100644 index 0000000..0081508 --- /dev/null +++ b/examples/wat2wasm.zig @@ -0,0 +1,32 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const wasmtime = @import("wasmtime"); +const fs = std.fs; +const ga = std.heap.c_allocator; +const Allocator = std.mem.Allocator; +const unicode = @import("std").unicode; + +const log = std.log.scoped(.wasmtime_zig); + +pub fn main() !void { + log.err("Starting...", .{ }); + const wat_path = if (builtin.os.tag == .windows) "examples\\simple.wat" else "examples/simple.wat"; + const wat_file = try fs.cwd().openFile(wat_path, .{}); + const wat = try wat_file.readToEndAlloc(ga, std.math.maxInt(u64)); + log.err("Read wat:\n{s}", .{ wat }); + defer ga.free(wat); + + const wasm = try wasmtime.Convert.wat2wasm(wat); + const wasm_slice = wasm.toSlice(); + log.err("Converted wasm:\n{s}", .{ wasm_slice }); + + const dir = try std.fs.cwd().openDir(".", .{ .iterate = true }); + var out = try dir.createFile("output.wasm", .{}); + defer out.close(); + if (std.unicode.utf8ValidateSlice(wasm_slice)) { + try out.writeAll(wasm_slice); + log.err("File written.", .{ }); + } else { + log.err("No valid utf-8", .{}); + } +} \ No newline at end of file From ae61f398026dd95ae6e5dbd5735b52dce4a2b6a7 Mon Sep 17 00:00:00 2001 From: Christoph Voigt Date: Mon, 4 Jul 2022 21:22:53 +0200 Subject: [PATCH 11/17] add module --- src/module.zig | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 src/module.zig diff --git a/src/module.zig b/src/module.zig new file mode 100644 index 0000000..14d08c5 --- /dev/null +++ b/src/module.zig @@ -0,0 +1,76 @@ +const std = @import("std"); + +pub const Config = @import("./config.zig").Config; +pub const Error = @import("./error.zig").Error; +pub const Engine = @import("engine.zig").Engine; +pub const Store = @import("./store.zig").Store; +pub const Convert = @import("./utils.zig").Convert; +pub const WasmError = @import("./utils.zig").WasmError; + +pub const wasm = @import("wasm"); + +const log = std.log.scoped(.wasmtime_zig); + +pub const ByteVec = wasm.ByteVec; + +pub const Module = struct { + inner: *wasm.Module, + + /// Initializes a new `Module` by first converting the given wat format + /// into wasm bytecode. + pub fn initFromWat(engine: *Engine, wat: []const u8) !Module { + + var wasm_bytes = try Convert.wat2wasm(wat); + // defer wasm_bytes.deinit(); + + var module = Module{ + .inner = try Module.initInner(engine, &wasm_bytes), + }; + log.err("Returning module: {s}", .{ &module }); + log.err("Returning moduleT: {s}", .{ @TypeOf(&module) }); + return module; + } + + fn initInner(engine: *Engine, wasm_bytes: *ByteVec) !*wasm.Module { + var inner: ?*wasm.Module = undefined; + + // /** + // * \brief Compiles a WebAssembly binary into a #wasmtime_module_t + // * + // * This function will compile a WebAssembly binary into an owned #wasm_module_t. + // * This performs the same as #wasm_module_new except that it returns a + // * #wasmtime_error_t type to get richer error information. + // * + // * On success the returned #wasmtime_error_t is `NULL` and the `ret` pointer is + // * filled in with a #wasm_module_t. On failure the #wasmtime_error_t is + // * non-`NULL` and the `ret` pointer is unmodified. + // * + // * This function does not take ownership of any of its arguments, but the + // * returned error and module are owned by the caller. + // */ + const err = wasmtime_module_new( // WASM_API_EXTERN wasmtime_error_t *wasmtime_module_new( + engine.inner, // wasm_engine_t *engine, + wasm_bytes.data, // const uint8_t *wasm, + wasm_bytes.size, // size_t wasm_len, + &inner.? // wasmtime_module_t **ret + ); + + if (err) |e| { + defer e.deinit(); + var msg = e.getMessage(); + defer msg.deinit(); + + log.err("unexpected error occurred: '{s}'", .{msg.toSlice()}); + return Error.ModuleInit; + } + + return inner.?; + } + + pub fn deinit(self: Module) void { + log.err("self: {s} ({s})", .{ &self, @TypeOf(&self) }); + self.inner.deinit(); + } + + extern "c" fn wasmtime_module_new(*wasm.Engine, [*]const u8, usize, **wasm.Module) ?*WasmError; +}; \ No newline at end of file From eaed7916ae3a8b10e74d5b69e57fdaa96a91fc00 Mon Sep 17 00:00:00 2001 From: Christoph Voigt Date: Mon, 4 Jul 2022 21:30:33 +0200 Subject: [PATCH 12/17] fmt module --- src/module.zig | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/module.zig b/src/module.zig index 14d08c5..699e190 100644 --- a/src/module.zig +++ b/src/module.zig @@ -19,15 +19,13 @@ pub const Module = struct { /// Initializes a new `Module` by first converting the given wat format /// into wasm bytecode. pub fn initFromWat(engine: *Engine, wat: []const u8) !Module { - var wasm_bytes = try Convert.wat2wasm(wat); // defer wasm_bytes.deinit(); var module = Module{ .inner = try Module.initInner(engine, &wasm_bytes), }; - log.err("Returning module: {s}", .{ &module }); - log.err("Returning moduleT: {s}", .{ @TypeOf(&module) }); + return module; } @@ -49,10 +47,10 @@ pub const Module = struct { // * returned error and module are owned by the caller. // */ const err = wasmtime_module_new( // WASM_API_EXTERN wasmtime_error_t *wasmtime_module_new( - engine.inner, // wasm_engine_t *engine, - wasm_bytes.data, // const uint8_t *wasm, - wasm_bytes.size, // size_t wasm_len, - &inner.? // wasmtime_module_t **ret + engine.inner, // wasm_engine_t *engine, + wasm_bytes.data, // const uint8_t *wasm, + wasm_bytes.size, // size_t wasm_len, + &inner.? // wasmtime_module_t **ret ); if (err) |e| { @@ -73,4 +71,4 @@ pub const Module = struct { } extern "c" fn wasmtime_module_new(*wasm.Engine, [*]const u8, usize, **wasm.Module) ?*WasmError; -}; \ No newline at end of file +}; From 84deec9da4c9b8b4ade2088738be5c427dcbacb7 Mon Sep 17 00:00:00 2001 From: Christoph Voigt Date: Tue, 5 Jul 2022 08:24:29 +0200 Subject: [PATCH 13/17] cleanup --- src/config.zig | 48 +++++++++++++++++++++++--------- src/engine.zig | 10 +++---- src/main.zig | 1 + src/slab.zig | 6 ++-- src/store.zig | 4 +-- src/{wat2wasm.zig => utils.zig} | 49 ++++----------------------------- 6 files changed, 49 insertions(+), 69 deletions(-) rename src/{wat2wasm.zig => utils.zig} (52%) diff --git a/src/config.zig b/src/config.zig index a3fc232..cb67d61 100644 --- a/src/config.zig +++ b/src/config.zig @@ -68,18 +68,40 @@ pub const Config = struct { .inner = try wasm.Config.init(), }; - if (options.debugInfo) { config.setDebugInfo(true); } - if (options.wasmThreads) { config.setWasmThreads(true); } - if (options.wasmReferenceTypes) { config.setWasmReferenceTypes(true); } - if (options.wasmSIMD) { config.setWasmSIMD(true); } - if (options.wasmBulkMemory) { config.setWasmBulkMemory(true); } - if (options.wasmMultiValue) { config.setWasmMultiValue(true); } - if (options.wasmMultiMemory) { config.setWasmMultiMemory(true); } - if (options.wasmMemory64) { config.setWasmMemory64(true); } - if (options.consumeFuel) { config.setConsumeFuel(true); } - if (options.craneliftDebugVerifier) { config.setCraneliftDebugVerifier(true); } + if (options.debugInfo) { + config.setDebugInfo(true); + } + if (options.wasmThreads) { + config.setWasmThreads(true); + } + if (options.wasmReferenceTypes) { + config.setWasmReferenceTypes(true); + } + if (options.wasmSIMD) { + config.setWasmSIMD(true); + } + if (options.wasmBulkMemory) { + config.setWasmBulkMemory(true); + } + if (options.wasmMultiValue) { + config.setWasmMultiValue(true); + } + if (options.wasmMultiMemory) { + config.setWasmMultiMemory(true); + } + if (options.wasmMemory64) { + config.setWasmMemory64(true); + } + if (options.consumeFuel) { + config.setConsumeFuel(true); + } + if (options.craneliftDebugVerifier) { + config.setCraneliftDebugVerifier(true); + } // if (options.craneLiftOptLevel != undefined) { config.setCraneLiftOptLevel(options.craneLiftOptLevel); } - if (options.epochInterruption) { try config.setEpochInterruption(true);} + if (options.epochInterruption) { + try config.setEpochInterruption(true); + } return config; } @@ -132,7 +154,7 @@ pub const Config = struct { pub fn setConsumeFuel(self: *Config, opt: bool) void { wasmtime_config_consume_fuel_set(self.inner, opt); } - + // setStrategy configures what compilation strategy is used to compile wasm code pub fn setStrategy(self: *Config, strat: *Strategy) !void { return wasmtime_config_strategy_set(self.inner, strat) orelse Error.ConfigStrategySet; @@ -194,4 +216,4 @@ pub const Config = struct { extern "c" fn wasmtime_config_profiler_set(*wasm.Config, *ProfilerStrategy) void; extern "c" fn wasmtime_config_cache_config_load(*wasm.Config, []const u8) void; // CString?? extern "c" fn wasmtime_config_epoch_interruption_set(*wasm.Config, bool) void; -}; \ No newline at end of file +}; diff --git a/src/engine.zig b/src/engine.zig index fc93591..4b71383 100644 --- a/src/engine.zig +++ b/src/engine.zig @@ -14,7 +14,7 @@ pub const Engine = struct { /// TODO: decide: call function "init" or "new"? /// init creates a new `Engine` with default configuration. pub fn init() !Engine { - return Engine { + return Engine{ .inner = try wasm.Engine.init(), }; } @@ -23,7 +23,7 @@ pub const Engine = struct { // // Note that once a `Config` is passed to this method it cannot be used again. pub fn withConfig(config: *Config) !Engine { - return Engine { + return Engine{ .inner = try wasm.Engine.withConfig(config.inner), }; } @@ -44,8 +44,7 @@ pub const Engine = struct { }; test "withConfig" { - - const o = Config.Options { + const o = Config.Options{ .debugInfo = true, }; @@ -53,5 +52,4 @@ test "withConfig" { var engine = try Engine.withConfig(&c); defer engine.deinit(); - -} \ No newline at end of file +} diff --git a/src/main.zig b/src/main.zig index 981cc17..13cd159 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4,6 +4,7 @@ const log = std.log.scoped(.wasmtime_zig); pub const wasm = @import("wasm"); +pub const Convert = @import("utils.zig").Convert; pub const Config = @import("config.zig").Config; pub const Engine = @import("engine.zig").Engine; pub const Store = @import("store.zig").Store; diff --git a/src/slab.zig b/src/slab.zig index 327a9ee..7953416 100644 --- a/src/slab.zig +++ b/src/slab.zig @@ -8,8 +8,7 @@ pub const Slab = struct { next: u64, pub fn init(allocator: Allocator) Slab { - - const s = Slab { + const s = Slab{ .list = ArrayList(u64).init(allocator), .next = 0, }; @@ -19,7 +18,7 @@ pub const Slab = struct { pub fn allocate(self: *Slab) !u64 { if (self.next == self.list.items.len) { - try self.list.append(self.next+1); + try self.list.append(self.next + 1); } const ret = self.next; self.next = self.list.items[ret]; @@ -32,7 +31,6 @@ pub const Slab = struct { } }; - test "wasmtime.Slab" { var slab = Slab.init(std.heap.c_allocator); diff --git a/src/store.zig b/src/store.zig index a6e5c8a..059b08e 100644 --- a/src/store.zig +++ b/src/store.zig @@ -16,7 +16,7 @@ pub const Store = struct { // init creates a new `Store` from the configuration provided in `engine` pub fn init(engine: *Engine) !Store { - return Store { + return Store{ .inner = try wasm.Store.init(engine.inner), .engine = engine, }; @@ -26,7 +26,7 @@ pub const Store = struct { self.inner.deinit(); } - pub fn context(self: *Store) *Context{ + pub fn context(self: *Store) *Context { return wasmtime_store_context(self.inner) orelse Error.StoreContext; } diff --git a/src/wat2wasm.zig b/src/utils.zig similarity index 52% rename from src/wat2wasm.zig rename to src/utils.zig index 2ec7b53..6360313 100644 --- a/src/wat2wasm.zig +++ b/src/utils.zig @@ -6,42 +6,20 @@ pub const er = @import("./error.zig"); pub const Error = er.Error; // pub const wasm = @import(".gyro/wasm-zig-zigwasm-github.com-3a96556d/pkg/src/main.zig"); -pub const wasm = @import(".gyro/wasm-zig-zigwasm-github.com-3a96556d/pkg/src/main.zig"); +pub const wasm = @import("wasm"); pub const ByteVec = wasm.ByteVec; pub const WasmError = opaque { /// Gets the error message pub fn getMessage(self: *WasmError) ByteVec { - - // log.info("self (*WasmError): {s}", .{ self }); - - // log.info("> Initializing bytes...", .{ }); var bytes: ByteVec = ByteVec.initWithCapacity(0); - // log.info("bytes: {s}", .{ bytes }); - // log.info("&bytes: {s}", .{ &bytes }); - // log.info("> Calling wasmtime_error_message(self, &bytes)...", .{ }); wasmtime_error_message(self, &bytes); - // log.info("self (*WasmError): {s}", .{ self }); - // log.info("bytes: {s}", .{ bytes }); - // log.info("&bytes: {s}", .{ &bytes }); - // log.info("bytes.toSlice(): {s}", .{ bytes.toSlice() }); - // log.info("&bytes.toSlice(): {s}", .{ &bytes.toSlice() }); - // std.debug.print("\nbytes WasmError: {s}\n", .{ bytes.?.toSlice() }); - // std.debug.print("\nbytes WasmError: {s}\n", .{ &bytes.?.toSlice() }); - - // var ret: ByteVec = bytes.?; - - // log.info("\nreturning bytes WasmError: {s}\n", .{ bytes }); - - // log.info("-------------------------------------------", .{ }); return bytes; - // return @ptrCast(*ByteVec, bytes.?); } pub fn deinit(self: *WasmError) void { - // std.debug.print("\ndeinit() WasmError: {s}\n", .{ self }); wasmtime_error_delete(self); } @@ -55,26 +33,14 @@ pub const Convert = struct { // Takes the text format in-memory as input, and returns either the binary // encoding of the text format or an error if parsing fails. pub fn wat2wasm(wat: []const u8) !ByteVec { - - var wat_bytes = ByteVec.initWithCapacity(wat.len); - defer wat_bytes.deinit(); - var retVec: ByteVec = undefined; - // log.info("\nwat: {s}\n", .{wat}); - // log.info("wat.len: {d}\n", .{wat.len}); - // log.info("&wat: {s}\n", .{&wat}); - // log.info("wat_bytes.size: {d}\n", .{wat_bytes.size}); - const err = wasmtime_wat2wasm(wat.ptr, wat.len, &retVec); - defer if (err == null) retVec.deinit(); + // defer if (err == null) retVec.deinit(); if (err) |e| { defer e.deinit(); var msg = e.getMessage(); - // log.info("Message &msg.toSlice(): {s}\n", .{ &msg.toSlice() }); - // log.info("Message msg.toSlice(): {s}\n", .{ msg.toSlice() }); - // defer msg.?.deinit(); defer msg.deinit(); log.err("unexpected error occurred: '{s}'", .{msg.toSlice()}); @@ -89,21 +55,16 @@ pub const Convert = struct { }; test "wat2wasm" { - const wasm_data1 = try Convert.wat2wasm("(module)"); + // Return value should be of type ByteVec try testing.expectEqual(@TypeOf(wasm_data1), @TypeOf(ByteVec.initWithCapacity(0))); - // Return value should be len == 8 try testing.expectEqual(wasm_data1.toSlice().len, 8); - - // Converting wat "____" to wasm should fail + // Converting wat "asd__" to wasm should fail var v = Convert.wat2wasm("asd__") catch |err| { - // log.info("Am I called?: {s}\n", .{ err }); try testing.expect(@TypeOf(err) != @TypeOf(ByteVec.initWithCapacity(0))); - // log.info("Am I called?: {s}\n", .{ err }); return; }; _ = v; - -} \ No newline at end of file +} From a3ed9aaf061579b75f3db38a3335c3e7a34701f9 Mon Sep 17 00:00:00 2001 From: Christoph Voigt Date: Tue, 5 Jul 2022 08:27:01 +0200 Subject: [PATCH 14/17] cleanup --- examples/wat2wasm.zig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/wat2wasm.zig b/examples/wat2wasm.zig index 0081508..0387de7 100644 --- a/examples/wat2wasm.zig +++ b/examples/wat2wasm.zig @@ -9,24 +9,24 @@ const unicode = @import("std").unicode; const log = std.log.scoped(.wasmtime_zig); pub fn main() !void { - log.err("Starting...", .{ }); + log.err("Starting...", .{}); const wat_path = if (builtin.os.tag == .windows) "examples\\simple.wat" else "examples/simple.wat"; const wat_file = try fs.cwd().openFile(wat_path, .{}); const wat = try wat_file.readToEndAlloc(ga, std.math.maxInt(u64)); - log.err("Read wat:\n{s}", .{ wat }); + log.err("Read wat:\n{s}", .{wat}); defer ga.free(wat); const wasm = try wasmtime.Convert.wat2wasm(wat); const wasm_slice = wasm.toSlice(); - log.err("Converted wasm:\n{s}", .{ wasm_slice }); + log.err("Converted wasm:\n{s}", .{wasm_slice}); const dir = try std.fs.cwd().openDir(".", .{ .iterate = true }); var out = try dir.createFile("output.wasm", .{}); defer out.close(); if (std.unicode.utf8ValidateSlice(wasm_slice)) { try out.writeAll(wasm_slice); - log.err("File written.", .{ }); + log.err("File written.", .{}); } else { log.err("No valid utf-8", .{}); } -} \ No newline at end of file +} From cb6d40aa2bfe06dc1f68a8b98327616b9308da75 Mon Sep 17 00:00:00 2001 From: Christoph Voigt Date: Tue, 5 Jul 2022 09:08:38 +0200 Subject: [PATCH 15/17] add func --- src/func.zig | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 src/func.zig diff --git a/src/func.zig b/src/func.zig new file mode 100644 index 0000000..bfee8b0 --- /dev/null +++ b/src/func.zig @@ -0,0 +1,74 @@ +const std = @import("std"); + +pub const Config = @import("./config.zig").Config; +pub const Error = @import("./error.zig").Error; +pub const Engine = @import("engine.zig").Engine; +pub const Store = @import("./store.zig").Store; +pub const Convert = @import("./utils.zig").Convert; +pub const WasmError = @import("./utils.zig").WasmError; + +pub const wasm = @import("wasm"); + +const log = std.log.scoped(.wasmtime_zig); + +pub const ByteVec = wasm.ByteVec; + +pub const Func = struct { + inner: *wasm.Func, + + pub const CallError = wasm.Func.CallError; + + pub fn init(store: *Store, callback: anytype) !Func { + return Func{ + .inner = try wasm.Func.init(store.inner, callback), + }; + } + + pub fn deinit(self: Func) void { + self.inner.deinit(); + } + + // \brief Call a WebAssembly function. + // + // This function is used to invoke a function defined within a store. For + // example this might be used after extracting a function from a + // #wasmtime_instance_t. + // + // \param store the store which owns `func` + // \param func the function to call + // \param args the arguments to the function call + // \param nargs the number of arguments provided + // \param results where to write the results of the function call + // \param nresults the number of results expected + // \param trap where to store a trap, if one happens. + // + // There are three possible return states from this function: + // + // 1. The returned error is non-null. This means `results` + // wasn't written to and `trap` will have `NULL` written to it. This state + // means that programmer error happened when calling the function, for + // example when the size of the arguments/results was wrong, the types of the + // arguments were wrong, or arguments may come from the wrong store. + // 2. The trap pointer is filled in. This means the returned error is `NULL` and + // `results` was not written to. This state means that the function was + // executing but hit a wasm trap while executing. + // 3. The error and trap returned are both `NULL` and `results` are written to. + // This means that the function call succeeded and the specified results were + // produced. + // + // The `trap` pointer cannot be `NULL`. The `args` and `results` pointers may be + // `NULL` if the corresponding length is zero. + // + // Does not take ownership of #wasmtime_val_t arguments. Gives ownership of + // #wasmtime_val_t results. + // + extern "c" fn wasmtime_func_call( + *wasm.Store, // wasmtime_context_t *store, + *wasm.Func, // const wasmtime_func_t *func, + *const wasm.ValVec, // const wasmtime_val_t *args, + usize, // size_t nargs, + *wasm.ValVec, // wasmtime_val_t *results, + usize, // size_t nresults, + *wasm.Trap, // wasm_trap_t **trap + ) ?*WasmError; +}; From c6c923193da08e3634df44814840bbb3c6d9bdec Mon Sep 17 00:00:00 2001 From: Christoph Voigt Date: Tue, 5 Jul 2022 09:09:17 +0200 Subject: [PATCH 16/17] remove logs --- examples/simple_new.zig | 48 +++++++++++++++++++++++++++++++++++++++++ src/module.zig | 1 - 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 examples/simple_new.zig diff --git a/examples/simple_new.zig b/examples/simple_new.zig new file mode 100644 index 0000000..a670cd4 --- /dev/null +++ b/examples/simple_new.zig @@ -0,0 +1,48 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const wasmtime = @import("wasmtime"); +const fs = std.fs; +const ga = std.heap.c_allocator; +const Allocator = std.mem.Allocator; + +const log = std.log.scoped(.wasmtime_zig); + +fn hello() void { + std.debug.print("Calling back...\n", .{}); + std.debug.print("> Hello World!\n", .{}); +} + +pub fn main() !void { + const wasm_path = if (builtin.os.tag == .windows) "examples\\simple.wat" else "examples/simple.wat"; + const wasm_file = try fs.cwd().openFile(wasm_path, .{}); + const wasm = try wasm_file.readToEndAlloc(ga, std.math.maxInt(u64)); + defer ga.free(wasm); + + var engine = try wasmtime.Engine.init(); + defer engine.deinit(); + std.debug.print("Engine initialized...\n", .{}); + + var store = try wasmtime.Store.init(&engine); + defer store.deinit(); + std.debug.print("Store initialized...\n", .{}); + + var module = try wasmtime.Module.initFromWat(&engine, wasm); + defer module.deinit(); + std.debug.print("Wasm module compiled...\n", .{}); + + var func = try wasmtime.Func.init(&store, hello); + std.debug.print("Func callback prepared...\n", .{}); + + _ = func; + + // var instance = try wasmtime.Instance.init(store, module, &.{func}); + // defer instance.deinit(); + // std.debug.print("Instance initialized...\n", .{}); + + // if (instance.getExportFunc(module, "run")) |f| { + // std.debug.print("Calling export...\n", .{}); + // try f.call(void, .{}); + // } else { + // std.debug.print("Export not found...\n", .{}); + // } +} diff --git a/src/module.zig b/src/module.zig index 699e190..bc3de95 100644 --- a/src/module.zig +++ b/src/module.zig @@ -66,7 +66,6 @@ pub const Module = struct { } pub fn deinit(self: Module) void { - log.err("self: {s} ({s})", .{ &self, @TypeOf(&self) }); self.inner.deinit(); } From 529a788f54e980ce99f618855d3a654e2cca164e Mon Sep 17 00:00:00 2001 From: Christoph Voigt Date: Tue, 5 Jul 2022 09:40:55 +0200 Subject: [PATCH 17/17] implement Func.call --- src/func.zig | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/src/func.zig b/src/func.zig index bfee8b0..ea780c1 100644 --- a/src/func.zig +++ b/src/func.zig @@ -11,7 +11,8 @@ pub const wasm = @import("wasm"); const log = std.log.scoped(.wasmtime_zig); -pub const ByteVec = wasm.ByteVec; +pub const ValVec = wasm.ByteVec; +pub const Trap = wasm.Trap; pub const Func = struct { inner: *wasm.Func, @@ -24,6 +25,80 @@ pub const Func = struct { }; } + pub fn call(self: Func, store: *Store, comptime ResultType: type, args: anytype) CallError!ResultType { + if (!comptime trait.isTuple(@TypeOf(args))) + @compileError("Expected 'args' to be a tuple, but found type '" ++ @typeName(@TypeOf(args)) ++ "'"); + + const args_len = args.len; + comptime var wasm_args: [args_len]Value = undefined; + inline for (wasm_args) |*arg, i| { + arg.* = switch (@TypeOf(args[i])) { + i32, u32 => .{ .kind = .i32, .of = .{ .i32 = @bitCast(i32, args[i]) } }, + i64, u64 => .{ .kind = .i64, .of = .{ .i64 = @bitCast(i64, args[i]) } }, + f32 => .{ .kind = .f32, .of = .{ .f32 = args[i] } }, + f64 => .{ .kind = .f64, .of = .{ .f64 = args[i] } }, + *Func => .{ .kind = .funcref, .of = .{ .ref = args[i] } }, + *Extern => .{ .kind = .anyref, .of = .{ .ref = args[i] } }, + else => |ty| @compileError("Unsupported argument type '" ++ @typeName(ty) + "'"), + }; + } + + // TODO multiple return values + const result_len: usize = if (ResultType == void) 0 else 1; + if (result_len != wasm_func_result_arity(self.inner)) return CallError.InvalidResultCount; + if (args_len != wasm_func_param_arity(self.inner)) return CallError.InvalidParamCount; + + const final_args = ValVec{ + .size = args_len, + .data = if (args_len == 0) undefined else @ptrCast([*]Value, &wasm_args), + }; + + var trap: ?*wasm.Trap = null; + var result_list = ValVec.initWithCapacity(result_len); + defer result_list.deinit(); + + const err = wasmtime_func_call(store.inner, // wasmtime_context_t *store, + self.inner, // const wasmtime_func_t *func, + &final_args, // const wasmtime_val_t *args, + final_args.size, // size_t nargs, + &result_list, // wasmtime_val_t *results, + result_list.size, // size_t nresults, + &trap // wasm_trap_t **trap + ); + + if (err) |e| { + defer e.deinit(); + var msg = e.getMessage(); + defer msg.deinit(); + + log.err("Unable to call function: '{s}'", .{msg.toSlice()}); + return CallError.InnerError; + } + + if (trap) |t| { + t.deinit(); + // TODO handle trap message + log.err("code unexpectedly trapped", .{}); + return CallError.Trap; + } + + if (ResultType == void) return; + + // TODO: Handle multiple returns + const result_ty = result_list.data[0]; + if (!wasm.Func.matchesKind(ResultType, result_ty.kind)) return CallError.InvalidResultType; + + return switch (ResultType) { + i32, u32 => @intCast(ResultType, result_ty.of.i32), + i64, u64 => @intCast(ResultType, result_ty.of.i64), + f32 => result_ty.of.f32, + f64 => result_ty.of.f64, + *Func => @ptrCast(?*Func, result_ty.of.ref).?, + *Extern => @ptrCast(?*Extern, result_ty.of.ref).?, + else => |ty| @compileError("Unsupported result type '" ++ @typeName(ty) ++ "'"), + }; + } + pub fn deinit(self: Func) void { self.inner.deinit(); } @@ -71,4 +146,6 @@ pub const Func = struct { usize, // size_t nresults, *wasm.Trap, // wasm_trap_t **trap ) ?*WasmError; + extern "c" fn wasm_func_result_arity(*const wasm.Func) usize; + extern "c" fn wasm_func_param_arity(*const wasm.Func) usize; };