Skip to content
Merged
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
12 changes: 1 addition & 11 deletions src/browser/cdp/selectivity/js-selectivity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,20 +251,10 @@ export class JSSelectivity {
throw new Error(`JS Selectivity: fs-cache is broken for ${sourceUrl}`);
}

const startOffsetsSet = new Set<number>();

grouppedByScriptCoverage[scriptId].forEach(entry => {
entry.functions.forEach(fn => {
fn.ranges.forEach(range => {
startOffsetsSet.add(range.startOffset);
});
});
});

const dependingSourceFiles = extractSourceFilesDeps(
sourceString,
sourceMapsString,
Array.from(startOffsetsSet),
grouppedByScriptCoverage[scriptId],
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now passing full coverage, with ranges
Ranges are necessary to perform source map function bounds checking

this._sourceRoot,
);

Expand Down
74 changes: 58 additions & 16 deletions src/browser/cdp/selectivity/utils.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { sortedIndex, memoize } from "lodash";
import { SourceMapConsumer, type RawSourceMap } from "source-map-js";
import { SourceFindPosition, SourceMapConsumer, type RawSourceMap } from "source-map-js";
import fs from "fs";
import path from "path";
import { URL } from "url";
import * as logger from "../../../utils/logger";
import type { CDPRuntime } from "../domains/runtime";
import type { CDPSessionId } from "../types";
import type { CDPScriptCoverage, CDPSessionId } from "../types";
import { softFileURLToPath } from "../../../utils/fs";
import type { CachedOnFs, HashFileContents, NormalizedDependencies, SelectivityCompressionType } from "./types";
import { WEBPACK_PROTOCOL } from "./constants";
Expand Down Expand Up @@ -68,19 +68,33 @@ export const patchSourceMapSources = (sourceMap: RawSourceMap, sourceRoot?: stri
return sourceMap;
};

/**
* With cdp coverage's offset style
* @returns sourcemap's 0-based line number
*/
const offsetToSourceMapLineNumber = (offsetToLine: number[], offset: number): number => {
let lineNumber = sortedIndex(offsetToLine, offset);

if (offset < offsetToLine[lineNumber]) {
lineNumber--;
}

return lineNumber;
};

/**
* Given compiled code, its source map, and the executed offsets
* It returns the original source files touched
* Useful for turning coverage ranges into real TS/JS module dependencies
* @param source Compiled source code
* @param sourceMaps Source maps JSON string
* @param startOffsets Executed start offsets (v8 format)
* @param coverages CDP script coverages
* @param sourceRoot Source root
*/
export const extractSourceFilesDeps = (
source: string,
sourceMaps: string,
startOffsets: number[],
coverages: CDPScriptCoverage[],
sourceRoot: string,
): Set<string> => {
const dependantSourceFiles = new Set<string>();
Expand All @@ -96,18 +110,46 @@ export const extractSourceFilesDeps = (
sourceOffset = source.indexOf("\n", sourceOffset);
}

for (const startOffset of startOffsets) {
let line = sortedIndex(offsetToLine, startOffset);

if (startOffset < offsetToLine[line]) {
line--;
}

const column = startOffset - offsetToLine[line];
const position = consumer.originalPositionFor({ line: line + 1, column });

if (position.source) {
dependantSourceFiles.add(position.source);
for (const scriptCoverage of coverages) {
for (const functionCall of scriptCoverage.functions) {
for (const range of functionCall.ranges) {
const startLine = offsetToSourceMapLineNumber(offsetToLine, range.startOffset);

const column = range.startOffset - offsetToLine[startLine];
const previousPosition = consumer.originalPositionFor({
line: startLine + 1,
column,
bias: SourceMapConsumer.GREATEST_LOWER_BOUND,
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With "GREATEST_LOWER_BOUND" bias (which is default) we might get position, before the actual function
So after calling "generatedPositionFor" we check generatedPosition.line >= startLine + 1 which ensures "source map is in function boundaries"

});

if (previousPosition.source) {
(previousPosition as SourceFindPosition).bias = SourceMapConsumer.GREATEST_LOWER_BOUND;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reusing "previousPosition" object

const generatedPosition = consumer.generatedPositionFor(previousPosition);

if (typeof generatedPosition.line === "number" && generatedPosition.line >= startLine + 1) {
dependantSourceFiles.add(previousPosition.source);
break;
}
}

// Bundler source file function wrapper case
const nextPosition = consumer.originalPositionFor({
line: startLine + 1,
column,
bias: SourceMapConsumer.LEAST_UPPER_BOUND,
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When processing bundler wrapper, we dont get valid source position with "GREATEST_LOWER_BOUND"
So we use "LEAST_UPPER_BOUND", which is looking for "next mapping" instead of "previous mapping"
generatedPosition.line <= endLine + 1 check insures we did not jump to another function

});

if (nextPosition.source) {
(nextPosition as SourceFindPosition).bias = SourceMapConsumer.LEAST_UPPER_BOUND;
const generatedPosition = consumer.generatedPositionFor(nextPosition);
const endLine = offsetToSourceMapLineNumber(offsetToLine, range.endOffset);

if (typeof generatedPosition.line === "number" && generatedPosition.line <= endLine + 1) {
dependantSourceFiles.add(nextPosition.source);
break;
}
}
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion test/src/browser/cdp/selectivity/js-selectivity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ describe("CDP/Selectivity/JSSelectivity", () => {
extractSourceFilesDepsStub,
"mock source\n//# sourceMappingURL=app.js.map",
"mock source map",
[0, 100],
mockCoverage.result,
sourceRoot,
);

Expand Down
Loading
Loading