• 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 { AbstractBinopExpr, AbstractInvokeExpr, ArkCastExpr, ArkUnopExpr } from '../base/Expr';
17import { AbstractFieldRef, AbstractRef, ArkArrayRef, ArkInstanceFieldRef, ArkStaticFieldRef } from '../base/Ref';
18import { Value } from '../base/Value';
19import { Scene } from '../../Scene';
20import ts from 'ohos-typescript';
21import { SceneOptions } from '../../Config';
22import { ArkMetadataKind, CommentItem, CommentsMetadata } from '../model/ArkMetadata';
23import { Stmt } from '../base/Stmt';
24import { ArkBaseModel } from '../model/ArkBaseModel';
25import { FullPosition } from '../base/Position';
26import { Local } from '../base/Local';
27import { NAME_PREFIX } from './Const';
28
29export class IRUtils {
30    public static moreThanOneAddress(value: Value): boolean {
31        if (
32            value instanceof AbstractBinopExpr ||
33            value instanceof AbstractInvokeExpr ||
34            value instanceof AbstractFieldRef ||
35            value instanceof ArkArrayRef ||
36            value instanceof ArkCastExpr ||
37            value instanceof ArkUnopExpr
38        ) {
39            return true;
40        }
41        return false;
42    }
43
44    public static generateTextForStmt(scene: Scene): void {
45        for (const method of scene.getMethods()) {
46            const cfg = method.getCfg();
47            if (cfg) {
48                for (const stmt of cfg.getStmts()) {
49                    stmt.setText(stmt.toString());
50                }
51            }
52        }
53    }
54
55    public static setComments(metadata: Stmt | ArkBaseModel, node: ts.Node, sourceFile: ts.SourceFile, options: SceneOptions): void {
56        const leadingCommentsMetadata = this.getCommentsMetadata(node, sourceFile, options, true);
57        if (leadingCommentsMetadata.getComments().length > 0) {
58            metadata.setMetadata(ArkMetadataKind.LEADING_COMMENTS, leadingCommentsMetadata);
59        }
60
61        const trailingCommentsMetadata = this.getCommentsMetadata(node, sourceFile, options, false);
62        if (trailingCommentsMetadata.getComments().length > 0) {
63            metadata.setMetadata(ArkMetadataKind.TRAILING_COMMENTS, trailingCommentsMetadata);
64        }
65    }
66
67    public static getCommentsMetadata(node: ts.Node, sourceFile: ts.SourceFile, options: SceneOptions, isLeading: boolean): CommentsMetadata {
68        const comments: CommentItem[] = [];
69        if ((isLeading && !options.enableLeadingComments) || (!isLeading && !options.enableTrailingComments)) {
70            return new CommentsMetadata(comments);
71        }
72
73        // node.pos is the start position of
74        const commentRanges =
75            (isLeading ? ts.getLeadingCommentRanges(sourceFile.text, node.pos) : ts.getTrailingCommentRanges(sourceFile.text, node.end)) || [];
76        // leading comment, while node.end is the
77        // end position of the statement
78        const getPosition = (pos: number, end: number): FullPosition => {
79            const start = ts.getLineAndCharacterOfPosition(sourceFile, pos);
80            const endPos = ts.getLineAndCharacterOfPosition(sourceFile, end);
81            return new FullPosition(start.line + 1, start.character + 1, endPos.line + 1, endPos.character + 1);
82        };
83
84        for (const range of commentRanges) {
85            comments.push({
86                content: sourceFile.text.substring(range.pos, range.end).replace(/\r\n/g, '\n'),
87                position: getPosition(range.pos, range.end),
88            });
89        }
90
91        return new CommentsMetadata(comments);
92    }
93
94    public static isTempLocal(value: Value): boolean {
95        return value instanceof Local && value.getName().startsWith(NAME_PREFIX);
96    }
97
98    public static findOperandIdx(stmt: Stmt, operand: Value): number {
99        let index: number = -1;
100        const operands = stmt.getDefAndUses();
101        for (let i = 0; i < operands.length; i++) {
102            if (operands[i] === operand) {
103                index = i;
104                break;
105            }
106        }
107        return index;
108    }
109
110    public static adjustOperandOriginalPositions(stmt: Stmt, oldValue: Value, newValue: Value): void {
111        const operandOriginalPositions = stmt.getOperandOriginalPositions();
112        if (!operandOriginalPositions) {
113            return;
114        }
115        const operandOriginalPositionSize = operandOriginalPositions.length;
116        const defUseSize = stmt.getDefAndUses().length;
117        const oldValueUseSize = oldValue.getUses().length;
118        const newValueUseSize = newValue.getUses().length;
119        const oldValueIdx = IRUtils.findOperandIdx(stmt, oldValue);
120        const baseValueOffset = 1;
121        const fieldValueOffset = 2;
122
123        if (oldValue instanceof AbstractRef && newValue instanceof AbstractRef) {
124            if (newValue instanceof ArkStaticFieldRef) {
125                operandOriginalPositions.splice(oldValueIdx + baseValueOffset, oldValueUseSize - newValueUseSize);
126            } else if (oldValue instanceof ArkStaticFieldRef) {
127                operandOriginalPositions.splice(oldValueIdx + baseValueOffset, 0, ...IRUtils.generateDefaultPositions(newValueUseSize - oldValueUseSize));
128            }
129
130            if (oldValue instanceof ArkInstanceFieldRef && newValue instanceof ArkArrayRef) {
131                if (operandOriginalPositionSize === defUseSize) {
132                    // may not reserve positions for field name
133                    operandOriginalPositions.splice(oldValueIdx + fieldValueOffset, 0, ...IRUtils.generateDefaultPositions(newValueUseSize - oldValueUseSize));
134                }
135            } else if (oldValue instanceof ArkArrayRef && newValue instanceof ArkInstanceFieldRef) {
136                operandOriginalPositions.splice(oldValueIdx + fieldValueOffset, oldValueUseSize - newValueUseSize);
137            }
138        } else if (oldValue instanceof AbstractInvokeExpr && newValue instanceof AbstractInvokeExpr) {
139            if (oldValueUseSize === newValueUseSize + 1) {
140                operandOriginalPositions.splice(oldValueIdx + baseValueOffset, 1);
141            } else if (oldValueUseSize === newValueUseSize - 1) {
142                operandOriginalPositions.splice(oldValueIdx + baseValueOffset, 0, FullPosition.DEFAULT);
143            }
144        }
145    }
146
147    public static generateDefaultPositions(count: number): FullPosition[] {
148        const defaultPositions: FullPosition[] = [];
149        for (let i = 0; i < count; i++) {
150            defaultPositions.push(FullPosition.DEFAULT);
151        }
152        return defaultPositions;
153    }
154}
155