• 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 path from 'path';
17import { transfer2UnixPath } from '../../utils/pathTransfer';
18import { ClassType, Type } from '../base/Type';
19import { MethodParameter } from './builder/ArkMethodBuilder';
20import {
21    ANONYMOUS_CLASS_PREFIX,
22    LEXICAL_ENV_NAME_PREFIX,
23    NAME_DELIMITER,
24    UNKNOWN_CLASS_NAME,
25    UNKNOWN_FILE_NAME,
26    UNKNOWN_NAMESPACE_NAME,
27    UNKNOWN_PROJECT_NAME,
28} from '../common/Const';
29import { CryptoUtils } from '../../utils/crypto_utils';
30
31export type Signature = FileSignature | NamespaceSignature | ClassSignature | MethodSignature | FieldSignature | LocalSignature | AliasTypeSignature;
32
33export interface ArkSignature {
34    getSignature(): Signature;
35}
36
37/**
38 * @category core/model
39 */
40export class FileSignature {
41    private projectName: string;
42    private fileName: string;
43    private hashcode: number;
44
45    public static readonly DEFAULT: FileSignature = new FileSignature(UNKNOWN_PROJECT_NAME, UNKNOWN_FILE_NAME);
46
47    constructor(projectName: string, fileName: string) {
48        this.projectName = projectName;
49        this.fileName = transfer2UnixPath(fileName);
50        this.hashcode = CryptoUtils.hashcode(this.toString());
51    }
52
53    public getProjectName(): string {
54        return this.projectName;
55    }
56
57    public getFileName(): string {
58        return this.fileName;
59    }
60
61    public toString(): string {
62        return `@${this.projectName}/${this.fileName}: `;
63    }
64
65    public toMapKey(): string {
66        return `${this.hashcode}${path.basename(this.fileName)}`;
67    }
68}
69
70export class NamespaceSignature {
71    private namespaceName: string;
72    private declaringFileSignature: FileSignature;
73    private declaringNamespaceSignature: NamespaceSignature | null;
74
75    public static readonly DEFAULT: NamespaceSignature = new NamespaceSignature(UNKNOWN_NAMESPACE_NAME, FileSignature.DEFAULT, null);
76
77    constructor(namespaceName: string, declaringFileSignature: FileSignature, declaringNamespaceSignature: NamespaceSignature | null = null) {
78        this.namespaceName = namespaceName;
79        this.declaringFileSignature = declaringFileSignature;
80        this.declaringNamespaceSignature = declaringNamespaceSignature;
81    }
82
83    public getNamespaceName(): string {
84        return this.namespaceName;
85    }
86
87    public getDeclaringFileSignature(): FileSignature {
88        return this.declaringFileSignature;
89    }
90
91    public getDeclaringNamespaceSignature(): NamespaceSignature | null {
92        return this.declaringNamespaceSignature;
93    }
94
95    public toString(): string {
96        if (this.declaringNamespaceSignature) {
97            return this.declaringNamespaceSignature.toString() + '.' + this.namespaceName;
98        } else {
99            return this.declaringFileSignature.toString() + this.namespaceName;
100        }
101    }
102
103    public toMapKey(): string {
104        if (this.declaringNamespaceSignature) {
105            return this.declaringNamespaceSignature.toMapKey() + '.' + this.namespaceName;
106        } else {
107            return this.declaringFileSignature.toMapKey() + this.namespaceName;
108        }
109    }
110}
111
112export class ClassSignature {
113    private declaringFileSignature: FileSignature;
114    private declaringNamespaceSignature: NamespaceSignature | null;
115    private className: string;
116
117    public static readonly DEFAULT: ClassSignature = new ClassSignature(UNKNOWN_CLASS_NAME, FileSignature.DEFAULT, null);
118
119    constructor(className: string, declaringFileSignature: FileSignature, declaringNamespaceSignature: NamespaceSignature | null = null) {
120        this.className = className;
121        this.declaringFileSignature = declaringFileSignature;
122        this.declaringNamespaceSignature = declaringNamespaceSignature;
123    }
124
125    /**
126     * Returns the declaring file signature.
127     * @returns The declaring file signature.
128     */
129    public getDeclaringFileSignature(): FileSignature {
130        return this.declaringFileSignature;
131    }
132
133    /**
134     * Get the declaring namespace's signature.
135     * @returns the declaring namespace's signature.
136     */
137    public getDeclaringNamespaceSignature(): NamespaceSignature | null {
138        return this.declaringNamespaceSignature;
139    }
140
141    /**
142     * Get the **string** name of class from the the class signature. The default value is `""`.
143     * @returns The name of this class.
144     */
145    public getClassName(): string {
146        return this.className;
147    }
148
149    /**
150     *
151     * @returns The name of the declare class.
152     */
153    public getDeclaringClassName(): string {
154        if (this.className.startsWith(ANONYMOUS_CLASS_PREFIX)) {
155            let temp = this.className;
156            do {
157                temp = temp.substring(temp.indexOf(NAME_DELIMITER) + 1, temp.lastIndexOf('.'));
158            } while (temp.startsWith(ANONYMOUS_CLASS_PREFIX));
159            return temp;
160        }
161        return this.className;
162    }
163
164    public setClassName(className: string): void {
165        this.className = className;
166    }
167
168    public getType(): ClassType {
169        return new ClassType(this);
170    }
171
172    public toString(): string {
173        if (this.declaringNamespaceSignature) {
174            return this.declaringNamespaceSignature.toString() + '.' + this.className;
175        } else {
176            return this.declaringFileSignature.toString() + this.className;
177        }
178    }
179
180    public toMapKey(): string {
181        if (this.declaringNamespaceSignature) {
182            return this.declaringNamespaceSignature.toMapKey() + '.' + this.className;
183        } else {
184            return this.declaringFileSignature.toMapKey() + this.className;
185        }
186    }
187}
188
189/**
190 * `AliasClassSignature` is used to extend `ClassSignature`, preserving the actual name used during invocation.
191 */
192export class AliasClassSignature extends ClassSignature {
193    private readonly aliasName: string;
194
195    constructor(aliasName: string, signature: ClassSignature) {
196        super(signature.getClassName(), signature.getDeclaringFileSignature(), signature.getDeclaringNamespaceSignature());
197        this.aliasName = aliasName;
198    }
199
200    /**
201     * Returns the name used in the code.
202     */
203    public getClassName(): string {
204        return this.aliasName;
205    }
206
207    /**
208     * Return the original name of declared class
209     */
210    public getOriginName(): string {
211        return super.getClassName();
212    }
213}
214
215export type BaseSignature = ClassSignature | NamespaceSignature;
216
217export class FieldSignature {
218    private declaringSignature: BaseSignature;
219    private fieldName: string;
220    private type: Type;
221    private staticFlag: boolean;
222
223    constructor(fieldName: string, declaringSignature: BaseSignature, type: Type, staticFlag: boolean = false) {
224        this.fieldName = fieldName;
225        this.declaringSignature = declaringSignature;
226        this.type = type;
227        this.staticFlag = staticFlag;
228    }
229
230    public getDeclaringSignature(): BaseSignature {
231        return this.declaringSignature;
232    }
233
234    public getBaseName(): string {
235        return this.declaringSignature instanceof ClassSignature ? this.declaringSignature.getClassName() : this.declaringSignature.getNamespaceName();
236    }
237
238    public getFieldName(): string {
239        return this.fieldName;
240    }
241
242    public getType(): Type {
243        return this.type;
244    }
245
246    public isStatic(): boolean {
247        return this.staticFlag;
248    }
249
250    // temp for being compatible with existing type inference
251    public setType(type: Type): void {
252        this.type = type;
253    }
254
255    // temp for being compatible with existing type inference
256    public setStaticFlag(flag: boolean): void {
257        this.staticFlag = flag;
258    }
259
260    public toString(): string {
261        let tmpSig = this.fieldName;
262        if (this.isStatic()) {
263            tmpSig = '[static]' + tmpSig;
264        }
265        return this.getDeclaringSignature().toString() + '.' + tmpSig;
266    }
267}
268
269export class MethodSubSignature {
270    private methodName: string;
271    private parameters: MethodParameter[];
272    private returnType: Type;
273    private staticFlag: boolean;
274
275    constructor(methodName: string, parameters: MethodParameter[], returnType: Type, staticFlag: boolean = false) {
276        this.methodName = methodName;
277        this.parameters = parameters;
278        this.returnType = returnType;
279        this.staticFlag = staticFlag;
280    }
281
282    public getMethodName(): string {
283        return this.methodName;
284    }
285
286    public getParameters(): MethodParameter[] {
287        return this.parameters;
288    }
289
290    public getParameterTypes(): Type[] {
291        const parameterTypes: Type[] = [];
292        this.parameters.forEach(parameter => {
293            parameterTypes.push(parameter.getType());
294        });
295        return parameterTypes;
296    }
297
298    public getReturnType(): Type {
299        return this.returnType;
300    }
301
302    public setReturnType(returnType: Type): void {
303        this.returnType = returnType;
304    }
305
306    public isStatic(): boolean {
307        return this.staticFlag;
308    }
309
310    public toString(ptrName?: string): string {
311        let paraStr = '';
312        this.getParameterTypes().forEach(parameterType => {
313            paraStr += parameterType.toString() + ', ';
314        });
315        paraStr = paraStr.replace(/, $/, '');
316        let tmpSig = `${ptrName ?? this.getMethodName()}(${paraStr})`;
317        if (this.isStatic()) {
318            tmpSig = '[static]' + tmpSig;
319        }
320        return tmpSig;
321    }
322}
323
324/**
325 * @category core/model
326 */
327export class MethodSignature {
328    private declaringClassSignature: ClassSignature;
329    private methodSubSignature: MethodSubSignature;
330
331    constructor(declaringClassSignature: ClassSignature, methodSubSignature: MethodSubSignature) {
332        this.declaringClassSignature = declaringClassSignature;
333        this.methodSubSignature = methodSubSignature;
334    }
335
336    /**
337     * Return the declaring class signature.
338     * A {@link ClassSignature} includes:
339     * - File Signature: including the **string** names of the project and file, respectively.
340     * The default value of project's name is "%unk" and the default value of file's name is "%unk".
341     * - Namespace Signature | **null**:  it may be a namespace signature or **null**.
342     * A namespace signature can indicate its **string** name of namespace and its file signature.
343     * - Class Name: the **string** name of this class.
344     * @returns The declaring class signature.
345     * @example
346     * 1. get class signature from ArkMethod.
347
348     ```typescript
349     let methodSignature = expr.getMethodSignature();
350     let name = methodSignature.getDeclaringClassSignature().getClassName();
351     ```
352     *
353     */
354    public getDeclaringClassSignature(): ClassSignature {
355        return this.declaringClassSignature;
356    }
357
358    /**
359     * Returns the sub-signature of this method signature.
360     * The sub-signature is part of the method signature, which is used to
361     * identify the name of the method, its parameters and the return value type.
362     * @returns The sub-signature of this method signature.
363     */
364    public getMethodSubSignature(): MethodSubSignature {
365        return this.methodSubSignature;
366    }
367
368    public getType(): Type {
369        return this.methodSubSignature.getReturnType();
370    }
371
372    public toString(ptrName?: string): string {
373        return this.declaringClassSignature.toString() + '.' + this.methodSubSignature.toString(ptrName);
374    }
375
376    public toMapKey(): string {
377        return this.declaringClassSignature.toMapKey() + '.' + this.methodSubSignature.toString();
378    }
379
380    public isMatch(signature: MethodSignature): boolean {
381        return this.toString() === signature.toString() && this.getType().toString() === signature.getType().toString();
382    }
383
384    public getParamLength(): number {
385        return this.methodSubSignature.getParameters().filter(p => !p.getName().startsWith(LEXICAL_ENV_NAME_PREFIX)).length;
386    }
387}
388
389export class LocalSignature {
390    private name: string;
391    private declaringMethodSignature: MethodSignature;
392
393    constructor(name: string, declaringMethodSignature: MethodSignature) {
394        this.name = name;
395        this.declaringMethodSignature = declaringMethodSignature;
396    }
397
398    public getName(): string {
399        return this.name;
400    }
401
402    public getDeclaringMethodSignature(): MethodSignature {
403        return this.declaringMethodSignature;
404    }
405
406    public toString(): string {
407        return this.declaringMethodSignature.toString() + '#' + this.name;
408    }
409}
410
411export class AliasTypeSignature {
412    private name: string;
413    private declaringMethodSignature: MethodSignature;
414
415    constructor(name: string, declaringMethodSignature: MethodSignature) {
416        this.name = name;
417        this.declaringMethodSignature = declaringMethodSignature;
418    }
419
420    public getName(): string {
421        return this.name;
422    }
423
424    public getDeclaringMethodSignature(): MethodSignature {
425        return this.declaringMethodSignature;
426    }
427
428    public toString(): string {
429        return this.declaringMethodSignature.toString() + '#' + this.name;
430    }
431}
432
433//TODO, reconstruct
434export function fieldSignatureCompare(leftSig: FieldSignature, rightSig: FieldSignature): boolean {
435    if (leftSig.getDeclaringSignature().toString() === rightSig.getDeclaringSignature().toString() && leftSig.getFieldName() === rightSig.getFieldName()) {
436        return true;
437    }
438    return false;
439}
440
441export function methodSignatureCompare(leftSig: MethodSignature, rightSig: MethodSignature): boolean {
442    if (
443        classSignatureCompare(leftSig.getDeclaringClassSignature(), rightSig.getDeclaringClassSignature()) &&
444        methodSubSignatureCompare(leftSig.getMethodSubSignature(), rightSig.getMethodSubSignature())
445    ) {
446        return true;
447    }
448    return false;
449}
450
451export function methodSubSignatureCompare(leftSig: MethodSubSignature, rightSig: MethodSubSignature): boolean {
452    if (
453        leftSig.getMethodName() === rightSig.getMethodName() &&
454        arrayCompare(leftSig.getParameterTypes(), rightSig.getParameterTypes()) &&
455        leftSig.getReturnType() === rightSig.getReturnType()
456    ) {
457        return true;
458    }
459    return false;
460}
461
462export function classSignatureCompare(leftSig: ClassSignature, rightSig: ClassSignature): boolean {
463    if (fileSignatureCompare(leftSig.getDeclaringFileSignature(), rightSig.getDeclaringFileSignature()) && leftSig.getClassName() === rightSig.getClassName()) {
464        return true;
465    }
466    return false;
467}
468
469export function fileSignatureCompare(leftSig: FileSignature, rightSig: FileSignature): boolean {
470    if (leftSig.getFileName() === rightSig.getFileName() && leftSig.getProjectName() === rightSig.getProjectName()) {
471        return true;
472    }
473    return false;
474}
475
476function arrayCompare(leftArray: any[], rightArray: any[]): boolean {
477    if (leftArray.length !== rightArray.length) {
478        return false;
479    }
480    for (let i = 0; i < leftArray.length; i++) {
481        if (leftArray[i] !== rightArray[i]) {
482            return false;
483        }
484    }
485    return true;
486}
487
488export function genSignature4ImportClause(arkFileName: string, importClauseName: string): string {
489    return `<${arkFileName}>.<${importClauseName}>`;
490}
491