• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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