Skip to content

Commit efec622

Browse files
authored
Add experimental option to use source wasm phase imports (#23175)
Now that node support has been landed we can test this, at least against the latest node canary builds. See nodejs/node#56919 Fixes: #23047
1 parent 4c5982b commit efec622

File tree

6 files changed

+68
-10
lines changed

6 files changed

+68
-10
lines changed

ChangeLog.md

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ See docs/process.md for more on how version tagging works.
2020

2121
4.0.5 (in development)
2222
----------------------
23+
- Added initial support for wasm source phase imports via
24+
`-sSOURCE_PHASE_IMPORTS`. This is currently experimental and not yet
25+
implemented in browsers. (#23175)
2326

2427
4.0.4 - 02/25/25
2528
----------------

site/source/docs/tools_reference/settings_reference.rst

+12
Original file line numberDiff line numberDiff line change
@@ -3347,3 +3347,15 @@ Use _ for non-pointer arguments, p for pointer/i53 arguments, and P for optional
33473347
Example use -sSIGNATURE_CONVERSIONS=someFunction:_p,anotherFunction:p
33483348

33493349
Default value: []
3350+
3351+
.. _source_phase_imports:
3352+
3353+
SOURCE_PHASE_IMPORTS
3354+
====================
3355+
3356+
Experimental support for wasm source phase imports.
3357+
This is only currently implemented in the pre-release/nightly version of node,
3358+
and not yet supported by browsers.
3359+
Requires EXPORT_ES6
3360+
3361+
Default value: false

src/preamble.js

+28-8
Original file line numberDiff line numberDiff line change
@@ -558,11 +558,32 @@ function instrumentWasmTableWithAbort() {
558558
}
559559
#endif
560560

561+
#if LOAD_SOURCE_MAP
562+
function receiveSourceMapJSON(sourceMap) {
563+
wasmSourceMap = new WasmSourceMap(sourceMap);
564+
{{{ runIfMainThread("removeRunDependency('source-map');") }}}
565+
}
566+
#endif
567+
568+
#if (PTHREADS || WASM_WORKERS) && (LOAD_SOURCE_MAP || USE_OFFSET_CONVERTER)
569+
// When using postMessage to send an object, it is processed by the structured
570+
// clone algorithm. The prototype, and hence methods, on that object is then
571+
// lost. This function adds back the lost prototype. This does not work with
572+
// nested objects that has prototypes, but it suffices for WasmSourceMap and
573+
// WasmOffsetConverter.
574+
function resetPrototype(constructor, attrs) {
575+
var object = Object.create(constructor.prototype);
576+
return Object.assign(object, attrs);
577+
}
578+
#endif
579+
580+
#if !SOURCE_PHASE_IMPORTS
561581
#if SINGLE_FILE
562582
// In SINGLE_FILE mode the wasm binary is encoded inline here as a data: URL.
563583
var wasmBinaryFile = '{{{ WASM_BINARY_FILE }}}';
564584
#else
565585
var wasmBinaryFile;
586+
566587
function findWasmBinary() {
567588
#if EXPORT_ES6 && !AUDIO_WORKLET
568589
if (Module['locateFile']) {
@@ -647,13 +668,6 @@ var splitModuleProxyHandler = {
647668
};
648669
#endif
649670

650-
#if LOAD_SOURCE_MAP
651-
function receiveSourceMapJSON(sourceMap) {
652-
wasmSourceMap = new WasmSourceMap(sourceMap);
653-
{{{ runIfMainThread("removeRunDependency('source-map');") }}}
654-
}
655-
#endif
656-
657671
#if SPLIT_MODULE || !WASM_ASYNC_COMPILATION
658672
function instantiateSync(file, info) {
659673
var module;
@@ -803,6 +817,7 @@ async function instantiateAsync(binary, binaryFile, imports) {
803817
return instantiateArrayBuffer(binaryFile, imports);
804818
}
805819
#endif // WASM_ASYNC_COMPILATION
820+
#endif // SOURCE_PHASE_IMPORTS
806821

807822
function getWasmImports() {
808823
#if PTHREADS
@@ -1004,10 +1019,14 @@ function getWasmImports() {
10041019
}
10051020
#endif
10061021

1022+
#if SOURCE_PHASE_IMPORTS
1023+
var instance = await WebAssembly.instantiate(wasmModule, info);
1024+
var exports = receiveInstantiationResult({instance, 'module':wasmModule});
1025+
return exports;
1026+
#else
10071027
#if !SINGLE_FILE
10081028
wasmBinaryFile ??= findWasmBinary();
10091029
#endif
1010-
10111030
#if WASM_ASYNC_COMPILATION
10121031
#if RUNTIME_DEBUG
10131032
dbg('asynchronously preparing wasm');
@@ -1039,6 +1058,7 @@ function getWasmImports() {
10391058
return receiveInstance(result[0]);
10401059
#endif
10411060
#endif // WASM_ASYNC_COMPILATION
1061+
#endif // SOURCE_PHASE_IMPORTS
10421062
}
10431063

10441064
#if !WASM_BIGINT

src/settings.js

+7
Original file line numberDiff line numberDiff line change
@@ -2182,6 +2182,13 @@ var LEGACY_RUNTIME = false;
21822182
// [link]
21832183
var SIGNATURE_CONVERSIONS = [];
21842184

2185+
// Experimental support for wasm source phase imports.
2186+
// This is only currently implemented in the pre-release/nightly version of node,
2187+
// and not yet supported by browsers.
2188+
// Requires EXPORT_ES6
2189+
// [link]
2190+
var SOURCE_PHASE_IMPORTS = false;
2191+
21852192
// For renamed settings the format is:
21862193
// [OLD_NAME, NEW_NAME]
21872194
// For removed settings (which now effectively have a fixed value and can no

test/test_other.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -358,13 +358,23 @@ def test_emcc_generate_config(self, compiler):
358358
@parameterized({
359359
'': ([],),
360360
'node': (['-sENVIRONMENT=node'],),
361+
# load a worker before startup to check ES6 modules there as well
362+
'pthreads': (['-pthread', '-sPTHREAD_POOL_SIZE=1'],),
361363
})
362364
def test_esm(self, args):
363365
self.run_process([EMCC, '-o', 'hello_world.mjs',
364366
'--extern-post-js', test_file('modularize_post_js.js'),
365367
test_file('hello_world.c')] + args)
366-
src = read_file('hello_world.mjs')
367-
self.assertContained('export default Module;', src)
368+
self.assertContained('export default Module;', read_file('hello_world.mjs'))
369+
self.assertContained('hello, world!', self.run_js('hello_world.mjs'))
370+
371+
@requires_node_canary
372+
def test_esm_source_phase_imports(self):
373+
self.node_args += ['--experimental-wasm-modules']
374+
self.run_process([EMCC, '-o', 'hello_world.mjs', '-sSOURCE_PHASE_IMPORTS',
375+
'--extern-post-js', test_file('modularize_post_js.js'),
376+
test_file('hello_world.c')])
377+
self.assertContained('import source wasmModule from', read_file('hello_world.mjs'))
368378
self.assertContained('hello, world!', self.run_js('hello_world.mjs'))
369379

370380
@parameterized({

tools/link.py

+6
Original file line numberDiff line numberDiff line change
@@ -1757,6 +1757,9 @@ def get_full_import_name(name):
17571757
if settings.ASYNCIFY == 2:
17581758
diagnostics.warning('experimental', '-sASYNCIFY=2 (JSPI) is still experimental')
17591759

1760+
if settings.SOURCE_PHASE_IMPORTS:
1761+
diagnostics.warning('experimental', '-sSOURCE_PHASE_IMPORTS is still experimental and not yet supported in browsers')
1762+
17601763
if settings.WASM2JS:
17611764
if settings.GENERATE_SOURCE_MAP:
17621765
exit_with_error('wasm2js does not support source maps yet (debug in wasm for now)')
@@ -2478,6 +2481,9 @@ def modularize():
24782481
})();
24792482
''' % {'EXPORT_NAME': settings.EXPORT_NAME}
24802483

2484+
if settings.SOURCE_PHASE_IMPORTS:
2485+
src = f"import source wasmModule from './{settings.WASM_BINARY_FILE}';\n\n" + src
2486+
24812487
# Given the async nature of how the Module function and Module object
24822488
# come into existence in AudioWorkletGlobalScope, store the Module
24832489
# function under a different variable name so that AudioWorkletGlobalScope

0 commit comments

Comments
 (0)