An­wen­dun­gen, die im Browser laufen, statt auf der Fest­plat­te in­stal­liert zu sein, werden immer um­fang­rei­cher. Neben typischer Bü­ro­soft­ware wie Microsoft 365 oder Google Docs, die mit immer neuen Funk­tio­nen aufwarten, werden auch Brow­ser­spie­le immer auf­wen­di­ger und verlangen mehr Res­sour­cen. Oftmals werden solche Web­an­wen­dun­gen in Ja­va­Script angeboten. In­zwi­schen setzen aber mehr und mehr Ent­wick­ler auf We­b­As­sem­bly – ein neuer Ansatz mit er­staun­li­chen Re­sul­ta­ten.

Was ist We­b­As­sem­bly?

We­b­As­sem­bly (Wasm) ist eine neue Art für Web­ent­wick­ler, An­wen­dun­gen im Internet zur Verfügung zu stellen. Bisher musste man dafür auf Ja­va­Script zu­rück­grei­fen. Doch Ja­va­Script läuft relativ langsam und kommt in manchen Szenarien an seine Grenzen. Deshalb hat das World Wide Web Con­sor­ti­um (W3C) eine neue Methode – We­b­As­sem­bly – vor­an­ge­trie­ben. Damit Wasm funk­tio­nie­ren kann, muss al­ler­dings auch der ent­spre­chen­de Browser mit der Sprache umgehen können. Deshalb waren Mozilla (Firefox), Microsoft (Edge), Apple (Safari) und Google (Chrome) an der Ent­wick­lung beteiligt. In allen aktuellen Brow­ser­ver­sio­nen der Her­stel­ler können An­wen­dun­gen in We­b­As­sem­bly aus­ge­führt werden.

Tipp

Um die Leis­tungs­fä­hig­keit von We­b­As­sem­bly selbst zu erleben, lohnt sich eine Runde Funky Karts. Das Spiel – ei­gent­lich eine mobile App – wurde in We­b­As­sem­bly um­ge­wan­delt, um es auch im Browser ausführen zu können. Der Ent­wick­ler hat für das Projekt einen in­ter­es­san­ten Blog geführt und dort die einzelnen Schritte bei der Um­wand­lung be­schrie­ben.

Prin­zi­pi­ell wird We­b­As­sem­bly als Bytecode dar­ge­stellt. Dies kann man als Zwi­schen­stu­fe gesehen werden zwischen Ma­schi­nen­code, der nur vom Computer ver­stan­den werden kann, und einer typischen Pro­gram­mier­spra­che, die für Menschen zwar lesbar ist, aber deshalb auch erst kom­pi­liert werden muss. Deshalb ist We­b­As­sem­bly ver­gleichs­wei­se schnell: Der Computer braucht kaum Aufwand, um den Code um­zu­wan­deln. Doch in Bytecode zu schreiben, ist eher un­ge­wöhn­lich: Der Vorteil von Wasm ist, dass man selbst nicht in dieser Pro­gram­mier­spra­che arbeiten muss. In der Praxis verfasst man die Web­an­wen­dung bei­spiels­wei­se in C oder C++.

Der Quelltext wird mit der Anwendung Em­scrip­ten um­ge­wan­delt. Dieses Werkzeug war schon bevor es We­b­As­sem­bly gab im Einsatz, um C/C++-Code in Ja­va­Script (bzw. ams.js) um­zu­wan­deln. In­zwi­schen lässt sich damit Code aber auch in Wasm um schreiben. Damit ist der Code vor­kom­pi­liert, muss also nicht erst im Moment des Aus­füh­rens kom­pi­liert oder in­ter­pre­tiert werden. Wenn der Nutzer die Anwendung schließ­lich im Browser öffnet, wird eine kleine virtuelle Maschine gestartet. In dieser läuft dann die Anwendung.

Vorteile von We­b­As­sem­bly

Derzeit hat We­b­As­sem­bly ei­gent­lich nur einen wirk­li­chen Nachteil: Es ver­brei­tet sich nur langsam. Web­ent­wick­ler sind an die Arbeit mit Ja­va­Script gewöhnt. Und es ist auch gar nicht geplant, Ja­va­Script zu ver­drän­gen. Die Pro­jekt­lei­tung legt in der Kom­mu­ni­ka­ti­on großen Wert darauf, Wasm als zu­sätz­li­che Option zu Ja­va­Script an­zu­prei­sen. Doch durch die Un­ter­stüt­zung der großen Brow­ser­her­stel­ler und durch das W3C nimmt die Ver­brei­tung an Fahrt auf. Das liegt auch daran, dass Website-Besucher keine eigenen Schritte einleiten müssen: Die Web­an­wen­dun­gen in We­b­As­sem­bly laden genauso einfach wie Code in Ja­va­Script – nur schneller.

Viele Ent­wick­ler, die ohnehin bereits in C, C++ oder z. B. Rust schreiben, können nun auch direkt für das Web pro­gram­mie­ren. Die Pro­gram­mier­spra­chen bringen mitunter auch andere Mög­lich­kei­ten zur Ge­stal­tung der Anwendung mit sich: Wer bei Ja­va­Script nicht die be­nö­tig­ten Libraries oder Frame­works für sein Programm findet, hat nun mehr Auswahl, um zum Ziel zu gelangen. Man findet als Ent­wick­ler also einige Gründe, sich We­b­As­sem­bly näher an­zu­schau­en:

  • Offener Web­stan­dard des W3C
  • Hohe Per­for­mance und niedrige Da­tei­grö­ße
  • Dadurch auch perfekt für mobiles Surfen geeignet
  • Auch Virtual Reality ist damit theo­re­tisch im Browser möglich
  • Keine neue Pro­gram­mier­spra­che notwendig
  • Auch C, C++ oder Rust können nun zur Pro­gram­mie­rung von Web­an­wen­dun­gen verwendet werden
  • Wird von allen großen Brow­ser­her­stel­lern un­ter­stützt
  • Keinerlei Ein­schrän­kung für den Nutzer

We­b­As­sem­bly in der Praxis

Es ist nicht vor­ge­se­hen, dass man tat­säch­lich in We­b­As­sem­bly pro­gram­miert. Der große Vorteil der Technik liegt gerade darin, dass man eine beliebte Hoch­spra­che wie etwa C verwendet und dieser Code dann in das leis­tungs­fä­hi­ge Wasm-Format über­tra­gen wird. Es kann aber durchaus sinnvoll sein, sich auch mit dem bereits kom­pi­lier­ten Code aus­ein­an­der­zu­set­zen und so hinter die Funk­ti­ons­wei­se von We­b­As­sem­bly zu blicken.

Den Quelltext gibt es in zwei ver­schie­de­nen Varianten: We­b­As­sem­bly Text Format (WAT) und We­b­As­sem­bly Binary Format (Wasm). Letzteres ist der ei­gent­li­che Code, den die Maschine laufen lässt. Da dieser aber aus­schließ­lich aus Binärcode besteht, ist er mehr oder weniger un­brauch­bar für eine Analyse durch Menschen. Deshalb gibt es die Zwi­schen­form WAT. Ein solcher Code verwendet lesbare Ausdrücke und kann deshalb auch vom Pro­gram­mie­rer selbst un­ter­sucht werden, bietet aber nicht den Ar­beits­kom­fort, den man von eta­blier­ten Pro­gram­mier­spra­chen kennt.

Für ein Beispiel benutzen wir einen ganz einfach ge­stal­te­ten Quelltext in C:

#define WASM_EXPORT __attribute__((visibility("default")))
WASM_EXPORT
int main() {
    return 1;
}

Der gleiche Code im WAT-Format ist um einiges länger:

(module
    (type $t0 (func))
    (type $t1 (func (result i32)))
    (func $__wasm_call_ctors (type $t0))
    (func $main (export "main") (type $t1) (result i32)
        i32.const 1)
    (table $T0 1 1 anyfunc)
    (memory $memory (export "memory") 2)
    (global $g0 (mut i32) (i32.const 66560))
    (global $__heap_base (export "__heap_base") i32 (i32.const 66560))
    (global $__data_end (export "__data_end") i32 (i32.const 1024)))

Die Les­bar­keit ist bereits stark ein­ge­schränkt, doch man kann ein paar Elemente ausmachen: Alles in We­b­As­sem­bly wird in Module ein­ge­teilt. Die Module sind wiederum in Funk­tio­nen ein­ge­teilt, welche durch Parameter spe­zi­fi­ziert werden. Insgesamt lassen sich 5 ver­schie­de­ne Elemente erkennen:

  • module: oberste Einheit in We­b­As­sem­bly
  • function: Grup­pie­rung innerhalb eines Moduls
  • memory: Array mit Bytes
  • global: Wert, der über mehrere Module hinweg verwendet werden kann
  • table: Speicher von Verweisen

Wenn der Code al­ler­dings in die Binärform übersetzt wird, kann man von all dem nichts mehr erkennen:

0000000: 0061 736d                                 ; WASM_BINARY_MAGIC
0000004: 0100 0000                                 ; WASM_BINARY_VERSION
; section "Type" (1)
0000008: 01                                        ; section code
0000009: 00                                        ; section size (guess)
000000a: 02                                        ; num types
; type 0
000000b: 60                                        ; func
000000c: 00                                        ; num params
000000d: 00                                        ; num results
; type 1
000000e: 60                                        ; func
000000f: 00                                        ; num params
0000010: 01                                        ; num results
0000011: 7f                                        ; i32
0000009: 08                                        ; FIXUP section size
; section "Function" (3)
0000012: 03                                        ; section code
0000013: 00                                        ; section size (guess)
0000014: 02                                        ; num functions
0000015: 00                                        ; function 0 signature index
0000016: 01                                        ; function 1 signature index
0000013: 03                                        ; FIXUP section size
; section "Table" (4)
0000017: 04                                        ; section code
0000018: 00                                        ; section size (guess)
0000019: 01                                        ; num tables
; table 0
000001a: 70                                        ; funcref
000001b: 01                                        ; limits: flags
000001c: 01                                        ; limits: initial
000001d: 01                                        ; limits: max
0000018: 05                                        ; FIXUP section size
; section "Memory" (5)
000001e: 05                                        ; section code
000001f: 00                                        ; section size (guess)
0000020: 01                                        ; num memories
; memory 0
0000021: 00                                        ; limits: flags
0000022: 02                                        ; limits: initial
000001f: 03                                        ; FIXUP section size
; section "Global" (6)
0000023: 06                                        ; section code
0000024: 00                                        ; section size (guess)
0000025: 03                                        ; num globals
0000026: 7f                                        ; i32
0000027: 01                                        ; global mutability
0000028: 41                                        ; i32.const
0000029: 8088 04                                   ; i32 literal
000002c: 0b                                        ; end
000002d: 7f                                        ; i32
000002e: 00                                        ; global mutability
000002f: 41                                        ; i32.const
0000030: 8088 04                                   ; i32 literal
0000033: 0b                                        ; end
0000034: 7f                                        ; i32
0000035: 00                                        ; global mutability
0000036: 41                                        ; i32.const
0000037: 8008                                      ; i32 literal
0000039: 0b                                        ; end
0000024: 15                                        ; FIXUP section size
; section "Export" (7)
000003a: 07                                        ; section code
000003b: 00                                        ; section size (guess)
000003c: 04                                        ; num exports
000003d: 04                                        ; string length
000003e: 6d61 696e                                main  ; export name
0000042: 00                                        ; export kind
0000043: 01                                        ; export func index
0000044: 06                                        ; string length
0000045: 6d65 6d6f 7279                           memory  ; export name
000004b: 02                                        ; export kind
000004c: 00                                        ; export memory index
000004d: 0b                                        ; string length
000004e: 5f5f 6865 6170 5f62 6173 65              __heap_base  ; export name
0000059: 03                                        ; export kind
000005a: 01                                        ; export global index
000005b: 0a                                        ; string length
000005c: 5f5f 6461 7461 5f65 6e64                 __data_end  ; export name
0000066: 03                                        ; export kind
0000067: 02                                        ; export global index
000003b: 2c                                        ; FIXUP section size
; section "Code" (10)
0000068: 0a                                        ; section code
0000069: 00                                        ; section size (guess)
000006a: 02                                        ; num functions
; function body 0
000006b: 00                                        ; func body size (guess)
000006c: 00                                        ; local decl count
000006d: 0b                                        ; end
000006b: 02                                        ; FIXUP func body size
; function body 1
000006e: 00                                        ; func body size (guess)
000006f: 00                                        ; local decl count
0000070: 41                                        ; i32.const
0000071: 01                                        ; i32 literal
0000072: 0b                                        ; end
000006e: 04                                        ; FIXUP func body size
0000069: 09                                        ; FIXUP section size
; section "name"
0000073: 00                                        ; section code
0000074: 00                                        ; section size (guess)
0000075: 04                                        ; string length
0000076: 6e61 6d65                                name  ; custom section name
000007a: 01                                        ; function name type
000007b: 00                                        ; subsection size (guess)
000007c: 02                                        ; num functions
000007d: 00                                        ; function index
000007e: 11                                        ; string length
000007f: 5f5f 7761 736d 5f63 616c 6c5f 6374 6f72  __wasm_call_ctor
000008f: 73                                       s  ; func name 0
0000090: 01                                        ; function index
0000091: 04                                        ; string length
0000092: 6d61 696e                                main  ; func name 1
000007b: 1a                                        ; FIXUP subsection size
0000096: 02                                        ; local name type
0000097: 00                                        ; subsection size (guess)
0000098: 02                                        ; num functions
0000099: 00                                        ; function index
000009a: 00                                        ; num locals
000009b: 01                                        ; function index
000009c: 00                                        ; num locals
0000097: 05                                        ; FIXUP subsection size
0000074: 28                                        ; FIXUP section size
Tipp

Wer selbst die ersten Schritte in We­b­As­sem­bly aus­pro­bie­ren möchte, kann auf das We­b­As­sem­bly Studio zu­rück­grei­fen. Dort steht Ihnen online eine Ent­wick­lungs­um­ge­bung für Wasm zur Verfügung.

Zum Hauptmenü