• 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 path from 'path';
17import fs from 'fs';
18import {FileUtils} from '../utils/FileUtils';
19import {ApiExtractor} from './ApiExtractor';
20import {ListUtil} from '../utils/ListUtil';
21import type {IOptions} from '../configs/IOptions';
22import { stringPropsSet, structPropsSet, enumPropsSet } from '../utils/OhsUtil';
23import { INameObfuscationOption } from '../configs/INameObfuscationOption';
24
25export let scanProjectConfig: {
26  mPropertyObfuscation?: boolean,
27  mKeepStringProperty?: boolean,
28  mExportObfuscation?: boolean,
29  mkeepFilesAndDependencies?: Set<string>,
30  isHarCompiled?: boolean
31} = {};
32
33/**
34 * if rename property is not open, api read and extract can be skipped
35 *
36 * init plugin, read api info of openHarmony sdk and generate file of reserved name, property and string.
37 * @param sdkDir absolute path like D:\\HuaweiApp\\ohsdk
38 * @param outputDir
39 */
40export function initPlugin(sdkDir: string, outputDir: string): void {
41  // create sdk api file if not exist
42  const ohSdkPath: string = path.resolve(sdkDir);
43  if (!ohSdkPath) {
44    console.error('SDK path is not found.');
45  }
46
47  const apiVersions: string[] = [''];
48
49  apiVersions.forEach((versionString) => {
50    ApiExtractor.parseOhSdk(ohSdkPath, versionString, true, outputDir);
51  });
52}
53
54/**
55 * need read api info or not
56 * @param customProfiles
57 */
58export function needReadApiInfo(customProfiles: IOptions): boolean {
59  return isEnabledPropertyObfuscation(customProfiles) || customProfiles.mExportObfuscation;
60}
61
62export function isEnabledPropertyObfuscation(customProfiles: IOptions): boolean {
63  return (customProfiles.mNameObfuscation &&
64    customProfiles.mNameObfuscation.mEnable &&
65    customProfiles.mNameObfuscation.mRenameProperties);
66}
67
68/**
69 * read project reserved properties for UT
70 * @param projectPaths can be dir or file
71 * @param customProfiles
72 */
73export function readProjectProperties(projectPaths: string[], customProfiles: IOptions, isOHProject?: boolean):
74  {projectAndLibsReservedProperties: string[]; libExportNames: string[]} {
75
76  let scanningCommonType: ApiExtractor.ApiType = undefined;
77  let scanningLibsType: ApiExtractor.ApiType = undefined;
78  if (isEnabledPropertyObfuscation(customProfiles)) {
79    scanningCommonType = ApiExtractor.ApiType.PROJECT;
80    scanningLibsType = ApiExtractor.ApiType.PROJECT_DEPENDS;
81  } else {
82    scanningCommonType = ApiExtractor.ApiType.CONSTRUCTOR_PROPERTY;
83    scanningLibsType = ApiExtractor.ApiType.CONSTRUCTOR_PROPERTY;
84  }
85  ApiExtractor.mConstructorPropertySet = new Set();
86  // This call is for UT.
87  initScanProjectConfig(customProfiles);
88
89  for (const projectPath of projectPaths) {
90    if (!fs.existsSync(projectPath)) {
91      console.error(`File ${FileUtils.getFileName(projectPath)} is not found.`);
92      return {projectAndLibsReservedProperties:[], libExportNames: []};
93    }
94    stringPropsSet.clear();
95    const sourcPath = isOHProject ? path.join(projectPath, 'src', 'main') : projectPath;
96    const projProperties: string[] = ApiExtractor.parseCommonProject(sourcPath, customProfiles, scanningCommonType);
97    const libExportNamesAndReservedProps = readThirdPartyLibProperties(projectPath, scanningLibsType);
98    const sdkProperties = libExportNamesAndReservedProps?.reservedProperties;
99
100    if (isEnabledPropertyObfuscation(customProfiles)) {
101      // read project code export names
102      customProfiles.mNameObfuscation.mReservedProperties = ListUtil.uniqueMergeList(projProperties,
103        customProfiles.mNameObfuscation.mReservedProperties, [...structPropsSet]);
104
105      // read project lib export names
106      if (sdkProperties && sdkProperties.length > 0) {
107        customProfiles.mNameObfuscation.mReservedProperties = ListUtil.uniqueMergeList(sdkProperties,
108          customProfiles.mNameObfuscation.mReservedProperties);
109      }
110
111      if (scanProjectConfig.mKeepStringProperty && stringPropsSet.size > 0) {
112        customProfiles.mNameObfuscation.mReservedProperties = ListUtil.uniqueMergeList([...stringPropsSet],
113          customProfiles.mNameObfuscation.mReservedProperties);
114      }
115    }
116    structPropsSet.clear();
117    stringPropsSet.clear();
118    if (scanProjectConfig.mExportObfuscation && libExportNamesAndReservedProps?.reservedLibExportNames) {
119      customProfiles.mNameObfuscation.mReservedNames = ListUtil.uniqueMergeList(libExportNamesAndReservedProps.reservedLibExportNames,
120        customProfiles.mNameObfuscation.mReservedNames);
121    }
122  }
123
124  return {
125    projectAndLibsReservedProperties: customProfiles.mNameObfuscation.mReservedProperties ?? [],
126    libExportNames: customProfiles.mNameObfuscation.mReservedNames ?? []
127  };
128}
129
130function initScanProjectConfig(customProfiles: IOptions, isHarCompiled?: boolean): void {
131  scanProjectConfig.mPropertyObfuscation = customProfiles.mNameObfuscation?.mRenameProperties;
132  scanProjectConfig.mKeepStringProperty = customProfiles.mNameObfuscation?.mKeepStringProperty;
133  scanProjectConfig.mExportObfuscation = customProfiles.mExportObfuscation;
134  scanProjectConfig.mkeepFilesAndDependencies = customProfiles.mKeepFileSourceCode?.mkeepFilesAndDependencies;
135  scanProjectConfig.isHarCompiled = isHarCompiled;
136}
137/**
138 * read project reserved properties by collected paths
139 * @param filesForCompilation set collection of files
140 * @param customProfiles
141 */
142export function readProjectPropertiesByCollectedPaths(filesForCompilation: Set<string>, customProfiles: IOptions, isHarCompiled: boolean): {
143  projectAndLibsReservedProperties: string[];
144  libExportNames: string[]} {
145  const apiType = ApiExtractor.ApiType;
146  let scanningCommonType = undefined;
147  let scanningLibsType = undefined;
148  if (needReadApiInfo(customProfiles)) {
149    scanningCommonType = apiType.PROJECT;
150    scanningLibsType = apiType.PROJECT_DEPENDS;
151  } else {
152    scanningCommonType = apiType.CONSTRUCTOR_PROPERTY;
153    scanningLibsType = apiType.CONSTRUCTOR_PROPERTY;
154  }
155  // The purpose of collecting constructor properties is to avoid generating the same name as the constructor property when obfuscating identifier names.
156  ApiExtractor.mConstructorPropertySet = new Set();
157
158  initScanProjectConfig(customProfiles, isHarCompiled);
159
160  stringPropsSet.clear();
161
162  const exportWhiteList = ApiExtractor.parseFileByPaths(filesForCompilation, scanningCommonType);
163  const exportNamesAndProperties: string[] = exportWhiteList.reservedProperties;
164  const exportNames: string[] = exportWhiteList.reservedExportNames;
165
166  const nameObfuscationConfig = customProfiles.mNameObfuscation;
167  if (isEnabledPropertyObfuscation(customProfiles)) {
168    mergeReservedProperties(nameObfuscationConfig, exportNamesAndProperties);
169  }
170  structPropsSet.clear();
171  stringPropsSet.clear();
172  enumPropsSet.clear();
173
174  if (scanProjectConfig.mExportObfuscation) {
175    mergeReservedNames(nameObfuscationConfig, exportNames);
176  }
177
178  // scanProjectConfig needs to be cleared to prevent affecting incremental compilation
179  scanProjectConfig = {};
180
181  return {
182    projectAndLibsReservedProperties: nameObfuscationConfig.mReservedProperties ?? [],
183    libExportNames: nameObfuscationConfig.mReservedNames ?? []
184  };
185}
186
187function mergeReservedProperties(nameObfuscationConfig: INameObfuscationOption, exportNamesAndProperties: string[]): void {
188  if (exportNamesAndProperties.length > 0) {
189    nameObfuscationConfig.mReservedProperties = ListUtil.uniqueMergeList(exportNamesAndProperties, nameObfuscationConfig.mReservedProperties);
190  }
191
192  if (scanProjectConfig.mKeepStringProperty && stringPropsSet.size > 0) {
193    nameObfuscationConfig.mReservedProperties = ListUtil.uniqueMergeList([...stringPropsSet], nameObfuscationConfig.mReservedProperties);
194  }
195
196  if (enumPropsSet.size > 0) {
197    nameObfuscationConfig.mReservedProperties = ListUtil.uniqueMergeList([...enumPropsSet], nameObfuscationConfig.mReservedProperties);
198  }
199
200  if (structPropsSet.size > 0) {
201    nameObfuscationConfig.mReservedProperties = ListUtil.uniqueMergeList([...structPropsSet], nameObfuscationConfig.mReservedProperties);
202  }
203}
204
205function mergeReservedNames(nameObfuscationConfig: INameObfuscationOption, exportNames: string[]): void {
206  if (exportNames.length > 0) {
207    nameObfuscationConfig.mReservedNames = ListUtil.uniqueMergeList(exportNames, nameObfuscationConfig.mReservedNames);
208  }
209}
210
211function readThirdPartyLibProperties(projectPath: string, scanningApiType: ApiExtractor.ApiType): {reservedProperties: string[];
212  reservedLibExportNames: string[] | undefined} {
213  if (!fs.lstatSync(projectPath).isDirectory()) {
214    return undefined;
215  }
216
217  // find third party lib and extract reserved names
218  const fileNames: string[] = fs.readdirSync(projectPath);
219  const hasNodeModules: boolean = fileNames.includes('node_modules');
220  const hasOHModules: boolean = fileNames.includes('oh_modules');
221  if (!hasNodeModules && !hasOHModules) {
222    return undefined;
223  }
224  if (hasNodeModules && hasOHModules) {
225    throw new Error(`There are both node_modules and oh_modules folders in ${projectPath}`);
226  }
227
228  let filePath: string = '';
229  if (hasNodeModules) {
230    filePath = path.join(projectPath, 'node_modules');
231  } else {
232    filePath = path.join(projectPath, 'oh_modules');
233  }
234
235  return ApiExtractor.parseThirdPartyLibs(filePath, scanningApiType);
236}
237