Skip to content

Commit 432ff16

Browse files
authored
Report missing EXPORTED_FUNCTIONS by default (#7311)
1 parent 6334959 commit 432ff16

File tree

4 files changed

+46
-38
lines changed

4 files changed

+46
-38
lines changed

emscripten.py

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from tools import gen_struct_info
2828
from tools import jsrun, tempfiles
2929
from tools.response_file import substitute_response_files
30-
from tools.shared import WINDOWS, asstr, path_from_root
30+
from tools.shared import WINDOWS, asstr, path_from_root, exit_with_error
3131
from tools.toolchain_profiler import ToolchainProfiler
3232

3333
if __name__ == '__main__':
@@ -618,18 +618,19 @@ def get_all_implemented(forwarded_json, metadata):
618618
# be a response file, in which case, load it
619619
def get_original_exported_functions():
620620
ret = shared.Settings.ORIGINAL_EXPORTED_FUNCTIONS
621-
if ret[0] == '@':
621+
if ret and ret[0] == '@':
622622
ret = json.loads(open(ret[1:]).read())
623623
return ret
624624

625625

626626
def check_all_implemented(all_implemented, pre):
627-
if shared.Settings.ASSERTIONS and shared.Settings.ORIGINAL_EXPORTED_FUNCTIONS:
628-
original_exports = get_original_exported_functions()
629-
for requested in original_exports:
630-
if not is_already_implemented(requested, pre, all_implemented):
631-
# could be a js library func
632-
logging.warning('function requested to be exported, but not implemented: "%s"', requested)
627+
for requested in get_original_exported_functions():
628+
if not is_already_implemented(requested, pre, all_implemented):
629+
# could be a js library func
630+
if shared.Settings.ERROR_ON_UNDEFINED_SYMBOLS:
631+
exit_with_error('undefined exported function: "%s"', requested)
632+
elif shared.Settings.WARN_ON_UNDEFINED_SYMBOLS:
633+
logging.warning('undefined exported function: "%s"', requested)
633634

634635

635636
def is_already_implemented(requested, pre, all_implemented):
@@ -694,8 +695,7 @@ def include_asm_consts(pre, forwarded_json, metadata):
694695
for s in range(len(all_sigs)):
695696
sig = all_sigs[s]
696697
if 'j' in sig:
697-
logging.error('emscript: EM_ASM should not receive i64s as inputs, they are not valid in JS')
698-
sys.exit(1)
698+
exit_with_error('emscript: EM_ASM should not receive i64s as inputs, they are not valid in JS')
699699
call_type = call_types[s] if s < len(call_types) else ''
700700
if '_emscripten_asm_const_' + call_type + sig in forwarded_json['Functions']['libraryFunctions']:
701701
continue # Only one invoker needs to be emitted for each ASM_CONST (signature x call_type) item
@@ -2020,16 +2020,7 @@ def create_exported_implemented_functions_wasm(pre, forwarded_json, metadata):
20202020
if key in all_exported_functions or export_all or (export_bindings and key.startswith('_emscripten_bind')):
20212021
exported_implemented_functions.add(key)
20222022

2023-
if shared.Settings.ASSERTIONS and shared.Settings.ORIGINAL_EXPORTED_FUNCTIONS:
2024-
original_exports = get_original_exported_functions()
2025-
for requested in original_exports:
2026-
# check if already implemented
2027-
# special-case malloc, EXPORTED by default for internal use, but we bake in a trivial allocator and warn at runtime if used in ASSERTIONS \
2028-
if requested not in all_implemented and \
2029-
requested != '_malloc' and \
2030-
(('function ' + asstr(requested)) not in pre): # could be a js library func
2031-
logging.warning('function requested to be exported, but not implemented: "%s"', requested)
2032-
2023+
check_all_implemented(all_implemented, pre)
20332024
return exported_implemented_functions
20342025

20352026

src/settings.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -698,13 +698,15 @@ var STRICT = 0;
698698
// (and don't want to mess with the existing buildsystem), and functions might
699699
// be implemented later on, say in --pre-js, so you may want to build with -s
700700
// WARN_ON_UNDEFINED_SYMBOLS=0 to disable the warnings if they annoy you. See
701-
// also ERROR_ON_UNDEFINED_SYMBOLS
701+
// also ERROR_ON_UNDEFINED_SYMBOLS. Any undefined symbols that are listed in-
702+
// EXPORTED_FUNCTIONS will also be reported.
702703
var WARN_ON_UNDEFINED_SYMBOLS = 1;
703704

704705
// If set to 1, we will give a link-time error on any undefined symbols (see
705-
// WARN_ON_UNDEFINED_SYMBOLS). The default value is 1. To allow undefined symbols
706-
// at link time set this to 0, in which case if an undefined function is called
707-
// a runtime error will occur.
706+
// WARN_ON_UNDEFINED_SYMBOLS). The default value is 1. To allow undefined
707+
// symbols at link time set this to 0, in which case if an undefined function is
708+
// called a runtime error will occur. Any undefined symbols that are listed in
709+
// EXPORTED_FUNCTIONS will also be reported.
708710
var ERROR_ON_UNDEFINED_SYMBOLS = 1;
709711

710712
// If set to 1, any -lfoo directives pointing to nonexisting library files will

tests/test_core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6342,7 +6342,7 @@ def process(filename):
63426342
open(filename, 'w').write(src)
63436343
'''
63446344

6345-
self.set_setting('EXPORTED_FUNCTIONS', self.get_setting('EXPORTED_FUNCTIONS') + ['_get_int', '_get_float', '_get_bool', '_get_string', '_print_int', '_print_float', '_print_bool', '_print_string', '_multi', '_pointer', '_call_ccall_again', '_malloc'])
6345+
self.set_setting('EXPORTED_FUNCTIONS', ['_get_int', '_get_float', '_get_bool', '_get_string', '_print_int', '_print_float', '_print_bool', '_print_string', '_multi', '_pointer', '_call_ccall_again', '_malloc'])
63466346
self.do_run_in_out_file_test('tests', 'core', 'test_ccall', js_transform=post)
63476347

63486348
if '-O2' in self.emcc_args or self.is_emterpreter():

tests/test_other.py

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1584,11 +1584,11 @@ def test_export_in_a(self):
15841584

15851585
# Sanity check: exporting without a definition does not cause it to appear.
15861586
# Note: exporting main prevents emcc from warning that it generated no code.
1587-
run_process([PYTHON, EMCC, 'main.c', '-s', '''EXPORTED_FUNCTIONS=['_main', '%s']''' % full_export_name])
1587+
run_process([PYTHON, EMCC, 'main.c', '-s', 'ERROR_ON_UNDEFINED_SYMBOLS=0', '-s', "EXPORTED_FUNCTIONS=['_main', '%s']" % full_export_name])
15881588
self.assertFalse(self.is_exported_in_wasm(expect_export, 'a.out.wasm'))
15891589

15901590
# Actual test: defining symbol in library and exporting it causes it to appear in the output.
1591-
run_process([PYTHON, EMCC, 'main.c', '-L.', '-lexport', '-s', '''EXPORTED_FUNCTIONS=['%s']''' % full_export_name])
1591+
run_process([PYTHON, EMCC, 'main.c', '-L.', '-lexport', '-s', "EXPORTED_FUNCTIONS=['%s']" % full_export_name])
15921592
self.assertTrue(self.is_exported_in_wasm(expect_export, 'a.out.wasm'))
15931593

15941594
def test_embed_file(self):
@@ -1840,6 +1840,20 @@ def test_link_memcpy(self):
18401840
''', output)
18411841
self.assertNotContained('warning: library.js memcpy should not be running, it is only for testing!', output)
18421842

1843+
def test_undefined_function(self):
1844+
cmd = [PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp')]
1845+
run_process(cmd)
1846+
1847+
# adding a missing symbol to EXPORTED_FUNCTIONS should cause failure
1848+
cmd += ['-s', "EXPORTED_FUNCTIONS=['foobar']"]
1849+
proc = run_process(cmd, stderr=PIPE, check=False)
1850+
self.assertNotEqual(proc.returncode, 0)
1851+
self.assertContained('undefined exported function: "foobar"', proc.stderr)
1852+
1853+
# setting ERROR_ON_UNDEFINED_SYMBOLS=0 suppresses error
1854+
cmd += ['-s', 'ERROR_ON_UNDEFINED_SYMBOLS=0']
1855+
proc = run_process(cmd)
1856+
18431857
def test_undefined_symbols(self):
18441858
with open('main.cpp', 'w') as f:
18451859
f.write(r'''
@@ -3969,9 +3983,9 @@ def test_bad_export(self):
39693983
self.clear()
39703984
cmd = [PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-s', 'EXPORTED_FUNCTIONS=["' + m + '_main"]']
39713985
print(cmd)
3972-
stderr = run_process(cmd, stderr=PIPE).stderr
3986+
stderr = run_process(cmd, stderr=PIPE, check=False).stderr
39733987
if m:
3974-
assert 'function requested to be exported, but not implemented: " _main"' in stderr, stderr
3988+
self.assertContained('undefined exported function: " _main"', stderr)
39753989
else:
39763990
self.assertContained('hello, world!', run_js('a.out.js'))
39773991

@@ -8792,20 +8806,21 @@ def test_emcc_parsing(self):
87928806
# extra newline in response file - should be ignored
87938807
("EXPORTED_FUNCTIONS=@response", ''),
87948808
# stray slash
8795-
("EXPORTED_FUNCTIONS=['_a', '_b', \\'_c', '_d']", '''function requested to be exported, but not implemented: "\\\\'_c'"'''),
8809+
("EXPORTED_FUNCTIONS=['_a', '_b', \\'_c', '_d']", '''undefined exported function: "\\\\'_c'"'''),
87968810
# stray slash
8797-
("EXPORTED_FUNCTIONS=['_a', '_b',\ '_c', '_d']", '''function requested to be exported, but not implemented: "\\\\ '_c'"'''),
8811+
("EXPORTED_FUNCTIONS=['_a', '_b',\ '_c', '_d']", '''undefined exported function: "\\\\ '_c'"'''),
87988812
# stray slash
8799-
('EXPORTED_FUNCTIONS=["_a", "_b", \\"_c", "_d"]', 'function requested to be exported, but not implemented: "\\\\"_c""'),
8813+
('EXPORTED_FUNCTIONS=["_a", "_b", \\"_c", "_d"]', 'undefined exported function: "\\\\"_c""'),
88008814
# stray slash
8801-
('EXPORTED_FUNCTIONS=["_a", "_b",\ "_c", "_d"]', 'function requested to be exported, but not implemented: "\\\\ "_c"'),
8815+
('EXPORTED_FUNCTIONS=["_a", "_b",\ "_c", "_d"]', 'undefined exported function: "\\\\ "_c"'),
88028816
# missing comma
8803-
('EXPORTED_FUNCTIONS=["_a", "_b" "_c", "_d"]', 'function requested to be exported, but not implemented: "_b" "_c"'),
8817+
('EXPORTED_FUNCTIONS=["_a", "_b" "_c", "_d"]', 'undefined exported function: "_b" "_c"'),
88048818
]:
88058819
print(export_arg)
8806-
err = run_process([PYTHON, EMCC, 'src.c', '-s', export_arg], stdout=PIPE, stderr=PIPE).stderr
8807-
print(err)
8820+
proc = run_process([PYTHON, EMCC, 'src.c', '-s', export_arg], stdout=PIPE, stderr=PIPE, check=not expected)
8821+
print(proc.stderr)
88088822
if not expected:
8809-
assert not err
8823+
assert not proc.stderr
88108824
else:
8811-
self.assertContained(expected, err)
8825+
self.assertNotEqual(proc.returncode, 0)
8826+
self.assertContained(expected, proc.stderr)

0 commit comments

Comments
 (0)