Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

Write low-level WebAssembly, from JavaScript

License

NotificationsYou must be signed in to change notification settings

zksecurity/wasmati

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Write low-level WebAssembly, from JavaScript

wasmati is a TS library that lets you create Wasm modules by writing out their instructions.

  • 🥷 You want to create low-level, hand-optimized Wasm libraries? wasmati is the tool to do so effectively.
  • 🚀 You want to sprinkle some Wasm in your JS app, to speed up critical parts? wasmati gives you a JS-native way to achieve that.
  • ⚠️ You want to compile Wasm modules from a high-level language, like Rust or C? wasmati is not for you.
npm i wasmati
// example.tsimport{i64,func,Module}from"wasmati";constmyMultiply=func({in:[i64,i64],out:[i64]},([x,y])=>{i64.mul(x,y);});letmodule=Module({exports:{ myMultiply}});let{ instance}=awaitmodule.instantiate();letresult=instance.exports.myMultiply(5n,20n);console.log({ result});
$ node --experimental-strip-types example.ts{ result: 100n }

Features

  • Works in all modern browsers,node anddeno

  • Parity with WebAssembly. The API directly corresponds to Wasm opcodes, likei32.add etc. All opcodes and language features of thelatest WebAssembly spec (2.0) are supported.
    In addition, wasmati supports the following extensions which are not part of the spec at the time of writing:

  • Readability. Wasm code looks imperative - like writing WAT by hand, just with better DX:

constmyFunction=func({in:[i32,i32],out:[i32]},([x,y])=>{local.get(x);local.get(y);i32.add();i32.const(2);i32.shl();call(otherFunction);});
  • Optional syntax sugar to reduce boilerplate assembly likelocal.get andi32.const
constmyFunction=func({in:[i32,i32],out:[i32]},([x,y])=>{i32.add(x,y);// local.get(x), local.get(y) are filled ini32.shl($,2);// $ is the top of the stack; i32.const(2) is filled incall(otherFunction);});// or alsoconstmyFunction=func({in:[i32,i32],out:[i32]},([x,y])=>{letz=i32.add(x,y);call(otherFunction,[i32.shl(z,2)]);});
  • Type-safe. Example: Local variables are typed; instructions know their input types:
constmyFunction=func({in:[i32,i32],locals:[i64],out:[i32]},([x,y],[u])=>{i32.add(x,u);// type error: Type '"i64"' is not assignable to type '"i32"'.});
  • Great debugging DX. Stack traces point to the exact line in your code where an invalid opcode is called:
Error: i32.add: Expected i32 on the stack, got i64.    ...    at file:///home/gregor/code/wasmati/examples/example.ts:16:9
  • Easy construction of modules. Just declare exports; dependencies and imports are collected for you. Nothing ends up in the module which isn't needed by any of its exports or its start function.
letmem=memory({min:10});letmodule=Module({exports:{ myFunction, mem}});letinstance=awaitmodule.instantiate();
  • Excellent type inference. Example: Exported function types are inferred fromfunc definitions:
instance.exports.myFunction;//                 ^ (arg_0: number, arg_1: number) => number
  • Atomic import declaration. Imports are declared as types along with their JS values. Abstracts away the global "import object" that is separate from "import declaration".
constconsoleLog=importFunc({in:[i32],out:[]},(x)=>console.log("logging from wasm:",x));constmyFunction=func({in:[i32,i32],out:[i32]},([x,y])=>{call(consoleLog,[x]);i32.add(x,y);});
  • Great composability and IO
    • Internal representation of modules / funcs / etc is a readable JSON object
    • Convert to/from Wasm bytecode withmodule.toBytes(),Module.fromBytes(bytes)

Features that aren't implemented yet

PRs welcome!

  • Wasmati build. We want to add an optional build step which takes as input a file that exports yourModule, and compiles it to a file which doesn't depend on wasmati at runtime. Instead, it hard-codes the Wasm bytecode as base64 string, correctly imports all dependencies (imports) for the instantiation like the original file did, instantiates the module (top-level await) and exports the module's exports.
// example.tsletmodule=Module({exports:{ myFunction, mem}});export{moduleasdefault};
import{myFunction}from"./example.wasm.js";// example.wasm.js does not depend on wasmati at runtime
  • Experimental Wasm opcodes. We want to support opcodes from recently standardized or in-progress feature proposals (like this one) which haven't yet made it to the spec. The eventual goal is to support proposals as soon as they are implemented in at least one JS engine.

  • Custom module sections. We want to support creation and parsing of "custom sections" like thename section

Some ideas that are a bit further out:

  • Decompiler: takeany Wasm file and create wasmati TS code from it -- to modify it, debug it etc
  • Source maps, so you can look at the culprit JS code when Wasm throws an error
  • Optional JS interpreter which can take DSL code and execute itin JS
    • could enable even more flexible debugging -- inspect the stack, global/local scope etc

[8]ページ先頭

©2009-2025 Movatter.jp