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 isParameter, 42 isPropertyAccessExpression 43} from 'typescript'; 44 45import type { 46 ClassElement, 47 Declaration, 48 Identifier, 49 Node, 50 SourceFile, 51 StructDeclaration, 52 Symbol, 53 TransformationContext, 54 Transformer, 55 TransformerFactory, 56 TypeChecker 57} from 'typescript'; 58 59import { 60 createScopeManager, 61 exportElementsWithoutSymbol, 62 exportSymbolAliasMap, 63 getNameWithScopeLoc, 64 isClassScope, 65 isGlobalScope, 66 isEnumScope, 67 isInterfaceScope, 68 isObjectLiteralScope, 69 nodeSymbolMap 70} from '../../utils/ScopeAnalyzer'; 71 72import type { 73 Label, 74 Scope, 75 ScopeManager 76} from '../../utils/ScopeAnalyzer'; 77 78import { 79 IDENTIFIER_CACHE, 80 MEM_METHOD_CACHE 81} from '../../utils/NameCacheUtil'; 82 83import type {INameGenerator, NameGeneratorOptions} from '../../generator/INameGenerator'; 84import type {IOptions} from '../../configs/IOptions'; 85import type { INameObfuscationOption, IUnobfuscationOption } from '../../configs/INameObfuscationOption'; 86import type {TransformPlugin} from '../TransformPlugin'; 87import type { MangledSymbolInfo } from '../../common/type'; 88import {TransformerOrder} from '../TransformPlugin'; 89import {getNameGenerator, NameGeneratorType} from '../../generator/NameFactory'; 90import {TypeUtils} from '../../utils/TypeUtils'; 91import { 92 isInTopLevelWhitelist, 93 isInLocalWhitelist, 94 isReservedLocalVariable, 95 isReservedTopLevel, 96 recordHistoryUnobfuscatedNames, 97 isInPropertyWhitelist, 98 isReservedProperty 99} from '../../utils/TransformUtil'; 100import {NodeUtils} from '../../utils/NodeUtils'; 101import {ApiExtractor} from '../../common/ApiExtractor'; 102import {performancePrinter, ArkObfuscator, cleanFileMangledNames} from '../../ArkObfuscator'; 103import { EventList, endSingleFileEvent, startSingleFileEvent } from '../../utils/PrinterUtils'; 104import { isViewPUBasedClass } from '../../utils/OhsUtil'; 105import { 106 AtKeepCollections, 107 LocalVariableCollections, 108 PropCollections, 109 UnobfuscationCollections 110} from '../../utils/CommonCollections'; 111import { MemoryDottingDefine } from '../../utils/MemoryDottingDefine'; 112import { shouldKeepCurFileParamerters, shouldKeepParameter } from '../../utils/KeepParameterUtils'; 113import { addToSet } from '../../utils/ProjectCollections'; 114 115namespace secharmony { 116 /** 117 * Rename Identifiers, including: 118 * 1. variable name 119 * 2. function name 120 * 3. label name 121 * 4. class name/interface name/ label name 122 * we need implement some features: 123 * 1. rename identifiers 124 * 2. store/restore name to/from nameCache file. 125 * 3. do scope analysis for identifier obfuscations 126 * 127 * @param option 128 */ 129 const createRenameIdentifierFactory = function (option: IOptions): TransformerFactory<Node> | null { 130 const profile: INameObfuscationOption | undefined = option?.mNameObfuscation; 131 const shouldPrintKeptNames: boolean = !!(option.mUnobfuscationOption?.mPrintKeptNames); 132 const enablePropertyObf: boolean = !!(profile?.mRenameProperties); 133 134 if (!profile || !profile.mEnable) { 135 return null; 136 } 137 138 let options: NameGeneratorOptions = {}; 139 globalGenerator = getNameGenerator(profile.mNameGeneratorType, options); 140 141 const enableToplevel: boolean = option?.mNameObfuscation?.mTopLevel; 142 const exportObfuscation: boolean = option?.mExportObfuscation; 143 let isInitializedReservedList = false; 144 return renameIdentifierFactory; 145 146 function renameIdentifierFactory(context: TransformationContext): Transformer<Node> { 147 initWhitelist(); 148 let mangledSymbolNames: Map<Symbol, MangledSymbolInfo> = new Map<Symbol, MangledSymbolInfo>(); 149 let mangledPropertyParameterSymbolNames: Map<Declaration, MangledSymbolInfo> = new Map<Declaration, MangledSymbolInfo>(); 150 let mangledLabelNames: Map<Label, string> = new Map<Label, string>(); 151 let fileExportNames: Set<string> = undefined; 152 let fileImportNames: Set<string> = undefined; 153 exportElementsWithoutSymbol.clear(); 154 exportSymbolAliasMap.clear(); 155 nodeSymbolMap.clear(); 156 157 let historyMangledNames: Set<string> = undefined; 158 if (historyNameCache && historyNameCache.size > 0) { 159 historyMangledNames = new Set<string>(Array.from(historyNameCache.values())); 160 } 161 162 let checker: TypeChecker = undefined; 163 let manager: ScopeManager = createScopeManager(); 164 let isCurFileParamertersKept: boolean = false; 165 return renameTransformer; 166 167 /** 168 * Transformer to rename identifiers 169 * 170 * @param node ast node of a file. 171 */ 172 function renameTransformer(node: Node): Node { 173 if (nameCache.size === 0) { 174 nameCache.set(IDENTIFIER_CACHE, new Map<string, string>()); 175 nameCache.set(MEM_METHOD_CACHE, new Map<string, string>()); 176 } 177 178 if (!isSourceFile(node) || ArkObfuscator.isKeptCurrentFile) { 179 return node; 180 } 181 isCurFileParamertersKept = shouldKeepCurFileParamerters(node, profile); 182 183 const checkRecordInfo = ArkObfuscator.recordStage(MemoryDottingDefine.CREATE_CHECKER); 184 startSingleFileEvent(EventList.CREATE_CHECKER, performancePrinter.timeSumPrinter); 185 checker = TypeUtils.createChecker(node); 186 endSingleFileEvent(EventList.CREATE_CHECKER, performancePrinter.timeSumPrinter); 187 ArkObfuscator.stopRecordStage(checkRecordInfo); 188 189 const scopeRecordInfo = ArkObfuscator.recordStage(MemoryDottingDefine.SCOPE_ANALYZE); 190 startSingleFileEvent(EventList.SCOPE_ANALYZE, performancePrinter.timeSumPrinter); 191 manager.analyze(node, checker, exportObfuscation); 192 endSingleFileEvent(EventList.SCOPE_ANALYZE, performancePrinter.timeSumPrinter); 193 ArkObfuscator.stopRecordStage(scopeRecordInfo); 194 195 let rootScope: Scope = manager.getRootScope(); 196 fileExportNames = rootScope.fileExportNames; 197 fileImportNames = rootScope.fileImportNames; 198 let renameProcessors: ((scope: Scope) => void)[] = [renameLabelsInScope, renameNamesInScope]; 199 if (profile.mRenameProperties) { 200 renameProcessors.push(renamePropertyParametersInScope); 201 } 202 203 const obfuscateNamesRecordInfo = ArkObfuscator.recordStage(MemoryDottingDefine.OBFUSCATE_NAMES); 204 startSingleFileEvent(EventList.CREATE_OBFUSCATED_NAMES, performancePrinter.timeSumPrinter); 205 getMangledNamesInScope(rootScope, renameProcessors); 206 endSingleFileEvent(EventList.CREATE_OBFUSCATED_NAMES, performancePrinter.timeSumPrinter); 207 ArkObfuscator.stopRecordStage(obfuscateNamesRecordInfo); 208 209 rootScope = undefined; 210 211 const obfuscateNodesRecordInfo = ArkObfuscator.recordStage(MemoryDottingDefine.OBFUSCATE_NODES); 212 startSingleFileEvent(EventList.OBFUSCATE_NODES, performancePrinter.timeSumPrinter); 213 let updatedNode: Node = renameIdentifiers(node); 214 215 // obfuscate property parameter declaration 216 if (profile.mRenameProperties) { 217 updatedNode = visitPropertyParameter(updatedNode); 218 } 219 220 let parentNodes = setParentRecursive(updatedNode, true); 221 endSingleFileEvent(EventList.OBFUSCATE_NODES, performancePrinter.timeSumPrinter); 222 ArkObfuscator.stopRecordStage(obfuscateNodesRecordInfo); 223 return parentNodes; 224 } 225 226 /** 227 * get mangled names of symbols stored in scopes. 228 * 229 * @param scope scope, such as global, module, function, block 230 * @param processors processors to get mangled names 231 */ 232 function getMangledNamesInScope(scope: Scope, processors: ((scope: Scope) => void)[]): void { 233 for (const process of processors) { 234 process(scope); 235 } 236 237 let subScope = undefined; 238 while (scope.children.length > 0) { 239 subScope = scope.children.pop(); 240 getMangledNamesInScope(subScope, processors); 241 subScope = undefined; 242 } 243 } 244 245 // process symbols in scope, exclude property name. 246 function renameNamesInScope(scope: Scope): void { 247 if (isExcludeScope(scope)) { 248 return; 249 } 250 251 if (!exportObfuscation) { 252 scope.defs.forEach((def) => { 253 let parentScope = scope; 254 while (parentScope) { 255 if (parentScope.importNames && parentScope.importNames.has(def.name)) { 256 scope.defs.delete(def); 257 scope.mangledNames.add(def.name); 258 } 259 parentScope = parentScope.parent; 260 } 261 }); 262 } 263 264 renames(scope, scope.defs, globalGenerator); 265 } 266 267 // process property parameters symbols in class scope 268 function renamePropertyParametersInScope(scope: Scope): void { 269 if (!isClassScope(scope)) { 270 return; 271 } 272 273 renamePropertyParameters(scope, scope.defs, globalGenerator); 274 } 275 276 function renames(scope: Scope, defs: Set<Symbol>, generator: INameGenerator): void { 277 defs.forEach((def) => { 278 const original: string = def.name; 279 let mangled: string = original; 280 const path: string = getNameWithScopeLoc(scope, original); 281 // No allow to rename reserved names. 282 if (!Reflect.has(def, 'obfuscateAsProperty') && 283 isInLocalWhitelist(original, UnobfuscationCollections.unobfuscatedNamesMap, path, shouldPrintKeptNames) || 284 (!exportObfuscation && scope.exportNames.has(def.name)) || 285 isSkippedGlobal(enableToplevel, scope)) { 286 scope.mangledNames.add(mangled); 287 mangledSymbolNames.set(def, {mangledName: mangled, originalNameWithScope: path}); 288 return; 289 } 290 291 if (mangledSymbolNames.has(def)) { 292 return; 293 } 294 295 const declarationOfSymbol: Declaration | undefined = def.declarations?.[0]; 296 if (isCurFileParamertersKept && shouldKeepParameter(declarationOfSymbol, profile, mangledSymbolNames, checker)) { 297 mangledSymbolNames.set(def, {mangledName: mangled, originalNameWithScope: path}); 298 return; 299 } 300 301 const historyName: string = historyNameCache?.get(path); 302 if (historyName) { 303 recordHistoryUnobfuscatedNames(path); // For incremental build 304 mangled = historyName; 305 } else if (Reflect.has(def, 'obfuscateAsProperty')) { 306 // obfuscate toplevel, export 307 mangled = getPropertyMangledName(original, path); 308 } else { 309 // obfuscate local variable 310 mangled = getMangled(scope, generator); 311 } 312 // add new names to name cache 313 let identifierCache = nameCache?.get(IDENTIFIER_CACHE); 314 (identifierCache as Map<string, string>).set(path, mangled); 315 let symbolInfo: MangledSymbolInfo = { 316 mangledName: mangled, 317 originalNameWithScope: path 318 }; 319 scope.mangledNames.add(mangled); 320 mangledSymbolNames.set(def, symbolInfo); 321 }); 322 } 323 324 function renamePropertyParameters(scope: Scope, defs: Set<Symbol>, generator: INameGenerator): void { 325 defs.forEach((def) => { 326 //only rename property parameters 327 if (!def.valueDeclaration || !isParameter(def.valueDeclaration)) { 328 return; 329 } 330 const originalName: string = def.name; 331 const path: string = getNameWithScopeLoc(scope, originalName); 332 let mangledName: string; 333 if (isInPropertyWhitelist(originalName, UnobfuscationCollections.unobfuscatedPropMap, shouldPrintKeptNames)) { 334 mangledName = originalName; 335 } else { 336 mangledName = getMangledPropertyParameters(scope, generator, originalName); 337 } 338 scope.mangledNames.add(mangledName); 339 let symbolInfo: MangledSymbolInfo = { 340 mangledName: mangledName, 341 originalNameWithScope: path 342 }; 343 mangledPropertyParameterSymbolNames.set(def.valueDeclaration, symbolInfo); 344 }); 345 } 346 347 function getMangledPropertyParameters(scope: Scope, localGenerator: INameGenerator, originalName: string): string { 348 const historyName: string = PropCollections.historyMangledTable?.get(originalName); 349 let mangledName: string = historyName ? historyName : PropCollections.globalMangledTable.get(originalName); 350 while (!mangledName) { 351 let tmpName = localGenerator.getName(); 352 if (isReservedLocalVariable(tmpName)) { 353 continue; 354 } 355 if (isReservedProperty(tmpName) || tmpName === originalName) { 356 continue; 357 } 358 if (historyMangledNames && historyMangledNames.has(tmpName)) { 359 continue; 360 } 361 362 // For incremental compilation, preventing generated names from conflicting with existing global name. 363 if (PropCollections.globalMangledNamesInCache.has(tmpName)) { 364 continue; 365 } 366 if (searchMangledInParent(scope, tmpName)) { 367 continue; 368 } 369 mangledName = tmpName; 370 } 371 PropCollections.globalMangledTable.set(originalName, mangledName); 372 return mangledName; 373 } 374 375 function getPropertyMangledName(original: string, nameWithScope: string): string { 376 if (isInTopLevelWhitelist(original, UnobfuscationCollections.unobfuscatedNamesMap, nameWithScope, enablePropertyObf, shouldPrintKeptNames)) { 377 return original; 378 } 379 380 const historyName: string = PropCollections.historyMangledTable?.get(original); 381 let mangledName: string = historyName ? historyName : PropCollections.globalMangledTable.get(original); 382 while (!mangledName) { 383 let tmpName = globalGenerator.getName(); 384 if (isReservedTopLevel(tmpName, enablePropertyObf) || 385 tmpName === original) { 386 continue; 387 } 388 389 /** 390 * In case b is obfuscated as a when only enable toplevel obfuscation: 391 * let b = 1; 392 * export let a = 1; 393 */ 394 if (cleanFileMangledNames && fileExportNames && fileExportNames.has(tmpName)) { 395 continue; 396 } 397 398 /** 399 * In case b is obfuscated as a when only enable toplevel obfuscation: 400 * import {a} from 'filePath'; 401 * let b = 1; 402 */ 403 if (cleanFileMangledNames && fileImportNames.has(tmpName)) { 404 continue; 405 } 406 407 /** 408 * In case a newly added variable get an obfuscated name that is already in history namecache 409 */ 410 if (historyMangledNames && historyMangledNames.has(tmpName)) { 411 continue; 412 } 413 414 /** 415 * For incremental compilation, preventing generated names from conflicting with existing global name. 416 */ 417 if (PropCollections.globalMangledNamesInCache.has(tmpName)) { 418 continue; 419 } 420 421 if (ApiExtractor.mConstructorPropertySet.has(tmpName)) { 422 continue; 423 } 424 425 if (ApiExtractor.mEnumMemberSet?.has(tmpName)) { 426 continue; 427 } 428 429 mangledName = tmpName; 430 } 431 432 PropCollections.globalMangledTable.set(original, mangledName); 433 return mangledName; 434 } 435 436 function isExcludeScope(scope: Scope): boolean { 437 if (isClassScope(scope)) { 438 return true; 439 } 440 441 if (isInterfaceScope(scope)) { 442 return true; 443 } 444 445 if (isEnumScope(scope)) { 446 return true; 447 } 448 449 return isObjectLiteralScope(scope); 450 } 451 452 function searchMangledInParent(scope: Scope, name: string): boolean { 453 let found: boolean = false; 454 let parentScope = scope; 455 while (parentScope) { 456 if (parentScope.mangledNames.has(name)) { 457 found = true; 458 break; 459 } 460 461 parentScope = parentScope.parent; 462 } 463 464 return found; 465 } 466 467 function getMangled(scope: Scope, localGenerator: INameGenerator): string { 468 let mangled: string = ''; 469 do { 470 mangled = localGenerator.getName()!; 471 // if it is a globally reserved name, it needs to be regenerated 472 if (isReservedLocalVariable(mangled)) { 473 mangled = ''; 474 continue; 475 } 476 477 if (fileExportNames && fileExportNames.has(mangled)) { 478 mangled = ''; 479 continue; 480 } 481 482 if (historyMangledNames && historyMangledNames.has(mangled)) { 483 mangled = ''; 484 continue; 485 } 486 487 /** 488 * For incremental compilation, preventing generated names from conflicting with existing global name. 489 */ 490 if (PropCollections.globalMangledNamesInCache.has(mangled)) { 491 mangled = ''; 492 continue; 493 } 494 495 if (searchMangledInParent(scope, mangled)) { 496 mangled = ''; 497 continue; 498 } 499 500 if (ApiExtractor.mConstructorPropertySet.has(mangled)) { 501 mangled = ''; 502 } 503 504 if (ApiExtractor.mEnumMemberSet?.has(mangled)) { 505 mangled = ''; 506 } 507 } while (mangled === ''); 508 509 return mangled; 510 } 511 512 // process labels in scope, the label can't rename as the name of top labels. 513 function renameLabelsInScope(scope: Scope): void { 514 const labels: Label[] = scope.labels; 515 if (labels.length > 0) { 516 let upperMangledLabels = getUpperMangledLabelNames(labels[0]); 517 for (const label of labels) { 518 let mangledLabel = getMangledLabel(label, upperMangledLabels); 519 mangledLabelNames.set(label, mangledLabel); 520 } 521 } 522 } 523 524 function getMangledLabel(label: Label, mangledLabels: string[]): string { 525 let mangledLabel: string = ''; 526 do { 527 mangledLabel = globalGenerator.getName(); 528 if (mangledLabel === label.name) { 529 mangledLabel = ''; 530 } 531 532 if (mangledLabels.includes(mangledLabel)) { 533 mangledLabel = ''; 534 } 535 } while (mangledLabel === ''); 536 537 return mangledLabel; 538 } 539 540 function getUpperMangledLabelNames(label: Label): string[] { 541 const results: string[] = []; 542 let parent: Label = label.parent; 543 while (parent) { 544 let mangledLabelName: string = mangledLabelNames.get(parent); 545 if (mangledLabelName) { 546 results.push(mangledLabelName); 547 } 548 parent = parent.parent; 549 } 550 551 return results; 552 } 553 554 function isFunctionLike(node: Node): boolean { 555 switch (node.kind) { 556 case SyntaxKind.FunctionDeclaration: 557 case SyntaxKind.MethodDeclaration: 558 case SyntaxKind.GetAccessor: 559 case SyntaxKind.SetAccessor: 560 case SyntaxKind.Constructor: 561 case SyntaxKind.FunctionExpression: 562 case SyntaxKind.ArrowFunction: 563 return true; 564 } 565 return false; 566 } 567 568 function nodeHasFunctionLikeChild(node: Node): boolean { 569 let hasFunctionLikeChild: boolean = false; 570 let childVisitor: (child: Node) => Node = (child: Node): Node => { 571 if (!hasFunctionLikeChild && child && isFunctionLike(child)) { 572 hasFunctionLikeChild = true; 573 } 574 return child; 575 }; 576 visitEachChild(node, childVisitor, context); 577 return hasFunctionLikeChild; 578 } 579 580 /** 581 * visit each node to change identifier name to mangled name 582 * - calculate shadow name index to find shadow node 583 * @param node 584 */ 585 function renameIdentifiers(node: Node): Node { 586 let needHandlePositionInfo: boolean = isFunctionLike(node) || nodeHasFunctionLikeChild(node); 587 if (needHandlePositionInfo) { 588 // Obtain line info for nameCache. 589 handlePositionInfo(node); 590 } 591 592 if (!isIdentifier(node) || !node.parent) { 593 return visitEachChild(node, renameIdentifiers, context); 594 } 595 596 if (isLabeledStatement(node.parent) || isBreakOrContinueStatement(node.parent)) { 597 return updateLabelNode(node); 598 } 599 600 return updateNameNode(node); 601 } 602 603 /** 604 * visit each property parameter to change identifier name to mangled name 605 * - calculate shadow name index to find shadow node 606 * @param node 607 */ 608 function visitPropertyParameter(node: Node): Node { 609 if (isConstructorDeclaration(node)) { 610 return visitPropertyParameterInConstructor(node); 611 } 612 613 return visitEachChild(node, visitPropertyParameter, context); 614 615 function visitPropertyParameterInConstructor(node: Node): Node { 616 if (!isIdentifier(node) || !node.parent) { 617 return visitEachChild(node, visitPropertyParameterInConstructor, context); 618 } 619 620 // we do not obfuscate the identifier of property access expression, like "a" in "this.a", 621 // since it will be obfuscated in renamePropertiesTransformer 622 if (NodeUtils.isPropertyNode(node)) { 623 return node; 624 } 625 626 return updatePropertyParameterNameNode(node); 627 } 628 } 629 630 function handlePositionInfo(node: Node): void { 631 const sourceFile = NodeUtils.getSourceFileOfNode(node); 632 if (node && node.pos < 0 && node.end < 0) { 633 // Node must have a real position for following operations. 634 // Adapting to the situation that the node does not have a real postion. 635 return; 636 } 637 const startPosition = sourceFile.getLineAndCharacterOfPosition(node.getStart()); 638 const endPosition = sourceFile.getLineAndCharacterOfPosition(node.getEnd()); 639 // 1: The line number in sourceFile starts from 0 while in IDE starts from 1. 640 const startLine = startPosition.line + 1; 641 const startCharacter = startPosition.character + 1; // 1: Same as above. 642 const endLine = endPosition.line + 1; // 1: Same as above. 643 const endCharacter = endPosition.character + 1; // 1: Same as above. 644 const lineAndColum: string = ':' + startLine + ':' + startCharacter + ':' + endLine + ':' + endCharacter; 645 646 let isProperty: boolean = isMethodDeclaration(node) || isGetAccessor(node) || 647 isSetAccessor(node) || (isConstructorDeclaration(node) && 648 !(isClassDeclaration(node.parent) && isViewPUBasedClass(node.parent))); 649 // Arrow functions are anoymous, only function expressions are considered. 650 let isPropertyParent: boolean = isFunctionExpression(node) && 651 (isPropertyDeclaration(node.parent) || isPropertyAssignment(node.parent)); 652 let isMemberMethod: boolean = isProperty || isPropertyParent; 653 if (isMemberMethod) { 654 writeMemberMethodCache(node, lineAndColum); 655 return; 656 } 657 658 let name = Reflect.get(node, 'name') as Identifier; 659 if (name?.kind === SyntaxKind.Identifier) { 660 identifierLineMap.set(name, lineAndColum); 661 } else if ((isFunctionExpression(node) || isArrowFunction(node)) && isVariableDeclaration(node.parent) && 662 node.parent.name?.kind === SyntaxKind.Identifier) { 663 // The node is anonymous, and we need to find its parent node. 664 // e.g.: let foo = function() {}; 665 identifierLineMap.set(node.parent.name, lineAndColum); 666 } 667 } 668 669 function writeMemberMethodCache(node: Node, lineAndColum: string): void { 670 let gotNode; 671 if (node.kind === SyntaxKind.Constructor) { 672 gotNode = node.parent; 673 } else if ((node.kind === SyntaxKind.FunctionExpression && 674 (isPropertyDeclaration(node.parent) || isPropertyAssignment(node.parent)))) { 675 gotNode = node.parent.initializer ?? node.parent; 676 } else { 677 gotNode = node; 678 } 679 680 let isIdentifierNode: boolean = gotNode.name && (isIdentifier(gotNode.name) || isPrivateIdentifier(gotNode.name)); 681 let valueName: string = ''; 682 683 if (isIdentifierNode) { 684 // The original method for retrieving method names used gotNode.name.escapedText. This approach limited the collection 685 // of method records in MemberMethodCache to cases where gotNode.name was an Identifier or PrivateIdentifier. 686 // To address the issue where method names starting with double underscores were transformed to start with triple underscores, 687 // we changed the retrieval method to use gotNode.name.text instead of escapedText. However, this change introduced the possibility 688 // of collecting method records when gotNode.name is a NumericLiteral or StringLiteral, which is not desired. 689 // To avoid altering the collection specifications of MemberMethodCache, we restricted the collection scenarios 690 // to match the original cases where only identifiers and private identifiers are collected. 691 valueName = gotNode.name.text; 692 } 693 694 if (valueName === '') { 695 return; 696 } 697 698 let originalName: string = valueName; 699 let keyName = originalName + lineAndColum; 700 if (node.kind === SyntaxKind.Constructor && classMangledName.has(gotNode.name)) { 701 valueName = classMangledName.get(gotNode.name); 702 classInfoInMemberMethodCache.add(keyName); 703 } 704 let memberMethodCache = nameCache?.get(MEM_METHOD_CACHE); 705 if (memberMethodCache) { 706 (memberMethodCache as Map<string, string>).set(keyName, valueName); 707 } 708 } 709 710 function updateNameNode(node: Identifier): Node { 711 // skip property in property access expression 712 if (NodeUtils.isPropertyAccessNode(node)) { 713 return node; 714 } 715 716 if (NodeUtils.isNewTargetNode(node)) { 717 return node; 718 } 719 720 let sym: Symbol | undefined = NodeUtils.findSymbolOfIdentifier(checker, node, nodeSymbolMap); 721 let mangledPropertyNameOfNoSymbolImportExport = ''; 722 if (!sym) { 723 if (shouldObfuscateNodeWithoutSymbol(node)) { 724 mangledPropertyNameOfNoSymbolImportExport = mangleNoSymbolImportExportPropertyName(node.text); 725 } else { 726 return node; 727 } 728 } 729 730 if (exportSymbolAliasMap.has(sym)) { 731 sym = exportSymbolAliasMap.get(sym); 732 } 733 734 // Add new names to name cache 735 const symbolInfo: MangledSymbolInfo = mangledSymbolNames.get(sym); 736 const identifierCache = nameCache?.get(IDENTIFIER_CACHE); 737 const lineAndColumn = identifierLineMap?.get(node); 738 // We only save the line info of FunctionLike. 739 const isFunction: boolean = sym ? Reflect.has(sym, 'isFunction') : false; 740 if (isFunction && symbolInfo && lineAndColumn) { 741 const originalName = symbolInfo.originalNameWithScope; 742 const pathWithLine: string = originalName + lineAndColumn; 743 (identifierCache as Map<string, string>).set(pathWithLine, symbolInfo.mangledName); 744 (identifierCache as Map<string, string>).delete(originalName); 745 } 746 747 let mangledName: string = mangledSymbolNames.get(sym)?.mangledName; 748 if (node?.parent.kind === SyntaxKind.ClassDeclaration) { 749 classMangledName.set(node, mangledName); 750 } 751 if (!mangledName && mangledPropertyNameOfNoSymbolImportExport !== '') { 752 mangledName = mangledPropertyNameOfNoSymbolImportExport; 753 } 754 755 if (!mangledName || mangledName === sym?.name) { 756 return node; 757 } 758 759 return factory.createIdentifier(mangledName); 760 } 761 762 function updatePropertyParameterNameNode(node: Identifier): Node { 763 let sym: Symbol | undefined = NodeUtils.findSymbolOfIdentifier(checker, node, nodeSymbolMap); 764 if (!sym || sym.valueDeclaration?.kind !== SyntaxKind.Parameter) { 765 return node; 766 } 767 768 let mangledName: string | undefined = mangledPropertyParameterSymbolNames.get(sym.valueDeclaration)?.mangledName; 769 if (!mangledName || mangledName === sym?.name) { 770 return node; 771 } 772 773 return factory.createIdentifier(mangledName); 774 } 775 776 function updateLabelNode(node: Identifier): Node { 777 let label: Label | undefined; 778 let labelName: string = ''; 779 780 mangledLabelNames.forEach((value, key) => { 781 if (key.refs.includes(node)) { 782 label = key; 783 labelName = value; 784 } 785 }); 786 787 return label ? factory.createIdentifier(labelName) : node; 788 } 789 790 /** 791 * import {A as B} from 'modulename'; 792 * import {C as D} from 'modulename'; 793 * above A、C have no symbol, so deal with them specially. 794 */ 795 function mangleNoSymbolImportExportPropertyName(original: string): string { 796 const path: string = '#' + original; 797 const historyName: string = historyNameCache?.get(path); 798 let mangled = historyName ?? getPropertyMangledName(original, path); 799 if (nameCache && nameCache.get(IDENTIFIER_CACHE)) { 800 (nameCache.get(IDENTIFIER_CACHE) as Map<string, string>).set(path, mangled); 801 } 802 return mangled; 803 } 804 805 function trySearchImportExportSpecifier(node: Node): boolean { 806 while (node.parent) { 807 node = node.parent; 808 if ((isImportSpecifier(node) || isExportSpecifier(node)) && node.propertyName && isIdentifier(node.propertyName)) { 809 return true; 810 } 811 } 812 return false; 813 } 814 815 function shouldObfuscateNodeWithoutSymbol(node: Identifier): boolean { 816 if (exportObfuscation && exportElementsWithoutSymbol.has(node) && trySearchImportExportSpecifier(node)) { 817 let isGlobalNode: boolean = exportElementsWithoutSymbol.get(node); 818 if ((isGlobalNode && enableToplevel) || !isGlobalNode) { 819 return true; 820 } 821 } 822 return false; 823 } 824 } 825 826 function initWhitelist(): void { 827 if (isInitializedReservedList) { 828 return; 829 } 830 if (enablePropertyObf) { 831 const tmpReservedProps: string[] = profile?.mReservedProperties ?? []; 832 tmpReservedProps.forEach(item => { 833 PropCollections.reservedProperties.add(item); 834 }); 835 PropCollections.globalMangledNamesInCache = new Set(PropCollections.historyMangledTable?.values()); 836 addToSet(PropCollections.reservedProperties, AtKeepCollections.keepSymbol.propertyNames); 837 addToSet(PropCollections.reservedProperties, AtKeepCollections.keepAsConsumer.propertyNames); 838 839 if (profile?.mUniversalReservedProperties) { 840 PropCollections.universalReservedProperties = [...profile.mUniversalReservedProperties]; 841 } 842 UnobfuscationCollections.reservedLangForTopLevel.forEach(element => { 843 UnobfuscationCollections.reservedLangForProperty.add(element); 844 }); 845 UnobfuscationCollections.reservedExportName.forEach(element => { 846 UnobfuscationCollections.reservedExportNameAndProp.add(element); 847 }); 848 UnobfuscationCollections.reservedSdkApiForGlobal.forEach(element => { 849 UnobfuscationCollections.reservedSdkApiForProp.add(element); 850 }); 851 } 852 LocalVariableCollections.reservedConfig = new Set(profile?.mReservedNames ?? []); 853 profile?.mReservedToplevelNames?.forEach(item => PropCollections.reservedProperties.add(item)); 854 addToSet(PropCollections.reservedProperties, AtKeepCollections.keepSymbol.globalNames); 855 addToSet(PropCollections.reservedProperties, AtKeepCollections.keepAsConsumer.globalNames); 856 profile?.mUniversalReservedToplevelNames?.forEach(item => PropCollections.universalReservedProperties.push(item)); 857 isInitializedReservedList = true; 858 } 859 }; 860 861 function isSkippedGlobal(enableTopLevel: boolean, scope: Scope): boolean { 862 return !enableTopLevel && isGlobalScope(scope); 863 } 864 865 export let transformerPlugin: TransformPlugin = { 866 'name': 'renameIdentifierPlugin', 867 'order': TransformerOrder.RENAME_IDENTIFIER_TRANSFORMER, 868 'createTransformerFactory': createRenameIdentifierFactory 869 }; 870 871 export let nameCache: Map<string, string | Map<string, string>> = new Map(); 872 export let historyNameCache: Map<string, string> = undefined; 873 export let historyUnobfuscatedNamesMap: Map<string, string[]> = undefined; 874 export let identifierLineMap: Map<Identifier, string> = new Map(); 875 export let classMangledName: Map<Node, string> = new Map(); 876 // Record the original class name and line number range to distinguish between class names and member method names. 877 export let classInfoInMemberMethodCache: Set<string> = new Set(); 878 // Generate obfuscated names for all property and variable names. 879 export let globalGenerator: INameGenerator; 880 881 export function clearCaches(): void { 882 nameCache.clear(); 883 historyNameCache = undefined; 884 historyUnobfuscatedNamesMap = undefined; 885 identifierLineMap.clear(); 886 classMangledName.clear(); 887 classInfoInMemberMethodCache.clear(); 888 UnobfuscationCollections.unobfuscatedNamesMap.clear(); 889 } 890} 891 892export = secharmony; 893