Skip to content

Commit d0d970c

Browse files
authored
Switch source map parsing to use the JSON parser (#7467)
This is in preparation for fixing #6805. Aside from cleaning up and reusing more code, it allows optional fields in the source map and random access to those fields, rather than walking through the whole JSON file just once. (It does keep the current behavior of walking through the actual mappings in a single pass synchronized with reading the binary). It also adds some unit tests.
1 parent 8d85f4e commit d0d970c

File tree

6 files changed

+174
-124
lines changed

6 files changed

+174
-124
lines changed

Diff for: src/source-map.h

+8-22
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
#define wasm_source_map_h
1919

2020
#include <optional>
21-
#include <unordered_map>
2221

2322
#include "wasm.h"
2423

@@ -32,7 +31,8 @@ struct MapParseException {
3231
};
3332

3433
class SourceMapReader {
35-
const std::vector<char>& buffer;
34+
std::vector<char>& buffer;
35+
std::string_view mappings;
3636

3737
// Current position in the source map buffer.
3838
size_t pos = 0;
@@ -53,9 +53,9 @@ class SourceMapReader {
5353
bool hasSymbol = false;
5454

5555
public:
56-
SourceMapReader(const std::vector<char>& buffer) : buffer(buffer) {}
56+
SourceMapReader(std::vector<char>& buffer) : buffer(buffer) {}
5757

58-
void readHeader(Module& wasm);
58+
void parse(Module& wasm);
5959

6060
std::optional<Function::DebugLocation>
6161
readDebugLocationAt(size_t currLocation);
@@ -65,10 +65,12 @@ class SourceMapReader {
6565

6666
private:
6767
char peek() {
68-
if (pos >= buffer.size()) {
68+
if (pos == mappings.size()) {
69+
return '"';
70+
} else if (pos > mappings.size()) {
6971
throw MapParseException("unexpected end of source map");
7072
}
71-
return buffer[pos];
73+
return mappings[pos];
7274
}
7375

7476
char get() {
@@ -77,22 +79,6 @@ class SourceMapReader {
7779
return c;
7880
}
7981

80-
bool maybeGet(char c) {
81-
if (pos < buffer.size() && peek() == c) {
82-
++pos;
83-
return true;
84-
}
85-
return false;
86-
}
87-
88-
void expect(char c) {
89-
using namespace std::string_literals;
90-
char got = get();
91-
if (got != c) {
92-
throw MapParseException("expected '"s + c + "', got '" + got + "'");
93-
}
94-
}
95-
9682
int32_t readBase64VLQ();
9783
};
9884

Diff for: src/wasm-binary.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -1464,7 +1464,7 @@ class WasmBinaryReader {
14641464
WasmBinaryReader(Module& wasm,
14651465
FeatureSet features,
14661466
const std::vector<char>& input,
1467-
const std::vector<char>& sourceMap = defaultEmptySourceMap);
1467+
std::vector<char>& sourceMap = defaultEmptySourceMap);
14681468

14691469
void setDebugInfo(bool value) { debugInfo = value; }
14701470
void setDWARF(bool value) { DWARF = value; }

Diff for: src/wasm.h

+5
Original file line numberDiff line numberDiff line change
@@ -2173,6 +2173,11 @@ class Function : public Importable {
21732173
? columnNumber < other.columnNumber
21742174
: symbolNameIndex < other.symbolNameIndex;
21752175
}
2176+
void dump() {
2177+
std::cerr << (symbolNameIndex ? symbolNameIndex.value() : -1) << " @ "
2178+
<< fileIndex << ":" << lineNumber << ":" << columnNumber
2179+
<< "\n";
2180+
}
21762181
};
21772182
// One can explicitly set the debug location of an expression to
21782183
// nullopt to stop the propagation of debug locations.

Diff for: src/wasm/source-map.cpp

+41-86
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "source-map.h"
1818
#include "support/colors.h"
19+
#include "support/json.h"
1920

2021
namespace wasm {
2122

@@ -33,96 +34,55 @@ void MapParseException::dump(std::ostream& o) const {
3334
Colors::normal(o);
3435
}
3536

36-
void SourceMapReader::readHeader(Module& wasm) {
37-
assert(pos == 0);
37+
void SourceMapReader::parse(Module& wasm) {
3838
if (buffer.empty()) {
3939
return;
4040
}
41-
42-
auto skipWhitespace = [&]() {
43-
while (pos < buffer.size() && (buffer[pos] == ' ' || buffer[pos] == '\n')) {
44-
++pos;
41+
json::Value json;
42+
json.parse(buffer.data(), json::Value::ASCII);
43+
if (!json.isObject()) {
44+
throw MapParseException("Source map is not valid JSON");
45+
}
46+
if (!(json.has("version") && json["version"]->isNumber() &&
47+
json["version"]->getInteger() == 3)) {
48+
throw MapParseException("Source map version missing or is not 3");
49+
}
50+
if (!(json.has("sources") && json["sources"]->isArray())) {
51+
throw MapParseException("Source map sources missing or not an array");
52+
}
53+
json::Ref s = json["sources"];
54+
for (size_t i = 0; i < s->size(); i++) {
55+
json::Ref v = s[i];
56+
if (!(s[i]->isString())) {
57+
throw MapParseException("Source map sources contains non-string");
4558
}
46-
};
47-
48-
auto findField = [&](const char* name) {
49-
bool matching = false;
50-
size_t len = strlen(name);
51-
size_t index = 0;
52-
while (1) {
53-
char ch = get();
54-
if (ch == '\"') {
55-
if (matching) {
56-
if (index == len) {
57-
// We matched a terminating quote.
58-
break;
59-
}
60-
matching = false;
61-
} else {
62-
// Beginning of a new potential match.
63-
matching = true;
64-
index = 0;
65-
}
66-
} else if (matching && name[index] == ch) {
67-
++index;
68-
} else if (matching) {
69-
matching = false;
70-
}
59+
wasm.debugInfoFileNames.push_back(v->getCString());
60+
}
61+
62+
if (json.has("names")) {
63+
json::Ref n = json["names"];
64+
if (!n->isArray()) {
65+
throw MapParseException("Source map names is not an array");
7166
}
72-
skipWhitespace();
73-
expect(':');
74-
skipWhitespace();
75-
return true;
76-
};
77-
78-
auto readString = [&](std::string& str) {
79-
std::vector<char> vec;
80-
skipWhitespace();
81-
expect('\"');
82-
while (1) {
83-
if (maybeGet('\"')) {
84-
break;
67+
for (size_t i = 0; i < n->size(); i++) {
68+
json::Ref v = n[i];
69+
if (!v->isString()) {
70+
throw MapParseException("Source map names contains non-string");
8571
}
86-
vec.push_back(get());
72+
wasm.debugInfoSymbolNames.push_back(v->getCString());
8773
}
88-
skipWhitespace();
89-
str = std::string(vec.begin(), vec.end());
90-
};
91-
92-
if (!findField("sources")) {
93-
throw MapParseException("cannot find the 'sources' field in map");
9474
}
9575

96-
skipWhitespace();
97-
expect('[');
98-
if (!maybeGet(']')) {
99-
do {
100-
std::string file;
101-
readString(file);
102-
wasm.debugInfoFileNames.push_back(file);
103-
} while (maybeGet(','));
104-
expect(']');
76+
if (!json.has("mappings")) {
77+
throw MapParseException("Source map mappings missing");
10578
}
106-
107-
if (findField("names")) {
108-
skipWhitespace();
109-
expect('[');
110-
if (!maybeGet(']')) {
111-
do {
112-
std::string symbol;
113-
readString(symbol);
114-
wasm.debugInfoSymbolNames.push_back(symbol);
115-
} while (maybeGet(','));
116-
expect(']');
117-
}
79+
json::Ref m = json["mappings"];
80+
if (!m->isString()) {
81+
throw MapParseException("Source map mappings is not a string");
11882
}
11983

120-
if (!findField("mappings")) {
121-
throw MapParseException("cannot find the 'mappings' field in map");
122-
}
123-
124-
expect('\"');
125-
if (maybeGet('\"')) {
84+
mappings = m->getCString();
85+
if (mappings.empty()) {
12686
// There are no mappings.
12787
location = 0;
12888
return;
@@ -134,10 +94,6 @@ void SourceMapReader::readHeader(Module& wasm) {
13494

13595
std::optional<Function::DebugLocation>
13696
SourceMapReader::readDebugLocationAt(size_t currLocation) {
137-
if (pos >= buffer.size()) {
138-
return std::nullopt;
139-
}
140-
14197
while (location && location <= currLocation) {
14298
do {
14399
char next = peek();
@@ -164,13 +120,13 @@ SourceMapReader::readDebugLocationAt(size_t currLocation) {
164120
} while (false);
165121

166122
// Check whether there is another record to read the position for.
167-
char next = get();
168-
if (next == '\"') {
123+
124+
if (peek() == '\"') {
169125
// End of records.
170126
location = 0;
171127
break;
172128
}
173-
if (next != ',') {
129+
if (get() != ',') {
174130
throw MapParseException("Expected delimiter");
175131
}
176132

@@ -181,7 +137,6 @@ SourceMapReader::readDebugLocationAt(size_t currLocation) {
181137
if (!hasInfo) {
182138
return std::nullopt;
183139
}
184-
185140
auto sym = hasSymbol ? symbol : std::optional<uint32_t>{};
186141
return Function::DebugLocation{file, line, col, sym};
187142
}

Diff for: src/wasm/wasm-binary.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -1774,7 +1774,7 @@ void WasmBinaryWriter::writeMemoryOrder(MemoryOrder order, bool isRMW) {
17741774
WasmBinaryReader::WasmBinaryReader(Module& wasm,
17751775
FeatureSet features,
17761776
const std::vector<char>& input,
1777-
const std::vector<char>& sourceMap)
1777+
std::vector<char>& sourceMap)
17781778
: wasm(wasm), allocator(wasm.allocator), input(input), builder(wasm),
17791779
sourceMapReader(sourceMap) {
17801780
wasm.features = features;
@@ -1824,7 +1824,7 @@ void WasmBinaryReader::read() {
18241824
}
18251825

18261826
readHeader();
1827-
sourceMapReader.readHeader(wasm);
1827+
sourceMapReader.parse(wasm);
18281828

18291829
// Read sections until the end
18301830
while (more()) {

0 commit comments

Comments
 (0)