• 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 { Scene } from '../../Scene';
17import { DataflowProblem, FlowFunction } from './DataflowProblem';
18import { Local } from '../base/Local';
19import { Value } from '../base/Value';
20import { ClassType, UndefinedType } from '../base/Type';
21import { ArkAssignStmt, ArkInvokeStmt, Stmt } from '../base/Stmt';
22import { ArkMethod } from '../model/ArkMethod';
23import { Constant } from '../base/Constant';
24import { AbstractRef, ArkInstanceFieldRef, ArkStaticFieldRef } from '../base/Ref';
25import { DataflowSolver } from './DataflowSolver';
26import { ArkInstanceInvokeExpr, ArkStaticInvokeExpr } from '../base/Expr';
27import { FileSignature, NamespaceSignature } from '../model/ArkSignature';
28import { ArkClass } from '../model/ArkClass';
29import { LocalEqual, RefEqual } from './Util';
30import { INSTANCE_INIT_METHOD_NAME, STATIC_INIT_METHOD_NAME } from '../common/Const';
31import { ArkField } from '../model/ArkField';
32import Logger, { LOG_MODULE_TYPE } from '../../utils/logger';
33
34const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'Scene');
35
36export class UndefinedVariableChecker extends DataflowProblem<Value> {
37    zeroValue: Constant = new Constant('undefined', UndefinedType.getInstance());
38    entryPoint: Stmt;
39    entryMethod: ArkMethod;
40    scene: Scene;
41    classMap: Map<FileSignature | NamespaceSignature, ArkClass[]>;
42    globalVariableMap: Map<FileSignature | NamespaceSignature, Local[]>;
43    outcomes: Outcome[] = [];
44    constructor(stmt: Stmt, method: ArkMethod) {
45        super();
46        this.entryPoint = stmt;
47        this.entryMethod = method;
48        this.scene = method.getDeclaringArkFile().getScene();
49        this.classMap = this.scene.getClassMap();
50        this.globalVariableMap = this.scene.getGlobalVariableMap();
51    }
52
53    getEntryPoint(): Stmt {
54        return this.entryPoint;
55    }
56
57    getEntryMethod(): ArkMethod {
58        return this.entryMethod;
59    }
60
61    private isUndefined(val: Value): boolean {
62        if (val instanceof Constant) {
63            let constant: Constant = val as Constant;
64            if (constant.getType() instanceof UndefinedType) {
65                return true;
66            }
67        }
68        return false;
69    }
70
71    getNormalFlowFunction(srcStmt: Stmt, tgtStmt: Stmt): FlowFunction<Value> {
72        let checkerInstance: UndefinedVariableChecker = this;
73        return new (class implements FlowFunction<Value> {
74            getDataFacts(dataFact: Value): Set<Value> {
75                let ret: Set<Value> = new Set();
76                if (checkerInstance.getEntryPoint() === srcStmt && checkerInstance.getZeroValue() === dataFact) {
77                    ret.add(checkerInstance.getZeroValue());
78                    return ret;
79                }
80                if (srcStmt instanceof ArkAssignStmt) {
81                    checkerInstance.insideNormalFlowFunction(ret, srcStmt, dataFact);
82                }
83                return ret;
84            }
85        })();
86    }
87
88    insideNormalFlowFunction(ret: Set<Value>, srcStmt: ArkAssignStmt, dataFact: Value): void {
89        if (!this.factEqual(srcStmt.getDef()!, dataFact)) {
90            if (!(dataFact instanceof Local && dataFact.getName() === srcStmt.getDef()!.toString())) {
91                ret.add(dataFact);
92            }
93        }
94        let ass: ArkAssignStmt = srcStmt as ArkAssignStmt;
95        let assigned: Value = ass.getLeftOp();
96        let rightOp: Value = ass.getRightOp();
97        if (this.getZeroValue() === dataFact) {
98            if (this.isUndefined(rightOp)) {
99                ret.add(assigned);
100            }
101        } else if (this.factEqual(rightOp, dataFact) || rightOp.getType() instanceof UndefinedType) {
102            ret.add(assigned);
103        } else if (rightOp instanceof ArkInstanceFieldRef) {
104            const base = rightOp.getBase();
105            if (base === dataFact || (!base.getDeclaringStmt() && base.getName() === dataFact.toString())) {
106                this.outcomes.push(new Outcome(rightOp, ass));
107                logger.info('undefined base');
108                logger.info(srcStmt.toString());
109                logger.info(srcStmt.getOriginPositionInfo().toString());
110            }
111        } else if (dataFact instanceof ArkInstanceFieldRef && rightOp === dataFact.getBase()) {
112            const field = new ArkInstanceFieldRef(srcStmt.getLeftOp() as Local, dataFact.getFieldSignature());
113            ret.add(field);
114        }
115    }
116
117    getCallFlowFunction(srcStmt: Stmt, method: ArkMethod): FlowFunction<Value> {
118        let checkerInstance: UndefinedVariableChecker = this;
119        return new (class implements FlowFunction<Value> {
120            getDataFacts(dataFact: Value): Set<Value> {
121                const ret: Set<Value> = new Set();
122                if (checkerInstance.getZeroValue() === dataFact) {
123                    checkerInstance.insideCallFlowFunction(ret, method);
124                } else {
125                    const callExpr = srcStmt.getExprs()[0];
126                    if (
127                        callExpr instanceof ArkInstanceInvokeExpr &&
128                        dataFact instanceof ArkInstanceFieldRef &&
129                        callExpr.getBase().getName() === dataFact.getBase().getName()
130                    ) {
131                        // todo:base转this
132                        const thisRef = new ArkInstanceFieldRef(
133                            new Local('this', new ClassType(method.getDeclaringArkClass().getSignature())),
134                            dataFact.getFieldSignature()
135                        );
136                        ret.add(thisRef);
137                    } else if (
138                        callExpr instanceof ArkStaticInvokeExpr &&
139                        dataFact instanceof ArkStaticFieldRef &&
140                        callExpr.getMethodSignature().getDeclaringClassSignature() === dataFact.getFieldSignature().getDeclaringSignature()
141                    ) {
142                        ret.add(dataFact);
143                    }
144                }
145                checkerInstance.addParameters(srcStmt, dataFact, method, ret);
146                return ret;
147            }
148        })();
149    }
150
151    insideCallFlowFunction(ret: Set<Value>, method: ArkMethod): void {
152        ret.add(this.getZeroValue());
153        // 加上调用函数能访问到的所有静态变量,如果不考虑多线程,加上所有变量,考虑则要统计之前已经处理过的变量并排除
154        for (const field of method.getDeclaringArkClass().getStaticFields(this.classMap)) {
155            if (field.getInitializer() === undefined) {
156                ret.add(new ArkStaticFieldRef(field.getSignature()));
157            }
158        }
159        for (const local of method.getDeclaringArkClass().getGlobalVariable(this.globalVariableMap)) {
160            ret.add(local);
161        }
162        // 加上所有未定义初始值的属性
163        if (method.getName() === INSTANCE_INIT_METHOD_NAME || method.getName() === STATIC_INIT_METHOD_NAME) {
164            for (const field of method.getDeclaringArkClass().getFields()) {
165                this.addUndefinedField(field, method, ret);
166            }
167        }
168    }
169
170    addUndefinedField(field: ArkField, method: ArkMethod, ret: Set<Value>): void {
171        let defined = false;
172        for (const stmt of method.getCfg()!.getStmts()) {
173            const def = stmt.getDef();
174            if (def instanceof ArkInstanceFieldRef && def.getFieldSignature() === field.getSignature()) {
175                defined = true;
176                break;
177            }
178        }
179        if (!defined) {
180            const fieldRef = new ArkInstanceFieldRef(new Local('this', new ClassType(method.getDeclaringArkClass().getSignature())), field.getSignature());
181            ret.add(fieldRef);
182        }
183    }
184
185    addParameters(srcStmt: Stmt, dataFact: Value, method: ArkMethod, ret: Set<Value>): void {
186        const callStmt = srcStmt as ArkInvokeStmt;
187        const args = callStmt.getInvokeExpr().getArgs();
188        for (let i = 0; i < args.length; i++) {
189            if (args[i] === dataFact || (this.isUndefined(args[i]) && this.getZeroValue() === dataFact)) {
190                const realParameter = [...method.getCfg()!.getBlocks()][0].getStmts()[i].getDef();
191                if (realParameter) {
192                    ret.add(realParameter);
193                }
194            } else if (dataFact instanceof ArkInstanceFieldRef && dataFact.getBase().getName() === args[i].toString()) {
195                const realParameter = [...method.getCfg()!.getBlocks()][0].getStmts()[i].getDef();
196                if (realParameter) {
197                    const retRef = new ArkInstanceFieldRef(realParameter as Local, dataFact.getFieldSignature());
198                    ret.add(retRef);
199                }
200            }
201        }
202    }
203
204    getExitToReturnFlowFunction(srcStmt: Stmt, tgtStmt: Stmt, callStmt: Stmt): FlowFunction<Value> {
205        let checkerInstance: UndefinedVariableChecker = this;
206        return new (class implements FlowFunction<Value> {
207            getDataFacts(dataFact: Value): Set<Value> {
208                let ret: Set<Value> = new Set<Value>();
209                if (dataFact === checkerInstance.getZeroValue()) {
210                    ret.add(checkerInstance.getZeroValue());
211                }
212                return ret;
213            }
214        })();
215    }
216
217    getCallToReturnFlowFunction(srcStmt: Stmt, tgtStmt: Stmt): FlowFunction<Value> {
218        let checkerInstance: UndefinedVariableChecker = this;
219        return new (class implements FlowFunction<Value> {
220            getDataFacts(dataFact: Value): Set<Value> {
221                const ret: Set<Value> = new Set();
222                if (checkerInstance.getZeroValue() === dataFact) {
223                    ret.add(checkerInstance.getZeroValue());
224                }
225                const defValue = srcStmt.getDef();
226                if (!(defValue && defValue === dataFact)) {
227                    ret.add(dataFact);
228                }
229                return ret;
230            }
231        })();
232    }
233
234    createZeroValue(): Value {
235        return this.zeroValue;
236    }
237
238    getZeroValue(): Value {
239        return this.zeroValue;
240    }
241
242    factEqual(d1: Value, d2: Value): boolean {
243        if (d1 instanceof Constant && d2 instanceof Constant) {
244            return d1 === d2;
245        } else if (d1 instanceof Local && d2 instanceof Local) {
246            return LocalEqual(d1, d2);
247        } else if (d1 instanceof AbstractRef && d2 instanceof AbstractRef) {
248            return RefEqual(d1, d2);
249        }
250        return false;
251    }
252
253    public getOutcomes(): Outcome[] {
254        return this.outcomes;
255    }
256}
257
258export class UndefinedVariableSolver extends DataflowSolver<Value> {
259    constructor(problem: UndefinedVariableChecker, scene: Scene) {
260        super(problem, scene);
261    }
262}
263
264class Outcome {
265    value: Value;
266    stmt: Stmt;
267    constructor(v: Value, s: Stmt) {
268        this.value = v;
269        this.stmt = s;
270    }
271}
272