Skip to content

Commit 6b9cce3

Browse files
authored
[MODULARIZE=instance] Document lack of global Module object (#24326)
Also, disabled tests that use the `Module` object internally. Because of this ccall/cwrap also don't work. It is conceivable that we could make this work in the future.
1 parent 1cb178b commit 6b9cce3

File tree

5 files changed

+70
-12
lines changed

5 files changed

+70
-12
lines changed

.circleci/config.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,8 @@ jobs:
811811
instance.test_iostream_and_determinism
812812
instance.test_fannkuch
813813
instance.test_fasta
814+
instance.test_EXPORTED_RUNTIME_METHODS
815+
instance.test_ccall
814816
esm_integration.test_fs_js_api*
815817
esm_integration.test_inlinejs3
816818
esm_integration.test_embind_val_basics

site/source/docs/compiling/Modularized-Output.rst

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ modularization, and not the ability to create multiple instances. In this
9595
mode a module is created that exports a single instance of the program rather
9696
than a factory function.
9797

98-
This setting only works when :ref:`export_es6` is enabled.
98+
This setting implicitly enables :ref:`export_es6` and will not work when
99+
:ref:`export_es6` is explictly disabled.
99100

100101
In this mode the default export of the module is an initializer function which
101102
allows input parameters to be passed to the instance. Other elements normally
@@ -108,6 +109,19 @@ found on the module object are instead exported directly. For example:
108109
await init({ print: myPrint });
109110
_nativeMethod();
110111

112+
Limitations
113+
-----------
114+
115+
Some major features still do not work in this mode. Many of these we hope to
116+
fix in future releses. Current limitations include:
117+
118+
* Internal usage (e.g. usage within EM_JS / JS libary code) of the `Module`
119+
object does not work. This is because symbols are exported directly using
120+
ES6 module syntax rathar than using a global `Module` object.
121+
122+
* `ccall`/`cwrap`, these depend on the internal `Module` object.
123+
124+
111125
Source Phase Imports (experimental)
112126
===================================
113127

@@ -117,7 +131,8 @@ the auto-generated code for finding and fetching the Wasm binary.
117131

118132
See :ref:`source_phase_imports`.
119133

120-
This setting only works when :ref:`export_es6` is enabled.
134+
This setting implicitly enables :ref:`export_es6` and will not work when
135+
:ref:`export_es6` is explictly disabled.
121136

122137

123138
ES Module Integration (experimental)
@@ -129,7 +144,12 @@ boilerplate code for linking up Wasm and JavaScript.
129144

130145
See :ref:`wasm_esm_integration`.
131146

132-
This setting only works when :ref:`export_es6` is enabled.
147+
Limitations
148+
-----------
149+
150+
This setting implicitly enables :ref:`export_es6` and sets :ref:`MODULARIZE` to
151+
``instance``. Because of this all the same limitations mentioned above for
152+
``-sMODULARIZE=intance`` apply.
133153

134154
.. _Source phase imports: https://github.com/tc39/proposal-source-phase-imports
135155
.. _Wasm ESM integration: https://github.com/WebAssembly/esm-integration

src/modules.mjs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -416,15 +416,16 @@ function exportSymbol(name) {
416416
// export parts of the JS runtime that the user asked for
417417
function exportRuntimeSymbols() {
418418
// optionally export something.
419-
function maybeExport(name) {
419+
function shouldExport(name) {
420420
// If requested to be exported, export it.
421421
if (EXPORTED_RUNTIME_METHODS.has(name)) {
422422
// Unless we are in MODULARIZE=instance mode then HEAP objects are
423423
// exported separately in updateMemoryViews
424424
if (MODULARIZE == 'instance' || !name.startsWith('HEAP')) {
425-
return exportSymbol(name);
425+
return true;
426426
}
427427
}
428+
return false;
428429
}
429430

430431
// All possible runtime elements that can be exported
@@ -521,8 +522,8 @@ function exportRuntimeSymbols() {
521522
}
522523
}
523524

524-
const exports = runtimeElements.map(maybeExport);
525-
const results = exports.filter((name) => name);
525+
const exports = runtimeElements.filter(shouldExport);
526+
const results = exports.map(exportSymbol);
526527

527528
if (MODULARIZE == 'instance') {
528529
if (results.length == 0) return '';

test/core/EXPORTED_RUNTIME_METHODS.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ int main() {
1717
EM_ASM({
1818
#if EXPORTED
1919
// test for additional things being exported
20-
assert(Module['addFunction']);
21-
assert(Module['lengthBytesUTF8']);
20+
assert(Module['addFunction'], 'missing addFunction export');
21+
assert(Module['lengthBytesUTF8'], 'missing lengthBytesUTF8 export');
2222
Module['setTempRet0'](42);
2323
assert(Module['getTempRet0']() == 42);
2424
// the main test here
@@ -28,9 +28,9 @@ int main() {
2828
// stubs that show a useful error if called. So it is only meaningful
2929
// to check they don't exist when assertions are disabled.
3030
if (!ASSERTIONS) {
31-
assert(!Module['addFunction']);
32-
assert(!Module['lengthBytesUTF8']);
33-
assert(!Module['dynCall']);
31+
assert(!Module['addFunction'], 'missing addFunction export');
32+
assert(!Module['lengthBytesUTF8'], 'missing lengthBytesUTF8 export');
33+
assert(!Module['dynCall'], 'missing dynCall export');
3434
}
3535
dynCall('viii', $0, [1, 4, 9]);
3636
#endif

test/test_core.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,22 @@ def decorated(self, *args, **kwargs):
5151
return decorated
5252

5353

54+
def no_modularize_instance(note):
55+
assert not callable(note)
56+
57+
def decorator(f):
58+
assert callable(f)
59+
60+
@wraps(f)
61+
def decorated(self, *args, **kwargs):
62+
if self.get_setting('MODULARIZE') == 'instance' or self.get_setting('WASM_ESM_INTEGRATION'):
63+
self.skipTest(note)
64+
f(self, *args, **kwargs)
65+
return decorated
66+
67+
return decorator
68+
69+
5470
def wasm_simd(f):
5571
assert callable(f)
5672

@@ -1745,6 +1761,7 @@ def test_mod_globalstruct(self):
17451761
def test_sizeof(self):
17461762
self.do_core_test('test_sizeof.c')
17471763

1764+
@no_modularize_instance('uses Module object directly')
17481765
def test_llvm_used(self):
17491766
self.do_core_test('test_llvm_used.c')
17501767

@@ -1754,6 +1771,7 @@ def test_set_align(self):
17541771

17551772
self.do_core_test('test_set_align.c')
17561773

1774+
@no_modularize_instance('uses Module object directly')
17571775
def test_emscripten_api(self):
17581776
self.set_setting('EXPORTED_FUNCTIONS', ['_main', '_save_me_aimee'])
17591777
self.do_core_test('test_emscripten_api.c')
@@ -5347,6 +5365,7 @@ def test_sscanf_float(self):
53475365
def test_langinfo(self):
53485366
self.do_core_test('test_langinfo.c')
53495367

5368+
@no_modularize_instance('uses Module object directly')
53505369
def test_files(self):
53515370
# Use closure here, to test we don't break FS stuff
53525371
if '-O3' in self.emcc_args and self.is_wasm2js():
@@ -6217,6 +6236,7 @@ def test_jslib(self):
62176236

62186237
@with_env_modify({'LC_ALL': 'latin-1', 'PYTHONUTF8': '0', 'PYTHONCOERCECLOCALE': '0'})
62196238
@crossplatform
6239+
@no_modularize_instance('uses MODULARIZE')
62206240
def test_unicode_js_library(self):
62216241
# First verify that we have correct overridden the default python file encoding.
62226242
# The follow program should fail, assuming the above LC_CTYPE + PYTHONUTF8
@@ -6890,6 +6910,7 @@ def test_autodebug_wasm(self):
68906910
### Integration tests
68916911

68926912
@crossplatform
6913+
@no_modularize_instance('ccall is not compatible with WASM_ESM_INTEGRATION')
68936914
def test_ccall(self):
68946915
self.emcc_args.append('-Wno-return-stack-address')
68956916
self.set_setting('EXPORTED_RUNTIME_METHODS', ['ccall', 'cwrap', 'STACK_SIZE'])
@@ -6934,6 +6955,7 @@ def test_ccall(self):
69346955
if self.maybe_closure():
69356956
self.do_core_test('test_ccall.cpp')
69366957

6958+
@no_modularize_instance('ccall is not compatible with WASM_ESM_INTEGRATION')
69376959
def test_ccall_cwrap_fast_path(self):
69386960
self.emcc_args.append('-Wno-return-stack-address')
69396961
self.set_setting('EXPORTED_RUNTIME_METHODS', ['ccall', 'cwrap'])
@@ -6948,6 +6970,7 @@ def test_ccall_cwrap_fast_path(self):
69486970
self.set_setting('EXPORTED_FUNCTIONS', ['_print_bool'])
69496971
self.do_runf('core/test_ccall.cpp', 'true')
69506972

6973+
@no_modularize_instance('uses Module object directly')
69516974
def test_EXPORTED_RUNTIME_METHODS(self):
69526975
self.set_setting('DEFAULT_LIBRARY_FUNCS_TO_INCLUDE', ['$dynCall', '$ASSERTIONS'])
69536976
self.do_core_test('EXPORTED_RUNTIME_METHODS.c')
@@ -6985,9 +7008,12 @@ def test_dyncall_specific(self):
69857008
'legacy': (['-sDYNCALLS'],),
69867009
})
69877010
def test_dyncall_pointers(self, args):
7011+
if args:
7012+
self.skipTest('dynCallLegacy is not yet comatible with WASM_ESM_INTEGRATION')
69887013
self.do_core_test('test_dyncall_pointers.c', emcc_args=args)
69897014

69907015
@also_with_wasm_bigint
7016+
@no_modularize_instance('uses Module object directly')
69917017
def test_getValue_setValue(self):
69927018
# these used to be exported, but no longer are by default
69937019
def test(args=None, asserts=False):
@@ -7030,6 +7056,7 @@ def test(args=None, asserts=False):
70307056
'': ([],),
70317057
'files': (['-DUSE_FILES'],),
70327058
})
7059+
@no_modularize_instance('uses Module object directly')
70337060
def test_FS_exports(self, extra_args):
70347061
# these used to be exported, but no longer are by default
70357062
def test(output_prefix='', args=None, assert_returncode=0):
@@ -7052,6 +7079,7 @@ def test(output_prefix='', args=None, assert_returncode=0):
70527079
self.set_setting('EXPORTED_RUNTIME_METHODS', ['FS_createDataFile'])
70537080
test(args=['-sFORCE_FILESYSTEM'])
70547081

7082+
@no_modularize_instance('uses Module object directly')
70557083
def test_legacy_exported_runtime_numbers(self):
70567084
# these used to be exported, but no longer are by default
70577085
def test(expected, args=None, assert_returncode=0):
@@ -7101,6 +7129,7 @@ def test_linker_response_file(self):
71017129
self.run_process([EMCC, "-Wl,@rsp_file", '-o', 'response_file.o.js'] + self.get_emcc_args())
71027130
self.do_run('response_file.o.js', 'hello, world', no_build=True)
71037131

7132+
@no_modularize_instance('uses Module object directly')
71047133
def test_exported_response(self):
71057134
src = r'''
71067135
#include <stdio.h>
@@ -7124,6 +7153,7 @@ def test_exported_response(self):
71247153
self.do_run(src, '''waka 5!''')
71257154
assert 'other_function' in read_file('src.js')
71267155

7156+
@no_modularize_instance('uses Module object directly')
71277157
def test_large_exported_response(self):
71287158
src = r'''
71297159
#include <stdio.h>
@@ -7899,6 +7929,7 @@ def get_wat_addr(call_index):
78997929
self.assertLessEqual(start_wat_addr, dwarf_addr)
79007930
self.assertLessEqual(dwarf_addr, end_wat_addr)
79017931

7932+
@no_modularize_instance('uses -sMODULARIZE')
79027933
def test_modularize_closure_pre(self):
79037934
# test that the combination of modularize + closure + pre-js works. in that mode,
79047935
# closure should not minify the Module object in a way that the pre-js cannot use it.
@@ -8049,6 +8080,7 @@ def test_async_loop(self):
80498080
def test_async_hello_v8(self):
80508081
self.test_async_hello()
80518082

8083+
@no_modularize_instance('ccall is not compatible with WASM_ESM_INTEGRATION')
80528084
def test_async_ccall_bad(self):
80538085
# check bad ccall use
80548086
# needs to flush stdio streams
@@ -8080,6 +8112,7 @@ def test_async_ccall_bad(self):
80808112
self.do_runf('main.c', 'The call to main is running asynchronously.')
80818113

80828114
@with_asyncify_and_jspi
8115+
@no_modularize_instance('ccall is not compatible with WASM_ESM_INTEGRATION')
80838116
def test_async_ccall_good(self):
80848117
# check reasonable ccall use
80858118
self.set_setting('ASYNCIFY')
@@ -8108,6 +8141,7 @@ def test_async_ccall_good(self):
81088141
'exit_runtime': (True,),
81098142
})
81108143
@with_asyncify_and_jspi
8144+
@no_modularize_instance('ccall is not compatible with WASM_ESM_INTEGRATION')
81118145
def test_async_ccall_promise(self, exit_runtime):
81128146
if self.get_setting('ASYNCIFY') == 2:
81138147
self.set_setting('JSPI_EXPORTS', ['stringf', 'floatf'])
@@ -9550,6 +9584,7 @@ def test_promise_await_error(self):
95509584
self.do_runf('core/test_promise_await.c', 'Aborted(emscripten_promise_await is only available with ASYNCIFY)',
95519585
assert_returncode=NON_ZERO)
95529586

9587+
@no_modularize_instance('uses Module object directly')
95539588
def test_emscripten_async_load_script(self):
95549589
create_file('script1.js', 'Module._set(456);''')
95559590
create_file('file1.txt', 'first')

0 commit comments

Comments
 (0)