You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
7.7 KiB
215 lines
7.7 KiB
const std = @import("std"); |
|
const expect = std.testing.expect; |
|
|
|
const Func = @import("./func.zig").Func; |
|
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 Val { |
|
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); |
|
} |
|
|
|
/// 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;
|
|
|