Der Go-WebAssembly-Spicker

Aus archium.org
Wechseln zu: Navigation, Suche


zurück

Inhaltsverzeichnis

Was ist WebAssembly?

Java-Entwickler programmieren hardwareunabhängig für die Java Virtual Machine JVM - eine virtuelle Maschine, die einheitlichen Byte-Code verarbeitet. Die Anpassung an die jeweilige Hardware-Architektur geschieht durch die JVM, der Entwickler braucht sich darum (fast) keine Gedanken machen. Einen ähnlichen Ansatz verfolgt Python mit der Just-in-time-Kompilierung von Objekt-Code. Perfektioniert wird diese Idee gar mit der LLVM – der Low Level Virtual Machine – die eine softwarebasierte Maschine simuliert und bemerkenswert viele Hardware-Architekturen unterstützt.[1] Für den Byte-Code der LLVM gibt es Compiler aus nahezu allen modernen Sprachen - auch Java!

Stellen wir uns vor, der Webbrowser sei eine solche virtuelle Maschine - dann haben wir verstanden, was WebAssembly, kurz WASM, ist.

WebAssembly ist ein Byte-Code-Standard zur Steuerung von Browser-Funktionen. Auch wenn die Browser-Funktionalität gegenwärtig noch immer auf JavaScript basiert, so ist die langfristige Richtung und das damit verbundene Potential vorgegeben: Mit WebAssembly werden die Browser langfristig unabhängig von den Skriptsprachen. Webprojekte werden frei von für jedermann transparenten Codes. Und damit wird auch die Performance der Webprojekte zunehmen - deutlich. Wird WebAssembly-Byte-Code mit Go generiert, wird beispielsweise auch die concurrency, also die nebenläufige Ausführung von Subroutinen, erheblich vereinfacht.

WebAssembly ist das lange herbeigesehnte und zuvor nur durch sperrige und speicherhungrige JavaScript-Frameworks simulierte missing link zwischen Internetseite und Desktop-App. Mit WebAssembly geht beides in einem!

Seit Ende 2019 ist WebAssembly ein offizieller Webstandard. In den kommenden Jahren wird Webassembly massiv an Bedeutung gewinnen, viele Webprojekte werden mit neuer Technologie neu implementiert werden müssen. Die Browserhersteller werden mit ihren Softwareprodukten in einen virtuellen Hardware-Markt einsteigen und in einen Wettlauf um die performanteste Plattform und die interessantesten Features treten! Längst gibt es beispielsweise auch Qt für WebAssembly. In naher Zukunft macht WebAssembly den Browser zum Desktop!

Go und WebAssembly

Es gibt sehr viele Wege zu WebAssembly. Wir müssen nicht in virtuellem Assembler programmieren[2], für sehr viele Programmiersprachen gibt es bereits WebAssembly-Kompiler. Go unterstützt WASM seit Version 1.11, also seit Sommer 2018.

Ein paar einfache Beispiele vom Text bis zur Leinwand

Für die Beispiele benötigen wir immer zwei Projekte:

  • simple WASM server
  • simple WASM app

Beispiel 1: "Hallo Welt"

Schritt 1 [simple WASM server]: Erzeuge einen funktionierenden Webserver in Datei "main.go"

package main

import (
	"log"
	"net/http"
)

const (
	addr = "localhost:8008"
	dir  = ""
)

func main() {
	log.Fatal(http.ListenAndServe(addr, http.FileServer(http.Dir(dir))))
}

Schritt 2 [simple WASM server]: Erzeuge eine Datei "index.html"

<html>
	<head>
		<meta charset="utf-8"/>
	</head>
	<body>It works!</body>
</html>

Schritt 3: Teste den Webserver

go build -i -o simpleWASMserver main.go
./simpleWASMserver

Im Browser nun die URL http://localhost:8008/ aufrufen. Es sollte "It works!" angezeigt werden.

Schritt 4 [simple WASM server]: Ändere die Datei "index.html" und bereite den Aufruf von WebAssembly-Code ("app.wasm") vor

Variante a: Für neuere Browser, die bereits WebAssembly unterstützen
<html>
  <head>
    <meta charset="utf-8" />
    <title>Go wasm</title>
    <script src="https://golang.org/misc/wasm/wasm_exec.js"></script>
    <script>
      const go = new Go();
      WebAssembly.instantiateStreaming(fetch("app.wasm"), go.importObject)
        .then((result) => {
          go.run(result.instance);
        })
        .catch((err) => {
          console.error(err);
        });
    </script>
  </head>
  <body></body>
</html>

Variante b: Mit Kompatibilität für ältere Browser, die noch kein WebAssembly-Streaming unterstützen
<html>
  <head>
    <meta charset="utf-8" />
    <title>Go wasm</title>
    <script src="https://golang.org/misc/wasm/wasm_exec.js"></script>
    <script>
      if (!WebAssembly.instantiateStreaming) {
        // polyfill
        WebAssembly.instantiateStreaming = async (resp, importObject) => {
          const source = await (await resp).arrayBuffer();
          return await WebAssembly.instantiate(source, importObject);
        };
      }

      const go = new Go();
      let mod, inst;
      WebAssembly.instantiateStreaming(fetch("app.wasm"), go.importObject)
        .then((result) => {
          go.run(result.instance);
        })
        .catch((err) => {
          console.error(err);
        });
    </script>
  </head>
  <body></body>
</html>

Schritt 5 [simple WASM app]: Erzeuge unseren WebAssembly-Code in Datei "main.go"

package main

import "fmt"

func main() {
	fmt.Println("Hello, WebAssembly!")
}

Schritt 6 [simple WASM app]: Kompiliere unseren WebAssembly-Code

Achtung!
Für WebAssembly benötigen wir veränderte Umgebungsvariablen
GOOS=js
GOARCH=wasm
GOOS=js GOARCH=wasm go build -o app.wasm
Achtung!
Anschließend ist der WebAssembly-ByteCode in Datei "app.wasm" in den Ordner des Webservers zu kopieren oder zu verlinken!

Schritt 7: Teste den Webserver erneut

Im Console-Log des Webservers wird nun der Schriftzug "Hello, WebAssembly!" zu lesen sein.

Beispiel 2: Kommunikation mit dem Document Object Model (DOM) des HTML-Documents

Datei "index.html" [simple WASM server]

Identisch mit Variante a oder b aus Beispiel 1!

Datei "main.go" [simple WASM app]

package main

import "fmt"

func main() {
	fmt.Println("Hello, WebAssembly!")

	js.Global().Get("console").Call("log", "Just another way to send a string to console-log!")
	js.Global().Get("document").Call("getElementsByTagName", "body").Index(0).Set("innerText", time.Now().String())
}

Beispiel 3: Dynamische Inhalte

Datei "index.html" [simple WASM server]

https://golang.org/misc/wasm/wasm_exec.html bzw. https://github.com/golang/go/blob/master/misc/wasm/wasm_exec.html dienen als Vorlage!

<html>
  <head>
    <meta charset="utf-8" />
    <title>Go wasm</title>
    <script src="https://golang.org/misc/wasm/wasm_exec.js"></script>
    <script>
      if (!WebAssembly.instantiateStreaming) {
        // polyfill
        WebAssembly.instantiateStreaming = async (resp, importObject) => {
          const source = await (await resp).arrayBuffer();
          return await WebAssembly.instantiate(source, importObject);
        };
      }

      const go = new Go();
      let mod, inst;
      WebAssembly.instantiateStreaming(fetch("app.wasm"), go.importObject)
        .then((result) => {
          mod = result.module;
          inst = result.instance;
          document.getElementById("convertButton").disabled = false;
        })
        .catch((err) => {
          console.error(err);
        });

      async function run() {
        console.clear();
        await go.run(inst);
        inst = await WebAssembly.instantiate(mod, go.importObject); // reset instance
      }
    </script>
  </head>
  <body>
    <input
      type="number"
      min="0"
      max="255"
      placeholder="0…255"
      size="5"
      id="decimal"
    />

    <button onClick="run();" id="convertButton" disabled>Convert</button><br />
    <div id="bin"></div>
    <div id="oct"></div>
    <div id="hex"></div>
  </body>
</html>

Datei "main.go" [simple WASM app]

package main

import (
	"time"
	"fmt"
	"strconv"
	"syscall/js"
)

func main() {
	var (
		inputString string
		inputInt    int
		err         error
	)

	inputString = js.Global().Get("document").Call("getElementById", "decimal").Get("value").String()
	if inputInt, err = strconv.Atoi(inputString); err != nil || inputInt < 0 || inputInt > 255 {
		inputInt = 0
	}

	js.Global().Get("document").Call("getElementById", "bin").Set("innerText", fmt.Sprintf("BIN: %08b\n", inputInt))
	time.Sleep(1 * time.Second) // Völlig Unnötig! Zeitverzögerung dient nur der Veranschaulichung!
	js.Global().Get("document").Call("getElementById", "oct").Set("innerText", fmt.Sprintf("OCT: %04o\n", inputInt))
	time.Sleep(1 * time.Second) // Völlig Unnötig! Zeitverzögerung dient nur der Veranschaulichung!
	js.Global().Get("document").Call("getElementById", "hex").Set("innerText", fmt.Sprintf("HEX: %02x\n", inputInt))
}

Teste den Webserver erneut

Die Input-Maske wird nun eine Zahl von 0-255 aufnehmen und in Binär-, Oktal- und Hexadezimal-Code konvertieren.

Hinweis
Die Zeitverzögerung zwischen den Eingriffen in das DOM des HTML-Documents ist natürlich vollkommen unnötig. Sie dient nur zur Veranschaulichung.

Beispiel 4: Auf einer HTML5-Canvas malen

Siehe https://www.w3schools.com/graphics/canvas_drawing.asp für die HTML5-Syntax.

Diesmal werden zusätzlich Elemente im Go-Code generiert und gelöscht. Vom ursprünglichen HTML5-Code bleibt nur noch der <header>…</header> und ein leerer <body></body> übrig.

Datei "index.html" [simple WASM server]

<html>
  <head>
    <meta charset="utf-8" />
    <title>Go wasm</title>
    <script src="https://golang.org/misc/wasm/wasm_exec.js"></script>
    <script>
      if (!WebAssembly.instantiateStreaming) {
        // polyfill
        WebAssembly.instantiateStreaming = async (resp, importObject) => {
          const source = await (await resp).arrayBuffer();
          return await WebAssembly.instantiate(source, importObject);
        };
      }

      const go = new Go();
      let mod, inst;
      WebAssembly.instantiateStreaming(fetch("app.wasm"), go.importObject)
        .then((result) => {
          mod = result.module;
          inst = result.instance;
          document.getElementById("runButton").disabled = false;
        })
        .catch((err) => {
          console.error(err);
        });

      async function run() {
        console.clear();
        await go.run(inst);
        inst = await WebAssembly.instantiate(mod, go.importObject); // reset instance
      }
    </script>
  </head>
  <body>
    <button onClick="run();" id="runButton" disabled>run</button><br />
  </body>
</html>

Datei "main.go" [simple WASM app]

package main

import (
	"math/rand"
	"syscall/js"
	"time"
)

func main() {
	var (
		dombase, canvas, button, ctx, grd js.Value
	)
	dombase = js.Global().Get("document")

	button = dombase.Call("getElementById", "runButton")

	canvas = dombase.Call("createElement", "canvas")
	canvas.Set("width", 800)
	canvas.Set("height", 600)

	dombase.Call("getElementsByTagName", "body").Index(0).Call("insertBefore", canvas, button)

	dombase.Call("getElementsByTagName", "body").Index(0).Call("removeChild", button)

	ctx = canvas.Call("getContext", "2d")
	grd = ctx.Call("createLinearGradient", 0, 0, 800, 0)
	grd.Call("addColorStop", 0, "red")
	grd.Call("addColorStop", 1, "white")
	ctx.Set("fillStyle", grd)
	ctx.Call("fillRect", 0, 0, 800, 600)

	ctx.Call("moveTo", 0, 0)
	for i := 0; i < 799; i++ {
		x := rand.Intn(100)
		y := rand.Intn(100)

		ctx.Call("lineTo", i+x, (i*2/3)+y)
		ctx.Call("stroke")
		time.Sleep(50 * time.Millisecond)
	}
}

Teste den Webserver erneut

Das Ergebnis sollte ungefähr folgendermaßen aussehen: ArchiumWASM Demo Canvas.png


Beispiel 5: Große Mengen HTML/CSS/JS-Code injizieren

Datei "index.html" [simple WASM server]

Identisch mit Variante a oder b aus Beispiel 1!

Datei "main.go" [simple WASM app]

package main

import (
	"syscall/js"
)

func main() {
	var (
		dombase, body js.Value
	)
	dombase = js.Global().Get("document")
	body = dombase.Call("getElementsByTagName", "body").Index(0)

	body.Set("innerHTML", `
	<table style="text-align:center;">
	<tr><td colspan="2">SVG</td></tr>
	<tr><td>Polygon</td><td>Path</td>
	<tr><td><svg height="210" width="500">
  	<polygon points="200,10 250,190 160,210" style="fill:lime;stroke:purple;stroke-width:1" />
	</svg></td><td>
	 <svg height="400" width="450">
  <path id="lineAB" d="M 100 350 l 150 -300" stroke="red"
  stroke-width="3" fill="none" />
  <path id="lineBC" d="M 250 50 l 150 300" stroke="red"
  stroke-width="3" fill="none" />
  <path d="M 175 200 l 150 0" stroke="green" stroke-width="3"
  fill="none" />
  <path d="M 100 350 q 150 -300 300 0" stroke="blue"
  stroke-width="5" fill="none" />
  <!-- Mark relevant points -->
  <g stroke="black" stroke-width="3" fill="black">
    <circle id="pointA" cx="100" cy="350" r="3" />
    <circle id="pointB" cx="250" cy="50" r="3" />
    <circle id="pointC" cx="400" cy="350" r="3" />
  </g>
  <!-- Label the points -->
  <g font-size="30" font-family="sans-serif" fill="black" stroke="none"
  text-anchor="middle">
    <text x="100" y="350" dx="-30">A</text>
    <text x="250" y="50" dy="-10">B</text>
    <text x="400" y="350" dx="30">C</text>
  </g>
</svg></td></tr>
	</table>`) //https://www.w3schools.com/graphics/svg_polygon.asp & https://www.w3schools.com/graphics/svg_path.asp
}

Beispiel 6: Images aus dem Go-Code laden

Datei "index.html" [simple WASM server]

Identisch mit Variante a oder b aus Beispiel 1!

Datei "main.go" [simple WASM app]

package main

import (
	"syscall/js"
)

func main() {
	const imageByteArrayAsBase64 = `
iVBORw0KGgoAAAANSUhEUgAAAJgAAAC0CAMAAABfch
VeAAAC/VBMVEUGAwkBAg8ABgkEAAYLBAIIBQsCCQwICwYMCQ4GDA8QCggTCgEVDg
ULERMPEQ0SDxMUDw4aEgMQFBYTFBIWGBUgFwYbGBcaGBsaHBknGwUgHiEeIB0iHh
0tHgMiJCEvIgkoJCMnJSg1JQYmKCU8KgcrLSovKyo+KBMrLS87LBBBLgU3LxtHLg
gwMzUyNDE3MzJMMgVJNwk4OTdMOQJSNgM4Oz0+Ojk8PDVYNwdVOQdSPAg+QD1ePA
RDPz9YQgVTQRpBQ0JeQAZlQgJGR0VuRAdrRgdKS0loSwl0SQNUTz5yTAJTTk1NUV
NPUU5uUQV7TwBVV1RaV0qAUwV9VwaFVgCLVgNhXlFdXlxsXUNdYGOLWwWBXhGGYQ
aPXgCTXABlZmSZYQSWZASPaQqVaA6baAxsbmyhaAGMayuWbgCPcBKbbQKfawN0cW
SYbCGkbwlzdXKbdQmscACocwCidA2GeGJ6e3ilewOhfQSeeSezdgF4fX+veAmsew
qyewCKf1x+gH2ifDiygAW3fgSthQO1fhOZgVKEhYOygiC8gw2+hQC4hRK7hwDDiQ
WMjou4kQSli1u/kAPCjQ3HjA3KjgC7jyrHkQC5jjnNkQKUlpO1k0/UkQrJmAfRlA
rOlwvWmADFoQDKngXTmwCvmm7GmjDRngCdn5zXngTbnAWinZyunYC3nmTaoQ3Vni
7OqA7VpQzepADApGXUrQDipwOnqKXbrQTZpyvgqwfmqw3qrQDisgDnsACur6y7rZ
TZtxHrtAPfuwHwsgbkugW9sp+7s6bquQrVt071twDvuA7ztg/pvgDuvADzugC2uL
S8trPxvgDpwwD2vQDLuonuwgDuvSb0wQO7vbrBvLruyAjDv7HwxB3rywvqv1nAwr
/Gwb/1zgHtzSTFx8TKxcTx0Rr10wXsx2bMybrJy8j02A/x1yD01zDO0M312DvV0c
P22EbT1dHl17Hc2MnY2tfk4NHe4d3x4rnj5eLo6ubr7ur18eLw8u/19/P5+/f6/P
n7/fr8//sGU4jmAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACx
MAAAsTAQCanBgAAAAHdElNRQfUChwRIgJBXOpUAAAcEUlEQVR42u2cfVzT953A2y
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
`
	var (
		dombase, body js.Value
	)

	dombase = js.Global().Get("document")
	body = dombase.Call("getElementsByTagName", "body").Index(0)

	image := dombase.Call("createElement", "img")
	image.Set("src", "data:image/png;base64,"+imageByteArrayAsBase64)
	body.Call("appendChild", image)
}

Teste den Webserver erneut

Das Ergebnis sollte folgendermaßen aussehen:
ArchiumWASM Demo Tux.png

Tipp: Code komplett aus Go erzeugen

Die Verwendung der Datei "index.html" diente in den Beispielen der Vereinfachung. Der HTML-Code kann natürlich auch komplett aus Go heraus erzeugt werden.
Die Datei "https://golang.org/misc/files/wasm_exec.js" wird ebenfalls lokal abgelegt:

[simple WASM server]: Datei "main.go" ohne "index.html"

package main

import (
	"fmt"
	"log"
	"net/http"
)

const (
	addr = "localhost:8008"
	dir  = ""
)

func myDefaultHandlerFunc(w http.ResponseWriter, r *http.Request, dirpath string) {
	fmt.Fprintf(w, `<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Go wasm</title>
    <script src="/files/wasm_exec.js"></script>
    <script>
      if (!WebAssembly.instantiateStreaming) {
        // polyfill
        WebAssembly.instantiateStreaming = async (resp, importObject) => {
          const source = await (await resp).arrayBuffer();
          return await WebAssembly.instantiate(source, importObject);
        };
      }

      const go = new Go();
      let mod, inst;
      WebAssembly.instantiateStreaming(fetch("/files/app.wasm"), go.importObject)
        .then((result) => {
          go.run(result.instance);
        })
        .catch((err) => {
          console.error(err);
        });
    </script>
</head>
<body></body>
</html>`)
}

func main() {
	http.Handle("/files/", http.StripPrefix("/files/", http.FileServer(http.Dir(dir))))
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { myDefaultHandlerFunc(w, r, dir) })

	log.Fatal(http.ListenAndServe(addr, nil))
}

Links

Anmerkungen

  1. Siehe LLVM bei Wikipedia
  2. Das wäre aber sehr wohl möglich, siehe hierzu das anschauliche Beispiel in der englischsprachigen Wikipedia: https://en.wikipedia.org/wiki/WebAssembly