Skip to content

Commit 2d77437

Browse files
authored
Enable EM_ASM in side modules (#18228)
The only way I could find to make work was to use `eval`. Sadly even using `new Function` didn't work since it evaluates the code not in the containing (module) scope but in the global scope. Depends on WebAssembly/binaryen#5274 Fixes: #18199
1 parent d260d63 commit 2d77437

10 files changed

+113
-16
lines changed

emcc.py

+2
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,8 @@ def get_binaryen_passes():
607607
passes += ['--signext-lowering']
608608
if optimizing:
609609
passes += ['--post-emscripten']
610+
if settings.SIDE_MODULE:
611+
passes += ['--pass-arg=post-emscripten-side-module']
610612
if optimizing:
611613
passes += [building.opt_level_to_str(settings.OPT_LEVEL, settings.SHRINK_LEVEL)]
612614
# when optimizing, use the fact that low memory is never used (1024 is a

emscripten.py

-2
Original file line numberDiff line numberDiff line change
@@ -337,8 +337,6 @@ def emscript(in_wasm, out_wasm, outfile_js, memfile):
337337
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)
338338

339339
if settings.SIDE_MODULE:
340-
if metadata.asmConsts:
341-
exit_with_error('EM_ASM is not supported in side modules')
342340
if metadata.emJsFuncs:
343341
exit_with_error('EM_JS is not supported in side modules')
344342
logger.debug('emscript: skipping remaining js glue generation')

site/source/docs/compiling/Dynamic-Linking.rst

+4
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@ Limitations
212212
startup
213213
`[doc] <https://emscripten.org/docs/porting/files/packaging_files.html#preloading-files>`__
214214
`[discuss] <https://groups.google.com/forum/#!topic/emscripten-discuss/cE3hUV3fDSw>`__.
215+
- ``EM_ASM`` code defined within side modules depends on ``eval`` support are
216+
is therefore incompatible with ``-sDYNAMIC_EXECUTION=0``.
217+
- ``EM_JS`` functions defined in side modules are not yet supported.
218+
215219

216220
Pthreads support
217221
----------------

src/library_dylink.js

+35
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ var LibraryDylink = {
8989
'_emscripten_tls_init',
9090
'__wasm_init_tls',
9191
'__wasm_call_ctors',
92+
'__start_em_asm',
93+
'__stop_em_asm',
9294
].includes(symName)
9395
#if SPLIT_MODULE
9496
// Exports synthesized by wasm-split should be prefixed with '%'
@@ -657,6 +659,39 @@ var LibraryDylink = {
657659
}
658660
#endif
659661

662+
#if MAIN_MODULE
663+
function addEmAsm(addr, body) {
664+
var args = [];
665+
var arity = 0;
666+
for (; arity < 16; arity++) {
667+
if (body.indexOf('$' + arity) != -1) {
668+
args.push('$' + arity);
669+
} else {
670+
break;
671+
}
672+
}
673+
args = args.join(',');
674+
var func = '(' + args +' ) => { ' + body + '};'
675+
#if DYLINK_DEBUG
676+
dbg('adding new EM_ASM constant at: ' + ptrToString(start));
677+
#endif
678+
{{{ makeEval('ASM_CONSTS[start] = eval(func)') }}};
679+
}
680+
681+
// Add any EM_ASM function that exist in the side module
682+
if ('__start_em_asm' in moduleExports) {
683+
var start = moduleExports['__start_em_asm'];
684+
var stop = moduleExports['__stop_em_asm'];
685+
{{{ from64('start') }}}
686+
{{{ from64('stop') }}}
687+
while (start < stop) {
688+
var jsString = UTF8ToString(start);
689+
addEmAsm(start, jsString);
690+
start = HEAPU8.indexOf(0, start) + 1;
691+
}
692+
}
693+
#endif
694+
660695
// initialize the module
661696
#if USE_PTHREADS
662697
// Only one thread (currently The main thread) should call

src/settings.js

+12-10
Original file line numberDiff line numberDiff line change
@@ -1251,12 +1251,13 @@ var BENCHMARK = false;
12511251
// [link]
12521252
var EXPORT_NAME = 'Module';
12531253

1254-
// When set to 0, we do not emit eval() and new Function(), which disables some functionality
1255-
// (causing runtime errors if attempted to be used), but allows the emitted code to be
1256-
// acceptable in places that disallow dynamic code execution (chrome packaged app,
1257-
// privileged firefox app, etc.). Pass this flag when developing an Emscripten application
1258-
// that is targeting a privileged or a certified execution environment, see
1259-
// Firefox Content Security Policy (CSP) webpage for details:
1254+
// When set to 0, we do not emit eval() and new Function(), which disables some
1255+
// functionality (causing runtime errors if attempted to be used), but allows
1256+
// the emitted code to be acceptable in places that disallow dynamic code
1257+
// execution (chrome packaged app, privileged firefox app, etc.). Pass this flag
1258+
// when developing an Emscripten application that is targeting a privileged or a
1259+
// certified execution environment, see Firefox Content Security Policy (CSP)
1260+
// webpage for details:
12601261
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src
12611262
// in particular the 'unsafe-eval' and 'wasm-unsafe-eval' policies.
12621263
//
@@ -1271,11 +1272,12 @@ var EXPORT_NAME = 'Module';
12711272
// - emscripten_run_script_int(),
12721273
// - emscripten_run_script_string(),
12731274
// - dlopen(),
1274-
// - the functions ccall() and cwrap() are still available, but they are restricted to only
1275-
// being able to call functions that have been exported in the Module object in advance.
1275+
// - the functions ccall() and cwrap() are still available, but they are
1276+
// restricted to only being able to call functions that have been exported in
1277+
// the Module object in advance.
12761278
//
1277-
// When set to -sDYNAMIC_EXECUTION=2 flag is set, attempts to call to eval() are demoted
1278-
// to warnings instead of throwing an exception.
1279+
// When -sDYNAMIC_EXECUTION=2 is set, attempts to call to eval() are demoted to
1280+
// warnings instead of throwing an exception.
12791281
// [link]
12801282
var DYNAMIC_EXECUTION = 1;
12811283

test/core/test_em_asm_main.c

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#include <stdio.h>
2+
#include <emscripten.h>
3+
4+
int test_side();
5+
6+
int main() {
7+
printf("in main\n");
8+
EM_ASM(out("em_asm in main"));
9+
return test_side();
10+
}

test/core/test_em_asm_main.out

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
in main
2+
em_asm in main
3+
no args works
4+
int types:
5+
char : 1
6+
signed char : 2
7+
unsigned char : 3
8+
short: 4
9+
signed short: 5
10+
unsigned short: 6
11+
int : 7
12+
signed int : 8
13+
unsigned int : 9
14+
long : 10
15+
signed long : 11
16+
unsigned long : 12
17+
terminator: 42

test/core/test_em_asm_side.c

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#include <emscripten.h>
2+
#include <stdio.h>
3+
4+
int test_side() {
5+
EM_ASM(out("no args works"));
6+
7+
EM_ASM({
8+
console.log("int types:");
9+
out(" char : " + $0);
10+
out(" signed char : " + $1);
11+
out("unsigned char : " + $2);
12+
out(" short: " + $3);
13+
out(" signed short: " + $4);
14+
out("unsigned short: " + $5);
15+
out(" int : " + $6);
16+
out(" signed int : " + $7);
17+
out("unsigned int : " + $8);
18+
out(" long : " + $9);
19+
out(" signed long : " + $10);
20+
out("unsigned long : " + $11);
21+
out(" terminator: " + $12);
22+
}, (char)1, (signed char)2, (unsigned char)3,
23+
(short)4, (signed short)5, (unsigned short)6,
24+
(int)7, (signed int)8, (unsigned int)9,
25+
(long)10, (signed long)11, (unsigned long)12, 42);
26+
27+
return 0;
28+
}

test/test_core.py

+5
Original file line numberDiff line numberDiff line change
@@ -2257,6 +2257,11 @@ def test_em_asm_arguments_side_effects(self):
22572257
def test_em_asm_direct(self):
22582258
self.do_core_test('test_em_asm_direct.c')
22592259

2260+
@needs_dylink
2261+
def test_em_asm_side_module(self):
2262+
self.build(test_file('core/test_em_asm_side.c'), js_outfile=False, emcc_args=['-sSIDE_MODULE'], output_basename='side')
2263+
self.do_core_test('test_em_asm_main.c', emcc_args=['-sMAIN_MODULE=2', 'side.wasm'])
2264+
22602265
@parameterized({
22612266
'': ([], False),
22622267
'pthreads': (['-sUSE_PTHREADS', '-sPROXY_TO_PTHREAD', '-sEXIT_RUNTIME'], False),

test/test_other.py

-4
Original file line numberDiff line numberDiff line change
@@ -11645,10 +11645,6 @@ def test_runtime_keepalive(self):
1164511645
self.set_setting('EXIT_RUNTIME')
1164611646
self.do_other_test('test_runtime_keepalive.cpp')
1164711647

11648-
def test_em_asm_side_module(self):
11649-
err = self.expect_fail([EMCC, '-sSIDE_MODULE', test_file('hello_world_em_asm.c')])
11650-
self.assertContained('EM_ASM is not supported in side modules', err)
11651-
1165211648
def test_em_js_side_module(self):
1165311649
err = self.expect_fail([EMXX, '-sSIDE_MODULE', test_file('core/test_em_js.cpp')])
1165411650
self.assertContained('EM_JS is not supported in side modules', err)

0 commit comments

Comments
 (0)