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 { StmtUseReplacer } from '../common/StmtUseReplacer'; 17import { Cfg } from '../graph/Cfg'; 18import { AbstractExpr, AbstractInvokeExpr, AliasTypeExpr, ArkConditionExpr } from './Expr'; 19import { AbstractFieldRef, ArkArrayRef } from './Ref'; 20import { Value } from './Value'; 21import { FullPosition, LineColPosition } from './Position'; 22import { ArkMetadata, ArkMetadataKind, ArkMetadataType } from '../model/ArkMetadata'; 23import { StmtDefReplacer } from '../common/StmtDefReplacer'; 24import { IRUtils } from '../common/IRUtils'; 25import { AliasType, ArrayType, IntersectionType, TupleType, Type, UnionType } from './Type'; 26import { ModifierType } from '../model/ArkBaseModel'; 27import { AbstractTypeExpr } from './TypeExpr'; 28 29/** 30 * @category core/base/stmt 31 */ 32export abstract class Stmt { 33 protected text?: string; // just for debug 34 protected originalText?: string; 35 protected originalPosition: LineColPosition = LineColPosition.DEFAULT; 36 protected cfg!: Cfg; 37 protected operandOriginalPositions?: FullPosition[]; // operandOriginalPositions correspond with 38 // def and uses one by one 39 metadata?: ArkMetadata; 40 41 public getMetadata(kind: ArkMetadataKind): ArkMetadataType | undefined { 42 return this.metadata?.getMetadata(kind); 43 } 44 45 public setMetadata(kind: ArkMetadataKind, value: ArkMetadataType): void { 46 if (!this.metadata) { 47 this.metadata = new ArkMetadata(); 48 } 49 return this.metadata?.setMetadata(kind, value); 50 } 51 52 /** Return a list of values which are uesd in this statement */ 53 public getUses(): Value[] { 54 return []; 55 } 56 57 public replaceUse(oldUse: Value, newUse: Value): void { 58 const stmtUseReplacer = new StmtUseReplacer(oldUse, newUse); 59 stmtUseReplacer.caseStmt(this); 60 } 61 62 /** 63 * Return the definition which is uesd in this statement. Generally, the definition is the left value of `=` in 64 * 3AC. For example, the definition in 3AC of `value = parameter0: @project-1/sample-1.ets: AnonymousClass-0` is 65 * `value`, and the definition in `$temp0 = staticinvoke <@_ProjectName/_FileName: xxx.create()>()` is `\$temp0`. 66 * @returns The definition in 3AC (may be a **null**). 67 * @example 68 * 1. get the def in stmt. 69 ```typescript 70 for (const block of this.blocks) { 71 for (const stmt of block.getStmts()) { 72 const defValue = stmt.getDef(); 73 ... 74 } 75 } 76 ``` 77 */ 78 public getDef(): Value | null { 79 return null; 80 } 81 82 public replaceDef(oldDef: Value, newDef: Value): void { 83 const stmtDefReplacer = new StmtDefReplacer(oldDef, newDef); 84 stmtDefReplacer.caseStmt(this); 85 } 86 87 public getDefAndUses(): Value[] { 88 const defAndUses: Value[] = []; 89 const def = this.getDef(); 90 if (def) { 91 defAndUses.push(def); 92 } 93 defAndUses.push(...this.getUses()); 94 return defAndUses; 95 } 96 97 /** 98 * Get the CFG (i.e., control flow graph) of an {@link ArkBody} in which the statement is. 99 * A CFG contains a set of basic blocks and statements corresponding to each basic block. 100 * Note that, "source code" and "three-address" are two types of {@link Stmt} in ArkAnalyzer. 101 * Source code {@link Stmt} represents the statement of ets/ts source code, while three-address code {@link Stmt} 102 * represents the statement after it has been converted into three-address code. Since the source code {@link 103 * Stmt} does not save its CFG reference, it returns **null**, while the `getCfg()` of the third address code 104 * {@link Stmt} will return its CFG reference. 105 * @returns The CFG (i.e., control flow graph) of an {@link ArkBody} in which the statement is. 106 * @example 107 * 1. get the ArkFile based on stmt. 108 ```typescript 109 const arkFile = stmt.getCfg()?.getDeclaringMethod().getDeclaringArkFile(); 110 ``` 111 2. get the ArkMethod based on stmt. 112 ```typescript 113 let sourceMethod: ArkMethod = stmt.getCfg()?.getDeclaringMethod(); 114 ``` 115 */ 116 public getCfg(): Cfg { 117 return this.cfg; 118 } 119 120 public setCfg(cfg: Cfg): void { 121 this.cfg = cfg; 122 } 123 124 /** 125 * Return true if the following statement may not execute after this statement. 126 * The ArkIfStmt and ArkGotoStmt will return true. 127 */ 128 public isBranch(): boolean { 129 return false; 130 } 131 132 /** Return the number of statements which this statement may go to */ 133 public getExpectedSuccessorCount(): number { 134 return 1; 135 } 136 137 public containsInvokeExpr(): boolean { 138 for (const use of this.getUses()) { 139 if (use instanceof AbstractInvokeExpr) { 140 return true; 141 } 142 } 143 return false; 144 } 145 146 /** 147 * Returns the method's invocation expression (including method signature and its arguments) 148 * in the current statement. An **undefined** will be returned if there is no method used in this statement. 149 * @returns the method's invocation expression from the statement. An **undefined** will be returned if there is 150 * no method can be found in this statement. 151 * @example 152 * 1. get invoke expr based on stmt. 153 ```typescript 154 let invoke = stmt.getInvokeExpr(); 155 ``` 156 */ 157 public getInvokeExpr(): AbstractInvokeExpr | undefined { 158 for (const use of this.getUses()) { 159 if (use instanceof AbstractInvokeExpr) { 160 return use as AbstractInvokeExpr; 161 } 162 } 163 return undefined; 164 } 165 166 /** 167 * Returns an array of expressions in the statement. 168 * @returns An array of expressions in the statement. 169 * @example 170 * 1. Traverse expression of statement. 171 172 ```typescript 173 for (const expr of stmt.getExprs()) { 174 ... 175 } 176 ``` 177 */ 178 public getExprs(): AbstractExpr[] { 179 let exprs: AbstractExpr[] = []; 180 for (const use of this.getUses()) { 181 if (use instanceof AbstractExpr) { 182 exprs.push(use); 183 } 184 } 185 return exprs; 186 } 187 188 public getTypeExprs(): AbstractTypeExpr[] { 189 let typeExprs: AbstractTypeExpr[] = []; 190 for (const value of this.getDefAndUses()) { 191 const valueType = value.getType(); 192 if (valueType instanceof AbstractTypeExpr) { 193 typeExprs.push(valueType); 194 } 195 } 196 return typeExprs; 197 } 198 199 public containsArrayRef(): boolean { 200 for (const use of this.getUses()) { 201 if (use instanceof ArkArrayRef) { 202 return true; 203 } 204 } 205 if (this.getDef() instanceof ArkArrayRef) { 206 return true; 207 } 208 return false; 209 } 210 211 public getArrayRef(): ArkArrayRef | undefined { 212 for (const use of this.getUses()) { 213 if (use instanceof ArkArrayRef) { 214 return use as ArkArrayRef; 215 } 216 } 217 218 if (this.getDef() instanceof ArkArrayRef) { 219 return undefined; 220 } 221 222 return undefined; 223 } 224 225 public containsFieldRef(): boolean { 226 for (const use of this.getUses()) { 227 if (use instanceof AbstractFieldRef) { 228 return true; 229 } 230 } 231 232 if (this.getDef() instanceof AbstractFieldRef) { 233 return true; 234 } 235 return false; 236 } 237 238 public getFieldRef(): AbstractFieldRef | undefined { 239 for (const use of this.getUses()) { 240 if (use instanceof AbstractFieldRef) { 241 return use as AbstractFieldRef; 242 } 243 } 244 if (this.getDef() instanceof AbstractFieldRef) { 245 return undefined; 246 } 247 return undefined; 248 } 249 250 public setOriginPositionInfo(originPositionInfo: LineColPosition): void { 251 this.originalPosition = originPositionInfo; 252 } 253 254 /** 255 * Returns the original position of the statement. 256 * The position consists of two parts: line number and column number. 257 * In the source file, the former (i.e., line number) indicates which line the statement is in, 258 * and the latter (i.e., column number) indicates the position of the statement in the line. 259 * The position is described as `LineColPosition(lineNo,colNum)` in ArkAnalyzer, 260 * and its default value is LineColPosition(-1,-1). 261 * @returns The original location of the statement. 262 * @example 263 * 1. Get the stmt position info to make some condition judgements. 264 ```typescript 265 for (const stmt of stmts) { 266 if (stmt.getOriginPositionInfo().getLineNo() === -1) { 267 stmt.setOriginPositionInfo(originalStmt.getOriginPositionInfo()); 268 this.stmtToOriginalStmt.set(stmt, originalStmt); 269 } 270 } 271 ``` 272 */ 273 public getOriginPositionInfo(): LineColPosition { 274 return this.originalPosition; 275 } 276 277 abstract toString(): string; 278 279 public setText(text: string): void { 280 this.text = text; 281 } 282 283 public setOriginalText(originalText: string): void { 284 this.originalText = originalText; 285 } 286 287 public getOriginalText(): string | undefined { 288 return this.originalText; 289 } 290 291 public setOperandOriginalPositions(operandOriginalPositions: FullPosition[]): void { 292 this.operandOriginalPositions = operandOriginalPositions; 293 } 294 295 public getOperandOriginalPositions(): FullPosition[] | undefined { 296 return this.operandOriginalPositions; 297 } 298 299 public getOperandOriginalPosition(indexOrOperand: number | Value): FullPosition | null { 300 let index: number = -1; 301 if (typeof indexOrOperand !== 'number') { 302 index = IRUtils.findOperandIdx(this, indexOrOperand); 303 } else { 304 index = indexOrOperand; 305 } 306 307 if (!this.operandOriginalPositions || index < 0 || index > this.operandOriginalPositions.length) { 308 return null; 309 } 310 return this.operandOriginalPositions[index]; 311 } 312} 313 314export class ArkAssignStmt extends Stmt { 315 private leftOp: Value; 316 private rightOp: Value; 317 318 constructor(leftOp: Value, rightOp: Value) { 319 super(); 320 this.leftOp = leftOp; 321 this.rightOp = rightOp; 322 } 323 324 /** 325 * Returns the left operand of the assigning statement. 326 * @returns The left operand of the assigning statement. 327 * @example 328 * 1. If the statement is `a=b;`, the right operand is `a`; if the statement is `dd = cc + 5;`, the right operand 329 * is `cc`. 330 */ 331 public getLeftOp(): Value { 332 return this.leftOp; 333 } 334 335 public setLeftOp(newLeftOp: Value): void { 336 this.leftOp = newLeftOp; 337 } 338 339 /** 340 * Returns the right operand of the assigning statement. 341 * @returns The right operand of the assigning statement. 342 * @example 343 * 1. If the statement is `a=b;`, the right operand is `b`; if the statement is `dd = cc + 5;`, the right operand 344 * is `cc + 5`. 345 * 2. Get the rightOp from stmt. 346 ```typescript 347 const rightOp = stmt.getRightOp(); 348 ``` 349 */ 350 public getRightOp(): Value { 351 return this.rightOp; 352 } 353 354 public setRightOp(rightOp: Value): void { 355 this.rightOp = rightOp; 356 } 357 358 public toString(): string { 359 const str = this.getLeftOp() + ' = ' + this.getRightOp(); 360 return str; 361 } 362 363 public getDef(): Value | null { 364 return this.leftOp; 365 } 366 367 public getUses(): Value[] { 368 let uses: Value[] = []; 369 uses.push(...this.leftOp.getUses()); 370 uses.push(this.rightOp); 371 uses.push(...this.rightOp.getUses()); 372 return uses; 373 } 374} 375 376export class ArkInvokeStmt extends Stmt { 377 private invokeExpr: AbstractInvokeExpr; 378 379 constructor(invokeExpr: AbstractInvokeExpr) { 380 super(); 381 this.invokeExpr = invokeExpr; 382 } 383 384 public replaceInvokeExpr(newExpr: AbstractInvokeExpr): void { 385 this.invokeExpr = newExpr; 386 } 387 388 public getInvokeExpr(): AbstractInvokeExpr { 389 return this.invokeExpr; 390 } 391 392 public toString(): string { 393 const str = this.invokeExpr.toString(); 394 return str; 395 } 396 397 public getUses(): Value[] { 398 let uses: Value[] = []; 399 uses.push(this.invokeExpr); 400 uses.push(...this.invokeExpr.getUses()); 401 return uses; 402 } 403} 404 405export class ArkIfStmt extends Stmt { 406 private conditionExpr: ArkConditionExpr; 407 408 constructor(conditionExpr: ArkConditionExpr) { 409 super(); 410 this.conditionExpr = conditionExpr; 411 } 412 413 /** 414 * The condition expression consisit of two values as operands and one binary operator as operator. 415 * The operator can indicate the relation between the two values, e.g., `<`, `<=`,`>`, `>=`, `==`, `!=`, `===`, 416 * `!==`. 417 * @returns a condition expression. 418 * @example 419 * 1. When a statement is `if (a > b)`, the operands are `a` and `b`, the operator is `<`. Therefore, the condition 420 * expression is `a > b`. 421 * 2. get a conditon expr from a condition statement. 422 ```typescript 423 let expr = (this.original as ArkIfStmt).getConditionExpr(); 424 ``` 425 */ 426 public getConditionExpr(): ArkConditionExpr { 427 return this.conditionExpr; 428 } 429 430 public setConditionExpr(newConditionExpr: ArkConditionExpr): void { 431 this.conditionExpr = newConditionExpr; 432 } 433 434 public isBranch(): boolean { 435 return true; 436 } 437 438 public getExpectedSuccessorCount(): number { 439 return 2; 440 } 441 442 public toString(): string { 443 const str = 'if ' + this.conditionExpr; 444 return str; 445 } 446 447 public getUses(): Value[] { 448 let uses: Value[] = []; 449 uses.push(this.conditionExpr); 450 uses.push(...this.conditionExpr.getUses()); 451 return uses; 452 } 453} 454 455export class ArkReturnStmt extends Stmt { 456 private op: Value; 457 458 constructor(op: Value) { 459 super(); 460 this.op = op; 461 } 462 463 public getExpectedSuccessorCount(): number { 464 return 0; 465 } 466 467 public getOp(): Value { 468 return this.op; 469 } 470 471 public setReturnValue(returnValue: Value): void { 472 this.op = returnValue; 473 } 474 475 public toString(): string { 476 const str = 'return ' + this.op; 477 return str; 478 } 479 480 public getUses(): Value[] { 481 let uses: Value[] = []; 482 uses.push(this.op); 483 uses.push(...this.op.getUses()); 484 return uses; 485 } 486} 487 488export class ArkReturnVoidStmt extends Stmt { 489 constructor() { 490 super(); 491 } 492 493 public getExpectedSuccessorCount(): number { 494 return 0; 495 } 496 497 public toString(): string { 498 const str = 'return'; 499 return str; 500 } 501} 502 503export class ArkThrowStmt extends Stmt { 504 private op: Value; 505 506 constructor(op: Value) { 507 super(); 508 this.op = op; 509 } 510 511 public getOp(): Value { 512 return this.op; 513 } 514 515 public setOp(newOp: Value): void { 516 this.op = newOp; 517 } 518 519 public toString(): string { 520 const str = 'throw ' + this.op; 521 return str; 522 } 523 524 public getUses(): Value[] { 525 let uses: Value[] = []; 526 uses.push(this.op); 527 uses.push(...this.op.getUses()); 528 return uses; 529 } 530} 531 532/** 533 * Statement of type alias definition combines with the left hand as {@link AliasType} and right hand as {@link AliasTypeExpr}. 534 * @category core/base/stmt 535 * @extends Stmt 536 * @example 537 ```typescript 538 type A = string; 539 type B = import('./abc').TypeB; 540 541 let c = 123; 542 declare type C = typeof c; 543 ``` 544 */ 545export class ArkAliasTypeDefineStmt extends Stmt { 546 private aliasType: AliasType; 547 private aliasTypeExpr: AliasTypeExpr; 548 549 constructor(aliasType: AliasType, typeAliasExpr: AliasTypeExpr) { 550 super(); 551 this.aliasType = aliasType; 552 this.aliasTypeExpr = typeAliasExpr; 553 } 554 555 public getAliasType(): AliasType { 556 return this.aliasType; 557 } 558 559 public getAliasTypeExpr(): AliasTypeExpr { 560 return this.aliasTypeExpr; 561 } 562 563 public getAliasName(): string { 564 return this.getAliasType().getName(); 565 } 566 567 public toString(): string { 568 let str = `type ${this.getAliasType().toString()} = ${this.getAliasTypeExpr().toString()}`; 569 if (this.getAliasType().containsModifier(ModifierType.DECLARE)) { 570 str = 'declare ' + str; 571 } 572 if (this.getAliasType().containsModifier(ModifierType.EXPORT)) { 573 str = 'export ' + str; 574 } 575 return str; 576 } 577 578 public getExprs(): AliasTypeExpr[] { 579 return [this.getAliasTypeExpr()]; 580 } 581 582 public getTypeExprs(): AbstractTypeExpr[] { 583 function getTypeExprsInType(originalObject: Type): AbstractTypeExpr[] { 584 let typeExprs: AbstractTypeExpr[] = []; 585 if (originalObject instanceof AbstractTypeExpr) { 586 typeExprs.push(originalObject); 587 } else if (originalObject instanceof ArrayType) { 588 typeExprs.push(...getTypeExprsInType(originalObject.getBaseType())); 589 } else if (originalObject instanceof UnionType || originalObject instanceof IntersectionType || originalObject instanceof TupleType) { 590 for (const member of originalObject.getTypes()) { 591 typeExprs.push(...getTypeExprsInType(member)); 592 } 593 } 594 return typeExprs; 595 } 596 597 const originalObject = this.getAliasTypeExpr().getOriginalObject(); 598 if (originalObject instanceof Type) { 599 return getTypeExprsInType(originalObject); 600 } 601 return []; 602 } 603} 604