Skip to content

Commit

Permalink
Enable EM_ASM in side modules
Browse files Browse the repository at this point in the history
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
  • Loading branch information
sbc100 committed Nov 17, 2022
1 parent afbc149 commit 36ce7dc
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 16 deletions.
2 changes: 0 additions & 2 deletions emscripten.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,8 +337,6 @@ def emscript(in_wasm, out_wasm, outfile_js, memfile):
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)

if settings.SIDE_MODULE:
if metadata.asmConsts:
exit_with_error('EM_ASM is not supported in side modules')
if metadata.emJsFuncs:
exit_with_error('EM_JS is not supported in side modules')
logger.debug('emscript: skipping remaining js glue generation')
Expand Down
31 changes: 31 additions & 0 deletions src/library_dylink.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ var LibraryDylink = {
'_emscripten_tls_init',
'__wasm_init_tls',
'__wasm_call_ctors',
'__start_em_asm',
'__stop_em_asm',
].includes(symName)
#if SPLIT_MODULE
// Exports synthesized by wasm-split should be prefixed with '%'
Expand Down Expand Up @@ -657,6 +659,35 @@ var LibraryDylink = {
}
#endif

#if MAIN_MODULE
function addEmAsm(addr, body) {
var args = [];
var arity = 0;
for (; arity < 16; arity++) {
if (body.indexOf('$' + arity) != -1) {
args.push('$' + arity);
} else {
break;
}
}
args = args.join(',');
var func = '(' + args +' ) => { ' + body + '};'

{{{ makeEval('ASM_CONSTS[start] = eval(func)') }}};
}

// Add any EM_ASM function that exist in the side module
if ('__start_em_asm' in moduleExports) {
var start = moduleExports['__start_em_asm'];
var stop = moduleExports['__stop_em_asm'];
while (start < stop) {
var jsString = UTF8ToString(start);
addEmAsm(start, jsString);
start = HEAPU8.indexOf(0, start) + 1;
}
}
#endif

// initialize the module
#if USE_PTHREADS
// Only one thread (currently The main thread) should call
Expand Down
22 changes: 12 additions & 10 deletions src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -1250,12 +1250,13 @@ var BENCHMARK = false;
// [link]
var EXPORT_NAME = 'Module';

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

Expand Down
10 changes: 10 additions & 0 deletions test/core/test_em_asm_main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <stdio.h>
#include <emscripten.h>

int test_side();

int main() {
printf("in main\n");
EM_ASM(out("em_asm in main"));
return test_side();
}
17 changes: 17 additions & 0 deletions test/core/test_em_asm_main.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
in main
em_asm in main
no args works
int types:
char : 1
signed char : 2
unsigned char : 3
short: 4
signed short: 5
unsigned short: 6
int : 7
signed int : 8
unsigned int : 9
long : 10
signed long : 11
unsigned long : 12
terminator: 42
28 changes: 28 additions & 0 deletions test/core/test_em_asm_side.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include <emscripten.h>
#include <stdio.h>

int test_side() {
EM_ASM(out("no args works"));

EM_ASM({
console.log("int types:");
out(" char : " + $0);
out(" signed char : " + $1);
out("unsigned char : " + $2);
out(" short: " + $3);
out(" signed short: " + $4);
out("unsigned short: " + $5);
out(" int : " + $6);
out(" signed int : " + $7);
out("unsigned int : " + $8);
out(" long : " + $9);
out(" signed long : " + $10);
out("unsigned long : " + $11);
out(" terminator: " + $12);
}, (char)1, (signed char)2, (unsigned char)3,
(short)4, (signed short)5, (unsigned short)6,
(int)7, (signed int)8, (unsigned int)9,
(long)10, (signed long)11, (unsigned long)12, 42);

return 0;
}
4 changes: 4 additions & 0 deletions test/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -2257,6 +2257,10 @@ def test_em_asm_arguments_side_effects(self):
def test_em_asm_direct(self):
self.do_core_test('test_em_asm_direct.c')

def test_em_asm_side_module(self):
self.build(test_file('core/test_em_asm_side.c'), js_outfile=False, emcc_args=['-sSIDE_MODULE'], output_basename='side')
self.do_core_test('test_em_asm_main.c', emcc_args=['-sMAIN_MODULE=2', 'side.wasm'])

@parameterized({
'': ([], False),
'pthreads': (['-sUSE_PTHREADS', '-sPROXY_TO_PTHREAD', '-sEXIT_RUNTIME'], False),
Expand Down
4 changes: 0 additions & 4 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -11614,10 +11614,6 @@ def test_runtime_keepalive(self):
self.set_setting('EXIT_RUNTIME')
self.do_other_test('test_runtime_keepalive.cpp')

def test_em_asm_side_module(self):
err = self.expect_fail([EMCC, '-sSIDE_MODULE', test_file('hello_world_em_asm.c')])
self.assertContained('EM_ASM is not supported in side modules', err)

def test_em_js_side_module(self):
err = self.expect_fail([EMXX, '-sSIDE_MODULE', test_file('core/test_em_js.cpp')])
self.assertContained('EM_JS is not supported in side modules', err)
Expand Down

0 comments on commit 36ce7dc

Please sign in to comment.