Skip to content

Commit 0282262

Browse files
committed
Allow JS library dependencies to be added in source code.
This change introduces a new EM_JS_DEPS macros that can be used to specific the JS library dependencies of user code in EM_JS and/or EM_ASM blocks. This is especially important for library authors who don't want to have their users maintain link-time list of required symbols. See #14729
1 parent 3715bef commit 0282262

20 files changed

+100
-31
lines changed

ChangeLog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ See docs/process.md for more on how version tagging works.
2020

2121
3.1.24 (in development)
2222
-----------------------
23+
- It is now possible to specify indirect dependencies on JS library functions
24+
directly C/C++ source code. For example, in the case of a EM_JS or EM_ASM
25+
JavaScript function depends on JS library function. See the `EM_JS_DEPS`
26+
macro in the `em_macros.h` header. Adding dependecies in this way avoids
27+
the need to specify them on the command line with
28+
`-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE`. (#17854)
2329

2430
3.1.23 - 09/23/22
2531
-----------------

emcc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1910,7 +1910,7 @@ def phase_linker_setup(options, state, newargs, user_settings):
19101910

19111911
if settings.USE_PTHREADS:
19121912
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += [
1913-
'$registerTLSInit',
1913+
'$registerTLSInit',
19141914
]
19151915

19161916
if settings.RELOCATABLE:

emscripten.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ def update_settings_glue(wasm_file, metadata):
165165
# exported. In theory it should always be present since its defined in compiler-rt.
166166
assert 'emscripten_stack_get_end' in metadata['exports']
167167

168+
for deps in metadata['jsDeps']:
169+
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.extend(deps.split(','))
170+
168171

169172
def apply_static_code_hooks(forwarded_json, code):
170173
code = shared.do_replace(code, '<<< ATINITS >>>', str(forwarded_json['ATINITS']))

src/settings_internal.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ var SIDE_MODULE_IMPORTS = [];
3838
// programs contains EM_JS or EM_ASM data section, in which case these symbols
3939
// won't exist.
4040
var EXPORT_IF_DEFINED = ['__start_em_asm', '__stop_em_asm',
41+
'__start_em_lib_deps', '__stop_em_lib_deps',
4142
'__start_em_js', '__stop_em_js'];
4243

4344
// Like EXPORTED_FUNCTIONS, but symbol is required to exist in native code.

system/include/emscripten/em_macros.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,9 @@
1414
#else
1515
#define EM_IMPORT(NAME)
1616
#endif
17+
18+
#define EM_JS_DEPS(name, deps) \
19+
EMSCRIPTEN_KEEPALIVE \
20+
__attribute__((section("em_lib_deps"))) \
21+
__attribute__((aligned(1))) \
22+
char __em_lib_deps_##name[] = deps;

test/core/dyncall_specific.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ int waka(int w, long long xy, int z) {
1919
return 42;
2020
}
2121

22+
EM_JS_DEPS(main, "$dynCall");
23+
2224
int main() {
2325
EM_ASM({
2426
// Note that these would need to use BigInts if the file were built with

test/core/test_asan_js_stack_op.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ EMSCRIPTEN_KEEPALIVE void c_func(char *str) {
55
printf("%s\n", str);
66
}
77

8+
EM_JS_DEPS(js_func, "$allocateUTF8OnStack");
9+
810
EM_JS(void, js_func, (void), {
911
_c_func(allocateUTF8OnStack('Hello, World!'));
1012
});

test/core/test_convertI32PairToI53Checked.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
// Uncomment to compute the expected results without testing:
1111
//#define GENERATE_ANSWERS
1212

13+
EM_JS_DEPS(test, "$convertI32PairToI53Checked");
14+
1315
double test(int64_t val) {
1416
int32_t lo = (uint32_t)val;
1517
int32_t hi = (uint64_t)val >> 32;

test/core/test_int53.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
// Uncomment to compute the expected result:
1212
//#define GENERATE_ANSWERS
1313

14+
EM_JS_DEPS(main, "$convertI32PairToI53,$convertU32PairToI53,$readI53FromU64,$readI53FromI64,$writeI53ToI64,$writeI53ToI64Clamped,$writeI53ToU64Clamped,$writeI53ToI64Signaling,$writeI53ToU64Signaling");
15+
1416
void writeI53ToI64_int64(int64_t *heapAddress, int64_t num) {
1517
#ifdef GENERATE_ANSWERS
1618
*heapAddress = num;

test/interop/test_add_function.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ extern "C" int baz() {
2020
return 3;
2121
}
2222

23+
EM_JS_DEPS(main, "$addFunction,$removeFunction");
24+
2325
int main(int argc, char **argv) {
2426
#if defined(GROWTH)
2527
EM_ASM({

test/other/test_offset_converter.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ void *get_pc(void) {
55
return __builtin_return_address(0);
66
}
77

8+
EM_JS_DEPS(test, "$ptrToString");
9+
810
void magic_test_function(void) {
911
void* pc = get_pc(); // This is line (9) and on which we fetch the PC (at column 14).
1012
EM_ASM({

test/other/test_runtime_keepalive.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#include <emscripten.h>
22
#include <stdio.h>
33

4+
EM_JS_DEPS(main, "$runtimeKeepalivePush,$runtimeKeepalivePop,$callUserCallback");
5+
46
int main() {
57
EM_ASM({
68
Module["onExit"] = () => { out("onExit"); };

test/stack_overflow.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@
55

66
#include <stdio.h>
77
#include <string.h>
8-
#include <emscripten.h>
98

10-
void __attribute__((noinline)) InteropString(char *staticBuffer)
11-
{
9+
#include <emscripten/em_asm.h>
10+
#include <emscripten/em_macros.h>
11+
12+
EM_JS_DEPS(main, "$allocateUTF8OnStack");
13+
14+
void __attribute__((noinline)) InteropString(char *staticBuffer) {
1215
char *string = (char*)EM_ASM_PTR({
1316
var str = "hello, this is a string! ";
1417
#if ONE_BIG_STRING
@@ -27,12 +30,12 @@ void __attribute__((noinline)) InteropString(char *staticBuffer)
2730
});
2831
}
2932

30-
int main()
31-
{
33+
int main() {
3234
// Make C side consume a large portion of the stack, before bumping the rest with C++<->JS interop.
3335
char staticBuffer[512288] = {};
3436
InteropString(staticBuffer);
3537
int stringLength = strlen(staticBuffer);
3638
printf("Got string: %s\n", staticBuffer);
3739
printf("Received a string of length %d.\n", stringLength);
40+
return 0;
3841
}

test/test_c_preprocessor.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ EM_JS(void, test_remove_cpp_comments_in_shaders, (void), {
3333
if (numFailed) throw numFailed + ' tests failed!';
3434
});
3535

36+
EM_JS_DEPS(main, "$preprocess_c_code,$remove_cpp_comments_in_shaders");
37+
3638
EM_JS(void, test_c_preprocessor, (void), {
3739
var numFailed = 0;
3840
function test(input, expected) {
@@ -183,4 +185,4 @@ int main()
183185
test_remove_cpp_comments_in_shaders();
184186

185187
test_c_preprocessor();
186-
}
188+
}

test/test_core.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -472,8 +472,6 @@ def test_sintvars(self):
472472
self.do_core_test('test_sintvars.c')
473473

474474
def test_int53(self):
475-
self.emcc_args += ['-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=[$convertI32PairToI53,$convertU32PairToI53,$readI53FromU64,$readI53FromI64,$writeI53ToI64,$writeI53ToI64Clamped,$writeI53ToU64Clamped,$writeI53ToI64Signaling,$writeI53ToU64Signaling]']
476-
477475
if common.EMTEST_REBASELINE:
478476
self.run_process([EMCC, test_file('core/test_int53.c'), '-o', 'a.js', '-DGENERATE_ANSWERS'] + self.emcc_args)
479477
ret = self.run_process(config.NODE_JS + ['a.js'], stdout=PIPE).stdout
@@ -482,7 +480,6 @@ def test_int53(self):
482480
self.do_core_test('test_int53.c', interleaved_output=False)
483481

484482
def test_int53_convertI32PairToI53Checked(self):
485-
self.emcc_args += ['-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=[$convertI32PairToI53Checked]']
486483
if common.EMTEST_REBASELINE:
487484
self.run_process([EMCC, test_file('core/test_convertI32PairToI53Checked.cpp'), '-o', 'a.js', '-DGENERATE_ANSWERS'] + self.emcc_args)
488485
ret = self.run_process(config.NODE_JS + ['a.js'], stdout=PIPE).stdout
@@ -6104,7 +6101,6 @@ def test_unistd_symlink_on_nodefs(self):
61046101

61056102
@also_with_wasm_bigint
61066103
def test_unistd_io(self):
6107-
self.set_setting('DEFAULT_LIBRARY_FUNCS_TO_INCLUDE', ['$ERRNO_CODES'])
61086104
orig_compiler_opts = self.emcc_args.copy()
61096105
for fs in ['MEMFS', 'NODEFS']:
61106106
self.clear()
@@ -7075,14 +7071,14 @@ def test_dyncall_specific(self, *args):
70757071
self.skipTest('not compatible with WASM_BIGINT')
70767072
cases = [
70777073
('DIRECT', []),
7078-
('DYNAMIC_SIG', ['-sDYNCALLS=1', '-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$dynCall']),
7074+
('DYNAMIC_SIG', ['-sDYNCALLS=1']),
70797075
]
70807076
if '-sMINIMAL_RUNTIME=1' in args:
70817077
self.emcc_args += ['--pre-js', test_file('minimal_runtime_exit_handling.js')]
70827078
else:
70837079
cases += [
70847080
('EXPORTED', []),
7085-
('EXPORTED_DYNAMIC_SIG', ['-sDYNCALLS=1', '-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$dynCall', '-sEXPORTED_RUNTIME_METHODS=dynCall']),
7081+
('EXPORTED_DYNAMIC_SIG', ['-sDYNCALLS=1', '-sEXPORTED_RUNTIME_METHODS=dynCall']),
70867082
('FROM_OUTSIDE', ['-sEXPORTED_RUNTIME_METHODS=dynCall_iiji'])
70877083
]
70887084

@@ -7248,7 +7244,6 @@ def test_add_function(self):
72487244
self.set_setting('WASM_ASYNC_COMPILATION', 0)
72497245
self.set_setting('RESERVED_FUNCTION_POINTERS')
72507246
self.set_setting('EXPORTED_RUNTIME_METHODS', ['callMain'])
7251-
self.set_setting('DEFAULT_LIBRARY_FUNCS_TO_INCLUDE', ['$addFunction', '$removeFunction'])
72527247
src = test_file('interop/test_add_function.cpp')
72537248
post_js = test_file('interop/test_add_function_post.js')
72547249
self.emcc_args += ['--post-js', post_js]
@@ -7646,7 +7641,6 @@ def test_webidl(self, mode, allow_memory_growth):
76467641
# TODO(): Remove once we make webidl output closure-warning free.
76477642
self.ldflags.append('-Wno-error=closure')
76487643
self.set_setting('WASM_ASYNC_COMPILATION', 0)
7649-
self.set_setting('DEFAULT_LIBRARY_FUNCS_TO_INCLUDE', ['$intArrayFromString'])
76507644
if self.maybe_closure():
76517645
# avoid closure minified names competing with our test code in the global name space
76527646
self.set_setting('MODULARIZE')
@@ -8534,7 +8528,6 @@ def test_fs_dict_none(self):
85348528
def test_stack_overflow_check(self):
85358529
self.set_setting('TOTAL_STACK', 1048576)
85368530
self.set_setting('STACK_OVERFLOW_CHECK', 2)
8537-
self.set_setting('DEFAULT_LIBRARY_FUNCS_TO_INCLUDE', '$allocateUTF8OnStack')
85388531
self.do_runf(test_file('stack_overflow.cpp'), 'Aborted(stack overflow', assert_returncode=NON_ZERO)
85398532

85408533
self.emcc_args += ['-DONE_BIG_STRING']
@@ -8942,7 +8935,6 @@ def test_asan_js_stack_op(self):
89428935
self.emcc_args.append('-fsanitize=address')
89438936
self.set_setting('ALLOW_MEMORY_GROWTH')
89448937
self.set_setting('INITIAL_MEMORY', '300mb')
8945-
self.set_setting('DEFAULT_LIBRARY_FUNCS_TO_INCLUDE', ['$allocateUTF8OnStack'])
89468938
self.do_runf(test_file('core/test_asan_js_stack_op.c'),
89478939
expected_output='Hello, World!')
89488940

test/test_other.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9644,7 +9644,7 @@ def get_file_gzipped_size(f):
96449644

96459645
# Tests the library_c_preprocessor.js functionality.
96469646
def test_c_preprocessor(self):
9647-
self.run_process([EMXX, test_file('test_c_preprocessor.c'), '--js-library', path_from_root('src/library_c_preprocessor.js'), '-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$remove_cpp_comments_in_shaders,$preprocess_c_code'])
9647+
self.run_process([EMXX, test_file('test_c_preprocessor.c'), '--js-library', path_from_root('src/library_c_preprocessor.js')])
96489648
normal = self.run_js('a.out.js')
96499649
print(str(normal))
96509650

@@ -9983,7 +9983,6 @@ def test_proxy_to_pthread_stack(self):
99839983
})
99849984
def test_offset_converter(self, *args):
99859985
self.set_setting('USE_OFFSET_CONVERTER')
9986-
self.set_setting('DEFAULT_LIBRARY_FUNCS_TO_INCLUDE', ['$ptrToString'])
99879986
self.emcc_args += ['--profiling-funcs']
99889987
self.do_runf(test_file('other/test_offset_converter.c'), 'ok', emcc_args=list(args))
99899988

@@ -9994,7 +9993,6 @@ def test_offset_converter(self, *args):
99949993
def test_offset_converter_source_map(self, *args):
99959994
self.set_setting('USE_OFFSET_CONVERTER')
99969995
self.set_setting('LOAD_SOURCE_MAP')
9997-
self.set_setting('DEFAULT_LIBRARY_FUNCS_TO_INCLUDE', ['$ptrToString'])
99989996
self.emcc_args += ['-gsource-map', '-DUSE_SOURCE_MAP']
99999997
self.do_runf(test_file('other/test_offset_converter.c'), 'ok', emcc_args=list(args))
100009998

@@ -11015,9 +11013,9 @@ def test_missing_malloc_export_indirect(self):
1101511013
# we used to include malloc by default. show a clear error in builds with
1101611014
# ASSERTIONS to help with any confusion when the user calls a JS API that
1101711015
# requires malloc
11018-
self.set_setting('DEFAULT_LIBRARY_FUNCS_TO_INCLUDE', '$allocateUTF8')
1101911016
create_file('unincluded_malloc.c', r'''
1102011017
#include <emscripten.h>
11018+
EM_JS_DEPS(main, "$allocateUTF8");
1102111019
int main() {
1102211020
EM_ASM({
1102311021
try {
@@ -11452,7 +11450,6 @@ def test_shell_Oz(self):
1145211450

1145311451
def test_runtime_keepalive(self):
1145411452
self.uses_es6 = True
11455-
self.set_setting('DEFAULT_LIBRARY_FUNCS_TO_INCLUDE', ['$runtimeKeepalivePush', '$runtimeKeepalivePop', '$callUserCallback'])
1145611453
self.set_setting('EXIT_RUNTIME')
1145711454
self.do_other_test('test_runtime_keepalive.cpp')
1145811455

@@ -12455,11 +12452,12 @@ def test_bigint64array_polyfill(self):
1245512452
self.assertEqual(v1, v2, msg=m)
1245612453

1245712454
def test_warn_once(self):
12458-
self.set_setting('DEFAULT_LIBRARY_FUNCS_TO_INCLUDE', ['$warnOnce'])
1245912455
create_file('main.c', r'''\
1246012456
#include <stdio.h>
1246112457
#include <emscripten.h>
1246212458
12459+
EM_JS_DEPS(main, "$warnOnce");
12460+
1246312461
int main() {
1246412462
EM_ASM({
1246512463
warnOnce("foo");
@@ -12500,3 +12498,28 @@ def test_fs_icase(self):
1250012498

1250112499
def test_strict_js_closure(self):
1250212500
self.do_runf(test_file('hello_world.c'), emcc_args=['-sSTRICT_JS', '-Werror=closure', '--closure=1', '-O3'])
12501+
12502+
def test_em_js_deps(self):
12503+
# Check that EM_JS_DEPS works. Specifically, multiple different instances in different
12504+
# object files.
12505+
create_file('f1.c', '''
12506+
#include <emscripten.h>
12507+
12508+
EM_JS_DEPS(other, "$allocateUTF8OnStack");
12509+
''')
12510+
create_file('f2.c', '''
12511+
#include <emscripten.h>
12512+
12513+
EM_JS_DEPS(main, "$getHeapMax");
12514+
12515+
int main() {
12516+
EM_ASM({
12517+
err(getHeapMax());
12518+
var x = stackSave();
12519+
allocateUTF8OnStack("hello");
12520+
stackRestore(x);
12521+
});
12522+
return 0;
12523+
}
12524+
''')
12525+
self.do_runf('f2.c', emcc_args=['f1.c'])

test/unistd/io.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include <sys/uio.h>
1414
#include <emscripten.h>
1515

16+
EM_JS_DEPS(main, "$ERRNO_CODES");
17+
1618
int main() {
1719
EM_ASM(
1820
FS.mkdir('/working');

0 commit comments

Comments
 (0)