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