Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions lib/quick-sort.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
// sorting in C++. By using our own JS-implemented Quick Sort (below), we get
// a ~3500ms mean speed-up in `bench/bench.html`.

function SortTemplate(comparator) {

/**
* Swap the elements indexed by `x` and `y` in the array `ary`.
*
Expand Down Expand Up @@ -85,7 +87,7 @@ function doQuickSort(ary, comparator, p, r) {
//
// * Every element in `ary[i+1 .. j-1]` is greater than the pivot.
for (var j = p; j < r; j++) {
if (comparator(ary[j], pivot) <= 0) {
if (comparator(ary[j], pivot, false) <= 0) {
i += 1;
swap(ary, i, j);
}
Expand All @@ -101,6 +103,15 @@ function doQuickSort(ary, comparator, p, r) {
}
}

return doQuickSort;
}

function cloneSort(comparator) {
let template = SortTemplate.toString();
let templateFn = new Function(`return ${template}`)();
return templateFn(comparator);
}

/**
* Sort the given array in-place with the given comparator function.
*
Expand All @@ -109,6 +120,13 @@ function doQuickSort(ary, comparator, p, r) {
* @param {function} comparator
* Function to use to compare two items.
*/
exports.quickSort = function (ary, comparator) {
doQuickSort(ary, comparator, 0, ary.length - 1);

let sortCache = new WeakMap();
exports.quickSort = function (ary, comparator, start = 0) {
let doQuickSort = sortCache.get(comparator);
if (doQuickSort === void 0) {
doQuickSort = cloneSort(comparator);
sortCache.set(comparator, doQuickSort);
}
doQuickSort(ary, comparator, start, ary.length - 1);
};
89 changes: 61 additions & 28 deletions lib/source-map-consumer.js
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,36 @@ function Mapping() {
* query (the ordered arrays in the `this.__generatedMappings` and
* `this.__originalMappings` properties).
*/

const compareGenerated = util.compareByGeneratedPositionsDeflatedNoLine;
function sortGenerated(array, start) {
let l = array.length;
let n = array.length - start;
if (n <= 1) {
return;
} else if (n == 2) {
let a = array[start];
let b = array[start + 1];
if (compareGenerated(a, b) > 0) {
array[start] = b;
array[start + 1] = a;
}
} else if (n < 20) {
for (let i = start; i < l; i++) {
for (let j = i; j > start; j--) {
let a = array[j - 1];
let b = array[j];
if (compareGenerated(a, b) <= 0) {
break;
}
array[j - 1] = b;
array[j] = a;
}
}
} else {
quickSort(array, compareGenerated, start);
}
}
BasicSourceMapConsumer.prototype._parseMappings =
function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
var generatedLine = 1;
Expand All @@ -480,11 +510,15 @@ BasicSourceMapConsumer.prototype._parseMappings =
var generatedMappings = [];
var mapping, str, segment, end, value;

let subarrayStart = 0;
while (index < length) {
if (aStr.charAt(index) === ';') {
generatedLine++;
index++;
previousGeneratedColumn = 0;

sortGenerated(generatedMappings, subarrayStart);
subarrayStart = generatedMappings.length;
}
else if (aStr.charAt(index) === ',') {
index++;
Expand All @@ -493,39 +527,27 @@ BasicSourceMapConsumer.prototype._parseMappings =
mapping = new Mapping();
mapping.generatedLine = generatedLine;

// Because each offset is encoded relative to the previous one,
// many segments often have the same encoding. We can exploit this
// fact by caching the parsed variable length fields of each segment,
// allowing us to avoid a second parse if we encounter the same
// segment again.
for (end = index; end < length; end++) {
if (this._charIsMappingSeparator(aStr, end)) {
break;
}
}
str = aStr.slice(index, end);

segment = cachedSegments[str];
if (segment) {
index += str.length;
} else {
segment = [];
while (index < end) {
base64VLQ.decode(aStr, index, temp);
value = temp.value;
index = temp.rest;
segment.push(value);
}

if (segment.length === 2) {
throw new Error('Found a source, but no line and column');
}
segment = [];
while (index < end) {
base64VLQ.decode(aStr, index, temp);
value = temp.value;
index = temp.rest;
segment.push(value);
}

if (segment.length === 3) {
throw new Error('Found a source and line, but no column');
}
if (segment.length === 2) {
throw new Error('Found a source, but no line and column');
}

cachedSegments[str] = segment;
if (segment.length === 3) {
throw new Error('Found a source and line, but no column');
}

// Generated column.
Expand Down Expand Up @@ -556,16 +578,27 @@ BasicSourceMapConsumer.prototype._parseMappings =

generatedMappings.push(mapping);
if (typeof mapping.originalLine === 'number') {
originalMappings.push(mapping);
let currentSource = mapping.source;
while (originalMappings.length <= currentSource) {
originalMappings.push(null);
}
if (originalMappings[currentSource] === null) {
originalMappings[currentSource] = [];
}
originalMappings[currentSource].push(mapping);
}
}
}

quickSort(generatedMappings, util.compareByGeneratedPositionsDeflated);
sortGenerated(generatedMappings, subarrayStart);
this.__generatedMappings = generatedMappings;

quickSort(originalMappings, util.compareByOriginalPositions);
this.__originalMappings = originalMappings;
for (var i = 0; i < originalMappings.length; i++) {
if (originalMappings[i] != null) {
quickSort(originalMappings[i], util.compareByOriginalPositionsNoSource);
}
}
this.__originalMappings = [].concat(...originalMappings);
};

/**
Expand Down
52 changes: 52 additions & 0 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,33 @@ function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
}
exports.compareByOriginalPositions = compareByOriginalPositions;

function compareByOriginalPositionsNoSource(mappingA, mappingB, onlyCompareOriginal) {
var cmp

cmp = mappingA.originalLine - mappingB.originalLine;
if (cmp !== 0) {
return cmp;
}

cmp = mappingA.originalColumn - mappingB.originalColumn;
if (cmp !== 0 || onlyCompareOriginal) {
return cmp;
}

cmp = mappingA.generatedColumn - mappingB.generatedColumn;
if (cmp !== 0) {
return cmp;
}

cmp = mappingA.generatedLine - mappingB.generatedLine;
if (cmp !== 0) {
return cmp;
}

return strcmp(mappingA.name, mappingB.name);
}
exports.compareByOriginalPositionsNoSource = compareByOriginalPositionsNoSource;

/**
* Comparator between two mappings with deflated source and name indices where
* the generated positions are compared.
Expand Down Expand Up @@ -424,6 +451,31 @@ function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGene
}
exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;

function compareByGeneratedPositionsDeflatedNoLine(mappingA, mappingB, onlyCompareGenerated) {
var cmp = mappingA.generatedColumn - mappingB.generatedColumn;
if (cmp !== 0 || onlyCompareGenerated) {
return cmp;
}

cmp = strcmp(mappingA.source, mappingB.source);
if (cmp !== 0) {
return cmp;
}

cmp = mappingA.originalLine - mappingB.originalLine;
if (cmp !== 0) {
return cmp;
}

cmp = mappingA.originalColumn - mappingB.originalColumn;
if (cmp !== 0) {
return cmp;
}

return strcmp(mappingA.name, mappingB.name);
}
exports.compareByGeneratedPositionsDeflatedNoLine = compareByGeneratedPositionsDeflatedNoLine;

function strcmp(aStr1, aStr2) {
if (aStr1 === aStr2) {
return 0;
Expand Down