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 { ModuleScene, Scene } from '../../Scene'; 17import { ArkExport, ExportInfo } from './ArkExport'; 18import { ImportInfo } from './ArkImport'; 19import { ArkClass } from './ArkClass'; 20import { ArkNamespace } from './ArkNamespace'; 21import { AliasClassSignature, ClassSignature, FileSignature, NamespaceSignature } from './ArkSignature'; 22import { ALL } from '../common/TSConst'; 23import { NAME_DELIMITER } from '../common/Const'; 24 25export const notStmtOrExprKind = [ 26 'ModuleDeclaration', 27 'ClassDeclaration', 28 'InterfaceDeclaration', 29 'EnumDeclaration', 30 'ExportDeclaration', 31 'ExportAssignment', 32 'MethodDeclaration', 33 'Constructor', 34 'FunctionDeclaration', 35 'GetAccessor', 36 'SetAccessor', 37 'ArrowFunction', 38 'FunctionExpression', 39 'MethodSignature', 40 'ConstructSignature', 41 'CallSignature', 42]; 43 44export enum Language { 45 TYPESCRIPT = 0, 46 ARKTS1_1 = 1, 47 ARKTS1_2 = 2, 48 JAVASCRIPT = 3, 49 UNKNOWN = -1, 50} 51 52/** 53 * @category core/model 54 */ 55export class ArkFile { 56 private language: Language; 57 private absoluteFilePath: string = ''; 58 private projectDir: string = ''; 59 private code: string = ''; 60 61 private defaultClass!: ArkClass; 62 63 // name to model 64 private namespaces: Map<string, ArkNamespace> = new Map<string, ArkNamespace>(); // don't contain nested namespaces 65 private classes: Map<string, ArkClass> = new Map<string, ArkClass>(); // don't contain class in namespace 66 67 private importInfoMap: Map<string, ImportInfo> = new Map<string, ImportInfo>(); 68 private exportInfoMap: Map<string, ExportInfo> = new Map<string, ExportInfo>(); 69 70 private scene!: Scene; 71 private moduleScene?: ModuleScene; 72 73 private fileSignature: FileSignature = FileSignature.DEFAULT; 74 75 private ohPackageJson5Path: string[] = []; 76 77 private anonymousClassNumber: number = 0; 78 79 constructor(language: Language) { 80 this.language = language; 81 } 82 83 /** 84 * Returns the program language of the file. 85 */ 86 public getLanguage(): Language { 87 return this.language; 88 } 89 90 public setLanguage(language: Language): void { 91 this.language = language; 92 } 93 94 /** 95 * Returns the **string** name of the file, which also acts as the file's relative path. 96 * @returns The file's name (also means its relative path). 97 */ 98 public getName(): string { 99 return this.fileSignature.getFileName(); 100 } 101 102 public setScene(scene: Scene): void { 103 this.scene = scene; 104 } 105 106 /** 107 * Returns the scene (i.e., {@link Scene}) built for the project. The {@link Scene} is the core class of ArkAnalyzer, 108 * through which users can access all the information of the analyzed code (project), 109 * including file list, class list, method list, property list, etc. 110 * @returns The scene of the file. 111 */ 112 public getScene(): Scene { 113 return this.scene; 114 } 115 116 public getModuleScene(): ModuleScene | undefined { 117 return this.moduleScene; 118 } 119 120 public setModuleScene(moduleScene: ModuleScene): void { 121 this.moduleScene = moduleScene; 122 } 123 124 public setProjectDir(projectDir: string): void { 125 this.projectDir = projectDir; 126 } 127 128 public getProjectDir(): string { 129 return this.projectDir; 130 } 131 132 /** 133 * Get a file path. 134 * @returns The absolute file path. 135 * @example 136 * 1. Read source code based on file path. 137 138 ```typescript 139 let str = fs.readFileSync(arkFile.getFilePath(), 'utf8'); 140 ``` 141 */ 142 public getFilePath(): string { 143 return this.absoluteFilePath; 144 } 145 146 public setFilePath(absoluteFilePath: string): void { 147 this.absoluteFilePath = absoluteFilePath; 148 } 149 150 public setCode(code: string): void { 151 this.code = code; 152 } 153 154 /** 155 * Returns the codes of file as a **string.** 156 * @returns the codes of file. 157 */ 158 public getCode(): string { 159 return this.code; 160 } 161 162 public addArkClass(arkClass: ArkClass, originName?: string): void { 163 const name = originName ?? arkClass.getName(); 164 this.classes.set(name, arkClass); 165 if (!originName && !arkClass.isAnonymousClass()) { 166 const index = name.indexOf(NAME_DELIMITER); 167 if (index > 0) { 168 const originName = name.substring(0, index); 169 this.addArkClass(arkClass, originName); 170 } 171 } 172 } 173 174 public getDefaultClass(): ArkClass { 175 return this.defaultClass; 176 } 177 178 public setDefaultClass(defaultClass: ArkClass): void { 179 this.defaultClass = defaultClass; 180 } 181 182 public getNamespace(namespaceSignature: NamespaceSignature): ArkNamespace | null { 183 const namespaceName = namespaceSignature.getNamespaceName(); 184 return this.getNamespaceWithName(namespaceName); 185 } 186 187 public getNamespaceWithName(namespaceName: string): ArkNamespace | null { 188 return this.namespaces.get(namespaceName) || null; 189 } 190 191 public getNamespaces(): ArkNamespace[] { 192 return Array.from(this.namespaces.values()); 193 } 194 195 /** 196 * Returns the class based on its class signature. If the class could not be found, **null** will be returned. 197 * @param classSignature - the class signature. 198 * @returns A class. If there is no class, the return will be a **null**. 199 */ 200 public getClass(classSignature: ClassSignature): ArkClass | null { 201 const className = classSignature instanceof AliasClassSignature ? classSignature.getOriginName() : classSignature.getClassName(); 202 return this.getClassWithName(className); 203 } 204 205 public getClassWithName(Class: string): ArkClass | null { 206 return this.classes.get(Class) || null; 207 } 208 209 public getClasses(): ArkClass[] { 210 return Array.from(new Set(this.classes.values())); 211 } 212 213 public addNamespace(namespace: ArkNamespace): void { 214 this.namespaces.set(namespace.getName(), namespace); 215 } 216 217 /** 218 * Returns an **array** of import information. 219 * The import information includes: clause's name, type, modifiers, location where it is imported from, etc. 220 * @returns An **array** of import information. 221 */ 222 public getImportInfos(): ImportInfo[] { 223 return Array.from(this.importInfoMap.values()); 224 } 225 226 public getImportInfoBy(name: string): ImportInfo | undefined { 227 return this.importInfoMap.get(name); 228 } 229 230 public addImportInfo(importInfo: ImportInfo): void { 231 this.importInfoMap.set(importInfo.getImportClauseName(), importInfo); 232 } 233 234 public removeImportInfo(importInfo: ImportInfo): boolean { 235 return this.importInfoMap.delete(importInfo.getImportClauseName()); 236 } 237 238 public removeNamespace(namespace: ArkNamespace): boolean { 239 let rtn = this.namespaces.delete(namespace.getName()); 240 rtn &&= this.getScene().removeNamespace(namespace); 241 return rtn; 242 } 243 244 public removeArkClass(arkClass: ArkClass): boolean { 245 let rtn = this.classes.delete(arkClass.getName()); 246 rtn &&= this.getScene().removeClass(arkClass); 247 return rtn; 248 } 249 250 public getExportInfos(): ExportInfo[] { 251 const exportInfos: ExportInfo[] = []; 252 this.exportInfoMap.forEach((value, key) => { 253 if (key !== ALL || value.getFrom()) { 254 exportInfos.push(value); 255 } 256 }); 257 return exportInfos; 258 } 259 260 /** 261 * Find out the {@link ExportInfo} of this {@link ArkFile} by the given export name. 262 * It returns an {@link ExportInfo} or 'undefined' if it failed to find. 263 * @param name 264 * @returns 265 * @example 266 ```typescript 267 // abc.ts ArkFile 268 export class A { 269 ... 270 } 271 272 export namespace B { 273 export namespace C { 274 export class D {} 275 } 276 } 277 278 // xyz.ts call getExportInfoBy 279 let arkFile = scene.getFile(fileSignature); 280 281 // a is the export class A defined in abc.ts 282 let a = arkFile.getExportInfoBy('A'); 283 284 // b is the export class D within namespace C defined in abc.ts 285 let b = arkFile.getExportInfoBy('B.C.D'); 286 ``` 287 */ 288 public getExportInfoBy(name: string): ExportInfo | undefined { 289 const separator = '.'; 290 const names = name.split(separator); 291 if (names.length === 1) { 292 return this.exportInfoMap.get(names[0]); 293 } 294 295 let index = 0; 296 let currExportInfo = this.exportInfoMap.get(names[index]); 297 if (currExportInfo === undefined) { 298 return undefined; 299 } 300 301 for (let i = 1; i < names.length; i++) { 302 const arkExport: ArkExport | null | undefined = currExportInfo.getArkExport(); 303 if (arkExport && arkExport instanceof ArkNamespace) { 304 currExportInfo = arkExport.getExportInfoBy(names[i]); 305 if (currExportInfo === undefined) { 306 return undefined; 307 } 308 } 309 } 310 return currExportInfo; 311 } 312 313 public addExportInfo(exportInfo: ExportInfo, key?: string): void { 314 this.exportInfoMap.set(key ?? exportInfo.getExportClauseName(), exportInfo); 315 } 316 317 public removeExportInfo(exportInfo: ExportInfo, key?: string): void { 318 if (key) { 319 this.exportInfoMap.delete(key); 320 return; 321 } 322 this.exportInfoMap.delete(exportInfo.getExportClauseName()); 323 } 324 325 public getProjectName(): string { 326 return this.fileSignature.getProjectName(); 327 } 328 329 public getModuleName(): string | undefined { 330 return this.moduleScene?.getModuleName(); 331 } 332 333 public setOhPackageJson5Path(ohPackageJson5Path: string[]): void { 334 this.ohPackageJson5Path = ohPackageJson5Path; 335 } 336 337 public getOhPackageJson5Path(): string[] { 338 return this.ohPackageJson5Path; 339 } 340 341 /** 342 * Returns the file signature of this file. A file signature consists of project's name and file's name. 343 * @returns The file signature of this file. 344 */ 345 public getFileSignature(): FileSignature { 346 return this.fileSignature; 347 } 348 349 public setFileSignature(fileSignature: FileSignature): void { 350 this.fileSignature = fileSignature; 351 } 352 353 public getAllNamespacesUnderThisFile(): ArkNamespace[] { 354 let namespaces: ArkNamespace[] = []; 355 namespaces.push(...this.namespaces.values()); 356 this.namespaces.forEach(ns => { 357 namespaces.push(...ns.getAllNamespacesUnderThisNamespace()); 358 }); 359 return namespaces; 360 } 361 362 public getAnonymousClassNumber(): number { 363 return this.anonymousClassNumber++; 364 } 365} 366