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