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 { AbstractExpr, ArkAssignStmt, ArkFile, ArkNormalBinopExpr, ArkStaticFieldRef, ArkUnopExpr, ClassSignature, Constant, ImportInfo, Local, NumberType, Stmt, Value } from 'arkanalyzer'; 17import { VarInfo } from '../../model/VarInfo'; 18import { CheckerStorage } from '../common/CheckerStorage'; 19import { NumberValue, ValueType } from '../../model/NumberValue'; 20import { StmtExt } from '../../model/StmtExt'; 21 22export class NumberUtils { 23 public static readonly mBinopList: string[] = ['+', '-', '*', '/', '%', '<<', '>>', '&', '|', '^', '>>>']; 24 25 private static isSupportOperator(operator: string): boolean { 26 return this.mBinopList.includes(operator); 27 } 28 29 public static isValueSupportCalculation(arkFile: ArkFile, valueStmtInfo: VarInfo, value: Value): boolean { 30 if (value instanceof Constant && value.getType() instanceof NumberType) { 31 return true; 32 } else if (value instanceof Local) { 33 let importInfo = NumberUtils.getValueImportInfo(arkFile, value); 34 if (importInfo) { 35 return NumberUtils.isImportValueSupportCalculate(arkFile, importInfo, value); 36 } else { 37 return NumberUtils.isMethodValueSupportCalculate(arkFile, valueStmtInfo, value); 38 } 39 } else if (value instanceof AbstractExpr && value.getType() instanceof NumberType) { 40 return NumberUtils.isExprSupportCalculate(arkFile, valueStmtInfo, value); 41 } else if (value instanceof ArkStaticFieldRef && value.getType() instanceof NumberType) { 42 return true; 43 } 44 return false; 45 } 46 47 private static isExprSupportCalculate(arkFile: ArkFile, valueStmtInfo: VarInfo, value: AbstractExpr): boolean { 48 if (value instanceof ArkNormalBinopExpr && this.isSupportOperator(value.getOperator())) { 49 return NumberUtils.isValueSupportCalculation(arkFile, valueStmtInfo, value.getOp1()) && 50 NumberUtils.isValueSupportCalculation(arkFile, valueStmtInfo, value.getOp2()); 51 } else if (value instanceof ArkUnopExpr) { 52 let op = value.getUses()[0]; 53 return NumberUtils.isValueSupportCalculation(arkFile, valueStmtInfo, op); 54 } 55 return false; 56 } 57 58 private static isMethodValueSupportCalculate(arkFile: ArkFile, valueStmtInfo: VarInfo, value: Local): boolean { 59 let scope = valueStmtInfo.scope; 60 let valueStmt: Stmt = valueStmtInfo.stmt; 61 if (!scope || !scope.defList) { 62 return false; 63 } 64 let hasFind = false; 65 let defList = scope.defList; 66 for (let defVar of defList) { 67 if (defVar.getName() !== value.getName()) { 68 continue; 69 } 70 hasFind = true; 71 let nearReDefStmtInfo = new VarInfo(defVar.defStmt, scope); 72 let reDefStmtInfos = defVar.redefInfo; 73 for (let reDefStmtInfo of reDefStmtInfos) { 74 let originalLine = valueStmt.getOriginPositionInfo().getLineNo(); 75 if (reDefStmtInfo.stmt.getOriginPositionInfo().getLineNo() >= originalLine) { 76 break; 77 } 78 nearReDefStmtInfo = reDefStmtInfo; 79 } 80 if (!nearReDefStmtInfo || !(nearReDefStmtInfo instanceof VarInfo)) { 81 continue; 82 } 83 let stmt = nearReDefStmtInfo.stmt; 84 if (stmt instanceof ArkAssignStmt) { 85 let rightOp = stmt.getRightOp(); 86 return NumberUtils.isValueSupportCalculation(arkFile, nearReDefStmtInfo, rightOp); 87 } 88 } 89 90 if (!hasFind && scope.parentScope != null) { 91 let defStmtInfo = new VarInfo(valueStmt, scope.parentScope); 92 return NumberUtils.isValueSupportCalculation(arkFile, defStmtInfo, value); 93 } 94 return false; 95 } 96 97 private static isImportValueSupportCalculate(arkFile: ArkFile, importInfo: ImportInfo, value: Local): boolean { 98 let exportInfo = importInfo.getLazyExportInfo(); 99 let importArkFile = exportInfo?.getDeclaringArkFile(); 100 if (!importArkFile) { 101 return false; 102 } 103 let scope = CheckerStorage.getInstance().getScope(importArkFile.getFilePath()); 104 if (!scope) { 105 return false; 106 } 107 for (let varDef of scope.defList) { 108 if (varDef.getName() !== value.getName()) { 109 continue; 110 } 111 let stmt = varDef.defStmt; 112 if (stmt instanceof ArkAssignStmt) { 113 let defStmtInfo = new VarInfo(stmt, scope); 114 let rightOp = stmt.getRightOp(); 115 return NumberUtils.isValueSupportCalculation(importArkFile, defStmtInfo, rightOp); 116 } 117 } 118 return false; 119 } 120 121 private static getValueImportInfo(arkFile: ArkFile, value: Local): any { 122 let importInfos = arkFile.getImportInfos(); 123 for (let importInfo of importInfos) { 124 if (importInfo.getImportClauseName() === value.getName()) { 125 return importInfo; 126 } 127 } 128 return undefined; 129 } 130 131 public static getNumberByScope(arkFile: ArkFile, valueStmtInfo: VarInfo, value: Value): NumberValue { 132 if (value instanceof Constant && value.getType() instanceof NumberType) { 133 let valueStr = value.getValue(); 134 let numberValue = Number(valueStr); 135 if (valueStr.includes('.')) { 136 return new NumberValue(numberValue, ValueType.DOUBLE); 137 } else { 138 return new NumberValue(numberValue, ValueType.INT); 139 } 140 } else if (value instanceof Local) { 141 let importInfo = this.getValueImportInfo(arkFile, value); 142 if (importInfo) { 143 return this.getImportNumberValue(arkFile, importInfo, value); 144 } else { 145 return this.getMethodNumberValue(arkFile, valueStmtInfo, value); 146 } 147 } else if (value instanceof AbstractExpr && value.getType() instanceof NumberType) { 148 return this.getExprNumberValue(arkFile, valueStmtInfo, value); 149 } else if (value instanceof ArkStaticFieldRef && value.getType() instanceof NumberType) { 150 return this.getStaticNumberValue(arkFile, valueStmtInfo, value); 151 } 152 return new NumberValue(0, ValueType.UNKNOWN); 153 } 154 155 private static getImportNumberValue(arkFile: ArkFile, importInfo: ImportInfo, value: Local): NumberValue { 156 let exportInfo = importInfo.getLazyExportInfo(); 157 let importArkFile = exportInfo?.getDeclaringArkFile(); 158 if (!importArkFile) { 159 return new NumberValue(0, ValueType.UNKNOWN); 160 } 161 let scope = CheckerStorage.getInstance().getScope(importArkFile.getFilePath()); 162 if (!scope) { 163 return new NumberValue(0, ValueType.UNKNOWN); 164 } 165 for (let varDef of scope.defList) { 166 if (varDef.getName() !== value.getName()) { 167 continue; 168 } 169 let stmt = varDef.defStmt; 170 if (stmt instanceof ArkAssignStmt) { 171 let defStmtInfo = new VarInfo(stmt, scope); 172 let rightOp = stmt.getRightOp(); 173 return this.getNumberByScope(importArkFile, defStmtInfo, rightOp); 174 } 175 } 176 return new NumberValue(0, ValueType.UNKNOWN); 177 } 178 179 private static getMethodNumberValue(arkFile: ArkFile, valueStmtInfo: VarInfo, value: Local): NumberValue { 180 let scope = valueStmtInfo.scope; 181 let valueStmt = valueStmtInfo.stmt; 182 if (!scope || !scope.defList) { 183 return new NumberValue(0, ValueType.UNKNOWN); 184 } 185 let hasFind = false; 186 let defList = scope.defList; 187 for (let defVar of defList) { 188 if (defVar.getName() !== value.getName()) { 189 continue; 190 } 191 hasFind = true; 192 193 let nearReDefStmtInfo = new VarInfo(defVar.defStmt, scope); 194 let reDefStmtInfos = defVar.redefInfo; 195 196 for (let reDefStmtInfo of reDefStmtInfos) { 197 let originalLine = valueStmt.getOriginPositionInfo().getLineNo(); 198 let redefLine = reDefStmtInfo.stmt.getOriginPositionInfo().getLineNo(); 199 if (redefLine >= originalLine) { 200 break; 201 } 202 nearReDefStmtInfo = reDefStmtInfo; 203 } 204 let stmt = nearReDefStmtInfo.stmt; 205 if (stmt instanceof ArkAssignStmt) { 206 let rightOp = stmt.getRightOp(); 207 return this.getNumberByScope(arkFile, nearReDefStmtInfo, rightOp); 208 } 209 } 210 211 if (!hasFind && scope.parentScope != null) { 212 let defStmtInfo = new VarInfo(valueStmt, scope.parentScope); 213 return this.getNumberByScope(arkFile, defStmtInfo, value); 214 } 215 return new NumberValue(0, ValueType.UNKNOWN); 216 } 217 218 private static getExprNumberValue(arkFile: ArkFile, stmeInfo: VarInfo, value: AbstractExpr): NumberValue { 219 if (value instanceof ArkNormalBinopExpr) { 220 if (this.isSupportOperator(value.getOperator())) { 221 let valueOfOp1 = this.getNumberByScope(arkFile, stmeInfo, value.getOp1()); 222 let valueOfOp2 = this.getNumberByScope(arkFile, stmeInfo, value.getOp2()); 223 switch (value.getOperator()) { 224 case '+': 225 return new NumberValue(valueOfOp1.value + valueOfOp2.value, valueOfOp1.type | valueOfOp2.type); 226 case '-': 227 return new NumberValue(valueOfOp1.value - valueOfOp2.value, valueOfOp1.type | valueOfOp2.type); 228 case '*': 229 return new NumberValue(valueOfOp1.value * valueOfOp2.value, valueOfOp1.type | valueOfOp2.type); 230 case '/': 231 return new NumberValue(valueOfOp1.value / valueOfOp2.value, valueOfOp1.type | valueOfOp2.type); 232 case '%': 233 return new NumberValue(valueOfOp1.value % valueOfOp2.value, valueOfOp1.type | valueOfOp2.type); 234 case '&': 235 return new NumberValue(valueOfOp1.value & valueOfOp2.value, valueOfOp1.type | valueOfOp2.type); 236 case '|': 237 return new NumberValue(valueOfOp1.value | valueOfOp2.value, valueOfOp1.type | valueOfOp2.type); 238 case '^': 239 return new NumberValue(valueOfOp1.value ^ valueOfOp2.value, valueOfOp1.type | valueOfOp2.type); 240 case '>>': 241 return new NumberValue(valueOfOp1.value >> valueOfOp2.value, valueOfOp1.type | valueOfOp2.type); 242 case '<<': 243 return new NumberValue(valueOfOp1.value << valueOfOp2.value, valueOfOp1.type | valueOfOp2.type); 244 case '>>>': 245 return new NumberValue(valueOfOp1.value >>> valueOfOp2.value, valueOfOp1.type | valueOfOp2.type); 246 } 247 } 248 } else if (value instanceof ArkUnopExpr) { 249 let op = value.getUses()[0]; 250 let valueOfOp = this.getNumberByScope(arkFile, stmeInfo, op); 251 switch (value.getOperator()) { 252 case '-': 253 return new NumberValue(-valueOfOp.value, valueOfOp.type); 254 case '~': 255 return new NumberValue(~valueOfOp.value, valueOfOp.type); 256 } 257 } 258 return new NumberValue(0, ValueType.UNKNOWN); 259 } 260 261 private static getStaticNumberValue(arkFile: ArkFile, valueStmtInfo: VarInfo, value: ArkStaticFieldRef): NumberValue { 262 let classSignature = value.getFieldSignature().getDeclaringSignature(); 263 if (!(classSignature instanceof ClassSignature)) { 264 return new NumberValue(0, ValueType.UNKNOWN); 265 } 266 267 let fileSignature = classSignature.getDeclaringFileSignature(); 268 let staticClassArkFile = arkFile.getScene().getFile(fileSignature); 269 270 if (staticClassArkFile) { 271 let staticClass = staticClassArkFile.getClass(classSignature); 272 if (!staticClass) { 273 return new NumberValue(0, ValueType.UNKNOWN); 274 } 275 276 let staticField = staticClass.getStaticFieldWithName(value.getFieldName()); 277 if (!staticField) { 278 return new NumberValue(0, ValueType.UNKNOWN); 279 } 280 281 let stmts = staticField.getInitializer(); 282 if (stmts.length === 0) { 283 return new NumberValue(0, ValueType.UNKNOWN); 284 } 285 286 let stmt = stmts[0]; 287 new VarInfo(stmt, (stmt as StmtExt).scope); 288 if (!(stmt instanceof ArkAssignStmt)) { 289 return new NumberValue(0, ValueType.UNKNOWN); 290 } 291 292 let initValue = stmt.getRightOp(); 293 return this.getNumberByScope(arkFile, valueStmtInfo, initValue); 294 } 295 return new NumberValue(0, ValueType.UNKNOWN); 296 } 297 298 public static getOriginalValueText(stmt: Stmt, value: Value): string { 299 let valStr = ''; 300 if (value instanceof Constant) { 301 valStr = value.toString(); 302 } else if (value instanceof Local) { 303 if (!value.toString().includes('%')) { 304 valStr = value.toString(); 305 } else { 306 let declaringStmt = value.getDeclaringStmt(); 307 if (declaringStmt instanceof ArkAssignStmt) { 308 return this.getOriginalValueText(stmt, declaringStmt.getRightOp()); 309 } 310 } 311 } else if (value instanceof ArkNormalBinopExpr) { 312 if (!value.toString().includes('%')) { 313 valStr = value.toString(); 314 } else { 315 let originalPosition = stmt.getOperandOriginalPosition(value); 316 if (!originalPosition) { 317 return this.getOriginalValueText(stmt, value.getOp1()) + ' ' + value.getOperator() + ' ' + 318 this.getOriginalValueText(stmt, value.getOp2()); 319 } 320 const text = stmt.getOriginalText(); 321 if (!text || text.length === 0) { 322 return this.getOriginalValueText(stmt, value.getOp1()) + ' ' + value.getOperator() + ' ' + 323 this.getOriginalValueText(stmt, value.getOp2()); 324 } 325 let startColum = stmt.getOriginPositionInfo().getColNo(); 326 return text.substring(originalPosition.getFirstCol() - startColum, originalPosition.getLastCol() - startColum); 327 } 328 } else if (value instanceof ArkUnopExpr) { 329 if (!value.toString().includes('%')) { 330 valStr = value.toString(); 331 } else { 332 valStr = value.getOperator() + this.getOriginalValueText(stmt, value.getUses()[0]); 333 } 334 } else if (value instanceof ArkStaticFieldRef) { 335 let fieldSignature = value.getFieldSignature(); 336 let declaringSignature = fieldSignature.getDeclaringSignature(); 337 if (declaringSignature instanceof ClassSignature) { 338 valStr = declaringSignature.getClassName() + '.' + fieldSignature.getFieldName(); 339 } 340 } 341 return valStr; 342 } 343}