Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: use syscall/js for interop #3

Merged
merged 2 commits into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 78 additions & 30 deletions go_wasm.patch
Original file line number Diff line number Diff line change
@@ -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.

Expand Down Expand Up @@ -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<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;
- },
+ 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)
Expand All @@ -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 @@
};
}
}
Expand Down
63 changes: 33 additions & 30 deletions lib.js
Original file line number Diff line number Diff line change
@@ -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;
}
34 changes: 13 additions & 21 deletions src/lib.go
Original file line number Diff line number Diff line change
@@ -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
}