• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2024-2025 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 { NodeID, BaseEdge, BaseExplicitGraph, BaseNode, Kind } from '../../core/graph/BaseExplicitGraph';
17import { CallGraph, CallSite, DynCallSite } from '../model/CallGraph';
18import { Value } from '../../core/base/Value';
19import { ArkAssignStmt, ArkReturnStmt, Stmt } from '../../core/base/Stmt';
20import { AbstractExpr, ArkNewArrayExpr, ArkNewExpr } from '../../core/base/Expr';
21import { AbstractFieldRef, ArkArrayRef, ArkInstanceFieldRef, ArkParameterRef, ArkStaticFieldRef, ArkThisRef } from '../../core/base/Ref';
22import { Local } from '../../core/base/Local';
23import { GraphPrinter } from '../../save/GraphPrinter';
24import { PrinterBuilder } from '../../save/PrinterBuilder';
25import { Constant } from '../../core/base/Constant';
26import { FunctionType, UnclearReferenceType } from '../../core/base/Type';
27import { ClassSignature, FieldSignature, FileSignature, MethodSignature } from '../../core/model/ArkSignature';
28import { ContextID } from './Context';
29import Logger, { LOG_MODULE_TYPE } from '../../utils/logger';
30import { GLOBAL_THIS_NAME } from '../../core/common/TSConst';
31import { ExportInfo } from '../../core/model/ArkExport';
32import { BuiltApiType, getBuiltInApiType, IsCollectionClass } from './PTAUtils';
33import { IPtsCollection } from './PtsDS';
34import { PointerAnalysisConfig } from './PointerAnalysisConfig';
35
36const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'PTA');
37export type PagNodeType = Value;
38
39/*
40 * Implementation of pointer-to assignment graph for pointer analysis
41 */
42
43const DUMMY_PAG_NODE_ID = -1;
44
45export enum PagEdgeKind {
46    Address,
47    Copy,
48    Load,
49    Write,
50    This,
51    Unknown,
52    InterProceduralCopy,
53}
54
55export enum StorageType {
56    APP_STORAGE,
57    LOCAL_STORAGE,
58    Undefined,
59}
60
61export enum StorageLinkEdgeType {
62    Property2Local,
63    Local2Property,
64    TwoWay,
65}
66
67export class PagEdge extends BaseEdge {
68    private stmt: Stmt | undefined;
69
70    constructor(n: PagNode, d: PagNode, k: PagEdgeKind, s?: Stmt) {
71        super(n, d, k);
72        this.stmt = s;
73    }
74
75    public getDotAttr(): string {
76        switch (this.getKind()) {
77            case PagEdgeKind.Address:
78                return 'color=green';
79            case PagEdgeKind.Copy:
80                if (this.stmt?.getInvokeExpr() !== undefined || this.stmt instanceof ArkReturnStmt) {
81                    return 'color=black,style=dotted';
82                }
83                return 'color=black';
84            case PagEdgeKind.Load:
85                return 'color=red';
86            case PagEdgeKind.Write:
87                return 'color=blue';
88            case PagEdgeKind.This:
89                return 'color=orange';
90            case PagEdgeKind.InterProceduralCopy:
91                return 'color=purple,style=dashed';
92            default:
93                return 'color=black';
94        }
95    }
96}
97
98export class AddrPagEdge extends PagEdge {
99    constructor(n: PagNode, d: PagNode, s: Stmt) {
100        super(n, d, PagEdgeKind.Address, s);
101    }
102}
103
104export class CopyPagEdge extends PagEdge {
105    constructor(n: PagNode, d: PagNode, s: Stmt) {
106        super(n, d, PagEdgeKind.Copy, s);
107    }
108}
109
110export class LoadPagEdge extends PagEdge {
111    constructor(n: PagNode, d: PagNode, s: Stmt) {
112        super(n, d, PagEdgeKind.Copy, s);
113    }
114}
115
116export class WritePagEdge extends PagEdge {
117    constructor(n: PagNode, d: PagNode, s: Stmt) {
118        super(n, d, PagEdgeKind.Write, s);
119    }
120}
121
122export class ThisPagEdge extends PagEdge {
123    constructor(n: PagNode, d: PagNode, s: Stmt) {
124        super(n, d, PagEdgeKind.This, s);
125    }
126}
127
128type PagEdgeSet = Set<PagEdge>;
129
130export enum PagNodeKind {
131    HeapObj,
132    LocalVar,
133    RefVar,
134    Param,
135    ThisRef,
136    Function,
137    GlobalThis,
138    ExportInfo,
139}
140
141export class PagNode extends BaseNode {
142    private cid: ContextID | undefined;
143    private value: Value;
144    private stmt: Stmt | undefined; // stmt is just used for graph print
145    private pointTo: IPtsCollection<NodeID>;
146
147    private addressInEdges!: PagEdgeSet;
148    private addressOutEdges!: PagEdgeSet;
149    private copyInEdges!: PagEdgeSet;
150    private copyOutEdges!: PagEdgeSet;
151    private loadInEdges!: PagEdgeSet;
152    private loadOutEdges!: PagEdgeSet;
153    private writeInEdges!: PagEdgeSet;
154    private writeOutEdges!: PagEdgeSet;
155    private thisInEdges!: PagEdgeSet;
156    private thisOutEdges!: PagEdgeSet;
157
158    // Point-to node of base class
159    // Only PagInstanceRefNode has this field
160    // Define in base class is for dot print
161    protected basePt!: NodeID;
162    protected clonedFrom!: NodeID;
163
164    constructor(id: NodeID, cid: ContextID | undefined = undefined, value: Value, k: Kind, s?: Stmt) {
165        super(id, k);
166        this.cid = cid;
167        this.value = value;
168        this.stmt = s;
169        let ptaConfig = PointerAnalysisConfig.getInstance();
170        this.pointTo = new ptaConfig.ptsCollectionCtor();
171    }
172
173    public getBasePt(): NodeID {
174        return this.basePt;
175    }
176
177    public setBasePt(pt: NodeID): void {
178        this.basePt = pt;
179    }
180
181    public getCid(): ContextID {
182        if (this.cid === undefined) {
183            throw new Error('cid is undefine');
184        }
185        return this.cid;
186    }
187
188    public setCid(cid: ContextID): void {
189        this.cid = cid;
190    }
191
192    public setStmt(s: Stmt): void {
193        this.stmt = s;
194    }
195
196    public getStmt(): Stmt | undefined {
197        return this.stmt;
198    }
199
200    public hasOutgoingCopyEdge(): boolean {
201        return this.copyOutEdges.size !== 0;
202    }
203
204    public getOutgoingCopyEdges(): PagEdgeSet {
205        return this.copyOutEdges;
206    }
207
208    public getIncomingCopyEdges(): PagEdgeSet {
209        return this.copyInEdges;
210    }
211
212    public getOutgoingLoadEdges(): PagEdgeSet {
213        return this.loadOutEdges;
214    }
215
216    public getOutgoingWriteEdges(): PagEdgeSet {
217        return this.writeOutEdges;
218    }
219
220    public getIncomingWriteEdges(): PagEdgeSet {
221        return this.writeInEdges;
222    }
223
224    public getOutgoingThisEdges(): PagEdgeSet {
225        return this.thisOutEdges;
226    }
227
228    public getIncomingThisEdges(): PagEdgeSet {
229        return this.thisInEdges;
230    }
231
232    public addAddressInEdge(e: AddrPagEdge): void {
233        this.addressInEdges === undefined ? (this.addressInEdges = new Set()) : undefined;
234        this.addressInEdges.add(e);
235        this.addIncomingEdge(e);
236    }
237
238    public addAddressOutEdge(e: AddrPagEdge): void {
239        this.addressOutEdges === undefined ? (this.addressOutEdges = new Set()) : undefined;
240        this.addressOutEdges.add(e);
241        this.addOutgoingEdge(e);
242    }
243
244    public addCopyInEdge(e: CopyPagEdge): void {
245        this.copyInEdges === undefined ? (this.copyInEdges = new Set()) : undefined;
246        this.copyInEdges.add(e);
247        this.addIncomingEdge(e);
248    }
249
250    public addCopyOutEdge(e: CopyPagEdge): void {
251        this.copyOutEdges === undefined ? (this.copyOutEdges = new Set()) : undefined;
252
253        this.copyOutEdges.add(e);
254        this.addOutgoingEdge(e);
255    }
256
257    public addLoadInEdge(e: LoadPagEdge): void {
258        this.loadInEdges === undefined ? (this.loadInEdges = new Set()) : undefined;
259        this.loadInEdges.add(e);
260        this.addIncomingEdge(e);
261    }
262
263    public addLoadOutEdge(e: LoadPagEdge): void {
264        this.loadOutEdges === undefined ? (this.loadOutEdges = new Set()) : undefined;
265        this.loadOutEdges.add(e);
266        this.addOutgoingEdge(e);
267    }
268
269    public addWriteInEdge(e: WritePagEdge): void {
270        this.writeInEdges = this.writeInEdges ?? new Set();
271        this.writeInEdges.add(e);
272        this.addIncomingEdge(e);
273    }
274
275    public addWriteOutEdge(e: LoadPagEdge): void {
276        this.writeOutEdges = this.writeOutEdges ?? new Set();
277        this.writeOutEdges.add(e);
278        this.addOutgoingEdge(e);
279    }
280
281    public addThisInEdge(e: ThisPagEdge): void {
282        this.thisInEdges = this.thisInEdges ?? new Set();
283        this.thisInEdges.add(e);
284        this.addIncomingEdge(e);
285    }
286
287    public addThisOutEdge(e: ThisPagEdge): void {
288        this.thisOutEdges = this.thisOutEdges ?? new Set();
289        this.thisOutEdges.add(e);
290        this.addOutgoingEdge(e);
291    }
292
293    public getValue(): Value {
294        return this.value;
295    }
296
297    public getPointTo(): IPtsCollection<NodeID> {
298        return this.pointTo;
299    }
300
301    public addPointToElement(node: NodeID): void {
302        this.pointTo.insert(node);
303    }
304
305    public setPointTo(pts: IPtsCollection<NodeID>): void {
306        this.pointTo = pts;
307    }
308
309    public getOutEdges(): {
310        AddressEdge: PagEdgeSet;
311        CopyEdge: PagEdgeSet;
312        LoadEdge: PagEdgeSet;
313        WriteEdge: PagEdgeSet;
314    } {
315        return {
316            AddressEdge: this.addressOutEdges,
317            CopyEdge: this.copyOutEdges,
318            LoadEdge: this.loadOutEdges,
319            WriteEdge: this.writeOutEdges,
320        };
321    }
322
323    public getClonedFrom(): NodeID {
324        return this.clonedFrom;
325    }
326
327    public setClonedFrom(id: NodeID): void {
328        this.clonedFrom = id;
329    }
330
331    public getDotAttr(): string {
332        switch (this.getKind()) {
333            case PagNodeKind.HeapObj:
334            case PagNodeKind.Function:
335            case PagNodeKind.GlobalThis:
336                return 'shape=box3d';
337            case PagNodeKind.LocalVar:
338                return 'shape=box';
339            case PagNodeKind.RefVar:
340                return 'shape=component';
341            case PagNodeKind.Param:
342                return 'shape=box';
343            case PagNodeKind.ExportInfo:
344                return 'shape=tab,color=purple';
345            case PagNodeKind.ThisRef:
346                return 'shape=box,color=orange';
347            default:
348                return 'shape=box';
349        }
350    }
351
352    public getDotLabel(): string {
353        let label: string;
354        let param: ArkParameterRef;
355
356        label = PagNodeKind[this.getKind()];
357        label = label + ` ID: ${this.getID()} Ctx: ${this.cid}`;
358        if (this.basePt) {
359            label = label + ` base:{${this.basePt}}`;
360        }
361        label = label + ` pts:{${Array.from(this.pointTo).join(',')}}`;
362
363        if (this.getKind() === PagNodeKind.Param) {
364            param = this.value as ArkParameterRef;
365            label = label + `\nParam#${param.getIndex()} ${param.toString()}`;
366        }
367
368        if (this.getKind() === PagNodeKind.ThisRef) {
369            label = label + `\n${(this.value as ArkThisRef).toString()}`;
370        }
371
372        if (this.getKind() === PagNodeKind.Function) {
373            label = label + ` thisPt:{${(this as unknown as PagFuncNode).getThisPt()}}`;
374        }
375
376        if (this.stmt) {
377            label = label + `\n${this.stmt.toString()}`;
378            let method = this.stmt.getCfg()?.getDeclaringMethod().getSubSignature().toString();
379            if (method) {
380                label = label + '\n' + method;
381            }
382            label = label + ' ln: ' + this.stmt.getOriginPositionInfo().getLineNo();
383        } else if (this.value) {
384            label += `\n${this.value.toString()}`;
385        }
386
387        return label;
388    }
389}
390
391export class PagLocalNode extends PagNode {
392    private relatedDynamicCallSite?: Set<DynCallSite>;
393    private relatedUnknownCallSite?: Set<CallSite>;
394    private storageLinked: boolean = false;
395    private storageType?: StorageType;
396    private propertyName?: string;
397
398    private sdkParam: boolean = false;
399
400    constructor(id: NodeID, cid: ContextID | undefined = undefined, value: Local, stmt?: Stmt) {
401        super(id, cid, value, PagNodeKind.LocalVar, stmt);
402    }
403
404    public addRelatedDynCallSite(cs: DynCallSite): void {
405        this.relatedDynamicCallSite = this.relatedDynamicCallSite ?? new Set();
406
407        this.relatedDynamicCallSite.add(cs);
408    }
409
410    public getRelatedDynCallSites(): Set<DynCallSite> {
411        return this.relatedDynamicCallSite ?? new Set();
412    }
413
414    public addRelatedUnknownCallSite(cs: CallSite): void {
415        this.relatedUnknownCallSite = this.relatedUnknownCallSite ?? new Set();
416
417        this.relatedUnknownCallSite.add(cs);
418    }
419
420    public getRelatedUnknownCallSites(): Set<CallSite> {
421        return this.relatedUnknownCallSite ?? new Set();
422    }
423
424    public setStorageLink(storageType: StorageType, propertyName: string): void {
425        this.storageLinked = true;
426        this.storageType = storageType;
427        this.propertyName = propertyName;
428    }
429
430    public getStorage(): { StorageType: StorageType; PropertyName: string } {
431        return {
432            StorageType: this.storageType!,
433            PropertyName: this.propertyName!,
434        };
435    }
436
437    public isStorageLinked(): boolean {
438        return this.storageLinked;
439    }
440
441    public setSdkParam(): void {
442        this.sdkParam = true;
443    }
444
445    public isSdkParam(): boolean {
446        return this.sdkParam;
447    }
448}
449
450export class PagInstanceFieldNode extends PagNode {
451    constructor(id: NodeID, cid: ContextID | undefined = undefined, instanceFieldRef: ArkInstanceFieldRef, stmt?: Stmt) {
452        super(id, cid, instanceFieldRef, PagNodeKind.RefVar, stmt);
453    }
454}
455
456export class PagStaticFieldNode extends PagNode {
457    constructor(id: NodeID, cid: ContextID | undefined = undefined, staticFieldRef: ArkStaticFieldRef, stmt?: Stmt) {
458        super(id, cid, staticFieldRef, PagNodeKind.RefVar, stmt);
459    }
460}
461
462export class PagThisRefNode extends PagNode {
463    pointToNode: NodeID[];
464    constructor(id: NodeID, thisRef: ArkThisRef) {
465        super(id, DUMMY_PAG_NODE_ID, thisRef, PagNodeKind.ThisRef);
466        this.pointToNode = [];
467    }
468
469    public getThisPTNode(): NodeID[] {
470        return this.pointToNode;
471    }
472
473    public addPTNode(ptNode: NodeID): void {
474        this.pointToNode.push(ptNode);
475    }
476}
477
478export class PagArrayNode extends PagNode {
479    base: Value;
480
481    constructor(id: NodeID, cid: ContextID | undefined = undefined, expr: ArkArrayRef, stmt?: Stmt) {
482        super(id, cid, expr, PagNodeKind.LocalVar, stmt);
483        this.base = expr.getBase();
484    }
485}
486
487/**
488 * below is heapObj like Node
489 */
490export class PagNewExprNode extends PagNode {
491    // store the cloned field node
492    fieldNodes!: Map<string, NodeID>;
493
494    constructor(id: NodeID, cid: ContextID | undefined = undefined, expr: AbstractExpr, stmt?: Stmt) {
495        super(id, cid, expr, PagNodeKind.HeapObj, stmt);
496    }
497
498    public addFieldNode(fieldSignature: AbstractFieldRef, nodeID: NodeID): boolean {
499        if (!this.fieldNodes) {
500            this.fieldNodes = new Map();
501        }
502        if (this.fieldNodes.has(fieldSignature.getFieldSignature().toString())) {
503            return false;
504        }
505        this.fieldNodes.set(fieldSignature.getFieldSignature().toString(), nodeID);
506        return true;
507    }
508
509    public getFieldNode(fieldSignature: AbstractFieldRef): NodeID | undefined {
510        if (!this.fieldNodes) {
511            return undefined;
512        }
513        return this.fieldNodes.get(fieldSignature.getFieldSignature().toString());
514    }
515
516    public getFieldNodes(): Map<string, NodeID> | undefined {
517        if (!this.fieldNodes) {
518            return undefined;
519        }
520        return this.fieldNodes;
521    }
522}
523
524export class PagNewContainerExprNode extends PagNode {
525    // store the cloned array ref node
526    elementNode: NodeID | undefined;
527
528    constructor(id: NodeID, cid: ContextID | undefined = undefined, expr: Value, stmt?: Stmt) {
529        super(id, cid, expr, PagNodeKind.HeapObj, stmt);
530    }
531
532    public addElementNode(nodeID: NodeID): boolean {
533        if (!this.elementNode) {
534            this.elementNode = nodeID;
535        }
536
537        return true;
538    }
539
540    public getElementNode(): NodeID | undefined {
541        if (this.elementNode) {
542            return this.elementNode;
543        }
544        return undefined;
545    }
546}
547
548export class PagParamNode extends PagNode {
549    constructor(id: NodeID, cid: ContextID | undefined = undefined, r: ArkParameterRef, stmt?: Stmt) {
550        super(id, cid, r, PagNodeKind.Param, stmt);
551    }
552}
553
554export class PagFuncNode extends PagNode {
555    private methodSignature!: MethodSignature;
556    private thisPt!: NodeID;
557    private methodType!: BuiltApiType;
558    // for Function.bind, store the original call message and caller cid
559    private originCallSite!: CallSite;
560    private argsOffset: number = 0;
561    private originCid!: ContextID;
562    // TODO: may add obj interface
563
564    constructor(id: NodeID, cid: ContextID | undefined = undefined, r: Value, stmt?: Stmt, method?: MethodSignature, thisInstanceID?: NodeID) {
565        super(id, cid, r, PagNodeKind.Function, stmt);
566        if (method) {
567            this.methodSignature = method;
568            this.methodType = getBuiltInApiType(method);
569        }
570
571        if (thisInstanceID) {
572            this.thisPt = thisInstanceID;
573        }
574    }
575
576    public setMethod(method: MethodSignature): void {
577        this.methodSignature = method;
578        this.methodType = getBuiltInApiType(method);
579    }
580
581    public getMethod(): MethodSignature {
582        return this.methodSignature;
583    }
584
585    public setThisPt(thisPt: NodeID): void {
586        this.thisPt = thisPt;
587    }
588
589    public getThisPt(): NodeID {
590        return this.thisPt;
591    }
592
593    public setCS(callsite: CallSite): void {
594        this.originCallSite = callsite;
595    }
596
597    public getCS(): CallSite {
598        return this.originCallSite;
599    }
600
601    public setArgsOffset(offset: number): void {
602        this.argsOffset = offset;
603    }
604
605    public getArgsOffset(): number {
606        return this.argsOffset;
607    }
608
609    public getMethodType(): BuiltApiType {
610        return this.methodType;
611    }
612
613    public setOriginCid(cid: ContextID): void {
614        this.originCid = cid;
615    }
616
617    public getOriginCid(): ContextID {
618        return this.originCid;
619    }
620}
621
622/**
623 * almost same as PagNewExprNode, used only for globalThis and its field reference
624 */
625export class PagGlobalThisNode extends PagNode {
626    fieldNodes: Map<string, NodeID>;
627
628    constructor(id: NodeID, cid: ContextID | undefined = undefined, r: Value, stmt?: Stmt) {
629        super(id, cid, r, PagNodeKind.GlobalThis, stmt);
630        this.fieldNodes = new Map();
631    }
632
633    public addFieldNode(fieldSignature: AbstractFieldRef, nodeID: NodeID): boolean {
634        if (this.fieldNodes.has(fieldSignature.getFieldSignature().toString())) {
635            return false;
636        }
637        this.fieldNodes.set(fieldSignature.getFieldSignature().toString(), nodeID);
638        return true;
639    }
640
641    public getFieldNode(fieldSignature: AbstractFieldRef): NodeID | undefined {
642        return this.fieldNodes.get(fieldSignature.getFieldSignature().toString());
643    }
644
645    public getFieldNodes(): Map<string, NodeID> | undefined {
646        return this.fieldNodes;
647    }
648}
649
650export class Pag extends BaseExplicitGraph {
651    private cg!: CallGraph;
652    private contextValueToIdMap: Map<Value, Map<ContextID, NodeID>> = new Map();
653    private ExportInfoToIdMap?: Map<ExportInfo, NodeID>;
654    // contextBaseToIdMap will only be used in instance field
655    // Value: instance field base value, NodeID: abstract nodes
656    private contextBaseToIdMap: Map<Value, Map<ContextID, NodeID[]>> = new Map();
657    // for reanalyze, will return new addr edges
658    private stashAddrEdge: PagEdgeSet = new Set();
659    private addrEdge: PagEdgeSet = new Set();
660    private clonedNodeMap: Map<NodeID, Map<NodeID, NodeID>> = new Map();
661
662    public getCG(): CallGraph {
663        return this.cg;
664    }
665
666    /*
667     * Clone a PagNode with same cid/value/stmt,
668     * but different Node ID
669     */
670    public getOrClonePagNode(src: PagNode, basePt: NodeID): PagNode {
671        if (src.getBasePt() !== undefined) {
672            throw new Error('This is a cloned ref node, can not be cloned again');
673        }
674
675        let cloneSet = this.clonedNodeMap.get(src.getID());
676        if (!cloneSet) {
677            cloneSet = new Map<NodeID, NodeID>();
678            this.clonedNodeMap.set(src.getID(), cloneSet);
679        } else {
680            let nodeID = cloneSet.get(basePt);
681            if (nodeID) {
682                return this.getNode(nodeID) as PagNode;
683            }
684        }
685
686        // Not found
687        let cloneNode = this.addPagNode(src.getCid(), src.getValue(), src.getStmt(), false);
688        cloneNode.setClonedFrom(src.getID());
689        cloneSet.set(basePt, cloneNode.getID());
690        return cloneNode;
691    }
692
693    public getOrClonePagFieldNode(src: PagInstanceFieldNode, basePt: NodeID): PagInstanceFieldNode | undefined {
694        let baseNode = this.getNode(basePt);
695        if (baseNode instanceof PagNewExprNode || baseNode instanceof PagGlobalThisNode) {
696            // check if real field node has been created with basePT, using FieldSignature as key
697            let existedNode = baseNode.getFieldNode(src.getValue() as ArkInstanceFieldRef);
698            if (existedNode) {
699                return this.getNode(existedNode) as PagInstanceFieldNode;
700            }
701
702            let fieldNode = this.getOrClonePagNode(src, basePt);
703            baseNode.addFieldNode(src.getValue() as ArkInstanceFieldRef, fieldNode.getID());
704            fieldNode.setBasePt(basePt);
705            return fieldNode;
706        } else {
707            logger.error(`Error clone field node ${src.getValue()}`);
708            return undefined;
709        }
710    }
711
712    public getOrClonePagContainerFieldNode(basePt: NodeID, src?: PagArrayNode, base?: Local): PagInstanceFieldNode | undefined {
713        let baseNode = this.getNode(basePt) as PagNode;
714        if (baseNode instanceof PagNewContainerExprNode) {
715            // check if Array Ref real node has been created or not, if not: create a real Array Ref node
716            let existedNode = baseNode.getElementNode();
717            let fieldNode!: PagNode;
718            if (existedNode) {
719                return this.getNode(existedNode) as PagInstanceFieldNode;
720            }
721
722            if (src) {
723                fieldNode = this.getOrClonePagNode(src, basePt);
724            } else if (base) {
725                const containerFieldSignature = new FieldSignature(
726                    'field',
727                    new ClassSignature('container', new FileSignature('container', 'lib.es2015.collection.d.ts')),
728                    new UnclearReferenceType('')
729                );
730                fieldNode = this.getOrClonePagNode(
731                    // TODO: cid check
732                    this.addPagNode(0, new ArkInstanceFieldRef(base, containerFieldSignature)),
733                    basePt
734                );
735            }
736
737            baseNode.addElementNode(fieldNode.getID());
738            fieldNode.setBasePt(basePt);
739            return fieldNode;
740        } else if (baseNode instanceof PagNewExprNode) {
741            // In some cases, the value of a variable of array type may not be an explicit array object.
742            // For example, it could be a return value of a function (assuming that the call depth has
743            // exceeded the k-limit).
744            // In such situation, the `baseNode` will be a PagNewExprNode instead of a PagNewContainerExprNode,
745            // and a warning will be raised.
746            logger.warn(`[PTA]: Trying to clone an array from a PagNewExprNode instead of a PagNewContainerExprNode`);
747        } else {
748            throw new Error(`Error clone array field node ${baseNode.getValue()}`);
749        }
750        return undefined;
751    }
752
753    public getOrClonePagFuncNode(basePt: NodeID): PagFuncNode | undefined {
754        let baseNode = this.getNode(basePt) as PagNode;
755        if (baseNode instanceof PagFuncNode) {
756            let clonedFuncNode = this.getOrClonePagNode(baseNode, basePt) as PagFuncNode;
757            return clonedFuncNode;
758        } else {
759            logger.error(`Error clone func node ${baseNode.getValue()}`);
760            return undefined;
761        }
762    }
763
764    public addPagNode(cid: ContextID, value: PagNodeType, stmt?: Stmt, refresh: boolean = true): PagNode {
765        let id: NodeID = this.nodeNum + 1;
766        let pagNode: PagNode;
767
768        if (value instanceof Local) {
769            pagNode = this.handleLocalNode(id, cid, value, stmt);
770        } else if (value instanceof ArkInstanceFieldRef) {
771            pagNode = this.handleInstanceFieldNode(id, cid, value, stmt);
772        } else if (value instanceof ArkStaticFieldRef) {
773            pagNode = this.handleStaticFieldNode(id, cid, value, stmt);
774        } else if (value instanceof ArkArrayRef) {
775            pagNode = new PagArrayNode(id, cid, value, stmt);
776        } else if (value instanceof ArkNewExpr) {
777            pagNode = this.handleNewExprNode(id, cid, value, stmt);
778        } else if (value instanceof ArkNewArrayExpr) {
779            pagNode = new PagNewContainerExprNode(id, cid, value, stmt);
780        } else if (value instanceof ArkParameterRef) {
781            pagNode = new PagParamNode(id, cid, value, stmt);
782        } else if (value instanceof ArkThisRef) {
783            throw new Error('This Node needs to use addThisNode method');
784        } else {
785            throw new Error('unsupported Value type ' + value.getType().toString());
786        }
787
788        this.addNode(pagNode!);
789        this.addContextOrExportInfoMap(refresh, cid, id, value, pagNode, stmt);
790
791        return pagNode!;
792    }
793
794    private handleLocalNode(id: NodeID, cid: ContextID, value: Local, stmt?: Stmt): PagNode {
795        const valueType = value.getType();
796        if (valueType instanceof FunctionType && value.getDeclaringStmt() === null) {
797            return new PagFuncNode(id, cid, value, stmt, valueType.getMethodSignature());
798        } else if (value.getName() === GLOBAL_THIS_NAME && value.getDeclaringStmt() == null) {
799            return new PagGlobalThisNode(id, -1, value);
800        } else {
801            return new PagLocalNode(id, cid, value, stmt);
802        }
803    }
804
805    private handleInstanceFieldNode(id: NodeID, cid: ContextID, value: ArkInstanceFieldRef, stmt?: Stmt): PagNode {
806        return this.createFieldNode(id, cid, value, stmt);
807    }
808
809    private handleStaticFieldNode(id: NodeID, cid: ContextID, value: ArkStaticFieldRef, stmt?: Stmt): PagNode {
810        return this.createFieldNode(id, cid, value, stmt);
811    }
812
813    private createFieldNode(id: NodeID, cid: ContextID, value: any, stmt?: Stmt): PagNode {
814        if (value.getType() instanceof FunctionType) {
815            return new PagFuncNode(id, cid, value, stmt, (value.getType() as FunctionType).getMethodSignature());
816        } else {
817            return value instanceof ArkStaticFieldRef ? new PagStaticFieldNode(id, cid, value, stmt) : new PagInstanceFieldNode(id, cid, value, stmt);
818        }
819    }
820
821    private handleNewExprNode(id: NodeID, cid: ContextID, value: ArkNewExpr, stmt?: Stmt): PagNode {
822        const classSignature = value.getClassType().getClassSignature();
823        if (IsCollectionClass(classSignature)) {
824            return new PagNewContainerExprNode(id, cid, value, stmt);
825        } else {
826            return new PagNewExprNode(id, cid, value, stmt);
827        }
828    }
829
830    private addContextOrExportInfoMap(refresh: boolean, cid: ContextID, id: NodeID, value: PagNodeType, pagNode: PagNode, stmt?: Stmt): void {
831        if (!(value instanceof ExportInfo)) {
832            this.addContextMap(refresh, cid, id, value, stmt!, pagNode!);
833        } else {
834            this.addExportInfoMap(id, value);
835        }
836    }
837
838    private addExportInfoMap(id: NodeID, v: ExportInfo): void {
839        this.ExportInfoToIdMap = this.ExportInfoToIdMap ?? new Map();
840        this.ExportInfoToIdMap.set(v, id);
841    }
842
843    private addContextMap(refresh: boolean, cid: ContextID, id: NodeID, value: Value, stmt: Stmt, pagNode: PagNode): void {
844        if (!refresh) {
845            return;
846        }
847
848        let ctx2NdMap = this.contextValueToIdMap.get(value);
849        if (!ctx2NdMap) {
850            ctx2NdMap = new Map();
851            this.contextValueToIdMap.set(value, ctx2NdMap);
852        }
853        ctx2NdMap.set(cid, id);
854
855        if (!(value instanceof ArkInstanceFieldRef || value instanceof ArkArrayRef)) {
856            return;
857        }
858
859        let base = value.getBase();
860        //TODO: remove below once this Local is not uniq in %instInit is fix
861        if (base instanceof Local && base.getName() === 'this') {
862            stmt
863                ?.getCfg()
864                ?.getStmts()
865                .forEach(s => {
866                    if (s instanceof ArkAssignStmt && s.getLeftOp() instanceof Local && (s.getLeftOp() as Local).getName() === 'this') {
867                        base = s.getLeftOp() as Local;
868                        return;
869                    }
870                });
871        }
872        let ctxMap = this.contextBaseToIdMap.get(base);
873        if (ctxMap === undefined) {
874            ctxMap = new Map();
875            ctxMap.set(cid, [pagNode.getID()]);
876        } else {
877            let nodes = ctxMap.get(cid);
878            if (nodes === undefined) {
879                nodes = [pagNode.getID()];
880            } else {
881                nodes.push(pagNode.getID());
882            }
883            ctxMap.set(cid, nodes);
884        }
885        this.contextBaseToIdMap.set(base, ctxMap);
886    }
887
888    /*
889     * This node has no context info
890     * but point to node info
891     */
892    public addPagThisRefNode(value: ArkThisRef): PagNode {
893        let id: NodeID = this.nodeNum + 1;
894        let pagNode = new PagThisRefNode(id, value);
895        this.addNode(pagNode);
896
897        return pagNode;
898    }
899
900    public addPagThisLocalNode(ptNode: NodeID, value: Local): PagNode {
901        let id: NodeID = this.nodeNum + 1;
902        let pagNode = new PagLocalNode(id, ptNode, value);
903        this.addNode(pagNode);
904
905        return pagNode;
906    }
907
908    public getOrNewThisRefNode(thisRefNodeID: NodeID, value: ArkThisRef): PagNode {
909        if (thisRefNodeID !== -1) {
910            return this.getNode(thisRefNodeID) as PagNode;
911        }
912
913        let thisRefNode = this.addPagThisRefNode(value);
914        return thisRefNode;
915    }
916
917    public getOrNewThisLocalNode(cid: ContextID, ptNode: NodeID, value: Local, s?: Stmt): PagNode {
918        if (ptNode !== -1) {
919            return this.getNode(ptNode) as PagNode;
920        } else {
921            return this.getOrNewNode(cid, value, s);
922        }
923    }
924
925    public hasExportNode(v: ExportInfo): NodeID | undefined {
926        this.ExportInfoToIdMap = this.ExportInfoToIdMap ?? new Map();
927        return this.ExportInfoToIdMap.get(v);
928    }
929
930    public hasCtxNode(cid: ContextID, v: Value): NodeID | undefined {
931        let ctx2nd = this.contextValueToIdMap.get(v);
932        if (!ctx2nd) {
933            return undefined;
934        }
935
936        let ndId = ctx2nd.get(cid);
937        if (!ndId) {
938            return undefined;
939        }
940
941        return ndId;
942    }
943
944    public hasCtxRetNode(cid: ContextID, v: Value): NodeID | undefined {
945        let ctx2nd = this.contextValueToIdMap.get(v);
946        if (!ctx2nd) {
947            return undefined;
948        }
949
950        let ndId = ctx2nd.get(cid);
951        if (!ndId) {
952            return undefined;
953        }
954
955        return ndId;
956    }
957    public getOrNewNode(cid: ContextID, v: PagNodeType, s?: Stmt): PagNode {
958        let nodeId;
959        // Value
960        if (!(v instanceof ExportInfo)) {
961            nodeId = this.hasCtxNode(cid, v);
962        } else {
963            // ExportInfo
964            nodeId = this.hasExportNode(v);
965        }
966
967        if (nodeId !== undefined) {
968            return this.getNode(nodeId) as PagNode;
969        }
970
971        return this.addPagNode(cid, v, s);
972    }
973
974    public getNodesByValue(v: Value): Map<ContextID, NodeID> | undefined {
975        return this.contextValueToIdMap.get(v);
976    }
977
978    public getNodesByBaseValue(v: Value): Map<ContextID, NodeID[]> | undefined {
979        return this.contextBaseToIdMap.get(v);
980    }
981
982    public addPagEdge(src: PagNode, dst: PagNode, kind: PagEdgeKind, stmt?: Stmt): boolean {
983        // TODO: check if the edge already existing
984        let edge = new PagEdge(src, dst, kind, stmt);
985        if (this.ifEdgeExisting(edge)) {
986            return false;
987        }
988
989        switch (kind) {
990            case PagEdgeKind.Copy:
991            case PagEdgeKind.InterProceduralCopy:
992                src.addCopyOutEdge(edge);
993                dst.addCopyInEdge(edge);
994                if (src instanceof PagFuncNode || src instanceof PagGlobalThisNode || src instanceof PagNewExprNode || src instanceof PagNewContainerExprNode) {
995                    this.addrEdge.add(edge);
996                    this.stashAddrEdge.add(edge);
997                }
998                break;
999            case PagEdgeKind.Address:
1000                src.addAddressOutEdge(edge);
1001                dst.addAddressInEdge(edge);
1002                this.addrEdge.add(edge);
1003                this.stashAddrEdge.add(edge);
1004                break;
1005            case PagEdgeKind.Write:
1006                src.addWriteOutEdge(edge);
1007                dst.addWriteInEdge(edge);
1008                break;
1009            case PagEdgeKind.Load:
1010                src.addLoadOutEdge(edge);
1011                dst.addLoadInEdge(edge);
1012                break;
1013            case PagEdgeKind.This:
1014                src.addThisOutEdge(edge);
1015                dst.addThisInEdge(edge);
1016                break;
1017            default:
1018        }
1019        return true;
1020    }
1021
1022    public getAddrEdges(): PagEdgeSet {
1023        return this.stashAddrEdge;
1024    }
1025
1026    public resetAddrEdges(): void {
1027        this.stashAddrEdge.clear();
1028    }
1029
1030    public getGraphName(): string {
1031        return 'PAG';
1032    }
1033
1034    public dump(name: string): void {
1035        let printer = new GraphPrinter<this>(this);
1036        PrinterBuilder.dump(printer, name);
1037    }
1038}
1039
1040export type InterProceduralSrcType = Local;
1041export type IntraProceduralEdge = {
1042    src: Value;
1043    dst: Value;
1044    kind: PagEdgeKind;
1045    stmt: Stmt;
1046};
1047export type InterProceduralEdge = {
1048    src: InterProceduralSrcType;
1049    dst: Value;
1050    kind: PagEdgeKind;
1051};
1052
1053export class FuncPag {
1054    private internalEdges!: Set<IntraProceduralEdge>;
1055    private normalCallSites!: Set<CallSite>;
1056    private dynamicCallSites!: Set<DynCallSite>;
1057    private unknownCallSites!: Set<CallSite>;
1058
1059    public getInternalEdges(): Set<IntraProceduralEdge> | undefined {
1060        return this.internalEdges;
1061    }
1062
1063    public addNormalCallSite(cs: CallSite): void {
1064        this.normalCallSites = this.normalCallSites ?? new Set();
1065        this.normalCallSites.add(cs);
1066    }
1067
1068    public getNormalCallSites(): Set<CallSite> {
1069        this.normalCallSites = this.normalCallSites ?? new Set();
1070        return this.normalCallSites;
1071    }
1072
1073    public addDynamicCallSite(cs: DynCallSite): void {
1074        this.dynamicCallSites = this.dynamicCallSites ?? new Set();
1075        this.dynamicCallSites.add(cs);
1076    }
1077
1078    public getDynamicCallSites(): Set<DynCallSite> {
1079        this.dynamicCallSites = this.dynamicCallSites ?? new Set();
1080        return this.dynamicCallSites;
1081    }
1082
1083    public addUnknownCallSite(cs: CallSite): void {
1084        this.unknownCallSites = this.unknownCallSites ?? new Set();
1085        this.unknownCallSites.add(cs);
1086    }
1087
1088    public getUnknownCallSites(): Set<CallSite> {
1089        this.unknownCallSites = this.unknownCallSites ?? new Set();
1090        return this.unknownCallSites;
1091    }
1092
1093    public addInternalEdge(stmt: ArkAssignStmt, k: PagEdgeKind): boolean {
1094        this.internalEdges === undefined ? (this.internalEdges = new Set()) : undefined;
1095        let lhOp = stmt.getLeftOp();
1096        let rhOp = stmt.getRightOp();
1097
1098        if (rhOp instanceof Constant) {
1099            return false;
1100        }
1101
1102        let iEdge: IntraProceduralEdge = {
1103            src: rhOp,
1104            dst: lhOp,
1105            kind: k,
1106            stmt: stmt,
1107        };
1108        this.internalEdges.add(iEdge);
1109
1110        return true;
1111    }
1112}
1113
1114export class InterFuncPag {
1115    private interFuncEdges: Set<InterProceduralEdge>;
1116
1117    constructor() {
1118        this.interFuncEdges = new Set();
1119    }
1120
1121    public getInterProceduralEdges(): Set<InterProceduralEdge> {
1122        return this.interFuncEdges;
1123    }
1124
1125    public addToInterProceduralEdgeSet(e: InterProceduralEdge): void {
1126        this.interFuncEdges.add(e);
1127    }
1128}
1129