• 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 rollupObject 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
16// Execute this file first to avoid circular dependency problems
17
18import { expect } from 'chai';
19import mocha from 'mocha';
20import fs from "fs";
21import path from "path";
22import MagicString from 'magic-string';
23import sinon from 'sinon';
24import * as ts from 'typescript'
25
26import {
27  getBuildModeInLowerCase,
28  getPackageInfo,
29  genSourceMapFileName,
30  isOhModules,
31  isEs2Abc,
32  writeArkguardObfuscatedSourceCode,
33  writeMinimizedSourceCode,
34  tryMangleFileName,
35  transformLazyImport
36} from '../../lib/ark_utils';
37import {
38  DEBUG,
39  RELEASE,
40  OH_MODULES,
41  EXTNAME_TS,
42  EXTNAME_JS,
43  EXTNAME_ETS,
44  OBFUSCATION_TOOL
45} from '../../lib/fast_build/ark_compiler/common/ark_define';
46import RollUpPluginMock from './mock/rollup_mock/rollup_plugin_mock';
47import {
48  BUNDLE_NAME_DEFAULT,
49  ENTRY_MODULE_NAME_DEFAULT,
50  EXTNAME_MAP,
51  ENTRYABILITY_JS
52} from './mock/rollup_mock/common';
53import projectConfig from './utils/processProjectConfig';
54import {
55  ES2ABC,
56  TS2ABC
57} from '../../lib/pre_define';
58import { changeFileExtension } from '../../lib/fast_build/ark_compiler/utils';
59import ModuleSourceFileMock from './mock/class_mock/module_source_files_mock';
60import {
61  genTemporaryPath,
62  toUnixPath
63} from '../../lib/utils';
64import {
65  ObConfigResolver,
66  MergedConfig
67} from '../../lib/fast_build/ark_compiler/common/ob_config_resolver';
68import {
69  utProcessArkConfig
70} from '../../lib/fast_build/ark_compiler/common/process_ark_config';
71import { ModuleSourceFile } from '../../lib/fast_build/ark_compiler/module/module_source_file';
72import { PROJECT_ROOT, TERSER_PROCESSED_EXPECTED_CODE } from './mock/rollup_mock/path_config';
73import { GEN_ABC_PLUGIN_NAME } from '../../lib/fast_build/ark_compiler/common/ark_define';
74import { SourceMapGenerator } from '../../lib/fast_build/ark_compiler/generate_sourcemap';
75import { ArkObfuscator } from 'arkguard';
76
77mocha.describe('test ark_utils file api', function () {
78  mocha.before(function () {
79    this.rollup = new RollUpPluginMock();
80  });
81
82  mocha.after(() => {
83    delete this.rollup;
84  });
85
86  mocha.it('1-1: test getBuildModeInLowerCase under build debug', function () {
87    this.rollup.build();
88    const buildMode = getBuildModeInLowerCase(this.rollup.share.projectConfig);
89    expect(buildMode === DEBUG).to.be.true;
90  });
91
92  mocha.it('1-2: test getBuildModeInLowerCase under build release', function () {
93    this.rollup.build(RELEASE);
94    const buildMode = getBuildModeInLowerCase(this.rollup.share.projectConfig);
95    expect(buildMode === RELEASE).to.be.true;
96  });
97
98  mocha.it('1-3: test getBuildModeInLowerCase under preview debug', function () {
99    this.rollup.preview();
100    const buildMode = getBuildModeInLowerCase(this.rollup.share.projectConfig);
101    expect(buildMode === DEBUG).to.be.true;
102  });
103
104  mocha.it('1-4: test getBuildModeInLowerCase under hot reload debug', function () {
105    this.rollup.hotReload();
106    const buildMode = getBuildModeInLowerCase(this.rollup.share.projectConfig);
107    expect(buildMode === DEBUG).to.be.true;
108  });
109
110  mocha.it('2-1: test getPackageInfo under build debug', function () {
111    this.rollup.build();
112    const returnInfo = getPackageInfo(this.rollup.share.projectConfig.aceModuleJsonPath);
113    expect(returnInfo[0] === BUNDLE_NAME_DEFAULT).to.be.true;
114    expect(returnInfo[1] === ENTRY_MODULE_NAME_DEFAULT).to.be.true;
115  });
116
117  mocha.it('2-2: test getPackageInfo under build release', function () {
118    this.rollup.build(RELEASE);
119    const returnInfo = getPackageInfo(this.rollup.share.projectConfig.aceModuleJsonPath);
120    expect(returnInfo[0] === BUNDLE_NAME_DEFAULT).to.be.true;
121    expect(returnInfo[1] === ENTRY_MODULE_NAME_DEFAULT).to.be.true;
122  });
123
124  mocha.it('2-3: test getPackageInfo under preview debug', function () {
125    this.rollup.preview();
126    const returnInfo = getPackageInfo(this.rollup.share.projectConfig.aceModuleJsonPath);
127    expect(returnInfo[0] === BUNDLE_NAME_DEFAULT).to.be.true;
128    expect(returnInfo[1] === ENTRY_MODULE_NAME_DEFAULT).to.be.true;
129  });
130
131  mocha.it('2-4: test getPackageInfo under hot reload debug', function () {
132    this.rollup.hotReload();
133    const returnInfo = getPackageInfo(this.rollup.share.projectConfig.aceModuleJsonPath);
134    expect(returnInfo[0] === BUNDLE_NAME_DEFAULT).to.be.true;
135    expect(returnInfo[1] === ENTRY_MODULE_NAME_DEFAULT).to.be.true;
136  });
137
138  mocha.it('3-1: test genSourceMapFileName under build debug', function () {
139    this.rollup.build();
140    for (const filePath of this.rollup.share.allFiles) {
141      if (filePath.endsWith(EXTNAME_TS) || filePath.endsWith(EXTNAME_JS)) {
142        const originPath = genSourceMapFileName(filePath);
143        const expectedPath = `${filePath}${EXTNAME_MAP}`;
144        expect(originPath === expectedPath).to.be.true;
145      } else if (filePath.endsWith(EXTNAME_ETS)) {
146        const originPath = genSourceMapFileName(filePath);
147        expect(originPath === filePath).to.be.true;
148      }
149    }
150  });
151
152  mocha.it('3-2: test genSourceMapFileName under build release', function () {
153    this.rollup.build(RELEASE);
154    for (const filePath of this.rollup.share.allFiles) {
155      if (filePath.endsWith(EXTNAME_TS) || filePath.endsWith(EXTNAME_JS)) {
156        const originPath = genSourceMapFileName(filePath);
157        const expectedPath = `${filePath}${EXTNAME_MAP}`;
158        expect(originPath === expectedPath).to.be.true;
159      }
160    }
161  });
162
163  mocha.it('3-3: test genSourceMapFileName under preview debug', function () {
164    this.rollup.preview();
165    for (const filePath of this.rollup.share.allFiles) {
166      if (filePath.endsWith(EXTNAME_TS) || filePath.endsWith(EXTNAME_JS)) {
167        const originPath = genSourceMapFileName(filePath);
168        const expectedPath = `${filePath}${EXTNAME_MAP}`;
169        expect(originPath === expectedPath).to.be.true;
170      }
171    }
172  });
173
174  mocha.it('3-4: test genSourceMapFileName under hot reload debug', function () {
175    this.rollup.hotReload();
176    for (const filePath of this.rollup.share.allFiles) {
177      if (filePath.endsWith(EXTNAME_TS) || filePath.endsWith(EXTNAME_JS)) {
178        const originPath = genSourceMapFileName(filePath);
179        const expectedPath = `${filePath}${EXTNAME_MAP}`;
180        expect(originPath === expectedPath).to.be.true;
181      }
182    }
183  });
184
185  mocha.it('4-1: test isOhModules under build debug', function () {
186    this.rollup.build();
187    const returnInfo = isOhModules(this.rollup.share.projectConfig);
188    expect(returnInfo === false).to.be.true;
189    this.rollup.share.projectConfig.packageDir = OH_MODULES;
190    const returnInfoOh = isOhModules(this.rollup.share.projectConfig);
191    expect(returnInfoOh).to.be.true;
192  });
193
194  mocha.it('4-2: test isOhModules under build release', function () {
195    this.rollup.build(RELEASE);
196    const returnInfo = isOhModules(this.rollup.share.projectConfig);
197    expect(returnInfo === false).to.be.true;
198    this.rollup.share.projectConfig.packageDir = OH_MODULES;
199    const returnInfoOh = isOhModules(this.rollup.share.projectConfig);
200    expect(returnInfoOh).to.be.true;
201  });
202
203  mocha.it('4-3: test isOhModules under preview debug', function () {
204    this.rollup.preview();
205    const returnInfo = isOhModules(this.rollup.share.projectConfig);
206    expect(returnInfo === false).to.be.true;
207    this.rollup.share.projectConfig.packageDir = OH_MODULES;
208    const returnInfoOh = isOhModules(this.rollup.share.projectConfig);
209    expect(returnInfoOh).to.be.true;
210  });
211
212  mocha.it('4-4: test isOhModules under hot reload debug', function () {
213    this.rollup.hotReload();
214    const returnInfo = isOhModules(this.rollup.share.projectConfig);
215    expect(returnInfo === false).to.be.true;
216    this.rollup.share.projectConfig.packageDir = OH_MODULES;
217    const returnInfoOh = isOhModules(this.rollup.share.projectConfig);
218    expect(returnInfoOh).to.be.true;
219  });
220
221  mocha.it('4-5: test isOhModules under hot fix debug', function () {
222    projectConfig.buildMode = DEBUG;
223    const returnInfo = isOhModules(projectConfig);
224    expect(returnInfo).to.be.true;
225  });
226
227  mocha.it('4-6: test isOhModules under hot fix release', function () {
228    projectConfig.buildMode = RELEASE;
229    const returnInfo = isOhModules(projectConfig);
230    expect(returnInfo).to.be.true;
231  });
232
233  mocha.it('5-1: test isEs2Abc under build debug', function () {
234    this.rollup.build();
235    this.rollup.share.projectConfig.pandaMode = ES2ABC;
236    const returnInfo = isEs2Abc(this.rollup.share.projectConfig);
237    expect(returnInfo).to.be.true;
238
239    this.rollup.share.projectConfig.pandaMode = TS2ABC;
240    const returnInfoTS2ABC = isEs2Abc(this.rollup.share.projectConfig);
241    expect(returnInfoTS2ABC === false).to.be.true;
242
243    this.rollup.share.projectConfig.pandaMode = "undefined";
244    const returnInfoUndef = isEs2Abc(this.rollup.share.projectConfig);
245    expect(returnInfoUndef).to.be.true;
246
247    this.rollup.share.projectConfig.pandaMode = undefined;
248    const returnInfoUndefined = isEs2Abc(this.rollup.share.projectConfig);
249    expect(returnInfoUndefined).to.be.true;
250  });
251
252  mocha.it('5-2: test isEs2Abc under build release', function () {
253    this.rollup.build(RELEASE);
254    this.rollup.share.projectConfig.pandaMode = ES2ABC;
255    const returnInfo = isEs2Abc(this.rollup.share.projectConfig);
256    expect(returnInfo).to.be.true;
257
258    this.rollup.share.projectConfig.pandaMode = TS2ABC;
259    const returnInfoTS2ABC = isEs2Abc(this.rollup.share.projectConfig);
260    expect(returnInfoTS2ABC).to.be.false;
261  });
262
263  mocha.it('5-3: test isEs2Abc under preview debug', function () {
264    this.rollup.preview();
265    this.rollup.share.projectConfig.pandaMode = ES2ABC;
266    const returnInfo = isEs2Abc(this.rollup.share.projectConfig);
267    expect(returnInfo).to.be.true;
268
269    this.rollup.share.projectConfig.pandaMode = TS2ABC;
270    const returnInfoTS2ABC = isEs2Abc(this.rollup.share.projectConfig);
271    expect(returnInfoTS2ABC).to.be.false;
272  });
273
274  mocha.it('5-4: test isEs2Abc under hot reload debug', function () {
275    this.rollup.hotReload();
276    this.rollup.share.projectConfig.pandaMode = ES2ABC;
277    const returnInfo = isEs2Abc(this.rollup.share.projectConfig);
278    expect(returnInfo).to.be.true;
279
280    this.rollup.share.projectConfig.pandaMode = TS2ABC;
281    const returnInfoTS2ABC = isEs2Abc(this.rollup.share.projectConfig);
282    expect(returnInfoTS2ABC).to.be.false;
283  });
284
285  mocha.it('5-5: test isEs2Abc under hot fix debug', function () {
286    projectConfig.buildMode = DEBUG;
287    projectConfig.pandaMode = ES2ABC;
288    const returnInfo = isEs2Abc(projectConfig);
289    expect(returnInfo).to.be.true;
290
291    projectConfig.pandaMode = TS2ABC;
292    const returnInfoTS2ABC = isEs2Abc(projectConfig);
293    expect(returnInfoTS2ABC).to.be.false;
294  });
295
296  mocha.it('5-6: test isEs2Abc under hot fix release', function () {
297    projectConfig.buildMode = RELEASE;
298    projectConfig.pandaMode = ES2ABC;
299    const returnInfo = isEs2Abc(projectConfig);
300    expect(returnInfo).to.be.true;
301
302    projectConfig.pandaMode = TS2ABC;
303    const returnInfoTS2ABC = isEs2Abc(projectConfig);
304    expect(returnInfoTS2ABC).to.be.false;
305  });
306
307  mocha.it('6-1: test the error message of writeArkguardObfuscatedSourceCode', async function () {
308    this.rollup.build(RELEASE);
309    SourceMapGenerator.initInstance(this.rollup);
310    const logger = this.rollup.share.getLogger(GEN_ABC_PLUGIN_NAME);
311    const stub = sinon.stub(logger, 'error');
312    const red: string = '\x1B[31m';
313    try {
314      await writeArkguardObfuscatedSourceCode(
315        {content: undefined, buildFilePath: '', relativeSourceFilePath: '', originSourceFilePath: ''},
316        logger, this.rollup.share.projectConfig, {});
317    } catch (e) {
318    }
319    expect(stub.calledWith(red,
320      `ArkTS:INTERNAL ERROR: Failed to obfuscate file '' with arkguard. TypeError: Cannot read properties of undefined (reading 'obfuscate')`
321    )).to.be.true;
322    stub.restore();
323    SourceMapGenerator.cleanSourceMapObject();
324  });
325
326  mocha.it('7-1: test the error message of writeMinimizedSourceCode', async function () {
327    this.rollup.build(RELEASE);
328    const logger = this.rollup.share.getLogger(GEN_ABC_PLUGIN_NAME);
329    const stub = sinon.stub(logger, 'error');
330    const red: string = '\x1B[31m';
331    const reset: string = '\x1B[39m';
332    try {
333      await writeMinimizedSourceCode(undefined, '', logger);
334    } catch (e) {
335    }
336    expect(stub.calledWith(red,
337      'ArkTS:INTERNAL ERROR: Failed to obfuscate source code for ', reset
338    )).to.be.true;
339    stub.restore();
340  });
341
342  mocha.it('8-1: test tryMangleFileName when obfuscation is disabled', async function () {
343    const filePath = '/mnt/application/entry/build/default/cache/default/default@CompileArkTS/esmodule/release/src/main/ets/entryability/EntryAbility.ts';
344    const originalFilePath = '/mnt/application/entry/src/main/ets/entryability/EntryAbility.ets';
345    const projectConfig = {
346      projectRootPath: '/mnt/application',
347      packageDir: 'oh_modules',
348      modulePathMap: {
349        entry: '/mnt/application/entry'
350      },
351      obfuscationMergedObConfig: {
352        options: {
353          enableFileNameObfuscation: false
354        }
355      }
356    }
357    const result = tryMangleFileName(filePath, projectConfig, originalFilePath);
358    expect(result === filePath).to.be.true;
359  });
360
361  mocha.it('8-2: test tryMangleFileName when obfuscation is enabled', async function () {
362    const filePath = '/mnt/application/entry/build/default/cache/default/default@CompileArkTS/esmodule/release/src/main/ets/entryability/EntryAbility.ts';
363    const originalFilePath = '/mnt/application/entry/src/main/ets/entryability/EntryAbility.ets';
364    const newFilePath = '/mnt/application/entry/build/default/cache/default/default@CompileArkTS/esmodule/release/src/main/ets/a/b.ts';
365    const projectConfig = {
366      projectRootPath: '/mnt/application',
367      packageDir: 'oh_modules',
368      modulePathMap: {
369        entry: '/mnt/application/entry'
370      },
371      obfuscationMergedObConfig: {
372        options: {
373          enableFileNameObfuscation: true
374        }
375      }
376    }
377
378    const printerConfig = {
379      //Print obfuscation time&memory usage of all files and obfuscation processes
380      mFilesPrinter: false,
381      //Print time&memory usage of a single file obfuscation in transform processes
382      mSingleFilePrinter: false,
383      //Print sum up time of transform processes during obfuscation
384      mSumPrinter: false,
385      //Output path of printer
386      mOutputPath: ""
387    }
388
389    const arkguardConfig = {
390      mRenameFileName: {
391        mEnable: true,
392        mNameGeneratorType: 1,
393        mReservedFileNames: [
394          'entry',
395          'mnt',
396          'application',
397          'entry',
398          'src',
399          'main',
400          'ets',
401          'build',
402          'default',
403          'cache',
404          'default',
405          'default@CompileArkTS',
406          'esmodule',
407          'release'
408        ],
409      },
410      mPerformancePrinter: printerConfig
411    }
412
413    let arkObfuscator: ArkObfuscator = new ArkObfuscator();
414    arkObfuscator.init(arkguardConfig);
415    const content = `function foo() {}`
416    const obfuscateResult = arkObfuscator.obfuscate(content, filePath, undefined);
417    obfuscateResult.then(result => {
418      const afterObfuscateFilePath = result.filePath;
419      expect(afterObfuscateFilePath === newFilePath).to.be.true;
420    })
421
422    const result = tryMangleFileName(filePath, projectConfig, originalFilePath);
423    expect(result === newFilePath).to.be.true;
424  });
425
426  mocha.it('9-1: test writeArkguardObfuscatedSourceCode when obfuscation is enabled', async function () {
427    this.rollup.build(RELEASE);
428    const logger = this.rollup.share.getLogger(GEN_ABC_PLUGIN_NAME);
429    const arkguardConfig = {
430      mCompact: false,
431      mDisableConsole: false,
432      mSimplify: false,
433      mRemoveComments: true,
434      mNameObfuscation: {
435        mEnable: true,
436        mNameGeneratorType: 1,
437        mReservedNames: [],
438        mRenameProperties: false,
439        mReservedProperties: [],
440        mKeepStringProperty: true,
441        mTopLevel: false,
442        mReservedToplevelNames: [],
443        mUniversalReservedProperties: [],
444        mUniversalReservedToplevelNames: [],
445      },
446      mRemoveDeclarationComments: {
447        mEnable: true,
448        mReservedComments: []
449      },
450      mEnableSourceMap: true,
451      mEnableNameCache: true,
452      mRenameFileName: {
453        mEnable: false,
454        mNameGeneratorType: 1,
455        mReservedFileNames: [],
456      },
457      mExportObfuscation: false,
458      mPerformancePrinter: {
459        mFilesPrinter: false,
460        mSingleFilePrinter: false,
461        mSumPrinter: false,
462        mOutputPath: ""
463      },
464      mKeepFileSourceCode: {
465        mKeepSourceOfPaths: new Set(),
466        mkeepFilesAndDependencies: new Set(),
467      },
468    };
469
470    const sourceMapGenerator: SourceMapGenerator = SourceMapGenerator.initInstance(this.rollup);
471    sourceMapGenerator.setNewSoureMaps(false);
472    const arkObfuscator: ArkObfuscator = new ArkObfuscator();
473    arkObfuscator.init(arkguardConfig);
474
475    const projectConfig = {
476      projectRootPath: PROJECT_ROOT,
477      arkObfuscator: arkObfuscator,
478      packageDir: 'oh_modules',
479      localPackageSet: new Set(),
480      useTsHar: false,
481      useNormalized: false
482    };
483
484    const sourceFileName = 'sourceFile.ts';
485    const originalSourceFilePath = path.join(__dirname, '../../test/ark_compiler_ut/testdata/har_obfuscation', sourceFileName);
486    const moduleInfo = {
487      content: fs.readFileSync(originalSourceFilePath, 'utf-8'),
488      buildFilePath: `${PROJECT_ROOT}/har_obfuscation/build/default/cache/${sourceFileName}`,
489      relativeSourceFilePath: `har_obfuscation/${sourceFileName}`,
490      originSourceFilePath: originalSourceFilePath,
491      rollupModuleId: originalSourceFilePath
492    };
493
494    try {
495      await writeArkguardObfuscatedSourceCode(moduleInfo, logger, projectConfig, {});
496    } catch (e) {
497    }
498    const expectedResult = `export function add(d: number, e: number): number {
499    return d + e;
500}
501export function findElement<a>(b: a[], c: (item: a) => boolean): a | undefined {
502    return b.find(c);
503}
504`;
505    const result = fs.readFileSync(moduleInfo.buildFilePath, 'utf-8');
506    expect(result === expectedResult).to.be.true;
507
508    const declFileName = 'sourceFile.d.ts';
509    const originalDeclFilePath = path.join(__dirname, '../../test/ark_compiler_ut/testdata/har_obfuscation/', declFileName);
510
511    const declModuleInfo = {
512      content: fs.readFileSync(originalDeclFilePath, 'utf-8'),
513      buildFilePath: `${PROJECT_ROOT}/har_obfuscation/build/default/cache/${declFileName}`,
514      relativeSourceFilePath: `har_obfuscation/build/default/cache/${declFileName}`,
515      originSourceFilePath: originalSourceFilePath,
516      rollupModuleId: originalSourceFilePath
517    };
518
519    try {
520      await writeArkguardObfuscatedSourceCode(declModuleInfo, logger, projectConfig, {});
521    } catch (e) {
522    }
523
524    const expectedDeclResult = `export declare function add(d: number, e: number): number;
525export declare function findElement<a>(b: a[], c: (item: a) => boolean): a | undefined;
526`;
527    const declResult = fs.readFileSync(declModuleInfo.buildFilePath, 'utf-8');
528    expect(declResult === expectedDeclResult).to.be.true;
529  });
530
531  mocha.it('10-1: test transformLazyImport (js code): perform lazy conversion', function () {
532    const code: string = `
533    import { test } from "./test";
534    import { test1 as t } from "./test1";
535    const a = "a" + test() + t();
536    `;
537    const expectCode: string = 'import lazy { test } from "./test";\n' +
538    'import lazy { test1 as t } from "./test1";\n' +
539    'const a = "a" + test() + t();\n';
540    const result: string = transformLazyImport(code, undefined, "index.js");
541    expect(result === expectCode).to.be.true;
542  });
543
544  mocha.it('10-2: test transformLazyImport (js code): no lazy conversion', function () {
545    const code: string = `
546    import lazy { test } from "./test";
547    import lazy { test1 as t } from "./test1";
548    import test2 from "./test2";
549    import * as test3 from "./test3";
550    import test4, { test5 } from "./test4";
551    import "test6";
552    `;
553    const expectCode: string = 'import lazy { test } from "./test";\n' +
554    'import lazy { test1 as t } from "./test1";\n' +
555    'import test2 from "./test2";\n' +
556    'import * as test3 from "./test3";\n' +
557    'import test4, { test5 } from "./test4";\n' +
558    'import "test6";\n';
559    const result: string = transformLazyImport(code, undefined, "index.js");
560    expect(result === expectCode).to.be.true;
561  });
562});