From a9bb2f0261fd88048d7e499df33808a23884cb02 Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Fri, 9 Aug 2013 00:36:35 -0700 Subject: [PATCH] - always throw an exception in exit - remove default exit status prints - added EXITSTATUS global to enable exit callbacks to determine the status --- src/postamble.js | 60 ++++++++++++++++++++++-------------------------- src/preamble.js | 1 + tests/runner.py | 30 ++++++++++++++++++++++-- 3 files changed, 57 insertions(+), 34 deletions(-) diff --git a/src/postamble.js b/src/postamble.js index 25a50bfc3..ad265b32d 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -2,7 +2,6 @@ // === Auto-generated postamble setup entry stuff === var initialStackTop; -var inMain; Module['callMain'] = Module.callMain = function callMain(args) { assert(runDependencies == 0, 'cannot call main when async dependencies remain! (listen on __ATMAIN__)'); @@ -27,40 +26,36 @@ Module['callMain'] = Module.callMain = function callMain(args) { argv.push(0); argv = allocate(argv, 'i32', ALLOC_NORMAL); + initialStackTop = STACKTOP; + + try { #if BENCHMARK - var start = Date.now(); + var start = Date.now(); #endif - initialStackTop = STACKTOP; - inMain = true; + var ret = Module['_main'](argc, argv, 0); - var ret; - try { - ret = Module['_main'](argc, argv, 0); +#if BENCHMARK + Module.realPrint('main() took ' + (Date.now() - start) + ' milliseconds'); +#endif + + // if we're not running an evented main loop, it's time to exit + if (!Module['noExitRuntime']) { + exit(ret); + } } catch(e) { - if (e && typeof e == 'object' && e.type == 'ExitStatus') { + if (e.name == 'ExitStatus') { // exit() throws this once it's done to make sure execution // has been stopped completely - Module.print('Exit Status: ' + e.value); - return e.value; + return; } else if (e == 'SimulateInfiniteLoop') { // running an evented main loop, don't immediately exit Module['noExitRuntime'] = true; + return; } else { throw e; } - } finally { - inMain = false; - } - -#if BENCHMARK - Module.realPrint('main() took ' + (Date.now() - start) + ' milliseconds'); -#endif - - // if we're not running an evented main loop, it's time to exit - if (!Module['noExitRuntime']) { - exit(ret); } } @@ -110,21 +105,21 @@ Module['run'] = Module.run = run; function exit(status) { ABORT = true; + EXITSTATUS = status; STACKTOP = initialStackTop; - // TODO call externally added 'exit' callbacks with the status code. - // It'd be nice to provide the same interface for all Module events (e.g. - // prerun, premain, postmain). Perhaps an EventEmitter so we can do: - // Module.on('exit', function (status) {}); - // exit the runtime exitRuntime(); - - if (inMain) { - // if we're still inside the callMain's try/catch, we need to throw an - // exception in order to immediately terminate execution. - throw { type: 'ExitStatus', value: status }; - } + + // throw an exception to halt the current execution + function ExitStatus() { + this.name = "ExitStatus"; + this.message = "Program terminated with exit(" + status + ")"; + this.status = status; + }; + ExitStatus.prototype = new Error(); + ExitStatus.prototype.constructor = ExitStatus; + throw new ExitStatus(); } Module['exit'] = Module.exit = exit; @@ -134,6 +129,7 @@ function abort(text) { } ABORT = true; + EXITSTATUS = 1; throw 'abort() at ' + (new Error().stack); } diff --git a/src/preamble.js b/src/preamble.js index 585db8329..95bf2dc25 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -241,6 +241,7 @@ var setjmpLabels = {}; #endif var ABORT = false; // whether we are quitting the application. no code should run after this. set in exit() and abort() +var EXITSTATUS = 0; var undef = 0; // tempInt is used for 32-bit signed values or smaller. tempBigInt is used diff --git a/tests/runner.py b/tests/runner.py index e77efffbd..8122bc81e 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -3185,6 +3185,25 @@ Exiting setjmp function, level: 0, prev_jmp: -1 ''' self.do_run(src, 'caught std::exception') + def test_async_exit(self): + open('main.c', 'w').write(r''' + #include + #include + #include "emscripten.h" + + void main_loop() { + exit(EXIT_SUCCESS); + } + + int main() { + emscripten_set_main_loop(main_loop, 60, 0); + return 0; + } + ''') + + Popen([PYTHON, EMCC, 'main.c']).communicate() + self.assertNotContained('Reached an unreachable!', run_js(self.in_dir('a.out.js'), stderr=STDOUT)) + def test_exit_stack(self): if self.emcc_args is None: return self.skip('requires emcc') if Settings.ASM_JS: return self.skip('uses report_stack without exporting') @@ -3222,6 +3241,7 @@ Exiting setjmp function, level: 0, prev_jmp: -1 } var Module = { postRun: function() { + Module.print('Exit Status: ' + EXITSTATUS); Module.print('postRun'); assert(initialStack == STACKTOP, [initialStack, STACKTOP]); Module.print('ok.'); @@ -10219,13 +10239,19 @@ def process(filename): printf("cleanup\n"); } - int main() - { + int main() { atexit(cleanup); // this atexit should still be called printf("hello, world!\n"); exit(118); // Unusual exit status to make sure it's working! } ''' + open('post.js', 'w').write(''' + Module.addOnExit(function () { + Module.print('Exit Status: ' + EXITSTATUS); + }); + Module.callMain(); + ''') + self.emcc_args += ['-s', 'INVOKE_RUN=0', '--post-js', 'post.js'] self.do_run(src, 'hello, world!\ncleanup\nExit Status: 118') def test_gc(self):