Skip to content

Commit

Permalink
fix: use syscall/js for interop (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
magic-akari committed Aug 11, 2023
1 parent c251cbe commit c655602
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 81 deletions.
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
}

0 comments on commit c655602

Please sign in to comment.