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