Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable EM_JS in side modules #19705

Merged
merged 1 commit into from
Jun 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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('*', ''));
}
}
sbc100 marked this conversation as resolved.
Show resolved Hide resolved
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