美文网首页Golang语言社区彬哥Go语言笔记
彬哥笔记 --20 Go语言 计划用Golang WebAsse

彬哥笔记 --20 Go语言 计划用Golang WebAsse

作者: Golang语言社区 | 来源:发表于2019-01-22 13:37 被阅读49次

          大家好,我是彬哥,本节给大家讲下go语言WebAssembly 相关,抛砖引玉了,主要是针对Go语言WebAssembly使用,计划做个网站出来,希望成功。

    运行结果图:


    结果

    步骤 一

    linux下编译wasm文件:

    Go语言文件 如下

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

    编译命令:

    GOARCH=wasm GOOS=js go build -o LollipopGo.wasm tmain.go
    

    步骤二

    用官方提供的wasm_exec.html
    文件位置:


    位置 文件位置

    文件内容:

    <!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("LollipopGo.wasm"), go.importObject).then((result) => {
                mod = result.module;
                inst = result.instance;
                document.getElementById("runButton").disabled = false;
            });
    
            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>
    

    wasm_exec.js文件:

    // 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.
    
    (() => {
        // Map web browser API and Node.js API to a single common API (preferring web standards over Node.js API).
        const isNodeJS = typeof process !== "undefined";
        if (isNodeJS) {
            global.require = require;
            global.fs = require("fs");
    
            const nodeCrypto = require("crypto");
            global.crypto = {
                getRandomValues(b) {
                    nodeCrypto.randomFillSync(b);
                },
            };
    
            global.performance = {
                now() {
                    const [sec, nsec] = process.hrtime();
                    return sec * 1000 + nsec / 1000000;
                },
            };
    
            const util = require("util");
            global.TextEncoder = util.TextEncoder;
            global.TextDecoder = util.TextDecoder;
        } else {
            if (typeof window !== "undefined") {
                window.global = window;
            } else if (typeof self !== "undefined") {
                self.global = self;
            } else {
                throw new Error("cannot export Go (neither window nor self is defined)");
            }
    
            let outputBuf = "";
            global.fs = {
                constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
                writeSync(fd, buf) {
                    outputBuf += decoder.decode(buf);
                    const nl = outputBuf.lastIndexOf("\n");
                    if (nl != -1) {
                        console.log(outputBuf.substr(0, nl));
                        outputBuf = outputBuf.substr(nl + 1);
                    }
                    return buf.length;
                },
                openSync(path, flags, mode) {
                    const err = new Error("not implemented");
                    err.code = "ENOSYS";
                    throw err;
                },
            };
        }
    
        const encoder = new TextEncoder("utf-8");
        const decoder = new TextDecoder("utf-8");
    
        global.Go = class {
            constructor() {
                this.argv = ["js"];
                this.env = {};
                this.exit = (code) => {
                    if (code !== 0) {
                        console.warn("exit code:", code);
                    }
                };
                this._callbackTimeouts = new Map();
                this._nextCallbackTimeoutID = 1;
    
                const mem = () => {
                    // The buffer may change when requesting more memory.
                    return new DataView(this._inst.exports.mem.buffer);
                }
    
                const setInt64 = (addr, v) => {
                    mem().setUint32(addr + 0, v, true);
                    mem().setUint32(addr + 4, Math.floor(v / 4294967296), true);
                }
    
                const getInt64 = (addr) => {
                    const low = mem().getUint32(addr + 0, true);
                    const high = mem().getInt32(addr + 4, true);
                    return low + high * 4294967296;
                }
    
                const loadValue = (addr) => {
                    const f = mem().getFloat64(addr, true);
                    if (!isNaN(f)) {
                        return f;
                    }
    
                    const id = mem().getUint32(addr, true);
                    return this._values[id];
                }
    
                const storeValue = (addr, v) => {
                    const nanHead = 0x7FF80000;
    
                    if (typeof v === "number") {
                        if (isNaN(v)) {
                            mem().setUint32(addr + 4, nanHead, true);
                            mem().setUint32(addr, 0, true);
                            return;
                        }
                        mem().setFloat64(addr, v, true);
                        return;
                    }
    
                    switch (v) {
                        case undefined:
                            mem().setUint32(addr + 4, nanHead, true);
                            mem().setUint32(addr, 1, true);
                            return;
                        case null:
                            mem().setUint32(addr + 4, nanHead, true);
                            mem().setUint32(addr, 2, true);
                            return;
                        case true:
                            mem().setUint32(addr + 4, nanHead, true);
                            mem().setUint32(addr, 3, true);
                            return;
                        case false:
                            mem().setUint32(addr + 4, nanHead, true);
                            mem().setUint32(addr, 4, true);
                            return;
                    }
    
                    let ref = this._refs.get(v);
                    if (ref === undefined) {
                        ref = this._values.length;
                        this._values.push(v);
                        this._refs.set(v, ref);
                    }
                    let typeFlag = 0;
                    switch (typeof v) {
                        case "string":
                            typeFlag = 1;
                            break;
                        case "symbol":
                            typeFlag = 2;
                            break;
                        case "function":
                            typeFlag = 3;
                            break;
                    }
                    mem().setUint32(addr + 4, nanHead | typeFlag, true);
                    mem().setUint32(addr, ref, true);
                }
    
                const loadSlice = (addr) => {
                    const array = getInt64(addr + 0);
                    const len = getInt64(addr + 8);
                    return new Uint8Array(this._inst.exports.mem.buffer, array, len);
                }
    
                const loadSliceOfValues = (addr) => {
                    const array = getInt64(addr + 0);
                    const len = getInt64(addr + 8);
                    const a = new Array(len);
                    for (let i = 0; i < len; i++) {
                        a[i] = loadValue(array + i * 8);
                    }
                    return a;
                }
    
                const loadString = (addr) => {
                    const saddr = getInt64(addr + 0);
                    const len = getInt64(addr + 8);
                    return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
                }
    
                const timeOrigin = Date.now() - performance.now();
                this.importObject = {
                    go: {
                        // func wasmExit(code int32)
                        "runtime.wasmExit": (sp) => {
                            const code = mem().getInt32(sp + 8, true);
                            this.exited = true;
                            delete this._inst;
                            delete this._values;
                            delete this._refs;
                            this.exit(code);
                        },
    
                        // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
                        "runtime.wasmWrite": (sp) => {
                            const fd = getInt64(sp + 8);
                            const p = getInt64(sp + 16);
                            const n = mem().getInt32(sp + 24, true);
                            fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
                        },
    
                        // func nanotime() int64
                        "runtime.nanotime": (sp) => {
                            setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
                        },
    
                        // func walltime() (sec int64, nsec int32)
                        "runtime.walltime": (sp) => {
                            const msec = (new Date).getTime();
                            setInt64(sp + 8, msec / 1000);
                            mem().setInt32(sp + 16, (msec % 1000) * 1000000, true);
                        },
    
                        // func scheduleCallback(delay int64) int32
                        "runtime.scheduleCallback": (sp) => {
                            const id = this._nextCallbackTimeoutID;
                            this._nextCallbackTimeoutID++;
                            this._callbackTimeouts.set(id, setTimeout(
                                () => { this._resolveCallbackPromise(); },
                                getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early
                            ));
                            mem().setInt32(sp + 16, id, true);
                        },
    
                        // func clearScheduledCallback(id int32)
                        "runtime.clearScheduledCallback": (sp) => {
                            const id = mem().getInt32(sp + 8, true);
                            clearTimeout(this._callbackTimeouts.get(id));
                            this._callbackTimeouts.delete(id);
                        },
    
                        // func getRandomData(r []byte)
                        "runtime.getRandomData": (sp) => {
                            crypto.getRandomValues(loadSlice(sp + 8));
                        },
    
                        // func stringVal(value string) ref
                        "syscall/js.stringVal": (sp) => {
                            storeValue(sp + 24, loadString(sp + 8));
                        },
    
                        // func valueGet(v ref, p string) ref
                        "syscall/js.valueGet": (sp) => {
                            storeValue(sp + 32, Reflect.get(loadValue(sp + 8), loadString(sp + 16)));
                        },
    
                        // func valueSet(v ref, p string, x ref)
                        "syscall/js.valueSet": (sp) => {
                            Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
                        },
    
                        // func valueIndex(v ref, i int) ref
                        "syscall/js.valueIndex": (sp) => {
                            storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
                        },
    
                        // valueSetIndex(v ref, i int, x ref)
                        "syscall/js.valueSetIndex": (sp) => {
                            Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
                        },
    
                        // func valueCall(v ref, m string, args []ref) (ref, bool)
                        "syscall/js.valueCall": (sp) => {
                            try {
                                const v = loadValue(sp + 8);
                                const m = Reflect.get(v, loadString(sp + 16));
                                const args = loadSliceOfValues(sp + 32);
                                storeValue(sp + 56, Reflect.apply(m, v, args));
                                mem().setUint8(sp + 64, 1);
                            } catch (err) {
                                storeValue(sp + 56, err);
                                mem().setUint8(sp + 64, 0);
                            }
                        },
    
                        // func valueInvoke(v ref, args []ref) (ref, bool)
                        "syscall/js.valueInvoke": (sp) => {
                            try {
                                const v = loadValue(sp + 8);
                                const args = loadSliceOfValues(sp + 16);
                                storeValue(sp + 40, Reflect.apply(v, undefined, args));
                                mem().setUint8(sp + 48, 1);
                            } catch (err) {
                                storeValue(sp + 40, err);
                                mem().setUint8(sp + 48, 0);
                            }
                        },
    
                        // func valueNew(v ref, args []ref) (ref, bool)
                        "syscall/js.valueNew": (sp) => {
                            try {
                                const v = loadValue(sp + 8);
                                const args = loadSliceOfValues(sp + 16);
                                storeValue(sp + 40, Reflect.construct(v, args));
                                mem().setUint8(sp + 48, 1);
                            } catch (err) {
                                storeValue(sp + 40, err);
                                mem().setUint8(sp + 48, 0);
                            }
                        },
    
                        // func valueLength(v ref) int
                        "syscall/js.valueLength": (sp) => {
                            setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
                        },
    
                        // valuePrepareString(v ref) (ref, int)
                        "syscall/js.valuePrepareString": (sp) => {
                            const str = encoder.encode(String(loadValue(sp + 8)));
                            storeValue(sp + 16, str);
                            setInt64(sp + 24, str.length);
                        },
    
                        // valueLoadString(v ref, b []byte)
                        "syscall/js.valueLoadString": (sp) => {
                            const str = loadValue(sp + 8);
                            loadSlice(sp + 16).set(str);
                        },
    
                        // func valueInstanceOf(v ref, t ref) bool
                        "syscall/js.valueInstanceOf": (sp) => {
                            mem().setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16));
                        },
    
                        "debug": (value) => {
                            console.log(value);
                        },
                    }
                };
            }
    
            async run(instance) {
                this._inst = instance;
                this._values = [ // TODO: garbage collection
                    NaN,
                    undefined,
                    null,
                    true,
                    false,
                    global,
                    this._inst.exports.mem,
                    this,
                ];
                this._refs = new Map();
                this._callbackShutdown = false;
                this.exited = false;
    
                const mem = new DataView(this._inst.exports.mem.buffer)
    
                // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
                let offset = 4096;
    
                const strPtr = (str) => {
                    let ptr = offset;
                    new Uint8Array(mem.buffer, offset, str.length + 1).set(encoder.encode(str + "\0"));
                    offset += str.length + (8 - (str.length % 8));
                    return ptr;
                };
    
                const argc = this.argv.length;
    
                const argvPtrs = [];
                this.argv.forEach((arg) => {
                    argvPtrs.push(strPtr(arg));
                });
    
                const keys = Object.keys(this.env).sort();
                argvPtrs.push(keys.length);
                keys.forEach((key) => {
                    argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
                });
    
                const argv = offset;
                argvPtrs.forEach((ptr) => {
                    mem.setUint32(offset, ptr, true);
                    mem.setUint32(offset + 4, 0, true);
                    offset += 8;
                });
    
                while (true) {
                    const callbackPromise = new Promise((resolve) => {
                        this._resolveCallbackPromise = () => {
                            if (this.exited) {
                                throw new Error("bad callback: Go program has already exited");
                            }
                            setTimeout(resolve, 0); // make sure it is asynchronous
                        };
                    });
                    this._inst.exports.run(argc, argv);
                    if (this.exited) {
                        break;
                    }
                    await callbackPromise;
                }
            }
    
            static _makeCallbackHelper(id, pendingCallbacks, go) {
                return function() {
                    pendingCallbacks.push({ id: id, args: arguments });
                    go._resolveCallbackPromise();
                };
            }
    
            static _makeEventCallbackHelper(preventDefault, stopPropagation, stopImmediatePropagation, fn) {
                return function(event) {
                    if (preventDefault) {
                        event.preventDefault();
                    }
                    if (stopPropagation) {
                        event.stopPropagation();
                    }
                    if (stopImmediatePropagation) {
                        event.stopImmediatePropagation();
                    }
                    fn(event);
                };
            }
        }
    
        if (isNodeJS) {
            if (process.argv.length < 3) {
                process.stderr.write("usage: go_js_wasm_exec [wasm binary] [arguments]\n");
                process.exit(1);
            }
    
            const go = new Go();
            go.argv = process.argv.slice(2);
            go.env = process.env;
            go.exit = process.exit;
            WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
                process.on("exit", (code) => { // Node.js exits if no callback is pending
                    if (code === 0 && !go.exited) {
                        // deadlock, make Go print error and stack traces
                        go._callbackShutdown = true;
                        go._inst.exports.run();
                    }
                });
                return go.run(result.instance);
            }).catch((err) => {
                throw err;
            });
        }
    })();
    
    

    步骤三 自建http服务器

    http_get.go

    package main
    
    import (
        "flag"
        "log"
        "net/http"
        "strings"
    )
    
    var (
        listen = flag.String("listen", ":8080", "listen address")
        dir    = flag.String("dir", ".", "directory to serve")
    )
    
    func main() {
        flag.Parse()
        log.Printf("listening on %q...", *listen)
        log.Fatal(http.ListenAndServe(*listen, http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
            if strings.HasSuffix(req.URL.Path, ".wasm") {
                resp.Header().Set("content-type", "application/wasm")
            }
            http.FileServer(http.Dir(*dir)).ServeHTTP(resp, req)
        })))
    }
    

    以上演示结束!代码上传到社区的git的codeclass


          每天坚持学习1小时Go语言,大家加油,我是彬哥,下期见!如果文章中不同观点、意见请文章下留言或者关注下方订阅号反馈!


    社区交流群:221273219
    Golang语言社区论坛 :
    www.Golang.Ltd
    LollipopGo游戏服务器地址:
    https://github.com/Golangltd/LollipopGo
    社区视频课程课件GIT地址:
    https://github.com/Golangltd/codeclass


    Golang语言社区

    相关文章

      网友评论

        本文标题:彬哥笔记 --20 Go语言 计划用Golang WebAsse

        本文链接:https://www.haomeiwen.com/subject/hmuljqtx.html