• 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 { ArkNewExpr, ArkStaticInvokeExpr } from '../../core/base/Expr';
17import { Scene } from '../../Scene';
18import { Stmt } from '../../core/base/Stmt';
19import { ArkClass } from '../../core/model/ArkClass';
20import { ClassSignature } from '../../core/model/ArkSignature';
21import { NodeID } from '../../core/graph/BaseExplicitGraph';
22import { CallGraph, CallSite, FuncID } from '../model/CallGraph';
23import { AbstractAnalysis } from './AbstractAnalysis';
24import Logger, { LOG_MODULE_TYPE } from '../../utils/logger';
25import { ClassType } from '../../core/base/Type';
26
27const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'RTA');
28
29export class RapidTypeAnalysis extends AbstractAnalysis {
30    // TODO: signature duplicated check
31    private instancedClasses: Set<ClassSignature> = new Set();
32    // TODO: Set duplicated check
33    private ignoredCalls: Map<ClassSignature, Set<{ caller: NodeID; callee: NodeID; callStmt: Stmt }>> = new Map();
34
35    constructor(scene: Scene, cg: CallGraph) {
36        super(scene, cg);
37    }
38
39    public resolveCall(callerMethod: NodeID, invokeStmt: Stmt): CallSite[] {
40        let invokeExpr = invokeStmt.getInvokeExpr();
41        let resolveResult: CallSite[] = [];
42
43        if (!invokeExpr) {
44            return [];
45        }
46
47        // process anonymous method call
48        this.getParamAnonymousMethod(invokeExpr).forEach(method => {
49            resolveResult.push(new CallSite(invokeStmt, undefined, this.cg.getCallGraphNodeByMethod(method).getID(), callerMethod));
50        });
51
52        let calleeMethod = this.resolveInvokeExpr(invokeExpr);
53        if (!calleeMethod) {
54            return resolveResult;
55        }
56
57        if (invokeExpr instanceof ArkStaticInvokeExpr) {
58            // get specific method
59            resolveResult.push(new CallSite(invokeStmt, undefined, this.cg.getCallGraphNodeByMethod(calleeMethod.getSignature()).getID(), callerMethod));
60        } else {
61            let declareClass = calleeMethod!.getDeclaringArkClass();
62            // TODO: super class method should be placed at the end
63            this.getClassHierarchy(declareClass).forEach((arkClass: ArkClass) => {
64                if (arkClass.isAbstract()) {
65                    return;
66                }
67
68                let possibleCalleeMethod = arkClass.getMethodWithName(calleeMethod!.getName());
69
70                if (
71                    possibleCalleeMethod &&
72                    possibleCalleeMethod.isGenerated() &&
73                    arkClass.getSignature().toString() !== declareClass.getSignature().toString()
74                ) {
75                    // remove the generated method in extended classes
76                    return;
77                }
78
79                if (!(possibleCalleeMethod && !possibleCalleeMethod.isAbstract())) {
80                    return;
81                }
82
83                if (!this.instancedClasses.has(arkClass.getSignature())) {
84                    this.addIgnoredCalls(
85                        arkClass.getSignature(),
86                        callerMethod,
87                        this.cg.getCallGraphNodeByMethod(possibleCalleeMethod.getSignature()).getID(),
88                        invokeStmt
89                    );
90                } else {
91                    resolveResult.push(
92                        new CallSite(invokeStmt, undefined, this.cg.getCallGraphNodeByMethod(possibleCalleeMethod.getSignature()).getID(), callerMethod)
93                    );
94                }
95            });
96        }
97
98        return resolveResult;
99    }
100
101    protected preProcessMethod(funcID: FuncID): CallSite[] {
102        let newCallSites: CallSite[] = [];
103        let instancedClasses: Set<ClassSignature> = this.collectInstancedClassesInMethod(funcID);
104        let newlyInstancedClasses = new Set(Array.from(instancedClasses).filter(item => !this.instancedClasses.has(item)));
105
106        newlyInstancedClasses.forEach(sig => {
107            let ignoredCalls = this.ignoredCalls.get(sig);
108            if (ignoredCalls) {
109                ignoredCalls.forEach(call => {
110                    this.cg.addDynamicCallEdge(call.caller, call.callee, call.callStmt);
111                    newCallSites.push(new CallSite(call.callStmt, undefined, call.callee, call.caller));
112                });
113            }
114            this.instancedClasses.add(sig);
115            this.ignoredCalls.delete(sig);
116        });
117        return newCallSites;
118    }
119
120    private collectInstancedClassesInMethod(funcID: FuncID): Set<ClassSignature> {
121        let instancedClasses: Set<ClassSignature> = new Set();
122        let arkMethod = this.cg.getArkMethodByFuncID(funcID);
123
124        if (!arkMethod) {
125            logger.error(`can not find arkMethod by funcID`);
126            return instancedClasses;
127        }
128
129        let cfg = arkMethod!.getCfg();
130        if (!cfg) {
131            logger.error(`arkMethod ${arkMethod.getSignature().toString()} has no cfg`);
132            return instancedClasses;
133        }
134
135        for (let stmt of cfg!.getStmts()) {
136            let stmtExpr = stmt.getExprs()[0];
137            if (stmtExpr instanceof ArkNewExpr) {
138                let classSig: ClassSignature = (stmtExpr.getType() as ClassType).getClassSignature();
139                if (classSig != null) {
140                    // TODO: need to check if different stmt has single sig
141                    instancedClasses.add(classSig);
142                }
143            }
144        }
145        return instancedClasses;
146    }
147
148    public addIgnoredCalls(arkClass: ClassSignature, callerID: FuncID, calleeID: FuncID, invokeStmt: Stmt): void {
149        let classMap = this.ignoredCalls.get(arkClass) ?? new Set();
150        classMap.add({ caller: callerID, callee: calleeID, callStmt: invokeStmt });
151        this.ignoredCalls.set(arkClass, classMap);
152    }
153}
154