Skip to content

Commit 3106ca5

Browse files
committed
Allow the createWasm function to use async/await where possible
The advantage if using `await` in the cases where we can is that it avoids the generation or wrapper function for each export. So instead of ``` var wasmExport = createWasm(); // returns empty object ... var malloc = ((..) => (malloc = wasmExports['malloc'])() ``` We can just generate: ``` var wasmExport = createWasm(); // returns empty object ... var malloc = wasmExports['malloc']; ``` This only currently works in MODULARIZE mode where the code is running inside a factory function. Otherwise it would end up using top-level-await. One wrinkle here is that this is not currently supported when closure is enabled because we run closure only on the contents of the factory function so closure ends up seeing this as a top level await when its not.
1 parent 399a077 commit 3106ca5

File tree

5 files changed

+42
-51
lines changed

5 files changed

+42
-51
lines changed

src/preamble.js

+6-7
Original file line numberDiff line numberDiff line change
@@ -1036,11 +1036,11 @@ function getWasmImports() {
10361036
trueModule = null;
10371037
#endif
10381038
#if SHARED_MEMORY || RELOCATABLE
1039-
receiveInstance(result['instance'], result['module']);
1039+
return receiveInstance(result['instance'], result['module']);
10401040
#else
10411041
// TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193, the above line no longer optimizes out down to the following line.
10421042
// When the regression is fixed, can restore the above PTHREADS-enabled path.
1043-
receiveInstance(result['instance']);
1043+
return receiveInstance(result['instance']);
10441044
#endif
10451045
}
10461046
#endif // WASM_ASYNC_COMPILATION
@@ -1076,8 +1076,7 @@ function getWasmImports() {
10761076
// Instantiate from the module posted from the main thread.
10771077
// We can just use sync instantiation in the worker.
10781078
var instance = new WebAssembly.Instance(module, getWasmImports());
1079-
receiveInstance(instance, module);
1080-
resolve();
1079+
resolve(receiveInstance(instance, module));
10811080
};
10821081
});
10831082
}
@@ -1095,16 +1094,16 @@ function getWasmImports() {
10951094
try {
10961095
#endif
10971096
var result = await instantiateAsync(wasmBinary, wasmBinaryFile, info);
1098-
receiveInstantiationResult(result);
1097+
var exports = receiveInstantiationResult(result);
10991098
#if LOAD_SOURCE_MAP
11001099
receiveSourceMapJSON(await getSourceMapAsync());
11011100
#endif
1102-
return result;
1101+
return exports;
11031102
#if MODULARIZE
11041103
} catch (e) {
11051104
// If instantiation fails, reject the module ready promise.
11061105
readyPromiseReject(e);
1107-
return;
1106+
return Promise.reject(e);
11081107
}
11091108
#endif
11101109
#else // WASM_ASYNC_COMPILATION

test/test_browser.py

+13-22
Original file line numberDiff line numberDiff line change
@@ -4795,28 +4795,19 @@ def test_browser_run_from_different_directory(self):
47954795

47964796
# Similar to `test_browser_run_from_different_directory`, but asynchronous because of `-sMODULARIZE`
47974797
def test_browser_run_from_different_directory_async(self):
4798-
for args, creations in [
4799-
(['-sMODULARIZE'], [
4800-
'Module();', # documented way for using modularize
4801-
'new Module();' # not documented as working, but we support it
4802-
]),
4803-
]:
4804-
print(args)
4805-
# compile the code with the modularize feature and the preload-file option enabled
4806-
self.compile_btest('browser_test_hello_world.c', ['-o', 'test.js', '-O3'] + args)
4807-
ensure_dir('subdir')
4808-
shutil.move('test.js', Path('subdir/test.js'))
4809-
shutil.move('test.wasm', Path('subdir/test.wasm'))
4810-
for creation in creations:
4811-
print(creation)
4812-
# Make sure JS is loaded from subdirectory
4813-
create_file('test-subdir.html', '''
4814-
<script src="subdir/test.js"></script>
4815-
<script>
4816-
%s
4817-
</script>
4818-
''' % creation)
4819-
self.run_browser('test-subdir.html', '/report_result?0')
4798+
# compile the code with the modularize feature and the preload-file option enabled
4799+
self.compile_btest('browser_test_hello_world.c', ['-o', 'test.js', '-O3', '-sMODULARIZE'])
4800+
ensure_dir('subdir')
4801+
shutil.move('test.js', Path('subdir/test.js'))
4802+
shutil.move('test.wasm', Path('subdir/test.wasm'))
4803+
# Make sure JS is loaded from subdirectory
4804+
create_file('test-subdir.html', '''
4805+
<script src="subdir/test.js"></script>
4806+
<script>
4807+
Module();
4808+
</script>
4809+
''')
4810+
self.run_browser('test-subdir.html', '/report_result?0')
48204811

48214812
# Similar to `test_browser_run_from_different_directory`, but
48224813
# also also we eval the initial code, so currentScript is not present. That prevents us

test/test_unicode_js_library.out

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
Unicode postjs: àČšñéáúÍųåêâăščếệüçλληνικάбългарскиPусскийСрпскиУкраїнська한국어中文普通话(中国大陆)普通话(香港)中文(台灣)粵語(香港)日本語हिन्दीภาษาไทย
21
Unicode snowman ☃ says hello! àČšñéáúÍųåêâăščếệüçλληνικάбългарскиPусскийСрпскиУкраїнська한국어中文普通话(中国大陆)普通话(香港)中文(台灣)粵語(香港)日本語हिन्दीภาษาไทย
2+
Unicode postjs: àČšñéáúÍųåêâăščếệüçλληνικάбългарскиPусскийСрпскиУкраїнська한국어中文普通话(中国大陆)普通话(香港)中文(台灣)粵語(香港)日本語हिन्दीภาษาไทย

tools/emscripten.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,14 @@ def create_sending(metadata, library_symbols):
893893
return '{\n ' + ',\n '.join(elems) + '\n}'
894894

895895

896+
def can_use_await():
897+
# In modularize mode we can use `await` since the factor function itself
898+
# is marked as `async` and the generated code all lives inside that factory
899+
# function.
900+
# FIXME(https://github.com/emscripten-core/emscripten/issues/23158)
901+
return settings.MODULARIZE and not settings.USE_CLOSURE_COMPILER
902+
903+
896904
def make_export_wrappers(function_exports):
897905
assert not settings.MINIMAL_RUNTIME
898906

@@ -932,7 +940,7 @@ def install_wrapper(sym):
932940
# With assertions enabled we create a wrapper that are calls get routed through, for
933941
# the lifetime of the program.
934942
wrapper += f"createExportWrapper('{name}', {nargs});"
935-
elif settings.WASM_ASYNC_COMPILATION or settings.PTHREADS:
943+
elif (settings.WASM_ASYNC_COMPILATION and not can_use_await()) or settings.PTHREADS:
936944
# With WASM_ASYNC_COMPILATION wrapper will replace the global var and Module var on
937945
# first use.
938946
args = [f'a{i}' for i in range(nargs)]
@@ -998,7 +1006,11 @@ def create_module(receiving, metadata, global_exports, library_symbols):
9981006

9991007
if not settings.MINIMAL_RUNTIME:
10001008
if settings.WASM_ASYNC_COMPILATION:
1001-
module.append("var wasmExports;\ncreateWasm();\n")
1009+
if can_use_await():
1010+
# In modullize mode the generated code is within a factory function.
1011+
module.append("var wasmExports = await createWasm();\n")
1012+
else:
1013+
module.append("var wasmExports;\ncreateWasm();\n")
10021014
else:
10031015
module.append("var wasmExports = createWasm();\n")
10041016

tools/link.py

+8-19
Original file line numberDiff line numberDiff line change
@@ -2379,28 +2379,20 @@ def modularize():
23792379
logger.debug(f'Modularizing, assigning to var {settings.EXPORT_NAME}')
23802380
src = read_file(final_js)
23812381

2382-
# When targetting node and ES6 we use `await import ..` in the generated code
2383-
# so the outer function needs to be marked as async.
2384-
async_emit = ''
2385-
if settings.EXPORT_ES6 and settings.ENVIRONMENT_MAY_BE_NODE:
2386-
async_emit = 'async '
2387-
2388-
# TODO: Remove when https://bugs.webkit.org/show_bug.cgi?id=223533 is resolved.
2389-
if async_emit != '' and settings.EXPORT_NAME == 'config':
2390-
diagnostics.warning('emcc', 'EXPORT_NAME should not be named "config" when targeting Safari')
2391-
2382+
if settings.WASM_ASYNC_COMPILATION or (settings.EXPORT_ES6 and settings.ENVIRONMENT_MAY_BE_NODE):
2383+
maybe_async = 'async '
2384+
else:
2385+
maybe_async = ''
23922386
if settings.MODULARIZE == 'instance':
23932387
src = '''
2394-
export default async function init(moduleArg = {}) {
2388+
export default %(maybe_async)sfunction init(moduleArg = {}) {
23952389
var moduleRtn;
23962390
23972391
%(src)s
23982392
2399-
return await moduleRtn;
2393+
return moduleRtn;
24002394
}
2401-
''' % {
2402-
'src': src,
2403-
}
2395+
''' % {'src': src, 'maybe_async': maybe_async}
24042396
else:
24052397
src = '''
24062398
%(maybe_async)sfunction(moduleArg = {}) {
@@ -2410,10 +2402,7 @@ def modularize():
24102402
24112403
return moduleRtn;
24122404
}
2413-
''' % {
2414-
'maybe_async': async_emit,
2415-
'src': src,
2416-
}
2405+
''' % {'src': src, 'maybe_async': maybe_async}
24172406

24182407
if settings.MINIMAL_RUNTIME and not settings.PTHREADS:
24192408
# Single threaded MINIMAL_RUNTIME programs do not need access to

0 commit comments

Comments
 (0)