Skip to content

Commit b57effb

Browse files
authored
Validate EM_JS/EM_ASM strings in side modules at build time (emscripten-core#20258)
1 parent 11f454c commit b57effb

5 files changed

+52
-2
lines changed

ChangeLog.md

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

2121
3.1.47 (in development)
2222
-----------------------
23+
- `EM_JS` and `EM_ASM` that are present in side module now have their syntax
24+
validated at build time. (#20258)
2325

2426
3.1.46 - 09/15/23
2527
-----------------

emscripten.py

+21-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import sys
2222

2323
from tools import building
24+
from tools import config
2425
from tools import diagnostics
2526
from tools import js_manipulation
2627
from tools import shared
@@ -328,7 +329,27 @@ def emscript(in_wasm, out_wasm, outfile_js, memfile, js_syms):
328329
if len(signature.params) != len(c_sig):
329330
diagnostics.warning('em-js-i64', 'using 64-bit arguments in EM_JS function without WASM_BIGINT is not yet fully supported: `%s` (%s, %s)', em_js_func, c_sig, signature.params)
330331

332+
asm_consts = create_asm_consts(metadata)
333+
em_js_funcs = create_em_js(metadata)
334+
331335
if settings.SIDE_MODULE:
336+
# When building side modules, valid the EM_ASM and EM_JS string by running
337+
# them through node. Without this step, syntax errors are not surfaced
338+
# until runtime.
339+
# We use subprocess directly here rather than shared.check_call since
340+
# check_call doesn't support the `intput` argument.
341+
if asm_consts:
342+
validate = '\n'.join([f'var tmp = {f};' for _, f in asm_consts])
343+
proc = subprocess.run(config.NODE_JS + ['--check', '-'], input=validate.encode('utf-8'))
344+
if proc.returncode:
345+
exit_with_error(f'EM_ASM function validation failed (node returned {proc.returncode})')
346+
347+
if em_js_funcs:
348+
validate = '\n'.join(em_js_funcs)
349+
proc = subprocess.run(config.NODE_JS + ['--check', '-'], input=validate.encode('utf-8'))
350+
if proc.returncode:
351+
exit_with_error(f'EM_JS function validation failed (node returned {proc.returncode})')
352+
332353
logger.debug('emscript: skipping remaining js glue generation')
333354
return
334355

@@ -388,8 +409,6 @@ def emscript(in_wasm, out_wasm, outfile_js, memfile, js_syms):
388409
# In regular runtime, atinits etc. exist in the preamble part
389410
pre = apply_static_code_hooks(forwarded_json, pre)
390411

391-
asm_consts = create_asm_consts(metadata)
392-
em_js_funcs = create_em_js(metadata)
393412
asm_const_pairs = ['%s: %s' % (key, value) for key, value in asm_consts]
394413
extra_code = ''
395414
if asm_const_pairs or settings.MAIN_MODULE:

test/other/test_em_asm_invalid.c

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#include <emscripten/em_asm.h>
2+
3+
int main() {
4+
EM_ASM({
5+
* this is not valid js *
6+
});
7+
}

test/other/test_em_js_invalid.c

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <emscripten/em_js.h>
2+
3+
EM_JS(int, foo, (), {
4+
* this is not valid js *
5+
});
6+
7+
int main() {
8+
foo();
9+
}

test/test_other.py

+13
Original file line numberDiff line numberDiff line change
@@ -11063,6 +11063,12 @@ def test_em_asm_strict_c(self):
1106311063
err = self.expect_fail([EMCC, '-std=c11', 'src.c'])
1106411064
self.assertIn('EM_ASM does not work in -std=c* modes, use -std=gnu* modes instead', err)
1106511065

11066+
def test_em_asm_invalid(self):
11067+
# Test that invalid EM_ASM in side modules since is detected at build time.
11068+
err = self.expect_fail([EMCC, '-sSIDE_MODULE', test_file('other/test_em_asm_invalid.c')])
11069+
self.assertContained("SyntaxError: Unexpected token '*'", err)
11070+
self.assertContained('emcc: error: EM_ASM function validation failed', err)
11071+
1106611072
def test_boost_graph(self):
1106711073
self.do_runf(test_file('test_boost_graph.cpp'), emcc_args=['-std=c++14', '-sUSE_BOOST_HEADERS'])
1106811074

@@ -12051,6 +12057,13 @@ def test_runtime_keepalive(self):
1205112057
self.set_setting('EXIT_RUNTIME')
1205212058
self.do_other_test('test_runtime_keepalive.cpp')
1205312059

12060+
@crossplatform
12061+
def test_em_js_invalid(self):
12062+
# Test that invalid EM_JS in side modules since is detected at build time.
12063+
err = self.expect_fail([EMCC, '-sSIDE_MODULE', test_file('other/test_em_js_invalid.c')])
12064+
self.assertContained("SyntaxError: Unexpected token '*'", err)
12065+
self.assertContained('emcc: error: EM_JS function validation failed', err)
12066+
1205412067
@crossplatform
1205512068
def test_em_js_side_module(self):
1205612069
self.build(test_file('other/test_em_js_side.c'), js_outfile=False, emcc_args=['-sSIDE_MODULE'], output_basename='side')

0 commit comments

Comments
 (0)