diff --git a/README.md b/README.md index a659dd8..ecff8b6 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ fn print_hello() void { pub fn main() !void { const wasm = @import("wasmslime"); - // Should deinit Wasm objects. Omitted for brevity. + // Should deinit Wasm objects, but this is omitted for brevity. var engine = try wasm.Engine.initDefault(); var module = try wasm.Module.initFromWat(engine, wat_bytes, null); var linker = wasm.Linker.init(engine); @@ -28,7 +28,7 @@ pub fn main() !void { var store = wasm.Store.init(engine); var context = store.getContext(); var instance = try linker.instantiate(context, module, null); - const run_func = try instance.getFunc(context, "main"); + const run_func = try instance.get(context, "main", wasm.Func); try run_func.call(context, .{}, void, null); } ``` diff --git a/src/engine.zig b/src/engine.zig index 2727ccb..755472f 100644 --- a/src/engine.zig +++ b/src/engine.zig @@ -48,7 +48,7 @@ pub const Engine = opaque { /// async-signal-safe. /// /// See also `Config.epoch_interruption_set`. - pub fn increment_epoch(self: *Engine) void { + pub fn incrementEpoch(self: *Engine) void { wasmtime_engine_increment_epoch(self); } diff --git a/src/extern.zig b/src/extern.zig index 65cef1e..d643119 100644 --- a/src/extern.zig +++ b/src/extern.zig @@ -46,20 +46,26 @@ pub const Extern = extern struct { return wasmtime_extern_type(context, self); } - pub fn fromFunc(func: Func) Extern { - return .{ .kind = .func, .of = .{ .func = func } }; + pub fn from(value: anytype) Extern { + return switch (@TypeOf(value)) { + Extern => value, // Convenience no-op for use with generic functions. + Func => .{ .kind = .func, .of = .{ .func = value } }, + Global => .{ .kind = .global, .of = .{ .global = value } }, + Table => .{ .kind = .table, .of = .{ .table = value } }, + Memory => .{ .kind = .memory, .of = .{ .memory = value } }, + else => @compileError("Expected one of 'Extern', 'Func', 'Global', 'Table' or 'Memory', but found '" ++ @typeName(@TypeOf(value)) ++ "'"), + }; } - 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; + pub fn as(self: Extern, comptime T: type) !T { + return switch (T) { + Extern => self, // Convenience no-op for use with generic functions. + Func => if (self.kind == .func) self.of.func else error.IncorrectType, + Global => if (self.kind == .global) self.of.global else error.IncorrectType, + Table => if (self.kind == .table) self.of.table else error.IncorrectType, + Memory => if (self.kind == .memory) self.of.memory else error.IncorrectType, + else => @compileError("Expected one of 'Extern', 'Func', 'Global', 'Table' or 'Memory', but found '" ++ @typeName(T) ++ "'"), + }; } extern "c" fn wasmtime_extern_delete(*Extern) void; @@ -86,20 +92,22 @@ pub const ExternType = opaque { 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 from(value: anytype) @TypeOf(value) { + return switch (@TypeOf(value)) { + *ExternType => value, // Convenience no-op for use with generic functions. + *FuncType => wasm_functype_as_externtype(value), + *MemoryType => wasm_memorytype_as_externtype(value), + else => @compileError("Expected one of '*ExternType', '*FuncType' or '*MemoryType', but found '" ++ @typeName(@TypeOf(value)) ++ "'"), + }; } - pub fn asMemoryType(self: *ExternType) ?*MemoryType { - return wasm_externtype_as_memorytype(self); + pub fn as(self: *ExternType, comptime T: type) !T { + return switch (T) { + *ExternType => self, // Convenience no-op for use with generic functions. + *FuncType => wasm_externtype_as_functype(self) orelse error.IncorrectType, + *MemoryType => wasm_externtype_as_memorytype(self) orelse error.IncorrectType, + else => @compileError("Expected one of '*ExternType', '*FuncType' or '*MemoryType', but found '" ++ @typeName(T) ++ "'"), + }; } extern "c" fn wasm_externtype_copy(*const ExternType) *ExternType; diff --git a/src/func.zig b/src/func.zig index 3d7643c..b90840c 100644 --- a/src/func.zig +++ b/src/func.zig @@ -26,32 +26,29 @@ pub const Caller = opaque { 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 { + /// Gets an exported `Extern` by name from the caller's context, converted + /// to the specified type. If `T` is `Extern` no conversion is done. + /// + /// Returns `error.ExportNotFound` if export with that name is not found. + /// Returns `error.IncorrectType` if the export isn't the right type. + pub fn get( + self: *Caller, + name: []const u8, + comptime T: type, + ) !T { 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; + return if (wasmtime_caller_export_get(self, name.ptr, name.len, &result)) result.as(T) else error.ExportNotFound; } /// 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()); + /// + /// Returns `error.ExportNotFound` if export with that name is not found. + /// Returns `error.IncorrectType` if the export isn't the a `Memory`. + pub fn getData( + self: *Caller, + name: []const u8, + ) ![]u8 { + return (try self.get(name, Memory)).getData(self.getContext()); } extern "c" fn wasmtime_caller_context(*Caller) *Store.Context; @@ -191,7 +188,7 @@ pub const Func = extern struct { } pub fn toExtern(self: Func) Extern { - return Extern.fromFunc(self); + return Extern.from(self, Func); } fn isTupleOrEmpty(comptime T: type) bool { diff --git a/src/instance.zig b/src/instance.zig index d21af5c..451e263 100644 --- a/src/instance.zig +++ b/src/instance.zig @@ -1,7 +1,6 @@ const Engine = @import("./engine.zig").Engine; const Module = @import("./module.zig").Module; const Store = @import("./store.zig").Store; -const Func = @import("./func.zig").Func; const Memory = @import("./memory.zig").Memory; const Diagnostics = @import("./diagnostics.zig").Diagnostics; const Error = @import("./error.zig").Error; @@ -40,50 +39,63 @@ pub const Instance = extern struct { /// /// 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 { + 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 { + /// Gets an exported `Extern` by name from this instance, converted to the + /// specified type. If `T` is `Extern` no conversion is done. + /// + /// Returns `error.ExportNotFound` if export with that name is not found. + /// Returns `error.IncorrectType` if the export isn't the right type. + pub fn get( + self: *const Instance, + context: *Store.Context, + name: []const u8, + comptime T: type, + ) !T { 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; + return if (wasmtime_instance_export_get(context, self, name.ptr, name.len, &result)) result.as(T) else error.ExportNotFound; } /// 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); + /// + /// Returns `error.ExportNotFound` if export with that name is not found. + /// Returns `error.IncorrectType` if the export isn't the a `Memory`. + pub fn getData( + self: *const Instance, + context: *Store.Context, + name: []const u8, + ) ![]u8 { + return (try self.get(context, name, Memory)).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 { + /// Gets an exported `Extern` by index from this instance, converted to + /// the specified type. If `T` is `Extern` no conversion is done. + /// + /// Returns `error.ExportNotFound` if export with that name is not found. + /// Returns `error.IncorrectType` if the export isn't the right type. + pub fn getByIndex( + self: *const Instance, + context: *Store.Context, + index: usize, + comptime T: type, + ) !struct { result: T, name: []const u8 } { 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; + return if (wasmtime_instance_export_nth(context, self, index, &name, &name_len, &result)) + .{ .result = result.as(T), .name = name[0..name_len] } + else + error.ExportNotFound; } extern "c" fn wasmtime_instance_new(*Store.Context, *const Module, [*]const Extern, usize, *Instance, *?*Trap) ?*Error; diff --git a/src/linker.zig b/src/linker.zig index 3a4e827..f4e20f9 100644 --- a/src/linker.zig +++ b/src/linker.zig @@ -124,6 +124,8 @@ pub const Linker = opaque { } /// Acquires the "default export" of the named module in this linker. + /// + /// Returns an error if the default export is not a function. pub fn getDefault( linker: *const Linker, context: *Store.Context, @@ -135,15 +137,20 @@ pub const Linker = opaque { return Diagnostics.handleError(err, error.LinkerGetDefault, result, diag); } - /// Loads an item by name from this linker. + /// Loads an item by name from this linker, converted to the + /// specified type. If `T` is `Extern` no conversion is done. + /// + /// Returns `error.ExportNotFound` if export with that name is not found. + /// Returns `error.IncorrectType` if the export isn't the right type. pub fn get( linker: *const Linker, context: *Store.Context, module: []const u8, name: []const u8, - ) ?Extern { + comptime T: type, + ) !Extern { var result: Extern = undefined; - return if (wasmtime_linker_get(linker, context, module.ptr, module.len, name.ptr, name.len, &result)) result else null; + return if (wasmtime_linker_get(linker, context, module.ptr, module.len, name.ptr, name.len, &result)) result.as(T) else error.ExportNotFound; } extern "c" fn wasmtime_linker_new(*Engine) *Linker; diff --git a/src/memory.zig b/src/memory.zig index 86eaa52..df1d99c 100644 --- a/src/memory.zig +++ b/src/memory.zig @@ -35,7 +35,7 @@ pub const Memory = extern struct { } pub fn toExtern(self: Memory) Extern { - return Extern.fromMemory(self); + return Extern.from(self, Memory); } extern "c" fn wasmtime_memory_new(*Store.Context, *const MemoryType, *Memory) ?*Error;