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
-[
](https://github.com/kubkon/wasmtime-zig)
+[
](https://github.com/kubkon/wasmtime-zig)
[
](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..aeaf6ec 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"
-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"
+ARCH="x86_64"
+OS="linux"
+ARCH_OS="${ARCH}-${OS}"
+OS_ARCH="${OS}-${ARCH}"
-wget -nv "https://ziglang.org/builds/$ZIG.tar.xz"
+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.38.1"
+WASMTIME="wasmtime-$WASMTIME_VERSION-${ARCH_OS}-c-api"
+GYRO_VERSION="0.7.0"
+GYRO="gyro-$GYRO_VERSION-${OS_ARCH}"
+
+# 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..dbac8c8 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"
-WASMTIME_VERSION="v0.24.0"
+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.38.1"
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..00ab3b5 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"
-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"
+ARCH="x86_64"
+OS="windows"
+ARCH_OS="${ARCH}-${OS}"
+OS_ARCH="${OS}-${ARCH}"
-curl -L "https://ziglang.org/builds/$ZIG.zip" -o "$ZIG.zip"
+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.38.1"
+WASMTIME="wasmtime-$WASMTIME_VERSION-${ARCH_OS}-c-api"
+GYRO_VERSION="0.7.0"
+GYRO="gyro-$GYRO_VERSION-${OS_ARCH}"
+
+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/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/examples/wat2wasm.zig b/examples/wat2wasm.zig
new file mode 100644
index 0000000..0387de7
--- /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", .{});
+ }
+}
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/config.zig b/src/config.zig
new file mode 100644
index 0000000..cb67d61
--- /dev/null
+++ b/src/config.zig
@@ -0,0 +1,219 @@
+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,
+
+ pub 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 = OptLevel.optLevelNone,
+ 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 != 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);
+ // }
+
+ // 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(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
+ // 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(self: *Config, 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;
+};
diff --git a/src/engine.zig b/src/engine.zig
new file mode 100644
index 0000000..4b71383
--- /dev/null
+++ b/src/engine.zig
@@ -0,0 +1,55 @@
+const std = @import("std");
+
+pub const Config = @import("./config.zig").Config;
+
+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) !Engine {
+ return Engine{
+ .inner = try wasm.Engine.withConfig(config.inner),
+ };
+ }
+
+ /// Frees the resources of the `Engine`
+ pub fn deinit(self: *Engine) void {
+ self.inner.deinit();
+ }
+
+ /// 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;
+};
+
+test "withConfig" {
+ const o = Config.Options{
+ .debugInfo = true,
+ };
+
+ var c: Config = try Config.init(o);
+
+ var engine = try Engine.withConfig(&c);
+ defer engine.deinit();
+}
diff --git a/src/error.zig b/src/error.zig
new file mode 100644
index 0000000..e7fcc83
--- /dev/null
+++ b/src/error.zig
@@ -0,0 +1,22 @@
+// @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,
+ ModuleWat2Wasm,
+ /// Failed to create a wasm function based on
+ /// the given `Store` and functype
+ FuncInit,
+ /// Failed to initialize a new `Instance`
+ InstanceInit,
+};
diff --git a/src/func.zig b/src/func.zig
new file mode 100644
index 0000000..ea780c1
--- /dev/null
+++ b/src/func.zig
@@ -0,0 +1,151 @@
+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 ValVec = wasm.ByteVec;
+pub const Trap = wasm.Trap;
+
+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 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();
+ }
+
+ // \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;
+ extern "c" fn wasm_func_result_arity(*const wasm.Func) usize;
+ extern "c" fn wasm_func_param_arity(*const wasm.Func) usize;
+};
diff --git a/src/main.zig b/src/main.zig
index 996f265..13cd159 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -1,349 +1,16 @@
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 = @intCast(i32, args[i]) } },
- i64, u64 => .{ .kind = .i64, .of = .{ .i64 = @intCast(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(?*c.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 {
- 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 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;
+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/module.zig b/src/module.zig
new file mode 100644
index 0000000..bc3de95
--- /dev/null
+++ b/src/module.zig
@@ -0,0 +1,73 @@
+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),
+ };
+
+ 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 {
+ self.inner.deinit();
+ }
+
+ extern "c" fn wasmtime_module_new(*wasm.Engine, [*]const u8, usize, **wasm.Module) ?*WasmError;
+};
diff --git a/src/slab.zig b/src/slab.zig
new file mode 100644
index 0000000..7953416
--- /dev/null
+++ b/src/slab.zig
@@ -0,0 +1,54 @@
+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..059b08e
--- /dev/null
+++ b/src/store.zig
@@ -0,0 +1,44 @@
+const std = @import("std");
+
+pub const Error = @import("./error.zig").Error;
+pub const Engine = @import("./engine.zig").Engine;
+
+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: *Engine,
+
+ // init creates a new `Store` from the configuration provided in `engine`
+ pub fn init(engine: *Engine) !Store {
+ return Store{
+ .inner = try wasm.Store.init(engine.inner),
+ .engine = engine,
+ };
+ }
+
+ pub fn deinit(self: *Store) void {
+ self.inner.deinit();
+ }
+
+ pub fn context(self: *Store) *Context {
+ return wasmtime_store_context(self.inner) orelse Error.StoreContext;
+ }
+
+ pub fn setEpochDeadline(self: *Store, deadline: u64) void {
+ wasmtime_context_set_epoch_deadline(self.context(), deadline);
+ }
+
+ extern "c" fn wasmtime_store_context(*Store) *Context;
+ extern "c" fn wasmtime_context_set_epoch_deadline(*Context, u64) void;
+
+ // not imlemented
+ // 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
+};
diff --git a/src/utils.zig b/src/utils.zig
new file mode 100644
index 0000000..6360313
--- /dev/null
+++ b/src/utils.zig
@@ -0,0 +1,70 @@
+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("wasm");
+
+pub const ByteVec = wasm.ByteVec;
+
+pub const WasmError = opaque {
+ /// Gets the error message
+ pub fn getMessage(self: *WasmError) ByteVec {
+ var bytes: ByteVec = ByteVec.initWithCapacity(0);
+ 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 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 retVec: ByteVec = undefined;
+
+ 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();
+ 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 "asd__" to wasm should fail
+ var v = Convert.wat2wasm("asd__") catch |err| {
+ try testing.expect(@TypeOf(err) != @TypeOf(ByteVec.initWithCapacity(0)));
+ return;
+ };
+ _ = v;
+}