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