• 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 { ClassType, GenericType, Type } from '../base/Type';
17import { ViewTree } from '../graph/ViewTree';
18import { ArkField } from './ArkField';
19import { ArkFile, Language } from './ArkFile';
20import { ArkMethod } from './ArkMethod';
21import { ArkNamespace } from './ArkNamespace';
22import { ClassSignature, FieldSignature, FileSignature, MethodSignature, NamespaceSignature } from './ArkSignature';
23import { Local } from '../base/Local';
24import { ArkExport, ExportType } from './ArkExport';
25import { TypeInference } from '../common/TypeInference';
26import { ANONYMOUS_CLASS_PREFIX, DEFAULT_ARK_CLASS_NAME, NAME_DELIMITER, NAME_PREFIX } from '../common/Const';
27import { getColNo, getLineNo, LineCol, setCol, setLine } from '../base/Position';
28import { ArkBaseModel } from './ArkBaseModel';
29import { ArkError } from '../common/ArkError';
30
31export enum ClassCategory {
32    CLASS = 0,
33    STRUCT = 1,
34    INTERFACE = 2,
35    ENUM = 3,
36    TYPE_LITERAL = 4,
37    OBJECT = 5,
38}
39
40/**
41 * @category core/model
42 */
43export class ArkClass extends ArkBaseModel implements ArkExport {
44    private category!: ClassCategory;
45    private code?: string;
46    private lineCol: LineCol = 0;
47
48    private declaringArkFile!: ArkFile;
49    private declaringArkNamespace: ArkNamespace | undefined;
50    private classSignature!: ClassSignature;
51    /**
52     * The keys of the `heritageClasses` map represent the names of superclass and interfaces.
53     * The superclass name is placed first; if it does not exist, an empty string `''` will occupy this position.
54     * The values of the `heritageClasses` map will be replaced with `ArkClass` or `null` during type inference.
55     */
56    private heritageClasses: Map<string, ArkClass | null | undefined> = new Map<string, ArkClass | null | undefined>();
57
58    private genericsTypes?: GenericType[];
59    private realTypes?: Type[];
60    private defaultMethod: ArkMethod | null = null;
61
62    // name to model
63    private methods: Map<string, ArkMethod> = new Map<string, ArkMethod>();
64    private fields: Map<string, ArkField> = new Map<string, ArkField>();
65    private extendedClasses: Map<string, ArkClass> = new Map<string, ArkClass>();
66    private staticMethods: Map<string, ArkMethod> = new Map<string, ArkMethod>();
67    private staticFields: Map<string, ArkField> = new Map<string, ArkField>();
68
69    private instanceInitMethod: ArkMethod = new ArkMethod();
70    private staticInitMethod: ArkMethod = new ArkMethod();
71
72    private anonymousMethodNumber: number = 0;
73    private indexSignatureNumber: number = 0;
74
75    private viewTree?: ViewTree;
76
77    constructor() {
78        super();
79    }
80
81    /**
82     * Returns the program language of the file where this class defined.
83     */
84    public getLanguage(): Language {
85        return this.getDeclaringArkFile().getLanguage();
86    }
87
88    /**
89     * Returns the **string**name of this class.
90     * @returns The name of this class.
91     */
92    public getName(): string {
93        return this.classSignature.getClassName();
94    }
95
96    /**
97     * Returns the codes of class as a **string.**
98     * @returns the codes of class.
99     */
100    public getCode(): string | undefined {
101        return this.code;
102    }
103
104    public setCode(code: string): void {
105        this.code = code;
106    }
107
108    /**
109     * Returns the line position of this class.
110     * @returns The line position of this class.
111     */
112    public getLine(): number {
113        return getLineNo(this.lineCol);
114    }
115
116    public setLine(line: number): void {
117        this.lineCol = setLine(this.lineCol, line);
118    }
119
120    /**
121     * Returns the column position of this class.
122     * @returns The column position of this class.
123     */
124    public getColumn(): number {
125        return getColNo(this.lineCol);
126    }
127
128    public setColumn(column: number): void {
129        this.lineCol = setCol(this.lineCol, column);
130    }
131
132    public getCategory(): ClassCategory {
133        return this.category;
134    }
135
136    public setCategory(category: ClassCategory): void {
137        this.category = category;
138    }
139
140    /**
141     * Returns the declaring file.
142     * @returns A file defined by ArkAnalyzer.
143     * @example
144     * 1. Get the {@link ArkFile} which the ArkClass is in.
145
146     ```typescript
147     const arkFile = arkClass.getDeclaringArkFile();
148     ```
149     */
150    public getDeclaringArkFile(): ArkFile {
151        return this.declaringArkFile;
152    }
153
154    public setDeclaringArkFile(declaringArkFile: ArkFile): void {
155        this.declaringArkFile = declaringArkFile;
156    }
157
158    /**
159     * Returns the declaring namespace of this class, which may also be an **undefined**.
160     * @returns The declaring namespace (may be **undefined**) of this class.
161     */
162    public getDeclaringArkNamespace(): ArkNamespace | undefined {
163        return this.declaringArkNamespace;
164    }
165
166    public setDeclaringArkNamespace(declaringArkNamespace: ArkNamespace | undefined): void {
167        this.declaringArkNamespace = declaringArkNamespace;
168    }
169
170    public isDefaultArkClass(): boolean {
171        return this.getName() === DEFAULT_ARK_CLASS_NAME;
172    }
173
174    public isAnonymousClass(): boolean {
175        return this.getName().startsWith(ANONYMOUS_CLASS_PREFIX);
176    }
177
178    /**
179     * Returns the signature of current class (i.e., {@link ClassSignature}).
180     * The {@link ClassSignature} can uniquely identify a class, according to which we can find the class from the scene.
181     * @returns The class signature.
182     */
183    public getSignature(): ClassSignature {
184        return this.classSignature;
185    }
186
187    public setSignature(classSig: ClassSignature): void {
188        this.classSignature = classSig;
189    }
190
191    public getSuperClassName(): string {
192        return this.heritageClasses.keys().next().value || '';
193    }
194
195    public addHeritageClassName(className: string): void {
196        this.heritageClasses.set(className, undefined);
197    }
198
199    /**
200     * Returns the superclass of this class.
201     * @returns The superclass of this class.
202     */
203    public getSuperClass(): ArkClass | null {
204        const heritageClass = this.getHeritageClass(this.getSuperClassName());
205        if (heritageClass && heritageClass.getCategory() !== ClassCategory.INTERFACE) {
206            return heritageClass;
207        }
208        return null;
209    }
210
211    private getHeritageClass(heritageClassName: string): ArkClass | null {
212        if (!heritageClassName) {
213            return null;
214        }
215        let superClass = this.heritageClasses.get(heritageClassName);
216        if (superClass === undefined) {
217            let type = TypeInference.inferUnclearRefName(heritageClassName, this);
218            if (type) {
219                type = TypeInference.replaceAliasType(type);
220            }
221            if (type instanceof ClassType && (superClass = this.declaringArkFile.getScene().getClass(type.getClassSignature()))) {
222                superClass.addExtendedClass(this);
223                const realGenericTypes = type.getRealGenericTypes();
224                if (realGenericTypes) {
225                    this.realTypes = realGenericTypes;
226                }
227            }
228            this.heritageClasses.set(heritageClassName, superClass || null);
229        }
230        return superClass || null;
231    }
232
233    public getAllHeritageClasses(): ArkClass[] {
234        const result: ArkClass[] = [];
235        this.heritageClasses.forEach((v, k) => {
236            const heritage = v ?? this.getHeritageClass(k);
237            if (heritage) {
238                result.push(heritage);
239            }
240        });
241        return result;
242    }
243
244    public getExtendedClasses(): Map<string, ArkClass> {
245        return this.extendedClasses;
246    }
247
248    public addExtendedClass(extendedClass: ArkClass): void {
249        this.extendedClasses.set(extendedClass.getName(), extendedClass);
250    }
251
252    public getImplementedInterfaceNames(): string[] {
253        if (this.category === ClassCategory.INTERFACE) {
254            return [];
255        }
256        return Array.from(this.heritageClasses.keys()).slice(1);
257    }
258
259    public hasImplementedInterface(interfaceName: string): boolean {
260        return this.heritageClasses.has(interfaceName) && this.getSuperClassName() !== interfaceName;
261    }
262
263    public getImplementedInterface(interfaceName: string): ArkClass | null {
264        const heritageClass = this.getHeritageClass(interfaceName);
265        if (heritageClass && heritageClass.getCategory() === ClassCategory.INTERFACE) {
266            return heritageClass;
267        }
268        return null;
269    }
270
271    /**
272     * Get the field according to its field signature.
273     * If no field cound be found, **null**will be returned.
274     * @param fieldSignature - the field's signature.
275     * @returns A field. If there is no field in this class, the return will be a **null**.
276     */
277    public getField(fieldSignature: FieldSignature): ArkField | null {
278        const fieldName = fieldSignature.getFieldName();
279        let fieldSearched: ArkField | null = this.getFieldWithName(fieldName);
280        if (!fieldSearched) {
281            fieldSearched = this.getStaticFieldWithName(fieldName);
282        }
283        return fieldSearched;
284    }
285
286    public getFieldWithName(fieldName: string): ArkField | null {
287        return this.fields.get(fieldName) || null;
288    }
289
290    public getStaticFieldWithName(fieldName: string): ArkField | null {
291        return this.staticFields.get(fieldName) || null;
292    }
293
294    /**
295     * Returns an **array** of fields in the class.
296     * @returns an **array** of fields in the class.
297     */
298    public getFields(): ArkField[] {
299        const allFields: ArkField[] = Array.from(this.fields.values());
300        allFields.push(...this.staticFields.values());
301        return allFields;
302    }
303
304    public addField(field: ArkField): void {
305        if (field.isStatic()) {
306            this.staticFields.set(field.getName(), field);
307        } else {
308            this.fields.set(field.getName(), field);
309        }
310    }
311
312    public addFields(fields: ArkField[]): void {
313        fields.forEach(field => {
314            this.addField(field);
315        });
316    }
317
318    public getRealTypes(): Type[] | undefined {
319        return this.realTypes ? Array.from(this.realTypes) : undefined;
320    }
321
322    public getGenericsTypes(): GenericType[] | undefined {
323        return this.genericsTypes ? Array.from(this.genericsTypes) : undefined;
324    }
325
326    public addGenericType(gType: GenericType): void {
327        if (!this.genericsTypes) {
328            this.genericsTypes = [];
329        }
330        this.genericsTypes.push(gType);
331    }
332
333    /**
334     * Returns all methods defined in the specific class in the form of an array.
335     * @param generated - indicating whether this API returns the methods that are dynamically
336     * generated at runtime. If it is not specified as true or false, the return will not include the generated method.
337     * @returns An array of all methods in this class.
338     * @example
339     * 1. Get methods defined in class `BookService`.
340
341     ```typescript
342     let classes: ArkClass[] = scene.getClasses();
343     let serviceClass : ArkClass = classes[1];
344     let methods: ArkMethod[] = serviceClass.getMethods();
345     let methodNames: string[] = methods.map(mthd => mthd.name);
346     console.log(methodNames);
347     ```
348     */
349    public getMethods(generated?: boolean): ArkMethod[] {
350        const allMethods = Array.from(this.methods.values()).filter(f => (!generated && !f.isGenerated()) || generated);
351        allMethods.push(...this.staticMethods.values());
352        return [...new Set(allMethods)];
353    }
354
355    public getMethod(methodSignature: MethodSignature): ArkMethod | null {
356        const methodName = methodSignature.getMethodSubSignature().getMethodName();
357        const methodSearched = this.getMethodWithName(methodName) ?? this.getStaticMethodWithName(methodName);
358        if (methodSearched === null) {
359            return null;
360        }
361        const implSignature = methodSearched.getImplementationSignature();
362        if (implSignature !== null && implSignature.isMatch(methodSignature)) {
363            return methodSearched;
364        }
365        const declareSignatures = methodSearched.getDeclareSignatures();
366        if (declareSignatures !== null) {
367            for (let i = 0; i < declareSignatures.length; i++) {
368                if (declareSignatures[i].isMatch(methodSignature)) {
369                    return methodSearched;
370                }
371            }
372        }
373        return null;
374    }
375
376    public getMethodWithName(methodName: string): ArkMethod | null {
377        return this.methods.get(methodName) || null;
378    }
379
380    public getStaticMethodWithName(methodName: string): ArkMethod | null {
381        return this.staticMethods.get(methodName) || null;
382    }
383
384    /**
385     * add a method in class.
386     * when a nested method with declare name, add both the declare origin name and signature name
387     * %${declare name}$${outer method name} in class.
388     */
389    public addMethod(method: ArkMethod, originName?: string): void {
390        const name = originName ?? method.getName();
391        if (method.isStatic()) {
392            this.staticMethods.set(name, method);
393        } else {
394            this.methods.set(name, method);
395        }
396        if (!originName && !method.isAnonymousMethod() && name.startsWith(NAME_PREFIX)) {
397            const index = name.indexOf(NAME_DELIMITER);
398            if (index > 1) {
399                const originName = name.substring(1, index);
400                this.addMethod(method, originName);
401            }
402        }
403    }
404
405    public setDefaultArkMethod(defaultMethod: ArkMethod): void {
406        this.defaultMethod = defaultMethod;
407        this.addMethod(defaultMethod);
408    }
409
410    public getDefaultArkMethod(): ArkMethod | null {
411        return this.defaultMethod;
412    }
413
414    public setViewTree(viewTree: ViewTree): void {
415        this.viewTree = viewTree;
416    }
417
418    /**
419     * Returns the view tree of the ArkClass.
420     * @returns The view tree of the ArkClass.
421     * @example
422     * 1. get viewTree of ArkClass.
423
424     ```typescript
425     for (let arkFiles of scene.getFiles()) {
426     for (let arkClasss of arkFiles.getClasses()) {
427     if (arkClasss.hasViewTree()) {
428     arkClasss.getViewTree();
429     }
430     }
431     }
432     ```
433     */
434    public getViewTree(): ViewTree | undefined {
435        return this.viewTree;
436    }
437
438    /**
439     * Check whether the view tree is defined.
440     * If it is defined, the return value is true, otherwise it is false.
441     * @returns True if the view tree is defined; false otherwise.
442     * @example
443     * 1. Judge viewTree of ArkClass.
444
445     ```typescript
446     for (let arkFiles of scene.getFiles()) {
447     for (let arkClasss of arkFiles.getClasses()) {
448     if (arkClasss.hasViewTree()) {
449     arkClasss.getViewTree();
450     }
451     }
452     }
453     ```
454     */
455    public hasViewTree(): boolean {
456        return this.viewTree !== undefined;
457    }
458
459    public getStaticFields(classMap: Map<FileSignature | NamespaceSignature, ArkClass[]>): ArkField[] {
460        const fields: ArkField[] = [];
461        let classes: ArkClass[] = [];
462        if (this.declaringArkNamespace) {
463            classes = classMap.get(this.declaringArkNamespace.getNamespaceSignature())!;
464        } else {
465            classes = classMap.get(this.declaringArkFile.getFileSignature())!;
466        }
467        for (const arkClass of classes) {
468            for (const field of arkClass.getFields()) {
469                if (field.isStatic()) {
470                    fields.push(field);
471                }
472            }
473        }
474        return fields;
475    }
476
477    public getGlobalVariable(globalMap: Map<FileSignature | NamespaceSignature, Local[]>): Local[] {
478        if (this.declaringArkNamespace) {
479            return globalMap.get(this.declaringArkNamespace.getNamespaceSignature())!;
480        }
481        return globalMap.get(this.declaringArkFile.getFileSignature())!;
482    }
483
484    public getAnonymousMethodNumber(): number {
485        return this.anonymousMethodNumber++;
486    }
487
488    public getIndexSignatureNumber(): number {
489        return this.indexSignatureNumber++;
490    }
491
492    getExportType(): ExportType {
493        return ExportType.CLASS;
494    }
495
496    public getInstanceInitMethod(): ArkMethod {
497        return this.instanceInitMethod;
498    }
499
500    public getStaticInitMethod(): ArkMethod {
501        return this.staticInitMethod;
502    }
503
504    public setInstanceInitMethod(arkMethod: ArkMethod): void {
505        this.instanceInitMethod = arkMethod;
506    }
507
508    public setStaticInitMethod(arkMethod: ArkMethod): void {
509        this.staticInitMethod = arkMethod;
510    }
511
512    public removeField(field: ArkField): boolean {
513        if (field.isStatic()) {
514            return this.staticFields.delete(field.getName());
515        }
516        return this.fields.delete(field.getName());
517    }
518
519    public removeMethod(method: ArkMethod): boolean {
520        let rtn: boolean = false;
521        if (method.isStatic()) {
522            rtn = this.staticMethods.delete(method.getName());
523        } else {
524            rtn = this.methods.delete(method.getName());
525        }
526        rtn &&= this.getDeclaringArkFile().getScene().removeMethod(method);
527        return rtn;
528    }
529
530    public validate(): ArkError {
531        return this.validateFields(['declaringArkFile', 'category', 'classSignature']);
532    }
533}
534