• 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 { AbstractExpr, ArkInstanceInvokeExpr, ArkPtrInvokeExpr, ArkStaticInvokeExpr } from '../base/Expr';
18import { Local } from '../base/Local';
19import {
20    AbstractFieldRef,
21    AbstractRef,
22    ArkArrayRef,
23    ArkInstanceFieldRef,
24    ArkParameterRef,
25    ArkStaticFieldRef, GlobalRef
26} from '../base/Ref';
27import { ArkAliasTypeDefineStmt, ArkAssignStmt, ArkReturnStmt, Stmt } from '../base/Stmt';
28import {
29    AliasType,
30    AnnotationNamespaceType,
31    AnyType,
32    ArrayType,
33    BigIntType,
34    BooleanType,
35    ClassType,
36    EnumValueType,
37    FunctionType,
38    GenericType,
39    IntersectionType,
40    NeverType,
41    NullType,
42    NumberType,
43    StringType,
44    TupleType,
45    Type,
46    UnclearReferenceType,
47    UndefinedType,
48    UnionType,
49    UnknownType,
50    VoidType,
51} from '../base/Type';
52import { ArkMethod } from '../model/ArkMethod';
53import { ArkExport } from '../model/ArkExport';
54import { ArkClass, ClassCategory } from '../model/ArkClass';
55import { ArkField } from '../model/ArkField';
56import { Value } from '../base/Value';
57import { Constant } from '../base/Constant';
58import { ArkNamespace } from '../model/ArkNamespace';
59import {
60    ALL,
61    ANY_KEYWORD,
62    BIGINT_KEYWORD,
63    BOOLEAN_KEYWORD,
64    CONSTRUCTOR_NAME, DEFAULT,
65    GLOBAL_THIS_NAME,
66    NEVER_KEYWORD,
67    NULL_KEYWORD,
68    NUMBER_KEYWORD,
69    PROMISE,
70    STRING_KEYWORD,
71    SUPER_NAME,
72    THIS_NAME,
73    UNDEFINED_KEYWORD,
74    VOID_KEYWORD,
75} from './TSConst';
76import { ModelUtils } from './ModelUtils';
77import { Builtin } from './Builtin';
78import { MethodSignature, MethodSubSignature, NamespaceSignature } from '../model/ArkSignature';
79import { INSTANCE_INIT_METHOD_NAME, LEXICAL_ENV_NAME_PREFIX, UNKNOWN_FILE_NAME } from './Const';
80import { EMPTY_STRING } from './ValueUtil';
81import { ImportInfo } from '../model/ArkImport';
82import { MethodParameter } from '../model/builder/ArkMethodBuilder';
83import { IRInference } from './IRInference';
84import { AbstractTypeExpr, KeyofTypeExpr, TypeQueryExpr } from '../base/TypeExpr';
85import { SdkUtils } from './SdkUtils';
86import { ModifierType } from '../model/ArkBaseModel';
87
88const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'TypeInference');
89
90export class TypeInference {
91    public static inferTypeInArkField(arkField: ArkField): void {
92        const arkClass = arkField.getDeclaringArkClass();
93        const stmts = arkField.getInitializer();
94        const method = arkClass.getMethodWithName(INSTANCE_INIT_METHOD_NAME) ?? arkClass.getMethodWithName(CONSTRUCTOR_NAME);
95        for (const stmt of stmts) {
96            if (method) {
97                this.resolveStmt(stmt, method);
98            }
99        }
100        const beforeType = arkField.getType();
101        if (!this.isUnclearType(beforeType)) {
102            return;
103        }
104        let rightType: Type | undefined;
105        let fieldRef: ArkInstanceFieldRef | undefined;
106        const lastStmt = stmts[stmts.length - 1];
107        if (lastStmt instanceof ArkAssignStmt) {
108            rightType = lastStmt.getRightOp().getType();
109            if (lastStmt.getLeftOp() instanceof ArkInstanceFieldRef) {
110                fieldRef = lastStmt.getLeftOp() as ArkInstanceFieldRef;
111            }
112        }
113        let fieldType;
114        if (beforeType) {
115            fieldType = this.inferUnclearedType(beforeType, arkClass);
116        }
117        if (fieldType) {
118            arkField.getSignature().setType(fieldType);
119            fieldRef?.setFieldSignature(arkField.getSignature());
120        } else if (rightType && this.isUnclearType(beforeType) && !this.isUnclearType(rightType)) {
121            arkField.getSignature().setType(rightType);
122            fieldRef?.setFieldSignature(arkField.getSignature());
123        }
124    }
125
126    /**
127     * Infer type for a given unclear type.
128     * It returns an array with 2 items, original object and original type.
129     * The original object is null if there is no object, or it failed to find the object.
130     * The original type is null if failed to infer the type.
131     * @param leftOpType
132     * @param declaringArkClass
133     * @param visited
134     * @returns
135     */
136    public static inferUnclearedType(leftOpType: Type, declaringArkClass: ArkClass, visited: Set<Type> = new Set()): Type | null | undefined {
137        if (visited.has(leftOpType)) {
138            return leftOpType;
139        } else {
140            visited.add(leftOpType);
141        }
142        let type;
143        if (leftOpType instanceof ClassType && leftOpType.getClassSignature().getDeclaringFileSignature().getFileName() === UNKNOWN_FILE_NAME) {
144            type = TypeInference.inferUnclearRefName(leftOpType.getClassSignature().getClassName(), declaringArkClass);
145        } else if (leftOpType instanceof UnionType || leftOpType instanceof IntersectionType || leftOpType instanceof TupleType) {
146            let types = leftOpType.getTypes();
147            for (let i = 0; i < types.length; i++) {
148                let newType = this.inferUnclearedType(types[i], declaringArkClass, visited);
149                if (newType) {
150                    types[i] = newType;
151                }
152            }
153            type = leftOpType;
154        } else if (leftOpType instanceof ArrayType) {
155            let baseType = this.inferUnclearedType(leftOpType.getBaseType(), declaringArkClass, visited);
156            if (baseType) {
157                leftOpType.setBaseType(baseType);
158                type = leftOpType;
159            }
160        } else if (leftOpType instanceof AliasType) {
161            let baseType = this.inferUnclearedType(leftOpType.getOriginalType(), declaringArkClass, visited);
162            if (baseType) {
163                leftOpType.setOriginalType(baseType);
164                type = leftOpType;
165            }
166        } else if (leftOpType instanceof AnnotationNamespaceType) {
167            type = this.inferBaseType(leftOpType.getOriginType(), declaringArkClass);
168        } else if (leftOpType instanceof UnclearReferenceType) {
169            type = this.inferUnclearRefType(leftOpType, declaringArkClass);
170        }
171        return type;
172    }
173
174    public static inferTypeInMethod(arkMethod: ArkMethod): void {
175        const arkClass = arkMethod.getDeclaringArkClass();
176        this.inferGenericType(arkMethod.getGenericTypes(), arkClass);
177        const signatures: MethodSignature[] = [];
178        arkMethod.getDeclareSignatures()?.forEach(m => signatures.push(m));
179        const impl = arkMethod.getImplementationSignature();
180        if (impl) {
181            signatures.push(impl);
182        }
183        signatures.forEach(s => {
184            s.getMethodSubSignature()
185                .getParameters()
186                .forEach(p => {
187                    this.inferParameterType(p, arkMethod);
188                });
189        });
190        const body = arkMethod.getBody();
191        if (!body) {
192            signatures.forEach(s => this.inferSignatureReturnType(s, arkMethod));
193            return;
194        }
195        body.getUsedGlobals()?.forEach((value, key) => {
196            if (value instanceof GlobalRef && !value.getRef()) {
197                const arkExport = ModelUtils.findGlobalRef(key, arkMethod);
198                if (arkExport instanceof Local) {
199                    arkExport.getUsedStmts().push(...value.getUsedStmts());
200                    value.setRef(arkExport);
201                }
202            }
203        });
204        const cfg = body.getCfg();
205        for (const block of cfg.getBlocks()) {
206            for (const stmt of block.getStmts()) {
207                this.resolveStmt(stmt, arkMethod);
208            }
209        }
210        signatures.forEach(s => this.inferSignatureReturnType(s, arkMethod));
211    }
212
213    private static resolveStmt(stmt: Stmt, arkMethod: ArkMethod): void {
214        try {
215            this.resolveTypeExprsInStmt(stmt, arkMethod);
216            this.resolveExprsInStmt(stmt, arkMethod);
217            this.resolveFieldRefsInStmt(stmt, arkMethod);
218            this.resolveArkAssignStmt(stmt, arkMethod);
219            this.resolveArkReturnStmt(stmt, arkMethod);
220        } catch (e) {
221            logger.warn('stmt is not correct: ' + stmt.toString());
222        }
223    }
224
225    /**
226     * @Deprecated
227     * @param arkMethod
228     */
229    public static inferSimpleTypeInMethod(arkMethod: ArkMethod): void {
230        const body = arkMethod.getBody();
231        if (!body) {
232            logger.warn('empty body');
233            return;
234        }
235        const cfg = body.getCfg();
236        for (const block of cfg.getBlocks()) {
237            for (const stmt of block.getStmts()) {
238                TypeInference.inferSimpleTypeInStmt(stmt);
239            }
240        }
241    }
242
243    /**
244     * infer type for Exprs in stmt which invoke method.
245     * such as ArkInstanceInvokeExpr ArkStaticInvokeExpr ArkNewExpr
246     */
247    private static resolveExprsInStmt(stmt: Stmt, arkMethod: ArkMethod): void {
248        for (const expr of stmt.getExprs()) {
249            const newExpr = expr.inferType(arkMethod);
250            if (
251                stmt.containsInvokeExpr() &&
252                ((expr instanceof ArkInstanceInvokeExpr && newExpr instanceof ArkStaticInvokeExpr) || newExpr instanceof ArkPtrInvokeExpr)
253            ) {
254                stmt.replaceUse(expr, newExpr);
255            }
256        }
257        if (stmt instanceof ArkAliasTypeDefineStmt && this.isUnclearType(stmt.getAliasType().getOriginalType())) {
258            stmt.getAliasType().setOriginalType(stmt.getAliasTypeExpr().getType());
259        }
260    }
261
262    /**
263     * infer value type for TypeExprs in stmt which specify the type such as TypeQueryExpr
264     */
265    private static resolveTypeExprsInStmt(stmt: Stmt, arkMethod: ArkMethod): void {
266        for (let typeExpr of stmt.getTypeExprs()) {
267            typeExpr.inferType(arkMethod);
268        }
269    }
270
271    /**
272     * infer type for fieldRefs in stmt.
273     */
274    private static resolveFieldRefsInStmt(stmt: Stmt, arkMethod: ArkMethod): void {
275        for (const use of stmt.getUses()) {
276            if (use instanceof AbstractRef) {
277                this.processRef(use, stmt, arkMethod);
278            }
279        }
280        const stmtDef = stmt.getDef();
281        if (stmtDef && stmtDef instanceof AbstractRef) {
282            if (
283                arkMethod.getName() === INSTANCE_INIT_METHOD_NAME &&
284                stmtDef instanceof ArkInstanceFieldRef &&
285                stmtDef.getBase().getName() === THIS_NAME &&
286                arkMethod.getDeclaringArkClass().isAnonymousClass() &&
287                stmtDef.getFieldName().indexOf('.') === -1
288            ) {
289                return;
290            }
291            const fieldRef = stmtDef.inferType(arkMethod);
292            stmt.replaceDef(stmtDef, fieldRef);
293        }
294    }
295
296    private static processRef(use: AbstractRef | ArkInstanceFieldRef, stmt: Stmt, arkMethod: ArkMethod): void {
297        const fieldRef = use.inferType(arkMethod);
298        if (fieldRef instanceof ArkStaticFieldRef && stmt instanceof ArkAssignStmt) {
299            stmt.replaceUse(use, fieldRef);
300        } else if (use instanceof ArkInstanceFieldRef && fieldRef instanceof ArkArrayRef && stmt instanceof ArkAssignStmt) {
301            const index = fieldRef.getIndex();
302            if (index instanceof Constant && index.getType() instanceof StringType) {
303                const local = arkMethod?.getBody()?.getLocals().get(index.getValue());
304                if (local) {
305                    fieldRef.setIndex(local);
306                }
307            }
308            stmt.replaceUse(use, fieldRef);
309        }
310    }
311
312    public static parseArkExport2Type(arkExport: ArkExport | undefined | null): Type | null {
313        if (!arkExport) {
314            return null;
315        }
316        if (arkExport instanceof ArkClass) {
317            return new ClassType(arkExport.getSignature(), arkExport.getGenericsTypes());
318        } else if (arkExport instanceof ArkNamespace) {
319            return AnnotationNamespaceType.getInstance(arkExport.getSignature());
320        } else if (arkExport instanceof ArkMethod) {
321            return new FunctionType(arkExport.getSignature());
322        } else if (arkExport instanceof Local) {
323            if (arkExport.getType() instanceof UnknownType || arkExport.getType() instanceof UnclearReferenceType) {
324                return null;
325            }
326            return arkExport.getType();
327        } else if (arkExport instanceof AliasType) {
328            return arkExport;
329        } else {
330            return null;
331        }
332    }
333
334    /**
335     * infer and pass type for ArkAssignStmt right and left
336     * @param stmt
337     * @param arkMethod
338     */
339    public static resolveArkAssignStmt(stmt: Stmt, arkMethod: ArkMethod): void {
340        if (!(stmt instanceof ArkAssignStmt)) {
341            return;
342        }
343        const arkClass = arkMethod.getDeclaringArkClass();
344        const rightOp = stmt.getRightOp();
345        if (rightOp instanceof Local && rightOp.getType() instanceof UnknownType) {
346            IRInference.inferLocal(rightOp, arkMethod);
347        }
348        let rightType: Type | null | undefined = rightOp.getType();
349        if (this.isUnclearType(rightType)) {
350            rightType = this.inferUnclearedType(rightType, arkClass);
351            if (rightType) {
352                this.setValueType(rightOp, rightType);
353            }
354        }
355        TypeInference.resolveLeftOp(stmt, arkClass, rightType, arkMethod);
356    }
357
358    private static resolveLeftOp(stmt: ArkAssignStmt, arkClass: ArkClass, rightType: Type | null | undefined, arkMethod: ArkMethod): void {
359        const leftOp = stmt.getLeftOp();
360        let leftType: Type | null | undefined = leftOp.getType();
361        if (this.isUnclearType(leftType)) {
362            const newLeftType = this.inferUnclearedType(leftType, arkClass);
363            if (!newLeftType && !this.isUnclearType(rightType)) {
364                leftType = rightType;
365            } else if (newLeftType) {
366                leftType = newLeftType;
367            }
368        } else if (leftOp instanceof Local && leftOp.getName() === THIS_NAME) {
369            const thisLocal = IRInference.inferThisLocal(arkMethod);
370            if (thisLocal) {
371                stmt.setLeftOp(thisLocal);
372            } else {
373                leftType = rightType;
374            }
375        }
376        if (leftType && !this.isUnclearType(leftType)) {
377            this.setValueType(leftOp, leftType);
378            if (leftOp instanceof Local && stmt.getOriginalText()?.startsWith(leftOp.getName())) {
379                let localDef = ModelUtils.findDeclaredLocal(leftOp, arkMethod);
380                if (localDef && this.isUnclearType(localDef.getType())) {
381                    localDef.setType(leftType);
382                }
383            }
384            if (rightType) {
385                IRInference.inferRightWithSdkType(leftType, rightType, arkClass);
386            }
387            if (leftOp instanceof AbstractFieldRef) {
388                const declaringSignature = leftOp.getFieldSignature().getDeclaringSignature();
389                if (declaringSignature instanceof NamespaceSignature && declaringSignature.getNamespaceName() === GLOBAL_THIS_NAME) {
390                    SdkUtils.computeGlobalThis(leftOp, arkMethod);
391                }
392            }
393        }
394    }
395
396    private static setValueType(value: Value, type: Type): void {
397        if (value instanceof Local || value instanceof ArkParameterRef) {
398            value.setType(type);
399        } else if (value instanceof AbstractFieldRef) {
400            value.getFieldSignature().setType(type);
401        }
402    }
403
404    public static isUnclearType(type: Type | null | undefined): boolean {
405        // TODO: For UnionType, IntersectionType and TupleType, it should recurse check every item of them.
406        if (!type || type instanceof UnknownType || type instanceof UnclearReferenceType || type instanceof NullType ||
407            type instanceof UndefinedType || type instanceof GenericType) {
408            return true;
409        } else if (
410            type instanceof ClassType &&
411            (type.getClassSignature().getDeclaringFileSignature().getFileName() === UNKNOWN_FILE_NAME ||
412                (type.getClassSignature().getClassName() === PROMISE && !type.getRealGenericTypes()) ||
413                (type.getClassSignature().getDeclaringFileSignature().getFileName() === Builtin.DUMMY_FILE_NAME &&
414                    type.getRealGenericTypes()?.find(t => t instanceof GenericType)))
415        ) {
416            return true;
417        } else if (type instanceof UnionType || type instanceof IntersectionType || type instanceof TupleType) {
418            return !!type.getTypes().find(t => this.checkType(t, e => e instanceof UnclearReferenceType));
419        } else if (type instanceof ArrayType) {
420            const baseType = type.getBaseType();
421            return this.checkType(baseType, t => t instanceof UnclearReferenceType) || baseType instanceof GenericType;
422        } else if (type instanceof AliasType) {
423            return this.isUnclearType(type.getOriginalType());
424        } else if (type instanceof KeyofTypeExpr) {
425            return this.isUnclearType(type.getOpType());
426        } else if (type instanceof TypeQueryExpr) {
427            return this.isUnclearType(type.getType());
428        }
429        return false;
430    }
431
432    // This is the temporal function to check Type recursively and can be removed after typeInfer supports multiple candidate types.
433    public static checkType(type: Type,
434                            check: (t: Type) => boolean,
435                            visited: Set<Type> = new Set()): boolean {
436        if (visited.has(type)) {
437            return false;
438        } else {
439            visited.add(type);
440        }
441        if (check(type)) {
442            return true;
443        } else if (type instanceof ClassType) {
444            return !!type.getRealGenericTypes()?.find(t => this.checkType(t, check, visited));
445        } else if (type instanceof UnionType || type instanceof IntersectionType || type instanceof TupleType) {
446            return !!type.getTypes().find(t => this.checkType(t, check, visited));
447        } else if (type instanceof ArrayType) {
448            return this.checkType(type.getBaseType(), check, visited);
449        } else if (type instanceof AliasType) {
450            return this.checkType(type.getOriginalType(), check, visited);
451        } else if (type instanceof KeyofTypeExpr) {
452            return this.checkType(type.getOpType(), check, visited);
453        } else if (type instanceof TypeQueryExpr) {
454            return this.checkType(type.getType(), check, visited);
455        }
456        return false;
457    }
458
459    public static inferSimpleTypeInStmt(stmt: Stmt): void {
460        if (stmt instanceof ArkAssignStmt) {
461            const leftOp = stmt.getLeftOp();
462            if (leftOp instanceof Local) {
463                const leftOpType = leftOp.getType();
464                if (leftOpType instanceof UnknownType) {
465                    const rightOp = stmt.getRightOp();
466                    leftOp.setType(rightOp.getType());
467                }
468            }
469        }
470    }
471
472    // Deal only with simple situations
473    public static buildTypeFromStr(typeStr: string): Type {
474        switch (typeStr) {
475            case BOOLEAN_KEYWORD:
476                return BooleanType.getInstance();
477            case NUMBER_KEYWORD:
478                return NumberType.getInstance();
479            case STRING_KEYWORD:
480                return StringType.getInstance();
481            case UNDEFINED_KEYWORD:
482                return UndefinedType.getInstance();
483            case NULL_KEYWORD:
484                return NullType.getInstance();
485            case ANY_KEYWORD:
486                return AnyType.getInstance();
487            case VOID_KEYWORD:
488                return VoidType.getInstance();
489            case NEVER_KEYWORD:
490                return NeverType.getInstance();
491            case BIGINT_KEYWORD:
492                return BigIntType.getInstance();
493            case 'RegularExpression': {
494                const classSignature = Builtin.REGEXP_CLASS_SIGNATURE;
495                return new ClassType(classSignature);
496            }
497            default:
498                return new UnclearReferenceType(typeStr);
499        }
500    }
501
502    public static inferValueType(value: Value, arkMethod: ArkMethod): Type | null {
503        if (value instanceof ArkInstanceFieldRef || value instanceof ArkInstanceInvokeExpr) {
504            this.inferValueType(value.getBase(), arkMethod);
505        }
506        if (value instanceof AbstractRef || value instanceof AbstractExpr || value instanceof Local) {
507            value.inferType(arkMethod);
508        }
509        return value.getType();
510    }
511
512    public static inferParameterType(param: MethodParameter, arkMethod: ArkMethod): void {
513        let pType = param.getType();
514        const arkClass = arkMethod.getDeclaringArkClass();
515        let type;
516        if (pType instanceof AbstractTypeExpr) {
517            pType.inferType(arkMethod);
518        } else if (param.getName() === 'value' && arkClass.hasComponentDecorator() && arkMethod.getName() === CONSTRUCTOR_NAME) {
519            type = this.parseArkExport2Type(arkClass);
520        } else {
521            type = TypeInference.inferUnclearedType(pType, arkClass);
522        }
523        if (type) {
524            param.setType(type);
525        }
526    }
527
528    public static inferSignatureReturnType(oldSignature: MethodSignature, arkMethod: ArkMethod): void {
529        if (oldSignature.getMethodSubSignature().getMethodName() === CONSTRUCTOR_NAME) {
530            const newReturnType = new ClassType(oldSignature.getDeclaringClassSignature());
531            oldSignature.getMethodSubSignature().setReturnType(newReturnType);
532            return;
533        }
534        const currReturnType = oldSignature.getType();
535        if (!this.isUnclearType(currReturnType)) {
536            return;
537        }
538
539        if (currReturnType instanceof AbstractTypeExpr) {
540            currReturnType.inferType(arkMethod);
541            return;
542        }
543
544        if (currReturnType instanceof ArrayType && currReturnType.getBaseType() instanceof AbstractTypeExpr) {
545            (currReturnType.getBaseType() as AbstractTypeExpr).inferType(arkMethod);
546            return;
547        }
548
549        const newReturnType = this.inferUnclearedType(currReturnType, arkMethod.getDeclaringArkClass());
550        if (newReturnType) {
551            oldSignature.getMethodSubSignature().setReturnType(newReturnType);
552        } else if (arkMethod.getBody()) {
553            const returnType = TypeInference.inferReturnType(arkMethod);
554            if (returnType) {
555                oldSignature.getMethodSubSignature().setReturnType(returnType);
556            }
557        }
558    }
559
560    private static inferReturnType(arkMethod: ArkMethod): Type | null {
561        const typeMap: Map<string, Type> = new Map();
562        for (let returnValue of arkMethod.getReturnValues()) {
563            const type = returnValue.getType();
564            if (type instanceof UnionType) {
565                type.flatType()
566                    .filter(t => !TypeInference.isUnclearType(t))
567                    .forEach(t => typeMap.set(t.toString(), t));
568            } else if (!TypeInference.isUnclearType(type)) {
569                typeMap.set(type.toString(), type);
570            }
571        }
572        if (typeMap.size > 0) {
573            const types: Type[] = Array.from(typeMap.values());
574            let returnType = types.length === 1 ? types[0] : new UnionType(types);
575            if (arkMethod.containsModifier(ModifierType.ASYNC)) {
576                const promise = arkMethod.getDeclaringArkFile().getScene().getSdkGlobal(PROMISE);
577                if (promise instanceof ArkClass) {
578                    returnType = new ClassType(promise.getSignature(), [returnType]);
579                }
580            }
581            return returnType;
582        }
583        return null;
584    }
585
586    public static inferGenericType(types: GenericType[] | undefined, arkClass: ArkClass): void {
587        types?.forEach(type => {
588            const defaultType = type.getDefaultType();
589            if (defaultType instanceof UnclearReferenceType) {
590                const newDefaultType = TypeInference.inferUnclearRefName(defaultType.getName(), arkClass);
591                if (newDefaultType) {
592                    type.setDefaultType(this.replaceTypeWithReal(newDefaultType));
593                }
594            }
595            const constraint = type.getConstraint();
596            if (constraint instanceof UnclearReferenceType) {
597                const newConstraint = TypeInference.inferUnclearRefName(constraint.getName(), arkClass);
598                if (newConstraint) {
599                    type.setConstraint(this.replaceTypeWithReal(newConstraint));
600                }
601            }
602        });
603    }
604
605    /**
606     * Infer type for a given {@link UnclearReferenceType} type.
607     * It returns original type.
608     * The original type is null if it failed to infer the type.
609     * @param urType
610     * @param arkClass
611     * @returns
612     */
613    public static inferUnclearRefType(urType: UnclearReferenceType, arkClass: ArkClass): Type | null {
614        const realTypes = urType.getGenericTypes();
615        this.inferRealGenericTypes(realTypes, arkClass);
616        if (urType.getName() === Builtin.ARRAY) {
617            return new ArrayType(realTypes[0] ?? AnyType.getInstance(), 1);
618        }
619        const type = this.inferUnclearRefName(urType.getName(), arkClass);
620        return type ? this.replaceTypeWithReal(type, realTypes) : null;
621    }
622
623    /**
624     * Find out the original object and type for a given unclear reference type name.
625     * It returns original type.
626     * The original type is null if it failed to infer the type.
627     * @param refName
628     * @param arkClass
629     * @returns
630     */
631    public static inferUnclearRefName(refName: string, arkClass: ArkClass): Type | null {
632        if (!refName) {
633            return null;
634        }
635        //split and iterate to infer each type
636        const singleNames = refName.split('.');
637        let type = null;
638        for (let i = 0; i < singleNames.length; i++) {
639            let genericName: string = EMPTY_STRING;
640            const name = singleNames[i].replace(/<(\w+)>/, (match, group1) => {
641                genericName = group1;
642                return EMPTY_STRING;
643            });
644            if (i === 0) {
645                type = singleNames.length > 1 ? this.inferBaseType(name, arkClass) : this.inferTypeByName(name, arkClass);
646            } else if (type) {
647                type = this.inferFieldType(type, name, arkClass)?.[1];
648            }
649            if (!type) {
650                return null;
651            }
652            if (genericName) {
653                const realTypes = genericName.split(',').map(generic => {
654                    const realType = this.inferUnclearRefName(generic, arkClass);
655                    return realType ?? new UnclearReferenceType(generic);
656                });
657                if (type instanceof ClassType) {
658                    type = new ClassType(type.getClassSignature(), realTypes);
659                } else if (type instanceof FunctionType) {
660                    type = new FunctionType(type.getMethodSignature(), realTypes);
661                }
662            }
663        }
664        return type;
665    }
666
667    /**
668     * Find out the original object and type for a given base type and the field name.
669     * It returns an array with 2 items, original object and original type.
670     * The original object is null if there is no object, or it failed to find the object.
671     * The original type is null if it failed to infer the type.
672     * @param baseType
673     * @param fieldName
674     * @param declareClass
675     * @returns
676     */
677    public static inferFieldType(baseType: Type, fieldName: string, declareClass: ArkClass): [any, Type] | null {
678        if (baseType instanceof AliasType) {
679            baseType = baseType.getOriginalType();
680        } else if (baseType instanceof UnionType && baseType.getCurrType()) {
681            baseType = baseType.getCurrType();
682        }
683        let propertyAndType: [any, Type] | null = null;
684        if (baseType instanceof ClassType) {
685            if (
686                fieldName === Builtin.ITERATOR_RESULT_VALUE &&
687                baseType.getClassSignature().getDeclaringFileSignature().getProjectName() === Builtin.DUMMY_PROJECT_NAME
688            ) {
689                const types = baseType.getRealGenericTypes();
690                if (types && types.length > 0) {
691                    return [null, types[0]];
692                }
693                return null;
694            }
695            propertyAndType = this.inferClassFieldType(declareClass, baseType, fieldName);
696        } else if (baseType instanceof AnnotationNamespaceType) {
697            const namespace = declareClass.getDeclaringArkFile().getScene().getNamespace(baseType.getNamespaceSignature());
698            if (namespace) {
699                const property = ModelUtils.findPropertyInNamespace(fieldName, namespace);
700                const propertyType = this.parseArkExport2Type(property);
701                if (propertyType) {
702                    propertyAndType = [property, propertyType];
703                }
704            }
705        } else {
706            logger.warn('infer unclear reference type fail: ' + fieldName);
707        }
708        return propertyAndType;
709    }
710
711    private static inferClassFieldType(declareClass: ArkClass, baseType: ClassType, fieldName: string): [any, Type] | null {
712        const arkClass = declareClass.getDeclaringArkFile().getScene().getClass(baseType.getClassSignature());
713        if (!arkClass) {
714            return null;
715        }
716        const property = ModelUtils.findPropertyInClass(fieldName, arkClass);
717        let propertyType: Type | null = null;
718        if (property instanceof ArkField) {
719            if (arkClass.getCategory() === ClassCategory.ENUM) {
720                let constant;
721                const propertyInitializer = property.getInitializer();
722                const lastStmt = propertyInitializer[propertyInitializer.length - 1];
723                if (lastStmt instanceof ArkAssignStmt && lastStmt.getRightOp() instanceof Constant) {
724                    constant = lastStmt.getRightOp() as Constant;
725                }
726                propertyType = new EnumValueType(property.getSignature(), constant);
727            } else {
728                propertyType = this.replaceTypeWithReal(property.getType(), baseType.getRealGenericTypes());
729            }
730        } else if (property) {
731            propertyType = this.parseArkExport2Type(property);
732        }
733        if (propertyType) {
734            return [property, propertyType];
735        } else if (arkClass.isAnonymousClass()) {
736            const fieldType = this.inferUnclearRefName(fieldName, arkClass);
737            return fieldType ? [null, fieldType] : null;
738        }
739        return null;
740    }
741
742    /**
743     * Find out the original object and type for a given base name.
744     * It returns original type.
745     * The original type is null if failed to infer the type.
746     * @param baseName
747     * @param arkClass
748     * @returns
749     */
750    public static inferBaseType(baseName: string, arkClass: ArkClass): Type | null {
751        if (SUPER_NAME === baseName) {
752            return this.parseArkExport2Type(arkClass.getSuperClass());
753        } else if (DEFAULT === baseName) {
754            return this.parseArkExport2Type(arkClass.getDeclaringArkFile().getExportInfoBy(DEFAULT)?.getArkExport());
755        }
756        const field = ModelUtils.getDefaultClass(arkClass)?.getDefaultArkMethod()?.getBody()?.getLocals()?.get(baseName);
757        if (field && !this.isUnclearType(field.getType())) {
758            return field.getType();
759        }
760        let arkExport: ArkExport | null =
761            ModelUtils.getClassWithName(baseName, arkClass) ??
762            ModelUtils.getDefaultClass(arkClass)?.getDefaultArkMethod()?.getBody()?.getAliasTypeByName(baseName) ??
763            ModelUtils.getNamespaceWithName(baseName, arkClass) ??
764            ModelUtils.getDefaultClass(arkClass)?.getMethodWithName(baseName) ??
765            ModelUtils.getArkExportInImportInfoWithName(baseName, arkClass.getDeclaringArkFile());
766        if (!arkExport && !arkClass.getDeclaringArkFile().getImportInfoBy(baseName)) {
767            arkExport = arkClass.getDeclaringArkFile().getScene().getSdkGlobal(baseName);
768        }
769        return this.parseArkExport2Type(arkExport);
770    }
771
772    public static inferTypeByName(typeName: string, arkClass: ArkClass): Type | null {
773        let arkExport: ArkExport | null =
774            ModelUtils.getClassWithName(typeName, arkClass) ??
775            ModelUtils.getDefaultClass(arkClass)?.getDefaultArkMethod()?.getBody()?.getAliasTypeByName(typeName) ??
776            ModelUtils.getArkExportInImportInfoWithName(typeName, arkClass.getDeclaringArkFile());
777        if (!arkExport && !arkClass.getDeclaringArkFile().getImportInfoBy(typeName)) {
778            arkExport = arkClass.getDeclaringArkFile().getScene().getSdkGlobal(typeName);
779        }
780        const type = this.parseArkExport2Type(arkExport);
781        if (type instanceof ClassType || type instanceof AliasType) {
782            return type;
783        }
784        return null;
785    }
786
787    public static getTypeByGlobalName(globalName: string, arkMethod: ArkMethod): Type | null {
788        const arkExport: ArkExport | null = arkMethod.getDeclaringArkFile().getScene().getSdkGlobal(globalName);
789        return this.parseArkExport2Type(arkExport);
790    }
791
792    public static inferRealGenericTypes(realTypes: Type[] | undefined, arkClass: ArkClass): void {
793        if (!realTypes) {
794            return;
795        }
796        for (let i = 0; i < realTypes.length; i++) {
797            const mayType = realTypes[i];
798            if (this.isUnclearType(mayType)) {
799                const newType = this.inferUnclearedType(mayType, arkClass);
800                if (newType) {
801                    realTypes[i] = newType;
802                }
803            }
804        }
805    }
806
807    public static inferDynamicImportType(from: string, arkClass: ArkClass): Type | null {
808        const importInfo = new ImportInfo();
809        importInfo.setNameBeforeAs(ALL);
810        importInfo.setImportClauseName(ALL);
811        importInfo.setImportFrom(from);
812        importInfo.setDeclaringArkFile(arkClass.getDeclaringArkFile());
813        return TypeInference.parseArkExport2Type(importInfo.getLazyExportInfo()?.getArkExport());
814    }
815
816    public static replaceTypeWithReal(type: Type, realTypes?: Type[], visited: Set<Type> = new Set()): Type {
817        if (visited.has(type)) {
818            return type;
819        } else {
820            visited.add(type);
821        }
822        if (type instanceof GenericType) {
823            const realType = realTypes?.[type.getIndex()] ?? type.getDefaultType() ?? type.getConstraint();
824            return realType ?? type;
825        } else if (type instanceof AnyType) {
826            const realType = realTypes?.[0];
827            return realType ?? type;
828        }
829        return this.replaceRecursiveType(type, visited, realTypes);
830    }
831
832    public static replaceRecursiveType(type: Type, visited: Set<Type>, realTypes?: Type[]): Type {
833        if (type instanceof ClassType) {
834            const replacedTypes = type.getRealGenericTypes()?.map(g => this.replaceTypeWithReal(g, realTypes, visited)) ?? realTypes;
835            return replacedTypes && replacedTypes.length > 0 ? new ClassType(type.getClassSignature(), replacedTypes) : type;
836        } else if (type instanceof FunctionType) {
837            const replacedTypes = type.getRealGenericTypes()?.map(g => this.replaceTypeWithReal(g, realTypes, visited)) ?? realTypes;
838            return replacedTypes && replacedTypes.length > 0 ? new FunctionType(type.getMethodSignature(), replacedTypes) : type;
839        } else if (type instanceof AliasType && realTypes) {
840            const newObjectType = this.replaceTypeWithReal(type.getOriginalType(), realTypes, visited);
841            const replacedTypes = type.getRealGenericTypes()?.map(g => this.replaceTypeWithReal(g, realTypes, visited)) ?? realTypes;
842            if (replacedTypes.length > 0) {
843                const newAliasType = new AliasType(type.getName(), newObjectType, type.getSignature(), type.getGenericTypes());
844                newAliasType.setRealGenericTypes(replacedTypes);
845                return newAliasType;
846            }
847        } else if (type instanceof UnionType && realTypes) {
848            const types: Type[] = [];
849            type.flatType().forEach(t => types.push(this.replaceTypeWithReal(t, realTypes, visited)));
850            return new UnionType(types, this.replaceTypeWithReal(type.getCurrType(), realTypes, visited));
851        } else if (type instanceof IntersectionType && realTypes) {
852            const types: Type[] = [];
853            type.getTypes().forEach(t => types.push(this.replaceTypeWithReal(t, realTypes, visited)));
854            return new IntersectionType(types);
855        } else if (type instanceof ArrayType && realTypes) {
856            const replacedBaseType = this.replaceTypeWithReal(type.getBaseType(), realTypes, visited);
857            return new ArrayType(replacedBaseType, type.getDimension());
858        } else if (type instanceof TupleType && realTypes) {
859            let replacedTypes: Type[] = [];
860            type.getTypes().forEach(t => replacedTypes.push(this.replaceTypeWithReal(t, realTypes, visited)));
861            return new TupleType(replacedTypes);
862        }
863        return type;
864    }
865
866    public static replaceAliasType(type: Type): Type {
867        let aliasType = type;
868        while (aliasType instanceof AliasType) {
869            aliasType = aliasType.getOriginalType();
870        }
871        return aliasType;
872    }
873
874    public static inferFunctionType(argType: FunctionType, paramSubSignature: MethodSubSignature | undefined, realTypes: Type[] | undefined): void {
875        const returnType = argType.getMethodSignature().getMethodSubSignature().getReturnType();
876        const declareType = paramSubSignature?.getReturnType();
877        if (declareType instanceof GenericType && realTypes && !this.isUnclearType(returnType) && !(returnType instanceof VoidType)) {
878            realTypes[declareType.getIndex()] = returnType;
879        }
880        const params = paramSubSignature?.getParameters();
881        if (!params) {
882            return;
883        }
884        argType
885            .getMethodSignature()
886            .getMethodSubSignature()
887            .getParameters()
888            .filter(p => !p.getName().startsWith(LEXICAL_ENV_NAME_PREFIX))
889            .forEach((p, i) => {
890                if (this.isUnclearType(p.getType())) {
891                    let type = params?.[i]?.getType();
892                    if (type instanceof GenericType && realTypes) {
893                        type = realTypes?.[type.getIndex()];
894                    }
895                    if (type) {
896                        p.setType(type);
897                    }
898                }
899            });
900    }
901
902    private static resolveArkReturnStmt(stmt: Stmt, arkMethod: ArkMethod): void {
903        if (!(stmt instanceof ArkReturnStmt)) {
904            return;
905        }
906        let returnType: Type | undefined = arkMethod.getSignature().getType();
907        if (returnType instanceof ClassType && returnType.getClassSignature().getClassName() === PROMISE) {
908            returnType = returnType.getRealGenericTypes()?.[0];
909        }
910        if (returnType) {
911            IRInference.inferRightWithSdkType(returnType, stmt.getOp().getType(), arkMethod.getDeclaringArkClass());
912        }
913    }
914}
915