Wasmtime bindings for Zig
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

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;