From ed9a1bd78e3c265f8ce3b70af54c01b0005fdee4 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Fri, 6 Sep 2024 16:34:03 -0700 Subject: [PATCH] Use consistent helper for tests in -sMODULARIZE As part of this I also extended the `#preprocess` directive so it can be used with `--extern-pre-js` and `--extern-post-js` as well as the existing supprot for `--pre-js` and `--post-js`. --- test/modularize_post_js.js | 10 +++++++ test/runner.py | 2 +- test/test_core.py | 14 +++------ test/test_other.py | 61 +++++++++++++++++--------------------- tools/link.py | 9 +++++- 5 files changed, 51 insertions(+), 45 deletions(-) create mode 100644 test/modularize_post_js.js diff --git a/test/modularize_post_js.js b/test/modularize_post_js.js new file mode 100644 index 0000000000000..18ad0c15b8d6e --- /dev/null +++ b/test/modularize_post_js.js @@ -0,0 +1,10 @@ +#preprocess + +// This file gets included via `--extern-post-js` in order to run tests +// that are built with `-sMODULARIZE`. + +#if PTHREADS +// Avoid instantiating the module on pthreads. +if (!isPthread) +#endif +{{{ EXPORT_NAME }}}(); diff --git a/test/runner.py b/test/runner.py index 1fddec1e8279b..9808181e224be 100755 --- a/test/runner.py +++ b/test/runner.py @@ -114,7 +114,7 @@ def check_js_engines(): def get_and_import_modules(): modules = [] - for filename in glob.glob(os.path.join(os.path.dirname(__file__), 'test*.py')): + for filename in glob.glob(os.path.join(common.TEST_ROOT, 'test*.py')): module_dir, module_file = os.path.split(filename) module_name, module_ext = os.path.splitext(module_file) __import__(module_name) diff --git a/test/test_core.py b/test/test_core.py index 3b85cb1901104..6c38e9fede6df 100644 --- a/test/test_core.py +++ b/test/test_core.py @@ -2468,10 +2468,7 @@ def test_pthread_proxying(self, modularize): if modularize: self.set_setting('MODULARIZE') self.set_setting('EXPORT_NAME=ModuleFactory') - # Only instantiate the module on the main thread. - create_file('extern-post.js', - 'if (typeof importScripts != "function") ModuleFactory();') - args = ['--extern-post-js=extern-post.js'] + args = ['--extern-post-js', test_file('modularize_post_js.js')] self.do_run_in_out_file_test('pthread/test_pthread_proxying.c', interleaved_output=False, emcc_args=args) @@ -6266,8 +6263,7 @@ def test_unicode_js_library(self): err = self.expect_fail([PYTHON, 'expect_fail.py'], expect_traceback=True) self.assertContained('UnicodeDecodeError', err) - create_file('modularize_post.js', '(async function main(){await Module();})()') - self.emcc_args += ['-sMODULARIZE', '--js-library', test_file('unicode_library.js'), '--extern-post-js', 'modularize_post.js', '--post-js', test_file('unicode_postjs.js')] + self.emcc_args += ['-sMODULARIZE', '--js-library', test_file('unicode_library.js'), '--extern-post-js', test_file('modularize_post_js.js'), '--post-js', test_file('unicode_postjs.js')] self.do_run_in_out_file_test('test_unicode_js_library.c') def test_funcptr_import_type(self): @@ -7924,14 +7920,13 @@ def get_wat_addr(call_index): def test_modularize_closure_pre(self): # test that the combination of modularize + closure + pre-js works. in that mode, # closure should not minify the Module object in a way that the pre-js cannot use it. - create_file('post.js', 'var TheModule = Module();\n') if self.is_wasm2js(): # TODO(sbc): Fix closure warnings with MODULARIZE + WASM=0 self.ldflags.append('-Wno-error=closure') self.emcc_args += [ '--pre-js', test_file('core/modularize_closure_pre.js'), - '--extern-post-js=post.js', + '--extern-post-js', test_file('modularize_post_js.js'), '--closure=1', '-g1', '-sMODULARIZE', @@ -9222,8 +9217,7 @@ def test_pthread_offset_converter_modularize(self): self.set_setting('USE_OFFSET_CONVERTER') self.set_setting('MODULARIZE') self.set_setting('EXPORT_NAME', 'foo') - create_file('post.js', 'if (!isPthread) foo();') - self.emcc_args += ['--extern-post-js', 'post.js'] + self.emcc_args += ['--extern-post-js', test_file('modularize_post_js.js')] if '-g' in self.emcc_args: self.emcc_args += ['-DDEBUG'] self.do_runf('core/test_return_address.c', 'passed') diff --git a/test/test_other.py b/test/test_other.py index 1c61d55ba1777..233aad7683723 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -349,9 +349,8 @@ def test_emcc_generate_config(self, compiler): 'node': (['-sENVIRONMENT=node'],), }) def test_emcc_output_mjs(self, args): - create_file('extern-post.js', 'await Module();') self.run_process([EMCC, '-o', 'hello_world.mjs', - '--extern-post-js', 'extern-post.js', + '--extern-post-js', test_file('modularize_post_js.js'), test_file('hello_world.c')] + args) src = read_file('hello_world.mjs') self.assertContained('export default Module;', src) @@ -363,11 +362,10 @@ def test_emcc_output_mjs(self, args): }) @node_pthreads def test_emcc_output_worker_mjs(self, args): - create_file('extern-post.js', 'if (!isPthread) await Module();') os.mkdir('subdir') self.run_process([EMCC, '-o', 'subdir/hello_world.mjs', '-sEXIT_RUNTIME', '-sPROXY_TO_PTHREAD', '-pthread', '-O1', - '--extern-post-js', 'extern-post.js', + '--extern-post-js', test_file('modularize_post_js.js'), test_file('hello_world.c')] + args) src = read_file('subdir/hello_world.mjs') self.assertContained("new URL('hello_world.wasm', import.meta.url)", src) @@ -377,9 +375,8 @@ def test_emcc_output_worker_mjs(self, args): @node_pthreads def test_emcc_output_worker_mjs_single_file(self): - create_file('extern-post.js', 'await Module();') self.run_process([EMCC, '-o', 'hello_world.mjs', '-pthread', - '--extern-post-js', 'extern-post.js', + '--extern-post-js', test_file('modularize_post_js.js'), test_file('hello_world.c'), '-sSINGLE_FILE']) src = read_file('hello_world.mjs') self.assertNotContained("new URL('data:", src) @@ -387,9 +384,8 @@ def test_emcc_output_worker_mjs_single_file(self): self.assertContained('hello, world!', self.run_js('hello_world.mjs')) def test_emcc_output_mjs_closure(self): - create_file('extern-post.js', 'await Module();') self.run_process([EMCC, '-o', 'hello_world.mjs', - '--extern-post-js', 'extern-post.js', + '--extern-post-js', test_file('modularize_post_js.js'), test_file('hello_world.c'), '--closure=1']) src = read_file('hello_world.mjs') self.assertContained('new URL("hello_world.wasm", import.meta.url)', src) @@ -1577,25 +1573,20 @@ def test_minimal_modularize_export_keepalive(self): EMSCRIPTEN_KEEPALIVE int libf1() { return 42; } ''') - def write_js_main(): - """ - With MINIMAL_RUNTIME, the module instantiation function isn't exported neither as a UMD nor as an ES6 module. - Thus, it's impossible to use `require` or `import`. - - This function simply appends the instantiation code to the generated code. - """ - runtime = read_file('test.js') - write_file('main.js', f'{runtime}\nModule().then((mod) => console.log(mod._libf1()));') + # With MINIMAL_RUNTIME, the module instantiation function isn't exported neither as a UMD nor as + # an ES6 module. + # Thus, it's impossible to use `require` or `import` and instead run the module + # as part of --extern-post-js. + create_file('post.js', 'Module().then((mod) => console.log(mod._libf1()));') + self.emcc_args += ['--extern-post-js=post.js'] # By default, no symbols should be exported when using MINIMAL_RUNTIME. - self.emcc('main.c', [], output_filename='test.js') - write_js_main() - self.assertContained('TypeError: mod._libf1 is not a function', self.run_js('main.js', assert_returncode=NON_ZERO)) + self.emcc('main.c', []) + self.assertContained('TypeError: mod._libf1 is not a function', self.run_js('a.out.js', assert_returncode=NON_ZERO)) # Ensures that EXPORT_KEEPALIVE=1 exports the symbols. - self.emcc('main.c', ['-sEXPORT_KEEPALIVE=1'], output_filename='test.js') - write_js_main() - self.assertContained('42\n', self.run_js('main.js')) + self.emcc('main.c', ['-sEXPORT_KEEPALIVE=1']) + self.assertContained('42\n', self.run_js('a.out.js')) @crossplatform def test_minimal_runtime_export_all_modularize(self): @@ -6682,7 +6673,12 @@ def test_define_modularize(self): create_file('a.out.js', src) self.assertContained("define([], () => Module);", src) - create_file('run_module.js', 'var m; (global.define = (deps, factory) => { m = factory(); }).amd = true; require("./a.out.js"); m();') + create_file('run_module.js', ''' +var m; +(global.define = (deps, factory) => { m = factory(); }).amd = true; +require("./a.out.js"); +m(); +''') output = self.run_js('run_module.js') self.assertContained('hello, world!\n', output) @@ -10385,7 +10381,7 @@ def test_node_js_pthread_module(self, es6): create_file('moduleLoader.mjs', ''' import test_module from "./subdir/module.mjs"; test_module().then((test_module_instance) => { - test_module_instance._main(); + console.log("done"); }); ''') else: @@ -10393,7 +10389,7 @@ def test_node_js_pthread_module(self, es6): create_file('moduleLoader.js', ''' const test_module = require("./subdir/module.js"); test_module().then((test_module_instance) => { - test_module_instance._main(); + console.log("done"); }); ''') ensure_dir('subdir') @@ -10403,7 +10399,7 @@ def test_node_js_pthread_module(self, es6): # run the module ret = self.run_js('moduleLoader' + ext) - self.assertContained('hello, world!', ret) + self.assertContained('hello, world!\ndone\n', ret) create_file('workerLoader.js', f''' const {{ Worker, isMainThread }} = require('worker_threads'); @@ -10412,7 +10408,7 @@ def test_node_js_pthread_module(self, es6): # run the same module, but inside of a worker ret = self.run_js('workerLoader.js') - self.assertContained('hello, world!', ret) + self.assertContained('hello, world!\ndone\n', ret) @no_windows('node system() does not seem to work, see https://github.com/emscripten-core/emscripten/pull/10547') @requires_node @@ -12180,15 +12176,14 @@ def test_standalone_syscalls(self): self.assertContained(expected, self.run_js('test.wasm', engine)) @parameterized({ - 'wasm2js': (['-sWASM=0'], ''), - 'modularize': (['-sMODULARIZE'], 'Module()'), + 'wasm2js': (['-sWASM=0'],), + 'modularize': (['-sMODULARIZE', '--extern-post-js', test_file('modularize_post_js.js')],), }) - def test_promise_polyfill(self, constant_args, extern_post_js): + def test_promise_polyfill(self, constant_args): def test(args, expect_fail): # legacy browsers may lack Promise, which wasm2js depends on. see what # happens when we kill the global Promise function. - create_file('extern-post.js', extern_post_js) - self.run_process([EMCC, test_file('hello_world.c')] + constant_args + args + ['--extern-post-js', 'extern-post.js']) + self.run_process([EMCC, test_file('hello_world.c')] + constant_args + args) js = read_file('a.out.js') create_file('a.out.js', 'Promise = undefined;\n' + js) return self.run_js('a.out.js', assert_returncode=NON_ZERO if expect_fail else 0) diff --git a/tools/link.py b/tools/link.py index 04eb551353c0c..e3ef39bcac895 100644 --- a/tools/link.py +++ b/tools/link.py @@ -301,7 +301,14 @@ def fix_windows_newlines(text): def read_js_files(files): - contents = '\n'.join(read_file(f) for f in files) + contents = [] + for f in files: + content = read_file(f) + if content.startswith('#preprocess\n'): + contents.append(shared.read_and_preprocess(f, expand_macros=True)) + else: + contents.append(content) + contents = '\n'.join(contents) return fix_windows_newlines(contents)