• 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, Method } from '../CallGraph';
17import { Scene } from '../../../Scene';
18import { AbstractInvokeExpr, ArkInstanceInvokeExpr, ArkStaticInvokeExpr } from '../../../core/base/Expr';
19import { NodeID } from '../../../core/graph/BaseExplicitGraph';
20import { ClassHierarchyAnalysis } from '../../algorithm/ClassHierarchyAnalysis';
21import { RapidTypeAnalysis } from '../../algorithm/RapidTypeAnalysis';
22import { ArkMethod } from '../../../core/model/ArkMethod';
23
24export class CallGraphBuilder {
25    private cg: CallGraph;
26    private scene: Scene;
27
28    constructor(c: CallGraph, s: Scene) {
29        this.cg = c;
30        this.scene = s;
31    }
32
33    public buildDirectCallGraphForScene(): void {
34        const methods = this.scene.getMethods();
35        this.buildDirectCallGraph(methods);
36
37        // set entries at end
38        this.setEntries();
39    }
40
41    /*
42     * Create CG Node for ArkMethods
43     */
44    public buildCGNodes(methods: ArkMethod[]): void {
45        for (const method of methods) {
46            let m = method.getSignature();
47            let kind = CallGraphNodeKind.real;
48            if (method.isGenerated()) {
49                kind = CallGraphNodeKind.intrinsic;
50            } else if (method.getBody() === undefined || method.getCfg() === undefined) {
51                kind = CallGraphNodeKind.blank;
52            } else if (method.getName() === 'constructor') {
53                kind = CallGraphNodeKind.constructor;
54            }
55
56            this.cg.addCallGraphNode(m, kind);
57        }
58    }
59
60    public buildDirectCallGraph(methods: ArkMethod[]): void {
61        this.buildCGNodes(methods);
62
63        for (const method of methods) {
64            let cfg = method.getCfg();
65            if (cfg === undefined) {
66                // abstract method cfg is undefined
67                continue;
68            }
69            let stmts = cfg.getStmts();
70            for (const stmt of stmts) {
71                let invokeExpr = stmt.getInvokeExpr();
72                if (invokeExpr === undefined) {
73                    continue;
74                }
75
76                let callee: Method | undefined = this.getDCCallee(invokeExpr);
77                // abstract method will also be added into direct cg
78                if (callee && invokeExpr instanceof ArkStaticInvokeExpr) {
79                    this.cg.addDirectOrSpecialCallEdge(method.getSignature(), callee, stmt);
80                } else if (
81                    callee && invokeExpr instanceof ArkInstanceInvokeExpr &&
82                    (this.isConstructor(callee) || this.scene.getMethod(callee)?.isGenerated())
83                ) {
84                    this.cg.addDirectOrSpecialCallEdge(method.getSignature(), callee, stmt, false);
85                } else {
86                    this.cg.addDynamicCallInfo(stmt, method.getSignature(), callee);
87                }
88            }
89        }
90    }
91
92    public buildClassHierarchyCallGraph(entries: Method[], displayGeneratedMethod: boolean = false): void {
93        let cgEntries: NodeID[] = [];
94        entries.forEach((entry: Method) => {
95            cgEntries.push(this.cg.getCallGraphNodeByMethod(entry).getID());
96        });
97        this.cg.setEntries(cgEntries);
98
99        let classHierarchyAnalysis: ClassHierarchyAnalysis = new ClassHierarchyAnalysis(this.scene, this.cg, this);
100        classHierarchyAnalysis.start(displayGeneratedMethod);
101    }
102
103    public buildCHA4WholeProject(displayGeneratedMethod: boolean = false): void {
104        let classHierarchyAnalysis: ClassHierarchyAnalysis = new ClassHierarchyAnalysis(this.scene, this.cg, this);
105        classHierarchyAnalysis.projectStart(displayGeneratedMethod);
106    }
107
108    public buildRapidTypeCallGraph(entries: Method[], displayGeneratedMethod: boolean = false): void {
109        let cgEntries: NodeID[] = [];
110        entries.forEach((entry: Method) => {
111            cgEntries.push(this.cg.getCallGraphNodeByMethod(entry).getID());
112        });
113        this.cg.setEntries(cgEntries);
114
115        let rapidTypeAnalysis: RapidTypeAnalysis = new RapidTypeAnalysis(this.scene, this.cg);
116        rapidTypeAnalysis.start(displayGeneratedMethod);
117    }
118
119    /// Get direct call callee
120    private getDCCallee(invokeExpr: AbstractInvokeExpr): Method | undefined {
121        return invokeExpr.getMethodSignature();
122    }
123
124    private isConstructor(m: Method): boolean {
125        return m.getMethodSubSignature().getMethodName() === 'constructor';
126    }
127
128    public setEntries(): void {
129        let nodesIter = this.cg.getNodesIter();
130        let entries = Array.from(nodesIter)
131            .filter(node => !node.hasIncomingEdges() && node.getKind() === CallGraphNodeKind.real && !(node as CallGraphNode).isBlankMethod)
132            .map(node => node.getID());
133        this.cg.setEntries(entries);
134    }
135}
136