• 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 { CallGraph, CallGraphNode, CallGraphNodeKind, CallSite, DynCallSite, FuncID, ICallSite } from '../model/CallGraph';
17import { Scene } from '../../Scene';
18import { ArkAssignStmt, ArkInvokeStmt, ArkReturnStmt, Stmt } from '../../core/base/Stmt';
19import {
20    AbstractExpr,
21    AbstractInvokeExpr,
22    ArkInstanceInvokeExpr,
23    ArkNewArrayExpr,
24    ArkNewExpr,
25    ArkPtrInvokeExpr,
26    ArkStaticInvokeExpr,
27} from '../../core/base/Expr';
28import { AbstractFieldRef, ArkArrayRef, ArkInstanceFieldRef, ArkParameterRef, ArkStaticFieldRef, ArkThisRef } from '../../core/base/Ref';
29import { Value } from '../../core/base/Value';
30import { ArkMethod } from '../../core/model/ArkMethod';
31import Logger, { LOG_MODULE_TYPE } from '../../utils/logger';
32import { Local } from '../../core/base/Local';
33import { NodeID } from '../../core/graph/BaseExplicitGraph';
34import { ClassSignature } from '../../core/model/ArkSignature';
35import { ArkClass } from '../../core/model/ArkClass';
36import { ArrayType, ClassType, FunctionType, StringType } from '../../core/base/Type';
37import { Constant, NullConstant } from '../../core/base/Constant';
38import { PAGStat } from '../common/Statistics';
39import { ContextID, DUMMY_CID, KLimitedContextSensitive } from './Context';
40import {
41    FuncPag,
42    InterFuncPag,
43    InterProceduralEdge,
44    IntraProceduralEdge,
45    Pag,
46    PagEdgeKind,
47    PagFuncNode,
48    PagGlobalThisNode,
49    PagLocalNode,
50    PagNewContainerExprNode,
51    PagNode,
52    PagNodeType,
53    PagThisRefNode,
54    StorageLinkEdgeType,
55    StorageType,
56} from './Pag';
57import { GLOBAL_THIS_NAME } from '../../core/common/TSConst';
58import { IPtsCollection } from './PtsDS';
59import { UNKNOWN_FILE_NAME } from '../../core/common/Const';
60import { BuiltApiType, getBuiltInApiType } from './PTAUtils';
61import { PointerAnalysisConfig, PtaAnalysisScale } from './PointerAnalysisConfig';
62
63const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'PTA');
64
65export class CSFuncID {
66    public cid: ContextID;
67    public funcID: FuncID;
68
69    constructor(cid: ContextID, fid: FuncID) {
70        this.cid = cid;
71        this.funcID = fid;
72    }
73}
74
75export class PagBuilder {
76    private pag: Pag;
77    private cg: CallGraph;
78    private scale: PtaAnalysisScale;
79    private funcPags: Map<FuncID, FuncPag>;
80    private interFuncPags?: Map<FuncID, InterFuncPag>;
81    private handledFunc: Set<string> = new Set();
82    private ctx: KLimitedContextSensitive;
83    private scene: Scene;
84    private worklist: CSFuncID[] = [];
85    private pagStat: PAGStat;
86    // TODO: change string to hash value
87    private staticField2UniqInstanceMap: Map<string, Value> = new Map();
88    private instanceField2UniqInstanceMap: Map<string, Value> = new Map();
89    private cid2ThisRefPtMap: Map<ContextID, NodeID> = new Map();
90    private cid2ThisRefMap: Map<ContextID, NodeID> = new Map();
91    private cid2ThisLocalMap: Map<ContextID, NodeID> = new Map();
92    private sdkMethodReturnValueMap: Map<ArkMethod, Map<ContextID, ArkNewExpr>> = new Map();
93    // record the SDK API param, and create fake Values
94    private methodParamValueMap: Map<FuncID, Map<number, Value>> = new Map();
95    private fakeSdkMethodParamDeclaringStmt: Stmt = new ArkAssignStmt(new Local(''), new Local(''));
96    private funcHandledThisRound: Set<FuncID> = new Set();
97    private updatedNodesThisRound: Map<NodeID, IPtsCollection<NodeID>> = new Map();
98    private singletonFuncMap: Map<FuncID, boolean> = new Map();
99    private globalThisValue: Local = new Local(GLOBAL_THIS_NAME);
100    private globalThisPagNode?: PagGlobalThisNode;
101    private storagePropertyMap: Map<StorageType, Map<string, Local>> = new Map();
102    private externalScopeVariableMap: Map<Local, Local[]> = new Map();
103    private retriggerNodesList: Set<NodeID> = new Set();
104
105    constructor(p: Pag, cg: CallGraph, s: Scene, kLimit: number, scale: PtaAnalysisScale) {
106        this.pag = p;
107        this.cg = cg;
108        this.scale = scale;
109        this.funcPags = new Map<FuncID, FuncPag>();
110        this.ctx = new KLimitedContextSensitive(kLimit);
111        this.scene = s;
112        this.pagStat = new PAGStat();
113    }
114
115    private buildFuncPagAndAddToWorklist(cs: CSFuncID): CSFuncID {
116        if (this.worklist.includes(cs)) {
117            return cs;
118        }
119
120        this.buildFuncPag(cs.funcID);
121        if (this.isSingletonFunction(cs.funcID)) {
122            cs.cid = DUMMY_CID;
123        }
124
125        this.worklist.push(cs);
126        return cs;
127    }
128
129    private addToFuncHandledListThisRound(id: FuncID): void {
130        if (this.funcHandledThisRound.has(id)) {
131            return;
132        }
133
134        this.funcHandledThisRound.add(id);
135    }
136
137    public buildForEntries(funcIDs: FuncID[]): void {
138        this.worklist = [];
139        funcIDs.forEach(funcID => {
140            let cid = this.ctx.getNewContextID(funcID);
141            let csFuncID = new CSFuncID(cid, funcID);
142            this.buildFuncPagAndAddToWorklist(csFuncID);
143        });
144
145        this.handleReachable();
146        this.globalThisPagNode = this.getOrNewGlobalThisNode(-1) as PagGlobalThisNode;
147        this.pag.addPagEdge(this.globalThisPagNode, this.globalThisPagNode, PagEdgeKind.Copy);
148    }
149
150    public handleReachable(): boolean {
151        if (this.worklist.length === 0) {
152            return false;
153        }
154        this.funcHandledThisRound.clear();
155
156        while (this.worklist.length > 0) {
157            let csFunc = this.worklist.shift() as CSFuncID;
158            this.buildPagFromFuncPag(csFunc.funcID, csFunc.cid);
159            this.addToFuncHandledListThisRound(csFunc.funcID);
160        }
161
162        return true;
163    }
164
165    public build(): void {
166        for (let funcID of this.cg.getEntries()) {
167            let cid = this.ctx.getNewContextID(funcID);
168            let csFuncID = new CSFuncID(cid, funcID);
169            this.buildFuncPagAndAddToWorklist(csFuncID);
170
171            this.handleReachable();
172        }
173    }
174
175    public buildFuncPag(funcID: FuncID): boolean {
176        if (this.funcPags.has(funcID)) {
177            return false;
178        }
179
180        let arkMethod = this.cg.getArkMethodByFuncID(funcID);
181        if (arkMethod == null) {
182            return false;
183        }
184
185        let cfg = arkMethod.getCfg();
186        if (!cfg) {
187            this.buildSDKFuncPag(funcID);
188            return false;
189        }
190
191        logger.trace(`[build FuncPag] ${arkMethod.getSignature().toString()}`);
192
193        let fpag = new FuncPag();
194        for (let stmt of cfg.getStmts()) {
195            if (stmt instanceof ArkAssignStmt) {
196                this.processExternalScopeValue(stmt.getRightOp(), funcID);
197                // Add non-call edges
198                let kind = this.getEdgeKindForAssignStmt(stmt);
199                if (kind !== PagEdgeKind.Unknown) {
200                    fpag.addInternalEdge(stmt, kind);
201                    continue;
202                }
203
204                // handle call
205                this.buildInvokeExprInAssignStmt(stmt, fpag);
206            } else if (stmt instanceof ArkInvokeStmt && this.scale === PtaAnalysisScale.WholeProgram) {
207                this.processExternalScopeValue(stmt.getInvokeExpr(), funcID);
208                this.buildInvokeExprInInvokeStmt(stmt, fpag);
209            } else {
210                // TODO: need handle other type of stmt?
211            }
212        }
213
214        this.funcPags.set(funcID, fpag);
215        this.pagStat.numTotalFunction++;
216        return true;
217    }
218
219    private buildInvokeExprInAssignStmt(stmt: ArkAssignStmt, fpag: FuncPag): void {
220        let ivkExpr = stmt.getInvokeExpr()!;
221        if (ivkExpr instanceof ArkStaticInvokeExpr) {
222            let callSites = this.cg.getCallSiteByStmt(stmt);
223            if (callSites.length !== 0) {
224                // direct call is already existing in CG
225                // TODO: API Invoke stmt has anonymous method param, how to add these param into callee
226                callSites.forEach(cs => {
227                    this.addFuncPagCallSite(fpag, cs, ivkExpr);
228                });
229            } else {
230                throw new Error('Can not find static callsite');
231            }
232        } else if (ivkExpr instanceof ArkInstanceInvokeExpr || ivkExpr instanceof ArkPtrInvokeExpr) {
233            let ptcs = this.cg.getDynCallsiteByStmt(stmt);
234            if (ptcs) {
235                this.addToDynamicCallSite(fpag, ptcs);
236            }
237        }
238    }
239
240    private addFuncPagCallSite(fpag: FuncPag, cs: CallSite, ivkExpr: AbstractInvokeExpr): void {
241        if (ivkExpr instanceof ArkStaticInvokeExpr) {
242            if (ivkExpr.getMethodSignature().getDeclaringClassSignature().getDeclaringFileSignature().getFileName() === UNKNOWN_FILE_NAME) {
243                fpag.addUnknownCallSite(cs);
244            } else {
245                fpag.addNormalCallSite(cs);
246            }
247        }
248    }
249
250    private buildInvokeExprInInvokeStmt(stmt: ArkInvokeStmt, fpag: FuncPag): void {
251        // TODO: discuss if we need a invokeStmt
252
253        let callSites = this.cg.getCallSiteByStmt(stmt);
254        if (callSites.length !== 0) {
255            // direct call or constructor call is already existing in CG
256            // TODO: some ptr invoke stmt is recognized as Static invoke in tests/resources/callgraph/funPtrTest1/fnPtrTest4.ts
257            // TODO: instance invoke(ptr invoke)
258            callSites.forEach(cs => {
259                if (this.cg.isUnknownMethod(cs.calleeFuncID)) {
260                    fpag.addUnknownCallSite(cs);
261                } else {
262                    fpag.addNormalCallSite(cs);
263                }
264            });
265
266            return;
267        }
268
269        let dycs = this.cg.getDynCallsiteByStmt(stmt);
270        if (dycs) {
271            this.addToDynamicCallSite(fpag, dycs);
272        } else {
273            throw new Error('Can not find callsite by stmt');
274        }
275    }
276
277    private processExternalScopeValue(value: Value, funcID: FuncID): void {
278        let dummyMainFuncID = this.cg.getDummyMainFuncID();
279        if (dummyMainFuncID && funcID === dummyMainFuncID) {
280            return;
281        }
282
283        if (value instanceof Local) {
284            this.handleValueFromExternalScope(value, funcID);
285        } else if (value instanceof ArkInstanceInvokeExpr) {
286            value.getUses().forEach(v => {
287                this.handleValueFromExternalScope(v, funcID);
288            });
289        }
290    }
291
292    /**
293     * will not create real funcPag, only create param values
294     */
295    private buildSDKFuncPag(funcID: FuncID): void {
296        // check if SDK method
297        let cgNode = this.cg.getNode(funcID) as CallGraphNode;
298        if (!cgNode.isSdkMethod()) {
299            return;
300        }
301        let paramArr: Map<number, Value> = this.createDummyParamValue(funcID);
302
303        this.methodParamValueMap.set(funcID, paramArr);
304    }
305
306    private createDummyParamValue(funcID: FuncID, type: number = 1): Map<number, Value> {
307        let arkMethod = this.cg.getArkMethodByFuncID(funcID);
308        if (!arkMethod) {
309            return new Map();
310        }
311
312        let args = arkMethod.getParameters();
313        if (!args) {
314            return new Map();
315        }
316
317        let paramArr: Map<number, Value> = new Map();
318
319        if (type === 0) {
320            // heapObj
321            args.forEach((arg, index) => {
322                let paramType = arg.getType();
323                if (!(paramType instanceof ClassType)) {
324                    return;
325                    // TODO: support more type
326                }
327
328                let argInstance: ArkNewExpr = new ArkNewExpr(paramType);
329                paramArr.set(index, argInstance);
330            });
331        } else if (type === 1) {
332            // Local
333            args.forEach((arg, index) => {
334                let argInstance: Local = new Local(arg.getName(), arg.getType());
335                argInstance.setDeclaringStmt(this.fakeSdkMethodParamDeclaringStmt);
336                paramArr.set(index, argInstance);
337            });
338        }
339
340        return paramArr;
341    }
342
343    private createDummyParamPagNodes(value: Map<number, Value>, funcID: FuncID): Map<number, NodeID> {
344        let paramPagNodes: Map<number, NodeID> = new Map();
345        let method = this.cg.getArkMethodByFuncID(funcID)!;
346        if (!method || !method.getCfg()) {
347            return paramPagNodes;
348        }
349
350        value.forEach((v, index) => {
351            let paramArkExprNode = this.pag.getOrNewNode(DUMMY_CID, v, this.fakeSdkMethodParamDeclaringStmt);
352            paramPagNodes.set(index, paramArkExprNode.getID());
353        });
354
355        return paramPagNodes;
356    }
357
358    public buildPagFromFuncPag(funcID: FuncID, cid: ContextID): void {
359        let funcPag = this.funcPags.get(funcID);
360        if (funcPag === undefined) {
361            return;
362        }
363        if (this.handledFunc.has(`${cid}-${funcID}`)) {
364            return;
365        }
366
367        this.addEdgesFromFuncPag(funcPag, cid, funcID);
368        let interFuncPag = this.interFuncPags?.get(funcID);
369        if (interFuncPag) {
370            this.addEdgesFromInterFuncPag(interFuncPag, cid);
371        }
372
373        this.addCallsEdgesFromFuncPag(funcPag, cid);
374        this.addDynamicCallSite(funcPag, funcID, cid);
375        this.addUnknownCallSite(funcPag, funcID);
376        this.handledFunc.add(`${cid}-${funcID}`);
377    }
378
379    /// Add Pag Nodes and Edges in function
380    public addEdgesFromFuncPag(funcPag: FuncPag, cid: ContextID, funcID: FuncID): boolean {
381        let inEdges = funcPag.getInternalEdges();
382        if (inEdges === undefined) {
383            return false;
384        }
385        let paramNodes;
386        let paramRefIndex = 0;
387        if (this.scale === PtaAnalysisScale.MethodLevel) {
388            paramNodes = this.createDummyParamPagNodes(this.createDummyParamValue(funcID, 0), funcID);
389        }
390
391        for (let e of inEdges) {
392            let srcPagNode = this.getOrNewPagNode(cid, e.src, e.stmt);
393            let dstPagNode = this.getOrNewPagNode(cid, e.dst, e.stmt);
394
395            this.pag.addPagEdge(srcPagNode, dstPagNode, e.kind, e.stmt);
396
397            // Take place of the real stmt for return
398            if (dstPagNode.getStmt() instanceof ArkReturnStmt) {
399                dstPagNode.setStmt(e.stmt);
400            }
401
402            // for demand-driven analysis, add fake parameter heapObj nodes
403            if (e.src instanceof ArkParameterRef && this.scale === PtaAnalysisScale.MethodLevel) {
404                let paramObjNodeID = paramNodes?.get(paramRefIndex++);
405                if (!paramObjNodeID) {
406                    continue;
407                }
408
409                this.pag.addPagEdge(this.pag.getNode(paramObjNodeID) as PagNode, srcPagNode, PagEdgeKind.Address);
410            }
411        }
412
413        return true;
414    }
415
416    /// add Copy edges interprocedural
417    public addCallsEdgesFromFuncPag(funcPag: FuncPag, cid: ContextID): boolean {
418        for (let cs of funcPag.getNormalCallSites()) {
419            let ivkExpr = cs.callStmt.getInvokeExpr();
420            let calleeCid = this.ctx.getOrNewContext(cid, cs.calleeFuncID, true);
421
422            let calleeCGNode = this.cg.getNode(cs.calleeFuncID) as CallGraphNode;
423
424            if (this.scale === PtaAnalysisScale.MethodLevel) {
425                this.addStaticPagCallReturnEdge(cs, cid, calleeCid);
426            }
427
428            // process the Storage API(Static)
429            if (!this.processStorage(cs, calleeCGNode, cid)) {
430                // If not Storage API, process normal edge
431                this.addStaticPagCallEdge(cs, cid, calleeCid);
432            }
433
434            // Add edge to thisRef for special calls
435            if (calleeCGNode.getKind() === CallGraphNodeKind.constructor || calleeCGNode.getKind() === CallGraphNodeKind.intrinsic) {
436                let callee = this.scene.getMethod(this.cg.getMethodByFuncID(cs.calleeFuncID)!)!;
437                if (ivkExpr instanceof ArkInstanceInvokeExpr) {
438                    let baseNode = this.getOrNewPagNode(cid, ivkExpr.getBase());
439                    let baseNodeID = baseNode.getID();
440
441                    this.addThisRefCallEdge(baseNodeID, cid, ivkExpr.getBase(), callee, calleeCid, cs.callerFuncID);
442                } else {
443                    logger.error(`constructor or intrinsic func is static ${ivkExpr!.toString()}`);
444                }
445            }
446        }
447
448        return true;
449    }
450
451    public addDynamicCallSite(funcPag: FuncPag, funcID: FuncID, cid: ContextID): void {
452        // add dyn callsite in funcpag to base node
453        for (let cs of funcPag.getDynamicCallSites()) {
454            let invokeExpr: AbstractInvokeExpr = cs.callStmt.getInvokeExpr()!;
455            let base!: Local;
456            if (invokeExpr instanceof ArkInstanceInvokeExpr) {
457                base = invokeExpr.getBase();
458            } else if (invokeExpr instanceof ArkPtrInvokeExpr && invokeExpr.getFuncPtrLocal() instanceof Local) {
459                base = invokeExpr.getFuncPtrLocal() as Local;
460            } else if (invokeExpr instanceof ArkPtrInvokeExpr && invokeExpr.getFuncPtrLocal() instanceof AbstractFieldRef) {
461                /**
462                 * TODO: wait for IR change
463                 * throw error in ptrInvoke with field ref
464                 * this.field() // field is lambda expression
465                 */
466                continue;
467            }
468            // TODO: check base under different cid
469            let baseNodeIDs = this.pag.getNodesByValue(base);
470            if (!baseNodeIDs) {
471                // bind the call site to export base
472                let interProceduralLocal = this.getSourceValueFromExternalScope(base, funcID);
473                if (interProceduralLocal) {
474                    baseNodeIDs = this.pag.getNodesByValue(interProceduralLocal);
475                }
476            }
477
478            if (!baseNodeIDs) {
479                logger.warn(`[build dynamic call site] can not handle call site with base ${base.toString()}`);
480                continue;
481            }
482
483            for (let nodeID of baseNodeIDs!.values()) {
484                let node = this.pag.getNode(nodeID);
485                if (!(node instanceof PagLocalNode)) {
486                    continue;
487                }
488
489                node.addRelatedDynCallSite(cs);
490            }
491
492            if (cs.callStmt instanceof ArkAssignStmt) {
493                this.getOrNewPagNode(cid, cs.callStmt.getLeftOp(), cs.callStmt);
494            }
495        }
496    }
497
498    public addUnknownCallSite(funcPag: FuncPag, funcID: FuncID): void {
499        let method = this.cg.getArkMethodByFuncID(funcID);
500
501        if (!method) {
502            throw new Error(`can not find ArkMethod by FuncID ${funcID}`);
503        }
504
505        let locals = method.getBody()?.getLocals()!;
506
507        funcPag.getUnknownCallSites().forEach(unknownCallSite => {
508            let calleeName = unknownCallSite.callStmt.getInvokeExpr()?.getMethodSignature().getMethodSubSignature().getMethodName()!;
509
510            let base = locals.get(calleeName);
511            if (!base) {
512                return;
513            }
514            let baseNodeIDs = this.pag.getNodesByValue(base);
515            if (!baseNodeIDs) {
516                logger.warn(`[build dynamic call site] can not handle call site with base ${base.toString()}`);
517                return;
518            }
519            for (let nodeID of baseNodeIDs!.values()) {
520                let node = this.pag.getNode(nodeID);
521                if (!(node instanceof PagLocalNode)) {
522                    continue;
523                }
524
525                node.addRelatedUnknownCallSite(unknownCallSite);
526            }
527        });
528    }
529
530    public addDynamicCallEdge(cs: ICallSite, baseClassPTNode: NodeID, cid: ContextID): NodeID[] {
531        let srcNodes: NodeID[] = [];
532        let ivkExpr = cs.callStmt.getInvokeExpr();
533
534        let ptNode = this.pag.getNode(baseClassPTNode) as PagNode;
535        let value = (ptNode as PagNode).getValue();
536        let callees: ArkMethod[] = this.getDynamicCallee(ptNode, value, ivkExpr!, cs);
537
538        for (let callee of callees) {
539            if (!callee) {
540                continue;
541            }
542            // get caller and callee CG node, add param and return value PAG edge
543            let dstCGNode = this.cg.getCallGraphNodeByMethod(callee.getSignature());
544            let callerNode = this.cg.getNode(cs.callerFuncID) as CallGraphNode;
545            if (!callerNode) {
546                throw new Error('Can not get caller method node');
547            }
548            // update call graph
549            // TODO: movo to cgbuilder
550
551            this.cg.addDynamicCallEdge(callerNode.getID(), dstCGNode.getID(), cs.callStmt);
552
553            if (this.cg.detectReachable(dstCGNode.getID(), callerNode.getID())) {
554                return srcNodes;
555            }
556
557            let calleeCid = this.ctx.getOrNewContext(cid, dstCGNode.getID(), true);
558            let staticCS = new CallSite(cs.callStmt, cs.args, dstCGNode.getID(), cs.callerFuncID);
559
560            if (this.scale === PtaAnalysisScale.MethodLevel) {
561                srcNodes.push(...this.addStaticPagCallReturnEdge(staticCS, baseClassPTNode, calleeCid));
562                continue;
563            }
564
565            if (getBuiltInApiType(ivkExpr?.getMethodSignature()!) === BuiltApiType.NotBuiltIn) {
566                srcNodes.push(...this.processNormalMethodPagCallEdge(staticCS, cid, calleeCid, baseClassPTNode));
567            } else {
568                // special SDK call: Container API, Function API
569                srcNodes.push(...this.processBuiltInMethodPagCallEdge(staticCS, cid, calleeCid, baseClassPTNode));
570            }
571        }
572
573        return srcNodes;
574    }
575
576    /**
577     * all possible callee methods of a dynamic call site
578     * handle both PtrInvokeExpr and InstanceInvokeExpr
579     */
580    private getDynamicCallee(ptNode: PagNode, value: Value, ivkExpr: AbstractInvokeExpr, cs: ICallSite): ArkMethod[] {
581        let callee: ArkMethod[] = [];
582
583        if (ptNode instanceof PagFuncNode) {
584            // function ptr invoke
585            let tempCallee = this.scene.getMethod(ptNode.getMethod());
586
587            if (!callee) {
588                return callee;
589            }
590            callee.push(tempCallee!);
591            return callee;
592        }
593        //else branch
594        let calleeName = ivkExpr!.getMethodSignature().getMethodSubSignature().getMethodName();
595        // instance method invoke
596        if (!(value instanceof ArkNewExpr || value instanceof ArkNewArrayExpr)) {
597            return callee;
598        }
599        let tempCallee;
600
601        // try to get callee by MethodSignature
602        if (value instanceof ArkNewExpr) {
603            // get class signature
604            let clsSig = (value.getType() as ClassType).getClassSignature() as ClassSignature;
605            let cls;
606
607            cls = this.scene.getClass(clsSig) as ArkClass;
608
609            while (!tempCallee && cls) {
610                tempCallee = cls.getMethodWithName(calleeName);
611                cls = cls.getSuperClass();
612            }
613
614            if (!tempCallee) {
615                tempCallee = this.scene.getMethod(ivkExpr!.getMethodSignature());
616            }
617        }
618
619        if (!tempCallee && cs.args) {
620            // while pts has {o_1, o_2} and invoke expr represents a method that only {o_1} has
621            // return empty node when {o_2} come in
622            // try to get callee by anonymous method in param
623            for (let arg of cs.args) {
624                // TODO: anonymous method param and return value pointer pass
625                let argType = arg.getType();
626                if (argType instanceof FunctionType) {
627                    callee.push(this.scene.getMethod(argType.getMethodSignature())!);
628                }
629            }
630        } else if (tempCallee) {
631            callee.push(tempCallee);
632        }
633
634        return callee;
635    }
636
637    public processNormalMethodPagCallEdge(staticCS: CallSite, cid: ContextID, calleeCid: ContextID, baseClassPTNode: NodeID): NodeID[] {
638        let srcNodes: NodeID[] = [];
639        let ivkExpr = staticCS.callStmt.getInvokeExpr()!;
640        let ptNode = this.pag.getNode(baseClassPTNode) as PagNode;
641        let dstCGNode = this.cg.getNode(staticCS.calleeFuncID) as CallGraphNode;
642        let callee = this.cg.getArkMethodByFuncID(staticCS.calleeFuncID);
643        // Dynamic call, Ptr call, normal SDK call
644        srcNodes.push(...this.addStaticPagCallEdge(staticCS, cid, calleeCid, ptNode));
645
646        // Pass base's pts to callee's this pointer
647        if (!dstCGNode.isSdkMethod() && ivkExpr instanceof ArkInstanceInvokeExpr) {
648            let srcBaseNode = this.addThisRefCallEdge(baseClassPTNode, cid, ivkExpr.getBase(), callee!, calleeCid, staticCS.callerFuncID);
649
650            if (srcBaseNode !== -1) {
651                srcNodes.push(srcBaseNode);
652            }
653        } else if (!dstCGNode.isSdkMethod() && ivkExpr instanceof ArkPtrInvokeExpr) {
654            let originCS = (ptNode as PagFuncNode).getCS();
655            if (!originCS) {
656                return srcNodes;
657            }
658
659            let thisValue = originCS.args![0];
660
661            if (!(thisValue instanceof Local)) {
662                return srcNodes;
663            }
664            this.addThisRefCallEdge(baseClassPTNode, (ptNode as PagFuncNode).getOriginCid(), thisValue, callee!, calleeCid, staticCS.callerFuncID);
665        }
666
667        return srcNodes;
668    }
669
670    /**
671     * include container API, Function API
672     */
673    public processBuiltInMethodPagCallEdge(staticCS: CallSite, cid: ContextID, calleeCid: ContextID, baseClassPTNode: NodeID): NodeID[] {
674        let srcNodes: NodeID[] = [];
675        let ivkExpr = staticCS.callStmt.getInvokeExpr()!;
676        let callee = this.scene.getMethod(ivkExpr.getMethodSignature());
677        let realCallee = this.cg.getArkMethodByFuncID(staticCS.calleeFuncID);
678        if (!callee) {
679            return srcNodes;
680        }
681
682        let builtInType = getBuiltInApiType(callee.getSignature());
683
684        if (builtInType === BuiltApiType.NotBuiltIn || !realCallee) {
685            return srcNodes;
686        }
687
688        switch (builtInType) {
689            case BuiltApiType.SetAdd:
690            case BuiltApiType.MapSet:
691                this.processContainerPagCallEdge(staticCS, cid, baseClassPTNode, builtInType);
692                break;
693
694            case BuiltApiType.FunctionCall:
695                /**
696                 * set this and param
697                 * function.call(thisArg, arg1, arg2, ...)
698                 */
699                this.handleFunctionCall(staticCS, cid, calleeCid, realCallee, srcNodes, baseClassPTNode);
700                break;
701
702            case BuiltApiType.FunctionApply:
703                /**
704                 * set this, resolve array param
705                 * function.apply(thisArg, [argsArray])
706                 */
707                this.handleFunctionApply(staticCS, cid, calleeCid, realCallee, srcNodes, baseClassPTNode);
708                break;
709
710            case BuiltApiType.FunctionBind:
711                /**
712                 * clone the function node and add the this pointer, origin callsite, args offset to it
713                 * let f = function.bind(thisArg, arg1, arg2, ...)
714                 * f();
715                 */
716                this.handleFunctionBind(staticCS, cid, baseClassPTNode, srcNodes);
717                break;
718            default:
719        }
720
721        return srcNodes;
722    }
723
724    private processContainerPagCallEdge(cs: CallSite, cid: ContextID, baseClassPTNode: NodeID, type: BuiltApiType): NodeID[] {
725        let srcNodes: NodeID[] = [];
726        let calleeNode = this.cg.getNode(cs.calleeFuncID) as CallGraphNode;
727        let calleeMethod: ArkMethod | null = this.scene.getMethod(calleeNode.getMethod());
728        let ptNode = this.pag.getNode(baseClassPTNode) as PagNode;
729
730        if (!calleeMethod || !(ptNode instanceof PagNewContainerExprNode)) {
731            return srcNodes;
732        }
733
734        let containerValue = (cs.callStmt.getInvokeExpr() as ArkInstanceInvokeExpr).getBase();
735
736        const containerValueProcess = (argIndex: number): void => {
737            let srcNode = this.pag.getOrNewNode(cid, cs.args![argIndex], cs.callStmt);
738            let realContainerFieldPagNode = this.pag.getOrClonePagContainerFieldNode(baseClassPTNode, undefined, containerValue);
739
740            if (realContainerFieldPagNode) {
741                // In some cases, the value of a variable of array type may not be an explicit array object,
742                // and the value of `realContainerFieldPagNode` will be undefined.
743                this.pag.addPagEdge(srcNode, realContainerFieldPagNode, PagEdgeKind.Copy, cs.callStmt);
744                srcNodes.push(srcNode.getID());
745            }
746        };
747
748        if (type === BuiltApiType.SetAdd) {
749            containerValueProcess(0);
750        } else if (type === BuiltApiType.MapSet) {
751            containerValueProcess(1);
752        }
753
754        return srcNodes;
755    }
756
757    private handleFunctionCall(
758        staticCS: CallSite,
759        cid: ContextID,
760        calleeCid: ContextID,
761        realCallee: ArkMethod,
762        srcNodes: NodeID[],
763        baseClassPTNode: NodeID
764    ): void {
765        this.buildFuncPagAndAddToWorklist(new CSFuncID(calleeCid, staticCS.calleeFuncID));
766        srcNodes.push(...this.addCallParamPagEdge(realCallee, staticCS.args!, staticCS.callStmt, cid, calleeCid, 1));
767        this.addThisEdge(staticCS, cid, realCallee, srcNodes, baseClassPTNode, calleeCid);
768    }
769
770    private handleFunctionApply(
771        staticCS: CallSite,
772        cid: ContextID,
773        calleeCid: ContextID,
774        realCallee: ArkMethod,
775        srcNodes: NodeID[],
776        baseClassPTNode: NodeID
777    ): void {
778        this.buildFuncPagAndAddToWorklist(new CSFuncID(calleeCid, staticCS.calleeFuncID));
779        let callerMethod = this.cg.getArkMethodByFuncID(staticCS.callerFuncID);
780        if (!callerMethod) {
781            throw new Error('Cannot get caller method');
782        }
783        let argsRealValues = this.transferArrayValues(callerMethod, staticCS.args![1]);
784        srcNodes.push(...this.addCallParamPagEdge(realCallee, argsRealValues, staticCS.callStmt, cid, calleeCid, 0));
785        this.addThisEdge(staticCS, cid, realCallee, srcNodes, baseClassPTNode, calleeCid);
786    }
787
788    private handleFunctionBind(staticCS: CallSite, cid: ContextID, baseClassPTNode: NodeID, srcNodes: NodeID[]): void {
789        let srcNode = this.pag.getOrClonePagFuncNode(baseClassPTNode);
790        if (!srcNode) {
791            return;
792        }
793        this.setFunctionThisPt(staticCS, srcNode, cid);
794
795        let dstNode = this.getOrNewPagNode(cid, (staticCS.callStmt as ArkAssignStmt).getLeftOp() as Local);
796        this.pag.addPagEdge(srcNode, dstNode, PagEdgeKind.Copy, staticCS.callStmt);
797        srcNodes.push(srcNode.getID());
798
799        srcNode.setCS(staticCS);
800        srcNode.setArgsOffset(1);
801        srcNode.setOriginCid(cid);
802    }
803
804    private addThisEdge(staticCS: CallSite, cid: ContextID, realCallee: ArkMethod, srcNodes: NodeID[], baseClassPTNode: NodeID, calleeCid: ContextID): void {
805        if (!(staticCS.args![0] instanceof NullConstant) && !realCallee.isStatic()) {
806            let srcNodeID = this.addThisRefCallEdge(baseClassPTNode, cid, staticCS.args![0] as Local, realCallee, calleeCid, staticCS.callerFuncID);
807
808            if (srcNodeID !== -1) {
809                srcNodes.push(srcNodeID);
810            }
811        }
812    }
813
814    private setFunctionThisPt(staticCS: CallSite, srcNode: PagFuncNode, cid: ContextID): void {
815        let thisLocal = staticCS.args![0];
816        if (!(thisLocal instanceof Local)) {
817            return;
818        }
819
820        let thisInstanceLocal = this.getRealThisLocal(thisLocal, staticCS.callerFuncID);
821        let baseThisNode = this.pag.getOrNewNode(cid, thisInstanceLocal);
822
823        for (let pt of baseThisNode.getPointTo()) {
824            srcNode.setThisPt(pt);
825        }
826    }
827
828    public handleUnkownDynamicCall(cs: DynCallSite, cid: ContextID): NodeID[] {
829        let srcNodes: NodeID[] = [];
830        let callerNode = this.cg.getNode(cs.callerFuncID) as CallGraphNode;
831        let ivkExpr = cs.callStmt.getInvokeExpr() as AbstractInvokeExpr;
832        logger.warn('Handling unknown dyn call site : \n  ' + callerNode.getMethod().toString() + '\n  --> ' + ivkExpr.toString() + '\n  CID: ' + cid);
833
834        let callees: ArkMethod[] = [];
835        let callee: ArkMethod | null = null;
836        callee = this.scene.getMethod(ivkExpr.getMethodSignature());
837        if (!callee) {
838            cs.args?.forEach(arg => {
839                if (!(arg.getType() instanceof FunctionType)) {
840                    return;
841                }
842
843                callee = this.scene.getMethod((arg.getType() as FunctionType).getMethodSignature());
844                if (callee) {
845                    callees.push(callee);
846                }
847            });
848        } else {
849            callees.push(callee);
850        }
851
852        if (callees.length === 0) {
853            return srcNodes;
854        }
855
856        callees.forEach(callee => {
857            let dstCGNode = this.cg.getCallGraphNodeByMethod(callee.getSignature());
858            if (!callerNode) {
859                throw new Error('Can not get caller method node');
860            }
861
862            if (this.processStorage(cs, dstCGNode, cid)) {
863                if (ivkExpr.getArgs().length !== 0) {
864                    // for AppStorage.set() instance invoke, add obj to reanalyze list
865                    let argsNode = this.pag.getOrNewNode(cid, cs.args![0]);
866                    srcNodes.push(argsNode.getID());
867                }
868            }
869
870            logger.warn(`\tAdd call edge of unknown call ${callee.getSignature().toString()}`);
871            this.cg.addDynamicCallEdge(callerNode.getID(), dstCGNode.getID(), cs.callStmt);
872            if (!this.cg.detectReachable(dstCGNode.getID(), callerNode.getID())) {
873                let calleeCid = this.ctx.getOrNewContext(cid, dstCGNode.getID(), true);
874                let staticCS = new CallSite(cs.callStmt, cs.args, dstCGNode.getID(), cs.callerFuncID);
875                let staticSrcNodes = this.addStaticPagCallEdge(staticCS, cid, calleeCid);
876                srcNodes.push(...staticSrcNodes);
877            }
878        });
879        return srcNodes;
880    }
881
882    public handleUnprocessedCallSites(processedCallSites: Set<DynCallSite>): NodeID[] {
883        let reAnalyzeNodes: NodeID[] = [];
884        for (let funcID of this.funcHandledThisRound) {
885            let funcPag = this.funcPags.get(funcID);
886            if (!funcPag) {
887                logger.error(`can not find funcPag of handled func ${funcID}`);
888                continue;
889            }
890            let callSites = funcPag.getDynamicCallSites();
891
892            const diffCallSites = new Set(Array.from(callSites).filter(item => !processedCallSites.has(item)));
893            diffCallSites.forEach(cs => {
894                let ivkExpr = cs.callStmt.getInvokeExpr();
895                if (!(ivkExpr instanceof ArkInstanceInvokeExpr)) {
896                    return;
897                }
898                // Get local of base class
899                let base = ivkExpr.getBase();
900                // TODO: remove this after multiple this local fixed
901                base = this.getRealThisLocal(base, cs.callerFuncID);
902                // Get PAG nodes for this base's local
903                let ctx2NdMap = this.pag.getNodesByValue(base);
904                if (!ctx2NdMap) {
905                    return;
906                }
907
908                for (let [cid] of ctx2NdMap.entries()) {
909                    reAnalyzeNodes.push(...this.handleUnkownDynamicCall(cs, cid));
910                }
911            });
912        }
913
914        return reAnalyzeNodes;
915    }
916
917    private addThisRefCallEdge(
918        baseClassPTNode: NodeID,
919        cid: ContextID,
920        baseLocal: Local,
921        callee: ArkMethod,
922        calleeCid: ContextID,
923        callerFunID: FuncID
924    ): NodeID {
925        let thisRefNodeID = this.recordThisRefNode(baseClassPTNode, callee, calleeCid);
926        if (thisRefNodeID === -1) {
927            return -1;
928        }
929
930        let thisRefNode = this.pag.getNode(thisRefNodeID) as PagThisRefNode;
931        let srcBaseLocal = baseLocal;
932        srcBaseLocal = this.getRealThisLocal(srcBaseLocal, callerFunID);
933        let srcNodeId = this.pag.hasCtxNode(cid, srcBaseLocal);
934        if (!srcNodeId) {
935            // this check is for export local and closure use
936            // replace the invoke base, because its origin base has no pag node
937            let interProceduralLocal = this.getSourceValueFromExternalScope(srcBaseLocal, callerFunID);
938            if (interProceduralLocal) {
939                srcNodeId = this.pag.hasCtxNode(cid, interProceduralLocal);
940            }
941        }
942
943        if (!srcNodeId) {
944            throw new Error('Can not get base node');
945        }
946
947        this.pag.addPagEdge(this.pag.getNode(srcNodeId) as PagNode, thisRefNode, PagEdgeKind.This);
948        return srcNodeId;
949    }
950
951    private recordThisRefNode(baseClassPTNode: NodeID, callee: ArkMethod, calleeCid: ContextID): NodeID {
952        if (!callee || !callee.getCfg()) {
953            logger.error(`callee is null`);
954            return -1;
955        }
956        let thisAssignStmt = callee
957            .getCfg()
958            ?.getStmts()
959            .filter(s => s instanceof ArkAssignStmt && s.getRightOp() instanceof ArkThisRef);
960        let thisPtr = (thisAssignStmt?.[0] as ArkAssignStmt).getRightOp() as ArkThisRef;
961        if (!thisPtr) {
962            throw new Error('Can not get this ptr');
963        }
964
965        // IMPORTANT: set cid 2 base Pt info firstly
966        this.cid2ThisRefPtMap.set(calleeCid, baseClassPTNode);
967        let thisRefNode = this.getOrNewThisRefNode(calleeCid, thisPtr) as PagThisRefNode;
968        thisRefNode.addPTNode(baseClassPTNode);
969
970        return thisRefNode.getID();
971    }
972
973    /*
974     * Add copy edges from arguments to parameters
975     *     ret edges from return values to callsite
976     * Return src node
977     */
978    public addStaticPagCallEdge(cs: CallSite, callerCid: ContextID, calleeCid?: ContextID, ptNode?: PagNode): NodeID[] {
979        if (!calleeCid) {
980            calleeCid = this.ctx.getOrNewContext(callerCid, cs.calleeFuncID, true);
981        }
982
983        let srcNodes: NodeID[] = [];
984        // Add reachable
985
986        let calleeNode = this.cg.getNode(cs.calleeFuncID) as CallGraphNode;
987        let calleeMethod: ArkMethod | null = this.scene.getMethod(calleeNode.getMethod());
988        if (!calleeMethod) {
989            // TODO: check if nodes need to delete
990            return srcNodes;
991        }
992        if (calleeNode.isSdkMethod()) {
993            srcNodes.push(...this.addSDKMethodPagCallEdge(cs, callerCid, calleeCid));
994            return srcNodes;
995        }
996
997        if (!calleeMethod.getCfg()) {
998            // method have no cfg body
999            return srcNodes;
1000        }
1001
1002        let calleeCS = this.buildFuncPagAndAddToWorklist(new CSFuncID(calleeCid, cs.calleeFuncID));
1003        // callee cid will updated if callee is singleton
1004        calleeCid = calleeCS.cid;
1005
1006        let realArgs: Value[] = cs.args ?? [];
1007        let argsOffset: number = 0;
1008        if (ptNode && ptNode instanceof PagFuncNode && ptNode.getCS()) {
1009            // for ptr invoke cloned by Function.bind()
1010            realArgs = ptNode.getCS().args ?? [];
1011            argsOffset = ptNode.getArgsOffset() ?? 0;
1012            callerCid = ptNode.getOriginCid() ?? callerCid;
1013        }
1014
1015        srcNodes.push(...this.addCallParamPagEdge(calleeMethod, realArgs, cs.callStmt, callerCid, calleeCid, argsOffset));
1016        srcNodes.push(...this.addCallReturnPagEdge(calleeMethod, cs.callStmt, callerCid, calleeCid));
1017
1018        return srcNodes;
1019    }
1020
1021    /**
1022     * only process the param PAG edge for invoke stmt
1023     */
1024    private addCallParamPagEdge(calleeMethod: ArkMethod, args: Value[], callStmt: Stmt, callerCid: ContextID, calleeCid: ContextID, offset: number): NodeID[] {
1025        let params = calleeMethod
1026            .getCfg()!
1027            .getStmts()
1028            .filter(stmt => stmt instanceof ArkAssignStmt && stmt.getRightOp() instanceof ArkParameterRef)
1029            .map(stmt => (stmt as ArkAssignStmt).getRightOp());
1030
1031        let srcNodes: NodeID[] = [];
1032
1033        /**
1034         *  process foreach situation
1035         *  e.g. arr.forEach((item) => { ... })
1036         *  cs.args is anonymous method local, will have only 1 parameter
1037         *  but inside foreach will have >= 1 parameters
1038         */
1039        if (callStmt.getInvokeExpr()?.getMethodSignature().getMethodSubSignature().getMethodName() === 'forEach') {
1040            srcNodes.push(...this.addForeachParamPagEdge(callerCid, calleeCid, callStmt, params));
1041            return srcNodes;
1042        }
1043
1044        // add args to parameters edges
1045        for (let i = offset; i <= args.length; i++) {
1046            let arg = args[i];
1047            let param = params[i - offset];
1048            if (!arg || !param) {
1049                return srcNodes;
1050            }
1051
1052            if (arg instanceof Constant || arg instanceof AbstractExpr) {
1053                // TODO: handle AbstractExpr
1054                continue;
1055            }
1056
1057            // Get or create new PAG node for argument and parameter
1058            let srcPagNode = this.getOrNewPagNode(callerCid, arg, callStmt);
1059            let dstPagNode = this.getOrNewPagNode(calleeCid, param, callStmt);
1060
1061            this.pag.addPagEdge(srcPagNode, dstPagNode, PagEdgeKind.Copy, callStmt);
1062            srcNodes.push(srcPagNode.getID());
1063            // TODO: handle other types of parmeters
1064        }
1065
1066        return srcNodes;
1067    }
1068
1069    /**
1070     * temporary solution for foreach
1071     * deprecate when foreach is handled by built-in method
1072     * connect the element node with the value inside foreach
1073     */
1074    private addForeachParamPagEdge(callerCid: ContextID, calleeCid: ContextID, callStmt: Stmt, params: Value[]): NodeID[] {
1075        // container value is the base value of callstmt, its points-to is PagNewContainerExprNode
1076        let srcNodes: NodeID[] = [];
1077        let containerValue = (callStmt.getInvokeExpr() as ArkInstanceInvokeExpr).getBase();
1078        let param = params[0];
1079        if (!containerValue || !param) {
1080            return srcNodes;
1081        }
1082
1083        let basePagNode = this.getOrNewPagNode(callerCid, containerValue, callStmt);
1084        let dstPagNode = this.getOrNewPagNode(calleeCid, param, callStmt);
1085
1086        for (let pt of basePagNode.getPointTo()) {
1087            let newContainerExprPagNode = this.pag.getNode(pt) as PagNewContainerExprNode;
1088
1089            // PagNewContainerExprNode's points-to is the element node
1090            if (!newContainerExprPagNode || !newContainerExprPagNode.getElementNode()) {
1091                continue;
1092            }
1093            let srcPagNode = this.pag.getNode(newContainerExprPagNode.getElementNode()!) as PagNode;
1094
1095            // connect the element node with the value inside foreach
1096            this.pag.addPagEdge(srcPagNode, dstPagNode, PagEdgeKind.Copy, callStmt);
1097            srcNodes.push(srcPagNode.getID());
1098        }
1099
1100        return srcNodes;
1101    }
1102
1103    /**
1104     * process the return value PAG edge for invoke stmt
1105     */
1106    public addCallReturnPagEdge(calleeMethod: ArkMethod, callStmt: Stmt, callerCid: ContextID, calleeCid: ContextID): NodeID[] {
1107        let srcNodes: NodeID[] = [];
1108        // add ret to caller edges
1109        let retStmts = calleeMethod.getReturnStmt();
1110        // TODO: call statement must be a assignment state
1111        if (callStmt instanceof ArkAssignStmt) {
1112            let retDst = callStmt.getLeftOp();
1113            for (let retStmt of retStmts) {
1114                let retValue = (retStmt as ArkReturnStmt).getOp();
1115                if (retValue instanceof Local) {
1116                    let srcPagNode = this.getOrNewPagNode(calleeCid, retValue, retStmt);
1117                    let dstPagNode = this.getOrNewPagNode(callerCid, retDst, callStmt);
1118
1119                    this.pag.addPagEdge(srcPagNode, dstPagNode, PagEdgeKind.Copy, retStmt);
1120                } else if (retValue instanceof Constant) {
1121                    continue;
1122                } else if (retValue instanceof AbstractExpr) {
1123                    logger.debug(retValue);
1124                    continue;
1125                } else {
1126                    throw new Error('return dst not a local or constant, but: ' + retValue.getType().toString());
1127                }
1128            }
1129        }
1130
1131        return srcNodes;
1132    }
1133
1134    public addStaticPagCallReturnEdge(cs: CallSite, callerCid: ContextID, calleeCid: ContextID): NodeID[] {
1135        if (!calleeCid) {
1136            calleeCid = this.ctx.getOrNewContext(callerCid, cs.calleeFuncID, true);
1137        }
1138
1139        let srcNodes: NodeID[] = [];
1140        // Add reachable
1141
1142        let calleeNode = this.cg.getNode(cs.calleeFuncID) as CallGraphNode;
1143        let calleeMethod: ArkMethod | null = this.scene.getMethod(calleeNode.getMethod());
1144        if (!calleeMethod) {
1145            // TODO: check if nodes need to delete
1146            return srcNodes;
1147        }
1148        srcNodes.push(...this.addSDKMethodReturnPagEdge(cs, callerCid, calleeCid, calleeMethod));
1149        return srcNodes;
1150    }
1151
1152    private addSDKMethodPagCallEdge(cs: CallSite, callerCid: ContextID, calleeCid: ContextID): NodeID[] {
1153        let srcNodes: NodeID[] = [];
1154        let calleeNode = this.cg.getNode(cs.calleeFuncID) as CallGraphNode;
1155        let calleeMethod: ArkMethod | null = this.scene.getMethod(calleeNode.getMethod());
1156
1157        if (!calleeMethod) {
1158            return srcNodes;
1159        }
1160        let methodType = getBuiltInApiType(calleeMethod.getSignature());
1161
1162        // block the container SDK
1163        if (methodType === BuiltApiType.SetAdd || BuiltApiType.MapSet) {
1164            return srcNodes;
1165        }
1166
1167        if (!this.methodParamValueMap.has(calleeNode.getID())) {
1168            this.buildSDKFuncPag(calleeNode.getID());
1169        }
1170
1171        srcNodes.push(...this.addSDKMethodReturnPagEdge(cs, callerCid, calleeCid, calleeMethod));
1172        srcNodes.push(...this.addSDKMethodParamPagEdge(cs, callerCid, calleeCid, calleeNode.getID()));
1173        return srcNodes;
1174    }
1175
1176    private addSDKMethodReturnPagEdge(cs: CallSite, callerCid: ContextID, calleeCid: ContextID, calleeMethod: ArkMethod): NodeID[] {
1177        let srcNodes: NodeID[] = [];
1178        let returnType = calleeMethod.getReturnType();
1179        if (!(returnType instanceof ClassType) || !(cs.callStmt instanceof ArkAssignStmt)) {
1180            return srcNodes;
1181        }
1182
1183        // check fake heap object exists or not
1184        let cidMap = this.sdkMethodReturnValueMap.get(calleeMethod);
1185        if (!cidMap) {
1186            cidMap = new Map();
1187        }
1188        let newExpr = cidMap.get(calleeCid);
1189        if (!newExpr) {
1190            if (returnType instanceof ClassType) {
1191                newExpr = new ArkNewExpr(returnType);
1192            }
1193        }
1194        cidMap.set(calleeCid, newExpr!);
1195        this.sdkMethodReturnValueMap.set(calleeMethod, cidMap);
1196
1197        let srcPagNode = this.getOrNewPagNode(calleeCid, newExpr!);
1198        let dstPagNode = this.getOrNewPagNode(callerCid, cs.callStmt.getLeftOp(), cs.callStmt);
1199
1200        this.pag.addPagEdge(srcPagNode, dstPagNode, PagEdgeKind.Address, cs.callStmt);
1201        srcNodes.push(srcPagNode.getID());
1202        return srcNodes;
1203    }
1204
1205    private addSDKMethodParamPagEdge(cs: CallSite, callerCid: ContextID, calleeCid: ContextID, funcID: FuncID): NodeID[] {
1206        let argNum = cs.args?.length;
1207        let srcNodes: NodeID[] = [];
1208
1209        if (!argNum) {
1210            return srcNodes;
1211        }
1212
1213        // add args to parameters edges
1214        for (let i = 0; i < argNum; i++) {
1215            let arg = cs.args?.[i];
1216            let paramValue;
1217
1218            if (arg instanceof Local && arg.getType() instanceof FunctionType) {
1219                // TODO: cannot find value
1220                paramValue = this.methodParamValueMap.get(funcID)!.get(i);
1221            } else {
1222                continue;
1223            }
1224
1225            if (!(arg && paramValue)) {
1226                continue;
1227            }
1228
1229            // Get or create new PAG node for argument and parameter
1230            let srcPagNode = this.getOrNewPagNode(callerCid, arg, cs.callStmt);
1231            let dstPagNode = this.getOrNewPagNode(calleeCid, paramValue, cs.callStmt);
1232
1233            if (dstPagNode instanceof PagLocalNode) {
1234                // set the fake param Value in PagLocalNode
1235                /**
1236                 * TODO: !!!
1237                 * some API param is in the form of anonymous method:
1238                 *  component/common.d.ts
1239                 *  declare function animateTo(value: AnimateParam, event: () => void): void;
1240                 *
1241                 * this param fake Value will create PagFuncNode rather than PagLocalNode
1242                 * when this API is called, the anonymous method pointer will not be able to pass into the fake Value PagNode
1243                 */
1244                dstPagNode.setSdkParam();
1245                let sdkParamInvokeStmt = new ArkInvokeStmt(new ArkPtrInvokeExpr((arg.getType() as FunctionType).getMethodSignature(), paramValue as Local, []));
1246
1247                // create new DynCallSite
1248                let sdkParamCallSite = new DynCallSite(sdkParamInvokeStmt, undefined, undefined, funcID);
1249                dstPagNode.addRelatedDynCallSite(sdkParamCallSite);
1250            }
1251
1252            this.pag.addPagEdge(srcPagNode, dstPagNode, PagEdgeKind.Copy, cs.callStmt);
1253            srcNodes.push(srcPagNode.getID());
1254        }
1255
1256        return srcNodes;
1257    }
1258
1259    public getOrNewPagNode(cid: ContextID, v: PagNodeType, s?: Stmt): PagNode {
1260        if (v instanceof ArkThisRef) {
1261            return this.getOrNewThisRefNode(cid, v as ArkThisRef);
1262        }
1263
1264        // this local is also not uniq!!!
1265        // remove below block once this issue fixed
1266
1267        // globalThis process can not be removed while all `globalThis` ref is the same Value
1268        if (v instanceof Local) {
1269            if (v.getName() === 'this') {
1270                return this.getOrNewThisLoalNode(cid, v as Local, s);
1271            } else if (v.getName() === GLOBAL_THIS_NAME && v.getDeclaringStmt() == null) {
1272                // globalThis node has no cid
1273                return this.getOrNewGlobalThisNode(-1);
1274            }
1275        }
1276
1277        if (v instanceof ArkInstanceFieldRef || v instanceof ArkStaticFieldRef) {
1278            v = this.getRealInstanceRef(v);
1279        }
1280
1281        return this.pag.getOrNewNode(cid, v, s);
1282    }
1283
1284    /**
1285     * return ThisRef PAG node according to cid, a cid has a unique ThisRef node
1286     * @param cid: current contextID
1287     */
1288    public getOrNewThisRefNode(cid: ContextID, v: ArkThisRef): PagNode {
1289        let thisRefNodeID = this.cid2ThisRefMap.get(cid);
1290        if (!thisRefNodeID) {
1291            thisRefNodeID = -1;
1292        }
1293
1294        let thisRefNode = this.pag.getOrNewThisRefNode(thisRefNodeID, v);
1295        this.cid2ThisRefMap.set(cid, thisRefNode.getID());
1296        return thisRefNode;
1297    }
1298
1299    // TODO: remove it once this local not uniq issue is fixed
1300    public getOrNewThisLoalNode(cid: ContextID, v: Local, s?: Stmt): PagNode {
1301        let thisLocalNodeID = this.cid2ThisLocalMap.get(cid);
1302        if (thisLocalNodeID) {
1303            return this.pag.getNode(thisLocalNodeID) as PagNode;
1304        }
1305
1306        let thisNode = this.pag.getOrNewNode(cid, v, s);
1307        this.cid2ThisLocalMap.set(cid, thisNode.getID());
1308        return thisNode;
1309    }
1310
1311    public getOrNewGlobalThisNode(cid: ContextID): PagNode {
1312        return this.pag.getOrNewNode(cid, this.getGlobalThisValue());
1313    }
1314
1315    public getUniqThisLocalNode(cid: ContextID): NodeID | undefined {
1316        return this.cid2ThisLocalMap.get(cid);
1317    }
1318
1319    /**
1320     * search the storage map to get propertyNode with given storage and propertyFieldName
1321     * @param storage storage type: AppStorage, LocalStorage etc.
1322     * @param propertyName string property key
1323     * @returns propertyNode: PagLocalNode
1324     */
1325    public getOrNewPropertyNode(storage: StorageType, propertyName: string, stmt: Stmt): PagNode {
1326        let propertyNode = this.getPropertyNode(storage, propertyName, stmt);
1327
1328        if (propertyNode) {
1329            return propertyNode;
1330        }
1331
1332        let storageMap = this.storagePropertyMap.get(storage)!;
1333        let propertyLocal = new Local(propertyName);
1334        storageMap.set(propertyName, propertyLocal);
1335        this.storagePropertyMap.set(storage, storageMap);
1336
1337        return this.getOrNewPagNode(-1, propertyLocal, stmt);
1338    }
1339
1340    public getPropertyNode(storage: StorageType, propertyName: string, stmt: Stmt): PagNode | undefined {
1341        let storageMap = this.storagePropertyMap.get(storage);
1342        let propertyLocal: Local | undefined;
1343
1344        if (!storageMap) {
1345            storageMap = new Map();
1346            this.storagePropertyMap.set(storage, storageMap);
1347        }
1348
1349        if (storageMap.has(propertyName)) {
1350            propertyLocal = storageMap.get(propertyName)!;
1351        }
1352
1353        if (propertyLocal) {
1354            return this.getOrNewPagNode(-1, propertyLocal, stmt);
1355        }
1356        return undefined;
1357    }
1358
1359    /**
1360     * add PagEdge
1361     * @param edgeKind: edge kind differs from API
1362     * @param propertyNode: PAG node created by protpertyName
1363     * @param obj: heapObj stored with Storage API
1364     */
1365    public addPropertyLinkEdge(propertyNode: PagNode, storageObj: Value, cid: ContextID, stmt: Stmt, edgeKind: number): boolean {
1366        if (!(storageObj.getType() instanceof ClassType)) {
1367            return false;
1368        }
1369
1370        if (edgeKind === StorageLinkEdgeType.Property2Local) {
1371            // propertyNode --> objNode
1372            this.pag.addPagEdge(propertyNode, this.pag.getOrNewNode(cid, storageObj), PagEdgeKind.Copy, stmt);
1373        } else if (edgeKind === StorageLinkEdgeType.Local2Property) {
1374            // propertyNode <-- objNode
1375            this.pag.addPagEdge(this.pag.getOrNewNode(cid, storageObj), propertyNode, PagEdgeKind.Copy, stmt);
1376        } else if (edgeKind === StorageLinkEdgeType.TwoWay) {
1377            // propertyNode <-> objNode
1378            this.pag.addPagEdge(propertyNode, this.pag.getOrNewNode(cid, storageObj), PagEdgeKind.Copy, stmt);
1379            this.pag.addPagEdge(this.pag.getOrNewNode(cid, storageObj), propertyNode, PagEdgeKind.Copy, stmt);
1380        }
1381        return true;
1382    }
1383
1384    /*
1385     * In ArkIR, ArkField has multiple instances for each stmt which use it
1386     * But the unique one is needed for pointer analysis
1387     * This is a temp solution to use a ArkField->(first instance)
1388     *  as the unique instance
1389     *
1390     * node merge condition:
1391     * instance field: value and ArkField
1392     * static field: ArkField
1393     */
1394    public getRealInstanceRef(v: Value): Value {
1395        if (!(v instanceof ArkInstanceFieldRef || v instanceof ArkStaticFieldRef)) {
1396            return v;
1397        }
1398
1399        let sig = v.getFieldSignature();
1400        let sigStr = sig.toString();
1401        let base: Local;
1402        let real: Value | undefined;
1403
1404        if (v instanceof ArkInstanceFieldRef) {
1405            base = (v as ArkInstanceFieldRef).getBase();
1406            if (base instanceof Local && base.getName() === GLOBAL_THIS_NAME && base.getDeclaringStmt() == null) {
1407                // replace the base in fieldRef
1408                base = this.getGlobalThisValue();
1409                (v as ArkInstanceFieldRef).setBase(base as Local);
1410            }
1411            let key = `${base.getSignature()}-${sigStr}`;
1412
1413            real = this.instanceField2UniqInstanceMap.get(key);
1414            if (!real) {
1415                this.instanceField2UniqInstanceMap.set(key, v);
1416                real = v;
1417            }
1418        } else {
1419            real = this.staticField2UniqInstanceMap.get(sigStr);
1420            if (!real) {
1421                this.staticField2UniqInstanceMap.set(sigStr, v);
1422                real = v;
1423            }
1424        }
1425        return real;
1426    }
1427
1428    /**
1429     * check if a method is singleton function
1430     * rule: static method, assign heap obj to global var or static field, return the receiver
1431     */
1432    public isSingletonFunction(funcID: FuncID): boolean {
1433        if (this.singletonFuncMap.has(funcID)) {
1434            return this.singletonFuncMap.get(funcID)!;
1435        }
1436
1437        let arkMethod = this.cg.getArkMethodByFuncID(funcID);
1438        if (!arkMethod) {
1439            this.singletonFuncMap.set(funcID, false);
1440            return false;
1441        }
1442
1443        if (!arkMethod.isStatic()) {
1444            this.singletonFuncMap.set(funcID, false);
1445            return false;
1446        }
1447
1448        let funcPag = this.funcPags.get(funcID)!;
1449        let heapObjects = [...funcPag.getInternalEdges()!].filter(edge => edge.kind === PagEdgeKind.Address).map(edge => edge.dst);
1450
1451        let returnValues = arkMethod.getReturnValues();
1452
1453        let result = this.isValueConnected([...funcPag.getInternalEdges()!], heapObjects, returnValues);
1454        this.singletonFuncMap.set(funcID, result);
1455        if (result) {
1456            logger.info(`function ${funcID} is marked as singleton function`);
1457        }
1458        return result;
1459    }
1460
1461    private isValueConnected(edges: IntraProceduralEdge[], leftNodes: Value[], targetNodes: Value[]): boolean {
1462        // build funcPag graph
1463        const graph = new Map<Value, Value[]>();
1464        let hasStaticFieldOrGlobalVar: boolean = false;
1465
1466        for (const edge of edges) {
1467            let dst = this.getRealInstanceRef(edge.dst);
1468            let src = this.getRealInstanceRef(edge.src);
1469            if (!graph.has(dst)) {
1470                graph.set(dst, []);
1471            }
1472            if (!graph.has(src)) {
1473                graph.set(src, []);
1474            }
1475
1476            if (dst instanceof ArkStaticFieldRef || src instanceof ArkStaticFieldRef) {
1477                hasStaticFieldOrGlobalVar = true;
1478            }
1479
1480            graph.get(src)!.push(dst);
1481        }
1482
1483        if (!hasStaticFieldOrGlobalVar) {
1484            return false;
1485        }
1486
1487        for (const targetNode of targetNodes) {
1488            for (const leftNode of leftNodes) {
1489                const visited = new Set<Value>();
1490                let meetStaticField = false;
1491                if (this.funcPagDfs(graph, visited, leftNode, targetNode, meetStaticField)) {
1492                    return true; // a value pair that satisfy condition
1493                }
1494
1495                if (!meetStaticField) {
1496                    break; // heap obj will not deal any more
1497                }
1498            }
1499        }
1500
1501        return false;
1502    }
1503
1504    private funcPagDfs(graph: Map<Value, Value[]>, visited: Set<Value>, currentNode: Value, targetNode: Value, staticFieldFound: boolean): boolean {
1505        if (currentNode === targetNode) {
1506            return staticFieldFound;
1507        }
1508
1509        visited.add(currentNode);
1510
1511        for (const neighbor of graph.get(currentNode) || []) {
1512            // TODO: add global variable
1513            const isSpecialNode = neighbor instanceof ArkStaticFieldRef;
1514
1515            if (!visited.has(neighbor)) {
1516                if (isSpecialNode) {
1517                    staticFieldFound = true;
1518                }
1519
1520                if (this.funcPagDfs(graph, visited, neighbor, targetNode, staticFieldFound)) {
1521                    return true;
1522                }
1523            }
1524        }
1525
1526        return false;
1527    }
1528
1529    public getGlobalThisValue(): Local {
1530        return this.globalThisValue;
1531    }
1532
1533    private getEdgeKindForAssignStmt(stmt: ArkAssignStmt): PagEdgeKind {
1534        if (this.stmtIsCreateAddressObj(stmt)) {
1535            return PagEdgeKind.Address;
1536        }
1537
1538        if (this.stmtIsCopyKind(stmt)) {
1539            return PagEdgeKind.Copy;
1540        }
1541
1542        if (this.stmtIsReadKind(stmt)) {
1543            return PagEdgeKind.Load;
1544        }
1545
1546        if (this.stmtIsWriteKind(stmt)) {
1547            return PagEdgeKind.Write;
1548        }
1549
1550        return PagEdgeKind.Unknown;
1551    }
1552
1553    /**
1554     * process Storage API
1555     * @returns boolean: check if the cs represent a Storage API, no matter the API will success or fail
1556     */
1557    private processStorage(cs: ICallSite, calleeCGNode: CallGraphNode, cid: ContextID): boolean {
1558        let storageName = calleeCGNode.getMethod().getDeclaringClassSignature().getClassName();
1559        let storageType: StorageType = this.getStorageType(storageName, cs, cid);
1560
1561        // TODO: add other storages
1562        if (storageType === StorageType.APP_STORAGE) {
1563            let calleeName = calleeCGNode.getMethod().getMethodSubSignature().getMethodName();
1564
1565            // TODO: complete AppStorage API
1566            if (calleeName === 'setOrCreate') {
1567                this.processStorageSetOrCreate(cs, cid);
1568            } else if (calleeName === 'link') {
1569                this.processStorageLink(cs, cid);
1570            } else if (calleeName === 'prop') {
1571                this.processStorageProp(cs, cid);
1572            } else if (calleeName === 'set') {
1573                this.processStorageSet(cs, cid);
1574            } else if (calleeName === 'get') {
1575                this.processStorageGet(cs, cid);
1576            }
1577            return true;
1578        } else if (storageType === StorageType.LOCAL_STORAGE) {
1579            // TODO: LocalStorage is not Static
1580        }
1581
1582        return false;
1583    }
1584
1585    private processStorageSetOrCreate(cs: ICallSite, cid: ContextID): void {
1586        let propertyStr = this.getPropertyName(cs.args![0]);
1587        if (!propertyStr) {
1588            return;
1589        }
1590
1591        let propertyName = propertyStr;
1592        let propertyNode = this.getOrNewPropertyNode(StorageType.APP_STORAGE, propertyName, cs.callStmt);
1593        let storageObj = cs.args![1];
1594
1595        this.addPropertyLinkEdge(propertyNode, storageObj, cid, cs.callStmt, StorageLinkEdgeType.Local2Property);
1596    }
1597
1598    private processStorageLink(cs: ICallSite, cid: ContextID): void {
1599        let propertyStr = this.getPropertyName(cs.args![0]);
1600        if (!propertyStr) {
1601            return;
1602        }
1603
1604        let propertyName = propertyStr;
1605        let propertyNode = this.getOrNewPropertyNode(StorageType.APP_STORAGE, propertyName, cs.callStmt);
1606        let leftOp = (cs.callStmt as ArkAssignStmt).getLeftOp() as Local;
1607        let linkedOpNode = this.pag.getOrNewNode(cid, leftOp) as PagNode;
1608        if (linkedOpNode instanceof PagLocalNode) {
1609            linkedOpNode.setStorageLink(StorageType.APP_STORAGE, propertyName);
1610        }
1611
1612        this.pag.addPagEdge(propertyNode, linkedOpNode, PagEdgeKind.Copy);
1613        this.pag.addPagEdge(linkedOpNode, propertyNode, PagEdgeKind.Copy);
1614    }
1615
1616    private processStorageProp(cs: ICallSite, cid: ContextID): void {
1617        let propertyStr = this.getPropertyName(cs.args![0]);
1618        if (!propertyStr) {
1619            return;
1620        }
1621
1622        let propertyName = propertyStr;
1623        let propertyNode = this.getOrNewPropertyNode(StorageType.APP_STORAGE, propertyName, cs.callStmt);
1624        let leftOp = (cs.callStmt as ArkAssignStmt).getLeftOp() as Local;
1625        let linkedOpNode = this.pag.getOrNewNode(cid, leftOp) as PagNode;
1626        if (linkedOpNode instanceof PagLocalNode) {
1627            linkedOpNode.setStorageLink(StorageType.APP_STORAGE, propertyName);
1628        }
1629
1630        this.pag.addPagEdge(propertyNode, linkedOpNode, PagEdgeKind.Copy);
1631    }
1632
1633    private processStorageSet(cs: ICallSite, cid: ContextID): void {
1634        let ivkExpr: AbstractInvokeExpr = cs.callStmt.getInvokeExpr()!;
1635        if (ivkExpr instanceof ArkInstanceInvokeExpr) {
1636            let base = ivkExpr.getBase();
1637            let baseNode = this.pag.getOrNewNode(cid, base) as PagLocalNode;
1638
1639            if (baseNode.isStorageLinked()) {
1640                let argsNode = this.pag.getOrNewNode(cid, cs.args![0]) as PagNode;
1641
1642                this.pag.addPagEdge(argsNode, baseNode, PagEdgeKind.Copy);
1643            }
1644        } else if (ivkExpr instanceof ArkStaticInvokeExpr) {
1645            // TODO: process AppStorage.set()
1646        }
1647    }
1648
1649    private processStorageGet(cs: ICallSite, cid: ContextID): void {
1650        if (!(cs.callStmt instanceof ArkAssignStmt)) {
1651            return;
1652        }
1653        let leftOp = (cs.callStmt as ArkAssignStmt).getLeftOp() as Local;
1654        let ivkExpr = cs.callStmt.getInvokeExpr();
1655        let propertyName!: string;
1656        if (ivkExpr instanceof ArkStaticInvokeExpr) {
1657            let propertyStr = this.getPropertyName(cs.args![0]);
1658            if (propertyStr) {
1659                propertyName = propertyStr;
1660            }
1661        } else if (ivkExpr instanceof ArkInstanceInvokeExpr) {
1662            let baseNode = this.pag.getOrNewNode(cid, ivkExpr.getBase()) as PagLocalNode;
1663            if (baseNode.isStorageLinked()) {
1664                propertyName = baseNode.getStorage().PropertyName!;
1665            }
1666        }
1667
1668        let propertyNode = this.getPropertyNode(StorageType.APP_STORAGE, propertyName, cs.callStmt);
1669        if (!propertyNode) {
1670            return;
1671        }
1672
1673        this.pag.addPagEdge(propertyNode, this.pag.getOrNewNode(cid, leftOp, cs.callStmt), PagEdgeKind.Copy, cs.callStmt);
1674    }
1675
1676    private getPropertyName(value: Value): string | undefined {
1677        if (value instanceof Local) {
1678            let type = value.getType();
1679            if (type instanceof StringType) {
1680                return type.getName();
1681            }
1682        } else if (value instanceof Constant) {
1683            return value.getValue();
1684        }
1685
1686        return undefined;
1687    }
1688
1689    /**
1690     * get storageType enum with method's Declaring ClassName
1691     *
1692     * @param storageName ClassName that method belongs to, currently support AppStorage and SubscribedAbstractProperty
1693     * SubscribedAbstractProperty: in following listing, `link1` is infered as ClassType `SubscribedAbstractProperty`,
1694     * it needs to get PAG node to check the StorageType
1695     * let link1: SubscribedAbstractProperty<A> = AppStorage.link('PropA');
1696     * link1.set(a);
1697     * @param cs: for search PAG node in SubscribedAbstractProperty
1698     * @param cid: for search PAG node in SubscribedAbstractProperty
1699     * @returns StorageType enum
1700     */
1701    private getStorageType(storageName: string, cs: ICallSite, cid: ContextID): StorageType {
1702        switch (storageName) {
1703            case 'AppStorage':
1704                return StorageType.APP_STORAGE;
1705            case 'SubscribedAbstractProperty': {
1706                let calleeBaseLocal = (cs.callStmt.getInvokeExpr() as ArkInstanceInvokeExpr).getBase();
1707                let calleeBaseLocalNode = this.pag.getOrNewNode(cid, calleeBaseLocal) as PagLocalNode;
1708                if (calleeBaseLocalNode.isStorageLinked()) {
1709                    let storage = calleeBaseLocalNode.getStorage();
1710
1711                    return storage.StorageType!;
1712                }
1713                return StorageType.Undefined;
1714            }
1715            default:
1716                return StorageType.Undefined;
1717        }
1718    }
1719
1720    /**\
1721     * ArkNewExpr, ArkNewArrayExpr, function ptr, globalThis
1722     */
1723    private stmtIsCreateAddressObj(stmt: ArkAssignStmt): boolean {
1724        let lhOp = stmt.getLeftOp();
1725        let rhOp = stmt.getRightOp();
1726        if (
1727            rhOp instanceof ArkNewExpr ||
1728            rhOp instanceof ArkNewArrayExpr ||
1729            (lhOp instanceof Local &&
1730                ((rhOp instanceof Local && rhOp.getType() instanceof FunctionType && rhOp.getDeclaringStmt() === null) ||
1731                    (rhOp instanceof AbstractFieldRef && rhOp.getType() instanceof FunctionType))) ||
1732            (rhOp instanceof Local && rhOp.getName() === GLOBAL_THIS_NAME && rhOp.getDeclaringStmt() == null)
1733        ) {
1734            return true;
1735        }
1736
1737        // TODO: add other Address Obj creation
1738        // like static object
1739        return false;
1740    }
1741
1742    private stmtIsCopyKind(stmt: ArkAssignStmt): boolean {
1743        let lhOp = stmt.getLeftOp();
1744        let rhOp = stmt.getRightOp();
1745
1746        let condition: boolean =
1747            (lhOp instanceof Local &&
1748                (rhOp instanceof Local || rhOp instanceof ArkParameterRef || rhOp instanceof ArkThisRef || rhOp instanceof ArkStaticFieldRef)) ||
1749            (lhOp instanceof ArkStaticFieldRef && rhOp instanceof Local);
1750
1751        if (condition) {
1752            return true;
1753        }
1754        return false;
1755    }
1756
1757    private stmtIsWriteKind(stmt: ArkAssignStmt): boolean {
1758        let lhOp = stmt.getLeftOp();
1759        let rhOp = stmt.getRightOp();
1760
1761        if (rhOp instanceof Local && (lhOp instanceof ArkInstanceFieldRef || lhOp instanceof ArkArrayRef)) {
1762            return true;
1763        }
1764        return false;
1765    }
1766
1767    private stmtIsReadKind(stmt: ArkAssignStmt): boolean {
1768        let lhOp = stmt.getLeftOp();
1769        let rhOp = stmt.getRightOp();
1770
1771        if (lhOp instanceof Local && (rhOp instanceof ArkInstanceFieldRef || rhOp instanceof ArkArrayRef)) {
1772            return true;
1773        }
1774        return false;
1775    }
1776
1777    public addToDynamicCallSite(funcPag: FuncPag, cs: DynCallSite): void {
1778        funcPag.addDynamicCallSite(cs);
1779        this.pagStat.numDynamicCall++;
1780
1781        logger.trace('[add dynamic callsite] ' + cs.callStmt.toString() + ':  ' + cs.callStmt.getCfg()?.getDeclaringMethod().getSignature().toString());
1782    }
1783
1784    public setPtForNode(node: NodeID, pts: IPtsCollection<NodeID> | undefined): void {
1785        if (!pts) {
1786            return;
1787        }
1788
1789        (this.pag.getNode(node) as PagNode).setPointTo(pts);
1790    }
1791
1792    public getRealThisLocal(input: Local, funcId: FuncID): Local {
1793        if (input.getName() !== 'this') {
1794            return input;
1795        }
1796        let real = input;
1797
1798        let f = this.cg.getArkMethodByFuncID(funcId);
1799        f
1800            ?.getCfg()
1801            ?.getStmts()
1802            .forEach(s => {
1803                if (s instanceof ArkAssignStmt && s.getLeftOp() instanceof Local) {
1804                    if ((s.getLeftOp() as Local).getName() === 'this') {
1805                        real = s.getLeftOp() as Local;
1806                        return;
1807                    }
1808                }
1809            });
1810        return real;
1811    }
1812
1813    public doStat(): void {
1814        this.pagStat.numTotalNode = this.pag.getNodeNum();
1815    }
1816
1817    public printStat(): void {
1818        this.pagStat.printStat();
1819    }
1820
1821    public getStat(): string {
1822        return this.pagStat.getStat();
1823    }
1824
1825    public getUnhandledFuncs(): FuncID[] {
1826        let handledFuncs = this.getHandledFuncs();
1827        let unhandleFuncs = Array.from(this.cg.getNodesIter())
1828            .filter(f => !handledFuncs.includes(f.getID()))
1829            .map(f => f.getID());
1830        return unhandleFuncs;
1831    }
1832
1833    public getHandledFuncs(): FuncID[] {
1834        return Array.from(this.funcPags.keys());
1835    }
1836
1837    /**
1838     * build export edge in internal func pag
1839     * @param value: Value that need to check if it is from import/export
1840     * @param originValue: if Value if InstanceFieldRef, the base will be passed to `value` recursively,
1841     *                      fieldRef will be passed to `originValue`
1842     */
1843    private handleValueFromExternalScope(value: Value, funcID: FuncID, originValue?: Value): void {
1844        if (value instanceof Local) {
1845            if (value.getDeclaringStmt() || value.getName() === 'this') {
1846                // not from external scope
1847                return;
1848            }
1849
1850            if (!value.getType()) {
1851                return;
1852            }
1853
1854            let srcLocal = this.getSourceValueFromExternalScope(value, funcID);
1855
1856            if (srcLocal) {
1857                // if `value` is from field base, use origin value(fieldRef) instead
1858                this.addInterFuncEdge(srcLocal, originValue ?? value, funcID);
1859            }
1860        } else if (value instanceof ArkInstanceFieldRef) {
1861            let base = value.getBase();
1862            if (base) {
1863                this.handleValueFromExternalScope(base, funcID, value);
1864            }
1865        }
1866    }
1867
1868    private addInterFuncEdge(src: Local, dst: Value, funcID: FuncID): void {
1869        this.interFuncPags = this.interFuncPags ?? new Map();
1870        let interFuncPag = this.interFuncPags.get(funcID) ?? new InterFuncPag();
1871        // Export a local
1872        // Add a InterProcedural edge
1873        if (dst instanceof Local) {
1874            let e: InterProceduralEdge = {
1875                src: src,
1876                dst: dst,
1877                kind: PagEdgeKind.InterProceduralCopy,
1878            };
1879            interFuncPag.addToInterProceduralEdgeSet(e);
1880            this.addExportVariableMap(src, dst as Local);
1881        } else if (dst instanceof ArkInstanceFieldRef) {
1882            // record the export base use
1883            this.addExportVariableMap(src, dst.getBase());
1884        }
1885        this.interFuncPags.set(funcID, interFuncPag);
1886
1887        // Put the function which the src belongs to to worklist
1888        let srcFunc = src.getDeclaringStmt()?.getCfg().getDeclaringMethod();
1889        if (srcFunc) {
1890            let srcFuncID = this.cg.getCallGraphNodeByMethod(srcFunc.getSignature()).getID();
1891            let cid = this.ctx.getNewContextID(srcFuncID);
1892            let csFuncID = new CSFuncID(cid, srcFuncID);
1893            this.buildFuncPagAndAddToWorklist(csFuncID);
1894        }
1895        // Extend other types of src here
1896    }
1897
1898    private getSourceValueFromExternalScope(value: Local, funcID: FuncID): Local | undefined {
1899        let sourceValue;
1900
1901        sourceValue = this.getDefaultMethodSourceValue(value, funcID);
1902        if (!sourceValue) {
1903            sourceValue = this.getExportSourceValue(value, funcID);
1904        }
1905
1906        return sourceValue;
1907    }
1908
1909    private getDefaultMethodSourceValue(value: Local, funcID: FuncID): Local | undefined {
1910        // namespace check
1911        let arkMethod = this.cg.getArkMethodByFuncID(funcID);
1912        if (!arkMethod) {
1913            return undefined;
1914        }
1915
1916        let declaringNameSpace = arkMethod.getDeclaringArkClass().getDeclaringArkNamespace();
1917        while (declaringNameSpace) {
1918            let nameSpaceLocals = declaringNameSpace.getDefaultClass().getDefaultArkMethod()?.getBody()?.getLocals() ?? new Map();
1919            if (nameSpaceLocals.has(value.getName())) {
1920                return nameSpaceLocals.get(value.getName());
1921            }
1922
1923            declaringNameSpace = declaringNameSpace.getDeclaringArkNamespace() ?? undefined;
1924        }
1925
1926        // file check
1927        let declaringFile = arkMethod.getDeclaringArkFile();
1928        let fileLocals = declaringFile.getDefaultClass().getDefaultArkMethod()?.getBody()?.getLocals() ?? new Map();
1929        if (!fileLocals.has(value.getName())) {
1930            return undefined;
1931        }
1932
1933        return fileLocals.get(value.getName());
1934    }
1935
1936    private getExportSourceValue(value: Local, funcID: FuncID): Local | undefined {
1937        let curMethod = this.cg.getArkMethodByFuncID(funcID);
1938        if (!curMethod) {
1939            return undefined;
1940        }
1941
1942        let curFile = curMethod.getDeclaringArkFile();
1943        let impInfo = curFile.getImportInfoBy(value.getName());
1944        if (!impInfo) {
1945            return undefined;
1946        }
1947
1948        let exportSource = impInfo.getLazyExportInfo();
1949        if (!exportSource) {
1950            return undefined;
1951        }
1952
1953        let exportSouceValue = exportSource.getArkExport();
1954        if (exportSouceValue instanceof Local) {
1955            return exportSouceValue;
1956        }
1957        return undefined;
1958    }
1959
1960    private addExportVariableMap(src: Local, dst: Local): void {
1961        let exportMap: Local[] = this.externalScopeVariableMap.get(src) ?? [];
1962        if (!exportMap.includes(dst)) {
1963            exportMap.push(dst);
1964            this.externalScopeVariableMap.set(src, exportMap);
1965        }
1966    }
1967
1968    public getExportVariableMap(src: Local): Local[] {
1969        return this.externalScopeVariableMap.get(src) ?? [];
1970    }
1971
1972    /// Add inter-procedural Pag Nodes and Edges
1973    public addEdgesFromInterFuncPag(interFuncPag: InterFuncPag, cid: ContextID): boolean {
1974        let edges = interFuncPag.getInterProceduralEdges();
1975        if (edges.size === 0) {
1976            return false;
1977        }
1978
1979        for (let e of edges) {
1980            // Existing local exported nodes -> ExportNode
1981            let exportLocal = e.src;
1982            let dstPagNode = this.getOrNewPagNode(cid, e.dst);
1983
1984            // get export local node in all cid
1985            let existingNodes = this.pag.getNodesByValue(exportLocal);
1986            existingNodes?.forEach(n => {
1987                this.pag.addPagEdge(this.pag.getNode(n)! as PagNode, dstPagNode, e.kind);
1988                this.retriggerNodesList.add(n);
1989            });
1990        }
1991
1992        return true;
1993    }
1994
1995    public getRetriggerNodes(): NodeID[] {
1996        let retriggerNodes = Array.from(this.retriggerNodesList);
1997        this.retriggerNodesList.clear();
1998        return retriggerNodes;
1999    }
2000
2001    public addUpdatedNode(nodeID: NodeID, diffPT: IPtsCollection<NodeID>): void {
2002        let ptaConfig = PointerAnalysisConfig.getInstance();
2003        let updatedNode = this.updatedNodesThisRound.get(nodeID) ?? new ptaConfig.ptsCollectionCtor();
2004        updatedNode.union(diffPT);
2005        this.updatedNodesThisRound.set(nodeID, updatedNode);
2006    }
2007
2008    public getUpdatedNodes(): Map<number, IPtsCollection<number>> {
2009        return this.updatedNodesThisRound;
2010    }
2011
2012    public resetUpdatedNodes(): void {
2013        this.updatedNodesThisRound.clear();
2014    }
2015
2016    private transferArrayValues(method: ArkMethod, arrayLocal: Value): Local[] {
2017        if (!(arrayLocal instanceof Local) || !(arrayLocal.getType() instanceof ArrayType)) {
2018            return [];
2019        }
2020
2021        /**
2022         * TODO: get array element values
2023         * need to resolve multi dimension array
2024         */
2025        const usedValuesInArray = arrayLocal.getUsedStmts().flatMap(stmt => {
2026            if (stmt instanceof ArkAssignStmt) {
2027                const rightOp = stmt.getRightOp();
2028                if (rightOp instanceof Local) {
2029                    return rightOp;
2030                }
2031            }
2032            return [];
2033        });
2034
2035        return usedValuesInArray;
2036    }
2037}
2038