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 { ClassType, Type, UnknownType } from '../../base/Type'; 17import { BodyBuilder } from './BodyBuilder'; 18import { buildViewTree } from '../../graph/builder/ViewTreeBuilder'; 19import { ArkClass, ClassCategory } from '../ArkClass'; 20import { ArkMethod } from '../ArkMethod'; 21import ts from 'ohos-typescript'; 22import { 23 buildDecorators, 24 buildGenericType, 25 buildModifiers, 26 buildParameters, 27 buildReturnType, 28 buildTypeParameters, 29 handlePropertyAccessExpression, 30} from './builderUtils'; 31import Logger, { LOG_MODULE_TYPE } from '../../../utils/logger'; 32import { ArkParameterRef, ArkThisRef, ClosureFieldRef } from '../../base/Ref'; 33import { ArkBody } from '../ArkBody'; 34import { Cfg } from '../../graph/Cfg'; 35import { ArkInstanceInvokeExpr, ArkStaticInvokeExpr } from '../../base/Expr'; 36import { MethodSignature, MethodSubSignature } from '../ArkSignature'; 37import { ArkAssignStmt, ArkInvokeStmt, ArkReturnStmt, ArkReturnVoidStmt, Stmt } from '../../base/Stmt'; 38import { BasicBlock } from '../../graph/BasicBlock'; 39import { Local } from '../../base/Local'; 40import { Value } from '../../base/Value'; 41import { CONSTRUCTOR_NAME, SUPER_NAME, THIS_NAME } from '../../common/TSConst'; 42import { ANONYMOUS_METHOD_PREFIX, CALL_SIGNATURE_NAME, DEFAULT_ARK_CLASS_NAME, DEFAULT_ARK_METHOD_NAME, NAME_DELIMITER, NAME_PREFIX } from '../../common/Const'; 43import { ArkSignatureBuilder } from './ArkSignatureBuilder'; 44import { IRUtils } from '../../common/IRUtils'; 45import { ArkErrorCode } from '../../common/ArkError'; 46 47const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'ArkMethodBuilder'); 48 49export type MethodLikeNode = 50 | ts.FunctionDeclaration 51 | ts.MethodDeclaration 52 | ts.ConstructorDeclaration 53 | ts.ArrowFunction 54 | ts.AccessorDeclaration 55 | ts.FunctionExpression 56 | ts.MethodSignature 57 | ts.ConstructSignatureDeclaration 58 | ts.CallSignatureDeclaration 59 | ts.FunctionTypeNode; 60 61export function buildDefaultArkMethodFromArkClass(declaringClass: ArkClass, mtd: ArkMethod, sourceFile: ts.SourceFile, node?: ts.ModuleDeclaration): void { 62 mtd.setDeclaringArkClass(declaringClass); 63 64 const methodSubSignature = ArkSignatureBuilder.buildMethodSubSignatureFromMethodName(DEFAULT_ARK_METHOD_NAME, true); 65 const methodSignature = new MethodSignature(mtd.getDeclaringArkClass().getSignature(), methodSubSignature); 66 mtd.setImplementationSignature(methodSignature); 67 mtd.setLineCol(0); 68 69 const defaultMethodNode = node ? node : sourceFile; 70 71 let bodyBuilder = new BodyBuilder(mtd.getSignature(), defaultMethodNode, mtd, sourceFile); 72 mtd.setBodyBuilder(bodyBuilder); 73} 74 75export function buildArkMethodFromArkClass( 76 methodNode: MethodLikeNode, 77 declaringClass: ArkClass, 78 mtd: ArkMethod, 79 sourceFile: ts.SourceFile, 80 declaringMethod?: ArkMethod 81): void { 82 mtd.setDeclaringArkClass(declaringClass); 83 declaringMethod !== undefined && mtd.setOuterMethod(declaringMethod); 84 85 ts.isFunctionDeclaration(methodNode) && mtd.setAsteriskToken(methodNode.asteriskToken !== undefined); 86 87 // All MethodLikeNode except FunctionTypeNode have questionToken. 88 !ts.isFunctionTypeNode(methodNode) && mtd.setQuestionToken(methodNode.questionToken !== undefined); 89 90 mtd.setCode(methodNode.getText(sourceFile)); 91 mtd.setModifiers(buildModifiers(methodNode)); 92 mtd.setDecorators(buildDecorators(methodNode, sourceFile)); 93 94 if (methodNode.typeParameters) { 95 mtd.setGenericTypes(buildTypeParameters(methodNode.typeParameters, sourceFile, mtd)); 96 } 97 98 // build methodDeclareSignatures and methodSignature as well as corresponding positions 99 const methodName = buildMethodName(methodNode, declaringClass, sourceFile, declaringMethod); 100 const methodParameters: MethodParameter[] = []; 101 buildParameters(methodNode.parameters, mtd, sourceFile).forEach(parameter => { 102 buildGenericType(parameter.getType(), mtd); 103 methodParameters.push(parameter); 104 }); 105 let returnType = UnknownType.getInstance(); 106 if (methodNode.type) { 107 returnType = buildGenericType(buildReturnType(methodNode.type, sourceFile, mtd), mtd); 108 } 109 const methodSubSignature = new MethodSubSignature(methodName, methodParameters, returnType, mtd.isStatic()); 110 const methodSignature = new MethodSignature(mtd.getDeclaringArkClass().getSignature(), methodSubSignature); 111 const { line, character } = ts.getLineAndCharacterOfPosition(sourceFile, methodNode.getStart(sourceFile)); 112 if (isMethodImplementation(methodNode)) { 113 mtd.setImplementationSignature(methodSignature); 114 mtd.setLine(line + 1); 115 mtd.setColumn(character + 1); 116 let bodyBuilder = new BodyBuilder(mtd.getSignature(), methodNode, mtd, sourceFile); 117 mtd.setBodyBuilder(bodyBuilder); 118 } else { 119 mtd.setDeclareSignatures(methodSignature); 120 mtd.setDeclareLinesAndCols([line + 1], [character + 1]); 121 } 122 123 if (mtd.hasBuilderDecorator()) { 124 mtd.setViewTree(buildViewTree(mtd)); 125 } else if (declaringClass.hasComponentDecorator() && mtd.getSubSignature().toString() === 'build()' && !mtd.isStatic()) { 126 declaringClass.setViewTree(buildViewTree(mtd)); 127 } 128 checkAndUpdateMethod(mtd, declaringClass); 129 declaringClass.addMethod(mtd); 130 IRUtils.setComments(mtd, methodNode, sourceFile, mtd.getDeclaringArkFile().getScene().getOptions()); 131} 132 133function buildMethodName(node: MethodLikeNode, declaringClass: ArkClass, sourceFile: ts.SourceFile, declaringMethod?: ArkMethod): string { 134 let name: string = ''; 135 if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) { 136 name = node.name ? node.name.text : buildAnonymousMethodName(node, declaringClass); 137 } else if (ts.isFunctionTypeNode(node)) { 138 //TODO: check name type 139 name = node.name ? node.name.getText(sourceFile) : buildAnonymousMethodName(node, declaringClass); 140 } else if (ts.isMethodDeclaration(node) || ts.isMethodSignature(node)) { 141 if (ts.isIdentifier(node.name)) { 142 name = (node.name as ts.Identifier).text; 143 } else if (ts.isComputedPropertyName(node.name)) { 144 if (ts.isIdentifier(node.name.expression)) { 145 name = node.name.expression.text; 146 } else if (ts.isPropertyAccessExpression(node.name.expression)) { 147 name = handlePropertyAccessExpression(node.name.expression); 148 } else { 149 logger.warn('Other method ComputedPropertyName found!'); 150 } 151 } else { 152 logger.warn('Other method declaration type found!'); 153 } 154 } 155 //TODO, hard code 156 else if (ts.isConstructorDeclaration(node)) { 157 name = CONSTRUCTOR_NAME; 158 } else if (ts.isConstructSignatureDeclaration(node)) { 159 name = 'construct-signature'; 160 } else if (ts.isCallSignatureDeclaration(node)) { 161 name = CALL_SIGNATURE_NAME; 162 } else if (ts.isGetAccessor(node) && ts.isIdentifier(node.name)) { 163 name = 'Get-' + node.name.text; 164 } else if (ts.isSetAccessor(node) && ts.isIdentifier(node.name)) { 165 name = 'Set-' + node.name.text; 166 } else if (ts.isArrowFunction(node)) { 167 name = buildAnonymousMethodName(node, declaringClass); 168 } 169 170 if (declaringMethod !== undefined && !declaringMethod.isDefaultArkMethod()) { 171 name = buildNestedMethodName(name, declaringMethod.getName()); 172 } 173 return name; 174} 175 176function buildAnonymousMethodName(node: MethodLikeNode, declaringClass: ArkClass): string { 177 return `${ANONYMOUS_METHOD_PREFIX}${declaringClass.getAnonymousMethodNumber()}`; 178} 179 180function buildNestedMethodName(originName: string, declaringMethodName: string): string { 181 if (originName.startsWith(NAME_PREFIX)) { 182 return `${originName}${NAME_DELIMITER}${declaringMethodName}`; 183 } 184 return `${NAME_PREFIX}${originName}${NAME_DELIMITER}${declaringMethodName}`; 185} 186 187export class ObjectBindingPatternParameter { 188 private propertyName: string = ''; 189 private name: string = ''; 190 private optional: boolean = false; 191 192 constructor() {} 193 194 public getName(): string { 195 return this.name; 196 } 197 198 public setName(name: string): void { 199 this.name = name; 200 } 201 202 public getPropertyName(): string { 203 return this.propertyName; 204 } 205 206 public setPropertyName(propertyName: string): void { 207 this.propertyName = propertyName; 208 } 209 210 public isOptional(): boolean { 211 return this.optional; 212 } 213 214 public setOptional(optional: boolean): void { 215 this.optional = optional; 216 } 217} 218 219export class ArrayBindingPatternParameter { 220 private propertyName: string = ''; 221 private name: string = ''; 222 private optional: boolean = false; 223 224 constructor() {} 225 226 public getName(): string { 227 return this.name; 228 } 229 230 public setName(name: string): void { 231 this.name = name; 232 } 233 234 public getPropertyName(): string { 235 return this.propertyName; 236 } 237 238 public setPropertyName(propertyName: string): void { 239 this.propertyName = propertyName; 240 } 241 242 public isOptional(): boolean { 243 return this.optional; 244 } 245 246 public setOptional(optional: boolean): void { 247 this.optional = optional; 248 } 249} 250 251export class MethodParameter implements Value { 252 private name: string = ''; 253 private type!: Type; 254 private optional: boolean = false; 255 private dotDotDotToken: boolean = false; 256 private objElements: ObjectBindingPatternParameter[] = []; 257 private arrayElements: ArrayBindingPatternParameter[] = []; 258 259 constructor() {} 260 261 public getName(): string { 262 return this.name; 263 } 264 265 public setName(name: string): void { 266 this.name = name; 267 } 268 269 public getType(): Type { 270 return this.type; 271 } 272 273 public setType(type: Type): void { 274 this.type = type; 275 } 276 277 public isOptional(): boolean { 278 return this.optional; 279 } 280 281 public setOptional(optional: boolean): void { 282 this.optional = optional; 283 } 284 285 public hasDotDotDotToken(): boolean { 286 return this.dotDotDotToken; 287 } 288 289 public setDotDotDotToken(dotDotDotToken: boolean): void { 290 this.dotDotDotToken = dotDotDotToken; 291 } 292 293 public addObjElement(element: ObjectBindingPatternParameter): void { 294 this.objElements.push(element); 295 } 296 297 public getObjElements(): ObjectBindingPatternParameter[] { 298 return this.objElements; 299 } 300 301 public setObjElements(objElements: ObjectBindingPatternParameter[]): void { 302 this.objElements = objElements; 303 } 304 305 public addArrayElement(element: ArrayBindingPatternParameter): void { 306 this.arrayElements.push(element); 307 } 308 309 public getArrayElements(): ArrayBindingPatternParameter[] { 310 return this.arrayElements; 311 } 312 313 public setArrayElements(arrayElements: ArrayBindingPatternParameter[]): void { 314 this.arrayElements = arrayElements; 315 } 316 317 public getUses(): Value[] { 318 return []; 319 } 320} 321 322function needDefaultConstructorInClass(arkClass: ArkClass): boolean { 323 const originClassType = arkClass.getCategory(); 324 return ( 325 arkClass.getMethodWithName(CONSTRUCTOR_NAME) === null && 326 (originClassType === ClassCategory.CLASS || originClassType === ClassCategory.OBJECT) && 327 arkClass.getName() !== DEFAULT_ARK_CLASS_NAME && 328 !arkClass.isDeclare() 329 ); 330} 331 332function recursivelyCheckAndBuildSuperConstructor(arkClass: ArkClass): void { 333 let superClass: ArkClass | null = arkClass.getSuperClass(); 334 while (superClass !== null) { 335 if (superClass.getMethodWithName(CONSTRUCTOR_NAME) === null) { 336 buildDefaultConstructor(superClass); 337 } 338 superClass = superClass.getSuperClass(); 339 } 340} 341 342export function buildDefaultConstructor(arkClass: ArkClass): boolean { 343 if (!needDefaultConstructorInClass(arkClass)) { 344 return false; 345 } 346 347 recursivelyCheckAndBuildSuperConstructor(arkClass); 348 349 const defaultConstructor: ArkMethod = new ArkMethod(); 350 defaultConstructor.setDeclaringArkClass(arkClass); 351 defaultConstructor.setCode(''); 352 defaultConstructor.setIsGeneratedFlag(true); 353 defaultConstructor.setLineCol(0); 354 355 const thisLocal = new Local(THIS_NAME, new ClassType(arkClass.getSignature())); 356 const locals: Set<Local> = new Set([thisLocal]); 357 const basicBlock = new BasicBlock(); 358 basicBlock.setId(0); 359 360 let parameters: MethodParameter[] = []; 361 let parameterArgs: Value[] = []; 362 const superConstructor = arkClass.getSuperClass()?.getMethodWithName(CONSTRUCTOR_NAME); 363 if (superConstructor) { 364 parameters = superConstructor.getParameters(); 365 366 for (let index = 0; index < parameters.length; index++) { 367 const parameterRef = new ArkParameterRef(index, parameters[index].getType()); 368 const parameterLocal = new Local(parameters[index].getName(), parameterRef.getType()); 369 locals.add(parameterLocal); 370 parameterArgs.push(parameterLocal); 371 basicBlock.addStmt(new ArkAssignStmt(parameterLocal, parameterRef)); 372 index++; 373 } 374 } 375 376 basicBlock.addStmt(new ArkAssignStmt(thisLocal, new ArkThisRef(new ClassType(arkClass.getSignature())))); 377 378 if (superConstructor) { 379 const superMethodSubSignature = new MethodSubSignature(SUPER_NAME, parameters, superConstructor.getReturnType()); 380 const superMethodSignature = new MethodSignature(arkClass.getSignature(), superMethodSubSignature); 381 const superInvokeExpr = new ArkStaticInvokeExpr(superMethodSignature, parameterArgs); 382 basicBlock.addStmt(new ArkInvokeStmt(superInvokeExpr)); 383 } 384 385 const methodSubSignature = new MethodSubSignature(CONSTRUCTOR_NAME, parameters, thisLocal.getType(), defaultConstructor.isStatic()); 386 defaultConstructor.setImplementationSignature(new MethodSignature(arkClass.getSignature(), methodSubSignature)); 387 basicBlock.addStmt(new ArkReturnStmt(thisLocal)); 388 389 const cfg = new Cfg(); 390 cfg.addBlock(basicBlock); 391 cfg.setStartingStmt(basicBlock.getHead()!); 392 cfg.setDeclaringMethod(defaultConstructor); 393 cfg.getStmts().forEach(s => s.setCfg(cfg)); 394 395 defaultConstructor.setBody(new ArkBody(locals, cfg)); 396 checkAndUpdateMethod(defaultConstructor, arkClass); 397 arkClass.addMethod(defaultConstructor); 398 399 return true; 400} 401 402export function buildInitMethod(initMethod: ArkMethod, fieldInitializerStmts: Stmt[], thisLocal: Local): void { 403 const classType = new ClassType(initMethod.getDeclaringArkClass().getSignature()); 404 const assignStmt = new ArkAssignStmt(thisLocal, new ArkThisRef(classType)); 405 const block = new BasicBlock(); 406 block.setId(0); 407 block.addStmt(assignStmt); 408 const locals: Set<Local> = new Set([thisLocal]); 409 for (const stmt of fieldInitializerStmts) { 410 block.addStmt(stmt); 411 if (stmt.getDef() && stmt.getDef() instanceof Local) { 412 locals.add(stmt.getDef() as Local); 413 } 414 } 415 block.addStmt(new ArkReturnVoidStmt()); 416 const cfg = new Cfg(); 417 cfg.addBlock(block); 418 for (const stmt of block.getStmts()) { 419 stmt.setCfg(cfg); 420 } 421 cfg.setStartingStmt(assignStmt); 422 cfg.buildDefUseStmt(locals); 423 cfg.setDeclaringMethod(initMethod); 424 initMethod.setBody(new ArkBody(locals, cfg)); 425} 426 427export function addInitInConstructor(constructor: ArkMethod): void { 428 const thisLocal = constructor.getBody()?.getLocals().get(THIS_NAME); 429 if (!thisLocal) { 430 return; 431 } 432 const cfg = constructor.getCfg(); 433 if (cfg === undefined) { 434 return; 435 } 436 const blocks = cfg.getBlocks(); 437 const firstBlockStmts = [...blocks][0].getStmts(); 438 let index = 0; 439 for (let i = 0; i < firstBlockStmts.length; i++) { 440 const stmt = firstBlockStmts[i]; 441 if (stmt instanceof ArkInvokeStmt && stmt.getInvokeExpr().getMethodSignature().getMethodSubSignature().getMethodName() === SUPER_NAME) { 442 index++; 443 continue; 444 } 445 if (stmt instanceof ArkAssignStmt) { 446 const rightOp = stmt.getRightOp(); 447 if (rightOp instanceof ArkParameterRef || rightOp instanceof ArkThisRef || rightOp instanceof ClosureFieldRef) { 448 index++; 449 continue; 450 } 451 } 452 break; 453 } 454 const initInvokeStmt = new ArkInvokeStmt( 455 new ArkInstanceInvokeExpr(thisLocal, constructor.getDeclaringArkClass().getInstanceInitMethod().getSignature(), []) 456 ); 457 initInvokeStmt.setCfg(cfg); 458 firstBlockStmts.splice(index, 0, initInvokeStmt); 459} 460 461export function isMethodImplementation(node: MethodLikeNode): boolean { 462 if ( 463 ts.isFunctionDeclaration(node) || 464 ts.isMethodDeclaration(node) || 465 ts.isConstructorDeclaration(node) || 466 ts.isGetAccessorDeclaration(node) || 467 ts.isSetAccessorDeclaration(node) || 468 ts.isFunctionExpression(node) || 469 ts.isArrowFunction(node) 470 ) { 471 if (node.body !== undefined) { 472 return true; 473 } 474 } 475 return false; 476} 477 478export function checkAndUpdateMethod(method: ArkMethod, cls: ArkClass): void { 479 let presentMethod: ArkMethod | null; 480 if (method.isStatic()) { 481 presentMethod = cls.getStaticMethodWithName(method.getName()); 482 } else { 483 presentMethod = cls.getMethodWithName(method.getName()); 484 } 485 if (presentMethod === null) { 486 return; 487 } 488 489 if (method.validate().errCode !== ArkErrorCode.OK || presentMethod.validate().errCode !== ArkErrorCode.OK) { 490 return; 491 } 492 const presentDeclareSignatures = presentMethod.getDeclareSignatures(); 493 const presentDeclareLineCols = presentMethod.getDeclareLineCols(); 494 const presentImplSignature = presentMethod.getImplementationSignature(); 495 const newDeclareSignature = method.getDeclareSignatures(); 496 const newDeclareLineCols = method.getDeclareLineCols(); 497 const newImplSignature = method.getImplementationSignature(); 498 499 if (presentDeclareSignatures !== null && presentImplSignature === null) { 500 if (newDeclareSignature === null || presentMethod.getDeclareSignatureIndex(newDeclareSignature[0]) >= 0) { 501 method.setDeclareSignatures(presentDeclareSignatures); 502 method.setDeclareLineCols(presentDeclareLineCols as number[]); 503 } else { 504 method.setDeclareSignatures(presentDeclareSignatures.concat(newDeclareSignature)); 505 method.setDeclareLineCols((presentDeclareLineCols as number[]).concat(newDeclareLineCols as number[])); 506 } 507 return; 508 } 509 if (presentDeclareSignatures === null && presentImplSignature !== null) { 510 if (newImplSignature === null) { 511 method.setImplementationSignature(presentImplSignature); 512 method.setLineCol(presentMethod.getLineCol() as number); 513 } 514 return; 515 } 516} 517