1/* 2 * Copyright (c) 2023-2024 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 { 17 createPrinter, 18 createTextWriter, 19 transform, 20 createObfTextSingleLineWriter, 21} from 'typescript'; 22 23import type { 24 CompilerOptions, 25 EmitTextWriter, 26 Node, 27 Printer, 28 PrinterOptions, 29 RawSourceMap, 30 SourceFile, 31 SourceMapGenerator, 32 TransformationResult, 33 TransformerFactory, 34} from 'typescript'; 35 36import path from 'path'; 37 38import { AtKeepCollections, LocalVariableCollections, PropCollections } from './utils/CommonCollections'; 39import type { IOptions } from './configs/IOptions'; 40import { FileUtils } from './utils/FileUtils'; 41import { TransformerManager } from './transformers/TransformerManager'; 42import { getSourceMapGenerator } from './utils/SourceMapUtil'; 43import { 44 decodeSourcemap, 45 ExistingDecodedSourceMap, 46 Source, 47 SourceMapLink, 48 SourceMapSegmentObj, 49 mergeSourceMap 50} from './utils/SourceMapMergingUtil'; 51import { 52 deleteLineInfoForNameString, 53 getMapFromJson, 54 IDENTIFIER_CACHE, 55 MEM_METHOD_CACHE 56} from './utils/NameCacheUtil'; 57import { ListUtil } from './utils/ListUtil'; 58import { needReadApiInfo, readProjectPropertiesByCollectedPaths } from './common/ApiReader'; 59import type { ReseverdSetForArkguard } from './common/ApiReader'; 60import { ApiExtractor } from './common/ApiExtractor'; 61import esInfo from './configs/preset/es_reserved_properties.json'; 62import optimizeEsInfo from './configs/preset/es_reserved_properties_optimized.json'; 63import { 64 EventList, 65 TimeSumPrinter, 66 TimeTracker, 67 endSingleFileEvent, 68 initPerformancePrinter, 69 startSingleFileEvent, 70} from './utils/PrinterUtils'; 71import { ObConfigResolver } from './initialization/ConfigResolver'; 72 73export { 74 EventList, 75 TimeSumPrinter, 76 blockPrinter, 77 endFilesEvent, 78 endSingleFileEvent, 79 printTimeSumData, 80 printTimeSumInfo, 81 startFilesEvent, 82 startSingleFileEvent, 83} from './utils/PrinterUtils'; 84import { Extension, type ProjectInfo, type FilePathObj } from './common/type'; 85export { type HvigorErrorInfo } from './common/type'; 86export { FileUtils } from './utils/FileUtils'; 87export { MemoryUtils } from './utils/MemoryUtils'; 88import { TypeUtils } from './utils/TypeUtils'; 89import { handleReservedConfig } from './utils/TransformUtil'; 90import { UnobfuscationCollections } from './utils/CommonCollections'; 91import { FilePathManager, FileContentManager, initProjectWhiteListManager } from './utils/ProjectCollections'; 92import { clearHistoryUnobfuscatedMap, historyAllUnobfuscatedNamesMap } from './initialization/Initializer'; 93import { MemoryDottingDefine } from './utils/MemoryDottingDefine'; 94import { nodeSymbolMap } from './utils/ScopeAnalyzer'; 95import { clearUnobfuscationNamesObj } from './initialization/CommonObject'; 96export { UnobfuscationCollections } from './utils/CommonCollections'; 97export * as ProjectCollections from './utils/ProjectCollections'; 98export { separateUniversalReservedItem, containWildcards, wildcardTransformer } from './utils/TransformUtil'; 99export type { ReservedNameInfo } from './utils/TransformUtil'; 100export type { ReseverdSetForArkguard } from './common/ApiReader'; 101 102export { initObfuscationConfig } from './initialization/Initializer'; 103export { nameCacheMap, unobfuscationNamesObj } from './initialization/CommonObject'; 104export { 105 collectResevedFileNameInIDEConfig, // For running unit test. 106 enableObfuscatedFilePathConfig, 107 enableObfuscateFileName, 108 generateConsumerObConfigFile, 109 getRelativeSourcePath, 110 handleObfuscatedFilePath, 111 handleUniversalPathInObf, 112 mangleFilePath, 113 MergedConfig, 114 ObConfigResolver, 115 readNameCache, 116 clearNameCache, 117 writeObfuscationNameCache, 118 writeUnobfuscationContent 119} from './initialization/ConfigResolver'; 120export { 121 collectReservedNameForObf 122} from './utils/NodeUtils'; 123 124export const renameIdentifierModule = require('./transformers/rename/RenameIdentifierTransformer'); 125export const renameFileNameModule = require('./transformers/rename/RenameFileNameTransformer'); 126 127export { getMapFromJson, readProjectPropertiesByCollectedPaths, deleteLineInfoForNameString, ApiExtractor, PropCollections }; 128export let orignalFilePathForSearching: string | undefined; 129export let cleanFileMangledNames: boolean = false; 130export interface PerformancePrinter { 131 filesPrinter?: TimeTracker; 132 singleFilePrinter?: TimeTracker; 133 timeSumPrinter?: TimeSumPrinter; 134} 135export let performancePrinter: PerformancePrinter = { 136 filesPrinter: new TimeTracker(), 137 singleFilePrinter: new TimeTracker(), 138 timeSumPrinter: new TimeSumPrinter(), 139}; 140 141// When the module is compiled, call this function to clear global collections. 142export function clearGlobalCaches(): void { 143 PropCollections.clearPropsCollections(); 144 UnobfuscationCollections.clear(); 145 LocalVariableCollections.clear(); 146 AtKeepCollections.clear(); 147 renameFileNameModule.clearCaches(); 148 clearUnobfuscationNamesObj(); 149 clearHistoryUnobfuscatedMap(); 150 ApiExtractor.mConstructorPropertySet.clear(); 151 ApiExtractor.mEnumMemberSet.clear(); 152} 153 154export type ObfuscationResultType = { 155 content: string; 156 sourceMap?: RawSourceMap; 157 nameCache?: { [k: string]: string | {} }; 158 filePath?: string; 159 unobfuscationNameMap?: Map<string, Set<string>>; 160}; 161 162export interface RecordInfo { 163 recordStage: string; 164 recordIndex: number; 165}; 166 167const JSON_TEXT_INDENT_LENGTH: number = 2; 168export class ArkObfuscator { 169 // Used only for testing 170 protected mWriteOriginalFile: boolean = false; 171 172 // A text writer of Printer 173 private mTextWriter: EmitTextWriter; 174 175 // Compiler Options for typescript,use to parse ast 176 private readonly mCompilerOptions: CompilerOptions; 177 178 // User custom obfuscation profiles. 179 protected mCustomProfiles: IOptions; 180 181 private mTransformers: TransformerFactory<Node>[]; 182 183 private static memoryDottingCallback: (stage: string) => RecordInfo; 184 185 private static memoryDottingStopCallback: (recordInfo: RecordInfo) => void; 186 187 static mProjectInfo: ProjectInfo | undefined; 188 189 // If isKeptCurrentFile is true, both identifier and property obfuscation are skipped. 190 static mIsKeptCurrentFile: boolean = false; 191 192 public isIncremental: boolean = false; 193 194 public shouldReObfuscate: boolean = false; 195 196 public filePathManager: FilePathManager | undefined; 197 198 public fileContentManager: FileContentManager | undefined; 199 200 public obfConfigResolver: ObConfigResolver; 201 202 public constructor() { 203 this.mCompilerOptions = {}; 204 this.mTransformers = []; 205 } 206 207 public getWriteOriginalFileForTest(): boolean { 208 return this.mWriteOriginalFile; 209 } 210 211 public setWriteOriginalFile(flag: boolean): void { 212 this.mWriteOriginalFile = flag; 213 } 214 215 // Pass the collected whitelists related to property obfuscation to Arkguard. 216 public addReservedSetForPropertyObf(properties: ReseverdSetForArkguard): void { 217 if (properties.structPropertySet && properties.structPropertySet.size > 0) { 218 for (let reservedProperty of properties.structPropertySet) { 219 UnobfuscationCollections.reservedStruct.add(reservedProperty); 220 } 221 } 222 223 if (properties.stringPropertySet && properties.stringPropertySet.size > 0) { 224 UnobfuscationCollections.reservedStrProp = properties.stringPropertySet; 225 } 226 227 if (properties.exportNameAndPropSet && properties.exportNameAndPropSet.size > 0) { 228 UnobfuscationCollections.reservedExportNameAndProp = properties.exportNameAndPropSet; 229 } 230 231 if (properties.enumPropertySet && properties.enumPropertySet.size > 0) { 232 for (let reservedEnum of properties.enumPropertySet) { 233 UnobfuscationCollections.reservedEnum.add(reservedEnum); 234 } 235 } 236 } 237 238 public addReservedSetForDefaultObf(properties: ReseverdSetForArkguard): void { 239 if (properties.exportNameSet && properties.exportNameSet.size > 0) { 240 UnobfuscationCollections.reservedExportName = properties.exportNameSet; 241 } 242 } 243 244 public setKeepSourceOfPaths(mKeepSourceOfPaths: Set<string>): void { 245 this.mCustomProfiles.mKeepFileSourceCode.mKeepSourceOfPaths = mKeepSourceOfPaths; 246 } 247 248 public handleTsHarComments(sourceFile: SourceFile, originalPath: string | undefined): void { 249 if (ArkObfuscator.projectInfo?.useTsHar && (originalPath?.endsWith(Extension.ETS) && !originalPath?.endsWith(Extension.DETS))) { 250 // @ts-ignore 251 sourceFile.writeTsHarComments = true; 252 } 253 } 254 255 public get customProfiles(): IOptions { 256 return this.mCustomProfiles; 257 } 258 259 public static get isKeptCurrentFile(): boolean { 260 return ArkObfuscator.mIsKeptCurrentFile; 261 } 262 263 public static set isKeptCurrentFile(isKeptFile: boolean) { 264 ArkObfuscator.mIsKeptCurrentFile = isKeptFile; 265 } 266 267 public static get projectInfo(): ProjectInfo { 268 return ArkObfuscator.mProjectInfo; 269 } 270 271 public static set projectInfo(projectInfo: ProjectInfo) { 272 ArkObfuscator.mProjectInfo = projectInfo; 273 } 274 275 public static recordStage(stage: string): RecordInfo | null { 276 if (ArkObfuscator.memoryDottingCallback) { 277 return ArkObfuscator.memoryDottingCallback(stage); 278 } 279 return null; 280 } 281 282 public static stopRecordStage(recordInfo: RecordInfo | null): void { 283 if (ArkObfuscator.memoryDottingStopCallback) { 284 if (recordInfo !== null) { 285 ArkObfuscator.memoryDottingStopCallback(recordInfo); 286 } 287 } 288 } 289 290 public isCurrentFileInKeepPathsForTest(customProfiles: IOptions, originalFilePath: string): boolean { 291 return this.isCurrentFileInKeepPaths(customProfiles, originalFilePath); 292 } 293 294 private isCurrentFileInKeepPaths(customProfiles: IOptions, originalFilePath: string): boolean { 295 const keepFileSourceCode = customProfiles.mKeepFileSourceCode; 296 if (keepFileSourceCode === undefined || keepFileSourceCode.mKeepSourceOfPaths.size === 0) { 297 return false; 298 } 299 const keepPaths: Set<string> = keepFileSourceCode.mKeepSourceOfPaths; 300 const originalPath = FileUtils.toUnixPath(originalFilePath); 301 return keepPaths.has(originalPath); 302 } 303 304 /** 305 * init ArkObfuscator according to user config 306 * should be called after constructor 307 */ 308 public init(config: IOptions | undefined, cachePath?: string): boolean { 309 if (!config) { 310 console.error('obfuscation config file is not found and no given config.'); 311 return false; 312 } 313 314 handleReservedConfig(config, 'mRenameFileName', 'mReservedFileNames', 'mUniversalReservedFileNames'); 315 handleReservedConfig(config, 'mRemoveDeclarationComments', 'mReservedComments', 'mUniversalReservedComments', 'mEnable'); 316 this.mCustomProfiles = config; 317 318 if (this.mCustomProfiles.mCompact) { 319 this.mTextWriter = createObfTextSingleLineWriter(); 320 } else { 321 this.mTextWriter = createTextWriter('\n'); 322 } 323 324 if (this.mCustomProfiles.mEnableSourceMap) { 325 this.mCompilerOptions.sourceMap = true; 326 } 327 328 const enableTopLevel: boolean = this.mCustomProfiles.mNameObfuscation?.mTopLevel; 329 const exportObfuscation: boolean = this.mCustomProfiles.mExportObfuscation; 330 const propertyObfuscation: boolean = this.mCustomProfiles.mNameObfuscation?.mRenameProperties; 331 /** 332 * clean mangledNames in case skip name check when generating names 333 */ 334 cleanFileMangledNames = enableTopLevel && !exportObfuscation && !propertyObfuscation; 335 336 // load transformers 337 this.mTransformers = new TransformerManager(this.mCustomProfiles).getTransformers(); 338 339 initPerformancePrinter(this.mCustomProfiles); 340 341 if (needReadApiInfo(this.mCustomProfiles)) { 342 // if -enable-property-obfuscation or -enable-export-obfuscation, collect language reserved keywords. 343 let languageSet: Set<string> = new Set(); 344 let presetLanguageWhitelist = this.mCustomProfiles.mStripLanguageDefaultWhitelist ? optimizeEsInfo : esInfo; 345 for (const key of Object.keys(presetLanguageWhitelist)) { 346 const valueArray = presetLanguageWhitelist[key]; 347 valueArray.forEach((element: string) => { 348 languageSet.add(element); 349 }); 350 } 351 UnobfuscationCollections.reservedLangForProperty = languageSet; 352 } 353 354 if (cachePath) { 355 this.initIncrementalCache(cachePath, !!this.mCustomProfiles.mNameObfuscation.mEnableAtKeep); 356 } 357 358 return true; 359 } 360 361 public static setMemoryDottingCallBack(memoryDottingCallback: (stage: string) => RecordInfo, 362 memoryDottingStopCallback: (recordInfo: RecordInfo) => void): void { 363 if (memoryDottingCallback) { 364 ArkObfuscator.memoryDottingCallback = memoryDottingCallback; 365 } 366 if (memoryDottingStopCallback) { 367 ArkObfuscator.memoryDottingStopCallback = memoryDottingStopCallback; 368 } 369 } 370 371 public static clearMemoryDottingCallBack(): void { 372 ArkObfuscator.memoryDottingCallback = undefined; 373 ArkObfuscator.memoryDottingStopCallback = undefined; 374 } 375 376 /** 377 * Init incremental cache according to cachePath 378 */ 379 private initIncrementalCache(cachePath: string, enableAtKeep: boolean): void { 380 this.filePathManager = new FilePathManager(cachePath); 381 382 this.isIncremental = this.filePathManager.isIncremental(); 383 384 this.fileContentManager = new FileContentManager(cachePath, this.isIncremental); 385 386 initProjectWhiteListManager(cachePath, this.isIncremental, enableAtKeep); 387 } 388 389 /** 390 * A Printer to output obfuscated codes. 391 */ 392 public createObfsPrinter(isDeclarationFile: boolean): Printer { 393 // set print options 394 let printerOptions: PrinterOptions = {}; 395 let removeOption = this.mCustomProfiles.mRemoveDeclarationComments; 396 let hasReservedList = removeOption?.mReservedComments?.length || removeOption?.mUniversalReservedComments?.length; 397 let keepDeclarationComments = hasReservedList || !removeOption?.mEnable; 398 399 if (isDeclarationFile && keepDeclarationComments) { 400 printerOptions.removeComments = false; 401 } 402 if ((!isDeclarationFile && this.mCustomProfiles.mRemoveComments) || (isDeclarationFile && !keepDeclarationComments)) { 403 printerOptions.removeComments = true; 404 } 405 406 return createPrinter(printerOptions); 407 } 408 409 protected isObfsIgnoreFile(fileName: string): boolean { 410 let suffix: string = FileUtils.getFileExtension(fileName); 411 412 return suffix !== 'js' && suffix !== 'ts' && suffix !== 'ets'; 413 } 414 415 private convertLineBasedOnSourceMap(targetCache: string, sourceMapLink?: SourceMapLink): Map<string, string> { 416 let originalCache: Map<string, string> = renameIdentifierModule.nameCache.get(targetCache); 417 let updatedCache: Map<string, string> = new Map<string, string>(); 418 for (const [key, value] of originalCache) { 419 if (!key.includes(':')) { 420 // No need to save line info for identifier which is not function-like, i.e. key without ':' here. 421 updatedCache[key] = value; 422 continue; 423 } 424 const [scopeName, oldStartLine, oldStartColumn, oldEndLine, oldEndColumn] = key.split(':'); 425 let newKey: string = key; 426 if (!sourceMapLink) { 427 // In Arkguard, we save line info of source code, so do not need to use sourcemap mapping. 428 newKey = `${scopeName}:${oldStartLine}:${oldEndLine}`; 429 updatedCache[newKey] = value; 430 continue; 431 } 432 const startPosition: SourceMapSegmentObj | null = sourceMapLink.traceSegment( 433 // 1: The line number in originalCache starts from 1 while in source map starts from 0. 434 Number(oldStartLine) - 1, Number(oldStartColumn) - 1, ''); // Minus 1 to get the correct original position. 435 if (!startPosition) { 436 // Do not save methods that do not exist in the source code, e.g. 'build' in ArkUI. 437 continue; 438 } 439 const endPosition: SourceMapSegmentObj | null = sourceMapLink.traceSegment( 440 Number(oldEndLine) - 1, Number(oldEndColumn) - 1, ''); // 1: Same as above. 441 if (!endPosition) { 442 // Do not save methods that do not exist in the source code, e.g. 'build' in ArkUI. 443 continue; 444 } 445 const startLine = startPosition.line + 1; // 1: The final line number in updatedCache should starts from 1. 446 const endLine = endPosition.line + 1; // 1: Same as above. 447 newKey = `${scopeName}:${startLine}:${endLine}`; 448 updatedCache[newKey] = value; 449 } 450 return updatedCache; 451 } 452 453 /** 454 * Obfuscate ast of a file. 455 * @param content ast or source code of a source file 456 * @param sourceFilePathObj 457 * @param previousStageSourceMap 458 * @param historyNameCache 459 * @param originalFilePath When filename obfuscation is enabled, it is used as the source code path. 460 */ 461 public async obfuscate( 462 content: SourceFile | string, 463 sourceFilePathObj: FilePathObj, 464 previousStageSourceMap?: RawSourceMap, 465 historyNameCache?: Map<string, string>, 466 originalFilePath?: string, 467 projectInfo?: ProjectInfo, 468 ): Promise<ObfuscationResultType> { 469 ArkObfuscator.projectInfo = projectInfo; 470 let result: ObfuscationResultType = { content: undefined }; 471 if (this.isObfsIgnoreFile(sourceFilePathObj.buildFilePath)) { 472 // need add return value 473 return result; 474 } 475 476 let ast: SourceFile = this.createAst(content, sourceFilePathObj.buildFilePath); 477 if (ast.statements.length === 0) { 478 return result; 479 } 480 481 if (historyNameCache && historyNameCache.size > 0 && this.mCustomProfiles.mNameObfuscation) { 482 renameIdentifierModule.historyNameCache = historyNameCache; 483 } 484 485 if (this.mCustomProfiles.mUnobfuscationOption?.mPrintKeptNames) { 486 let historyUnobfuscatedNames = historyAllUnobfuscatedNamesMap.get(sourceFilePathObj.relativeFilePath); 487 if (historyUnobfuscatedNames) { 488 renameIdentifierModule.historyUnobfuscatedNamesMap = new Map(Object.entries(historyUnobfuscatedNames)); 489 } 490 } 491 492 originalFilePath = originalFilePath ?? ast.fileName; 493 if (this.mCustomProfiles.mRenameFileName?.mEnable) { 494 orignalFilePathForSearching = originalFilePath; 495 } 496 ArkObfuscator.isKeptCurrentFile = this.isCurrentFileInKeepPaths(this.mCustomProfiles, originalFilePath); 497 498 this.handleDeclarationFile(ast); 499 500 ast = this.obfuscateAst(ast); 501 502 this.writeObfuscationResult(ast, sourceFilePathObj.buildFilePath, result, previousStageSourceMap, originalFilePath); 503 504 this.clearCaches(); 505 return result; 506 } 507 508 private createAst(content: SourceFile | string, sourceFilePath: string): SourceFile { 509 const recordInfo = ArkObfuscator.recordStage(MemoryDottingDefine.CREATE_AST); 510 startSingleFileEvent(EventList.CREATE_AST, performancePrinter.timeSumPrinter); 511 let ast: SourceFile; 512 if (typeof content === 'string') { 513 ast = TypeUtils.createObfSourceFile(sourceFilePath, content); 514 } else { 515 ast = content; 516 } 517 endSingleFileEvent(EventList.CREATE_AST, performancePrinter.timeSumPrinter); 518 ArkObfuscator.stopRecordStage(recordInfo); 519 520 return ast; 521 } 522 523 private obfuscateAst(ast: SourceFile): SourceFile { 524 const recordInfo = ArkObfuscator.recordStage(MemoryDottingDefine.OBFUSCATE_AST); 525 startSingleFileEvent(EventList.OBFUSCATE_AST, performancePrinter.timeSumPrinter); 526 let transformedResult: TransformationResult<Node> = transform(ast, this.mTransformers, this.mCompilerOptions); 527 endSingleFileEvent(EventList.OBFUSCATE_AST, performancePrinter.timeSumPrinter); 528 ArkObfuscator.stopRecordStage(recordInfo); 529 ast = transformedResult.transformed[0] as SourceFile; 530 return ast; 531 } 532 533 private handleDeclarationFile(ast: SourceFile): void { 534 if (ast.isDeclarationFile) { 535 if (!this.mCustomProfiles.mRemoveDeclarationComments || !this.mCustomProfiles.mRemoveDeclarationComments.mEnable) { 536 //@ts-ignore 537 ast.reservedComments = undefined; 538 //@ts-ignore 539 ast.universalReservedComments = undefined; 540 } else { 541 //@ts-ignore 542 ast.reservedComments ??= this.mCustomProfiles.mRemoveDeclarationComments.mReservedComments ? 543 this.mCustomProfiles.mRemoveDeclarationComments.mReservedComments : []; 544 //@ts-ignore 545 ast.universalReservedComments = this.mCustomProfiles.mRemoveDeclarationComments.mUniversalReservedComments ?? []; 546 } 547 } else { 548 //@ts-ignore 549 ast.reservedComments = this.mCustomProfiles.mRemoveComments ? [] : undefined; 550 //@ts-ignore 551 ast.universalReservedComments = this.mCustomProfiles.mRemoveComments ? [] : undefined; 552 } 553 } 554 555 /** 556 * write obfuscated code, sourcemap and namecache 557 */ 558 private writeObfuscationResult(ast: SourceFile, sourceFilePath: string, result: ObfuscationResultType, 559 previousStageSourceMap?: RawSourceMap, originalFilePath?: string): void { 560 // convert ast to output source file and generate sourcemap if needed. 561 let sourceMapGenerator: SourceMapGenerator = undefined; 562 if (this.mCustomProfiles.mEnableSourceMap) { 563 startSingleFileEvent(EventList.GET_SOURCEMAP_GENERATOR, performancePrinter.timeSumPrinter); 564 sourceMapGenerator = getSourceMapGenerator(sourceFilePath); 565 endSingleFileEvent(EventList.GET_SOURCEMAP_GENERATOR, performancePrinter.timeSumPrinter); 566 } 567 568 startSingleFileEvent(EventList.CREATE_PRINTER, performancePrinter.timeSumPrinter); 569 if (sourceFilePath.endsWith('.js')) { 570 TypeUtils.tsToJs(ast); 571 } 572 this.handleTsHarComments(ast, originalFilePath); 573 const recordInfo = ArkObfuscator.recordStage(MemoryDottingDefine.CREATE_PRINTER); 574 this.createObfsPrinter(ast.isDeclarationFile).writeFile(ast, this.mTextWriter, sourceMapGenerator); 575 endSingleFileEvent(EventList.CREATE_PRINTER, performancePrinter.timeSumPrinter); 576 ArkObfuscator.stopRecordStage(recordInfo); 577 578 result.filePath = ast.fileName; 579 result.content = this.mTextWriter.getText(); 580 581 if (this.mCustomProfiles.mUnobfuscationOption?.mPrintKeptNames) { 582 this.handleUnobfuscationNames(result); 583 } 584 585 if (this.mCustomProfiles.mEnableSourceMap && sourceMapGenerator) { 586 this.handleSourceMapAndNameCache(sourceMapGenerator, sourceFilePath, result, previousStageSourceMap); 587 } 588 } 589 590 private handleUnobfuscationNames(result: ObfuscationResultType): void { 591 result.unobfuscationNameMap = new Map(UnobfuscationCollections.unobfuscatedNamesMap); 592 } 593 594 private handleSourceMapAndNameCache(sourceMapGenerator: SourceMapGenerator, sourceFilePath: string, 595 result: ObfuscationResultType, previousStageSourceMap?: RawSourceMap): void { 596 startSingleFileEvent(EventList.SOURCEMAP_MERGE, performancePrinter.timeSumPrinter); 597 let sourceMapJson: RawSourceMap = sourceMapGenerator.toJSON(); 598 sourceMapJson.sourceRoot = ''; 599 sourceMapJson.file = path.basename(sourceFilePath); 600 if (previousStageSourceMap) { 601 sourceMapJson = mergeSourceMap(previousStageSourceMap as RawSourceMap, sourceMapJson); 602 } 603 result.sourceMap = sourceMapJson; 604 endSingleFileEvent(EventList.SOURCEMAP_MERGE, performancePrinter.timeSumPrinter); 605 startSingleFileEvent(EventList.CREATE_NAMECACHE, performancePrinter.timeSumPrinter); 606 let nameCache = renameIdentifierModule.nameCache; 607 if (this.mCustomProfiles.mEnableNameCache) { 608 let newIdentifierCache!: Object; 609 let newMemberMethodCache!: Object; 610 if (previousStageSourceMap) { 611 // The process in sdk, need to use sourcemap mapping. 612 // 1: Only one file in the source map; 0: The first and the only one. 613 const sourceFileName = previousStageSourceMap.sources?.length === 1 ? previousStageSourceMap.sources[0] : ''; 614 const source: Source = new Source(sourceFileName, null); 615 const decodedSourceMap: ExistingDecodedSourceMap = decodeSourcemap(previousStageSourceMap); 616 let sourceMapLink: SourceMapLink = new SourceMapLink(decodedSourceMap, [source]); 617 newIdentifierCache = this.convertLineBasedOnSourceMap(IDENTIFIER_CACHE, sourceMapLink); 618 newMemberMethodCache = this.convertLineBasedOnSourceMap(MEM_METHOD_CACHE, sourceMapLink); 619 } else { 620 // The process in Arkguard. 621 newIdentifierCache = this.convertLineBasedOnSourceMap(IDENTIFIER_CACHE); 622 newMemberMethodCache = this.convertLineBasedOnSourceMap(MEM_METHOD_CACHE); 623 } 624 nameCache.set(IDENTIFIER_CACHE, newIdentifierCache); 625 nameCache.set(MEM_METHOD_CACHE, newMemberMethodCache); 626 result.nameCache = { [IDENTIFIER_CACHE]: newIdentifierCache, [MEM_METHOD_CACHE]: newMemberMethodCache }; 627 } 628 endSingleFileEvent(EventList.CREATE_NAMECACHE, performancePrinter.timeSumPrinter); 629 } 630 631 private clearCaches(): void { 632 // clear cache of text writer 633 this.mTextWriter.clear(); 634 renameIdentifierModule.clearCaches(); 635 if (cleanFileMangledNames) { 636 PropCollections.globalMangledTable.clear(); 637 } 638 UnobfuscationCollections.unobfuscatedNamesMap.clear(); 639 nodeSymbolMap.clear(); 640 } 641} 642