Skip to content

Commit

Permalink
feat(wasm-api): update core API & bindings
Browse files Browse the repository at this point in the history
- add _panic(), timer(), epoch() core API fns
- update printI/U64() fns to accept bigint
- update C & Zig bindings
- update tests
- add clang-format
  • Loading branch information
postspectacular committed Aug 29, 2022
1 parent 06bcf05 commit b185ea5
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 85 deletions.
12 changes: 12 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
BasedOnStyle: Google
AllowAllParametersOfDeclarationOnNextLine: false
AlignConsecutiveAssignments: true
AllowShortBlocksOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
BinPackParameters: false
BreakBeforeBraces: Attach
ColumnLimit: 96
ReflowComments: false
SortIncludes: true
54 changes: 34 additions & 20 deletions packages/wasm-api/include/wasmapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,33 @@ extern "C" {

// Declares an imported symbol from named import module
// The prefix is only used for the C side, NOT for exported name
#define WASM_IMPORT(MODULE, TYPE, NAME, PREFIX) \
extern __attribute__((import_module(MODULE), import_name(#NAME))) \
TYPE PREFIX##NAME
#define WASM_IMPORT(MODULE, TYPE, NAME, PREFIX) \
extern __attribute__((import_module(MODULE), import_name(#NAME))) TYPE PREFIX##NAME

// Same as EMSCRIPTEN_KEEP_ALIVE, ensures symbol will be exported
#define WASM_KEEP __attribute__((used))

// Generate malloc/free wrappers only if explicitly enabled by defining this
// symbol. If undefined some function stubs are exported.
#ifdef WASMAPI_MALLOC

#include <stdlib.h>
size_t WASM_KEEP _wasm_allocate(size_t numBytes) {
return (size_t)malloc(numBytes);
}
void WASM_KEEP _wasm_free(size_t addr) { free((void*)addr); }
void WASM_KEEP _wasm_free(size_t addr, size_t numBytes) {
free((void*)addr);
}

#else
size_t WASM_KEEP _wasm_allocate(size_t num_bytes) { return 0; }
void WASM_KEEP _wasm_free(size_t addr) {}
#endif

size_t WASM_KEEP _wasm_allocate(size_t numBytes) {
return 0;
}
void WASM_KEEP _wasm_free(size_t addr, size_t numBytes) {
}

#endif // WASMAPI_MALLOC

WASM_IMPORT("wasmapi", void, printI8, wasm_)(int8_t x);
WASM_IMPORT("wasmapi", void, printU8, wasm_)(uint8_t x);
Expand All @@ -44,23 +52,29 @@ WASM_IMPORT("wasmapi", void, printU64Hex, wasm_)(uint64_t x);
WASM_IMPORT("wasmapi", void, printF32, wasm_)(float x);
WASM_IMPORT("wasmapi", void, printF64, wasm_)(double x);

WASM_IMPORT("wasmapi", void, _printI8Array, wasm)(void* addr, size_t len);
WASM_IMPORT("wasmapi", void, _printU8Array, wasm)(void* addr, size_t len);
WASM_IMPORT("wasmapi", void, _printI16Array, wasm)(void* addr, size_t len);
WASM_IMPORT("wasmapi", void, _printU16Array, wasm)(void* addr, size_t len);
WASM_IMPORT("wasmapi", void, _printI32Array, wasm)(void* addr, size_t len);
WASM_IMPORT("wasmapi", void, _printU32Array, wasm)(void* addr, size_t len);
WASM_IMPORT("wasmapi", void, _printI64Array, wasm)(void* addr, size_t len);
WASM_IMPORT("wasmapi", void, _printU64Array, wasm)(void* addr, size_t len);
WASM_IMPORT("wasmapi", void, _printF32Array, wasm)(void* addr, size_t len);
WASM_IMPORT("wasmapi", void, _printF64Array, wasm)(void* addr, size_t len);
WASM_IMPORT("wasmapi", void, _printI8Array, wasm)(const int8_t* addr, size_t len);
WASM_IMPORT("wasmapi", void, _printU8Array, wasm)(const uint8_t* addr, size_t len);
WASM_IMPORT("wasmapi", void, _printI16Array, wasm)(const int16_t* addr, size_t len);
WASM_IMPORT("wasmapi", void, _printU16Array, wasm)(const uint16_t* addr, size_t len);
WASM_IMPORT("wasmapi", void, _printI32Array, wasm)(const int32_t* addr, size_t len);
WASM_IMPORT("wasmapi", void, _printU32Array, wasm)(const uint32_t* addr, size_t len);
WASM_IMPORT("wasmapi", void, _printI64Array, wasm)(const int64_t* addr, size_t len);
WASM_IMPORT("wasmapi", void, _printU64Array, wasm)(const uint64_t* addr, size_t len);
WASM_IMPORT("wasmapi", void, _printF32Array, wasm)(const float* addr, size_t len);
WASM_IMPORT("wasmapi", void, _printF64Array, wasm)(const double* addr, size_t len);

WASM_IMPORT("wasmapi", void, _printStr0, wasm)(void* addr);
WASM_IMPORT("wasmapi", void, _printStr, wasm)(void* addr, size_t len);
WASM_IMPORT("wasmapi", void, _printStr0, wasm)(const char* addr);
WASM_IMPORT("wasmapi", void, _printStr, wasm)(const char* addr, size_t len);

WASM_IMPORT("wasmapi", void, debug, wasm_)(void);
WASM_IMPORT("wasmapi", void, panic, wasm_)(const char*, size_t len);

void wasm_printPtr(void* ptr) { wasm_printU32Hex((size_t)ptr); }
WASM_IMPORT("wasmapi", double, timer, wasm_)(void);
WASM_IMPORT("wasmapi", uint64_t, epoch, wasm_)(void);

void wasm_printPtr(const void* ptr) {
wasm_printU32Hex((size_t)ptr);
}

#ifdef __cplusplus
}
Expand Down
89 changes: 44 additions & 45 deletions packages/wasm-api/include/wasmapi.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
const std = @import("std");
const root = @import("root");

pub extern "wasmapi" fn _panic(addr: [*]const u8, len: usize) noreturn;

pub fn panic(msg: []const u8, _: ?*std.builtin.StackTrace) noreturn {
_panic(msg.ptr, msg.len);
unreachable;
}

/// Obtains the allocator to be exposed to the WASM host env
/// (via `_wasm_allocate()` and `_wasm_free()`).
/// If the user defines a public `WASM_ALLOCATOR` in their root file
Expand Down Expand Up @@ -32,9 +39,9 @@ pub export fn _wasm_allocate(numBytes: usize) usize {
/// Frees chunk of heap memory (previously allocated using `_wasm_allocate()`)
/// starting at given address and of given byte length.
/// Note: This is a no-op if no allocator is configured (see `allocator()`)
pub export fn _wasm_free(addr: usize, numBytes: usize) void {
pub export fn _wasm_free(addr: [*]u8, numBytes: usize) void {
if (allocator()) |alloc| {
var mem = [2]usize{ addr, numBytes };
var mem = [2]usize{ @ptrToInt(addr), numBytes };
alloc.free(@ptrCast(*[]u8, &mem).*);
}
}
Expand All @@ -60,26 +67,12 @@ pub extern "wasmapi" fn printU32(x: u32) void;
/// Prints hex number using configured JS logger
pub extern "wasmapi" fn printU32Hex(x: u32) void;

/// Prints decomposed i64 number using configured JS logger
pub extern "wasmapi" fn _printI64(hi: i32, lo: i32) void;
/// Convenience wrapper for _printI64(), accepting an i64
pub fn printI64(x: i64) void {
_printI64(@truncate(i32, x >> 32), @truncate(i32, x));
}

/// Prints decomposed u64 number using configured JS logger
pub extern "wasmapi" fn _printU64(hi: u32, lo: u32) void;
/// Convenience wrapper for _printU64(), accepting an u64
pub fn printU64(x: u64) void {
_printU64(@truncate(u32, x >> 32), @truncate(u32, x));
}

/// Prints decomposed u64 hex number using configured JS logger
pub extern "wasmapi" fn _printU64Hex(hi: u32, lo: u32) void;
/// Convenience wrapper for _printU64Hex(), accepting an u64
pub fn printU64Hex(x: u64) void {
_printU64Hex(@truncate(u32, x >> 32), @truncate(u32, x));
}
/// Prints i64 number using configured JS logger
pub extern "wasmapi" fn printI64(x: i64) void;
/// Prints u64 number using configured JS logger
pub extern "wasmapi" fn printU64(x: u64) void;
/// Prints u64 hex number using configured JS logger
pub extern "wasmapi" fn printU64Hex(x: u64) void;

/// Prints number using configured JS logger
pub extern "wasmapi" fn printF32(x: f32) void;
Expand All @@ -92,74 +85,74 @@ pub fn printPtr(ptr: *const anyopaque) void {
}

/// Prints number array using configured JS logger
pub extern "wasmapi" fn _printI8Array(addr: usize, len: usize) void;
pub extern "wasmapi" fn _printI8Array(addr: [*]const i8, len: usize) void;
/// Prints number array using configured JS logger
pub extern "wasmapi" fn _printU8Array(addr: usize, len: usize) void;
pub extern "wasmapi" fn _printU8Array(addr: [*]const u8, len: usize) void;
/// Prints number array using configured JS logger
pub extern "wasmapi" fn _printI16Array(addr: usize, len: usize) void;
pub extern "wasmapi" fn _printI16Array(addr: [*]const i16, len: usize) void;
/// Prints number array using configured JS logger
pub extern "wasmapi" fn _printU16Array(addr: usize, len: usize) void;
pub extern "wasmapi" fn _printU16Array(addr: [*]const u16, len: usize) void;
/// Prints number array using configured JS logger
pub extern "wasmapi" fn _printI32Array(addr: usize, len: usize) void;
pub extern "wasmapi" fn _printI32Array(addr: [*]const i32, len: usize) void;
/// Prints number array using configured JS logger
pub extern "wasmapi" fn _printU32Array(addr: usize, len: usize) void;
pub extern "wasmapi" fn _printU32Array(addr: [*]const u32, len: usize) void;
/// Prints number array using configured JS logger
pub extern "wasmapi" fn _printI64Array(addr: usize, len: usize) void;
pub extern "wasmapi" fn _printI64Array(addr: [*]const i64, len: usize) void;
/// Prints number array using configured JS logger
pub extern "wasmapi" fn _printU64Array(addr: usize, len: usize) void;
pub extern "wasmapi" fn _printU64Array(addr: [*]const u64, len: usize) void;
/// Prints number array using configured JS logger
pub extern "wasmapi" fn _printF32Array(addr: usize, len: usize) void;
pub extern "wasmapi" fn _printF32Array(addr: [*]const f32, len: usize) void;
/// Prints number array using configured JS logger
pub extern "wasmapi" fn _printF64Array(addr: usize, len: usize) void;
pub extern "wasmapi" fn _printF64Array(addr: [*]const f64, len: usize) void;

/// Prints number array using configured JS logger
pub fn printI8Array(buf: []const i8) void {
_printI8Array(@ptrToInt(buf.ptr), buf.len);
_printI8Array(buf.ptr, buf.len);
}
/// Prints number array using configured JS logger
pub fn printU8Array(buf: []const u8) void {
_printU8Array(@ptrToInt(buf.ptr), buf.len);
_printU8Array(buf.ptr, buf.len);
}
/// Prints number array using configured JS logger
pub fn printI16Array(buf: []const i16) void {
_printI16Array(@ptrToInt(buf.ptr), buf.len);
_printI16Array(buf.ptr, buf.len);
}
/// Prints number array using configured JS logger
pub fn printU16Array(buf: []const u16) void {
_printU16Array(@ptrToInt(buf.ptr), buf.len);
_printU16Array(buf.ptr, buf.len);
}
/// Prints number array using configured JS logger
pub fn printI32Array(buf: []const i32) void {
_printI32Array(@ptrToInt(buf.ptr), buf.len);
_printI32Array(buf.ptr, buf.len);
}
/// Prints number array using configured JS logger
pub fn printU32Array(buf: []const u32) void {
_printU32Array(@ptrToInt(buf.ptr), buf.len);
_printU32Array(buf.ptr, buf.len);
}
/// Prints number array using configured JS logger
pub fn printI64Array(buf: []const i64) void {
_printI64Array(@ptrToInt(buf.ptr), buf.len);
_printI64Array(buf.ptr, buf.len);
}
/// Prints number array using configured JS logger
pub fn printU64Array(buf: []const u64) void {
_printU64Array(@ptrToInt(buf.ptr), buf.len);
_printU64Array(buf.ptr, buf.len);
}
/// Prints number array using configured JS logger
pub fn printF32Array(buf: []const f32) void {
_printF32Array(@ptrToInt(buf.ptr), buf.len);
_printF32Array(buf.ptr, buf.len);
}
/// Prints number array using configured JS logger
pub fn printF64Array(buf: []const f64) void {
_printF64Array(@ptrToInt(buf.ptr), buf.len);
_printF64Array(buf.ptr, buf.len);
}

/// Prints a zero-terminated string using configured JS logger
pub extern "wasmapi" fn _printStr0(addr: usize) void;
pub extern "wasmapi" fn _printStr0(addr: [*]const u8) void;
/// Prints a string of given length using configured JS logger
pub extern "wasmapi" fn _printStr(addr: usize, len: usize) void;
pub extern "wasmapi" fn _printStr(addr: [*]const u8, len: usize) void;
/// Convenience wrapper for _printStr, accepting a slice as arg
pub fn printStr(msg: []const u8) void {
_printStr(@ptrToInt(msg.ptr), msg.len);
_printStr(msg.ptr, msg.len);
}

/// Calls std.fmt.allocPrint to format given string, then calls `printStr()`
Expand All @@ -175,3 +168,9 @@ pub fn printFmt(comptime fmt: []const u8, args: anytype) void {

/// Triggers the JS/browser debugger
pub extern "wasmapi" fn debug() void;

/// Returns a JS/browser highres timer value (via `performance.now()`)
pub extern "wasmapi" fn timer() f64;

/// Returns a JS/browser Unix epoch (via `Date.now()`)
pub extern "wasmapi" fn epoch() u64;
11 changes: 7 additions & 4 deletions packages/wasm-api/src/api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { BigType, FloatType, Fn, Fn2 } from "@thi.ng/api";
import type { BigType, FloatType, Fn } from "@thi.ng/api";
import type { WasmBridge } from "./bridge.js";

export const PKG_NAME = "@thi.ng/wasm-api";
Expand Down Expand Up @@ -146,9 +146,9 @@ export interface CoreAPI extends WebAssembly.ModuleImports {
printI32: Fn<number, void>;
printU32: Fn<number, void>;
printU32Hex: Fn<number, void>;
_printI64: Fn2<number, number, void>;
_printU64: Fn2<number, number, void>;
_printU64Hex: Fn2<number, number, void>;
printI64: Fn<bigint, void>;
printU64: Fn<bigint, void>;
printU64Hex: Fn<bigint, void>;
printF32: Fn<number, void>;
printF64: Fn<number, void>;
_printI8Array: (addr: number, len: number) => void;
Expand All @@ -164,6 +164,9 @@ export interface CoreAPI extends WebAssembly.ModuleImports {
_printStr0: (addr: number) => void;
_printStr: (addr: number, len: number) => void;
debug: () => void;
_panic: (addr: number, len: number) => void;
timer: () => number;
epoch: () => bigint;
}

export interface WasmTypeBase {
Expand Down
31 changes: 17 additions & 14 deletions packages/wasm-api/src/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,19 @@ import type {
import { INotifyMixin } from "@thi.ng/api/mixins/inotify";
import { defError } from "@thi.ng/errors/deferror";
import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
import { U16, U32, U64HL, U8 } from "@thi.ng/hex";
import { U16, U32, U64BIG, U8 } from "@thi.ng/hex";
import type { ILogger } from "@thi.ng/logger";
import { ConsoleLogger } from "@thi.ng/logger/console";
import {
BigIntArray,
CoreAPI,
EVENT_MEMORY_CHANGED,
IWasmAPI,
WasmExports,
IWasmMemoryAccess,
EVENT_MEMORY_CHANGED,
WasmExports,
} from "./api.js";

const B32 = BigInt(32);

export const Panic = defError(() => "Panic");
export const OutOfMemoryError = defError(() => "Out of memory");

/**
Expand Down Expand Up @@ -73,21 +72,18 @@ export class WasmBridge<T extends WasmExports = WasmExports>
this.api = {
printI8: logN,
printU8: logN,
printU8Hex: (x: number) => this.logger.debug(`0x${U8(x)}`),
printI16: logN,
printU16: logN,
printU16Hex: (x: number) => this.logger.debug(`0x${U16(x)}`),
printI32: logN,
printU32: (x: number) => this.logger.debug(x >>> 0),
printU32Hex: (x: number) => this.logger.debug(`0x${U32(x)}`),
_printI64: (hi: number, lo: number) =>
this.logger.debug((BigInt(hi) << B32) | BigInt(lo)),
_printU64: (hi: number, lo: number) =>
this.logger.debug((BigInt(hi >>> 0) << B32) | BigInt(lo >>> 0)),
_printU64Hex: (hi: number, lo: number) =>
this.logger.debug(`0x${U64HL(hi, lo)}`),
printI64: (x: bigint) => this.logger.debug(x),
printU64: (x: bigint) => this.logger.debug(x),
printF32: logN,
printF64: logN,
printU8Hex: (x: number) => this.logger.debug(`0x${U8(x)}`),
printU16Hex: (x: number) => this.logger.debug(`0x${U16(x)}`),
printU32Hex: (x: number) => this.logger.debug(`0x${U32(x)}`),
printU64Hex: (x: bigint) => this.logger.debug(`0x${U64BIG(x)}`),

_printI8Array: logA(this.getI8Array.bind(this)),
_printU8Array: logA(this.getU8Array.bind(this)),
Expand All @@ -109,6 +105,13 @@ export class WasmBridge<T extends WasmExports = WasmExports>
debug: () => {
debugger;
},

_panic: (addr, len) => {
throw new Panic(this.getString(addr, len));
},

timer: () => performance.now(),
epoch: () => BigInt(Date.now()),
};
}

Expand Down
Binary file modified packages/wasm-api/test/custom.wasm
Binary file not shown.
8 changes: 6 additions & 2 deletions packages/wasm-api/test/custom.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ const std = @import("std");

/// Fill vec2 with random values
/// Associate this function with the "custom" import section
extern "custom" fn setVec2(addr: usize) void;
extern "custom" fn setVec2(addr: *[2]f32) void;

export fn test_setVec2() void {
var foo = [2]f32{ 0, 0 };
js.printF32Array(foo[0..]);
setVec2(@ptrToInt(&foo));
setVec2(&foo);
js.printF32Array(foo[0..]);
}

export fn test_epoch() void {
js.printU64(js.epoch());
}
6 changes: 6 additions & 0 deletions packages/wasm-api/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ group("wasm-api", {
custom: async ({ done }) => {
interface CustomWasm extends WasmExports {
test_setVec2: () => void;
test_epoch: () => void;
}
class CustomAPI implements IWasmAPI {
parent!: WasmBridge;
Expand Down Expand Up @@ -103,6 +104,11 @@ group("wasm-api", {
assert.strictEqual(logger.journal.length, 3);
assert.strictEqual(logger.journal[1][3], "0, 0");
assert.strictEqual(logger.journal[2][3], "10, 20");

logger.clear();
const epoch = bridge.api.epoch();
bridge.exports.test_epoch();
assert.ok(logger.journal[0][3] >= epoch);
done();
},
});

0 comments on commit b185ea5

Please sign in to comment.