Skip to content

Commit 7c58a8f

Browse files
authored
Enable EM_JS in side modules (#19705)
This works in a similar way to EM_ASM. See #18228. Depends on WebAssembly/binaryen#5780
1 parent 86eef90 commit 7c58a8f

9 files changed

+84
-6
lines changed

emcc.py

+4
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,10 @@ def process_dynamic_libs(dylibs, lib_dirs):
831831
for dylib in dylibs:
832832
exports = webassembly.get_exports(dylib)
833833
exports = set(e.name for e in exports)
834+
# EM_JS function are exports with a special prefix. We need to strip
835+
# this prefix to get the actaul symbol name. For the main module, this
836+
# is handled by extract_metadata.py.
837+
exports = [shared.maybe_strip_prefix(e, '__em_js__') for e in exports]
834838
settings.SIDE_MODULE_EXPORTS.extend(sorted(exports))
835839

836840
imports = webassembly.get_imports(dylib)

emscripten.py

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

346346
if settings.SIDE_MODULE:
347-
if metadata.emJsFuncs:
348-
exit_with_error('EM_JS is not supported in side modules')
349347
logger.debug('emscript: skipping remaining js glue generation')
350348
return
351349

src/library_dylink.js

+36-1
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,9 @@ var LibraryDylink = {
178178
'__wasm_call_ctors',
179179
'__start_em_asm',
180180
'__stop_em_asm',
181-
].includes(symName)
181+
'__start_em_js',
182+
'__stop_em_js',
183+
].includes(symName) || symName.startsWith('__em_js__')
182184
#if SPLIT_MODULE
183185
// Exports synthesized by wasm-split should be prefixed with '%'
184186
|| symName[0] == '%'
@@ -779,6 +781,39 @@ var LibraryDylink = {
779781
start = HEAPU8.indexOf(0, start) + 1;
780782
}
781783
}
784+
785+
function addEmJs(name, cSig, body) {
786+
// The signature here is a C signature (e.g. "(int foo, char* bar)").
787+
// See `create_em_js` in emcc.py` for the build-time version of this
788+
// code.
789+
var jsArgs = [];
790+
cSig = cSig.slice(1, -1)
791+
if (cSig != 'void') {
792+
cSig = cSig.split(',');
793+
for (var i in cSig) {
794+
var jsArg = cSig[i].split(' ').pop();
795+
jsArgs.push(jsArg.replace('*', ''));
796+
}
797+
}
798+
var func = `(${jsArgs}) => ${body};`;
799+
#if DYLINK_DEBUG
800+
dbg(`adding new EM_JS function: ${jsArgs} = ${func}`);
801+
#endif
802+
{{{ makeEval('moduleExports[name] = eval(func)') }}};
803+
}
804+
805+
for (var name in moduleExports) {
806+
if (name.startsWith('__em_js__')) {
807+
var start = moduleExports[name]
808+
{{{ from64('start') }}}
809+
var jsString = UTF8ToString(start);
810+
// EM_JS strings are stored in the data section in the form
811+
// SIG<::>BODY.
812+
var parts = jsString.split('<::>');
813+
addEmJs(name.replace('__em_js__', ''), parts[0], parts[1]);
814+
delete moduleExports[name];
815+
}
816+
}
782817
#endif
783818

784819
// initialize the module
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
14888
1+
15270

test/other/test_em_js_main.c

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

test/other/test_em_js_main.out

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
in main
2+
hello from side module 42 + hello
3+
hello again
4+
hello from void func

test/other/test_em_js_side.c

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#include <emscripten/em_js.h>
2+
#include <stdio.h>
3+
4+
EM_JS(void*, js_side_func, (int num, char* ptr), {
5+
out(`hello from side module ${num} + ${UTF8ToString(ptr)}`);
6+
return 99;
7+
});
8+
9+
EM_JS(void, js_side_func2, (char *ptr), {
10+
out(UTF8ToString(ptr));
11+
});
12+
13+
EM_JS(void, js_side_func_void, (), {
14+
out(`hello from void func`);
15+
});
16+
17+
void test_side() {
18+
js_side_func(42, "hello");
19+
js_side_func2("hello again");
20+
js_side_func_void();
21+
}

test/test_other.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -11964,8 +11964,8 @@ def test_runtime_keepalive(self):
1196411964

1196511965
@crossplatform
1196611966
def test_em_js_side_module(self):
11967-
err = self.expect_fail([EMXX, '-sSIDE_MODULE', test_file('core/test_em_js.cpp')])
11968-
self.assertContained('EM_JS is not supported in side modules', err)
11967+
self.build(test_file('other/test_em_js_side.c'), js_outfile=False, emcc_args=['-sSIDE_MODULE'], output_basename='side')
11968+
self.do_other_test('test_em_js_main.c', emcc_args=['-sMAIN_MODULE=2', 'side.wasm'])
1196911969

1197011970
def test_em_js_main_module(self):
1197111971
self.set_setting('MAIN_MODULE', 2)

tools/shared.py

+7
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,13 @@ def strip_prefix(string, prefix):
673673
return string[len(prefix):]
674674

675675

676+
def maybe_strip_prefix(string, prefix):
677+
if string.startswith(prefix):
678+
return string[len(prefix):]
679+
else:
680+
return string
681+
682+
676683
def make_writable(filename):
677684
assert os.path.isfile(filename)
678685
old_mode = stat.S_IMODE(os.stat(filename).st_mode)

0 commit comments

Comments
 (0)