Skip to content
Open
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
56 changes: 40 additions & 16 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -10194,13 +10194,13 @@ def test_dash_s_list_parsing(self):
# Simple one-per-line response file format
("[email protected]", ''),
# 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'),
]:
Expand Down Expand Up @@ -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 <emscripten.h>
#include <stdio.h>

__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.h>
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 <emscripten.h>
#include <stdio.h>

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):
Expand Down
26 changes: 20 additions & 6 deletions tools/emscripten.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 '
Expand Down Expand Up @@ -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}"
Expand Down