• 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 ts, { HeritageClause, ParameterDeclaration, TypeNode, TypeParameterDeclaration } from 'ohos-typescript';
17import {
18    AliasType,
19    ArrayType,
20    ClassType,
21    FunctionType,
22    GenericType,
23    IntersectionType,
24    TupleType,
25    Type,
26    UnclearReferenceType,
27    UnionType,
28    UnknownType,
29} from '../../base/Type';
30import { TypeInference } from '../../common/TypeInference';
31import { ArkField } from '../ArkField';
32import Logger, { LOG_MODULE_TYPE } from '../../../utils/logger';
33import { ArkClass } from '../ArkClass';
34import { ArkMethod } from '../ArkMethod';
35import { Decorator } from '../../base/Decorator';
36import { ArrayBindingPatternParameter, buildArkMethodFromArkClass, MethodParameter, ObjectBindingPatternParameter } from './ArkMethodBuilder';
37import { buildNormalArkClassFromArkMethod } from './ArkClassBuilder';
38import { Builtin } from '../../common/Builtin';
39import { modifierKind2Enum } from '../ArkBaseModel';
40import { ArkValueTransformer } from '../../common/ArkValueTransformer';
41import { KeyofTypeExpr, TypeQueryExpr } from '../../base/TypeExpr';
42import {
43    ANY_KEYWORD,
44    BIGINT_KEYWORD,
45    BOOLEAN_KEYWORD,
46    NEVER_KEYWORD,
47    NULL_KEYWORD,
48    NUMBER_KEYWORD,
49    STRING_KEYWORD,
50    THIS_NAME,
51    UNDEFINED_KEYWORD,
52    VOID_KEYWORD,
53} from '../../common/TSConst';
54import { ArkSignatureBuilder } from './ArkSignatureBuilder';
55import { ArkInstanceFieldRef } from '../../base/Ref';
56import { Local } from '../../base/Local';
57import { Value } from '../../base/Value';
58
59const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'builderUtils');
60
61export function handleQualifiedName(node: ts.QualifiedName): string {
62    let right = (node.right as ts.Identifier).text;
63    let left: string = '';
64    if (node.left.kind === ts.SyntaxKind.Identifier) {
65        left = (node.left as ts.Identifier).text;
66    } else if (node.left.kind === ts.SyntaxKind.QualifiedName) {
67        left = handleQualifiedName(node.left as ts.QualifiedName);
68    }
69    let qualifiedName = left + '.' + right;
70    return qualifiedName;
71}
72
73export function handlePropertyAccessExpression(node: ts.PropertyAccessExpression): string {
74    let right = (node.name as ts.Identifier).text;
75    let left: string = '';
76    if (ts.SyntaxKind[node.expression.kind] === 'Identifier') {
77        left = (node.expression as ts.Identifier).text;
78    } else if (ts.isStringLiteral(node.expression)) {
79        left = node.expression.text;
80    } else if (ts.isPropertyAccessExpression(node.expression)) {
81        left = handlePropertyAccessExpression(node.expression as ts.PropertyAccessExpression);
82    }
83    let propertyAccessExpressionName = left + '.' + right;
84    return propertyAccessExpressionName;
85}
86
87export function buildDecorators(node: ts.Node, sourceFile: ts.SourceFile): Set<Decorator> {
88    let decorators: Set<Decorator> = new Set();
89    ts.getAllDecorators(node).forEach(decoratorNode => {
90        let decorator = parseDecorator(decoratorNode);
91        if (decorator) {
92            decorator.setContent(decoratorNode.expression.getText(sourceFile));
93            decorators.add(decorator);
94        }
95    });
96    return decorators;
97}
98
99function parseDecorator(node: ts.Decorator): Decorator | undefined {
100    if (!node.expression) {
101        return undefined;
102    }
103
104    let expression = node.expression;
105    if (ts.isIdentifier(expression)) {
106        return new Decorator(expression.text);
107    }
108    if (!ts.isCallExpression(expression) || !ts.isIdentifier(expression.expression)) {
109        return undefined;
110    }
111
112    let decorator = new Decorator(expression.expression.text);
113
114    if (expression.arguments.length > 0) {
115        const arg = expression.arguments[0];
116        if (ts.isArrowFunction(arg) && ts.isIdentifier(arg.body)) {
117            decorator.setParam(arg.body.text);
118        }
119    }
120
121    return decorator;
122}
123
124export function buildModifiers(node: ts.Node): number {
125    let modifiers: number = 0;
126
127    if (ts.canHaveModifiers(node)) {
128        ts.getModifiers(node)?.forEach(modifier => {
129            modifiers |= modifierKind2Enum(modifier.kind);
130        });
131    }
132
133    return modifiers;
134}
135
136export function buildHeritageClauses(heritageClauses?: ts.NodeArray<HeritageClause>): Map<string, string> {
137    let heritageClausesMap: Map<string, string> = new Map<string, string>();
138    heritageClauses?.forEach(heritageClause => {
139        heritageClause.types.forEach(type => {
140            let heritageClauseName: string = '';
141            if (type.typeArguments) {
142                heritageClauseName = type.getText();
143            } else if (ts.isIdentifier(type.expression)) {
144                heritageClauseName = (type.expression as ts.Identifier).text;
145            } else if (ts.isPropertyAccessExpression(type.expression)) {
146                heritageClauseName = handlePropertyAccessExpression(type.expression);
147            } else {
148                heritageClauseName = type.getText();
149            }
150            heritageClausesMap.set(heritageClauseName, ts.SyntaxKind[heritageClause.token]);
151        });
152    });
153    return heritageClausesMap;
154}
155
156export function buildTypeParameters(
157    typeParameters: ts.NodeArray<TypeParameterDeclaration>,
158    sourceFile: ts.SourceFile,
159    arkInstance: ArkMethod | ArkClass
160): GenericType[] {
161    const genericTypes: GenericType[] = [];
162    let index = 0;
163    if (arkInstance instanceof ArkMethod) {
164        const len = arkInstance.getDeclaringArkClass().getGenericsTypes()?.length;
165        if (len) {
166            index = len;
167        }
168    }
169    typeParameters.forEach(typeParameter => {
170        const genericType = tsNode2Type(typeParameter, sourceFile, arkInstance);
171        if (genericType instanceof GenericType) {
172            genericType.setIndex(index++);
173            genericTypes.push(genericType);
174        }
175
176        if (typeParameter.modifiers) {
177            logger.warn('This typeparameter has modifiers.');
178        }
179
180        if (typeParameter.expression) {
181            logger.warn('This typeparameter has expression.');
182        }
183    });
184    return genericTypes;
185}
186
187function buildObjectBindingPatternParam(methodParameter: MethodParameter, paramNameNode: ts.ObjectBindingPattern): void {
188    methodParameter.setName('ObjectBindingPattern');
189    let elements: ObjectBindingPatternParameter[] = [];
190    paramNameNode.elements.forEach(element => {
191        let paraElement = new ObjectBindingPatternParameter();
192        if (element.propertyName) {
193            if (ts.isIdentifier(element.propertyName)) {
194                paraElement.setPropertyName(element.propertyName.text);
195            } else {
196                logger.warn('New propertyName of ObjectBindingPattern found, please contact developers to support this!');
197            }
198        }
199
200        if (element.name) {
201            if (ts.isIdentifier(element.name)) {
202                paraElement.setName(element.name.text);
203            } else {
204                logger.warn('New name of ObjectBindingPattern found, please contact developers to support this!');
205            }
206        }
207
208        if (element.initializer) {
209            logger.warn('TODO: support ObjectBindingPattern initializer.');
210        }
211
212        if (element.dotDotDotToken) {
213            paraElement.setOptional(true);
214        }
215        elements.push(paraElement);
216    });
217    methodParameter.setObjElements(elements);
218}
219
220function buildBindingElementOfBindingPatternParam(element: ts.BindingElement, paraElement: ArrayBindingPatternParameter): void {
221    if (element.propertyName) {
222        if (ts.isIdentifier(element.propertyName)) {
223            paraElement.setPropertyName(element.propertyName.text);
224        } else {
225            logger.warn('New propertyName of ArrayBindingPattern found, please contact developers to support this!');
226        }
227    }
228
229    if (element.name) {
230        if (ts.isIdentifier(element.name)) {
231            paraElement.setName(element.name.text);
232        } else {
233            logger.warn('New name of ArrayBindingPattern found, please contact developers to support this!');
234        }
235    }
236
237    if (element.initializer) {
238        logger.warn('TODO: support ArrayBindingPattern initializer.');
239    }
240
241    if (element.dotDotDotToken) {
242        paraElement.setOptional(true);
243    }
244}
245
246function buildArrayBindingPatternParam(methodParameter: MethodParameter, paramNameNode: ts.ArrayBindingPattern): void {
247    methodParameter.setName('ArrayBindingPattern');
248    let elements: ArrayBindingPatternParameter[] = [];
249    paramNameNode.elements.forEach(element => {
250        let paraElement = new ArrayBindingPatternParameter();
251        if (ts.isBindingElement(element)) {
252            buildBindingElementOfBindingPatternParam(element, paraElement);
253        } else if (ts.isOmittedExpression(element)) {
254            logger.warn('TODO: support OmittedExpression for ArrayBindingPattern parameter name.');
255        }
256        elements.push(paraElement);
257    });
258    methodParameter.setArrayElements(elements);
259}
260
261export function buildParameters(params: ts.NodeArray<ParameterDeclaration>, arkInstance: ArkMethod | ArkField, sourceFile: ts.SourceFile): MethodParameter[] {
262    let parameters: MethodParameter[] = [];
263    params.forEach(parameter => {
264        let methodParameter = new MethodParameter();
265
266        // name
267        if (ts.isIdentifier(parameter.name)) {
268            methodParameter.setName(parameter.name.text);
269        } else if (ts.isObjectBindingPattern(parameter.name)) {
270            buildObjectBindingPatternParam(methodParameter, parameter.name);
271        } else if (ts.isArrayBindingPattern(parameter.name)) {
272            buildArrayBindingPatternParam(methodParameter, parameter.name);
273        } else {
274            logger.warn('Parameter name is not identifier, ObjectBindingPattern nor ArrayBindingPattern, please contact developers to support this!');
275        }
276
277        // questionToken
278        if (parameter.questionToken) {
279            methodParameter.setOptional(true);
280        }
281
282        // type
283        if (parameter.type) {
284            methodParameter.setType(buildGenericType(tsNode2Type(parameter.type, sourceFile, arkInstance), arkInstance));
285        } else {
286            methodParameter.setType(UnknownType.getInstance());
287        }
288
289        // initializer
290        if (parameter.initializer) {
291            //TODO?
292        }
293
294        // dotDotDotToken
295        if (parameter.dotDotDotToken) {
296            methodParameter.setDotDotDotToken(true);
297        }
298
299        // modifiers
300        if (parameter.modifiers) {
301            //
302        }
303
304        parameters.push(methodParameter);
305    });
306    return parameters;
307}
308
309export function buildGenericType(type: Type, arkInstance: ArkMethod | ArkField | AliasType): Type {
310    function replace(urType: UnclearReferenceType): Type {
311        const typeName = urType.getName();
312        let gType;
313        if (arkInstance instanceof AliasType) {
314            gType = arkInstance.getGenericTypes()?.find(f => f.getName() === typeName);
315        } else {
316            if (arkInstance instanceof ArkMethod) {
317                gType = arkInstance.getGenericTypes()?.find(f => f.getName() === typeName);
318            }
319            if (!gType) {
320                gType = arkInstance
321                    .getDeclaringArkClass()
322                    .getGenericsTypes()
323                    ?.find(f => f.getName() === typeName);
324            }
325        }
326        if (gType) {
327            return gType;
328        }
329        const types = urType.getGenericTypes();
330        for (let i = 0; i < types.length; i++) {
331            const mayType = types[i];
332            if (mayType instanceof UnclearReferenceType) {
333                types[i] = replace(mayType);
334            }
335        }
336        return urType;
337    }
338
339    if (type instanceof UnclearReferenceType) {
340        return replace(type);
341    } else if (type instanceof ClassType && arkInstance instanceof AliasType) {
342        type.setRealGenericTypes(arkInstance.getGenericTypes());
343    } else if (type instanceof UnionType || type instanceof TupleType) {
344        const types = type.getTypes();
345        for (let i = 0; i < types.length; i++) {
346            const mayType = types[i];
347            if (mayType instanceof UnclearReferenceType) {
348                types[i] = replace(mayType);
349            }
350        }
351    } else if (type instanceof ArrayType) {
352        const baseType = type.getBaseType();
353        if (baseType instanceof UnclearReferenceType) {
354            type.setBaseType(replace(baseType));
355        }
356    } else if (type instanceof FunctionType) {
357        const returnType = type.getMethodSignature().getType();
358        if (returnType instanceof UnclearReferenceType) {
359            type.getMethodSignature().getMethodSubSignature().setReturnType(replace(returnType));
360        }
361    }
362    return type;
363}
364
365export function buildReturnType(node: TypeNode, sourceFile: ts.SourceFile, method: ArkMethod): Type {
366    if (node) {
367        return tsNode2Type(node, sourceFile, method);
368    } else {
369        return UnknownType.getInstance();
370    }
371}
372
373export function tsNode2Type(
374    typeNode: ts.TypeNode | ts.TypeParameterDeclaration,
375    sourceFile: ts.SourceFile,
376    arkInstance: ArkMethod | ArkClass | ArkField
377): Type {
378    if (ts.isTypeReferenceNode(typeNode)) {
379        const genericTypes: Type[] = [];
380        if (typeNode.typeArguments) {
381            for (const typeArgument of typeNode.typeArguments) {
382                genericTypes.push(tsNode2Type(typeArgument, sourceFile, arkInstance));
383            }
384        }
385        let referenceNodeName = typeNode.typeName;
386        if (ts.isQualifiedName(referenceNodeName)) {
387            let parameterTypeStr = handleQualifiedName(referenceNodeName as ts.QualifiedName);
388            return new UnclearReferenceType(parameterTypeStr, genericTypes);
389        } else {
390            let parameterTypeStr = referenceNodeName.text;
391            if (parameterTypeStr === Builtin.OBJECT) {
392                return Builtin.OBJECT_CLASS_TYPE;
393            }
394            return new UnclearReferenceType(parameterTypeStr, genericTypes);
395        }
396    } else if (ts.isUnionTypeNode(typeNode) || ts.isIntersectionTypeNode(typeNode)) {
397        let multipleTypePara: Type[] = [];
398        typeNode.types.forEach(tmpType => {
399            multipleTypePara.push(tsNode2Type(tmpType, sourceFile, arkInstance));
400        });
401        if (ts.isUnionTypeNode(typeNode)) {
402            return new UnionType(multipleTypePara);
403        } else {
404            return new IntersectionType(multipleTypePara);
405        }
406    } else if (ts.isLiteralTypeNode(typeNode)) {
407        return ArkValueTransformer.resolveLiteralTypeNode(typeNode, sourceFile);
408    } else if (ts.isTypeLiteralNode(typeNode)) {
409        let cls: ArkClass = new ArkClass();
410        let declaringClass: ArkClass;
411
412        if (arkInstance instanceof ArkMethod) {
413            declaringClass = arkInstance.getDeclaringArkClass();
414        } else if (arkInstance instanceof ArkField) {
415            declaringClass = arkInstance.getDeclaringArkClass();
416        } else {
417            declaringClass = arkInstance;
418        }
419        if (declaringClass.getDeclaringArkNamespace()) {
420            cls.setDeclaringArkNamespace(declaringClass.getDeclaringArkNamespace());
421            cls.setDeclaringArkFile(declaringClass.getDeclaringArkFile());
422        } else {
423            cls.setDeclaringArkFile(declaringClass.getDeclaringArkFile());
424        }
425        buildNormalArkClassFromArkMethod(typeNode, cls, sourceFile);
426
427        return new ClassType(cls.getSignature());
428    } else if (ts.isFunctionTypeNode(typeNode)) {
429        let mtd: ArkMethod = new ArkMethod();
430        let cls: ArkClass;
431        if (arkInstance instanceof ArkMethod) {
432            cls = arkInstance.getDeclaringArkClass();
433        } else if (arkInstance instanceof ArkClass) {
434            cls = arkInstance;
435        } else {
436            cls = arkInstance.getDeclaringArkClass();
437        }
438        buildArkMethodFromArkClass(typeNode, cls, mtd, sourceFile);
439        return new FunctionType(mtd.getSignature());
440    } else if (ts.isTypeParameterDeclaration(typeNode)) {
441        const name = typeNode.name.text;
442        let defaultType;
443        if (typeNode.default) {
444            defaultType = tsNode2Type(typeNode.default, sourceFile, arkInstance);
445        }
446        let constraint;
447        if (typeNode.constraint) {
448            constraint = tsNode2Type(typeNode.constraint, sourceFile, arkInstance);
449        }
450        return new GenericType(name, defaultType, constraint);
451    } else if (ts.isTupleTypeNode(typeNode)) {
452        const types: Type[] = [];
453        typeNode.elements.forEach(element => {
454            types.push(tsNode2Type(element, sourceFile, arkInstance));
455        });
456        return new TupleType(types);
457    } else if (ts.isArrayTypeNode(typeNode)) {
458        return new ArrayType(tsNode2Type((typeNode as ts.ArrayTypeNode).elementType, sourceFile, arkInstance), 1);
459    } else if (ts.isParenthesizedTypeNode(typeNode)) {
460        return tsNode2Type(typeNode.type, sourceFile, arkInstance);
461    } else if (ts.isTypeOperatorNode(typeNode)) {
462        return buildTypeFromTypeOperator(typeNode as ts.TypeOperatorNode, sourceFile, arkInstance);
463    } else if (ts.isTypeQueryNode(typeNode)) {
464        return buildTypeFromTypeQuery(typeNode as ts.TypeQueryNode, sourceFile, arkInstance);
465    } else if (typeNode.kind === ts.SyntaxKind.ObjectKeyword) {
466        // TODO: type object which is different from Object is needed to support, such as let a: object = {}
467        return new UnclearReferenceType('object');
468    } else {
469        return buildTypeFromPreStr(ts.SyntaxKind[typeNode.kind]);
470    }
471}
472
473export function buildTypeFromPreStr(preStr: string): Type {
474    let postStr = '';
475    switch (preStr) {
476        case 'BooleanKeyword':
477            postStr = BOOLEAN_KEYWORD;
478            break;
479        case 'FalseKeyword':
480            postStr = BOOLEAN_KEYWORD;
481            break;
482        case 'TrueKeyword':
483            postStr = BOOLEAN_KEYWORD;
484            break;
485        case 'NumberKeyword':
486            postStr = NUMBER_KEYWORD;
487            break;
488        case 'NumericLiteral':
489            postStr = NUMBER_KEYWORD;
490            break;
491        case 'FirstLiteralToken':
492            postStr = NUMBER_KEYWORD;
493            break;
494        case 'StringKeyword':
495            postStr = STRING_KEYWORD;
496            break;
497        case 'StringLiteral':
498            postStr = STRING_KEYWORD;
499            break;
500        case 'UndefinedKeyword':
501            postStr = UNDEFINED_KEYWORD;
502            break;
503        case 'NullKeyword':
504            postStr = NULL_KEYWORD;
505            break;
506        case 'AnyKeyword':
507            postStr = ANY_KEYWORD;
508            break;
509        case 'VoidKeyword':
510            postStr = VOID_KEYWORD;
511            break;
512        case 'NeverKeyword':
513            postStr = NEVER_KEYWORD;
514            break;
515        case 'BigIntKeyword':
516            postStr = BIGINT_KEYWORD;
517            break;
518        default:
519            postStr = preStr;
520    }
521    return TypeInference.buildTypeFromStr(postStr);
522}
523
524function buildTypeFromTypeOperator(typeOperatorNode: ts.TypeOperatorNode, sourceFile: ts.SourceFile, arkInstance: ArkMethod | ArkClass | ArkField): Type {
525    const typeNode = typeOperatorNode.type;
526    let type = tsNode2Type(typeNode, sourceFile, arkInstance);
527
528    switch (typeOperatorNode.operator) {
529        case ts.SyntaxKind.ReadonlyKeyword: {
530            if (type instanceof ArrayType || type instanceof TupleType) {
531                type.setReadonlyFlag(true);
532            }
533            return type;
534        }
535        case ts.SyntaxKind.KeyOfKeyword:
536            return new KeyofTypeExpr(type);
537        case ts.SyntaxKind.UniqueKeyword:
538            return UnknownType.getInstance();
539        default:
540            return UnknownType.getInstance();
541    }
542}
543
544function buildTypeFromTypeQuery(typeQueryNode: ts.TypeQueryNode, sourceFile: ts.SourceFile, arkInstance: ArkMethod | ArkClass | ArkField): Type {
545    const exprNameNode = typeQueryNode.exprName;
546    let opValue: Value;
547    if (ts.isQualifiedName(exprNameNode)) {
548        if (exprNameNode.left.getText(sourceFile) === THIS_NAME) {
549            const fieldName = exprNameNode.right.getText(sourceFile);
550            if (arkInstance instanceof ArkMethod) {
551                const fieldSignature =
552                    arkInstance.getDeclaringArkClass().getFieldWithName(fieldName)?.getSignature() ??
553                    ArkSignatureBuilder.buildFieldSignatureFromFieldName(fieldName);
554                const baseLocal =
555                    arkInstance.getBody()?.getLocals().get(THIS_NAME) ??
556                    new Local(
557                        THIS_NAME,
558                        new ClassType(arkInstance.getDeclaringArkClass().getSignature(), arkInstance.getDeclaringArkClass().getGenericsTypes())
559                    );
560                opValue = new ArkInstanceFieldRef(baseLocal, fieldSignature);
561            } else if (arkInstance instanceof ArkClass) {
562                const fieldSignature =
563                    arkInstance.getFieldWithName(fieldName)?.getSignature() ?? ArkSignatureBuilder.buildFieldSignatureFromFieldName(fieldName);
564                const baseLocal = new Local(THIS_NAME, new ClassType(arkInstance.getSignature(), arkInstance.getGenericsTypes()));
565                opValue = new ArkInstanceFieldRef(baseLocal, fieldSignature);
566            } else {
567                const fieldSignature = arkInstance.getSignature();
568                const baseLocal = new Local(
569                    THIS_NAME,
570                    new ClassType(arkInstance.getDeclaringArkClass().getSignature(), arkInstance.getDeclaringArkClass().getGenericsTypes())
571                );
572                opValue = new ArkInstanceFieldRef(baseLocal, fieldSignature);
573            }
574        } else {
575            const exprName = exprNameNode.getText(sourceFile);
576            opValue = new Local(exprName, UnknownType.getInstance());
577        }
578    } else {
579        const exprName = exprNameNode.escapedText.toString();
580        opValue = new Local(exprName, UnknownType.getInstance());
581    }
582
583    let expr = new TypeQueryExpr(opValue);
584    if (typeQueryNode.typeArguments) {
585        for (const typeArgument of typeQueryNode.typeArguments) {
586            expr.addGenericType(tsNode2Type(typeArgument, sourceFile, arkInstance));
587        }
588    }
589    return expr;
590}
591