• 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 { StmtUseReplacer } from '../common/StmtUseReplacer';
17import { Cfg } from '../graph/Cfg';
18import { AbstractExpr, AbstractInvokeExpr, AliasTypeExpr, ArkConditionExpr } from './Expr';
19import { AbstractFieldRef, ArkArrayRef } from './Ref';
20import { Value } from './Value';
21import { FullPosition, LineColPosition } from './Position';
22import { ArkMetadata, ArkMetadataKind, ArkMetadataType } from '../model/ArkMetadata';
23import { StmtDefReplacer } from '../common/StmtDefReplacer';
24import { IRUtils } from '../common/IRUtils';
25import { AliasType, ArrayType, IntersectionType, TupleType, Type, UnionType } from './Type';
26import { ModifierType } from '../model/ArkBaseModel';
27import { AbstractTypeExpr } from './TypeExpr';
28
29/**
30 * @category core/base/stmt
31 */
32export abstract class Stmt {
33    protected text?: string; // just for debug
34    protected originalText?: string;
35    protected originalPosition: LineColPosition = LineColPosition.DEFAULT;
36    protected cfg!: Cfg;
37    protected operandOriginalPositions?: FullPosition[]; // operandOriginalPositions correspond with
38    // def and uses one by one
39    metadata?: ArkMetadata;
40
41    public getMetadata(kind: ArkMetadataKind): ArkMetadataType | undefined {
42        return this.metadata?.getMetadata(kind);
43    }
44
45    public setMetadata(kind: ArkMetadataKind, value: ArkMetadataType): void {
46        if (!this.metadata) {
47            this.metadata = new ArkMetadata();
48        }
49        return this.metadata?.setMetadata(kind, value);
50    }
51
52    /** Return a list of values which are uesd in this statement */
53    public getUses(): Value[] {
54        return [];
55    }
56
57    public replaceUse(oldUse: Value, newUse: Value): void {
58        const stmtUseReplacer = new StmtUseReplacer(oldUse, newUse);
59        stmtUseReplacer.caseStmt(this);
60    }
61
62    /**
63     * Return the definition which is uesd in this statement. Generally, the definition is the left value of `=` in
64     * 3AC.  For example, the definition in 3AC of `value = parameter0: @project-1/sample-1.ets: AnonymousClass-0` is
65     * `value`,  and the definition in `$temp0 = staticinvoke <@_ProjectName/_FileName: xxx.create()>()` is `\$temp0`.
66     * @returns The definition in 3AC (may be a **null**).
67     * @example
68     * 1. get the def in stmt.
69    ```typescript
70    for (const block of this.blocks) {
71    for (const stmt of block.getStmts()) {
72        const defValue = stmt.getDef();
73        ...
74        }
75    }
76    ```
77     */
78    public getDef(): Value | null {
79        return null;
80    }
81
82    public replaceDef(oldDef: Value, newDef: Value): void {
83        const stmtDefReplacer = new StmtDefReplacer(oldDef, newDef);
84        stmtDefReplacer.caseStmt(this);
85    }
86
87    public getDefAndUses(): Value[] {
88        const defAndUses: Value[] = [];
89        const def = this.getDef();
90        if (def) {
91            defAndUses.push(def);
92        }
93        defAndUses.push(...this.getUses());
94        return defAndUses;
95    }
96
97    /**
98     * Get the CFG (i.e., control flow graph) of an {@link ArkBody} in which the statement is.
99     * A CFG contains a set of basic blocks and statements corresponding to each basic block.
100     * Note that, "source code" and "three-address" are two types of {@link Stmt} in ArkAnalyzer.
101     * Source code {@link Stmt} represents the statement of ets/ts source code, while three-address code {@link Stmt}
102     * represents the statement after it has been converted into three-address code.  Since the source code {@link
103     * Stmt} does not save its CFG reference, it returns **null**, while the `getCfg()` of the third address code
104     * {@link Stmt} will return its CFG reference.
105     * @returns The CFG (i.e., control flow graph) of an {@link ArkBody} in which the statement is.
106     * @example
107     * 1. get the ArkFile based on stmt.
108    ```typescript
109    const arkFile = stmt.getCfg()?.getDeclaringMethod().getDeclaringArkFile();
110    ```
111    2. get the ArkMethod based on stmt.
112    ```typescript
113    let sourceMethod: ArkMethod = stmt.getCfg()?.getDeclaringMethod();
114    ```
115     */
116    public getCfg(): Cfg {
117        return this.cfg;
118    }
119
120    public setCfg(cfg: Cfg): void {
121        this.cfg = cfg;
122    }
123
124    /**
125     * Return true if the following statement may not execute after this statement.
126     * The ArkIfStmt and ArkGotoStmt will return true.
127     */
128    public isBranch(): boolean {
129        return false;
130    }
131
132    /** Return the number of statements which this statement may go to */
133    public getExpectedSuccessorCount(): number {
134        return 1;
135    }
136
137    public containsInvokeExpr(): boolean {
138        for (const use of this.getUses()) {
139            if (use instanceof AbstractInvokeExpr) {
140                return true;
141            }
142        }
143        return false;
144    }
145
146    /**
147     * Returns the method's invocation expression (including method signature and its arguments)
148     * in the current statement. An **undefined** will be returned if there is no method used in this statement.
149     * @returns  the method's invocation expression from the statement. An **undefined** will be returned if there is
150     *     no method can be found in this statement.
151     * @example
152     * 1. get invoke expr based on stmt.
153    ```typescript
154    let invoke = stmt.getInvokeExpr();
155    ```
156     */
157    public getInvokeExpr(): AbstractInvokeExpr | undefined {
158        for (const use of this.getUses()) {
159            if (use instanceof AbstractInvokeExpr) {
160                return use as AbstractInvokeExpr;
161            }
162        }
163        return undefined;
164    }
165
166    /**
167     * Returns an array of expressions in the statement.
168     * @returns An array of expressions in the statement.
169     * @example
170     * 1. Traverse expression of statement.
171
172    ```typescript
173    for (const expr of stmt.getExprs()) {
174        ...
175    }
176    ```
177     */
178    public getExprs(): AbstractExpr[] {
179        let exprs: AbstractExpr[] = [];
180        for (const use of this.getUses()) {
181            if (use instanceof AbstractExpr) {
182                exprs.push(use);
183            }
184        }
185        return exprs;
186    }
187
188    public getTypeExprs(): AbstractTypeExpr[] {
189        let typeExprs: AbstractTypeExpr[] = [];
190        for (const value of this.getDefAndUses()) {
191            const valueType = value.getType();
192            if (valueType instanceof AbstractTypeExpr) {
193                typeExprs.push(valueType);
194            }
195        }
196        return typeExprs;
197    }
198
199    public containsArrayRef(): boolean {
200        for (const use of this.getUses()) {
201            if (use instanceof ArkArrayRef) {
202                return true;
203            }
204        }
205        if (this.getDef() instanceof ArkArrayRef) {
206            return true;
207        }
208        return false;
209    }
210
211    public getArrayRef(): ArkArrayRef | undefined {
212        for (const use of this.getUses()) {
213            if (use instanceof ArkArrayRef) {
214                return use as ArkArrayRef;
215            }
216        }
217
218        if (this.getDef() instanceof ArkArrayRef) {
219            return undefined;
220        }
221
222        return undefined;
223    }
224
225    public containsFieldRef(): boolean {
226        for (const use of this.getUses()) {
227            if (use instanceof AbstractFieldRef) {
228                return true;
229            }
230        }
231
232        if (this.getDef() instanceof AbstractFieldRef) {
233            return true;
234        }
235        return false;
236    }
237
238    public getFieldRef(): AbstractFieldRef | undefined {
239        for (const use of this.getUses()) {
240            if (use instanceof AbstractFieldRef) {
241                return use as AbstractFieldRef;
242            }
243        }
244        if (this.getDef() instanceof AbstractFieldRef) {
245            return undefined;
246        }
247        return undefined;
248    }
249
250    public setOriginPositionInfo(originPositionInfo: LineColPosition): void {
251        this.originalPosition = originPositionInfo;
252    }
253
254    /**
255     * Returns the original position of the statement.
256     * The position consists of two parts: line number and column number.
257     * In the source file, the former (i.e., line number) indicates which line the statement is in,
258     * and the latter (i.e., column number) indicates the position of the statement in the line.
259     * The position is described as `LineColPosition(lineNo,colNum)` in ArkAnalyzer,
260     * and its default value is LineColPosition(-1,-1).
261     * @returns The original location of the statement.
262     * @example
263     * 1. Get the stmt position info to make some condition judgements.
264    ```typescript
265    for (const stmt of stmts) {
266        if (stmt.getOriginPositionInfo().getLineNo() === -1) {
267            stmt.setOriginPositionInfo(originalStmt.getOriginPositionInfo());
268            this.stmtToOriginalStmt.set(stmt, originalStmt);
269        }
270    }
271    ```
272     */
273    public getOriginPositionInfo(): LineColPosition {
274        return this.originalPosition;
275    }
276
277    abstract toString(): string;
278
279    public setText(text: string): void {
280        this.text = text;
281    }
282
283    public setOriginalText(originalText: string): void {
284        this.originalText = originalText;
285    }
286
287    public getOriginalText(): string | undefined {
288        return this.originalText;
289    }
290
291    public setOperandOriginalPositions(operandOriginalPositions: FullPosition[]): void {
292        this.operandOriginalPositions = operandOriginalPositions;
293    }
294
295    public getOperandOriginalPositions(): FullPosition[] | undefined {
296        return this.operandOriginalPositions;
297    }
298
299    public getOperandOriginalPosition(indexOrOperand: number | Value): FullPosition | null {
300        let index: number = -1;
301        if (typeof indexOrOperand !== 'number') {
302            index = IRUtils.findOperandIdx(this, indexOrOperand);
303        } else {
304            index = indexOrOperand;
305        }
306
307        if (!this.operandOriginalPositions || index < 0 || index > this.operandOriginalPositions.length) {
308            return null;
309        }
310        return this.operandOriginalPositions[index];
311    }
312}
313
314export class ArkAssignStmt extends Stmt {
315    private leftOp: Value;
316    private rightOp: Value;
317
318    constructor(leftOp: Value, rightOp: Value) {
319        super();
320        this.leftOp = leftOp;
321        this.rightOp = rightOp;
322    }
323
324    /**
325     * Returns the left operand of the assigning statement.
326     * @returns The left operand of the assigning statement.
327     * @example
328     * 1. If the statement is `a=b;`, the right operand is `a`; if the statement is `dd = cc + 5;`, the right operand
329     *     is `cc`.
330     */
331    public getLeftOp(): Value {
332        return this.leftOp;
333    }
334
335    public setLeftOp(newLeftOp: Value): void {
336        this.leftOp = newLeftOp;
337    }
338
339    /**
340     * Returns the right operand of the assigning statement.
341     * @returns The right operand of the assigning statement.
342     * @example
343     * 1. If the statement is `a=b;`, the right operand is `b`; if the statement is `dd = cc + 5;`, the right operand
344     *     is `cc + 5`.
345     * 2. Get the rightOp from stmt.
346    ```typescript
347    const rightOp = stmt.getRightOp();
348    ```
349     */
350    public getRightOp(): Value {
351        return this.rightOp;
352    }
353
354    public setRightOp(rightOp: Value): void {
355        this.rightOp = rightOp;
356    }
357
358    public toString(): string {
359        const str = this.getLeftOp() + ' = ' + this.getRightOp();
360        return str;
361    }
362
363    public getDef(): Value | null {
364        return this.leftOp;
365    }
366
367    public getUses(): Value[] {
368        let uses: Value[] = [];
369        uses.push(...this.leftOp.getUses());
370        uses.push(this.rightOp);
371        uses.push(...this.rightOp.getUses());
372        return uses;
373    }
374}
375
376export class ArkInvokeStmt extends Stmt {
377    private invokeExpr: AbstractInvokeExpr;
378
379    constructor(invokeExpr: AbstractInvokeExpr) {
380        super();
381        this.invokeExpr = invokeExpr;
382    }
383
384    public replaceInvokeExpr(newExpr: AbstractInvokeExpr): void {
385        this.invokeExpr = newExpr;
386    }
387
388    public getInvokeExpr(): AbstractInvokeExpr {
389        return this.invokeExpr;
390    }
391
392    public toString(): string {
393        const str = this.invokeExpr.toString();
394        return str;
395    }
396
397    public getUses(): Value[] {
398        let uses: Value[] = [];
399        uses.push(this.invokeExpr);
400        uses.push(...this.invokeExpr.getUses());
401        return uses;
402    }
403}
404
405export class ArkIfStmt extends Stmt {
406    private conditionExpr: ArkConditionExpr;
407
408    constructor(conditionExpr: ArkConditionExpr) {
409        super();
410        this.conditionExpr = conditionExpr;
411    }
412
413    /**
414     * The condition expression consisit of two values as operands and one binary operator as operator.
415     * The operator can indicate the relation between the two values, e.g., `<`, `<=`,`>`, `>=`, `==`, `!=`, `===`,
416     * `!==`.
417     * @returns a condition expression.
418     * @example
419     * 1. When a statement is `if (a > b)`, the operands are `a` and `b`, the operator is `<`. Therefore, the condition
420     *     expression is `a > b`.
421     * 2. get a conditon expr from a condition statement.
422    ```typescript
423     let expr = (this.original as ArkIfStmt).getConditionExpr();
424    ```
425     */
426    public getConditionExpr(): ArkConditionExpr {
427        return this.conditionExpr;
428    }
429
430    public setConditionExpr(newConditionExpr: ArkConditionExpr): void {
431        this.conditionExpr = newConditionExpr;
432    }
433
434    public isBranch(): boolean {
435        return true;
436    }
437
438    public getExpectedSuccessorCount(): number {
439        return 2;
440    }
441
442    public toString(): string {
443        const str = 'if ' + this.conditionExpr;
444        return str;
445    }
446
447    public getUses(): Value[] {
448        let uses: Value[] = [];
449        uses.push(this.conditionExpr);
450        uses.push(...this.conditionExpr.getUses());
451        return uses;
452    }
453}
454
455export class ArkReturnStmt extends Stmt {
456    private op: Value;
457
458    constructor(op: Value) {
459        super();
460        this.op = op;
461    }
462
463    public getExpectedSuccessorCount(): number {
464        return 0;
465    }
466
467    public getOp(): Value {
468        return this.op;
469    }
470
471    public setReturnValue(returnValue: Value): void {
472        this.op = returnValue;
473    }
474
475    public toString(): string {
476        const str = 'return ' + this.op;
477        return str;
478    }
479
480    public getUses(): Value[] {
481        let uses: Value[] = [];
482        uses.push(this.op);
483        uses.push(...this.op.getUses());
484        return uses;
485    }
486}
487
488export class ArkReturnVoidStmt extends Stmt {
489    constructor() {
490        super();
491    }
492
493    public getExpectedSuccessorCount(): number {
494        return 0;
495    }
496
497    public toString(): string {
498        const str = 'return';
499        return str;
500    }
501}
502
503export class ArkThrowStmt extends Stmt {
504    private op: Value;
505
506    constructor(op: Value) {
507        super();
508        this.op = op;
509    }
510
511    public getOp(): Value {
512        return this.op;
513    }
514
515    public setOp(newOp: Value): void {
516        this.op = newOp;
517    }
518
519    public toString(): string {
520        const str = 'throw ' + this.op;
521        return str;
522    }
523
524    public getUses(): Value[] {
525        let uses: Value[] = [];
526        uses.push(this.op);
527        uses.push(...this.op.getUses());
528        return uses;
529    }
530}
531
532/**
533 * Statement of type alias definition combines with the left hand as {@link AliasType} and right hand as {@link AliasTypeExpr}.
534 * @category core/base/stmt
535 * @extends Stmt
536 * @example
537 ```typescript
538 type A = string;
539 type B = import('./abc').TypeB;
540
541 let c = 123;
542 declare type C = typeof c;
543 ```
544 */
545export class ArkAliasTypeDefineStmt extends Stmt {
546    private aliasType: AliasType;
547    private aliasTypeExpr: AliasTypeExpr;
548
549    constructor(aliasType: AliasType, typeAliasExpr: AliasTypeExpr) {
550        super();
551        this.aliasType = aliasType;
552        this.aliasTypeExpr = typeAliasExpr;
553    }
554
555    public getAliasType(): AliasType {
556        return this.aliasType;
557    }
558
559    public getAliasTypeExpr(): AliasTypeExpr {
560        return this.aliasTypeExpr;
561    }
562
563    public getAliasName(): string {
564        return this.getAliasType().getName();
565    }
566
567    public toString(): string {
568        let str = `type ${this.getAliasType().toString()} = ${this.getAliasTypeExpr().toString()}`;
569        if (this.getAliasType().containsModifier(ModifierType.DECLARE)) {
570            str = 'declare ' + str;
571        }
572        if (this.getAliasType().containsModifier(ModifierType.EXPORT)) {
573            str = 'export ' + str;
574        }
575        return str;
576    }
577
578    public getExprs(): AliasTypeExpr[] {
579        return [this.getAliasTypeExpr()];
580    }
581
582    public getTypeExprs(): AbstractTypeExpr[] {
583        function getTypeExprsInType(originalObject: Type): AbstractTypeExpr[] {
584            let typeExprs: AbstractTypeExpr[] = [];
585            if (originalObject instanceof AbstractTypeExpr) {
586                typeExprs.push(originalObject);
587            } else if (originalObject instanceof ArrayType) {
588                typeExprs.push(...getTypeExprsInType(originalObject.getBaseType()));
589            } else if (originalObject instanceof UnionType || originalObject instanceof IntersectionType || originalObject instanceof TupleType) {
590                for (const member of originalObject.getTypes()) {
591                    typeExprs.push(...getTypeExprsInType(member));
592                }
593            }
594            return typeExprs;
595        }
596
597        const originalObject = this.getAliasTypeExpr().getOriginalObject();
598        if (originalObject instanceof Type) {
599            return getTypeExprsInType(originalObject);
600        }
601        return [];
602    }
603}
604