Skip to content

Commit f14fbd8

Browse files
committed
Allow exporting of symbols that are not valid JS identifiers
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
1 parent 666966f commit f14fbd8

File tree

2 files changed

+56
-18
lines changed

2 files changed

+56
-18
lines changed

test/test_other.py

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14809,21 +14809,45 @@ def test_cxx20_modules_std_headers(self):
1480914809
self.do_runf('main.cpp', 'Hello Module!', cflags=['-std=c++20', '-fmodules'])
1481014810

1481114811
def test_invalid_export_name(self):
14812-
create_file('test.c', '__attribute__((export_name("my.func"))) void myfunc() {}')
14813-
expected = 'emcc: error: invalid export name: "_my.func"'
14814-
self.assert_fail([EMCC, 'test.c'], expected)
14812+
create_file('main.c', r'''
14813+
#include <emscripten.h>
14814+
#include <stdio.h>
14815+
14816+
__attribute__((export_name("my.func"))) int myfunc() { return 42; }
14817+
14818+
int main() {
14819+
int rtn = EM_ASM_INT(return Module['_my.func']());
14820+
printf("got: %d\n", rtn);
14821+
14822+
int rtn2 = EM_ASM_INT(return wasmExports['my.func']());
14823+
printf("got2: %d\n", rtn2);
14824+
return 0;
14825+
}
14826+
''')
14827+
expected = 'emcc: error: export name is not a valid JS symbol: "_my.func". Use `Module` or `wasmExports` to access this symbol'
14828+
self.assert_fail([EMCC, '-Werror', 'main.c'], expected)
14829+
14830+
# With warning suppressed the above program should work.
14831+
self.do_runf('main.c', 'got: 42\ngot2: 42\n', cflags=['-Wno-js-compiler'])
1481514832

1481614833
# When we are generating only wasm and not JS we don't need exports to
1481714834
# be valid JS symbols.
14818-
self.run_process([EMCC, 'test.c', '--no-entry', '-o', 'out.wasm'])
14819-
14820-
# GCC (and clang) and JavaScript also allow $ in symbol names
14821-
create_file('valid.c', '''
14822-
#include <emscripten.h>
14823-
EMSCRIPTEN_KEEPALIVE
14824-
void my$func() {}
14825-
''')
14826-
self.run_process([EMCC, 'valid.c'])
14835+
self.run_process([EMCC, '-Werror', 'main.c', '--no-entry', '-o', 'out.wasm'])
14836+
14837+
def test_export_with_dollarsign(self):
14838+
# GCC (and clang) and JavaScript both allow $ in symbol names
14839+
create_file('main.c', r'''
14840+
#include <emscripten.h>
14841+
#include <stdio.h>
14842+
14843+
EMSCRIPTEN_KEEPALIVE int my$func() { return 42; }
14844+
14845+
int main() {
14846+
int rtn = EM_ASM_INT(return _my$func());
14847+
printf("got: %d\n", rtn);
14848+
return 0;
14849+
}''')
14850+
self.do_runf('main.c', 'got: 42\n')
1482714851

1482814852
@also_with_modularize
1482914853
def test_instantiate_wasm(self):

tools/emscripten.py

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

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

344344
# memory and global initializers
345345

@@ -937,10 +937,16 @@ def create_receiving(function_exports, other_exports, library_symbols, aliases):
937937
# folks try to call/use a reference that was taken before the
938938
# wasm module is available.
939939
for sym in mangled:
940-
assignment = sym
941-
if (settings.MODULARIZE or not settings.MINIMAL_RUNTIME) and should_export(sym) and settings.MODULARIZE != 'instance':
942-
assignment += f" = Module['{sym}']"
943-
receiving.append(f"var {assignment} = makeInvalidEarlyAccess('{sym}');")
940+
module_export = (settings.MODULARIZE or not settings.MINIMAL_RUNTIME) and should_export(sym) and settings.MODULARIZE != 'instance'
941+
if not js_manipulation.isidentifier(sym) and not module_export:
942+
continue
943+
assignment = f'var {sym}'
944+
if module_export:
945+
if js_manipulation.isidentifier(sym):
946+
assignment += f" = Module['{sym}']"
947+
else:
948+
assignment = f"Module['{sym}']"
949+
receiving.append(f"{assignment} = makeInvalidEarlyAccess('{sym}');")
944950
else:
945951
# Declare all exports in a single var statement
946952
sep = ',\n '
@@ -975,7 +981,15 @@ def create_receiving(function_exports, other_exports, library_symbols, aliases):
975981
sig_str = sym.removeprefix('dynCall_')
976982
assignment += f" = dynCalls['{sig_str}']"
977983
if do_module_exports and should_export(mangled):
978-
assignment += f" = Module['{mangled}']"
984+
if js_manipulation.isidentifier(mangled):
985+
assignment += f" = Module['{mangled}']"
986+
else:
987+
assignment = f"Module['{mangled}']"
988+
elif not js_manipulation.isidentifier(mangled):
989+
# Symbol is not a valid JS identify and also not exported. In this case we
990+
# have nothing to do here.
991+
continue
992+
979993
if sym in alias_inverse_map:
980994
for target in alias_inverse_map[sym]:
981995
assignment += f" = {target}"

0 commit comments

Comments
 (0)