Skip to content

Commit 1887cc4

Browse files
committed
Fix broken JS glue for AUDIO_WORKLETS with EXPORT_ES6
WASM Audio Worklets with EXPORT_ES6 may break at runtime: test.js:989 Uncaught (in promise) TypeError: Cannot set property wasmTable of #<Object> which has only a getter at receiveInstance (test.js:989:25) at receiveInstantiationResult (test.js:1011:5) The read-only getter at issue is created in ASSERTIONS-enabled builds, and conflicts with the current way of exporting wasmTable on the Module object. Exporting wasmTable via EXPORTED_RUNTIME_METHODS prevents the getter from being created in normal builds. In MINIMAL_RUNTIME builds, we make sure to delete the getter before manually exporting as before. We also prevent an ES6 Audio Worklet from loading the .wasm binary via `new URL()`, as `URL` is unavailable in AudioWorkletGlobalScope.
1 parent aef8142 commit 1887cc4

File tree

4 files changed

+22
-22
lines changed

4 files changed

+22
-22
lines changed

src/postamble_minimal.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -197,19 +197,19 @@ WebAssembly.instantiate(Module['wasm'], imports).then((output) => {
197197
#if AUDIO_WORKLET
198198
// If we are in the audio worklet environment, we can only access the Module object
199199
// and not the global scope of the main JS script. Therefore we need to export
200-
// all functions that the audio worklet scope needs onto the Module object.
201-
Module['wasmTable'] = wasmTable;
200+
// all symbols that the audio worklet scope needs onto the Module object.
202201
#if ASSERTIONS
203-
// In ASSERTIONS-enabled builds, the following symbols have gotten read-only getters
204-
// saved to the Module. Remove those getters so we can manually export the stack
205-
// functions here.
202+
// In ASSERTIONS-enabled builds, the needed symbols have gotten read-only getters
203+
// saved to the Module. Remove the getters so we can manually export them here.
206204
delete Module['stackSave'];
207205
delete Module['stackAlloc'];
208206
delete Module['stackRestore'];
207+
delete Module['wasmTable'];
209208
#endif
210209
Module['stackSave'] = stackSave;
211210
Module['stackAlloc'] = stackAlloc;
212211
Module['stackRestore'] = stackRestore;
212+
Module['wasmTable'] = wasmTable;
213213
#endif
214214

215215
#if !IMPORTED_MEMORY

src/preamble.js

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -608,14 +608,14 @@ function instrumentWasmTableWithAbort() {
608608
#endif
609609

610610
var wasmBinaryFile;
611-
#if EXPORT_ES6 && USE_ES6_IMPORT_META && !SINGLE_FILE
611+
#if EXPORT_ES6 && USE_ES6_IMPORT_META && !SINGLE_FILE && !AUDIO_WORKLET
612612
if (Module['locateFile']) {
613613
#endif
614614
wasmBinaryFile = '{{{ WASM_BINARY_FILE }}}';
615615
if (!isDataURI(wasmBinaryFile)) {
616616
wasmBinaryFile = locateFile(wasmBinaryFile);
617617
}
618-
#if EXPORT_ES6 && USE_ES6_IMPORT_META && !SINGLE_FILE // in single-file mode, repeating WASM_BINARY_FILE would emit the contents again
618+
#if EXPORT_ES6 && USE_ES6_IMPORT_META && !SINGLE_FILE && !AUDIO_WORKLET // In single-file mode, repeating WASM_BINARY_FILE would emit the contents again. For an Audio Worklet, we cannot use `new URL()`.
619619
} else {
620620
#if ENVIRONMENT_MAY_BE_SHELL
621621
if (ENVIRONMENT_IS_SHELL)
@@ -1000,13 +1000,6 @@ function createWasm() {
10001000
#if ASSERTIONS && !PURE_WASI
10011001
assert(wasmTable, 'table not found in wasm exports');
10021002
#endif
1003-
1004-
#if AUDIO_WORKLET
1005-
// If we are in the audio worklet environment, we can only access the Module object
1006-
// and not the global scope of the main JS script. Therefore we need to export
1007-
// all functions that the audio worklet scope needs onto the Module object.
1008-
Module['wasmTable'] = wasmTable;
1009-
#endif
10101003
#endif
10111004

10121005
#if hasExportedSymbol('__wasm_call_ctors')

test/test_browser.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5747,6 +5747,8 @@ def test_full_js_library_strict(self):
57475747
'pthreads_and_closure': (['-pthread', '--closure', '1', '-Oz'],),
57485748
'minimal_runtime': (['-sMINIMAL_RUNTIME'],),
57495749
'minimal_runtime_pthreads_and_closure': (['-sMINIMAL_RUNTIME', '-pthread', '--closure', '1', '-Oz'],),
5750+
'pthreads_es6': (['-pthread', '-sPTHREAD_POOL_SIZE=2', '-sEXPORT_ES6'],),
5751+
'es6': (['-sEXPORT_ES6'],),
57505752
})
57515753
def test_audio_worklet(self, args):
57525754
if '-sMEMORY64' in args and is_firefox():

tools/link.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,9 +1339,12 @@ def phase_linker_setup(options, state, newargs):
13391339
settings.AUDIO_WORKLET_FILE = unsuffixed(os.path.basename(target)) + '.aw.js'
13401340
settings.JS_LIBRARIES.append((0, shared.path_from_root('src', 'library_webaudio.js')))
13411341
if not settings.MINIMAL_RUNTIME:
1342+
# If we are in the audio worklet environment, we can only access the Module object
1343+
# and not the global scope of the main JS script. Therefore we need to export
1344+
# all symbols that the audio worklet scope needs onto the Module object.
13421345
# MINIMAL_RUNTIME exports these manually, since this export mechanism is placed
13431346
# in global scope that is not suitable for MINIMAL_RUNTIME loader.
1344-
settings.EXPORTED_RUNTIME_METHODS += ['stackSave', 'stackAlloc', 'stackRestore']
1347+
settings.EXPORTED_RUNTIME_METHODS += ['stackSave', 'stackAlloc', 'stackRestore', 'wasmTable']
13451348

13461349
if settings.FORCE_FILESYSTEM and not settings.MINIMAL_RUNTIME:
13471350
# when the filesystem is forced, we export by default methods that filesystem usage
@@ -2349,13 +2352,15 @@ def modularize():
23492352
'script_url_node': script_url_node,
23502353
'src': src,
23512354
}
2352-
# Given the async nature of how the Module function and Module object
2353-
# come into existence in AudioWorkletGlobalScope, store the Module
2354-
# function under a different variable name so that AudioWorkletGlobalScope
2355-
# will be able to reference it without aliasing/conflicting with the
2356-
# Module variable name.
2357-
if settings.AUDIO_WORKLET and settings.MODULARIZE:
2358-
src += f'globalThis.AudioWorkletModule = {settings.EXPORT_NAME};'
2355+
2356+
# Given the async nature of how the Module function and Module object
2357+
# come into existence in AudioWorkletGlobalScope, store the Module
2358+
# function under a different variable name so that AudioWorkletGlobalScope
2359+
# will be able to reference it without aliasing/conflicting with the
2360+
# Module variable name. This should happen even in MINIMAL_RUNTIME builds
2361+
# for MODULARIZE and EXPORT_ES6 to work correctly.
2362+
if settings.AUDIO_WORKLET and settings.MODULARIZE:
2363+
src += f'globalThis.AudioWorkletModule = {settings.EXPORT_NAME};'
23592364

23602365
# Export using a UMD style export, or ES6 exports if selected
23612366
if settings.EXPORT_ES6:

0 commit comments

Comments
 (0)