• 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 Logger, { LOG_MODULE_TYPE } from '../../utils/logger';
17import { Local } from '../base/Local';
18import { ArkInstanceFieldRef, ArkStaticFieldRef } from '../base/Ref';
19import { ArkAssignStmt } from '../base/Stmt';
20import { ClassType } from '../base/Type';
21import { Value } from '../base/Value';
22import { BasicBlock } from '../graph/BasicBlock';
23import { ArkClass } from '../model/ArkClass';
24import { ArkFile } from '../model/ArkFile';
25import { ArkMethod } from '../model/ArkMethod';
26import { ArkNamespace } from '../model/ArkNamespace';
27
28const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'VisibleValue');
29
30export class VisibleValue {
31    private scopeChain: Scope[]; // 不包含currScope
32    private currScope: Scope;
33    private currVisibleValues: Value[];
34
35    constructor() {
36        // TODO:填充全局变量
37        this.currScope = new Scope([], 0);
38        this.scopeChain = [this.currScope];
39        this.currVisibleValues = [...this.currScope.values];
40    }
41
42    /** get values that is visible in curr scope */
43    public getCurrVisibleValues(): Value[] {
44        return this.currVisibleValues;
45    }
46
47    public getScopeChain(): Scope[] {
48        return this.scopeChain;
49    }
50
51    /** udpate visible values after entered a scope, only support step by step */
52    public updateIntoScope(model: ArkModel): void {
53        let name = '';
54        if (model instanceof BasicBlock) {
55            name = 'block: ' + model.toString();
56        } else {
57            name = model.getName();
58        }
59        logger.info('---- into scope:{', name, '}');
60
61        // get values in this scope
62        let values: Value[] = [];
63        if (model instanceof ArkFile || model instanceof ArkNamespace) {
64            values = this.getVisibleValuesIntoFileOrNameSpace(model);
65        } else if (model instanceof ArkClass) {
66            values = this.getVisibleValuesIntoClass(model);
67        } else if (model instanceof ArkMethod) {
68            values = this.getVisibleValuesIntoMethod(model);
69        } else if (model instanceof BasicBlock) {
70            values = this.getVisibleValuesIntoBasicBlock(model);
71        }
72
73        // handle scope chain
74        const targetDepth = this.getTargetDepth(model);
75        this.addScope(values, targetDepth, model);
76    }
77
78    /** udpate visible values after left a scope, only support step by step */
79    public updateOutScope(): void {
80        const currModel = this.currScope.arkModel as ArkModel;
81
82        let name = '';
83        if (currModel instanceof BasicBlock) {
84            name = 'block: ' + currModel.toString();
85        } else {
86            name = currModel.getName();
87        }
88        logger.info('---- out scope:{', name, '}');
89
90        let targetDepth = this.currScope.depth;
91        if (currModel instanceof BasicBlock) {
92            const successorsCnt = currModel.getSuccessors().length;
93            // if successorsCnt <= 0, unchange
94            if (successorsCnt > 1) {
95                targetDepth += 1; // goto inner scope
96            }
97        }
98        this.deleteScope(targetDepth);
99    }
100
101    /** clear up previous scope */
102    private deleteScope(targetDepth: number): void {
103        const prevDepth = this.currScope.depth;
104        if (targetDepth > prevDepth) {
105            return;
106        }
107
108        let popScopeValuesCnt = 0;
109        let popScopeCnt = 0;
110        for (let i = this.scopeChain.length - 1; i >= 0; i--) {
111            if (this.scopeChain[i].depth < targetDepth) {
112                break;
113            }
114            popScopeCnt += 1;
115            popScopeValuesCnt += this.scopeChain[i].values.length;
116        }
117
118        this.scopeChain.splice(this.scopeChain.length - popScopeCnt, popScopeCnt)[0]; // popScopeCnt >= 1
119        this.currScope = this.scopeChain[this.scopeChain.length - 1];
120        const totalValuesCnt = this.currVisibleValues.length;
121        this.currVisibleValues.splice(totalValuesCnt - popScopeValuesCnt, popScopeValuesCnt);
122    }
123
124    /** add this scope to scope chain and update visible values */
125    private addScope(values: Value[], targetDepth: number, model: ArkModel): void {
126        const newScope = new Scope(values, targetDepth, model);
127        this.currScope = newScope;
128        this.scopeChain.push(this.currScope);
129        this.currVisibleValues.push(...this.currScope.values);
130    }
131
132    // TODO:构造嵌套关系树
133    private getTargetDepth(model: ArkModel): number {
134        const prevDepth = this.currScope.depth;
135        const prevModel = this.currScope.arkModel;
136        let targetDepth = prevDepth + 1;
137        if (model instanceof BasicBlock) {
138            const predecessorsCnt = model.getPredecessors().length;
139            if (predecessorsCnt <= 1) {
140                targetDepth = prevDepth + 1;
141            } else {
142                targetDepth = prevDepth;
143            }
144        } else if (model instanceof ArkFile && prevModel instanceof ArkFile) {
145            targetDepth = prevDepth;
146        } else if (model instanceof ArkNamespace && prevModel instanceof ArkNamespace) {
147            targetDepth = prevDepth;
148        } else if (model instanceof ArkClass && prevModel instanceof ArkClass) {
149            targetDepth = prevDepth;
150        } else if (model instanceof ArkMethod && prevModel instanceof ArkMethod) {
151            targetDepth = prevDepth;
152        }
153        return targetDepth;
154    }
155
156    private getVisibleValuesIntoFileOrNameSpace(fileOrNameSpace: ArkFile | ArkNamespace): Value[] {
157        let values: Value[] = [];
158        return values;
159    }
160
161    private getVisibleValuesIntoClass(cls: ArkClass): Value[] {
162        const values: Value[] = [];
163        const fields = cls.getFields();
164        const classSignature = cls.getSignature();
165        for (const field of fields) {
166            if (field.isStatic()) {
167                const staticFieldRef = new ArkStaticFieldRef(field.getSignature());
168                values.push(staticFieldRef);
169            } else {
170                const instanceFieldRef = new ArkInstanceFieldRef(new Local('this', new ClassType(classSignature)), field.getSignature());
171                values.push(instanceFieldRef);
172            }
173        }
174        return values;
175    }
176
177    private getVisibleValuesIntoMethod(method: ArkMethod): Value[] {
178        let visibleValues: Value[] = [];
179        return visibleValues;
180    }
181
182    private getVisibleValuesIntoBasicBlock(basiceBlock: BasicBlock): Value[] {
183        const visibleValues: Value[] = [];
184        for (const stmt of basiceBlock.getStmts()) {
185            if (stmt instanceof ArkAssignStmt) {
186                visibleValues.push(stmt.getLeftOp());
187            }
188        }
189        return visibleValues;
190    }
191}
192
193type ArkModel = ArkFile | ArkNamespace | ArkClass | ArkMethod | BasicBlock;
194
195export class Scope {
196    public values: Value[];
197    public depth: number;
198    public arkModel: ArkModel | null;
199    constructor(values: Value[], depth: number = -1, arkModel: ArkModel | null = null) {
200        this.values = values;
201        this.depth = depth;
202        this.arkModel = arkModel;
203    }
204}
205