|
132 | 132 | const decoder = new TextDecoder("utf-8"); |
133 | 133 | let reinterpretBuf = new DataView(new ArrayBuffer(8)); |
134 | 134 | var logLine = []; |
| 135 | + const wasmExit = {}; // thrown to exit via proc_exit (not an error) |
135 | 136 |
|
136 | 137 | global.Go = class { |
137 | 138 | constructor() { |
|
270 | 271 | fd_close: () => 0, // dummy |
271 | 272 | fd_fdstat_get: () => 0, // dummy |
272 | 273 | fd_seek: () => 0, // dummy |
273 | | - "proc_exit": (code) => { |
274 | | - if (global.process) { |
275 | | - // Node.js |
276 | | - process.exit(code); |
277 | | - } else { |
278 | | - // Can't exit in a browser. |
279 | | - throw 'trying to exit with code ' + code; |
280 | | - } |
| 274 | + proc_exit: (code) => { |
| 275 | + this.exited = true; |
| 276 | + this.exitCode = code; |
| 277 | + this._resolveExitPromise(); |
| 278 | + throw wasmExit; |
281 | 279 | }, |
282 | 280 | random_get: (bufPtr, bufLen) => { |
283 | 281 | crypto.getRandomValues(loadSlice(bufPtr, bufLen)); |
|
293 | 291 | // func sleepTicks(timeout float64) |
294 | 292 | "runtime.sleepTicks": (timeout) => { |
295 | 293 | // Do not sleep, only reactivate scheduler after the given timeout. |
296 | | - setTimeout(this._inst.exports.go_scheduler, timeout); |
| 294 | + setTimeout(() => { |
| 295 | + if (this.exited) return; |
| 296 | + try { |
| 297 | + this._inst.exports.go_scheduler(); |
| 298 | + } catch (e) { |
| 299 | + if (e !== wasmExit) throw e; |
| 300 | + } |
| 301 | + }, timeout); |
297 | 302 | }, |
298 | 303 |
|
299 | 304 | // func finalizeRef(v ref) |
|
465 | 470 | this._ids = new Map(); // mapping from JS values to reference ids |
466 | 471 | this._idPool = []; // unused ids that have been garbage collected |
467 | 472 | this.exited = false; // whether the Go program has exited |
| 473 | + this.exitCode = 0; |
468 | 474 |
|
469 | 475 | if (this._inst.exports._start) { |
470 | | - this._inst.exports._start(); |
| 476 | + let exitPromise = new Promise((resolve, reject) => { |
| 477 | + this._resolveExitPromise = resolve; |
| 478 | + }); |
| 479 | + |
| 480 | + // Run program, but catch the wasmExit exception that's thrown |
| 481 | + // to return back here. |
| 482 | + try { |
| 483 | + this._inst.exports._start(); |
| 484 | + } catch (e) { |
| 485 | + if (e !== wasmExit) throw e; |
| 486 | + } |
471 | 487 |
|
472 | | - // TODO: wait until the program exists. |
473 | | - await new Promise(() => {}); |
| 488 | + await exitPromise; |
| 489 | + return this.exitCode; |
474 | 490 | } else { |
475 | 491 | this._inst.exports._initialize(); |
476 | 492 | } |
|
480 | 496 | if (this.exited) { |
481 | 497 | throw new Error("Go program has already exited"); |
482 | 498 | } |
483 | | - this._inst.exports.resume(); |
| 499 | + try { |
| 500 | + this._inst.exports.resume(); |
| 501 | + } catch (e) { |
| 502 | + if (e !== wasmExit) throw e; |
| 503 | + } |
484 | 504 | if (this.exited) { |
485 | 505 | this._resolveExitPromise(); |
486 | 506 | } |
|
0 commit comments