• 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 { COMPONENT_LIFECYCLE_METHOD_NAME, getCallbackMethodFromStmt, LIFECYCLE_METHOD_NAME } from '../../utils/entryMethodUtils';
18import { Constant } from '../base/Constant';
19import { AbstractInvokeExpr, ArkConditionExpr, ArkInstanceInvokeExpr, ArkNewExpr, ArkStaticInvokeExpr, RelationalBinaryOperator } from '../base/Expr';
20import { Local } from '../base/Local';
21import { ArkAssignStmt, ArkIfStmt, ArkInvokeStmt, ArkReturnVoidStmt } from '../base/Stmt';
22import { ClassType, NumberType, Type } from '../base/Type';
23import { BasicBlock } from '../graph/BasicBlock';
24import { Cfg } from '../graph/Cfg';
25import { ArkBody } from '../model/ArkBody';
26import { ArkClass } from '../model/ArkClass';
27import { ArkFile, Language } from '../model/ArkFile';
28import { ArkMethod } from '../model/ArkMethod';
29import { ClassSignature, FileSignature, MethodSignature } from '../model/ArkSignature';
30import { ArkSignatureBuilder } from '../model/builder/ArkSignatureBuilder';
31import { CONSTRUCTOR_NAME } from './TSConst';
32import { checkAndUpdateMethod } from '../model/builder/ArkMethodBuilder';
33import { ValueUtil } from './ValueUtil';
34
35/**
36收集所有的onCreate,onStart等函数,构造一个虚拟函数,具体为:
37%statInit()
38...
39count = 0
40while (true) {
41    if (count === 1) {
42        temp1 = new ability
43        temp2 = new want
44        temp1.onCreate(temp2)
45    }
46    if (count === 2) {
47        onDestroy()
48    }
49    ...
50    if (count === *) {
51        callbackMethod1()
52    }
53    ...
54}
55return
56如果是instanceInvoke还要先实例化对象,如果是其他文件的类或者方法还要添加import信息
57 */
58
59export class DummyMainCreater {
60    private entryMethods: ArkMethod[] = [];
61    private classLocalMap: Map<ArkMethod, Local | null> = new Map();
62    private dummyMain: ArkMethod = new ArkMethod();
63    private scene: Scene;
64    private tempLocalIndex: number = 0;
65
66    constructor(scene: Scene) {
67        this.scene = scene;
68        // Currently get entries from module.json5 can't visit all of abilities
69        // Todo: handle ablity/component jump, then get entries from module.json5
70        this.entryMethods = this.getMethodsFromAllAbilities();
71        this.entryMethods.push(...this.getEntryMethodsFromComponents());
72        this.entryMethods.push(...this.getCallbackMethods());
73    }
74
75    public setEntryMethods(methods: ArkMethod[]): void {
76        this.entryMethods = methods;
77    }
78
79    public createDummyMain(): void {
80        const dummyMainFile = new ArkFile(Language.UNKNOWN);
81        dummyMainFile.setScene(this.scene);
82        const dummyMainFileSignature = new FileSignature(this.scene.getProjectName(), '@dummyFile');
83        dummyMainFile.setFileSignature(dummyMainFileSignature);
84        this.scene.setFile(dummyMainFile);
85        const dummyMainClass = new ArkClass();
86        dummyMainClass.setDeclaringArkFile(dummyMainFile);
87        const dummyMainClassSignature = new ClassSignature(
88            '@dummyClass',
89            dummyMainClass.getDeclaringArkFile().getFileSignature(),
90            dummyMainClass.getDeclaringArkNamespace()?.getSignature() || null
91        );
92        dummyMainClass.setSignature(dummyMainClassSignature);
93        dummyMainFile.addArkClass(dummyMainClass);
94
95        this.dummyMain = new ArkMethod();
96        this.dummyMain.setDeclaringArkClass(dummyMainClass);
97        const methodSubSignature = ArkSignatureBuilder.buildMethodSubSignatureFromMethodName('@dummyMain');
98        const methodSignature = new MethodSignature(this.dummyMain.getDeclaringArkClass().getSignature(), methodSubSignature);
99        this.dummyMain.setImplementationSignature(methodSignature);
100        this.dummyMain.setLineCol(0);
101        checkAndUpdateMethod(this.dummyMain, dummyMainClass);
102        dummyMainClass.addMethod(this.dummyMain);
103
104        let defaultMethods: ArkMethod[] = [];
105        for (const method of this.entryMethods) {
106            if (method.getDeclaringArkClass().isDefaultArkClass() || method.isStatic()) {
107                defaultMethods.push(method);
108                continue;
109            }
110            const declaringArkClass = method.getDeclaringArkClass();
111            let newLocal: Local | null = null;
112            for (const local of this.classLocalMap.values()) {
113                if ((local?.getType() as ClassType).getClassSignature() === declaringArkClass.getSignature()) {
114                    newLocal = local;
115                    break;
116                }
117            }
118            if (!newLocal) {
119                newLocal = new Local('%' + this.tempLocalIndex, new ClassType(declaringArkClass.getSignature()));
120                this.tempLocalIndex++;
121            }
122            this.classLocalMap.set(method, newLocal);
123        }
124        for (const defaultMethod of defaultMethods) {
125            this.classLocalMap.set(defaultMethod, null);
126        }
127        const localSet = new Set(Array.from(this.classLocalMap.values()).filter((value): value is Local => value !== null));
128        const dummyBody = new ArkBody(localSet, this.createDummyMainCfg());
129        this.dummyMain.setBody(dummyBody);
130        this.addCfg2Stmt();
131        this.scene.addToMethodsMap(this.dummyMain);
132    }
133
134    private addStaticInit(dummyCfg: Cfg, firstBlock: BasicBlock): void {
135        let isStartingStmt = true;
136        for (const method of this.scene.getStaticInitMethods()) {
137            const staticInvokeExpr = new ArkStaticInvokeExpr(method.getSignature(), []);
138            const invokeStmt = new ArkInvokeStmt(staticInvokeExpr);
139            if (isStartingStmt) {
140                dummyCfg.setStartingStmt(invokeStmt);
141                isStartingStmt = false;
142            }
143            firstBlock.addStmt(invokeStmt);
144        }
145    }
146
147    private addClassInit(firstBlock: BasicBlock): void {
148        const locals = Array.from(new Set(this.classLocalMap.values()));
149        for (const local of locals) {
150            if (!local) {
151                continue;
152            }
153            let clsType = local.getType() as ClassType;
154            let cls = this.scene.getClass(clsType.getClassSignature())!;
155            const assStmt = new ArkAssignStmt(local!, new ArkNewExpr(clsType));
156            firstBlock.addStmt(assStmt);
157            local.setDeclaringStmt(assStmt);
158            let consMtd = cls.getMethodWithName(CONSTRUCTOR_NAME);
159            if (consMtd) {
160                let ivkExpr = new ArkInstanceInvokeExpr(local, consMtd.getSignature(), []);
161                let ivkStmt = new ArkInvokeStmt(ivkExpr);
162                firstBlock.addStmt(ivkStmt);
163            }
164        }
165    }
166
167    private addParamInit(method: ArkMethod, paramLocals: Local[], invokeBlock: BasicBlock): void {
168        let paramIdx = 0;
169        for (const param of method.getParameters()) {
170            let paramType: Type | undefined = param.getType();
171            // In ArkIR from abc scenario, param type is undefined in some cases
172            // Then try to get it from super class(SDK)
173            // TODO - need handle method overload to get the correct method
174            if (!paramType) {
175                let superCls = method.getDeclaringArkClass().getSuperClass();
176                let methodInSuperCls = superCls?.getMethodWithName(method.getName());
177                if (methodInSuperCls) {
178                    paramType = methodInSuperCls.getParameters()[paramIdx]?.getType();
179                    method = methodInSuperCls;
180                }
181            }
182            const paramLocal = new Local('%' + this.tempLocalIndex++, paramType);
183            paramLocals.push(paramLocal);
184            if (paramType instanceof ClassType) {
185                const assStmt = new ArkAssignStmt(paramLocal, new ArkNewExpr(paramType));
186                paramLocal.setDeclaringStmt(assStmt);
187                invokeBlock.addStmt(assStmt);
188            }
189            paramIdx++;
190        }
191    }
192
193    private addBranches(whileBlock: BasicBlock, countLocal: Local, dummyCfg: Cfg): void {
194        let lastBlocks: BasicBlock[] = [whileBlock];
195        let count = 0;
196        for (let method of this.entryMethods) {
197            count++;
198            const condition = new ArkConditionExpr(countLocal, new Constant(count.toString(), NumberType.getInstance()), RelationalBinaryOperator.Equality);
199            const ifStmt = new ArkIfStmt(condition);
200            const ifBlock = new BasicBlock();
201            ifBlock.addStmt(ifStmt);
202            dummyCfg.addBlock(ifBlock);
203            for (const block of lastBlocks) {
204                ifBlock.addPredecessorBlock(block);
205                block.addSuccessorBlock(ifBlock);
206            }
207            const invokeBlock = new BasicBlock();
208            const paramLocals: Local[] = [];
209            this.addParamInit(method, paramLocals, invokeBlock);
210            const local = this.classLocalMap.get(method);
211            let invokeExpr: AbstractInvokeExpr;
212            if (local) {
213                invokeExpr = new ArkInstanceInvokeExpr(local, method.getSignature(), paramLocals);
214            } else {
215                invokeExpr = new ArkStaticInvokeExpr(method.getSignature(), paramLocals);
216            }
217            const invokeStmt = new ArkInvokeStmt(invokeExpr);
218            invokeBlock.addStmt(invokeStmt);
219            dummyCfg.addBlock(invokeBlock);
220            ifBlock.addSuccessorBlock(invokeBlock);
221            invokeBlock.addPredecessorBlock(ifBlock);
222            lastBlocks = [ifBlock, invokeBlock];
223        }
224        for (const block of lastBlocks) {
225            block.addSuccessorBlock(whileBlock);
226            whileBlock.addPredecessorBlock(block);
227        }
228    }
229
230    private createDummyMainCfg(): Cfg {
231        const dummyCfg = new Cfg();
232        dummyCfg.setDeclaringMethod(this.dummyMain);
233        const firstBlock = new BasicBlock();
234        this.addStaticInit(dummyCfg, firstBlock);
235        this.addClassInit(firstBlock);
236        const countLocal = new Local('count', NumberType.getInstance());
237        const zero = ValueUtil.getOrCreateNumberConst(0);
238        const countAssignStmt = new ArkAssignStmt(countLocal, zero);
239        const truE = ValueUtil.getBooleanConstant(true);
240        const conditionTrue = new ArkConditionExpr(truE, zero, RelationalBinaryOperator.Equality);
241        const whileStmt = new ArkIfStmt(conditionTrue);
242        firstBlock.addStmt(countAssignStmt);
243        dummyCfg.addBlock(firstBlock);
244        dummyCfg.setStartingStmt(firstBlock.getStmts()[0]);
245        const whileBlock = new BasicBlock();
246        whileBlock.addStmt(whileStmt);
247        dummyCfg.addBlock(whileBlock);
248        firstBlock.addSuccessorBlock(whileBlock);
249        whileBlock.addPredecessorBlock(firstBlock);
250        this.addBranches(whileBlock, countLocal, dummyCfg);
251        const returnStmt = new ArkReturnVoidStmt();
252        const returnBlock = new BasicBlock();
253        returnBlock.addStmt(returnStmt);
254        dummyCfg.addBlock(returnBlock);
255        whileBlock.addSuccessorBlock(returnBlock);
256        returnBlock.addPredecessorBlock(whileBlock);
257        return dummyCfg;
258    }
259
260    private addCfg2Stmt(): void {
261        const cfg = this.dummyMain.getCfg();
262        if (!cfg) {
263            return;
264        }
265        for (const block of cfg.getBlocks()) {
266            for (const stmt of block.getStmts()) {
267                stmt.setCfg(cfg);
268            }
269        }
270    }
271
272    public getDummyMain(): ArkMethod {
273        return this.dummyMain;
274    }
275
276    private getEntryMethodsFromComponents(): ArkMethod[] {
277        const COMPONENT_BASE_CLASSES = ['CustomComponent', 'ViewPU'];
278        let methods: ArkMethod[] = [];
279        this.scene
280            .getClasses()
281            .filter(cls => {
282                if (COMPONENT_BASE_CLASSES.includes(cls.getSuperClassName())) {
283                    return true;
284                }
285                if (cls.hasDecorator('Component')) {
286                    return true;
287                }
288                return false;
289            })
290            .forEach(cls => {
291                methods.push(...cls.getMethods().filter(mtd => COMPONENT_LIFECYCLE_METHOD_NAME.includes(mtd.getName())));
292            });
293        return methods;
294    }
295
296    private classInheritsAbility(arkClass: ArkClass): boolean {
297        const ABILITY_BASE_CLASSES = ['UIExtensionAbility', 'Ability', 'FormExtensionAbility', 'UIAbility', 'BackupExtensionAbility'];
298        if (ABILITY_BASE_CLASSES.includes(arkClass.getSuperClassName())) {
299            return true;
300        }
301        let superClass = arkClass.getSuperClass();
302        while (superClass) {
303            if (ABILITY_BASE_CLASSES.includes(superClass.getSuperClassName())) {
304                return true;
305            }
306            superClass = superClass.getSuperClass();
307        }
308        return false;
309    }
310
311    public getMethodsFromAllAbilities(): ArkMethod[] {
312        let methods: ArkMethod[] = [];
313        this.scene
314            .getClasses()
315            .filter(cls => this.classInheritsAbility(cls))
316            .forEach(cls => {
317                methods.push(...cls.getMethods().filter(mtd => LIFECYCLE_METHOD_NAME.includes(mtd.getName())));
318            });
319        return methods;
320    }
321
322    public getCallbackMethods(): ArkMethod[] {
323        const callbackMethods: ArkMethod[] = [];
324        this.scene.getMethods().forEach(method => {
325            if (!method.getCfg()) {
326                return;
327            }
328            method
329                .getCfg()!
330                .getStmts()
331                .forEach(stmt => {
332                    const cbMethod = getCallbackMethodFromStmt(stmt, this.scene);
333                    if (cbMethod && !callbackMethods.includes(cbMethod)) {
334                        callbackMethods.push(cbMethod);
335                    }
336                });
337        });
338        return callbackMethods;
339    }
340}
341