diff --git a/src/closure-externs/closure-externs.js b/src/closure-externs/closure-externs.js index 1346cac539330..f0e808cb67c6f 100644 --- a/src/closure-externs/closure-externs.js +++ b/src/closure-externs/closure-externs.js @@ -14,6 +14,7 @@ // Special placeholder for `import.meta` and `await import`. var EMSCRIPTEN$IMPORT$META; var EMSCRIPTEN$AWAIT$IMPORT; +var EMSCRIPTEN$EXPORT$DEFAULT; // Don't minify startWorker which we use to start workers once the runtime is ready. /** @@ -157,9 +158,10 @@ var wakaUnknownBefore; // Module loaders externs, for AMD etc. /** + * @param {Object} deps * @param {Function} wrapper */ -var define = function (wrapper) {}; +var define = function (deps, wrapper) {}; /** * @type {Worker} @@ -228,20 +230,6 @@ var sampleRate; */ var id; -/** - * Used in MODULARIZE mode as the name of the incoming module argument. - * This is generated outside of the code we pass to closure so from closure's - * POV this is "extern". - */ -var moduleArg; - -/** - * Used in MODULARIZE mode. - * We need to access this after the code we pass to closure so from closure's - * POV this is "extern". - */ -var moduleRtn; - /** * This was removed from upstream closure compiler in * https://github.com/google/closure-compiler/commit/f83322c1b. diff --git a/src/closure-externs/modularize-externs.js b/src/closure-externs/modularize-externs.js deleted file mode 100644 index fd29362e99559..0000000000000 --- a/src/closure-externs/modularize-externs.js +++ /dev/null @@ -1,7 +0,0 @@ -// Due to the way MODULARIZE works, Closure is run on generated code that does not define _scriptName, -// but only after MODULARIZE has finished, _scriptName is injected to the generated code. -// Therefore it cannot be minified. -/** - * @suppress {duplicate, undefinedVars} - */ -var _scriptName; diff --git a/src/parseTools.mjs b/src/parseTools.mjs index 509ff01727b44..3f7725e48ad37 100644 --- a/src/parseTools.mjs +++ b/src/parseTools.mjs @@ -42,16 +42,17 @@ export function processMacros(text, filename) { // Simple #if/else/endif preprocessing for a file. Checks if the // ident checked is true in our global. // Also handles #include x.js (similar to C #include ) -export function preprocess(filename) { +export function preprocess(filename, closureFriendly = true) { let text = read(filename); - if (EXPORT_ES6 && USE_ES6_IMPORT_META) { + if (closureFriendly && EXPORT_ES6 && USE_ES6_IMPORT_META) { // `eval`, Terser and Closure don't support module syntax; to allow it, // we need to temporarily replace `import.meta` and `await import` usages // with placeholders during preprocess phase, and back after all the other ops. // See also: `phase_final_emitting` in emcc.py. text = text .replace(/\bimport\.meta\b/g, 'EMSCRIPTEN$IMPORT$META') - .replace(/\bawait import\b/g, 'EMSCRIPTEN$AWAIT$IMPORT'); + .replace(/\bawait import\b/g, 'EMSCRIPTEN$AWAIT$IMPORT') + .replace(/\bexport default\b/g, 'EMSCRIPTEN$EXPORT$DEFAULT ='); } // Remove windows line endings, if any text = text.replace(/\r\n/g, '\n'); @@ -945,7 +946,7 @@ function receiveI64ParamAsI53Unchecked(name) { return `${name} = Number(${name});`; } return `var ${name} = convertI32PairToI53(${name}_low, ${name}_high);`; -} +X} // Convert a pointer value under wasm64 from BigInt (used at local level API // level) to Number (used in JS library code). No-op under wasm32. @@ -1069,6 +1070,29 @@ function ENVIRONMENT_IS_WORKER_THREAD() { return '(' + envs.join('||') + ')'; } +function nodePthreadDetection() { + // Under node we detect that we are running in a pthread by checking the + // workerData property. + if (EXPORT_ES6) { + return "(await import('worker_threads')).workerData === 'em-pthread'" + } else { + return "require('worker_threads').workerData === 'em-pthread'" + } +} + +function declareInstanceExports() { + const allExports = Array.from(EXPORTED_FUNCTIONS.keys()).concat(Array.from(EXPORTED_RUNTIME_METHODS.keys())); + const mangledExports = allExports.map((e) => `__exp_${e}`) + const mangledExportsAs = allExports.map((e) => `__exp_${e} as ${e}`) + // Declare a top level var for each export so that code in the init function + // can assign to it and update the live module bindings. + if (allExports.length == 0) return ''; + let rtn = 'var ' + mangledExports.join(', ') + ';\n'; + // Export the functions with their original name. + rtn += 'export {' + mangledExportsAs.join(', ') + '};\n' + return rtn; +} + addToCompileTimeContext({ ATEXITS, ATINITS, @@ -1134,4 +1158,6 @@ addToCompileTimeContext({ storeException, to64, toIndexType, + nodePthreadDetection, + declareInstanceExports, }); diff --git a/src/postamble_modularize.js b/src/postamble_modularize.js index d8d3a59a4671e..c563f91c537b0 100644 --- a/src/postamble_modularize.js +++ b/src/postamble_modularize.js @@ -6,18 +6,6 @@ #if WASM_ASYNC_COMPILATION -#if USE_READY_PROMISE -moduleRtn = readyPromise; -#else -moduleRtn = {}; -#endif - -#else // WASM_ASYNC_COMPILATION - -moduleRtn = Module; - -#endif // WASM_ASYNC_COMPILATION - #if ASSERTIONS // Assertion for attempting to access module properties on the incoming // moduleArg. In the past we used this object as the prototype of the module @@ -35,3 +23,70 @@ for (const prop of Object.keys(Module)) { } } #endif + +#if USE_READY_PROMISE + return readyPromise; +#else + return {}; +#endif +#else // WASM_ASYNC_COMPILATION + return Module; +#endif // WASM_ASYNC_COMPILATION +}; // End factory function + +#if ASSERTIONS && MODULARIZE != 'instance' +(() => { + // Create a small, never-async wrapper around {{{ EXPORT_NAME }}} which + // checks for callers incorrectly using it with `new`. + var real_{{{ EXPORT_NAME }}} = {{{ EXPORT_NAME }}}; + {{{ EXPORT_NAME }}} = function(arg) { + if (new.target) throw new Error("{{{ EXPORT_NAME }}}() should not be called with `new {{{ EXPORT_NAME }}}()`"); + return real_{{{ EXPORT_NAME }}}(arg); + } +})(); +#endif + +// Export using a UMD style export, or ES6 exports if selected +#if EXPORT_ES6 +#if MODULARIZE == 'instance' +{{{ declareInstanceExports() }}} +#else +export default {{{ EXPORT_NAME }}}; +#endif +#else +if (typeof exports === 'object' && typeof module === 'object') { + module.exports = {{{ EXPORT_NAME }}}; + // This default export looks redundant, but it allows TS to import this + // commonjs style module. + module.exports.default = {{{ EXPORT_NAME }}}; +} else if (typeof define === 'function' && define['amd']) { + define([], () => {{{ EXPORT_NAME }}}); +} +#endif + + +#if PTHREADS + +// Create code for detecting if we are running in a pthread. +// Normally this detection is done when the module is itself run but +// when running in MODULARIZE mode we need use this to know if we should +// run the module constructor on startup (true only for pthreads). +#if ENVIRONMENT_MAY_BE_WEB || ENVIRONMENT_MAY_BE_WORKER +var isPthread = globalThis.self?.name?.startsWith('em-pthread'); +#if ENVIRONMENT_MAY_BE_NODE +// In order to support both web and node we also need to detect node here. +var isNode = typeof globalThis.process?.versions?.node == 'string'; +if (isNode) isPthread = {{{ nodePthreadDetection() }}}; +#endif +#elif ENVIRONMENT_MAY_BE_NODE +var isPthread = {{{ nodePthreadDetection() }}}; +#endif ENVIRONMENT_MAY_BE_WEB || ENVIRONMENT_MAY_BE_WORKER + +// When running as a pthread, construct a new instance on startup +#if MODULARIZE == 'instance' +isPthread && init(); +#else +isPthread && {{{ EXPORT_NAME }}}(); +#endif + +#endif // PTHREADS diff --git a/src/preamble_modularize.js b/src/preamble_modularize.js new file mode 100644 index 0000000000000..8f65a036450b8 --- /dev/null +++ b/src/preamble_modularize.js @@ -0,0 +1,18 @@ +#if !MINIMAL_RUNTIME || PTHREADS +#if EXPORT_ES6 && USE_ES6_IMPORT_META +var _scriptName = import.meta.url; +#else +var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined; +#if ENVIRONMENT_MAY_BE_NODE +if (typeof __filename != 'undefined') _scriptName = _scriptName || __filename; +#endif +#endif +#endif + +#if MODULARIZE == 'instance' +export default {{{ asyncIf(WASM_ASYNC_COMPILATION || (EXPORT_ES6 && ENVIRONMENT_MAY_BE_NODE)) }}}function init(moduleArg = {}) { +#else +var {{{ EXPORT_NAME }}} = {{{ asyncIf(WASM_ASYNC_COMPILATION || (EXPORT_ES6 && ENVIRONMENT_MAY_BE_NODE)) }}}function(moduleArg = {}) { +#endif + +var Module = moduleArg; diff --git a/src/shell.js b/src/shell.js index f0bd6fba612b4..065d9037577ba 100644 --- a/src/shell.js +++ b/src/shell.js @@ -21,7 +21,7 @@ // before the code. Then that object will be used in the code, and you // can continue to use Module afterwards as well. #if MODULARIZE -var Module = moduleArg; +#include "preamble_modularize.js" #elif USE_CLOSURE_COMPILER /** @type{Object} */ var Module; @@ -99,9 +99,8 @@ if (ENVIRONMENT_IS_PTHREAD) { #endif #endif -#if ENVIRONMENT_MAY_BE_NODE +#if ENVIRONMENT_MAY_BE_NODE && (PTHREADS || WASM_WORKERS) if (ENVIRONMENT_IS_NODE) { -#if PTHREADS || WASM_WORKERS #if EXPORT_ES6 var worker_threads = await import('worker_threads'); #else @@ -114,9 +113,8 @@ if (ENVIRONMENT_IS_NODE) { // is hosting a pthread. ENVIRONMENT_IS_PTHREAD = ENVIRONMENT_IS_WORKER && worker_threads['workerData'] == 'em-pthread' #endif // PTHREADS -#endif // PTHREADS || WASM_WORKERS } -#endif // ENVIRONMENT_MAY_BE_NODE +#endif // ENVIRONMENT_MAY_BE_NODE && (PTHREADS || WASM_WORKERS) #if WASM_WORKERS var ENVIRONMENT_IS_WASM_WORKER = Module['$ww']; diff --git a/src/shell_minimal.js b/src/shell_minimal.js index 4ddf6b3b0303c..4318bcd8d6d4b 100644 --- a/src/shell_minimal.js +++ b/src/shell_minimal.js @@ -5,7 +5,7 @@ */ #if MODULARIZE -var Module = moduleArg; +#include "preamble_modularize.js" #elif USE_CLOSURE_COMPILER /** @type{Object} */ var Module; diff --git a/test/code_size/hello_webgl2_wasm.json b/test/code_size/hello_webgl2_wasm.json index 57518ddaba94c..245a59c58a717 100644 --- a/test/code_size/hello_webgl2_wasm.json +++ b/test/code_size/hello_webgl2_wasm.json @@ -1,10 +1,10 @@ { "a.html": 454, "a.html.gz": 328, - "a.js": 4538, - "a.js.gz": 2320, + "a.js": 6293, + "a.js.gz": 3087, "a.wasm": 10206, "a.wasm.gz": 6663, - "total": 15198, - "total_gz": 9311 + "total": 16953, + "total_gz": 10078 } diff --git a/test/code_size/hello_webgl2_wasm2js.json b/test/code_size/hello_webgl2_wasm2js.json index 3074c7154d437..ae41177a32d35 100644 --- a/test/code_size/hello_webgl2_wasm2js.json +++ b/test/code_size/hello_webgl2_wasm2js.json @@ -1,8 +1,8 @@ { "a.html": 346, "a.html.gz": 262, - "a.js": 22202, - "a.js.gz": 11604, - "total": 22548, - "total_gz": 11866 + "a.js": 24118, + "a.js.gz": 12472, + "total": 24464, + "total_gz": 12734 } diff --git a/test/code_size/hello_webgl_wasm.json b/test/code_size/hello_webgl_wasm.json index 1aad262f62aa7..99bee8be1aefe 100644 --- a/test/code_size/hello_webgl_wasm.json +++ b/test/code_size/hello_webgl_wasm.json @@ -1,10 +1,10 @@ { "a.html": 454, "a.html.gz": 328, - "a.js": 4076, - "a.js.gz": 2163, + "a.js": 5799, + "a.js.gz": 2911, "a.wasm": 10206, "a.wasm.gz": 6663, - "total": 14736, - "total_gz": 9154 + "total": 16459, + "total_gz": 9902 } diff --git a/test/code_size/hello_webgl_wasm2js.json b/test/code_size/hello_webgl_wasm2js.json index c0b731a9418e9..78a2bc41b44b9 100644 --- a/test/code_size/hello_webgl_wasm2js.json +++ b/test/code_size/hello_webgl_wasm2js.json @@ -1,8 +1,8 @@ { "a.html": 346, "a.html.gz": 262, - "a.js": 21728, - "a.js.gz": 11435, - "total": 22074, - "total_gz": 11697 + "a.js": 23615, + "a.js.gz": 12296, + "total": 23961, + "total_gz": 12558 } diff --git a/test/modularize_post_js.js b/test/modularize_post_js.js index 18ad0c15b8d6e..6f6b44484358c 100644 --- a/test/modularize_post_js.js +++ b/test/modularize_post_js.js @@ -5,6 +5,11 @@ #if PTHREADS // Avoid instantiating the module on pthreads. -if (!isPthread) +#if EXPORT_ES6 +const isMainThread = (await import('worker_threads')).isMainThread; +#else +const { isMainThread } = require('worker_threads'); +#endif +if (isMainThread) #endif {{{ EXPORT_NAME }}}(); diff --git a/test/other/codesize/test_codesize_cxx_ctors1.size b/test/other/codesize/test_codesize_cxx_ctors1.size index 74b7b8e42b7cc..4f8ba557ee1b8 100644 --- a/test/other/codesize/test_codesize_cxx_ctors1.size +++ b/test/other/codesize/test_codesize_cxx_ctors1.size @@ -1 +1 @@ -128894 +128914 diff --git a/test/other/codesize/test_codesize_cxx_ctors2.size b/test/other/codesize/test_codesize_cxx_ctors2.size index e98afd98c4d08..e231c13fee48e 100644 --- a/test/other/codesize/test_codesize_cxx_ctors2.size +++ b/test/other/codesize/test_codesize_cxx_ctors2.size @@ -1 +1 @@ -128343 +128363 diff --git a/test/other/codesize/test_codesize_cxx_except.size b/test/other/codesize/test_codesize_cxx_except.size index c2c735ee85f4b..eb55c181c5216 100644 --- a/test/other/codesize/test_codesize_cxx_except.size +++ b/test/other/codesize/test_codesize_cxx_except.size @@ -1 +1 @@ -170928 +170948 diff --git a/test/other/codesize/test_codesize_cxx_except_wasm.size b/test/other/codesize/test_codesize_cxx_except_wasm.size index e5a548bdc8852..27d22ec7b1d75 100644 --- a/test/other/codesize/test_codesize_cxx_except_wasm.size +++ b/test/other/codesize/test_codesize_cxx_except_wasm.size @@ -1 +1 @@ -142143 +142163 diff --git a/test/other/codesize/test_codesize_cxx_except_wasm_exnref.size b/test/other/codesize/test_codesize_cxx_except_wasm_exnref.size index b3e5808388b4f..c6bf43ceae592 100644 --- a/test/other/codesize/test_codesize_cxx_except_wasm_exnref.size +++ b/test/other/codesize/test_codesize_cxx_except_wasm_exnref.size @@ -1 +1 @@ -144730 +144750 diff --git a/test/other/codesize/test_codesize_cxx_lto.size b/test/other/codesize/test_codesize_cxx_lto.size index b9f08711851c1..a10677fcc662b 100644 --- a/test/other/codesize/test_codesize_cxx_lto.size +++ b/test/other/codesize/test_codesize_cxx_lto.size @@ -1 +1 @@ -121844 +121864 diff --git a/test/other/codesize/test_codesize_cxx_mangle.size b/test/other/codesize/test_codesize_cxx_mangle.size index d7b5814d237cc..f1d783a2932c3 100644 --- a/test/other/codesize/test_codesize_cxx_mangle.size +++ b/test/other/codesize/test_codesize_cxx_mangle.size @@ -1 +1 @@ -232437 +232457 diff --git a/test/other/codesize/test_codesize_cxx_noexcept.size b/test/other/codesize/test_codesize_cxx_noexcept.size index fcdf3f0fb5f09..75539b68cab43 100644 --- a/test/other/codesize/test_codesize_cxx_noexcept.size +++ b/test/other/codesize/test_codesize_cxx_noexcept.size @@ -1 +1 @@ -131701 +131721 diff --git a/test/other/codesize/test_codesize_cxx_wasmfs.size b/test/other/codesize/test_codesize_cxx_wasmfs.size index edc5f6791c328..8140672e8a8ef 100644 --- a/test/other/codesize/test_codesize_cxx_wasmfs.size +++ b/test/other/codesize/test_codesize_cxx_wasmfs.size @@ -1 +1 @@ -168848 +168868 diff --git a/test/other/codesize/test_codesize_minimal_esm.gzsize b/test/other/codesize/test_codesize_minimal_esm.gzsize index 499020422191c..9639a4ce0392a 100644 --- a/test/other/codesize/test_codesize_minimal_esm.gzsize +++ b/test/other/codesize/test_codesize_minimal_esm.gzsize @@ -1 +1 @@ -1509 +1496 diff --git a/test/other/codesize/test_codesize_minimal_esm.jssize b/test/other/codesize/test_codesize_minimal_esm.jssize index 7f7703d078eb4..91bb47f6b7363 100644 --- a/test/other/codesize/test_codesize_minimal_esm.jssize +++ b/test/other/codesize/test_codesize_minimal_esm.jssize @@ -1 +1 @@ -3157 +3085 diff --git a/test/other/test_unoptimized_code_size.js.size b/test/other/test_unoptimized_code_size.js.size index 5b5e77051524d..7f87dbc51f13a 100644 --- a/test/other/test_unoptimized_code_size.js.size +++ b/test/other/test_unoptimized_code_size.js.size @@ -1 +1 @@ -53887 +53857 diff --git a/test/other/test_unoptimized_code_size_no_asserts.js.size b/test/other/test_unoptimized_code_size_no_asserts.js.size index 85517970c8c6c..96cfaf21dbd07 100644 --- a/test/other/test_unoptimized_code_size_no_asserts.js.size +++ b/test/other/test_unoptimized_code_size_no_asserts.js.size @@ -1 +1 @@ -29086 +29056 diff --git a/test/other/test_unoptimized_code_size_strict.js.size b/test/other/test_unoptimized_code_size_strict.js.size index be31bd8016b9c..396228de4247f 100644 --- a/test/other/test_unoptimized_code_size_strict.js.size +++ b/test/other/test_unoptimized_code_size_strict.js.size @@ -1 +1 @@ -52670 +52640 diff --git a/tools/acorn-optimizer.mjs b/tools/acorn-optimizer.mjs index a6932295e1f5c..f55e2d296a8f4 100755 --- a/tools/acorn-optimizer.mjs +++ b/tools/acorn-optimizer.mjs @@ -891,7 +891,7 @@ function emitDCEGraph(ast) { // We must have found the info we need. assert( foundWasmImportsAssign, - 'could not find the assignment to "wasmImports". perhaps --pre-js or --post-js code moved it out of the global scope? (things like that should be done after emcc runs, as they do not need to be run through the optimizer which is the special thing about --pre-js/--post-js code)', + 'could not find the assignment to "wasmImports"', ); // Read exports that were declared in extraInfo if (extraInfo) { diff --git a/tools/building.py b/tools/building.py index c1caf75ca6840..35a26b52dc8b5 100644 --- a/tools/building.py +++ b/tools/building.py @@ -554,7 +554,14 @@ def closure_compiler(filename, advanced=True, extra_closure_args=None): CLOSURE_EXTERNS = [path_from_root('src/closure-externs/closure-externs.js')] if settings.MODULARIZE: - CLOSURE_EXTERNS += [path_from_root('src/closure-externs/modularize-externs.js')] + temp = shared.get_temp_files().get('.js', prefix='emcc_closure_externs_').name + utils.write_file(temp, f''' +/** + * @suppress {{duplicate}} + */ +var {settings.EXPORT_NAME}; +''') + CLOSURE_EXTERNS += [temp] if settings.USE_WEBGPU: CLOSURE_EXTERNS += [path_from_root('src/closure-externs/webgpu-externs.js')] diff --git a/tools/emscripten.py b/tools/emscripten.py index 0db61d81e9c61..fb462c15607e8 100644 --- a/tools/emscripten.py +++ b/tools/emscripten.py @@ -899,8 +899,7 @@ def can_use_await(): # function. # However, because closure does not see this (it runs only on the inner code), # it sees this as a top-level-await, which it does not yet support. - # FIXME(https://github.com/emscripten-core/emscripten/issues/23158) - return settings.MODULARIZE and not settings.USE_CLOSURE_COMPILER + return settings.MODULARIZE def make_export_wrappers(function_exports): diff --git a/tools/link.py b/tools/link.py index 6c3ca352ff6d7..51a299907ce23 100644 --- a/tools/link.py +++ b/tools/link.py @@ -700,9 +700,6 @@ def phase_linker_setup(options, state, newargs): # noqa: C901, PLR0912, PLR0915 options.post_js.append(utils.path_from_root('src/threadprofiler.js')) settings.REQUIRED_EXPORTS.append('emscripten_main_runtime_thread_id') - options.extern_pre_js = read_js_files(options.extern_pre_js) - options.extern_post_js = read_js_files(options.extern_post_js) - # TODO: support source maps with js_transform if options.js_transform and settings.GENERATE_SOURCE_MAP: logger.warning('disabling source maps because a js transform is being done') @@ -2094,7 +2091,8 @@ def fix_es6_import_statements(js_file): src = read_file(js_file) write_file(js_file, src .replace('EMSCRIPTEN$IMPORT$META', 'import.meta') - .replace('EMSCRIPTEN$AWAIT$IMPORT', 'await import')) + .replace('EMSCRIPTEN$AWAIT$IMPORT', 'await import') + .replace('EMSCRIPTEN$EXPORT$DEFAULT =', 'export default')) save_intermediate('es6-module') @@ -2131,9 +2129,7 @@ def phase_final_emitting(options, state, target, wasm_target): if settings.AUDIO_WORKLET == 1: create_worker_file('src/audio_worklet.js', target_dir, settings.AUDIO_WORKLET_FILE, options) - if settings.MODULARIZE: - modularize() - elif settings.USE_CLOSURE_COMPILER: + if not settings.MODULARIZE and settings.USE_CLOSURE_COMPILER: module_export_name_substitution() # Run a final optimization pass to clean up items that were not possible to @@ -2158,13 +2154,15 @@ def phase_final_emitting(options, state, target, wasm_target): # Apply pre and postjs files if options.extern_pre_js or options.extern_post_js: + extern_pre_js = read_js_files(options.extern_pre_js) + extern_post_js = read_js_files(options.extern_post_js) logger.debug('applying extern pre/postjses') src = read_file(final_js) final_js += '.epp.js' with open(final_js, 'w', encoding='utf-8') as f: - f.write(options.extern_pre_js) + f.write(extern_pre_js) f.write(src) - f.write(options.extern_post_js) + f.write(extern_post_js) save_intermediate('extern-pre-post') js_manipulation.handle_license(final_js) @@ -2379,165 +2377,6 @@ def phase_binaryen(target, options, wasm_target): write_file(final_js, js) -def node_pthread_detection(): - # Under node we detect that we are running in a pthread by checking the - # workerData property. - if settings.EXPORT_ES6: - return "(await import('worker_threads')).workerData === 'em-pthread';\n" - else: - return "require('worker_threads').workerData === 'em-pthread'\n" - - -def modularize(): - global final_js - logger.debug(f'Modularizing, assigning to var {settings.EXPORT_NAME}') - generated_js = read_file(final_js) - - # When targetting node and ES6 we use `await import ..` in the generated code - # so the outer function needs to be marked as async. - if settings.WASM_ASYNC_COMPILATION or (settings.EXPORT_ES6 and settings.ENVIRONMENT_MAY_BE_NODE): - maybe_async = 'async ' - else: - maybe_async = '' - - if settings.MODULARIZE == 'instance': - wrapper_function = ''' -export default %(maybe_async)s function init(moduleArg = {}) { - var moduleRtn; - -%(generated_js)s - - return moduleRtn; -} -''' % { - 'generated_js': generated_js, - 'maybe_async': maybe_async, - } - else: - wrapper_function = ''' -%(maybe_async)sfunction(moduleArg = {}) { - var moduleRtn; - -%(generated_js)s - - return moduleRtn; -} -''' % { - 'maybe_async': maybe_async, - 'generated_js': generated_js - } - - if settings.MINIMAL_RUNTIME and not settings.PTHREADS: - # Single threaded MINIMAL_RUNTIME programs do not need access to - # document.currentScript, so a simple export declaration is enough. - src = f'var {settings.EXPORT_NAME} = {wrapper_function};' - else: - script_url_node = '' - # When MODULARIZE this JS may be executed later, - # after document.currentScript is gone, so we save it. - # In EXPORT_ES6 + PTHREADS the 'thread' is actually an ES6 module - # webworker running in strict mode, so doesn't have access to 'document'. - # In this case use 'import.meta' instead. - if settings.EXPORT_ES6 and settings.USE_ES6_IMPORT_META: - script_url = 'import.meta.url' - else: - script_url = "typeof document != 'undefined' ? document.currentScript?.src : undefined" - if settings.ENVIRONMENT_MAY_BE_NODE: - script_url_node = "if (typeof __filename != 'undefined') _scriptName = _scriptName || __filename;" - if settings.MODULARIZE == 'instance': - src = '''\ - var _scriptName = %(script_url)s; - %(script_url_node)s - %(wrapper_function)s -''' % { - 'script_url': script_url, - 'script_url_node': script_url_node, - 'wrapper_function': wrapper_function, - } - else: - src = '''\ -var %(EXPORT_NAME)s = (() => { - var _scriptName = %(script_url)s; - %(script_url_node)s - return (%(wrapper_function)s); -})(); -''' % { - 'EXPORT_NAME': settings.EXPORT_NAME, - 'script_url': script_url, - 'script_url_node': script_url_node, - 'wrapper_function': wrapper_function, - } - - if settings.ASSERTIONS and settings.MODULARIZE != 'instance': - src += '''\ -(() => { - // Create a small, never-async wrapper around %(EXPORT_NAME)s which - // checks for callers incorrectly using it with `new`. - var real_%(EXPORT_NAME)s = %(EXPORT_NAME)s; - %(EXPORT_NAME)s = function(arg) { - if (new.target) throw new Error("%(EXPORT_NAME)s() should not be called with `new %(EXPORT_NAME)s()`"); - return real_%(EXPORT_NAME)s(arg); - } -})(); -''' % {'EXPORT_NAME': settings.EXPORT_NAME} - - # Given the async nature of how the Module function and Module object - # come into existence in AudioWorkletGlobalScope, store the Module - # function under a different variable name so that AudioWorkletGlobalScope - # will be able to reference it without aliasing/conflicting with the - # Module variable name. This should happen even in MINIMAL_RUNTIME builds - # for MODULARIZE and EXPORT_ES6 to work correctly. - if settings.AUDIO_WORKLET: - src += f'globalThis.AudioWorkletModule = {settings.EXPORT_NAME};\n' - - # Export using a UMD style export, or ES6 exports if selected - if settings.EXPORT_ES6: - if settings.MODULARIZE == 'instance': - exports = settings.EXPORTED_FUNCTIONS + settings.EXPORTED_RUNTIME_METHODS - # Declare a top level var for each export so that code in the init function - # can assign to it and update the live module bindings. - src += 'var ' + ', '.join(['__exp_' + export for export in exports]) + ';\n' - # Export the functions with their original name. - exports = ['__exp_' + export + ' as ' + export for export in exports] - src += 'export {' + ', '.join(exports) + '};\n' - else: - src += 'export default %s;\n' % settings.EXPORT_NAME - elif not settings.MINIMAL_RUNTIME: - src += '''\ -if (typeof exports === 'object' && typeof module === 'object') { - module.exports = %(EXPORT_NAME)s; - // This default export looks redundant, but it allows TS to import this - // commonjs style module. - module.exports.default = %(EXPORT_NAME)s; -} else if (typeof define === 'function' && define['amd']) - define([], () => %(EXPORT_NAME)s); -''' % {'EXPORT_NAME': settings.EXPORT_NAME} - - if settings.PTHREADS: - # Create code for detecting if we are running in a pthread. - # Normally this detection is done when the module is itself run but - # when running in MODULARIZE mode we need use this to know if we should - # run the module constructor on startup (true only for pthreads). - if settings.ENVIRONMENT_MAY_BE_WEB or settings.ENVIRONMENT_MAY_BE_WORKER: - src += "var isPthread = globalThis.self?.name?.startsWith('em-pthread');\n" - # In order to support both web and node we also need to detect node here. - if settings.ENVIRONMENT_MAY_BE_NODE: - src += "var isNode = typeof globalThis.process?.versions?.node == 'string';\n" - src += f'if (isNode) isPthread = {node_pthread_detection()}\n' - elif settings.ENVIRONMENT_MAY_BE_NODE: - src += f'var isPthread = {node_pthread_detection()}\n' - src += '// When running as a pthread, construct a new instance on startup\n' - if settings.MODULARIZE == 'instance': - src += 'isPthread && init();\n' - else: - src += 'isPthread && %s();\n' % settings.EXPORT_NAME - - final_js += '.modular.js' - write_file(final_js, src) - shared.get_temp_files().note(final_js) - save_intermediate('modularized') - - def module_export_name_substitution(): assert not settings.MODULARIZE global final_js diff --git a/tools/preprocessor.mjs b/tools/preprocessor.mjs index 8b15db9d2f03e..827c2f382c83f 100755 --- a/tools/preprocessor.mjs +++ b/tools/preprocessor.mjs @@ -31,7 +31,7 @@ loadSettingsFile(settingsFile); const parseTools = await import('../src/parseTools.mjs'); await import('../src/modules.mjs'); -let output = parseTools.preprocess(inputFile); +let output = parseTools.preprocess(inputFile, /*closureFriendly=*/false); if (expandMacros) { output = parseTools.processMacros(output, inputFile); }