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