• 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 { Scene } from '../../Scene';
17import { AbstractInvokeExpr } from '../../core/base/Expr';
18import { Stmt } from '../../core/base/Stmt';
19import { FunctionType } from '../../core/base/Type';
20import { ArkClass } from '../../core/model/ArkClass';
21import { ArkMethod } from '../../core/model/ArkMethod';
22import { MethodSignature } from '../../core/model/ArkSignature';
23import Logger, { LOG_MODULE_TYPE } from '../../utils/logger';
24import { NodeID } from '../../core/graph/BaseExplicitGraph';
25import { CallGraph, FuncID, CallSite, CallGraphNode } from '../model/CallGraph';
26import { CallGraphBuilder } from '../model/builder/CallGraphBuilder';
27import { createPtsCollectionCtor, IPtsCollection, PtsCollectionType } from '../pointerAnalysis/PtsDS';
28
29const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'CG');
30
31export abstract class AbstractAnalysis {
32    protected scene: Scene;
33    protected cg: CallGraph;
34    protected cgBuilder!: CallGraphBuilder;
35    protected workList: FuncID[] = [];
36    protected processedMethod!: IPtsCollection<FuncID>;
37
38    constructor(s: Scene, cg: CallGraph) {
39        this.scene = s;
40        this.cg = cg;
41    }
42
43    public getScene(): Scene {
44        return this.scene;
45    }
46
47    public getCallGraph(): CallGraph {
48        return this.cg;
49    }
50
51    protected abstract resolveCall(sourceMethod: NodeID, invokeStmt: Stmt): CallSite[];
52    protected abstract preProcessMethod(funcID: FuncID): CallSite[];
53
54    public resolveInvokeExpr(invokeExpr: AbstractInvokeExpr): ArkMethod | undefined {
55        const method = this.scene.getMethod(invokeExpr.getMethodSignature());
56        if (method != null) {
57            return method;
58        }
59        return undefined;
60    }
61
62    public getClassHierarchy(arkClass: ArkClass): ArkClass[] {
63        // TODO: remove abstract class
64        let classWorkList: ArkClass[] = [arkClass];
65        // TODO: check class with no super Class
66        let classHierarchy: ArkClass[] = [];
67
68        while (classWorkList.length > 0) {
69            // TODO: no dumplicated check, TS doesn't allow multi extend
70            let tempClass = classWorkList.shift()!;
71            classWorkList.push(...tempClass.getExtendedClasses().values());
72            classHierarchy.push(tempClass);
73        }
74
75        return classHierarchy;
76    }
77
78    public start(displayGeneratedMethod: boolean): void {
79        this.init();
80        while (this.workList.length !== 0) {
81            const method = this.workList.shift() as FuncID;
82            const cgNode = this.cg.getNode(method) as CallGraphNode;
83
84            if (this.processedMethod.contains(method) || cgNode.isSdkMethod()) {
85                continue;
86            }
87
88            // pre process for RTA only
89            this.preProcessMethod(method).forEach((cs: CallSite) => {
90                this.workList.push(cs.calleeFuncID);
91            });
92
93            this.processMethod(method).forEach((cs: CallSite) => {
94                this.processCallSite(method, cs, displayGeneratedMethod);
95            });
96        }
97    }
98
99    public projectStart(displayGeneratedMethod: boolean): void {
100        this.cgBuilder.buildCGNodes(this.scene.getMethods());
101
102        for (let n of this.cg.getNodesIter()) {
103            let cgNode = n as CallGraphNode;
104
105            if (cgNode.isSdkMethod()) {
106                continue;
107            }
108
109            this.preProcessMethod(cgNode.getID());
110
111            this.processMethod(cgNode.getID()).forEach((cs: CallSite) => {
112                this.processCallSite(cgNode.getID(), cs, displayGeneratedMethod, true);
113            });
114        }
115
116        this.cgBuilder.setEntries();
117    }
118
119    private processCallSite(method: FuncID, cs: CallSite, displayGeneratedMethod: boolean, isProject: boolean = false): void {
120        let me = this.cg.getArkMethodByFuncID(cs.calleeFuncID);
121        let meNode = this.cg.getNode(cs.calleeFuncID) as CallGraphNode;
122        this.addCallGraphEdge(method, me, cs, displayGeneratedMethod);
123
124        if (isProject) {
125            return;
126        }
127
128        this.processedMethod.insert(cs.callerFuncID);
129
130        if (this.processedMethod.contains(cs.calleeFuncID) || meNode.isSdkMethod()) {
131            return;
132        }
133
134        if (displayGeneratedMethod || !me?.isGenerated()) {
135            this.workList.push(cs.calleeFuncID);
136            logger.trace(`New workList item ${cs.calleeFuncID}: ${this.cg.getArkMethodByFuncID(cs.calleeFuncID)?.getSignature().toString()}`);
137        }
138    }
139
140    protected init(): void {
141        this.processedMethod = new (createPtsCollectionCtor(PtsCollectionType.BitVector))();
142        this.cg.getEntries().forEach(entryFunc => {
143            this.workList.push(entryFunc);
144        });
145    }
146
147    protected processMethod(methodID: FuncID): CallSite[] {
148        let cgNode = this.cg.getNode(methodID) as CallGraphNode;
149        let arkMethod = this.scene.getMethod(cgNode.getMethod(), true);
150        let calleeMethods: CallSite[] = [];
151
152        if (!arkMethod) {
153            throw new Error('can not find method');
154        }
155
156        const cfg = arkMethod.getCfg();
157        if (!cfg) {
158            return [];
159        }
160        cfg.getStmts().forEach(stmt => {
161            if (stmt.containsInvokeExpr()) {
162                this.resolveCall(cgNode.getID(), stmt).forEach(callSite => {
163                    calleeMethods.push(callSite);
164                    this.cg.addStmtToCallSiteMap(stmt, callSite);
165                    this.cg.addMethodToCallSiteMap(callSite.calleeFuncID, callSite);
166                });
167            }
168        });
169
170        return calleeMethods;
171    }
172
173    protected getParamAnonymousMethod(invokeExpr: AbstractInvokeExpr): MethodSignature[] {
174        let paramMethod: MethodSignature[] = [];
175
176        invokeExpr.getArgs().forEach(args => {
177            let argsType = args.getType();
178            if (argsType instanceof FunctionType) {
179                paramMethod.push(argsType.getMethodSignature());
180            }
181        });
182
183        return paramMethod;
184    }
185
186    protected addCallGraphEdge(caller: FuncID, callee: ArkMethod | null, cs: CallSite, displayGeneratedMethod: boolean): void {
187        // check if need to display generated method
188        if (!callee) {
189            logger.error(`FuncID has no method ${cs.calleeFuncID}`);
190        } else {
191            if (displayGeneratedMethod || !callee?.isGenerated()) {
192                this.cg.addDynamicCallEdge(caller, cs.calleeFuncID, cs.callStmt);
193            }
194        }
195    }
196}
197