1/*
2 * Copyright (c) 2024 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
16
17import type { RawSourceMap } from 'typescript';
18import Benchmark from 'benchmark';
19import {
20  ArkObfuscator,
21  renameIdentifierModule
22} from '../../src/ArkObfuscator';
23import {
24  decodeSourcemap,
25  mergeSourceMap,
26  Source,
27  SourceMapLink
28} from '../../src/utils/SourceMapMergingUtil';
29import {
30  IDENTIFIER_CACHE,
31  MEM_METHOD_CACHE,
32} from '../../src/utils/NameCacheUtil';
33
34const benchmarkSuite = new Benchmark.Suite;
35const namePaddingSize: number = 56;
36const dataPaddingSize: number = 16;
37
38const previousMap = {
39  version: 3,
40  file: 'EntryAbility.ts',
41  sourceRoot: '',
42  sources: [ 'entry/src/main/ets/entryability/EntryAbility.ts' ],
43  names: [],
44  mappings: 'OAAO,SAAS;OACT,KAAK;YACL,MAAM;AAEb,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,SAAS;IACjD,QAAQ,CAAC,IAAI,EAAE,WAAW;'
45    + 'QACxB,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,kBAAkB,CAAC,CAAC;IAClE,CAAC;'
46    + 'IAED,SAAS;QACP,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,mBAAmB,CAAC,CAAC;'
47    + 'IACnE,CAAC;IAED,mBAAmB,CAAC,WAAW,EAAE,MAAM,CAAC,WAAW;QACjD,yDAAyD;'
48    + 'QACzD,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,6BAA6B,CAAC,CAAC;'
49    + 'QAE3E,WAAW,CAAC,WAAW,CAAC,aAAa,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;YACnD,IAAI,GAAG,CAAC,IAAI,EAAE;'
50    + 'gBACZ,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,+CAA+C,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;'
51    + 'gBAC3G,OAAO;aACR;YACD,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,oDAAoD,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;'
52    + 'QAClH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,oBAAoB;QAClB,yDAAyD;QACzD,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,8BAA8B,CAAC,CAAC;'
53    + 'IAC9E,CAAC;IAED,YAAY;QACV,oCAAoC;QACpC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,sBAAsB,CAAC,CAAC;IACtE,CAAC;'
54    + 'IAED,YAAY;QACV,iCAAiC;QACjC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,sBAAsB,CAAC,CAAC;IACtE,CAAC;CACF',
55  sourcesContent: undefined
56};
57
58const currentMap = {
59  version: 3,
60  file: 'EntryAbility.ts',
61  sourceRoot: '',
62  sources: [
63    'entry/build/default/cache/default/default@CompileArkTS/esmodule/release/entry/src/main/ets/entryability/EntryAbility.ts'
64  ],
65  names: [],
66  mappings: 'AAAA,OAAO,SAAS,MAAM,6BAA6B,CAAC;AACpD,OAAO,KAAK,MAAM,aAAa,CAAC;AAChC,OAAO,KAAK,MAAM,MAAM,cAAc,CAAC;'
67    + 'AACvC,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,SAAS;IAC/C,QAAQ,CAAC,CAAI,EAAE,CAAW;'
68    + 'QACtB,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,kBAAkB,CAAC,CAAC;'
69    + 'IACpE,CAAC;IACD,SAAS;QACL,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,mBAAmB,CAAC,CAAC;'
70    + 'IACrE,CAAC;IACD,mBAAmB,CAAC,GAAa,MAAM,CAAC,WAAW;QAE/C,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,6BAA6B,CAAC,CAAC;'
71    + 'QAC3E,EAAY,WAAW,CAAC,aAAa,EAAE,CAAC,CAAG,EAAE,CAAI,EAAE,EAAE;YACjD,IAAI,EAAI,IAAI,EAAE;'
72    + 'gBACV,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,+CAA+C,EAAE,IAAI,CAAC,SAAS,GAAK,IAAI,EAAE,CAAC,CAAC;'
73    + 'gBAC3G,OAAO;aACV;YACD,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,oDAAoD,EAAE,IAAI,CAAC,SAAS,GAAM,IAAI,EAAE,CAAC,CAAC;'
74    + 'QACpH,CAAC,CAAC,CAAC;IACP,CAAC;IACD,oBAAoB;QAEhB,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,8BAA8B,CAAC,CAAC;'
75    + 'IAChF,CAAC;IACD,YAAY;QAER,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,sBAAsB,CAAC,CAAC;IACxE,CAAC;IACD,YAAY;'
76    + 'QAER,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,sBAAsB,CAAC,CAAC;IACxE,CAAC;CACJ',
77  sourcesContent: undefined
78};
79
80renameIdentifierModule.nameCache = new Map([
81  [
82    'IdentifierCache',
83    new Map([
84      ['EntryAbility#onWindowStageCreate#windowStage', 'a'],
85      ['EntryAbility#onWindowStageCreate#__function', 'b'],
86      ['EntryAbility#onWindowStageCreate#$0#err', 'c'],
87      ['EntryAbility#onWindowStageCreate#$0#data', 'd'],
88      ['EntryAbility#onCreate#want', 'e'],
89      ['EntryAbility#onCreate#launchParam', 'f']
90    ])
91  ],
92  [
93    'MemberMethodCache',
94    new Map([
95      ['onCreate:5:5:7:6', 'onCreate'],
96      ['onDestroy:8:5:10:6', 'onDestroy'],
97      ['onWindowStageCreate:11:5:20:6', 'onWindowStageCreate'],
98      ['onWindowStageDestroy:21:5:23:6', 'onWindowStageDestroy'],
99      ['onForeground:24:5:26:6', 'onForeground'],
100      ['onBackground:27:5:29:6', 'onBackground']
101    ])
102  ]
103]);
104
105function alignColumn(name, ops, variance, runsSampled) {
106  const namePadding = name.padEnd(namePaddingSize);
107  const opsPadding = ops.padEnd(dataPaddingSize);
108  const variancePadding = variance.padEnd(dataPaddingSize);
109  const runsSampledPadding = runsSampled.toString().padEnd(dataPaddingSize);
110  return `| ${namePadding} | ${opsPadding} | ${variancePadding} | ${runsSampledPadding} |`;
111}
112
113class ArkObfuscatorMock extends ArkObfuscator {
114  convertLineBasedOnSourceMapMock(targetCache: string, sourceMapLink?: SourceMapLink): Map<string, string> {
115      // Ignore `property is private and only accessible within class` error
116      // @ts-ignore
117      return this.convertLineBasedOnSourceMap(targetCache, sourceMapLink);
118  }
119};
120
121describe('SourceMap Benchmark Test', function() {
122  after(() => {
123      benchmarkSuite.on('complete', function() {
124        console.log('\n  Benchmark Summary Results:\n');
125        console.log(' ', alignColumn('Test Name', 'Ops/Sec', 'Variance', 'Number of Runs'));
126        console.log(' ',alignColumn('-'.repeat(namePaddingSize), '-'.repeat(dataPaddingSize),
127                                '-'.repeat(dataPaddingSize), '-'.repeat(dataPaddingSize)));
128        this.forEach(result => {
129          console.log(' ', alignColumn(result.name, `${Math.round(result.hz)}`,
130                      `${result.stats.rme.toFixed(2)}%`, result.stats.sample.length));
131        });
132        console.log('\n');
133      });
134  });
135
136  it('run sourcemap benchmark test', function() {
137      let decodedSourceMap;
138      benchmarkSuite.add('decodeSourcemap', function() {
139        decodedSourceMap = decodeSourcemap(previousMap as RawSourceMap);
140      });
141
142      benchmarkSuite.add('mergeSourceMap', function() {
143        mergeSourceMap(previousMap as RawSourceMap, currentMap as RawSourceMap);
144      });
145
146      benchmarkSuite.add('convertLineBasedOnSourceMap', function() {
147        const sourceFileName = previousMap.sources?.length === 1 ? previousMap.sources[0] : '';
148        const source = new Source(sourceFileName, null);
149        const sourceMapLink = new SourceMapLink(decodedSourceMap!, [source]);
150        const arkObfuscator = new ArkObfuscatorMock();
151        arkObfuscator.convertLineBasedOnSourceMapMock(IDENTIFIER_CACHE, sourceMapLink);
152        arkObfuscator.convertLineBasedOnSourceMapMock(MEM_METHOD_CACHE, sourceMapLink);
153      });
154
155      benchmarkSuite.on('cycle', (event: any) => {
156        console.log(' ', String(event.target));
157      });
158
159      benchmarkSuite.run({ 'async': true });
160  });
161});