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