diff --git a/go_wasm.patch b/go_wasm.patch index ecbc6fb..b6716ba 100644 --- a/go_wasm.patch +++ b/go_wasm.patch @@ -1,8 +1,8 @@ diff --git a/go_wasm.js b/go_wasm.js -index 8021b44..21cfd42 100644 +index 8021b44..a4836ba 100644 --- a/go_wasm.js +++ b/go_wasm.js -@@ -4,135 +4,11 @@ +@@ -4,135 +4,10 @@ // // This file has been modified for use by the TinyGo compiler. @@ -132,14 +132,71 @@ index 8021b44..21cfd42 100644 - const encoder = new TextEncoder("utf-8"); const decoder = new TextDecoder("utf-8"); - var logLine = []; +- var logLine = []; - global.Go = class { + export class Go { constructor() { this._callbackTimeouts = new Map(); this._nextCallbackTimeoutID = 1; -@@ -307,10 +183,15 @@ +@@ -249,50 +124,12 @@ + this.importObject = { + wasi_snapshot_preview1: { + // https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#fd_write +- fd_write: function(fd, iovs_ptr, iovs_len, nwritten_ptr) { +- let nwritten = 0; +- if (fd == 1) { +- for (let iovs_i=0; iovs_i 0, // dummy +- fd_fdstat_get: () => 0, // dummy +- fd_seek: () => 0, // dummy +- "proc_exit": (code) => { +- if (global.process) { +- // Node.js +- process.exit(code); +- } else { +- // Can't exit in a browser. +- throw 'trying to exit with code ' + code; +- } +- }, +- random_get: (bufPtr, bufLen) => { +- crypto.getRandomValues(loadSlice(bufPtr, bufLen)); +- return 0; +- }, ++ fd_write() {}, ++ fd_close() {}, ++ fd_fdstat_get() {}, ++ fd_seek() {}, ++ proc_exit() {}, ++ random_get() {} + }, + env: { + // func ticks() float64 +@@ -307,10 +144,15 @@ }, // func finalizeRef(v ref) @@ -159,39 +216,30 @@ index 8021b44..21cfd42 100644 }, // func stringVal(value string) ref -@@ -456,6 +337,22 @@ - }; - } - -+ storeString(str) { -+ const addr = this._inst.exports.getBuffer(); -+ const buf = this._inst.exports.memory.buffer; -+ -+ const mem = new Uint8Array(buf); -+ const view = mem.subarray(addr); -+ return encoder.encodeInto(str, view).written; -+ } -+ -+ loadString(len) { -+ const addr = this._inst.exports.getBuffer(); -+ const buf = this._inst.exports.memory.buffer; -+ -+ return decoder.decode(new DataView(buf, addr, len)); -+ } -+ - async run(instance) { - this._inst = instance; - this._values = [ // JS values that Go currently has references to, indexed by reference id -@@ -464,7 +361,7 @@ +@@ -464,7 +306,12 @@ null, true, false, - global, -+ "undefined" != typeof globalThis ? globalThis : global || self, ++ // fake global ++ { ++ set format(fn){ instance.format = fn; }, ++ Array, ++ Object, ++ }, this, ]; this._goRefCounts = []; // number of references that Go has to a JS value, indexed by reference id -@@ -511,25 +408,3 @@ +@@ -472,8 +319,6 @@ + this._idPool = []; // unused ids that have been garbage collected + this.exited = false; // whether the Go program has exited + +- const mem = new DataView(this._inst.exports.memory.buffer) +- + while (true) { + const callbackPromise = new Promise((resolve) => { + this._resolveCallbackPromise = () => { +@@ -511,25 +356,3 @@ }; } } diff --git a/lib.js b/lib.js index 1e60a65..5609c72 100644 --- a/lib.js +++ b/lib.js @@ -1,44 +1,47 @@ import { Go } from "./go_wasm.js"; const go = new Go(); -let mod; +let inst; export default async function init(wasm_url) { - if (!mod) { - if (!wasm_url) { - wasm_url = new URL("lib.wasm", import.meta.url); - } + if (inst) { + return await inst; + } - if (typeof wasm_url === "string") { - wasm_url = new URL(wasm_url); - } + if (!wasm_url) { + wasm_url = new URL("lib.wasm", import.meta.url); + } - if ( - typeof __webpack_require__ !== "function" && - wasm_url.protocol === "file:" - ) { - const fs = await import("node:fs"); - const bytes = fs.readFileSync(wasm_url); - mod = new WebAssembly.Module(bytes); - } else if ("compileStreaming" in WebAssembly) { - mod = await WebAssembly.compileStreaming(fetch(wasm_url)); - } else { - const response = await fetch(wasm_url); - const bytes = await response.arrayBuffer(); - mod = new WebAssembly.Module(bytes); - } + if (typeof wasm_url === "string") { + wasm_url = new URL(wasm_url); } -} -export function format(input) { - const inst = new WebAssembly.Instance(mod, go.importObject); + if ( + typeof __webpack_require__ !== "function" && + wasm_url.protocol === "file:" + ) { + inst = import("node:fs/promises") + .then((fs) => fs.readFile(wasm_url)) + .then((bytes) => WebAssembly.instantiate(bytes, go.importObject)); + } else if ("instantiateStreaming" in WebAssembly) { + inst = WebAssembly.instantiateStreaming( + fetch(wasm_url), + go.importObject + ); + } else { + inst = fetch(wasm_url) + .then((response) => response.arrayBuffer()) + .then((bytes) => WebAssembly.instantiate(bytes, go.importObject)); + } + inst = (await inst).instance; go.run(inst); +} - const input_len = go.storeString(input); - const output_len = inst.exports.format(input_len); - if (output_len < 0) { - throw new Error(go.loadString(-output_len)); +export function format(input) { + const [err, result] = inst.format(input); + if (err) { + throw new Error(result); } - return go.loadString(output_len); + return result; } diff --git a/src/lib.go b/src/lib.go index a52e4dc..478ce70 100644 --- a/src/lib.go +++ b/src/lib.go @@ -1,31 +1,23 @@ package main -import "go/format" +import ( + "go/format" + "syscall/js" +) -const buf_len = 8192 +func Format(this js.Value, args []js.Value) any { + input := ([]byte)(args[0].String()) -var buf [buf_len]byte - -//go:export getBuffer -func GetBuffer() *byte { - return &buf[0] -} - -//go:export format -func Format(input_len uint) int { - input := buf[:input_len] output, err := format.Source(input) if err != nil { - return -copy(buf[:], []byte(err.Error())) + return []interface{}{true, err.Error()} } - result := len(output) - if result > buf_len { - return -copy(buf[:], []byte("Buffer out of memory")) - } - - copy(buf[:], output) - return result + return []interface{}{false, string(output)} } -func main() {} +func main() { + done := make(chan bool) + js.Global().Set("format", js.FuncOf(Format)) + <-done +}