AssemblyScript Loader

Working repo

Exercise

Implement fizzbuzz in AssemblyScript.

Hint:

  if (number is divisible by 15) then
	"fizzbuzz"
  if (number is divisible by 3) then
	"fizz"
  if (number is divisible by 5) then
	"buzz"
Solution
// assembly/index.ts
export function fizzbuzz(n: i32): String | null {
  if (n % 15 === 0) {
    return 'fizzbuzz';
  }

  if (n % 3 === 0) {
    return 'fizz';
  }

  if (n % 5 === 0) {
    return 'buzz';
  }

  return null;
}

Strings

Update index.html to call fizzbuzz()

// index.html
<script>
    const WL = new WasmLoader();
    WL.wasm('/build/optimized.wasm')
    .then(instance => {
      const { fizzbuzz } = instance;

      document.write(fizzbuzz(3));
    });
</script>

Running fizzbuzz(3) outputs a number and not a string. Remember that Web Assembly only deals in numbers so AssemblyScript allocates space for the strings (see: untouched.wat) and passes them into memory. The number being returned is a pointer to the memory address of the string being returned. Fortunately AssemblyScript includes a loader that lets us allocate and read from memory.

Let's import the loader into the page:

// index.html
<script src="https://cdn.jsdelivr.net/npm/@assemblyscript/loader/umd/index.js"></script>

In loader.js file, replace the WebAssembly method calls with loader.

// js/loader.js
class WasmLoader {
    constructor() {...}

    async wasm(path, imports = this._imports) {
        console.log(`fetching ${path}`);

        if (!loader.instantiateStreaming) {
            return this.wasmFallback(path, imports);
        }

        const { instance } = await loader.instantiateStreaming(fetch(path), imports);

        return instance?.exports;
    }

    async wasmFallback(path, imports) {
        console.log('using fallback');
        const response = await fetch(path);
        const bytes = await response?.arrayBuffer();
        const { instance } = await loader.instantiate(bytes, imports);

        return instance?.exports;
    }
}

The AssemblyScript loader will require internal glue code to be sent with our wasm. Adding the --exportRuntime flag will compile our wasm with these helper functions.

// package.json
"asbuild:untouched": "asc assembly/index.ts --target debug --exportRuntime",
"asbuild:optimized": "asc assembly/index.ts --target release --exportRuntime"