commit d60c7c078bfee64add1dd4b025632db20eb0f477
parent a0b750c5fc0df2f7ab481542365d24cc42645465
Author: Lorenz A <me@lorenzackermann.xyz>
Date: Tue, 16 Dec 2025 13:52:42 +0000
Bug 2004238 - [devtools] Turn devtools/shared/heapsnapshot/CensusUtils.js into an ES class. r=devtools-reviewers,nchevobbe
Differential Revision: https://phabricator.services.mozilla.com/D276560
Diffstat:
1 file changed, 203 insertions(+), 206 deletions(-)
diff --git a/devtools/shared/heapsnapshot/CensusUtils.js b/devtools/shared/heapsnapshot/CensusUtils.js
@@ -13,55 +13,53 @@ const {
* A Visitor visits each node and edge of a census report tree as the census
* report is being traversed by `walk`.
*/
-function Visitor() {}
+class Visitor {
+ /**
+ * The `enter` method is called when a new sub-report is entered in traversal.
+ *
+ * @param {object} breakdown
+ * The breakdown for the sub-report that is being entered by traversal.
+ *
+ * @param {object} report
+ * The report generated by the given breakdown.
+ *
+ * @param {any} edge
+ * The edge leading to this sub-report. The edge is null if (but not iff!
+ * eg, null allocation stack edges) we are entering the root report.
+ */
+ enter() {}
+ /**
+ * The `exit` method is called when traversal of a sub-report has finished.
+ *
+ * @param {object} breakdown
+ * The breakdown for the sub-report whose traversal has finished.
+ *
+ * @param {object} report
+ * The report generated by the given breakdown.
+ *
+ * @param {any} edge
+ * The edge leading to this sub-report. The edge is null if (but not iff!
+ * eg, null allocation stack edges) we are entering the root report.
+ */
+ exit() {}
+ /**
+ * The `count` method is called when leaf nodes (reports whose breakdown is
+ * by: "count") in the report tree are encountered.
+ *
+ * @param {object} breakdown
+ * The count breakdown for this report.
+ *
+ * @param {object} report
+ * The report generated by a breakdown by "count".
+ *
+ * @param {any|null} edge
+ * The edge leading to this count report. The edge is null if we are
+ * entering the root report.
+ */
+ count() {}
+}
exports.Visitor = Visitor;
-/**
- * The `enter` method is called when a new sub-report is entered in traversal.
- *
- * @param {object} breakdown
- * The breakdown for the sub-report that is being entered by traversal.
- *
- * @param {object} report
- * The report generated by the given breakdown.
- *
- * @param {any} edge
- * The edge leading to this sub-report. The edge is null if (but not iff!
- * eg, null allocation stack edges) we are entering the root report.
- */
-Visitor.prototype.enter = function () {};
-
-/**
- * The `exit` method is called when traversal of a sub-report has finished.
- *
- * @param {object} breakdown
- * The breakdown for the sub-report whose traversal has finished.
- *
- * @param {object} report
- * The report generated by the given breakdown.
- *
- * @param {any} edge
- * The edge leading to this sub-report. The edge is null if (but not iff!
- * eg, null allocation stack edges) we are entering the root report.
- */
-Visitor.prototype.exit = function () {};
-
-/**
- * The `count` method is called when leaf nodes (reports whose breakdown is
- * by: "count") in the report tree are encountered.
- *
- * @param {object} breakdown
- * The count breakdown for this report.
- *
- * @param {object} report
- * The report generated by a breakdown by "count".
- *
- * @param {any|null} edge
- * The edge leading to this count report. The edge is null if we are
- * entering the root report.
- */
-Visitor.prototype.count = function () {};
-
/*** getReportEdges *********************************************************/
const EDGES = Object.create(null);
@@ -193,165 +191,164 @@ function isMap(obj) {
return Object.prototype.toString.call(obj) === "[object Map]";
}
+const basisTotalBytes = (exports.basisTotalBytes = Symbol("basisTotalBytes"));
+const basisTotalCount = (exports.basisTotalCount = Symbol("basisTotalCount"));
+
/**
* A Visitor for computing the difference between the census report being
* traversed and the given other census.
- *
- * @param {object} otherCensus
- * The other census report.
*/
-function DiffVisitor(otherCensus) {
- // The other census we are comparing against.
- this._otherCensus = otherCensus;
-
- // The total bytes and count of the basis census we are traversing.
- this._totalBytes = 0;
- this._totalCount = 0;
-
- // Stack maintaining the current corresponding sub-report for the other
- // census we are comparing against.
- this._otherCensusStack = [];
-
- // Stack maintaining the set of edges visited at each sub-report.
- this._edgesVisited = [new Set()];
-
- // The final delta census. Valid only after traversal.
- this._results = null;
-
- // Stack maintaining the results corresponding to each sub-report we are
- // currently traversing.
- this._resultsStack = [];
-}
-
-DiffVisitor.prototype = Object.create(Visitor.prototype);
-
-/**
- * Given a report and an outgoing edge, get the edge's referent.
- */
-DiffVisitor.prototype._get = function (report, edge) {
- if (!report) {
- return undefined;
+class DiffVisitor extends Visitor {
+ /**
+ * @param {object} otherCensus
+ * The other census report.
+ */
+ constructor(otherCensus) {
+ super();
+
+ // The other census we are comparing against.
+ this._otherCensus = otherCensus;
+
+ // The total bytes and count of the basis census we are traversing.
+ this._totalBytes = 0;
+ this._totalCount = 0;
+
+ // Stack maintaining the current corresponding sub-report for the other
+ // census we are comparing against.
+ this._otherCensusStack = [];
+
+ // Stack maintaining the set of edges visited at each sub-report.
+ this._edgesVisited = [new Set()];
+
+ // The final delta census. Valid only after traversal.
+ this._results = null;
+
+ // Stack maintaining the results corresponding to each sub-report we are
+ // currently traversing.
+ this._resultsStack = [];
}
- return isMap(report) ? report.get(edge) : report[edge];
-};
-
-/**
- * Given a report, an outgoing edge, and a value, set the edge's referent to
- * the given value.
- */
-DiffVisitor.prototype._set = function (report, edge, val) {
- if (isMap(report)) {
- report.set(edge, val);
- } else {
- report[edge] = val;
+ /**
+ * Given a report and an outgoing edge, get the edge's referent.
+ */
+ _get(report, edge) {
+ if (!report) {
+ return undefined;
+ }
+ return isMap(report) ? report.get(edge) : report[edge];
}
-};
-
-/**
- * @overrides Visitor.prototype.enter
- */
-DiffVisitor.prototype.enter = function (breakdown, report, edge) {
- const newResults = breakdown.by === "allocationStack" ? new Map() : {};
- let newOther;
-
- if (!this._results) {
- // This is the first time we have entered a sub-report.
- this._results = newResults;
- newOther = this._otherCensus;
- } else {
- const topResults = this._resultsStack[this._resultsStack.length - 1];
- this._set(topResults, edge, newResults);
-
- const topOther = this._otherCensusStack[this._otherCensusStack.length - 1];
- newOther = this._get(topOther, edge);
+ /**
+ * Given a report, an outgoing edge, and a value, set the edge's referent to
+ * the given value.
+ */
+ _set(report, edge, val) {
+ if (isMap(report)) {
+ report.set(edge, val);
+ } else {
+ report[edge] = val;
+ }
}
+ /**
+ * @override
+ */
+ enter(breakdown, report, edge) {
+ const newResults = breakdown.by === "allocationStack" ? new Map() : {};
+ let newOther;
+
+ if (!this._results) {
+ // This is the first time we have entered a sub-report.
+ this._results = newResults;
+ newOther = this._otherCensus;
+ } else {
+ const topResults = this._resultsStack[this._resultsStack.length - 1];
+ this._set(topResults, edge, newResults);
+
+ const topOther =
+ this._otherCensusStack[this._otherCensusStack.length - 1];
+ newOther = this._get(topOther, edge);
+ }
- this._resultsStack.push(newResults);
- this._otherCensusStack.push(newOther);
+ this._resultsStack.push(newResults);
+ this._otherCensusStack.push(newOther);
- const visited = this._edgesVisited[this._edgesVisited.length - 1];
- visited.add(edge);
- this._edgesVisited.push(new Set());
-};
-
-/**
- * @overrides Visitor.prototype.exit
- */
-DiffVisitor.prototype.exit = function (breakdown) {
- // Find all the edges in the other census report that were not traversed and
- // add them to the results directly.
- const other = this._otherCensusStack[this._otherCensusStack.length - 1];
- if (other) {
const visited = this._edgesVisited[this._edgesVisited.length - 1];
- const unvisited = getReportEdges(breakdown, other)
- .map(e => e.edge)
- .filter(e => !visited.has(e));
- const results = this._resultsStack[this._resultsStack.length - 1];
- for (const edg of unvisited) {
- this._set(results, edg, this._get(other, edg));
- }
+ visited.add(edge);
+ this._edgesVisited.push(new Set());
}
+ /**
+ * @override
+ */
+ exit(breakdown) {
+ // Find all the edges in the other census report that were not traversed and
+ // add them to the results directly.
+ const other = this._otherCensusStack[this._otherCensusStack.length - 1];
+ if (other) {
+ const visited = this._edgesVisited[this._edgesVisited.length - 1];
+ const unvisited = getReportEdges(breakdown, other)
+ .map(e => e.edge)
+ .filter(e => !visited.has(e));
+ const results = this._resultsStack[this._resultsStack.length - 1];
+ for (const edg of unvisited) {
+ this._set(results, edg, this._get(other, edg));
+ }
+ }
- this._otherCensusStack.pop();
- this._resultsStack.pop();
- this._edgesVisited.pop();
-};
-
-/**
- * @overrides Visitor.prototype.count
- */
-DiffVisitor.prototype.count = function (breakdown, report) {
- const other = this._otherCensusStack[this._otherCensusStack.length - 1];
- const results = this._resultsStack[this._resultsStack.length - 1];
-
- if (breakdown.count) {
- this._totalCount += report.count;
- }
- if (breakdown.bytes) {
- this._totalBytes += report.bytes;
+ this._otherCensusStack.pop();
+ this._resultsStack.pop();
+ this._edgesVisited.pop();
}
+ /**
+ * @override
+ */
+ count(breakdown, report) {
+ const other = this._otherCensusStack[this._otherCensusStack.length - 1];
+ const results = this._resultsStack[this._resultsStack.length - 1];
- if (other) {
if (breakdown.count) {
- results.count = other.count - report.count;
+ this._totalCount += report.count;
}
if (breakdown.bytes) {
- results.bytes = other.bytes - report.bytes;
- }
- } else {
- if (breakdown.count) {
- results.count = -report.count;
+ this._totalBytes += report.bytes;
}
- if (breakdown.bytes) {
- results.bytes = -report.bytes;
+
+ if (other) {
+ if (breakdown.count) {
+ results.count = other.count - report.count;
+ }
+ if (breakdown.bytes) {
+ results.bytes = other.bytes - report.bytes;
+ }
+ } else {
+ if (breakdown.count) {
+ results.count = -report.count;
+ }
+ if (breakdown.bytes) {
+ results.bytes = -report.bytes;
+ }
}
}
-};
-
-const basisTotalBytes = (exports.basisTotalBytes = Symbol("basisTotalBytes"));
-const basisTotalCount = (exports.basisTotalCount = Symbol("basisTotalCount"));
-/**
- * Get the resulting report of the difference between the traversed census
- * report and the other census report.
- *
- * @returns {object}
- * The delta census report.
- */
-DiffVisitor.prototype.results = function () {
- if (!this._results) {
- throw new Error("Attempt to get results before computing diff!");
- }
+ /**
+ * Get the resulting report of the difference between the traversed census
+ * report and the other census report.
+ *
+ * @returns {object}
+ * The delta census report.
+ */
+ results() {
+ if (!this._results) {
+ throw new Error("Attempt to get results before computing diff!");
+ }
- if (this._resultsStack.length) {
- throw new Error("Attempt to get results while still computing diff!");
- }
+ if (this._resultsStack.length) {
+ throw new Error("Attempt to get results while still computing diff!");
+ }
- this._results[basisTotalBytes] = this._totalBytes;
- this._results[basisTotalCount] = this._totalCount;
+ this._results[basisTotalBytes] = this._totalBytes;
+ this._results[basisTotalCount] = this._totalCount;
- return this._results;
-};
+ return this._results;
+ }
+}
/**
* Take the difference between two censuses. The resulting delta report
@@ -436,33 +433,33 @@ exports.countToBucketBreakdown = function (breakdown) {
/**
* A Visitor for finding report leaves by their DFS index.
*/
-function GetLeavesVisitor(targetIndices) {
- this._index = -1;
- this._targetIndices = targetIndices;
- this._leaves = [];
-}
+class GetLeavesVisitor extends Visitor {
+ constructor(targetIndices) {
+ super();
-GetLeavesVisitor.prototype = Object.create(Visitor.prototype);
-
-/**
- * @overrides Visitor.prototype.enter
- */
-GetLeavesVisitor.prototype.enter = function (breakdown, report) {
- this._index++;
- if (this._targetIndices.has(this._index)) {
- this._leaves.push(report);
+ this._index = -1;
+ this._targetIndices = targetIndices;
+ this._leaves = [];
}
-};
-
-/**
- * Get the accumulated report leaves after traversal.
- */
-GetLeavesVisitor.prototype.leaves = function () {
- if (this._index === -1) {
- throw new Error("Attempt to call `leaves` before traversing report!");
+ /**
+ * @override
+ */
+ enter(breakdown, report) {
+ this._index++;
+ if (this._targetIndices.has(this._index)) {
+ this._leaves.push(report);
+ }
}
- return this._leaves;
-};
+ /**
+ * Get the accumulated report leaves after traversal.
+ */
+ leaves() {
+ if (this._index === -1) {
+ throw new Error("Attempt to call `leaves` before traversing report!");
+ }
+ return this._leaves;
+ }
+}
/**
* Given a set of indices of leaves in a pre-order depth-first traversal of the