detached dfc9db1

build(systemviews): add wasm runtime assets

commit
dfc9db138e1886fddd432cedeb646478b181ae41
author
brice · · 2026-04-12 09:01 UTC
parent
cf2b13e
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +575 -0
A assets/systemviews/systemviews.wasm +0 -0 binary file: no text diff

binary file: no text diff

A assets/systemviews/wasm_exec.js +575 -0
@@ -0,0 +1,575 @@
1+// Copyright 2018 The Go Authors. All rights reserved.
2+// Use of this source code is governed by a BSD-style
3+// license that can be found in the LICENSE file.
4+
5+"use strict";
6+
7+(() => {
8+ const enosys = () => {
9+ const err = new Error("not implemented");
10+ err.code = "ENOSYS";
11+ return err;
12+ };
13+
14+ if (!globalThis.fs) {
15+ let outputBuf = "";
16+ globalThis.fs = {
17+ constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1, O_DIRECTORY: -1 }, // unused
18+ writeSync(fd, buf) {
19+ outputBuf += decoder.decode(buf);
20+ const nl = outputBuf.lastIndexOf("\n");
21+ if (nl != -1) {
22+ console.log(outputBuf.substring(0, nl));
23+ outputBuf = outputBuf.substring(nl + 1);
24+ }
25+ return buf.length;
26+ },
27+ write(fd, buf, offset, length, position, callback) {
28+ if (offset !== 0 || length !== buf.length || position !== null) {
29+ callback(enosys());
30+ return;
31+ }
32+ const n = this.writeSync(fd, buf);
33+ callback(null, n);
34+ },
35+ chmod(path, mode, callback) { callback(enosys()); },
36+ chown(path, uid, gid, callback) { callback(enosys()); },
37+ close(fd, callback) { callback(enosys()); },
38+ fchmod(fd, mode, callback) { callback(enosys()); },
39+ fchown(fd, uid, gid, callback) { callback(enosys()); },
40+ fstat(fd, callback) { callback(enosys()); },
41+ fsync(fd, callback) { callback(null); },
42+ ftruncate(fd, length, callback) { callback(enosys()); },
43+ lchown(path, uid, gid, callback) { callback(enosys()); },
44+ link(path, link, callback) { callback(enosys()); },
45+ lstat(path, callback) { callback(enosys()); },
46+ mkdir(path, perm, callback) { callback(enosys()); },
47+ open(path, flags, mode, callback) { callback(enosys()); },
48+ read(fd, buffer, offset, length, position, callback) { callback(enosys()); },
49+ readdir(path, callback) { callback(enosys()); },
50+ readlink(path, callback) { callback(enosys()); },
51+ rename(from, to, callback) { callback(enosys()); },
52+ rmdir(path, callback) { callback(enosys()); },
53+ stat(path, callback) { callback(enosys()); },
54+ symlink(path, link, callback) { callback(enosys()); },
55+ truncate(path, length, callback) { callback(enosys()); },
56+ unlink(path, callback) { callback(enosys()); },
57+ utimes(path, atime, mtime, callback) { callback(enosys()); },
58+ };
59+ }
60+
61+ if (!globalThis.process) {
62+ globalThis.process = {
63+ getuid() { return -1; },
64+ getgid() { return -1; },
65+ geteuid() { return -1; },
66+ getegid() { return -1; },
67+ getgroups() { throw enosys(); },
68+ pid: -1,
69+ ppid: -1,
70+ umask() { throw enosys(); },
71+ cwd() { throw enosys(); },
72+ chdir() { throw enosys(); },
73+ }
74+ }
75+
76+ if (!globalThis.path) {
77+ globalThis.path = {
78+ resolve(...pathSegments) {
79+ return pathSegments.join("/");
80+ }
81+ }
82+ }
83+
84+ if (!globalThis.crypto) {
85+ throw new Error("globalThis.crypto is not available, polyfill required (crypto.getRandomValues only)");
86+ }
87+
88+ if (!globalThis.performance) {
89+ throw new Error("globalThis.performance is not available, polyfill required (performance.now only)");
90+ }
91+
92+ if (!globalThis.TextEncoder) {
93+ throw new Error("globalThis.TextEncoder is not available, polyfill required");
94+ }
95+
96+ if (!globalThis.TextDecoder) {
97+ throw new Error("globalThis.TextDecoder is not available, polyfill required");
98+ }
99+
100+ const encoder = new TextEncoder("utf-8");
101+ const decoder = new TextDecoder("utf-8");
102+
103+ globalThis.Go = class {
104+ constructor() {
105+ this.argv = ["js"];
106+ this.env = {};
107+ this.exit = (code) => {
108+ if (code !== 0) {
109+ console.warn("exit code:", code);
110+ }
111+ };
112+ this._exitPromise = new Promise((resolve) => {
113+ this._resolveExitPromise = resolve;
114+ });
115+ this._pendingEvent = null;
116+ this._scheduledTimeouts = new Map();
117+ this._nextCallbackTimeoutID = 1;
118+
119+ const setInt64 = (addr, v) => {
120+ this.mem.setUint32(addr + 0, v, true);
121+ this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true);
122+ }
123+
124+ const setInt32 = (addr, v) => {
125+ this.mem.setUint32(addr + 0, v, true);
126+ }
127+
128+ const getInt64 = (addr) => {
129+ const low = this.mem.getUint32(addr + 0, true);
130+ const high = this.mem.getInt32(addr + 4, true);
131+ return low + high * 4294967296;
132+ }
133+
134+ const loadValue = (addr) => {
135+ const f = this.mem.getFloat64(addr, true);
136+ if (f === 0) {
137+ return undefined;
138+ }
139+ if (!isNaN(f)) {
140+ return f;
141+ }
142+
143+ const id = this.mem.getUint32(addr, true);
144+ return this._values[id];
145+ }
146+
147+ const storeValue = (addr, v) => {
148+ const nanHead = 0x7FF80000;
149+
150+ if (typeof v === "number" && v !== 0) {
151+ if (isNaN(v)) {
152+ this.mem.setUint32(addr + 4, nanHead, true);
153+ this.mem.setUint32(addr, 0, true);
154+ return;
155+ }
156+ this.mem.setFloat64(addr, v, true);
157+ return;
158+ }
159+
160+ if (v === undefined) {
161+ this.mem.setFloat64(addr, 0, true);
162+ return;
163+ }
164+
165+ let id = this._ids.get(v);
166+ if (id === undefined) {
167+ id = this._idPool.pop();
168+ if (id === undefined) {
169+ id = this._values.length;
170+ }
171+ this._values[id] = v;
172+ this._goRefCounts[id] = 0;
173+ this._ids.set(v, id);
174+ }
175+ this._goRefCounts[id]++;
176+ let typeFlag = 0;
177+ switch (typeof v) {
178+ case "object":
179+ if (v !== null) {
180+ typeFlag = 1;
181+ }
182+ break;
183+ case "string":
184+ typeFlag = 2;
185+ break;
186+ case "symbol":
187+ typeFlag = 3;
188+ break;
189+ case "function":
190+ typeFlag = 4;
191+ break;
192+ }
193+ this.mem.setUint32(addr + 4, nanHead | typeFlag, true);
194+ this.mem.setUint32(addr, id, true);
195+ }
196+
197+ const loadSlice = (addr) => {
198+ const array = getInt64(addr + 0);
199+ const len = getInt64(addr + 8);
200+ return new Uint8Array(this._inst.exports.mem.buffer, array, len);
201+ }
202+
203+ const loadSliceOfValues = (addr) => {
204+ const array = getInt64(addr + 0);
205+ const len = getInt64(addr + 8);
206+ const a = new Array(len);
207+ for (let i = 0; i < len; i++) {
208+ a[i] = loadValue(array + i * 8);
209+ }
210+ return a;
211+ }
212+
213+ const loadString = (addr) => {
214+ const saddr = getInt64(addr + 0);
215+ const len = getInt64(addr + 8);
216+ return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
217+ }
218+
219+ const testCallExport = (a, b) => {
220+ this._inst.exports.testExport0();
221+ return this._inst.exports.testExport(a, b);
222+ }
223+
224+ const timeOrigin = Date.now() - performance.now();
225+ this.importObject = {
226+ _gotest: {
227+ add: (a, b) => a + b,
228+ callExport: testCallExport,
229+ },
230+ gojs: {
231+ // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
232+ // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported
233+ // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
234+ // This changes the SP, thus we have to update the SP used by the imported function.
235+
236+ // func wasmExit(code int32)
237+ "runtime.wasmExit": (sp) => {
238+ sp >>>= 0;
239+ const code = this.mem.getInt32(sp + 8, true);
240+ this.exited = true;
241+ delete this._inst;
242+ delete this._values;
243+ delete this._goRefCounts;
244+ delete this._ids;
245+ delete this._idPool;
246+ this.exit(code);
247+ },
248+
249+ // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
250+ "runtime.wasmWrite": (sp) => {
251+ sp >>>= 0;
252+ const fd = getInt64(sp + 8);
253+ const p = getInt64(sp + 16);
254+ const n = this.mem.getInt32(sp + 24, true);
255+ fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
256+ },
257+
258+ // func resetMemoryDataView()
259+ "runtime.resetMemoryDataView": (sp) => {
260+ sp >>>= 0;
261+ this.mem = new DataView(this._inst.exports.mem.buffer);
262+ },
263+
264+ // func nanotime1() int64
265+ "runtime.nanotime1": (sp) => {
266+ sp >>>= 0;
267+ setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
268+ },
269+
270+ // func walltime() (sec int64, nsec int32)
271+ "runtime.walltime": (sp) => {
272+ sp >>>= 0;
273+ const msec = (new Date).getTime();
274+ setInt64(sp + 8, msec / 1000);
275+ this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true);
276+ },
277+
278+ // func scheduleTimeoutEvent(delay int64) int32
279+ "runtime.scheduleTimeoutEvent": (sp) => {
280+ sp >>>= 0;
281+ const id = this._nextCallbackTimeoutID;
282+ this._nextCallbackTimeoutID++;
283+ this._scheduledTimeouts.set(id, setTimeout(
284+ () => {
285+ this._resume();
286+ while (this._scheduledTimeouts.has(id)) {
287+ // for some reason Go failed to register the timeout event, log and try again
288+ // (temporary workaround for https://github.com/golang/go/issues/28975)
289+ console.warn("scheduleTimeoutEvent: missed timeout event");
290+ this._resume();
291+ }
292+ },
293+ getInt64(sp + 8),
294+ ));
295+ this.mem.setInt32(sp + 16, id, true);
296+ },
297+
298+ // func clearTimeoutEvent(id int32)
299+ "runtime.clearTimeoutEvent": (sp) => {
300+ sp >>>= 0;
301+ const id = this.mem.getInt32(sp + 8, true);
302+ clearTimeout(this._scheduledTimeouts.get(id));
303+ this._scheduledTimeouts.delete(id);
304+ },
305+
306+ // func getRandomData(r []byte)
307+ "runtime.getRandomData": (sp) => {
308+ sp >>>= 0;
309+ crypto.getRandomValues(loadSlice(sp + 8));
310+ },
311+
312+ // func finalizeRef(v ref)
313+ "syscall/js.finalizeRef": (sp) => {
314+ sp >>>= 0;
315+ const id = this.mem.getUint32(sp + 8, true);
316+ this._goRefCounts[id]--;
317+ if (this._goRefCounts[id] === 0) {
318+ const v = this._values[id];
319+ this._values[id] = null;
320+ this._ids.delete(v);
321+ this._idPool.push(id);
322+ }
323+ },
324+
325+ // func stringVal(value string) ref
326+ "syscall/js.stringVal": (sp) => {
327+ sp >>>= 0;
328+ storeValue(sp + 24, loadString(sp + 8));
329+ },
330+
331+ // func valueGet(v ref, p string) ref
332+ "syscall/js.valueGet": (sp) => {
333+ sp >>>= 0;
334+ const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16));
335+ sp = this._inst.exports.getsp() >>> 0; // see comment above
336+ storeValue(sp + 32, result);
337+ },
338+
339+ // func valueSet(v ref, p string, x ref)
340+ "syscall/js.valueSet": (sp) => {
341+ sp >>>= 0;
342+ Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
343+ },
344+
345+ // func valueDelete(v ref, p string)
346+ "syscall/js.valueDelete": (sp) => {
347+ sp >>>= 0;
348+ Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16));
349+ },
350+
351+ // func valueIndex(v ref, i int) ref
352+ "syscall/js.valueIndex": (sp) => {
353+ sp >>>= 0;
354+ storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
355+ },
356+
357+ // valueSetIndex(v ref, i int, x ref)
358+ "syscall/js.valueSetIndex": (sp) => {
359+ sp >>>= 0;
360+ Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
361+ },
362+
363+ // func valueCall(v ref, m string, args []ref) (ref, bool)
364+ "syscall/js.valueCall": (sp) => {
365+ sp >>>= 0;
366+ try {
367+ const v = loadValue(sp + 8);
368+ const m = Reflect.get(v, loadString(sp + 16));
369+ const args = loadSliceOfValues(sp + 32);
370+ const result = Reflect.apply(m, v, args);
371+ sp = this._inst.exports.getsp() >>> 0; // see comment above
372+ storeValue(sp + 56, result);
373+ this.mem.setUint8(sp + 64, 1);
374+ } catch (err) {
375+ sp = this._inst.exports.getsp() >>> 0; // see comment above
376+ storeValue(sp + 56, err);
377+ this.mem.setUint8(sp + 64, 0);
378+ }
379+ },
380+
381+ // func valueInvoke(v ref, args []ref) (ref, bool)
382+ "syscall/js.valueInvoke": (sp) => {
383+ sp >>>= 0;
384+ try {
385+ const v = loadValue(sp + 8);
386+ const args = loadSliceOfValues(sp + 16);
387+ const result = Reflect.apply(v, undefined, args);
388+ sp = this._inst.exports.getsp() >>> 0; // see comment above
389+ storeValue(sp + 40, result);
390+ this.mem.setUint8(sp + 48, 1);
391+ } catch (err) {
392+ sp = this._inst.exports.getsp() >>> 0; // see comment above
393+ storeValue(sp + 40, err);
394+ this.mem.setUint8(sp + 48, 0);
395+ }
396+ },
397+
398+ // func valueNew(v ref, args []ref) (ref, bool)
399+ "syscall/js.valueNew": (sp) => {
400+ sp >>>= 0;
401+ try {
402+ const v = loadValue(sp + 8);
403+ const args = loadSliceOfValues(sp + 16);
404+ const result = Reflect.construct(v, args);
405+ sp = this._inst.exports.getsp() >>> 0; // see comment above
406+ storeValue(sp + 40, result);
407+ this.mem.setUint8(sp + 48, 1);
408+ } catch (err) {
409+ sp = this._inst.exports.getsp() >>> 0; // see comment above
410+ storeValue(sp + 40, err);
411+ this.mem.setUint8(sp + 48, 0);
412+ }
413+ },
414+
415+ // func valueLength(v ref) int
416+ "syscall/js.valueLength": (sp) => {
417+ sp >>>= 0;
418+ setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
419+ },
420+
421+ // valuePrepareString(v ref) (ref, int)
422+ "syscall/js.valuePrepareString": (sp) => {
423+ sp >>>= 0;
424+ const str = encoder.encode(String(loadValue(sp + 8)));
425+ storeValue(sp + 16, str);
426+ setInt64(sp + 24, str.length);
427+ },
428+
429+ // valueLoadString(v ref, b []byte)
430+ "syscall/js.valueLoadString": (sp) => {
431+ sp >>>= 0;
432+ const str = loadValue(sp + 8);
433+ loadSlice(sp + 16).set(str);
434+ },
435+
436+ // func valueInstanceOf(v ref, t ref) bool
437+ "syscall/js.valueInstanceOf": (sp) => {
438+ sp >>>= 0;
439+ this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0);
440+ },
441+
442+ // func copyBytesToGo(dst []byte, src ref) (int, bool)
443+ "syscall/js.copyBytesToGo": (sp) => {
444+ sp >>>= 0;
445+ const dst = loadSlice(sp + 8);
446+ const src = loadValue(sp + 32);
447+ if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) {
448+ this.mem.setUint8(sp + 48, 0);
449+ return;
450+ }
451+ const toCopy = src.subarray(0, dst.length);
452+ dst.set(toCopy);
453+ setInt64(sp + 40, toCopy.length);
454+ this.mem.setUint8(sp + 48, 1);
455+ },
456+
457+ // func copyBytesToJS(dst ref, src []byte) (int, bool)
458+ "syscall/js.copyBytesToJS": (sp) => {
459+ sp >>>= 0;
460+ const dst = loadValue(sp + 8);
461+ const src = loadSlice(sp + 16);
462+ if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) {
463+ this.mem.setUint8(sp + 48, 0);
464+ return;
465+ }
466+ const toCopy = src.subarray(0, dst.length);
467+ dst.set(toCopy);
468+ setInt64(sp + 40, toCopy.length);
469+ this.mem.setUint8(sp + 48, 1);
470+ },
471+
472+ "debug": (value) => {
473+ console.log(value);
474+ },
475+ }
476+ };
477+ }
478+
479+ async run(instance) {
480+ if (!(instance instanceof WebAssembly.Instance)) {
481+ throw new Error("Go.run: WebAssembly.Instance expected");
482+ }
483+ this._inst = instance;
484+ this.mem = new DataView(this._inst.exports.mem.buffer);
485+ this._values = [ // JS values that Go currently has references to, indexed by reference id
486+ NaN,
487+ 0,
488+ null,
489+ true,
490+ false,
491+ globalThis,
492+ this,
493+ ];
494+ this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id
495+ this._ids = new Map([ // mapping from JS values to reference ids
496+ [0, 1],
497+ [null, 2],
498+ [true, 3],
499+ [false, 4],
500+ [globalThis, 5],
501+ [this, 6],
502+ ]);
503+ this._idPool = []; // unused ids that have been garbage collected
504+ this.exited = false; // whether the Go program has exited
505+
506+ // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
507+ let offset = 4096;
508+
509+ const strPtr = (str) => {
510+ const ptr = offset;
511+ const bytes = encoder.encode(str + "\0");
512+ new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes);
513+ offset += bytes.length;
514+ if (offset % 8 !== 0) {
515+ offset += 8 - (offset % 8);
516+ }
517+ return ptr;
518+ };
519+
520+ const argc = this.argv.length;
521+
522+ const argvPtrs = [];
523+ this.argv.forEach((arg) => {
524+ argvPtrs.push(strPtr(arg));
525+ });
526+ argvPtrs.push(0);
527+
528+ const keys = Object.keys(this.env).sort();
529+ keys.forEach((key) => {
530+ argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
531+ });
532+ argvPtrs.push(0);
533+
534+ const argv = offset;
535+ argvPtrs.forEach((ptr) => {
536+ this.mem.setUint32(offset, ptr, true);
537+ this.mem.setUint32(offset + 4, 0, true);
538+ offset += 8;
539+ });
540+
541+ // The linker guarantees global data starts from at least wasmMinDataAddr.
542+ // Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr.
543+ const wasmMinDataAddr = 4096 + 8192;
544+ if (offset >= wasmMinDataAddr) {
545+ throw new Error("total length of command line and environment variables exceeds limit");
546+ }
547+
548+ this._inst.exports.run(argc, argv);
549+ if (this.exited) {
550+ this._resolveExitPromise();
551+ }
552+ await this._exitPromise;
553+ }
554+
555+ _resume() {
556+ if (this.exited) {
557+ throw new Error("Go program has already exited");
558+ }
559+ this._inst.exports.resume();
560+ if (this.exited) {
561+ this._resolveExitPromise();
562+ }
563+ }
564+
565+ _makeFuncWrapper(id) {
566+ const go = this;
567+ return function () {
568+ const event = { id: id, this: this, args: arguments };
569+ go._pendingEvent = event;
570+ go._resume();
571+ return event.result;
572+ };
573+ }
574+ }
575+})();
@@ -0,0 +1,575 @@
1+// Copyright 2018 The Go Authors. All rights reserved.
2+// Use of this source code is governed by a BSD-style
3+// license that can be found in the LICENSE file.
4+
5+"use strict";
6+
7+(() => {
8+ const enosys = () => {
9+ const err = new Error("not implemented");
10+ err.code = "ENOSYS";
11+ return err;
12+ };
13+
14+ if (!globalThis.fs) {
15+ let outputBuf = "";
16+ globalThis.fs = {
17+ constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1, O_DIRECTORY: -1 }, // unused
18+ writeSync(fd, buf) {
19+ outputBuf += decoder.decode(buf);
20+ const nl = outputBuf.lastIndexOf("\n");
21+ if (nl != -1) {
22+ console.log(outputBuf.substring(0, nl));
23+ outputBuf = outputBuf.substring(nl + 1);
24+ }
25+ return buf.length;
26+ },
27+ write(fd, buf, offset, length, position, callback) {
28+ if (offset !== 0 || length !== buf.length || position !== null) {
29+ callback(enosys());
30+ return;
31+ }
32+ const n = this.writeSync(fd, buf);
33+ callback(null, n);
34+ },
35+ chmod(path, mode, callback) { callback(enosys()); },
36+ chown(path, uid, gid, callback) { callback(enosys()); },
37+ close(fd, callback) { callback(enosys()); },
38+ fchmod(fd, mode, callback) { callback(enosys()); },
39+ fchown(fd, uid, gid, callback) { callback(enosys()); },
40+ fstat(fd, callback) { callback(enosys()); },
41+ fsync(fd, callback) { callback(null); },
42+ ftruncate(fd, length, callback) { callback(enosys()); },
43+ lchown(path, uid, gid, callback) { callback(enosys()); },
44+ link(path, link, callback) { callback(enosys()); },
45+ lstat(path, callback) { callback(enosys()); },
46+ mkdir(path, perm, callback) { callback(enosys()); },
47+ open(path, flags, mode, callback) { callback(enosys()); },
48+ read(fd, buffer, offset, length, position, callback) { callback(enosys()); },
49+ readdir(path, callback) { callback(enosys()); },
50+ readlink(path, callback) { callback(enosys()); },
51+ rename(from, to, callback) { callback(enosys()); },
52+ rmdir(path, callback) { callback(enosys()); },
53+ stat(path, callback) { callback(enosys()); },
54+ symlink(path, link, callback) { callback(enosys()); },
55+ truncate(path, length, callback) { callback(enosys()); },
56+ unlink(path, callback) { callback(enosys()); },
57+ utimes(path, atime, mtime, callback) { callback(enosys()); },
58+ };
59+ }
60+
61+ if (!globalThis.process) {
62+ globalThis.process = {
63+ getuid() { return -1; },
64+ getgid() { return -1; },
65+ geteuid() { return -1; },
66+ getegid() { return -1; },
67+ getgroups() { throw enosys(); },
68+ pid: -1,
69+ ppid: -1,
70+ umask() { throw enosys(); },
71+ cwd() { throw enosys(); },
72+ chdir() { throw enosys(); },
73+ }
74+ }
75+
76+ if (!globalThis.path) {
77+ globalThis.path = {
78+ resolve(...pathSegments) {
79+ return pathSegments.join("/");
80+ }
81+ }
82+ }
83+
84+ if (!globalThis.crypto) {
85+ throw new Error("globalThis.crypto is not available, polyfill required (crypto.getRandomValues only)");
86+ }
87+
88+ if (!globalThis.performance) {
89+ throw new Error("globalThis.performance is not available, polyfill required (performance.now only)");
90+ }
91+
92+ if (!globalThis.TextEncoder) {
93+ throw new Error("globalThis.TextEncoder is not available, polyfill required");
94+ }
95+
96+ if (!globalThis.TextDecoder) {
97+ throw new Error("globalThis.TextDecoder is not available, polyfill required");
98+ }
99+
100+ const encoder = new TextEncoder("utf-8");
101+ const decoder = new TextDecoder("utf-8");
102+
103+ globalThis.Go = class {
104+ constructor() {
105+ this.argv = ["js"];
106+ this.env = {};
107+ this.exit = (code) => {
108+ if (code !== 0) {
109+ console.warn("exit code:", code);
110+ }
111+ };
112+ this._exitPromise = new Promise((resolve) => {
113+ this._resolveExitPromise = resolve;
114+ });
115+ this._pendingEvent = null;
116+ this._scheduledTimeouts = new Map();
117+ this._nextCallbackTimeoutID = 1;
118+
119+ const setInt64 = (addr, v) => {
120+ this.mem.setUint32(addr + 0, v, true);
121+ this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true);
122+ }
123+
124+ const setInt32 = (addr, v) => {
125+ this.mem.setUint32(addr + 0, v, true);
126+ }
127+
128+ const getInt64 = (addr) => {
129+ const low = this.mem.getUint32(addr + 0, true);
130+ const high = this.mem.getInt32(addr + 4, true);
131+ return low + high * 4294967296;
132+ }
133+
134+ const loadValue = (addr) => {
135+ const f = this.mem.getFloat64(addr, true);
136+ if (f === 0) {
137+ return undefined;
138+ }
139+ if (!isNaN(f)) {
140+ return f;
141+ }
142+
143+ const id = this.mem.getUint32(addr, true);
144+ return this._values[id];
145+ }
146+
147+ const storeValue = (addr, v) => {
148+ const nanHead = 0x7FF80000;
149+
150+ if (typeof v === "number" && v !== 0) {
151+ if (isNaN(v)) {
152+ this.mem.setUint32(addr + 4, nanHead, true);
153+ this.mem.setUint32(addr, 0, true);
154+ return;
155+ }
156+ this.mem.setFloat64(addr, v, true);
157+ return;
158+ }
159+
160+ if (v === undefined) {
161+ this.mem.setFloat64(addr, 0, true);
162+ return;
163+ }
164+
165+ let id = this._ids.get(v);
166+ if (id === undefined) {
167+ id = this._idPool.pop();
168+ if (id === undefined) {
169+ id = this._values.length;
170+ }
171+ this._values[id] = v;
172+ this._goRefCounts[id] = 0;
173+ this._ids.set(v, id);
174+ }
175+ this._goRefCounts[id]++;
176+ let typeFlag = 0;
177+ switch (typeof v) {
178+ case "object":
179+ if (v !== null) {
180+ typeFlag = 1;
181+ }
182+ break;
183+ case "string":
184+ typeFlag = 2;
185+ break;
186+ case "symbol":
187+ typeFlag = 3;
188+ break;
189+ case "function":
190+ typeFlag = 4;
191+ break;
192+ }
193+ this.mem.setUint32(addr + 4, nanHead | typeFlag, true);
194+ this.mem.setUint32(addr, id, true);
195+ }
196+
197+ const loadSlice = (addr) => {
198+ const array = getInt64(addr + 0);
199+ const len = getInt64(addr + 8);
200+ return new Uint8Array(this._inst.exports.mem.buffer, array, len);
201+ }
202+
203+ const loadSliceOfValues = (addr) => {
204+ const array = getInt64(addr + 0);
205+ const len = getInt64(addr + 8);
206+ const a = new Array(len);
207+ for (let i = 0; i < len; i++) {
208+ a[i] = loadValue(array + i * 8);
209+ }
210+ return a;
211+ }
212+
213+ const loadString = (addr) => {
214+ const saddr = getInt64(addr + 0);
215+ const len = getInt64(addr + 8);
216+ return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
217+ }
218+
219+ const testCallExport = (a, b) => {
220+ this._inst.exports.testExport0();
221+ return this._inst.exports.testExport(a, b);
222+ }
223+
224+ const timeOrigin = Date.now() - performance.now();
225+ this.importObject = {
226+ _gotest: {
227+ add: (a, b) => a + b,
228+ callExport: testCallExport,
229+ },
230+ gojs: {
231+ // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
232+ // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported
233+ // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
234+ // This changes the SP, thus we have to update the SP used by the imported function.
235+
236+ // func wasmExit(code int32)
237+ "runtime.wasmExit": (sp) => {
238+ sp >>>= 0;
239+ const code = this.mem.getInt32(sp + 8, true);
240+ this.exited = true;
241+ delete this._inst;
242+ delete this._values;
243+ delete this._goRefCounts;
244+ delete this._ids;
245+ delete this._idPool;
246+ this.exit(code);
247+ },
248+
249+ // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
250+ "runtime.wasmWrite": (sp) => {
251+ sp >>>= 0;
252+ const fd = getInt64(sp + 8);
253+ const p = getInt64(sp + 16);
254+ const n = this.mem.getInt32(sp + 24, true);
255+ fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
256+ },
257+
258+ // func resetMemoryDataView()
259+ "runtime.resetMemoryDataView": (sp) => {
260+ sp >>>= 0;
261+ this.mem = new DataView(this._inst.exports.mem.buffer);
262+ },
263+
264+ // func nanotime1() int64
265+ "runtime.nanotime1": (sp) => {
266+ sp >>>= 0;
267+ setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
268+ },
269+
270+ // func walltime() (sec int64, nsec int32)
271+ "runtime.walltime": (sp) => {
272+ sp >>>= 0;
273+ const msec = (new Date).getTime();
274+ setInt64(sp + 8, msec / 1000);
275+ this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true);
276+ },
277+
278+ // func scheduleTimeoutEvent(delay int64) int32
279+ "runtime.scheduleTimeoutEvent": (sp) => {
280+ sp >>>= 0;
281+ const id = this._nextCallbackTimeoutID;
282+ this._nextCallbackTimeoutID++;
283+ this._scheduledTimeouts.set(id, setTimeout(
284+ () => {
285+ this._resume();
286+ while (this._scheduledTimeouts.has(id)) {
287+ // for some reason Go failed to register the timeout event, log and try again
288+ // (temporary workaround for https://github.com/golang/go/issues/28975)
289+ console.warn("scheduleTimeoutEvent: missed timeout event");
290+ this._resume();
291+ }
292+ },
293+ getInt64(sp + 8),
294+ ));
295+ this.mem.setInt32(sp + 16, id, true);
296+ },
297+
298+ // func clearTimeoutEvent(id int32)
299+ "runtime.clearTimeoutEvent": (sp) => {
300+ sp >>>= 0;
301+ const id = this.mem.getInt32(sp + 8, true);
302+ clearTimeout(this._scheduledTimeouts.get(id));
303+ this._scheduledTimeouts.delete(id);
304+ },
305+
306+ // func getRandomData(r []byte)
307+ "runtime.getRandomData": (sp) => {
308+ sp >>>= 0;
309+ crypto.getRandomValues(loadSlice(sp + 8));
310+ },
311+
312+ // func finalizeRef(v ref)
313+ "syscall/js.finalizeRef": (sp) => {
314+ sp >>>= 0;
315+ const id = this.mem.getUint32(sp + 8, true);
316+ this._goRefCounts[id]--;
317+ if (this._goRefCounts[id] === 0) {
318+ const v = this._values[id];
319+ this._values[id] = null;
320+ this._ids.delete(v);
321+ this._idPool.push(id);
322+ }
323+ },
324+
325+ // func stringVal(value string) ref
326+ "syscall/js.stringVal": (sp) => {
327+ sp >>>= 0;
328+ storeValue(sp + 24, loadString(sp + 8));
329+ },
330+
331+ // func valueGet(v ref, p string) ref
332+ "syscall/js.valueGet": (sp) => {
333+ sp >>>= 0;
334+ const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16));
335+ sp = this._inst.exports.getsp() >>> 0; // see comment above
336+ storeValue(sp + 32, result);
337+ },
338+
339+ // func valueSet(v ref, p string, x ref)
340+ "syscall/js.valueSet": (sp) => {
341+ sp >>>= 0;
342+ Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
343+ },
344+
345+ // func valueDelete(v ref, p string)
346+ "syscall/js.valueDelete": (sp) => {
347+ sp >>>= 0;
348+ Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16));
349+ },
350+
351+ // func valueIndex(v ref, i int) ref
352+ "syscall/js.valueIndex": (sp) => {
353+ sp >>>= 0;
354+ storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
355+ },
356+
357+ // valueSetIndex(v ref, i int, x ref)
358+ "syscall/js.valueSetIndex": (sp) => {
359+ sp >>>= 0;
360+ Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
361+ },
362+
363+ // func valueCall(v ref, m string, args []ref) (ref, bool)
364+ "syscall/js.valueCall": (sp) => {
365+ sp >>>= 0;
366+ try {
367+ const v = loadValue(sp + 8);
368+ const m = Reflect.get(v, loadString(sp + 16));
369+ const args = loadSliceOfValues(sp + 32);
370+ const result = Reflect.apply(m, v, args);
371+ sp = this._inst.exports.getsp() >>> 0; // see comment above
372+ storeValue(sp + 56, result);
373+ this.mem.setUint8(sp + 64, 1);
374+ } catch (err) {
375+ sp = this._inst.exports.getsp() >>> 0; // see comment above
376+ storeValue(sp + 56, err);
377+ this.mem.setUint8(sp + 64, 0);
378+ }
379+ },
380+
381+ // func valueInvoke(v ref, args []ref) (ref, bool)
382+ "syscall/js.valueInvoke": (sp) => {
383+ sp >>>= 0;
384+ try {
385+ const v = loadValue(sp + 8);
386+ const args = loadSliceOfValues(sp + 16);
387+ const result = Reflect.apply(v, undefined, args);
388+ sp = this._inst.exports.getsp() >>> 0; // see comment above
389+ storeValue(sp + 40, result);
390+ this.mem.setUint8(sp + 48, 1);
391+ } catch (err) {
392+ sp = this._inst.exports.getsp() >>> 0; // see comment above
393+ storeValue(sp + 40, err);
394+ this.mem.setUint8(sp + 48, 0);
395+ }
396+ },
397+
398+ // func valueNew(v ref, args []ref) (ref, bool)
399+ "syscall/js.valueNew": (sp) => {
400+ sp >>>= 0;
401+ try {
402+ const v = loadValue(sp + 8);
403+ const args = loadSliceOfValues(sp + 16);
404+ const result = Reflect.construct(v, args);
405+ sp = this._inst.exports.getsp() >>> 0; // see comment above
406+ storeValue(sp + 40, result);
407+ this.mem.setUint8(sp + 48, 1);
408+ } catch (err) {
409+ sp = this._inst.exports.getsp() >>> 0; // see comment above
410+ storeValue(sp + 40, err);
411+ this.mem.setUint8(sp + 48, 0);
412+ }
413+ },
414+
415+ // func valueLength(v ref) int
416+ "syscall/js.valueLength": (sp) => {
417+ sp >>>= 0;
418+ setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
419+ },
420+
421+ // valuePrepareString(v ref) (ref, int)
422+ "syscall/js.valuePrepareString": (sp) => {
423+ sp >>>= 0;
424+ const str = encoder.encode(String(loadValue(sp + 8)));
425+ storeValue(sp + 16, str);
426+ setInt64(sp + 24, str.length);
427+ },
428+
429+ // valueLoadString(v ref, b []byte)
430+ "syscall/js.valueLoadString": (sp) => {
431+ sp >>>= 0;
432+ const str = loadValue(sp + 8);
433+ loadSlice(sp + 16).set(str);
434+ },
435+
436+ // func valueInstanceOf(v ref, t ref) bool
437+ "syscall/js.valueInstanceOf": (sp) => {
438+ sp >>>= 0;
439+ this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0);
440+ },
441+
442+ // func copyBytesToGo(dst []byte, src ref) (int, bool)
443+ "syscall/js.copyBytesToGo": (sp) => {
444+ sp >>>= 0;
445+ const dst = loadSlice(sp + 8);
446+ const src = loadValue(sp + 32);
447+ if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) {
448+ this.mem.setUint8(sp + 48, 0);
449+ return;
450+ }
451+ const toCopy = src.subarray(0, dst.length);
452+ dst.set(toCopy);
453+ setInt64(sp + 40, toCopy.length);
454+ this.mem.setUint8(sp + 48, 1);
455+ },
456+
457+ // func copyBytesToJS(dst ref, src []byte) (int, bool)
458+ "syscall/js.copyBytesToJS": (sp) => {
459+ sp >>>= 0;
460+ const dst = loadValue(sp + 8);
461+ const src = loadSlice(sp + 16);
462+ if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) {
463+ this.mem.setUint8(sp + 48, 0);
464+ return;
465+ }
466+ const toCopy = src.subarray(0, dst.length);
467+ dst.set(toCopy);
468+ setInt64(sp + 40, toCopy.length);
469+ this.mem.setUint8(sp + 48, 1);
470+ },
471+
472+ "debug": (value) => {
473+ console.log(value);
474+ },
475+ }
476+ };
477+ }
478+
479+ async run(instance) {
480+ if (!(instance instanceof WebAssembly.Instance)) {
481+ throw new Error("Go.run: WebAssembly.Instance expected");
482+ }
483+ this._inst = instance;
484+ this.mem = new DataView(this._inst.exports.mem.buffer);
485+ this._values = [ // JS values that Go currently has references to, indexed by reference id
486+ NaN,
487+ 0,
488+ null,
489+ true,
490+ false,
491+ globalThis,
492+ this,
493+ ];
494+ this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id
495+ this._ids = new Map([ // mapping from JS values to reference ids
496+ [0, 1],
497+ [null, 2],
498+ [true, 3],
499+ [false, 4],
500+ [globalThis, 5],
501+ [this, 6],
502+ ]);
503+ this._idPool = []; // unused ids that have been garbage collected
504+ this.exited = false; // whether the Go program has exited
505+
506+ // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
507+ let offset = 4096;
508+
509+ const strPtr = (str) => {
510+ const ptr = offset;
511+ const bytes = encoder.encode(str + "\0");
512+ new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes);
513+ offset += bytes.length;
514+ if (offset % 8 !== 0) {
515+ offset += 8 - (offset % 8);
516+ }
517+ return ptr;
518+ };
519+
520+ const argc = this.argv.length;
521+
522+ const argvPtrs = [];
523+ this.argv.forEach((arg) => {
524+ argvPtrs.push(strPtr(arg));
525+ });
526+ argvPtrs.push(0);
527+
528+ const keys = Object.keys(this.env).sort();
529+ keys.forEach((key) => {
530+ argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
531+ });
532+ argvPtrs.push(0);
533+
534+ const argv = offset;
535+ argvPtrs.forEach((ptr) => {
536+ this.mem.setUint32(offset, ptr, true);
537+ this.mem.setUint32(offset + 4, 0, true);
538+ offset += 8;
539+ });
540+
541+ // The linker guarantees global data starts from at least wasmMinDataAddr.
542+ // Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr.
543+ const wasmMinDataAddr = 4096 + 8192;
544+ if (offset >= wasmMinDataAddr) {
545+ throw new Error("total length of command line and environment variables exceeds limit");
546+ }
547+
548+ this._inst.exports.run(argc, argv);
549+ if (this.exited) {
550+ this._resolveExitPromise();
551+ }
552+ await this._exitPromise;
553+ }
554+
555+ _resume() {
556+ if (this.exited) {
557+ throw new Error("Go program has already exited");
558+ }
559+ this._inst.exports.resume();
560+ if (this.exited) {
561+ this._resolveExitPromise();
562+ }
563+ }
564+
565+ _makeFuncWrapper(id) {
566+ const go = this;
567+ return function () {
568+ const event = { id: id, this: this, args: arguments };
569+ go._pendingEvent = event;
570+ go._resume();
571+ return event.result;
572+ };
573+ }
574+ }
575+})();

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close