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 } from '../../common/ArkIRTransformer'; 18import { Trap } from '../../base/Trap'; 19import { ArkCaughtExceptionRef } from '../../base/Ref'; 20import { UnknownType } from '../../base/Type'; 21import { FullPosition } from '../../base/Position'; 22import { 23 ArkAssignStmt, 24 ArkIfStmt, 25 ArkInvokeStmt, 26 ArkReturnStmt, 27 ArkReturnVoidStmt, 28 ArkThrowStmt, 29 Stmt, 30} from '../../base/Stmt'; 31import { BlockBuilder, TryStatementBuilder } from './CfgBuilder'; 32import Logger, { LOG_MODULE_TYPE } from '../../../utils/logger'; 33 34const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'TrapBuilder'); 35 36/** 37 * Builder for traps from try...catch 38 */ 39export class TrapBuilder { 40 public buildTraps( 41 blockBuilderToCfgBlock: Map<BlockBuilder, BasicBlock>, 42 blockBuildersBeforeTry: Set<BlockBuilder>, 43 arkIRTransformer: ArkIRTransformer, 44 basicBlockSet: Set<BasicBlock> 45 ): Trap[] { 46 const traps: Trap[] = []; 47 for (const blockBuilderBeforeTry of blockBuildersBeforeTry) { 48 if (blockBuilderBeforeTry.nexts.length === 0) { 49 logger.error(`can't find try block.`); 50 continue; 51 } 52 const blockBuilderContainTry = blockBuilderBeforeTry.nexts[0]; 53 const stmtsCnt = blockBuilderBeforeTry.stmts.length; 54 const tryStmtBuilder = blockBuilderBeforeTry.stmts[stmtsCnt - 1] as TryStatementBuilder; 55 const finallyBlockBuilder = tryStmtBuilder.finallyStatement?.block; 56 if (!finallyBlockBuilder) { 57 logger.error(`can't find finally block or dummy finally block.`); 58 continue; 59 } 60 const { bfsBlocks: tryBfsBlocks, tailBlocks: tryTailBlocks } = this.getAllBlocksBFS( 61 blockBuilderToCfgBlock, 62 blockBuilderContainTry, 63 finallyBlockBuilder 64 ); 65 let catchBfsBlocks: BasicBlock[] = []; 66 let catchTailBlocks: BasicBlock[] = []; 67 const catchBlockBuilder = tryStmtBuilder.catchStatement?.block; 68 if (catchBlockBuilder) { 69 ({ bfsBlocks: catchBfsBlocks, tailBlocks: catchTailBlocks } = this.getAllBlocksBFS(blockBuilderToCfgBlock, catchBlockBuilder)); 70 } 71 const finallyStmts = finallyBlockBuilder.stmts; 72 const blockBuilderAfterFinally = tryStmtBuilder.afterFinal?.block; 73 if (!blockBuilderAfterFinally) { 74 logger.error(`can't find block after try...catch.`); 75 continue; 76 } 77 if (finallyStmts.length === 1 && finallyStmts[0].code === 'dummyFinally') { 78 // no finally block 79 const trapsIfNoFinally = this.buildTrapsIfNoFinally( 80 tryBfsBlocks, 81 tryTailBlocks, 82 catchBfsBlocks, 83 catchTailBlocks, 84 finallyBlockBuilder, 85 blockBuilderAfterFinally, 86 basicBlockSet, 87 blockBuilderToCfgBlock 88 ); 89 if (trapsIfNoFinally) { 90 traps.push(...trapsIfNoFinally); 91 } 92 } else { 93 const trapsIfFinallyExist = this.buildTrapsIfFinallyExist( 94 tryBfsBlocks, 95 tryTailBlocks, 96 catchBfsBlocks, 97 catchTailBlocks, 98 finallyBlockBuilder, 99 blockBuilderAfterFinally, 100 basicBlockSet, 101 arkIRTransformer, 102 blockBuilderToCfgBlock 103 ); 104 traps.push(...trapsIfFinallyExist); 105 } 106 } 107 return traps; 108 } 109 110 private buildTrapsIfNoFinally( 111 tryBfsBlocks: BasicBlock[], 112 tryTailBlocks: BasicBlock[], 113 catchBfsBlocks: BasicBlock[], 114 catchTailBlocks: BasicBlock[], 115 finallyBlockBuilder: BlockBuilder, 116 blockBuilderAfterFinally: BlockBuilder, 117 basicBlockSet: Set<BasicBlock>, 118 blockBuilderToCfgBlock: Map<BlockBuilder, BasicBlock> 119 ): Trap[] | null { 120 if (catchBfsBlocks.length === 0) { 121 logger.error(`catch block expected.`); 122 return null; 123 } 124 if (!blockBuilderToCfgBlock.has(blockBuilderAfterFinally)) { 125 logger.error(`can't find basicBlock corresponding to the blockBuilder.`); 126 return null; 127 } 128 let blockAfterFinally: BasicBlock = blockBuilderToCfgBlock.get(blockBuilderAfterFinally)!; 129 if (!blockBuilderToCfgBlock.has(finallyBlockBuilder)) { 130 logger.error(`can't find basicBlock corresponding to the blockBuilder.`); 131 return null; 132 } 133 const finallyBlock = blockBuilderToCfgBlock.get(finallyBlockBuilder)!; 134 let dummyFinallyIdxInPredecessors = -1; 135 for (let i = 0; i < blockAfterFinally.getPredecessors().length; i++) { 136 if (blockAfterFinally.getPredecessors()[i] === finallyBlock) { 137 dummyFinallyIdxInPredecessors = i; 138 break; 139 } 140 } 141 if (dummyFinallyIdxInPredecessors === -1) { 142 return null; 143 } 144 blockAfterFinally.getPredecessors().splice(dummyFinallyIdxInPredecessors, 1); 145 for (const tryTailBlock of tryTailBlocks) { 146 tryTailBlock.setSuccessorBlock(0, blockAfterFinally); 147 blockAfterFinally.addPredecessorBlock(tryTailBlock); 148 } 149 basicBlockSet.delete(finallyBlock); 150 151 for (const catchTailBlock of catchTailBlocks) { 152 catchTailBlock.addSuccessorBlock(blockAfterFinally); 153 blockAfterFinally.addPredecessorBlock(catchTailBlock); 154 } 155 for (const tryTailBlock of tryTailBlocks) { 156 tryTailBlock.addExceptionalSuccessorBlock(catchBfsBlocks[0]); 157 } 158 return [new Trap(tryBfsBlocks, catchBfsBlocks)]; 159 } 160 161 private buildTrapsIfFinallyExist( 162 tryBfsBlocks: BasicBlock[], 163 tryTailBlocks: BasicBlock[], 164 catchBfsBlocks: BasicBlock[], 165 catchTailBlocks: BasicBlock[], 166 finallyBlockBuilder: BlockBuilder, 167 blockBuilderAfterFinally: BlockBuilder, 168 basicBlockSet: Set<BasicBlock>, 169 arkIRTransformer: ArkIRTransformer, 170 blockBuilderToCfgBlock: Map<BlockBuilder, BasicBlock> 171 ): Trap[] { 172 const { bfsBlocks: finallyBfsBlocks, tailBlocks: finallyTailBlocks } = this.getAllBlocksBFS( 173 blockBuilderToCfgBlock, 174 finallyBlockBuilder, 175 blockBuilderAfterFinally 176 ); 177 const copyFinallyBfsBlocks = this.copyFinallyBlocks(finallyBfsBlocks, finallyTailBlocks, basicBlockSet, arkIRTransformer, blockBuilderToCfgBlock); 178 const traps: Trap[] = []; 179 if (catchBfsBlocks.length !== 0) { 180 for (const catchTailBlock of catchTailBlocks) { 181 catchTailBlock.addSuccessorBlock(finallyBfsBlocks[0]); 182 finallyBfsBlocks[0].addPredecessorBlock(catchTailBlock); 183 } 184 // try -> catch trap 185 for (const tryTailBlock of tryTailBlocks) { 186 tryTailBlock.addExceptionalSuccessorBlock(catchBfsBlocks[0]); 187 } 188 traps.push(new Trap(tryBfsBlocks, catchBfsBlocks)); 189 // catch -> finally trap 190 for (const catchTailBlock of catchTailBlocks) { 191 catchTailBlock.addExceptionalSuccessorBlock(copyFinallyBfsBlocks[0]); 192 } 193 traps.push(new Trap(catchBfsBlocks, copyFinallyBfsBlocks)); 194 } else { 195 // try -> finally trap 196 for (const tryTailBlock of tryTailBlocks) { 197 tryTailBlock.addExceptionalSuccessorBlock(copyFinallyBfsBlocks[0]); 198 } 199 traps.push(new Trap(tryBfsBlocks, copyFinallyBfsBlocks)); 200 } 201 return traps; 202 } 203 204 private getAllBlocksBFS( 205 blockBuilderToCfgBlock: Map<BlockBuilder, BasicBlock>, 206 startBlockBuilder: BlockBuilder, 207 endBlockBuilder?: BlockBuilder 208 ): { bfsBlocks: BasicBlock[]; tailBlocks: BasicBlock[] } { 209 const bfsBlocks: BasicBlock[] = []; 210 const tailBlocks: BasicBlock[] = []; 211 const queue: BlockBuilder[] = []; 212 const visitedBlockBuilders = new Set<BlockBuilder>(); 213 queue.push(startBlockBuilder); 214 while (queue.length !== 0) { 215 const currBlockBuilder = queue.splice(0, 1)[0]; 216 if (visitedBlockBuilders.has(currBlockBuilder)) { 217 continue; 218 } 219 visitedBlockBuilders.add(currBlockBuilder); 220 if (!blockBuilderToCfgBlock.has(currBlockBuilder)) { 221 logger.error(`can't find basicBlock corresponding to the blockBuilder.`); 222 continue; 223 } 224 const currBlock = blockBuilderToCfgBlock.get(currBlockBuilder)!; 225 bfsBlocks.push(currBlock); 226 227 const childList = currBlockBuilder.nexts; 228 if (childList.length !== 0) { 229 for (const child of childList) { 230 // A tail block's successor may be within the traversal range 231 if (child === endBlockBuilder) { 232 tailBlocks.push(currBlock); 233 } else { 234 queue.push(child); 235 } 236 } 237 } else { 238 tailBlocks.push(currBlock); 239 } 240 } 241 return { bfsBlocks, tailBlocks }; 242 } 243 244 private copyFinallyBlocks( 245 finallyBfsBlocks: BasicBlock[], 246 finallyTailBlocks: BasicBlock[], 247 basicBlockSet: Set<BasicBlock>, 248 arkIRTransformer: ArkIRTransformer, 249 blockBuilderToCfgBlock: Map<BlockBuilder, BasicBlock> 250 ): BasicBlock[] { 251 const copyFinallyBfsBlocks = this.copyBlocks(finallyBfsBlocks); 252 const caughtExceptionRef = new ArkCaughtExceptionRef(UnknownType.getInstance()); 253 const { value: exceptionValue, stmts: exceptionAssignStmts } = arkIRTransformer.generateAssignStmtForValue(caughtExceptionRef, [FullPosition.DEFAULT]); 254 copyFinallyBfsBlocks[0].addHead(exceptionAssignStmts); 255 const finallyPredecessorsCnt = copyFinallyBfsBlocks[0].getPredecessors().length; 256 copyFinallyBfsBlocks[0].getPredecessors().splice(0, finallyPredecessorsCnt); 257 const throwStmt = new ArkThrowStmt(exceptionValue); 258 let copyFinallyTailBlocks = copyFinallyBfsBlocks.splice(copyFinallyBfsBlocks.length - finallyTailBlocks.length, finallyTailBlocks.length); 259 if (copyFinallyTailBlocks.length > 1) { 260 const newCopyFinallyTailBlock = new BasicBlock(); 261 copyFinallyTailBlocks.forEach((copyFinallyTailBlock: BasicBlock) => { 262 copyFinallyTailBlock.addSuccessorBlock(newCopyFinallyTailBlock); 263 newCopyFinallyTailBlock.addPredecessorBlock(copyFinallyTailBlock); 264 }); 265 copyFinallyBfsBlocks.push(...copyFinallyTailBlocks); 266 copyFinallyTailBlocks = [newCopyFinallyTailBlock]; 267 } 268 copyFinallyTailBlocks[0].addStmt(throwStmt); 269 copyFinallyBfsBlocks.push(...copyFinallyTailBlocks); 270 copyFinallyBfsBlocks.forEach((copyFinallyBfsBlock: BasicBlock) => { 271 basicBlockSet.add(copyFinallyBfsBlock); 272 }); 273 return copyFinallyBfsBlocks; 274 } 275 276 private copyBlocks(sourceBlocks: BasicBlock[]): BasicBlock[] { 277 const sourceToTarget = new Map<BasicBlock, BasicBlock>(); 278 const targetBlocks: BasicBlock[] = []; 279 for (const sourceBlock of sourceBlocks) { 280 const targetBlock = new BasicBlock(); 281 for (const stmt of sourceBlock.getStmts()) { 282 targetBlock.addStmt(this.copyStmt(stmt)!); 283 } 284 sourceToTarget.set(sourceBlock, targetBlock); 285 targetBlocks.push(targetBlock); 286 } 287 for (const sourceBlock of sourceBlocks) { 288 const targetBlock = sourceToTarget.get(sourceBlock)!; 289 for (const predecessor of sourceBlock.getPredecessors()) { 290 const targetPredecessor = sourceToTarget.get(predecessor); 291 // Only include blocks within the copy range, so that predecessor and successor relationships to 292 // external blocks can be trimmed 293 if (targetPredecessor) { 294 targetBlock.addPredecessorBlock(targetPredecessor); 295 } 296 297 } 298 for (const successor of sourceBlock.getSuccessors()) { 299 const targetSuccessor = sourceToTarget.get(successor); 300 if (targetSuccessor) { 301 targetBlock.addSuccessorBlock(targetSuccessor); 302 } 303 } 304 } 305 return targetBlocks; 306 } 307 308 private copyStmt(sourceStmt: Stmt): Stmt | null { 309 if (sourceStmt instanceof ArkAssignStmt) { 310 return new ArkAssignStmt(sourceStmt.getLeftOp(), sourceStmt.getRightOp()); 311 } else if (sourceStmt instanceof ArkInvokeStmt) { 312 return new ArkInvokeStmt(sourceStmt.getInvokeExpr()); 313 } else if (sourceStmt instanceof ArkIfStmt) { 314 return new ArkIfStmt(sourceStmt.getConditionExpr()); 315 } else if (sourceStmt instanceof ArkReturnStmt) { 316 return new ArkReturnStmt(sourceStmt.getOp()); 317 } else if (sourceStmt instanceof ArkReturnVoidStmt) { 318 return new ArkReturnVoidStmt(); 319 } else if (sourceStmt instanceof ArkThrowStmt) { 320 return new ArkThrowStmt(sourceStmt.getOp()); 321 } 322 return null; 323 } 324} 325