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 * as ts from 'ohos-typescript'; 17import { Local } from '../../base/Local'; 18import { ArkAliasTypeDefineStmt, ArkReturnStmt, ArkReturnVoidStmt, Stmt } from '../../base/Stmt'; 19import { BasicBlock } from '../BasicBlock'; 20import { Cfg } from '../Cfg'; 21import { ArkClass } from '../../model/ArkClass'; 22import { ArkMethod } from '../../model/ArkMethod'; 23import { ArkIRTransformer, ValueAndStmts } from '../../common/ArkIRTransformer'; 24import { ModelUtils } from '../../common/ModelUtils'; 25import { IRUtils } from '../../common/IRUtils'; 26import { AliasType, ClassType, UnclearReferenceType, UnknownType, VoidType } from '../../base/Type'; 27import { Trap } from '../../base/Trap'; 28import { GlobalRef } from '../../base/Ref'; 29import { LoopBuilder } from './LoopBuilder'; 30import { SwitchBuilder } from './SwitchBuilder'; 31import { ConditionBuilder } from './ConditionBuilder'; 32import { TrapBuilder } from './TrapBuilder'; 33import { CONSTRUCTOR_NAME, PROMISE } from '../../common/TSConst'; 34import { ModifierType } from '../../model/ArkBaseModel'; 35import { ParameterDeclaration } from 'ohos-typescript'; 36 37class StatementBuilder { 38 type: string; 39 //节点对应源代码 40 code: string; 41 next: StatementBuilder | null; 42 lasts: Set<StatementBuilder>; 43 walked: boolean; 44 index: number; 45 // TODO:以下两个属性需要获取 46 line: number; //行号//ast节点存了一个start值为这段代码的起始地址,可以从start开始往回查原文有几个换行符确定行号 47 column: number; // 列 48 astNode: ts.Node | null; //ast节点对象 49 scopeID: number; 50 addressCode3: string[] = []; 51 block: BlockBuilder | null; 52 ifExitPass: boolean; 53 passTmies: number = 0; 54 numOfIdentifier: number = 0; 55 isDoWhile: boolean = false; 56 57 constructor(type: string, code: string, astNode: ts.Node | null, scopeID: number) { 58 this.type = type; 59 this.code = code; 60 this.next = null; 61 this.lasts = new Set(); 62 this.walked = false; 63 this.index = 0; 64 this.line = -1; 65 this.column = -1; 66 this.astNode = astNode; 67 this.scopeID = scopeID; 68 this.block = null; 69 this.ifExitPass = false; 70 } 71} 72 73class ConditionStatementBuilder extends StatementBuilder { 74 nextT: StatementBuilder | null; 75 nextF: StatementBuilder | null; 76 loopBlock: BlockBuilder | null; 77 condition: string; 78 doStatement: StatementBuilder | null = null; 79 80 constructor(type: string, code: string, astNode: ts.Node, scopeID: number) { 81 super(type, code, astNode, scopeID); 82 this.nextT = null; 83 this.nextF = null; 84 this.loopBlock = null; 85 this.condition = ''; 86 } 87} 88 89export class SwitchStatementBuilder extends StatementBuilder { 90 nexts: StatementBuilder[]; 91 cases: Case[] = []; 92 default: StatementBuilder | null = null; 93 afterSwitch: StatementBuilder | null = null; 94 95 constructor(type: string, code: string, astNode: ts.Node, scopeID: number) { 96 super(type, code, astNode, scopeID); 97 this.nexts = []; 98 } 99} 100 101export class TryStatementBuilder extends StatementBuilder { 102 tryFirst: StatementBuilder | null = null; 103 tryExit: StatementBuilder | null = null; 104 catchStatement: StatementBuilder | null = null; 105 catchError: string = ''; 106 finallyStatement: StatementBuilder | null = null; 107 afterFinal: StatementBuilder | null = null; 108 109 constructor(type: string, code: string, astNode: ts.Node, scopeID: number) { 110 super(type, code, astNode, scopeID); 111 } 112} 113 114class Case { 115 value: string; 116 stmt: StatementBuilder; 117 valueNode!: ts.Node; 118 119 constructor(value: string, stmt: StatementBuilder) { 120 this.value = value; 121 this.stmt = stmt; 122 } 123} 124 125class DefUseChain { 126 def: StatementBuilder; 127 use: StatementBuilder; 128 129 constructor(def: StatementBuilder, use: StatementBuilder) { 130 this.def = def; 131 this.use = use; 132 } 133} 134 135class Variable { 136 name: string; 137 lastDef: StatementBuilder; 138 defUse: DefUseChain[]; 139 properties: Variable[] = []; 140 propOf: Variable | null = null; 141 142 constructor(name: string, lastDef: StatementBuilder) { 143 this.name = name; 144 this.lastDef = lastDef; 145 this.defUse = []; 146 } 147} 148 149class Scope { 150 id: number; 151 152 constructor(id: number) { 153 this.id = id; 154 } 155} 156 157export class BlockBuilder { 158 id: number; 159 stmts: StatementBuilder[]; 160 nexts: BlockBuilder[] = []; 161 lasts: BlockBuilder[] = []; 162 walked: boolean = false; 163 164 constructor(id: number, stmts: StatementBuilder[]) { 165 this.id = id; 166 this.stmts = stmts; 167 } 168} 169 170class Catch { 171 errorName: string; 172 from: number; 173 to: number; 174 withLabel: number; 175 176 constructor(errorName: string, from: number, to: number, withLabel: number) { 177 this.errorName = errorName; 178 this.from = from; 179 this.to = to; 180 this.withLabel = withLabel; 181 } 182} 183 184class TextError extends Error { 185 constructor(message: string) { 186 // 调用父类的构造函数,并传入错误消息 187 super(message); 188 189 // 设置错误类型的名称 190 this.name = 'TextError'; 191 } 192} 193 194export class CfgBuilder { 195 name: string; 196 astRoot: ts.Node; 197 entry: StatementBuilder; 198 exit: StatementBuilder; 199 loopStack: ConditionStatementBuilder[]; 200 switchExitStack: StatementBuilder[]; 201 functions: CfgBuilder[]; 202 breakin: string; 203 statementArray: StatementBuilder[]; 204 dotEdges: number[][]; 205 scopes: Scope[]; 206 tempVariableNum: number; 207 current3ACstm: StatementBuilder; 208 blocks: BlockBuilder[]; 209 currentDeclarationKeyword: string; 210 variables: Variable[]; 211 declaringClass: ArkClass; 212 importFromPath: string[]; 213 catches: Catch[]; 214 exits: StatementBuilder[] = []; 215 emptyBody: boolean = false; 216 arrowFunctionWithoutBlock: boolean = false; 217 218 private sourceFile: ts.SourceFile; 219 private declaringMethod: ArkMethod; 220 221 constructor(ast: ts.Node, name: string, declaringMethod: ArkMethod, sourceFile: ts.SourceFile) { 222 this.name = name; 223 this.astRoot = ast; 224 this.declaringMethod = declaringMethod; 225 this.declaringClass = declaringMethod.getDeclaringArkClass(); 226 this.entry = new StatementBuilder('entry', '', ast, 0); 227 this.loopStack = []; 228 this.switchExitStack = []; 229 this.functions = []; 230 this.breakin = ''; 231 this.statementArray = []; 232 this.dotEdges = []; 233 this.exit = new StatementBuilder('exit', 'return;', null, 0); 234 this.scopes = []; 235 this.tempVariableNum = 0; 236 this.current3ACstm = this.entry; 237 this.blocks = []; 238 this.currentDeclarationKeyword = ''; 239 this.variables = []; 240 this.importFromPath = []; 241 this.catches = []; 242 this.sourceFile = sourceFile; 243 this.arrowFunctionWithoutBlock = true; 244 } 245 246 public getDeclaringMethod(): ArkMethod { 247 return this.declaringMethod; 248 } 249 250 judgeLastType(s: StatementBuilder, lastStatement: StatementBuilder): void { 251 if (lastStatement.type === 'ifStatement') { 252 let lastIf = lastStatement as ConditionStatementBuilder; 253 if (lastIf.nextT == null) { 254 lastIf.nextT = s; 255 s.lasts.add(lastIf); 256 } else { 257 lastIf.nextF = s; 258 s.lasts.add(lastIf); 259 } 260 } else if (lastStatement.type === 'loopStatement') { 261 let lastLoop = lastStatement as ConditionStatementBuilder; 262 lastLoop.nextT = s; 263 s.lasts.add(lastLoop); 264 } else if (lastStatement.type === 'catchOrNot') { 265 let lastLoop = lastStatement as ConditionStatementBuilder; 266 lastLoop.nextT = s; 267 s.lasts.add(lastLoop); 268 } else { 269 lastStatement.next = s; 270 s.lasts.add(lastStatement); 271 } 272 } 273 274 ASTNodeBreakStatement(c: ts.Node, lastStatement: StatementBuilder): void { 275 let p: ts.Node | null = c; 276 while (p && p !== this.astRoot) { 277 if (ts.isWhileStatement(p) || ts.isDoStatement(p) || ts.isForStatement(p) || ts.isForInStatement(p) || ts.isForOfStatement(p)) { 278 const lastLoopNextF = this.loopStack[this.loopStack.length - 1].nextF!; 279 this.judgeLastType(lastLoopNextF, lastStatement); 280 lastLoopNextF.lasts.add(lastStatement); 281 return; 282 } 283 if (ts.isCaseClause(p) || ts.isDefaultClause(p)) { 284 const lastSwitchExit = this.switchExitStack[this.switchExitStack.length - 1]; 285 this.judgeLastType(lastSwitchExit, lastStatement); 286 lastSwitchExit.lasts.add(lastStatement); 287 return; 288 } 289 p = p.parent; 290 } 291 } 292 293 ASTNodeIfStatement(c: ts.IfStatement, lastStatement: StatementBuilder, scopeID: number): StatementBuilder { 294 let ifstm: ConditionStatementBuilder = new ConditionStatementBuilder('ifStatement', '', c, scopeID); 295 this.judgeLastType(ifstm, lastStatement); 296 let ifexit: StatementBuilder = new StatementBuilder('ifExit', '', c, scopeID); 297 this.exits.push(ifexit); 298 ifstm.condition = c.expression.getText(this.sourceFile); 299 ifstm.code = 'if (' + ifstm.condition + ')'; 300 if (ts.isBlock(c.thenStatement)) { 301 this.walkAST(ifstm, ifexit, [...c.thenStatement.statements]); 302 } else { 303 this.walkAST(ifstm, ifexit, [c.thenStatement]); 304 } 305 if (c.elseStatement) { 306 if (ts.isBlock(c.elseStatement)) { 307 this.walkAST(ifstm, ifexit, [...c.elseStatement.statements]); 308 } else { 309 this.walkAST(ifstm, ifexit, [c.elseStatement]); 310 } 311 } 312 if (!ifstm.nextT) { 313 ifstm.nextT = ifexit; 314 ifexit.lasts.add(ifstm); 315 } 316 if (!ifstm.nextF) { 317 ifstm.nextF = ifexit; 318 ifexit.lasts.add(ifstm); 319 } 320 return ifexit; 321 } 322 323 ASTNodeWhileStatement(c: ts.WhileStatement, lastStatement: StatementBuilder, scopeID: number): StatementBuilder { 324 this.breakin = 'loop'; 325 let loopstm = new ConditionStatementBuilder('loopStatement', '', c, scopeID); 326 this.loopStack.push(loopstm); 327 this.judgeLastType(loopstm, lastStatement); 328 let loopExit = new StatementBuilder('loopExit', '', c, scopeID); 329 this.exits.push(loopExit); 330 loopstm.nextF = loopExit; 331 loopExit.lasts.add(loopstm); 332 loopstm.condition = c.expression.getText(this.sourceFile); 333 loopstm.code = 'while (' + loopstm.condition + ')'; 334 if (ts.isBlock(c.statement)) { 335 this.walkAST(loopstm, loopstm, [...c.statement.statements]); 336 } else { 337 this.walkAST(loopstm, loopstm, [c.statement]); 338 } 339 if (!loopstm.nextF) { 340 loopstm.nextF = loopExit; 341 loopExit.lasts.add(loopstm); 342 } 343 if (!loopstm.nextT) { 344 loopstm.nextT = loopExit; 345 loopExit.lasts.add(loopstm); 346 } 347 this.loopStack.pop(); 348 return loopExit; 349 } 350 351 ASTNodeForStatement(c: ts.ForInOrOfStatement | ts.ForStatement, lastStatement: StatementBuilder, scopeID: number): StatementBuilder { 352 this.breakin = 'loop'; 353 let loopstm = new ConditionStatementBuilder('loopStatement', '', c, scopeID); 354 this.loopStack.push(loopstm); 355 this.judgeLastType(loopstm, lastStatement); 356 let loopExit = new StatementBuilder('loopExit', '', c, scopeID); 357 this.exits.push(loopExit); 358 loopstm.nextF = loopExit; 359 loopExit.lasts.add(loopstm); 360 loopstm.code = 'for ('; 361 if (ts.isForStatement(c)) { 362 loopstm.code += 363 c.initializer?.getText(this.sourceFile) + '; ' + c.condition?.getText(this.sourceFile) + '; ' + c.incrementor?.getText(this.sourceFile); 364 } else if (ts.isForOfStatement(c)) { 365 loopstm.code += c.initializer?.getText(this.sourceFile) + ' of ' + c.expression.getText(this.sourceFile); 366 } else { 367 loopstm.code += c.initializer?.getText(this.sourceFile) + ' in ' + c.expression.getText(this.sourceFile); 368 } 369 loopstm.code += ')'; 370 if (ts.isBlock(c.statement)) { 371 this.walkAST(loopstm, loopstm, [...c.statement.statements]); 372 } else { 373 this.walkAST(loopstm, loopstm, [c.statement]); 374 } 375 if (!loopstm.nextF) { 376 loopstm.nextF = loopExit; 377 loopExit.lasts.add(loopstm); 378 } 379 if (!loopstm.nextT) { 380 loopstm.nextT = loopExit; 381 loopExit.lasts.add(loopstm); 382 } 383 this.loopStack.pop(); 384 return loopExit; 385 } 386 387 ASTNodeDoStatement(c: ts.DoStatement, lastStatement: StatementBuilder, scopeID: number): StatementBuilder { 388 this.breakin = 'loop'; 389 let loopstm = new ConditionStatementBuilder('loopStatement', '', c, scopeID); 390 this.loopStack.push(loopstm); 391 let loopExit = new StatementBuilder('loopExit', '', c, scopeID); 392 this.exits.push(loopExit); 393 loopstm.nextF = loopExit; 394 loopExit.lasts.add(loopstm); 395 loopstm.condition = c.expression.getText(this.sourceFile); 396 loopstm.code = 'while (' + loopstm.condition + ')'; 397 loopstm.isDoWhile = true; 398 if (ts.isBlock(c.statement)) { 399 this.walkAST(lastStatement, loopstm, [...c.statement.statements]); 400 } else { 401 this.walkAST(lastStatement, loopstm, [c.statement]); 402 } 403 let lastType = lastStatement.type; 404 if (lastType === 'ifStatement' || lastType === 'loopStatement') { 405 let lastCondition = lastStatement as ConditionStatementBuilder; 406 loopstm.nextT = lastCondition.nextT; 407 lastCondition.nextT?.lasts.add(loopstm); 408 } else { 409 loopstm.nextT = lastStatement.next; 410 lastStatement.next?.lasts.add(loopstm); 411 } 412 if (loopstm.nextT && loopstm.nextT !== loopstm) { 413 loopstm.nextT.isDoWhile = true; 414 loopstm.doStatement = loopstm.nextT; 415 } 416 this.loopStack.pop(); 417 return loopExit; 418 } 419 420 ASTNodeSwitchStatement(c: ts.SwitchStatement, lastStatement: StatementBuilder, scopeID: number): StatementBuilder { 421 this.breakin = 'switch'; 422 let switchstm = new SwitchStatementBuilder('switchStatement', '', c, scopeID); 423 this.judgeLastType(switchstm, lastStatement); 424 let switchExit = new StatementBuilder('switchExit', '', null, scopeID); 425 this.exits.push(switchExit); 426 this.switchExitStack.push(switchExit); 427 switchExit.lasts.add(switchstm); 428 switchstm.code = 'switch (' + c.expression + ')'; 429 let lastCaseExit: StatementBuilder | null = null; 430 for (let i = 0; i < c.caseBlock.clauses.length; i++) { 431 const clause = c.caseBlock.clauses[i]; 432 let casestm: StatementBuilder; 433 if (ts.isCaseClause(clause)) { 434 casestm = new StatementBuilder('statement', 'case ' + clause.expression.getText(this.sourceFile) + ':', clause, scopeID); 435 } else { 436 casestm = new StatementBuilder('statement', 'default:', clause, scopeID); 437 } 438 switchstm.nexts.push(casestm); 439 casestm.lasts.add(switchstm); 440 let caseExit = new StatementBuilder('caseExit', '', null, scopeID); 441 this.exits.push(caseExit); 442 this.walkAST(casestm, caseExit, [...clause.statements]); 443 if (ts.isCaseClause(clause)) { 444 const cas = new Case(casestm.code, casestm.next!); 445 switchstm.cases.push(cas); 446 } else { 447 switchstm.default = casestm.next; 448 } 449 switchstm.nexts[switchstm.nexts.length - 1] = casestm.next!; 450 for (const stmt of [...casestm.lasts]) { 451 casestm.next!.lasts.add(stmt); 452 } 453 casestm.next!.lasts.delete(casestm); 454 455 if (lastCaseExit) { 456 lastCaseExit.next = casestm.next; 457 casestm.next?.lasts.add(lastCaseExit); 458 } 459 lastCaseExit = caseExit; 460 if (i === c.caseBlock.clauses.length - 1) { 461 caseExit.next = switchExit; 462 switchExit.lasts.add(caseExit); 463 } 464 } 465 this.switchExitStack.pop(); 466 return switchExit; 467 } 468 469 ASTNodeTryStatement(c: ts.TryStatement, lastStatement: StatementBuilder, scopeID: number): StatementBuilder { 470 let trystm = new TryStatementBuilder('tryStatement', 'try', c, scopeID); 471 this.judgeLastType(trystm, lastStatement); 472 let tryExit = new StatementBuilder('tryExit', '', c, scopeID); 473 this.exits.push(tryExit); 474 trystm.tryExit = tryExit; 475 this.walkAST(trystm, tryExit, [...c.tryBlock.statements]); 476 trystm.tryFirst = trystm.next; 477 trystm.next?.lasts.add(trystm); 478 if (c.catchClause) { 479 let text = 'catch'; 480 if (c.catchClause.variableDeclaration) { 481 text += '(' + c.catchClause.variableDeclaration.getText(this.sourceFile) + ')'; 482 } 483 let catchOrNot = new ConditionStatementBuilder('catchOrNot', text, c, scopeID); 484 let catchExit = new StatementBuilder('catch exit', '', c, scopeID); 485 catchOrNot.nextF = catchExit; 486 catchExit.lasts.add(catchOrNot); 487 this.walkAST(catchOrNot, catchExit, [...c.catchClause.block.statements]); 488 if (!catchOrNot.nextT) { 489 catchOrNot.nextT = catchExit; 490 catchExit.lasts.add(catchOrNot); 491 } 492 const catchStatement = new StatementBuilder('statement', catchOrNot.code, c.catchClause, catchOrNot.nextT.scopeID); 493 catchStatement.next = catchOrNot.nextT; 494 trystm.catchStatement = catchStatement; 495 catchStatement.lasts.add(trystm); 496 if (c.catchClause.variableDeclaration) { 497 trystm.catchError = c.catchClause.variableDeclaration.getText(this.sourceFile); 498 } else { 499 trystm.catchError = 'Error'; 500 } 501 } 502 let final = new StatementBuilder('statement', 'finally', c, scopeID); 503 let finalExit = new StatementBuilder('finallyExit', '', c, scopeID); 504 this.exits.push(finalExit); 505 if (c.finallyBlock && c.finallyBlock.statements.length > 0) { 506 this.walkAST(final, finalExit, [...c.finallyBlock.statements]); 507 } else { 508 let dummyFinally = new StatementBuilder('statement', 'dummyFinally', c, new Scope(this.scopes.length).id); 509 final.next = dummyFinally; 510 dummyFinally.lasts.add(final); 511 dummyFinally.next = finalExit; 512 finalExit.lasts.add(dummyFinally); 513 } 514 trystm.finallyStatement = final.next; 515 tryExit.next = final.next; 516 final.next?.lasts.add(tryExit); 517 518 trystm.next = finalExit; 519 finalExit.lasts.add(trystm); 520 return finalExit; 521 } 522 523 walkAST(lastStatement: StatementBuilder, nextStatement: StatementBuilder, nodes: ts.Node[]): void { 524 let scope = new Scope(this.scopes.length); 525 this.scopes.push(scope); 526 for (let i = 0; i < nodes.length; i++) { 527 let c = nodes[i]; 528 if (ts.isVariableStatement(c) || ts.isExpressionStatement(c) || ts.isThrowStatement(c) || ts.isTypeAliasDeclaration(c) || ts.isParameter(c)) { 529 let s = new StatementBuilder('statement', c.getText(this.sourceFile), c, scope.id); 530 this.judgeLastType(s, lastStatement); 531 lastStatement = s; 532 } else if (!this.declaringMethod.isDefaultArkMethod() && ts.isFunctionDeclaration(c)) { 533 let s = new StatementBuilder('functionDeclarationStatement', c.getText(this.sourceFile), c, scope.id); 534 this.judgeLastType(s, lastStatement); 535 lastStatement = s; 536 } else if (!this.declaringMethod.isDefaultArkMethod() && ts.isClassDeclaration(c)) { 537 let s = new StatementBuilder('classDeclarationStatement', c.getText(this.sourceFile), c, scope.id); 538 this.judgeLastType(s, lastStatement); 539 lastStatement = s; 540 } else if (ts.isReturnStatement(c)) { 541 let s = new StatementBuilder('returnStatement', c.getText(this.sourceFile), c, scope.id); 542 this.judgeLastType(s, lastStatement); 543 lastStatement = s; 544 break; 545 } else if (ts.isBreakStatement(c)) { 546 this.ASTNodeBreakStatement(c, lastStatement); 547 return; 548 } else if (ts.isContinueStatement(c)) { 549 const lastLoop = this.loopStack[this.loopStack.length - 1]; 550 this.judgeLastType(lastLoop, lastStatement); 551 lastLoop.lasts.add(lastStatement); 552 return; 553 } else if (ts.isIfStatement(c)) { 554 lastStatement = this.ASTNodeIfStatement(c, lastStatement, scope.id); 555 } else if (ts.isWhileStatement(c)) { 556 lastStatement = this.ASTNodeWhileStatement(c, lastStatement, scope.id); 557 } 558 if (ts.isForStatement(c) || ts.isForInStatement(c) || ts.isForOfStatement(c)) { 559 lastStatement = this.ASTNodeForStatement(c, lastStatement, scope.id); 560 } else if (ts.isDoStatement(c)) { 561 lastStatement = this.ASTNodeDoStatement(c, lastStatement, scope.id); 562 } else if (ts.isSwitchStatement(c)) { 563 lastStatement = this.ASTNodeSwitchStatement(c, lastStatement, scope.id); 564 } else if (ts.isBlock(c)) { 565 let blockExit = new StatementBuilder('blockExit', '', c, scope.id); 566 this.exits.push(blockExit); 567 this.walkAST(lastStatement, blockExit, c.getChildren(this.sourceFile)[1].getChildren(this.sourceFile)); 568 lastStatement = blockExit; 569 } else if (ts.isTryStatement(c)) { 570 lastStatement = this.ASTNodeTryStatement(c, lastStatement, scope.id); 571 } else if (ts.isExportAssignment(c)) { 572 if (ts.isNewExpression(c.expression) || ts.isObjectLiteralExpression(c.expression)) { 573 let s = new StatementBuilder('statement', c.getText(this.sourceFile), c, scope.id); 574 this.judgeLastType(s, lastStatement); 575 lastStatement = s; 576 } 577 } 578 } 579 if (lastStatement.type !== 'breakStatement' && lastStatement.type !== 'continueStatement' && lastStatement.type !== 'returnStatement') { 580 lastStatement.next = nextStatement; 581 nextStatement.lasts.add(lastStatement); 582 } 583 } 584 585 addReturnInEmptyMethod(): void { 586 if (this.entry.next === this.exit) { 587 const ret = new StatementBuilder('returnStatement', 'return;', null, this.entry.scopeID); 588 this.entry.next = ret; 589 ret.lasts.add(this.entry); 590 ret.next = this.exit; 591 this.exit.lasts = new Set([ret]); 592 } 593 } 594 595 deleteExitAfterCondition(last: ConditionStatementBuilder, exit: StatementBuilder): void { 596 if (last.nextT === exit) { 597 last.nextT = exit.next; 598 const lasts = exit.next!.lasts; 599 lasts.delete(exit); 600 lasts.add(last); 601 } else if (last.nextF === exit) { 602 last.nextF = exit.next; 603 const lasts = exit.next!.lasts; 604 lasts.delete(exit); 605 lasts.add(last); 606 } 607 } 608 609 deleteExitAfterSwitch(last: SwitchStatementBuilder, exit: StatementBuilder): void { 610 if (exit.type === 'switchExit') { 611 last.afterSwitch = exit.next; 612 } 613 exit.next!.lasts.delete(exit); 614 last.nexts = last.nexts.filter(item => item !== exit); 615 if (last.nexts.length === 0) { 616 last.next = exit.next; 617 exit.next?.lasts.add(last); 618 } 619 } 620 621 deleteExit(): void { 622 for (const exit of this.exits) { 623 const lasts = [...exit.lasts]; 624 for (const last of lasts) { 625 if (last instanceof ConditionStatementBuilder) { 626 this.deleteExitAfterCondition(last, exit); 627 } else if (last instanceof SwitchStatementBuilder) { 628 this.deleteExitAfterSwitch(last, exit); 629 } else if (last instanceof TryStatementBuilder && exit.type === 'finallyExit') { 630 last.afterFinal = exit.next; 631 last.next = last.tryFirst; 632 exit.lasts.delete(last); 633 } else { 634 last.next = exit.next; 635 const lasts = exit.next!.lasts; 636 lasts.delete(exit); 637 lasts.add(last); 638 } 639 } 640 } 641 // 部分语句例如return后面的exit语句的next无法在上面清除 642 for (const exit of this.exits) { 643 if (exit.next && exit.next.lasts.has(exit)) { 644 exit.next.lasts.delete(exit); 645 } 646 } 647 } 648 649 addStmt2BlockStmtQueueInSpecialCase(stmt: StatementBuilder, stmtQueue: StatementBuilder[]): StatementBuilder | null { 650 if (stmt.next) { 651 if (((stmt.type === 'continueStatement' || stmt.next.type === 'loopStatement') && stmt.next.block) || stmt.next.type.includes('exit')) { 652 return null; 653 } 654 stmt.next.passTmies++; 655 if (stmt.next.passTmies === stmt.next.lasts.size || stmt.next.type === 'loopStatement' || stmt.next.isDoWhile) { 656 if ( 657 stmt.next.scopeID !== stmt.scopeID && 658 !(stmt.next instanceof ConditionStatementBuilder && stmt.next.doStatement) && 659 !(ts.isCaseClause(stmt.astNode!) || ts.isDefaultClause(stmt.astNode!)) 660 ) { 661 stmtQueue.push(stmt.next); 662 return null; 663 } 664 return stmt.next; 665 } 666 } 667 return null; 668 } 669 670 addStmt2BlockStmtQueue(stmt: StatementBuilder, stmtQueue: StatementBuilder[]): StatementBuilder | null { 671 if (stmt instanceof ConditionStatementBuilder) { 672 stmtQueue.push(stmt.nextF!); 673 stmtQueue.push(stmt.nextT!); 674 } else if (stmt instanceof SwitchStatementBuilder) { 675 if (stmt.nexts.length === 0) { 676 stmtQueue.push(stmt.afterSwitch!); 677 } 678 for (let i = stmt.nexts.length - 1; i >= 0; i--) { 679 stmtQueue.push(stmt.nexts[i]); 680 } 681 if (stmt.afterSwitch && stmt.afterSwitch.lasts.size === 0) { 682 stmtQueue.push(stmt.afterSwitch); 683 } 684 } else if (stmt instanceof TryStatementBuilder) { 685 if (stmt.finallyStatement) { 686 stmtQueue.push(stmt.finallyStatement); 687 } 688 if (stmt.catchStatement) { 689 stmtQueue.push(stmt.catchStatement); 690 } 691 if (stmt.tryFirst) { 692 stmtQueue.push(stmt.tryFirst); 693 } 694 } else if (stmt.next) { 695 return this.addStmt2BlockStmtQueueInSpecialCase(stmt, stmtQueue); 696 } 697 return null; 698 } 699 700 buildBlocks(): void { 701 const stmtQueue = [this.entry]; 702 const handledStmts: Set<StatementBuilder> = new Set(); 703 while (stmtQueue.length > 0) { 704 let stmt = stmtQueue.pop()!; 705 if (stmt.type.includes('exit')) { 706 continue; 707 } 708 if (handledStmts.has(stmt)) { 709 continue; 710 } 711 const block = new BlockBuilder(this.blocks.length, []); 712 this.blocks.push(block); 713 while (stmt && !handledStmts.has(stmt)) { 714 if (stmt.type === 'loopStatement' && block.stmts.length > 0 && !stmt.isDoWhile) { 715 stmtQueue.push(stmt); 716 break; 717 } 718 if (stmt.type.includes('Exit')) { 719 break; 720 } 721 block.stmts.push(stmt); 722 stmt.block = block; 723 handledStmts.add(stmt); 724 const addRet = this.addStmt2BlockStmtQueue(stmt, stmtQueue); 725 if (addRet instanceof StatementBuilder) { 726 stmt = addRet; 727 } else { 728 break; 729 } 730 } 731 } 732 } 733 734 buildConditionNextBlocks(originStatement: ConditionStatementBuilder, block: BlockBuilder, isLastStatement: boolean): void { 735 let nextT = originStatement.nextT?.block; 736 if (nextT && (isLastStatement || nextT !== block) && !originStatement.nextT?.type.includes(' exit')) { 737 block.nexts.push(nextT); 738 nextT.lasts.push(block); 739 } 740 let nextF = originStatement.nextF?.block; 741 if (nextF && (isLastStatement || nextF !== block) && !originStatement.nextF?.type.includes(' exit')) { 742 block.nexts.push(nextF); 743 nextF.lasts.push(block); 744 } 745 } 746 747 buildSwitchNextBlocks(originStatement: SwitchStatementBuilder, block: BlockBuilder, isLastStatement: boolean): void { 748 if (originStatement.nexts.length === 0) { 749 const nextBlock = originStatement.afterSwitch!.block; 750 if (nextBlock && (isLastStatement || nextBlock !== block)) { 751 block.nexts.push(nextBlock); 752 nextBlock.lasts.push(block); 753 } 754 } 755 for (const next of originStatement.nexts) { 756 const nextBlock = next.block; 757 if (nextBlock && (isLastStatement || nextBlock !== block)) { 758 block.nexts.push(nextBlock); 759 nextBlock.lasts.push(block); 760 } 761 } 762 } 763 764 buildNormalNextBlocks(originStatement: StatementBuilder, block: BlockBuilder, isLastStatement: boolean): void { 765 let next = originStatement.next?.block; 766 if (next && (isLastStatement || next !== block) && !originStatement.next?.type.includes(' exit')) { 767 block.nexts.push(next); 768 next.lasts.push(block); 769 } 770 } 771 772 buildBlocksNextLast(): void { 773 for (let block of this.blocks) { 774 for (let originStatement of block.stmts) { 775 let isLastStatement = block.stmts.indexOf(originStatement) === block.stmts.length - 1; 776 if (originStatement instanceof ConditionStatementBuilder) { 777 this.buildConditionNextBlocks(originStatement, block, isLastStatement); 778 } else if (originStatement instanceof SwitchStatementBuilder) { 779 this.buildSwitchNextBlocks(originStatement, block, isLastStatement); 780 } else { 781 this.buildNormalNextBlocks(originStatement, block, isLastStatement); 782 } 783 } 784 } 785 } 786 787 addReturnBlock(returnStatement: StatementBuilder, notReturnStmts: StatementBuilder[]): void { 788 let returnBlock = new BlockBuilder(this.blocks.length, [returnStatement]); 789 returnStatement.block = returnBlock; 790 this.blocks.push(returnBlock); 791 for (const notReturnStmt of notReturnStmts) { 792 if (notReturnStmt instanceof ConditionStatementBuilder) { 793 if (this.exit === notReturnStmt.nextT) { 794 notReturnStmt.nextT = returnStatement; 795 notReturnStmt.block?.nexts.splice(0, 0, returnBlock); 796 } else if (this.exit === notReturnStmt.nextF) { 797 notReturnStmt.nextF = returnStatement; 798 notReturnStmt.block?.nexts.push(returnBlock); 799 } 800 } else { 801 notReturnStmt.next = returnStatement; 802 notReturnStmt.block?.nexts.push(returnBlock); 803 } 804 returnStatement.lasts.add(notReturnStmt); 805 returnStatement.next = this.exit; 806 const lasts = [...this.exit.lasts]; 807 lasts[lasts.indexOf(notReturnStmt)] = returnStatement; 808 this.exit.lasts = new Set(lasts); 809 returnBlock.lasts.push(notReturnStmt.block!); 810 } 811 this.exit.block = returnBlock; 812 } 813 814 addReturnStmt(): void { 815 let notReturnStmts: StatementBuilder[] = []; 816 for (let stmt of [...this.exit.lasts]) { 817 if (stmt.type !== 'returnStatement') { 818 notReturnStmts.push(stmt); 819 } 820 } 821 if (notReturnStmts.length < 1) { 822 return; 823 } 824 const returnStatement = new StatementBuilder('returnStatement', 'return;', null, this.exit.scopeID); 825 let TryOrSwitchExit = false; 826 if (notReturnStmts.length === 1 && notReturnStmts[0].block) { 827 let p: ts.Node | null = notReturnStmts[0].astNode; 828 while (p && p !== this.astRoot) { 829 if (ts.isTryStatement(p) || ts.isSwitchStatement(p)) { 830 TryOrSwitchExit = true; 831 break; 832 } 833 p = p.parent; 834 } 835 } 836 if (notReturnStmts.length === 1 && !(notReturnStmts[0] instanceof ConditionStatementBuilder) && !TryOrSwitchExit) { 837 const notReturnStmt = notReturnStmts[0]; 838 notReturnStmt.next = returnStatement; 839 returnStatement.lasts = new Set([notReturnStmt]); 840 returnStatement.next = this.exit; 841 const lasts = [...this.exit.lasts]; 842 lasts[lasts.indexOf(notReturnStmt)] = returnStatement; 843 this.exit.lasts = new Set(lasts); 844 notReturnStmt.block?.stmts.push(returnStatement); 845 returnStatement.block = notReturnStmt.block; 846 } else { 847 this.addReturnBlock(returnStatement, notReturnStmts); 848 } 849 } 850 851 resetWalked(): void { 852 for (let stmt of this.statementArray) { 853 stmt.walked = false; 854 } 855 } 856 857 addStmtBuilderPosition(): void { 858 for (const stmt of this.statementArray) { 859 if (stmt.astNode) { 860 const { line, character } = ts.getLineAndCharacterOfPosition(this.sourceFile, stmt.astNode.getStart(this.sourceFile)); 861 stmt.line = line + 1; 862 stmt.column = character + 1; 863 } 864 } 865 } 866 867 CfgBuilder2Array(stmt: StatementBuilder): void { 868 if (stmt.walked) { 869 return; 870 } 871 stmt.walked = true; 872 stmt.index = this.statementArray.length; 873 if (!stmt.type.includes(' exit')) { 874 this.statementArray.push(stmt); 875 } 876 if (stmt.type === 'ifStatement' || stmt.type === 'loopStatement' || stmt.type === 'catchOrNot') { 877 let cstm = stmt as ConditionStatementBuilder; 878 if (cstm.nextT == null || cstm.nextF == null) { 879 this.errorTest(cstm); 880 return; 881 } 882 this.CfgBuilder2Array(cstm.nextF); 883 this.CfgBuilder2Array(cstm.nextT); 884 } else if (stmt.type === 'switchStatement') { 885 let sstm = stmt as SwitchStatementBuilder; 886 for (let ss of sstm.nexts) { 887 this.CfgBuilder2Array(ss); 888 } 889 } else if (stmt.type === 'tryStatement') { 890 let trystm = stmt as TryStatementBuilder; 891 if (trystm.tryFirst) { 892 this.CfgBuilder2Array(trystm.tryFirst); 893 } 894 if (trystm.catchStatement) { 895 this.CfgBuilder2Array(trystm.catchStatement); 896 } 897 if (trystm.finallyStatement) { 898 this.CfgBuilder2Array(trystm.finallyStatement); 899 } 900 if (trystm.next) { 901 this.CfgBuilder2Array(trystm.next); 902 } 903 } else { 904 if (stmt.next != null) { 905 this.CfgBuilder2Array(stmt.next); 906 } 907 } 908 } 909 910 getDotEdges(stmt: StatementBuilder): void { 911 if (this.statementArray.length === 0) { 912 this.CfgBuilder2Array(this.entry); 913 } 914 if (stmt.walked) { 915 return; 916 } 917 stmt.walked = true; 918 if (stmt.type === 'ifStatement' || stmt.type === 'loopStatement' || stmt.type === 'catchOrNot') { 919 let cstm = stmt as ConditionStatementBuilder; 920 if (cstm.nextT == null || cstm.nextF == null) { 921 this.errorTest(cstm); 922 return; 923 } 924 let edge = [cstm.index, cstm.nextF.index]; 925 this.dotEdges.push(edge); 926 edge = [cstm.index, cstm.nextT.index]; 927 this.dotEdges.push(edge); 928 this.getDotEdges(cstm.nextF); 929 this.getDotEdges(cstm.nextT); 930 } else if (stmt.type === 'switchStatement') { 931 let sstm = stmt as SwitchStatementBuilder; 932 for (let ss of sstm.nexts) { 933 let edge = [sstm.index, ss.index]; 934 this.dotEdges.push(edge); 935 this.getDotEdges(ss); 936 } 937 } else { 938 if (stmt.next != null) { 939 let edge = [stmt.index, stmt.next.index]; 940 this.dotEdges.push(edge); 941 this.getDotEdges(stmt.next); 942 } 943 } 944 } 945 946 errorTest(stmt: StatementBuilder): void { 947 let mes = 'ifnext error '; 948 if (this.declaringClass?.getDeclaringArkFile()) { 949 mes += this.declaringClass?.getDeclaringArkFile().getName() + '.' + this.declaringClass.getName() + '.' + this.name; 950 } 951 mes += '\n' + stmt.code; 952 throw new TextError(mes); 953 } 954 955 buildStatementBuilder4ArrowFunction(stmt: ts.Node): void { 956 let s = new StatementBuilder('statement', stmt.getText(this.sourceFile), stmt, 0); 957 this.entry.next = s; 958 s.lasts = new Set([this.entry]); 959 s.next = this.exit; 960 this.exit.lasts = new Set([s]); 961 } 962 963 private getParamPropertyNodes(constructorParams: ts.NodeArray<ParameterDeclaration>): ts.Node[] { 964 let stmts: ts.Node[] = []; 965 constructorParams.forEach(parameter => { 966 if (parameter.modifiers !== undefined) { 967 stmts.push(parameter); 968 } 969 }); 970 return stmts; 971 } 972 973 buildCfgBuilder(): void { 974 let stmts: ts.Node[] = []; 975 if (ts.isSourceFile(this.astRoot)) { 976 stmts = [...this.astRoot.statements]; 977 } else if ( 978 ts.isFunctionDeclaration(this.astRoot) || 979 ts.isMethodDeclaration(this.astRoot) || 980 ts.isConstructorDeclaration(this.astRoot) || 981 ts.isGetAccessorDeclaration(this.astRoot) || 982 ts.isSetAccessorDeclaration(this.astRoot) || 983 ts.isFunctionExpression(this.astRoot) || 984 ts.isClassStaticBlockDeclaration(this.astRoot) 985 ) { 986 this.astRoot.body ? stmts = [...this.astRoot.body.statements] : this.emptyBody = true; 987 } else if (ts.isArrowFunction(this.astRoot)) { 988 if (ts.isBlock(this.astRoot.body)) { 989 stmts = [...this.astRoot.body.statements]; 990 } 991 } else if ( 992 ts.isMethodSignature(this.astRoot) || 993 ts.isConstructSignatureDeclaration(this.astRoot) || 994 ts.isCallSignatureDeclaration(this.astRoot) || 995 ts.isFunctionTypeNode(this.astRoot) 996 ) { 997 this.emptyBody = true; 998 } else if (ts.isModuleDeclaration(this.astRoot) && ts.isModuleBlock(this.astRoot.body!)) { 999 stmts = [...this.astRoot.body.statements]; 1000 } 1001 // For constructor, add parameter property node to stmts which can be used when build body 1002 if (ts.isConstructorDeclaration(this.astRoot)) { 1003 stmts = [...this.getParamPropertyNodes(this.astRoot.parameters), ...stmts]; 1004 } 1005 if (!ModelUtils.isArkUIBuilderMethod(this.declaringMethod)) { 1006 this.walkAST(this.entry, this.exit, stmts); 1007 } else { 1008 this.handleBuilder(stmts); 1009 } 1010 if (ts.isArrowFunction(this.astRoot) && !ts.isBlock(this.astRoot.body)) { 1011 this.buildStatementBuilder4ArrowFunction(this.astRoot.body); 1012 } 1013 1014 this.addReturnInEmptyMethod(); 1015 this.deleteExit(); 1016 this.CfgBuilder2Array(this.entry); 1017 this.addStmtBuilderPosition(); 1018 this.buildBlocks(); 1019 this.blocks = this.blocks.filter(b => b.stmts.length !== 0); 1020 this.buildBlocksNextLast(); 1021 this.addReturnStmt(); 1022 } 1023 1024 private handleBuilder(stmts: ts.Node[]): void { 1025 let lastStmt = this.entry; 1026 for (const stmt of stmts) { 1027 const stmtBuilder = new StatementBuilder('statement', stmt.getText(this.sourceFile), stmt, 0); 1028 lastStmt.next = stmtBuilder; 1029 stmtBuilder.lasts.add(lastStmt); 1030 lastStmt = stmtBuilder; 1031 } 1032 lastStmt.next = this.exit; 1033 this.exit.lasts.add(lastStmt); 1034 } 1035 1036 public isBodyEmpty(): boolean { 1037 return this.emptyBody; 1038 } 1039 1040 public buildCfg(): { 1041 cfg: Cfg; 1042 locals: Set<Local>; 1043 globals: Map<string, GlobalRef> | null; 1044 aliasTypeMap: Map<string, [AliasType, ArkAliasTypeDefineStmt]>; 1045 traps: Trap[]; 1046 } { 1047 if (ts.isArrowFunction(this.astRoot) && !ts.isBlock(this.astRoot.body)) { 1048 return this.buildCfgForSimpleArrowFunction(); 1049 } 1050 1051 return this.buildNormalCfg(); 1052 } 1053 1054 public buildCfgForSimpleArrowFunction(): { 1055 cfg: Cfg; 1056 locals: Set<Local>; 1057 globals: Map<string, GlobalRef> | null; 1058 aliasTypeMap: Map<string, [AliasType, ArkAliasTypeDefineStmt]>; 1059 traps: Trap[]; 1060 } { 1061 const stmts: Stmt[] = []; 1062 const arkIRTransformer = new ArkIRTransformer(this.sourceFile, this.declaringMethod); 1063 arkIRTransformer.prebuildStmts().forEach(stmt => stmts.push(stmt)); 1064 const expressionBodyNode = (this.astRoot as ts.ArrowFunction).body as ts.Expression; 1065 const expressionBodyStmts: Stmt[] = []; 1066 let { 1067 value: expressionBodyValue, 1068 valueOriginalPositions: expressionBodyPositions, 1069 stmts: tempStmts, 1070 } = arkIRTransformer.tsNodeToValueAndStmts(expressionBodyNode); 1071 tempStmts.forEach(stmt => expressionBodyStmts.push(stmt)); 1072 if (IRUtils.moreThanOneAddress(expressionBodyValue)) { 1073 ({ 1074 value: expressionBodyValue, 1075 valueOriginalPositions: expressionBodyPositions, 1076 stmts: tempStmts, 1077 } = arkIRTransformer.generateAssignStmtForValue(expressionBodyValue, expressionBodyPositions)); 1078 tempStmts.forEach(stmt => expressionBodyStmts.push(stmt)); 1079 } 1080 const returnStmt = new ArkReturnStmt(expressionBodyValue); 1081 returnStmt.setOperandOriginalPositions([expressionBodyPositions[0], ...expressionBodyPositions]); 1082 expressionBodyStmts.push(returnStmt); 1083 arkIRTransformer.mapStmtsToTsStmt(expressionBodyStmts, expressionBodyNode); 1084 expressionBodyStmts.forEach(stmt => stmts.push(stmt)); 1085 const cfg = new Cfg(); 1086 const blockInCfg = new BasicBlock(); 1087 blockInCfg.setId(0); 1088 stmts.forEach(stmt => { 1089 blockInCfg.addStmt(stmt); 1090 stmt.setCfg(cfg); 1091 }); 1092 cfg.addBlock(blockInCfg); 1093 cfg.setStartingStmt(stmts[0]); 1094 return { 1095 cfg: cfg, 1096 locals: arkIRTransformer.getLocals(), 1097 globals: arkIRTransformer.getGlobals(), 1098 aliasTypeMap: arkIRTransformer.getAliasTypeMap(), 1099 traps: [], 1100 }; 1101 } 1102 1103 public buildNormalCfg(): { 1104 cfg: Cfg; 1105 locals: Set<Local>; 1106 globals: Map<string, GlobalRef> | null; 1107 aliasTypeMap: Map<string, [AliasType, ArkAliasTypeDefineStmt]>; 1108 traps: Trap[]; 1109 } { 1110 const { blockBuilderToCfgBlock, basicBlockSet, arkIRTransformer } = this.initializeBuild(); 1111 const { blocksContainLoopCondition, blockBuildersBeforeTry, blockBuildersContainSwitch, valueAndStmtsOfSwitchAndCasesAll } = this.processBlocks( 1112 blockBuilderToCfgBlock, 1113 basicBlockSet, 1114 arkIRTransformer 1115 ); 1116 1117 const currBlockId = this.blocks.length; 1118 this.linkBasicBlocks(blockBuilderToCfgBlock); 1119 this.adjustBlocks( 1120 blockBuilderToCfgBlock, 1121 blocksContainLoopCondition, 1122 basicBlockSet, 1123 blockBuildersContainSwitch, 1124 valueAndStmtsOfSwitchAndCasesAll, 1125 arkIRTransformer 1126 ); 1127 1128 const trapBuilder = new TrapBuilder(); 1129 const traps = trapBuilder.buildTraps(blockBuilderToCfgBlock, blockBuildersBeforeTry, arkIRTransformer, basicBlockSet); 1130 1131 const cfg = this.createCfg(blockBuilderToCfgBlock, basicBlockSet, currBlockId); 1132 return { 1133 cfg, 1134 locals: arkIRTransformer.getLocals(), 1135 globals: arkIRTransformer.getGlobals(), 1136 aliasTypeMap: arkIRTransformer.getAliasTypeMap(), 1137 traps, 1138 }; 1139 } 1140 1141 private initializeBuild(): { 1142 blockBuilderToCfgBlock: Map<BlockBuilder, BasicBlock>; 1143 basicBlockSet: Set<BasicBlock>; 1144 arkIRTransformer: ArkIRTransformer; 1145 } { 1146 const blockBuilderToCfgBlock = new Map<BlockBuilder, BasicBlock>(); 1147 const basicBlockSet = new Set<BasicBlock>(); 1148 const arkIRTransformer = new ArkIRTransformer(this.sourceFile, this.declaringMethod); 1149 return { blockBuilderToCfgBlock, basicBlockSet, arkIRTransformer }; 1150 } 1151 1152 private processBlocks( 1153 blockBuilderToCfgBlock: Map<BlockBuilder, BasicBlock>, 1154 basicBlockSet: Set<BasicBlock>, 1155 arkIRTransformer: ArkIRTransformer 1156 ): { 1157 blocksContainLoopCondition: Set<BlockBuilder>; 1158 blockBuildersBeforeTry: Set<BlockBuilder>; 1159 blockBuildersContainSwitch: BlockBuilder[]; 1160 valueAndStmtsOfSwitchAndCasesAll: ValueAndStmts[][]; 1161 } { 1162 const blocksContainLoopCondition = new Set<BlockBuilder>(); 1163 const blockBuildersBeforeTry = new Set<BlockBuilder>(); 1164 const blockBuildersContainSwitch: BlockBuilder[] = []; 1165 const valueAndStmtsOfSwitchAndCasesAll: ValueAndStmts[][] = []; 1166 for (let i = 0; i < this.blocks.length; i++) { 1167 const stmtsInBlock: Stmt[] = []; 1168 if (i === 0) { 1169 arkIRTransformer.prebuildStmts().forEach(stmt => stmtsInBlock.push(stmt)); 1170 } 1171 const stmtsCnt = this.blocks[i].stmts.length; 1172 if (this.blocks[i].stmts[stmtsCnt - 1].type === 'tryStatement') { 1173 blockBuildersBeforeTry.add(this.blocks[i]); 1174 } 1175 for (const statementBuilder of this.blocks[i].stmts) { 1176 if (statementBuilder.type === 'loopStatement') { 1177 blocksContainLoopCondition.add(this.blocks[i]); 1178 } else if (statementBuilder instanceof SwitchStatementBuilder) { 1179 blockBuildersContainSwitch.push(this.blocks[i]); 1180 const valueAndStmtsOfSwitchAndCases = arkIRTransformer.switchStatementToValueAndStmts(statementBuilder.astNode as ts.SwitchStatement); 1181 valueAndStmtsOfSwitchAndCasesAll.push(valueAndStmtsOfSwitchAndCases); 1182 continue; 1183 } 1184 if (statementBuilder.astNode && statementBuilder.code !== '') { 1185 arkIRTransformer.tsNodeToStmts(statementBuilder.astNode).forEach(s => stmtsInBlock.push(s)); 1186 } else if (statementBuilder.code.startsWith('return')) { 1187 stmtsInBlock.push(this.generateReturnStmt(arkIRTransformer)); 1188 } 1189 } 1190 const blockInCfg = new BasicBlock(); 1191 blockInCfg.setId(this.blocks[i].id); 1192 for (const stmt of stmtsInBlock) { 1193 blockInCfg.addStmt(stmt); 1194 } 1195 basicBlockSet.add(blockInCfg); 1196 blockBuilderToCfgBlock.set(this.blocks[i], blockInCfg); 1197 } 1198 return { 1199 blocksContainLoopCondition, 1200 blockBuildersBeforeTry, 1201 blockBuildersContainSwitch, 1202 valueAndStmtsOfSwitchAndCasesAll, 1203 }; 1204 } 1205 1206 private generateReturnStmt(arkIRTransformer: ArkIRTransformer): Stmt { 1207 if (this.name === CONSTRUCTOR_NAME) { 1208 this.declaringMethod.getSubSignature().setReturnType(arkIRTransformer.getThisLocal().getType()); 1209 return new ArkReturnStmt(arkIRTransformer.getThisLocal()); 1210 } 1211 if (this.declaringMethod.getSubSignature().getReturnType() instanceof UnknownType && !this.declaringMethod.getAsteriskToken()) { 1212 if (this.declaringMethod.containsModifier(ModifierType.ASYNC)) { 1213 const promise = this.declaringMethod.getDeclaringArkFile().getScene().getSdkGlobal(PROMISE); 1214 if (promise instanceof ArkClass) { 1215 this.declaringMethod.getSubSignature().setReturnType(new ClassType(promise.getSignature())); 1216 } else { 1217 this.declaringMethod.getSubSignature().setReturnType(new UnclearReferenceType(PROMISE, [VoidType.getInstance()])); 1218 } 1219 } else { 1220 this.declaringMethod.getSubSignature().setReturnType(VoidType.getInstance()); 1221 } 1222 } 1223 return new ArkReturnVoidStmt(); 1224 } 1225 1226 private adjustBlocks( 1227 blockBuilderToCfgBlock: Map<BlockBuilder, BasicBlock>, 1228 blocksContainLoopCondition: Set<BlockBuilder>, 1229 basicBlockSet: Set<BasicBlock>, 1230 blockBuildersContainSwitch: BlockBuilder[], 1231 valueAndStmtsOfSwitchAndCasesAll: ValueAndStmts[][], 1232 arkIRTransformer: ArkIRTransformer 1233 ): void { 1234 const loopBuilder = new LoopBuilder(); 1235 loopBuilder.rebuildBlocksInLoop(blockBuilderToCfgBlock, blocksContainLoopCondition, basicBlockSet, this.blocks); 1236 const switchBuilder = new SwitchBuilder(); 1237 switchBuilder.buildSwitch(blockBuilderToCfgBlock, blockBuildersContainSwitch, valueAndStmtsOfSwitchAndCasesAll, arkIRTransformer, basicBlockSet); 1238 const conditionalBuilder = new ConditionBuilder(); 1239 conditionalBuilder.rebuildBlocksContainConditionalOperator(basicBlockSet, ModelUtils.isArkUIBuilderMethod(this.declaringMethod)); 1240 } 1241 1242 private createCfg(blockBuilderToCfgBlock: Map<BlockBuilder, BasicBlock>, basicBlockSet: Set<BasicBlock>, prevBlockId: number): Cfg { 1243 let currBlockId = prevBlockId; 1244 for (const blockBuilder of this.blocks) { 1245 if (blockBuilder.id === -1) { 1246 blockBuilder.id = currBlockId++; 1247 const block = blockBuilderToCfgBlock.get(blockBuilder) as BasicBlock; 1248 block.setId(blockBuilder.id); 1249 } 1250 } 1251 1252 const cfg = new Cfg(); 1253 const startingBasicBlock = blockBuilderToCfgBlock.get(this.blocks[0])!; 1254 cfg.setStartingStmt(startingBasicBlock.getStmts()[0]); 1255 currBlockId = 0; 1256 for (const basicBlock of basicBlockSet) { 1257 basicBlock.setId(currBlockId++); 1258 cfg.addBlock(basicBlock); 1259 } 1260 for (const stmt of cfg.getStmts()) { 1261 stmt.setCfg(cfg); 1262 } 1263 return cfg; 1264 } 1265 1266 private linkBasicBlocks(blockBuilderToCfgBlock: Map<BlockBuilder, BasicBlock>): void { 1267 for (const [blockBuilder, cfgBlock] of blockBuilderToCfgBlock) { 1268 for (const successorBlockBuilder of blockBuilder.nexts) { 1269 if (!blockBuilderToCfgBlock.get(successorBlockBuilder)) { 1270 continue; 1271 } 1272 const successorBlock = blockBuilderToCfgBlock.get(successorBlockBuilder) as BasicBlock; 1273 cfgBlock.addSuccessorBlock(successorBlock); 1274 } 1275 for (const predecessorBlockBuilder of blockBuilder.lasts) { 1276 if (!blockBuilderToCfgBlock.get(predecessorBlockBuilder)) { 1277 continue; 1278 } 1279 const predecessorBlock = blockBuilderToCfgBlock.get(predecessorBlockBuilder) as BasicBlock; 1280 cfgBlock.addPredecessorBlock(predecessorBlock); 1281 } 1282 } 1283 } 1284} 1285