All Projects → rsms → Wasm Util

rsms / Wasm Util

Licence: mit
WebAssembly utilities

Programming Languages

typescript
32286 projects

Labels

Projects that are alternatives of or similar to Wasm Util

rust-webview-todomvc-yew
lightweight desktop todomvc implementation using rust,wasm and web-view
Stars: ✭ 92 (-70.61%)
Mutual labels:  wasm
Octopus
Security Analysis tool for WebAssembly module (wasm) and Blockchain Smart Contracts (BTC/ETH/NEO/EOS)
Stars: ✭ 261 (-16.61%)
Mutual labels:  wasm
Modfy.video
A video transcoder and converter built using Web Assembly and FFMPEG to transcode and convert videos right in your browser while protecting your privacy
Stars: ✭ 283 (-9.58%)
Mutual labels:  wasm
x-img-diff-js
🎨 JavaScript library which compares 2 images and detect structural difference information using OpenCV(WebAssembly)
Stars: ✭ 43 (-86.26%)
Mutual labels:  wasm
wasm-arrays
A couple of helper functions to make WebAssembly array parameters easy to use
Stars: ✭ 33 (-89.46%)
Mutual labels:  wasm
Idawasm
IDA Pro loader and processor modules for WebAssembly
Stars: ✭ 264 (-15.65%)
Mutual labels:  wasm
oasis-rs
A humane blockchain programming framework.
Stars: ✭ 71 (-77.32%)
Mutual labels:  wasm
Highway
Performance-portable, length-agnostic SIMD with runtime dispatch
Stars: ✭ 301 (-3.83%)
Mutual labels:  wasm
Riscv Rust
RISC-V processor emulator written in Rust+WASM
Stars: ✭ 253 (-19.17%)
Mutual labels:  wasm
Wasabi
A dynamic analysis framework for WebAssembly programs.
Stars: ✭ 279 (-10.86%)
Mutual labels:  wasm
p2p-git-portal-poc
p2p git portal proof-of-concept using Svelte Golang/WASM (experimental)
Stars: ✭ 29 (-90.73%)
Mutual labels:  wasm
leaflet-rs
A wasm-bindgen wrapper for Leaflet.js
Stars: ✭ 19 (-93.93%)
Mutual labels:  wasm
Nectarjs
🔱 Javascript's God Mode. No VM. No Bytecode. No GC. Just native binaries.
Stars: ✭ 3,113 (+894.57%)
Mutual labels:  wasm
wasmer-zig
Zig bindings for the Wasmer WebAssembly runtime
Stars: ✭ 24 (-92.33%)
Mutual labels:  wasm
Wasmer Ruby
💎🕸 WebAssembly runtime for Ruby
Stars: ✭ 286 (-8.63%)
Mutual labels:  wasm
ux-charts
Simple, responsive, modern Charts with zero dependencies
Stars: ✭ 22 (-92.97%)
Mutual labels:  wasm
Wasm Git
GIT for nodejs and the browser using https://libgit2.org compiled to WebAssembly with https://emscripten.org
Stars: ✭ 261 (-16.61%)
Mutual labels:  wasm
Pigo
Fast face detection, pupil/eyes localization and facial landmark points detection library in pure Go.
Stars: ✭ 3,542 (+1031.63%)
Mutual labels:  wasm
Ant Design Blazor
🌈A set of enterprise-class UI components based on Ant Design and Blazor WebAssembly.
Stars: ✭ 3,890 (+1142.81%)
Mutual labels:  wasm
Glas
WebGL in WebAssembly with AssemblyScript
Stars: ✭ 278 (-11.18%)
Mutual labels:  wasm

wasm-util

Utilities for working with WebAssembly (aka WASM), to be used with TypeScript and JavaScript. This code currently supports version MVP-13 (candidate for v1) of WebAssembly.

Overview:

  • ast provides a full TypeScript type system for the complete WebAssembly specification.
  • ast.c provides constructors for all parts of a WebAssembly module.
  • ast.t is a table of AST node types and their respective internal symbols.
  • ast.sect_id is a table of section names and their respective identifiers as varunit7 objects.
  • ast.get provides helper functions for convenient access and traversal of an AST.
  • emit provides helpers for emitting WASM byte code from an AST.
  • repr generates human-readable text representations of an AST or ArrayBuffer.
  • lbtext generates Linear Bytecode text from AST instructions

I found myself relying on a very complicated tool chain (source build of llvm, binaryen, etc) while all I was looking for was to get close to WebAssembly. The prime component of wasm-util is ast which provides a convenient way of building complete WASM modules, with full static type-checking if you're using TypeScript.

Following is an example of building a module that provides the factorial function. Let's start by describing the function we're making in a C-like syntax:

int64 factorial(int64 n) {
  return (n == 0) ?
    1
  :
    n * factorial(n-1);
}

The equivalent WebAssembly code looks like this:

get_local 0    // push parameter #0 on stack.
i64.const 0    // push constant int64 "0" on stack.
i64.eq         // execute "eq" which pops two operands from stack
               //  and pushes int32 "1" or "0" on stack.
if i64         // pops one int32 from stack; if its not "0":
  i64.const 1  //   push constant int64 "0" on stack.
else           // else (if operand was "0"):
  get_local 0  //   push parameter #0 on stack. $1
  get_local 0  //   push parameter #0 on stack.
  i64.const 1  //   push constant int64 "0" on stack.
  i64.sub      //   execute "sub[tract]" which pops two operands
               //    from stack (parameter #0 and constant int64 "1")
               //    and finally pushes the result int64 on stack.
  call 0       //   call function #0 ("factorial") which pops one
               //    int64 from the stack and when it returns an
               //    int64 has been pushed on stack
  i64.mul      //   execute "sub[tract]" which pops two operands
               //    from stack ($1 and result from function call)
               //    and finally pushes the resulting int64 on stack
end            // ends function, returning one int64 result (on stack.)
               // Stack now contains one int64 value that's the result from one of
               // the two branches above.

The above code was printed by lbtext, for which we provided an AST built with the ast module:

import ... 'wasm-util/ast'
const mod = c.module([

  type_section([
    func_type([i64], i64), // type index = 0
  ]),
  
  function_section([
    varuint32(0), // function index = 0, using type index 0
  ]),
  
  export_section([
    // exports "factorial" as function at index 0
    export_entry(str_ascii("factorial"), external_kind.function, varuint32(0)),
  ]),

  code_section([
    // body of function at index 0:
    function_body([ /* additional local variables here */ ], [
      if_(i64, // i64 = result type of `if` expression
        i64.eq(get_local(i64, 0), i64.const(0)), // condition
        [ // then
          i64.const(1)
        ], [ // else
          i64.mul(
            get_local(i64, 0),
            call(i64, varuint32(0), [ // 0 = function index
              i64.sub(get_local(i64, 0), i64.const(1))
            ]))])])]
  )]
)

We can now generate WASM bytecode through the Emittable interface:

const emitbuf = new BufferedEmitter(new ArrayBuffer(mod.z))
mod.emit(emitbuf)
// the array buffer (emitbuf.buffer) now contains the complete module code

Or print a human-readable representation of the AST:

import { strRepr } from 'wasm-util/repr'
console.log(strRepr(mod))

Which yields the following in the console:

(module 13
  (section type 6 1
    (func_type (i64) i64))
  (section function 2 1 0)
  (section export 13 1
    (export_entry "factorial" external_kind.function 0))
  (section code 25 1
    (function_body 23 0
      (if [i64]
        (i64.eq
          (get_local [0])
          (i64.const [0])
        )
        (then
          (i64.const [1]))
        (else
          (i64.mul
            (get_local [0])
            (call [0]
              (i64.sub
                (get_local [0])
                (i64.const [1])
              )))) end) end)))

A complete version of this "factorial" demo can be found at test/build_factorial_test.ts.

ast

ast provides a full TypeScript type system for the complete WebAssembly specification including AST constructors and access functions.

Noteworthy properties of the AST:

  • Nodes are immutable and contains no parents, meaning that any subtree can be used in multiple locations without the need to copy any data (e.g. macros can be trivially "implemented" by building a structure once and using it multiple times.)
  • Nodes' underlying type structures are uniform to allow efficient JavaScript VM optimizations.
  • Nodes' TypeScript types are rich in expression but with almost zero effect on runtime code — i.e. the type_section constructor returns the same kind of underlying structure as import_section, but the two functions when operated in TypeScript returns two exclusive, incompatible types (TypeSection and ImportSection, respectively.)
  • Each node has the ability to emit WASM bytecode that represents itself in a very efficient and side-effect-free manner.

The AST is built in a way that makes it as portable and light-weight as possible, with two basic types: atoms and cells. An atom is a single value and represents some amount of bytes corresponding to actual WASM bytecode. A cell is a compount structure that contains other atoms and cells, possibly also represents WASM bytecode.

// N is the common type of all AST nodes
interface N extends Emittable {
  readonly t :TypeTag  // type
  readonly z :uint32   // size in bytes (includes size of any children)
  readonly v :any      // value
}
interface Atom<T> extends N {
  readonly v :T
}
interface Cell<T extends N> extends N {
  readonly v :T[]
}
interface Module ...

For the full type system, see ast.ts

Static type checking with TypeScript

When used in TypeScript, the operand types, immediate types and result types of opcode and compound instructions are checked at compile time. For instance, say that we're converting a certain code path from i32 to i64 and forget something:

i64.eq(i64.const(1), i32.const(3))

Then the TypeScript compiler will complain:

error TS2345: Argument of type 'Op' is not assignable to parameter of type 'Op'. Type 'I64' is not assignable to type 'I32'. Property '_I32' is missing in type 'I64'.

We can correct the error by replacing i32.const(3) with i64.const(3) in this case since the i64.eq function has the type signature (i64, i64) i32.

emit

AST nodes has the ability to efficiently produce its corresponding WASM bytecode through the Emittable interface:

interface Emittable {
  emit(e :Emitter) :Emitter
}

Which takes an Emitter as its parameter and returns a potentially different Emitter which reflects the state after emitting code for the callee node. The Emitter interface looks like this:

interface Emitter {
  writeU8(v :uint8) :Emitter
  writeU16(v :uint16) :Emitter
  writeU32(v :uint32) :Emitter
  writeF32(v :float32) :Emitter
  writeF64(v :float64) :Emitter
  writeBytes(v :ArrayLike<uint8>) :Emitter
}

Each modifying operation returns a potentially different Emitter which is the result of the receiver + modifications, thus modifying operations should be called like this:

e = e.writeU32(1)
e = e.writeU32(2)

However NOT like this:

e.writeU32(1)
e.writeU32(2)
// e represents same state as before `e.writeU32(1)`

This interface makes it possible to implement emitters with immutable persistent data structures.

A concrete implementation of an Emitter is provided by emit which writes to an ArrayBuffer:

class BufferedEmitter implements Emitter {
  readonly buffer :ArrayBuffer
  readonly view   :DataView
           length :uint32
  constructor(buffer :ArrayBuffer)
}

repr

repr has the ability to generate human-readable text representations of AST nodes.

There's an example of using repr to visualize an AST earlier in this document.

The repr function generates a S-expression representation of the AST in a form that is similar to how the AST would have been built using ast.

The reprBuffer function generates rows and columns of bytes values representing an ArrayBuffer with optional terminal-color higlighting of a range of bytes. Useful for visualizing what an Emitter produces, or for pointing out bytes in a module that causes an error with the spec interpreter.

function repr(n :N, w :Writer, options? :Options)

function reprBuffer(
  buffer          :ArrayBuffer,
  w               :Writer,
  limit?          :number,
  highlightRange? :number[],
  options?        :Options)

type Writer = (s :string)=>void
interface Options {
  readonly colors         :boolean  // explicitly enable or disable terminal colors
  readonly immSeparator   :string   // defaults to `:`
  readonly detailedTypes? :boolean, // `vi32(9)` or just `9`
}

// Convenience function that returns a string
function strRepr(n :N, options? :Options) :string

// Convenience function that returns a string
function strReprBuffer(
  buffer          :ArrayBuffer,
  limit?          :number,
  highlightRange? :number[],
  options?        :Options) :string

eval

eval is rather specialized module for executing the WebAssembly spec interpreter. It only works with Nodejs as it needs access to both the file system and process spawning.

The specEval function evaluates a WASM module and resolves a promise with any stdout output from the spec interpreter.

function specEval(buf :ArrayBuffer, options? :SpecOptions) :Promise<string>

interface SpecOptions {
  eval?      :string  // S-expression to evaluate after loading the module
  timeout?   :number  // 0 = no timeout. Defaults to 30000ms.
  logErrors? :boolean // when true, logs errors to stderr
  trace?     :boolean // trace execution, printing to stdout
}

Have a look at test/build_test.js for an example where specEval is used to test the functionality of a module built with ast.

lbtext

lbtext can be used to generate Linear Bytecode text from AST code. E.g.

get_local 0
i64.const 2
i64.div_s
end

The printCode function takes a list of operations to print.

function printCode(instructions :N[], writer :Writer)
type Writer = (s :string)=>void

Building and testing

First-time setup:

git clone https://github.com/WebAssembly/spec.git wasm-spec
cd wasm-spec/interpreter
# install ocaml in some way, perhaps with homebrew or aptitude, then
make test && make opt
cd ../..
yarn || npm

Building JavaScript from TypeScript source:

$ node_modules/.bin/tsc  // puts things in "build" directory

Running tests:

$ test/test.js

Upgrading the spec interpreter:

git -C wasm-spec pull origin
cd wasm-spec/interpreter
make clean && make test && make opt
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].