Skip to content

Commit 584bdd8

Browse files
author
bcoe
committed
module: port source map sorting logic from chromium
Digging in to the delta between V8's source map library, and chromium's the most significant difference that jumped out at me was that we were failing to sort generated columns. Since negative offsets are not restricted in the spec, this can lead to bugs. fixes: #31286
1 parent 9c70292 commit 584bdd8

2 files changed

Lines changed: 51 additions & 1 deletion

File tree

lib/internal/source_map/source_map.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ class SourceMap {
212212
let sourceIndex = 0;
213213
let sourceLineNumber = 0;
214214
let sourceColumnNumber = 0;
215+
let hadNegativeColumnOffset = false;
215216

216217
const sources = [];
217218
const originalToCanonicalURLMap = {};
@@ -241,7 +242,13 @@ class SourceMap {
241242
break;
242243
}
243244

244-
columnNumber += decodeVLQ(stringCharIterator);
245+
// A negative columnOffset is valid, and if one is observed sorting is
246+
// necessary, see: https://github.com/mozilla/source-map/pull/92
247+
const columnOffset = decodeVLQ(stringCharIterator);
248+
if (columnOffset < 0) {
249+
hadNegativeColumnOffset = true;
250+
}
251+
columnNumber += columnOffset;
245252
if (isSeparator(stringCharIterator.peek())) {
246253
this.#mappings.push([lineNumber, columnNumber]);
247254
continue;
@@ -261,6 +268,9 @@ class SourceMap {
261268
this.#mappings.push([lineNumber, columnNumber, sourceURL,
262269
sourceLineNumber, sourceColumnNumber]);
263270
}
271+
if (hadNegativeColumnOffset) {
272+
this.#mappings.sort(compareSourceMapEntry);
273+
}
264274
};
265275
}
266276

@@ -321,6 +331,21 @@ function cloneSourceMapV3(payload) {
321331
return payload;
322332
}
323333

334+
/**
335+
* @param {Array} entry1 source map entry [lineNumber, columnNumber, sourceURL,
336+
* sourceLineNumber, sourceColumnNumber]
337+
* @param {Array} entry2 source map entry.
338+
* @return {number}
339+
*/
340+
function compareSourceMapEntry(entry1, entry2) {
341+
const [lineNumber1, columnNumber1] = entry1;
342+
const [lineNumber2, columnNumber2] = entry2;
343+
if (lineNumber1 !== lineNumber2) {
344+
return lineNumber1 - lineNumber2;
345+
}
346+
return columnNumber1 - columnNumber2;
347+
}
348+
324349
module.exports = {
325350
SourceMap
326351
};

test/parallel/test-source-map-api.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,28 @@ const { readFileSync } = require('fs');
124124
assert.strictEqual(originalColumn, knownDecodings[column]);
125125
}
126126
}
127+
128+
// Test that generated columns are sorted when a negative offset is
129+
// observed, see: https://github.com/mozilla/source-map/pull/92
130+
{
131+
function makeMinimalMap(generatedColumns, originalColumns) {
132+
return {
133+
sources: ['test.js'],
134+
// Mapping from the 0th line, 0th column of the output file to the 0th
135+
// source file, 0th line, ${column}th column.
136+
mappings: generatedColumns.map((g, i) => `${g}AA${originalColumns[i]}`)
137+
.join(',')
138+
};
139+
}
140+
// U = 10
141+
// F = -2
142+
// A = 0
143+
// E = 2
144+
const sourceMap = new SourceMap(makeMinimalMap(
145+
['U', 'F', 'F'],
146+
['A', 'E', 'E']
147+
));
148+
assert.strictEqual(sourceMap.findEntry(0, 6).originalColumn, 4);
149+
assert.strictEqual(sourceMap.findEntry(0, 8).originalColumn, 2);
150+
assert.strictEqual(sourceMap.findEntry(0, 10).originalColumn, 0);
151+
}

0 commit comments

Comments
 (0)