Hello WebAssembly

Mit WebAssembly, kurz wasm, erscheint ein Bytecode-Format, dass die Ausführung von Web-Applikationen erheblich beschleunigen soll. Entwickelt von Microsoft, Google, Mozilla und Apple in einer WebAssembly Community Group, hat WebAssembly zum Ziel, ein standardisiertes, schlankes und effizientes Format für die Entwicklung von Web-Applikationen bereitzustellen.

Ein WebAssembly wird in einer geschützten Sandbox des Browsers ausgeführt und erreicht dank der vorgängigen Kompilation fast die Geschwindigkeit von nativen Applikationen. Im Moment kann ein WebAssembly mit den Programmiersprachen C/C++ geschrieben werden, weitere Sprachen folgen mit Sicherheit. Kompiliert werden die Programme mit Emscripten, ein LLVM basierter Compiler, der aus C/C++ Code JavaScript generiert.

emscripten

Die Installation von Emscripten gestaltet sich ein wenig aufwändig, da der Compiler aus den Quellen selber kompiliert werden muss. Die nötigen Schritte für Linux oder Windows sind hier genau beschrieben .

Tipp: Wer mit Windows 10 arbeitet, kann die Toolchain auch in der Bash on Ubuntu on Windows installieren :-) Zuerst sollte das Windows Subsystem for Linux nach dieser Anleitung auf die neueste Version aktualisiert werden. Danach installieren wir noch die notwendigen Abhängigkeiten mit Hilfe von apt :

$ apt-get install build-essential cmake

Jetzt kann mit der oben genannten Anleitung die Installation für Linux oder eben Bash on Ubuntu on Windows starten. Als erstes muss das Emscripten SDK aus den Quellen gebaut werden. Dieser Vorgang dauert je nach Rechnerleistung schnell 30 Minuten oder länger.

$ git clone https://github.com/juj/emsdk.git
$ cd emsdk

$ ./emsdk install --build=Release sdk-incoming-64bit binaryen-master-64bit
$ ./emsdk activate --global --build=Release sdk-incoming-64bit binaryen-master-64bit

Die Umgebung wird mit folgenden Befehl initialisiert. Dabei werden die nötigen Variablen gesetzt, so dass der Emscripten Compiler verwendet werden kann.

$ source ./emsdk_env.sh

Nun kann es endlich losgehen. Als Beispiel für ein WebAssembly dient folgendes Code-Snippet:

#include <stdio.h>
#include <emscripten/emscripten.h>

int main(int argc, char ** argv) {
    printf("WebAssembly::main \n");
    return 0;
}

#ifdef __cplusplus
extern "C" {
#endif

extern void myJavaScriptFunction(char* message);

int EMSCRIPTEN_KEEPALIVE multiply(int a, int b) {
    int result = a * b;
    printf("WebAssembly::multiply -> %d * %d = %d \n", a, b, result);
    return result;
}

void EMSCRIPTEN_KEEPALIVE callJavascript(char* message){
    printf("WebAssembly::callJavascript -> %s \n", message);
    myJavaScriptFunction(message);
}

#ifdef __cplusplus
}
#endif

Unser WebAssembly definiert mit main den Einstiegspunkt der Applikation. Zusätzlich werden die beiden Funktionen multiply und callJavascript exportiert, damit sie per JavaScript aufgerufen werden können. Den Quellcode übersetzen wir mit Hilfe des Emscripten Compiler Frontend (emcc).

$ emcc webassembly.c -s WASM=1 -o index.html --shell-file template.html

emcc generiert grundsätzlich automatisch ein Html-File mit den nötigen Initialisierungen sowie einer kleiner Demoseite. Damit aber die eigenen Funktionen gestestet werden können, habe ich den Code in ein eigenes Template ausgelagert.

Der Compiler generiert nun die folgenden Dateien:

  • index.js - Bildet die Schnittstelle zwischen WebAssembly und Javascript
  • index.wasm - Das kompilierte WebAssembly
  • index.html - Html Seite

Aus dem JavaScript-Code können WebAssembly-Funktionen direkt mit Module.ccall aufgerufen werden.

var result = Module.ccall(
  "multiply", // name der C function
  "number", // return type
  ["number", "number"], // argument types
  [22, 123]
); // arguments

Der umgekehrte Weg funktioniert natürlich ebenfalls. So kann eine C-Api in JavaScript implementiert werden. Damit das geht sind aber mehrere Schritte nötig. Als erstes markieren wir die Api als extern.

extern "C" {
  extern void myJavaScriptFunction(char* message);
}

Dann folgt die eigentliche Implementation der Api. Der Code kann dabei in einem beliebigen JavaScript-File abgelegt werden.

mergeInto(LibraryManager.library, {
  myJavaScriptFunction: function (message) {
    alert(message);
  },
});

Mit emcc kompilieren wir wieder das WebAssembly, diesmal mit der –js-library Option und dem JavaScript-File das unsere C-Api Implementation beeinhaltet

$ emcc webassembly.c -s WASM=1 -o index.html --shell-file template.html --js-library myfunctions.js

Die fertige Demo-Applikation gibts hier zum Download. Ob der eigene Browser das Feature bereits unterstützt, kann mit http://caniuse.com geprüft werden.

Eine gute Übersicht rund ums Thema gibts bei developer.mozilla.org. Die Spezifikation sowie weitere Tools sind auf der Github Seite des Projekts hinterlegt.