Skip to content

Commit

Permalink
Detect and report late assignment of Module API elements
Browse files Browse the repository at this point in the history
We had some bug reports recently when we switched to using async/await
in MODULUARIZE mode.  One of the side effects was the `--post-js` code
then runs after module instantiation and startup, which means assignment
of properties like `onRuntimeInitialized` is too late if it happens in
`--post-js`.

This was always the case for sync instantiation too.

We now detect this too-late-assignment at abort with a useful error.

See emscripten-core#23626 and emscripten-core#23420
  • Loading branch information
sbc100 committed Feb 8, 2025
1 parent c7a3cc2 commit 602f717
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 0 deletions.
6 changes: 6 additions & 0 deletions src/postamble.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ function run() {
#endif
#if expectToReceiveOnModule('onRuntimeInitialized')
Module['onRuntimeInitialized']?.();
#if ASSERTIONS
consumedModuleProp('onRuntimeInitialized');
#endif
#endif

#if HAS_MAIN
Expand Down Expand Up @@ -297,6 +300,9 @@ if (Module['preInit']) {
Module['preInit'].pop()();
}
}
#if ASSERTIONS
consumedModuleProp('preInit');
#endif
#endif

run();
Expand Down
6 changes: 6 additions & 0 deletions src/preamble.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ function preRun() {
addOnPreRun(Module['preRun'].shift());
}
}
#if ASSERTIONS
consumedModuleProp('preRun');
#endif
#endif
<<< ATPRERUNS >>>
}
Expand Down Expand Up @@ -276,6 +279,9 @@ function postRun() {
addOnPostRun(Module['postRun'].shift());
}
}
#if ASSERTIONS
consumedModuleProp('postRun');
#endif
#endif

<<< ATPOSTRUNS >>>
Expand Down
12 changes: 12 additions & 0 deletions src/runtime_debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ function legacyModuleProp(prop, newName, incoming=true) {
}
}

function consumedModuleProp(prop) {
if (!Object.getOwnPropertyDescriptor(Module, prop)) {
Object.defineProperty(Module, prop, {
configurable: true,
set() {
abort(`Attempt to set \`Module.${prop}\` after it has already been processed. This can happen, for example, when code is injected via '--post-js' rather than '--pre-js'`);

}
});
}
}

function ignoredModuleProp(prop) {
if (Object.getOwnPropertyDescriptor(Module, prop)) {
abort(`\`Module.${prop}\` was supplied but \`${prop}\` not included in INCOMING_MODULE_JS_API`);
Expand Down
9 changes: 9 additions & 0 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -15612,3 +15612,12 @@ def test_instantiate_wasm(self):
return {}; // Compiling asynchronously, no exports.
}''')
self.do_runf('test_manual_wasm_instantiate.c', emcc_args=['--pre-js=pre.js'])

def test_late_module_api_assignment(self):
# When sync instantiation is used (or when async/await is used in MODULARIZE mode) certain
# Module properties cannot be assigned in `--post-js` code because its too late by the time
# it runs.
for prop in ('onRuntimeInitialized', 'postRun', 'preRun', 'preInit'):
create_file('post.js', f'Module.{prop} = () => console.log("will never fire since assigned too late")')
expected = f"Aborted(Attempt to set `Module.{prop}` after it has already been processed. This can happen, for example, when code is injected via '--post-js' rather than '--pre-js')"
self.do_runf(test_file('hello_world.c'), expected, emcc_args=['--post-js=post.js', '-sWASM_ASYNC_COMPILATION=0'], assert_returncode=NON_ZERO)

0 comments on commit 602f717

Please sign in to comment.