wasmly

programatically build a web assembly module

22 releases

0.2.0 Mar 29, 2019
0.1.7 Mar 14, 2019
0.0.13 Mar 7, 2019
0.0.2 Feb 9, 2019

30 downloads per month
Used in 2 crates

MIT license

47KB
1.5K SLoC

Rust 1K SLoC JavaScript 273 SLoC // 0.0% comments

Wasmly

This is a minimalistic web assembly tool for creating modules from arrays of bytes. This helps you develop much more closer to how web assembly apps exist at a low level.

If you check this project out and just want to get the dependencies to setup:

make setup

Go into any example directory:

make
make serve

Then open a browser to http://localhost:9999

Check the console for output!

If you are using this as a npm module

npm install wasmly

Very Simple Modules

If all you are doing is writing a single exported "main" function that takes in inputs, returns an output, and exposes "memory". Try this to save boiler plate:

var fs = require('fs');
let {makeSimple,int,I32,I32_CONST,END} = require("wasmly");

// main() -> i32
app = makeSimple([],[I32],[   
  vec([]),             // no local variables
  I32_CONST, int(42),  // return 42
  END
])

fs.writeFileSync('out.wasm',Buffer.from(app))

Simplest Module Possible

If you are trying to create the most simplest module from scratch. This has no functions

var fs = require('fs');
let {flatten,MAGIC_NUMBER,VERSION_1} = require("wasmly");

app = [
  MAGIC_NUMBER,
  VERSION_1,
]

// this is just a nested array of bytes:
// [[[0, 97, 115, 109]][[1, 0, 0, 0]]]

fs.writeFileSync('out.wasm',Buffer.from(flatten(app)))

Not very useful!

main() From Scratch

...
// main() -> i32 { return 42 }
main_function_signature = [FUNC,vec([]),vec([I32])] // function signature returns 42
main_function_code = bytevec([
  vec([]),              // no local variables
  [I32_CONST, int(42)], // return 42
  END
])

//lets make memory at least 2 pages and at most 10 pages long
memory = [LIMIT_MIN_MAX,uint(2),uint(10)]

// put it all together as a module
app = [
  MAGIC_NUMBER,
  VERSION_1,
  [SECTION_TYPE,bytevec(vec([main_function_signature]))],
  [SECTION_FUNCTION,bytevec(vec([int(0)]))],
  [SECTION_MEMORY,bytevec(vec([memory]))],
  [SECTION_EXPORT,bytevec(vec([
    [str("main"),DESC_FUNCTION,0],
    [str("memory"),DESC_MEMORY,0]
  ]))],
  [SECTION_CODE,bytevec(vec([main_function_code]))]
]
...

Memory Allocator

Let's make a very simple memory allocator.

...
// malloc(length:i32) -> i32 { ... }
malloc_function_signature = [FUNC,vec([I32]),vec([I32])] // function signature returns 42
malloc_function_code = bytevec([
  vec([
    [1, I32] // current_heap:i32
  ]),
  // current_heap = global.heap
  GLOBAL_GET, 0,
  LOCAL_SET,  1,
  // memorycurrent_heap = length
  GLOBAL_GET, 0,
  LOCAL_GET,  0,
  I32_STORE,  0, 0,
  // global.heap = current_heap + 1 + length
  LOCAL_GET,  1,
  I32_CONST,  1,
  I32_ADD,
  LOCAL_GET,  0,
  I32_ADD,
  GLOBAL_SET, 0,
  // return current_heap + 1
  LOCAL_GET,  1,
  I32_CONST,  5,
  I32_ADD,
  END
])

// create a heap global set to zero
heap_global = [I32,MUTABLE,I32_CONST, int(0),END]

//lets make memory at least 2 pages and at most 10 pages long
memory = [LIMIT_MIN_MAX,uint(2),uint(10)]

// put it all together as a module
app = [
  MAGIC_NUMBER,
  VERSION_1,
  [SECTION_TYPE,bytevec(vec([malloc_function_signature]))],
  [SECTION_FUNCTION,bytevec(vec([0]))],
  [SECTION_MEMORY,bytevec(vec([memory]))],
  [SECTION_GLOBAL,bytevec(vec([heap_global]))],
  [SECTION_EXPORT,bytevec(vec([
     [str("malloc"),DESC_FUNCTION,0],
     [str("memory"),DESC_MEMORY,0],
     [str("heap"),DESC_GLOBAL,0]
   ]))],
  [SECTION_CODE,bytevec(vec([malloc_function_code]))]
]
...

No runtime deps