• Stars
    star
    353
  • Rank 120,322 (Top 3 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created almost 8 years ago
  • Updated almost 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

WebAssembly utilities

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

More Repositories

1

inter

The Inter font family
Python
16,727
star
2

peertalk

iOS and Mac Cocoa library for communicating over USB
Objective-C
3,424
star
3

fb-mac-messenger

⚡️ Mac app wrapping Facebook's Messenger for desktop
Objective-C
2,856
star
4

kod

Programmers' editor for OS X [DEAD PROJECT]
Objective-C
2,296
star
5

node-imagemagick

Imagemagick module for NodeJS — NEW MAINTAINER: @yourdeveloper
JavaScript
1,807
star
6

markdown-wasm

Very fast Markdown parser and HTML generator implemented in WebAssembly, based on md4c
C
1,446
star
7

gotalk

Async peer communication protocol & library
Go
1,193
star
8

estrella

Lightweight and versatile build tool based on the esbuild compiler
TypeScript
1,098
star
9

raster

Raster — simple CSS grid system
CSS
806
star
10

js-lru

A fast, simple & universal Least Recently Used (LRU) map for JavaScript
JavaScript
774
star
11

sol

A sunny little virtual machine
C
516
star
12

scrup

Take a screenshot (in OS X) — paste the URL somewhere a second later
Objective-C
405
star
13

chromium-tabs

[historical] Chromium tabs for cocoa applications (project no longer maintained)
Objective-C
388
star
14

figplug

Figma plugin builder
TypeScript
336
star
15

cocui

Cocoa meets WebKit for more rapid UI development
Objective-C
329
star
16

ec2-webapp

A template I use to quickly set up Node.js-backed web apps on Amazon EC2
324
star
17

llvmbox

Self contained, fully static llvm tools & libs
C
317
star
18

move

A simple, functional-biased, prototypal and powerful programming language that runs on any ES3 (or better) JavaScript platform, aimed toward people new to programming
JavaScript
302
star
19

immutable-cpp

Persistent immutable data structures for C++
C++
281
star
20

rsm

Virtual computer
C
267
star
21

compis

Contemporary systems programming language in the spirit of C
C
196
star
22

uilayer

CALayer-style API for building rich, high-performance UI graphics in WebKit
JavaScript
192
star
23

sublime-theme

My Sublime Text theme
Python
187
star
24

scripter

The Scripter Figma plugin
JavaScript
178
star
25

hue

A functional programming language
C++
170
star
26

gitblog

Git-based blog/cms for PHP, meant as a replacement for Wordpress
PHP
164
star
27

co

A programming language in early development
TypeScript
147
star
28

graphviz

Graphviz web app
JavaScript
118
star
29

LazyDispatch

Thin API and concept on top of libdispatch (aka Grand Central Dispatch) for Cocoa Objective-C code.
Objective-C
102
star
30

afcgi

Asynchronous/multiplexing FastCGI for nginx (incl. ref server implementation)
C
101
star
31

rsms-utils

Collection of CLI programs to help with everyday computer life
Shell
99
star
32

colang

Programming language and compiler —WORK IN PROGRESS—
C
71
star
33

xsys

A well-defined system API for abstracting the OS platform
C
68
star
34

figma-plugins

Collection of Figma plugins
TypeScript
67
star
35

mkweb

simple static website generator
JavaScript
63
star
36

fontkit

JS & WASM library for working with fonts
C
62
star
37

js-object-merge

3-way JavaScript Object merging -- Object.merge(v1, v1a, v1b) -> v2
JavaScript
54
star
38

tc

Tokyo Cabinet Python bindings — In need of a new maintainer
C
54
star
39

smolmsg

Simple messages
Go
54
star
40

js-wasmc

Simplifies building of WebAssembly modules in C/C++ and JS
JavaScript
53
star
41

sigi-pixel-font

Sigi pixel fonts [archived]
52
star
42

WebView-OSX-Screensaver

WebKit web view as a screensaver on OS X
Objective-C
52
star
43

Go.tmbundle

TextMate bundle for the Go programming language
51
star
44

oui

Web-app client-server framework developed as part of dropular.net
JavaScript
49
star
45

wlang

Programming language in development
C
47
star
46

smisk

High performance web service framework, written in C but controlled by Python. Used by Spotify infra 2009–2015.
Python
45
star
47

memex

Software for archiving my digital stuff like tweets
Go
45
star
48

workenv

My personal work environment
Emacs Lisp
44
star
49

ckit

The little C kit
C
44
star
50

dropub

DroPub — drop and publish. Simple OS X MenuItem managing secure transfer of files in the background
Objective-C
42
star
51

serve-http

Simple, safe single-file local web server
JavaScript
42
star
52

go-uuid

Binary sortable universally unique identifier
Go
38
star
53

ghp

Go Hypertext Preprocessor
Go
38
star
54

TypoFig

Mac app for assisting type design in Figma
Objective-C
38
star
55

opencv-face-track-basics

Basic code for tracking faces and eyes using OpenCV
C++
37
star
56

qemu-macos-x86-arm64

Run arm64 Linux Alpine virtualized on macOS x86_64 with QEMU
Shell
35
star
57

tspkg

Create small, fast and easily-distributable packages from TypeScript projects
JavaScript
34
star
58

go-immutable

Immutable data structures for Go
Go
34
star
59

webkit-editor

Experimental text editor which runs in the browser
JavaScript
32
star
60

html5-video

Video player in HTML5
JavaScript
30
star
61

hovden-stitch

A typeface weekend project from 2002 with a classic "stitching"/"embroidery" look
30
star
62

jo

Go-style JavaScript ES6 compiler and packager, based on Babel
JavaScript
30
star
63

libcss-osx

Building libcss as Mac OS X universal binary. Developed as part of Kod (rsms/kod)
Objective-C
29
star
64

tumblr-theme-hunch

The theme used on my blog
28
star
65

dawn-lib

Builds Dawn on Linux and macOS as one single easier-to-use library
Shell
28
star
66

browser-require

CommonJS module require() for web browsers
JavaScript
25
star
67

jsont

A minimal and portable JSON tokenizer for building highly effective and strict parsers (in C and C++)
C
25
star
68

prog-lang-tutorial

JavaScript
25
star
69

node-fsdocs

Simple, ACID and versioned file-system based document database
JavaScript
25
star
70

js-fragment

Client-side templating for modern thinkers
JavaScript
24
star
71

node-couchdb-min

Simplistic CouchDB client with a minimal level of abstraction and connection pooling.
JavaScript
24
star
72

fontctrl

Font manager, keeping font files up to date with a distributed repository model
Go
23
star
73

web-clipboard-promise

Demonstrates lazily-evaluated clipboard data on the Web platform
JavaScript
22
star
74

bezier-tangent

Bézier curve toy
JavaScript
22
star
75

twitter-icon

Alternative icon for Twitter.app
21
star
76

dropular-2010

Redacted snapshot of dropular.net, May 2010
JavaScript
21
star
77

dawn-wire-example

[WIP] Demo of a minimal but functional Dawn-based WebGPU client and server
C++
21
star
78

phpab

Abstract Base – universal PHP runtime library
PHP
18
star
79

NodeCocoa

Embed node.js in Cocoa or write whole Cocoa apps in node.js
Objective-C
17
star
80

ortho-remote

Some code for playing with the Teenage Engineering Ortho Remote
Objective-C
16
star
81

ml-kern

Kerning done by machines (a project to learn more about ML)
JavaScript
16
star
82

asl-logging

Convenience functions and example code for using ASL (Apple System Log facility)
C
14
star
83

js-miniglob

Minimal glob JavaScript implementation ported from Go's path/filepath
JavaScript
13
star
84

hunch-cocoa

An assortment of Cocoa — mixed extensions and additions to Cocoa
Objective-C
13
star
85

wasm-loader

WebAssembly module loader with import resolution
TypeScript
12
star
86

cgui

super duper simple gui for C, wrapping imgui and stb
C
12
star
87

cmdr

Helps writing command-line programs with subcommands in Go
Go
12
star
88

spotifycocoa

Cocoa framework of libspotify
Objective-C
12
star
89

macfusion

Fork of http://svn.macfusionapp.org/macfusion2/trunk/ — With mainly UI changes like menu item icon and OS-standard volume icons) — Download latest release build: http://cloud.github.com/downloads/rsms/Macfusion/Macfusion.zip
Objective-C
12
star
90

hunch-upload

Multiple concurrent files uploads with progress in pure HTML
JavaScript
11
star
91

functional.js

Work in a functional style with JavaScript and TypeScript
JavaScript
11
star
92

coxlang

Programming language w/ subproject that implements the Go scheduler in C++
C++
11
star
93

lolcatos

The lolcat operating system
Assembly
11
star
94

cometps

Simple comet pub/sub
C
9
star
95

node-imgdb

Image fingerprinting
C++
8
star
96

ssl-client-auth-demo

Demonstrates "client-authenticated TLS handshake"
Shell
8
star
97

connect_facebook

Facebook session support for Connect
8
star
98

mode

Node module manager and repository
JavaScript
8
star
99

flup

Drag-and-drop to quickly put images on Flickr
Objective-C
8
star
100

ipvec

Educational persistent vector implementation in C
C
8
star