From 0a9848104e95dc65af341cdca5db835946d9f9c2 Mon Sep 17 00:00:00 2001 From: Brian Ketelsen Date: Tue, 2 Oct 2018 18:01:44 -0400 Subject: [PATCH] update wasm_exec.js --- markdownvecty/wasm_exec.js | 119 ++++++++++++++++++++++--------------- 1 file changed, 72 insertions(+), 47 deletions(-) diff --git a/markdownvecty/wasm_exec.js b/markdownvecty/wasm_exec.js index ecb0965..94b9552 100644 --- a/markdownvecty/wasm_exec.js +++ b/markdownvecty/wasm_exec.js @@ -27,11 +27,17 @@ global.TextEncoder = util.TextEncoder; global.TextDecoder = util.TextDecoder; } else { - window.global = window; + 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, O_NONBLOCK: -1, O_SYNC: -1 }, // unused + 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"); @@ -91,9 +97,11 @@ } const storeValue = (addr, v) => { + const nanHead = 0x7FF80000; + if (typeof v === "number") { if (isNaN(v)) { - mem().setUint32(addr + 4, 0x7FF80000, true); // NaN + mem().setUint32(addr + 4, nanHead, true); mem().setUint32(addr, 0, true); return; } @@ -101,51 +109,44 @@ return; } - mem().setUint32(addr + 4, 0x7FF80000, true); // NaN - 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; } - if (typeof v === "string") { - let ref = this._stringRefs.get(v); - if (ref === undefined) { - ref = this._values.length; - this._values.push(v); - this._stringRefs.set(v, ref); - } - mem().setUint32(addr, ref, true); - return; - } - - if (typeof v === "symbol") { - let ref = this._symbolRefs.get(v); - if (ref === undefined) { - ref = this._values.length; - this._values.push(v); - this._symbolRefs.set(v, ref); - } - mem().setUint32(addr, ref, true); - return; - } - - let ref = v[this._refProp]; + let ref = this._refs.get(v); if (ref === undefined) { ref = this._values.length; this._values.push(v); - v[this._refProp] = ref; + 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); } @@ -176,8 +177,12 @@ go: { // func wasmExit(code int32) "runtime.wasmExit": (sp) => { + const code = mem().getInt32(sp + 8, true); this.exited = true; - this.exit(mem().getInt32(sp + 8, true)); + delete this._inst; + delete this._values; + delete this._refs; + this.exit(code); }, // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) @@ -328,16 +333,10 @@ false, global, this._inst.exports.mem, - () => { // resolveCallbackPromise - if (this.exited) { - throw new Error("bad callback: Go program has already exited"); - } - setTimeout(this._resolveCallbackPromise, 0); // make sure it is asynchronous - }, + this, ]; - this._stringRefs = new Map(); - this._symbolRefs = new Map(); - this._refProp = Symbol(); + this._refs = new Map(); + this._callbackShutdown = false; this.exited = false; const mem = new DataView(this._inst.exports.mem.buffer) @@ -374,7 +373,12 @@ while (true) { const callbackPromise = new Promise((resolve) => { - this._resolveCallbackPromise = 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) { @@ -383,6 +387,28 @@ 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) { @@ -396,17 +422,16 @@ go.env = process.env; go.exit = process.exit; WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => { - process.on("exit", () => { // Node.js exits if no callback is pending - if (!go.exited) { - console.error("error: all goroutines asleep and no JavaScript callback pending - deadlock!"); - process.exit(1); + 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) => { - console.error(err); - go.exited = true; - process.exit(1); + throw err; }); } })();