diff --git a/test/test_other.py b/test/test_other.py index 76b051cc9206a..4526f04eea8ed 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -10194,13 +10194,13 @@ def test_dash_s_list_parsing(self): # Simple one-per-line response file format ("EXPORTED_FUNCTIONS=@response.txt", ''), # stray slash - ("EXPORTED_FUNCTIONS=['_a', '_b', \\'_c', '_d']", '''invalid export name: "\\\\'_c'"'''), + ("EXPORTED_FUNCTIONS=['_a', '_b', \\'_c', '_d']", r'''emcc: error: undefined exported symbol: "\\'_c'"'''), # stray slash - ("EXPORTED_FUNCTIONS=['_a', '_b',\\ '_c', '_d']", '''invalid export name: "\\\\ '_c'"'''), + ("EXPORTED_FUNCTIONS=['_a', '_b',\\ '_c', '_d']", r'''emcc: error: undefined exported symbol: "\\ '_c'"'''), # stray slash - ('EXPORTED_FUNCTIONS=["_a", "_b", \\"_c", "_d"]', 'invalid export name: "\\\\"_c""'), + ('EXPORTED_FUNCTIONS=["_a", "_b", \\"_c", "_d"]', r'emcc: error: undefined exported symbol: "\\"_c""'), # stray slash - ('EXPORTED_FUNCTIONS=["_a", "_b",\\ "_c", "_d"]', 'invalid export name: "\\\\ "_c"'), + ('EXPORTED_FUNCTIONS=["_a", "_b",\\ "_c", "_d"]', r'emcc: error: undefined exported symbol: "\\ "_c""'), # missing comma ('EXPORTED_FUNCTIONS=["_a", "_b" "_c", "_d"]', 'wasm-ld: error: symbol exported via --export not found: b" "_c'), ]: @@ -14809,21 +14809,45 @@ def test_cxx20_modules_std_headers(self): self.do_runf('main.cpp', 'Hello Module!', cflags=['-std=c++20', '-fmodules']) def test_invalid_export_name(self): - create_file('test.c', '__attribute__((export_name("my.func"))) void myfunc() {}') - expected = 'emcc: error: invalid export name: "_my.func"' - self.assert_fail([EMCC, 'test.c'], expected) + create_file('main.c', r''' + #include + #include + + __attribute__((export_name("my.func"))) int myfunc() { return 42; } + + int main() { + int rtn = EM_ASM_INT(return Module['_my.func']()); + printf("got: %d\n", rtn); + + int rtn2 = EM_ASM_INT(return wasmExports['my.func']()); + printf("got2: %d\n", rtn2); + return 0; + } + ''') + expected = 'emcc: error: export name is not a valid JS symbol: "_my.func". Use `Module` or `wasmExports` to access this symbol' + self.assert_fail([EMCC, '-Werror', 'main.c'], expected) + + # With warning suppressed the above program should work. + self.do_runf('main.c', 'got: 42\ngot2: 42\n', cflags=['-Wno-js-compiler']) # When we are generating only wasm and not JS we don't need exports to # be valid JS symbols. - self.run_process([EMCC, 'test.c', '--no-entry', '-o', 'out.wasm']) - - # GCC (and clang) and JavaScript also allow $ in symbol names - create_file('valid.c', ''' - #include - EMSCRIPTEN_KEEPALIVE - void my$func() {} - ''') - self.run_process([EMCC, 'valid.c']) + self.run_process([EMCC, '-Werror', 'main.c', '--no-entry', '-o', 'out.wasm']) + + def test_export_with_dollarsign(self): + # GCC (and clang) and JavaScript both allow $ in symbol names + create_file('main.c', r''' + #include + #include + + EMSCRIPTEN_KEEPALIVE int my$func() { return 42; } + + int main() { + int rtn = EM_ASM_INT(return _my$func()); + printf("got: %d\n", rtn); + return 0; + }''') + self.do_runf('main.c', 'got: 42\n') @also_with_modularize def test_instantiate_wasm(self): diff --git a/tools/emscripten.py b/tools/emscripten.py index 353e940e21ddb..d9851eba68c41 100644 --- a/tools/emscripten.py +++ b/tools/emscripten.py @@ -339,7 +339,7 @@ def emscript(in_wasm, out_wasm, outfile_js, js_syms, finalize=True, base_metadat for e in settings.EXPORTED_FUNCTIONS: if not js_manipulation.isidentifier(e): - exit_with_error(f'invalid export name: "{e}"') + diagnostics.warning('js-compiler', f'export name is not a valid JS symbol: "{e}". Use `Module` or `wasmExports` to access this symbol') # memory and global initializers @@ -937,10 +937,16 @@ def create_receiving(function_exports, other_exports, library_symbols, aliases): # folks try to call/use a reference that was taken before the # wasm module is available. for sym in mangled: - assignment = sym - if (settings.MODULARIZE or not settings.MINIMAL_RUNTIME) and should_export(sym) and settings.MODULARIZE != 'instance': - assignment += f" = Module['{sym}']" - receiving.append(f"var {assignment} = makeInvalidEarlyAccess('{sym}');") + module_export = (settings.MODULARIZE or not settings.MINIMAL_RUNTIME) and should_export(sym) and settings.MODULARIZE != 'instance' + if not js_manipulation.isidentifier(sym) and not module_export: + continue + assignment = f'var {sym}' + if module_export: + if js_manipulation.isidentifier(sym): + assignment += f" = Module['{sym}']" + else: + assignment = f"Module['{sym}']" + receiving.append(f"{assignment} = makeInvalidEarlyAccess('{sym}');") else: # Declare all exports in a single var statement sep = ',\n ' @@ -975,7 +981,15 @@ def create_receiving(function_exports, other_exports, library_symbols, aliases): sig_str = sym.removeprefix('dynCall_') assignment += f" = dynCalls['{sig_str}']" if do_module_exports and should_export(mangled): - assignment += f" = Module['{mangled}']" + if js_manipulation.isidentifier(mangled): + assignment += f" = Module['{mangled}']" + else: + assignment = f"Module['{mangled}']" + elif not js_manipulation.isidentifier(mangled): + # Symbol is not a valid JS identify and also not exported. In this case we + # have nothing to do here. + continue + if sym in alias_inverse_map: for target in alias_inverse_map[sym]: assignment += f" = {target}"