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