wasm_exec.js 20 KB

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