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 { ArkClass } from '../core/model/ArkClass'; 18import { ArkMethod } from '../core/model/ArkMethod'; 19import { ClassSignature, MethodSignature } from '../core/model/ArkSignature'; 20import Logger, { LOG_MODULE_TYPE } from './logger'; 21import { ModelUtils } from '../core/common/ModelUtils'; 22 23const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'callGraphUtils'); 24 25export class MethodSignatureManager { 26 private _workList: MethodSignature[] = []; 27 private _processedList: MethodSignature[] = []; 28 29 get workList(): MethodSignature[] { 30 return this._workList; 31 } 32 33 set workList(list: MethodSignature[]) { 34 this._workList = list; 35 } 36 37 get processedList(): MethodSignature[] { 38 return this._processedList; 39 } 40 41 set processedList(list: MethodSignature[]) { 42 this._processedList = list; 43 } 44 45 public findInWorkList(signature: MethodSignature): MethodSignature | undefined { 46 return this.workList.find(item => item === signature); 47 } 48 49 public findInProcessedList(signature: MethodSignature): boolean { 50 let result = this.processedList.find(item => item.toString() === signature.toString()); 51 return typeof result !== 'undefined'; 52 } 53 54 public addToWorkList(signature: MethodSignature): void { 55 if (!isItemRegistered<MethodSignature>(signature, this.workList, (a, b) => a.toString() === b.toString())) { 56 this.workList.push(signature); 57 } 58 } 59 60 public addToProcessedList(signature: MethodSignature): void { 61 if (!isItemRegistered<MethodSignature>(signature, this.processedList, (a, b) => a === b)) { 62 this.processedList.push(signature); 63 } 64 } 65 66 public removeFromWorkList(signature: MethodSignature): void { 67 this.workList = this.workList.filter(item => item !== signature); 68 } 69 70 public removeFromProcessedList(signature: MethodSignature): void { 71 this.processedList = this.processedList.filter(item => item.toString() !== signature.toString()); 72 } 73} 74 75export class SceneManager { 76 private _scene!: Scene; 77 78 get scene(): Scene { 79 return this._scene; 80 } 81 82 set scene(value: Scene) { 83 this._scene = value; 84 } 85 86 public getMethod(method: MethodSignature): ArkMethod | null { 87 let targetMethod = this._scene.getMethod(method); 88 if (targetMethod != null) { 89 return targetMethod; 90 } 91 // 支持SDK调用解析 92 let file = this._scene.getFile(method.getDeclaringClassSignature().getDeclaringFileSignature()); 93 if (file) { 94 const methods = ModelUtils.getAllMethodsInFile(file); 95 for (let methodUnderFile of methods) { 96 if (method.toString() === methodUnderFile.getSignature().toString()) { 97 return methodUnderFile; 98 } 99 } 100 } 101 return targetMethod; 102 } 103 104 public getClass(arkClass: ClassSignature): ArkClass | null { 105 if (typeof arkClass.getClassName() === 'undefined') { 106 return null; 107 } 108 let classInstance = this._scene.getClass(arkClass); 109 if (classInstance != null) { 110 return classInstance; 111 } 112 let sdkOrTargetProjectFile = this._scene.getFile(arkClass.getDeclaringFileSignature()); 113 // TODO: support get sdk class, targetProject class waiting to be supported 114 if (sdkOrTargetProjectFile != null) { 115 for (let classUnderFile of ModelUtils.getAllClassesInFile(sdkOrTargetProjectFile)) { 116 if (classUnderFile.getSignature().toString() === arkClass.toString()) { 117 return classUnderFile; 118 } 119 } 120 } 121 return classInstance; 122 } 123 124 public getExtendedClasses(arkClass: ClassSignature): ArkClass[] { 125 let sourceClass = this.getClass(arkClass); 126 let classList = [sourceClass]; // 待处理类 127 let extendedClasses: ArkClass[] = []; // 已经处理的类 128 129 while (classList.length > 0) { 130 let tempClass = classList.shift(); 131 if (tempClass == null) { 132 continue; 133 } 134 let firstLevelSubclasses: ArkClass[] = Array.from(tempClass.getExtendedClasses().values()); 135 136 if (!firstLevelSubclasses) { 137 continue; 138 } 139 140 for (let subclass of firstLevelSubclasses) { 141 if (!isItemRegistered<ArkClass>(subclass, extendedClasses, (a, b) => a.getSignature().toString() === b.getSignature().toString())) { 142 // 子类未处理,加入到classList 143 classList.push(subclass); 144 } 145 } 146 147 // 当前类处理完毕,标记为已处理 148 if (!isItemRegistered<ArkClass>(tempClass, extendedClasses, (a, b) => a.getSignature().toString() === b.getSignature().toString())) { 149 extendedClasses.push(tempClass); 150 } 151 } 152 return extendedClasses; 153 } 154} 155 156export function isItemRegistered<T>(item: T, array: T[], compareFunc: (a: T, b: T) => boolean): boolean { 157 for (let tempItem of array) { 158 if (compareFunc(tempItem, item)) { 159 return true; 160 } 161 } 162 return false; 163} 164 165export function splitStringWithRegex(input: string): string[] { 166 // 正则表达式匹配 "a.b.c()" 并捕获 "a" "b" "c" 167 const regex = /^(\w+)\.(\w+)\.(\w+)\(\)$/; 168 const match = input.match(regex); 169 170 if (match) { 171 // 返回捕获的部分,忽略整个匹配结果 172 return match.slice(1); 173 } else { 174 // 如果输入不匹配,返回空数组 175 return []; 176 } 177} 178 179export function printCallGraphDetails(methods: Set<MethodSignature>, calls: Map<MethodSignature, MethodSignature[]>, rootDir: string): void { 180 // 打印 Methods 181 logger.info('Call Graph:\n'); 182 logger.info('\tMethods:'); 183 methods.forEach(method => { 184 logger.info(`\t\t${method}`); 185 }); 186 187 // 打印 Calls 188 logger.info('\tCalls:'); 189 const arrow = '->'; 190 calls.forEach((calledMethods, method) => { 191 // 对于每个调用源,只打印一次调用源和第一个目标方法 192 const modifiedMethodName = `<${method}`; 193 logger.info(`\t\t${modifiedMethodName.padEnd(4)} ${arrow}`); 194 195 for (let i = 0; i < calledMethods.length; i++) { 196 const modifiedCalledMethod = `\t\t<${calledMethods[i]}`; 197 logger.info(`\t\t${modifiedCalledMethod}`); 198 } 199 logger.info('\n'); 200 }); 201} 202 203export function extractLastBracketContent(input: string): string { 204 // 正则表达式匹配最后一个尖括号内的内容,直到遇到左圆括号 205 const match = input.match(/<([^<>]*)\(\)>$/); 206 if (match && match[1]) { 207 return match[1].trim(); 208 } 209 return ''; 210} 211