• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 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 fs from 'fs';
17import path from 'path';
18import JSON5 from 'json5';
19import {
20  ApiExtractor,
21  renamePropertyModule,
22  getMapFromJson,
23  renameFileNameModule
24} from 'arkguard';
25import { identifierCaches } from '../../../ark_utils';
26
27/* ObConfig's properties:
28 *   ruleOptions: {
29*     enable: boolean
30 *    rules: string[]
31 *   }
32 *   consumerRules: string[]
33 *
34 * ObfuscationConfig's properties:
35 *   selfConfig: ObConfig
36 *   dependencies: { libraries: ObConfig[], hars: string[] }
37 *   sdkApis: string[]
38 *   obfuscationCacheDir: string
39 *   exportRulePath: string
40 */
41
42enum OptionType {
43  NONE,
44  KEEP_DTS,
45  KEEP_GLOBAL_NAME,
46  KEEP_PROPERTY_NAME,
47  KEEP_FILE_NAME,
48  KEEP_COMMENTS,
49  DISABLE_OBFUSCATION,
50  ENABLE_PROPERTY_OBFUSCATION,
51  ENABLE_STRING_PROPERTY_OBFUSCATION,
52  ENABLE_TOPLEVEL_OBFUSCATION,
53  ENABLE_FILENAME_OBFUSCATION,
54  ENABLE_EXPORT_OBFUSCATION,
55  COMPACT,
56  REMOVE_LOG,
57  REMOVE_COMMENTS,
58  PRINT_NAMECACHE,
59  APPLY_NAMECACHE,
60}
61
62function isFileExist(filePath: string): boolean {
63  let exist = false;
64  try {
65    fs.accessSync(filePath, fs.constants.F_OK);
66  } catch (err) {
67    exist = !err;
68  }
69  return exist;
70}
71
72function sortAndDeduplicateStringArr(arr: string[]) {
73  if (arr.length == 0) {
74    return arr;
75  }
76
77  arr.sort((a, b) => {
78    return a.localeCompare(b);
79  });
80
81  let tmpArr: string[] = [arr[0]];
82  for (let i = 1; i < arr.length; i++) {
83    if (arr[i] != arr[i - 1]) {
84      tmpArr.push(arr[i]);
85    }
86  }
87  return tmpArr;
88}
89
90class ObOptions {
91  disableObfuscation: boolean = false;
92  enablePropertyObfuscation: boolean = false;
93  enableStringPropertyObfuscation: boolean = false;
94  enableToplevelObfuscation: boolean = false;
95  enableFileNameObfuscation: boolean = false;
96  enableExportObfuscation: boolean = false;
97  removeComments: boolean = false;
98  compact: boolean = false;
99  removeLog: boolean = false;
100  printNameCache: string = '';
101  applyNameCache: string = '';
102
103  merge(other: ObOptions) {
104    this.disableObfuscation = this.disableObfuscation || other.disableObfuscation;
105    this.enablePropertyObfuscation = this.enablePropertyObfuscation || other.enablePropertyObfuscation;
106    this.enableToplevelObfuscation = this.enableToplevelObfuscation || other.enableToplevelObfuscation;
107    this.enableStringPropertyObfuscation = this.enableStringPropertyObfuscation || other.enableStringPropertyObfuscation;
108    this.removeComments = this.removeComments || other.removeComments;
109    this.compact = this.compact || other.compact;
110    this.removeLog = this.removeLog || other.removeLog;
111    this.enableFileNameObfuscation = this.enableFileNameObfuscation || other.enableFileNameObfuscation;
112    this.enableExportObfuscation = this.enableExportObfuscation || other.enableExportObfuscation;
113    if (other.printNameCache.length > 0) {
114      this.printNameCache = other.printNameCache;
115    }
116    if (other.applyNameCache.length > 0) {
117      this.applyNameCache = other.applyNameCache;
118    }
119  }
120}
121
122export class MergedConfig {
123  options: ObOptions = new ObOptions();
124  reservedPropertyNames: string[] = [];
125  reservedGlobalNames: string[] = [];
126  reservedNames: string[] = [];
127  reservedFileNames: string[] = [];
128  keepComments: string[] = [];
129
130  merge(other: MergedConfig) {
131    this.options.merge(other.options);
132    this.reservedPropertyNames.push(...other.reservedPropertyNames);
133    this.reservedGlobalNames.push(...other.reservedGlobalNames);
134    this.reservedFileNames.push(...other.reservedFileNames);
135    this.keepComments.push(...other.keepComments);
136  }
137
138  sortAndDeduplicate() {
139    this.reservedPropertyNames = sortAndDeduplicateStringArr(
140      this.reservedPropertyNames
141    );
142    this.reservedGlobalNames = sortAndDeduplicateStringArr(this.reservedGlobalNames);
143    this.reservedFileNames = sortAndDeduplicateStringArr(this.reservedFileNames);
144    this.keepComments = sortAndDeduplicateStringArr(this.keepComments);
145  }
146
147  serializeMergedConfig(): string {
148    let resultStr: string = '';
149    const keys = Object.keys(this.options);
150    for (const key of keys) {
151      // skip the export of some switches.
152      if (this.options[key] === true && ObConfigResolver.exportedSwitchMap.has(String(key))) {
153        resultStr += ObConfigResolver.exportedSwitchMap.get(String(key)) + '\n';
154      }
155    }
156
157    if (this.reservedGlobalNames.length > 0) {
158      resultStr += ObConfigResolver.KEEP_GLOBAL_NAME + '\n';
159      this.reservedGlobalNames.forEach((item) => {
160        resultStr += item + '\n';
161      });
162    }
163    if (this.reservedPropertyNames.length > 0) {
164      resultStr += ObConfigResolver.KEEP_PROPERTY_NAME + '\n';
165      this.reservedPropertyNames.forEach((item) => {
166        resultStr += item + '\n';
167      });
168    }
169    return resultStr;
170  }
171}
172
173
174export class ObConfigResolver {
175  sourceObConfig: any;
176  logger: any;
177  isHarCompiled: boolean | undefined;
178  isTerser: boolean;
179
180  constructor(projectConfig: any, logger: any, isTerser?: boolean) {
181    this.sourceObConfig = projectConfig.obfuscationOptions;
182    this.logger = logger;
183    this.isHarCompiled = projectConfig.compileHar;
184    this.isTerser = isTerser;
185  }
186
187  public resolveObfuscationConfigs(): MergedConfig {
188    let sourceObConfig = this.sourceObConfig;
189    if (!sourceObConfig) {
190      return new MergedConfig();
191    }
192    let enableObfuscation: boolean = sourceObConfig.selfConfig.ruleOptions.enable;
193
194    let selfConfig: MergedConfig = new MergedConfig();
195    if (enableObfuscation) {
196      this.getSelfConfigs(selfConfig);
197      enableObfuscation = !selfConfig.options.disableObfuscation;
198    } else {
199      selfConfig.options.disableObfuscation = true;
200    }
201
202    let needConsumerConfigs: boolean = this.isHarCompiled && sourceObConfig.selfConfig.consumerRules &&
203      sourceObConfig.selfConfig.consumerRules.length > 0;
204    let needDependencyConfigs: boolean = enableObfuscation || needConsumerConfigs;
205
206    let dependencyConfigs: MergedConfig = new MergedConfig();
207    const dependencyMaxLength: number = Math.max(sourceObConfig.dependencies.libraries.length, sourceObConfig.dependencies.hars.length)
208    if (needDependencyConfigs && dependencyMaxLength > 0) {
209      dependencyConfigs = new MergedConfig();
210      this.getDependencyConfigs(sourceObConfig, dependencyConfigs);
211      enableObfuscation = enableObfuscation && !dependencyConfigs.options.disableObfuscation;
212    }
213    const mergedConfigs: MergedConfig = this.getMergedConfigs(selfConfig, dependencyConfigs);
214
215    if (enableObfuscation && mergedConfigs.options.enablePropertyObfuscation) {
216      const systemApiCachePath: string = path.join(sourceObConfig.obfuscationCacheDir, 'systemApiCache.json');
217      if (isFileExist(systemApiCachePath)) {
218        this.getSystemApiConfigsByCache(selfConfig, systemApiCachePath);
219      } else {
220        this.getSystemApiCache(selfConfig, systemApiCachePath);
221      }
222    }
223
224    if (needConsumerConfigs) {
225      let selfConsumerConfig = new MergedConfig();
226      this.getSelfConsumerConfig(selfConsumerConfig);
227      this.genConsumerConfigFiles(sourceObConfig, selfConsumerConfig, dependencyConfigs);
228    }
229
230    return mergedConfigs;
231  }
232
233  private getSelfConfigs(selfConfigs: MergedConfig) {
234    if (this.sourceObConfig.selfConfig.ruleOptions.rules) {
235      const configPaths: string[] = this.sourceObConfig.selfConfig.ruleOptions.rules;
236      for (const path of configPaths) {
237        this.getConfigByPath(path, selfConfigs);
238      }
239    }
240  }
241
242  private getConfigByPath(path: string, configs: MergedConfig) {
243    let fileContent = undefined;
244    try {
245      fileContent = fs.readFileSync(path, 'utf-8');
246    } catch (err) {
247      this.logger.error(`Failed to open ${path}. Error message: ${err}`);
248      throw err;
249    }
250    this.handleConfigContent(fileContent, configs, path);
251  }
252
253  // obfuscation options
254  static readonly KEEP_DTS = '-keep-dts';
255  static readonly KEEP_GLOBAL_NAME = '-keep-global-name';
256  static readonly KEEP_PROPERTY_NAME = '-keep-property-name';
257  static readonly KEPP_FILE_NAME = '-keep-file-name';
258  static readonly KEEP_COMMENTS = '-keep-comments';
259  static readonly DISABLE_OBFUSCATION = '-disable-obfuscation';
260  static readonly ENABLE_PROPERTY_OBFUSCATION = '-enable-property-obfuscation';
261  static readonly ENABLE_STRING_PROPERTY_OBFUSCATION = '-enable-string-property-obfuscation';
262  static readonly ENABLE_TOPLEVEL_OBFUSCATION = '-enable-toplevel-obfuscation';
263  static readonly ENABLE_FILENAME_OBFUSCATION = '-enable-filename-obfuscation';
264  static readonly ENABLE_EXPORT_OBFUSCATION = '-enable-export-obfuscation';
265  static readonly REMOVE_COMMENTS = '-remove-comments';
266  static readonly COMPACT = '-compact';
267  static readonly REMOVE_LOG = '-remove-log';
268  static readonly PRINT_NAMECACHE = '-print-namecache';
269  static readonly APPLY_NAMECACHE = '-apply-namecache';
270
271  // renameFileName, printNameCache, applyNameCache, removeComments and keepComments won't be reserved in obfuscation.txt file.
272  static exportedSwitchMap: Map<string, string> = new Map([
273    ['disableObfuscation', ObConfigResolver.KEEP_DTS],
274    ['enablePropertyObfuscation', ObConfigResolver.ENABLE_PROPERTY_OBFUSCATION],
275    ['enableStringPropertyObfuscation', ObConfigResolver.ENABLE_STRING_PROPERTY_OBFUSCATION],
276    ['enableToplevelObfuscation', ObConfigResolver.ENABLE_TOPLEVEL_OBFUSCATION],
277    ['compact', ObConfigResolver.COMPACT],
278    ['removeLog', ObConfigResolver.REMOVE_LOG],
279  ]);
280
281  private getTokenType(token: string): OptionType {
282    switch (token) {
283      case ObConfigResolver.KEEP_DTS:
284        return OptionType.KEEP_DTS;
285      case ObConfigResolver.KEEP_GLOBAL_NAME:
286        return OptionType.KEEP_GLOBAL_NAME;
287      case ObConfigResolver.KEEP_PROPERTY_NAME:
288        return OptionType.KEEP_PROPERTY_NAME;
289      case ObConfigResolver.KEPP_FILE_NAME:
290        return OptionType.KEEP_FILE_NAME;
291      case ObConfigResolver.KEEP_COMMENTS:
292        return OptionType.KEEP_COMMENTS;
293      case ObConfigResolver.DISABLE_OBFUSCATION:
294        return OptionType.DISABLE_OBFUSCATION;
295      case ObConfigResolver.ENABLE_PROPERTY_OBFUSCATION:
296        return OptionType.ENABLE_PROPERTY_OBFUSCATION;
297      case ObConfigResolver.ENABLE_STRING_PROPERTY_OBFUSCATION:
298        return OptionType.ENABLE_STRING_PROPERTY_OBFUSCATION;
299      case ObConfigResolver.ENABLE_TOPLEVEL_OBFUSCATION:
300        return OptionType.ENABLE_TOPLEVEL_OBFUSCATION;
301      case ObConfigResolver.ENABLE_FILENAME_OBFUSCATION:
302        return OptionType.ENABLE_FILENAME_OBFUSCATION;
303      case ObConfigResolver.ENABLE_EXPORT_OBFUSCATION:
304        return OptionType.ENABLE_EXPORT_OBFUSCATION;
305      case ObConfigResolver.REMOVE_COMMENTS:
306        return OptionType.REMOVE_COMMENTS;
307      case ObConfigResolver.COMPACT:
308        return OptionType.COMPACT;
309      case ObConfigResolver.REMOVE_LOG:
310        return OptionType.REMOVE_LOG;
311      case ObConfigResolver.PRINT_NAMECACHE:
312        return OptionType.PRINT_NAMECACHE;
313      case ObConfigResolver.APPLY_NAMECACHE:
314        return OptionType.APPLY_NAMECACHE;
315      default:
316        return OptionType.NONE;
317    }
318  }
319
320  private handleConfigContent(data: string, configs: MergedConfig, configPath: string) {
321    data = this.removeComments(data);
322    const tokens = data.split(/[',', '\t', ' ', '\n', '\r\n']/).filter((item) => {
323      if (item !== '') {
324        return item;
325      }
326    });
327
328    let type: OptionType = OptionType.NONE;
329    let tokenType: OptionType;
330    let dtsFilePaths: string[] = [];
331    for (let i = 0; i < tokens.length; i++) {
332      const token = tokens[i];
333      tokenType = this.getTokenType(token);
334      // handle switches cases
335      switch (tokenType) {
336        case OptionType.DISABLE_OBFUSCATION: {
337          configs.options.disableObfuscation = true;
338          continue;
339        }
340        case OptionType.ENABLE_PROPERTY_OBFUSCATION: {
341          configs.options.enablePropertyObfuscation = true;
342          continue;
343        }
344        case OptionType.ENABLE_STRING_PROPERTY_OBFUSCATION: {
345          configs.options.enableStringPropertyObfuscation = true;
346        }
347        case OptionType.ENABLE_TOPLEVEL_OBFUSCATION: {
348          configs.options.enableToplevelObfuscation = true;
349          continue;
350        }
351        case OptionType.REMOVE_COMMENTS: {
352          configs.options.removeComments = true;
353          continue;
354        }
355        case OptionType.ENABLE_FILENAME_OBFUSCATION: {
356          configs.options.enableFileNameObfuscation = true;
357          continue;
358        }
359        case OptionType.ENABLE_EXPORT_OBFUSCATION: {
360          configs.options.enableExportObfuscation = true;
361          continue;
362        }
363        case OptionType.COMPACT: {
364          configs.options.compact = true;
365          continue;
366        }
367        case OptionType.REMOVE_LOG: {
368          configs.options.removeLog = true;
369          continue;
370        }
371        case OptionType.KEEP_DTS:
372        case OptionType.KEEP_GLOBAL_NAME:
373        case OptionType.KEEP_PROPERTY_NAME:
374        case OptionType.KEEP_FILE_NAME:
375        case OptionType.KEEP_COMMENTS:
376        case OptionType.PRINT_NAMECACHE:
377        case OptionType.APPLY_NAMECACHE:
378          type = tokenType;
379          continue;
380        default: {
381          // fall-through
382        }
383      }
384      // handle 'keep' options and 'namecache' options
385      switch (type) {
386        case OptionType.KEEP_DTS: {
387          dtsFilePaths.push(token);
388          continue;
389        }
390        case OptionType.KEEP_GLOBAL_NAME: {
391          configs.reservedGlobalNames.push(token);
392          continue;
393        }
394        case OptionType.KEEP_PROPERTY_NAME: {
395          configs.reservedPropertyNames.push(token);
396          continue;
397        }
398        case OptionType.KEEP_FILE_NAME: {
399          configs.reservedFileNames.push(token);
400          continue;
401        }
402        case OptionType.KEEP_COMMENTS: {
403          configs.keepComments.push(token);
404          continue;
405        }
406        case OptionType.PRINT_NAMECACHE: {
407          configs.options.printNameCache = token;
408          type = OptionType.NONE;
409          continue;
410        }
411        case OptionType.APPLY_NAMECACHE: {
412          configs.options.applyNameCache = token;
413          type = OptionType.NONE;
414          this.determineNameCachePath(token, configPath);
415          continue;
416        }
417        default:
418          continue;
419      }
420    }
421
422    this.resolveDts(dtsFilePaths, configs);
423  }
424
425  // get names in .d.ts files and add them into reserved list
426  private resolveDts(dtsFilePaths: string[], configs: MergedConfig) {
427    ApiExtractor.mPropertySet.clear();
428    dtsFilePaths.forEach((token) => {
429      ApiExtractor.traverseApiFiles(token, ApiExtractor.ApiType.PROJECT);
430    });
431    configs.reservedNames = configs.reservedNames.concat(
432      [...ApiExtractor.mPropertySet]
433    );
434    configs.reservedPropertyNames = configs.reservedPropertyNames.concat(
435      [...ApiExtractor.mPropertySet]
436    );
437    ApiExtractor.mPropertySet.clear();
438  }
439
440  // the content from '#' to '\n' are comments
441  private removeComments(data: string) {
442    const commentStart = '#';
443    const commentEnd = '\n';
444    let tmpStr = '';
445    var isInComments = false;
446    for (let i = 0; i < data.length; i++) {
447      if (isInComments) {
448        isInComments = data[i] != commentEnd;
449      } else if (data[i] != commentStart) {
450        tmpStr += data[i];
451      } else {
452        isInComments = true;
453      }
454    }
455    return tmpStr;
456  }
457
458  /**
459   * systemConfigs includes the API directorys.
460   * component directory and pre_define.js file path needs to be concatenated
461   * @param systemConfigs
462   */
463  private getSystemApiCache(systemConfigs: MergedConfig, systemApiCachePath: string) {
464    ApiExtractor.mPropertySet.clear();
465    const sdkApis: string[] = sortAndDeduplicateStringArr(this.sourceObConfig.sdkApis);
466    for (let apiPath of sdkApis) {
467      this.getSdkApiCache(apiPath);
468      const UIPath: string =  path.join(apiPath,'../build-tools/ets-loader/lib/pre_define.js');
469      if (fs.existsSync(UIPath)) {
470        this.getUIApiCache(UIPath);
471      }
472    }
473    const savedNameAndPropertyList: string[] = sortAndDeduplicateStringArr([...ApiExtractor.mPropertySet])
474    const systemApiContent = {
475      ReservedNames: savedNameAndPropertyList,
476      ReservedPropertyNames: savedNameAndPropertyList,
477    };
478    systemConfigs.reservedPropertyNames.push(...savedNameAndPropertyList);
479    systemConfigs.reservedNames.push(...savedNameAndPropertyList);
480    if (!fs.existsSync(path.dirname(systemApiCachePath))) {
481      fs.mkdirSync(path.dirname(systemApiCachePath), {recursive: true});
482    }
483    fs.writeFileSync(systemApiCachePath, JSON.stringify(systemApiContent, null, 2));
484    ApiExtractor.mPropertySet.clear();
485  }
486
487  private getSdkApiCache(sdkApiPath: string) {
488    ApiExtractor.traverseApiFiles(sdkApiPath, ApiExtractor.ApiType.API);
489    const componentPath: string =  path.join(sdkApiPath,'../component');
490    if (fs.existsSync(componentPath)) {
491      ApiExtractor.traverseApiFiles(componentPath, ApiExtractor.ApiType.COMPONENT);
492    }
493  }
494
495  private getUIApiCache(uiApiPath: string) {
496    ApiExtractor.extractStringsFromFile(uiApiPath);
497  }
498
499  private getDependencyConfigs(sourceObConfig: any, dependencyConfigs: MergedConfig): void {
500    for (const lib of sourceObConfig.dependencies.libraries || []) {
501      if(lib.consumerRules && lib.consumerRules.length > 0) {
502        for (const path of lib.consumerRules) {
503          const thisLibConfigs = new MergedConfig();
504          this.getConfigByPath(path, dependencyConfigs);
505          dependencyConfigs.merge(thisLibConfigs);
506        }
507      }
508    }
509
510    if (sourceObConfig.dependencies && sourceObConfig.dependencies.hars && sourceObConfig.dependencies.hars.length > 0) {
511      for (const path of sourceObConfig.dependencies.hars) {
512        const thisHarConfigs = new MergedConfig();
513        this.getConfigByPath(path, dependencyConfigs);
514        dependencyConfigs.merge(thisHarConfigs);
515      }
516    }
517  }
518
519  private getSystemApiConfigsByCache(systemConfigs: MergedConfig, systemApiCachePath: string) {
520    let systemApiContent: { ReservedPropertyNames?: string[], ReservedNames?: string[] } = JSON.parse(fs.readFileSync(systemApiCachePath, 'utf-8'));
521    if (systemApiContent.ReservedPropertyNames) {
522      systemConfigs.reservedPropertyNames = systemApiContent.ReservedPropertyNames;
523    }
524    if (systemApiContent.ReservedNames) {
525      systemConfigs.reservedNames = systemApiContent.ReservedNames;
526    }
527  }
528
529  private getSelfConsumerConfig(selfConsumerConfig: MergedConfig) {
530    for (const path of this.sourceObConfig.selfConfig.consumerRules) {
531      this.getConfigByPath(path, selfConsumerConfig);
532    }
533  }
534
535  private getMergedConfigs(selfConfigs: MergedConfig, dependencyConfigs: MergedConfig): MergedConfig {
536    if (dependencyConfigs) {
537        selfConfigs.merge(dependencyConfigs);
538    }
539    selfConfigs.sortAndDeduplicate();
540    return selfConfigs;
541  }
542
543  private genConsumerConfigFiles(sourceObConfig: any, selfConsumerConfig: MergedConfig, dependencyConfigs: MergedConfig) {
544    selfConsumerConfig.merge(dependencyConfigs);
545    selfConsumerConfig.sortAndDeduplicate();
546    this.writeConsumerConfigFile(selfConsumerConfig, sourceObConfig.exportRulePath);
547  }
548
549  public writeConsumerConfigFile(selfConsumerConfig: MergedConfig, outpath: string) {
550    const configContent: string = selfConsumerConfig.serializeMergedConfig();
551    fs.writeFileSync(outpath, configContent);
552  }
553
554  private determineNameCachePath(nameCachePath: string, configPath: string): void {
555    if (!fs.existsSync(nameCachePath)) {
556      throw new Error(`The applied namecache file '${nameCachePath}' configured by '${configPath}' does not exist.`);
557    }
558  }
559}
560
561export function readNameCache(nameCachePath: string, logger: any): void {
562  try {
563    const fileContent = fs.readFileSync(nameCachePath, 'utf-8');
564    const nameCache: { IdentifierCache?: Object, PropertyCache?: Object, FileNameCache?: Object } = JSON.parse(fileContent);
565    if (nameCache.PropertyCache) {
566      renamePropertyModule.historyMangledTable = getMapFromJson(nameCache.PropertyCache);
567    }
568    if (nameCache.FileNameCache) {
569      renameFileNameModule.historyFileNameMangledTable = getMapFromJson(nameCache.FileNameCache);
570    }
571
572    Object.assign(identifierCaches, nameCache.IdentifierCache);
573  } catch (err) {
574    logger.error(`Failed to open ${nameCachePath}. Error message: ${err}`);
575  }
576}
577
578export function getArkguardNameCache(enablePropertyObfuscation: boolean, enableFileNameObfuscation: boolean): string {
579  let writeContent: string = '';
580  const nameCacheCollection: { IdentifierCache?: Object, PropertyCache?: Object, FileNameCache?: Object } = {};
581  nameCacheCollection.IdentifierCache = identifierCaches;
582
583  if (enablePropertyObfuscation) {
584    const mergedPropertyNameCache: Map<string, string> = new Map();
585    fillNameCache(renamePropertyModule.historyMangledTable, mergedPropertyNameCache);
586    fillNameCache(renamePropertyModule.globalMangledTable, mergedPropertyNameCache);
587    nameCacheCollection.PropertyCache = Object.fromEntries(mergedPropertyNameCache);
588  }
589
590  if (enableFileNameObfuscation) {
591    const mergedFileNameCache: Map<string, string> = new Map();
592    fillNameCache(renameFileNameModule.historyFileNameMangledTable, mergedFileNameCache);
593    fillNameCache(renameFileNameModule.globalFileNameMangledTable, mergedFileNameCache);
594    nameCacheCollection.FileNameCache = Object.fromEntries(mergedFileNameCache);
595  }
596
597  writeContent += JSON.stringify(nameCacheCollection, null, 2);
598  return writeContent;
599}
600
601function fillNameCache(table: Map<string, string>, nameCache: Map<string, string>): void {
602  if (table) {
603    for (const [key, value] of table.entries()) {
604      nameCache.set(key, value);
605    }
606  }
607  return;
608}
609
610export function writeObfuscationNameCache(projectConfig:any, obfuscationCacheDir?: string, printNameCache?: string): void {
611  let writeContent: string = '';
612  if (projectConfig.arkObfuscator) {
613    writeContent = getArkguardNameCache(projectConfig.obfuscationMergedObConfig.options.enablePropertyObfuscation,
614      projectConfig.obfuscationMergedObConfig.options.enableFileNameObfuscation);
615  } else if (projectConfig.terserConfig) {
616    writeContent = JSON.stringify(projectConfig.terserConfig.nameCache, null, 2);
617  } else {
618    return;
619  }
620  if (obfuscationCacheDir && obfuscationCacheDir.length > 0) {
621    const defaultNameCachePath: string = path.join(obfuscationCacheDir, 'nameCache.json');
622    if (!fs.existsSync(path.dirname(defaultNameCachePath))) {
623      fs.mkdirSync(path.dirname(defaultNameCachePath), {recursive: true});
624    }
625    fs.writeFileSync(defaultNameCachePath, writeContent);
626  }
627  if (printNameCache && printNameCache.length > 0) {
628    fs.writeFileSync(printNameCache, writeContent);
629  }
630}
631
632export function generateConsumerObConfigFile(obfuscationOptions: any, logger: any) {
633  const projectConfig = { obfuscationOptions, compileHar: true };
634  const obConfig: ObConfigResolver =  new ObConfigResolver(projectConfig, logger);
635  obConfig.resolveObfuscationConfigs();
636}
637
638/**
639 * Collect reserved file name configured in oh-package.json5 and module.json5.
640 * @param ohPackagePath The 'main' and 'types' fileds in oh-package.json5 need to be reserved.
641 * @param moduleJsonPath The 'srcEntry' filed in module.json5 needs to be reserved.
642 * @returns reservedFileNames
643 */
644export function collectResevedFileNameInIDEConfig(ohPackagePath: string, moduleJsonPath: string, projectPath: string, cachePath: string): string[] {
645  const reservedFileNames: string[] = [];
646  if (fs.existsSync(ohPackagePath)) {
647    const ohPackageContent = JSON5.parse(fs.readFileSync(ohPackagePath, 'utf-8'));
648    ohPackageContent.main && reservedFileNames.push(ohPackageContent.main);
649    ohPackageContent.types && reservedFileNames.push(ohPackageContent.types);
650  }
651
652  if (fs.existsSync(moduleJsonPath)) {
653    const moduleJsonContent = JSON5.parse(fs.readFileSync(moduleJsonPath, 'utf-8'));
654    moduleJsonContent.module?.srcEntry && reservedFileNames.push(moduleJsonContent.module?.srcEntry);
655  }
656
657  /* Get the reserved file name
658   * projectPath: /library/src/main/ets
659   * cachePath: /library/build/default/cache/default/default@HarCompileArkTs/esmodules/release
660   * target reserved path: /library/build/default/cache/default/default@HarCompileArkTs/esmodules/release/src/main/ets
661   */
662  reservedFileNames.push(projectPath);
663  reservedFileNames.push(cachePath);
664  return reservedFileNames;
665}
666
667export function mangleFilePath(originalPath: string): string {
668  const mangledFilePath = renameFileNameModule.getMangleCompletePath(originalPath);
669  return mangledFilePath;
670}
671
672export function resetObfuscation(): void {
673  renamePropertyModule.globalMangledTable?.clear();
674  renamePropertyModule.historyMangledTable?.clear();
675  renameFileNameModule.globalFileNameMangledTable?.clear();
676  renameFileNameModule.historyFileNameMangledTable?.clear();
677  ApiExtractor.mPropertySet?.clear();
678}