├── .gitignore ├── LICENSE ├── README.md ├── build.zig ├── deps.zig ├── gyro.zzz └── src └── main.zig /.gitignore: -------------------------------------------------------------------------------- 1 | gyro.lock 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Jakub Konka & Luuk de Gram 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wasm-zig 2 | 3 | Common Wasm runtime bindings to C API. 4 | 5 | This library is intended to be used in conjunction with some runtime-specific bindings such 6 | as [wasmer-zig] or [wasmtime-zig]. 7 | 8 | [wasmer-zig]: https://github.com/kubkon/wasmer-zig 9 | [wasmtime-zig]: https://github.com/kubkon/wasmtime-zig 10 | 11 | To add this library as your dependency, we strongly recommend [gyro]. 12 | 13 | [gyro]: https://github.com/mattnite/gyro 14 | 15 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn build(b: *std.build.Builder) void { 4 | // Standard release options allow the person running `zig build` to select 5 | // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. 6 | const mode = b.standardReleaseOptions(); 7 | 8 | const lib = b.addStaticLibrary("wasm-zig", "src/main.zig"); 9 | lib.setBuildMode(mode); 10 | lib.install(); 11 | 12 | var main_tests = b.addTest("src/main.zig"); 13 | main_tests.setBuildMode(mode); 14 | 15 | const test_step = b.step("test", "Run library tests"); 16 | test_step.dependOn(&main_tests.step); 17 | } 18 | -------------------------------------------------------------------------------- /deps.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | pub const pkgs = struct { 3 | pub fn addAllTo(artifact: *std.build.LibExeObjStep) void { 4 | @setEvalBranchQuota(1_000_000); 5 | inline for (std.meta.declarations(pkgs)) |decl| { 6 | if (decl.is_pub and decl.data == .Var) { 7 | artifact.addPackage(@field(pkgs, decl.name)); 8 | } 9 | } 10 | } 11 | }; 12 | 13 | pub const base_dirs = struct { 14 | }; 15 | -------------------------------------------------------------------------------- /gyro.zzz: -------------------------------------------------------------------------------- 1 | pkgs: 2 | wasm: 3 | version: 0.0.0 4 | description: "Common Wasm runtime binding to C API" 5 | source_url: "https://github.com/zigwasm/wasm-zig" 6 | 7 | root: src/main.zig 8 | files: 9 | README.md 10 | LICENSE 11 | -------------------------------------------------------------------------------- /src/main.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const testing = std.testing; 3 | const meta = std.meta; 4 | const trait = std.meta.trait; 5 | const log = std.log.scoped(.wasm_zig); 6 | 7 | var CALLBACK: usize = undefined; 8 | 9 | // @TODO: Split these up into own error sets 10 | pub const Error = error{ 11 | /// Failed to initialize a `Config` 12 | ConfigInit, 13 | /// Failed to initialize an `Engine` (i.e. invalid config) 14 | EngineInit, 15 | /// Failed to initialize a `Store` 16 | StoreInit, 17 | /// Failed to initialize a `Module` 18 | ModuleInit, 19 | /// Failed to create a wasm function based on 20 | /// the given `Store` and functype 21 | FuncInit, 22 | /// Failed to initialize a new `Instance` 23 | InstanceInit, 24 | }; 25 | 26 | pub const Config = opaque { 27 | pub fn init() !*Config { 28 | const config = wasm_config_new() orelse return Error.ConfigInit; 29 | return config; 30 | } 31 | 32 | extern "c" fn wasm_config_new() ?*Config; 33 | }; 34 | 35 | pub const Engine = opaque { 36 | /// Initializes a new `Engine` 37 | pub fn init() !*Engine { 38 | return wasm_engine_new() orelse Error.EngineInit; 39 | } 40 | 41 | pub fn withConfig(config: *Config) !*Engine { 42 | return wasm_engine_new_with_config(config) orelse Error.EngineInit; 43 | } 44 | 45 | /// Frees the resources of the `Engine` 46 | pub fn deinit(self: *Engine) void { 47 | wasm_engine_delete(self); 48 | } 49 | 50 | extern "c" fn wasm_engine_new() ?*Engine; 51 | extern "c" fn wasm_engine_new_with_config(*Config) ?*Engine; 52 | extern "c" fn wasm_engine_delete(*Engine) void; 53 | }; 54 | 55 | pub const Store = opaque { 56 | /// Initializes a new `Store` based on the given `Engine` 57 | pub fn init(engine: *Engine) !*Store { 58 | return wasm_store_new(engine) orelse Error.StoreInit; 59 | } 60 | 61 | /// Frees the resource of the `Store` itself 62 | pub fn deinit(self: *Store) void { 63 | wasm_store_delete(self); 64 | } 65 | 66 | extern "c" fn wasm_store_new(*Engine) ?*Store; 67 | extern "c" fn wasm_store_delete(*Store) void; 68 | }; 69 | 70 | pub const Module = opaque { 71 | /// Initializes a new `Module` using the supplied Store and wasm bytecode 72 | pub fn init(store: *Store, bytes: []const u8) !*Module { 73 | var byte_vec = ByteVec.initWithCapacity(bytes.len); 74 | defer byte_vec.deinit(); 75 | 76 | var i: usize = 0; 77 | var ptr = byte_vec.data; 78 | while (i < bytes.len) : (i += 1) { 79 | ptr.* = bytes[i]; 80 | ptr += 1; 81 | } 82 | 83 | return wasm_module_new(store, &byte_vec) orelse return Error.ModuleInit; 84 | } 85 | 86 | pub fn deinit(self: *Module) void { 87 | wasm_module_delete(self); 88 | } 89 | 90 | /// Returns a list of export types in `ExportTypeVec` 91 | pub fn exports(self: *Module) ExportTypeVec { 92 | var vec: ExportTypeVec = undefined; 93 | wasm_module_exports(self, &vec); 94 | return vec; 95 | } 96 | 97 | extern "c" fn wasm_module_new(*Store, *const ByteVec) ?*Module; 98 | extern "c" fn wasm_module_delete(*Module) void; 99 | extern "c" fn wasm_module_exports(?*const Module, *ExportTypeVec) void; 100 | }; 101 | 102 | fn cb(params: ?*const Valtype, results: ?*Valtype) callconv(.C) ?*Trap { 103 | _ = params; 104 | _ = results; 105 | const func = @intToPtr(fn () void, CALLBACK); 106 | func(); 107 | return null; 108 | } 109 | 110 | pub const Func = opaque { 111 | pub const CallError = error{ 112 | /// Failed to call the function 113 | /// and resulted into an error 114 | InnerError, 115 | /// When the user provided a different ResultType to `Func.call` 116 | /// than what is defined by the wasm binary 117 | InvalidResultType, 118 | /// The given argument count to `Func.call` mismatches that 119 | /// of the func argument count of the wasm binary 120 | InvalidParamCount, 121 | /// The wasm function number of results mismatch that of the given 122 | /// ResultType to `Func.Call`. Note that `void` equals to 0 result types. 123 | InvalidResultCount, 124 | /// Function call resulted in an unexpected trap. 125 | Trap, 126 | }; 127 | pub fn init(store: *Store, callback: anytype) !*Func { 128 | const cb_meta = @typeInfo(@TypeOf(callback)); 129 | switch (cb_meta) { 130 | .Fn => { 131 | if (cb_meta.Fn.args.len > 0 or cb_meta.Fn.return_type.? != void) { 132 | @compileError("only callbacks with no input args and no results are currently supported"); 133 | } 134 | }, 135 | else => @compileError("only functions can be used as callbacks into Wasm"), 136 | } 137 | CALLBACK = @ptrToInt(callback); 138 | 139 | var args = ValtypeVec.empty(); 140 | var results = ValtypeVec.empty(); 141 | 142 | const functype = wasm_functype_new(&args, &results) orelse return Error.FuncInit; 143 | defer wasm_functype_delete(functype); 144 | 145 | return wasm_func_new(store, functype, cb) orelse Error.FuncInit; 146 | } 147 | 148 | /// Returns the `Func` as an `Extern` 149 | /// 150 | /// Owned by `self` and shouldn't be deinitialized 151 | pub fn asExtern(self: *Func) *Extern { 152 | return wasm_func_as_extern(self).?; 153 | } 154 | 155 | /// Returns the `Func` from an `Extern` 156 | /// return null if extern's type isn't a functype 157 | /// 158 | /// Owned by `extern_func` and shouldn't be deinitialized 159 | pub fn fromExtern(extern_func: *Extern) ?*Func { 160 | return extern_func.asFunc(); 161 | } 162 | 163 | /// Creates a copy of the current `Func` 164 | /// returned copy is owned by the caller and must be freed 165 | /// by the owner 166 | pub fn copy(self: *Func) *Func { 167 | return self.wasm_func_copy().?; 168 | } 169 | 170 | /// Tries to call the wasm function 171 | /// expects `args` to be tuple of arguments 172 | pub fn call(self: *Func, comptime ResultType: type, args: anytype) CallError!ResultType { 173 | if (!comptime trait.isTuple(@TypeOf(args))) 174 | @compileError("Expected 'args' to be a tuple, but found type '" ++ @typeName(@TypeOf(args)) ++ "'"); 175 | 176 | const args_len = args.len; 177 | comptime var wasm_args: [args_len]Value = undefined; 178 | inline for (wasm_args) |*arg, i| { 179 | arg.* = switch (@TypeOf(args[i])) { 180 | i32, u32 => .{ .kind = .i32, .of = .{ .i32 = @bitCast(i32, args[i]) } }, 181 | i64, u64 => .{ .kind = .i64, .of = .{ .i64 = @bitCast(i64, args[i]) } }, 182 | f32 => .{ .kind = .f32, .of = .{ .f32 = args[i] } }, 183 | f64 => .{ .kind = .f64, .of = .{ .f64 = args[i] } }, 184 | *Func => .{ .kind = .funcref, .of = .{ .ref = args[i] } }, 185 | *Extern => .{ .kind = .anyref, .of = .{ .ref = args[i] } }, 186 | else => |ty| @compileError("Unsupported argument type '" ++ @typeName(ty) + "'"), 187 | }; 188 | } 189 | 190 | // TODO multiple return values 191 | const result_len: usize = if (ResultType == void) 0 else 1; 192 | if (result_len != wasm_func_result_arity(self)) return CallError.InvalidResultCount; 193 | if (args_len != wasm_func_param_arity(self)) return CallError.InvalidParamCount; 194 | 195 | const final_args = ValVec{ 196 | .size = args_len, 197 | .data = if (args_len == 0) undefined else @ptrCast([*]Value, &wasm_args), 198 | }; 199 | 200 | var result_list = ValVec.initWithCapacity(result_len); 201 | defer result_list.deinit(); 202 | 203 | const trap = wasm_func_call(self, &final_args, &result_list); 204 | 205 | if (trap) |t| { 206 | t.deinit(); 207 | // TODO handle trap message 208 | log.err("code unexpectedly trapped", .{}); 209 | return CallError.Trap; 210 | } 211 | 212 | if (ResultType == void) return; 213 | 214 | // TODO: Handle multiple returns 215 | const result_ty = result_list.data[0]; 216 | if (!matchesKind(ResultType, result_ty.kind)) return CallError.InvalidResultType; 217 | 218 | return switch (ResultType) { 219 | i32, u32 => @intCast(ResultType, result_ty.of.i32), 220 | i64, u64 => @intCast(ResultType, result_ty.of.i64), 221 | f32 => result_ty.of.f32, 222 | f64 => result_ty.of.f64, 223 | *Func => @ptrCast(?*Func, result_ty.of.ref).?, 224 | *Extern => @ptrCast(?*Extern, result_ty.of.ref).?, 225 | else => |ty| @compileError("Unsupported result type '" ++ @typeName(ty) ++ "'"), 226 | }; 227 | } 228 | 229 | pub fn deinit(self: *Func) void { 230 | wasm_func_delete(self); 231 | } 232 | 233 | /// Returns tue if the given `kind` of `Valkind` can coerce to type `T` 234 | pub fn matchesKind(comptime T: type, kind: Valkind) bool { 235 | return switch (T) { 236 | i32, u32 => kind == .i32, 237 | i64, u64 => kind == .i64, 238 | f32 => kind == .f32, 239 | f64 => kind == .f64, 240 | *Func => kind == .funcref, 241 | *Extern => kind == .ref, 242 | else => false, 243 | }; 244 | } 245 | 246 | extern "c" fn wasm_func_new(*Store, ?*anyopaque, Callback) ?*Func; 247 | extern "c" fn wasm_func_delete(*Func) void; 248 | extern "c" fn wasm_func_as_extern(*Func) ?*Extern; 249 | extern "c" fn wasm_func_copy(*const Func) ?*Func; 250 | extern "c" fn wasm_func_call(*Func, *const ValVec, *ValVec) ?*Trap; 251 | extern "c" fn wasm_func_result_arity(*Func) usize; 252 | extern "c" fn wasm_func_param_arity(*Func) usize; 253 | }; 254 | 255 | pub const Instance = opaque { 256 | /// Initializes a new `Instance` using the given `store` and `mode`. 257 | /// The given slice defined in `import` must match what was initialized 258 | /// using the same `Store` as given. 259 | pub fn init(store: *Store, module: *Module, import: []const *Func) !*Instance { 260 | var trap: ?*Trap = null; 261 | var imports = ExternVec.initWithCapacity(import.len); 262 | defer imports.deinit(); 263 | 264 | var ptr = imports.data; 265 | for (import) |func| { 266 | ptr.* = func.asExtern(); 267 | ptr += 1; 268 | } 269 | 270 | const instance = wasm_instance_new(store, module, &imports, &trap); 271 | 272 | if (trap) |t| { 273 | defer t.deinit(); 274 | // TODO handle trap message 275 | log.err("code unexpectedly trapped", .{}); 276 | return Error.InstanceInit; 277 | } 278 | 279 | return instance orelse Error.InstanceInit; 280 | } 281 | 282 | /// Returns an export func by its name if found 283 | /// Asserts the export is of type `Func` 284 | /// The returned `Func` is a copy and must be freed by the caller 285 | pub fn getExportFunc(self: *Instance, module: *Module, name: []const u8) ?*Func { 286 | return if (self.getExport(module, name)) |exp| { 287 | defer exp.deinit(); // free the copy 288 | return exp.asFunc().copy(); 289 | } else null; 290 | } 291 | 292 | /// Returns an export by its name and `null` when not found 293 | /// The `Extern` is copied and must be freed manually 294 | /// 295 | /// a `Module` must be provided to find an extern by its name, rather than index. 296 | /// use getExportByIndex for quick access to an extern by index. 297 | pub fn getExport(self: *Instance, module: *Module, name: []const u8) ?*Extern { 298 | var externs: ExternVec = undefined; 299 | wasm_instance_exports(self, &externs); 300 | defer externs.deinit(); 301 | 302 | var exports = module.exports(); 303 | defer exports.deinit(); 304 | 305 | return for (exports.toSlice()) |export_type, index| { 306 | const ty = export_type orelse continue; 307 | const type_name = ty.name(); 308 | defer type_name.deinit(); 309 | 310 | if (std.mem.eql(u8, name, type_name.toSlice())) { 311 | if (externs.data[index]) |ext| { 312 | break ext.copy(); 313 | } 314 | } 315 | } else null; 316 | } 317 | 318 | /// Returns an export by a given index. Returns null when the index 319 | /// is out of bounds. The extern is non-owned, meaning it's illegal 320 | /// behaviour to free its memory. 321 | pub fn getExportByIndex(self: *Instance, index: u32) ?*Extern { 322 | var externs: ExternVec = undefined; 323 | wasm_instance_exports(self, &externs); 324 | defer externs.deinit(); 325 | 326 | if (index > externs.size) return null; 327 | return externs.data[index].?; 328 | } 329 | 330 | /// Returns an exported `Memory` when found and `null` when not. 331 | /// The result is copied and must be freed manually by calling `deinit()` on the result. 332 | pub fn getExportMem(self: *Instance, module: *Module, name: []const u8) ?*Memory { 333 | return if (self.getExport(module, name)) |exp| { 334 | defer exp.deinit(); // free the copy 335 | return exp.asMemory().copy(); 336 | } else null; 337 | } 338 | 339 | /// Frees the `Instance`'s resources 340 | pub fn deinit(self: *Instance) void { 341 | wasm_instance_delete(self); 342 | } 343 | 344 | extern "c" fn wasm_instance_new(*Store, *const Module, *const ExternVec, *?*Trap) ?*Instance; 345 | extern "c" fn wasm_instance_delete(*Instance) void; 346 | extern "c" fn wasm_instance_exports(*Instance, *ExternVec) void; 347 | }; 348 | 349 | pub const Trap = opaque { 350 | pub fn deinit(self: *Trap) void { 351 | wasm_trap_delete(self); 352 | } 353 | 354 | /// Returns the trap message. 355 | /// Memory of the returned `ByteVec` must be freed using `deinit` 356 | pub fn message(self: *Trap) *ByteVec { 357 | var bytes: ?*ByteVec = null; 358 | wasm_trap_message(self, &bytes); 359 | return bytes.?; 360 | } 361 | 362 | extern "c" fn wasm_trap_delete(*Trap) void; 363 | extern "c" fn wasm_trap_message(*const Trap, out: *?*ByteVec) void; 364 | }; 365 | 366 | pub const Extern = opaque { 367 | /// Returns the `Extern` as a function 368 | /// returns `null` when the given `Extern` is not a function 369 | /// 370 | /// Asserts `Extern` is of type `Func` 371 | pub fn asFunc(self: *Extern) *Func { 372 | return wasm_extern_as_func(self).?; 373 | } 374 | 375 | /// Returns the `Extern` as a `Memory` object 376 | /// returns `null` when the given `Extern` is not a memory object 377 | /// 378 | /// Asserts `Extern` is of type `Memory` 379 | pub fn asMemory(self: *Extern) *Memory { 380 | return wasm_extern_as_memory(self).?; 381 | } 382 | 383 | /// Returns the `Extern` as a `Global` 384 | /// returns `null` when the given `Extern` is not a global 385 | /// 386 | /// Asserts `Extern` is of type `Global` 387 | pub fn asGlobal(self: *Extern) *Global { 388 | return wasm_extern_as_global(self).?; 389 | } 390 | 391 | /// Returns the `Extern` as a `Table` 392 | /// returns `null` when the given `Extern` is not a table 393 | /// 394 | /// Asserts `Extern` is of type `Table` 395 | pub fn asTable(self: *Extern) *Table { 396 | return wasm_extern_as_table(self).?; 397 | } 398 | 399 | /// Frees the memory of the `Extern` 400 | pub fn deinit(self: *Extern) void { 401 | wasm_extern_delete(self); 402 | } 403 | 404 | /// Creates a copy of the `Extern` and returns it 405 | /// Memory of the copied version must be freed manually by calling `deinit` 406 | /// 407 | /// Asserts the copy succeeds 408 | pub fn copy(self: *Extern) *Extern { 409 | return wasm_extern_copy(self).?; 410 | } 411 | 412 | /// Checks if the given externs are equal and returns true if so 413 | pub fn eql(self: *const Extern, other: *const Extern) bool { 414 | return wasm_extern_same(self, other); 415 | } 416 | 417 | /// Returns the type of an `Extern` as `ExternType` 418 | pub fn toType(self: *const Extern) *ExternType { 419 | return wasm_extern_type(self).?; 420 | } 421 | 422 | /// Returns the kind of an `Extern` 423 | pub fn kind(self: *const Extern) ExternKind { 424 | return wasm_extern_kind(self); 425 | } 426 | 427 | extern "c" fn wasm_extern_as_func(*Extern) ?*Func; 428 | extern "c" fn wasm_extern_as_memory(*Extern) ?*Memory; 429 | extern "c" fn wasm_extern_as_global(*Extern) ?*Global; 430 | extern "c" fn wasm_extern_as_table(*Extern) ?*Table; 431 | extern "c" fn wasm_extern_delete(*Extern) void; 432 | extern "c" fn wasm_extern_copy(*Extern) ?*Extern; 433 | extern "c" fn wasm_extern_same(*const Extern, *const Extern) bool; 434 | extern "c" fn wasm_extern_type(?*const Extern) ?*ExternType; 435 | extern "c" fn wasm_extern_kind(?*const Extern) ExternKind; 436 | }; 437 | 438 | pub const ExternKind = std.wasm.ExternalKind; 439 | 440 | pub const ExternType = opaque { 441 | /// Creates an `ExternType` from an existing `Extern` 442 | pub fn fromExtern(extern_object: *const Extern) *ExternType { 443 | return Extern.wasm_extern_type(extern_object).?; 444 | } 445 | 446 | /// Frees the memory of given `ExternType` 447 | pub fn deinit(self: *ExternType) void { 448 | wasm_externtype_delete(self); 449 | } 450 | 451 | /// Copies the given export type. Returned copy's memory must be 452 | /// freed manually by calling `deinit()` on the object. 453 | pub fn copy(self: *ExportType) *ExportType { 454 | return wasm_externtype_copy(self).?; 455 | } 456 | 457 | /// Returns the `ExternKind` from a given export type. 458 | pub fn kind(self: *const ExportType) ExternKind { 459 | return wasm_externtype_kind(self); 460 | } 461 | 462 | extern "c" fn wasm_externtype_delete(?*ExportType) void; 463 | extern "c" fn wasm_externtype_copy(?*ExportType) ?*ExportType; 464 | extern "c" fn wasm_externtype_kind(?*const ExternType) ExternKind; 465 | }; 466 | 467 | pub const Memory = opaque { 468 | /// Creates a new `Memory` object for the given `Store` and `MemoryType` 469 | pub fn init(store: *Store, mem_type: *const MemoryType) !*Memory { 470 | return wasm_memory_new(store, mem_type) orelse error.MemoryInit; 471 | } 472 | 473 | /// Returns the `MemoryType` of a given `Memory` object 474 | pub fn getType(self: *const Memory) *MemoryType { 475 | return wasm_memory_type(self).?; 476 | } 477 | 478 | /// Frees the memory of the `Memory` object 479 | pub fn deinit(self: *Memory) void { 480 | wasm_memory_delete(self); 481 | } 482 | 483 | /// Creates a copy of the given `Memory` object 484 | /// Returned copy must be freed manually. 485 | pub fn copy(self: *const Memory) ?*Memory { 486 | return wasm_memory_copy(self); 487 | } 488 | 489 | /// Returns true when the given `Memory` objects are equal 490 | pub fn eql(self: *const Memory, other: *const Memory) bool { 491 | return wasm_memory_same(self, other); 492 | } 493 | 494 | /// Returns a pointer-to-many bytes 495 | /// 496 | /// Tip: Use toSlice() to get a slice for better ergonomics 497 | pub fn data(self: *Memory) [*]u8 { 498 | return wasm_memory_data(self); 499 | } 500 | 501 | /// Returns the data size of the `Memory` object. 502 | pub fn size(self: *const Memory) usize { 503 | return wasm_memory_data_size(self); 504 | } 505 | 506 | /// Returns the amount of pages the `Memory` object consists of 507 | /// where each page is 65536 bytes 508 | pub fn pages(self: *const Memory) u32 { 509 | return wasm_memory_size(self); 510 | } 511 | 512 | /// Convenient helper function to represent the memory 513 | /// as a slice of bytes. Memory is however still owned by wasm 514 | /// and must be freed by calling `deinit` on the original `Memory` object 515 | pub fn toSlice(self: *Memory) []const u8 { 516 | var slice: []const u8 = undefined; 517 | slice.ptr = self.data(); 518 | slice.len = self.size(); 519 | return slice; 520 | } 521 | 522 | /// Increases the amount of memory pages by the given count. 523 | pub fn grow(self: *Memory, page_count: u32) error{OutOfMemory}!void { 524 | if (!wasm_memory_grow(self, page_count)) return error.OutOfMemory; 525 | } 526 | 527 | extern "c" fn wasm_memory_delete(*Memory) void; 528 | extern "c" fn wasm_memory_copy(*const Memory) ?*Memory; 529 | extern "c" fn wasm_memory_same(*const Memory, *const Memory) bool; 530 | extern "c" fn wasm_memory_new(*Store, *const MemoryType) ?*Memory; 531 | extern "c" fn wasm_memory_type(*const Memory) *MemoryType; 532 | extern "c" fn wasm_memory_data(*Memory) [*]u8; 533 | extern "c" fn wasm_memory_data_size(*const Memory) usize; 534 | extern "c" fn wasm_memory_grow(*Memory, delta: u32) bool; 535 | extern "c" fn wasm_memory_size(*const Memory) u32; 536 | }; 537 | 538 | pub const Limits = extern struct { 539 | min: u32, 540 | max: u32, 541 | }; 542 | 543 | pub const MemoryType = opaque { 544 | pub fn init(limits: Limits) !*MemoryType { 545 | return wasm_memorytype_new(&limits) orelse return error.InitMemoryType; 546 | } 547 | 548 | pub fn deinit(self: *MemoryType) void { 549 | wasm_memorytype_delete(self); 550 | } 551 | 552 | extern "c" fn wasm_memorytype_new(*const Limits) ?*MemoryType; 553 | extern "c" fn wasm_memorytype_delete(*MemoryType) void; 554 | }; 555 | 556 | // TODO: implement table and global types 557 | pub const Table = opaque {}; 558 | pub const Global = opaque {}; 559 | 560 | pub const ExportType = opaque { 561 | /// Returns the name of the given `ExportType` 562 | pub fn name(self: *ExportType) *ByteVec { 563 | return self.wasm_exporttype_name().?; 564 | } 565 | 566 | extern "c" fn wasm_exporttype_name(*ExportType) ?*ByteVec; 567 | }; 568 | 569 | pub const ExportTypeVec = extern struct { 570 | size: usize, 571 | data: [*]?*ExportType, 572 | 573 | /// Returns a slice of an `ExportTypeVec`. 574 | /// Memory is still owned by the runtime and can only be freed using 575 | /// `deinit()` on the original `ExportTypeVec` 576 | pub fn toSlice(self: *const ExportTypeVec) []const ?*ExportType { 577 | return self.data[0..self.size]; 578 | } 579 | 580 | pub fn deinit(self: *ExportTypeVec) void { 581 | self.wasm_exporttype_vec_delete(); 582 | } 583 | 584 | extern "c" fn wasm_exporttype_vec_delete(*ExportTypeVec) void; 585 | }; 586 | 587 | pub const Callback = fn (?*const Valtype, ?*Valtype) callconv(.C) ?*Trap; 588 | 589 | pub const ByteVec = extern struct { 590 | size: usize, 591 | data: [*]u8, 592 | 593 | /// Initializes a new wasm byte vector 594 | pub fn initWithCapacity(size: usize) ByteVec { 595 | var bytes: ByteVec = undefined; 596 | wasm_byte_vec_new_uninitialized(&bytes, size); 597 | return bytes; 598 | } 599 | 600 | /// Initializes and copies contents of the input slice 601 | pub fn fromSlice(slice: []const u8) ByteVec { 602 | var bytes: ByteVec = undefined; 603 | wasm_byte_vec_new(&bytes, slice.len, slice.ptr); 604 | return bytes; 605 | } 606 | 607 | /// Returns a slice to the byte vector 608 | pub fn toSlice(self: ByteVec) []const u8 { 609 | return self.data[0..self.size]; 610 | } 611 | 612 | /// Frees the memory allocated by initWithCapacity 613 | pub fn deinit(self: *ByteVec) void { 614 | wasm_byte_vec_delete(self); 615 | } 616 | 617 | extern "c" fn wasm_byte_vec_new(*ByteVec, usize, [*]const u8) void; 618 | extern "c" fn wasm_byte_vec_new_uninitialized(*ByteVec, usize) void; 619 | extern "c" fn wasm_byte_vec_delete(*ByteVec) void; 620 | }; 621 | 622 | pub const NameVec = extern struct { 623 | size: usize, 624 | data: [*]const u8, 625 | 626 | pub fn fromSlice(slice: []const u8) NameVec { 627 | return .{ .size = slice.len, .data = slice.ptr }; 628 | } 629 | }; 630 | 631 | pub const ExternVec = extern struct { 632 | size: usize, 633 | data: [*]?*Extern, 634 | 635 | pub fn empty() ExternVec { 636 | return .{ .size = 0, .data = undefined }; 637 | } 638 | 639 | pub fn deinit(self: *ExternVec) void { 640 | wasm_extern_vec_delete(self); 641 | } 642 | 643 | pub fn initWithCapacity(size: usize) ExternVec { 644 | var externs: ExternVec = undefined; 645 | wasm_extern_vec_new_uninitialized(&externs, size); 646 | return externs; 647 | } 648 | 649 | extern "c" fn wasm_extern_vec_new_empty(*ExternVec) void; 650 | extern "c" fn wasm_extern_vec_new_uninitialized(*ExternVec, usize) void; 651 | extern "c" fn wasm_extern_vec_delete(*ExternVec) void; 652 | }; 653 | 654 | pub const Valkind = enum(u8) { 655 | i32 = 0, 656 | i64 = 1, 657 | f32 = 2, 658 | f64 = 3, 659 | anyref = 128, 660 | funcref = 129, 661 | }; 662 | 663 | pub const Value = extern struct { 664 | kind: Valkind, 665 | of: extern union { 666 | i32: i32, 667 | i64: i64, 668 | f32: f32, 669 | f64: f64, 670 | ref: ?*anyopaque, 671 | }, 672 | }; 673 | 674 | pub const Valtype = opaque { 675 | /// Initializes a new `Valtype` based on the given `Valkind` 676 | pub fn init(valKind: Valkind) *Valtype { 677 | return wasm_valtype_new(@enumToInt(valKind)); 678 | } 679 | 680 | pub fn deinit(self: *Valtype) void { 681 | wasm_valtype_delete(self); 682 | } 683 | 684 | /// Returns the `Valkind` of the given `Valtype` 685 | pub fn kind(self: *Valtype) Valkind { 686 | return @intToEnum(Valkind, wasm_valtype_kind(self)); 687 | } 688 | 689 | extern "c" fn wasm_valtype_new(kind: u8) *Valtype; 690 | extern "c" fn wasm_valtype_delete(*Valkind) void; 691 | extern "c" fn wasm_valtype_kind(*Valkind) u8; 692 | }; 693 | 694 | pub const ValtypeVec = extern struct { 695 | size: usize, 696 | data: [*]?*Valtype, 697 | 698 | pub fn empty() ValtypeVec { 699 | return .{ .size = 0, .data = undefined }; 700 | } 701 | }; 702 | 703 | pub const ValVec = extern struct { 704 | size: usize, 705 | data: [*]Value, 706 | 707 | pub fn initWithCapacity(size: usize) ValVec { 708 | var bytes: ValVec = undefined; 709 | wasm_val_vec_new_uninitialized(&bytes, size); 710 | return bytes; 711 | } 712 | 713 | pub fn deinit(self: *ValVec) void { 714 | self.wasm_val_vec_delete(); 715 | } 716 | 717 | extern "c" fn wasm_val_vec_new_uninitialized(*ValVec, usize) void; 718 | extern "c" fn wasm_val_vec_delete(*ValVec) void; 719 | }; 720 | 721 | // Func 722 | pub extern "c" fn wasm_functype_new(args: *ValtypeVec, results: *ValtypeVec) ?*anyopaque; 723 | pub extern "c" fn wasm_functype_delete(functype: *anyopaque) void; 724 | 725 | pub const WasiConfig = opaque { 726 | /// Options to inherit when inherriting configs 727 | /// By default all is `true` as you often want to 728 | /// inherit everything rather than something specifically. 729 | const InheritOptions = struct { 730 | argv: bool = true, 731 | env: bool = true, 732 | std_in: bool = true, 733 | std_out: bool = true, 734 | std_err: bool = true, 735 | }; 736 | 737 | pub fn init() !*WasiConfig { 738 | return wasi_config_new() orelse error.ConfigInit; 739 | } 740 | 741 | pub fn deinit(self: *WasiConfig) void { 742 | wasi_config_delete(self); 743 | } 744 | 745 | /// Allows to inherit the native environment into the current config. 746 | /// Inherits everything by default. 747 | pub fn inherit(self: *WasiConfig, options: InheritOptions) void { 748 | if (options.argv) self.inheritArgv(); 749 | if (options.env) self.inheritEnv(); 750 | if (options.std_in) self.inheritStdIn(); 751 | if (options.std_out) self.inheritStdOut(); 752 | if (options.std_err) self.inheritStdErr(); 753 | } 754 | 755 | pub fn inheritArgv(self: *WasiConfig) void { 756 | wasi_config_inherit_argv(self); 757 | } 758 | 759 | pub fn inheritEnv(self: *WasiConfig) void { 760 | wasi_config_inherit_env(self); 761 | } 762 | 763 | pub fn inheritStdIn(self: *WasiConfig) void { 764 | wasi_config_inherit_stdin(self); 765 | } 766 | 767 | pub fn inheritStdOut(self: *WasiConfig) void { 768 | wasi_config_inherit_stdout(self); 769 | } 770 | 771 | pub fn inheritStdErr(self: *WasiConfig) void { 772 | wasi_config_inherit_stderr(self); 773 | } 774 | 775 | extern "c" fn wasi_config_new() ?*WasiConfig; 776 | extern "c" fn wasi_config_delete(?*WasiConfig) void; 777 | extern "c" fn wasi_config_inherit_argv(?*WasiConfig) void; 778 | extern "c" fn wasi_config_inherit_env(?*WasiConfig) void; 779 | extern "c" fn wasi_config_inherit_stdin(?*WasiConfig) void; 780 | extern "c" fn wasi_config_inherit_stdout(?*WasiConfig) void; 781 | extern "c" fn wasi_config_inherit_stderr(?*WasiConfig) void; 782 | }; 783 | 784 | pub const WasiInstance = opaque { 785 | pub fn init(store: *Store, name: [*:0]const u8, config: ?*WasiConfig, trap: *?*Trap) !*WasiInstance { 786 | return wasi_instance_new(store, name, config, trap) orelse error.InstanceInit; 787 | } 788 | 789 | pub fn deinit(self: *WasiInstance) void { 790 | wasm_instance_delete(self); 791 | } 792 | 793 | extern "c" fn wasi_instance_new(?*Store, [*:0]const u8, ?*WasiConfig, *?*Trap) ?*WasiInstance; 794 | extern "c" fn wasm_instance_delete(?*WasiInstance) void; 795 | }; 796 | 797 | test "run_tests" { 798 | testing.refAllDecls(@This()); 799 | } 800 | --------------------------------------------------------------------------------