• 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 { FieldSignature } from '../model/ArkSignature';
18import { Local } from './Local';
19import { ArrayType, ClassType, LexicalEnvType, Type, UnknownType } from './Type';
20import { Value } from './Value';
21import { TypeInference } from '../common/TypeInference';
22import { ArkMethod } from '../model/ArkMethod';
23import { Stmt } from './Stmt';
24import { IRInference } from '../common/IRInference';
25
26const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'Ref');
27
28/**
29 * @category core/base/ref
30 */
31export abstract class AbstractRef implements Value {
32    abstract getUses(): Value[];
33
34    abstract getType(): Type;
35
36    public inferType(arkMethod: ArkMethod): AbstractRef {
37        return this;
38    }
39}
40
41export class ArkArrayRef extends AbstractRef {
42    private base: Local; // 数组变量
43    private index: Value; // 索引
44
45    constructor(base: Local, index: Value) {
46        super();
47        this.base = base;
48        this.index = index;
49    }
50
51    /**
52     * Returns the base of this array reference. Array reference refers to access to array elements.
53     * Array references usually consist of an local variable and an index.
54     * For example, `a[i]` is a typical array reference, where `a` is the base (i.e., local variable)
55     * pointing to the actual memory location where the array is stored
56     * and `i` is the index indicating access to the `i-th` element from array `a`.
57     * @returns the base of this array reference.
58     * @example
59     * 1. Get the base and the specific elements.
60
61     ```typescript
62     // Create an array
63     let myArray: number[] = [10, 20, 30, 40];
64     // Create an ArrayRef object representing a reference to myArray[2]
65     let arrayRef = new ArkArrayRef(myArray, 2);
66     // Use the getBase() method to get the base of the array
67     let baseArray = arrayRef.getBase();
68
69     console.log("Base array:", baseArray);  // Output: Base array: [10, 20, 30, 40]
70
71     // Use baseArray and obeject index of ArrayRef to access to specific array elements
72     let element = baseArray[arrayRef.index];
73     console.log("Element at index", arrayRef.index, ":", element);  // Output: Element at index 2 : 30
74     ```
75     */
76    public getBase(): Local {
77        return this.base;
78    }
79
80    public setBase(newBase: Local): void {
81        this.base = newBase;
82    }
83
84    /**
85     * Returns the index of this array reference.
86     * In TypeScript, an array reference means that the variable stores
87     * the memory address of the array rather than the actual data of the array.
88     * @returns The index of this array reference.
89     */
90    public getIndex(): Value {
91        return this.index;
92    }
93
94    public setIndex(newIndex: Value): void {
95        this.index = newIndex;
96    }
97
98    public getType(): Type {
99        let baseType = TypeInference.replaceTypeWithReal(this.base.getType());
100        if (baseType instanceof ArrayType) {
101            return baseType.getBaseType();
102        } else {
103            logger.warn(`the type of base in ArrayRef is not ArrayType`);
104            return UnknownType.getInstance();
105        }
106    }
107
108    public getUses(): Value[] {
109        let uses: Value[] = [];
110        uses.push(this.base);
111        uses.push(...this.base.getUses());
112        uses.push(this.index);
113        uses.push(...this.index.getUses());
114        return uses;
115    }
116
117    public toString(): string {
118        return this.base + '[' + this.index + ']';
119    }
120}
121
122export abstract class AbstractFieldRef extends AbstractRef {
123    private fieldSignature: FieldSignature;
124
125    constructor(fieldSignature: FieldSignature) {
126        super();
127        this.fieldSignature = fieldSignature;
128    }
129
130    /**
131     * Returns the the field name as a **string**.
132     * @returns The the field name.
133     */
134    public getFieldName(): string {
135        return this.fieldSignature.getFieldName();
136    }
137
138    /**
139     * Returns a field signature, which consists of a class signature,
140     * a **string** field name, and a **boolean** label indicating whether it is static or not.
141     * @returns The field signature.
142     * @example
143     * 1. Compare two Fields
144
145     ```typescript
146     const fieldSignature = new FieldSignature();
147     fieldSignature.setFieldName(...);
148     const fieldRef = new ArkInstanceFieldRef(baseValue as Local, fieldSignature);
149     ...
150     if (fieldRef.getFieldSignature().getFieldName() ===
151     targetField.getFieldSignature().getFieldName()) {
152     ...
153     }
154     ```
155     */
156    public getFieldSignature(): FieldSignature {
157        return this.fieldSignature;
158    }
159
160    public setFieldSignature(newFieldSignature: FieldSignature): void {
161        this.fieldSignature = newFieldSignature;
162    }
163
164    public getType(): Type {
165        return this.fieldSignature.getType();
166    }
167}
168
169export class ArkInstanceFieldRef extends AbstractFieldRef {
170    private base: Local; // which obj this field belong to
171
172    constructor(base: Local, fieldSignature: FieldSignature) {
173        super(fieldSignature);
174        this.base = base;
175    }
176
177    /**
178     * Returns the local of field, showing which object this field belongs to.
179     * A {@link Local} consists of :
180     * - Name: the **string** name of local value, e.g., "$temp0".
181     * - Type: the type of value.
182     * @returns The object that the field belongs to.
183     * @example
184     * 1. Get a base.
185
186     ```typescript
187     if (expr instanceof ArkInstanceFieldRef) {
188     ...
189     let base = expr.getBase();
190     if (base.getName() == 'this') {
191     ...
192     }
193     ...
194     }
195     ```
196     */
197    public getBase(): Local {
198        return this.base;
199    }
200
201    public setBase(newBase: Local): void {
202        this.base = newBase;
203    }
204
205    public getUses(): Value[] {
206        let uses: Value[] = [];
207        uses.push(this.base);
208        uses.push(...this.base.getUses());
209        return uses;
210    }
211
212    public toString(): string {
213        return this.base.toString() + '.<' + this.getFieldSignature() + '>';
214    }
215
216    public inferType(arkMethod: ArkMethod): AbstractRef {
217        return IRInference.inferFieldRef(this, arkMethod);
218    }
219}
220
221export class ArkStaticFieldRef extends AbstractFieldRef {
222    constructor(fieldSignature: FieldSignature) {
223        super(fieldSignature);
224    }
225
226    public getUses(): Value[] {
227        return [];
228    }
229
230    public toString(): string {
231        return this.getFieldSignature().toString();
232    }
233}
234
235export class ArkParameterRef extends AbstractRef {
236    private index: number;
237    private paramType: Type;
238
239    constructor(index: number, paramType: Type) {
240        super();
241        this.index = index;
242        this.paramType = paramType;
243    }
244
245    public getIndex(): number {
246        return this.index;
247    }
248
249    public setIndex(index: number): void {
250        this.index = index;
251    }
252
253    public getType(): Type {
254        return this.paramType;
255    }
256
257    public setType(newType: Type): void {
258        this.paramType = newType;
259    }
260
261    public inferType(arkMethod: ArkMethod): AbstractRef {
262        return IRInference.inferParameterRef(this, arkMethod);
263    }
264
265    public getUses(): Value[] {
266        return [];
267    }
268
269    public toString(): string {
270        return 'parameter' + this.index + ': ' + this.paramType;
271    }
272}
273
274export class ArkThisRef extends AbstractRef {
275    private type: ClassType;
276
277    constructor(type: ClassType) {
278        super();
279        this.type = type;
280    }
281
282    public getType(): ClassType {
283        return this.type;
284    }
285
286    public getUses(): Value[] {
287        return [];
288    }
289
290    public toString(): string {
291        return 'this: ' + this.type;
292    }
293}
294
295export class ArkCaughtExceptionRef extends AbstractRef {
296    private type: Type;
297
298    constructor(type: Type) {
299        super();
300        this.type = type;
301    }
302
303    public getType(): Type {
304        return this.type;
305    }
306
307    public getUses(): Value[] {
308        return [];
309    }
310
311    public toString(): string {
312        return 'caughtexception: ' + this.type;
313    }
314}
315
316export class GlobalRef extends AbstractRef {
317    private name: string;
318    private ref: Value | null;
319    private usedStmts: Stmt[];
320
321    constructor(name: string, ref?: Value) {
322        super();
323        this.name = name;
324        this.ref = ref ?? null;
325        this.usedStmts = [];
326    }
327
328    public getName(): string {
329        return this.name;
330    }
331
332    public getUses(): Value[] {
333        return this.ref?.getUses() || [];
334    }
335
336    public getType(): Type {
337        return this.ref?.getType() || UnknownType.getInstance();
338    }
339
340    public getRef(): Value | null {
341        return this.ref || null;
342    }
343
344    public setRef(value: Value): void {
345        this.ref = value;
346    }
347
348    public getUsedStmts(): Stmt[] {
349        return this.usedStmts;
350    }
351
352    public addUsedStmts(usedStmts: Stmt | Stmt[]): void {
353        if (usedStmts instanceof Stmt) {
354            this.usedStmts.push(usedStmts);
355        } else {
356            usedStmts.forEach(stmt => this.usedStmts.push(stmt));
357        }
358    }
359
360    public toString(): string {
361        return this.getName();
362    }
363}
364
365export class ClosureFieldRef extends AbstractRef {
366    private base: Local;
367    private fieldName: string;
368    private type: Type;
369
370    constructor(base: Local, fieldName: string, type: Type) {
371        super();
372        this.base = base;
373        this.fieldName = fieldName;
374        this.type = type;
375    }
376
377    public getUses(): Value[] {
378        return [];
379    }
380
381    public getBase(): Local {
382        return this.base;
383    }
384
385    public getType(): Type {
386        return this.type;
387    }
388
389    public getFieldName(): string {
390        return this.fieldName;
391    }
392
393    public toString(): string {
394        return this.base.toString() + '.' + this.getFieldName();
395    }
396
397    public inferType(arkMethod: ArkMethod): AbstractRef {
398        if (TypeInference.isUnclearType(this.type)) {
399            let type: Type | undefined = this.base.getType();
400            if (type instanceof LexicalEnvType) {
401                type = type
402                    .getClosures()
403                    .find(c => c.getName() === this.fieldName)
404                    ?.getType();
405            }
406            if (type && !TypeInference.isUnclearType(type)) {
407                this.type = type;
408            }
409        }
410        return this;
411    }
412}
413