From 0d91422f541a2c240b0e84bc833af3fa10c82a6b Mon Sep 17 00:00:00 2001 From: copygirl Date: Thu, 17 Aug 2023 15:45:16 +0200 Subject: [PATCH] Allow wrapped function to return error union When an error is returned by a wrapped host function, it causes a trap to be passed back to wasm. The trap's message will mention the error enum's name. --- README.md | 4 ++-- src/func.zig | 44 +++++++++++++++++++++++++++++++++----------- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index ecff8b6..d767885 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ const wat_bytes = \\ (func (export "main") (call $hello))) ; -fn print_hello() void { +fn print_hello() !void { const stdout = @import("std").io.getStdOut().writer(); - stdout.print("Hello, Slime!\n", .{}) catch unreachable; + try stdout.print("Hello, Slime!\n", .{}); } pub fn main() !void { diff --git a/src/func.zig b/src/func.zig index b90840c..97df4b0 100644 --- a/src/func.zig +++ b/src/func.zig @@ -1,6 +1,7 @@ const std = @import("std"); const assert = std.debug.assert; const panic = std.debug.panic; +const c_alloc = std.heap.c_allocator; const Store = @import("./store.zig").Store; const Extern = @import("./extern.zig").Extern; @@ -98,7 +99,7 @@ pub const Func = extern struct { .callback = &(struct { const meta = @typeInfo(@TypeOf(func)).Fn; const ArgsType = std.meta.ArgsTuple(@TypeOf(func)); - const ResultType = meta.return_type orelse void; + const ReturnType = meta.return_type orelse void; fn callback( env: ?*anyopaque, @@ -118,7 +119,7 @@ pub const Func = extern struct { } assert(num_args == meta.params.len - offset); - assert(num_results == getNumResultsFromResultType(ResultType)); + assert(num_results == getNumResultsFromReturnType(ReturnType)); // Turn the "env" parameter back into a function pointer. const func_ptr: *const @TypeOf(func) = @ptrCast(env); @@ -129,13 +130,24 @@ pub const Func = extern struct { 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); + const func_results_maybe_error = @call(.auto, func_ptr, func_args); + + // If function returns an ErrorUnion, convert it to + // its payload, or return a trap if an error occurred. + const func_results = if (@typeInfo(@TypeOf(func_results_maybe_error)) == .ErrorUnion) + func_results_maybe_error catch |err| { + const fmt = "Error '{s}' occurred in host code"; + const msg = std.fmt.allocPrintZ(c_alloc, fmt, .{@errorName(err)}); + return Trap.init(msg catch "Error occurred in host code"); + } + else + func_results_maybe_error; // Pass the result(s) back to WASM. - if (comptime std.meta.trait.isTuple(ResultType)) { + if (comptime std.meta.trait.isTuple(@TypeOf(func_results))) { inline for (func_results, 0..) |res, i| results[i] = Val.fromValue(@TypeOf(res), res); - } else if (ResultType != void) + } else if (@TypeOf(func_results) != void) results[0] = Val.fromValue(@TypeOf(func_results), func_results); return null; @@ -165,7 +177,7 @@ pub const Func = extern struct { val.* = Val.fromValue(@TypeOf(arg), arg); // Set up function results. - const num_results = comptime getNumResultsFromResultType(TResult); + const num_results = comptime getNumResultsFromReturnType(TResult); var results_array: [num_results]Val = undefined; var trap: ?*Trap = null; @@ -198,10 +210,16 @@ pub const Func = extern struct { }; } - 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; + fn getNumResultsFromReturnType(comptime T: type) usize { + const meta = @typeInfo(T); + return comptime if (meta == .ErrorUnion) + getNumResultsFromReturnType(meta.ErrorUnion.payload) + else if (std.meta.trait.isTuple(T)) + meta.Struct.fields.len + else if (T != void) + 1 + else + 0; } pub const Callback = fn ( @@ -257,7 +275,11 @@ pub const FuncType = opaque { inline for (meta.params[offset..], 0..) |p, i| params.data[i] = ValType.fromType(p.type.?); - const ResultType = meta.return_type orelse void; + const ResultType = switch (@typeInfo(meta.return_type orelse void)) { + .ErrorUnion => |u| u.payload, + else => |t| t, + }; + var results = if (comptime std.meta.trait.isTuple(ResultType)) blk: { var res = ValTypeVec.initUninitialized(ResultType.len); inline for (ResultType, res.toSlice()) |T, *r|