1/*
2 * Copyright (C) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { HeapLoader } from '../logic/HeapLoader';
17import { AllocationFunction, FileInfo } from './UiStruct';
18
19export enum EdgeType {
20  CONTEXT = 0,
21  ELEMENT = 1,
22  PROPERTY = 2,
23  INTERNAL = 3,
24  HIDDEN = 4,
25  SHORTCUT = 5,
26  WEAK = 6,
27  STRING_OR_NUMBER = 6,
28  NODE = 7,
29  INVISIBLE = 8,
30}
31export enum NodeType {
32  HIDDEN = 0,
33  ARRAY = 1,
34  STRING = 2,
35  OBJECT = 3,
36  CODE = 4,
37  CLOSURE = 5,
38  REGEXP = 6,
39  NUMBER = 7,
40  NATIVE = 8,
41  SYNTHETIC = 9,
42  CONCATENATED_STRING = 10,
43  SLICED_STRING = 11,
44  SYMBOL = 12,
45  BIGINT = 13,
46  OBJECT_SHAPE = 14,
47}
48
49function getNodeTypeName(nodeType: NodeType): keyof typeof NodeType {
50  return Object.keys(NodeType).find(
51    (key) => NodeType[key as keyof typeof NodeType] === nodeType
52  ) as keyof typeof NodeType;
53}
54
55function getEdgeTypeName(nodeType: EdgeType): keyof typeof EdgeType {
56  return Object.keys(EdgeType).find(
57    (key) => EdgeType[key as keyof typeof EdgeType] === nodeType
58  ) as keyof typeof EdgeType;
59}
60
61export enum DetachedNessState {
62  UNKNOWN,
63  ATTACHED,
64  DETACHED,
65}
66
67export class HeapNode {
68  fileId: number;
69  nodeIndex: number;
70  nodeOldIndex: number;
71  type: NodeType;
72  name: string;
73  nameIdx!: number;
74  id: number;
75  selfSize: number;
76  edgeCount: number;
77  traceNodeId: number;
78  detachedness: number;
79  edges: Set<HeapEdge>;
80  distance: number = -5;
81  retainedSize: number;
82  displayName: string = '';
83  firstEdgeIndex: number;
84  flag: number;
85  retainsCount: number = 0;
86  retainsEdgeIdx: Array<number>;
87  retainsNodeIdx: Array<number>;
88
89  constructor(
90    fileId: number,
91    nodeIndex: number,
92    type: number,
93    name: string,
94    id: number,
95    selfSize: number,
96    edgeCount: number,
97    traceNodeId: number,
98    detachedness: number,
99    firstEdgeIndex: number
100  ) {
101    this.fileId = fileId;
102    this.nodeIndex = nodeIndex;
103    this.nodeOldIndex = nodeIndex * 7;
104    this.type = type;
105    this.name = name;
106    this.id = id;
107    this.selfSize = selfSize;
108    this.retainedSize = selfSize;
109    this.edgeCount = edgeCount;
110    this.traceNodeId = traceNodeId;
111    this.detachedness = detachedness;
112    this.firstEdgeIndex = firstEdgeIndex;
113    this.edges = new Set<HeapEdge>();
114    this.retainsEdgeIdx = [];
115    this.retainsNodeIdx = [];
116    this.flag = 0;
117  }
118
119  className(): string {
120    switch (this.type) {
121      case NodeType.HIDDEN:
122        return '(system)';
123      case NodeType.OBJECT:
124      case NodeType.NATIVE:
125        return this.nodeName();
126      case NodeType.CODE:
127        return '(compiled code)';
128      default:
129        return `(${getNodeTypeName(this.type)})`.toLowerCase();
130    }
131  }
132
133  nodeName(): string {
134    return this.displayName || this.name;
135  }
136
137  addEdge(edge: HeapEdge): void {
138    this.edges.add(edge);
139  }
140
141  idHidden(): boolean {
142    return this.type === NodeType.HIDDEN;
143  }
144
145  isArray(): boolean {
146    return this.type === NodeType.ARRAY;
147  }
148
149  isUserRoot(): boolean {
150    return this.type !== NodeType.SYNTHETIC;
151  }
152
153  isDocumentDOMTreesRoot(): boolean {
154    return this.type !== NodeType.SYNTHETIC && this.name === '(Document DOM trees)';
155  }
156}
157
158export class HeapEdge {
159  edgeOldIndex: number;
160  edgeIndex: number;
161  type: EdgeType;
162  nameOrIndex: string;
163  nodeId: number;
164  fromNodeId: number;
165  toNodeId: number;
166  retainsNode: Array<HeapNode>;
167  retainEdge: Array<HeapEdge>;
168
169  constructor(
170    edgeIndex: number,
171    type: number,
172    nameOrIndex: string,
173    nodeId: number,
174    fromNodeId: number,
175    toNodeId: number
176  ) {
177    this.edgeIndex = edgeIndex;
178    this.edgeOldIndex = edgeIndex * 3;
179    this.type = type;
180    this.nameOrIndex = nameOrIndex;
181    this.nodeId = nodeId;
182    this.fromNodeId = fromNodeId;
183    this.toNodeId = toNodeId;
184    this.retainsNode = [];
185    this.retainEdge = [];
186  }
187}
188
189export class HeapTraceFunctionInfo {
190  id: number;
191  index: number;
192  name: string;
193  scriptName: string;
194  scriptId: number;
195  line: number;
196  column: number;
197
198  constructor(
199    id: number,
200    index: number,
201    name: string,
202    scriptName: string,
203    scriptId: number,
204    line: number,
205    column: number
206  ) {
207    this.id = id;
208    this.index = index;
209    this.name = name;
210    this.scriptName = scriptName;
211    this.scriptId = scriptId;
212    this.line = line;
213    this.column = column;
214  }
215}
216
217export class HeapSample {
218  timestamp: number;
219  lastAssignedId: number;
220  size: number = 0;
221
222  constructor(timestamp: number, lastAssignedId: number) {
223    this.timestamp = timestamp;
224    this.lastAssignedId = lastAssignedId;
225  }
226}
227export class HeapLocation {
228  objectIndex: number;
229  scriptId: number;
230  line: number;
231  column: number;
232
233  constructor(objectIndex: number, scriptId: number, line: number, column: number) {
234    this.objectIndex = objectIndex;
235    this.scriptId = scriptId;
236    this.line = line;
237    this.column = column;
238  }
239}
240
241export class HeapSnapshotStruct {
242  nodeCount!: number;
243  edgeCount!: number;
244  functionCount!: number;
245  nodeMap: Map<number, HeapNode>;
246  edges: Array<HeapEdge>;
247  functionInfos: Array<HeapTraceFunctionInfo>;
248  traceNodes: Array<AllocationFunction>;
249  samples: Array<HeapSample>;
250  strings: Array<string>;
251  rootNodeId: number = -1;
252
253  constructor() {
254    this.nodeMap = new Map<number, HeapNode>();
255    this.edges = [];
256    this.functionInfos = [];
257    this.traceNodes = [];
258    this.samples = [];
259    this.strings = [];
260  }
261
262  public clear(): void {
263    this.nodeMap.clear();
264    this.edges.length = 0;
265    this.functionInfos.length = 0;
266    this.traceNodes.length = 0;
267    this.samples.length = 0;
268    this.strings.length = 0;
269  }
270}
271
272export class FileStruct extends FileInfo {
273  snapshotStruct: HeapSnapshotStruct;
274  isParseSuccess: boolean;
275  heapLoader!: HeapLoader;
276
277  constructor() {
278    super();
279    this.isParseSuccess = true;
280    this.snapshotStruct = new HeapSnapshotStruct();
281  }
282}
283