Skip to content

Commit

Permalink
Enable EM_JS in side modules (#19705)
Browse files Browse the repository at this point in the history
This works in a similar way to EM_ASM. See #18228.

Depends on WebAssembly/binaryen#5780
  • Loading branch information
sbc100 committed Jun 26, 2023
1 parent 86eef90 commit 7c58a8f
Show file tree
Hide file tree
Showing 9 changed files with 84 additions and 6 deletions.
4 changes: 4 additions & 0 deletions emcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,10 @@ def process_dynamic_libs(dylibs, lib_dirs):
for dylib in dylibs:
exports = webassembly.get_exports(dylib)
exports = set(e.name for e in exports)
# EM_JS function are exports with a special prefix. We need to strip
# this prefix to get the actaul symbol name. For the main module, this
# is handled by extract_metadata.py.
exports = [shared.maybe_strip_prefix(e, '__em_js__') for e in exports]
settings.SIDE_MODULE_EXPORTS.extend(sorted(exports))

imports = webassembly.get_imports(dylib)
Expand Down
2 changes: 0 additions & 2 deletions emscripten.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,8 +344,6 @@ def emscript(in_wasm, out_wasm, outfile_js, memfile, js_syms):
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.emJsFuncs:
exit_with_error('EM_JS is not supported in side modules')
logger.debug('emscript: skipping remaining js glue generation')
return

Expand Down
37 changes: 36 additions & 1 deletion src/library_dylink.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,9 @@ var LibraryDylink = {
'__wasm_call_ctors',
'__start_em_asm',
'__stop_em_asm',
].includes(symName)
'__start_em_js',
'__stop_em_js',
].includes(symName) || symName.startsWith('__em_js__')
#if SPLIT_MODULE
// Exports synthesized by wasm-split should be prefixed with '%'
|| symName[0] == '%'
Expand Down Expand Up @@ -779,6 +781,39 @@ var LibraryDylink = {
start = HEAPU8.indexOf(0, start) + 1;
}
}

function addEmJs(name, cSig, body) {
// The signature here is a C signature (e.g. "(int foo, char* bar)").
// See `create_em_js` in emcc.py` for the build-time version of this
// code.
var jsArgs = [];
cSig = cSig.slice(1, -1)
if (cSig != 'void') {
cSig = cSig.split(',');
for (var i in cSig) {
var jsArg = cSig[i].split(' ').pop();
jsArgs.push(jsArg.replace('*', ''));
}
}
var func = `(${jsArgs}) => ${body};`;
#if DYLINK_DEBUG
dbg(`adding new EM_JS function: ${jsArgs} = ${func}`);
#endif
{{{ makeEval('moduleExports[name] = eval(func)') }}};
}

for (var name in moduleExports) {
if (name.startsWith('__em_js__')) {
var start = moduleExports[name]
{{{ from64('start') }}}
var jsString = UTF8ToString(start);
// EM_JS strings are stored in the data section in the form
// SIG<::>BODY.
var parts = jsString.split('<::>');
addEmJs(name.replace('__em_js__', ''), parts[0], parts[1]);
delete moduleExports[name];
}
}
#endif

// initialize the module
Expand Down
2 changes: 1 addition & 1 deletion test/other/metadce/test_metadce_hello_dylink.jssize
Original file line number Diff line number Diff line change
@@ -1 +1 @@
14888
15270
9 changes: 9 additions & 0 deletions test/other/test_em_js_main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include <stdio.h>
#include <emscripten.h>

int test_side();

int main() {
printf("in main\n");
return test_side();
}
4 changes: 4 additions & 0 deletions test/other/test_em_js_main.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
in main
hello from side module 42 + hello
hello again
hello from void func
21 changes: 21 additions & 0 deletions test/other/test_em_js_side.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include <emscripten/em_js.h>
#include <stdio.h>

EM_JS(void*, js_side_func, (int num, char* ptr), {
out(`hello from side module ${num} + ${UTF8ToString(ptr)}`);
return 99;
});

EM_JS(void, js_side_func2, (char *ptr), {
out(UTF8ToString(ptr));
});

EM_JS(void, js_side_func_void, (), {
out(`hello from void func`);
});

void test_side() {
js_side_func(42, "hello");
js_side_func2("hello again");
js_side_func_void();
}
4 changes: 2 additions & 2 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -11964,8 +11964,8 @@ def test_runtime_keepalive(self):

@crossplatform
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)
self.build(test_file('other/test_em_js_side.c'), js_outfile=False, emcc_args=['-sSIDE_MODULE'], output_basename='side')
self.do_other_test('test_em_js_main.c', emcc_args=['-sMAIN_MODULE=2', 'side.wasm'])

def test_em_js_main_module(self):
self.set_setting('MAIN_MODULE', 2)
Expand Down
7 changes: 7 additions & 0 deletions tools/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,13 @@ def strip_prefix(string, prefix):
return string[len(prefix):]


def maybe_strip_prefix(string, prefix):
if string.startswith(prefix):
return string[len(prefix):]
else:
return string


def make_writable(filename):
assert os.path.isfile(filename)
old_mode = stat.S_IMODE(os.stat(filename).st_mode)
Expand Down

0 comments on commit 7c58a8f

Please sign in to comment.