Skip to content

Commit 347fc8a

Browse files
authored
Source Maps: Support 5 segment mappings (#6795)
Support 5-segment source mappings, which add a name. Reference: https://github.com/tc39/source-map/blob/main/source-map-rev3.md#proposed-format
1 parent cb53f0c commit 347fc8a

13 files changed

+253
-59
lines changed

src/ir/module-utils.cpp

+57-4
Original file line numberDiff line numberDiff line change
@@ -38,23 +38,44 @@ static void updateLocationSet(std::set<Function::DebugLocation>& locations,
3838
std::swap(locations, updatedLocations);
3939
}
4040

41+
// Update the symbol name indices when moving a set of debug locations from one
42+
// module to another.
43+
static void updateSymbolSet(std::set<Function::DebugLocation>& locations,
44+
std::vector<Index>& symbolIndexMap) {
45+
std::set<Function::DebugLocation> updatedLocations;
46+
47+
for (auto iter : locations) {
48+
if (iter.symbolNameIndex) {
49+
iter.symbolNameIndex = symbolIndexMap[*iter.symbolNameIndex];
50+
}
51+
updatedLocations.insert(iter);
52+
}
53+
locations.clear();
54+
std::swap(locations, updatedLocations);
55+
}
56+
4157
// Copies a function into a module. If newName is provided it is used as the
4258
// name of the function (otherwise the original name is copied). If fileIndexMap
4359
// is specified, it is used to rename source map filename indices when copying
60+
// the function from one module to another one. If symbolNameIndexMap is
61+
// specified, it is used to rename source map symbol name indices when copying
4462
// the function from one module to another one.
4563
Function* copyFunction(Function* func,
4664
Module& out,
4765
Name newName,
48-
std::optional<std::vector<Index>> fileIndexMap) {
49-
auto ret = copyFunctionWithoutAdd(func, out, newName, fileIndexMap);
66+
std::optional<std::vector<Index>> fileIndexMap,
67+
std::optional<std::vector<Index>> symbolNameIndexMap) {
68+
auto ret = copyFunctionWithoutAdd(
69+
func, out, newName, fileIndexMap, symbolNameIndexMap);
5070
return out.addFunction(std::move(ret));
5171
}
5272

5373
std::unique_ptr<Function>
5474
copyFunctionWithoutAdd(Function* func,
5575
Module& out,
5676
Name newName,
57-
std::optional<std::vector<Index>> fileIndexMap) {
77+
std::optional<std::vector<Index>> fileIndexMap,
78+
std::optional<std::vector<Index>> symbolNameIndexMap) {
5879
auto ret = std::make_unique<Function>();
5980
ret->name = newName.is() ? newName : func->name;
6081
ret->hasExplicitName = func->hasExplicitName;
@@ -76,6 +97,18 @@ copyFunctionWithoutAdd(Function* func,
7697
updateLocationSet(ret->prologLocation, *fileIndexMap);
7798
updateLocationSet(ret->epilogLocation, *fileIndexMap);
7899
}
100+
if (symbolNameIndexMap) {
101+
for (auto& iter : ret->debugLocations) {
102+
if (iter.second) {
103+
if (iter.second->symbolNameIndex.has_value()) {
104+
iter.second->symbolNameIndex =
105+
(*symbolNameIndexMap)[*(iter.second->symbolNameIndex)];
106+
}
107+
}
108+
updateSymbolSet(ret->prologLocation, *symbolNameIndexMap);
109+
updateSymbolSet(ret->epilogLocation, *symbolNameIndexMap);
110+
}
111+
}
79112
ret->module = func->module;
80113
ret->base = func->base;
81114
ret->noFullInline = func->noFullInline;
@@ -199,8 +232,27 @@ void copyModuleItems(const Module& in, Module& out) {
199232
}
200233
}
201234

235+
std::optional<std::vector<Index>> symbolNameIndexMap;
236+
if (!in.debugInfoSymbolNames.empty()) {
237+
std::unordered_map<std::string, Index> debugInfoSymbolNameIndices;
238+
for (Index i = 0; i < out.debugInfoSymbolNames.size(); i++) {
239+
debugInfoSymbolNameIndices[out.debugInfoSymbolNames[i]] = i;
240+
}
241+
symbolNameIndexMap.emplace();
242+
for (Index i = 0; i < in.debugInfoSymbolNames.size(); i++) {
243+
std::string file = in.debugInfoSymbolNames[i];
244+
auto iter = debugInfoSymbolNameIndices.find(file);
245+
if (iter == debugInfoSymbolNameIndices.end()) {
246+
Index index = out.debugInfoSymbolNames.size();
247+
out.debugInfoSymbolNames.push_back(file);
248+
debugInfoSymbolNameIndices[file] = index;
249+
}
250+
symbolNameIndexMap->push_back(debugInfoSymbolNameIndices[file]);
251+
}
252+
}
253+
202254
for (auto& curr : in.functions) {
203-
copyFunction(curr.get(), out, Name(), fileIndexMap);
255+
copyFunction(curr.get(), out, Name(), fileIndexMap, symbolNameIndexMap);
204256
}
205257
for (auto& curr : in.globals) {
206258
copyGlobal(curr.get(), out);
@@ -241,6 +293,7 @@ void copyModule(const Module& in, Module& out) {
241293
out.start = in.start;
242294
out.customSections = in.customSections;
243295
out.debugInfoFileNames = in.debugInfoFileNames;
296+
out.debugInfoSymbolNames = in.debugInfoSymbolNames;
244297
out.features = in.features;
245298
}
246299

src/ir/module-utils.h

+12-9
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,24 @@
2525
namespace wasm::ModuleUtils {
2626

2727
// Copies a function into a module. If newName is provided it is used as the
28-
// name of the function (otherwise the original name is copied). If fileIndexMap
29-
// is specified, it is used to rename source map filename indices when copying
30-
// the function from one module to another one.
31-
Function*
32-
copyFunction(Function* func,
33-
Module& out,
34-
Name newName = Name(),
35-
std::optional<std::vector<Index>> fileIndexMap = std::nullopt);
28+
// name of the function (otherwise the original name is copied). When specified,
29+
// fileIndexMap and symbolNameIndexMap are used to rename source map filename
30+
// and symbol name indices when copying the function from one module to another
31+
// one.
32+
Function* copyFunction(
33+
Function* func,
34+
Module& out,
35+
Name newName = Name(),
36+
std::optional<std::vector<Index>> fileIndexMap = std::nullopt,
37+
std::optional<std::vector<Index>> symbolNameIndexMap = std::nullopt);
3638

3739
// As above, but does not add the copy to the module.
3840
std::unique_ptr<Function> copyFunctionWithoutAdd(
3941
Function* func,
4042
Module& out,
4143
Name newName = Name(),
42-
std::optional<std::vector<Index>> fileIndexMap = std::nullopt);
44+
std::optional<std::vector<Index>> fileIndexMap = std::nullopt,
45+
std::optional<std::vector<Index>> symbolNameIndexMap = std::nullopt);
4346

4447
Global* copyGlobal(Global* global, Module& out);
4548

src/parser/contexts.h

+24-3
Original file line numberDiff line numberDiff line change
@@ -1398,6 +1398,7 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
13981398
typeNames;
13991399
const std::unordered_map<Index, Index>& implicitElemIndices;
14001400

1401+
std::unordered_map<std::string_view, Index> debugSymbolNameIndices;
14011402
std::unordered_map<std::string_view, Index> debugFileIndices;
14021403

14031404
// The index of the current module element.
@@ -1777,12 +1778,32 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
17771778
}
17781779
contents = contents.substr(lineSize + 1);
17791780

1780-
lexer = Lexer(contents);
1781+
auto colSize = contents.find(':');
1782+
if (colSize == contents.npos) {
1783+
colSize = contents.size();
1784+
if (colSize == 0) {
1785+
return;
1786+
}
1787+
}
1788+
lexer = Lexer(contents.substr(0, colSize));
17811789
auto col = lexer.takeU32();
1782-
if (!col || !lexer.empty()) {
1790+
if (!col) {
17831791
return;
17841792
}
17851793

1794+
std::optional<BinaryLocation> symbolNameIndex;
1795+
if (colSize != contents.size()) {
1796+
contents = contents.substr(colSize + 1);
1797+
auto symbolName = contents;
1798+
auto [it, inserted] = debugSymbolNameIndices.insert(
1799+
{symbolName, debugSymbolNameIndices.size()});
1800+
if (inserted) {
1801+
assert(wasm.debugInfoSymbolNames.size() == it->second);
1802+
wasm.debugInfoSymbolNames.push_back(std::string(symbolName));
1803+
}
1804+
symbolNameIndex = it->second;
1805+
}
1806+
17861807
// TODO: If we ever parallelize the parse, access to
17871808
// `wasm.debugInfoFileNames` will have to be protected by a lock.
17881809
auto [it, inserted] =
@@ -1792,7 +1813,7 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
17921813
wasm.debugInfoFileNames.push_back(std::string(file));
17931814
}
17941815
irBuilder.setDebugLocation(
1795-
Function::DebugLocation({it->second, *line, *col}));
1816+
Function::DebugLocation({it->second, *line, *col, symbolNameIndex}));
17961817
}
17971818

17981819
Result<> makeBlock(Index pos,

src/passes/Print.cpp

+9-1
Original file line numberDiff line numberDiff line change
@@ -2516,7 +2516,15 @@ void PrintSExpression::printDebugLocation(
25162516
} else {
25172517
auto fileName = currModule->debugInfoFileNames[location->fileIndex];
25182518
o << ";;@ " << fileName << ":" << location->lineNumber << ":"
2519-
<< location->columnNumber << '\n';
2519+
<< location->columnNumber;
2520+
2521+
if (location->symbolNameIndex) {
2522+
auto symbolName =
2523+
currModule->debugInfoSymbolNames[*(location->symbolNameIndex)];
2524+
o << ":" << symbolName;
2525+
}
2526+
2527+
o << '\n';
25202528
}
25212529
doIndent(o, indent);
25222530
}

src/wasm-binary.h

+1
Original file line numberDiff line numberDiff line change
@@ -1674,6 +1674,7 @@ class WasmBinaryReader {
16741674
// Debug information reading helpers
16751675
void setDebugLocations(std::istream* sourceMap_) { sourceMap = sourceMap_; }
16761676
std::unordered_map<std::string, Index> debugInfoFileIndices;
1677+
std::unordered_map<std::string, Index> debugInfoSymbolNameIndices;
16771678
void readNextDebugLocation();
16781679
void readSourceMapHeader();
16791680

src/wasm.h

+11-7
Original file line numberDiff line numberDiff line change
@@ -2073,22 +2073,25 @@ class Function : public Importable {
20732073
std::unordered_map<Index, Name> localNames;
20742074
std::unordered_map<Name, Index> localIndices;
20752075

2076-
// Source maps debugging info: map expression nodes to their file, line, col.
2076+
// Source maps debugging info: map expression nodes to their file, line, col,
2077+
// symbol name.
20772078
struct DebugLocation {
20782079
BinaryLocation fileIndex, lineNumber, columnNumber;
2080+
std::optional<BinaryLocation> symbolNameIndex;
20792081
bool operator==(const DebugLocation& other) const {
20802082
return fileIndex == other.fileIndex && lineNumber == other.lineNumber &&
2081-
columnNumber == other.columnNumber;
2083+
columnNumber == other.columnNumber &&
2084+
symbolNameIndex == other.symbolNameIndex;
20822085
}
20832086
bool operator!=(const DebugLocation& other) const {
20842087
return !(*this == other);
20852088
}
20862089
bool operator<(const DebugLocation& other) const {
2087-
return fileIndex != other.fileIndex
2088-
? fileIndex < other.fileIndex
2089-
: lineNumber != other.lineNumber
2090-
? lineNumber < other.lineNumber
2091-
: columnNumber < other.columnNumber;
2090+
return fileIndex != other.fileIndex ? fileIndex < other.fileIndex
2091+
: lineNumber != other.lineNumber ? lineNumber < other.lineNumber
2092+
: columnNumber != other.columnNumber
2093+
? columnNumber < other.columnNumber
2094+
: symbolNameIndex < other.symbolNameIndex;
20922095
}
20932096
};
20942097
// One can explicitly set the debug location of an expression to
@@ -2300,6 +2303,7 @@ class Module {
23002303

23012304
// Source maps debug info.
23022305
std::vector<std::string> debugInfoFileNames;
2306+
std::vector<std::string> debugInfoSymbolNames;
23032307

23042308
// `features` are the features allowed to be used in this module and should be
23052309
// respected regardless of the value of`hasFeaturesSection`.

src/wasm/wasm-binary.cpp

+61-13
Original file line numberDiff line numberDiff line change
@@ -1189,7 +1189,7 @@ void WasmBinaryWriter::writeSymbolMap() {
11891189
}
11901190

11911191
void WasmBinaryWriter::initializeDebugInfo() {
1192-
lastDebugLocation = {0, /* lineNumber = */ 1, 0};
1192+
lastDebugLocation = {0, /* lineNumber = */ 1, 0, std::nullopt};
11931193
}
11941194

11951195
void WasmBinaryWriter::writeSourceMapProlog() {
@@ -1225,7 +1225,17 @@ void WasmBinaryWriter::writeSourceMapProlog() {
12251225
// TODO respect JSON string encoding, e.g. quotes and control chars.
12261226
*sourceMap << "\"" << wasm->debugInfoFileNames[i] << "\"";
12271227
}
1228-
*sourceMap << "],\"names\":[],\"mappings\":\"";
1228+
*sourceMap << "],\"names\":[";
1229+
1230+
for (size_t i = 0; i < wasm->debugInfoSymbolNames.size(); i++) {
1231+
if (i > 0) {
1232+
*sourceMap << ",";
1233+
}
1234+
// TODO respect JSON string encoding, e.g. quotes and control chars.
1235+
*sourceMap << "\"" << wasm->debugInfoSymbolNames[i] << "\"";
1236+
}
1237+
1238+
*sourceMap << "],\"mappings\":\"";
12291239
}
12301240

12311241
static void writeBase64VLQ(std::ostream& out, int32_t n) {
@@ -1249,21 +1259,31 @@ static void writeBase64VLQ(std::ostream& out, int32_t n) {
12491259
void WasmBinaryWriter::writeSourceMapEpilog() {
12501260
// write source map entries
12511261
size_t lastOffset = 0;
1252-
Function::DebugLocation lastLoc = {0, /* lineNumber = */ 1, 0};
1262+
BinaryLocation lastFileIndex = 0;
1263+
BinaryLocation lastLineNumber = 1;
1264+
BinaryLocation lastColumnNumber = 0;
1265+
BinaryLocation lastSymbolNameIndex = 0;
12531266
for (const auto& [offset, loc] : sourceMapLocations) {
12541267
if (lastOffset > 0) {
12551268
*sourceMap << ",";
12561269
}
12571270
writeBase64VLQ(*sourceMap, int32_t(offset - lastOffset));
12581271
lastOffset = offset;
12591272
if (loc) {
1260-
// There is debug information for this location, so emit the next 3
1261-
// fields and update lastLoc.
1262-
writeBase64VLQ(*sourceMap, int32_t(loc->fileIndex - lastLoc.fileIndex));
1263-
writeBase64VLQ(*sourceMap, int32_t(loc->lineNumber - lastLoc.lineNumber));
1264-
writeBase64VLQ(*sourceMap,
1265-
int32_t(loc->columnNumber - lastLoc.columnNumber));
1266-
lastLoc = *loc;
1273+
writeBase64VLQ(*sourceMap, int32_t(loc->fileIndex - lastFileIndex));
1274+
lastFileIndex = loc->fileIndex;
1275+
1276+
writeBase64VLQ(*sourceMap, int32_t(loc->lineNumber - lastLineNumber));
1277+
lastLineNumber = loc->lineNumber;
1278+
1279+
writeBase64VLQ(*sourceMap, int32_t(loc->columnNumber - lastColumnNumber));
1280+
lastColumnNumber = loc->columnNumber;
1281+
1282+
if (loc->symbolNameIndex) {
1283+
writeBase64VLQ(*sourceMap,
1284+
int32_t(*loc->symbolNameIndex - lastSymbolNameIndex));
1285+
lastSymbolNameIndex = *loc->symbolNameIndex;
1286+
}
12671287
}
12681288
}
12691289
*sourceMap << "\"}";
@@ -1716,7 +1736,7 @@ WasmBinaryReader::WasmBinaryReader(Module& wasm,
17161736
FeatureSet features,
17171737
const std::vector<char>& input)
17181738
: wasm(wasm), allocator(wasm.allocator), input(input), sourceMap(nullptr),
1719-
nextDebugPos(0), nextDebugLocation{0, 0, 0},
1739+
nextDebugPos(0), nextDebugLocation{0, 0, 0, std::nullopt},
17201740
nextDebugLocationHasDebugInfo(false), debugLocation() {
17211741
wasm.features = features;
17221742
}
@@ -2885,6 +2905,21 @@ void WasmBinaryReader::readSourceMapHeader() {
28852905
mustReadChar(']');
28862906
}
28872907

2908+
if (findField("names")) {
2909+
skipWhitespace();
2910+
mustReadChar('[');
2911+
if (!maybeReadChar(']')) {
2912+
do {
2913+
std::string symbol;
2914+
readString(symbol);
2915+
Index index = wasm.debugInfoSymbolNames.size();
2916+
wasm.debugInfoSymbolNames.push_back(symbol);
2917+
debugInfoSymbolNameIndices[symbol] = index;
2918+
} while (maybeReadChar(','));
2919+
mustReadChar(']');
2920+
}
2921+
}
2922+
28882923
if (!findField("mappings")) {
28892924
throw MapParseException("cannot find the 'mappings' field in map");
28902925
}
@@ -2911,7 +2946,12 @@ void WasmBinaryReader::readSourceMapHeader() {
29112946
uint32_t lineNumber =
29122947
readBase64VLQ(*sourceMap) + 1; // adjust zero-based line number
29132948
uint32_t columnNumber = readBase64VLQ(*sourceMap);
2914-
nextDebugLocation = {fileIndex, lineNumber, columnNumber};
2949+
std::optional<BinaryLocation> symbolNameIndex;
2950+
peek = sourceMap->peek();
2951+
if (!(peek == ',' || peek == '\"')) {
2952+
symbolNameIndex = readBase64VLQ(*sourceMap);
2953+
}
2954+
nextDebugLocation = {fileIndex, lineNumber, columnNumber, symbolNameIndex};
29152955
nextDebugLocationHasDebugInfo = true;
29162956
}
29172957
}
@@ -2966,7 +3006,15 @@ void WasmBinaryReader::readNextDebugLocation() {
29663006
int32_t columnNumberDelta = readBase64VLQ(*sourceMap);
29673007
uint32_t columnNumber = nextDebugLocation.columnNumber + columnNumberDelta;
29683008

2969-
nextDebugLocation = {fileIndex, lineNumber, columnNumber};
3009+
std::optional<BinaryLocation> symbolNameIndex;
3010+
peek = sourceMap->peek();
3011+
if (!(peek == ',' || peek == '\"')) {
3012+
int32_t symbolNameIndexDelta = readBase64VLQ(*sourceMap);
3013+
symbolNameIndex =
3014+
nextDebugLocation.symbolNameIndex.value_or(0) + symbolNameIndexDelta;
3015+
}
3016+
3017+
nextDebugLocation = {fileIndex, lineNumber, columnNumber, symbolNameIndex};
29703018
nextDebugLocationHasDebugInfo = true;
29713019
}
29723020
}

src/wasm/wasm.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -1863,6 +1863,9 @@ void Module::updateMaps() {
18631863
assert(tagsMap.size() == tags.size());
18641864
}
18651865

1866-
void Module::clearDebugInfo() { debugInfoFileNames.clear(); }
1866+
void Module::clearDebugInfo() {
1867+
debugInfoFileNames.clear();
1868+
debugInfoSymbolNames.clear();
1869+
}
18671870

18681871
} // namespace wasm

0 commit comments

Comments
 (0)