Skip to content

Commit

Permalink
De-Module[]-arize print and printErr (#6756)
Browse files Browse the repository at this point in the history
This JS change replaces calls to Module['print'] (to print to stdout) and Module['printErr'] (to print to stderr) with calls to out() and err() respectively, which are defined in the main scope directly. This allows the optimizer to dce things more effectively, and shrinks our hello world JS by over 1% (with and without closure).

More generally, we have too many things that we use on Module when we don't need to. It makes sense to allow users to define Module['print'] (as how we emit to stdout), and to optionally be where we export that function if the user requested it, but it is silly to constantly looking up Module['print'] everywhere - it increases size and adds overhead. In other words, Module makes sense as an interface to receiving stuff or sending stuff, but not internal work.

Also, using Module everywhere makes it harder to do some bigger refactorings for code size that I have some ideas about, so removing this unhelpful complexity is a step in the right direction.

There are two possible changes people might see here:

*    If you change Module['print'] during program execution, we don't notice that. This was never documented as intended to work, but probably worked in most cases. We did have a test or two that depended on this. The docs have been updated to mention this.
*    If you expect Module['print'] to always exist, it won't unless it is explicitly exported. That is in line with the other exporting changes we've been making. As with those changes, we will show a clear error in an assertions builds in that case, with instructions for how to fix it.

Almost all of this patch is autogenerated (using tools/update_js.py). For the main non-autogenerated portion, see src/shell.js.

Note that we can't use print(), printErr() as the names since those are used in some shell environments (we use those as stdout and stderr if they are defined, in fact).
  • Loading branch information
kripken committed Jun 28, 2018
1 parent a9d1803 commit cd42e9d
Show file tree
Hide file tree
Showing 113 changed files with 904 additions and 832 deletions.
1 change: 1 addition & 0 deletions ChangeLog.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Current Trunk
-------------
- Correctness fix for stack handling in invoke_*()s. This may add noticeable overhead to programs using C++ exceptions and (less likely) setjmp/longjmp - please report any issues. See #6666 #6702
- Deprecate Module.ENVIRONMENT: Now that we have a compile-time option to set the environment, also having a runtime one on Module is complexity that we are better off without. When Module.ENVIRONMENT is used with ASSERTIONS it will show an error to direct users to the new option (-s ENVIRONMENT=web , or node, etc., at compile time).
- Breaking change: Do not export print/printErr by default. Similar to other similar changes (like getValue/setValue). We now use out() and err() functions in JS to print to stdout/stderr respectively. See #6756.

v1.38.6: 06/13/2018
-------------------
Expand Down
8 changes: 4 additions & 4 deletions emscripten.py
Original file line number Diff line number Diff line change
Expand Up @@ -953,19 +953,19 @@ def create_mftCall_funcs(function_table_data, settings):

def get_function_pointer_error(sig, function_table_sigs, settings):
if settings['ASSERTIONS'] <= 1:
extra = ' Module["printErr"]("Build with ASSERTIONS=2 for more info.");'
extra = ' err("Build with ASSERTIONS=2 for more info.");'
pointer = ' '
else:
pointer = ' \'" + x + "\' '
extra = ' Module["printErr"]("This pointer might make sense in another type signature: '
extra = ' err("This pointer might make sense in another type signature: '
# sort signatures, attempting to show most likely related ones first
sigs = list(function_table_sigs)
sigs.sort(key=signature_sort_key(sig))
for other in sigs:
if other != sig:
extra += other + ': " + debug_table_' + other + '[x] + " '
extra += '"); '
return 'Module["printErr"]("Invalid function pointer' + pointer + 'called with signature \'' + sig + '\'. ' + \
return 'err("Invalid function pointer' + pointer + 'called with signature \'' + sig + '\'. ' + \
'Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? ' + \
'Or calling a function with an incorrect type, which will fail? ' + \
'(it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)' + \
Expand Down Expand Up @@ -1215,7 +1215,7 @@ def setup_function_pointers(function_table_sigs, settings):
table_access = 'parentModule["' + table_access + '"]' # side module tables were merged into the parent, we need to access the global one
table_read = table_access + '[x]'
prelude = '''
if (x < 0 || x >= %s.length) { Module.printErr("Function table mask error (out of range)"); %s ; abort(x) }''' % (table_access, get_function_pointer_error(sig, function_table_sigs, settings))
if (x < 0 || x >= %s.length) { err("Function table mask error (out of range)"); %s ; abort(x) }''' % (table_access, get_function_pointer_error(sig, function_table_sigs, settings))
asm_setup += '''
function ftCall_%s(%s) {%s
return %s(%s);
Expand Down
5 changes: 3 additions & 2 deletions site/source/docs/api_reference/module.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ When generating HTML, Emscripten creates a ``Module`` object with default method

Module['print'] = function(text) { alert('stdout: ' + text) };

Note that once the Module object is received by the main JavaScript file, it will look for `Module['print']` and so forth at that time, and use them accordingly. Changing their values later may not be noticed.

Affecting execution
===================

The following ``Module`` attributes affect code execution.
The following ``Module`` attributes affect code execution. Set them to customize behavior.


.. js:attribute:: Module.arguments
Expand All @@ -62,7 +63,7 @@ The following ``Module`` attributes affect code execution.

.. js:attribute:: Module.logReadFiles

If set, :js:attr:`Module.printErr` will log when any file is read.
If set, stderr will log when any file is read.


.. js:attribute:: Module.onAbort
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ following JavaScript:
.. note:: The function ``alert`` is present in browsers, but not in *node*
or other JavaScript shells. A more generic alternative is to call
:js:func:`Module.print`.
`console.log`.


A faster way to call JavaScript from C is to write "inline JavaScript",
Expand Down Expand Up @@ -307,7 +307,7 @@ You can also send values from C into JavaScript inside :c:macro:`EM_ASM_`
.. code-block:: cpp
EM_ASM_({
Module.print('I received: ' + $0);
console.log('I received: ' + $0);
}, 100);
This will show ``I received: 100``.
Expand All @@ -318,7 +318,7 @@ and then ``101``.
.. code-block:: cpp
int x = EM_ASM_INT({
Module.print('I received: ' + $0);
console.log('I received: ' + $0);
return $0 + 1;
}, 100);
printf("%d\n", x);
Expand Down
2 changes: 1 addition & 1 deletion site/source/docs/porting/files/packaging_files.rst
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ Monitoring file usage

.. important:: Only package the files your app actually needs, in order to reduce download size and improve startup speed.

There is an option to log which files are actually used at runtime. To use it, define the :js:attr:`Module.logReadFiles` object. The :js:attr:`Module.printErr` function will be called on each file that is read (this function must also be defined, and should log to a convenient place).
There is an option to log which files are actually used at runtime. To use it, define the :js:attr:`Module.logReadFiles` object. Each file that is read will be logged to stderr.

An alternative approach is to look at :js:func:`FS.readFiles` in your compiled JavaScript. This is an object with keys for all the files that were read from. You may find it easier to use than logging as it records files rather than potentially multiple file accesses.

Expand Down
2 changes: 2 additions & 0 deletions site/source/docs/tools_reference/emcc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,8 @@ Options that are modified or new in *emcc* are listed below:
``--pre-js <file>``
Specify a file whose contents are added before the emitted code and optimized together with it. Note that this might not literally be the very first thing in the JS output, for example if ``MODULARIZE`` is used (see ``src/settings.js``). If you want that, you can just prepend to the output from emscripten; the benefit of ``--pre-js`` is that it optimizes the code with the rest of the emscripten output, which allows better dead code elimination and minification, and it should only be used for that purpose. In particular, ``--pre-js`` code should not alter the main output from emscripten in ways that could confuse the optimizer, such as using ``--pre-js`` + ``--post-js`` to put all the output in an inner function scope (see ``MODULARIZE`` for that).

`--pre-js` (but not `--post-js`) is also useful for specifying things on the ``Module`` object, as it appears before the JS looks at ``Module`` (for example, you can define ``Module['print']`` there).

.. _emcc-post-js:

``--post-js <file>``
Expand Down
4 changes: 2 additions & 2 deletions src/Fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,10 @@ var Fetch = {
else if (Module['pthreadMainPrefixURL']) fetchJs = Module['pthreadMainPrefixURL'] + fetchJs;
Fetch.worker = new Worker(fetchJs);
Fetch.worker.onmessage = function(e) {
Module['print']('fetch-worker sent a message: ' + e.filename + ':' + e.lineno + ': ' + e.message);
out('fetch-worker sent a message: ' + e.filename + ':' + e.lineno + ': ' + e.message);
};
Fetch.worker.onerror = function(e) {
Module['printErr']('fetch-worker sent an error! ' + e.filename + ':' + e.lineno + ': ' + e.message);
err('fetch-worker sent an error! ' + e.filename + ':' + e.lineno + ': ' + e.message);
};
}
#else
Expand Down
10 changes: 5 additions & 5 deletions src/emrun_postjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ if (typeof window === "object" && (typeof ENVIRONMENT_IS_PTHREAD === 'undefined'
// If the address contains localhost, or we are running the page from port 6931, we can assume we're running the test runner and should post stdout logs.
if (document.URL.search("localhost") != -1 || document.URL.search(":6931/") != -1) {
var emrun_http_sequence_number = 1;
var prevPrint = Module['print'];
var prevErr = Module['printErr'];
var prevPrint = out;
var prevErr = err;
function emrun_exit() { if (emrun_num_post_messages_in_flight == 0) postExit('^exit^'+EXITSTATUS); else emrun_should_close_itself = true; };
Module['addOnExit'](emrun_exit);
Module['print'] = function emrun_print(text) { post('^out^'+(emrun_http_sequence_number++)+'^'+encodeURIComponent(text)); prevPrint(text); }
Module['printErr'] = function emrun_printErr(text) { post('^err^'+(emrun_http_sequence_number++)+'^'+encodeURIComponent(text)); prevErr(text); }
out = function emrun_print(text) { post('^out^'+(emrun_http_sequence_number++)+'^'+encodeURIComponent(text)); prevPrint(text); }
err = function emrun_printErr(text) { post('^err^'+(emrun_http_sequence_number++)+'^'+encodeURIComponent(text)); prevErr(text); }

// Notify emrun web server that this browser has successfully launched the page.
post('^pageload^');
Expand All @@ -49,7 +49,7 @@ if (typeof window === "object" && (typeof ENVIRONMENT_IS_PTHREAD === 'undefined'
// To use from C code, call e.g. EM_ASM({emrun_file_dump("file.dat", HEAPU8.subarray($0, $0 + $1));}, my_data_pointer, my_data_pointer_byte_length);
function emrun_file_dump(filename, data) {
var http = new XMLHttpRequest();
Module['print']('Dumping out file "' + filename + '" with ' + data.length + ' bytes of data.');
out('Dumping out file "' + filename + '" with ' + data.length + ' bytes of data.');
http.open("POST", "stdio.html?file=" + filename, true);
http.send(data); // XXX this does not work in workers, for some odd reason (issue #2681)
}
Expand Down
8 changes: 4 additions & 4 deletions src/jsifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ function JSify(data, functionsOnly) {
// name the function; overwrite if it's already named
snippet = snippet.replace(/function(?:\s+([^(]+))?\s*\(/, 'function ' + finalName + '(');
if (LIBRARY_DEBUG && !LibraryManager.library[ident + '__asm']) {
snippet = snippet.replace('{', '{ var ret = (function() { if (runtimeDebug) Module.printErr("[library call:' + finalName + ': " + Array.prototype.slice.call(arguments).map(prettyPrint) + "]"); ');
snippet = snippet.substr(0, snippet.length-1) + '}).apply(this, arguments); if (runtimeDebug && typeof ret !== "undefined") Module.printErr(" [ return:" + prettyPrint(ret)); return ret; \n}';
snippet = snippet.replace('{', '{ var ret = (function() { if (runtimeDebug) err("[library call:' + finalName + ': " + Array.prototype.slice.call(arguments).map(prettyPrint) + "]"); ');
snippet = snippet.substr(0, snippet.length-1) + '}).apply(this, arguments); if (runtimeDebug && typeof ret !== "undefined") err(" [ return:" + prettyPrint(ret)); return ret; \n}';
}
return snippet;
}
Expand Down Expand Up @@ -247,7 +247,7 @@ function JSify(data, functionsOnly) {
}
if (!(MAIN_MODULE || SIDE_MODULE)) {
// emit a stub that will fail at runtime
LibraryManager.library[shortident] = new Function("Module['printErr']('missing function: " + shortident + "'); abort(-1);");
LibraryManager.library[shortident] = new Function("err('missing function: " + shortident + "'); abort(-1);");
} else {
var target = (MAIN_MODULE ? '' : 'parent') + "Module['_" + shortident + "']";
var assertion = '';
Expand Down Expand Up @@ -389,7 +389,7 @@ function JSify(data, functionsOnly) {
if (LibraryManager.library[shortident + '__asm']) {
warn('cannot kill asm library function ' + item.ident);
} else {
LibraryManager.library[shortident] = new Function("Module['printErr']('dead function: " + shortident + "'); abort(-1);");
LibraryManager.library[shortident] = new Function("err('dead function: " + shortident + "'); abort(-1);");
delete LibraryManager.library[shortident + '__inline'];
delete LibraryManager.library[shortident + '__deps'];
}
Expand Down
58 changes: 29 additions & 29 deletions src/library.js
Original file line number Diff line number Diff line change
Expand Up @@ -1114,27 +1114,27 @@ LibraryManager.library = {
var info = EXCEPTIONS.infos[ptr];
if (info.adjusted === adjusted) {
#if EXCEPTION_DEBUG
Module.printErr('de-adjusted exception ptr ' + adjusted + ' to ' + ptr);
err('de-adjusted exception ptr ' + adjusted + ' to ' + ptr);
#endif
return ptr;
}
}
#if EXCEPTION_DEBUG
Module.printErr('no de-adjustment for unknown exception ptr ' + adjusted);
err('no de-adjustment for unknown exception ptr ' + adjusted);
#endif
return adjusted;
},
addRef: function(ptr) {
#if EXCEPTION_DEBUG
Module.printErr('addref ' + ptr);
err('addref ' + ptr);
#endif
if (!ptr) return;
var info = EXCEPTIONS.infos[ptr];
info.refcount++;
},
decRef: function(ptr) {
#if EXCEPTION_DEBUG
Module.printErr('decref ' + ptr);
err('decref ' + ptr);
#endif
if (!ptr) return;
var info = EXCEPTIONS.infos[ptr];
Expand All @@ -1155,7 +1155,7 @@ LibraryManager.library = {
delete EXCEPTIONS.infos[ptr];
___cxa_free_exception(ptr);
#if EXCEPTION_DEBUG
Module.printErr('decref freeing exception ' + [ptr, EXCEPTIONS.last, 'stack', EXCEPTIONS.caught]);
err('decref freeing exception ' + [ptr, EXCEPTIONS.last, 'stack', EXCEPTIONS.caught]);
#endif
}
},
Expand All @@ -1177,7 +1177,7 @@ LibraryManager.library = {
return _free(ptr);
} catch(e) { // XXX FIXME
#if ASSERTIONS
Module.printErr('exception during cxa_free_exception: ' + e);
err('exception during cxa_free_exception: ' + e);
#endif
}
},
Expand All @@ -1195,7 +1195,7 @@ LibraryManager.library = {
__cxa_throw__deps: ['_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch', '$EXCEPTIONS'],
__cxa_throw: function(ptr, type, destructor) {
#if EXCEPTION_DEBUG
Module.printErr('Compiled code throwing an exception, ' + [ptr,type,destructor]);
err('Compiled code throwing an exception, ' + [ptr,type,destructor]);
#endif
EXCEPTIONS.infos[ptr] = {
ptr: ptr,
Expand Down Expand Up @@ -1227,7 +1227,7 @@ LibraryManager.library = {
EXCEPTIONS.infos[ptr].rethrown = true;
}
#if EXCEPTION_DEBUG
Module.printErr('Compiled code RE-throwing an exception, popped ' + [ptr, EXCEPTIONS.last, 'stack', EXCEPTIONS.caught]);
err('Compiled code RE-throwing an exception, popped ' + [ptr, EXCEPTIONS.last, 'stack', EXCEPTIONS.caught]);
#endif
EXCEPTIONS.last = ptr;
{{{ makeThrow('ptr') }}}
Expand Down Expand Up @@ -1258,7 +1258,7 @@ LibraryManager.library = {
if (info) info.rethrown = false;
EXCEPTIONS.caught.push(ptr);
#if EXCEPTION_DEBUG
Module.printErr('cxa_begin_catch ' + [ptr, 'stack', EXCEPTIONS.caught]);
err('cxa_begin_catch ' + [ptr, 'stack', EXCEPTIONS.caught]);
#endif
EXCEPTIONS.addRef(EXCEPTIONS.deAdjust(ptr));
return ptr;
Expand All @@ -1274,7 +1274,7 @@ LibraryManager.library = {
// Call destructor if one is registered then clear it.
var ptr = EXCEPTIONS.caught.pop();
#if EXCEPTION_DEBUG
Module.printErr('cxa_end_catch popped ' + [ptr, EXCEPTIONS.last, 'stack', EXCEPTIONS.caught]);
err('cxa_end_catch popped ' + [ptr, EXCEPTIONS.last, 'stack', EXCEPTIONS.caught]);
#endif
if (ptr) {
EXCEPTIONS.decRef(EXCEPTIONS.deAdjust(ptr));
Expand All @@ -1283,7 +1283,7 @@ LibraryManager.library = {
},
__cxa_get_exception_ptr: function(ptr) {
#if EXCEPTION_DEBUG
Module.printErr('cxa_get_exception_ptr ' + ptr);
err('cxa_get_exception_ptr ' + ptr);
#endif
// TODO: use info.adjusted?
return ptr;
Expand All @@ -1297,7 +1297,7 @@ LibraryManager.library = {
},

__cxa_call_unexpected: function(exception) {
Module.printErr('Unexpected exception thrown, this is not properly supported - aborting');
err('Unexpected exception thrown, this is not properly supported - aborting');
ABORT = true;
throw exception;
},
Expand Down Expand Up @@ -1354,7 +1354,7 @@ LibraryManager.library = {
// can_catch receives a **, add indirection
if (!___cxa_find_matching_catch.buffer) ___cxa_find_matching_catch.buffer = _malloc(4);
#if EXCEPTION_DEBUG
Module.print("can_catch on " + [thrown]);
out("can_catch on " + [thrown]);
#endif
{{{ makeSetValue('___cxa_find_matching_catch.buffer', '0', 'thrown', '*') }}};
thrown = ___cxa_find_matching_catch.buffer;
Expand All @@ -1367,7 +1367,7 @@ LibraryManager.library = {
thrown = {{{ makeGetValue('thrown', '0', '*') }}}; // undo indirection
info.adjusted = thrown;
#if EXCEPTION_DEBUG
Module.print(" can_catch found " + [thrown, typeArray[i]]);
out(" can_catch found " + [thrown, typeArray[i]]);
#endif
{{{ makeStructuralReturn(['thrown', 'typeArray[i]']) }}};
}
Expand All @@ -1382,7 +1382,7 @@ LibraryManager.library = {
__resumeException__deps: ['$EXCEPTIONS', function() { Functions.libraryFunctions['___resumeException'] = 1 }], // will be called directly from compiled code
__resumeException: function(ptr) {
#if EXCEPTION_DEBUG
Module.print("Resuming exception " + [ptr, EXCEPTIONS.last]);
out("Resuming exception " + [ptr, EXCEPTIONS.last]);
#endif
if (!EXCEPTIONS.last) { EXCEPTIONS.last = ptr; }
{{{ makeThrow('ptr') }}}
Expand Down Expand Up @@ -1758,7 +1758,7 @@ LibraryManager.library = {
// the shared library is a shared wasm library (see tools/shared.py WebAssembly.make_shared_library)
var lib_data = FS.readFile(filename, { encoding: 'binary' });
if (!(lib_data instanceof Uint8Array)) lib_data = new Uint8Array(lib_data);
//Module.printErr('libfile ' + filename + ' size: ' + lib_data.length);
//err('libfile ' + filename + ' size: ' + lib_data.length);
lib_module = loadWebAssemblyModule(lib_data);
#else
// the shared library is a JS file, which we eval
Expand All @@ -1770,7 +1770,7 @@ LibraryManager.library = {
#endif
} catch (e) {
#if ASSERTIONS
Module.printErr('Error in loading dynamic library: ' + e);
err('Error in loading dynamic library: ' + e);
#endif
DLFCN.errorMsg = 'Could not evaluate dynamic lib: ' + filename + '\n' + e;
return 0;
Expand Down Expand Up @@ -2596,7 +2596,7 @@ LibraryManager.library = {
}

var matches = new RegExp('^'+pattern, "i").exec(Pointer_stringify(buf))
// Module['print'](Pointer_stringify(buf)+ ' is matched by '+((new RegExp('^'+pattern)).source)+' into: '+JSON.stringify(matches));
// out(Pointer_stringify(buf)+ ' is matched by '+((new RegExp('^'+pattern)).source)+' into: '+JSON.stringify(matches));

function initDate() {
function fixup(value, min, max) {
Expand Down Expand Up @@ -3250,7 +3250,7 @@ LibraryManager.library = {
__setErrNo: function(value) {
if (Module['___errno_location']) {{{ makeSetValue("Module['___errno_location']()", 0, 'value', 'i32') }}};
#if ASSERTIONS
else Module.printErr('failed to set errno from JS');
else err('failed to set errno from JS');
#endif
return value;
},
Expand Down Expand Up @@ -4244,9 +4244,9 @@ LibraryManager.library = {
console.log(str);
}
} else if (flags & 6 /*EM_LOG_ERROR|EM_LOG_WARN*/) {
Module.printErr(str);
err(str);
} else {
Module.print(str);
out(str);
}
},

Expand Down Expand Up @@ -4395,33 +4395,33 @@ LibraryManager.library = {

_Unwind_RaiseException__deps: ['__cxa_throw'],
_Unwind_RaiseException: function(ex) {
Module.printErr('Warning: _Unwind_RaiseException is not correctly implemented');
err('Warning: _Unwind_RaiseException is not correctly implemented');
return ___cxa_throw(ex, 0, 0);
},

_Unwind_DeleteException: function(ex) {
Module.printErr('TODO: Unwind_DeleteException');
err('TODO: Unwind_DeleteException');
},

// autodebugging

emscripten_autodebug_i64: function(line, valuel, valueh) {
Module.print('AD:' + [line, valuel, valueh]);
out('AD:' + [line, valuel, valueh]);
},
emscripten_autodebug_i32: function(line, value) {
Module.print('AD:' + [line, value]);
out('AD:' + [line, value]);
},
emscripten_autodebug_i16: function(line, value) {
Module.print('AD:' + [line, value]);
out('AD:' + [line, value]);
},
emscripten_autodebug_i8: function(line, value) {
Module.print('AD:' + [line, value]);
out('AD:' + [line, value]);
},
emscripten_autodebug_float: function(line, value) {
Module.print('AD:' + [line, value]);
out('AD:' + [line, value]);
},
emscripten_autodebug_double: function(line, value) {
Module.print('AD:' + [line, value]);
out('AD:' + [line, value]);
},

// misc definitions to avoid unnecessary unresolved symbols from fastcomp
Expand Down
Loading

0 comments on commit cd42e9d

Please sign in to comment.