Skip to content

Instantly share code, notes, and snippets.

@cure53
Last active October 17, 2023 00:16
Show Gist options
  • Star 38 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save cure53/f4581cee76d2445d8bd91f03d4fa7d3b to your computer and use it in GitHub Desktop.
Save cure53/f4581cee76d2445d8bd91f03d4fa7d3b to your computer and use it in GitHub Desktop.
Calling alert from WASM

Calling alert from WebAssembly (WASM)

This very simple and minimal tutorial documents in a few easy steps how to play with WebAssembly (WASM) and get first results within minutes.

While the code below is mostly useless, it will show, how to call the alert function from within a WASM file and thus demonstrate how to import and export DOM objects.

Of course, this exercise has no real use. It is just meant to show, that getting started with WASM isn't hard. And there is no need for a complex build-chain, tons of tools or a dedicated VMs. Just use a browser, one online tool and that's it.

And Now?

To execute the code shown below, we need a very modern browser. WASM likely needs to be enabled via configuration flag. We tested on Chrome Version 52.0.2741.0 Canary and activated chrome://flags/#enable-webassembly. Same for Firefox 49.0a1 with javascript.options.wasm: true.

Tools

We need nothing else but the browser, one HTML file and a WASM file. We can use the amazing tool linked below to generate the WASM file for us - from WAST. No complex build chain needed:

https://cdn.rawgit.com/WebAssembly/sexpr-wasm-prototype/2bb13aa785be9908b95d0e2e09950b39a26004fa/demo/index.html

Setup

Create an HTML file using the following source code:

<script>
/* An XHR fetching the WASM file as ArrayBuffer */
var xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
xhr.onload = ready;
xhr.open('GET', 'test.wasm');
xhr.send(null);

/* Execute once the file was fetched  */
function ready() {
    var info = {
        foo: {bar: alert} // "import" the alert method
    };
    var a = Wasm.instantiateModule(
        new Uint8Array(xhr.response), info
    ).exports;
    a.f(); // execute the f method, that is foo.bar()
}
</script>

Now, let's create some WAST code to feed to the tool linked above and generate a WASM for for us (test.wasm, as referenced in the HTML):

(module ;; start the module
  (import "foo" "bar") ;; import foo.bar
  (func $bar (call_import 0 )) ;; map bar()
  (export "f" 0) ;; export bar()
)

Click "Download", grab the resulting WASM file, place it in the same folder as your HTML file, open the HTML in Chrome or any WASM-compatible browser of choice, see the alert pop. Done :) Why? Because why not!

More Info

@cgvwzq
Copy link

cgvwzq commented Mar 23, 2017

alert from wasm

This HTML runs a wasm module that imports and calls alert. The Uint8Array contains the binary version of the wasm module below:

<script>
WebAssembly.instantiate(new Uint8Array([0,97,115,109,1,0,0,0,1,8,2,96,1,127,0,96,0,0,2,8,1,2,106,115,1,95,0,0,3,2,1,1,8,1,1,10,9,1,7,0,65,185,10,16,0,11]), {js:{_:alert}});
</script>

Module that imports a JS external function and calls it with an integer argument:

(module
    (func $_ (import "js" "_") (param i32))
    (func $main
        i32.const 1337
        (call $_)
    )
    (start $main)
)

How to

In order to translate the S-expression text format files (.wast) into binary (.wasm) we can use the WebAssembly Binary Toolkit:

$ wast2wasm alert.wast -o alert.wasm

The generated binary file is passed as a BufferArray to the WebAssembly JS API in order to compile/load modules. And this is how it looks like (145 bytes vs 772 bytes in text format):

$ hexdump -C alert.wasm
00000000  00 61 73 6d 01 00 00 00  01 08 02 60 01 7f 00 60  |.asm.......`...`|
00000010  00 00 02 08 01 02 6a 73  01 5f 00 00 03 02 01 01  |......js._......|
00000020  08 01 01 0a 09 01 07 00  41 b9 0a 10 00 0b        |........A.....|
0000002e

LLVM also has experimental support for a WebAssembly backend (compile it with LLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly). So we can compile C/C++ into LLVM's IR, and then into .wast:

$ clang -emit-llvm --target=wasm32 -S sample.c
$ llc sample.ll -march=wasm32
$ s2wasm sample.s > sample.wast

Links

@Zibri
Copy link

Zibri commented Mar 13, 2019

HOW would you do the same with a string?

@guest271314
Copy link

@Zibri See https://depth-first.com/articles/2020/01/13/first-steps-in-webassembly-hello-world/, https://www.webassemblyman.com/wat/hello_world.zip

WebAssembly.instantiate(new Uint8Array([0,97,115,109,1,0,0,0,1,8,2,96,1,127,0,96,0,0,2,15,1,3,101,110,118,7,106,115,112,114,105,110,116,0,0,3,2,1,1,5,3,1,0,1,7,27,2,10,112,97,103,101,109,101,109,111,114,121,2,0,10,104,101,108,108,111,119,111,114,108,100,0,1,10,8,1,6,0,65,0,16,0,11,11,19,1,0,65,0,11,13,72,101,108,108,111,32,87,111,114,108,100,33,0]), {
  env: {
    jsprint: function jsprint(byteOffset) {
      console.log(new TextDecoder().decode(new Uint8Array(memory.buffer).filter(Boolean)));
    }
  }
})
.then(results => {
  instance = results.instance;
  memory = instance.exports.pagememory;
  instance.exports.helloworld();
}).catch(console.error);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment