• 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 } 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