diff --git a/src/closure-externs/closure-externs.js b/src/closure-externs/closure-externs.js index 8199fe8460569..0a908316b56d7 100644 --- a/src/closure-externs/closure-externs.js +++ b/src/closure-externs/closure-externs.js @@ -11,10 +11,6 @@ * The closure_compiler() method in tools/shared.py refers to this file when calling closure. */ -// Special placeholder for `await import` and `await`. -var EMSCRIPTEN$AWAIT$IMPORT; -var EMSCRIPTEN$AWAIT; - // Don't minify createRequire var createRequire; diff --git a/src/closure-externs/modularize-externs.js b/src/closure-externs/modularize-externs.js index c0471f5ef49a4..4fddfdb23b8ef 100644 --- a/src/closure-externs/modularize-externs.js +++ b/src/closure-externs/modularize-externs.js @@ -17,3 +17,6 @@ var moduleArg; * @suppress {duplicate, undefinedVars} */ var Module; + +// Special placeholder for `await`. +var EMSCRIPTEN$AWAIT; diff --git a/src/parseTools.mjs b/src/parseTools.mjs index 61e4d20565de1..1826aa945e17b 100644 --- a/src/parseTools.mjs +++ b/src/parseTools.mjs @@ -48,7 +48,10 @@ function mangleUnsupportedSyntax(text) { // See also: `fix_js_mangling` in link.py. // FIXME: Remove after https://github.com/google/closure-compiler/issues/3835 is fixed. if (EXPORT_ES6) { - text = text.replaceAll('await import', 'EMSCRIPTEN$AWAIT$IMPORT'); + // Use a low-precedence `||` pattern so Closure doesn't strip parentheses. + // High-precedence placeholders trick Closure into optimizing `(PLACEHOLDER).y` + // into `PLACEHOLDER.y`, breaking execution order once swapped back to `await`. + text = text.replaceAll('await import', 'EMSCRIPTEN$AWAIT||import'); } text = text.replaceAll('await createWasm()', 'EMSCRIPTEN$AWAIT(createWasm())'); text = text.replaceAll('await run()', 'EMSCRIPTEN$AWAIT(run())'); diff --git a/test/test_other.py b/test/test_other.py index 6c8c26e4906d7..99fd4cca0f47a 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -467,6 +467,16 @@ def test_esm_worker_single_file(self): self.assertContained("new Worker(new URL('hello_world.mjs', import.meta.url), {", src) self.assertContained('Hello, world!', self.run_js('hello_world.mjs')) + def test_esm_worker_closure(self): + self.run_process([EMCC, '-o', 'hello_world.mjs', + '-sEXIT_RUNTIME', '-sPROXY_TO_PTHREAD', '-pthread', '-O2', + test_file('hello_world.c'), '--closure=1']) + create_file('run.mjs', ''' + import Module from './hello_world.mjs'; + await Module(); + ''') + self.assertContained('Hello, world!', self.run_js('run.mjs')) + def test_esm_closure(self): self.run_process([EMCC, '-o', 'hello_world.mjs', '--extern-post-js', test_file('modularize_post_js.js'), diff --git a/tools/link.py b/tools/link.py index 110669e4dfbfa..db52cd9ced9ba 100644 --- a/tools/link.py +++ b/tools/link.py @@ -2129,8 +2129,8 @@ def phase_source_transforms(options): save_intermediate('transformed') -# Unmangle previously mangled `await import` and `await` references in -# both main code and libraries. +# Unmangle previously mangled `await` references in both +# main code and libraries. # See also: `mangleUnsupportedSyntax` in parseTools.mjs. def fix_js_mangling(js_file): # Mangling only takes place under closure in MODULARIZE mode. @@ -2140,13 +2140,14 @@ def fix_js_mangling(js_file): src = read_file(js_file) if settings.EXPORT_ES6: - # Also remove the line containing `export{};`, which is inserted by + # Safely matches 'EMSCRIPTEN$AWAIT' with any amount of surrounding + # whitespace around '||' + src = re.sub(r'EMSCRIPTEN\$AWAIT\s*\|\|\s*', 'await ', src) + # Remove the line containing `export{};`, which is inserted by # Closure to mark the file as an ES6 module. # https://github.com/google/closure-compiler/issues/4084#issuecomment-1505056519 # https://github.com/google/closure-compiler/blob/v20260401/src/com/google/javascript/jscomp/ConvertChunksToESModules.java#L111-L113 - src = src \ - .replace('EMSCRIPTEN$AWAIT$IMPORT', 'await import') \ - .replace('export{};\n', '') + src = src.replace('export{};\n', '') src = src.replace('EMSCRIPTEN$AWAIT(', 'await (')