@memlab/core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/dist/__tests__/parser/HeapParser.test.d.ts +11 -0
- package/dist/__tests__/parser/HeapParser.test.d.ts.map +1 -0
- package/dist/__tests__/parser/HeapParser.test.js +54 -0
- package/dist/__tests__/parser/NodeHeap.test.d.ts +11 -0
- package/dist/__tests__/parser/NodeHeap.test.d.ts.map +1 -0
- package/dist/__tests__/parser/NodeHeap.test.js +96 -0
- package/dist/__tests__/parser/StringNode.test.d.ts +11 -0
- package/dist/__tests__/parser/StringNode.test.d.ts.map +1 -0
- package/dist/__tests__/parser/StringNode.test.js +61 -0
- package/dist/__tests__/parser/traverse/HeapNodeTraverse.test.d.ts +16 -0
- package/dist/__tests__/parser/traverse/HeapNodeTraverse.test.d.ts.map +1 -0
- package/dist/__tests__/parser/traverse/HeapNodeTraverse.test.js +140 -0
- package/dist/__tests__/utils/utils.test.d.ts +11 -0
- package/dist/__tests__/utils/utils.test.d.ts.map +1 -0
- package/dist/__tests__/utils/utils.test.js +81 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +62 -0
- package/dist/lib/BaseOption.d.ts +31 -0
- package/dist/lib/BaseOption.d.ts.map +1 -0
- package/dist/lib/BaseOption.js +109 -0
- package/dist/lib/BrowserInfo.d.ts +33 -0
- package/dist/lib/BrowserInfo.d.ts.map +1 -0
- package/dist/lib/BrowserInfo.js +117 -0
- package/dist/lib/Config.d.ts +203 -0
- package/dist/lib/Config.d.ts.map +1 -0
- package/dist/lib/Config.js +427 -0
- package/dist/lib/Console.d.ts +67 -0
- package/dist/lib/Console.d.ts.map +1 -0
- package/dist/lib/Console.js +344 -0
- package/dist/lib/Constant.d.ts +38 -0
- package/dist/lib/Constant.d.ts.map +1 -0
- package/dist/lib/Constant.js +58 -0
- package/dist/lib/FileManager.d.ts +69 -0
- package/dist/lib/FileManager.d.ts.map +1 -0
- package/dist/lib/FileManager.js +309 -0
- package/dist/lib/HeapAnalyzer.d.ts +51 -0
- package/dist/lib/HeapAnalyzer.d.ts.map +1 -0
- package/dist/lib/HeapAnalyzer.js +719 -0
- package/dist/lib/HeapParser.d.ts +19 -0
- package/dist/lib/HeapParser.d.ts.map +1 -0
- package/dist/lib/HeapParser.js +128 -0
- package/dist/lib/InternalValueSetter.d.ts +14 -0
- package/dist/lib/InternalValueSetter.d.ts.map +1 -0
- package/dist/lib/InternalValueSetter.js +43 -0
- package/dist/lib/NodeHeap.d.ts +16 -0
- package/dist/lib/NodeHeap.d.ts.map +1 -0
- package/dist/lib/NodeHeap.js +62 -0
- package/dist/lib/ProcessManager.d.ts +25 -0
- package/dist/lib/ProcessManager.d.ts.map +1 -0
- package/dist/lib/ProcessManager.js +67 -0
- package/dist/lib/Serializer.d.ts +49 -0
- package/dist/lib/Serializer.d.ts.map +1 -0
- package/dist/lib/Serializer.js +702 -0
- package/dist/lib/StringLoader.d.ts +26 -0
- package/dist/lib/StringLoader.d.ts.map +1 -0
- package/dist/lib/StringLoader.js +290 -0
- package/dist/lib/Types.d.ts +432 -0
- package/dist/lib/Types.d.ts.map +1 -0
- package/dist/lib/Types.js +11 -0
- package/dist/lib/Utils.d.ts +223 -0
- package/dist/lib/Utils.d.ts.map +1 -0
- package/dist/lib/Utils.js +1736 -0
- package/dist/lib/heap-data/HeapEdge.d.ts +27 -0
- package/dist/lib/heap-data/HeapEdge.d.ts.map +1 -0
- package/dist/lib/heap-data/HeapEdge.js +75 -0
- package/dist/lib/heap-data/HeapLocation.d.ts +22 -0
- package/dist/lib/heap-data/HeapLocation.d.ts.map +1 -0
- package/dist/lib/heap-data/HeapLocation.js +40 -0
- package/dist/lib/heap-data/HeapNode.d.ts +55 -0
- package/dist/lib/heap-data/HeapNode.d.ts.map +1 -0
- package/dist/lib/heap-data/HeapNode.js +344 -0
- package/dist/lib/heap-data/HeapSnapshot.d.ts +85 -0
- package/dist/lib/heap-data/HeapSnapshot.d.ts.map +1 -0
- package/dist/lib/heap-data/HeapSnapshot.js +462 -0
- package/dist/lib/heap-data/HeapStringNode.d.ts +18 -0
- package/dist/lib/heap-data/HeapStringNode.d.ts.map +1 -0
- package/dist/lib/heap-data/HeapStringNode.js +43 -0
- package/dist/lib/heap-data/HeapUtils.d.ts +17 -0
- package/dist/lib/heap-data/HeapUtils.d.ts.map +1 -0
- package/dist/lib/heap-data/HeapUtils.js +25 -0
- package/dist/logger/LeakClusterLogger.d.ts +40 -0
- package/dist/logger/LeakClusterLogger.d.ts.map +1 -0
- package/dist/logger/LeakClusterLogger.js +228 -0
- package/dist/logger/LeakTraceDetailsLogger.d.ts +19 -0
- package/dist/logger/LeakTraceDetailsLogger.d.ts.map +1 -0
- package/dist/logger/LeakTraceDetailsLogger.js +50 -0
- package/dist/modes/BaseMode.d.ts +30 -0
- package/dist/modes/BaseMode.d.ts.map +1 -0
- package/dist/modes/BaseMode.js +95 -0
- package/dist/modes/InteractionTestMode.d.ts +23 -0
- package/dist/modes/InteractionTestMode.d.ts.map +1 -0
- package/dist/modes/InteractionTestMode.js +46 -0
- package/dist/modes/MeasureMode.d.ts +23 -0
- package/dist/modes/MeasureMode.d.ts.map +1 -0
- package/dist/modes/MeasureMode.js +58 -0
- package/dist/modes/RunningModes.d.ts +15 -0
- package/dist/modes/RunningModes.d.ts.map +1 -0
- package/dist/modes/RunningModes.js +40 -0
- package/dist/paths/TraceFinder.d.ts +31 -0
- package/dist/paths/TraceFinder.d.ts.map +1 -0
- package/dist/paths/TraceFinder.js +537 -0
- package/dist/trace-cluster/ClusterUtils.d.ts +14 -0
- package/dist/trace-cluster/ClusterUtils.d.ts.map +1 -0
- package/dist/trace-cluster/ClusterUtils.js +17 -0
- package/dist/trace-cluster/ClusterUtilsHelper.d.ts +38 -0
- package/dist/trace-cluster/ClusterUtilsHelper.d.ts.map +1 -0
- package/dist/trace-cluster/ClusterUtilsHelper.js +373 -0
- package/dist/trace-cluster/ClusteringHeuristics.d.ts +22 -0
- package/dist/trace-cluster/ClusteringHeuristics.d.ts.map +1 -0
- package/dist/trace-cluster/ClusteringHeuristics.js +23 -0
- package/dist/trace-cluster/EvalutationMetric.d.ts +22 -0
- package/dist/trace-cluster/EvalutationMetric.d.ts.map +1 -0
- package/dist/trace-cluster/EvalutationMetric.js +158 -0
- package/dist/trace-cluster/TraceBucket.d.ts +36 -0
- package/dist/trace-cluster/TraceBucket.d.ts.map +1 -0
- package/dist/trace-cluster/TraceBucket.js +238 -0
- package/dist/trace-cluster/TraceElement.d.ts +71 -0
- package/dist/trace-cluster/TraceElement.d.ts.map +1 -0
- package/dist/trace-cluster/TraceElement.js +182 -0
- package/dist/trace-cluster/strategies/TraceAsClusterStrategy.d.ts +15 -0
- package/dist/trace-cluster/strategies/TraceAsClusterStrategy.d.ts.map +1 -0
- package/dist/trace-cluster/strategies/TraceAsClusterStrategy.js +37 -0
- package/dist/trace-cluster/strategies/TraceSimilarityStrategy.d.ts +15 -0
- package/dist/trace-cluster/strategies/TraceSimilarityStrategy.d.ts.map +1 -0
- package/dist/trace-cluster/strategies/TraceSimilarityStrategy.js +60 -0
- package/package.json +60 -0
- package/static/run-meta.json +10 -0
- package/static/visit-order.json +27 -0
|
@@ -0,0 +1,702 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*
|
|
8
|
+
* @emails oncall+ws_labs
|
|
9
|
+
* @format
|
|
10
|
+
*/
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const fs_1 = __importDefault(require("fs"));
|
|
16
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
17
|
+
const Config_1 = __importDefault(require("./Config"));
|
|
18
|
+
const Utils_1 = __importDefault(require("./Utils"));
|
|
19
|
+
const Console_1 = __importDefault(require("./Console"));
|
|
20
|
+
const TraceFinder_1 = __importDefault(require("../paths/TraceFinder"));
|
|
21
|
+
const REGEXP_NAME_CLEANUP = /[[]\(\)]/g;
|
|
22
|
+
const EMPTY_JSONIFY_OPTIONS = {
|
|
23
|
+
fiberNodeReturnTrace: {},
|
|
24
|
+
};
|
|
25
|
+
function JSONifyNodeRetainSize(node) {
|
|
26
|
+
const nodeRetainSize = node.retainedSize;
|
|
27
|
+
return nodeRetainSize ? `$retained-size:${nodeRetainSize}` : '';
|
|
28
|
+
}
|
|
29
|
+
function getNodeNameInJSON(node, args = {}) {
|
|
30
|
+
const leakedIdSet = args.leakedIdSet;
|
|
31
|
+
const isLeaked = leakedIdSet ? leakedIdSet.has(node.id) : false;
|
|
32
|
+
let name = node.name === '' ? '<anonymous>' : node.name;
|
|
33
|
+
const nodeImpact = JSONifyNodeRetainSize(node);
|
|
34
|
+
if (Utils_1.default.isFiberNode(node)) {
|
|
35
|
+
name = Utils_1.default.extractFiberNodeInfo(node);
|
|
36
|
+
}
|
|
37
|
+
else if (node.type === 'closure') {
|
|
38
|
+
name = Utils_1.default.extractClosureNodeInfo(node);
|
|
39
|
+
}
|
|
40
|
+
// replace all [, ], (, and )
|
|
41
|
+
name = name.replace(REGEXP_NAME_CLEANUP, ' ');
|
|
42
|
+
const leakTag = isLeaked ? '$memLabTag:leaked' : '';
|
|
43
|
+
// figure out the node is allocated in which snapshot
|
|
44
|
+
let snapshotTag = '';
|
|
45
|
+
if (args.nodeIdsInSnapshots) {
|
|
46
|
+
for (let i = 0; i < args.nodeIdsInSnapshots.length; i++) {
|
|
47
|
+
const snapshotIds = args.nodeIdsInSnapshots[i];
|
|
48
|
+
if (snapshotIds.has(node.id)) {
|
|
49
|
+
snapshotTag = `$snapshotIdTag:${i + 1}`;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const highlightTag = node.highlight ? '$highlight' : '';
|
|
55
|
+
const alternateTag = Utils_1.default.isAlternateNode(node)
|
|
56
|
+
? '$memLabTag:alternate'
|
|
57
|
+
: '';
|
|
58
|
+
if (name === 'system / Context') {
|
|
59
|
+
name = '<function scope>';
|
|
60
|
+
}
|
|
61
|
+
let ret = `[${name}](${node.type})`;
|
|
62
|
+
ret += ` @${node.id}`;
|
|
63
|
+
ret += ` ${nodeImpact}`;
|
|
64
|
+
ret += ` ${leakTag}`;
|
|
65
|
+
ret += ` ${alternateTag}`;
|
|
66
|
+
ret += ` ${snapshotTag}`;
|
|
67
|
+
ret += ` ${highlightTag}`;
|
|
68
|
+
return ret;
|
|
69
|
+
}
|
|
70
|
+
function getEdgeNameInJSON(edge, edgeRetainSize = 0) {
|
|
71
|
+
const prefix = ` --${JSONifyEdgeNameAndType(edge)}`;
|
|
72
|
+
const suffix = '---> ';
|
|
73
|
+
const sizeInfo = edgeRetainSize > 0 ? `(retaining bytes: ${edgeRetainSize})` : '';
|
|
74
|
+
return `${prefix}${sizeInfo}${suffix}`;
|
|
75
|
+
}
|
|
76
|
+
function JSONifyEdgeNameAndType(edge) {
|
|
77
|
+
const edgeName = filterJSONPropName(edge.name_or_index);
|
|
78
|
+
const edgeType = edge.type === 'context' ? 'variable' : edge.type;
|
|
79
|
+
return `${edgeName} (${edgeType})`;
|
|
80
|
+
}
|
|
81
|
+
function filterJSONPropName(name_or_index) {
|
|
82
|
+
if (name_or_index === 'hasOwnProperty') {
|
|
83
|
+
name_or_index += ' ';
|
|
84
|
+
}
|
|
85
|
+
if (typeof name_or_index === 'string') {
|
|
86
|
+
// replace all [, ], (, and )
|
|
87
|
+
name_or_index = name_or_index.replace(/[[\]()]/g, '');
|
|
88
|
+
}
|
|
89
|
+
return name_or_index;
|
|
90
|
+
}
|
|
91
|
+
function JSONifyDetachedHTMLElement(node, args, options) {
|
|
92
|
+
const info = Object.create(null);
|
|
93
|
+
// options for elem.__reactFiber$xxx
|
|
94
|
+
const fiberOptions = Object.assign({}, options);
|
|
95
|
+
const nextDepth = options.forceJSONifyDepth
|
|
96
|
+
? options.forceJSONifyDepth - 1
|
|
97
|
+
: undefined;
|
|
98
|
+
fiberOptions.forceJSONifyDepth = nextDepth;
|
|
99
|
+
// options for elem.__reactProps$xxx
|
|
100
|
+
const propsOptions = Object.assign({}, options);
|
|
101
|
+
propsOptions.forceJSONifyDepth = 1;
|
|
102
|
+
for (const edge of node.references) {
|
|
103
|
+
const key = JSONifyEdgeNameAndType(edge);
|
|
104
|
+
if (Utils_1.default.isReactFiberEdge(edge)) {
|
|
105
|
+
info[key] = JSONifyNode(edge.toNode, args, fiberOptions);
|
|
106
|
+
}
|
|
107
|
+
else if (Utils_1.default.isReactPropsEdge(edge)) {
|
|
108
|
+
info[key] = JSONifyNode(edge.toNode, args, propsOptions);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
info[key] = JSONifyNodeInShort(edge.toNode);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return info;
|
|
115
|
+
}
|
|
116
|
+
function calculateReturnTrace(node, cache) {
|
|
117
|
+
if (!node || !Utils_1.default.isFiberNode(node)) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
if (cache[node.nodeIndex]) {
|
|
121
|
+
return cache[node.nodeIndex];
|
|
122
|
+
}
|
|
123
|
+
const returnNode = Utils_1.default.getToNodeByEdge(node, 'return', 'property');
|
|
124
|
+
const returnNodeTrace = calculateReturnTrace(returnNode, cache);
|
|
125
|
+
return (cache[node.nodeIndex] = node.nodeIndex + ' | ' + returnNodeTrace);
|
|
126
|
+
}
|
|
127
|
+
// use properties that should be serialized with more in-depth info
|
|
128
|
+
const objectNodeUsefulProps = new Set(['_context']);
|
|
129
|
+
function JSONifyNodeOneLevel(node) {
|
|
130
|
+
const info = Object.create(null);
|
|
131
|
+
for (const edge of node.references) {
|
|
132
|
+
const key = JSONifyEdgeNameAndType(edge);
|
|
133
|
+
info[key] = JSONifyNodeShallow(edge.toNode);
|
|
134
|
+
}
|
|
135
|
+
return info;
|
|
136
|
+
}
|
|
137
|
+
function JSONifyNodeShallow(node) {
|
|
138
|
+
const info = Object.create(null);
|
|
139
|
+
for (const edge of node.references) {
|
|
140
|
+
const key = JSONifyEdgeNameAndType(edge);
|
|
141
|
+
if (objectNodeUsefulProps.has(edge.name_or_index)) {
|
|
142
|
+
info[key] = JSONifyNodeShallow(edge.toNode);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
info[key] = JSONifyNodeInShort(edge.toNode);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return info;
|
|
149
|
+
}
|
|
150
|
+
const fiberNodeUsefulProps = new Set([
|
|
151
|
+
'stateNode',
|
|
152
|
+
'type',
|
|
153
|
+
'elementType',
|
|
154
|
+
]);
|
|
155
|
+
function JSONifyFiberNodeShallow(node) {
|
|
156
|
+
const info = Object.create(null);
|
|
157
|
+
for (const edge of node.references) {
|
|
158
|
+
const key = JSONifyEdgeNameAndType(edge);
|
|
159
|
+
if (fiberNodeUsefulProps.has(edge.name_or_index) &&
|
|
160
|
+
Utils_1.default.isObjectNode(edge.toNode)) {
|
|
161
|
+
info[key] = JSONifyNodeShallow(edge.toNode);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
info[key] = JSONifyNodeInShort(edge.toNode);
|
|
165
|
+
}
|
|
166
|
+
return info;
|
|
167
|
+
}
|
|
168
|
+
// calculate the summary of return chain of the FiberNode
|
|
169
|
+
function JSONifyFiberNodeReturnTrace(node, args, options) {
|
|
170
|
+
const cache = options.fiberNodeReturnTrace;
|
|
171
|
+
const returnTrace = calculateReturnTrace(node, cache);
|
|
172
|
+
if (!returnTrace) {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
const idxs = returnTrace.split(' | ');
|
|
176
|
+
const trace = Object.create(null);
|
|
177
|
+
let num = 0;
|
|
178
|
+
for (const idx of idxs) {
|
|
179
|
+
const index = Number(idx);
|
|
180
|
+
let key = `${num++}`;
|
|
181
|
+
if (Number.isNaN(index)) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
const parent = node.snapshot.nodes.get(index);
|
|
185
|
+
const parentInfo = getNodeNameInJSON(parent, args);
|
|
186
|
+
key = `${key}: --return (property)---> ${parentInfo}`;
|
|
187
|
+
const info = JSONifyFiberNodeShallow(parent);
|
|
188
|
+
trace[key] = info;
|
|
189
|
+
}
|
|
190
|
+
return trace;
|
|
191
|
+
}
|
|
192
|
+
function JSONifyFiberNode(node, args, options) {
|
|
193
|
+
const info = Object.create(null);
|
|
194
|
+
// create an option to cache the FiberNode return chain
|
|
195
|
+
if (!options.fiberNodeReturnTrace) {
|
|
196
|
+
options.fiberNodeReturnTrace = Object.create(null);
|
|
197
|
+
}
|
|
198
|
+
const returnTraceJSON = JSONifyFiberNodeReturnTrace(node, args, options);
|
|
199
|
+
info['React Fiber return chain (extra)'] = returnTraceJSON;
|
|
200
|
+
const propsOptions = Object.assign({}, options);
|
|
201
|
+
// for FiberNode, force expand a few more levels
|
|
202
|
+
if (propsOptions.forceJSONifyDepth === undefined) {
|
|
203
|
+
propsOptions.forceJSONifyDepth = 1;
|
|
204
|
+
}
|
|
205
|
+
propsOptions.forceJSONifyDepth--;
|
|
206
|
+
for (const edge of node.references) {
|
|
207
|
+
const key = JSONifyEdgeNameAndType(edge);
|
|
208
|
+
info[key] =
|
|
209
|
+
propsOptions.forceJSONifyDepth >= 1
|
|
210
|
+
? JSONifyNode(edge.toNode, args, propsOptions)
|
|
211
|
+
: JSONifyNodeInShort(edge.toNode);
|
|
212
|
+
}
|
|
213
|
+
return info;
|
|
214
|
+
}
|
|
215
|
+
function JSONifyClosure(node, args, options) {
|
|
216
|
+
const info = Object.create(null);
|
|
217
|
+
for (const edge of node.references) {
|
|
218
|
+
if (edge.name_or_index === 'shared' ||
|
|
219
|
+
edge.name_or_index === 'context' ||
|
|
220
|
+
edge.name_or_index === 'displayName') {
|
|
221
|
+
const key = filterJSONPropName(edge.name_or_index);
|
|
222
|
+
info[key] = JSONifyNode(edge.toNode, args, options);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return info;
|
|
226
|
+
}
|
|
227
|
+
function JSONifyNumberNode(node,
|
|
228
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
229
|
+
_args,
|
|
230
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
231
|
+
_options) {
|
|
232
|
+
const info = Object.create(null);
|
|
233
|
+
info.value = Utils_1.default.getNumberNodeValue(node);
|
|
234
|
+
return info;
|
|
235
|
+
}
|
|
236
|
+
function JSONifyCode(node, args, options) {
|
|
237
|
+
const info = Object.create(null);
|
|
238
|
+
for (const edge of node.references) {
|
|
239
|
+
if (edge.name_or_index === 'name_or_scope_info' &&
|
|
240
|
+
edge.toNode.name === '(function scope info)') {
|
|
241
|
+
const key = 'variables with non-number values in closure scope chain';
|
|
242
|
+
info[key] = JSONifyNode(edge.toNode, args, options);
|
|
243
|
+
}
|
|
244
|
+
else if (edge.name_or_index === 'script_or_debug_info') {
|
|
245
|
+
info['script URL'] = edge.toNode.name;
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
const key = filterJSONPropName(edge.name_or_index);
|
|
249
|
+
info[key] = JSONifyNode(edge.toNode, args, options);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return info;
|
|
253
|
+
}
|
|
254
|
+
function JSONifyContext(node, args, options) {
|
|
255
|
+
const info = Object.create(null);
|
|
256
|
+
const key = 'variables in scope (used by nested closures)';
|
|
257
|
+
const closure_vars = (info[key] = Object.create(null));
|
|
258
|
+
for (const edge of node.references) {
|
|
259
|
+
const key = filterJSONPropName(edge.name_or_index);
|
|
260
|
+
if (edge.type === 'context') {
|
|
261
|
+
closure_vars[key] = JSONifyNodeInShort(edge.toNode);
|
|
262
|
+
}
|
|
263
|
+
else if (edge.type === '') {
|
|
264
|
+
info[key] = JSONifyNode(edge.toNode, args, options);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return info;
|
|
268
|
+
}
|
|
269
|
+
function JSONifyOrdinaryValue(node, args, options) {
|
|
270
|
+
const info = Object.create(null);
|
|
271
|
+
for (const edge of node.references) {
|
|
272
|
+
if (edge.name_or_index === 'map' && edge.type === 'internal') {
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
const key = JSONifyEdgeNameAndType(edge);
|
|
276
|
+
const toNode = edge.toNode;
|
|
277
|
+
const toNodeName = toNode.name;
|
|
278
|
+
const edgeName = edge.name_or_index;
|
|
279
|
+
if (edgeName === 'function_data' ||
|
|
280
|
+
edgeName === 'name_or_scope_info' ||
|
|
281
|
+
toNode.type === 'concatenated string' ||
|
|
282
|
+
(toNode.type === 'array' && toNode.edge_count < 200) ||
|
|
283
|
+
toNodeName === 'system / SourcePositionTableWithFrameCache' ||
|
|
284
|
+
toNodeName === 'system / StackTraceFrame' ||
|
|
285
|
+
toNodeName === 'system / StackFrameInfo' ||
|
|
286
|
+
edgeName === 'line_ends' ||
|
|
287
|
+
(edgeName === 'properties' && edge.type === 'internal')) {
|
|
288
|
+
info[key] = JSONifyNode(toNode, args, options);
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
info[key] = JSONifyNodeInShort(toNode);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return info;
|
|
295
|
+
}
|
|
296
|
+
function JSONifyNode(node, args, options) {
|
|
297
|
+
if (!node) {
|
|
298
|
+
return {};
|
|
299
|
+
}
|
|
300
|
+
let info;
|
|
301
|
+
const depths = options.forceJSONifyDepth;
|
|
302
|
+
if (Utils_1.default.isDetachedDOMNode(node) && depths !== 0) {
|
|
303
|
+
info = JSONifyDetachedHTMLElement(node, args, EMPTY_JSONIFY_OPTIONS);
|
|
304
|
+
}
|
|
305
|
+
else if (Utils_1.default.isFiberNode(node) && depths !== 0) {
|
|
306
|
+
info = JSONifyFiberNode(node, args, options);
|
|
307
|
+
}
|
|
308
|
+
else if (Utils_1.default.shouldShowMoreInfo(node)) {
|
|
309
|
+
info = JSONifyNodeOneLevel(node);
|
|
310
|
+
}
|
|
311
|
+
else if (node.type === 'closure') {
|
|
312
|
+
info = JSONifyClosure(node, args, options);
|
|
313
|
+
}
|
|
314
|
+
else if (node.type === 'code') {
|
|
315
|
+
info = JSONifyCode(node, args, options);
|
|
316
|
+
}
|
|
317
|
+
else if (node.name === 'system / Context') {
|
|
318
|
+
info = JSONifyContext(node, args, options);
|
|
319
|
+
}
|
|
320
|
+
else if (node.type === 'number') {
|
|
321
|
+
info = JSONifyNumberNode(node, args, options);
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
info = JSONifyOrdinaryValue(node, args, options);
|
|
325
|
+
}
|
|
326
|
+
if (node.location) {
|
|
327
|
+
info[`${filterJSONPropName('allocation location (extra)')}`] = {
|
|
328
|
+
script_id: node.location.script_id,
|
|
329
|
+
line: node.location.line,
|
|
330
|
+
column: node.location.column,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
if (node.dominatorNode) {
|
|
334
|
+
info['dominator id (extra)'] = `@${node.dominatorNode.id}`;
|
|
335
|
+
}
|
|
336
|
+
return info;
|
|
337
|
+
}
|
|
338
|
+
function JSONifyTabsOrder() {
|
|
339
|
+
const file = Utils_1.default.getSnapshotSequenceFilePath();
|
|
340
|
+
return fs_1.default.readFileSync(file, 'UTF-8');
|
|
341
|
+
}
|
|
342
|
+
function shouldHighlight(node) {
|
|
343
|
+
return Utils_1.default.isDetachedDOMNode(node) || Utils_1.default.isDetachedFiberNode(node);
|
|
344
|
+
}
|
|
345
|
+
function JSONifyPath(path, _snapshot, args) {
|
|
346
|
+
if (!path.node) {
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
const ret = Object.create(null);
|
|
350
|
+
let idx = 0;
|
|
351
|
+
let encounterDetachedNode = false;
|
|
352
|
+
ret['$tabsOrder:' + JSONifyTabsOrder()] = '';
|
|
353
|
+
ret[`${idx++}: ${getNodeNameInJSON(path.node, args)}`] = JSONifyNode(path.node, args, EMPTY_JSONIFY_OPTIONS);
|
|
354
|
+
let pathItem = path;
|
|
355
|
+
while (pathItem === null || pathItem === void 0 ? void 0 : pathItem.edge) {
|
|
356
|
+
const edge = pathItem.edge;
|
|
357
|
+
const nextNode = edge.toNode;
|
|
358
|
+
if (!encounterDetachedNode && shouldHighlight(nextNode)) {
|
|
359
|
+
encounterDetachedNode = true;
|
|
360
|
+
nextNode.highlight = true;
|
|
361
|
+
}
|
|
362
|
+
const edgeRetainSize = pathItem.edgeRetainSize;
|
|
363
|
+
ret[`${idx++}: ${getEdgeNameInJSON(edge, edgeRetainSize)}${getNodeNameInJSON(nextNode, args)}`] = JSONifyNode(nextNode, args, EMPTY_JSONIFY_OPTIONS);
|
|
364
|
+
pathItem = pathItem.next;
|
|
365
|
+
}
|
|
366
|
+
return ret;
|
|
367
|
+
}
|
|
368
|
+
function JSONifyNodeInShort(node) {
|
|
369
|
+
const wrapper = {
|
|
370
|
+
_isMemLabWrapper: true,
|
|
371
|
+
tags: {
|
|
372
|
+
retainedSize: node.retainedSize,
|
|
373
|
+
id: node.id,
|
|
374
|
+
type: node.type,
|
|
375
|
+
},
|
|
376
|
+
value: getNodeValue(node),
|
|
377
|
+
};
|
|
378
|
+
return wrapper;
|
|
379
|
+
}
|
|
380
|
+
function getNodeValue(node) {
|
|
381
|
+
var _a;
|
|
382
|
+
if (node.type === 'number') {
|
|
383
|
+
return (_a = Utils_1.default.getNumberNodeValue(node)) !== null && _a !== void 0 ? _a : '[<empty number>]';
|
|
384
|
+
}
|
|
385
|
+
if (Utils_1.default.isStringNode(node)) {
|
|
386
|
+
const str = Utils_1.default.getStringNodeValue(node);
|
|
387
|
+
if (str !== '') {
|
|
388
|
+
return `"${str}"`;
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
return '[<empty string>]';
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
if (node.name === 'symbol') {
|
|
395
|
+
const nameNode = Utils_1.default.getToNodeByEdge(node, 'name');
|
|
396
|
+
if (nameNode) {
|
|
397
|
+
return `Symbol(${getNodeTypeShortName(nameNode)})`;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
const id = `@${node.id}`;
|
|
401
|
+
if (Utils_1.default.isFiberNode(node)) {
|
|
402
|
+
return `[${Utils_1.default.extractFiberNodeInfo(node)}] ${id}`;
|
|
403
|
+
}
|
|
404
|
+
if (node.name === 'system / Context') {
|
|
405
|
+
return `[<function scope>] ${id}`;
|
|
406
|
+
}
|
|
407
|
+
if (node.name === 'system / Oddball') {
|
|
408
|
+
let v = node.references[1].toNode.name;
|
|
409
|
+
if (v === 'hole') {
|
|
410
|
+
return 'undefined';
|
|
411
|
+
}
|
|
412
|
+
try {
|
|
413
|
+
v = eval(v);
|
|
414
|
+
}
|
|
415
|
+
catch (_b) {
|
|
416
|
+
if (Config_1.default.verbose) {
|
|
417
|
+
Console_1.default.error(`unknown Oddball: ${v}`);
|
|
418
|
+
}
|
|
419
|
+
return '<unknown Oddball>';
|
|
420
|
+
}
|
|
421
|
+
return v + '';
|
|
422
|
+
}
|
|
423
|
+
if (node.name === 'symbol') {
|
|
424
|
+
const nameNode = Utils_1.default.getToNodeByEdge(node, 'name');
|
|
425
|
+
if (nameNode) {
|
|
426
|
+
return `Symbol(${getNodeValue(nameNode)})`;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
if (Utils_1.default.isFiberNode(node)) {
|
|
430
|
+
return `[${Utils_1.default.extractFiberNodeInfo(node)}]`;
|
|
431
|
+
}
|
|
432
|
+
if (node.name === 'system / Context') {
|
|
433
|
+
return `[<function scope>]`;
|
|
434
|
+
}
|
|
435
|
+
if (node.name === '') {
|
|
436
|
+
return `[<${node.type}>]`;
|
|
437
|
+
}
|
|
438
|
+
return `[${node.name}]`;
|
|
439
|
+
}
|
|
440
|
+
function getNodeTypeShortName(node) {
|
|
441
|
+
const value = getNodeValue(node);
|
|
442
|
+
if (node.type === 'number' ||
|
|
443
|
+
node.name === 'system / Oddball' ||
|
|
444
|
+
node.name === 'symbol' ||
|
|
445
|
+
node.type === 'concatenated string' ||
|
|
446
|
+
node.type === 'string') {
|
|
447
|
+
return value + '';
|
|
448
|
+
}
|
|
449
|
+
const id = `@${node.id}`;
|
|
450
|
+
return `${value} ${id}`;
|
|
451
|
+
}
|
|
452
|
+
function stringifyNode(node, str) {
|
|
453
|
+
if (!node ||
|
|
454
|
+
node.type === 'code' ||
|
|
455
|
+
node.type === 'hidden' ||
|
|
456
|
+
node.type === 'array' ||
|
|
457
|
+
node.type === 'native' ||
|
|
458
|
+
node.type === 'closure') {
|
|
459
|
+
return str;
|
|
460
|
+
}
|
|
461
|
+
let info;
|
|
462
|
+
if (node.name === 'system / Context') {
|
|
463
|
+
info = { closure_vars: Object.create(null) };
|
|
464
|
+
for (const edge of node.references) {
|
|
465
|
+
if (edge.type === 'context') {
|
|
466
|
+
const key = filterJSONPropName(edge.name_or_index);
|
|
467
|
+
info.closure_vars[key] =
|
|
468
|
+
JSONifyNodeInShort(edge.toNode);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
info = Object.create(null);
|
|
474
|
+
for (const edge of node.references) {
|
|
475
|
+
const key = filterJSONPropName(edge.name_or_index);
|
|
476
|
+
if (edge.type === 'property') {
|
|
477
|
+
info[key] = JSONifyNodeInShort(edge.toNode);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
const nodeJSON = JSON.stringify(info, null, 2);
|
|
482
|
+
str += beautifyJSON(nodeJSON);
|
|
483
|
+
str += '\n';
|
|
484
|
+
return str;
|
|
485
|
+
}
|
|
486
|
+
function beautifyJSON(nodeJSON) {
|
|
487
|
+
const indent = ' ';
|
|
488
|
+
return nodeJSON
|
|
489
|
+
.split('\n')
|
|
490
|
+
.map(l => {
|
|
491
|
+
// add indentation to each line
|
|
492
|
+
l = indent + l;
|
|
493
|
+
return l;
|
|
494
|
+
})
|
|
495
|
+
.join('\n');
|
|
496
|
+
}
|
|
497
|
+
function summarizeObjectShape(node, options = {}) {
|
|
498
|
+
const refs = node.references;
|
|
499
|
+
const props = [];
|
|
500
|
+
for (const edge of refs) {
|
|
501
|
+
const name = edge.name_or_index;
|
|
502
|
+
if (edge.type === 'internal') {
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
if (!Config_1.default.edgeIgnoreSetInShape.has(name)) {
|
|
506
|
+
props.push(name);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
let keys = options.compact ? props.slice(0, 5) : props;
|
|
510
|
+
keys = keys.sort();
|
|
511
|
+
keys = options.color ? keys.map(k => chalk_1.default.green(k)) : keys;
|
|
512
|
+
keys = keys.length < props.length ? keys.concat('...') : keys;
|
|
513
|
+
const sep = options.color ? chalk_1.default.grey(', ') : ', ';
|
|
514
|
+
const beg = options.color ? chalk_1.default.bold('{ ') : '{ ';
|
|
515
|
+
const end = options.color ? chalk_1.default.bold(' }') : ' }';
|
|
516
|
+
return `Object ${beg}${keys.join(sep)}${end}`;
|
|
517
|
+
}
|
|
518
|
+
// convert a heap object into a string showing its shape
|
|
519
|
+
function summarizeNodeShape(node, options = {}) {
|
|
520
|
+
if (Utils_1.default.isStringNode(node)) {
|
|
521
|
+
return options.color ? chalk_1.default.blue.bold('string') : 'string';
|
|
522
|
+
}
|
|
523
|
+
if (!Utils_1.default.isPlainJSObjectNode(node)) {
|
|
524
|
+
const name = node.name;
|
|
525
|
+
return options.color ? chalk_1.default.blue.bold(name) : name;
|
|
526
|
+
}
|
|
527
|
+
return summarizeObjectShape(node, options);
|
|
528
|
+
}
|
|
529
|
+
function summarizeUnboundedObjects(unboundedObjects, options = {}) {
|
|
530
|
+
const sizeSep = options.color ? chalk_1.default.grey(' > ') : ' > ';
|
|
531
|
+
const prefix = options.color ? chalk_1.default.grey('· ') : '· ';
|
|
532
|
+
const opt = Object.assign({ compact: true }, options);
|
|
533
|
+
return unboundedObjects
|
|
534
|
+
.map(item => {
|
|
535
|
+
const id = options.color ? chalk_1.default.grey(`@${item.id}`) : `@${item.id}`;
|
|
536
|
+
const name = summarizeNodeShape(item.node, opt);
|
|
537
|
+
return (`${prefix}${name} [${item.type}](${id}): ` +
|
|
538
|
+
item.history.map(v => Utils_1.default.getReadableBytes(v)).join(sizeSep));
|
|
539
|
+
})
|
|
540
|
+
.join('\n');
|
|
541
|
+
}
|
|
542
|
+
function summarizeUnboundedObjectsToCSV(unboundedObjects) {
|
|
543
|
+
return unboundedObjects
|
|
544
|
+
.map(item => {
|
|
545
|
+
return `${item.name},${item.id},${item.type},` + item.history.join(',');
|
|
546
|
+
})
|
|
547
|
+
.join('\n');
|
|
548
|
+
}
|
|
549
|
+
function summarizeTab(tab, color = false) {
|
|
550
|
+
let res = tab.name;
|
|
551
|
+
if (tab.JSHeapUsedSize) {
|
|
552
|
+
const bytes = Utils_1.default.getReadableBytes(tab.JSHeapUsedSize);
|
|
553
|
+
res += color ? `[${chalk_1.default.green(bytes)}]` : ` [${bytes}]`;
|
|
554
|
+
}
|
|
555
|
+
if (tab.type) {
|
|
556
|
+
res += color ? `(${chalk_1.default.green(tab.type)})` : ` (${tab.type})`;
|
|
557
|
+
}
|
|
558
|
+
if (tab.snapshot) {
|
|
559
|
+
res += color ? `[${chalk_1.default.green('s' + tab.idx)}]` : ` [s${tab.idx}]`;
|
|
560
|
+
}
|
|
561
|
+
return res;
|
|
562
|
+
}
|
|
563
|
+
function summarizeTabsOrder(tabsOrder, options = {}) {
|
|
564
|
+
const tabSep = options.color ? chalk_1.default.grey('>') : '>';
|
|
565
|
+
let res = '';
|
|
566
|
+
for (let i = 0; i < tabsOrder.length; ++i) {
|
|
567
|
+
const tab = tabsOrder[i];
|
|
568
|
+
const sep = i < tabsOrder.length - 1 ? tabSep : '';
|
|
569
|
+
const isCurrentTab = i === options.progress;
|
|
570
|
+
let tabSummaryString = summarizeTab(tab, options.color);
|
|
571
|
+
if (options.color && isCurrentTab) {
|
|
572
|
+
tabSummaryString = chalk_1.default.bold(tabSummaryString);
|
|
573
|
+
}
|
|
574
|
+
const tabSummaryStringWithSeparator = `${tabSummaryString} ${sep} `;
|
|
575
|
+
if (options.color &&
|
|
576
|
+
options.progress !== undefined &&
|
|
577
|
+
i > options.progress) {
|
|
578
|
+
res += chalk_1.default.dim(tabSummaryStringWithSeparator);
|
|
579
|
+
}
|
|
580
|
+
else {
|
|
581
|
+
res += tabSummaryStringWithSeparator;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
return res;
|
|
585
|
+
}
|
|
586
|
+
function summarizeNodeName(node, options) {
|
|
587
|
+
const name = getNodeTypeShortName(node);
|
|
588
|
+
const nodeStr = name.split('@')[0].trim();
|
|
589
|
+
return options.color ? chalk_1.default.green(nodeStr) : nodeStr;
|
|
590
|
+
}
|
|
591
|
+
function summarizeNode(node, options = {}) {
|
|
592
|
+
const nodeRetainSize = Utils_1.default.getReadableBytes(node.retainedSize);
|
|
593
|
+
let nodeImpact = '';
|
|
594
|
+
if (nodeRetainSize) {
|
|
595
|
+
nodeImpact = options.color
|
|
596
|
+
? chalk_1.default.grey('[') + chalk_1.default.blue(nodeRetainSize) + chalk_1.default.grey(']')
|
|
597
|
+
: `[${nodeRetainSize}]`;
|
|
598
|
+
}
|
|
599
|
+
const name = summarizeNodeName(node, options);
|
|
600
|
+
const type = options.color ? chalk_1.default.grey(`(${node.type})`) : `(${node.type})`;
|
|
601
|
+
const id = options.color ? chalk_1.default.grey(`@${node.id}`) : `@${node.id}`;
|
|
602
|
+
return `${name} ${type} ${id} ${nodeImpact}`;
|
|
603
|
+
}
|
|
604
|
+
function summarizeEdgeName(edge, options = {}) {
|
|
605
|
+
let name = `${edge.name_or_index}`;
|
|
606
|
+
if (options.abstract) {
|
|
607
|
+
if (edge.is_index) {
|
|
608
|
+
name = '<numeric-element>';
|
|
609
|
+
}
|
|
610
|
+
if (edge.fromNode.type === 'array' &&
|
|
611
|
+
edge.type === 'internal' &&
|
|
612
|
+
!isNaN(parseInt(name, 10))) {
|
|
613
|
+
name = '<array-element>';
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
return options.color ? chalk_1.default.white(name) : name;
|
|
617
|
+
}
|
|
618
|
+
function summarizeEdge(edge, edgeRetainSize, options = {}) {
|
|
619
|
+
const edgeImpact = edgeRetainSize
|
|
620
|
+
? `[${Utils_1.default.getReadableBytes(edgeRetainSize)}]`
|
|
621
|
+
: '';
|
|
622
|
+
const edgeType = edge.type === 'context' ? 'variable' : edge.type;
|
|
623
|
+
const beg = options.color ? chalk_1.default.grey('--') : '--';
|
|
624
|
+
const end = (options.color ? chalk_1.default.grey('---') : '---') + '>';
|
|
625
|
+
const type = options.color ? chalk_1.default.grey(`(${edgeType})`) : `(${edgeType})`;
|
|
626
|
+
const name = summarizeEdgeName(edge, options);
|
|
627
|
+
return ` ${beg}${name} ${type}${edgeImpact}${end} `;
|
|
628
|
+
}
|
|
629
|
+
function summarizePath(pathArg, nodeIdInPaths, snapshot, options = {}) {
|
|
630
|
+
var _a, _b;
|
|
631
|
+
const depth = (_a = options.depth) !== null && _a !== void 0 ? _a : 0;
|
|
632
|
+
if (depth > 5) {
|
|
633
|
+
return '...';
|
|
634
|
+
}
|
|
635
|
+
const excludeKeySet = options.excludeKeySet || new Set();
|
|
636
|
+
let ret = '';
|
|
637
|
+
let p = pathArg;
|
|
638
|
+
let hasWeakMapEdge = false;
|
|
639
|
+
let weakMapKeyObjectId = undefined;
|
|
640
|
+
let weakMapEdgeIdx = undefined;
|
|
641
|
+
while (p) {
|
|
642
|
+
const node = p.node;
|
|
643
|
+
const edge = p.edge;
|
|
644
|
+
if (node) {
|
|
645
|
+
nodeIdInPaths.add(node.id);
|
|
646
|
+
ret += `${summarizeNode(node, options)}\n`;
|
|
647
|
+
// if we need to further expand node properties in the summary
|
|
648
|
+
if (Config_1.default.dumpNodeInfo) {
|
|
649
|
+
ret += stringifyNode(node, ret);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
if (edge) {
|
|
653
|
+
if (Utils_1.default.isWeakMapEdgeToValue(edge)) {
|
|
654
|
+
hasWeakMapEdge = true;
|
|
655
|
+
weakMapEdgeIdx = edge.edgeIndex;
|
|
656
|
+
weakMapKeyObjectId = Utils_1.default.getWeakMapEdgeKeyId(edge);
|
|
657
|
+
}
|
|
658
|
+
ret += edge ? summarizeEdge(edge, (_b = p.edgeRetainSize) !== null && _b !== void 0 ? _b : 0, options) : '';
|
|
659
|
+
}
|
|
660
|
+
p = p.next;
|
|
661
|
+
}
|
|
662
|
+
if (!Config_1.default.chaseWeakMapEdge || !hasWeakMapEdge || depth >= 1000) {
|
|
663
|
+
return ret;
|
|
664
|
+
}
|
|
665
|
+
Console_1.default.midLevel(`depth: ${depth}`);
|
|
666
|
+
// recursively dump the path for the key
|
|
667
|
+
// but first make sure we do not dump the same WeakMap edge again
|
|
668
|
+
if (weakMapKeyObjectId) {
|
|
669
|
+
const keyNode = snapshot.getNodeById(weakMapKeyObjectId);
|
|
670
|
+
excludeKeySet.add(weakMapKeyObjectId);
|
|
671
|
+
const finder = new TraceFinder_1.default();
|
|
672
|
+
let keyNodePath = finder.getPathToGCRoots(snapshot, keyNode);
|
|
673
|
+
if (!keyNodePath) {
|
|
674
|
+
return ret;
|
|
675
|
+
}
|
|
676
|
+
// if the shortest path contains the same WeakMap edge,
|
|
677
|
+
// we need to exclude the edge and re-search the shortest path
|
|
678
|
+
if (weakMapEdgeIdx !== undefined &&
|
|
679
|
+
Utils_1.default.pathHasEdgeWithIndex(keyNodePath, weakMapEdgeIdx)) {
|
|
680
|
+
finder.annotateShortestPaths(snapshot, excludeKeySet);
|
|
681
|
+
keyNodePath = finder.getPathToGCRoots(snapshot, keyNode);
|
|
682
|
+
if (!keyNodePath) {
|
|
683
|
+
return ret;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
const subPathSummary = summarizePath(keyNodePath, nodeIdInPaths, snapshot, Object.assign(Object.assign({}, options), { depth: depth + 1 }));
|
|
687
|
+
const sep = '------';
|
|
688
|
+
ret += `\n${sep} WeakMap key node path (depth: ${depth}) ${sep}\n`;
|
|
689
|
+
ret += subPathSummary;
|
|
690
|
+
}
|
|
691
|
+
return ret;
|
|
692
|
+
}
|
|
693
|
+
exports.default = {
|
|
694
|
+
JSONifyPath,
|
|
695
|
+
summarizeEdgeName,
|
|
696
|
+
summarizeNodeName,
|
|
697
|
+
summarizeNodeShape,
|
|
698
|
+
summarizePath,
|
|
699
|
+
summarizeTabsOrder,
|
|
700
|
+
summarizeUnboundedObjects,
|
|
701
|
+
summarizeUnboundedObjectsToCSV,
|
|
702
|
+
};
|