• 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 {
19  ArkObfuscator,
20  initObfuscationConfig,
21  readProjectPropertiesByCollectedPaths,
22  performancePrinter,
23  EventList,
24  blockPrinter,
25  endFilesEvent,
26  startFilesEvent
27} from 'arkguard';
28
29import {
30  TS2ABC,
31  ESMODULE,
32  NODE_MODULES,
33  OH_MODULES,
34  OBFUSCATION_TOOL
35} from './ark_define';
36import {
37  isAotMode,
38  isDebug,
39  isBranchElimination
40} from '../utils';
41import {
42  isHarmonyOs,
43  isLinux,
44  isMac,
45  isWindows,
46  toUnixPath
47} from '../../../utils';
48import { getArkBuildDir } from '../../../ark_utils';
49import { checkAotConfig } from '../../../gen_aot';
50import { projectConfig as mainProjectConfig } from '../../../../main';
51import type { MergedConfig } from './ob_config_resolver';
52import type { ReseverdSetForArkguard } from 'arkguard';
53import { MemoryMonitor } from '../../meomry_monitor/rollup-plugin-memory-monitor';
54import { MemoryDefine } from '../../meomry_monitor/memory_define';
55import { initObfLogger, printObfLogger } from './ob_config_resolver';
56
57type ArkConfig = {
58  arkRootPath: string;
59  ts2abcPath: string;
60  js2abcPath: string;
61  mergeAbcPath: string;
62  es2abcPath: string;
63  aotCompilerPath: string;
64  nodePath: string;
65  isDebug: boolean;
66  isBranchElimination: boolean;
67  optTryCatchFunc: boolean;
68};
69
70export function initArkConfig(projectConfig: Object): ArkConfig {
71  let arkRootPath: string = path.join(__dirname, '..', '..', '..', '..', 'bin', 'ark');
72  if (projectConfig.arkFrontendDir) {
73    arkRootPath = projectConfig.arkFrontendDir;
74  }
75  let arkConfig: ArkConfig = {
76    arkRootPath: '',
77    ts2abcPath: '',
78    js2abcPath: '',
79    mergeAbcPath: '',
80    es2abcPath: '',
81    aotCompilerPath: '',
82    nodePath: '',
83    isDebug: false,
84    isBranchElimination: false,
85    optTryCatchFunc: false
86  };
87  arkConfig.nodePath = 'node';
88  if (projectConfig.nodeJs) {
89    arkConfig.nodePath = projectConfig.nodePath;
90  }
91  arkConfig.isDebug = isDebug(projectConfig);
92  arkConfig.isBranchElimination = isBranchElimination(projectConfig);
93  arkConfig.optTryCatchFunc = mainProjectConfig.optTryCatchFunc;
94  arkConfig.arkRootPath = arkRootPath;
95  processPlatformInfo(arkConfig);
96  processCompatibleVersion(projectConfig, arkConfig);
97  return arkConfig;
98}
99
100export function initArkProjectConfig(share: Object): Object {
101  let projectConfig: Object = share.projectConfig;
102  let arkProjectConfig: Object = {};
103  let entryPackageName: string = share.projectConfig.entryPackageName || '';
104  let entryModuleVersion: string = share.projectConfig.entryModuleVersion || '';
105  arkProjectConfig.entryPackageInfo = `${entryPackageName}|${entryModuleVersion}`;
106  arkProjectConfig.projectRootPath = share.projectConfig.projectTopDir;
107  if (projectConfig.aceBuildJson && fs.existsSync(projectConfig.aceBuildJson)) {
108    const buildJsonInfo = JSON.parse(fs.readFileSync(projectConfig.aceBuildJson).toString());
109    arkProjectConfig.projectRootPath = buildJsonInfo.projectRootPath;
110    arkProjectConfig.modulePathMap = buildJsonInfo.modulePathMap;
111    arkProjectConfig.isOhosTest = buildJsonInfo.isOhosTest;
112    arkProjectConfig.arkRouterMap = buildJsonInfo.routerMap;
113    // Collect bytecode har's declaration files entries include dynamic import and workers, use
114    // by es2abc for dependency resolution.
115    arkProjectConfig.declarationEntry = buildJsonInfo.declarationEntry;
116    if (buildJsonInfo.patchConfig) {
117      arkProjectConfig.oldMapFilePath = buildJsonInfo.patchConfig.oldMapFilePath;
118    }
119    if (checkAotConfig(projectConfig.compileMode, buildJsonInfo,
120      (error: string) => { share.throwArkTsCompilerError(error) })) {
121      arkProjectConfig.processTs = true;
122      arkProjectConfig.pandaMode = TS2ABC;
123      arkProjectConfig.anBuildOutPut = buildJsonInfo.anBuildOutPut;
124      arkProjectConfig.anBuildMode = buildJsonInfo.anBuildMode;
125      arkProjectConfig.apPath = buildJsonInfo.apPath;
126    } else {
127      arkProjectConfig.processTs = false;
128      arkProjectConfig.pandaMode = buildJsonInfo.pandaMode;
129    }
130
131    if (projectConfig.compileMode === ESMODULE) {
132      arkProjectConfig.nodeModulesPath = buildJsonInfo.nodeModulesPath;
133      // harNameOhmMap stores har packages that are dependent for bytecode har when compile bytecode har.
134      arkProjectConfig.harNameOhmMap = buildJsonInfo.harNameOhmMap;
135      arkProjectConfig.hspNameOhmMap = buildJsonInfo.hspNameOhmMap;
136      projectConfig.packageDir = buildJsonInfo.packageManagerType === 'ohpm' ? OH_MODULES : NODE_MODULES;
137    }
138    if (buildJsonInfo.dynamicImportLibInfo) {
139      arkProjectConfig.dynamicImportLibInfo = buildJsonInfo.dynamicImportLibInfo;
140    }
141    if (buildJsonInfo.byteCodeHarInfo) {
142      arkProjectConfig.byteCodeHarInfo = buildJsonInfo.byteCodeHarInfo;
143    }
144    // Currently, the IDE does not have this configuration option, and cacheBytecodeHar is true by default.
145    arkProjectConfig.cacheBytecodeHar = !Object.prototype.hasOwnProperty.call(buildJsonInfo, 'cacheBytecodeHar') ||
146      buildJsonInfo.cacheBytecodeHar;
147  }
148  if (projectConfig.aceManifestPath && fs.existsSync(projectConfig.aceManifestPath)) {
149    const manifestJsonInfo = JSON.parse(fs.readFileSync(projectConfig.aceManifestPath).toString());
150    if (manifestJsonInfo.minPlatformVersion) {
151      arkProjectConfig.minPlatformVersion = manifestJsonInfo.minPlatformVersion;
152    }
153  }
154  if (projectConfig.aceModuleJsonPath && fs.existsSync(projectConfig.aceModuleJsonPath)) {
155    const moduleJsonInfo = JSON.parse(fs.readFileSync(projectConfig.aceModuleJsonPath).toString());
156    if (moduleJsonInfo.app.minAPIVersion) {
157      arkProjectConfig.minPlatformVersion = moduleJsonInfo.app.minAPIVersion;
158    }
159    if (moduleJsonInfo.module) {
160      arkProjectConfig.moduleName = moduleJsonInfo.module.name;
161    }
162    if (moduleJsonInfo.app) {
163      arkProjectConfig.bundleName = moduleJsonInfo.app.bundleName;
164    }
165  }
166
167  // Hotreload attributes are initialized by arkui in main.js, just copy them.
168  arkProjectConfig.hotReload = mainProjectConfig.hotReload;
169  arkProjectConfig.coldReload = mainProjectConfig.coldReload;
170  arkProjectConfig.isFirstBuild = mainProjectConfig.isFirstBuild;
171  arkProjectConfig.patchAbcPath = mainProjectConfig.patchAbcPath;
172  arkProjectConfig.changedFileList = mainProjectConfig.changedFileList;
173
174  if (mainProjectConfig.es2abcCompileTsInAotMode || mainProjectConfig.es2abcCompileTsInNonAotMode) {
175    arkProjectConfig.pandaMode = mainProjectConfig.pandaMode;
176    arkProjectConfig.processTs = mainProjectConfig.processTs;
177  }
178  arkProjectConfig.compileMode = projectConfig.compileMode;
179  arkProjectConfig.entryObj = mainProjectConfig.entryObj;
180  arkProjectConfig.entryArrayForObf = mainProjectConfig.entryArrayForObf;
181  arkProjectConfig.cardEntryObj = mainProjectConfig.cardEntryObj;
182  if (mainProjectConfig.updateVersionInfo) {
183    arkProjectConfig.updateVersionInfo = mainProjectConfig.updateVersionInfo;
184  }
185  if (!isDebug(projectConfig)) {
186    arkProjectConfig.useTsHar = mainProjectConfig.useTsHar;
187    startFilesEvent(EventList.OBFUSCATION_INITIALIZATION, performancePrinter.timeSumPrinter);
188    const recordInfo = MemoryMonitor.recordStage(MemoryDefine.INIT_ARK_PROJECT_CONFIG);
189    MemoryMonitor.stopRecordStage(recordInfo);
190    initObfLogger(share);
191    initObfuscationConfig(projectConfig, arkProjectConfig, printObfLogger);
192    endFilesEvent(EventList.OBFUSCATION_INITIALIZATION, performancePrinter.timeSumPrinter);
193  } else {
194    // Set performance printer to undefined in case we cannot disable it without obfuscation initialization
195    blockPrinter();
196  }
197  return arkProjectConfig;
198}
199
200function initTerserConfig(projectConfig: any, logger: any, mergedObConfig: MergedConfig, isHarCompiled: boolean): any {
201  const isCompact = projectConfig.obfuscationOptions ? mergedObConfig.options.compact : isHarCompiled;
202  const minifyOptions = {
203    format: {
204      beautify: !isCompact,
205      indent_level: 2
206    },
207    compress: {
208      join_vars: false,
209      sequences: 0,
210      directives: false,
211      drop_console: mergedObConfig.options.removeLog
212    },
213    mangle: {
214      reserved: mergedObConfig.reservedNames,
215      toplevel: mergedObConfig.options.enableToplevelObfuscation
216    }
217  };
218  const applyNameCache: string | undefined = mergedObConfig.options.applyNameCache;
219  if (applyNameCache && applyNameCache.length > 0) {
220    if (fs.existsSync(applyNameCache)) {
221      minifyOptions.nameCache = JSON.parse(fs.readFileSync(applyNameCache, 'utf-8'));
222    } else {
223      logger.error(`ArkTS:ERROR Namecache file ${applyNameCache} does not exist`);
224    }
225  } else {
226    if (projectConfig.obfuscationOptions && projectConfig.obfuscationOptions.obfuscationCacheDir) {
227      const defaultNameCachePath: string = path.join(projectConfig.obfuscationOptions.obfuscationCacheDir, 'nameCache.json');
228      if (fs.existsSync(defaultNameCachePath)) {
229        minifyOptions.nameCache = JSON.parse(fs.readFileSync(defaultNameCachePath, 'utf-8'));
230      } else {
231        minifyOptions.nameCache = {};
232      }
233    }
234  }
235
236  if (mergedObConfig.options.enablePropertyObfuscation) {
237    minifyOptions.mangle.properties = {
238      reserved: mergedObConfig.reservedPropertyNames,
239      keep_quoted: !mergedObConfig.options.enableStringPropertyObfuscation
240    };
241  }
242  return minifyOptions;
243}
244
245// Scan the source code of project and libraries to collect whitelists.
246export function readProjectAndLibsSource(allFiles: Set<string>, mergedObConfig: MergedConfig, arkObfuscator: ArkObfuscator, isHarCompiled: boolean,
247  keepFilesAndDependencies: Set<string>): void {
248  if (mergedObConfig?.options === undefined || mergedObConfig.options.disableObfuscation || allFiles.size === 0) {
249    return;
250  }
251  const obfOptions = mergedObConfig.options;
252  let projectAndLibs: ReseverdSetForArkguard = readProjectPropertiesByCollectedPaths(allFiles,
253    {
254      mNameObfuscation: {
255        mEnable: true,
256        mReservedProperties: [],
257        mRenameProperties: obfOptions.enablePropertyObfuscation,
258        mKeepStringProperty: !obfOptions.enableStringPropertyObfuscation,
259        mEnableAtKeep: obfOptions.enableAtKeep
260      },
261      mExportObfuscation: obfOptions.enableExportObfuscation,
262      mKeepFileSourceCode: {
263        mKeepSourceOfPaths: new Set(),
264        mkeepFilesAndDependencies: keepFilesAndDependencies,
265      }
266    }, isHarCompiled);
267  if (obfOptions.enablePropertyObfuscation) {
268    arkObfuscator.addReservedSetForPropertyObf(projectAndLibs);
269  }
270  if (obfOptions.enableExportObfuscation) {
271    arkObfuscator.addReservedSetForDefaultObf(projectAndLibs);
272  }
273  if (obfOptions.enableAtKeep) {
274    // emit atKeep names and consumer configs
275    arkObfuscator.obfConfigResolver.emitConsumerConfigFiles();
276  }
277}
278
279function processPlatformInfo(arkConfig: ArkConfig): void {
280  const arkPlatformPath: string = getArkBuildDir(arkConfig.arkRootPath);
281  if (isWindows()) {
282    arkConfig.es2abcPath = path.join(arkPlatformPath, 'bin', 'es2abc.exe');
283    arkConfig.ts2abcPath = path.join(arkPlatformPath, 'src', 'index.js');
284    arkConfig.mergeAbcPath = path.join(arkPlatformPath, 'bin', 'merge_abc.exe');
285    arkConfig.js2abcPath = path.join(arkPlatformPath, 'bin', 'js2abc.exe');
286    arkConfig.aotCompilerPath = path.join(arkPlatformPath, 'bin', 'ark_aot_compiler.exe');
287    return;
288  }
289  if (isLinux() || isMac()) {
290    arkConfig.es2abcPath = path.join(arkPlatformPath, 'bin', 'es2abc');
291    arkConfig.ts2abcPath = path.join(arkPlatformPath, 'src', 'index.js');
292    arkConfig.mergeAbcPath = path.join(arkPlatformPath, 'bin', 'merge_abc');
293    arkConfig.js2abcPath = path.join(arkPlatformPath, 'bin', 'js2abc');
294    arkConfig.aotCompilerPath = path.join(arkPlatformPath, 'bin', 'ark_aot_compiler');
295    return;
296  }
297  if (isHarmonyOs()) {
298    arkConfig.es2abcPath = path.join(arkPlatformPath, 'bin', 'es2abc');
299    return;
300  }
301}
302
303function processCompatibleVersion(projectConfig: Object, arkConfig: ArkConfig): void {
304  const platformPath: string = getArkBuildDir(arkConfig.arkRootPath);
305  if (projectConfig.minPlatformVersion && projectConfig.minPlatformVersion.toString() === '8') {
306    // use ts2abc to compile apps with 'CompatibleSdkVersion' set to 8
307    arkConfig.ts2abcPath = path.join(platformPath, 'legacy_api8', 'src', 'index.js');
308    projectConfig.pandaMode = TS2ABC;
309  }
310}
311
312export const utProcessArkConfig = {
313  processCompatibleVersion,
314  initTerserConfig
315};
316