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.
main
copygirl 1 year ago
parent c85bae6d5f
commit 0d91422f54
  1. 4
      README.md
  2. 44
      src/func.zig

@ -13,9 +13,9 @@ const wat_bytes =
\\ (func (export "main") (call $hello))) \\ (func (export "main") (call $hello)))
; ;
fn print_hello() void { fn print_hello() !void {
const stdout = @import("std").io.getStdOut().writer(); const stdout = @import("std").io.getStdOut().writer();
stdout.print("Hello, Slime!\n", .{}) catch unreachable; try stdout.print("Hello, Slime!\n", .{});
} }
pub fn main() !void { pub fn main() !void {

@ -1,6 +1,7 @@
const std = @import("std"); const std = @import("std");
const assert = std.debug.assert; const assert = std.debug.assert;
const panic = std.debug.panic; const panic = std.debug.panic;
const c_alloc = std.heap.c_allocator;
const Store = @import("./store.zig").Store; const Store = @import("./store.zig").Store;
const Extern = @import("./extern.zig").Extern; const Extern = @import("./extern.zig").Extern;
@ -98,7 +99,7 @@ pub const Func = extern struct {
.callback = &(struct { .callback = &(struct {
const meta = @typeInfo(@TypeOf(func)).Fn; const meta = @typeInfo(@TypeOf(func)).Fn;
const ArgsType = std.meta.ArgsTuple(@TypeOf(func)); const ArgsType = std.meta.ArgsTuple(@TypeOf(func));
const ResultType = meta.return_type orelse void; const ReturnType = meta.return_type orelse void;
fn callback( fn callback(
env: ?*anyopaque, env: ?*anyopaque,
@ -118,7 +119,7 @@ pub const Func = extern struct {
} }
assert(num_args == meta.params.len - offset); 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. // Turn the "env" parameter back into a function pointer.
const func_ptr: *const @TypeOf(func) = @ptrCast(env); 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.?) }); panic("Cannot convert {} to {s}", .{ arg, @typeName(p.type.?) });
// Call the function with the arguments, get the results. // 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. // 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| inline for (func_results, 0..) |res, i|
results[i] = Val.fromValue(@TypeOf(res), res); 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); results[0] = Val.fromValue(@TypeOf(func_results), func_results);
return null; return null;
@ -165,7 +177,7 @@ pub const Func = extern struct {
val.* = Val.fromValue(@TypeOf(arg), arg); val.* = Val.fromValue(@TypeOf(arg), arg);
// Set up function results. // Set up function results.
const num_results = comptime getNumResultsFromResultType(TResult); const num_results = comptime getNumResultsFromReturnType(TResult);
var results_array: [num_results]Val = undefined; var results_array: [num_results]Val = undefined;
var trap: ?*Trap = null; var trap: ?*Trap = null;
@ -198,10 +210,16 @@ pub const Func = extern struct {
}; };
} }
fn getNumResultsFromResultType(comptime T: type) usize { fn getNumResultsFromReturnType(comptime T: type) usize {
return comptime if (std.meta.trait.isTuple(T)) const meta = @typeInfo(T);
@typeInfo(T).Struct.fields.len return comptime if (meta == .ErrorUnion)
else if (T != void) 1 else 0; 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 ( pub const Callback = fn (
@ -257,7 +275,11 @@ pub const FuncType = opaque {
inline for (meta.params[offset..], 0..) |p, i| inline for (meta.params[offset..], 0..) |p, i|
params.data[i] = ValType.fromType(p.type.?); 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 results = if (comptime std.meta.trait.isTuple(ResultType)) blk: {
var res = ValTypeVec.initUninitialized(ResultType.len); var res = ValTypeVec.initUninitialized(ResultType.len);
inline for (ResultType, res.toSlice()) |T, *r| inline for (ResultType, res.toSlice()) |T, *r|

Loading…
Cancel
Save