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 path from 'path';
17import {ApiExtractor} from './ApiExtractor';
18import {ListUtil} from '../utils/ListUtil';
19import type {IOptions} from '../configs/IOptions';
20import { stringPropsSet, structPropsSet, enumPropsSet } from '../utils/OhsUtil';
21
22export const scanProjectConfig: {
23  mPropertyObfuscation?: boolean,
24  mKeepStringProperty?: boolean,
25  mExportObfuscation?: boolean,
26  mkeepFilesAndDependencies?: Set<string>,
27  isHarCompiled?: boolean
28} = {};
29
30/**
31 * if rename property is not open, api read and extract can be skipped
32 *
33 * init plugin, read api info of openHarmony sdk and generate file of reserved name, property and string.
34 * @param sdkDir absolute path like D:\\HuaweiApp\\ohsdk
35 * @param outputDir
36 */
37export function initPlugin(sdkDir: string, outputDir: string): void {
38  // create sdk api file if not exist
39  const ohSdkPath: string = path.resolve(sdkDir);
40  if (!ohSdkPath) {
41    console.error('SDK path is not found.');
42  }
43
44  const apiVersions: string[] = [''];
45
46  apiVersions.forEach((versionString) => {
47    ApiExtractor.parseOhSdk(ohSdkPath, versionString, true, outputDir);
48  });
49}
50
51/**
52 * need read api info or not
53 * @param customProfiles
54 */
55export function needReadApiInfo(customProfiles: IOptions): boolean {
56  return isEnabledPropertyObfuscation(customProfiles) || customProfiles.mExportObfuscation;
57}
58
59export function isEnabledPropertyObfuscation(customProfiles: IOptions): boolean {
60  return (customProfiles.mNameObfuscation &&
61    customProfiles.mNameObfuscation.mEnable &&
62    customProfiles.mNameObfuscation.mRenameProperties);
63}
64
65function initScanProjectConfig(customProfiles: IOptions, isHarCompiled?: boolean) {
66  scanProjectConfig.mPropertyObfuscation = customProfiles.mNameObfuscation?.mRenameProperties;
67  scanProjectConfig.mKeepStringProperty = customProfiles.mNameObfuscation?.mKeepStringProperty;
68  scanProjectConfig.mExportObfuscation = customProfiles.mExportObfuscation;
69  scanProjectConfig.mkeepFilesAndDependencies = customProfiles.mKeepFileSourceCode?.mkeepFilesAndDependencies;
70  scanProjectConfig.isHarCompiled = isHarCompiled;
71}
72
73export interface ReseverdSetForArkguard {
74  structPropertySet: Set<string> | undefined;
75  stringPropertySet: Set<string> | undefined;
76  exportNameAndPropSet: Set<string> | undefined;
77  exportNameSet: Set<string> | undefined;
78  enumPropertySet: Set<string> | undefined;
79}
80
81/**
82 * read project reserved properties by collected paths
83 * @param filesForCompilation set collection of files
84 * @param customProfiles
85 */
86export function readProjectPropertiesByCollectedPaths(filesForCompilation: Set<string>,
87  customProfiles: IOptions, isHarCompiled: boolean): ReseverdSetForArkguard {
88  const ApiType = ApiExtractor.ApiType;
89  let scanningCommonType = undefined;
90  let scanningLibsType = undefined;
91  if (needReadApiInfo(customProfiles)) {
92    scanningCommonType = ApiType.PROJECT;
93    scanningLibsType = ApiType.PROJECT_DEPENDS;
94  } else {
95    scanningCommonType = ApiType.CONSTRUCTOR_PROPERTY;
96    scanningLibsType = ApiType.CONSTRUCTOR_PROPERTY;
97  }
98  // The purpose of collecting constructor properties is to avoid generating the same name as the constructor property when obfuscating identifier names.
99  ApiExtractor.mConstructorPropertySet = new Set();
100
101  initScanProjectConfig(customProfiles, isHarCompiled);
102
103  stringPropsSet.clear();
104
105  const exportWhiteList = ApiExtractor.parseFileByPaths(filesForCompilation, scanningCommonType);
106  const exportNamesAndProperties: Set<string> | undefined = exportWhiteList.reservedExportPropertyAndName;
107  const exportNames: Set<string> | undefined = exportWhiteList.reservedExportNames;
108
109  // if -enable-property-obfuscation, collect structPropsSet, exportNamesAndProperties and
110  // stringPropsSet(if -enable-string-property-obufscation is not enabled) as whitelists.
111  let exportNameAndPropSet: Set<string>;
112  let structPropertySet: Set<string>;
113  let stringPropertySet: Set<string>;
114  let enumPropertySet: Set<string>;
115  if (isEnabledPropertyObfuscation(customProfiles)) {
116    exportNameAndPropSet = new Set(exportNamesAndProperties);
117    structPropertySet = new Set(structPropsSet);
118    enumPropertySet = new Set(enumPropsSet);
119    if (scanProjectConfig.mKeepStringProperty) {
120      stringPropertySet = new Set(stringPropsSet);
121    }
122  }
123  structPropsSet.clear();
124  stringPropsSet.clear();
125  enumPropsSet.clear();
126
127  let exportNameSet: Set<string>;
128  if (scanProjectConfig.mExportObfuscation) {
129    exportNameSet = new Set(exportNames);
130  }
131
132  return {
133    structPropertySet: structPropertySet,
134    stringPropertySet: stringPropertySet,
135    exportNameAndPropSet: exportNameAndPropSet,
136    exportNameSet: exportNameSet,
137    enumPropertySet: enumPropertySet,
138  };
139}
140