commit
6b66acb99e
21 changed files with 1829 additions and 0 deletions
@ -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 |
@ -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(¶ms, &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 std = @import("std"); |
||||
std.testing.refAllDecls(@This()); |
||||
} |
@ -0,0 +1,83 @@ |
||||
const Extern = @import("./extern.zig").Extern; |
||||
const Store = @import("./store.zig").Store; |
||||
const Diagnostics = @import("./diagnostics.zig").Diagnostics; |
||||
const Error = @import("./error.zig").Error; |
||||
|
||||
/// Representation of a memory in Wasmtime. |
||||
pub const Memory = extern struct { |
||||
store_id: u64, |
||||
index: usize, |
||||
|
||||
pub fn init(context: *Store.Context, mem_type: *const MemoryType, diag: ?*Diagnostics) !Memory { |
||||
var result: Memory = undefined; |
||||
const err = wasmtime_memory_new(context, mem_type, &result); |
||||
return Diagnostics.handleError(err, error.MemoryInit, result, diag); |
||||
} |
||||
|
||||
pub fn getType(self: *const Memory, context: *const Store.Context) *MemoryType { |
||||
return wasmtime_memory_type(context, self); |
||||
} |
||||
|
||||
pub fn getData(self: *const Memory, context: *const Store.Context) []u8 { |
||||
var data = wasmtime_memory_data(context, self); |
||||
var size = wasmtime_memory_data_size(context, self); |
||||
return data[0..size]; |
||||
} |
||||
|
||||
pub fn getSize(self: *const Memory, context: *const Store.Context) u64 { |
||||
return wasmtime_memory_size(context, self); |
||||
} |
||||
|
||||
pub fn grow(self: *const Memory, context: *Store.Context, delta: u64, diag: ?*Diagnostics) !u64 { |
||||
var prev_size: u64 = undefined; |
||||
const err = wasmtime_memory_grow(context, self, delta, &prev_size); |
||||
return Diagnostics.handleError(err, error.MemoryGrow, prev_size, diag); |
||||
} |
||||
|
||||
pub fn toExtern(self: Memory) Extern { |
||||
return Extern.fromMemory(self); |
||||
} |
||||
|
||||
extern "c" fn wasmtime_memory_new(*Store.Context, *const MemoryType, *Memory) ?*Error; |
||||
extern "c" fn wasmtime_memory_type(*const Store.Context, *const Memory) *MemoryType; |
||||
extern "c" fn wasmtime_memory_data(*const Store.Context, *const Memory) [*]u8; |
||||
extern "c" fn wasmtime_memory_data_size(*const Store.Context, *const Memory) usize; |
||||
extern "c" fn wasmtime_memory_size(*const Store.Context, *const Memory) u64; |
||||
extern "c" fn wasmtime_memory_grow(*Store.Context, *const Memory, u64, *u64) ?*Error; |
||||
}; |
||||
|
||||
/// An opaque object representing the type of a memory. |
||||
pub const MemoryType = opaque { |
||||
pub fn init(min: u64, max: ?u64, is_64: bool) *MemoryType { |
||||
return wasmtime_memorytype_new(min, max != null, max orelse 0, is_64); |
||||
} |
||||
|
||||
pub fn copy(self: *const MemoryType) *MemoryType { |
||||
return wasm_functype_copy(self); |
||||
} |
||||
|
||||
pub fn deinit(self: *MemoryType) void { |
||||
wasm_functype_delete(self); |
||||
} |
||||
|
||||
pub fn getMinimum(self: *const MemoryType) u64 { |
||||
return wasmtime_memorytype_minimum(self); |
||||
} |
||||
|
||||
pub fn getMaximum(self: *const MemoryType) ?u64 { |
||||
var result: u64 = undefined; |
||||
return if (wasmtime_memorytype_maximum(self, &result)) result else null; |
||||
} |
||||
|
||||
pub fn is64(self: *const MemoryType) bool { |
||||
return wasmtime_memorytype_is64(self); |
||||
} |
||||
|
||||
extern "c" fn wasm_functype_copy(*const MemoryType) *MemoryType; |
||||
extern "c" fn wasm_functype_delete(*MemoryType) void; |
||||
|
||||
extern "c" fn wasmtime_memorytype_new(min: u64, max_present: bool, max: u64, is_64: bool) *MemoryType; |
||||
extern "c" fn wasmtime_memorytype_minimum(*const MemoryType) u64; |
||||
extern "c" fn wasmtime_memorytype_maximum(*const MemoryType, *u64) bool; |
||||
extern "c" fn wasmtime_memorytype_is64(*const MemoryType) bool; |
||||
}; |
@ -0,0 +1,114 @@ |
||||
const Engine = @import("./engine.zig").Engine; |
||||
const Diagnostics = @import("./diagnostics.zig").Diagnostics; |
||||
const Error = @import("./error.zig").Error; |
||||
const ByteVec = @import("./vec.zig").ByteVec; |
||||
|
||||
const _instance = @import("./instance.zig"); |
||||
const ImportTypeVec = _instance.ImportTypeVec; |
||||
const ExportTypeVec = _instance.ExportTypeVec; |
||||
|
||||
const wat2wasm = @import("./wat2wasm.zig").wat2wasm; |
||||
|
||||
/// A compiled Wasmtime module. |
||||
/// |
||||
/// This type represents a compiled WebAssembly module. The compiled module is |
||||
/// ready to be instantiated and can be inspected for imports/exports. It is |
||||
/// safe to use a module across multiple threads simultaneously. |
||||
pub const Module = opaque { |
||||
/// Compiles a WebAssembly binary into a `Module`. |
||||
pub fn init(engine: *Engine, wasm_bytes: []const u8, diag: ?*Diagnostics) !*Module { |
||||
var result: *Module = undefined; |
||||
const err = wasmtime_module_new(engine, wasm_bytes.ptr, wasm_bytes.len, &result); |
||||
return Diagnostics.handleError(err, error.ModuleInit, result, diag); |
||||
} |
||||
|
||||
pub fn initFromWat(engine: *Engine, wat_bytes: []const u8, diag: ?*Diagnostics) !*Module { |
||||
const wasm_bytes = try wat2wasm(wat_bytes); |
||||
defer wasm_bytes.deinit(); |
||||
return init(engine, wasm_bytes.toSlice(), diag); |
||||
} |
||||
|
||||
pub fn deinit(self: *Module) void { |
||||
wasmtime_module_delete(self); |
||||
} |
||||
|
||||
/// Creates a shallow clone of the specified module, increasing the |
||||
/// internal reference count. |
||||
pub fn clone(self: *Module) *Module { |
||||
return wasmtime_module_clone(self); |
||||
} |
||||
|
||||
/// Returns the list of imports that this module expects. |
||||
/// |
||||
/// The list of imports returned are the types of items expected to be |
||||
/// passed to `Instance.init`. You can use `Import.getType` to learn about |
||||
/// the expected type of each import. |
||||
pub fn getImports(self: *Module) ImportTypeVec { |
||||
var result: ImportTypeVec = undefined; |
||||
wasmtime_module_imports(self, &result); |
||||
return result; |
||||
} |
||||
|
||||
/// Returns the list of exports that this module provides. |
||||
/// |
||||
/// The list of exports returned are in the same order as the items |
||||
/// returned by `Instance.getExports`. |
||||
pub fn getExports(self: *Module) ExportTypeVec { |
||||
var result: ExportTypeVec = undefined; |
||||
wasmtime_module_exports(self, &result); |
||||
return result; |
||||
} |
||||
|
||||
/// Validate a WebAssembly binary. |
||||
/// |
||||
/// This function will validate the provided byte sequence to determine if |
||||
/// it is a valid WebAssembly binary within the context of the engine |
||||
/// provided. |
||||
pub fn validate(engine: *Engine, wasm_bytes: []const u8, diag: ?*Diagnostics) !void { |
||||
const err = wasmtime_module_validate(engine, wasm_bytes.ptr, wasm_bytes.len); |
||||
try Diagnostics.handleError(err, error.ModuleValidate, {}, diag); |
||||
} |
||||
|
||||
/// This function serializes compiled module artifacts as blob data. |
||||
pub fn serialize(self: *Module, diag: ?*Diagnostics) !ByteVec { |
||||
var result: ByteVec = undefined; |
||||
const err = wasmtime_module_serialize(self, &result); |
||||
return Diagnostics.handleError(err, error.ModuleSerialize, result, diag); |
||||
} |
||||
|
||||
/// Build a module from serialized data. |
||||
/// |
||||
/// This function is not safe to receive arbitrary user input. See the |
||||
/// Rust documentation for more information on what inputs are safe to |
||||
/// pass in here (e.g. only that of `Module.serialize`). |
||||
pub fn deserialize(engine: *Engine, bytes: []const u8, diag: ?*Diagnostics) !*Module { |
||||
var result: *Module = undefined; |
||||
const err = wasmtime_module_deserialize(engine, bytes.ptr, bytes.len, &result); |
||||
return Diagnostics.handleError(err, error.ModuleDeserialize, result, diag); |
||||
} |
||||
|
||||
/// Deserialize a module from an on-disk file. |
||||
/// |
||||
/// This function is the same as `Module.deserialize` except that it reads |
||||
/// the data for the serialized module from the path on disk. This can be |
||||
/// faster than the alternative which may require copying the data around. |
||||
/// |
||||
/// This function is not safe to receive arbitrary user input. See the |
||||
/// Rust documentation for more information on what inputs are safe to |
||||
/// pass in here (e.g. only that of `Module.serialize`). |
||||
pub fn deserializeFromFile(engine: *Engine, path: [*]const u8, diag: ?*Diagnostics) !*Module { |
||||
var result: *Module = undefined; |
||||
const err = wasmtime_module_deserialize_file(engine, path.ptr, &result); |
||||
return Diagnostics.handleError(err, error.ModuleDeserializeFromFile, result, diag); |
||||
} |
||||
|
||||
extern "c" fn wasmtime_module_new(*Engine, [*]const u8, usize, **Module) ?*Error; |
||||
extern "c" fn wasmtime_module_delete(*Module) void; |
||||
extern "c" fn wasmtime_module_clone(*Module) *Module; |
||||
extern "c" fn wasmtime_module_imports(*const Module, *ImportTypeVec) void; |
||||
extern "c" fn wasmtime_module_exports(*const Module, *ExportTypeVec) void; |
||||
extern "c" fn wasmtime_module_validate(*Engine, [*]const u8, usize) ?*Error; |
||||
extern "c" fn wasmtime_module_serialize(*Module, *ByteVec) ?*Error; |
||||
extern "c" fn wasmtime_module_deserialize(*Engine, [*]const u8, usize, **Module) ?*Error; |
||||
extern "c" fn wasmtime_module_deserialize_file(*Engine, [*]const u8, **Module) ?*Error; |
||||
}; |
@ -0,0 +1,107 @@ |
||||
const Engine = @import("./engine.zig").Engine; |
||||
const Diagnostics = @import("./diagnostics.zig").Diagnostics; |
||||
const Error = @import("./error.zig").Error; |
||||
|
||||
/// A store is the unit of isolation between WebAssembly instances in an |
||||
/// embedding of Wasmtime. Values in one `Store` cannot flow into another. |
||||
/// Stores are cheap to create and cheap to dispose. It's expected that |
||||
/// one-off stores are common in embeddings. |
||||
/// |
||||
/// Objects stored within a `Store` are referenced with integer handles rather |
||||
/// than interior pointers. This means that most APIs require that the store |
||||
/// be explicitly passed in, which is done via `Store.Context`. It is safe to |
||||
/// move a `Store` to any thread at any time. A store generally cannot be |
||||
/// concurrently used, however. |
||||
pub const Store = opaque { |
||||
/// Creates a new store within the specified engine. |
||||
/// The returned store must be deleted with `deinit`. |
||||
pub fn init(engine: *Engine) *Store { |
||||
// TODO: Support passing in user-provided data. |
||||
return wasmtime_store_new(engine, null, null); |
||||
} |
||||
|
||||
/// Deletes a store. |
||||
pub fn deinit(self: *Store) void { |
||||
wasmtime_store_delete(self); |
||||
} |
||||
|
||||
/// Returns the interior `Context` pointer to this store |
||||
pub fn getContext(self: *Store) *Context { |
||||
return wasmtime_store_context(self); |
||||
} |
||||
|
||||
extern "c" fn wasmtime_store_new(*Engine, ?*anyopaque, ?*const fn (*anyopaque) void) *Store; |
||||
extern "c" fn wasmtime_store_delete(*Store) void; |
||||
extern "c" fn wasmtime_store_context(*Store) *Context; |
||||
// extern "c" fn wasmtime_store_limiter(*Store, memory_size: i64, table_elements: i64, instances: i64, tables: i64, memories: i64) void; |
||||
// extern "c" fn wasmtime_store_epoch_deadline_callback(*Store, *const fn (*Context, *anyopaque, *u64) ?*Error, *anyopaque) void; |
||||
|
||||
/// Interior pointer into a `Store`, used as "context" for many functions. |
||||
/// |
||||
/// This context pointer is used pervasively throught Wasmtime's API. This |
||||
/// can be acquired from `Store.getContext` or `Caller.getContext`. The |
||||
/// context pointer for a store is the same for the entire lifetime of a |
||||
/// store, so it can safely be stored adjacent to a `Store` itself. |
||||
/// |
||||
/// Usage of a `Context` must not outlive the original `Store`. |
||||
/// Additionally `Context` can only be used in situations where it has |
||||
/// explicitly been granted access to doing so. For example finalizers |
||||
/// cannot use `Context` because they are not given access to it. |
||||
pub const Context = opaque { |
||||
/// Returns the user-specified data associated with the specified store. |
||||
pub fn getData(self: *const Context) ?*anyopaque { |
||||
return wasmtime_context_get_data(self); |
||||
} |
||||
|
||||
/// Overwrites the user-specified data associated with this store. |
||||
/// |
||||
/// Note that this does not execute the original finalizer for the |
||||
/// provided data, and the original finalizer will be executed for the |
||||
/// provided data when the store is deleted. |
||||
pub fn setData(self: *Context, data: ?*anyopaque) void { |
||||
wasmtime_context_set_data(self, data); |
||||
} |
||||
|
||||
/// Perform garbage collection within the given context. |
||||
/// |
||||
/// Garbage collects `ExternRef`s that are used within this store. Any |
||||
/// `ExternRef`s that are discovered to be unreachable by other code |
||||
/// or objects will have their finalizers run. |
||||
pub fn runGarbageCollection(self: *Context) void { |
||||
wasmtime_context_gc(self); |
||||
} |
||||
|
||||
// TODO: Do the documentation for fuel related functions. |
||||
|
||||
pub fn getFuelRemaining(self: *const Context) ?u64 { |
||||
var result: u64 = undefined; |
||||
if (wasmtime_context_fuel_remaining(self, &result)) result else null; |
||||
} |
||||
|
||||
pub fn getFuelConsumed(self: *const Context) ?u64 { |
||||
var result: u64 = undefined; |
||||
if (wasmtime_context_fuel_consumed(self, &result)) result else null; |
||||
} |
||||
|
||||
pub fn addFuel(self: *Context, fuel: u64, diag: ?*Diagnostics) !void { |
||||
const err = wasmtime_context_add_fuel(self, fuel); |
||||
try Diagnostics.handleError(err, error.ContextAddFuel, {}, diag); |
||||
} |
||||
|
||||
pub fn consumeFuel(self: *Context, fuel: u64, diag: ?*Diagnostics) !u64 { |
||||
var result: u64 = undefined; |
||||
const err = wasmtime_context_consume_fuel(self, fuel, &result); |
||||
try Diagnostics.handleError(err, error.ContextConsumeFuel, result, diag); |
||||
} |
||||
|
||||
extern "c" fn wasmtime_context_get_data(*const Context) ?*anyopaque; |
||||
extern "c" fn wasmtime_context_set_data(*Context, ?*anyopaque) void; |
||||
extern "c" fn wasmtime_context_gc(*Context) void; |
||||
extern "c" fn wasmtime_context_add_fuel(*Context, fuel: u64) ?*Error; |
||||
extern "c" fn wasmtime_context_fuel_consumed(*const Context, fuel: *u64) bool; |
||||
extern "c" fn wasmtime_context_fuel_remaining(*const Context, fuel: *u64) bool; |
||||
extern "c" fn wasmtime_context_consume_fuel(*Context, fuel: u64, remaining: *u64) ?*Error; |
||||
// extern "c" fn wasmtime_context_set_wasi(*Context, *wasi_config_t) ?*Error; |
||||
// extern "c" fn wasmtime_context_set_epoch_deadline(*Context, ticks_beyond_current: u64) void; |
||||
}; |
||||
}; |
@ -0,0 +1,122 @@ |
||||
const Message = @import("./vec.zig").Message; |
||||
|
||||
pub const TrapError = error{ |
||||
/// The current stack space was exhausted. |
||||
StackOverflow, |
||||
/// An out-of-bounds memory access. |
||||
MemoryOutOfBounds, |
||||
/// A wasm atomic operation was presented with a not-naturally-aligned linear-memory address. |
||||
HeapMisaligned, |
||||
/// An out-of-bounds access to a table. |
||||
TableOutOfBounds, |
||||
/// Indirect call to a null table entry. |
||||
IndirectCallToNull, |
||||
/// Signature mismatch on indirect call. |
||||
BadSignature, |
||||
/// An integer arithmetic operation caused an overflow. |
||||
IntegerOverflow, |
||||
/// An integer division by zero. |
||||
IntegerDevisionByZero, |
||||
/// Failed float-to-int conversion. |
||||
BadConversionToInteger, |
||||
/// Code that was supposed to have been unreachable was reached. |
||||
UnreachableCodeReached, |
||||
/// Execution has potentially run too long and may be interrupted. |
||||
Interrupt, |
||||
/// Execution has run out of the configured fuel amount. |
||||
OutOfFuel, |
||||
/// A trap without a known type has occurred. |
||||
UnknownTrap, |
||||
}; |
||||
|
||||
/// Code of an instruction trap. |
||||
pub const TrapCode = enum(u8) { |
||||
/// The current stack space was exhausted. |
||||
stack_overflow, |
||||
/// An out-of-bounds memory access. |
||||
memory_out_of_bounds, |
||||
/// A wasm atomic operation was presented with a not-naturally-aligned linear-memory address. |
||||
heap_misaligned, |
||||
/// An out-of-bounds access to a table. |
||||
table_out_of_bounds, |
||||
/// Indirect call to a null table entry. |
||||
indirect_call_to_null, |
||||
/// Signature mismatch on indirect call. |
||||
bad_signature, |
||||
/// An integer arithmetic operation caused an overflow. |
||||
integer_overflow, |
||||
/// An integer division by zero. |
||||
integer_division_by_zero, |
||||
/// Failed float-to-int conversion. |
||||
bad_conversion_to_integer, |
||||
/// Code that was supposed to have been unreachable was reached. |
||||
unreachable_code_reached, |
||||
/// Execution has potentially run too long and may be interrupted. |
||||
interrupt, |
||||
/// Execution has run out of the configured fuel amount. |
||||
out_of_fuel, |
||||
|
||||
pub fn toError(self: TrapCode) TrapError { |
||||
return switch (self) { |
||||
// zig fmt: off |
||||
.stack_overflow => TrapError.StackOverflow, |
||||
.memory_out_of_bounds => TrapError.MemoryOutOfBounds, |
||||
.heap_misaligned => TrapError.HeapMisaligned, |
||||
.table_out_of_bounds => TrapError.TableOutOfBounds, |
||||
.indirect_call_to_null => TrapError.IndirectCallToNull, |
||||
.bad_signature => TrapError.BadSignature, |
||||
.integer_overflow => TrapError.IntegerOverflow, |
||||
.integer_division_by_zero => TrapError.IntegerDevisionByZero, |
||||
.bad_conversion_to_integer => TrapError.BadConversionToInteger, |
||||
.unreachable_code_reached => TrapError.UnreachableCodeReached, |
||||
.interrupt => TrapError.Interrupt, |
||||
.out_of_fuel => TrapError.OutOfFuel, |
||||
// zig fmt: on |
||||
}; |
||||
} |
||||
}; |
||||
|
||||
pub const Trap = struct { |
||||
/// Creates a new trap. |
||||
/// The `Trap` returned is owned by the caller. |
||||
pub fn init(message: []const u8) Trap { |
||||
return wasmtime_trap_new(message.ptr, message.len); |
||||
} |
||||
|
||||
pub fn copy(self: *const Trap) Trap { |
||||
return wasm_trap_copy(self); |
||||
} |
||||
|
||||
pub fn deinit(self: *Trap) void { |
||||
wasm_trap_delete(self); |
||||
} |
||||
|
||||
pub fn getMessage(self: *const Trap) Message { |
||||
var result: Message = undefined; |
||||
wasm_trap_message(self, &result); |
||||
return result; |
||||
} |
||||
|
||||
/// Attempts to extract the trap code from this trap. |
||||
/// |
||||
/// Returns a `TrapCode` if the trap is an instruction trap triggered |
||||
/// while executing Wasm. Returns `null` otherwise, for example when the |
||||
/// trap is created using `init`, or occur with WASI modules exiting with |
||||
/// a certain exit code. |
||||
pub fn getTrapCode(self: *const Trap) ?TrapCode { |
||||
var result: TrapCode = undefined; |
||||
const has_code = wasmtime_trap_code(self, &result); |
||||
return if (has_code) result else null; |
||||
} |
||||
|
||||
extern "c" fn wasm_trap_copy(*const Trap) *Trap; |
||||
extern "c" fn wasm_trap_delete(*Trap) void; |
||||
extern "c" fn wasm_trap_message(*const Trap, *Message) void; |
||||
// extern "c" fn wasm_trap_origin(*const Trap) *Frame; |
||||
// extern "c" fn wasm_trap_trace(*const Trap, *FrameVec) void; |
||||
|
||||
extern "c" fn wasmtime_trap_new([*]const u8, usize) *Trap; |
||||
extern "c" fn wasmtime_trap_code(*const Trap, *TrapCode) bool; |
||||
// extern "c" fn wasmtime_frame_func_name(*const Frame) *const Name; |
||||
// extern "c" fn wasmtime_frame_module_name(*const Frame) *const Name; |
||||
}; |
@ -0,0 +1,222 @@ |
||||
const std = @import("std"); |
||||
const expect = std.testing.expect; |
||||
|
||||
const Func = @import("./func.zig").Func; |
||||
const Extern = @import("./extern.zig").Extern; |
||||
const Vec = @import("./vec.zig").Vec; |
||||
|
||||
/// A host-defined un-forgeable reference to pass into WebAssembly. |
||||
pub const ExternRef = opaque { |
||||
/// Creates a new `ExternRef` value wrapping the provided data, returning |
||||
/// the pointer to the ExternRef. When the reference is reclaimed, the |
||||
/// wrapped data is cleaned up with the provided `finalizer`. |
||||
/// |
||||
/// The returned value must be deleted with `deinit`. |
||||
pub fn init(data: *anyopaque, finalizer: ?*const Finalizer) *ExternRef { |
||||
return wasmtime_externref_new(data, finalizer); |
||||
} |
||||
|
||||
/// Creates a shallow copy of the `ExternRef` argument, returning a |
||||
/// separately owned pointer. Increases the reference count. |
||||
pub fn clone(self: *ExternRef) *ExternRef { |
||||
return wasmtime_externref_clone(self); |
||||
} |
||||
|
||||
/// Decrements the reference count of the `ExternRef`, deleting it and |
||||
/// calling its finalizer if it's the last reference. |
||||
pub fn deinit(self: *ExternRef) void { |
||||
wasmtime_externref_delete(self); |
||||
} |
||||
|
||||
/// Returns the original `data` passed to `init`. |
||||
pub fn getData(self: *ExternRef) *anyopaque { |
||||
return wasmtime_externref_data(self); |
||||
} |
||||
|
||||
pub const Finalizer = fn (data: *anyopaque) void; |
||||
extern "c" fn wasmtime_externref_new(*anyopaque, ?*const Finalizer) *ExternRef; |
||||
extern "c" fn wasmtime_externref_clone(*ExternRef) *ExternRef; |
||||
extern "c" fn wasmtime_externref_delete(*ExternRef) void; |
||||
extern "c" fn wasmtime_externref_data(*ExternRef) *anyopaque; |
||||
}; |
||||
|
||||
pub const ValKind = enum(u8) { |
||||
i32 = 0, |
||||
i64 = 1, |
||||
f32 = 2, |
||||
f64 = 3, |
||||
v128 = 4, |
||||
func_ref = 5, |
||||
extern_ref = 6, |
||||
|
||||
pub fn fromType(comptime T: type) ValKind { |
||||
return switch (@typeInfo(T)) { |
||||
.Bool => ValKind.i32, |
||||
.Int => |i| switch (i.bits) { |
||||
32 => ValKind.i32, |
||||
64 => ValKind.i64, |
||||
128 => ValKind.v128, |
||||
else => @compileError("Only 32/64/128-bit ints are supported"), |
||||
}, |
||||
.Float => |f| switch (f.bits) { |
||||
32 => ValKind.f32, |
||||
64 => ValKind.f64, |
||||
else => @compileError("Only 32/64-bit floats are supported"), |
||||
}, |
||||
else => @compileError("Unsupported type " ++ @typeName(T)), |
||||
}; |
||||
} |
||||
}; |
||||
|
||||
/// Container for different kinds of wasm values. |
||||
/// |
||||
/// Note that this structure may contain an owned value, namely `ExternRef`, |
||||
/// depending on the context in which this is used. APIs which consume a `Val` |
||||
/// do not take ownership, but APIs that return `Val` require that `deinit` is |
||||
/// called to deallocate the value. |
||||
pub const Val = extern struct { |
||||
kind: ValKind, |
||||
of: extern union { |
||||
i32: i32, |
||||
i64: i64, |
||||
f32: f32, |
||||
f64: f64, |
||||
v128: u128, |
||||
func_ref: Func, |
||||
extern_ref: *ExternRef, |
||||
}, |
||||
|
||||
pub fn copy(self: *const Val) Val { |
||||
var result: Val = undefined; |
||||
wasmtime_val_copy(&result, self); |
||||
return result; |
||||
} |
||||
|
||||
/// Note that this only deletes the contents, not the memory that `val` |
||||
/// points to itself (which is owned by the caller). |
||||
pub fn deinit(self: *Val) void { |
||||
wasmtime_val_delete(self); |
||||
} |
||||
|
||||
pub fn fromValue(value: anytype) Val { |
||||
const T = @TypeOf(value); |
||||
return switch (@typeInfo(T)) { |
||||
// TODO: Support .ComptimeInt and .ComptimeFloat. |
||||
.Bool => .{ .kind = ValKind.i32, .of = .{ .i32 = @intFromBool(value) } }, |
||||
.Int => |i| switch (i.bits) { |
||||
// @bitCast is used here because value may be signed or unsigned. |
||||
32 => .{ .kind = ValKind.i32, .of = .{ .i32 = @bitCast(value) } }, |
||||
64 => .{ .kind = ValKind.i64, .of = .{ .i64 = @bitCast(value) } }, |
||||
128 => .{ .kind = ValKind.v128, .of = .{ .v128 = @bitCast(value) } }, |
||||
else => @compileError("Only 32/64/128-bit ints are supported"), |
||||
}, |
||||
.Float => |f| switch (f.bits) { |
||||
32 => .{ .kind = ValKind.f32, .of = .{ .f32 = value } }, |
||||
64 => .{ .kind = ValKind.f64, .of = .{ .f64 = value } }, |
||||
else => @compileError("Only 32/64-bit floats are supported"), |
||||
}, |
||||
else => @compileError("Unsupported type " ++ @typeName(T)), |
||||
}; |
||||
} |
||||
|
||||
pub fn toValue(self: Val, comptime T: type) !T { |
||||
if (self.kind != ValKind.fromType(T)) |
||||
return error.InvalidConversion; |
||||
return switch (@typeInfo(T)) { |
||||
.Bool => self.of.i32 != 0, |
||||
.Int => |i| switch (i.bits) { |
||||
32 => @bitCast(self.of.i32), |
||||
64 => @bitCast(self.of.i64), |
||||
128 => @bitCast(self.of.v128), |
||||
else => unreachable, |
||||
}, |
||||
.Float => |f| switch (f.bits) { |
||||
32 => self.of.f32, |
||||
64 => self.of.f64, |
||||
else => unreachable, |
||||
}, |
||||
else => unreachable, |
||||
}; |
||||
// Use of "unreachable" assumes `fromType` has already |
||||
// caused a compile time error for unsupported types. |
||||
} |
||||
|
||||
// TODO: Add format function. |
||||
|
||||
extern "c" fn wasmtime_val_copy(*Val, *const Val) void; |
||||
extern "c" fn wasmtime_val_delete(*Val) void; |
||||
|
||||
test "convert value to Val and back" { |
||||
var small = Val.fromValue(-12); |
||||
defer small.deinit(); |
||||
try expect(small.kind == ValKind.i32); |
||||
try expect(small.of.i32 == -12); |
||||
try expect(small.toValue(i32) == -12); |
||||
|
||||
var large = Val.fromValue(10000000000000000000); |
||||
defer large.deinit(); |
||||
try expect(large.kind == ValKind.i64); |
||||
try expect(large.of.i64 == @as(i64, @bitCast(10000000000000000000))); |
||||
try expect(large.toValue(u64) == 10000000000000000000); |
||||
|
||||
var pi = Val.fromValue(3.14159); |
||||
defer pi.deinit(); |
||||
try expect(pi.kind == ValKind.f32); |
||||
try expect(pi.of.f32 == 3.14159); |
||||
try expect(pi.toValue(f32) == 3.14159); |
||||
|
||||
var yes = Val.fromValue(true); |
||||
defer yes.deinit(); |
||||
try expect(yes.kind == ValKind.i32); |
||||
try expect(yes.of.i32 == 1); |
||||
try expect(yes.toValue(bool) == true); |
||||
|
||||
var no = Val.fromValue(false); |
||||
defer no.deinit(); |
||||
try expect(no.kind == ValKind.i32); |
||||
try expect(no.of.i32 == 0); |
||||
try expect(no.toValue(bool) == false); |
||||
} |
||||
}; |
||||
|
||||
// pub const ValVec = Vec(Val, wasm_val_vec_new_empty, wasm_val_vec_new_uninitialized, wasm_val_vec_new, wasm_val_vec_copy, wasm_val_vec_delete); |
||||
// extern "c" fn wasm_val_vec_new_empty(*anyopaque, usize) void; |
||||
// extern "c" fn wasm_val_vec_new_uninitialized(*anyopaque, usize) void; |
||||
// extern "c" fn wasm_val_vec_new(*anyopaque, usize, [*]const Val) void; |
||||
// extern "c" fn wasm_val_vec_copy(*anyopaque, *const anyopaque) void; |
||||
// extern "c" fn wasm_val_vec_delete(*anyopaque) void; |
||||
|
||||
/// An object representing the type of a value. |
||||
pub const ValType = opaque { |
||||
pub fn init(kind: ValKind) *ValType { |
||||
return wasm_valtype_new(kind); |
||||
} |
||||
|
||||
pub fn fromType(comptime T: type) *ValType { |
||||
return init(ValKind.fromType(T)); |
||||
} |
||||
|
||||
pub fn copy(self: *const ValType) *ValType { |
||||
return wasm_valtype_copy(self); |
||||
} |
||||
|
||||
pub fn deinit(self: *ValType) void { |
||||
wasm_valtype_delete(self); |
||||
} |
||||
|
||||
pub fn getKind(self: *const ValType) ValKind { |
||||
return wasm_valtype_kind(self); |
||||
} |
||||
|
||||
extern "c" fn wasm_valtype_new(ValKind) *ValType; |
||||
extern "c" fn wasm_valtype_copy(*const ValType) *ValType; |
||||
extern "c" fn wasm_valtype_delete(*ValType) void; |
||||
extern "c" fn wasm_valtype_kind(*const ValType) ValKind; |
||||
}; |
||||
|
||||
pub const ValTypeVec = Vec(*ValType, wasm_valtype_vec_new_empty, wasm_valtype_vec_new_uninitialized, wasm_valtype_vec_new, wasm_valtype_vec_copy, wasm_valtype_vec_delete); |
||||
extern "c" fn wasm_valtype_vec_new_empty(*anyopaque, usize) void; |
||||
extern "c" fn wasm_valtype_vec_new_uninitialized(*anyopaque, usize) void; |
||||
extern "c" fn wasm_valtype_vec_new(*anyopaque, usize, [*]const ?*ValType) void; |
||||
extern "c" fn wasm_valtype_vec_copy(*anyopaque, *const anyopaque) void; |
||||
extern "c" fn wasm_valtype_vec_delete(*anyopaque) void; |
@ -0,0 +1,58 @@ |
||||
pub const ByteVec = Vec(u8, wasm_byte_vec_new_empty, wasm_byte_vec_new_uninitialized, wasm_byte_vec_new, wasm_byte_vec_copy, wasm_byte_vec_delete); |
||||
extern "c" fn wasm_byte_vec_new_empty(*anyopaque, usize) void; |
||||
extern "c" fn wasm_byte_vec_new_uninitialized(*anyopaque, usize) void; |
||||
extern "c" fn wasm_byte_vec_new(*anyopaque, usize, [*]const u8) void; |
||||
extern "c" fn wasm_byte_vec_copy(*anyopaque, *const anyopaque) void; |
||||
extern "c" fn wasm_byte_vec_delete(*anyopaque) void; |
||||
|
||||
pub const Name = ByteVec; |
||||
pub const Message = ByteVec; |
||||
|
||||
pub fn Vec( |
||||
comptime T: type, |
||||
comptime empty_fn: anytype, |
||||
comptime new_uninitialized_fn: anytype, |
||||
comptime new_fn: anytype, |
||||
comptime copy_fn: anytype, |
||||
comptime delete_fn: anytype, |
||||
) type { |
||||
return extern struct { |
||||
const Self = @This(); |
||||
const name = @typeName(T); |
||||
|
||||
size: usize, |
||||
data: [*]T, |
||||
|
||||
pub fn initEmpty(size: usize) Self { |
||||
var result: Self = undefined; |
||||
@call(.auto, empty_fn, .{ &result, size }); |
||||
return result; |
||||
} |
||||
|
||||
pub fn initUninitialized(size: usize) Self { |
||||
var result: Self = undefined; |
||||
@call(.auto, new_uninitialized_fn, .{ &result, size }); |
||||
return result; |
||||
} |
||||
|
||||
pub fn initFromSlice(slice: []const T) Self { |
||||
var result: Self = undefined; |
||||
@call(.auto, new_fn, .{ &result, slice.len, slice.ptr }); |
||||
return result; |
||||
} |
||||
|
||||
pub fn copy(self: Self) Self { |
||||
var result: Self = undefined; |
||||
@call(.auto, copy_fn, .{ &result, &self }); |
||||
return result; |
||||
} |
||||
|
||||
pub fn deinit(self: Self) void { |
||||
@call(.auto, delete_fn, .{@constCast(&self)}); |
||||
} |
||||
|
||||
pub fn toSlice(self: Self) []const T { |
||||
return self.data[0..self.size]; |
||||
} |
||||
}; |
||||
} |
@ -0,0 +1,11 @@ |
||||
const ByteVec = @import("./vec.zig").ByteVec; |
||||
const Diagnostics = @import("./diagnostics.zig").Diagnostics; |
||||
const Error = @import("./error.zig").Error; |
||||
|
||||
pub fn wat2wasm(wat_bytes: []const u8, diag: ?*Diagnostics) !ByteVec { |
||||
var result: ByteVec = undefined; |
||||
const err = wasmtime_wat2wasm(wat_bytes.ptr, wat_bytes.len, &result); |
||||
return Diagnostics.handleError(err, error.Wat2Wasm, result, diag); |
||||
} |
||||
|
||||
extern "c" fn wasmtime_wat2wasm([*]const u8, usize, *ByteVec) ?*Error; |
Loading…
Reference in new issue