diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /devtools/client/debugger/src/workers/parser/findOutOfScopeLocations.js | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/client/debugger/src/workers/parser/findOutOfScopeLocations.js')
-rw-r--r-- | devtools/client/debugger/src/workers/parser/findOutOfScopeLocations.js | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/devtools/client/debugger/src/workers/parser/findOutOfScopeLocations.js b/devtools/client/debugger/src/workers/parser/findOutOfScopeLocations.js new file mode 100644 index 0000000000..e277857a51 --- /dev/null +++ b/devtools/client/debugger/src/workers/parser/findOutOfScopeLocations.js @@ -0,0 +1,135 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */ + +// @flow + +import type { AstLocation, AstPosition } from "./types"; +import type { SourceId } from "../../types"; + +import findIndex from "lodash/findIndex"; +import findLastIndex from "lodash/findLastIndex"; + +import { containsLocation, containsPosition } from "./utils/contains"; + +import { getSymbols } from "./getSymbols"; + +function findSymbols(source) { + const { functions, comments } = getSymbols(source); + return { functions, comments }; +} + +/** + * Returns the location for a given function path. If the path represents a + * function declaration, the location will begin after the function identifier + * but before the function parameters. + */ + +function getLocation(func) { + const location = { ...func.location }; + + // if the function has an identifier, start the block after it so the + // identifier is included in the "scope" of its parent + const identifierEnd = func?.identifier?.loc?.end; + if (identifierEnd) { + location.start = identifierEnd; + } + + return location; +} + +/** + * Find the nearest location containing the input position and + * return new locations without inner locations under that nearest location + * + * @param locations Notice! The locations MUST be sorted by `sortByStart` + * so that we can do linear time complexity operation. + */ +function removeInnerLocations(locations: AstLocation[], position: AstPosition) { + // First, let's find the nearest position-enclosing function location, + // which is to find the last location enclosing the position. + const newLocs = locations.slice(); + const parentIndex = findLastIndex(newLocs, loc => + containsPosition(loc, position) + ); + if (parentIndex < 0) { + return newLocs; + } + + // Second, from the nearest location, loop locations again, stop looping + // once seeing the 1st location not enclosed by the nearest location + // to find the last inner locations inside the nearest location. + const innerStartIndex = parentIndex + 1; + const parentLoc = newLocs[parentIndex]; + const outerBoundaryIndex = findIndex( + newLocs, + loc => !containsLocation(parentLoc, loc), + innerStartIndex + ); + const innerBoundaryIndex = + outerBoundaryIndex < 0 ? newLocs.length - 1 : outerBoundaryIndex - 1; + + // Third, remove those inner functions + newLocs.splice(innerStartIndex, innerBoundaryIndex - parentIndex); + return newLocs; +} + +/** + * Return an new locations array which excludes + * items that are completely enclosed by another location in the input locations + * + * @param locations Notice! The locations MUST be sorted by `sortByStart` + * so that we can do linear time complexity operation. + */ +function removeOverlaps(locations: AstLocation[]) { + if (locations.length == 0) { + return []; + } + const firstParent = locations[0]; + return locations.reduce(deduplicateNode, [firstParent]); +} + +function deduplicateNode(nodes, location) { + const parent = nodes[nodes.length - 1]; + if (!containsLocation(parent, location)) { + nodes.push(location); + } + return nodes; +} + +/** + * Sorts an array of locations by start position + */ +function sortByStart(a: AstLocation, b: AstLocation) { + if (a.start.line < b.start.line) { + return -1; + } else if (a.start.line === b.start.line) { + return a.start.column - b.start.column; + } + + return 1; +} + +/** + * Returns an array of locations that are considered out of scope for the given + * location. + */ +function findOutOfScopeLocations( + sourceId: SourceId, + position: AstPosition +): AstLocation[] { + const { functions, comments } = findSymbols(sourceId); + const commentLocations = comments.map(c => c.location); + let locations = functions + .map(getLocation) + .concat(commentLocations) + .sort(sortByStart); + // Must remove inner locations then filter, otherwise, + // we will mis-judge in-scope inner locations as out of scope. + locations = removeInnerLocations(locations, position).filter( + loc => !containsPosition(loc, position) + ); + return removeOverlaps(locations); +} + +export default findOutOfScopeLocations; |