clone

a browser-first diagram editor with live svg previews, local drafts and encrypted, accountless sharing

main
diagramzip / renderers / edge / artifacts / d2 / wasm_exec.js
wasm_exec.js 16.6K
1// Copyright 2018 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45"use strict";67(() => {8 const enosys = () => {9 const err = new Error("not implemented");10 err.code = "ENOSYS";11 return err;12 };1314 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 }, // unused18 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 }6061 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 }7576 if (!globalThis.path) {77 globalThis.path = {78 resolve(...pathSegments) {79 return pathSegments.join("/");80 }81 }82 }8384 if (!globalThis.crypto) {85 throw new Error("globalThis.crypto is not available, polyfill required (crypto.getRandomValues only)");86 }8788 if (!globalThis.performance) {89 throw new Error("globalThis.performance is not available, polyfill required (performance.now only)");90 }9192 if (!globalThis.TextEncoder) {93 throw new Error("globalThis.TextEncoder is not available, polyfill required");94 }9596 if (!globalThis.TextDecoder) {97 throw new Error("globalThis.TextDecoder is not available, polyfill required");98 }99100 const encoder = new TextEncoder("utf-8");101 const decoder = new TextDecoder("utf-8");102103 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;118119 const setInt64 = (addr, v) => {120 this.mem.setUint32(addr + 0, v, true);121 this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true);122 }123124 const setInt32 = (addr, v) => {125 this.mem.setUint32(addr + 0, v, true);126 }127128 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 }133134 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 }142143 const id = this.mem.getUint32(addr, true);144 return this._values[id];145 }146147 const storeValue = (addr, v) => {148 const nanHead = 0x7FF80000;149150 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 }159160 if (v === undefined) {161 this.mem.setFloat64(addr, 0, true);162 return;163 }164165 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 }196197 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 }202203 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 }212213 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 }218219 const testCallExport = (a, b) => {220 this._inst.exports.testExport0();221 return this._inst.exports.testExport(a, b);222 }223224 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 imported233 // 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.235236 // 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 },248249 // 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 },257258 // func resetMemoryDataView()259 "runtime.resetMemoryDataView": (sp) => {260 sp >>>= 0;261 this.mem = new DataView(this._inst.exports.mem.buffer);262 },263264 // func nanotime1() int64265 "runtime.nanotime1": (sp) => {266 sp >>>= 0;267 setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);268 },269270 // 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 },277278 // func scheduleTimeoutEvent(delay int64) int32279 "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 again288 // (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 },297298 // 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 },305306 // func getRandomData(r []byte)307 "runtime.getRandomData": (sp) => {308 sp >>>= 0;309 crypto.getRandomValues(loadSlice(sp + 8));310 },311312 // 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 },324325 // func stringVal(value string) ref326 "syscall/js.stringVal": (sp) => {327 sp >>>= 0;328 storeValue(sp + 24, loadString(sp + 8));329 },330331 // func valueGet(v ref, p string) ref332 "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 above336 storeValue(sp + 32, result);337 },338339 // 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 },344345 // func valueDelete(v ref, p string)346 "syscall/js.valueDelete": (sp) => {347 sp >>>= 0;348 Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16));349 },350351 // func valueIndex(v ref, i int) ref352 "syscall/js.valueIndex": (sp) => {353 sp >>>= 0;354 storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));355 },356357 // 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 },362363 // 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 above372 storeValue(sp + 56, result);373 this.mem.setUint8(sp + 64, 1);374 } catch (err) {375 sp = this._inst.exports.getsp() >>> 0; // see comment above376 storeValue(sp + 56, err);377 this.mem.setUint8(sp + 64, 0);378 }379 },380381 // 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 above389 storeValue(sp + 40, result);390 this.mem.setUint8(sp + 48, 1);391 } catch (err) {392 sp = this._inst.exports.getsp() >>> 0; // see comment above393 storeValue(sp + 40, err);394 this.mem.setUint8(sp + 48, 0);395 }396 },397398 // 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 above406 storeValue(sp + 40, result);407 this.mem.setUint8(sp + 48, 1);408 } catch (err) {409 sp = this._inst.exports.getsp() >>> 0; // see comment above410 storeValue(sp + 40, err);411 this.mem.setUint8(sp + 48, 0);412 }413 },414415 // func valueLength(v ref) int416 "syscall/js.valueLength": (sp) => {417 sp >>>= 0;418 setInt64(sp + 16, parseInt(loadValue(sp + 8).length));419 },420421 // 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 },428429 // 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 },435436 // func valueInstanceOf(v ref, t ref) bool437 "syscall/js.valueInstanceOf": (sp) => {438 sp >>>= 0;439 this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0);440 },441442 // 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 },456457 // 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 },471472 "debug": (value) => {473 console.log(value);474 },475 }476 };477 }478479 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 id486 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 id495 this._ids = new Map([ // mapping from JS values to reference ids496 [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 collected504 this.exited = false; // whether the Go program has exited505506 // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.507 let offset = 4096;508509 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 };519520 const argc = this.argv.length;521522 const argvPtrs = [];523 this.argv.forEach((arg) => {524 argvPtrs.push(strPtr(arg));525 });526 argvPtrs.push(0);527528 const keys = Object.keys(this.env).sort();529 keys.forEach((key) => {530 argvPtrs.push(strPtr(`${key}=${this.env[key]}`));531 });532 argvPtrs.push(0);533534 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 });540541 // 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 }547548 this._inst.exports.run(argc, argv);549 if (this.exited) {550 this._resolveExitPromise();551 }552 await this._exitPromise;553 }554555 _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 }564565 _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})();