1/* 2 * Copyright (c) 2023-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 { 17 SyntaxKind, 18 factory, 19 forEachChild, 20 isBreakOrContinueStatement, 21 isConstructorDeclaration, 22 isExportSpecifier, 23 isIdentifier, 24 isImportSpecifier, 25 isLabeledStatement, 26 isMetaProperty, 27 isSourceFile, 28 isStructDeclaration, 29 setParentRecursive, 30 visitEachChild, 31 isPropertyDeclaration, 32 isMethodDeclaration, 33 isGetAccessor, 34 isSetAccessor, 35 isClassDeclaration, 36 isFunctionExpression, 37 isArrowFunction, 38 isVariableDeclaration, 39 isPropertyAssignment, 40 isPrivateIdentifier 41} from 'typescript'; 42 43import type { 44 ClassElement, 45 Identifier, 46 Node, 47 SourceFile, 48 StructDeclaration, 49 Symbol, 50 TransformationContext, 51 Transformer, 52 TransformerFactory, 53 TypeChecker 54} from 'typescript'; 55 56import { 57 createScopeManager, 58 isClassScope, 59 isGlobalScope, 60 isEnumScope, 61 isInterfaceScope, 62 isObjectLiteralScope, 63 noSymbolIdentifier, 64} from '../../utils/ScopeAnalyzer'; 65 66import type { 67 Label, 68 Scope, 69 ScopeManager 70} from '../../utils/ScopeAnalyzer'; 71 72import { 73 IDENTIFIER_CACHE, 74 MEM_METHOD_CACHE 75} from '../../utils/NameCacheUtil'; 76 77import type {INameGenerator, NameGeneratorOptions} from '../../generator/INameGenerator'; 78import type {IOptions} from '../../configs/IOptions'; 79import type { INameObfuscationOption, IUnobfuscationOption } from '../../configs/INameObfuscationOption'; 80import type {TransformPlugin} from '../TransformPlugin'; 81import type { MangledSymbolInfo } from '../../common/type'; 82import {TransformerOrder} from '../TransformPlugin'; 83import {getNameGenerator, NameGeneratorType} from '../../generator/NameFactory'; 84import {TypeUtils} from '../../utils/TypeUtils'; 85import { 86 isInTopLevelWhitelist, 87 isInLocalWhitelist, 88 isReservedLocalVariable, 89 isReservedTopLevel, 90 recordHistoryUnobfuscatedNames 91} from '../../utils/TransformUtil'; 92import {NodeUtils} from '../../utils/NodeUtils'; 93import {ApiExtractor} from '../../common/ApiExtractor'; 94import {performancePrinter, ArkObfuscator, cleanFileMangledNames} from '../../ArkObfuscator'; 95import { EventList } from '../../utils/PrinterUtils'; 96import { isViewPUBasedClass } from '../../utils/OhsUtil'; 97import { 98 PropCollections, 99 UnobfuscationCollections, 100 LocalVariableCollections 101} from '../../utils/CommonCollections'; 102 103namespace secharmony { 104 /** 105 * Rename Identifiers, including: 106 * 1. variable name 107 * 2. function name 108 * 3. label name 109 * 4. class name/interface name/ label name 110 * we need implement some features: 111 * 1. rename identifiers 112 * 2. store/restore name to/from nameCache file. 113 * 3. do scope analysis for identifier obfuscations 114 * 115 * @param option 116 */ 117 const createRenameIdentifierFactory = function (option: IOptions): TransformerFactory<Node> { 118 const profile: INameObfuscationOption | undefined = option?.mNameObfuscation; 119 const unobfuscationOption: IUnobfuscationOption | undefined = option?.mUnobfuscationOption; 120 if (!profile || !profile.mEnable) { 121 return null; 122 } 123 124 let options: NameGeneratorOptions = {}; 125 let generator: INameGenerator = getNameGenerator(profile.mNameGeneratorType, options); 126 127 const openTopLevel: boolean = option?.mNameObfuscation?.mTopLevel; 128 const exportObfuscation: boolean = option?.mExportObfuscation; 129 let isInitializedReservedList = false; 130 return renameIdentifierFactory; 131 132 function renameIdentifierFactory(context: TransformationContext): Transformer<Node> { 133 initWhitelist(); 134 let mangledSymbolNames: Map<Symbol, MangledSymbolInfo> = new Map<Symbol, MangledSymbolInfo>(); 135 let mangledLabelNames: Map<Label, string> = new Map<Label, string>(); 136 let fileExportNames: Set<string> = undefined; 137 let fileImportNames: Set<string> = undefined; 138 noSymbolIdentifier.clear(); 139 140 let historyMangledNames: Set<string> = undefined; 141 if (historyNameCache && historyNameCache.size > 0) { 142 historyMangledNames = new Set<string>(Array.from(historyNameCache.values())); 143 } 144 145 let checker: TypeChecker = undefined; 146 let manager: ScopeManager = createScopeManager(); 147 148 return renameTransformer; 149 150 /** 151 * Transformer to rename identifiers 152 * 153 * @param node ast node of a file. 154 */ 155 function renameTransformer(node: Node): Node { 156 if (nameCache.size === 0) { 157 nameCache.set(IDENTIFIER_CACHE, new Map<string, string>()); 158 nameCache.set(MEM_METHOD_CACHE, new Map<string, string>()); 159 } 160 161 if (!isSourceFile(node) || ArkObfuscator.isKeptCurrentFile) { 162 return node; 163 } 164 165 performancePrinter?.singleFilePrinter?.startEvent(EventList.CREATE_CHECKER, performancePrinter.timeSumPrinter); 166 checker = TypeUtils.createChecker(node); 167 performancePrinter?.singleFilePrinter?.endEvent(EventList.CREATE_CHECKER, performancePrinter.timeSumPrinter); 168 169 performancePrinter?.singleFilePrinter?.startEvent(EventList.SCOPE_ANALYZE, performancePrinter.timeSumPrinter); 170 manager.analyze(node, checker, exportObfuscation); 171 performancePrinter?.singleFilePrinter?.endEvent(EventList.SCOPE_ANALYZE, performancePrinter.timeSumPrinter); 172 173 let root: Scope = manager.getRootScope(); 174 fileExportNames = root.fileExportNames; 175 fileImportNames = root.fileImportNames; 176 177 performancePrinter?.singleFilePrinter?.startEvent(EventList.CREATE_OBFUSCATED_NAMES, performancePrinter.timeSumPrinter); 178 renameInScope(root); 179 performancePrinter?.singleFilePrinter?.endEvent(EventList.CREATE_OBFUSCATED_NAMES, performancePrinter.timeSumPrinter); 180 181 root = undefined; 182 183 performancePrinter?.singleFilePrinter?.startEvent(EventList.OBFUSCATE_NODES, performancePrinter.timeSumPrinter); 184 let ret: Node = visit(node); 185 186 let parentNodes = setParentRecursive(ret, true); 187 performancePrinter?.singleFilePrinter?.endEvent(EventList.OBFUSCATE_NODES, performancePrinter.timeSumPrinter); 188 return parentNodes; 189 } 190 191 /** 192 * rename symbol table store in scopes... 193 * 194 * @param scope scope, such as global, module, function, block 195 */ 196 function renameInScope(scope: Scope): void { 197 // process labels in scope, the label can't rename as the name of top labels. 198 renameLabelsInScope(scope); 199 // process symbols in scope, exclude property name. 200 renameNamesInScope(scope); 201 202 let subScope = undefined; 203 while (scope.children.length > 0) { 204 subScope = scope.children.pop(); 205 renameInScope(subScope); 206 subScope = undefined; 207 } 208 } 209 210 function renameNamesInScope(scope: Scope): void { 211 if (isExcludeScope(scope)) { 212 return; 213 } 214 215 if (!exportObfuscation) { 216 scope.defs.forEach((def) => { 217 let parentScope = scope; 218 while (parentScope) { 219 if (parentScope.importNames && parentScope.importNames.has(def.name)) { 220 scope.defs.delete(def); 221 scope.mangledNames.add(def.name); 222 } 223 parentScope = parentScope.parent; 224 } 225 }); 226 } 227 228 renames(scope, scope.defs, generator); 229 } 230 231 function renames(scope: Scope, defs: Set<Symbol>, generator: INameGenerator): void { 232 defs.forEach((def) => { 233 const original: string = def.name; 234 let mangled: string = original; 235 const path: string = scope.loc + '#' + original; 236 // No allow to rename reserved names. 237 if (!Reflect.has(def, 'obfuscateAsProperty') && 238 isInLocalWhitelist(original, UnobfuscationCollections.unobfuscatedNamesMap, path) || 239 (!exportObfuscation && scope.exportNames.has(def.name)) || 240 isSkippedGlobal(openTopLevel, scope)) { 241 scope.mangledNames.add(mangled); 242 mangledSymbolNames.set(def, {mangledName: mangled, originalNameWithScope: path}); 243 return; 244 } 245 246 if (mangledSymbolNames.has(def)) { 247 return; 248 } 249 250 const historyName: string = historyNameCache?.get(path); 251 if (historyName) { 252 recordHistoryUnobfuscatedNames(path); // For incremental build 253 mangled = historyName; 254 } else if (Reflect.has(def, 'obfuscateAsProperty')) { 255 // obfuscate toplevel, export 256 mangled = getPropertyMangledName(original, path); 257 } else { 258 // obfuscate local variable 259 mangled = getMangled(scope, generator); 260 } 261 // add new names to name cache 262 let identifierCache = nameCache?.get(IDENTIFIER_CACHE); 263 (identifierCache as Map<string, string>).set(path, mangled); 264 let symbolInfo: MangledSymbolInfo = { 265 mangledName: mangled, 266 originalNameWithScope: path 267 }; 268 scope.mangledNames.add(mangled); 269 mangledSymbolNames.set(def, symbolInfo); 270 }); 271 } 272 273 function getPropertyMangledName(original: string, nameWithScope: string): string { 274 if (isInTopLevelWhitelist(original, UnobfuscationCollections.unobfuscatedNamesMap, nameWithScope)) { 275 return original; 276 } 277 278 const historyName: string = PropCollections.historyMangledTable?.get(original); 279 let mangledName: string = historyName ? historyName : PropCollections.globalMangledTable.get(original); 280 while (!mangledName) { 281 let tmpName = generator.getName(); 282 if (isReservedTopLevel(tmpName) || 283 tmpName === original) { 284 continue; 285 } 286 287 /** 288 * In case b is obfuscated as a when only enable toplevel obfuscation: 289 * let b = 1; 290 * export let a = 1; 291 */ 292 if (cleanFileMangledNames && fileExportNames && fileExportNames.has(tmpName)) { 293 continue; 294 } 295 296 /** 297 * In case b is obfuscated as a when only enable toplevel obfuscation: 298 * import {a} from 'filePath'; 299 * let b = 1; 300 */ 301 if (cleanFileMangledNames && fileImportNames.has(tmpName)) { 302 continue; 303 } 304 305 /** 306 * In case a newly added variable get an obfuscated name that is already in history namecache 307 */ 308 if (historyMangledNames && historyMangledNames.has(tmpName)) { 309 continue; 310 } 311 312 if (PropCollections.newlyOccupiedMangledProps.has(tmpName) || PropCollections.mangledPropsInNameCache.has(tmpName)) { 313 continue; 314 } 315 316 if (ApiExtractor.mConstructorPropertySet?.has(tmpName)) { 317 continue; 318 } 319 320 mangledName = tmpName; 321 } 322 323 PropCollections.globalMangledTable.set(original, mangledName); 324 PropCollections.newlyOccupiedMangledProps.add(mangledName); 325 return mangledName; 326 } 327 328 function isExcludeScope(scope: Scope): boolean { 329 if (isClassScope(scope)) { 330 return true; 331 } 332 333 if (isInterfaceScope(scope)) { 334 return true; 335 } 336 337 if (isEnumScope(scope)) { 338 return true; 339 } 340 341 return isObjectLiteralScope(scope); 342 } 343 344 function searchMangledInParent(scope: Scope, name: string): boolean { 345 let found: boolean = false; 346 let parentScope = scope; 347 while (parentScope) { 348 if (parentScope.mangledNames.has(name)) { 349 found = true; 350 break; 351 } 352 353 parentScope = parentScope.parent; 354 } 355 356 return found; 357 } 358 359 function getMangled(scope: Scope, localGenerator: INameGenerator): string { 360 let mangled: string = ''; 361 do { 362 mangled = localGenerator.getName()!; 363 // if it is a globally reserved name, it needs to be regenerated 364 if (isReservedLocalVariable(mangled)) { 365 mangled = ''; 366 continue; 367 } 368 369 if (fileExportNames && fileExportNames.has(mangled)) { 370 mangled = ''; 371 continue; 372 } 373 374 if (historyMangledNames && historyMangledNames.has(mangled)) { 375 mangled = ''; 376 continue; 377 } 378 379 if (searchMangledInParent(scope, mangled)) { 380 mangled = ''; 381 continue; 382 } 383 384 if (ApiExtractor.mConstructorPropertySet?.has(mangled)) { 385 mangled = ''; 386 } 387 } while (mangled === ''); 388 389 return mangled; 390 } 391 392 function renameLabelsInScope(scope: Scope): void { 393 const labels: Label[] = scope.labels; 394 if (labels.length > 0) { 395 let upperMangledLabels = getUpperMangledLabelNames(labels[0]); 396 for (const label of labels) { 397 let mangledLabel = getMangledLabel(label, upperMangledLabels); 398 mangledLabelNames.set(label, mangledLabel); 399 } 400 } 401 } 402 403 function getMangledLabel(label: Label, mangledLabels: string[]): string { 404 let mangledLabel: string = ''; 405 do { 406 mangledLabel = generator.getName(); 407 if (mangledLabel === label.name) { 408 mangledLabel = ''; 409 } 410 411 if (mangledLabels.includes(mangledLabel)) { 412 mangledLabel = ''; 413 } 414 } while (mangledLabel === ''); 415 416 return mangledLabel; 417 } 418 419 function getUpperMangledLabelNames(label: Label): string[] { 420 const results: string[] = []; 421 let parent: Label = label.parent; 422 while (parent) { 423 let mangledLabelName: string = mangledLabelNames.get(parent); 424 if (mangledLabelName) { 425 results.push(mangledLabelName); 426 } 427 parent = parent.parent; 428 } 429 430 return results; 431 } 432 433 function isFunctionLike(node: Node): boolean { 434 switch (node.kind) { 435 case SyntaxKind.FunctionDeclaration: 436 case SyntaxKind.MethodDeclaration: 437 case SyntaxKind.GetAccessor: 438 case SyntaxKind.SetAccessor: 439 case SyntaxKind.Constructor: 440 case SyntaxKind.FunctionExpression: 441 case SyntaxKind.ArrowFunction: 442 return true; 443 } 444 return false; 445 } 446 447 function nodeHasFunctionLikeChild(node: Node): boolean { 448 let hasFunctionLikeChild: boolean = false; 449 let childVisitor: (child: Node) => Node = (child: Node): Node => { 450 if (!hasFunctionLikeChild && child && isFunctionLike(child)) { 451 hasFunctionLikeChild = true; 452 } 453 return child; 454 }; 455 visitEachChild(node, childVisitor, context); 456 return hasFunctionLikeChild; 457 } 458 459 /** 460 * visit each node to change identifier name to mangled name 461 * - calculate shadow name index to find shadow node 462 * @param node 463 */ 464 function visit(node: Node): Node { 465 let needHandlePositionInfo: boolean = isFunctionLike(node) || nodeHasFunctionLikeChild(node); 466 if (needHandlePositionInfo) { 467 // Obtain line info for nameCache. 468 handlePositionInfo(node); 469 } 470 471 if (!isIdentifier(node) || !node.parent) { 472 return visitEachChild(node, visit, context); 473 } 474 475 if (isLabeledStatement(node.parent) || isBreakOrContinueStatement(node.parent)) { 476 return updateLabelNode(node); 477 } 478 479 return updateNameNode(node); 480 } 481 482 function handlePositionInfo(node: Node): void { 483 const sourceFile = NodeUtils.getSourceFileOfNode(node); 484 if (node && node.pos < 0 && node.end < 0) { 485 // Node must have a real position for following operations. 486 // Adapting to the situation that the node does not have a real postion. 487 return; 488 } 489 const startPosition = sourceFile.getLineAndCharacterOfPosition(node.getStart()); 490 const endPosition = sourceFile.getLineAndCharacterOfPosition(node.getEnd()); 491 // 1: The line number in sourceFile starts from 0 while in IDE starts from 1. 492 const startLine = startPosition.line + 1; 493 const startCharacter = startPosition.character + 1; // 1: Same as above. 494 const endLine = endPosition.line + 1; // 1: Same as above. 495 const endCharacter = endPosition.character + 1; // 1: Same as above. 496 const lineAndColum: string = ':' + startLine + ':' + startCharacter + ':' + endLine + ':' + endCharacter; 497 498 let isProperty: boolean = isMethodDeclaration(node) || isGetAccessor(node) || 499 isSetAccessor(node) || (isConstructorDeclaration(node) && 500 !(isClassDeclaration(node.parent) && isViewPUBasedClass(node.parent))); 501 // Arrow functions are anoymous, only function expressions are considered. 502 let isPropertyParent: boolean = isFunctionExpression(node) && 503 (isPropertyDeclaration(node.parent) || isPropertyAssignment(node.parent)); 504 let isMemberMethod: boolean = isProperty || isPropertyParent; 505 if (isMemberMethod) { 506 writeMemberMethodCache(node, lineAndColum); 507 return; 508 } 509 510 let name = Reflect.get(node, 'name') as Identifier; 511 if (name?.kind === SyntaxKind.Identifier) { 512 identifierLineMap.set(name, lineAndColum); 513 } else if ((isFunctionExpression(node) || isArrowFunction(node)) && isVariableDeclaration(node.parent) && 514 node.parent.name?.kind === SyntaxKind.Identifier) { 515 // The node is anonymous, and we need to find its parent node. 516 // e.g.: let foo = function() {}; 517 identifierLineMap.set(node.parent.name, lineAndColum); 518 } 519 } 520 521 function writeMemberMethodCache(node: Node, lineAndColum: string): void { 522 let gotNode; 523 if (node.kind === SyntaxKind.Constructor) { 524 gotNode = node.parent; 525 } else if ((node.kind === SyntaxKind.FunctionExpression && 526 (isPropertyDeclaration(node.parent) || isPropertyAssignment(node.parent)))) { 527 gotNode = node.parent.initializer ?? node.parent; 528 } else { 529 gotNode = node; 530 } 531 532 let isIdentifierNode: boolean = gotNode.name && (isIdentifier(gotNode.name) || isPrivateIdentifier(gotNode.name)); 533 let valueName: string = ''; 534 535 if (isIdentifierNode) { 536 // The original method for retrieving method names used gotNode.name.escapedText. This approach limited the collection 537 // of method records in MemberMethodCache to cases where gotNode.name was an Identifier or PrivateIdentifier. 538 // To address the issue where method names starting with double underscores were transformed to start with triple underscores, 539 // we changed the retrieval method to use gotNode.name.text instead of escapedText. However, this change introduced the possibility 540 // of collecting method records when gotNode.name is a NumericLiteral or StringLiteral, which is not desired. 541 // To avoid altering the collection specifications of MemberMethodCache, we restricted the collection scenarios 542 // to match the original cases where only identifiers and private identifiers are collected. 543 valueName = gotNode.name.text; 544 } 545 546 if (valueName === '') { 547 return; 548 } 549 550 let originalName: string = valueName; 551 let keyName = originalName + lineAndColum; 552 if (node.kind === SyntaxKind.Constructor && classMangledName.has(gotNode.name)) { 553 valueName = classMangledName.get(gotNode.name); 554 classInfoInMemberMethodCache.add(keyName); 555 } 556 let memberMethodCache = nameCache?.get(MEM_METHOD_CACHE); 557 if (memberMethodCache) { 558 (memberMethodCache as Map<string, string>).set(keyName, valueName); 559 } 560 } 561 562 function updateNameNode(node: Identifier): Node { 563 // skip property in property access expression 564 if (NodeUtils.isPropertyAccessNode(node)) { 565 return node; 566 } 567 568 if (NodeUtils.isNewTargetNode(node)) { 569 return node; 570 } 571 572 let sym: Symbol | undefined = NodeUtils.findSymbolOfIdentifier(checker, node); 573 let mangledPropertyNameOfNoSymbolImportExport = ''; 574 if (!sym) { 575 if (exportObfuscation && noSymbolIdentifier.has(node.text) && trySearchImportExportSpecifier(node)) { 576 mangledPropertyNameOfNoSymbolImportExport = mangleNoSymbolImportExportPropertyName(node.text); 577 } else { 578 return node; 579 } 580 } 581 582 // Add new names to name cache 583 const symbolInfo: MangledSymbolInfo = mangledSymbolNames.get(sym); 584 const identifierCache = nameCache?.get(IDENTIFIER_CACHE); 585 const lineAndColumn = identifierLineMap?.get(node); 586 // We only save the line info of FunctionLike. 587 const isFunction: boolean = sym ? Reflect.has(sym, 'isFunction') : false; 588 if (isFunction && symbolInfo && lineAndColumn) { 589 const originalName = symbolInfo.originalNameWithScope; 590 const pathWithLine: string = originalName + lineAndColumn; 591 (identifierCache as Map<string, string>).set(pathWithLine, symbolInfo.mangledName); 592 (identifierCache as Map<string, string>).delete(originalName); 593 } 594 595 let mangledName: string = mangledSymbolNames.get(sym)?.mangledName; 596 if (node?.parent.kind === SyntaxKind.ClassDeclaration) { 597 classMangledName.set(node, mangledName); 598 } 599 if (!mangledName && mangledPropertyNameOfNoSymbolImportExport !== '') { 600 mangledName = mangledPropertyNameOfNoSymbolImportExport; 601 } 602 603 if (!mangledName || mangledName === sym?.name) { 604 return node; 605 } 606 607 return factory.createIdentifier(mangledName); 608 } 609 610 function updateLabelNode(node: Identifier): Node { 611 let label: Label | undefined; 612 let labelName: string = ''; 613 614 mangledLabelNames.forEach((value, key) => { 615 if (key.refs.includes(node)) { 616 label = key; 617 labelName = value; 618 } 619 }); 620 621 return label ? factory.createIdentifier(labelName) : node; 622 } 623 624 /** 625 * import {A as B} from 'modulename'; 626 * import {C as D} from 'modulename'; 627 * above A、C have no symbol, so deal with them specially. 628 */ 629 function mangleNoSymbolImportExportPropertyName(original: string): string { 630 const path: string = '#' + original; 631 const historyName: string = historyNameCache?.get(path); 632 let mangled = historyName ?? getPropertyMangledName(original, path); 633 if (nameCache && nameCache.get(IDENTIFIER_CACHE)) { 634 (nameCache.get(IDENTIFIER_CACHE) as Map<string, string>).set(path, mangled); 635 } 636 return mangled; 637 } 638 639 function trySearchImportExportSpecifier(node: Node): boolean { 640 while (node.parent) { 641 node = node.parent; 642 if ((isImportSpecifier(node) || isExportSpecifier(node)) && node.propertyName && isIdentifier(node.propertyName)) { 643 return true; 644 } 645 } 646 return false; 647 } 648 } 649 650 function initWhitelist(): void { 651 if (isInitializedReservedList) { 652 return; 653 } 654 if (profile?.mRenameProperties) { 655 PropCollections.enablePropertyObfuscation = true; 656 const tmpReservedProps: string[] = profile?.mReservedProperties ?? []; 657 tmpReservedProps.forEach(item => { 658 PropCollections.reservedProperties.add(item); 659 }); 660 PropCollections.mangledPropsInNameCache = new Set(PropCollections.historyMangledTable?.values()); 661 if (profile?.mUniversalReservedProperties) { 662 PropCollections.universalReservedProperties = [...profile.mUniversalReservedProperties]; 663 } 664 UnobfuscationCollections.reservedLangForTopLevel.forEach(element => { 665 UnobfuscationCollections.reservedLangForProperty.add(element); 666 }); 667 UnobfuscationCollections.reservedExportName.forEach(element => { 668 UnobfuscationCollections.reservedExportNameAndProp.add(element); 669 }); 670 UnobfuscationCollections.reservedSdkApiForGlobal.forEach(element => { 671 UnobfuscationCollections.reservedSdkApiForProp.add(element); 672 }); 673 } 674 LocalVariableCollections.reservedConfig = new Set(profile?.mReservedNames ?? []); 675 LocalVariableCollections.reservedStruct = new Set(); 676 profile?.mReservedToplevelNames?.forEach(item => PropCollections.reservedProperties.add(item)); 677 profile?.mUniversalReservedToplevelNames?.forEach(item => PropCollections.universalReservedProperties.push(item)); 678 isInitializedReservedList = true; 679 } 680 }; 681 682 function isSkippedGlobal(enableTopLevel: boolean, scope: Scope): boolean { 683 return !enableTopLevel && isGlobalScope(scope); 684 } 685 686 export let transformerPlugin: TransformPlugin = { 687 'name': 'renameIdentifierPlugin', 688 'order': TransformerOrder.RENAME_IDENTIFIER_TRANSFORMER, 689 'createTransformerFactory': createRenameIdentifierFactory 690 }; 691 692 export let nameCache: Map<string, string | Map<string, string>> = new Map(); 693 export let historyNameCache: Map<string, string> = undefined; 694 export let historyUnobfuscatedNamesMap: Map<string, string[]> = undefined; 695 export let identifierLineMap: Map<Identifier, string> = new Map(); 696 export let classMangledName: Map<Node, string> = new Map(); 697 // Record the original class name and line number range to distinguish between class names and member method names. 698 export let classInfoInMemberMethodCache: Set<string> = new Set(); 699 700 export function clearCaches(): void { 701 nameCache.clear(); 702 historyNameCache = undefined; 703 historyUnobfuscatedNamesMap = undefined; 704 identifierLineMap.clear(); 705 classMangledName.clear(); 706 classInfoInMemberMethodCache.clear(); 707 UnobfuscationCollections.unobfuscatedNamesMap.clear(); 708 } 709} 710 711export = secharmony;