- Consolidated duplicate UndoManagers to single instance - Fixed connection promise to only resolve on 'connected' status - Fixed WebSocketProvider import (WebsocketProvider) - Added proper doc.destroy() cleanup - Renamed isPresenceInitialized property to avoid conflict Co-Authored-By: Paperclip <noreply@paperclip.ing>
373 lines
10 KiB
JavaScript
373 lines
10 KiB
JavaScript
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true,
|
|
});
|
|
exports.ChromeHeapSnapshotProcessor = void 0;
|
|
var _invariant = _interopRequireDefault(require("invariant"));
|
|
function _interopRequireDefault(e) {
|
|
return e && e.__esModule ? e : { default: e };
|
|
}
|
|
const CHILDREN_FIELD_TYPE = "__CHILDREN__";
|
|
class ChromeHeapSnapshotProcessor {
|
|
constructor(snapshotData) {
|
|
this._snapshotData = snapshotData;
|
|
this._globalStringTable = new ChromeHeapSnapshotStringTable(
|
|
this._snapshotData.strings,
|
|
);
|
|
}
|
|
traceFunctionInfos() {
|
|
return new ChromeHeapSnapshotRecordIterator(
|
|
this._snapshotData.trace_function_infos,
|
|
this._snapshotData.snapshot.meta.trace_function_info_fields,
|
|
{
|
|
name: "string",
|
|
script_name: "string",
|
|
},
|
|
this._globalStringTable,
|
|
undefined,
|
|
);
|
|
}
|
|
locations() {
|
|
return new ChromeHeapSnapshotRecordIterator(
|
|
this._snapshotData.locations,
|
|
this._snapshotData.snapshot.meta.location_fields,
|
|
null,
|
|
this._globalStringTable,
|
|
undefined,
|
|
);
|
|
}
|
|
nodes() {
|
|
return new ChromeHeapSnapshotRecordIterator(
|
|
this._snapshotData.nodes,
|
|
this._snapshotData.snapshot.meta.node_fields,
|
|
this._snapshotData.snapshot.meta.node_types,
|
|
this._globalStringTable,
|
|
undefined,
|
|
);
|
|
}
|
|
edges() {
|
|
return new ChromeHeapSnapshotRecordIterator(
|
|
this._snapshotData.edges,
|
|
this._snapshotData.snapshot.meta.edge_fields,
|
|
this._snapshotData.snapshot.meta.edge_types,
|
|
this._globalStringTable,
|
|
undefined,
|
|
);
|
|
}
|
|
traceTree() {
|
|
return new ChromeHeapSnapshotRecordIterator(
|
|
this._snapshotData.trace_tree,
|
|
this._snapshotData.snapshot.meta.trace_node_fields,
|
|
{
|
|
children: CHILDREN_FIELD_TYPE,
|
|
},
|
|
this._globalStringTable,
|
|
undefined,
|
|
);
|
|
}
|
|
}
|
|
exports.ChromeHeapSnapshotProcessor = ChromeHeapSnapshotProcessor;
|
|
class ChromeHeapSnapshotStringTable {
|
|
constructor(strings) {
|
|
this._strings = strings;
|
|
this._indexCache = new Map();
|
|
}
|
|
add(value) {
|
|
this._syncIndexCache();
|
|
let index = this._indexCache.get(value);
|
|
if (index != null) {
|
|
return index;
|
|
}
|
|
index = this._strings.length;
|
|
this._strings.push(value);
|
|
this._indexCache.set(value, index);
|
|
return index;
|
|
}
|
|
get(index) {
|
|
(0, _invariant.default)(
|
|
index >= 0 && index < this._strings.length,
|
|
"index out of string table range",
|
|
);
|
|
return this._strings[index];
|
|
}
|
|
_syncIndexCache() {
|
|
if (this._strings.length > this._indexCache.size) {
|
|
for (let i = this._indexCache.size; i < this._strings.length; ++i) {
|
|
this._indexCache.set(this._strings[i], i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
class ChromeHeapSnapshotRecordAccessor {
|
|
constructor(
|
|
buffer,
|
|
recordFields,
|
|
recordTypes,
|
|
globalStringTable,
|
|
position,
|
|
parent,
|
|
) {
|
|
if (parent) {
|
|
this._recordSize = parent._recordSize;
|
|
this._fieldToOffset = parent._fieldToOffset;
|
|
this._fieldToType = parent._fieldToType;
|
|
} else {
|
|
this._recordSize = recordFields.length;
|
|
this._fieldToOffset = new Map(
|
|
Object.entries(recordFields).map(([offsetStr, name]) => [
|
|
String(name),
|
|
Number(offsetStr),
|
|
]),
|
|
);
|
|
if (Array.isArray(recordTypes)) {
|
|
this._fieldToType = new Map(
|
|
Object.entries(recordTypes).map(([offsetStr, type]) => [
|
|
recordFields[Number(offsetStr)],
|
|
type,
|
|
]),
|
|
);
|
|
} else {
|
|
this._fieldToType = new Map(Object.entries(recordTypes || {}));
|
|
}
|
|
}
|
|
this._buffer = buffer;
|
|
this._position = position;
|
|
(0, _invariant.default)(
|
|
this._position % this._recordSize === 0,
|
|
"Record accessor constructed at invalid offset",
|
|
);
|
|
(0, _invariant.default)(
|
|
this._buffer.length % this._recordSize === 0,
|
|
"Record accessor constructed with wrong size buffer",
|
|
);
|
|
this._globalStringTable = globalStringTable;
|
|
}
|
|
getString(field) {
|
|
const dynamicValue = this._getScalar(field);
|
|
if (typeof dynamicValue === "string") {
|
|
return dynamicValue;
|
|
}
|
|
throw new Error("Not a string or enum field: " + field);
|
|
}
|
|
getNumber(field) {
|
|
const dynamicValue = this._getScalar(field);
|
|
if (typeof dynamicValue === "number") {
|
|
return dynamicValue;
|
|
}
|
|
throw new Error("Not a number field: " + field);
|
|
}
|
|
getChildren(field) {
|
|
const fieldType = this._fieldToType.get(field);
|
|
if (fieldType !== CHILDREN_FIELD_TYPE) {
|
|
throw new Error("Not a children field: " + field);
|
|
}
|
|
const childrenBuffer = this._getRaw(field);
|
|
(0, _invariant.default)(
|
|
Array.isArray(childrenBuffer),
|
|
"Expected array in children-typed field",
|
|
);
|
|
return new ChromeHeapSnapshotRecordIterator(
|
|
childrenBuffer,
|
|
[],
|
|
null,
|
|
this._globalStringTable,
|
|
-this._fieldToOffset.size,
|
|
this,
|
|
);
|
|
}
|
|
setString(field, value) {
|
|
this._setRaw(field, this._encodeString(field, value));
|
|
}
|
|
setNumber(field, value) {
|
|
const fieldType = this._fieldToType.get(field);
|
|
if (
|
|
Array.isArray(fieldType) ||
|
|
fieldType === "string" ||
|
|
fieldType === CHILDREN_FIELD_TYPE
|
|
) {
|
|
throw new Error("Not a number field: " + field);
|
|
}
|
|
this._setRaw(field, value);
|
|
}
|
|
moveToRecord(recordIndex) {
|
|
this._moveToPosition(recordIndex * this._recordSize);
|
|
}
|
|
append(record) {
|
|
const savedPosition = this._position;
|
|
try {
|
|
return this.moveAndInsert(this._buffer.length / this._recordSize, record);
|
|
} finally {
|
|
this._position = savedPosition;
|
|
}
|
|
}
|
|
moveAndInsert(recordIndex, record) {
|
|
this._moveToPosition(recordIndex * this._recordSize, true);
|
|
let didResizeBuffer = false;
|
|
try {
|
|
for (const field of this._fieldToOffset.keys()) {
|
|
if (!Object.prototype.hasOwnProperty.call(record, field)) {
|
|
throw new Error("Missing value for field: " + field);
|
|
}
|
|
}
|
|
this._buffer.splice(this._position, 0, ...new Array(this._recordSize));
|
|
didResizeBuffer = true;
|
|
for (const field of Object.keys(record)) {
|
|
this._set(field, record[field]);
|
|
}
|
|
return this._position / this._recordSize;
|
|
} catch (e) {
|
|
if (didResizeBuffer) {
|
|
this._buffer.splice(this._position, this._recordSize);
|
|
}
|
|
throw e;
|
|
}
|
|
}
|
|
protectedHasNext() {
|
|
if (this._position < 0) {
|
|
return this._buffer.length > 0;
|
|
}
|
|
return this._position < this._buffer.length;
|
|
}
|
|
protectedTryMoveNext() {
|
|
if (this.protectedHasNext()) {
|
|
this._moveToPosition(this._position + this._recordSize, true);
|
|
}
|
|
}
|
|
_getRaw(field) {
|
|
this._validatePosition();
|
|
const offset = this._fieldToOffset.get(field);
|
|
if (offset == null) {
|
|
throw new Error("Unknown field: " + field);
|
|
}
|
|
return this._buffer[this._position + offset];
|
|
}
|
|
_getScalar(field) {
|
|
const rawValue = this._getRaw(field);
|
|
if (Array.isArray(rawValue)) {
|
|
throw new Error("Not a scalar field: " + field);
|
|
}
|
|
const fieldType = this._fieldToType.get(field);
|
|
if (Array.isArray(fieldType)) {
|
|
(0, _invariant.default)(
|
|
rawValue >= 0 && rawValue < fieldType.length,
|
|
"raw value does not match field enum type",
|
|
);
|
|
return fieldType[rawValue];
|
|
}
|
|
if (fieldType === "string") {
|
|
return this._globalStringTable.get(rawValue);
|
|
}
|
|
return rawValue;
|
|
}
|
|
_setRaw(field, rawValue) {
|
|
this._validatePosition();
|
|
const offset = this._fieldToOffset.get(field);
|
|
if (offset == null) {
|
|
throw new Error("Unknown field: " + field);
|
|
}
|
|
this._buffer[this._position + offset] = rawValue;
|
|
}
|
|
_set(field, value) {
|
|
if (typeof value === "string") {
|
|
this.setString(field, value);
|
|
} else if (typeof value === "number") {
|
|
this.setNumber(field, value);
|
|
} else if (Array.isArray(value)) {
|
|
this._setChildren(field, value);
|
|
} else {
|
|
throw new Error("Unsupported value for field: " + field);
|
|
}
|
|
}
|
|
_setChildren(field, value) {
|
|
const fieldType = this._fieldToType.get(field);
|
|
if (fieldType !== CHILDREN_FIELD_TYPE) {
|
|
throw new Error("Not a children field: " + field);
|
|
}
|
|
this._setRaw(field, []);
|
|
const childIt = this.getChildren(field);
|
|
for (const child of value) {
|
|
childIt.append(child);
|
|
}
|
|
}
|
|
_encodeString(field, value) {
|
|
const fieldType = this._fieldToType.get(field);
|
|
if (Array.isArray(fieldType)) {
|
|
const index = fieldType.indexOf(value);
|
|
(0, _invariant.default)(
|
|
index >= 0,
|
|
"Cannot define new values in enum field",
|
|
);
|
|
return index;
|
|
}
|
|
if (fieldType === "string") {
|
|
return this._globalStringTable.add(value);
|
|
}
|
|
throw new Error("Not a string or enum field: " + field);
|
|
}
|
|
_validatePosition(allowEnd = false, position = this._position) {
|
|
if (!Number.isInteger(position)) {
|
|
throw new Error(`Position ${position} is not an integer`);
|
|
}
|
|
if (position % this._recordSize !== 0) {
|
|
throw new Error(
|
|
`Position ${position} is not a multiple of record size ${this._recordSize}`,
|
|
);
|
|
}
|
|
if (position < 0) {
|
|
throw new Error(`Position ${position} is out of range`);
|
|
}
|
|
const maxPosition = allowEnd
|
|
? this._buffer.length
|
|
: this._buffer.length - 1;
|
|
if (position > maxPosition) {
|
|
throw new Error(`Position ${position} is out of range`);
|
|
}
|
|
if (this._buffer.length - position < this._recordSize) {
|
|
if (!(allowEnd && this._buffer.length === position)) {
|
|
throw new Error(
|
|
`Record at position ${position} is truncated: expected ${this._recordSize} fields but found ${this._buffer.length - position}`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
_moveToPosition(nextPosition, allowEnd = false) {
|
|
this._validatePosition(allowEnd, nextPosition);
|
|
this._position = nextPosition;
|
|
}
|
|
}
|
|
class ChromeHeapSnapshotRecordIterator extends ChromeHeapSnapshotRecordAccessor {
|
|
constructor(
|
|
buffer,
|
|
recordFields,
|
|
recordTypes,
|
|
globalStringTable,
|
|
position = -recordFields.length,
|
|
parent,
|
|
) {
|
|
super(
|
|
buffer,
|
|
recordFields,
|
|
recordTypes,
|
|
globalStringTable,
|
|
position,
|
|
parent,
|
|
);
|
|
}
|
|
next() {
|
|
this.protectedTryMoveNext();
|
|
const done = !this.protectedHasNext();
|
|
return done
|
|
? {
|
|
done,
|
|
}
|
|
: {
|
|
done,
|
|
value: this,
|
|
};
|
|
}
|
|
[Symbol.iterator]() {
|
|
return this;
|
|
}
|
|
}
|