Skip to content

Commit edb2d32

Browse files
authored
Allow exporting of symbols that are not valid JS identifiers (#25922)
In this case we now generate a warning instead of an errors. Such symbols are not directly accessible in the module scope (since we cannot declare them there). They are only accessible via `wasmExports` or `Module` dictionary objects. See: llvm/llvm-project#169043 Fixes: #24825, #23560
1 parent f216821 commit edb2d32

File tree

2 files changed

+60
-22
lines changed

2 files changed

+60
-22
lines changed

test/test_other.py

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10237,13 +10237,13 @@ def test_dash_s_list_parsing(self):
1023710237
# Simple one-per-line response file format
1023810238
1023910239
# stray slash
10240-
("EXPORTED_FUNCTIONS=['_a', '_b', \\'_c', '_d']", '''invalid export name: "\\\\'_c'"'''),
10240+
("EXPORTED_FUNCTIONS=['_a', '_b', \\'_c', '_d']", r'''emcc: error: undefined exported symbol: "\\'_c'"'''),
1024110241
# stray slash
10242-
("EXPORTED_FUNCTIONS=['_a', '_b',\\ '_c', '_d']", '''invalid export name: "\\\\ '_c'"'''),
10242+
("EXPORTED_FUNCTIONS=['_a', '_b',\\ '_c', '_d']", r'''emcc: error: undefined exported symbol: "\\ '_c'"'''),
1024310243
# stray slash
10244-
('EXPORTED_FUNCTIONS=["_a", "_b", \\"_c", "_d"]', 'invalid export name: "\\\\"_c""'),
10244+
('EXPORTED_FUNCTIONS=["_a", "_b", \\"_c", "_d"]', r'emcc: error: undefined exported symbol: "\\"_c""'),
1024510245
# stray slash
10246-
('EXPORTED_FUNCTIONS=["_a", "_b",\\ "_c", "_d"]', 'invalid export name: "\\\\ "_c"'),
10246+
('EXPORTED_FUNCTIONS=["_a", "_b",\\ "_c", "_d"]', r'emcc: error: undefined exported symbol: "\\ "_c""'),
1024710247
# missing comma
1024810248
('EXPORTED_FUNCTIONS=["_a", "_b" "_c", "_d"]', 'wasm-ld: error: symbol exported via --export not found: b" "_c'),
1024910249
]:
@@ -14852,21 +14852,45 @@ def test_cxx20_modules_std_headers(self):
1485214852
self.do_runf('main.cpp', 'Hello Module!', cflags=['-std=c++20', '-fmodules'])
1485314853

1485414854
def test_invalid_export_name(self):
14855-
create_file('test.c', '__attribute__((export_name("my.func"))) void myfunc() {}')
14856-
expected = 'emcc: error: invalid export name: "_my.func"'
14857-
self.assert_fail([EMCC, 'test.c'], expected)
14855+
create_file('main.c', r'''
14856+
#include <emscripten.h>
14857+
#include <stdio.h>
14858+
14859+
__attribute__((export_name("my.func"))) int myfunc() { return 42; }
14860+
14861+
int main() {
14862+
int rtn = EM_ASM_INT(return Module['_my.func']());
14863+
printf("got: %d\n", rtn);
14864+
14865+
int rtn2 = EM_ASM_INT(return wasmExports['my.func']());
14866+
printf("got2: %d\n", rtn2);
14867+
return 0;
14868+
}
14869+
''')
14870+
expected = 'emcc: error: export name is not a valid JS symbol: "_my.func". Use `Module` or `wasmExports` to access this symbol'
14871+
self.assert_fail([EMCC, '-Werror', 'main.c'], expected)
14872+
14873+
# With warning suppressed the above program should work.
14874+
self.do_runf('main.c', 'got: 42\ngot2: 42\n', cflags=['-Wno-js-compiler'])
1485814875

1485914876
# When we are generating only wasm and not JS we don't need exports to
1486014877
# be valid JS symbols.
14861-
self.run_process([EMCC, 'test.c', '--no-entry', '-o', 'out.wasm'])
14862-
14863-
# GCC (and clang) and JavaScript also allow $ in symbol names
14864-
create_file('valid.c', '''
14865-
#include <emscripten.h>
14866-
EMSCRIPTEN_KEEPALIVE
14867-
void my$func() {}
14868-
''')
14869-
self.run_process([EMCC, 'valid.c'])
14878+
self.run_process([EMCC, '-Werror', 'main.c', '--no-entry', '-o', 'out.wasm'])
14879+
14880+
def test_export_with_dollarsign(self):
14881+
# GCC (and clang) and JavaScript both allow $ in symbol names
14882+
create_file('main.c', r'''
14883+
#include <emscripten.h>
14884+
#include <stdio.h>
14885+
14886+
EMSCRIPTEN_KEEPALIVE int my$func() { return 42; }
14887+
14888+
int main() {
14889+
int rtn = EM_ASM_INT(return _my$func());
14890+
printf("got: %d\n", rtn);
14891+
return 0;
14892+
}''')
14893+
self.do_runf('main.c', 'got: 42\n')
1487014894

1487114895
@also_with_modularize
1487214896
def test_instantiate_wasm(self):

tools/emscripten.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ def emscript(in_wasm, out_wasm, outfile_js, js_syms, finalize=True, base_metadat
419419

420420
for e in settings.EXPORTED_FUNCTIONS:
421421
if not js_manipulation.isidentifier(e):
422-
exit_with_error(f'invalid export name: "{e}"')
422+
diagnostics.warning('js-compiler', f'export name is not a valid JS symbol: "{e}". Use `Module` or `wasmExports` to access this symbol')
423423

424424
# memory and global initializers
425425

@@ -1019,10 +1019,16 @@ def create_receiving(function_exports, other_exports, library_symbols, aliases):
10191019
# folks try to call/use a reference that was taken before the
10201020
# wasm module is available.
10211021
for sym in mangled:
1022-
assignment = sym
1023-
if (settings.MODULARIZE or not settings.MINIMAL_RUNTIME) and should_export(sym) and settings.MODULARIZE != 'instance':
1024-
assignment += f" = Module['{sym}']"
1025-
receiving.append(f"var {assignment} = makeInvalidEarlyAccess('{sym}');")
1022+
module_export = (settings.MODULARIZE or not settings.MINIMAL_RUNTIME) and should_export(sym) and settings.MODULARIZE != 'instance'
1023+
if not js_manipulation.isidentifier(sym) and not module_export:
1024+
continue
1025+
assignment = f'var {sym}'
1026+
if module_export:
1027+
if js_manipulation.isidentifier(sym):
1028+
assignment += f" = Module['{sym}']"
1029+
else:
1030+
assignment = f"Module['{sym}']"
1031+
receiving.append(f"{assignment} = makeInvalidEarlyAccess('{sym}');")
10261032
else:
10271033
# Declare all exports in a single var statement
10281034
sep = ',\n '
@@ -1057,7 +1063,15 @@ def create_receiving(function_exports, other_exports, library_symbols, aliases):
10571063
sig_str = sym.removeprefix('dynCall_')
10581064
assignment += f" = dynCalls['{sig_str}']"
10591065
if do_module_exports and should_export(mangled):
1060-
assignment += f" = Module['{mangled}']"
1066+
if js_manipulation.isidentifier(mangled):
1067+
assignment += f" = Module['{mangled}']"
1068+
else:
1069+
assignment = f"Module['{mangled}']"
1070+
elif not js_manipulation.isidentifier(mangled):
1071+
# Symbol is not a valid JS identify and also not exported. In this case we
1072+
# have nothing to do here.
1073+
continue
1074+
10611075
if sym in alias_inverse_map:
10621076
for target in alias_inverse_map[sym]:
10631077
assignment += f" = {target}"

0 commit comments

Comments
 (0)