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(comptime T: type, value: T) Val { 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(i32, -12); defer small.deinit(); try expect(small.kind == ValKind.i32); try expect(small.of.i32 == -12); try expect(try small.toValue(i32) == -12); var large = Val.fromValue(u64, 10000000000000000000); defer large.deinit(); try expect(large.kind == ValKind.i64); // The provided integer can't be represented as an i64, but Wasm // only knows about signed integers, so we have to store it as one. try expect(large.of.i64 == @as(i64, @bitCast(@as(u64, 10000000000000000000)))); try expect(try large.toValue(u64) == 10000000000000000000); var pi = Val.fromValue(f32, 3.14159); defer pi.deinit(); try expect(pi.kind == ValKind.f32); try expect(pi.of.f32 == 3.14159); try expect(try pi.toValue(f32) == 3.14159); var yes = Val.fromValue(bool, true); defer yes.deinit(); try expect(yes.kind == ValKind.i32); try expect(yes.of.i32 == 1); try expect(try yes.toValue(bool) == true); var no = Val.fromValue(bool, false); defer no.deinit(); try expect(no.kind == ValKind.i32); try expect(no.of.i32 == 0); try expect(try 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;