From d5cf8de31e310618c651a2ecf2a29c6842191ceb Mon Sep 17 00:00:00 2001 From: Ash Date: Wed, 13 Sep 2023 21:12:41 +0100 Subject: [PATCH] chore: reorganise and add JS bridge code --- .gitignore | 1 + Makefile | 4 - README.md | 6 +- bun.lockb | Bin 0 -> 175647 bytes example.ts | 15 - package.json | 32 ++ src/bridge/index.ts | 2 + src/bridge/lib/format.ts | 47 ++ src/bridge/lib/get-query-params.ts | 12 + src/groqfmt/Makefile | 4 + go.mod => src/groqfmt/go.mod | 0 go.sum => src/groqfmt/go.sum | 0 main.go => src/groqfmt/main.go | 0 groqfmt.d.ts => src/groqfmt/types.ts | 12 +- src/groqfmt/wasm-exec.js | 664 +++++++++++++++++++++++++++ tsconfig.json | 19 + wasm-exec.js | 654 -------------------------- 17 files changed, 792 insertions(+), 680 deletions(-) delete mode 100644 Makefile create mode 100755 bun.lockb delete mode 100644 example.ts create mode 100644 package.json create mode 100644 src/bridge/index.ts create mode 100644 src/bridge/lib/format.ts create mode 100644 src/bridge/lib/get-query-params.ts create mode 100644 src/groqfmt/Makefile rename go.mod => src/groqfmt/go.mod (100%) rename go.sum => src/groqfmt/go.sum (100%) rename main.go => src/groqfmt/main.go (100%) rename groqfmt.d.ts => src/groqfmt/types.ts (59%) create mode 100644 src/groqfmt/wasm-exec.js create mode 100644 tsconfig.json delete mode 100644 wasm-exec.js diff --git a/.gitignore b/.gitignore index ec8b21e..a55f56c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ dist +node_modules # Created by https://www.toptal.com/developers/gitignore/api/macos,go # Edit at https://www.toptal.com/developers/gitignore?templates=macos,go diff --git a/Makefile b/Makefile deleted file mode 100644 index 16487a0..0000000 --- a/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -all: build - -build: - GOOS=js GOARCH=wasm go build -o dist/groqfmt.wasm diff --git a/README.md b/README.md index b80bd89..bec022f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# groqfmt-wasm +# @groqfmt/wasm -groqfmt-wasm is a formatter for [the GROQ query language](https://github.com/sanity-io/GROQ), designed to be compiled to WebAssembly. This tool is largely based on the existing [groqfmt](https://github.com/sanity-io/groqfmt) tool, and built on top of other tools from the GROQ ecosystem. +@groqfmt/wasm is a formatter for [the GROQ query language](https://github.com/sanity-io/GROQ), designed to be compiled to WebAssembly. This tool is largely based on the existing [groqfmt](https://github.com/sanity-io/groqfmt) tool, and built on top of other tools from the GROQ ecosystem. -Currently the formatter is exposed to JS as the global function `groqfmt`, because I can't get TinyGo's exports working. I'd like to fix that. +The formatter is exposed to JS as the global function `groqfmt`. diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..214830bcb55b573437457d478a12e9676a4a0d15 GIT binary patch literal 175647 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

a9zF0om%D_0EDt-ZoU>b3x!D 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 literal 0 HcmV?d00001 diff --git a/example.ts b/example.ts deleted file mode 100644 index 080a676..0000000 --- a/example.ts +++ /dev/null @@ -1,15 +0,0 @@ -/// -import './wasm-exec.js' - -const go = new Go() -const wasmCode = await Deno.readFile('./dist/groqfmt.wasm') -const wasmModule = new WebAssembly.Module(wasmCode) -const wasmInstance = new WebAssembly.Instance(wasmModule, go.importObject) - -go.run(wasmInstance) - -console.log( - groqfmt( - `*[_type =='hellloDave'] { _id, name, 'someOtherThing': count(thing)}`, - ).result, -) diff --git a/package.json b/package.json new file mode 100644 index 0000000..1c4cb59 --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "name": "@groqfmt/wasm", + "author": "Ash", + "version": "1.0.0", + "license": "MIT", + "repository": "github:juice49/groqfmt-wasm", + "keywords": [ + "groq", + "sanity", + "format", + "wasm", + "webassembly" + ], + "files": [ + "dist" + ], + "types": "./dist/types.d.ts", + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "source": "./src/bridge/index.ts", + "scripts": { + "groqfmt:build": "cd ./src/groqfmt && make", + "bridge:build": "pkg build --strict && pkg check --strict && cp ./src/groqfmt/wasm-exec.js ./dist", + "build": "bun run groqfmt:build && bun run bridge:build" + }, + "devDependencies": { + "@sanity/pkg-utils": "^2.4.8", + "bun-types": "latest", + "prettier": "^3.0.3", + "typescript": "^5.0.0" + } +} diff --git a/src/bridge/index.ts b/src/bridge/index.ts new file mode 100644 index 0000000..df767d1 --- /dev/null +++ b/src/bridge/index.ts @@ -0,0 +1,2 @@ +export * from '../groqfmt/types' +export * from './lib/format' diff --git a/src/bridge/lib/format.ts b/src/bridge/lib/format.ts new file mode 100644 index 0000000..7f86a33 --- /dev/null +++ b/src/bridge/lib/format.ts @@ -0,0 +1,47 @@ +import { type Groqfmt, type GroqfmtResult } from '../../groqfmt/types' +import getQueryParams from './get-query-params' + +/** @public */ +export type GroqfmtResultEnhanced = GroqfmtResult & { + params?: Record +} + +/** @public */ +export function format({ + input, + groqfmt, +}: { + input: string + groqfmt: Groqfmt +}): GroqfmtResultEnhanced { + const { result, error } = groqfmt(input) + + if (error) { + try { + const url = new URL(input) + const query = url.searchParams.get('query') + + if (query) { + return { + ...groqfmt(query), + params: getQueryParams(url), + } + } + + return { + result, + error, + } + } catch {} + + return { + result, + error, + } + } + + return { + result, + error, + } +} diff --git a/src/bridge/lib/get-query-params.ts b/src/bridge/lib/get-query-params.ts new file mode 100644 index 0000000..cef1f84 --- /dev/null +++ b/src/bridge/lib/get-query-params.ts @@ -0,0 +1,12 @@ +export default function getQueryParams(url: URL): Record { + return [...url.searchParams.entries()].reduce((params, [key, value]) => { + if (!key.startsWith('$')) { + return params + } + + return { + ...params, + [key.slice(1)]: value.replaceAll('"', ''), + } + }, {}) +} diff --git a/src/groqfmt/Makefile b/src/groqfmt/Makefile new file mode 100644 index 0000000..dfb9cfb --- /dev/null +++ b/src/groqfmt/Makefile @@ -0,0 +1,4 @@ +all: build + +build: + GOOS=js GOARCH=wasm go build -o ../../dist/groqfmt.wasm diff --git a/go.mod b/src/groqfmt/go.mod similarity index 100% rename from go.mod rename to src/groqfmt/go.mod diff --git a/go.sum b/src/groqfmt/go.sum similarity index 100% rename from go.sum rename to src/groqfmt/go.sum diff --git a/main.go b/src/groqfmt/main.go similarity index 100% rename from main.go rename to src/groqfmt/main.go diff --git a/groqfmt.d.ts b/src/groqfmt/types.ts similarity index 59% rename from groqfmt.d.ts rename to src/groqfmt/types.ts index c3c3cdd..d5ef0cf 100644 --- a/groqfmt.d.ts +++ b/src/groqfmt/types.ts @@ -1,10 +1,12 @@ -interface GroqfmtError { +/** @public */ +export interface GroqfmtError { begin: number end?: number message: string } -type GroqfmtResult = +/** @public */ +export type GroqfmtResult = | { error: GroqfmtError result: undefined @@ -14,9 +16,11 @@ type GroqfmtResult = result: string } -declare const groqfmt: (query: string) => GroqfmtResult +/** @public */ +export type Groqfmt = (query: string) => GroqfmtResult -declare const Go: { +/**@public */ +export interface Go { new (): { run: (instance: WebAssembly.Instance) => Promise importObject: WebAssembly.Imports diff --git a/src/groqfmt/wasm-exec.js b/src/groqfmt/wasm-exec.js new file mode 100644 index 0000000..1d882b4 --- /dev/null +++ b/src/groqfmt/wasm-exec.js @@ -0,0 +1,664 @@ +// 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. + +'use strict' + +// https://github.com/golang/go/issues/56860#issuecomment-1321274898 +// https://go-review.googlesource.com/c/go/+/463975/2/misc/wasm/wasm_exec_node.js#b30 +globalThis.crypto ??= require('crypto') +;(() => { + const enosys = () => { + const err = new Error('not implemented') + err.code = 'ENOSYS' + return err + } + + if (!globalThis.fs) { + let outputBuf = '' + globalThis.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.substring(0, nl)) + outputBuf = outputBuf.substring(nl + 1) + } + return buf.length + }, + write(fd, buf, offset, length, position, callback) { + if (offset !== 0 || length !== buf.length || position !== null) { + callback(enosys()) + return + } + const n = this.writeSync(fd, buf) + callback(null, n) + }, + chmod(path, mode, callback) { + callback(enosys()) + }, + chown(path, uid, gid, callback) { + callback(enosys()) + }, + close(fd, callback) { + callback(enosys()) + }, + fchmod(fd, mode, callback) { + callback(enosys()) + }, + fchown(fd, uid, gid, callback) { + callback(enosys()) + }, + fstat(fd, callback) { + callback(enosys()) + }, + fsync(fd, callback) { + callback(null) + }, + ftruncate(fd, length, callback) { + callback(enosys()) + }, + lchown(path, uid, gid, callback) { + callback(enosys()) + }, + link(path, link, callback) { + callback(enosys()) + }, + lstat(path, callback) { + callback(enosys()) + }, + mkdir(path, perm, callback) { + callback(enosys()) + }, + open(path, flags, mode, callback) { + callback(enosys()) + }, + read(fd, buffer, offset, length, position, callback) { + callback(enosys()) + }, + readdir(path, callback) { + callback(enosys()) + }, + readlink(path, callback) { + callback(enosys()) + }, + rename(from, to, callback) { + callback(enosys()) + }, + rmdir(path, callback) { + callback(enosys()) + }, + stat(path, callback) { + callback(enosys()) + }, + symlink(path, link, callback) { + callback(enosys()) + }, + truncate(path, length, callback) { + callback(enosys()) + }, + unlink(path, callback) { + callback(enosys()) + }, + utimes(path, atime, mtime, callback) { + callback(enosys()) + }, + } + } + + if (!globalThis.process) { + globalThis.process = { + getuid() { + return -1 + }, + getgid() { + return -1 + }, + geteuid() { + return -1 + }, + getegid() { + return -1 + }, + getgroups() { + throw enosys() + }, + pid: -1, + ppid: -1, + umask() { + throw enosys() + }, + cwd() { + throw enosys() + }, + chdir() { + throw enosys() + }, + } + } + + if (!globalThis.crypto) { + throw new Error( + 'globalThis.crypto is not available, polyfill required (crypto.getRandomValues only)', + ) + } + + if (!globalThis.performance) { + throw new Error( + 'globalThis.performance is not available, polyfill required (performance.now only)', + ) + } + + if (!globalThis.TextEncoder) { + throw new Error( + 'globalThis.TextEncoder is not available, polyfill required', + ) + } + + if (!globalThis.TextDecoder) { + throw new Error( + 'globalThis.TextDecoder is not available, polyfill required', + ) + } + + const encoder = new TextEncoder('utf-8') + const decoder = new TextDecoder('utf-8') + + globalThis.Go = class { + constructor() { + this.argv = ['js'] + this.env = {} + this.exit = code => { + if (code !== 0) { + console.warn('exit code:', code) + } + } + this._exitPromise = new Promise(resolve => { + this._resolveExitPromise = resolve + }) + this._pendingEvent = null + this._scheduledTimeouts = new Map() + this._nextCallbackTimeoutID = 1 + + const setInt64 = (addr, v) => { + this.mem.setUint32(addr + 0, v, true) + this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true) + } + + const getInt64 = addr => { + const low = this.mem.getUint32(addr + 0, true) + const high = this.mem.getInt32(addr + 4, true) + return low + high * 4294967296 + } + + const loadValue = addr => { + const f = this.mem.getFloat64(addr, true) + if (f === 0) { + return undefined + } + if (!isNaN(f)) { + return f + } + + const id = this.mem.getUint32(addr, true) + return this._values[id] + } + + const storeValue = (addr, v) => { + const nanHead = 0x7ff80000 + + if (typeof v === 'number' && v !== 0) { + if (isNaN(v)) { + this.mem.setUint32(addr + 4, nanHead, true) + this.mem.setUint32(addr, 0, true) + return + } + this.mem.setFloat64(addr, v, true) + return + } + + if (v === undefined) { + this.mem.setFloat64(addr, 0, true) + return + } + + let id = this._ids.get(v) + if (id === undefined) { + id = this._idPool.pop() + if (id === undefined) { + id = this._values.length + } + this._values[id] = v + this._goRefCounts[id] = 0 + this._ids.set(v, id) + } + this._goRefCounts[id]++ + let typeFlag = 0 + switch (typeof v) { + case 'object': + if (v !== null) { + typeFlag = 1 + } + break + case 'string': + typeFlag = 2 + break + case 'symbol': + typeFlag = 3 + break + case 'function': + typeFlag = 4 + break + } + this.mem.setUint32(addr + 4, nanHead | typeFlag, true) + this.mem.setUint32(addr, id, 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: { + // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters) + // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported + // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function). + // This changes the SP, thus we have to update the SP used by the imported function. + + // func wasmExit(code int32) + 'runtime.wasmExit': sp => { + sp >>>= 0 + const code = this.mem.getInt32(sp + 8, true) + this.exited = true + delete this._inst + delete this._values + delete this._goRefCounts + delete this._ids + delete this._idPool + this.exit(code) + }, + + // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) + 'runtime.wasmWrite': sp => { + sp >>>= 0 + const fd = getInt64(sp + 8) + const p = getInt64(sp + 16) + const n = this.mem.getInt32(sp + 24, true) + fs.writeSync( + fd, + new Uint8Array(this._inst.exports.mem.buffer, p, n), + ) + }, + + // func resetMemoryDataView() + 'runtime.resetMemoryDataView': sp => { + sp >>>= 0 + this.mem = new DataView(this._inst.exports.mem.buffer) + }, + + // func nanotime1() int64 + 'runtime.nanotime1': sp => { + sp >>>= 0 + setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000) + }, + + // func walltime() (sec int64, nsec int32) + 'runtime.walltime': sp => { + sp >>>= 0 + const msec = new Date().getTime() + setInt64(sp + 8, msec / 1000) + this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true) + }, + + // func scheduleTimeoutEvent(delay int64) int32 + 'runtime.scheduleTimeoutEvent': sp => { + sp >>>= 0 + const id = this._nextCallbackTimeoutID + this._nextCallbackTimeoutID++ + this._scheduledTimeouts.set( + id, + setTimeout( + () => { + this._resume() + while (this._scheduledTimeouts.has(id)) { + // for some reason Go failed to register the timeout event, log and try again + // (temporary workaround for https://github.com/golang/go/issues/28975) + console.warn('scheduleTimeoutEvent: missed timeout event') + this._resume() + } + }, + getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early + ), + ) + this.mem.setInt32(sp + 16, id, true) + }, + + // func clearTimeoutEvent(id int32) + 'runtime.clearTimeoutEvent': sp => { + sp >>>= 0 + const id = this.mem.getInt32(sp + 8, true) + clearTimeout(this._scheduledTimeouts.get(id)) + this._scheduledTimeouts.delete(id) + }, + + // func getRandomData(r []byte) + 'runtime.getRandomData': sp => { + sp >>>= 0 + crypto.getRandomValues(loadSlice(sp + 8)) + }, + + // func finalizeRef(v ref) + 'syscall/js.finalizeRef': sp => { + sp >>>= 0 + const id = this.mem.getUint32(sp + 8, true) + this._goRefCounts[id]-- + if (this._goRefCounts[id] === 0) { + const v = this._values[id] + this._values[id] = null + this._ids.delete(v) + this._idPool.push(id) + } + }, + + // func stringVal(value string) ref + 'syscall/js.stringVal': sp => { + sp >>>= 0 + storeValue(sp + 24, loadString(sp + 8)) + }, + + // func valueGet(v ref, p string) ref + 'syscall/js.valueGet': sp => { + sp >>>= 0 + const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16)) + sp = this._inst.exports.getsp() >>> 0 // see comment above + storeValue(sp + 32, result) + }, + + // func valueSet(v ref, p string, x ref) + 'syscall/js.valueSet': sp => { + sp >>>= 0 + Reflect.set( + loadValue(sp + 8), + loadString(sp + 16), + loadValue(sp + 32), + ) + }, + + // func valueDelete(v ref, p string) + 'syscall/js.valueDelete': sp => { + sp >>>= 0 + Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16)) + }, + + // func valueIndex(v ref, i int) ref + 'syscall/js.valueIndex': sp => { + sp >>>= 0 + storeValue( + sp + 24, + Reflect.get(loadValue(sp + 8), getInt64(sp + 16)), + ) + }, + + // valueSetIndex(v ref, i int, x ref) + 'syscall/js.valueSetIndex': sp => { + sp >>>= 0 + 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 => { + sp >>>= 0 + try { + const v = loadValue(sp + 8) + const m = Reflect.get(v, loadString(sp + 16)) + const args = loadSliceOfValues(sp + 32) + const result = Reflect.apply(m, v, args) + sp = this._inst.exports.getsp() >>> 0 // see comment above + storeValue(sp + 56, result) + this.mem.setUint8(sp + 64, 1) + } catch (err) { + sp = this._inst.exports.getsp() >>> 0 // see comment above + storeValue(sp + 56, err) + this.mem.setUint8(sp + 64, 0) + } + }, + + // func valueInvoke(v ref, args []ref) (ref, bool) + 'syscall/js.valueInvoke': sp => { + sp >>>= 0 + try { + const v = loadValue(sp + 8) + const args = loadSliceOfValues(sp + 16) + const result = Reflect.apply(v, undefined, args) + sp = this._inst.exports.getsp() >>> 0 // see comment above + storeValue(sp + 40, result) + this.mem.setUint8(sp + 48, 1) + } catch (err) { + sp = this._inst.exports.getsp() >>> 0 // see comment above + storeValue(sp + 40, err) + this.mem.setUint8(sp + 48, 0) + } + }, + + // func valueNew(v ref, args []ref) (ref, bool) + 'syscall/js.valueNew': sp => { + sp >>>= 0 + try { + const v = loadValue(sp + 8) + const args = loadSliceOfValues(sp + 16) + const result = Reflect.construct(v, args) + sp = this._inst.exports.getsp() >>> 0 // see comment above + storeValue(sp + 40, result) + this.mem.setUint8(sp + 48, 1) + } catch (err) { + sp = this._inst.exports.getsp() >>> 0 // see comment above + storeValue(sp + 40, err) + this.mem.setUint8(sp + 48, 0) + } + }, + + // func valueLength(v ref) int + 'syscall/js.valueLength': sp => { + sp >>>= 0 + setInt64(sp + 16, parseInt(loadValue(sp + 8).length)) + }, + + // valuePrepareString(v ref) (ref, int) + 'syscall/js.valuePrepareString': sp => { + sp >>>= 0 + 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 => { + sp >>>= 0 + const str = loadValue(sp + 8) + loadSlice(sp + 16).set(str) + }, + + // func valueInstanceOf(v ref, t ref) bool + 'syscall/js.valueInstanceOf': sp => { + sp >>>= 0 + this.mem.setUint8( + sp + 24, + loadValue(sp + 8) instanceof loadValue(sp + 16) ? 1 : 0, + ) + }, + + // func copyBytesToGo(dst []byte, src ref) (int, bool) + 'syscall/js.copyBytesToGo': sp => { + sp >>>= 0 + const dst = loadSlice(sp + 8) + const src = loadValue(sp + 32) + if ( + !(src instanceof Uint8Array || src instanceof Uint8ClampedArray) + ) { + this.mem.setUint8(sp + 48, 0) + return + } + const toCopy = src.subarray(0, dst.length) + dst.set(toCopy) + setInt64(sp + 40, toCopy.length) + this.mem.setUint8(sp + 48, 1) + }, + + // func copyBytesToJS(dst ref, src []byte) (int, bool) + 'syscall/js.copyBytesToJS': sp => { + sp >>>= 0 + const dst = loadValue(sp + 8) + const src = loadSlice(sp + 16) + if ( + !(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray) + ) { + this.mem.setUint8(sp + 48, 0) + return + } + const toCopy = src.subarray(0, dst.length) + dst.set(toCopy) + setInt64(sp + 40, toCopy.length) + this.mem.setUint8(sp + 48, 1) + }, + + debug: value => { + console.log(value) + }, + }, + } + } + + async run(instance) { + if (!(instance instanceof WebAssembly.Instance)) { + throw new Error('Go.run: WebAssembly.Instance expected') + } + this._inst = instance + this.mem = new DataView(this._inst.exports.mem.buffer) + this._values = [ + // JS values that Go currently has references to, indexed by reference id + NaN, + 0, + null, + true, + false, + globalThis, + this, + ] + this._goRefCounts = new Array(this._values.length).fill(Infinity) // number of references that Go has to a JS value, indexed by reference id + this._ids = new Map([ + // mapping from JS values to reference ids + [0, 1], + [null, 2], + [true, 3], + [false, 4], + [globalThis, 5], + [this, 6], + ]) + this._idPool = [] // unused ids that have been garbage collected + this.exited = false // whether the Go program has exited + + // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. + let offset = 4096 + + const strPtr = str => { + const ptr = offset + const bytes = encoder.encode(str + '\0') + new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes) + offset += bytes.length + if (offset % 8 !== 0) { + offset += 8 - (offset % 8) + } + return ptr + } + + const argc = this.argv.length + + const argvPtrs = [] + this.argv.forEach(arg => { + argvPtrs.push(strPtr(arg)) + }) + argvPtrs.push(0) + + const keys = Object.keys(this.env).sort() + keys.forEach(key => { + argvPtrs.push(strPtr(`${key}=${this.env[key]}`)) + }) + argvPtrs.push(0) + + const argv = offset + argvPtrs.forEach(ptr => { + this.mem.setUint32(offset, ptr, true) + this.mem.setUint32(offset + 4, 0, true) + offset += 8 + }) + + // The linker guarantees global data starts from at least wasmMinDataAddr. + // Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr. + const wasmMinDataAddr = 4096 + 8192 + if (offset >= wasmMinDataAddr) { + throw new Error( + 'total length of command line and environment variables exceeds limit', + ) + } + + this._inst.exports.run(argc, argv) + if (this.exited) { + this._resolveExitPromise() + } + await this._exitPromise + } + + _resume() { + if (this.exited) { + throw new Error('Go program has already exited') + } + this._inst.exports.resume() + if (this.exited) { + this._resolveExitPromise() + } + } + + _makeFuncWrapper(id) { + const go = this + return function () { + const event = { id: id, this: this, args: arguments } + go._pendingEvent = event + go._resume() + return event.result + } + } + } +})() diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..d19d3bb --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "lib": ["dom", "dom.iterable", "ESNext"], + "module": "esnext", + "target": "esnext", + "moduleResolution": "bundler", + "moduleDetection": "force", + "allowImportingTsExtensions": true, + "noEmit": true, + "outDir": "dist", + "composite": true, + "strict": true, + "downlevelIteration": true, + "skipLibCheck": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "allowJs": true + } +} diff --git a/wasm-exec.js b/wasm-exec.js deleted file mode 100644 index eb739e4..0000000 --- a/wasm-exec.js +++ /dev/null @@ -1,654 +0,0 @@ -// 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. -// -// This file has been modified for use by the TinyGo compiler. - -(() => { - // Map multiple JavaScript environments to a single common API, - // preferring web standards over Node.js API. - // - // Environments considered: - // - Browsers - // - Node.js - // - Electron - // - Parcel - - if (typeof global !== "undefined") { - // global already exists - } else if (typeof window !== "undefined") { - window.global = window; - } else if (typeof self !== "undefined") { - self.global = self; - } else { - throw new Error( - "cannot export Go (neither global, window nor self is defined)" - ); - } - - if (!global.require && typeof require !== "undefined") { - global.require = require; - } - - if (!global.fs && global.require) { - global.fs = require("fs"); - } - - const enosys = () => { - const err = new Error("not implemented"); - err.code = "ENOSYS"; - return err; - }; - - if (!global.fs) { - 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; - }, - write(fd, buf, offset, length, position, callback) { - if (offset !== 0 || length !== buf.length || position !== null) { - callback(enosys()); - return; - } - const n = this.writeSync(fd, buf); - callback(null, n); - }, - chmod(path, mode, callback) { - callback(enosys()); - }, - chown(path, uid, gid, callback) { - callback(enosys()); - }, - close(fd, callback) { - callback(enosys()); - }, - fchmod(fd, mode, callback) { - callback(enosys()); - }, - fchown(fd, uid, gid, callback) { - callback(enosys()); - }, - fstat(fd, callback) { - callback(enosys()); - }, - fsync(fd, callback) { - callback(null); - }, - ftruncate(fd, length, callback) { - callback(enosys()); - }, - lchown(path, uid, gid, callback) { - callback(enosys()); - }, - link(path, link, callback) { - callback(enosys()); - }, - lstat(path, callback) { - callback(enosys()); - }, - mkdir(path, perm, callback) { - callback(enosys()); - }, - open(path, flags, mode, callback) { - callback(enosys()); - }, - read(fd, buffer, offset, length, position, callback) { - callback(enosys()); - }, - readdir(path, callback) { - callback(enosys()); - }, - readlink(path, callback) { - callback(enosys()); - }, - rename(from, to, callback) { - callback(enosys()); - }, - rmdir(path, callback) { - callback(enosys()); - }, - stat(path, callback) { - callback(enosys()); - }, - symlink(path, link, callback) { - callback(enosys()); - }, - truncate(path, length, callback) { - callback(enosys()); - }, - unlink(path, callback) { - callback(enosys()); - }, - utimes(path, atime, mtime, callback) { - callback(enosys()); - }, - }; - } - - if (!global.process) { - global.process = { - getuid() { - return -1; - }, - getgid() { - return -1; - }, - geteuid() { - return -1; - }, - getegid() { - return -1; - }, - getgroups() { - throw enosys(); - }, - pid: -1, - ppid: -1, - umask() { - throw enosys(); - }, - cwd() { - throw enosys(); - }, - chdir() { - throw enosys(); - }, - }; - } - - if (!global.crypto) { - const nodeCrypto = require("crypto"); - global.crypto = { - getRandomValues(b) { - nodeCrypto.randomFillSync(b); - }, - }; - } - - if (!global.performance) { - global.performance = { - now() { - const [sec, nsec] = process.hrtime(); - return sec * 1000 + nsec / 1000000; - }, - }; - } - - if (!global.TextEncoder) { - global.TextEncoder = require("util").TextEncoder; - } - - if (!global.TextDecoder) { - global.TextDecoder = require("util").TextDecoder; - } - - // End of polyfills for common API. - - const encoder = new TextEncoder("utf-8"); - const decoder = new TextDecoder("utf-8"); - var logLine = []; - - global.Go = class { - constructor() { - this._callbackTimeouts = new Map(); - this._nextCallbackTimeoutID = 1; - - const mem = () => { - // The buffer may change when requesting more memory. - return new DataView(this._inst.exports.memory.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 (f === 0) { - return undefined; - } - 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; - } - if (v === 0) { - mem().setUint32(addr + 4, nanHead, true); - mem().setUint32(addr, 1, true); - return; - } - mem().setFloat64(addr, v, true); - return; - } - - switch (v) { - case undefined: - mem().setFloat64(addr, 0, 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 id = this._ids.get(v); - if (id === undefined) { - id = this._idPool.pop(); - if (id === undefined) { - id = this._values.length; - } - this._values[id] = v; - this._goRefCounts[id] = 0; - this._ids.set(v, id); - } - this._goRefCounts[id]++; - let typeFlag = 1; - switch (typeof v) { - case "string": - typeFlag = 2; - break; - case "symbol": - typeFlag = 3; - break; - case "function": - typeFlag = 4; - break; - } - mem().setUint32(addr + 4, nanHead | typeFlag, true); - mem().setUint32(addr, id, true); - }; - - const loadSlice = (array, len, cap) => { - return new Uint8Array(this._inst.exports.memory.buffer, array, len); - }; - - const loadSliceOfValues = (array, len, cap) => { - const a = new Array(len); - for (let i = 0; i < len; i++) { - a[i] = loadValue(array + i * 8); - } - return a; - }; - - const loadString = (ptr, len) => { - return decoder.decode( - new DataView(this._inst.exports.memory.buffer, ptr, len) - ); - }; - - const timeOrigin = Date.now() - performance.now(); - 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 < iovs_len; iovs_i++) { - let iov_ptr = iovs_ptr + iovs_i * 8; // assuming wasm32 - let ptr = mem().getUint32(iov_ptr + 0, true); - let len = mem().getUint32(iov_ptr + 4, true); - nwritten += len; - for (let i = 0; i < len; i++) { - let c = mem().getUint8(ptr + i); - if (c == 13) { - // CR - // ignore - } else if (c == 10) { - // LF - // write line - let line = decoder.decode(new Uint8Array(logLine)); - logLine = []; - console.log(line); - } else { - logLine.push(c); - } - } - } - } else { - console.error("invalid file descriptor:", fd); - } - mem().setUint32(nwritten_ptr, nwritten, true); - return 0; - }, - fd_close: () => 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; - }, - }, - env: { - // func ticks() float64 - "runtime.ticks": () => { - return timeOrigin + performance.now(); - }, - - // func sleepTicks(timeout float64) - "runtime.sleepTicks": (timeout) => { - // Do not sleep, only reactivate scheduler after the given timeout. - setTimeout(this._inst.exports.go_scheduler, timeout); - }, - - // func finalizeRef(v ref) - "syscall/js.finalizeRef": (sp) => { - // Note: TinyGo does not support finalizers so this should never be - // called. - console.error("syscall/js.finalizeRef not implemented"); - }, - - // func stringVal(value string) ref - "syscall/js.stringVal": (ret_ptr, value_ptr, value_len) => { - const s = loadString(value_ptr, value_len); - storeValue(ret_ptr, s); - }, - - // func valueGet(v ref, p string) ref - "syscall/js.valueGet": (retval, v_addr, p_ptr, p_len) => { - let prop = loadString(p_ptr, p_len); - let value = loadValue(v_addr); - let result = Reflect.get(value, prop); - storeValue(retval, result); - }, - - // func valueSet(v ref, p string, x ref) - "syscall/js.valueSet": (v_addr, p_ptr, p_len, x_addr) => { - const v = loadValue(v_addr); - const p = loadString(p_ptr, p_len); - const x = loadValue(x_addr); - Reflect.set(v, p, x); - }, - - // func valueDelete(v ref, p string) - "syscall/js.valueDelete": (v_addr, p_ptr, p_len) => { - const v = loadValue(v_addr); - const p = loadString(p_ptr, p_len); - Reflect.deleteProperty(v, p); - }, - - // func valueIndex(v ref, i int) ref - "syscall/js.valueIndex": (ret_addr, v_addr, i) => { - storeValue(ret_addr, Reflect.get(loadValue(v_addr), i)); - }, - - // valueSetIndex(v ref, i int, x ref) - "syscall/js.valueSetIndex": (v_addr, i, x_addr) => { - Reflect.set(loadValue(v_addr), i, loadValue(x_addr)); - }, - - // func valueCall(v ref, m string, args []ref) (ref, bool) - "syscall/js.valueCall": ( - ret_addr, - v_addr, - m_ptr, - m_len, - args_ptr, - args_len, - args_cap - ) => { - const v = loadValue(v_addr); - const name = loadString(m_ptr, m_len); - const args = loadSliceOfValues(args_ptr, args_len, args_cap); - try { - const m = Reflect.get(v, name); - storeValue(ret_addr, Reflect.apply(m, v, args)); - mem().setUint8(ret_addr + 8, 1); - } catch (err) { - storeValue(ret_addr, err); - mem().setUint8(ret_addr + 8, 0); - } - }, - - // func valueInvoke(v ref, args []ref) (ref, bool) - "syscall/js.valueInvoke": ( - ret_addr, - v_addr, - args_ptr, - args_len, - args_cap - ) => { - try { - const v = loadValue(v_addr); - const args = loadSliceOfValues(args_ptr, args_len, args_cap); - storeValue(ret_addr, Reflect.apply(v, undefined, args)); - mem().setUint8(ret_addr + 8, 1); - } catch (err) { - storeValue(ret_addr, err); - mem().setUint8(ret_addr + 8, 0); - } - }, - - // func valueNew(v ref, args []ref) (ref, bool) - "syscall/js.valueNew": ( - ret_addr, - v_addr, - args_ptr, - args_len, - args_cap - ) => { - const v = loadValue(v_addr); - const args = loadSliceOfValues(args_ptr, args_len, args_cap); - try { - storeValue(ret_addr, Reflect.construct(v, args)); - mem().setUint8(ret_addr + 8, 1); - } catch (err) { - storeValue(ret_addr, err); - mem().setUint8(ret_addr + 8, 0); - } - }, - - // func valueLength(v ref) int - "syscall/js.valueLength": (v_addr) => { - return loadValue(v_addr).length; - }, - - // valuePrepareString(v ref) (ref, int) - "syscall/js.valuePrepareString": (ret_addr, v_addr) => { - const s = String(loadValue(v_addr)); - const str = encoder.encode(s); - storeValue(ret_addr, str); - setInt64(ret_addr + 8, str.length); - }, - - // valueLoadString(v ref, b []byte) - "syscall/js.valueLoadString": ( - v_addr, - slice_ptr, - slice_len, - slice_cap - ) => { - const str = loadValue(v_addr); - loadSlice(slice_ptr, slice_len, slice_cap).set(str); - }, - - // func valueInstanceOf(v ref, t ref) bool - "syscall/js.valueInstanceOf": (v_addr, t_addr) => { - return loadValue(v_addr) instanceof loadValue(t_addr); - }, - - // func copyBytesToGo(dst []byte, src ref) (int, bool) - "syscall/js.copyBytesToGo": ( - ret_addr, - dest_addr, - dest_len, - dest_cap, - source_addr - ) => { - let num_bytes_copied_addr = ret_addr; - let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable - - const dst = loadSlice(dest_addr, dest_len); - const src = loadValue(source_addr); - if ( - !(src instanceof Uint8Array || src instanceof Uint8ClampedArray) - ) { - mem().setUint8(returned_status_addr, 0); // Return "not ok" status - return; - } - const toCopy = src.subarray(0, dst.length); - dst.set(toCopy); - setInt64(num_bytes_copied_addr, toCopy.length); - mem().setUint8(returned_status_addr, 1); // Return "ok" status - }, - - // copyBytesToJS(dst ref, src []byte) (int, bool) - // Originally copied from upstream Go project, then modified: - // https://github.com/golang/go/blob/3f995c3f3b43033013013e6c7ccc93a9b1411ca9/misc/wasm/wasm_exec.js#L404-L416 - "syscall/js.copyBytesToJS": ( - ret_addr, - dest_addr, - source_addr, - source_len, - source_cap - ) => { - let num_bytes_copied_addr = ret_addr; - let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable - - const dst = loadValue(dest_addr); - const src = loadSlice(source_addr, source_len); - if ( - !(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray) - ) { - mem().setUint8(returned_status_addr, 0); // Return "not ok" status - return; - } - const toCopy = src.subarray(0, dst.length); - dst.set(toCopy); - setInt64(num_bytes_copied_addr, toCopy.length); - mem().setUint8(returned_status_addr, 1); // Return "ok" status - }, - }, - }; - } - - async run(instance) { - this._inst = instance; - this._values = [ - // JS values that Go currently has references to, indexed by reference id - NaN, - 0, - null, - true, - false, - global, - this, - ]; - this._goRefCounts = []; // number of references that Go has to a JS value, indexed by reference id - this._ids = new Map(); // mapping from JS values to reference ids - 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 = () => { - if (this.exited) { - throw new Error("bad callback: Go program has already exited"); - } - setTimeout(resolve, 0); // make sure it is asynchronous - }; - }); - this._inst.exports._start(); - if (this.exited) { - break; - } - await callbackPromise; - } - } - - _resume() { - if (this.exited) { - throw new Error("Go program has already exited"); - } - this._inst.exports.resume(); - if (this.exited) { - this._resolveExitPromise(); - } - } - - _makeFuncWrapper(id) { - const go = this; - return function () { - const event = { id: id, this: this, args: arguments }; - go._pendingEvent = event; - go._resume(); - return event.result; - }; - } - }; - - if ( - global.require && - global.require.main === module && - global.process && - global.process.versions && - !global.process.versions.electron - ) { - if (process.argv.length != 3) { - console.error("usage: go_js_wasm_exec [wasm binary] [arguments]"); - process.exit(1); - } - - const go = new Go(); - WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject) - .then((result) => { - return go.run(result.instance); - }) - .catch((err) => { - console.error(err); - process.exit(1); - }); - } -})();