Skip to content

Commit 36ce7dc

Browse files
committed
Enable EM_ASM in side modules
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. Fixes: #18199
1 parent afbc149 commit 36ce7dc

8 files changed

+102
-16
lines changed

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')

src/library_dylink.js

+31
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,35 @@ 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+
676+
{{{ makeEval('ASM_CONSTS[start] = eval(func)') }}};
677+
}
678+
679+
// Add any EM_ASM function that exist in the side module
680+
if ('__start_em_asm' in moduleExports) {
681+
var start = moduleExports['__start_em_asm'];
682+
var stop = moduleExports['__stop_em_asm'];
683+
while (start < stop) {
684+
var jsString = UTF8ToString(start);
685+
addEmAsm(start, jsString);
686+
start = HEAPU8.indexOf(0, start) + 1;
687+
}
688+
}
689+
#endif
690+
660691
// initialize the module
661692
#if USE_PTHREADS
662693
// Only one thread (currently The main thread) should call

src/settings.js

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

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

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

+4
Original file line numberDiff line numberDiff line change
@@ -2257,6 +2257,10 @@ 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+
def test_em_asm_side_module(self):
2261+
self.build(test_file('core/test_em_asm_side.c'), js_outfile=False, emcc_args=['-sSIDE_MODULE'], output_basename='side')
2262+
self.do_core_test('test_em_asm_main.c', emcc_args=['-sMAIN_MODULE=2', 'side.wasm'])
2263+
22602264
@parameterized({
22612265
'': ([], False),
22622266
'pthreads': (['-sUSE_PTHREADS', '-sPROXY_TO_PTHREAD', '-sEXIT_RUNTIME'], False),

test/test_other.py

-4
Original file line numberDiff line numberDiff line change
@@ -11614,10 +11614,6 @@ def test_runtime_keepalive(self):
1161411614
self.set_setting('EXIT_RUNTIME')
1161511615
self.do_other_test('test_runtime_keepalive.cpp')
1161611616

11617-
def test_em_asm_side_module(self):
11618-
err = self.expect_fail([EMCC, '-sSIDE_MODULE', test_file('hello_world_em_asm.c')])
11619-
self.assertContained('EM_ASM is not supported in side modules', err)
11620-
1162111617
def test_em_js_side_module(self):
1162211618
err = self.expect_fail([EMXX, '-sSIDE_MODULE', test_file('core/test_em_js.cpp')])
1162311619
self.assertContained('EM_JS is not supported in side modules', err)

0 commit comments

Comments
 (0)