Initial commit

main
copygirl 9 months ago
commit 6b66acb99e
  1. 11
      .editorconfig
  2. 2
      .gitignore
  3. 6
      README.md
  4. 7
      UNLICENSE.txt
  5. 29
      build.zig
  6. 30
      src/config.zig
  7. 96
      src/diagnostics.zig
  8. 60
      src/engine.zig
  9. 37
      src/error.zig
  10. 135
      src/extern.zig
  11. 308
      src/func.zig
  12. 152
      src/instance.zig
  13. 220
      src/linker.zig
  14. 19
      src/main.zig
  15. 83
      src/memory.zig
  16. 114
      src/module.zig
  17. 107
      src/store.zig
  18. 122
      src/trap.zig
  19. 222
      src/val.zig
  20. 58
      src/vec.zig
  21. 11
      src/wat2wasm.zig

@ -0,0 +1,11 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true

2
.gitignore vendored

@ -0,0 +1,2 @@
/zig-cache/
/zig-out/

@ -0,0 +1,6 @@
# wasmslime
.. is a library providing [Wasmtime] bindings for use with the [Zig] language.
[Wasmtime]: https://wasmtime.dev/
[Zig]: ziglang.org/

@ -0,0 +1,7 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -0,0 +1,29 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const lib = b.addStaticLibrary(.{
.name = "wasmslime",
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = optimize,
});
lib.linkLibC();
lib.linkSystemLibrary("wasmtime");
b.installArtifact(lib);
const main_tests = b.addTest(.{
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = optimize,
});
const run_main_tests = b.addRunArtifact(main_tests);
const test_step = b.step("test", "Run library tests");
test_step.dependOn(&run_main_tests.step);
}

@ -0,0 +1,30 @@
// https://github.com/bytecodealliance/wasmtime/blob/main/crates/c-api/include/wasmtime/config.h
/// Global engine configuration
///
/// This structure represents global configuration used when constructing an
/// `Engine`. Configuration is safe to share between threads. Typically you'll
/// create a config object and immediately pass it into
/// `Engine.initWithConfig`, however.
///
/// For more information about configuration see the Rust documentation as
/// well at https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html.
pub const Config = opaque {
/// Creates a new empty configuration object.
///
/// The object returned is owned by the caller and will need to be deleted
/// with `deinit`. May return `error.ConfigInit` if a configuration object
/// could not be allocated.
pub fn init() !*Config {
return wasm_config_new() orelse error.ConfigInit;
}
pub fn deinit(self: *Config) void {
wasm_config_delete(self);
}
// TODO: More bindings!
extern "c" fn wasm_config_new() ?*Config;
extern "c" fn wasm_config_delete(*Config) void;
};

@ -0,0 +1,96 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const Error = @import("./error.zig").Error;
const Trap = @import("./trap.zig").Trap;
const TrapCode = @import("./trap.zig").TrapCode;
const TrapError = @import("./trap.zig").TrapError;
pub const ExitStatus = c_int;
pub const ErrorUnion = union(enum) {
none: void,
err: ?ExitStatus,
trap: ?TrapCode,
};
pub const Diagnostics = struct {
allocator: Allocator,
debug_print: bool,
data: ErrorUnion = .{ .none = {} },
message: ?[]const u8 = null,
pub fn init(allocator: Allocator, debug_print: bool) !*Diagnostics {
var result = try allocator.create(Diagnostics);
result.* = .{ .allocator = allocator, .debug_print = debug_print };
return result;
}
pub fn clear(self: *Diagnostics) void {
if (self.message) |msg| self.allocator.free(msg);
self.data = .{ .none = {} };
self.message = null;
}
pub fn deinit(self: *Diagnostics) void {
if (self.message) |msg| self.allocator.free(msg);
self.allocator.destroy(self);
}
pub fn handleError(
err: ?*Error,
err_enum: anyerror,
result: anytype,
diag: ?*Diagnostics,
) @TypeOf(err_enum)!@TypeOf(result) {
if (err) |e| {
defer e.deinit();
if (diag) |d| {
var msg = e.getMessage();
defer msg.deinit();
d.data = .{ .err = e.getExitStatus() };
d.message = try d.allocator.alloc(u8, msg.size);
@memcpy(@constCast(d.message.?), msg.data);
if (d.debug_print) std.debug.print("{s}\n", .{d.message.?});
}
return err_enum;
} else {
if (diag) |d| d.clear();
return result;
}
}
pub fn handleTrap(
trap: ?*Trap,
result: anytype,
diag: ?*Diagnostics,
) !@TypeOf(result) {
if (trap) |t| {
defer t.deinit();
const code = t.getTrapCode();
if (diag) |d| {
var msg = t.getMessage();
defer msg.deinit();
d.data = .{ .trap = code };
d.message = try d.allocator.alloc(u8, msg.size);
@memcpy(@constCast(d.message.?), msg.data);
if (d.debug_print) std.debug.print("{s}\n", .{d.message.?});
}
return if (code) |c| c.toError() else TrapError.UnknownTrap;
} else {
if (diag) |d| d.clear();
return result;
}
}
pub fn handleErrorOrTrap(
err: ?*Error,
err_enum: anyerror,
trap: ?*Trap,
result: anytype,
diag: ?*Diagnostics,
) !@TypeOf(result) {
try handleError(err, err_enum, {}, diag);
return try handleTrap(trap, result, diag);
}
};

@ -0,0 +1,60 @@
const Config = @import("./config.zig").Config;
/// Compilation environment and configuration.
///
/// An engine is typically global in a program and contains all the
/// configuration necessary for compiling wasm code. From an engine you'll
/// typically create a `Store`. Engines are created with `initDefault` or
/// `initWithConfig`.
///
/// An engine is safe to share between threads. Multiple stores can be created
/// within the same engine with each store living on a separate thread.
/// Typically you'll create one `Engine` for the lifetime of your program.
///
/// Engines are reference counted internally so `deinit` can be called at any
/// time after a `Store` has been created from one.
pub const Engine = opaque {
/// Creates a new engine with the default configuration.
///
/// The object returned is owned by the caller and will need to be deleted
/// with `deinit`. This may return `error.ModuleInit` if the engine could
/// not be allocated.
pub fn initDefault() !*Engine {
return wasm_engine_new() orelse error.ModuleInit;
}
/// Creates a new engine with the specified configuration.
///
/// This function will take ownership of the configuration specified
/// regardless of the outcome of this function. You do not need to call
/// `Config.deinit` on the argument. The object returned is owned by the
/// caller and will need to be deleted with `deinit`. This may return
/// `error.ModuleInit` if the engine could not be allocated.
pub fn initWithConfig(config: *Config) !*Engine {
return wasm_engine_new_with_config(config) orelse error.ModuleInit;
}
pub fn deinit(self: *Engine) void {
wasm_engine_delete(self);
}
/// Increments the engine-local epoch variable.
///
/// This function will increment the engine's current epoch which can be
/// used to force WebAssembly code to trap if the current epoch goes
/// beyond the `Store` configured epoch deadline.
///
/// This function is safe to call from any thread, and it is also
/// async-signal-safe.
///
/// See also `Config.epoch_interruption_set`.
pub fn increment_epoch(self: *Engine) void {
wasmtime_engine_increment_epoch(self);
}
extern "c" fn wasm_engine_new() ?*Engine;
extern "c" fn wasm_engine_new_with_config(*Config) ?*Engine;
extern "c" fn wasm_engine_delete(*Engine) void;
extern "c" fn wasmtime_engine_increment_epoch(*Engine) void;
};

@ -0,0 +1,37 @@
const Name = @import("./vec.zig").Name;
/// This opaque type represents an error generated by Wasmtime. Errors
/// primarily have an error message associated with them at this time, which
/// you can acquire by calling `format`.
///
/// Errors are safe to share across threads and must be deleted with `deinit`.
pub const Error = opaque {
/// Creates a new error with the provided message.
pub fn init(message: [:0]const u8) *Error {
return wasmtime_error_new(message.ptr);
}
pub fn deinit(self: *Error) void {
wasmtime_error_delete(self);
}
pub fn getMessage(self: *const Error) Name {
var result: Name = undefined;
wasmtime_error_message(self, &result);
return result;
}
/// Attempts to extract a WASI-specific exit status from this error.
/// Returns status if error is a WASI "exit" trap, or `null` otherwise.
pub fn getExitStatus(self: *Error) ?c_int {
var result: c_int = undefined;
const has_status = wasmtime_error_exit_status(self, &result);
return if (has_status) result else null;
}
extern "c" fn wasmtime_error_new([*:0]const u8) *Error;
extern "c" fn wasmtime_error_delete(*Error) void;
extern "c" fn wasmtime_error_message(*const Error, *Name) void;
extern "c" fn wasmtime_error_exit_status(*const Error, *c_int) bool;
// extern "c" fn wasmtime_error_wasm_trace(*const Error, *FrameVec) void;
};

@ -0,0 +1,135 @@
const Store = @import("./store.zig").Store;
const FuncType = @import("./func.zig").FuncType;
const MemoryType = @import("./memory.zig").MemoryType;
// Re-exported for convenience (for now).
pub const Func = @import("./func.zig").Func;
pub const Memory = @import("./memory.zig").Memory;
/// Representation of a table in Wasmtime.
pub const Table = extern struct {
store_id: u64,
index: usize,
};
/// Representation of a global in Wasmtime.
pub const Global = extern struct {
store_id: u64,
index: usize,
};
pub const ExternKind = enum(u8) {
func = 0,
global = 1,
table = 2,
memory = 3,
};
/// Container for different kinds of extern items.
///
/// Note that this structure may contain an owned value, namely `Module`,
/// depending on the context in which this is used. APIs which consume an
/// `Extern` do not take ownership, but APIs that return `Extern` require that
/// `deinit` is called to deallocate the value.
pub const Extern = extern struct {
kind: ExternKind,
of: extern union {
func: Func,
global: Global,
table: Table,
memory: Memory,
},
pub fn deinit(self: *Extern) void {
wasmtime_extern_delete(self);
}
pub fn getType(self: *const Extern, context: *const Store.Context) *ExternType {
return wasmtime_extern_type(context, self);
}
pub fn fromFunc(func: Func) Extern {
return .{ .kind = .func, .of = .{ .func = func } };
}
pub fn asFunc(self: Extern) ?Func {
return if (self.kind == .func) self.of.func else null;
}
pub fn fromMemory(memory: Memory) Extern {
return .{ .kind = .memory, .of = .{ .memory = memory } };
}
pub fn asMemory(self: Extern) ?Memory {
return if (self.kind == .memory) self.of.memory else null;
}
extern "c" fn wasmtime_extern_delete(*Extern) void;
extern "c" fn wasmtime_extern_type(*const Store.Context, *const Extern) *ExternType;
};
// pub const ExternVec = Vec(*Extern, wasm_extern_vec_new_empty, wasm_extern_vec_new_uninitialized, wasm_extern_vec_new, wasm_extern_vec_copy, wasm_extern_vec_delete);
// extern "c" fn wasm_extern_vec_new_empty(*anyopaque, usize) void;
// extern "c" fn wasm_extern_vec_new_uninitialized(*anyopaque, usize) void;
// extern "c" fn wasm_extern_vec_new(*anyopaque, usize, [*]const ?*Extern) void;
// extern "c" fn wasm_extern_vec_copy(*anyopaque, *const anyopaque) void;
// extern "c" fn wasm_extern_vec_delete(*anyopaque) void;
pub const ExternType = opaque {
pub fn copy(self: *const ExternType) *ExternType {
return wasm_externtype_copy(self);
}
pub fn deinit(self: *ExternType) void {
wasm_externtype_delete(self);
}
pub fn getKind(self: *const ExternType) ExternKind {
return wasm_externtype_kind(self);
}
pub fn fromFuncType(func: *FuncType) *ExternType {
return wasm_functype_as_externtype(func);
}
pub fn asFuncType(self: *ExternType) ?*FuncType {
return wasm_externtype_as_functype(self);
}
pub fn fromMemoryType(func: *MemoryType) *ExternType {
return wasm_memorytype_as_externtype(func);
}
pub fn asMemoryType(self: *ExternType) ?*MemoryType {
return wasm_externtype_as_memorytype(self);
}
extern "c" fn wasm_externtype_copy(*const ExternType) *ExternType;
extern "c" fn wasm_externtype_delete(*ExternType) void;
extern "c" fn wasm_externtype_kind(*const ExternType) ExternKind;
extern "c" fn wasm_functype_as_externtype(*FuncType) *ExternType;
// extern "c" fn wasm_tabletype_as_externtype(*TableType) *ExternType;
// extern "c" fn wasm_globaltype_as_externtype(*GlobalType) *ExternType;
extern "c" fn wasm_memorytype_as_externtype(*MemoryType) *ExternType;
extern "c" fn wasm_functype_as_externtype_const(*const FuncType) *const ExternType;
// extern "c" fn wasm_tabletype_as_externtype_const(*const TableType) *const ExternType;
// extern "c" fn wasm_globaltype_as_externtype_const(*const GlobalType) *const ExternType;
extern "c" fn wasm_memorytype_as_externtype_const(*const MemoryType) *const ExternType;
extern "c" fn wasm_externtype_as_functype(*ExternType) ?*FuncType;
// extern "c" fn wasm_externtype_as_tabletype(*ExternType) ?*TableType;
// extern "c" fn wasm_externtype_as_globaltype(*ExternType) ?*GlobalType;
extern "c" fn wasm_externtype_as_memorytype(*ExternType) ?*MemoryType;
extern "c" fn wasm_externtype_as_functype_const(*const ExternType) ?*const FuncType;
// extern "c" fn wasm_externtype_as_tabletype_const(*const ExternType) ?*const TableType;
// extern "c" fn wasm_externtype_as_globaltype_const(*const ExternType) ?*const GlobalType;
extern "c" fn wasm_externtype_as_memorytype_const(*const ExternType) ?*const MemoryType;
};
// pub const ExternTypeVec = Vec(*ExternType, wasm_externtype_vec_new_empty, wasm_externtype_vec_new_uninitialized, wasm_externtype_vec_new, wasm_externtype_vec_copy, wasm_externtype_vec_delete);
// extern "c" fn wasm_externtype_vec_new_empty(*anyopaque, usize) void;
// extern "c" fn wasm_externtype_vec_new_uninitialized(*anyopaque, usize) void;
// extern "c" fn wasm_externtype_vec_new(*anyopaque, usize, [*]const ?*ExternType) void;
// extern "c" fn wasm_externtype_vec_copy(*anyopaque, *const anyopaque) void;
// extern "c" fn wasm_externtype_vec_delete(*anyopaque) void;

@ -0,0 +1,308 @@
const std = @import("std");
const assert = std.debug.assert;
const panic = std.debug.panic;
const Store = @import("./store.zig").Store;
const Diagnostics = @import("./diagnostics.zig").Diagnostics;
const Error = @import("./error.zig").Error;
const Trap = @import("./trap.zig").Trap;
const _extern = @import("./extern.zig");
const Extern = _extern.Extern;
const Memory = _extern.Memory;
const _val = @import("./val.zig");
const Val = _val.Val;
const ValType = _val.ValType;
const ValTypeVec = _val.ValTypeVec;
/// Structure used to learn about the caller of a host-defined function.
///
/// This structure is an argument to `Func.Callback`. Its purpose is to
/// acquire a `Store.Context` pointer to interact with objects, but it can
/// also be used to inspect the state of the caller (such as getting memories
/// and functions) with `getExport`.
pub const Caller = opaque {
/// Returns the store context of the caller object.
pub fn getContext(self: *Caller) *Store.Context {
return wasmtime_caller_context(self);
}
/// Get an exported `Extern` from the caller's context.
/// Returns `error.ExportNotFound` if export is not found.
pub fn getExport(self: *Caller, name: []const u8) !Extern {
var result: Extern = undefined;
return if (wasmtime_caller_export_get(self, name.ptr, name.len, &result)) result else error.ExportNotFound;
}
/// Get an exported `Func` from the caller's context.
/// Returns `error.ExportNotFound` if export is not found.
/// Returns `error.ExportIncorrectType` if the export isn't a `Func`.
pub fn getFunc(self: *Caller, name: []const u8) !Func {
return (try self.getExport(name)).asFunc() orelse error.ExportIncorrectType;
}
/// Get an exported `Memory` from the caller's context.
/// Returns `error.ExportNotFound` if export is not found.
/// Returns `error.ExportIncorrectType` if the export isn't a `Memory`.
pub fn getMemory(self: *Caller, name: []const u8) !Memory {
return (try self.getExport(name)).asMemory() orelse error.ExportIncorrectType;
}
/// Get an exported `Memory`'s data slice from the caller's context.
/// Returns `error.ExportNotFound` if export is not found.
/// Returns `error.ExportIncorrectType` if the export isn't a `Memory`.
pub fn getMemoryData(self: *Caller, name: []const u8) ![]u8 {
return (try self.getMemory(name)).getData(self.getContext());
}
extern "c" fn wasmtime_caller_context(*Caller) *Store.Context;
extern "c" fn wasmtime_caller_export_get(*Caller, [*]const u8, usize, *Extern) bool;
};
/// Representation of a function in Wasmtime.
pub const Func = extern struct {
/// Internal identifier of what store this belongs to, never zero.
store_id: u64,
/// Internal index within the store.
index: usize,
/// Inserts a host-defined function into the `store` provided, which can
/// be used to then instantiate a module with or define within a `Linker`.
/// The returned function can only be used with the specified `store`.
pub fn init(
context: *Store.Context,
func_type: *FuncType,
callback: *const Callback,
env: ?*anyopaque,
env_finalizer: ?*const Finalizer,
) Func {
var result: Func = undefined;
wasmtime_func_new(context, func_type, callback, env, env_finalizer, &result);
return result;
}
pub fn initFromFn(context: *Store.Context, func: anytype) Func {
const func_type = FuncType.fromFn(func);
const wrapped = Wrapped.init(func);
return init(context, func_type, wrapped.callback, wrapped.env, wrapped.env_finalizer);
}
pub const Wrapped = struct {
env: ?*anyopaque,
env_finalizer: ?*const Finalizer,
callback: *const Callback,
pub fn init(func: anytype) Wrapped {
return .{
// Use the function's pointer itself as the "env" variable
// that'll be passed to the callback. This way we can call
// the original function from our custom callback.
.env = @ptrCast(@constCast(&func)),
.env_finalizer = null, // No finalizer necessary.
.callback = &(struct {
const meta = @typeInfo(@TypeOf(func)).Fn;
const ArgsType = std.meta.ArgsTuple(@TypeOf(func));
const ResultType = meta.return_type orelse void;
fn callback(
env: ?*anyopaque,
caller: *Caller,
args: [*]const Val,
num_args: usize,
results: [*]Val,
num_results: usize,
) ?*Trap {
var func_args: ArgsType = undefined;
comptime var offset = 0;
// If the first function parameter accepts a *Caller, pass that in!
if (meta.params[0].type.? == *Caller) {
func_args[0] = caller;
offset = 1;
}
assert(num_args == meta.params.len - offset);
assert(num_results == getNumResultsFromResultType(ResultType));
// Turn the "env" parameter back into a function pointer.
const func_ptr: *const @TypeOf(func) = @ptrCast(env);
// Build argument tuple from the arguments WASM has given us.
inline for (meta.params[offset..], args, offset..) |p, arg, i|
func_args[i] = arg.toValue(p.type.?) catch
panic("Cannot convert {} to {s}", .{ arg, @typeName(p.type.?) });
// Call the function with the arguments, get the results.
const func_results = @call(.auto, func_ptr, func_args);
// Pass the result(s) back to WASM.
if (comptime std.meta.trait.isTuple(ResultType)) {
inline for (func_results, 0..) |res, i|
results[i] = Val.fromValue(res);
} else if (ResultType != void)
results[0] = Val.fromValue(func_results);
return null;
}
}.callback),
};
}
};
pub fn getType(self: *const Func, context: *Store.Context) *FuncType {
return wasmtime_func_type(context, self);
}
pub fn call(
self: *const Func,
context: *const Store.Context,
args: anytype,
comptime TResult: type,
diag: ?*Diagnostics,
) !TResult {
if (!comptime isTupleOrEmpty(@TypeOf(args)))
@compileError("Expected tuple, but found '" ++ @typeName(@TypeOf(args)) ++ "'");
// Set up function arguments.
var args_array: [args.len]Val = undefined;
inline for (&args_array, args) |*val, arg|
val.* = Val.fromValue(arg);
// Set up function results.
const num_results = comptime getNumResultsFromResultType(TResult);
var results_array: [num_results]Val = undefined;
var trap: ?*Trap = null;
const err = wasmtime_func_call(context, self, &args_array, args_array.len, &results_array, results_array.len, &trap);
try Diagnostics.handleErrorOrTrap(err, error.FuncCall, trap, {}, diag);
if (comptime std.meta.trait.isTuple(TResult)) {
var result: TResult = undefined;
inline for (TResult, results_array, result) |T, val, *res| {
defer val.deinit();
res.* = val.toValue(T) catch
panic("Cannot convert {} to {s}", .{ val, @typeName(T) });
}
return result;
} else if (TResult != void) {
defer results_array[0].deinit();
return results_array[0].toValue(TResult) catch
panic("Cannot convert {} to {s}", .{ results_array[0], @typeName(TResult) });
} else return {};
}
pub fn toExtern(self: Func) Extern {
return Extern.fromFunc(self);
}
fn isTupleOrEmpty(comptime T: type) bool {
return switch (@typeInfo(T)) {
.Struct => |s| s.is_tuple or s.fields.len == 0,
else => false,
};
}
fn getNumResultsFromResultType(comptime T: type) usize {
return comptime if (std.meta.trait.isTuple(T))
@typeInfo(T).Struct.fields.len
else if (T != void) 1 else 0;
}
pub const Callback = fn (
env: ?*anyopaque,
caller: *Caller,
args: [*]const Val,
num_args: usize,
results: [*]Val,
num_results: usize,
) ?*Trap;
pub const Finalizer = fn (env: *anyopaque) void;
extern "c" fn wasmtime_func_new(
context: *Store.Context,
func_type: *const FuncType,
callback: *const Callback,
env: ?*anyopaque,
env_finalizer: ?*const Finalizer,
result: *Func,
) void;
extern "c" fn wasmtime_func_type(
context: *const Store.Context,
func: *const Func,
) *FuncType;
extern "c" fn wasmtime_func_call(
context: *const Store.Context,
func: *const Func,
args: [*]const Val,
num_args: usize,
results: [*]Val,
num_results: usize,
trap: *?*Trap,
) ?*Error;
};
/// An opaque object representing the type of a function.
pub const FuncType = opaque {
pub fn init(params: *ValTypeVec, results: *ValTypeVec) *FuncType {
return wasm_functype_new(params, results);
}
pub fn fromFn(func: anytype) *FuncType {
const meta = @typeInfo(@TypeOf(func)).Fn;
comptime var offset = 0;
// If the first function parameter accepts a *Caller, skip it!
if (meta.params.len > 0 and meta.params[0].type.? == *Caller) offset = 1;
var params = ValTypeVec.initUninitialized(meta.params.len - offset);
inline for (meta.params[offset..], 0..) |p, i|
params.data[i] = ValType.fromType(p.type.?);
const ResultType = meta.return_type orelse void;
var results = if (comptime std.meta.trait.isTuple(ResultType)) blk: {
var res = ValTypeVec.initUninitialized(ResultType.len);
inline for (ResultType, res.toSlice()) |T, *r|
r.* = Val.fromValue(T);
break :blk res;
} else if (ResultType != void) blk: {
var res = ValTypeVec.initUninitialized(1);
res.data[0] = ValType.fromType(ResultType);
break :blk res;
} else ValTypeVec.initEmpty(0);
return init(&params, &results);
}
pub fn copy(self: *const FuncType) *FuncType {
return wasm_functype_copy(self);
}
pub fn deinit(self: *FuncType) void {
wasm_functype_delete(self);
}
pub fn getParams(self: *const FuncType) *const ValTypeVec {
return wasm_functype_params(self);
}
pub fn getResults(self: *const FuncType) *const ValTypeVec {
return wasm_functype_results(self);
}
extern "c" fn wasm_functype_new(*ValTypeVec, *ValTypeVec) *FuncType;
extern "c" fn wasm_functype_copy(*const FuncType) *FuncType;
extern "c" fn wasm_functype_delete(*FuncType) void;
extern "c" fn wasm_functype_params(*const FuncType) *const ValTypeVec;
extern "c" fn wasm_functype_results(*const FuncType) *const ValTypeVec;
};
// pub const FuncTypeVec = Vec(*FuncType, wasm_functype_vec_new_empty, wasm_functype_vec_new_uninitialized, wasm_functype_vec_new, wasm_functype_vec_copy, wasm_functype_vec_delete);
// extern "c" fn wasm_functype_vec_new_empty(*anyopaque, usize) void;
// extern "c" fn wasm_functype_vec_new_uninitialized(*anyopaque, usize) void;
// extern "c" fn wasm_functype_vec_new(*anyopaque, usize, [*]const ?*FuncType) void;
// extern "c" fn wasm_functype_vec_copy(*anyopaque, *const anyopaque) void;
// extern "c" fn wasm_functype_vec_delete(*anyopaque) void;

@ -0,0 +1,152 @@
const Engine = @import("./engine.zig").Engine;
const Module = @import("./module.zig").Module;
const Store = @import("./store.zig").Store;
const Diagnostics = @import("./diagnostics.zig").Diagnostics;
const Error = @import("./error.zig").Error;
const Trap = @import("./trap.zig").Trap;
const Name = @import("./vec.zig").Name;
const Vec = @import("./vec.zig").Vec;
const _extern = @import("./extern.zig");
const Extern = _extern.Extern;
const ExternType = _extern.ExternType;
const Func = _extern.Func;
const Memory = _extern.Memory;
/// Representation of a instance in Wasmtime.
///
/// Instances are represented with a 64-bit identifying integer in Wasmtime.
/// They do not have any destructor associated with them. Instances cannot
/// interoperate between `Store` instances and if the wrong instance is passed
/// to the wrong store then it may trigger an assertion to abort the process.
pub const Instance = extern struct {
/// Internal identifier of what store this belongs to, never zero.
store_id: u64,
/// Internal index within the store.
index: usize,
/// This function will instantiate a WebAssembly module with the provided
/// imports, creating a WebAssembly instance. The returned instance can
/// then afterwards be inspected for exports.
///
/// This function requires that `imports` is the same size as the imports
/// that `module` has. Additionally the `imports` array must be 1:1 lined
/// up with the imports of the `module` specified. This is intended to be
/// relatively low level, and `Linker` is provided for a more ergonomic
/// name-based resolution API.
///
/// Note that this function requires that all `imports` specified must be
/// owned by the `Store` represented by the `context` provided as well.
///
/// This function does not take ownership of any of its arguments, but all
/// return values (Error or Trap) are owned by the caller.
pub fn init(context: *Store.Context, module: *const Module, imports: []const Extern, diag: ?*Diagnostics) !Instance {
var result: Instance = undefined;
var trap: ?*Trap = null;
const err = wasmtime_instance_new(context, module, imports.ptr, imports.len, &result, &trap);
return try Diagnostics.handleErrorOrTrap(err, error.InstanceInit, trap, result, diag);
}
/// Get an exported `Extern` by name from this instance.
/// Returns `error.ExportNotFound` if export is not found.
pub fn getExport(self: *const Instance, context: *Store.Context, name: []const u8) !Extern {
var result: Extern = undefined;
return if (wasmtime_instance_export_get(context, self, name.ptr, name.len, &result)) result else error.ExportNotFound;
}
/// Get an exported `Func` by name from this instance.
/// Returns `error.ExportNotFound` if export is not found.
/// Returns `error.ExportIncorrectType` if the export isn't a `Func`.
pub fn getFunc(self: *const Instance, context: *Store.Context, name: []const u8) !Func {
return (try self.getExport(context, name)).asFunc() orelse error.ExportIncorrectType;
}
/// Get an exported `Memory` by name from this instance.
/// Returns `error.ExportNotFound` if export is not found.
/// Returns `error.ExportIncorrectType` if the export isn't a `Memory`.
pub fn getMemory(self: *const Instance, context: *Store.Context, name: []const u8) !Memory {
return (try self.getExport(context, name)).asMemory() orelse error.ExportIncorrectType;
}
/// Get an exported `Memory`'s data slice from this instance.
/// Returns `error.ExportNotFound` if export is not found.
/// Returns `error.ExportIncorrectType` if the export isn't a `Memory`.
pub fn getMemoryData(self: *const Instance, context: *Store.Context, name: []const u8) ![]u8 {
return (try self.getMemory(context, name)).getData(context);
}
/// Get an export by index from an instance, or
/// `error.ExportNotFound` if not found.
pub fn getExportByIndex(self: *const Instance, context: *Store.Context, index: usize) !Extern {
var result: Extern = undefined;
// TODO: Add a way to also return `name`?
// NOTE: Apparently `name` is owned by the store and must be used immediately.
var name: [*]const u8 = null;
var name_len: usize = undefined;
return if (wasmtime_instance_export_nth(context, self, index, &name, &name_len, &result)) result else error.ExportNotFound;
}
extern "c" fn wasmtime_instance_new(*Store.Context, *const Module, [*]const Extern, usize, *Instance, *?*Trap) ?*Error;
extern "c" fn wasmtime_instance_export_get(*Store.Context, *const Instance, [*]const u8, usize, *Extern) bool;
extern "c" fn wasmtime_instance_export_nth(*Store.Context, *const Instance, usize, *[*]const u8, *usize, *Extern) bool;
};
/// An opaque object representing the type of an import.
pub const ImportType = opaque {
/// Returns the module this import is importing from.
pub fn getModule(self: *const ImportType) []const u8 {
return wasm_importtype_module(self).toSlice();
}
/// Returns the name this import is importing from.
///
/// Note that `null` can be returned which means that the import name is
/// not provided. This is for imports with the module linking proposal
/// that only have the module specified.
pub fn getName(self: *const ImportType) ?[]const u8 {
return if (wasm_importtype_name(self)) |name| name.toSlice() else null;
}
/// Returns the type of item this import is importing.
pub fn getType(self: *const ImportType) *const ExternType {
return wasm_importtype_type(self);
}
// extern "c" fn wasm_importtype_new(*Name, *Name, *ExternType) *ImportType;
// extern "c" fn wasm_importtype_copy(*const ImportType) *ImportType;
extern "c" fn wasm_importtype_module(*const ImportType) *const Name;
extern "c" fn wasm_importtype_name(*const ImportType) ?*const Name;
extern "c" fn wasm_importtype_type(*const ImportType) *const ExternType;
};
pub const ImportTypeVec = Vec(*ImportType, wasm_importtype_vec_new_empty, wasm_importtype_vec_new_uninitialized, wasm_importtype_vec_new, wasm_importtype_vec_copy, wasm_importtype_vec_delete);
extern "c" fn wasm_importtype_vec_new_empty(*anyopaque, usize) void;
extern "c" fn wasm_importtype_vec_new_uninitialized(*anyopaque, usize) void;
extern "c" fn wasm_importtype_vec_new(*anyopaque, usize, [*]const ?*ImportType) void;
extern "c" fn wasm_importtype_vec_copy(*anyopaque, *const anyopaque) void;
extern "c" fn wasm_importtype_vec_delete(*anyopaque) void;
/// An opaque object representing the type of an export.
pub const ExportType = opaque {
/// Returns the name of this export.
pub fn getName(self: *const ExportType) ?[]const u8 {
return if (wasm_exporttype_name(self)) |name| name.toSlice() else null;
}
/// Returns the type of this export.
pub fn getType(self: *const ExportType) *const ExternType {
return wasm_exporttype_type(self);
}
// extern "c" fn wasm_exporttype_new(*Name, *Name, *ExternType) *ExportType;
// extern "c" fn wasm_exporttype_copy(*const ExportType) *ExportType;
extern "c" fn wasm_exporttype_name(*const ExportType) ?*const Name;
extern "c" fn wasm_exporttype_type(*const ExportType) *const ExternType;
};
pub const ExportTypeVec = Vec(*ExportType, wasm_exporttype_vec_new_empty, wasm_exporttype_vec_new_uninitialized, wasm_exporttype_vec_new, wasm_exporttype_vec_copy, wasm_exporttype_vec_delete);
extern "c" fn wasm_exporttype_vec_new_empty(*anyopaque, usize) void;
extern "c" fn wasm_exporttype_vec_new_uninitialized(*anyopaque, usize) void;
extern "c" fn wasm_exporttype_vec_new(*anyopaque, usize, [*]const ?*ImportType) void;
extern "c" fn wasm_exporttype_vec_copy(*anyopaque, *const anyopaque) void;
extern "c" fn wasm_exporttype_vec_delete(*anyopaque) void;

@ -0,0 +1,220 @@
const Engine = @import("./engine.zig").Engine;
const Store = @import("./store.zig").Store;
const Module = @import("./module.zig").Module;
const Instance = @import("./instance.zig").Instance;
const Func = @import("./func.zig").Func;
const FuncType = @import("./func.zig").FuncType;
const Extern = @import("./extern.zig").Extern;
const Diagnostics = @import("./diagnostics.zig").Diagnostics;
const Error = @import("./error.zig").Error;
const Trap = @import("./trap.zig").Trap;
/// Object used to conveniently link together and instantiate Wasm modules.
/// This type is intended to make it easier to manage a set of modules that
/// link together, or to make it easier to link WebAssembly modules to WASI.
///
/// A `Linker` is a higher level way to instantiate a module than
/// `Instance.init` since it works at the "string" level of imports rather
/// than requiring 1:1 mappings.
pub const Linker = opaque {
/// Creates a new linker for the specified engine.
pub fn init(engine: *Engine) *Linker {
return wasmtime_linker_new(engine);
}
/// Deletes a linker
pub fn deinit(self: *Linker) void {
wasmtime_linker_delete(self);
}
/// Configures whether this linker allows later definitions to shadow
/// previous definitions. (The default is `false`.)
pub fn setAllowShadowing(self: *Linker, value: bool) void {
wasmtime_linker_allow_shadowing(self, value);
}
/// Defines a new item in this linker.
pub fn define(
self: *Linker,
context: *Store.Context,
module: []const u8,
name: []const u8,
item: *Extern,
diag: ?*Diagnostics,
) !void {
const err = wasmtime_linker_define(self, context, module.ptr, module.len, name.ptr, name.len, item);
try Diagnostics.handleError(err, error.LinkerDefine, {}, diag);
}
/// Defines a new function in this linker.
///
/// Note that this function does not create a `Func`. This creates a
/// store-independent function within the linker, allowing this function
/// definition to be used with multiple stores.
pub fn defineFunc(
self: *Linker,
module: []const u8,
name: []const u8,
func_type: *FuncType,
callback: *const Func.Callback,
env: ?*anyopaque,
env_finalizer: ?*const Func.Finalizer,
diag: ?*Diagnostics,
) !void {
const err = wasmtime_linker_define_func(self, module.ptr, module.len, name.ptr, name.len, func_type, callback, env, env_finalizer);
try Diagnostics.handleError(err, error.LinkerDefineFunc, {}, diag);
}
pub fn defineFuncFromFn(
self: *Linker,
module: []const u8,
name: []const u8,
func: anytype,
diag: ?*Diagnostics,
) !void {
const func_type = FuncType.fromFn(func);
const wrapped = Func.Wrapped.init(func);
try defineFunc(self, module, name, func_type, wrapped.callback, wrapped.env, wrapped.env_finalizer, diag);
}
/// Defines an instance under the specified name in this linker.
///
/// This function will take all of the exports of the `instance` provided
/// and defined them under a module called `name` with a field name as the
/// export's own name.
pub fn defineInstance(
self: *Linker,
context: *Store.Context,
name: []const u8,
instance: *const Instance,
diag: ?*Diagnostics,
) !void {
const err = wasmtime_linker_define_instance(self, context, name.ptr, name.len, instance);
try Diagnostics.handleError(err, error.LinkerDefineInstance, {}, diag);
}
/// Instantiates a `Module` with the items defined in this linker.
///
/// This function will attempt to satisfy all of the imports of the
/// `module` provided with items previously defined in this linker. If any
/// name isn't defined in the linker (or if the previously defined item is
/// of the wrong type) then an error is returned.
pub fn instantiate(
linker: *const Linker,
context: *Store.Context,
module: *const Module,
diag: ?*Diagnostics,
) !Instance {
var result: Instance = undefined;
var trap: ?*Trap = null;
const err = wasmtime_linker_instantiate(linker, context, module, &result, &trap);
return Diagnostics.handleErrorOrTrap(err, error.LinkerInstantiate, trap, result, diag);
}
/// Defines automatic instantiations of a `Module` in this linker.
pub fn autoInstantiateModule(
linker: *const Linker,
context: *Store.Context,
name: []const u8,
module: *const Module,
diag: ?*Diagnostics,
) !void {
const err = wasmtime_linker_module(linker, context, name.ptr, name.len, module);
try Diagnostics.handleError(err, error.LinkerAutoInstantiateModule, {}, diag);
}
/// Acquires the "default export" of the named module in this linker.
pub fn getDefault(
linker: *const Linker,
context: *Store.Context,
module: []const u8,
diag: ?*Diagnostics,
) !Func {
var result: Func = undefined;
const err = wasmtime_linker_get_default(linker, context, module.ptr, module.len, &result);
return Diagnostics.handleError(err, error.LinkerGetDefault, result, diag);
}
/// Loads an item by name from this linker.
pub fn get(
linker: *const Linker,
context: *Store.Context,
module: []const u8,
name: []const u8,
) ?Extern {
var result: Extern = undefined;
return if (wasmtime_linker_get(linker, context, module.ptr, module.len, name.ptr, name.len, &result)) result else null;
}
extern "c" fn wasmtime_linker_new(*Engine) *Linker;
extern "c" fn wasmtime_linker_delete(*Linker) void;
extern "c" fn wasmtime_linker_allow_shadowing(*Linker, bool) void;
extern "c" fn wasmtime_linker_define(
linker: *Linker,
context: *Store.Context,
module: [*]const u8,
module_len: usize,
name: [*]const u8,
name_len: usize,
item: *Extern,
) ?*Error;
extern "c" fn wasmtime_linker_define_func(
linker: *Linker,
module: [*]const u8,
module_len: usize,
name: [*]const u8,
name_len: usize,
func_type: *FuncType,
callback: *const Func.Callback,
env: ?*anyopaque,
env_finalizer: ?*const Func.Finalizer,
) ?*Error;
// extern "c" fn wasmtime_linker_define_wasi(
// linker: *Linker,
// ) ?*Error;
extern "c" fn wasmtime_linker_define_instance(
linker: *Linker,
context: *Store.Context,
name: [*]const u8,
name_len: usize,
instance: *const Instance,
) ?*Error;
extern "c" fn wasmtime_linker_instantiate(
linker: *const Linker,
context: *Store.Context,
module: *const Module,
result: *Instance,
trap: *?*Trap,
) ?*Error;
extern "c" fn wasmtime_linker_module(
linker: *Linker,
context: *Store.Context,
name: [*]const u8,
name_len: usize,
module: *const Module,
) ?*Error;
extern "c" fn wasmtime_linker_get_default(
linker: *const Linker,
context: *Store.Context,
name: [*]const u8,
name_len: usize,
result: *Func,
) ?*Error;
extern "c" fn wasmtime_linker_get(
linker: *const Linker,
context: *Store.Context,
module: [*]const u8,
module_len: usize,
name: [*]const u8,
name_len: usize,
result: *Extern,
) bool;
};

@ -0,0 +1,19 @@
pub usingnamespace @import("./config.zig");
pub usingnamespace @import("./diagnostics.zig");
pub usingnamespace @import("./engine.zig");
pub usingnamespace @import("./error.zig");
pub usingnamespace @import("./extern.zig");
pub usingnamespace @import("./func.zig");
pub usingnamespace @import("./instance.zig");
pub usingnamespace @import("./linker.zig");
pub usingnamespace @import("./module.zig");
pub usingnamespace @import("./store.zig");
pub usingnamespace @import("./trap.zig");
pub usingnamespace @import("./val.zig");
pub usingnamespace @import("./vec.zig");
pub usingnamespace @import("./wat2wasm.zig");
test {
const