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