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 { Constant } from '../../core/base/Constant'; 17import { 18 AbstractBinopExpr, 19 AbstractExpr, 20 ArkAwaitExpr, 21 ArkCastExpr, 22 ArkDeleteExpr, 23 ArkInstanceInvokeExpr, 24 ArkInstanceOfExpr, 25 ArkNewArrayExpr, 26 ArkNewExpr, 27 ArkNormalBinopExpr, 28 ArkStaticInvokeExpr, 29 ArkTypeOfExpr, 30 ArkUnopExpr, 31 ArkYieldExpr, 32 NormalBinaryOperator, 33} from '../../core/base/Expr'; 34import { Local } from '../../core/base/Local'; 35import { ArkClass, ClassCategory } from '../../core/model/ArkClass'; 36import { ArkMethod } from '../../core/model/ArkMethod'; 37import { ClassSignature, MethodSignature } from '../../core/model/ArkSignature'; 38import { ArkCodeBuffer } from '../ArkStream'; 39import Logger, { LOG_MODULE_TYPE } from '../../utils/logger'; 40import { PrinterUtils } from '../base/PrinterUtils'; 41import { SourceMethod } from './SourceMethod'; 42import { 43 AliasType, 44 ArrayType, 45 ClassType, 46 FunctionType, 47 GenericType, 48 IntersectionType, 49 LiteralType, 50 PrimitiveType, 51 StringType, 52 TupleType, 53 Type, 54 UnclearReferenceType, 55 UnionType, 56 UnknownType, 57 VoidType, 58} from '../../core/base/Type'; 59import { SourceClass } from './SourceClass'; 60import { Value } from '../../core/base/Value'; 61import { AbstractRef, ArkArrayRef, ArkInstanceFieldRef, ArkStaticFieldRef, ArkThisRef } from '../../core/base/Ref'; 62import { ArkFile } from '../../core/model/ArkFile'; 63import { 64 COMPONENT_CREATE_FUNCTION, 65 COMPONENT_CUSTOMVIEW, 66 COMPONENT_IF, 67 COMPONENT_POP_FUNCTION 68} from '../../core/common/EtsConst'; 69import { INSTANCE_INIT_METHOD_NAME } from '../../core/common/Const'; 70import { ArkAssignStmt } from '../../core/base/Stmt'; 71import { ArkNamespace } from '../../core/model/ArkNamespace'; 72import { AbstractTypeExpr, KeyofTypeExpr, TypeQueryExpr } from '../../core/base/TypeExpr'; 73import { ArkBaseModel } from '../../core/model/ArkBaseModel'; 74import { ArkField } from '../../core/model/ArkField'; 75import { ExportInfo } from '../../core/model/ArkExport'; 76import { ImportInfo } from '../../core/model/ArkImport'; 77import { BIGINT_KEYWORD, SUPER_NAME } from '../../core/common/TSConst'; 78 79const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'SourceTransformer'); 80 81export interface TransformerContext { 82 getArkFile(): ArkFile; 83 84 getDeclaringArkNamespace(): ArkNamespace | undefined; 85 86 getMethod(signature: MethodSignature): ArkMethod | null; 87 88 getClass(signature: ClassSignature): ArkClass | null; 89 90 getPrinter(): ArkCodeBuffer; 91 92 transTemp2Code(temp: Local, isLeftOp: boolean): string; 93 94 isInBuilderMethod(): boolean; 95} 96 97export class SourceTransformer { 98 protected context: TransformerContext; 99 100 constructor(context: TransformerContext) { 101 this.context = context; 102 } 103 104 private anonymousMethodToString(method: ArkMethod, indent: string): string { 105 let mtdPrinter = new SourceMethod(method, indent); 106 mtdPrinter.setInBuilder(this.context.isInBuilderMethod()); 107 return mtdPrinter.dump().trimStart(); 108 } 109 110 private anonymousClassToString(cls: ArkClass, indent: string): string { 111 let clsPrinter = new SourceClass(cls, indent); 112 return clsPrinter.dump().trimStart(); 113 } 114 115 public instanceInvokeExprToString(invokeExpr: ArkInstanceInvokeExpr, isAttr: boolean): string { 116 let methodName = invokeExpr.getMethodSignature().getMethodSubSignature().getMethodName(); 117 if (methodName === INSTANCE_INIT_METHOD_NAME) { 118 return ''; 119 } 120 let args: string[] = []; 121 invokeExpr.getArgs().forEach(v => { 122 args.push(this.valueToString(v)); 123 }); 124 let genericCode = isAttr ? '' : this.genericTypesToString(invokeExpr.getRealGenericTypes()); 125 if (isAttr && this.context.isInBuilderMethod()) { 126 return `.${methodName}${genericCode}(${args.join(', ')})`; 127 } 128 129 return `${this.valueToString(invokeExpr.getBase())}.${methodName}${genericCode}(${args.join(', ')})`; 130 } 131 132 private transBuilderMethod(className: string, methodName: string, args: string[], invokeExpr: ArkStaticInvokeExpr, genericCode: string): string | null { 133 if (className === COMPONENT_CUSTOMVIEW) { 134 if (methodName === COMPONENT_CREATE_FUNCTION) { 135 // Anonymous @Builder method 136 if (args.length > 1) { 137 // remove the substring '() =>' or '(x, y): type =>' at the beginning of args[1] 138 const pattern = /^\([^)]*\)\s*:\s*\w*\s*=>\s*/; 139 args[1] = args[1].replace(pattern, ''); 140 } 141 return `${args.join(' ')}`; 142 } 143 if (methodName === COMPONENT_POP_FUNCTION) { 144 return ''; 145 } 146 } 147 148 if (PrinterUtils.isComponentCreate(invokeExpr)) { 149 if (className === COMPONENT_IF) { 150 return `if (${args.join(', ')})`; 151 } 152 return `${className}${genericCode}(${args.join(', ')})`; 153 } 154 155 if (PrinterUtils.isComponentIfBranchInvoke(invokeExpr)) { 156 let arg0 = invokeExpr.getArg(0) as Constant; 157 if (arg0.getValue() === '0') { 158 return ``; 159 } else { 160 return '} else {'; 161 } 162 } 163 164 if (PrinterUtils.isComponentPop(invokeExpr)) { 165 return '}'; 166 } 167 168 return null; 169 } 170 171 public staticInvokeExprToString(invokeExpr: ArkStaticInvokeExpr): string { 172 let methodSignature = invokeExpr.getMethodSignature(); 173 let method = this.context.getMethod(methodSignature); 174 if (method && PrinterUtils.isAnonymousMethod(method.getName())) { 175 return this.anonymousMethodToString(method, this.context.getPrinter().getIndent()); 176 } 177 178 let classSignature = methodSignature.getDeclaringClassSignature(); 179 let className = PrinterUtils.getStaticInvokeClassFullName(classSignature, this.context.getDeclaringArkNamespace()); 180 let methodName = methodSignature.getMethodSubSignature().getMethodName(); 181 let args: string[] = []; 182 invokeExpr.getArgs().forEach(v => { 183 args.push(this.valueToString(v)); 184 }); 185 186 let genericCode = this.genericTypesToString(invokeExpr.getRealGenericTypes()); 187 188 if (this.context.isInBuilderMethod()) { 189 const res = this.transBuilderMethod(className, methodName, args, invokeExpr, genericCode); 190 if (res !== null) { 191 return res; 192 } 193 } 194 195 if (className && className.length > 0 && methodName !== SUPER_NAME) { 196 return `${className}.${methodName}${genericCode}(${args.join(', ')})`; 197 } 198 return `${methodName}${genericCode}(${args.join(', ')})`; 199 } 200 201 private genericTypesToString(types: Type[] | undefined): string { 202 if (!types) { 203 return ''; 204 } 205 206 let code = this.typeArrayToString(types); 207 if (code.length > 0) { 208 return `<${code}>`; 209 } 210 return ''; 211 } 212 213 public typeArrayToString(types: Type[], split: string = ', '): string { 214 let typesStr: string[] = []; 215 types.forEach(t => { 216 typesStr.push(this.typeToString(t)); 217 }); 218 219 return typesStr.join(split); 220 } 221 222 public static constToString(value: Constant): string { 223 if (value.getType().toString() === 'string') { 224 return `'${PrinterUtils.escape(value.getValue())}'`; 225 } else if (value.getType().toString() === BIGINT_KEYWORD) { 226 return `${value.getValue()}n`; 227 } else { 228 return value.getValue(); 229 } 230 } 231 232 private exprToString(expr: AbstractExpr): string { 233 if (expr instanceof ArkInstanceInvokeExpr) { 234 const isAttr = PrinterUtils.isComponentAttributeInvoke(expr); 235 return `${this.instanceInvokeExprToString(expr, isAttr)}`; 236 } 237 238 if (expr instanceof ArkStaticInvokeExpr) { 239 return `${this.staticInvokeExprToString(expr)}`; 240 } 241 242 if (expr instanceof ArkNewArrayExpr) { 243 return `new Array<${this.typeToString(expr.getBaseType())}>(${expr.getSize()})`; 244 } 245 246 if (expr instanceof ArkNewExpr) { 247 return `new ${this.typeToString(expr.getType())}()`; 248 } 249 250 if (expr instanceof ArkDeleteExpr) { 251 return `delete ${this.valueToString(expr.getField())}`; 252 } 253 254 if (expr instanceof AbstractBinopExpr) { 255 let op1: Value = expr.getOp1(); 256 let op2: Value = expr.getOp2(); 257 let operator: string = expr.getOperator(); 258 259 return `${this.valueToString(op1, false, operator)} ${operator} ${this.valueToString(op2, false, operator)}`; 260 } 261 262 if (expr instanceof ArkTypeOfExpr) { 263 return `typeof(${this.valueToString(expr.getOp())})`; 264 } 265 266 if (expr instanceof ArkInstanceOfExpr) { 267 return `${this.valueToString(expr.getOp())} instanceof ${this.typeToString(expr.getType())}`; 268 } 269 270 if (expr instanceof ArkCastExpr) { 271 let baseOp = expr.getOp(); 272 return `${this.valueToString(baseOp)} as ${this.typeToString(expr.getType())}`; 273 } 274 275 if (expr instanceof ArkUnopExpr) { 276 return `${expr.getOperator()}${this.valueToString(expr.getOp())}`; 277 } 278 279 if (expr instanceof ArkAwaitExpr) { 280 return `await ${this.valueToString(expr.getPromise())}`; 281 } 282 283 if (expr instanceof ArkYieldExpr) { 284 return `yield ${this.valueToString(expr.getYieldValue())}`; 285 } 286 287 logger.info(`exprToString ${expr.constructor} not support.`); 288 // ArkPhiExpr 289 return `${expr}`; 290 } 291 292 public refToString(value: AbstractRef): string { 293 if (value instanceof ArkInstanceFieldRef) { 294 return `${this.valueToString(value.getBase())}.${value.getFieldName()}`; 295 } 296 297 if (value instanceof ArkStaticFieldRef) { 298 return `${value.getFieldSignature().getBaseName()}.${value.getFieldName()}`; 299 } 300 301 if (value instanceof ArkArrayRef) { 302 let index = value.getIndex(); 303 if (index instanceof Constant && index.getType() instanceof StringType && PrinterUtils.isTemp(index.getValue())) { 304 return `${this.valueToString(value.getBase())}[${this.valueToString(new Local(index.getValue()))}]`; 305 } 306 return `${this.valueToString(value.getBase())}[${this.valueToString(value.getIndex())}]`; 307 } 308 309 if (value instanceof ArkThisRef) { 310 return 'this'; 311 } 312 313 // ArkCaughtExceptionRef 314 logger.info(`refToString ${value.constructor} not support.`); 315 return `${value}`; 316 } 317 318 public valueToString(value: Value, isLeftOp: boolean = false, operator?: string): string { 319 if (value instanceof AbstractExpr) { 320 return this.exprToString(value); 321 } 322 323 if (value instanceof AbstractRef) { 324 return this.refToString(value); 325 } 326 327 if (value instanceof Constant) { 328 return SourceTransformer.constToString(value); 329 } 330 331 if (value instanceof Local) { 332 return this.localToString(value, isLeftOp, operator); 333 } 334 335 logger.info(`valueToString ${value.constructor} not support.`); 336 return `${value}`; 337 } 338 339 private localToString(value: Local, isLeftOp: boolean = false, operator?: string): string { 340 if (PrinterUtils.isAnonymousMethod(value.getName())) { 341 let methodSignature = (value.getType() as FunctionType).getMethodSignature(); 342 let anonymousMethod = this.context.getMethod(methodSignature); 343 if (anonymousMethod) { 344 return this.anonymousMethodToString(anonymousMethod, this.context.getPrinter().getIndent()); 345 } 346 } 347 if (PrinterUtils.isAnonymousClass(value.getName())) { 348 let clsSignature = (value.getType() as ClassType).getClassSignature(); 349 let cls = this.context.getClass(clsSignature); 350 if (cls) { 351 return this.anonymousClassToString(cls, this.context.getPrinter().getIndent()); 352 } 353 } 354 355 if (operator === NormalBinaryOperator.Division || operator === NormalBinaryOperator.Multiplication || operator === NormalBinaryOperator.Remainder) { 356 if (PrinterUtils.isTemp(value.getName())) { 357 let stmt = value.getDeclaringStmt(); 358 if (stmt instanceof ArkAssignStmt && stmt.getRightOp() instanceof ArkNormalBinopExpr) { 359 return `(${this.context.transTemp2Code(value, isLeftOp)})`; 360 } 361 } 362 } 363 364 return this.context.transTemp2Code(value, isLeftOp); 365 } 366 367 public literalObjectToString(type: ClassType): string { 368 let name = type.getClassSignature().getClassName(); 369 if (PrinterUtils.isAnonymousClass(name)) { 370 let cls = this.context.getClass(type.getClassSignature()); 371 if (cls) { 372 return this.anonymousClassToString(cls, this.context.getPrinter().getIndent()); 373 } 374 } 375 return name; 376 } 377 378 public typeToString(type: Type): string { 379 if (type instanceof LiteralType) { 380 return this.literalType2string(type); 381 } 382 383 if (type instanceof PrimitiveType || type instanceof GenericType) { 384 return type.getName(); 385 } 386 387 if (type instanceof UnionType || type instanceof IntersectionType) { 388 return this.multipleType2string(type); 389 } 390 391 if (type instanceof UnknownType) { 392 return 'any'; 393 } 394 395 if (type instanceof VoidType) { 396 return 'void'; 397 } 398 399 if (type instanceof ClassType) { 400 return this.classType2string(type); 401 } 402 if (type instanceof ArrayType) { 403 return this.arrayType2string(type); 404 } 405 if (type instanceof TupleType) { 406 return this.tupleType2string(type); 407 } 408 409 if (type instanceof FunctionType) { 410 let methodSignature = type.getMethodSignature(); 411 let method = this.context.getMethod(methodSignature); 412 if (method && PrinterUtils.isAnonymousMethod(method.getName())) { 413 return new SourceMethod(method).toArrowFunctionTypeString(); 414 } 415 } 416 417 if (type instanceof UnclearReferenceType) { 418 return this.unclearReferenceType2string(type); 419 } 420 421 if (type instanceof AliasType) { 422 return this.aliasType2string(type); 423 } 424 425 if (type instanceof KeyofTypeExpr) { 426 return this.keyofTypeExpr2string(type); 427 } 428 429 if (type instanceof TypeQueryExpr) { 430 return this.typeQueryExpr2string(type); 431 } 432 433 if (!type) { 434 return 'any'; 435 } 436 437 logger.info(`valueToString ${type.constructor} not support.`); 438 return type.toString(); 439 } 440 441 private literalType2string(type: LiteralType): string { 442 let literalName = type.getLiteralName(); 443 if (typeof literalName === 'string' && literalName.endsWith('Keyword')) { 444 return literalName.substring(0, literalName.length - 'Keyword'.length).toLowerCase(); 445 } 446 return `${literalName}`; 447 } 448 449 private multipleType2string(type: UnionType | IntersectionType): string { 450 let typesStr: string[] = []; 451 for (const member of type.getTypes()) { 452 if (member instanceof UnionType || member instanceof IntersectionType) { 453 typesStr.push(`(${this.typeToString(member)})`); 454 } else { 455 typesStr.push(this.typeToString(member)); 456 } 457 } 458 if (type instanceof UnionType) { 459 return typesStr.join(' | '); 460 } else { 461 return typesStr.join(' & '); 462 } 463 } 464 465 private arrayType2string(type: ArrayType): string { 466 const readonly = type.getReadonlyFlag() ? 'readonly ' : ''; 467 const dimensions: string[] = []; 468 for (let i = 0; i < type.getDimension(); i++) { 469 dimensions.push('[]'); 470 } 471 472 let baseType = type.getBaseType(); 473 if (baseType instanceof UnionType || baseType instanceof IntersectionType || baseType instanceof AbstractTypeExpr) { 474 return `${readonly}(${this.typeToString(baseType)})${dimensions.join('')}`; 475 } 476 return `${readonly}${this.typeToString(baseType)}${dimensions.join('')}`; 477 } 478 479 private tupleType2string(type: TupleType): string { 480 const readonly = type.getReadonlyFlag() ? 'readonly ' : ''; 481 let typesStr: string[] = []; 482 for (const member of type.getTypes()) { 483 typesStr.push(this.typeToString(member)); 484 } 485 return `${readonly}[${typesStr.join(', ')}]`; 486 } 487 488 private aliasType2string(type: AliasType): string { 489 let typesStr: string[] = []; 490 let genericTypes = type.getRealGenericTypes() ?? type.getGenericTypes(); 491 if (genericTypes) { 492 for (const gType of genericTypes) { 493 typesStr.push(this.typeToString(gType)); 494 } 495 } 496 if (typesStr.length > 0) { 497 return `${type.getName()}<${typesStr.join(', ')}>`; 498 } 499 return type.getName(); 500 } 501 502 private keyofTypeExpr2string(type: KeyofTypeExpr): string { 503 if (type.getOpType() instanceof UnionType || type.getOpType() instanceof IntersectionType) { 504 return `keyof (${this.typeToString(type.getOpType())})`; 505 } 506 return `keyof ${this.typeToString(type.getOpType())}`; 507 } 508 509 private typeQueryExpr2string(type: TypeQueryExpr): string { 510 const gTypes = type.getGenerateTypes(); 511 const genericStr = this.genericTypesToString(gTypes); 512 const opValue = type.getOpValue(); 513 if (opValue instanceof ArkBaseModel) { 514 if (opValue instanceof ArkClass || opValue instanceof ArkMethod || opValue instanceof ArkNamespace || opValue instanceof ArkField) { 515 return `typeof ${opValue.getName()}${genericStr}`; 516 } else if (opValue instanceof ExportInfo) { 517 return `typeof ${opValue.getExportClauseName()}${genericStr}`; 518 } else if (opValue instanceof ImportInfo) { 519 return `typeof ${opValue.getImportClauseName()}${genericStr}`; 520 } else { 521 return `typeof *invalid*`; 522 } 523 } else { 524 return `typeof ${this.valueToString(opValue)}${genericStr}`; 525 } 526 } 527 528 private unclearReferenceType2string(type: UnclearReferenceType): string { 529 let genericTypes = type.getGenericTypes(); 530 if (genericTypes.length > 0) { 531 return `${type.getName()}<${genericTypes.map(value => this.typeToString(value)).join(', ')}>`; 532 } 533 return type.getName(); 534 } 535 536 private classType2string(type: ClassType): string { 537 const name = PrinterUtils.getStaticInvokeClassFullName(type.getClassSignature()); 538 if (PrinterUtils.isDefaultClass(name)) { 539 return 'any'; 540 } 541 if (PrinterUtils.isAnonymousClass(name)) { 542 let cls = this.context.getClass(type.getClassSignature()); 543 if (cls && cls.getCategory() === ClassCategory.TYPE_LITERAL) { 544 return this.anonymousClassToString(cls, this.context.getPrinter().getIndent()); 545 } 546 return 'Object'; 547 } 548 let genericTypes = type.getRealGenericTypes(); 549 if (genericTypes && genericTypes.length > 0) { 550 return `${name}${this.genericTypesToString(genericTypes)}`; 551 } 552 return name; 553 } 554} 555