Skip to content

Commit 9e21488

Browse files
authored
Use mainScriptUrlOrBlob if present under EXPORT_ES6 (#23890)
Spun out from #23804 . This is a fix for #23769 which was introduced by #23171 , where the use of the bundler pattern hard-coding a script name was made to apply to all ES6 exports. `mainScriptUrlOrBlob` is necessary under es6 exports to support running a threaded emscripten module from another origin, e.g. to distribute an emscripten module on a CDN (the script will load for the main thread but won’t be allowed to create workers). Using blobs is an effective workaround.
1 parent 3374924 commit 9e21488

File tree

4 files changed

+72
-37
lines changed

4 files changed

+72
-37
lines changed

src/lib/libpthread.js

+9
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,15 @@ var LibraryPThread = {
423423
var p = trustedTypes.createPolicy('emscripten#workerPolicy1', { createScriptURL: (ignored) => import.meta.url });
424424
worker = new Worker(p.createScriptURL('ignored'), {{{ pthreadWorkerOptions }}});
425425
} else
426+
#endif
427+
#if expectToReceiveOnModule('mainScriptUrlOrBlob')
428+
if (Module['mainScriptUrlOrBlob']) {
429+
var pthreadMainJs = Module['mainScriptUrlOrBlob'];
430+
if (typeof pthreadMainJs != 'string') {
431+
pthreadMainJs = URL.createObjectURL(pthreadMainJs);
432+
}
433+
worker = new Worker(pthreadMainJs, {{{ pthreadWorkerOptions }}});
434+
} else
426435
#endif
427436
// We need to generate the URL with import.meta.url as the base URL of the JS file
428437
// instead of just using new URL(import.meta.url) because bundler's only recognize

test/pthread/main_js_as_blob_loader.html renamed to test/pthread/main_js_with_loader.html

+1-23
Original file line numberDiff line numberDiff line change
@@ -147,28 +147,6 @@
147147
};
148148
</script>
149149

150-
<script>
151-
152-
function download(url) {
153-
return fetch(url).then((rsp) => rsp.blob());
154-
}
155-
156-
function addToDom(scriptCodeBlob) {
157-
return new Promise((resolve, reject) => {
158-
var script = document.createElement('script');
159-
var objectUrl = URL.createObjectURL(scriptCodeBlob);
160-
script.src = objectUrl;
161-
script.onload = () => {
162-
Module['mainScriptUrlOrBlob'] = scriptCodeBlob;
163-
URL.revokeObjectURL(objectUrl);
164-
resolve();
165-
}
166-
document.body.appendChild(script);
167-
});
168-
}
169-
170-
download('hello_thread_with_blob_url.js').then(addToDom);
171-
172-
</script>
150+
<script type="module" src="loader.mjs"></script>
173151
</body>
174152
</html>

test/test_browser.py

+42-5
Original file line numberDiff line numberDiff line change
@@ -4763,18 +4763,55 @@ def test_pthread_growth(self, emcc_args):
47634763
def test_pthread_reltime(self):
47644764
self.btest_exit('pthread/test_pthread_reltime.cpp', emcc_args=['-pthread', '-sPTHREAD_POOL_SIZE'])
47654765

4766-
# Tests that it is possible to load the main .js file of the application manually via a Blob URL,
4766+
# Tests that it is possible to load the main .js file of the application manually via mainScriptUrlOrBlob,
47674767
# and still use pthreads.
4768-
def test_load_js_from_blob_with_pthreads(self):
4768+
@parameterized({
4769+
'blob': (False, True),
4770+
'url': (False, False),
4771+
'blob_es6': (True, True),
4772+
'url_es6': (True, False),
4773+
})
4774+
def test_mainScriptUrlOrBlob(self, es6, use_blob):
47694775
# TODO: enable this with wasm, currently pthreads/atomics have limitations
47704776
self.set_setting('EXIT_RUNTIME')
4777+
js_name = 'hello_thread_with_loader.%s' % ('mjs' if es6 else 'js')
4778+
if es6:
4779+
self.emcc_args += ['-sEXPORT_ES6']
4780+
if es6 and use_blob:
4781+
create_file('loader.mjs', '''
4782+
Module['locateFile'] = (path,_prefix) => path;
4783+
let blob = await (await fetch('hello_thread_with_loader.mjs')).blob();
4784+
Module['mainScriptUrlOrBlob'] = blob;
4785+
(await import(URL.createObjectURL(blob))).default(Module);
4786+
''')
4787+
elif use_blob:
4788+
create_file('loader.mjs', '''
4789+
let blob = await (await fetch('hello_thread_with_loader.js')).blob();
4790+
Module['mainScriptUrlOrBlob'] = blob;
4791+
var script = document.createElement('script');
4792+
script.src = URL.createObjectURL(blob);
4793+
document.body.appendChild(script);
4794+
''')
4795+
elif es6:
4796+
create_file('loader.mjs', '''
4797+
Module['mainScriptUrlOrBlob'] = 'hello_thread_with_loader.mjs';
4798+
(await import('./hello_thread_with_loader.mjs')).default(Module);
4799+
''')
4800+
else:
4801+
create_file('loader.mjs', '''
4802+
var script = document.createElement('script');
4803+
Module['mainScriptUrlOrBlob'] = 'hello_thread_with_loader.js';
4804+
script.src = Module['mainScriptUrlOrBlob'];
4805+
document.body.appendChild(script);
4806+
''')
4807+
47714808
self.compile_btest('pthread/hello_thread.c', ['-pthread', '-o', 'out.js'], reporting=Reporting.JS_ONLY)
47724809

47734810
# Now run the test with the JS file renamed and with its content
47744811
# stored in Module['mainScriptUrlOrBlob'].
4775-
shutil.move('out.js', 'hello_thread_with_blob_url.js')
4776-
shutil.copy(test_file('pthread/main_js_as_blob_loader.html'), 'hello_thread_with_blob_url.html')
4777-
self.run_browser('hello_thread_with_blob_url.html', '/report_result?exit:0')
4812+
shutil.move('out.js', js_name)
4813+
shutil.copy(test_file('pthread/main_js_with_loader.html'), 'hello_thread_with_loader.html')
4814+
self.run_browser('hello_thread_with_loader.html', '/report_result?exit:0')
47784815

47794816
# Tests that SINGLE_FILE works as intended in generated HTML (with and without Worker)
47804817
@also_with_proxying

test/test_other.py

+20-9
Original file line numberDiff line numberDiff line change
@@ -15866,15 +15866,26 @@ def test_rollup(self):
1586615866
def test_rlimit(self):
1586715867
self.do_other_test('test_rlimit.c', emcc_args=['-O1'])
1586815868

15869-
def test_mainScriptUrlOrBlob(self):
15869+
@parameterized({
15870+
'': (False,),
15871+
'es6': (True,),
15872+
})
15873+
def test_mainScriptUrlOrBlob(self, es6):
15874+
ext = "js"
15875+
args = []
15876+
if es6:
15877+
ext = "mjs"
15878+
args = ['-sEXPORT_ES6', '--extern-post-js', test_file('modularize_post_js.js')]
15879+
outfile = ('a.out.%s' % ext)
1587015880
# Use `foo.js` instead of the current script name when creating new threads
15871-
create_file('pre.js', 'Module = { mainScriptUrlOrBlob: "./foo.js" }')
15872-
self.run_process([EMCC, test_file('hello_world.c'), '-sEXIT_RUNTIME', '-sPROXY_TO_PTHREAD', '-pthread', '--pre-js=pre.js'])
15881+
create_file('pre.js', 'Module = { mainScriptUrlOrBlob: "./foo.%s" }' % ext)
1587315882

15874-
# First run without foo.js present to verify that the pthread creation fails
15875-
err = self.run_js('a.out.js', assert_returncode=NON_ZERO)
15876-
self.assertContained('Cannot find module.*foo.js', err, regex=True)
15883+
self.run_process([EMCC, test_file('hello_world.c'), '-sEXIT_RUNTIME', '-sPROXY_TO_PTHREAD', '-pthread', '--pre-js=pre.js', '-o', outfile] + args)
15884+
15885+
# First run without foo.[m]js present to verify that the pthread creation fails
15886+
err = self.run_js(outfile, assert_returncode=NON_ZERO)
15887+
self.assertContained('Cannot find module.*foo\\.', err, regex=True)
1587715888

15878-
# Now create foo.js and the program should run as expected.
15879-
shutil.copy('a.out.js', 'foo.js')
15880-
self.assertContained('hello, world', self.run_js('a.out.js'))
15889+
# Now create foo.[m]js and the program should run as expected.
15890+
shutil.copy(outfile, ('foo.%s' % ext))
15891+
self.assertContained('hello, world', self.run_js(outfile))

0 commit comments

Comments
 (0)