src/examples/wasm: Show both methods supported
Adds another example showing the simple case of executing main, adds a README explaining how everything fits together and how to execute the compiled code in the browser. Include a minimal webserver for local testing.
Этот коммит содержится в:
родитель
a00a51e70e
коммит
586023b45d
12 изменённых файлов: 244 добавлений и 25 удалений
|
@ -70,7 +70,8 @@ commands:
|
||||||
- run: tinygo build -size short -o test.elf -target=circuitplay-express examples/blinky1
|
- run: tinygo build -size short -o test.elf -target=circuitplay-express examples/blinky1
|
||||||
- run: tinygo build -size short -o test.elf -target=stm32f4disco examples/blinky1
|
- run: tinygo build -size short -o test.elf -target=stm32f4disco examples/blinky1
|
||||||
- run: tinygo build -size short -o test.elf -target=stm32f4disco examples/blinky2
|
- run: tinygo build -size short -o test.elf -target=stm32f4disco examples/blinky2
|
||||||
- run: tinygo build -o wasm.wasm -target=wasm examples/wasm
|
- run: tinygo build -o wasm.wasm -target=wasm examples/wasm/export
|
||||||
|
- run: tinygo build -o wasm.wasm -target=wasm examples/wasm/main
|
||||||
test-linux:
|
test-linux:
|
||||||
parameters:
|
parameters:
|
||||||
llvm:
|
llvm:
|
||||||
|
|
1
src/examples/wasm/.gitignore
предоставленный
Обычный файл
1
src/examples/wasm/.gitignore
предоставленный
Обычный файл
|
@ -0,0 +1 @@
|
||||||
|
html/*
|
15
src/examples/wasm/Makefile
Обычный файл
15
src/examples/wasm/Makefile
Обычный файл
|
@ -0,0 +1,15 @@
|
||||||
|
export: clean wasm_exec
|
||||||
|
tinygo build -o ./html/wasm.wasm -target wasm ./export/wasm.go
|
||||||
|
cp ./export/wasm.js ./html/
|
||||||
|
cp ./export/index.html ./html/
|
||||||
|
|
||||||
|
main: clean wasm_exec
|
||||||
|
tinygo build -o ./html/wasm.wasm -target wasm ./main/main.go
|
||||||
|
cp ./main/index.html ./html/
|
||||||
|
|
||||||
|
wasm_exec:
|
||||||
|
cp ../../../targets/wasm_exec.js ./html/
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf ./html
|
||||||
|
mkdir ./html
|
111
src/examples/wasm/README.md
Обычный файл
111
src/examples/wasm/README.md
Обычный файл
|
@ -0,0 +1,111 @@
|
||||||
|
# TinyGo WebAssembly examples
|
||||||
|
|
||||||
|
The examples here show two different ways of using WebAssembly with TinyGo;
|
||||||
|
|
||||||
|
1. Defining and exporting functions via the `//go:export <name>` directive. See
|
||||||
|
[the export folder](./export) for an example of this.
|
||||||
|
1. Defining and executing a `func main()`. This is similar to how the Go
|
||||||
|
standard library implementation works. See [the main folder](./main) for an
|
||||||
|
example of this.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Build using the `tinygo` compiler:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ tinygo build -o ./wasm.wasm -target wasm ./main/main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates a `wasm.wasm` file, which we can load in JavaScript and execute in
|
||||||
|
a browser.
|
||||||
|
|
||||||
|
This examples folder contains two examples that can be built using `make`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ make export
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ make main
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
Start the local webserver:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ go run main.go
|
||||||
|
Serving ./html on http://localhost:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
`fmt.Println` prints to the browser console.
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
Execution of the contents require a few JS helper functions which are called
|
||||||
|
from WebAssembly. We have defined these in
|
||||||
|
[wasm_exec.js](../../../targets/wasm_exec.js). It is based on
|
||||||
|
`$GOROOT/misc/wasm/wasm_exec.js` from the standard library, but is slightly
|
||||||
|
different. Ensure you are using the same version of `wasm_exec.js` as the
|
||||||
|
version of `tinygo` you are using to compile.
|
||||||
|
|
||||||
|
The general steps required to run the WebAssembly file in the browser includes
|
||||||
|
loading it into JavaScript with `WebAssembly.instantiateStreaming`, or
|
||||||
|
`WebAssembly.instantiate` in some browsers:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const go = new Go(); // Defined in wasm_exec.js
|
||||||
|
const WASM_URL = 'wasm.wasm';
|
||||||
|
|
||||||
|
var wasm;
|
||||||
|
|
||||||
|
if ('instantiateStreaming' in WebAssembly) {
|
||||||
|
WebAssembly.instantiateStreaming(fetch(WASM_URL), go.importObject).then(function (obj) {
|
||||||
|
wasm = obj.instance;
|
||||||
|
go.run(wasm);
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
fetch(WASM_URL).then(resp =>
|
||||||
|
resp.arrayBuffer()
|
||||||
|
).then(bytes =>
|
||||||
|
WebAssembly.instantiate(bytes, go.importObject).then(function (obj) {
|
||||||
|
wasm = obj.instance;
|
||||||
|
go.run(wasm);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If you have used explicit exports, you can call them by invoking them under the
|
||||||
|
`wasm.exports` namespace. See the [`export`](./export/wasm.js) directory for an
|
||||||
|
example of this.
|
||||||
|
|
||||||
|
In addition to this piece of JavaScript, it is important that the file is served
|
||||||
|
with the correct `Content-Type` header set.
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const dir = "./html"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fs := http.FileServer(http.Dir(dir))
|
||||||
|
log.Print("Serving " + dir + " on http://localhost:8080")
|
||||||
|
http.ListenAndServe(":8080", http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
if strings.HasSuffix(req.URL.Path, ".wasm") {
|
||||||
|
resp.Header().Set("content-type", "application/wasm")
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.ServeHTTP(resp, req)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This simple server serves anything inside the `./html` directory on port `8080`,
|
||||||
|
setting any `*.wasm` files `Content-Type` header appropriately.
|
20
src/examples/wasm/export/index.html
Обычный файл
20
src/examples/wasm/export/index.html
Обычный файл
|
@ -0,0 +1,20 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Go WebAssembly</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<script src="wasm_exec.js" defer></script>
|
||||||
|
<script src="wasm.js" defer></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>WebAssembly</h1>
|
||||||
|
<p>Add two numbers, using WebAssembly:</p>
|
||||||
|
<input type="number" id="a" value="2" /> + <input type="number" id="b" value="2" /> = <input type="number"
|
||||||
|
id="result" readonly />
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -16,10 +16,10 @@ func add(a, b int) int {
|
||||||
//go:export update
|
//go:export update
|
||||||
func update() {
|
func update() {
|
||||||
document := js.Global().Get("document")
|
document := js.Global().Get("document")
|
||||||
a_str := document.Call("getElementById", "a").Get("value").String()
|
aStr := document.Call("getElementById", "a").Get("value").String()
|
||||||
b_str := document.Call("getElementById", "b").Get("value").String()
|
bStr := document.Call("getElementById", "b").Get("value").String()
|
||||||
a, _ := strconv.Atoi(a_str)
|
a, _ := strconv.Atoi(aStr)
|
||||||
b, _ := strconv.Atoi(b_str)
|
b, _ := strconv.Atoi(bStr)
|
||||||
result := a + b
|
result := add(a, b)
|
||||||
document.Call("getElementById", "result").Set("value", result)
|
document.Call("getElementById", "result").Set("value", result)
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const WASM_URL = '../../../wasm.wasm';
|
const WASM_URL = 'wasm.wasm';
|
||||||
|
|
||||||
var wasm;
|
var wasm;
|
||||||
|
|
8
src/examples/wasm/main/README.md
Обычный файл
8
src/examples/wasm/main/README.md
Обычный файл
|
@ -0,0 +1,8 @@
|
||||||
|
# WebAssembly main execution example
|
||||||
|
|
||||||
|
A simple hello world that prints to the browser console.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Note that `index.html` is copied almost verbatim from the Go 1.12 source at
|
||||||
|
`$GOROOT/misc/wasm/wasm_exec.html`. Its license applies to this file.
|
49
src/examples/wasm/main/index.html
Обычный файл
49
src/examples/wasm/main/index.html
Обычный файл
|
@ -0,0 +1,49 @@
|
||||||
|
<!doctype html>
|
||||||
|
<!--
|
||||||
|
Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
Use of this source code is governed by a BSD-style
|
||||||
|
license that can be found in the LICENSE file.
|
||||||
|
-->
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Go wasm</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<!--
|
||||||
|
Add the following polyfill for Microsoft Edge 17/18 support:
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/text-encoding@0.7.0/lib/encoding.min.js"></script>
|
||||||
|
(see https://caniuse.com/#feat=textencoder)
|
||||||
|
-->
|
||||||
|
<script src="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("wasm.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>
|
||||||
|
|
||||||
|
<button onClick="run();" id="runButton" disabled>Run</button>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
9
src/examples/wasm/main/main.go
Обычный файл
9
src/examples/wasm/main/main.go
Обычный файл
|
@ -0,0 +1,9 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("Hello world!")
|
||||||
|
}
|
21
src/examples/wasm/server.go
Обычный файл
21
src/examples/wasm/server.go
Обычный файл
|
@ -0,0 +1,21 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const dir = "./html"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fs := http.FileServer(http.Dir(dir))
|
||||||
|
log.Print("Serving " + dir + " on http://localhost:8080")
|
||||||
|
http.ListenAndServe(":8080", http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
if strings.HasSuffix(req.URL.Path, ".wasm") {
|
||||||
|
resp.Header().Set("content-type", "application/wasm")
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.ServeHTTP(resp, req)
|
||||||
|
}))
|
||||||
|
}
|
|
@ -1,16 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8"/>
|
|
||||||
<title>Go WebAssembly</title>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
||||||
<script src="../../../targets/wasm_exec.js" defer></script>
|
|
||||||
<script src="wasm.js" defer></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>WebAssembly</h1>
|
|
||||||
<p>Add two numbers, using WebAssembly:</p>
|
|
||||||
<input type="number" id="a" value="2"/> + <input type="number" id="b" value="2"/> = <input type="number" id="result" readonly/>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче