• 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 { 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