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