From 17ef4e3c39b288375f027a6b6735809b8dacf59e Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 28 Nov 2017 13:15:15 -0800 Subject: [PATCH 01/42] NO_EXIT_RUNTIME=1 by default: avoid including runtime shutdown overhead in the general case. show warnings in ASSERTIONS mode when exiting the runtime was necessary --- emcc.py | 9 ++++++--- src/library.js | 5 +++++ src/library_fs.js | 19 ++++++++++++++++++- src/settings.js | 8 ++++++-- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/emcc.py b/emcc.py index 7973465254a65..57f761ae5416f 100755 --- a/emcc.py +++ b/emcc.py @@ -1068,9 +1068,12 @@ def check(input_file): if not shared.Settings.NO_FILESYSTEM and not shared.Settings.ONLY_MY_CODE: shared.Settings.EXPORTED_FUNCTIONS += ['___errno_location'] # so FS can report errno back to C - if not shared.Settings.NO_EXIT_RUNTIME: - shared.Settings.EXPORTED_FUNCTIONS += ['_fflush'] # to flush the streams on FS quit - # TODO this forces 4 syscalls, maybe we should avoid it? + # to flush streams on FS exit, we need to be able to call fflush + # we only include it if the runtime is exitable, or when ASSERTIONS + # (ASSERTIONS will check that streams do not need to be flushed, + # helping people see when they should have disabled NO_EXIT_RUNTIME) + if not shared.Settings.NO_EXIT_RUNTIME or shared.Settings.ASSERTIONS: + shared.Settings.EXPORTED_FUNCTIONS += ['_fflush'] if shared.Settings.USE_PTHREADS: if not any(s.startswith('PTHREAD_POOL_SIZE=') for s in settings_changes): diff --git a/src/library.js b/src/library.js index 6ab358296d4d5..77ea2046c69d4 100644 --- a/src/library.js +++ b/src/library.js @@ -596,6 +596,11 @@ LibraryManager.library = { atexit__proxy: 'sync', atexit__sig: 'ii', atexit: function(func, arg) { +#if ASSERTIONS +#if NO_EXIT_RUNTIME == 1 + Runtime.warnOnce('atexit() called, but NO_EXIT_RUNTIME, so atexits() will not be called'); +#endif +#endif __ATEXIT__.unshift({ func: func, arg: arg }); }, __cxa_atexit: 'atexit', diff --git a/src/library_fs.js b/src/library_fs.js index 87e3cb11d9911..ba36b0b77ec6c 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -1420,7 +1420,24 @@ mergeInto(LibraryManager.library, { FS.init.initialized = false; // force-flush all streams, so we get musl std streams printed out var fflush = Module['_fflush']; - if (fflush) fflush(0); + if (fflush) { +#if ASSERTIONS +#if NO_EXIT_RUNTIME == 1 + // compiler settings do not allow exiting the runtime, so flushing + // the streams is not possible. but in ASSERTIONS mode we check + // if there was something to flush, and if so tell the user they + // should request that the runtime be exitable. + var print = Module['print']; + var printErr = Module['printErr']; + Module['print'] = Module['printErr'] = function(x) { + Runtime.warnOnce('stdio streams had content in them that was not flushed. you should set NO_EXIT_RUNTIME to 0'); + } + Module['print'] = print; + Module['printErr'] = printErr; +#endif +#endif + fflush(0); + } // close all of our streams for (var i = 0; i < FS.streams.length; i++) { var stream = FS.streams[i]; diff --git a/src/settings.js b/src/settings.js index 341f0aca18a72..509fc0bf71a13 100644 --- a/src/settings.js +++ b/src/settings.js @@ -44,8 +44,12 @@ var VERBOSE = 0; // When set to 1, will generate more verbose output during comp var INVOKE_RUN = 1; // Whether we will run the main() function. Disable if you embed the generated // code in your own, and will call main() yourself at the right time (which you // can do with Module.callMain(), with an optional parameter of commandline args). -var NO_EXIT_RUNTIME = 0; // If set, the runtime is not quit when main() completes (allowing code to - // run afterwards, for example from the browser main event loop). +var NO_EXIT_RUNTIME = 1; // If 1, the runtime is not quit when main() completes (allowing code to + // run afterwards, for example from the browser main event loop). atexit()s + // are also not executed, and we can avoid including code for runtime shutdown, + // like flushing the stdio streams. + // Set this to 0 if you do want atexit()s or stdio streams to be flushed + // on exit. var MEM_INIT_METHOD = 0; // How to represent the initial memory content. // 0: embed a base64 string literal representing the initial memory data // 1: create a *.mem file containing the binary data of the initial memory; From f3eb3640a83a7588ea40bb77880107b5c67ca618 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 28 Nov 2017 14:01:42 -0800 Subject: [PATCH 02/42] fixes --- src/library_fs.js | 19 +------------------ src/library_syscall.js | 15 ++++++++++++++- src/postamble.js | 29 +++++++++++++++++++++++++++++ tests/test_benchmark.py | 1 - tests/test_other.py | 31 ++++++++++++++++++++++++++----- 5 files changed, 70 insertions(+), 25 deletions(-) diff --git a/src/library_fs.js b/src/library_fs.js index ba36b0b77ec6c..87e3cb11d9911 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -1420,24 +1420,7 @@ mergeInto(LibraryManager.library, { FS.init.initialized = false; // force-flush all streams, so we get musl std streams printed out var fflush = Module['_fflush']; - if (fflush) { -#if ASSERTIONS -#if NO_EXIT_RUNTIME == 1 - // compiler settings do not allow exiting the runtime, so flushing - // the streams is not possible. but in ASSERTIONS mode we check - // if there was something to flush, and if so tell the user they - // should request that the runtime be exitable. - var print = Module['print']; - var printErr = Module['printErr']; - Module['print'] = Module['printErr'] = function(x) { - Runtime.warnOnce('stdio streams had content in them that was not flushed. you should set NO_EXIT_RUNTIME to 0'); - } - Module['print'] = print; - Module['printErr'] = printErr; -#endif -#endif - fflush(0); - } + if (fflush) fflush(0); // close all of our streams for (var i = 0; i < FS.streams.length; i++) { var stream = FS.streams[i]; diff --git a/src/library_syscall.js b/src/library_syscall.js index 2787b6f08f19c..3e9d70b59610b 100644 --- a/src/library_syscall.js +++ b/src/library_syscall.js @@ -779,7 +779,20 @@ var SyscallsLibrary = { return SYSCALLS.doReadv(stream, iov, iovcnt); }, #if NO_FILESYSTEM - __syscall146__postset: '/* flush anything remaining in the buffer during shutdown */ __ATEXIT__.push(function() { var fflush = Module["_fflush"]; if (fflush) fflush(0); var printChar = ___syscall146.printChar; if (!printChar) return; var buffers = ___syscall146.buffers; if (buffers[1].length) printChar(1, {{{ charCode("\n") }}}); if (buffers[2].length) printChar(2, {{{ charCode("\n") }}}); });', + $flush_NO_FILESYSTEM: function() { + // flush anything remaining in the buffers during shutdown + var fflush = Module["_fflush"]; + if (fflush) fflush(0); + var printChar = ___syscall146.printChar; + if (!printChar) return; + var buffers = ___syscall146.buffers; + if (buffers[1].length) printChar(1, {{{ charCode("\n") }}}); + if (buffers[2].length) printChar(2, {{{ charCode("\n") }}}); + }, + __syscall146__deps: ['$flush_NO_FILESYSTEM'], +#if NO_EXIT_RUNTIME == 0 + __syscall146__postset: '__ATEXIT__.push(flush_NO_FILESYSTEM);', +#endif #endif __syscall146: function(which, varargs) { // writev #if NO_FILESYSTEM == 0 diff --git a/src/postamble.js b/src/postamble.js index 4a4713a0bdb9c..1ea828b4d9106 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -310,7 +310,36 @@ function exit(status, implicit) { if (implicit && Module['noExitRuntime']) { #if ASSERTIONS Module.printErr('exit(' + status + ') implicitly called by end of main(), but noExitRuntime, so not exiting the runtime (you can use emscripten_force_exit, if you want to force a true shutdown)'); + +#if NO_EXIT_RUNTIME == 1 + // compiler settings do not allow exiting the runtime, so flushing + // the streams is not possible. but in ASSERTIONS mode we check + // if there was something to flush, and if so tell the user they + // should request that the runtime be exitable. + // how we flush the streams depends on whether we are in NO_FILESYSTEM + // mode (which has its own special function for this; otherwise, all + // the code is inside libc) +#if NO_FILESYSTEM + var flush = typeof flush_NO_FILESYSTEM === 'function' ? flush_NO_FILESYSTEM : null; +#else + var flush = Module['_fflush']; #endif + if (flush) { + var print = Module['print']; + var printErr = Module['printErr']; + var has = false; + Module['print'] = Module['printErr'] = function(x) { + has = true; + } + flush(0); + Module['print'] = print; + Module['printErr'] = printErr; + if (has) { + Runtime.warnOnce('stdio streams had content in them that was not flushed. you should set NO_EXIT_RUNTIME to 0'); + } + } +#endif // NO_EXIT_RUNTIME +#endif // ASSERTIONS return; } diff --git a/tests/test_benchmark.py b/tests/test_benchmark.py index 5ffcab8712144..b97973979b52a 100644 --- a/tests/test_benchmark.py +++ b/tests/test_benchmark.py @@ -138,7 +138,6 @@ def process(filename): OPTIMIZATIONS, '--memory-init-file', '0', '--js-transform', 'python hardcode.py', '-s', 'TOTAL_MEMORY=256*1024*1024', - '-s', 'NO_EXIT_RUNTIME=1', '-s', 'NO_FILESYSTEM=1', '-s', 'EXPORTED_RUNTIME_METHODS=[]', '-s', 'BENCHMARK=%d' % (1 if IGNORE_COMPILATION and not has_output_parser else 0), diff --git a/tests/test_other.py b/tests/test_other.py index 665844510ce24..2ec64f89d5b08 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -1624,7 +1624,7 @@ def test_js_link(self): Module.print(MESSAGE); ''') - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'before.js', '--post-js', 'after.js', '-s', 'NO_EXIT_RUNTIME=1']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'before.js', '--post-js', 'after.js']).communicate() self.assertContained('hello from main\nhello from js\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) def test_sdl_endianness(self): @@ -1802,7 +1802,7 @@ def test_prepost(self): }; ''') - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '-s', 'NO_EXIT_RUNTIME=1']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js']).communicate() self.assertContained('pre-run\nhello from main\npost-run\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) # never run, so no preRun or postRun @@ -1813,7 +1813,7 @@ def test_prepost(self): # noInitialRun prevents run for no_initial_run, run_dep in [(0, 0), (1, 0), (0, 1)]: print(no_initial_run, run_dep) - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-s', 'NO_EXIT_RUNTIME=1']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp')]).communicate() src = 'var Module = { noInitialRun: %d };\n' % no_initial_run + open(os.path.join(self.get_dir(), 'a.out.js')).read() if run_dep: src = src.replace('// {{PRE_RUN_ADDITIONS}}', '// {{PRE_RUN_ADDITIONS}}\naddRunDependency("test");') \ @@ -1836,7 +1836,7 @@ def test_prepost(self): preInit: function() { Module.print('pre-init') } }; ''') - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '-s', 'NO_EXIT_RUNTIME=1']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js']).communicate() self.assertContained('pre-init\npre-run\nhello from main\npost-run\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) def test_prepost2(self): @@ -1855,7 +1855,7 @@ def test_prepost2(self): open(os.path.join(self.get_dir(), 'pre2.js'), 'w').write(''' Module.postRun = function() { Module.print('post-run') }; ''') - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '--pre-js', 'pre2.js', '-s', 'NO_EXIT_RUNTIME=1']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '--pre-js', 'pre2.js']).communicate() self.assertContained('pre-run\nhello from main\npost-run\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) def test_prepre(self): @@ -3233,6 +3233,27 @@ def test_no_exit_runtime(self): assert ('_ZN5WasteILi2EED' in src) == exit, 'destructors should not appear if no exit' assert ('atexit(' in src) == exit, 'atexit should not appear or be called' + def test_no_exit_runtime_warnings(self): + open('code.cpp', 'w').write(r''' +#include +int main(int argc, char **argv) { + printf("hello\n"); + printf("world"); // no newline, not flushed + return 0; +} +''') + + for no_exit in [0, 1]: + for assertions in [0, 1]: + print(no_exit, assertions) + subprocess.check_call([PYTHON, EMCC, 'code.cpp', '-s', 'NO_EXIT_RUNTIME=%d' % no_exit, '-s', 'ASSERTIONS=%d' % assertions]) + output = run_js(os.path.join(self.get_dir(), 'a.out.js'), stderr=PIPE, full_output=True) + exit = 1-no_exit + assert 'hello' in output + assert ('world' in output) == exit, 'unflushed content is shown only when exiting the runtime' + if no_exit and assertions: + assert 'stdio streams had content in them that was not flushed. you should set NO_EXIT_RUNTIME to 0' in output, 'warning should be shown' + def test_os_oz(self): if os.environ.get('EMCC_DEBUG'): return self.skip('cannot run in debug mode') try: From 1106aed3553c9fe99c70ef51af1861a03edb4a03 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 28 Nov 2017 14:06:10 -0800 Subject: [PATCH 03/42] more tests --- src/library.js | 2 +- tests/test_other.py | 23 ++++++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/library.js b/src/library.js index 77ea2046c69d4..b956f6e1febda 100644 --- a/src/library.js +++ b/src/library.js @@ -598,7 +598,7 @@ LibraryManager.library = { atexit: function(func, arg) { #if ASSERTIONS #if NO_EXIT_RUNTIME == 1 - Runtime.warnOnce('atexit() called, but NO_EXIT_RUNTIME, so atexits() will not be called'); + Runtime.warnOnce('atexit() called, but NO_EXIT_RUNTIME, so atexits() will not be called. set NO_EXIT_RUNTIME to 0'); #endif #endif __ATEXIT__.unshift({ func: func, arg: arg }); diff --git a/tests/test_other.py b/tests/test_other.py index 2ec64f89d5b08..5988b12df0ac1 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -3233,16 +3233,14 @@ def test_no_exit_runtime(self): assert ('_ZN5WasteILi2EED' in src) == exit, 'destructors should not appear if no exit' assert ('atexit(' in src) == exit, 'atexit should not appear or be called' - def test_no_exit_runtime_warnings(self): + def test_no_exit_runtime_warnings_flush(self): open('code.cpp', 'w').write(r''' #include int main(int argc, char **argv) { printf("hello\n"); printf("world"); // no newline, not flushed - return 0; } ''') - for no_exit in [0, 1]: for assertions in [0, 1]: print(no_exit, assertions) @@ -3251,8 +3249,23 @@ def test_no_exit_runtime_warnings(self): exit = 1-no_exit assert 'hello' in output assert ('world' in output) == exit, 'unflushed content is shown only when exiting the runtime' - if no_exit and assertions: - assert 'stdio streams had content in them that was not flushed. you should set NO_EXIT_RUNTIME to 0' in output, 'warning should be shown' + assert (no_exit and assertions) == ('stdio streams had content in them that was not flushed. you should set NO_EXIT_RUNTIME to 0' in output), 'warning should be shown' + + def test_no_exit_runtime_warnings_atexit(self): + open('code.cpp', 'w').write(r''' +#include +void bye() {} +int main() { + atexit(bye); +} +''') + for no_exit in [0, 1]: + for assertions in [0, 1]: + print(no_exit, assertions) + subprocess.check_call([PYTHON, EMCC, 'code.cpp', '-s', 'NO_EXIT_RUNTIME=%d' % no_exit, '-s', 'ASSERTIONS=%d' % assertions]) + output = run_js(os.path.join(self.get_dir(), 'a.out.js'), stderr=PIPE, full_output=True) + exit = 1-no_exit + assert (no_exit and assertions) == ('atexit() called, but NO_EXIT_RUNTIME, so atexits() will not be called. set NO_EXIT_RUNTIME to 0' in output), 'warning should be shown' def test_os_oz(self): if os.environ.get('EMCC_DEBUG'): return self.skip('cannot run in debug mode') From 05f07be230cf61509622c50e5b95c253ab3a2803 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 28 Nov 2017 14:11:04 -0800 Subject: [PATCH 04/42] fixes --- emcc.py | 4 ---- tests/test_other.py | 25 +++++++++---------------- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/emcc.py b/emcc.py index 57f761ae5416f..cc82b66b4e90e 100755 --- a/emcc.py +++ b/emcc.py @@ -1203,10 +1203,6 @@ def check(input_file): if 'interpret' in shared.Settings.BINARYEN_METHOD: logging.warning('disabling EVAL_CTORS as the bundled interpreter confuses the ctor tool') shared.Settings.EVAL_CTORS = 0 - else: - # for wasm, we really want no-exit-runtime, so that atexits don't stop us - if final_suffix in JS_CONTAINING_SUFFIXES and not shared.Settings.NO_EXIT_RUNTIME: - logging.warning('you should enable -s NO_EXIT_RUNTIME=1 so that EVAL_CTORS can work at full efficiency (it gets rid of atexit calls which might disrupt EVAL_CTORS)') # memory growth does not work in dynamic linking, except for wasm if not shared.Settings.WASM and (shared.Settings.MAIN_MODULE or shared.Settings.SIDE_MODULE): diff --git a/tests/test_other.py b/tests/test_other.py index 5988b12df0ac1..e67232dd76d22 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -3366,12 +3366,12 @@ def test_global_inits(self): ''') for opts, has_global in [ - (['-O2', '-g'], True), - (['-O2', '-g', '-s', 'NO_EXIT_RUNTIME=1'], False), # no-exit-runtime removes the atexits, and then globalgce can work it's magic to remove the global initializer entirely - (['-Os', '-g'], True), - (['-Os', '-g', '-s', 'NO_EXIT_RUNTIME=1'], False), - (['-O2', '-g', '--llvm-lto', '1'], True), - (['-O2', '-g', '-s', 'NO_EXIT_RUNTIME=1', '--llvm-lto', '1'], False), + (['-O2', '-g', '-s', 'NO_EXIT_RUNTIME=0'], True), + (['-O2', '-g'], False), # no-exit-runtime removes the atexits, and then globalgce can work it's magic to remove the global initializer entirely + (['-Os', '-g', '-s', 'NO_EXIT_RUNTIME=0'], True), + (['-Os', '-g'], False), + (['-O2', '-g', '--llvm-lto', '1', '-s', 'NO_EXIT_RUNTIME=0'], True), + (['-O2', '-g', '--llvm-lto', '1'], False), ]: print(opts, has_global) Popen([PYTHON, EMCC, 'main.cpp', '-c'] + opts).communicate() @@ -5340,17 +5340,17 @@ def test_require(self): assert output == ('hello, world!\n', ''), 'expected no output, got\n===\nSTDOUT\n%s\n===\nSTDERR\n%s\n===\n' % output def test_require_modularize(self): - Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-s', 'MODULARIZE=1']).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-s', 'MODULARIZE=1', '-s', 'ASSERTIONS=0']).communicate() src = open('a.out.js').read() assert "module['exports'] = Module;" in src output = Popen(NODE_JS + ['-e', 'var m = require("./a.out.js"); m();'], stdout=PIPE, stderr=PIPE).communicate() assert output == ('hello, world!\n', ''), 'expected output, got\n===\nSTDOUT\n%s\n===\nSTDERR\n%s\n===\n' % output - Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-s', 'MODULARIZE=1', '-s', 'EXPORT_NAME="NotModule"']).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-s', 'MODULARIZE=1', '-s', 'EXPORT_NAME="NotModule"', '-s', 'ASSERTIONS=0']).communicate() src = open('a.out.js').read() assert "module['exports'] = NotModule;" in src output = Popen(NODE_JS + ['-e', 'var m = require("./a.out.js"); m();'], stdout=PIPE, stderr=PIPE).communicate() assert output == ('hello, world!\n', ''), 'expected output, got\n===\nSTDOUT\n%s\n===\nSTDERR\n%s\n===\n' % output - Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-s', 'MODULARIZE=1', '-s', 'NO_EXIT_RUNTIME=1']).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-s', 'MODULARIZE=1']).communicate() # We call require() twice to ensure it returns wrapper function each time output = Popen(NODE_JS + ['-e', 'require("./a.out.js")();var m = require("./a.out.js"); m();'], stdout=PIPE, stderr=PIPE).communicate() assert output[0] == 'hello, world!\nhello, world!\n', 'expected output, got\n===\nSTDOUT\n%s\n===\nSTDERR\n%s\n===\n' % output @@ -7766,13 +7766,6 @@ def test_native_link_error_message(self): out, err = Popen([PYTHON, EMCC, 'hello_world.a', '-o', 'hello_world.js'], stdout=PIPE, stderr=PIPE).communicate() assert 'exists but was not an LLVM bitcode file suitable for Emscripten. Perhaps accidentally mixing native built object files with Emscripten?' in err - # Tests that the warning message about pairing WASM with EVAL_CTORS appropriately triggers the warning about NO_EXIT_RUNTIME. - def test_binaryen_no_exit_runtime_warn_message(self): - out, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-o', 'hello_world.js', '-s', 'WASM=1', '-Oz'], stdout=PIPE, stderr=PIPE).communicate() - assert 'you should enable -s NO_EXIT_RUNTIME=1' in err - out, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-o', 'hello_world.bc', '-s', 'WASM=1', '-Oz'], stdout=PIPE, stderr=PIPE).communicate() - assert 'you should enable -s NO_EXIT_RUNTIME=1' not in err - def test_o_level_clamp(self): for level in [3, 4, 20]: out, err = Popen([PYTHON, EMCC, '-O' + str(level), path_from_root('tests', 'hello_world.c')], stdout=PIPE, stderr=PIPE).communicate() From 3d623ebbfcc4306ddee9f5c7f627d1f486b16c3b Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 28 Nov 2017 14:14:35 -0800 Subject: [PATCH 05/42] bonus: the new default of NO_EXIT_RUNTIME=1 is the common case on the web, actually --- tests/test_browser.py | 18 +++++++++--------- tests/test_core.py | 11 ----------- tests/test_interactive.py | 14 +++++++------- 3 files changed, 16 insertions(+), 27 deletions(-) diff --git a/tests/test_browser.py b/tests/test_browser.py index dc29226250816..5ca13ffd61b34 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -785,7 +785,7 @@ def test_sdl_key(self): ''' % ('setTimeout(function() {' if delay else '', '}, 1);' if delay else '', 'setTimeout(function() {' if delay else '', '}, 1);' if delay else '')) open(os.path.join(self.get_dir(), 'sdl_key.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_key.c')).read())) - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_key.c'), '-o', 'page.html'] + defines + emterps + ['--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main']''', '-s', 'NO_EXIT_RUNTIME=1', '-lSDL', '-lGL']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_key.c'), '-o', 'page.html'] + defines + emterps + ['--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main']''', '-lSDL', '-lGL']).communicate() self.run_browser('page.html', '', '/report_result?223092870') def test_sdl_key_proxy(self): @@ -832,7 +832,7 @@ def post(): ''') open('test.html', 'w').write(html) - self.btest('sdl_key_proxy.c', '223092870', args=['--proxy-to-worker', '--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']''', '-s', 'NO_EXIT_RUNTIME=1', '-lSDL', '-lGL'], manual_reference=True, post_build=post) + self.btest('sdl_key_proxy.c', '223092870', args=['--proxy-to-worker', '--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']''', '-lSDL', '-lGL'], manual_reference=True, post_build=post) def test_keydown_preventdefault_proxy(self): def post(): @@ -888,7 +888,7 @@ def post(): open('test.html', 'w').write(html) - self.btest('keydown_preventdefault_proxy.cpp', '300', args=['--proxy-to-worker', '-s', '''EXPORTED_FUNCTIONS=['_main']''', '-s', 'NO_EXIT_RUNTIME=1'], manual_reference=True, post_build=post) + self.btest('keydown_preventdefault_proxy.cpp', '300', args=['--proxy-to-worker', '-s', '''EXPORTED_FUNCTIONS=['_main']'''], manual_reference=True, post_build=post) def test_sdl_text(self): open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' @@ -1209,7 +1209,7 @@ def test_emscripten_get_now(self): def test_fflush(self): return self.skip('Skipping due to https://github.com/kripken/emscripten/issues/2770') - self.btest('test_fflush.cpp', '0', args=['-s', 'NO_EXIT_RUNTIME=1', '--shell-file', path_from_root('tests', 'test_fflush.html')]) + self.btest('test_fflush.cpp', '0', args=['--shell-file', path_from_root('tests', 'test_fflush.html')]) def test_file_db(self): secret = str(time.time()) @@ -2159,7 +2159,7 @@ def test_runtime_misuse(self): self.btest(filename, expected=str(second_code), args=['--post-js', 'post.js', '--memory-init-file', '0'] + extra_args + mode) print('sync, runtime still alive, so all good') open(os.path.join(self.get_dir(), 'post.js'), 'w').write(post_prep + 'expected_ok = true; Module.postRun.push(function() { ' + post_test + ' });' + post_hook); - self.btest(filename, expected='606', args=['--post-js', 'post.js', '--memory-init-file', '0', '-s', 'NO_EXIT_RUNTIME=1'] + extra_args + mode) + self.btest(filename, expected='606', args=['--post-js', 'post.js', '--memory-init-file', '0'] + extra_args + mode) def test_cwrap_early(self): self.btest(os.path.join('browser', 'cwrap_early.cpp'), args=['-O2', '-s', 'ASSERTIONS=1', '--pre-js', path_from_root('tests', 'browser', 'cwrap_early.js')], expected='0') @@ -2296,7 +2296,7 @@ def test_html5_webgl_create_context2(self): def test_html5_webgl_destroy_context(self): for opts in [[], ['-O2', '-g1'], ['-s', 'FULL_ES2=1']]: print(opts) - self.btest(path_from_root('tests', 'webgl_destroy_context.cpp'), args=opts + ['--shell-file', path_from_root('tests/webgl_destroy_context_shell.html'), '-s', 'NO_EXIT_RUNTIME=1', '-lGL'], expected='0', timeout=20) + self.btest(path_from_root('tests', 'webgl_destroy_context.cpp'), args=opts + ['--shell-file', path_from_root('tests/webgl_destroy_context_shell.html'), '-lGL'], expected='0', timeout=20) def test_webgl_context_params(self): if WINDOWS: return self.skip('SKIPPED due to bug https://bugzilla.mozilla.org/show_bug.cgi?id=1310005 - WebGL implementation advertises implementation defined GL_IMPLEMENTATION_COLOR_READ_TYPE/FORMAT pair that it cannot read with') @@ -2496,7 +2496,7 @@ def test_asm_swapping(self): ''') for opts in [[], ['-O1'], ['-O2', '-profiling'], ['-O2']]: print(opts) - opts += ['-s', 'NO_EXIT_RUNTIME=1', '--pre-js', 'run.js', '-s', 'SWAPPABLE_ASM_MODULE=1'] # important that both modules are built with the same opts + opts += ['--pre-js', 'run.js', '-s', 'SWAPPABLE_ASM_MODULE=1'] # important that both modules are built with the same opts open('second.cpp', 'w').write(self.with_report_result(open(path_from_root('tests', 'asm_swap2.cpp')).read())) Popen([PYTHON, EMCC, 'second.cpp'] + opts).communicate() Popen([PYTHON, path_from_root('tools', 'distill_asm.py'), 'a.out.js', 'second.js', 'swap-in']).communicate() @@ -2575,7 +2575,7 @@ def test_sdl2_key(self): ''') open(os.path.join(self.get_dir(), 'sdl2_key.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl2_key.c')).read())) - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl2_key.c'), '-o', 'page.html'] + defines + ['-s', 'USE_SDL=2','--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']''', '-s', 'NO_EXIT_RUNTIME=1']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl2_key.c'), '-o', 'page.html'] + defines + ['-s', 'USE_SDL=2','--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']''']).communicate() self.run_browser('page.html', '', '/report_result?37182145') def test_sdl2_text(self): @@ -3177,7 +3177,7 @@ def test_pthread_proxy_to_pthread(self): # Test that a pthread can spawn another pthread of its own. def test_pthread_create_pthread(self): for opt in [['-s', 'USE_PTHREADS=2', '--separate-asm'], ['-s', 'USE_PTHREADS=1']]: - self.btest(path_from_root('tests', 'pthread', 'test_pthread_create_pthread.cpp'), expected='1', args=opt + ['-O3', '-s', 'PTHREAD_POOL_SIZE=2', '-s', 'NO_EXIT_RUNTIME=1'], timeout=30) + self.btest(path_from_root('tests', 'pthread', 'test_pthread_create_pthread.cpp'), expected='1', args=opt + ['-O3', '-s', 'PTHREAD_POOL_SIZE=2'], timeout=30) # Test another case of pthreads spawning pthreads, but this time the callers immediately join on the threads they created. def test_pthread_nested_spawns(self): diff --git a/tests/test_core.py b/tests/test_core.py index 4ccec8cf5633a..95e90fbd8eaef 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -4986,8 +4986,6 @@ def test_dlmalloc_partial_2(self): if 'SAFE_HEAP' in str(self.emcc_args): return self.skip('we do unsafe stuff here') # present part of the symbols of dlmalloc, not all. malloc is harder to link than new which is weak. - Settings.NO_EXIT_RUNTIME = 1 # if we exit, then we flush streams, but the bad malloc we install here messes that up - self.do_run_in_out_file_test('tests', 'core', 'test_dlmalloc_partial_2') def test_libcxx(self): @@ -5514,8 +5512,6 @@ def test_poppler(self): if WINDOWS: return self.skip('test_poppler depends on freetype, which uses a ./configure script to build and therefore currently only runs on Linux and OS X.') def test(): - Settings.NO_EXIT_RUNTIME = 1 - Building.COMPILER_TEST_OPTS += [ '-I' + path_from_root('tests', 'freetype', 'include'), '-I' + path_from_root('tests', 'poppler', 'include') @@ -5649,8 +5645,6 @@ def image_compare(output, err): self.emcc_args += ['--minify', '0'] # to compare the versions - Settings.NO_EXIT_RUNTIME = 1 - def do_test(): self.do_run(open(path_from_root('tests', 'openjpeg', 'codec', 'j2k_to_image.c'), 'r').read(), 'Successfully generated', # The real test for valid output is in image_compare @@ -6258,9 +6252,6 @@ def test_tracing(self): def test_eval_ctors(self): if '-O2' not in str(self.emcc_args) or '-O1' in str(self.emcc_args): return self.skip('need js optimizations') - if self.is_wasm(): - self.emcc_args += ['-s', 'NO_EXIT_RUNTIME=1'] - orig_args = self.emcc_args[:] + ['-s', 'EVAL_CTORS=0'] print('leave printf in ctor') @@ -6386,7 +6377,6 @@ def test_embind(self): self.do_run(src, 'abs(-10): 10\nabs(-11): 11'); def test_embind_2(self): - Settings.NO_EXIT_RUNTIME = 1 # we emit some post.js that we need to see Building.COMPILER_TEST_OPTS += ['--bind', '--post-js', 'post.js'] open('post.js', 'w').write(''' function printLerp() { @@ -6412,7 +6402,6 @@ def test_embind_2(self): self.do_run(src, 'lerp 166'); def test_embind_3(self): - Settings.NO_EXIT_RUNTIME = 1 # we emit some post.js that we need to see Building.COMPILER_TEST_OPTS += ['--bind', '--post-js', 'post.js'] open('post.js', 'w').write(''' function ready() { diff --git a/tests/test_interactive.py b/tests/test_interactive.py index 955a05e5c3c92..d4eef171f7c45 100644 --- a/tests/test_interactive.py +++ b/tests/test_interactive.py @@ -39,10 +39,10 @@ def test_sdl_touch(self): self.btest(path_from_root('tests', 'sdl_touch.c'), args=['-O2', '-g1', '--closure', '1'], expected='0') def test_sdl_wm_togglefullscreen(self): - self.btest('sdl_wm_togglefullscreen.c', expected='1', args=['-s', 'NO_EXIT_RUNTIME=1']) + self.btest('sdl_wm_togglefullscreen.c', expected='1') def test_sdl2_togglefullscreen(self): - self.btest('sdl_togglefullscreen.c', expected='1', args=['-s', 'USE_SDL=2', '-s', 'NO_EXIT_RUNTIME=1']) + self.btest('sdl_togglefullscreen.c', expected='1', args=['-s', 'USE_SDL=2']) def test_sdl_audio(self): shutil.copyfile(path_from_root('tests', 'sounds', 'alarmvictory_1.ogg'), os.path.join(self.get_dir(), 'sound.ogg')) @@ -144,7 +144,7 @@ def test_openal_al_soft_source_spatialize(self): self.btest('openal_playback.cpp', '1', args=['-DTEST_AL_SOFT_SOURCE_SPATIALIZE=1', '-DTEST_LOOPED_PLAYBACK=1', '--preload-file', path_from_root('tests', 'sounds', 'the_entertainer.wav') + '@/audio.wav'],) def test_openal_capture(self): - self.btest('openal_capture.c', expected='0', args=['-s', 'NO_EXIT_RUNTIME=1']) + self.btest('openal_capture.c', expected='0') def get_freealut_library(self): if WINDOWS and Building.which('cmake'): @@ -166,16 +166,16 @@ def test_glfw_dropfile(self): self.btest('test_glfw_dropfile.c', expected='1', args=['-s', 'USE_GLFW=3', '-lglfw', '-lGL']) def test_glfw_fullscreen(self): - self.btest('test_glfw_fullscreen.c', expected='1', args=['-s', 'NO_EXIT_RUNTIME=1', '-s', 'USE_GLFW=3']) + self.btest('test_glfw_fullscreen.c', expected='1', args=['-s', 'USE_GLFW=3']) def test_glfw_get_key_stuck(self): - self.btest('test_glfw_get_key_stuck.c', expected='1', args=['-s', 'NO_EXIT_RUNTIME=1', '-s', 'USE_GLFW=3']) + self.btest('test_glfw_get_key_stuck.c', expected='1', args=['-s', 'USE_GLFW=3']) def test_glfw_joystick(self): - self.btest('glfw_joystick.c', expected='1', args=['-s', 'NO_EXIT_RUNTIME=1', '-s', 'USE_GLFW=3']) + self.btest('glfw_joystick.c', expected='1', args=['-s', 'USE_GLFW=3']) def test_glfw_pointerlock(self): - self.btest('test_glfw_pointerlock.c', expected='1', args=['-s', 'NO_EXIT_RUNTIME=1', '-s', 'USE_GLFW=3']) + self.btest('test_glfw_pointerlock.c', expected='1', args=['-s', 'USE_GLFW=3']) def test_cpuprofiler_memoryprofiler(self): self.btest('hello_world_gles.c', expected='0', args=['-DLONGTEST=1', '-DTEST_MEMORYPROFILER_ALLOCATIONS_MAP=1', '-O2', '--cpuprofiler', '--memoryprofiler']) From 7ca10a107fb24607fea297aff533d7ea5bfb2214 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 28 Nov 2017 14:22:08 -0800 Subject: [PATCH 06/42] fix --- src/library_syscall.js | 2 +- tests/test_browser.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/library_syscall.js b/src/library_syscall.js index 3e9d70b59610b..26f20f6530f6d 100644 --- a/src/library_syscall.js +++ b/src/library_syscall.js @@ -787,7 +787,7 @@ var SyscallsLibrary = { if (!printChar) return; var buffers = ___syscall146.buffers; if (buffers[1].length) printChar(1, {{{ charCode("\n") }}}); - if (buffers[2].length) printChar(2, {{{ charCode("\n") }}}); + if (buffers[2].length) printChar(2, {{{ charCode("\n") }}}); }, __syscall146__deps: ['$flush_NO_FILESYSTEM'], #if NO_EXIT_RUNTIME == 0 diff --git a/tests/test_browser.py b/tests/test_browser.py index 5ca13ffd61b34..e3b96a1191574 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -2156,7 +2156,7 @@ def test_runtime_misuse(self): self.btest(filename, expected='600', args=['--post-js', 'post.js', '--memory-init-file', '1'] + extra_args + mode) print('sync startup, call too late') open(os.path.join(self.get_dir(), 'post.js'), 'w').write(post_prep + 'Module.postRun.push(function() { ' + post_test + ' });' + post_hook); - self.btest(filename, expected=str(second_code), args=['--post-js', 'post.js', '--memory-init-file', '0'] + extra_args + mode) + self.btest(filename, expected=str(second_code), args=['--post-js', 'post.js', '--memory-init-file', '0', '-s', 'NO_EXIT_RUNTIME=0'] + extra_args + mode) print('sync, runtime still alive, so all good') open(os.path.join(self.get_dir(), 'post.js'), 'w').write(post_prep + 'expected_ok = true; Module.postRun.push(function() { ' + post_test + ' });' + post_hook); self.btest(filename, expected='606', args=['--post-js', 'post.js', '--memory-init-file', '0'] + extra_args + mode) From ac6a066d5cfe97c25fbddf077974d280577b3ba7 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 28 Nov 2017 14:23:19 -0800 Subject: [PATCH 07/42] fix --- tests/test_browser.py | 2 +- tests/test_core.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_browser.py b/tests/test_browser.py index e3b96a1191574..1ca6bc6ac61a2 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -2153,7 +2153,7 @@ def test_runtime_misuse(self): print('\n', filename, extra_args, mode) print('mem init, so async, call too early') open(os.path.join(self.get_dir(), 'post.js'), 'w').write(post_prep + post_test + post_hook) - self.btest(filename, expected='600', args=['--post-js', 'post.js', '--memory-init-file', '1'] + extra_args + mode) + self.btest(filename, expected='600', args=['--post-js', 'post.js', '--memory-init-file', '1', '-s', 'NO_EXIT_RUNTIME=0'] + extra_args + mode) print('sync startup, call too late') open(os.path.join(self.get_dir(), 'post.js'), 'w').write(post_prep + 'Module.postRun.push(function() { ' + post_test + ' });' + post_hook); self.btest(filename, expected=str(second_code), args=['--post-js', 'post.js', '--memory-init-file', '0', '-s', 'NO_EXIT_RUNTIME=0'] + extra_args + mode) diff --git a/tests/test_core.py b/tests/test_core.py index 95e90fbd8eaef..346d20c7587a1 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1105,7 +1105,7 @@ def test_exceptions_uncaught(self): def test_exceptions_uncaught_2(self): Settings.DISABLE_EXCEPTION_CATCHING = 0 - + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams src = r''' #include #include @@ -2091,6 +2091,7 @@ def test_strtol_oct(self): def test_atexit(self): # Confirms they are called in reverse order + Settings.NO_EXIT_RUNTIME = 0 # needs atexits self.do_run_in_out_file_test('tests', 'core', 'test_atexit') def test_pthread_specific(self): @@ -4055,7 +4056,7 @@ def test_sscanf_whitespace(self): def test_sscanf_other_whitespace(self): Settings.SAFE_HEAP = 0 # use i16s in printf - + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.do_run_in_out_file_test('tests', 'core', 'test_sscanf_other_whitespace') def test_sscanf_3(self): From fa0f63160e8910c1cfd70cd16760d7af420937a2 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 28 Nov 2017 15:18:31 -0800 Subject: [PATCH 08/42] docs --- site/source/docs/getting_started/FAQ.rst | 2 +- site/source/docs/optimizing/Optimizing-Code.rst | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/site/source/docs/getting_started/FAQ.rst b/site/source/docs/getting_started/FAQ.rst index 1552175ef1e2b..467d0af6885c8 100644 --- a/site/source/docs/getting_started/FAQ.rst +++ b/site/source/docs/getting_started/FAQ.rst @@ -230,7 +230,7 @@ Another option is to define an ``onRuntimeInitialized`` function, Module['onRuntimeInitialized'] = function() { ... }; -That method will be called when the runtime is ready and it is ok for you to call compiled code. In practice, that is exactly the same time at which ``main()`` would be called, so ``onRuntimeInitialized`` doesn't let you do anything new, but it can be convenient in some cases - for example, if you use ``onRuntimeInitialized`` and don't define a ``main()`` function, then the runtime will not be shut down after ``main()`` exits, and you can keep calling compiled methods (you can also have a ``main()`` and build with ``-s NO_EXIT_RUNTIME=1`` to keep the runtime from being shut down). Thus, for libraries, ``onRuntimeInitialized`` can be convenient. +That method will be called when the runtime is ready and it is ok for you to call compiled code. In practice, that is exactly the same time at which ``main()`` would be called, so ``onRuntimeInitialized`` doesn't let you do anything new, but you can set it from JavaScript at runtime in a flexible way. Here is an example of how to use it: diff --git a/site/source/docs/optimizing/Optimizing-Code.rst b/site/source/docs/optimizing/Optimizing-Code.rst index be31b2e41841d..9f58b738f470f 100644 --- a/site/source/docs/optimizing/Optimizing-Code.rst +++ b/site/source/docs/optimizing/Optimizing-Code.rst @@ -38,18 +38,6 @@ Advanced compiler settings There are several flags you can :ref:`pass to the compiler ` to affect code generation, which will also affect performance — for example :ref:`DISABLE_EXCEPTION_CATCHING `. These are documented in `src/settings.js `_. Some of these will be directly affected by the optimization settings (you can find out which ones by searching for ``apply_opt_level`` in `tools/shared.py `_). -A few useful flags are: - -- - .. _optimizing-code-no-exit-runtime: - - ``NO_EXIT_RUNTIME``: Building with ``-s NO_EXIT_RUNTIME=1`` lets the compiler know that you don't want to shut down the runtime environment after the ``main()`` function finishes. This allows it to discard the ``atexit`` and global destructor calls it would otherwise make, improving code size and startup speed. - - This is useful if your ``main()`` function finishes but you still want to execute code, for example in an app that uses a :ref:`main loop function `. - - .. note:: Emscripten will not shut down the runtime if it detects :c:func:`emscripten_set_main_loop`, but it is better to optimise away the unnecessary code. - - Code size ========= From 9e299c5ca3b2721e6bcfc28578d24e547264398f Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 28 Nov 2017 15:18:35 -0800 Subject: [PATCH 09/42] fix --- tests/test_other.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_other.py b/tests/test_other.py index e67232dd76d22..8c1b480e2ce09 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -5840,7 +5840,7 @@ def test(main_args=[], library_args=[], expected='hello from main\nhello from li #include void library_func() { #ifdef USE_PRINTF - printf("hello from library: %p", (int)&library_func); + printf("hello from library: %p\n", (int)&library_func); #else puts("hello from library"); #endif From 890c5ea741f25edc4505b862082c02d9a663c1f8 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 28 Nov 2017 15:23:05 -0800 Subject: [PATCH 10/42] fix --- tests/test_core.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_core.py b/tests/test_core.py index 346d20c7587a1..ebbfae35a0c22 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1068,7 +1068,7 @@ def test_exceptions_white_list_2(self): def test_exceptions_uncaught(self): Settings.DISABLE_EXCEPTION_CATCHING = 0 - + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams src = r''' #include #include @@ -4142,6 +4142,7 @@ def clean(out, err): @sync def test_files_m(self): # Test for Module.stdin etc. + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams post = ''' def process(filename): @@ -5008,6 +5009,7 @@ def test_typeid(self): self.do_run_in_out_file_test('tests', 'core', 'test_typeid') def test_static_variable(self): + Settings.NO_EXIT_RUNTIME = 0 # needs atexit self.do_run_in_out_file_test('tests', 'core', 'test_static_variable') def test_fakestat(self): From bc3abe898baae08e993739b6765b3bf81384b7fa Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 28 Nov 2017 15:45:19 -0800 Subject: [PATCH 11/42] fix FORCE_FILESYSTEM: it actually never had any code to make it work directly, it just happened to work because other things included it --- src/library_fs.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/library_fs.js b/src/library_fs.js index 87e3cb11d9911..a7a4d7e3f2f9e 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -1919,3 +1919,7 @@ mergeInto(LibraryManager.library, { } }); +if (FORCE_FILESYSTEM) { + DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.push('$FS'); +} + From 1655df21ac086898f90e4314b1d769ceb8ea01bf Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 28 Nov 2017 15:51:52 -0800 Subject: [PATCH 12/42] fix --- tests/test_core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_core.py b/tests/test_core.py index ebbfae35a0c22..97b0a448cc8a1 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -531,6 +531,7 @@ def test_frexp(self): self.do_run_in_out_file_test('tests', 'core', 'test_frexp') def test_rounding(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams for precise_f32 in [0, 1]: print(precise_f32) Settings.PRECISE_F32 = precise_f32 From 268a781ca542370a64e7ed7972aa661e8da1b635 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 28 Nov 2017 15:58:41 -0800 Subject: [PATCH 13/42] fixes --- tests/test_core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_core.py b/tests/test_core.py index 97b0a448cc8a1..66ea00c34373d 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -497,6 +497,7 @@ def test_zerodiv(self): self.do_run_in_out_file_test('tests', 'core', 'test_zerodiv') def test_zero_multiplication(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.do_run_in_out_file_test('tests', 'core', 'test_zero_multiplication') def test_isnan(self): @@ -6481,7 +6482,7 @@ def test_embind_f_no_rtti(self): #include int main(int argc, char** argv){ - printf("418"); + printf("418\n"); return 0; } ''' From 1da089032d78f1e1d1225433f9066f845d7f54bb Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 28 Nov 2017 16:14:40 -0800 Subject: [PATCH 14/42] fix --- tests/test_core.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_core.py b/tests/test_core.py index 66ea00c34373d..da5296d66a3b0 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5741,6 +5741,8 @@ def test_lifetime(self): def test_cases(self): if Building.LLVM_OPTS: return self.skip("Our code is not exactly 'normal' llvm assembly") + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams + emcc_args = self.emcc_args # The following tests link to libc, and must be run with EMCC_LEAVE_INPUTS_RAW = 0 From b80bdf7b83a1778289f0e2457460392ceb2e7db1 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 28 Nov 2017 16:21:33 -0800 Subject: [PATCH 15/42] fix --- tests/test_core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_core.py b/tests/test_core.py index da5296d66a3b0..921eea778bba8 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -882,6 +882,7 @@ def test_setjmp_noleak(self): def test_exceptions(self): Settings.EXCEPTION_DEBUG = 1 + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams Settings.DISABLE_EXCEPTION_CATCHING = 0 if '-O2' in self.emcc_args: From 0f6e8fadad2d8f54b7a135b138c6db93ab7e7d19 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 28 Nov 2017 16:22:41 -0800 Subject: [PATCH 16/42] fix --- tests/test_core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_core.py b/tests/test_core.py index 921eea778bba8..03cd010eb42c5 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1319,7 +1319,8 @@ def test_rename(self): self.do_run_in_out_file_test('tests', 'stdio', 'test_rename', force_c=True) def test_remove(self): - self.do_run_in_out_file_test('tests', 'cstdio', 'test_remove') + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams + self.do_run_in_out_file_test('tests', 'cstdio', 'test_remove') def test_alloca_stack(self): self.do_run_in_out_file_test('tests', 'core', 'test_alloca_stack') From 2672778746930a113f26a4e946a30552c767c835 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 28 Nov 2017 16:26:21 -0800 Subject: [PATCH 17/42] better test --- tests/test_other.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tests/test_other.py b/tests/test_other.py index 8c1b480e2ce09..5685098aadef5 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -3239,17 +3239,23 @@ def test_no_exit_runtime_warnings_flush(self): int main(int argc, char **argv) { printf("hello\n"); printf("world"); // no newline, not flushed +#if FLUSH + printf("\n"); +#endif } ''') for no_exit in [0, 1]: for assertions in [0, 1]: - print(no_exit, assertions) - subprocess.check_call([PYTHON, EMCC, 'code.cpp', '-s', 'NO_EXIT_RUNTIME=%d' % no_exit, '-s', 'ASSERTIONS=%d' % assertions]) - output = run_js(os.path.join(self.get_dir(), 'a.out.js'), stderr=PIPE, full_output=True) - exit = 1-no_exit - assert 'hello' in output - assert ('world' in output) == exit, 'unflushed content is shown only when exiting the runtime' - assert (no_exit and assertions) == ('stdio streams had content in them that was not flushed. you should set NO_EXIT_RUNTIME to 0' in output), 'warning should be shown' + for flush in [0, 1]: + print(no_exit, assertions, flush) + cmd = [PYTHON, EMCC, 'code.cpp', '-s', 'NO_EXIT_RUNTIME=%d' % no_exit, '-s', 'ASSERTIONS=%d' % assertions] + if flush: cmd += ['-DFLUSH'] + subprocess.check_call(cmd) + output = run_js(os.path.join(self.get_dir(), 'a.out.js'), stderr=PIPE, full_output=True) + exit = 1-no_exit + assert 'hello' in output + assert ('world' in output) == (exit or flush), 'unflushed content is shown only when exiting the runtime' + assert (no_exit and assertions and not flush) == ('stdio streams had content in them that was not flushed. you should set NO_EXIT_RUNTIME to 0' in output), 'warning should be shown' def test_no_exit_runtime_warnings_atexit(self): open('code.cpp', 'w').write(r''' From 49a5234ddd4247c81ca8195ee364a3ae04711c25 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 28 Nov 2017 16:35:33 -0800 Subject: [PATCH 18/42] fixes --- tests/test_core.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index 03cd010eb42c5..4722771ff783a 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1132,6 +1132,7 @@ def test_exceptions_uncaught_2(self): def test_exceptions_typed(self): Settings.DISABLE_EXCEPTION_CATCHING = 0 + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.emcc_args += ['-s', 'SAFE_HEAP=0'] # Throwing null will cause an ignorable null pointer access. self.do_run_in_out_file_test('tests', 'core', 'test_exceptions_typed') @@ -3405,12 +3406,12 @@ def test_dylink_i64(self): ''', 'other says 175a1ddee82b8c31.') def test_dylink_i64_b(self): - self.dylink_test(''' + self.dylink_test(r''' #include #include extern int64_t sidey(); int main() { - printf("other says %lld.", sidey()); + printf("other says %lld.\n", sidey()); return 0; } ''', ''' @@ -6801,6 +6802,7 @@ def test_float_literals(self): @sync def test_exit_status(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams src = r''' #include #include From b8daf47ca13f46e6ae6bd92eab44220fe4c5eaa5 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 28 Nov 2017 21:16:03 -0800 Subject: [PATCH 19/42] fixes --- tests/test_other.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/test_other.py b/tests/test_other.py index 5685098aadef5..ad1066c4b2fc6 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -1601,7 +1601,7 @@ def test(link_cmd, lib_suffix=''): Popen(compiler + [os.path.join(self.get_dir(), 'main.cpp')] + link_cmd + ['-lother', '-c']).communicate() print('...') # The normal build system is over. We need to do an additional step to link in the dynamic libraries, since we ignored them before - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.o')] + link_cmd + ['-lother']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.o')] + link_cmd + ['-lother', '-s', 'NO_EXIT_RUNTIME=0']).communicate() self.assertContained('*hello from lib\n|hello from lib|\n*', run_js(os.path.join(self.get_dir(), 'a.out.js'))) @@ -3799,7 +3799,7 @@ def test_returncode(self): return 123; } ''') - Popen([PYTHON, EMCC, 'src.cpp']).communicate() + Popen([PYTHON, EMCC, 'src.cpp', '-s', 'NO_EXIT_RUNTIME=0']).communicate() for engine in JS_ENGINES: print(engine) process = Popen(engine + ['a.out.js'], stdout=PIPE, stderr=PIPE) @@ -4777,7 +4777,7 @@ def test_locale_wrong(self): } } ''') - Popen([PYTHON, EMCC, 'src.cpp']).communicate() + Popen([PYTHON, EMCC, 'src.cpp', '-s', 'NO_EXIT_RUNTIME=0']).communicate() self.assertContained('Constructed locale "C"\nThis locale is the global locale.\nThis locale is the C locale.', run_js('a.out.js', args=['C'])) self.assertContained('''Can't construct locale "waka": collate_byname::collate_byname failed to construct for waka''', run_js('a.out.js', args=['waka'], assert_returncode=1)) @@ -5324,7 +5324,7 @@ def test_link_with_a_static(self): Popen([PYTHON, EMAR, 'rc', 'libtest.a', 'y.o']).communicate() Popen([PYTHON, EMAR, 'rc', 'libtest.a', 'x.o']).communicate() Popen([PYTHON, EMRANLIB, 'libtest.a']).communicate() - Popen([PYTHON, EMCC, 'z.o', 'libtest.a'] + args).communicate() + Popen([PYTHON, EMCC, 'z.o', 'libtest.a', '-s', 'NO_EXIT_RUNTIME=0'] + args).communicate() out = run_js('a.out.js', assert_returncode=161) def test_link_with_bad_o_in_a(self): @@ -5341,7 +5341,7 @@ def test_link_with_bad_o_in_a(self): def test_require(self): inname = path_from_root('tests', 'hello_world.c') - Building.emcc(inname, output_filename='a.out.js') + Building.emcc(inname, args=['-s', 'ASSERTIONS=0'], output_filename='a.out.js') output = Popen(NODE_JS + ['-e', 'require("./a.out.js")'], stdout=PIPE, stderr=PIPE).communicate() assert output == ('hello, world!\n', ''), 'expected no output, got\n===\nSTDOUT\n%s\n===\nSTDERR\n%s\n===\n' % output @@ -5578,13 +5578,13 @@ def test_module_onexit(self): #include int main() { EM_ASM({ - Module.onExit = function(status) { Module.print('exiting now, status ' + status) }; + Module['onExit'] = function(status) { Module.print('exiting now, status ' + status) }; }); return 14; } ''') try_delete('a.out.js') - Popen([PYTHON, EMCC, 'src.cpp']).communicate() + Popen([PYTHON, EMCC, 'src.cpp', '-s', 'NO_EXIT_RUNTIME=0']).communicate() self.assertContained('exiting now, status 14', run_js('a.out.js', assert_returncode=14)) def test_underscore_exit(self): @@ -6282,7 +6282,7 @@ def test_realpath(self): perror("Resolve failed"); return 1; } else { - printf("Resolved: %s", t_realpath_buf); + printf("Resolved: %s\n", t_realpath_buf); free(t_realpath_buf); return 0; } @@ -6316,7 +6316,7 @@ def test_realpath_nodefs(self): perror("Resolve failed"); return 1; } else { - printf("Resolved: %s", t_realpath_buf); + printf("Resolved: %s\n", t_realpath_buf); free(t_realpath_buf); return 0; } @@ -7438,7 +7438,7 @@ def test_binaryen_names(self): try_delete('a.out.js') subprocess.check_call([PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp')] + args + ['-s', 'BINARYEN=1']) code = open('a.out.wasm', 'rb').read() - assert (b'__fflush_unlocked' in code) == expect_names, 'an internal function not exported nor imported must only appear in the binary if we have a names section' + assert (code.count(b'malloc') == 2) == expect_names, 'name section adds the name of malloc (there is also another one for the export' sizes[str(args)] = os.stat('a.out.wasm').st_size print(sizes) assert sizes["['-O2']"] < sizes["['-O2', '--profiling-funcs']"], 'when -profiling-funcs, the size increases due to function names' From ce7b4f429bcfaf59568b09f9a05063b1c86a7c97 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Wed, 29 Nov 2017 09:30:21 -0800 Subject: [PATCH 20/42] warn on exitin with a non-zero code when NO_EXIT_RUNTIME (as then we don't emit it) --- src/postamble.js | 57 +++++++++++++++++++++++++-------------------- tests/test_other.py | 27 +++++++++++++++------ 2 files changed, 52 insertions(+), 32 deletions(-) diff --git a/src/postamble.js b/src/postamble.js index 1ea828b4d9106..8510487e9253f 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -307,39 +307,46 @@ function run(args) { Module['run'] = Module.run = run; function exit(status, implicit) { - if (implicit && Module['noExitRuntime']) { #if ASSERTIONS - Module.printErr('exit(' + status + ') implicitly called by end of main(), but noExitRuntime, so not exiting the runtime (you can use emscripten_force_exit, if you want to force a true shutdown)'); - #if NO_EXIT_RUNTIME == 1 - // compiler settings do not allow exiting the runtime, so flushing - // the streams is not possible. but in ASSERTIONS mode we check - // if there was something to flush, and if so tell the user they - // should request that the runtime be exitable. - // how we flush the streams depends on whether we are in NO_FILESYSTEM - // mode (which has its own special function for this; otherwise, all - // the code is inside libc) + // if a non-default (0) returncode is provided, the user should set + // NO_EXIT_RUNTIME so we emit it + if (status !== 0) { + Module['printErr']('exiting with status ' + status + ' != 0; you should set NO_EXIT_RUNTIME=0 for full exit support'); + } + // compiler settings do not allow exiting the runtime, so flushing + // the streams is not possible. but in ASSERTIONS mode we check + // if there was something to flush, and if so tell the user they + // should request that the runtime be exitable. + // how we flush the streams depends on whether we are in NO_FILESYSTEM + // mode (which has its own special function for this; otherwise, all + // the code is inside libc) #if NO_FILESYSTEM - var flush = typeof flush_NO_FILESYSTEM === 'function' ? flush_NO_FILESYSTEM : null; + var flush = typeof flush_NO_FILESYSTEM === 'function' ? flush_NO_FILESYSTEM : null; #else - var flush = Module['_fflush']; + var flush = Module['_fflush']; #endif - if (flush) { - var print = Module['print']; - var printErr = Module['printErr']; - var has = false; - Module['print'] = Module['printErr'] = function(x) { - has = true; - } - flush(0); - Module['print'] = print; - Module['printErr'] = printErr; - if (has) { - Runtime.warnOnce('stdio streams had content in them that was not flushed. you should set NO_EXIT_RUNTIME to 0'); - } + if (flush) { + var print = Module['print']; + var printErr = Module['printErr']; + var has = false; + Module['print'] = Module['printErr'] = function(x) { + has = true; } + flush(0); + Module['print'] = print; + Module['printErr'] = printErr; + if (has) { + Runtime.warnOnce('stdio streams had content in them that was not flushed. you should set NO_EXIT_RUNTIME to 0'); + } + } #endif // NO_EXIT_RUNTIME #endif // ASSERTIONS + + if (implicit && Module['noExitRuntime']) { + // we may have warned about this earlier, if a situation justifies doing so. + // otherwise, we reached the end of main() (an implicit exit), and can just + // leave here. return; } diff --git a/tests/test_other.py b/tests/test_other.py index ad1066c4b2fc6..51ae1a601f531 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -3796,15 +3796,28 @@ def test_returncode(self): open('src.cpp', 'w').write(r''' #include int main() { - return 123; + return CODE; } ''') - Popen([PYTHON, EMCC, 'src.cpp', '-s', 'NO_EXIT_RUNTIME=0']).communicate() - for engine in JS_ENGINES: - print(engine) - process = Popen(engine + ['a.out.js'], stdout=PIPE, stderr=PIPE) - output = process.communicate() - assert process.returncode == 123, process.returncode + for code in [0, 123]: + for no_exit in [0, 1]: + subprocess.check_call([PYTHON, EMCC, 'src.cpp', '-DCODE=%d' % code, '-s', 'NO_EXIT_RUNTIME=%d' % no_exit]) + for engine in JS_ENGINES: + print(code, engine) + process = Popen(engine + ['a.out.js'], stdout=PIPE, stderr=PIPE) + out, err = process.communicate() + if no_exit == 0: + # if we exit the runtime, then code should be as expected, and output empty + assert process.returncode == code, [process.returncode, out, err] + assert not out, out + assert not err, err + else: + # otherwise, we don't exit, so we won't emit a non-zero code + assert process.returncode == 0, [process.returncode, out, err] + assert (('exiting with status %d != 0; you should set NO_EXIT_RUNTIME=0 for full exit support' % code) in err) == (code != 0), [out, err] + if code == 0: + assert not out, out + assert not err, err def test_mkdir_silly(self): open('src.cpp', 'w').write(r''' From 15a708326b68d134c05856c45342e05213fadcb2 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Wed, 29 Nov 2017 09:34:30 -0800 Subject: [PATCH 21/42] fixes --- emcc.py | 4 +++- tests/test_core.py | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/emcc.py b/emcc.py index cc82b66b4e90e..a6bd65ecc4d35 100755 --- a/emcc.py +++ b/emcc.py @@ -466,7 +466,9 @@ def filter_emscripten_options(argv): # The preprocessor define EMSCRIPTEN is deprecated. Don't pass it to code in strict mode. Code should use the define __EMSCRIPTEN__ instead. if not shared.Settings.STRICT: cmd += ['-DEMSCRIPTEN'] - if use_js: cmd += ['-s', 'ERROR_ON_UNDEFINED_SYMBOLS=1'] # configure tests should fail when an undefined symbol exists + if use_js: + cmd += ['-s', 'ERROR_ON_UNDEFINED_SYMBOLS=1'] # configure tests should fail when an undefined symbol exists + cmd += ['-s', 'NO_EXIT_RUNTIME=0'] # configure tests want a more shell-like style, where we emit return codes on exit() logging.debug('just configuring: ' + ' '.join(cmd)) if debug_configure: open(tempout, 'a').write('emcc, just configuring: ' + ' '.join(cmd) + '\n\n') diff --git a/tests/test_core.py b/tests/test_core.py index 4722771ff783a..a2308e7078d5b 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1241,6 +1241,7 @@ def test_isdigit_l(self): self.do_run_in_out_file_test('tests', 'core', 'test_isdigit_l') def test_iswdigit(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.do_run_in_out_file_test('tests', 'core', 'test_iswdigit') def test_polymorph(self): From 1af3ea8fe6926d51f70e0ff1ed1db763a378a04a Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Wed, 29 Nov 2017 10:48:34 -0800 Subject: [PATCH 22/42] if we exit with a non-zero code, emit it even if NO_EXIT_RUNTIME (useful for configure/cmake, and easy to do with no overhead). --- src/postamble.js | 25 ++++++++++++++----------- tests/test_other.py | 28 ++++++++++++++-------------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/postamble.js b/src/postamble.js index 8510487e9253f..32814b16bd225 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -309,11 +309,6 @@ Module['run'] = Module.run = run; function exit(status, implicit) { #if ASSERTIONS #if NO_EXIT_RUNTIME == 1 - // if a non-default (0) returncode is provided, the user should set - // NO_EXIT_RUNTIME so we emit it - if (status !== 0) { - Module['printErr']('exiting with status ' + status + ' != 0; you should set NO_EXIT_RUNTIME=0 for full exit support'); - } // compiler settings do not allow exiting the runtime, so flushing // the streams is not possible. but in ASSERTIONS mode we check // if there was something to flush, and if so tell the user they @@ -343,17 +338,25 @@ function exit(status, implicit) { #endif // NO_EXIT_RUNTIME #endif // ASSERTIONS - if (implicit && Module['noExitRuntime']) { - // we may have warned about this earlier, if a situation justifies doing so. - // otherwise, we reached the end of main() (an implicit exit), and can just - // leave here. + // if this is just main exit-ing implicitly, and the status is 0, then we + // don't need to do anything here and can just leave. if the status is + // non-zero, though, then we need to report it. + // (we may have warned about this earlier, if a situation justifies doing so) + if (implicit && Module['noExitRuntime'] && status === 0) { return; } if (Module['noExitRuntime']) { #if ASSERTIONS - Module.printErr('exit(' + status + ') called, but noExitRuntime, so halting execution but not exiting the runtime or preventing further async execution (you can use emscripten_force_exit, if you want to force a true shutdown)'); -#endif + // if exit() was called, we may warn the user if the runtime isn't actually being shut down + if (!implicit) { +#if NO_EXIT_RUNTIME + Module.printErr('exit(' + status + ') called, but compiled with NO_EXIT_RUNTIME, so halting execution but not exiting the runtime or preventing further async execution (build with NO_EXIT_RUNTIME=0, if you want a true shutdown)'); +#else + Module.printErr('exit(' + status + ') called, but noExitRuntime, so halting execution but not exiting the runtime or preventing further async execution (you can use emscripten_force_exit, if you want to force a true shutdown)'); +#endif // NO_EXIT_RUNTIME + } +#endif // ASSERTIONS } else { #if USE_PTHREADS PThread.terminateAllThreads(); diff --git a/tests/test_other.py b/tests/test_other.py index 51ae1a601f531..3f3dc7e9a4b55 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -3795,29 +3795,29 @@ def test_argv0_node(self): def test_returncode(self): open('src.cpp', 'w').write(r''' #include + #include int main() { + #if CALL_EXIT + exit(CODE); + #else return CODE; + #endif } ''') for code in [0, 123]: for no_exit in [0, 1]: - subprocess.check_call([PYTHON, EMCC, 'src.cpp', '-DCODE=%d' % code, '-s', 'NO_EXIT_RUNTIME=%d' % no_exit]) - for engine in JS_ENGINES: - print(code, engine) - process = Popen(engine + ['a.out.js'], stdout=PIPE, stderr=PIPE) - out, err = process.communicate() - if no_exit == 0: - # if we exit the runtime, then code should be as expected, and output empty + for call_exit in [0, 1]: + subprocess.check_call([PYTHON, EMCC, 'src.cpp', '-DCODE=%d' % code, '-s', 'NO_EXIT_RUNTIME=%d' % no_exit, '-DCALL_EXIT=%d' % call_exit]) + for engine in JS_ENGINES: + print(code, no_exit, call_exit, engine) + process = Popen(engine + ['a.out.js'], stdout=PIPE, stderr=PIPE) + out, err = process.communicate() + # we always emit the right exit code, whether we exit the runtime or not assert process.returncode == code, [process.returncode, out, err] assert not out, out - assert not err, err - else: - # otherwise, we don't exit, so we won't emit a non-zero code - assert process.returncode == 0, [process.returncode, out, err] - assert (('exiting with status %d != 0; you should set NO_EXIT_RUNTIME=0 for full exit support' % code) in err) == (code != 0), [out, err] - if code == 0: - assert not out, out + if not call_exit: assert not err, err + assert ('compiled with NO_EXIT_RUNTIME, so halting execution but not exiting the runtime or preventing further async execution (build with NO_EXIT_RUNTIME=0, if you want a true shutdown)' in err) == (no_exit and call_exit), err def test_mkdir_silly(self): open('src.cpp', 'w').write(r''' From 04ce11d99b73d6edfa5ccb2a75c4efd2d098a7ec Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Wed, 29 Nov 2017 10:59:58 -0800 Subject: [PATCH 23/42] fixes --- tests/test_core.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index a2308e7078d5b..b7d247c9b686b 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -181,6 +181,7 @@ def test_i32_mul_precise(self): self.do_run_in_out_file_test('tests', 'core', 'test_i32_mul_precise') def test_i16_emcc_intrinsic(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.do_run_in_out_file_test('tests', 'core', 'test_i16_emcc_intrinsic') def test_double_i64_conversion(self): @@ -1545,6 +1546,7 @@ def test_emscripten_run_script_string_utf8(self): def test_emscripten_get_now(self): self.banned_js_engines = [V8_ENGINE] # timer limitations in v8 shell + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams if self.run_name == 'asm2': self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage @@ -2148,6 +2150,7 @@ def test_strptime_days(self): self.do_run_in_out_file_test('tests', 'core', 'test_strptime_days') def test_strptime_reentrant(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.do_run_in_out_file_test('tests', 'core', 'test_strptime_reentrant') def test_strftime(self): @@ -3160,7 +3163,7 @@ def test(): #include extern int sidey(); int main() { - printf("other says %d.", sidey()); + printf("other says %d.\\n", sidey()); return 0; } ''', ''' @@ -3178,7 +3181,7 @@ def test_dylink_floats(self): #include extern float sidey(); int main() { - printf("other says %.2f.", sidey()+1); + printf("other says %.2f.\\n", sidey()+1); return 0; } ''', ''' @@ -3634,7 +3637,7 @@ def test(syslibs, expect_pass=True, need_reverse=True): } ''', side=r''' #include - void side() { std::cout << "cout hello from side"; } + void side() { std::cout << "cout hello from side\n"; } ''', expected=['cout hello from side\n'], need_reverse=need_reverse) except Exception as e: @@ -4451,6 +4454,7 @@ def test_wprintf(self): self.do_run_from_file(src, output) def test_direct_string_constant_usage(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.do_run_in_out_file_test('tests', 'core', 'test_direct_string_constant_usage') def test_std_cout_new(self): @@ -6862,6 +6866,7 @@ def test_vswprintf_utf8(self): self.do_run_from_file(path_from_root('tests', 'vswprintf_utf8.c'), path_from_root('tests', 'vswprintf_utf8.out')) def test_async(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.banned_js_engines = [SPIDERMONKEY_ENGINE, V8_ENGINE] # needs setTimeout which only node has src = r''' @@ -7005,6 +7010,7 @@ def test_async_exit(self): ''', 'f\nhello\nf\nhello\nf\nhello\nf\nhello\nf\nhello\nexit\n') def test_coroutine(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams src = r''' #include #include From 6ac52fb1b0b5485ed7cb655a8f0383bca476453f Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Wed, 29 Nov 2017 11:33:22 -0800 Subject: [PATCH 24/42] fix flush assertion for c++ code, add testing --- src/postamble.js | 4 ++-- tests/test_core.py | 2 ++ tests/test_other.py | 40 +++++++++++++++++++++++++++------------- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/postamble.js b/src/postamble.js index 32814b16bd225..9118d23976921 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -317,9 +317,9 @@ function exit(status, implicit) { // mode (which has its own special function for this; otherwise, all // the code is inside libc) #if NO_FILESYSTEM - var flush = typeof flush_NO_FILESYSTEM === 'function' ? flush_NO_FILESYSTEM : null; + var flush = {{{ '$flush_NO_FILESYSTEM' in addedLibraryItems ? 'flush_NO_FILESYSTEM' : 'null' }}}; #else - var flush = Module['_fflush']; + var flush = {{{ '$FS' in addedLibraryItems ? 'FS.quit' : "Module['_fflush']" }}}; #endif if (flush) { var print = Module['print']; diff --git a/tests/test_core.py b/tests/test_core.py index b7d247c9b686b..7c12ead5c7e1b 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -147,6 +147,7 @@ def test_i64_zextneg(self): self.do_run_in_out_file_test('tests', 'core', 'test_i64_zextneg') def test_i64_7z(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.do_run_in_out_file_test('tests', 'core', 'test_i64_7z', args=['hallo']) @@ -4461,6 +4462,7 @@ def test_std_cout_new(self): self.do_run_in_out_file_test('tests', 'core', 'test_std_cout_new') def test_istream(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams test_path = path_from_root('tests', 'core', 'test_istream') src, output = (test_path + s for s in ('.c', '.out')) diff --git a/tests/test_other.py b/tests/test_other.py index 3f3dc7e9a4b55..3442c123e39a3 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -3234,7 +3234,8 @@ def test_no_exit_runtime(self): assert ('atexit(' in src) == exit, 'atexit should not appear or be called' def test_no_exit_runtime_warnings_flush(self): - open('code.cpp', 'w').write(r''' + # check we warn if there is unflushed info + open('code.c', 'w').write(r''' #include int main(int argc, char **argv) { printf("hello\n"); @@ -3244,18 +3245,31 @@ def test_no_exit_runtime_warnings_flush(self): #endif } ''') - for no_exit in [0, 1]: - for assertions in [0, 1]: - for flush in [0, 1]: - print(no_exit, assertions, flush) - cmd = [PYTHON, EMCC, 'code.cpp', '-s', 'NO_EXIT_RUNTIME=%d' % no_exit, '-s', 'ASSERTIONS=%d' % assertions] - if flush: cmd += ['-DFLUSH'] - subprocess.check_call(cmd) - output = run_js(os.path.join(self.get_dir(), 'a.out.js'), stderr=PIPE, full_output=True) - exit = 1-no_exit - assert 'hello' in output - assert ('world' in output) == (exit or flush), 'unflushed content is shown only when exiting the runtime' - assert (no_exit and assertions and not flush) == ('stdio streams had content in them that was not flushed. you should set NO_EXIT_RUNTIME to 0' in output), 'warning should be shown' + open('code.cpp', 'w').write(r''' +#include +int main() { + using namespace std; + cout << "hello" << std::endl; + cout << "world"; // no newline, not flushed +#if FLUSH + std::cout << std::endl; +#endif +} +''') + for src in ['code.c', 'code.cpp']: + for no_exit in [0, 1]: + for assertions in [0, 1]: + for flush in [0, 1]: + # TODO: also check NO_FILESYSTEM here. it never worked though, buffered output was not emitted at shutdown + print(src, no_exit, assertions, flush) + cmd = [PYTHON, EMCC, src, '-s', 'NO_EXIT_RUNTIME=%d' % no_exit, '-s', 'ASSERTIONS=%d' % assertions] + if flush: cmd += ['-DFLUSH'] + subprocess.check_call(cmd) + output = run_js(os.path.join(self.get_dir(), 'a.out.js'), stderr=PIPE, full_output=True) + exit = 1-no_exit + assert 'hello' in output, output + assert ('world' in output) == (exit or flush), 'unflushed content is shown only when exiting the runtime' + assert (no_exit and assertions and not flush) == ('stdio streams had content in them that was not flushed. you should set NO_EXIT_RUNTIME to 0' in output), 'warning should be shown' def test_no_exit_runtime_warnings_atexit(self): open('code.cpp', 'w').write(r''' From 748f2eb89fdc6efe1b403c62108cc92fba67cabe Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Wed, 29 Nov 2017 11:38:07 -0800 Subject: [PATCH 25/42] fixes --- tests/test_core.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/test_core.py b/tests/test_core.py index 7c12ead5c7e1b..78840f2e7de41 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -2049,6 +2049,7 @@ def test_structbyval(self): def test_stdlibs(self): # safe heap prints a warning that messes up our output. Settings.SAFE_HEAP = 0 + Settings.NO_EXIT_RUNTIME = 0 # needs atexit self.do_run_in_out_file_test('tests', 'core', 'test_stdlibs') def test_stdbool(self): @@ -3862,7 +3863,7 @@ def test_random(self): int main() { srandom(0xdeadbeef); - printf("%ld", random()); + printf("%ld\n", random()); } ''' self.do_run(src, '956867869') @@ -3959,6 +3960,7 @@ def test_transtrcase(self): def test_printf(self): self.banned_js_engines = [NODE_JS, V8_ENGINE] # SpiderMonkey and V8 do different things to float64 typed arrays, un-NaNing, etc. + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams src = open(path_from_root('tests', 'printf', 'test.c'), 'r').read() expected = open(path_from_root('tests', 'printf', 'output.txt'), 'r').read() self.do_run(src, expected) @@ -4062,6 +4064,7 @@ def test_sscanf_n(self): self.do_run_in_out_file_test('tests', 'core', 'test_sscanf_n') def test_sscanf_whitespace(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.do_run_in_out_file_test('tests', 'core', 'test_sscanf_whitespace') def test_sscanf_other_whitespace(self): @@ -5029,6 +5032,7 @@ def test_fakestat(self): def test_mmap(self): Settings.TOTAL_MEMORY = 128*1024*1024 + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams test_path = path_from_root('tests', 'core', 'test_mmap') src, output = (test_path + s for s in ('.c', '.out')) @@ -5213,6 +5217,7 @@ def test_simd2(self): @SIMD def test_simd3(self): Settings.PRECISE_F32 = 1 # SIMD currently requires Math.fround + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.emcc_args = self.emcc_args + ['-msse2'] test_path = path_from_root('tests', 'core', 'test_simd3') src, output = (test_path + s for s in ('.c', '.out')) @@ -5233,6 +5238,7 @@ def test_simd5(self): @SIMD def test_simd6(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams # test_simd6 is to test x86 min and max intrinsics on NaN and -0.0 self.emcc_args = self.emcc_args + ['-msse'] test_path = path_from_root('tests', 'core', 'test_simd6') @@ -5295,6 +5301,7 @@ def test_simd13(self): @SIMD def test_simd14(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.emcc_args = self.emcc_args + ['-msse', '-msse2'] test_path = path_from_root('tests', 'core', 'test_simd14') src, output = (test_path + s for s in ('.c', '.out')) From 061166180b767958e8ca9c7fafd54d4fd1e6740d Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Wed, 29 Nov 2017 13:54:29 -0800 Subject: [PATCH 26/42] update Changelog --- ChangeLog.markdown | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ChangeLog.markdown b/ChangeLog.markdown index 8c10b374fb5f6..b6228ee5b439b 100644 --- a/ChangeLog.markdown +++ b/ChangeLog.markdown @@ -7,7 +7,11 @@ To browse or download snapshots of old tagged versions, visit https://github.com Not all changes are documented here. In particular, new features, user-oriented fixes, options, command-line parameters, usage changes, deprecations, significant internal modifications and optimizations etc. generally deserve a mention. To examine the full set of changes between versions, visit the link to full changeset diff at the end of each section. -Current trunk code +Current Trunk +------------- + - Breaking change: Change `NO_EXIT_RUNTIME` to 1 by default. This means that by default we don't include code to shut down the runtime, flush stdio streams, run atexits, etc., which is better for code size. When `ASSERTIONS` is on, we warn at runtime if there is text buffered in the streams that should be flushed, or atexits are used. + +v1.37.17: 7/25/2017 ------------------ - Updated to libc++'s "v2" ABI, which provides better alignment for string data and other improvements. This is an ABI-incompatible change, so bitcode files from previous versions will not be compatible. - To see a list of commits in the active development branch 'incoming', which have not yet been packaged in a release, see From 096f7ec51d7558f809c40f35ccb69330685d8968 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Wed, 29 Nov 2017 13:55:03 -0800 Subject: [PATCH 27/42] fix --- tests/test_core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_core.py b/tests/test_core.py index 78840f2e7de41..29511f24dfb14 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1240,6 +1240,7 @@ def test_inherit(self): self.do_run_in_out_file_test('tests', 'core', 'test_inherit') def test_isdigit_l(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.do_run_in_out_file_test('tests', 'core', 'test_isdigit_l') def test_iswdigit(self): From e040af1f66947ef298344012a8d632e95e972063 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 29 Nov 2017 16:53:56 -0800 Subject: [PATCH 28/42] fix browser.test_asm_swapping --- tests/test_browser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_browser.py b/tests/test_browser.py index 8a86bd74af47a..19aa663b9ed52 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -2462,7 +2462,7 @@ def test_glfw_events(self): def test_asm_swapping(self): self.clear() open('run.js', 'w').write(r''' -Module['_main'] = function() { +Module['onRuntimeInitialized'] = function() { // test proper initial result var result = Module._func(); console.log('first: ' + result); From 39ef9445bff138685456423eaa0be05636816423 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 29 Nov 2017 16:56:29 -0800 Subject: [PATCH 29/42] fix browser.test_emterpreter_async_sleep2_safeheap --- src/postamble.js | 4 +++- tests/test_browser.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/postamble.js b/src/postamble.js index eb9afce6dd93f..0099d881cfe6b 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -332,7 +332,9 @@ function exit(status, implicit) { Module['print'] = Module['printErr'] = function(x) { has = true; } - flush(0); + try { // it doesn't matter if it fails + flush(0); + } catch(e) {} Module['print'] = print; Module['printErr'] = printErr; if (has) { diff --git a/tests/test_browser.py b/tests/test_browser.py index 19aa663b9ed52..0ce604d7f569f 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -2897,7 +2897,7 @@ def test_emterpreter_async_sleep2(self): def test_emterpreter_async_sleep2_safeheap(self): # check that safe-heap machinery does not cause errors in async operations - self.btest('emterpreter_async_sleep2_safeheap.cpp', '17', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-Oz', '-profiling', '-s', 'SAFE_HEAP=1', '-s', 'ASSERTIONS=1', '-s', 'EMTERPRETIFY_WHITELIST=["_main","_callback","_fix"]']) + self.btest('emterpreter_async_sleep2_safeheap.cpp', '17', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-Oz', '-profiling', '-s', 'SAFE_HEAP=1', '-s', 'ASSERTIONS=1', '-s', 'EMTERPRETIFY_WHITELIST=["_main","_callback","_fix"]', '-s', 'NO_EXIT_RUNTIME=0']) def test_sdl_audio_beep_sleep(self): self.btest('sdl_audio_beep_sleep.cpp', '1', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-Os', '-s', 'ASSERTIONS=1', '-s', 'DISABLE_EXCEPTION_CATCHING=0', '-profiling', '-s', 'SAFE_HEAP=1', '-lSDL'], timeout=60) From e6e1bc432adf95785808b012aa9e758dad995bb4 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 29 Nov 2017 17:15:50 -0800 Subject: [PATCH 30/42] fixes --- tests/test_browser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_browser.py b/tests/test_browser.py index 0ce604d7f569f..fa5fe1e91f059 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -1244,14 +1244,14 @@ def test_fs_idbfs_fsync(self): }; ''') - args = ['--pre-js', 'pre.js', '-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-lidbfs.js'] + args = ['--pre-js', 'pre.js', '-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-lidbfs.js', '-s', 'NO_EXIT_RUNTIME=0'] for mode in [[], ['-s', 'MEMFS_APPEND_TO_TYPED_ARRAYS=1']]: secret = str(time.time()) self.btest(path_from_root('tests', 'fs', 'test_idbfs_fsync.c'), '1', force_c=True, args=args + mode + ['-DFIRST', '-DSECRET=\"' + secret + '\"', '-s', '''EXPORTED_FUNCTIONS=['_main', '_success']''']) self.btest(path_from_root('tests', 'fs', 'test_idbfs_fsync.c'), '1', force_c=True, args=args + mode + ['-DSECRET=\"' + secret + '\"', '-s', '''EXPORTED_FUNCTIONS=['_main', '_success']''']) def test_fs_memfs_fsync(self): - args = ['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1']; + args = ['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-s', 'NO_EXIT_RUNTIME=0']; for mode in [[], ['-s', 'MEMFS_APPEND_TO_TYPED_ARRAYS=1']]: secret = str(time.time()) self.btest(path_from_root('tests', 'fs', 'test_memfs_fsync.c'), '1', force_c=True, args=args + mode + ['-DSECRET=\"' + secret + '\"', '-s', '''EXPORTED_FUNCTIONS=['_main']''']) From f04b0cdf4956ebe57b852fcd3da95c1b5c6ef5ef Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 29 Nov 2017 17:16:27 -0800 Subject: [PATCH 31/42] always exit pthreads: if we do not, the main thread simply hangs on them. this was always broken it seems, but became noticable with NO_EXIT_RUNTIME=1 by default --- src/pthread-main.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pthread-main.js b/src/pthread-main.js index 1a53cf108c330..6d75bc336e2aa 100644 --- a/src/pthread-main.js +++ b/src/pthread-main.js @@ -132,8 +132,7 @@ this.onmessage = function(e) { } // The thread might have finished without calling pthread_exit(). If so, then perform the exit operation ourselves. // (This is a no-op if explicit pthread_exit() had been called prior.) - if (!Module['noExitRuntime']) PThread.threadExit(result); - else console.log('pthread noExitRuntime: not quitting.'); + PThread.threadExit(result); } else if (e.data.cmd === 'cancel') { // Main thread is asking for a pthread_cancel() on this thread. if (threadInfoStruct && PThread.thisThreadCancelState == 0/*PTHREAD_CANCEL_ENABLE*/) { PThread.threadCancel(); From 5e81d112c8d706bf109a7e966b17f41f116f7610 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 29 Nov 2017 17:23:44 -0800 Subject: [PATCH 32/42] emrun sets NO_EXIT_RUNTIME=0. it is a mode where we specifically want to get the exit code from the running program, as if it were a shell command, not a browser app --- emcc.py | 2 ++ site/source/docs/tools_reference/emcc.rst | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/emcc.py b/emcc.py index a6bd65ecc4d35..c7d5e83810476 100755 --- a/emcc.py +++ b/emcc.py @@ -661,6 +661,8 @@ def detect_fixed_language_mode(args): if options.emrun: options.pre_js += open(shared.path_from_root('src', 'emrun_prejs.js')).read() + '\n' options.post_js += open(shared.path_from_root('src', 'emrun_postjs.js')).read() + '\n' + # emrun mode waits on program exit + shared.Settings.NO_EXIT_RUNTIME = 0 if options.cpu_profiler: options.post_js += open(shared.path_from_root('src', 'cpuprofiler.js')).read() + '\n' diff --git a/site/source/docs/tools_reference/emcc.rst b/site/source/docs/tools_reference/emcc.rst index ae3f8bcaece94..cc29232f88e2f 100644 --- a/site/source/docs/tools_reference/emcc.rst +++ b/site/source/docs/tools_reference/emcc.rst @@ -405,7 +405,7 @@ Options that are modified or new in *emcc* are listed below: .. _emcc-emrun: ``--emrun`` - Enables the generated output to be aware of the :ref:`emrun ` command line tool. This allows ``stdout``, ``stderr`` and ``exit(returncode)`` capture when running the generated application through *emrun*. + Enables the generated output to be aware of the :ref:`emrun ` command line tool. This allows ``stdout``, ``stderr`` and ``exit(returncode)`` capture when running the generated application through *emrun*. (This enables `NO_EXIT_RUNTIME=0`, allowing normal runtime exiting with return code passing.) ``--cpuprofiler`` Embeds a simple CPU profiler onto the generated page. Use this to perform cursory interactive performance profiling. From 72e7255cfb143a580722cde217579d1734f3c9e3 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 30 Nov 2017 16:44:25 -0800 Subject: [PATCH 33/42] add faq entry, and mention the faq --- site/source/docs/getting_started/FAQ.rst | 21 +++++++++++++++++++++ src/library.js | 2 +- src/postamble.js | 2 +- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/site/source/docs/getting_started/FAQ.rst b/site/source/docs/getting_started/FAQ.rst index 467d0af6885c8..a8c936cc0152b 100644 --- a/site/source/docs/getting_started/FAQ.rst +++ b/site/source/docs/getting_started/FAQ.rst @@ -248,6 +248,27 @@ Here is an example of how to use it: The crucial thing is that ``Module`` exists, and has the property ``onRuntimeInitialized``, before the script containing emscripten output (``my_project.js`` in this example) is loaded. +.. _faq-NO_EXIT_RUNTIME: + +What does "exiting the runtime" mean? Why don't ``atexit()s`` run? +================================================================== + +(You may need this answer if you see an error saying something like ``atexit() called, but NO_EXIT_RUNTIME`` or ``stdio streams had content in them that was not flushed. you should set NO_EXIT_RUNTIME to 0``.) + +By default Emscripten sets ``NO_EXIT_RUNTIME=1``, which means that we don't include code to shut down the runtime. That means that when ``main()`` exits, we don't flush the stdio streams, or call the destructors of global C++ objects, or call ``atexit`` callbacks. This lets us emit smaller code by default, and is normally what you want on the web: even though ``main()`` exited, you may have something asynchronous happening later that you want to execute. + +In some cases, though, you may want a more "commandline" experience, where we do shut down the runtime when ``main()`` exits. You can build with ``-s NO_EXIT_RUNTIME=0``, and then we will call ``atexits`` and so forth. When you build with ``ASSERTIONS``, you should get a warning when you need this. For example, if your program prints something without a newline, + +:: + + #include + + int main() { + printf("hello"); // note no newline + } + +If we don't shut down the runtime and flush the stdio streams, "hello" won't be printed. In an ``ASSERTIONS`` build you'll get a notification saying ``stdio streams had content in them that was not flushed. you should set NO_EXIT_RUNTIME to 0``. + .. _faq-dead-code-elimination: Why do functions in my C/C++ source code vanish when I compile to JavaScript, and/or I get ``No functions to process``? diff --git a/src/library.js b/src/library.js index b956f6e1febda..cd1aa877147ba 100644 --- a/src/library.js +++ b/src/library.js @@ -598,7 +598,7 @@ LibraryManager.library = { atexit: function(func, arg) { #if ASSERTIONS #if NO_EXIT_RUNTIME == 1 - Runtime.warnOnce('atexit() called, but NO_EXIT_RUNTIME, so atexits() will not be called. set NO_EXIT_RUNTIME to 0'); + Runtime.warnOnce('atexit() called, but NO_EXIT_RUNTIME, so atexits() will not be called. set NO_EXIT_RUNTIME to 0 (see the FAQ)'); #endif #endif __ATEXIT__.unshift({ func: func, arg: arg }); diff --git a/src/postamble.js b/src/postamble.js index 0099d881cfe6b..1dfd5f4e8c388 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -338,7 +338,7 @@ function exit(status, implicit) { Module['print'] = print; Module['printErr'] = printErr; if (has) { - Runtime.warnOnce('stdio streams had content in them that was not flushed. you should set NO_EXIT_RUNTIME to 0'); + Runtime.warnOnce('stdio streams had content in them that was not flushed. you should set NO_EXIT_RUNTIME to 0 (see the FAQ)'); } } #endif // NO_EXIT_RUNTIME From 4dc9381b2bade4e696c1e59ba62a9b0ac9b629cb Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 30 Nov 2017 16:55:04 -0800 Subject: [PATCH 34/42] explain flushing check --- src/postamble.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/postamble.js b/src/postamble.js index 1dfd5f4e8c388..993bf39d3ae4e 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -313,11 +313,15 @@ Module['run'] = run; function exit(status, implicit) { #if ASSERTIONS #if NO_EXIT_RUNTIME == 1 - // compiler settings do not allow exiting the runtime, so flushing + // Compiler settings do not allow exiting the runtime, so flushing // the streams is not possible. but in ASSERTIONS mode we check // if there was something to flush, and if so tell the user they // should request that the runtime be exitable. - // how we flush the streams depends on whether we are in NO_FILESYSTEM + // Normally we would not even include flush() at all, but in ASSERTIONS + // builds we do so just for this check, and here we see if there is any + // content to flush, that is, we check if there would have been + // something a non-ASSERTIONS build would have not seen. + // How we flush the streams depends on whether we are in NO_FILESYSTEM // mode (which has its own special function for this; otherwise, all // the code is inside libc) #if NO_FILESYSTEM From 6bf8d53dfdc203d021e954cef9fd01d627df444b Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Thu, 30 Nov 2017 20:09:42 -0800 Subject: [PATCH 35/42] fix an existing emterpreter-async bug: if we are unwinding the stack as we leave main(), then do not call exit, we are not exiting yet - code is yet to run later --- src/postamble.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/postamble.js b/src/postamble.js index 993bf39d3ae4e..9b5995044bc85 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -210,8 +210,16 @@ Module['callMain'] = function callMain(args) { Module.realPrint('main() took ' + (Date.now() - start) + ' milliseconds'); #endif +#if EMTERPRETIFY_ASYNC + // if we are saving the stack, then do not call exit, we are not + // really exiting now, just unwinding the JS stack + if (EmterpreterAsync.state !== 1) { +#endif // EMTERPRETIFY_ASYNC // if we're not running an evented main loop, it's time to exit - exit(ret, /* implicit = */ true); + exit(ret, /* implicit = */ true); +#if EMTERPRETIFY_ASYNC + } +#endif // EMTERPRETIFY_ASYNC } catch(e) { if (e instanceof ExitStatus) { From 195a99cced897521648e8693a23dbd3fd7f5f559 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Mon, 4 Dec 2017 17:06:02 -0800 Subject: [PATCH 36/42] improve text --- src/library.js | 2 +- src/postamble.js | 4 ++-- tests/test_other.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/library.js b/src/library.js index cd1aa877147ba..d6de1ed5f0e42 100644 --- a/src/library.js +++ b/src/library.js @@ -598,7 +598,7 @@ LibraryManager.library = { atexit: function(func, arg) { #if ASSERTIONS #if NO_EXIT_RUNTIME == 1 - Runtime.warnOnce('atexit() called, but NO_EXIT_RUNTIME, so atexits() will not be called. set NO_EXIT_RUNTIME to 0 (see the FAQ)'); + Runtime.warnOnce('atexit() called, but NO_EXIT_RUNTIME is set, so atexits() will not be called. set NO_EXIT_RUNTIME to 0 (see the FAQ)'); #endif #endif __ATEXIT__.unshift({ func: func, arg: arg }); diff --git a/src/postamble.js b/src/postamble.js index 9b5995044bc85..5be518dd15c1a 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -369,9 +369,9 @@ function exit(status, implicit) { // if exit() was called, we may warn the user if the runtime isn't actually being shut down if (!implicit) { #if NO_EXIT_RUNTIME - Module.printErr('exit(' + status + ') called, but compiled with NO_EXIT_RUNTIME, so halting execution but not exiting the runtime or preventing further async execution (build with NO_EXIT_RUNTIME=0, if you want a true shutdown)'); + Module.printErr('exit(' + status + ') called, but NO_EXIT_RUNTIME is set, so halting execution but not exiting the runtime or preventing further async execution (build with NO_EXIT_RUNTIME=0, if you want a true shutdown)'); #else - Module.printErr('exit(' + status + ') called, but noExitRuntime, so halting execution but not exiting the runtime or preventing further async execution (you can use emscripten_force_exit, if you want to force a true shutdown)'); + Module.printErr('exit(' + status + ') called, but noExitRuntime is set due to an async operation, so halting execution but not exiting the runtime or preventing further async execution (you can use emscripten_force_exit, if you want to force a true shutdown)'); #endif // NO_EXIT_RUNTIME } #endif // ASSERTIONS diff --git a/tests/test_other.py b/tests/test_other.py index 884142f2de1ed..74e4b8c236b2a 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -3285,7 +3285,7 @@ def test_no_exit_runtime_warnings_atexit(self): subprocess.check_call([PYTHON, EMCC, 'code.cpp', '-s', 'NO_EXIT_RUNTIME=%d' % no_exit, '-s', 'ASSERTIONS=%d' % assertions]) output = run_js(os.path.join(self.get_dir(), 'a.out.js'), stderr=PIPE, full_output=True) exit = 1-no_exit - assert (no_exit and assertions) == ('atexit() called, but NO_EXIT_RUNTIME, so atexits() will not be called. set NO_EXIT_RUNTIME to 0' in output), 'warning should be shown' + assert (no_exit and assertions) == ('atexit() called, but NO_EXIT_RUNTIME is set, so atexits() will not be called. set NO_EXIT_RUNTIME to 0' in output), 'warning should be shown' def test_os_oz(self): if os.environ.get('EMCC_DEBUG'): return self.skip('cannot run in debug mode') @@ -3831,7 +3831,7 @@ def test_returncode(self): assert not out, out if not call_exit: assert not err, err - assert ('compiled with NO_EXIT_RUNTIME, so halting execution but not exiting the runtime or preventing further async execution (build with NO_EXIT_RUNTIME=0, if you want a true shutdown)' in err) == (no_exit and call_exit), err + assert ('but NO_EXIT_RUNTIME is set, so halting execution but not exiting the runtime or preventing further async execution (build with NO_EXIT_RUNTIME=0, if you want a true shutdown)' in err) == (no_exit and call_exit), err def test_mkdir_silly(self): open('src.cpp', 'w').write(r''' From be26e6b43bfc5551dbaeba7816c2ce3473dad389 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Mon, 4 Dec 2017 17:24:02 -0800 Subject: [PATCH 37/42] warn on calling emscripten_force_exit when NO_EXIT_RUNTIME --- site/source/docs/api_reference/emscripten.h.rst | 2 ++ src/library_browser.js | 5 +++++ tests/test_other.py | 16 ++++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/site/source/docs/api_reference/emscripten.h.rst b/site/source/docs/api_reference/emscripten.h.rst index cfc1df0074f8b..b54d41a9d2132 100644 --- a/site/source/docs/api_reference/emscripten.h.rst +++ b/site/source/docs/api_reference/emscripten.h.rst @@ -334,6 +334,8 @@ Functions The difference is that ``emscripten_force_exit`` will shut down the runtime even if you previously called :c:func:`emscripten_exit_with_live_runtime` or otherwise kept the runtime alive. In other words, this method gives you the option to completely shut down the runtime after it was kept alive beyond the completion of ``main()``. + Note that if ``NO_EXIT_RUNTIME`` is set (which it is by default) then the runtime cannot be shut down, as we do not include the code to do so. Build with ``-s NO_EXIT_RUNTIME=0`` if you want to be able to exit the runtime. + :param int status: The same as for the *libc* function `exit() `_. .. c:function:: double emscripten_get_device_pixel_ratio(void) diff --git a/src/library_browser.js b/src/library_browser.js index dbca0a916df3f..298982a9bbe5c 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -1270,6 +1270,11 @@ var LibraryBrowser = { emscripten_force_exit__proxy: 'sync', emscripten_force_exit__sig: 'vi', emscripten_force_exit: function(status) { +#if NO_EXIT_RUNTIME +#if ASSERTIONS + Runtime.warnOnce('emscripten_force_exit cannot actually shut down the runtime, as the build has NO_EXIT_RUNTIME set'); +#endif +#endif Module['noExitRuntime'] = false; Module['exit'](status); }, diff --git a/tests/test_other.py b/tests/test_other.py index 9c19951d2953c..e55a06339cf24 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -3844,6 +3844,22 @@ def test_returncode(self): assert not err, err assert ('but NO_EXIT_RUNTIME is set, so halting execution but not exiting the runtime or preventing further async execution (build with NO_EXIT_RUNTIME=0, if you want a true shutdown)' in err) == (no_exit and call_exit), err + def test_emscripten_force_exit_NO_EXIT_RUNTIME(self): + open('src.cpp', 'w').write(r''' + #include + int main() { + #if CALL_EXIT + emscripten_force_exit(0); + #endif + } + ''') + for no_exit in [0, 1]: + for call_exit in [0, 1]: + subprocess.check_call([PYTHON, EMCC, 'src.cpp', '-s', 'NO_EXIT_RUNTIME=%d' % no_exit, '-DCALL_EXIT=%d' % call_exit]) + print(no_exit, call_exit) + out = run_js('a.out.js', stdout=PIPE, stderr=PIPE, full_output=True) + assert ('emscripten_force_exit cannot actually shut down the runtime, as the build has NO_EXIT_RUNTIME set' in out) == (no_exit and call_exit), out + def test_mkdir_silly(self): open('src.cpp', 'w').write(r''' #include From f6167e789586c32796ee9a2766542b025276a02d Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Mon, 4 Dec 2017 17:26:24 -0800 Subject: [PATCH 38/42] whitespace --- site/source/docs/api_reference/emscripten.h.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/source/docs/api_reference/emscripten.h.rst b/site/source/docs/api_reference/emscripten.h.rst index b54d41a9d2132..5337729ec7539 100644 --- a/site/source/docs/api_reference/emscripten.h.rst +++ b/site/source/docs/api_reference/emscripten.h.rst @@ -334,7 +334,7 @@ Functions The difference is that ``emscripten_force_exit`` will shut down the runtime even if you previously called :c:func:`emscripten_exit_with_live_runtime` or otherwise kept the runtime alive. In other words, this method gives you the option to completely shut down the runtime after it was kept alive beyond the completion of ``main()``. - Note that if ``NO_EXIT_RUNTIME`` is set (which it is by default) then the runtime cannot be shut down, as we do not include the code to do so. Build with ``-s NO_EXIT_RUNTIME=0`` if you want to be able to exit the runtime. + Note that if ``NO_EXIT_RUNTIME`` is set (which it is by default) then the runtime cannot be shut down, as we do not include the code to do so. Build with ``-s NO_EXIT_RUNTIME=0`` if you want to be able to exit the runtime. :param int status: The same as for the *libc* function `exit() `_. From 9aa1ddb8206dc91dcaf8b81f1d3241fcd292c395 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 15 Dec 2017 17:12:32 -0600 Subject: [PATCH 39/42] metadce is now more effective, update test --- tests/test_other.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/test_other.py b/tests/test_other.py index 82d2e5a07d703..3954b94061a76 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -7755,14 +7755,14 @@ def test_binaryen_metadce(self): sizes = {} # in -Os, -Oz, we remove imports wasm doesn't need for args, expected_len, expected_exists, expected_not_exists in [ - ([], 24, ['abort', 'tempDoublePtr'], ['waka']), - (['-O1'], 21, ['abort', 'tempDoublePtr'], ['waka']), - (['-O2'], 21, ['abort', 'tempDoublePtr'], ['waka']), - (['-O3'], 16, ['abort'], ['tempDoublePtr', 'waka']), # in -O3, -Os and -Oz we metadce - (['-Os'], 16, ['abort'], ['tempDoublePtr', 'waka']), - (['-Oz'], 16, ['abort'], ['tempDoublePtr', 'waka']), + ([], 25, ['abort', 'tempDoublePtr'], ['waka']), + (['-O1'], 20, ['abort', 'tempDoublePtr'], ['waka']), + (['-O2'], 20, ['abort', 'tempDoublePtr'], ['waka']), + (['-O3'], 14, ['abort'], ['tempDoublePtr', 'waka']), # in -O3, -Os and -Oz we metadce + (['-Os'], 14, ['abort'], ['tempDoublePtr', 'waka']), + (['-Oz'], 14, ['abort'], ['tempDoublePtr', 'waka']), # finally, check what happens when we export pretty much nothing. wasm should be almost empty - (['-Os', '-s', 'EXPORTED_FUNCTIONS=[]', '-s', 'EXPORTED_RUNTIME_METHODS=[]'], 9, ['abort'], ['tempDoublePtr', 'waka']), + (['-Os', '-s', 'EXPORTED_FUNCTIONS=[]', '-s', 'EXPORTED_RUNTIME_METHODS=[]'], 2, ['STACKTOP'], ['tempDoublePtr', 'waka']), ]: print(args, expected_len, expected_exists, expected_not_exists) subprocess.check_call([PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp')] + args + ['-s', 'WASM=1', '-g2']) @@ -7774,7 +7774,8 @@ def test_binaryen_metadce(self): relevant = js[start+2:end-2] relevant = relevant.replace(' ', '').replace('"', '').replace("'", '').split(',') sent = [x.split(':')[0].strip() for x in relevant] - assert len(sent) == expected_len, (len(sent), expected_len) + print(' seen: ' + str(sent)) + assert len(sent) == expected_len, (expected_len, len(sent)) for exists in expected_exists: assert exists in sent, [exists, sent] for not_exists in expected_not_exists: From 66345ff4f4faf9e381aee2020b46d6a4732842e2 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sat, 16 Dec 2017 09:03:46 -0600 Subject: [PATCH 40/42] faq entry on Module.* is not a function [ci skip] --- site/source/docs/getting_started/FAQ.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/site/source/docs/getting_started/FAQ.rst b/site/source/docs/getting_started/FAQ.rst index 1552175ef1e2b..9ba0cdae47351 100644 --- a/site/source/docs/getting_started/FAQ.rst +++ b/site/source/docs/getting_started/FAQ.rst @@ -324,6 +324,14 @@ Emscripten by default does *not* give fatal errors on undefined symbols, so you Aside from just forgetting to link in a necessary object file, one possible cause for this error is inline functions in headers. If you have a header with ``inline int my_func() { .. }`` then *Clang* may not actually inline the function (since inline is just a hint), and also not generate code for it (since it's in a header). The result is that the generated bitcode and JavaScript will not have that function implemented. One solution is to add ``static`` to the function declaration, which forces code to be generated in the object file: ``static inline int my_func() { .. }``. +Why do I get ``TypeError: Module.someThing is not a function``? +=============================================================== + +The ``Module`` object will contain exported methods. For something to appear there, you should add it to ``EXPORTED_FUNCTIONS`` for compiled code, or ``EXTRA_EXPORTED_RUNTIME_METHODS`` for a runtime method (like ``getValue``). + +.. note:: You can use runtime methods directly, without exporting them, if the compiler can see them used. For example, you can use ``getValue`` in ``EM_ASM`` code, or a ``--pre-js``, by calling it directly. The optimizer will not remove that JS runtime method because it sees it is used. You only need to use ``Module.getValue`` if you want to call that method from outside the JS code the compiler can see, and then you need to export it. + +.. note:: Emscripten used to export many runtime methods by default. This increased code size, and for that reason we've changed that default. If you depend on something that used to be exported, you should see a warning pointing you to the solution, in an unoptimized build, or a build with ``ASSERTIONS`` enabled, which we hope will minimize any annoyance. See ``Changelog.markdown`` for details. Why do I get an odd python error complaining about libcxx.bc or libcxxabi.bc? ============================================================================= From 83059d25e675dd93a33911e4af082fb20a8c223d Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sat, 16 Dec 2017 09:06:21 -0600 Subject: [PATCH 41/42] improve message --- src/postamble.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/postamble.js b/src/postamble.js index 2ee728216fbd9..5a1f07576ba11 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -352,7 +352,7 @@ function exit(status, implicit) { Module['print'] = print; Module['printErr'] = printErr; if (has) { - Runtime.warnOnce('stdio streams had content in them that was not flushed. you should set NO_EXIT_RUNTIME to 0 (see the FAQ)'); + Runtime.warnOnce('stdio streams had content in them that was not flushed. you should set NO_EXIT_RUNTIME to 0 (see the FAQ), or make sure to emit a newline when you printf etc.'); } } #endif // NO_EXIT_RUNTIME From 42997872d75ac6b022b01af7b2c3e3ca57797be4 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sat, 16 Dec 2017 09:08:13 -0600 Subject: [PATCH 42/42] fix browser.test_emscripten_main_loop - the pthreads part needs the runtime to exit --- tests/test_browser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_browser.py b/tests/test_browser.py index 1fce6c2b126c4..2a13f99429ccc 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -1684,7 +1684,7 @@ def test_emscripten_fs_api2(self): self.btest('emscripten_fs_api_browser2.cpp', '1', args=['-s', "ASSERTIONS=1"]) def test_emscripten_main_loop(self): - for args in [[], ['-s', 'USE_PTHREADS=1', '-s', 'PROXY_TO_PTHREAD=1']]: + for args in [[], ['-s', 'USE_PTHREADS=1', '-s', 'PROXY_TO_PTHREAD=1', '-s', 'NO_EXIT_RUNTIME=0']]: self.btest('emscripten_main_loop.cpp', '0', args=args) def test_emscripten_main_loop_settimeout(self):