• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 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 { BasicBlock } from '../BasicBlock';
17import { ArkIRTransformer, ValueAndStmts } from '../../common/ArkIRTransformer';
18import { Stmt } from '../../base/Stmt';
19import { BlockBuilder, SwitchStatementBuilder } from './CfgBuilder';
20import Logger, { LOG_MODULE_TYPE } from '../../../utils/logger';
21
22const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'SwitchBuilder');
23
24/**
25 * Builder for switch statement in CFG
26 */
27export class SwitchBuilder {
28    public buildSwitch(
29        blockBuilderToCfgBlock: Map<BlockBuilder, BasicBlock>,
30        blockBuildersContainSwitch: BlockBuilder[],
31        valueAndStmtsOfSwitchAndCasesAll: ValueAndStmts[][],
32        arkIRTransformer: ArkIRTransformer,
33        basicBlockSet: Set<BasicBlock>
34    ): void {
35        for (let i = 0; i < blockBuildersContainSwitch.length; i++) {
36            const blockBuilderContainSwitch = blockBuildersContainSwitch[i];
37
38            if (!blockBuilderToCfgBlock.has(blockBuilderContainSwitch)) {
39                logger.error(`can't find basicBlock corresponding to the blockBuilder.`);
40                continue;
41            }
42
43            const blockContainSwitch = blockBuilderToCfgBlock.get(blockBuilderContainSwitch)!;
44            const valueAndStmtsOfSwitch = valueAndStmtsOfSwitchAndCasesAll[i][0];
45            const stmtsOfSwitch = valueAndStmtsOfSwitch.stmts;
46            stmtsOfSwitch.forEach((stmt: Stmt) => {
47                blockContainSwitch.addStmt(stmt);
48            });
49
50            const stmtsCnt = blockBuilderContainSwitch.stmts.length;
51            const switchStmtBuilder = blockBuilderContainSwitch.stmts[stmtsCnt - 1] as SwitchStatementBuilder;
52            const cases = switchStmtBuilder.cases;
53            let nonEmptyCaseCnt = 0;
54            for (const currCase of cases) {
55                if (currCase.stmt.block) {
56                    // there are stmts after this case
57                    nonEmptyCaseCnt++;
58                }
59            }
60            if (nonEmptyCaseCnt === 0) {
61                continue;
62            }
63
64            const caseCnt = cases.length;
65            const caseIfBlocks = this.generateIfBlocksForCases(
66                valueAndStmtsOfSwitchAndCasesAll[i],
67                caseCnt,
68                blockContainSwitch,
69                basicBlockSet,
70                arkIRTransformer
71            );
72            this.linkIfBlockAndCaseBlock(blockContainSwitch, caseIfBlocks, switchStmtBuilder, blockBuilderToCfgBlock);
73        }
74    }
75
76    private generateIfBlocksForCases(
77        valueAndStmtsOfSwitchAndCases: ValueAndStmts[],
78        caseCnt: number,
79        blockContainSwitch: BasicBlock,
80        basicBlockSet: Set<BasicBlock>,
81        arkIRTransformer: ArkIRTransformer
82    ): BasicBlock[] {
83        const valueAndStmtsOfSwitch = valueAndStmtsOfSwitchAndCases[0];
84        const valueOfSwitch = valueAndStmtsOfSwitch.value;
85        const caseIfBlocks: BasicBlock[] = [];
86
87        for (let j = 0; j < caseCnt; j++) {
88            let caseIfBlock: BasicBlock;
89            if (j === 0) {
90                caseIfBlock = blockContainSwitch;
91            } else {
92                caseIfBlock = new BasicBlock();
93                basicBlockSet.add(caseIfBlock);
94            }
95            caseIfBlocks.push(caseIfBlock);
96
97            const caseValueAndStmts = valueAndStmtsOfSwitchAndCases[j + 1];
98            const caseValue = caseValueAndStmts.value;
99            const caseStmts = caseValueAndStmts.stmts;
100            caseStmts.forEach((stmt: Stmt) => {
101                caseIfBlock.addStmt(stmt);
102            });
103            const caseIfStmts = arkIRTransformer.generateIfStmtForValues(
104                valueOfSwitch,
105                valueAndStmtsOfSwitch.valueOriginalPositions,
106                caseValue,
107                caseValueAndStmts.valueOriginalPositions
108            );
109            caseIfStmts.forEach((stmt: Stmt) => {
110                caseIfBlock.addStmt(stmt);
111            });
112        }
113        return caseIfBlocks;
114    }
115
116    private linkIfBlockAndCaseBlock(
117        blockContainSwitch: BasicBlock,
118        caseIfBlocks: BasicBlock[],
119        switchStmtBuilder: SwitchStatementBuilder,
120        blockBuilderToCfgBlock: Map<BlockBuilder, BasicBlock>
121    ): boolean {
122        const successorsOfBlockContainSwitch = Array.from(blockContainSwitch.getSuccessors());
123        const expectedSuccessorsOfCaseIfBlock: BasicBlock[] = [];
124        const defaultStmtBuilder = switchStmtBuilder.default;
125        if (defaultStmtBuilder && defaultStmtBuilder.block) {
126            expectedSuccessorsOfCaseIfBlock.push(...successorsOfBlockContainSwitch.splice(-1, 1));
127        } else {
128            const afterSwitchStmtBuilder = switchStmtBuilder.afterSwitch;
129            const afterSwitchBlockBuilder = afterSwitchStmtBuilder?.block;
130            if (!afterSwitchBlockBuilder || !blockBuilderToCfgBlock.has(afterSwitchBlockBuilder)) {
131                logger.error(`can't find basicBlock corresponding to the blockBuilder.`);
132                return false;
133            }
134            expectedSuccessorsOfCaseIfBlock.push(blockBuilderToCfgBlock.get(afterSwitchBlockBuilder)!);
135        }
136        const caseCnt = switchStmtBuilder.cases.length;
137        for (let i = caseCnt - 1; i >= 0; i--) {
138            const currCase = switchStmtBuilder.cases[i];
139            if (currCase.stmt.block) {
140                expectedSuccessorsOfCaseIfBlock.push(...successorsOfBlockContainSwitch.splice(-1, 1));
141            } else {
142                // if there are no stmts after this case, reuse the successor of the next case
143                expectedSuccessorsOfCaseIfBlock.push(...expectedSuccessorsOfCaseIfBlock.slice(-1));
144            }
145        }
146        expectedSuccessorsOfCaseIfBlock.reverse();
147
148        blockContainSwitch.getSuccessors().forEach(successor => {
149            successor.getPredecessors().splice(0, 1);
150        });
151        blockContainSwitch.getSuccessors().splice(0);
152        for (let j = 0; j < caseCnt; j++) {
153            const caseIfBlock = caseIfBlocks[j];
154            caseIfBlock.addSuccessorBlock(expectedSuccessorsOfCaseIfBlock[j]);
155            expectedSuccessorsOfCaseIfBlock[j].addPredecessorBlock(caseIfBlock);
156            if (j === caseCnt - 1) {
157                // the false branch of last case should be default or block after switch statement
158                caseIfBlock.addSuccessorBlock(expectedSuccessorsOfCaseIfBlock[j + 1]);
159                expectedSuccessorsOfCaseIfBlock[j + 1].addPredecessorBlock(caseIfBlock);
160            } else {
161                caseIfBlock.addSuccessorBlock(caseIfBlocks[j + 1]);
162                caseIfBlocks[j + 1].addPredecessorBlock(caseIfBlock);
163            }
164        }
165        return true;
166    }
167}
168