Skip to content

Commit

Permalink
Use TraceMap's map method to avoid allocating decoded mappings
Browse files Browse the repository at this point in the history
  • Loading branch information
jridgewell committed Jan 29, 2022
1 parent fcc9f44 commit ca1349e
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 83 deletions.
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"prebuild": "rm -rf dist",
"prepublishOnly": "npm run preversion",
"preversion": "run-s test build",
"test": "run-s -n test:lint 'test:only -- --no-cache'",
"test": "run-s -n test:lint test:only",
"test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand",
"test:lint": "run-s -n test:lint:*",
"test:lint:prettier": "prettier --check '{src,test}/**/*.ts'",
Expand All @@ -58,7 +58,7 @@
},
"dependencies": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/trace-mapping": "^0.2.0",
"@jridgewell/trace-mapping": "^0.2.2",
"sourcemap-codec": "1.4.8"
}
}
147 changes: 73 additions & 74 deletions src/source-map-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,88 +28,21 @@ export default class SourceMapTree {
* resolving each mapping in terms of the original source files.
*/
traceMappings(): DecodedSourceMap {
const mappings: SourceMapSegment[][] = [];
const names = new FastStringArray();
const sources = new FastStringArray();
const sourcesContent: (string | null)[] = [];
const { sources: rootSources, map } = this;
const rootNames = map.names;
const rootMappings = map.decodedMappings();

let lastLineWithSegment = -1;
for (let i = 0; i < rootMappings.length; i++) {
const segments = rootMappings[i];
const tracedSegments: SourceMapSegment[] = [];
let lastTraced: SourceMapSegment = [0];

for (let j = 0; j < segments.length; j++) {
const segment = segments[j];

let traced: MappingSource = SOURCELESS_MAPPING;
// 1-length segments only move the current generated column, there's no source information
// to gather from it.
if (segment.length !== 1) {
const source = rootSources[segment[1]];
traced = source.originalPositionFor(
segment[2],
segment[3],
segment.length === 5 ? rootNames[segment[4]] : ''
);

// If the trace returned unefined, then there's no original source containing the mapping.
// It may have returned null, in which case it's a source-less mapping that might need to
// be preserved.
if (traced === INVALID_MAPPING) continue;
}

if (traced === SOURCELESS_MAPPING) {
if (lastTraced.length === 1) {
// This is a consecutive source-less segment, which doesn't carry any new information.
continue;
}
lastTraced = [segment[0]];
} else {
// So we traced a segment down into its original source file. Now push a
// new segment pointing to this location.
const { column, line, name } = traced;
const { content, filename } = traced.source;

// Store the source location, and ensure we keep sourcesContent up to
// date with the sources array.
const sourceIndex = sources.put(filename);
sourcesContent[sourceIndex] = content;

if (
lastTraced.length !== 1 &&
lastTraced[1] === sourceIndex &&
lastTraced[2] === line &&
lastTraced[3] === column
) {
// This is a duplicate mapping pointing at the exact same starting point in the source
// file. It doesn't provide any new information, and only bloats the sourcemap.
continue;
}

// This looks like unnecessary duplication, but it noticeably increases performance. If we
// were to push the nameIndex onto length-4 array, v8 would internally allocate 22 slots!
// That's 68 wasted bytes! Array literals have the same capacity as their length, saving
// memory.
if (name) {
lastTraced = [segment[0], sourceIndex, line, column, names.put(name)];
} else {
lastTraced = [segment[0], sourceIndex, line, column];
}
}
let lastGenLine = -1;
let lastSourcesIndex = -1;
let lastSourceLine = -1;
let lastSourceColumn = -1;

tracedSegments.push(lastTraced);
lastLineWithSegment = i;
}
const mappings = map.map(trace);

mappings.push(tracedSegments);
}

if (mappings.length > lastLineWithSegment + 1) {
mappings.length = lastLineWithSegment + 1;
if (mappings.length > lastGenLine + 1) {
mappings.length = lastGenLine + 1;
}

// TODO: Make all sources relative to the sourceRoot.
Expand All @@ -120,6 +53,72 @@ export default class SourceMapTree {
sources: sources.array,
sourcesContent,
});

function trace(
genLine: number,
genColumn: number,
sourcesIndex: number,
sourceLine: number,
sourceColumn: number,
namesIndex: number
): SourceMapSegment | null {
let traced: MappingSource = SOURCELESS_MAPPING;
// 1-length segments only move the current generated column, there's no source information
// to gather from it.
if (sourcesIndex !== -1) {
const source = rootSources[sourcesIndex];
traced = source.originalPositionFor(
sourceLine,
sourceColumn,
namesIndex === -1 ? '' : rootNames[namesIndex]
);

// If the trace is invalid, then the trace ran into a sourcemap that doesn't contain a
// respective segment into an original source.
if (traced === INVALID_MAPPING) return null;
}

if (traced === SOURCELESS_MAPPING) {
if (lastSourcesIndex === -1) {
// This is a consecutive source-less segment, which doesn't carry any new information.
return null;
}
lastSourcesIndex = lastSourceLine = lastSourceColumn = -1;
return [genColumn];
}

// So we traced a segment down into its original source file. Now push a
// new segment pointing to this location.
const { column, line, name } = traced;
const { content, filename } = traced.source;

// Store the source location, and ensure we keep sourcesContent up to
// date with the sources array.
const sIndex = sources.put(filename);
sourcesContent[sIndex] = content;

if (
lastGenLine === genLine &&
lastSourcesIndex === sIndex &&
lastSourceLine === line &&
lastSourceColumn === column
) {
// This is a duplicate mapping pointing at the exact same starting point in the source
// file. It doesn't carry any new information, and only bloats the sourcemap.
return null;
}
lastGenLine = genLine;
lastSourcesIndex = sIndex;
lastSourceLine = line;
lastSourceColumn = column;

// This looks like unnecessary duplication, but it noticeably increases performance. If we
// were to push the nameIndex onto length-4 array, v8 would internally allocate 22 slots!
// That's 68 wasted bytes! Array literals have the same capacity as their length, saving
// memory.
if (name) return [genColumn, sIndex, line, column, names.put(name)];
return [genColumn, sIndex, line, column];
}
}

/**
Expand Down

0 comments on commit ca1349e

Please sign in to comment.