194 lines
4.4 KiB
Markdown
194 lines
4.4 KiB
Markdown
# @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~~
|