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 16import childProcess from 'child_process'; 17import path from 'path'; 18import fs from 'fs'; 19import { 20 FILESINFO_TXT, 21 MODULES_ABC, 22 SOURCEMAPS 23} from '../../pre_define'; 24import { 25 mkDir, 26 writeFileSync 27} from '../../utils'; 28import { 29 ES_ANNOTATIONS, 30 SOURCEMAPS_JSON, 31 WIDGETS_ABC, 32 yellow, 33 DECORATOR_WHITE_LIST, 34 BYTECODE_OBFUSCATION_PROPERTY_WHITE_LIST, 35 IDENTIFIER_CACHE, 36 MEMBER_METHOD_CACHE 37} from './common/ark_define'; 38import { 39 ReservedNameInfo, 40 separateUniversalReservedItem, 41 UnobfuscationCollections, 42} from 'arkguard'; 43import { 44 SourceMapSegmentObj, 45 decodeSourcemap, 46 SourceMapLink, 47 Source 48} from 'arkguard/lib/utils/SourceMapMergingUtil'; 49import { MergedConfig } from './common/ob_config_resolver'; 50import { ModuleInfo } from './module/module_mode'; 51import { initArkConfig } from './common/process_ark_config'; 52import esInfo from 'arkguard/lib/configs/preset/es_reserved_properties.json'; 53import { 54 CommonLogger, 55 LogData, 56 LogDataFactory 57} from './logger'; 58import { 59 ArkTSErrorDescription, 60 ErrorCode 61} from './error_code'; 62import { FILE_WHITE_LISTS } from 'arkguard/lib/utils/ProjectCollections'; 63import { 64 FileKeepInfo, 65 FileReservedInfo 66} from 'arkguard/lib/utils/ProjectCollections'; 67import { SourceMapGenerator } from './generate_sourcemap'; 68 69const FILE_NAME_CACHE: string = 'FileNameCache'; 70const OBF_NAME_MAP: string = 'ObfNameMap'; 71const PROPERTY_CACHE: string = 'ProPertyCache'; 72const ENTRY_PACKAGE_INFO: string = 'entryPackageInfo'; 73const COMPILE_SDK_VERSION: string = 'compileSdkVersion'; 74 75export class BytecodeObfuscator { 76 public static enable = false; 77 private static instance: BytecodeObfuscator | undefined = undefined; 78 public cmdArgs: string[]; 79 private projectConfig: Object; 80 private logger: Object; 81 private originDir: string; 82 private obfDir: string; 83 private obfuscationCacheDir: string; 84 private bytecodeObfuscateConfig: BytecodeObfuscationConfig; 85 private debugging: boolean; 86 private enhanced: boolean; 87 private arkConfig: ArkConfig; 88 private configPath: string; 89 private nameCachePath: string; 90 private backupSourceMapPath: string; 91 92 constructor(share: Object, arkProjectConfig: Object) { 93 this.projectConfig = Object.assign(arkProjectConfig, share.projectConfig); 94 const rollup = { 95 share: share, 96 }; 97 this.logger = CommonLogger.getInstance(rollup); 98 this.obfuscationCacheDir = this.projectConfig.obfuscationOptions.obfuscationCacheDir; 99 this.originDir = path.join(this.obfuscationCacheDir, 'origin'); 100 this.obfDir = path.join(this.obfuscationCacheDir, 'obf'); 101 this.configPath = path.join(this.obfuscationCacheDir, 'config.json'); 102 this.backupSourceMapPath = path.join(this.originDir, SOURCEMAPS_JSON); 103 /** 104 * When enhanced == true, 105 * a bytecode har will be obfuscated when it is referenced by HAP, 106 * but it is not obfuscated by default 107 */ 108 this.enhanced = false; 109 this.debugging = arkProjectConfig.obfuscationMergedObConfig?.options.bytecodeObf?.debugging; 110 this.arkConfig = initArkConfig(this.projectConfig); 111 this.cmdArgs = []; 112 } 113 114 public static cleanBcObfuscatorObject(): void { 115 if (this.instance) { 116 this.instance = undefined; 117 } 118 if (this.enable) { 119 this.enable = null; 120 } 121 } 122 123 static initForTest(share: Object): void { 124 if (!share.arkProjectConfig.isBytecodeObfEnabled) { 125 this.enable = false; 126 return; 127 } 128 this.enable = true; 129 BytecodeObfuscator.instance = new BytecodeObfuscator(share, share.arkProjectConfig); 130 BytecodeObfuscator.instance.bytecodeObfuscateConfig = 131 new BytecodeObfuscationConfig(share, share.arkProjectConfig); 132 } 133 134 static init(share: Object, arkProjectConfig: Object): void { 135 if (!arkProjectConfig.isBytecodeObfEnabled) { 136 this.enable = false; 137 return; 138 } 139 this.enable = true; 140 BytecodeObfuscator.instance = new BytecodeObfuscator(share, arkProjectConfig); 141 if (!isVersionCompatible( 142 BytecodeObfuscator.instance.projectConfig.compatibleSdkVersion, 143 BytecodeObfuscator.instance.projectConfig.compatibleSdkVersionStage)) { 144 const errInfo: LogData = LogDataFactory.newInstance( 145 ErrorCode.BYTECODE_OBFUSCATION_UNSUPPORT_COMPATIBLESDKVERSION, 146 ArkTSErrorDescription, 147 `bytecodeObfuscation only can be used in the beta3 version of API 12 or higher version`, 148 '', 149 [ 150 'Check the compatibleSdkVersion and compatibleSdkVersionStage in build-profile.json', 151 'if compatibleSdkVersion is set to API 12,then compatibleSdkVersionStage needs to be configured as beta3', 152 ] 153 ); 154 BytecodeObfuscator.instance.logger.printErrorAndExit(errInfo); 155 } 156 if (!fs.existsSync(BytecodeObfuscator.instance.originDir)) { 157 mkDir(BytecodeObfuscator.instance.originDir); 158 } 159 if (!fs.existsSync(BytecodeObfuscator.instance.obfDir)) { 160 mkDir(BytecodeObfuscator.instance.obfDir); 161 } 162 163 BytecodeObfuscator.instance.bytecodeObfuscateConfig = 164 new BytecodeObfuscationConfig(share, arkProjectConfig); 165 this.getInstance().getSystemApiConfigsByCache(); 166 this.getInstance().bytecodeObfuscateConfig.addReservedNames(collectReservedLangForProperty()); 167 } 168 169 static getInstance(): BytecodeObfuscator { 170 return BytecodeObfuscator.instance; 171 } 172 173 public removeStructProp(): void { 174 if (!BytecodeObfuscator.enable || 175 !this.bytecodeObfuscateConfig.obfuscationRules.enableDecoratorObfuscation) { 176 return; 177 } 178 179 const reservedNames = this.bytecodeObfuscateConfig.getReservedNames(); 180 181 for (const item of [...UnobfuscationCollections.reservedStruct]) { 182 if (!reservedNames?.has(item)) { 183 UnobfuscationCollections.reservedStruct.delete(item); 184 } 185 } 186 } 187 188 public execute(moduleInfos: Map<String, ModuleInfo>): void { 189 if (!BytecodeObfuscator.enable) { 190 return; 191 } 192 this.collectSkipModuleName(); 193 this.convertAbstractPathToRecordName(moduleInfos); 194 this.collectWhiteList(); 195 const fileWhiteListsCachePath = path.join(this.obfuscationCacheDir, FILE_WHITE_LISTS); 196 this.collectPropertyBySourceScan(fileWhiteListsCachePath); 197 fs.writeFileSync(this.configPath, JSON.stringify(this.bytecodeObfuscateConfig, (key, value) => { 198 if (value instanceof Set) { 199 return Array.from(value); 200 } 201 return value; 202 }, 2)); 203 this.generateObfCmd(); 204 this.createWarnings(); 205 const outPutABC: string = this.projectConfig.widgetCompile ? WIDGETS_ABC : MODULES_ABC; 206 const abcPath = path.join(this.projectConfig.aceModuleBuild, outPutABC); 207 //backup abc file 208 fs.copyFileSync(abcPath, this.bytecodeObfuscateConfig.abcFilePath); 209 const result = childProcess.spawnSync(this.arkConfig.bcObfuscatorPath, this.cmdArgs, { 210 env: { ...process.env } 211 }); 212 if (result.status !== 0) { 213 const stderrString = result.stderr ? result.stderr.toString() : ''; 214 const errInfo: LogData = LogDataFactory.newInstanceFromBytecodeObfuscation(stderrString, result.status); 215 BytecodeObfuscator.instance.logger.printErrorAndExit(errInfo); 216 } 217 fs.copyFileSync(this.bytecodeObfuscateConfig.obfAbcFilePath, abcPath); 218 if (this.projectConfig.widgetCompile) { 219 return; 220 } 221 this.updateAfterObfuscation(); 222 } 223 224 private collectSkipModuleName(): void { 225 const matchedPaths = new Set<string>(); 226 227 // collect keep bytecode har module pkgName 228 const regex = /\/oh_modules\/([^/]+)$/; 229 this.projectConfig.obfuscationMergedObConfig.keepSourceOfPaths.forEach(item => { 230 const match = item.match(regex); 231 if (match) { 232 matchedPaths.add(match[1]); 233 } 234 }); 235 // collect remote har pkgName,which shouldn't to be obfuscated 236 this.projectConfig.byteCodeHarInfo && 237 Object.entries(this.projectConfig.byteCodeHarInfo).forEach(([key, value]) => { 238 if (!isVersionCompatible(value?.compatibleSdkVersion, null) || !this.enhanced || matchedPaths.has(key)) { 239 this.bytecodeObfuscateConfig.addSkippedRemoteHarList(key); 240 this.projectConfig.byteCodeHarToDependencyKeys?.get(key)?.forEach(dependentModule => { 241 this.bytecodeObfuscateConfig.addSkippedRemoteHarList(dependentModule); 242 }); 243 } 244 }); 245 } 246 247 public convertAbstractPathToRecordName(moduleInfos: Map<String, ModuleInfo>): void { 248 if (this.bytecodeObfuscateConfig.obfuscationRules.keepOptions && 249 this.bytecodeObfuscateConfig.obfuscationRules.keepOptions.keepPaths.size > 0) { 250 const convertedPath: Set<string> = new Set<string>(); 251 this.bytecodeObfuscateConfig.obfuscationRules.keepOptions.keepPaths.forEach(path => { 252 if (moduleInfos.get(path)) { 253 convertedPath.add(moduleInfos.get(path).recordName); 254 this.bytecodeObfuscateConfig.removeKeepPaths(path); 255 } 256 }); 257 this.bytecodeObfuscateConfig.addKeepPaths(convertedPath); 258 } 259 } 260 261 public generateObfCmd(): void { 262 if (this.debugging) { 263 this.cmdArgs.push('--debug'); 264 this.cmdArgs.push('--debug-file'); 265 const defaultDebugLogPath: string = path.join(this.obfuscationCacheDir, 'debug.log'); 266 if (!fs.existsSync(defaultDebugLogPath)) { 267 writeFileSync(defaultDebugLogPath, ''); 268 } 269 this.cmdArgs.push(defaultDebugLogPath); 270 } 271 this.cmdArgs.push(this.configPath); 272 } 273 274 private createWarnings(): void { 275 this.logger.warn( 276 yellow, 277 'ArkTS:WARN When enabling bytecode obfuscation, the following exceptions may occur:\n' + 278 '1. When enabling fileName, export, and property obfuscation, and a HAR is depended on by both HAP and HSP,\n' + 279 'this scenario may result in the error:' + 280 ' \"The requested module \'xxxx\' does not provide an export name \'x\' which is imported by \'xxxxx\'.\"' 281 ); 282 283 if (this.enhanced) { 284 this.logger.warn( 285 yellow, 286 'ArkTS:WARN When enabling the enhanced option, the following exceptions may occur:\n' + 287 '1. When enabling export and property obfuscation, and a HAR is depended on by both HAP and HSP,\n' + 288 ' this scenario may result in the error:' + 289 ' \"The requested module \'xxxx\' does not provide an export name \'x\' which is imported by \'xxxxx\'.\"' 290 ); 291 } 292 } 293 294 private updateAfterObfuscation(): void { 295 let nameCache: Object; 296 let sourceMap: Object; 297 298 this.nameCachePath = this.bytecodeObfuscateConfig.obfuscationRules.printNameCache || 299 this.bytecodeObfuscateConfig.defaultNameCachePath; 300 301 const sourceMapPath = path.join(this.projectConfig.cachePath, SOURCEMAPS); 302 const souceMapJsonPath = path.join(this.projectConfig.cachePath, SOURCEMAPS_JSON); 303 nameCache = JSON.parse(fs.readFileSync(this.nameCachePath, 'utf-8')); 304 sourceMap = JSON.parse(fs.readFileSync(sourceMapPath, 'utf-8')); 305 //copy origin sourceMaps for Incremental compilation 306 if (fs.existsSync(souceMapJsonPath)) { 307 fs.copyFileSync(souceMapJsonPath, this.backupSourceMapPath); 308 } 309 const nameMapping: Map<string, string> = generateMapping(nameCache); 310 const sourceMapUpdated: Object = updateSourceMap(sourceMap, nameMapping); 311 fs.writeFileSync(sourceMapPath, JSON.stringify(sourceMapUpdated, null, 2)); 312 fs.writeFileSync(souceMapJsonPath, SourceMapGenerator.getInstance().convertSourceMapToCache(sourceMapUpdated)); 313 const nameCacheUpdated: Object = this.bytecodeObfuscateConfig.obfuscationRules.compact ? 314 nameCache : processNameCache(nameCache, sourceMap); 315 fs.writeFileSync(this.nameCachePath, JSON.stringify(nameCacheUpdated, null, 2)); 316 } 317 318 private collectWhiteList(): void { 319 this.bytecodeObfuscateConfig.addReservedNames(ES_ANNOTATIONS); 320 this.bytecodeObfuscateConfig.addReservedProperties(BYTECODE_OBFUSCATION_PROPERTY_WHITE_LIST); 321 } 322 323 public getBackupSourceMapPath(): string { 324 return this ? this.backupSourceMapPath : ''; 325 } 326 327 public setKeepSourceOfPaths(keepPath: Set<string>): void { 328 this.bytecodeObfuscateConfig.addKeepPaths(keepPath); 329 } 330 331 public isDecoratorObfuscationEnabled(): boolean { 332 return this.bytecodeObfuscateConfig?.obfuscationRules?.enableDecoratorObfuscation; 333 } 334 335 private getSystemApiConfigsByCache(): void { 336 const uniqueNames = new Set([ 337 ...(UnobfuscationCollections.reservedSdkApiForLocal || []), 338 ...(UnobfuscationCollections.reservedSdkApiForGlobal || []), 339 ...(UnobfuscationCollections.reservedSdkApiForProp || []) 340 ]); 341 this.bytecodeObfuscateConfig.addReservedNames(uniqueNames); 342 } 343 344 collectPropertyBySourceScan(fileWhiteListsCachePath: string): void { 345 const fileContent = fs.readFileSync(fileWhiteListsCachePath, 'utf8'); 346 const parsed: Object = JSON.parse(fileContent); 347 const allValues: string[] = []; 348 349 Object.values(parsed).forEach(fileData => { 350 this.processFileKeepInfo(fileData.fileKeepInfo, allValues); 351 this.processFileReservedInfo(fileData.fileReservedInfo, allValues); 352 // only collect decorator when decorator Obfuscation is disabled 353 if (!this.isDecoratorObfuscationEnabled()) { 354 this.processDecoratorMap(fileData.bytecodeObfuscateKeepInfo?.decoratorMap); 355 } 356 }); 357 358 this.bytecodeObfuscateConfig.addReservedNames(allValues); 359 } 360 361 private processFileKeepInfo(fileKeepInfo: FileKeepInfo, allValues: string[]): void { 362 if (!fileKeepInfo) { 363 return; 364 } 365 366 let { 367 keepSymbol, 368 keepAsConsumer, 369 structProperties, 370 enumProperties, 371 stringProperties, 372 exported, 373 } = fileKeepInfo; 374 375 const rules = this.bytecodeObfuscateConfig.obfuscationRules; 376 if (rules.enableDecoratorObfuscation) { 377 const reservedNames = this.bytecodeObfuscateConfig.getReservedNames(); 378 structProperties = new Set( 379 Array.from(structProperties || []).filter(item => reservedNames?.has(item)) 380 ); 381 } 382 383 const arrayProperties = [ 384 keepSymbol?.propertyNames, 385 keepSymbol?.globalNames, 386 keepAsConsumer?.propertyNames, 387 keepAsConsumer?.globalNames, 388 exported?.propertyNames, 389 exported?.globalNames, 390 structProperties, 391 enumProperties, 392 stringProperties 393 ]; 394 395 arrayProperties.forEach(arr => { 396 if (arr) { 397 allValues.push(...arr); 398 } 399 }); 400 } 401 402 private processFileReservedInfo(fileReservedInfo: FileReservedInfo, allValues: string[]): void { 403 if (!fileReservedInfo) { 404 return; 405 } 406 407 const { enumProperties, propertyParams } = fileReservedInfo; 408 409 [enumProperties, propertyParams].forEach(props => { 410 if (props) { 411 allValues.push(...props); 412 } 413 }); 414 } 415 416 private processDecoratorMap(decoratorMap: Object): void { 417 if (!decoratorMap) { 418 return; 419 } 420 for (const [decoratorName, propertyList] of Object.entries(decoratorMap)) { 421 const processedList = new Set( 422 [...propertyList].flatMap(name => 423 name.includes('.') ? name.split('.') : [name] 424 ) 425 ); 426 427 this.bytecodeObfuscateConfig.addReservedNames(processedList); 428 processedList.forEach(name => { 429 UnobfuscationCollections.reservedSdkApiForGlobal.add(name); 430 }); 431 } 432 } 433} 434 435export class BytecodeObfuscationConfig { 436 abcFilePath: string; 437 obfAbcFilePath: string; 438 obfPaFilePath: string; 439 compileSdkVersion: string; 440 targetApiVersion: number; 441 targetApiSubVersion: string; 442 filesInfoPath: string; 443 entryPackageInfo: string; 444 sourceMapsPath: string; 445 defaultNameCachePath: string; 446 skippedRemoteHarList: Set<string>; 447 useNormalizedOHMUrl: boolean; 448 obfuscationRules: { 449 disableObfuscation: boolean; 450 compact: boolean; 451 enableExportObfuscation: boolean; 452 enableRemoveLog: boolean; 453 enableDecoratorObfuscation: boolean; 454 printNameCache: string; 455 applyNameCache: string; 456 reservedNames: Set<string>; 457 propertyObfuscation: { 458 enable: boolean; 459 reservedProperties: Set<string>; 460 universalReservedProperties: Set<string>; 461 }; 462 toplevelObfuscation: { 463 enable: boolean; 464 reservedToplevelNames: Set<string>; 465 universalReservedToplevelNames: Set<string>; 466 }; 467 fileNameObfuscation: { 468 enable: boolean; 469 reservedFileNames: Set<string>; 470 universalReservedFileNames: Set<string>; 471 reservedRemoteHarPkgNames: Set<string>; 472 }; 473 keepOptions: { 474 enable: boolean; 475 keepPaths: Set<string>; 476 }; 477 }; 478 479 constructor(share: Object, arkProjectConfig: Object) { 480 const mergedObConfig: MergedConfig = arkProjectConfig.obfuscationMergedObConfig; 481 const obfuscationCacheDir: string = share.projectConfig.obfuscationOptions.obfuscationCacheDir; 482 const originDir: string = path.join(obfuscationCacheDir, 'origin'); 483 const obfDir: string = path.join(obfuscationCacheDir, 'obf'); 484 const outPutABC: string = share.projectConfig.widgetCompile ? WIDGETS_ABC : MODULES_ABC; 485 this.abcFilePath = path.join(originDir, outPutABC); 486 this.obfAbcFilePath = path.join(obfDir, outPutABC); 487 this.targetApiVersion = share.projectConfig.compatibleSdkVersion; 488 this.targetApiSubVersion = share.projectConfig.compatibleSdkVersionStage; 489 this.filesInfoPath = path.join(share.projectConfig.cachePath, FILESINFO_TXT); 490 this.sourceMapsPath = path.join(share.projectConfig.cachePath, SOURCEMAPS); 491 this.useNormalizedOHMUrl = share.projectConfig.useNormalizedOHMUrl; 492 this.compileSdkVersion = share.projectConfig.etsLoaderVersion; 493 this.entryPackageInfo = arkProjectConfig.entryPackageInfo; 494 this.defaultNameCachePath = path.join(obfuscationCacheDir, 'nameCache.json'); 495 this.skippedRemoteHarList = new Set<string>(); 496 if (mergedObConfig.options.bytecodeObf.debugging) { 497 const parsedPath = path.parse(this.obfAbcFilePath); 498 this.obfPaFilePath = path.join(parsedPath.dir, `${parsedPath.name}.pa`); 499 } else { 500 this.obfPaFilePath = ''; 501 } 502 const { options, reservedNames, reservedPropertyNames, universalReservedPropertyNames, 503 reservedGlobalNames, universalReservedGlobalNames, reservedFileNames, 504 keepSourceOfPaths } = mergedObConfig; 505 const fileNameReservedInfo: ReservedNameInfo = processReservedFileNames(reservedFileNames); 506 507 this.obfuscationRules = { 508 disableObfuscation: false, 509 compact: options.compact, 510 enableExportObfuscation: options.enableExportObfuscation, 511 enableRemoveLog: options.removeLog, 512 enableDecoratorObfuscation: false, 513 printNameCache: options.printNameCache, 514 applyNameCache: options.applyNameCache, 515 reservedNames: new Set<string>(reservedNames), 516 propertyObfuscation: { 517 enable: options.enablePropertyObfuscation, 518 reservedProperties: new Set<string>(reservedPropertyNames), 519 universalReservedProperties: new Set( 520 universalReservedPropertyNames?.map(regexp => regexp.toString()) 521 ) 522 }, 523 toplevelObfuscation: { 524 enable: options.enableToplevelObfuscation, 525 reservedToplevelNames: new Set<string>(reservedGlobalNames), 526 universalReservedToplevelNames: new Set( 527 universalReservedGlobalNames?.map(regexp => regexp.toString()) || [] 528 ) 529 }, 530 fileNameObfuscation: { 531 enable: options.enableFileNameObfuscation, 532 reservedFileNames: new Set<string>(fileNameReservedInfo.specificReservedArray), 533 universalReservedFileNames: new Set( 534 fileNameReservedInfo.universalReservedArray?.map(regexp => regexp.toString()) || [] 535 ) 536 , 537 reservedRemoteHarPkgNames: new Set<string>() 538 }, 539 keepOptions: { 540 enable: keepSourceOfPaths.length > 0, 541 keepPaths: new Set<string>(keepSourceOfPaths), 542 } 543 }; 544 } 545 546 getReservedNames(): Set<string> { 547 return this.obfuscationRules.reservedNames; 548 } 549 550 addToSet(set: Set<string>, values: string | string[] | Set<string>): void { 551 if (typeof values === 'string') { 552 set.add(values); 553 } else { 554 values.forEach(value => set.add(value)); 555 } 556 } 557 558 addReservedProperties(values: string | string[] | Set<string>): void { 559 this.addToSet(this.obfuscationRules.propertyObfuscation.reservedProperties, values); 560 } 561 562 addReservedNames(values: string | string[] | Set<string>): void { 563 this.addToSet(this.obfuscationRules.reservedNames, values); 564 } 565 566 addSkippedRemoteHarList(values: string | string[] | Set<string>): void { 567 this.addToSet(this.skippedRemoteHarList, values); 568 } 569 570 addKeepPaths(values: string | string[] | Set<string>): void { 571 this.addToSet(this.obfuscationRules.keepOptions.keepPaths, values); 572 } 573 574 removeKeepPaths(values: string | string[] | Set<string>): void { 575 this.removeFromSet(this.obfuscationRules.keepOptions.keepPaths, values); 576 } 577 578 removeFromSet( 579 set: Set<string>, 580 values: string | string[] | Set<string> 581 ): void { 582 if (typeof values === 'string') { 583 set.delete(values); 584 } else { 585 values.forEach((value) => set.delete(value)); 586 } 587 } 588} 589 590function processReservedFileNames(reservedFileNames: string[]): ReservedNameInfo { 591 const fileNameReservedInfo: ReservedNameInfo = separateUniversalReservedItem(reservedFileNames, false); 592 fileNameReservedInfo.specificReservedArray = fileNameReservedInfo.specificReservedArray.map(fileName => { 593 return fileName.replace(/^(?!\.)[^.]+\.[^.]+$/, match => { 594 return match.replace(/\.[^.]+$/, ''); 595 }); 596 }); 597 return fileNameReservedInfo; 598} 599 600function collectReservedLangForProperty(): Set<string> { 601 let languageSet: Set<string> = new Set<string>; 602 for (const key of Object.keys(esInfo)) { 603 const valueArray = esInfo[key]; 604 valueArray.forEach((element: string) => { 605 languageSet.add(element); 606 }); 607 } 608 return languageSet; 609} 610 611function isVersionCompatible( 612 compatibleSdkVersion: number | null | undefined, 613 compatibleSdkVersionStage: string | null | undefined 614): boolean { 615 if (compatibleSdkVersion == null) { 616 return false; 617 } 618 619 if (compatibleSdkVersion >= 13) { 620 return true; 621 } 622 623 if (compatibleSdkVersion === 12) { 624 return compatibleSdkVersionStage === 'beta3'; 625 } 626 627 return false; 628} 629 630function generateMapping(nameCache: Object): Map<string, string> { 631 const whiteList = new Set<string>([ 632 FILE_NAME_CACHE, 633 OBF_NAME_MAP, 634 PROPERTY_CACHE, 635 ENTRY_PACKAGE_INFO, 636 COMPILE_SDK_VERSION 637 ]); 638 const obfSourceFileMapping = new Map<string, string>(); 639 640 Object.entries(nameCache).forEach(([key, value]) => { 641 if (!whiteList.has(key)) { 642 const { ObfSourceFile: obfSourceFile, OriSourceFile: originSouceFile } = value; 643 if (obfSourceFile && originSouceFile) { 644 obfSourceFileMapping.set(originSouceFile, obfSourceFile); 645 } 646 } 647 }); 648 return obfSourceFileMapping; 649} 650 651function updateSourceMap(sourceMap: Object, obfSourceFileMapping: Map<string, string>): Object { 652 const newSourceMap: Object = {}; 653 Object.entries(sourceMap).forEach(([key, value]) => { 654 const obfSourceKey = obfSourceFileMapping.get(key) || key; 655 newSourceMap[obfSourceKey] = value; 656 }); 657 return newSourceMap; 658} 659 660function processNameCache(nameCache: Object, sourceMap: Object): Object { 661 const nameCacheUpdated = {}; 662 const entries = Object.entries(nameCache); 663 for (let i = 0; i < entries.length; i++) { 664 const [nameCacheKey, nameCacheValue] = entries[i]; 665 processNameCacheEntry(nameCacheKey, nameCacheValue, sourceMap, nameCacheUpdated); 666 } 667 return nameCacheUpdated; 668} 669 670function processNameCacheEntry(nameCacheKey: string, nameCacheValue: Object, sourceMap: Object, nameCacheUpdated: Object): void { 671 if (!nameCacheValue.OriSourceFile) { 672 nameCacheUpdated[nameCacheKey] = nameCacheValue; 673 return; 674 } 675 const sourceMapKey = nameCacheValue.OriSourceFile; 676 const sourcemap = sourceMap[sourceMapKey]; 677 if (!sourcemap) { 678 return; 679 } 680 const sourceFileName = sourcemap.sources?.length === 1 ? sourcemap.sources[0] : ''; 681 const source = new Source(sourceFileName, null); 682 const decodedSourceMap = decodeSourcemap(sourcemap); 683 const sourceMapLink = new SourceMapLink(decodedSourceMap, [source]); 684 685 if (!nameCacheUpdated[nameCacheKey]) { 686 nameCacheUpdated[nameCacheKey] = {}; 687 } 688 for (const [itemKey, itemValue] of Object.entries(nameCacheValue)) { 689 if (itemKey !== IDENTIFIER_CACHE && itemKey !== MEMBER_METHOD_CACHE) { 690 nameCacheUpdated[nameCacheKey][itemKey] = itemValue; 691 continue; 692 } 693 processItemKey(nameCacheKey, itemKey, itemValue, nameCacheUpdated, sourceMapLink); 694 } 695} 696/** 697 698 * @param itemKey IdentifierCache || MemberMethodCache 699 * @param itemValue 700 */ 701function processItemKey(nameCacheKey, itemKey, itemValue, nameCacheUpdated, sourceMapLink): void { 702 if (!nameCacheUpdated[nameCacheKey][itemKey]) { 703 nameCacheUpdated[nameCacheKey][itemKey] = {}; 704 } 705 /** 706 * key=>originName eg.#fun1:3:24 707 * value => obfuscated Name 708 */ 709 for (const [key, value] of Object.entries(itemValue)) { 710 const [scopeName, oldStartLine, oldEndLine] = key.split(':'); 711 const startPosition: SourceMapSegmentObj = sourceMapLink.traceSegment(Number(oldStartLine) - 1, 0, ''); 712 const endPosition: SourceMapSegmentObj = sourceMapLink.traceSegment( 713 Number(oldEndLine) - 1, 0, ''); 714 if (!startPosition || !endPosition) { 715 nameCacheUpdated[nameCacheKey][itemKey][key] = value; 716 continue; 717 } 718 const startLine = startPosition.line + 1; 719 const endLine = endPosition.line + 1; 720 const newKey = `${scopeName}:${startLine}:${endLine}`; 721 nameCacheUpdated[nameCacheKey][itemKey][newKey] = value; 722 } 723}