• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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