Skip to content

Commit 5486c4a

Browse files
authored
Fix relative paths in wasm backend source maps. Fixes #9837 (#9882)
1 parent e7ef5b2 commit 5486c4a

File tree

4 files changed

+66
-20
lines changed

4 files changed

+66
-20
lines changed

tests/test_core.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7181,6 +7181,12 @@ def encode_utf8(data):
71817181
else:
71827182
return data
71837183

7184+
def source_map_file_loc(name):
7185+
if shared.Settings.WASM_BACKEND:
7186+
return name
7187+
# in fastcomp, we have the absolute path, which is not good
7188+
return os.path.abspath(name)
7189+
71847190
data = json.load(open(map_filename))
71857191
if str is bytes:
71867192
# Python 2 compatibility
@@ -7190,7 +7196,7 @@ def encode_utf8(data):
71907196
# the output file.
71917197
self.assertPathsIdentical(map_referent, data['file'])
71927198
assert len(data['sources']) == 1, data['sources']
7193-
self.assertPathsIdentical(os.path.abspath('src.cpp'), data['sources'][0])
7199+
self.assertPathsIdentical(source_map_file_loc('src.cpp'), data['sources'][0])
71947200
if hasattr(data, 'sourcesContent'):
71957201
# the sourcesContent attribute is optional, but if it is present it
71967202
# needs to containt valid source text.
@@ -7203,7 +7209,7 @@ def encode_utf8(data):
72037209
mappings = encode_utf8(mappings)
72047210
seen_lines = set()
72057211
for m in mappings:
7206-
self.assertPathsIdentical(os.path.abspath('src.cpp'), m['source'])
7212+
self.assertPathsIdentical(source_map_file_loc('src.cpp'), m['source'])
72077213
seen_lines.add(m['originalLine'])
72087214
# ensure that all the 'meaningful' lines in the original code get mapped
72097215
# when optimizing, the binaryen optimizer may remove some of them (by inlining, etc.)
@@ -8243,7 +8249,7 @@ def test_ubsan_full_static_cast(self, args):
82438249
'g4': ('-g4', [
82448250
"src.cpp:3:12: runtime error: reference binding to null pointer of type 'int'",
82458251
'in main ',
8246-
'/src.cpp:3:8'
8252+
'src.cpp:3:8'
82478253
]),
82488254
})
82498255
@no_fastcomp('ubsan not supported on fastcomp')

tests/test_other.py

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8894,7 +8894,8 @@ def test_wasm_sourcemap(self):
88948894
'--dwarfdump-output',
88958895
path_from_root('tests', 'other', 'wasm_sourcemap', 'foo.wasm.dump'),
88968896
'-o', 'a.out.wasm.map',
8897-
path_from_root('tests', 'other', 'wasm_sourcemap', 'foo.wasm')]
8897+
path_from_root('tests', 'other', 'wasm_sourcemap', 'foo.wasm'),
8898+
'--basepath=' + os.getcwd()]
88988899
run_process(wasm_map_cmd)
88998900
output = open('a.out.wasm.map').read()
89008901
# has "sources" entry with file (includes also `--prefix =wasm-src:///` replacement)
@@ -8909,12 +8910,35 @@ def test_wasm_sourcemap_dead(self):
89098910
'--dwarfdump-output',
89108911
path_from_root('tests', 'other', 'wasm_sourcemap_dead', 't.wasm.dump'),
89118912
'-o', 'a.out.wasm.map',
8912-
path_from_root('tests', 'other', 'wasm_sourcemap_dead', 't.wasm')]
8913+
path_from_root('tests', 'other', 'wasm_sourcemap_dead', 't.wasm'),
8914+
'--basepath=' + os.getcwd()]
89138915
run_process(wasm_map_cmd, stdout=PIPE, stderr=PIPE)
89148916
output = open('a.out.wasm.map').read()
89158917
# has only two entries
89168918
self.assertRegexpMatches(output, r'"mappings":\s*"[A-Za-z0-9+/]+,[A-Za-z0-9+/]+"')
89178919

8920+
@no_fastcomp()
8921+
def test_wasm_sourcemap_relative_paths(self):
8922+
def test(infile, source_map_added_dir=''):
8923+
expected_source_map_path = os.path.join(source_map_added_dir, 'a.cpp')
8924+
print(infile, expected_source_map_path)
8925+
shutil.copyfile(path_from_root('tests', 'hello_123.c'), infile)
8926+
infiles = [
8927+
infile,
8928+
os.path.abspath(infile),
8929+
'./' + infile
8930+
]
8931+
for curr in infiles:
8932+
print(' ', curr)
8933+
run_process([PYTHON, EMCC, curr, '-g4'])
8934+
with open('a.out.wasm.map', 'r') as f:
8935+
self.assertIn('"%s"' % expected_source_map_path, str(f.read()))
8936+
8937+
test('a.cpp')
8938+
8939+
os.mkdir('inner')
8940+
test(os.path.join('inner', 'a.cpp'), 'inner')
8941+
89188942
def test_wasm_producers_section(self):
89198943
# no producers section by default
89208944
run_process([PYTHON, EMCC, path_from_root('tests', 'hello_world.c')])
@@ -9690,17 +9714,17 @@ def test_lsan_leaks(self, ext):
96909714
@parameterized({
96919715
'c': ['c', [
96929716
r'in malloc.*a\.out\.wasm\+0x',
9693-
r'(?im)in f (/|[a-z]:).*/test_lsan_leaks\.c:6:21$',
9694-
r'(?im)in main (/|[a-z]:).*/test_lsan_leaks\.c:10:16$',
9695-
r'(?im)in main (/|[a-z]:).*/test_lsan_leaks\.c:12:3$',
9696-
r'(?im)in main (/|[a-z]:).*/test_lsan_leaks\.c:13:3$',
9717+
r'(?im)in f (|[/a-z\.]:).*/test_lsan_leaks\.c:6:21$',
9718+
r'(?im)in main (|[/a-z\.]:).*/test_lsan_leaks\.c:10:16$',
9719+
r'(?im)in main (|[/a-z\.]:).*/test_lsan_leaks\.c:12:3$',
9720+
r'(?im)in main (|[/a-z\.]:).*/test_lsan_leaks\.c:13:3$',
96979721
]],
96989722
'cpp': ['cpp', [
96999723
r'in operator new\[\]\(unsigned long\).*a\.out\.wasm\+0x',
9700-
r'(?im)in f\(\) (/|[a-z]:).*/test_lsan_leaks\.cpp:4:21$',
9701-
r'(?im)in main (/|[a-z]:).*/test_lsan_leaks\.cpp:8:16$',
9702-
r'(?im)in main (/|[a-z]:).*/test_lsan_leaks\.cpp:10:3$',
9703-
r'(?im)in main (/|[a-z]:).*/test_lsan_leaks\.cpp:11:3$',
9724+
r'(?im)in f\(\) (|[/a-z\.]:).*/test_lsan_leaks\.cpp:4:21$',
9725+
r'(?im)in main (|[/a-z\.]:).*/test_lsan_leaks\.cpp:8:16$',
9726+
r'(?im)in main (|[/a-z\.]:).*/test_lsan_leaks\.cpp:10:3$',
9727+
r'(?im)in main (|[/a-z\.]:).*/test_lsan_leaks\.cpp:11:3$',
97049728
]],
97059729
})
97069730
@no_fastcomp('lsan not supported on fastcomp')

tools/shared.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2871,10 +2871,14 @@ def path_to_system_js_libraries(library_name):
28712871

28722872
@staticmethod
28732873
def emit_wasm_source_map(wasm_file, map_file):
2874+
# source file paths must be relative to the location of the map (which is
2875+
# emitted alongside the wasm)
2876+
base_path = os.path.dirname(os.path.abspath(Settings.WASM_BINARY_FILE))
28742877
sourcemap_cmd = [PYTHON, path_from_root('tools', 'wasm-sourcemap.py'),
28752878
wasm_file,
28762879
'--dwarfdump=' + LLVM_DWARFDUMP,
2877-
'-o', map_file]
2880+
'-o', map_file,
2881+
'--basepath=' + base_path]
28782882
check_call(sourcemap_cmd)
28792883

28802884
@staticmethod

tools/wasm-sourcemap.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"""
1212

1313
import argparse
14-
from collections import OrderedDict, namedtuple
14+
from collections import OrderedDict
1515
import json
1616
import logging
1717
from math import floor, log
@@ -39,6 +39,7 @@ def parse_args():
3939
parser.add_argument('-u', '--source-map-url', nargs='?', help='specifies sourceMappingURL section contest')
4040
parser.add_argument('--dwarfdump', help="path to llvm-dwarfdump executable")
4141
parser.add_argument('--dwarfdump-output', nargs='?', help=argparse.SUPPRESS)
42+
parser.add_argument('--basepath', help='base path for source files, which will be relative to this')
4243
return parser.parse_args()
4344

4445

@@ -73,7 +74,13 @@ def resolve(self, name):
7374
# SourceMapPrefixes contains resolver for file names that are:
7475
# - "sources" is for names that output to source maps JSON
7576
# - "load" is for paths that used to load source text
76-
SourceMapPrefixes = namedtuple('SourceMapPrefixes', 'sources, load')
77+
class SourceMapPrefixes:
78+
def __init__(self, sources, load):
79+
self.sources = sources
80+
self.load = load
81+
82+
def provided(self):
83+
return bool(self.sources.prefixes or self.load.prefixes)
7784

7885

7986
def encode_vlq(n):
@@ -243,11 +250,10 @@ def read_dwarf_entries(wasm, options):
243250
return sorted(entries, key=lambda entry: entry['address'])
244251

245252

246-
def build_sourcemap(entries, code_section_offset, prefixes, collect_sources):
253+
def build_sourcemap(entries, code_section_offset, prefixes, collect_sources, base_path):
247254
sources = []
248255
sources_content = [] if collect_sources else None
249256
mappings = []
250-
251257
sources_map = {}
252258
last_address = 0
253259
last_source_id = 0
@@ -264,7 +270,13 @@ def build_sourcemap(entries, code_section_offset, prefixes, collect_sources):
264270
column = 1
265271
address = entry['address'] + code_section_offset
266272
file_name = entry['file']
267-
source_name = prefixes.sources.resolve(file_name)
273+
# if prefixes were provided, we use that; otherwise, we emit a relative
274+
# path
275+
if prefixes.provided():
276+
source_name = prefixes.sources.resolve(file_name)
277+
else:
278+
file_name = os.path.relpath(os.path.abspath(file_name), base_path)
279+
source_name = file_name
268280
if source_name not in sources_map:
269281
source_id = len(sources)
270282
sources_map[source_name] = source_id
@@ -311,7 +323,7 @@ def main():
311323
prefixes = SourceMapPrefixes(sources=Prefixes(options.prefix), load=Prefixes(options.load_prefix))
312324

313325
logger.debug('Saving to %s' % options.output)
314-
map = build_sourcemap(entries, code_section_offset, prefixes, options.sources)
326+
map = build_sourcemap(entries, code_section_offset, prefixes, options.sources, options.basepath)
315327
with open(options.output, 'w') as outfile:
316328
json.dump(map, outfile, separators=(',', ':'))
317329

0 commit comments

Comments
 (0)