Skip to content

Commit 53552f2

Browse files
committed
Process --pre-js and --post-js files in jsifier.js
This teats pre and post JS files more like normal runtime and library files which simplifies the code and avoids the special case processing in emcc.py. This also means that pre and post JS also now go through macro preprocessor which should be useful in some cases.
1 parent 0245b5b commit 53552f2

11 files changed

+72
-40
lines changed

ChangeLog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ See docs/process.md for more on how version tagging works.
2323
- The `STACK_SIZE`, `STACK_ALIGN`, `POINTER_SIZE`, and `ASSERTIONS` JavaScript
2424
globals were removed by default. In debug builds a clear error is shown if
2525
you try to use these. (#18503)
26+
- --pre-js and --post-js files are now fed through the JS preprocesor, just
27+
like JS library files and the core runtime JS files. This means they can
28+
now contain #if/#else/#endif blocks and {{{ }}} macro blocks. (#18525)
2629

2730
3.1.30 - 01/11/23
2831
-----------------

emcc.py

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,11 @@ def get_all_js_syms():
524524

525525
# We define a cache hit as when the settings and `--js-library` contents are
526526
# identical.
527-
input_files = [json.dumps(settings.external_dict(), sort_keys=True, indent=2)]
527+
# Ignore certain settings that can are no relevant to library deps. Here we
528+
# skip PRE_JS_FILES/POST_JS_FILES which don't effect the library symbol list
529+
# and can contain full paths to temporary files.
530+
skip_settings = {'PRE_JS_FILES', 'POST_JS_FILES'}
531+
input_files = [json.dumps(settings.external_dict(skip_keys=skip_settings), sort_keys=True, indent=2)]
528532
for jslib in sorted(glob.glob(utils.path_from_root('src') + '/library*.js')):
529533
input_files.append(read_file(jslib))
530534
for jslib in settings.JS_LIBRARIES:
@@ -1119,7 +1123,7 @@ def package_files(options, target):
11191123
if options.preload_files:
11201124
# Preloading files uses --pre-js code that runs before the module is loaded.
11211125
file_code = shared.check_call(cmd, stdout=PIPE).stdout
1122-
options.pre_js = js_manipulation.add_files_pre_js(options.pre_js, file_code)
1126+
js_manipulation.add_files_pre_js(options.pre_js, file_code)
11231127
else:
11241128
# Otherwise, we are embedding files, which does not require --pre-js code,
11251129
# and instead relies on a static constrcutor to populate the filesystem.
@@ -1293,6 +1297,9 @@ def run(args):
12931297
if len(options.preload_files) or len(options.embed_files):
12941298
linker_arguments += package_files(options, target)
12951299

1300+
settings.PRE_JS_FILES = [os.path.abspath(f) for f in options.pre_js]
1301+
settings.POST_JS_FILES = [os.path.abspath(f) for f in options.post_js]
1302+
12961303
if options.oformat == OFormat.OBJECT:
12971304
logger.debug(f'link_to_object: {linker_arguments} -> {target}')
12981305
building.link_to_object(linker_arguments, target)
@@ -1750,8 +1757,6 @@ def phase_linker_setup(options, state, newargs):
17501757
exit_with_error('PTHREADS_PROFILING only works with ASSERTIONS enabled')
17511758
options.post_js.append(utils.path_from_root('src/threadprofiler.js'))
17521759

1753-
options.pre_js = read_js_files(options.pre_js)
1754-
options.post_js = read_js_files(options.post_js)
17551760
options.extern_pre_js = read_js_files(options.extern_pre_js)
17561761
options.extern_post_js = read_js_files(options.extern_post_js)
17571762

@@ -3021,7 +3026,8 @@ def phase_post_link(options, state, in_wasm, wasm_target, target):
30213026

30223027
phase_emscript(options, in_wasm, wasm_target, memfile)
30233028

3024-
phase_source_transforms(options)
3029+
if options.js_transform:
3030+
phase_source_transforms(options)
30253031

30263032
if memfile and not settings.MINIMAL_RUNTIME:
30273033
# MINIMAL_RUNTIME doesn't use `var memoryInitializer` but instead expects Module['mem'] to
@@ -3055,28 +3061,14 @@ def phase_emscript(options, in_wasm, wasm_target, memfile):
30553061

30563062
@ToolchainProfiler.profile_block('source transforms')
30573063
def phase_source_transforms(options):
3058-
global final_js
3059-
3060-
# Apply pre and postjs files
3061-
if final_js and (options.pre_js or options.post_js):
3062-
logger.debug('applying pre/postjses')
3063-
src = read_file(final_js)
3064-
final_js += '.pp.js'
3065-
with open(final_js, 'w', encoding='utf-8') as f:
3066-
# pre-js code goes right after the Module integration code (so it
3067-
# can use Module), we have a marker for it
3068-
f.write(do_replace(src, '// {{PRE_JSES}}', options.pre_js))
3069-
f.write(options.post_js)
3070-
save_intermediate('pre-post')
3071-
30723064
# Apply a source code transformation, if requested
3073-
if options.js_transform:
3074-
safe_copy(final_js, final_js + '.tr.js')
3075-
final_js += '.tr.js'
3076-
posix = not shared.WINDOWS
3077-
logger.debug('applying transform: %s', options.js_transform)
3078-
shared.check_call(building.remove_quotes(shlex.split(options.js_transform, posix=posix) + [os.path.abspath(final_js)]))
3079-
save_intermediate('transformed')
3065+
global final_js
3066+
safe_copy(final_js, final_js + '.tr.js')
3067+
final_js += '.tr.js'
3068+
posix = not shared.WINDOWS
3069+
logger.debug('applying transform: %s', options.js_transform)
3070+
shared.check_call(building.remove_quotes(shlex.split(options.js_transform, posix=posix) + [os.path.abspath(final_js)]))
3071+
save_intermediate('transformed')
30803072

30813073

30823074
@ToolchainProfiler.profile_block('memory initializer')

src/jsifier.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,10 @@ function ${name}(${args}) {
526526
const postFile = MINIMAL_RUNTIME ? 'postamble_minimal.js' : 'postamble.js';
527527
includeFile(postFile);
528528

529+
for (const fileName of POST_JS_FILES) {
530+
includeFile(fileName);
531+
}
532+
529533
print('//FORWARDED_DATA:' + JSON.stringify({
530534
librarySymbols: librarySymbols,
531535
warnings: warnings,

src/parseTools.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,3 +1104,11 @@ function getUnsharedTextDecoderView(heap, start, end) {
11041104
// or can use .subarray() otherwise.
11051105
return `${heap}.buffer instanceof SharedArrayBuffer ? ${shared} : ${unshared}`;
11061106
}
1107+
1108+
function preJS() {
1109+
let result = '';
1110+
for (const fileName of PRE_JS_FILES) {
1111+
result += preprocess(fileName);
1112+
}
1113+
return result;
1114+
}

src/settings_internal.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,3 +243,7 @@ var WEAK_IMPORTS = [];
243243
var STACK_FIRST = false;
244244

245245
var HAVE_EM_ASM = true;
246+
247+
var PRE_JS_FILES = [];
248+
249+
var POST_JS_FILES = [];

src/shell.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ Module['ready'] = new Promise(function(resolve, reject) {
7272

7373
// --pre-jses are emitted after the Module integration code, so that they can
7474
// refer to Module (if they choose; they can also define Module)
75-
// {{PRE_JSES}}
75+
{{{ preJS() }}}
7676

7777
// Sometimes an existing Module object exists with properties
7878
// meant to overwrite the default module functionality. Here

src/shell_minimal.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,7 @@ function ready() {
136136

137137
// --pre-jses are emitted after the Module integration code, so that they can
138138
// refer to Module (if they choose; they can also define Module)
139-
140-
// {{PRE_JSES}}
139+
{{{ preJS() }}}
141140

142141
#if USE_PTHREADS
143142

test/return64bit/testbindstart.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
2-
(function() { // Start of self-calling lambda used to avoid polluting global namespace.
3-
1+
(function() { // Start of IIFE used to avoid polluting global namespace.

test/test_other.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9041,6 +9041,21 @@ def test_js_preprocess(self):
90419041
self.assertContained('JSLIB: EXIT_RUNTIME', err)
90429042
self.assertNotContained('JSLIB: MAIN_MODULE', err)
90439043

9044+
def test_js_preprocess_pre_post(self):
9045+
create_file('pre.js', '''
9046+
#if ASSERTIONS
9047+
console.log('assertions enabled')
9048+
#else
9049+
console.log('assertions disabled')
9050+
#endif
9051+
''')
9052+
create_file('post.js', '''
9053+
console.log({{{ POINTER_SIZE }}});
9054+
''')
9055+
self.emcc_args += ['--pre-js', 'pre.js', '--post-js', 'post.js']
9056+
self.do_runf(test_file('hello_world.c'), 'assertions enabled\n4')
9057+
self.do_runf(test_file('hello_world.c'), 'assertions disabled\n4', emcc_args=['-sASSERTIONS=0'])
9058+
90449059
def test_html_preprocess(self):
90459060
src_file = test_file('module/test_stdin.c')
90469061
output_file = 'test_stdin.html'

tools/js_manipulation.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import re
77

88
from .settings import settings
9-
from . import utils
9+
from . import utils, shared
1010

1111
emscripten_license = '''\
1212
/**
@@ -28,25 +28,34 @@
2828
emscripten_license_regex = r'\/\*\*?(\s*\*?\s*@license)?(\s*\*?\s*Copyright \d+ The Emscripten Authors\s*\*?\s*SPDX-License-Identifier: MIT)+\s*\*\/\s*'
2929

3030

31-
def add_files_pre_js(user_pre_js, files_pre_js):
31+
def add_files_pre_js(pre_js_list, files_pre_js):
3232
# the normal thing is to just combine the pre-js content
33+
filename = shared.get_temp_files().get('.js').name
34+
utils.write_file(filename, files_pre_js)
35+
pre_js_list.insert(0, filename)
3336
if not settings.ASSERTIONS:
34-
return files_pre_js + user_pre_js
37+
return
3538

3639
# if a user pre-js tramples the file code's changes to Module.preRun
3740
# that could be confusing. show a clear error at runtime if assertions are
3841
# enabled
39-
return files_pre_js + '''
42+
pre = shared.get_temp_files().get('.js').name
43+
post = shared.get_temp_files().get('.js').name
44+
utils.write_file(pre, '''
4045
// All the pre-js content up to here must remain later on, we need to run
4146
// it.
4247
if (Module['ENVIRONMENT_IS_PTHREAD']) Module['preRun'] = [];
4348
var necessaryPreJSTasks = Module['preRun'].slice();
44-
''' + user_pre_js + '''
49+
''')
50+
utils.write_file(post, '''
4551
if (!Module['preRun']) throw 'Module.preRun should exist because file support used it; did a pre-js delete it?';
4652
necessaryPreJSTasks.forEach(function(task) {
4753
if (Module['preRun'].indexOf(task) < 0) throw 'All preRun tasks that exist before user pre-js code should remain after; did you replace Module or modify Module.preRun?';
4854
});
49-
'''
55+
''')
56+
57+
pre_js_list.insert(1, pre)
58+
pre_js_list.append(post)
5059

5160

5261
def handle_license(js_target):

tools/settings.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,8 @@ def infer_types(self):
156156
def dict(self):
157157
return self.attrs
158158

159-
def external_dict(self):
160-
external_settings = {k: v for k, v in self.dict().items() if k not in INTERNAL_SETTINGS}
159+
def external_dict(self, skip_keys={}): # noqa
160+
external_settings = {k: v for k, v in self.dict().items() if k not in INTERNAL_SETTINGS and k not in skip_keys}
161161
# Only the names of the legacy settings are used by the JS compiler
162162
# so we can reduce the size of serialized json by simplifying this
163163
# otherwise complex value.

0 commit comments

Comments
 (0)