| # @tybys/wasm-util |
|
|
| WebAssembly related utils for browser environment |
|
|
| **The output code is ES2019** |
|
|
| ## Features |
|
|
| All example code below need to be bundled by ES module bundlers like `webpack` / `rollup`, or specify import map in browser native ES module runtime. |
|
|
| ### WASI polyfill for browser |
|
|
| The API is similar to the `require('wasi').WASI` in Node.js. |
|
|
| You can use `memfs-browser` to provide filesystem capability. |
|
|
| - Example: [https://github.com/toyobayashi/wasi-wabt](https://github.com/toyobayashi/wasi-wabt) |
| - Demo: [https://toyobayashi.github.io/wasi-wabt/](https://toyobayashi.github.io/wasi-wabt/) |
|
|
| ```js |
| import { load, WASI } from '@tybys/wasm-util' |
| import { Volume, createFsFromVolume } from 'memfs-browser' |
| |
| const fs = createFsFromVolume(Volume.fromJSON({ |
| '/home/wasi': null |
| })) |
| |
| const wasi = new WASI({ |
| args: ['chrome', 'file.wasm'], |
| env: { |
| NODE_ENV: 'development', |
| WASI_SDK_PATH: '/opt/wasi-sdk' |
| }, |
| preopens: { |
| '/': '/' |
| }, |
| fs, |
| |
| // redirect stdout / stderr |
| |
| // print (text) { console.log(text) }, |
| // printErr (text) { console.error(text) } |
| }) |
| |
| const imports = { |
| wasi_snapshot_preview1: wasi.wasiImport |
| } |
| |
| const { module, instance } = await load('/path/to/file.wasm', imports) |
| wasi.start(instance) |
| // wasi.initialize(instance) |
| ``` |
|
|
| Implemented syscalls: [wasi_snapshot_preview1](#wasi_snapshot_preview1) |
|
|
| ### `load` / `loadSync` |
|
|
| `loadSync` has 4KB wasm size limit in browser. |
|
|
| ```js |
| // bundler |
| import { load, loadSync } from '@tybys/wasm-util' |
| |
| const imports = { /* ... */ } |
| |
| // using path |
| const { module, instance } = await load('/path/to/file.wasm', imports) |
| const { module, instance } = loadSync('/path/to/file.wasm', imports) |
| |
| // using URL |
| const { module, instance } = await load(new URL('./file.wasm', import.meta.url), imports) |
| const { module, instance } = loadSync(new URL('./file.wasm', import.meta.url), imports) |
| |
| // using Uint8Array |
| const buffer = new Uint8Array([ |
| 0x00, 0x61, 0x73, 0x6d, |
| 0x01, 0x00, 0x00, 0x00 |
| ]) |
| const { module, instance } = await load(buffer, imports) |
| const { module, instance } = loadSync(buffer, imports) |
| |
| // auto asyncify |
| const { |
| module, |
| instance: asyncifiedInstance |
| } = await load(buffer, imports, { /* asyncify options */}) |
| asyncifiedInstance.exports.fn() // => return Promise |
| ``` |
|
|
| ### Extend Memory instance |
|
|
| ```js |
| import { Memory, extendMemory } from '@tybys/wasm-util' |
| |
| const memory = new WebAssembly.Memory({ initial: 256 }) |
| // const memory = instance.exports.memory |
| |
| extendMemory(memory) |
| console.log(memory instanceof Memory) |
| console.log(memory instanceof WebAssembly.Memory) |
| // expose memory view getters like Emscripten |
| const { HEAPU8, HEAPU32, view } = memory |
| ``` |
|
|
| ### Asyncify wrap |
|
|
| Build the C code using `clang`, `wasm-ld` and `wasm-opt` |
|
|
| ```c |
| void async_sleep(int ms); |
| |
| int main() { |
| async_sleep(200); |
| return 0; |
| } |
| ``` |
|
|
| ```js |
| import { Asyncify } from '@tybys/wasm-util' |
| |
| const asyncify = new Asyncify() |
| |
| const imports = { |
| env: { |
| async_sleep: asyncify.wrapImportFunction(function (ms) { |
| return new Promise((resolve) => { |
| setTimeout(resolve, ms) |
| }) |
| }) |
| } |
| } |
| |
| // async_sleep(200) |
| const bytes = await (await fetch('/asyncfied_by_wasm-opt.wasm')).arrayBuffer() |
| const { instance } = await WebAssembly.instantiate(bytes, imports) |
| const asyncifiedInstance = asyncify.init(instance.exports.memory, instance, { |
| wrapExports: ['_start'] |
| }) |
| |
| const p = asyncifedInstance._start() |
| console.log(typeof p.then === 'function') |
| const now = Date.now() |
| await p |
| console.log(Date.now() - now >= 200) |
| ``` |
|
|
| ### wasi_snapshot_preview1 |
|
|
| - [x] args_get |
| - [x] args_sizes_get |
| - [x] environ_get |
| - [x] environ_sizes_get |
| - [x] clock_res_get |
| - [x] clock_time_get |
| - [ ] ~~fd_advise~~ |
| - [x] fd_allocate |
| - [x] fd_close |
| - [x] fd_datasync |
| - [x] fd_fdstat_get |
| - [ ] ~~fd_fdstat_set_flags~~ |
| - [x] fd_fdstat_set_rights |
| - [x] fd_filestat_get |
| - [x] fd_filestat_set_size |
| - [x] fd_filestat_set_times |
| - [x] fd_pread |
| - [x] fd_prestat_get |
| - [x] fd_prestat_dir_name |
| - [x] fd_pwrite |
| - [x] fd_read |
| - [x] fd_readdir |
| - [x] fd_renumber |
| - [x] fd_seek |
| - [x] fd_sync |
| - [x] fd_tell |
| - [x] fd_write |
| - [x] path_create_directory |
| - [x] path_filestat_get |
| - [x] path_filestat_set_times |
| - [x] path_link |
| - [x] path_open |
| - [x] path_readlink |
| - [x] path_remove_directory |
| - [x] path_rename |
| - [x] path_symlink |
| - [x] path_unlink_file |
| - [x] poll_oneoff (timer only) |
| - [x] proc_exit |
| - [ ] ~~proc_raise~~ |
| - [x] sched_yield |
| - [x] random_get |
| - [ ] ~~sock_recv~~ |
| - [ ] ~~sock_send~~ |
| - [ ] ~~sock_shutdown~~ |
|
|