* Copyright (c) 2023-2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
SyntaxKind,
factory,
forEachChild,
isBreakOrContinueStatement,
isConstructorDeclaration,
isExportSpecifier,
isIdentifier,
isImportSpecifier,
isLabeledStatement,
isMetaProperty,
isSourceFile,
isStructDeclaration,
setParentRecursive,
visitEachChild,
isPropertyDeclaration,
isMethodDeclaration,
isGetAccessor,
isSetAccessor,
isClassDeclaration,
isFunctionExpression,
isArrowFunction,
isVariableDeclaration,
isPropertyAssignment,
isPrivateIdentifier,
isParameter,
isPropertyAccessExpression,
isAnnotationDeclaration,
isVariableStatement,
getIllegalDecorators,
getAnnotationsFromIllegalDecorators,
canHaveIllegalDecorators
} from 'typescript';
import type {
ClassElement,
Declaration,
Identifier,
Node,
SourceFile,
StructDeclaration,
Symbol,
TransformationContext,
Transformer,
TransformerFactory,
TypeChecker
} from 'typescript';
import {
createScopeManager,
exportElementsWithoutSymbol,
exportSymbolAliasMap,
getNameWithScopeLoc,
isClassScope,
isGlobalScope,
isEnumScope,
isInterfaceScope,
isObjectLiteralScope,
nodeSymbolMap
} from '../../utils/ScopeAnalyzer';
import type {
Label,
Scope,
ScopeManager
} from '../../utils/ScopeAnalyzer';
import {
IDENTIFIER_CACHE,
MEM_METHOD_CACHE
} from '../../utils/NameCacheUtil';
import type {INameGenerator, NameGeneratorOptions} from '../../generator/INameGenerator';
import type {IOptions} from '../../configs/IOptions';
import type { INameObfuscationOption, IUnobfuscationOption } from '../../configs/INameObfuscationOption';
import type {TransformPlugin} from '../TransformPlugin';
import { annotationPrefix, type MangledSymbolInfo } from '../../common/type';
import {TransformerOrder} from '../TransformPlugin';
import {getNameGenerator, NameGeneratorType} from '../../generator/NameFactory';
import {TypeUtils} from '../../utils/TypeUtils';
import {
isInTopLevelWhitelist,
isInLocalWhitelist,
isReservedLocalVariable,
isReservedTopLevel,
recordHistoryUnobfuscatedNames,
isInPropertyWhitelist,
isReservedProperty
} from '../../utils/TransformUtil';
import {NodeUtils} from '../../utils/NodeUtils';
import {ApiExtractor} from '../../common/ApiExtractor';
import {performancePrinter, ArkObfuscator, cleanFileMangledNames, FileUtils} from '../../ArkObfuscator';
import { endSingleFileEvent, startSingleFileEvent } from '../../utils/PrinterUtils';
import { EventList, endSingleFileForMoreTimeEvent, startSingleFileForMoreTimeEvent } from '../../utils/PrinterTimeAndMemUtils';
import { isViewPUBasedClass } from '../../utils/OhsUtil';
import {
AtIntentCollections,
AtKeepCollections,
BytecodeObfuscationCollections,
LocalVariableCollections,
PropCollections,
UnobfuscationCollections
} from '../../utils/CommonCollections';
import { MemoryDottingDefine } from '../../utils/MemoryDottingDefine';
import { shouldKeepCurFileParamerters, shouldKeepParameter } from '../../utils/KeepParameterUtils';
import { addToSet } from '../../utils/ProjectCollections';
namespace secharmony {
* Rename Identifiers, including:
* 1. variable name
* 2. function name
* 3. label name
* 4. class name/interface name/ label name
* we need implement some features:
* 1. rename identifiers
* 2. store/restore name to/from nameCache file.
* 3. do scope analysis for identifier obfuscations
*
* @param option
*/
const createRenameIdentifierFactory = function (option: IOptions): TransformerFactory<Node> | null {
const profile: INameObfuscationOption | undefined = option?.mNameObfuscation;
const shouldPrintKeptNames: boolean = !!(option.mUnobfuscationOption?.mPrintKeptNames);
const enablePropertyObf: boolean = !!(profile?.mRenameProperties);
if (!profile || !profile.mEnable) {
return null;
}
let options: NameGeneratorOptions = {};
globalGenerator = getNameGenerator(profile.mNameGeneratorType, options);
const enableToplevel: boolean = option?.mNameObfuscation?.mTopLevel;
const exportObfuscation: boolean = option?.mExportObfuscation;
let isInitializedReservedList = false;
return renameIdentifierFactory;
function renameIdentifierFactory(context: TransformationContext): Transformer<Node> {
startSingleFileEvent(EventList.INIT_WHITELIST);
initWhitelist();
endSingleFileEvent(EventList.INIT_WHITELIST);
let mangledSymbolNames: Map<Symbol, MangledSymbolInfo> = new Map<Symbol, MangledSymbolInfo>();
let mangledPropertyParameterSymbolNames: Map<Declaration, MangledSymbolInfo> = new Map<Declaration, MangledSymbolInfo>();
let mangledLabelNames: Map<Label, string> = new Map<Label, string>();
let fileExportNames: Set<string> = undefined;
let fileImportNames: Set<string> = undefined;
let currentFileType: string | undefined = undefined;
exportElementsWithoutSymbol.clear();
exportSymbolAliasMap.clear();
nodeSymbolMap.clear();
let historyMangledNames: Set<string> = undefined;
if (historyNameCache && historyNameCache.size > 0) {
historyMangledNames = new Set<string>(Array.from(historyNameCache.values()));
}
let checker: TypeChecker = undefined;
let manager: ScopeManager = createScopeManager();
let isCurFileParamertersKept: boolean = false;
return renameTransformer;
* Transformer to rename identifiers
*
* @param node ast node of a file.
*/
function renameTransformer(node: Node): Node {
if (nameCache.size === 0) {
nameCache.set(IDENTIFIER_CACHE, new Map<string, string>());
nameCache.set(MEM_METHOD_CACHE, new Map<string, string>());
}
if (!isSourceFile(node) || ArkObfuscator.isKeptCurrentFile) {
return node;
}
currentFileType = FileUtils.getFileSuffix(node.fileName).ext;
isCurFileParamertersKept = shouldKeepCurFileParamerters(node, profile);
const checkRecordInfo = ArkObfuscator.recordStage(MemoryDottingDefine.CREATE_CHECKER);
startSingleFileEvent(EventList.CREATE_CHECKER, performancePrinter.timeSumPrinter);
checker = TypeUtils.createChecker(node);
endSingleFileEvent(EventList.CREATE_CHECKER, performancePrinter.timeSumPrinter);
ArkObfuscator.stopRecordStage(checkRecordInfo);
const scopeRecordInfo = ArkObfuscator.recordStage(MemoryDottingDefine.SCOPE_ANALYZE);
startSingleFileEvent(EventList.SCOPE_ANALYZE, performancePrinter.timeSumPrinter);
manager.analyze(node, checker, exportObfuscation, currentFileType);
endSingleFileEvent(EventList.SCOPE_ANALYZE, performancePrinter.timeSumPrinter);
ArkObfuscator.stopRecordStage(scopeRecordInfo);
let rootScope: Scope = manager.getRootScope();
fileExportNames = rootScope.fileExportNames;
fileImportNames = rootScope.fileImportNames;
let renameProcessors: ((scope: Scope) => void)[] = [renameLabelsInScope, renameNamesInScope];
if (profile.mRenameProperties) {
renameProcessors.push(renamePropertyParametersInScope);
}
const obfuscateNamesRecordInfo = ArkObfuscator.recordStage(MemoryDottingDefine.OBFUSCATE_NAMES);
startSingleFileEvent(EventList.CREATE_OBFUSCATED_NAMES, performancePrinter.timeSumPrinter);
getMangledNamesInScope(rootScope, renameProcessors);
endSingleFileEvent(EventList.CREATE_OBFUSCATED_NAMES, performancePrinter.timeSumPrinter);
ArkObfuscator.stopRecordStage(obfuscateNamesRecordInfo);
rootScope = undefined;
const obfuscateNodesRecordInfo = ArkObfuscator.recordStage(MemoryDottingDefine.OBFUSCATE_NODES);
startSingleFileEvent(EventList.OBFUSCATE_NODES, performancePrinter.timeSumPrinter);
startSingleFileEvent(EventList.RENAME_IDENTIFIERS);
let updatedNode: Node = renameIdentifiers(node);
endSingleFileEvent(EventList.RENAME_IDENTIFIERS);
if (profile.mRenameProperties) {
startSingleFileEvent(EventList.VISIT_PROPERTY_PARAMETER);
updatedNode = visitPropertyParameter(updatedNode);
endSingleFileEvent(EventList.VISIT_PROPERTY_PARAMETER);
}
let parentNodes = setParentRecursive(updatedNode, true);
endSingleFileEvent(EventList.OBFUSCATE_NODES, performancePrinter.timeSumPrinter);
ArkObfuscator.stopRecordStage(obfuscateNodesRecordInfo);
return parentNodes;
}
* get mangled names of symbols stored in scopes.
*
* @param scope scope, such as global, module, function, block
* @param processors processors to get mangled names
*/
function getMangledNamesInScope(scope: Scope, processors: ((scope: Scope) => void)[]): void {
for (const process of processors) {
process(scope);
}
let subScope = undefined;
while (scope.children.length > 0) {
subScope = scope.children.pop();
getMangledNamesInScope(subScope, processors);
subScope = undefined;
}
}
function renameNamesInScope(scope: Scope): void {
if (isExcludeScope(scope)) {
return;
}
if (!exportObfuscation) {
scope.defs.forEach((def) => {
let parentScope = scope;
while (parentScope) {
if (parentScope.importNames && parentScope.importNames.has(def.name)) {
scope.defs.delete(def);
scope.mangledNames.add(def.name);
}
parentScope = parentScope.parent;
}
});
}
startSingleFileEvent(EventList.RENAME);
renames(scope, scope.defs, globalGenerator);
endSingleFileEvent(EventList.RENAME);
}
function renamePropertyParametersInScope(scope: Scope): void {
if (!isClassScope(scope)) {
return;
}
renamePropertyParameters(scope, scope.defs, globalGenerator);
}
function renames(scope: Scope, defs: Set<Symbol>, generator: INameGenerator): void {
defs.forEach((def) => {
const original: string = def.name;
let mangled: string = original;
const path: string = getNameWithScopeLoc(scope, original);
if (!Reflect.has(def, 'obfuscateAsProperty') &&
isInLocalWhitelist(original, UnobfuscationCollections.unobfuscatedNamesMap, path, shouldPrintKeptNames) ||
(!exportObfuscation && scope.exportNames.has(def.name)) ||
isSkippedGlobal(enableToplevel, scope)) {
scope.mangledNames.add(mangled);
mangledSymbolNames.set(def, {mangledName: mangled, originalNameWithScope: path});
return;
}
if (mangledSymbolNames.has(def)) {
return;
}
const declarationOfSymbol: Declaration | undefined = def.declarations?.[0];
if (isCurFileParamertersKept && shouldKeepParameter(declarationOfSymbol, profile, mangledSymbolNames, checker)) {
mangledSymbolNames.set(def, {mangledName: mangled, originalNameWithScope: path});
return;
}
const historyName: string = historyNameCache?.get(path);
if (historyName) {
recordHistoryUnobfuscatedNames(path);
mangled = historyName;
} else if (Reflect.has(def, 'obfuscateAsProperty')) {
mangled = getPropertyOrAnnotationMangledName(original, path);
} else {
mangled = getMangledLocalName(scope, generator);
}
let identifierCache = nameCache?.get(IDENTIFIER_CACHE);
(identifierCache as Map<string, string>).set(path, mangled);
let symbolInfo: MangledSymbolInfo = {
mangledName: mangled,
originalNameWithScope: path
};
scope.mangledNames.add(mangled);
mangledSymbolNames.set(def, symbolInfo);
});
}
function renamePropertyParameters(scope: Scope, defs: Set<Symbol>, generator: INameGenerator): void {
defs.forEach((def) => {
if (!def.valueDeclaration || !isParameter(def.valueDeclaration)) {
return;
}
const originalName: string = def.name;
const path: string = getNameWithScopeLoc(scope, originalName);
let mangledName: string;
if (isInPropertyWhitelist(originalName, UnobfuscationCollections.unobfuscatedPropMap, shouldPrintKeptNames)) {
mangledName = originalName;
} else {
mangledName = getMangledPropertyParameters(scope, generator, originalName);
}
scope.mangledNames.add(mangledName);
let symbolInfo: MangledSymbolInfo = {
mangledName: mangledName,
originalNameWithScope: path
};
mangledPropertyParameterSymbolNames.set(def.valueDeclaration, symbolInfo);
});
}
function getMangledPropertyParameters(scope: Scope, localGenerator: INameGenerator, originalName: string): string {
const historyName: string = PropCollections.historyMangledTable?.get(originalName);
let mangledName: string = historyName ? historyName : PropCollections.globalMangledTable.get(originalName);
while (!mangledName) {
let tmpName = localGenerator.getName();
if (isReservedLocalVariable(tmpName)) {
continue;
}
if (isReservedProperty(tmpName) || tmpName === originalName) {
continue;
}
if (historyMangledNames && historyMangledNames.has(tmpName)) {
continue;
}
if (PropCollections.globalMangledNamesInCache.has(tmpName)) {
continue;
}
if (searchMangledInParent(scope, tmpName)) {
continue;
}
mangledName = tmpName;
}
PropCollections.globalMangledTable.set(originalName, mangledName);
return mangledName;
}
function getMangledName(original: string): string {
const historyName: string = PropCollections.historyMangledTable?.get(original);
let mangledName: string = historyName ? historyName : PropCollections.globalMangledTable.get(original);
while (!mangledName) {
let tmpName = globalGenerator.getName();
if (isReservedTopLevel(tmpName, enablePropertyObf) ||
tmpName === original) {
continue;
}
* In case b is obfuscated as a when only enable toplevel obfuscation:
* let b = 1;
* export let a = 1;
*/
if (cleanFileMangledNames && fileExportNames && fileExportNames.has(tmpName)) {
continue;
}
* In case b is obfuscated as a when only enable toplevel obfuscation:
* import {a} from 'filePath';
* let b = 1;
*/
if (cleanFileMangledNames && fileImportNames.has(tmpName)) {
continue;
}
* In case a newly added variable get an obfuscated name that is already in history namecache
*/
if (historyMangledNames && historyMangledNames.has(tmpName)) {
continue;
}
* For incremental compilation, preventing generated names from conflicting with existing global name.
*/
if (PropCollections.globalMangledNamesInCache.has(tmpName)) {
continue;
}
if (ApiExtractor.mConstructorPropertySet.has(tmpName)) {
continue;
}
if (ApiExtractor.mEnumMemberSet?.has(tmpName)) {
continue;
}
mangledName = tmpName;
}
PropCollections.globalMangledTable.set(original, mangledName);
return mangledName;
}
function getPropertyMangledName(originalName: string, nameWithScope: string): string {
if (isInTopLevelWhitelist(originalName, UnobfuscationCollections.unobfuscatedNamesMap, nameWithScope, enablePropertyObf, shouldPrintKeptNames)) {
return originalName;
}
let mangledName = getMangledName(originalName);
return mangledName;
}
function getAnnotationMangledNameWithPrefix(originalName: string, nameWithScope: string): string {
if (isInTopLevelWhitelist(originalName, UnobfuscationCollections.unobfuscatedNamesMap, nameWithScope, enablePropertyObf, shouldPrintKeptNames)) {
return `${annotationPrefix}${originalName}`;
}
let mangledName: string = `${annotationPrefix}${getMangledName(originalName)}`;
return mangledName;
}
function getPropertyOrAnnotationMangledName(originalName: string, nameWithScope: string): string {
let name: string | undefined = originalName.startsWith(annotationPrefix) ? originalName.substring(annotationPrefix.length) : undefined;
if (name) {
return getAnnotationMangledNameWithPrefix(name, nameWithScope);
} else {
return getPropertyMangledName(originalName, nameWithScope);
}
}
function isExcludeScope(scope: Scope): boolean {
if (isClassScope(scope)) {
return true;
}
if (isInterfaceScope(scope)) {
return true;
}
if (isEnumScope(scope)) {
return true;
}
return isObjectLiteralScope(scope);
}
function searchMangledInParent(scope: Scope, name: string): boolean {
let found: boolean = false;
let parentScope = scope;
while (parentScope) {
if (parentScope.mangledNames.has(name)) {
found = true;
break;
}
parentScope = parentScope.parent;
}
return found;
}
function getMangledLocalName(scope: Scope, localGenerator: INameGenerator): string {
let mangled: string = '';
do {
mangled = localGenerator.getName()!;
if (isReservedLocalVariable(mangled)) {
mangled = '';
continue;
}
if (fileExportNames && fileExportNames.has(mangled)) {
mangled = '';
continue;
}
if (historyMangledNames && historyMangledNames.has(mangled)) {
mangled = '';
continue;
}
* For incremental compilation, preventing generated names from conflicting with existing global name.
*/
if (PropCollections.globalMangledNamesInCache.has(mangled)) {
mangled = '';
continue;
}
if (searchMangledInParent(scope, mangled)) {
mangled = '';
continue;
}
if (ApiExtractor.mConstructorPropertySet.has(mangled)) {
mangled = '';
}
if (ApiExtractor.mEnumMemberSet?.has(mangled)) {
mangled = '';
}
} while (mangled === '');
return mangled;
}
function renameLabelsInScope(scope: Scope): void {
const labels: Label[] = scope.labels;
if (labels.length > 0) {
let upperMangledLabels = getUpperMangledLabelNames(labels[0]);
for (const label of labels) {
let mangledLabel = getMangledLabel(label, upperMangledLabels);
mangledLabelNames.set(label, mangledLabel);
}
}
}
function getMangledLabel(label: Label, mangledLabels: string[]): string {
let mangledLabel: string = '';
do {
mangledLabel = globalGenerator.getName();
if (mangledLabel === label.name) {
mangledLabel = '';
}
if (mangledLabels.includes(mangledLabel)) {
mangledLabel = '';
}
} while (mangledLabel === '');
return mangledLabel;
}
function getUpperMangledLabelNames(label: Label): string[] {
const results: string[] = [];
let parent: Label = label.parent;
while (parent) {
let mangledLabelName: string = mangledLabelNames.get(parent);
if (mangledLabelName) {
results.push(mangledLabelName);
}
parent = parent.parent;
}
return results;
}
function isFunctionLike(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.Constructor:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return true;
}
return false;
}
function nodeHasFunctionLikeChild(node: Node): boolean {
let hasFunctionLikeChild: boolean = false;
let childVisitor: (child: Node) => Node = (child: Node): Node => {
if (!hasFunctionLikeChild && child && isFunctionLike(child)) {
hasFunctionLikeChild = true;
}
return child;
};
visitEachChild(node, childVisitor, context);
return hasFunctionLikeChild;
}
* visit each node to change identifier name to mangled name
* - calculate shadow name index to find shadow node
* @param node
*/
function renameIdentifiers(node: Node): Node {
startSingleFileForMoreTimeEvent(EventList.HANDLE_POSITION_INFO);
let needHandlePositionInfo: boolean = isFunctionLike(node) || nodeHasFunctionLikeChild(node);
if (needHandlePositionInfo) {
handlePositionInfo(node);
}
endSingleFileForMoreTimeEvent(EventList.HANDLE_POSITION_INFO);
if (canHaveIllegalDecorators(node)) {
const illegalDecorators = getIllegalDecorators(node);
const annotationDecorators = getAnnotationsFromIllegalDecorators(node);
if (!illegalDecorators?.length && !annotationDecorators?.length) {
return visitEachChild(node, renameIdentifiers, context);
}
const reservedDecorators = factory.createNodeArray([
...(illegalDecorators ?? []),
...(annotationDecorators ?? []),
]);
let updated = visitEachChild(node, renameIdentifiers, context);
const visitedReservedDecorators = factory.createNodeArray(
reservedDecorators.map(decorator => visitEachChild(decorator, renameIdentifiers, context))
);
return factory.updateIllegalDecorators(updated, visitedReservedDecorators);
}
if (!isIdentifier(node) || !node.parent) {
return visitEachChild(node, renameIdentifiers, context);
}
if (isLabeledStatement(node.parent) || isBreakOrContinueStatement(node.parent)) {
return updateLabelNode(node);
}
startSingleFileForMoreTimeEvent(EventList.UPDATE_NAME_NODE);
const updatedNode = updateNameNode(node);
endSingleFileForMoreTimeEvent(EventList.UPDATE_NAME_NODE);
return updatedNode;
}
* visit each property parameter to change identifier name to mangled name
* - calculate shadow name index to find shadow node
* @param node
*/
function visitPropertyParameter(node: Node): Node {
if (isConstructorDeclaration(node)) {
return visitPropertyParameterInConstructor(node);
}
return visitEachChild(node, visitPropertyParameter, context);
function visitPropertyParameterInConstructor(node: Node): Node {
if (!isIdentifier(node) || !node.parent) {
return visitEachChild(node, visitPropertyParameterInConstructor, context);
}
if (NodeUtils.isPropertyNode(node)) {
return node;
}
return updatePropertyParameterNameNode(node);
}
}
function handlePositionInfo(node: Node): void {
const sourceFile = NodeUtils.getSourceFileOfNode(node);
if (node && node.pos < 0 && node.end < 0) {
return;
}
const startPosition = sourceFile.getLineAndCharacterOfPosition(node.getStart());
const endPosition = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
const startLine = startPosition.line + 1;
const startCharacter = startPosition.character + 1;
const endLine = endPosition.line + 1;
const endCharacter = endPosition.character + 1;
const lineAndColum: string = ':' + startLine + ':' + startCharacter + ':' + endLine + ':' + endCharacter;
let isProperty: boolean = isMethodDeclaration(node) || isGetAccessor(node) ||
isSetAccessor(node) || (isConstructorDeclaration(node) &&
!(isClassDeclaration(node.parent) && isViewPUBasedClass(node.parent)));
let isPropertyParent: boolean = isFunctionExpression(node) &&
(isPropertyDeclaration(node.parent) || isPropertyAssignment(node.parent));
let isMemberMethod: boolean = isProperty || isPropertyParent;
if (isMemberMethod) {
writeMemberMethodCache(node, lineAndColum);
return;
}
let name = Reflect.get(node, 'name') as Identifier;
if (name?.kind === SyntaxKind.Identifier) {
identifierLineMap.set(name, lineAndColum);
} else if ((isFunctionExpression(node) || isArrowFunction(node)) && isVariableDeclaration(node.parent) &&
node.parent.name?.kind === SyntaxKind.Identifier) {
identifierLineMap.set(node.parent.name, lineAndColum);
}
}
function writeMemberMethodCache(node: Node, lineAndColum: string): void {
let gotNode;
if (node.kind === SyntaxKind.Constructor) {
gotNode = node.parent;
} else if ((node.kind === SyntaxKind.FunctionExpression &&
(isPropertyDeclaration(node.parent) || isPropertyAssignment(node.parent)))) {
gotNode = node.parent.initializer ?? node.parent;
} else {
gotNode = node;
}
let isIdentifierNode: boolean = gotNode.name && (isIdentifier(gotNode.name) || isPrivateIdentifier(gotNode.name));
let valueName: string = '';
if (isIdentifierNode) {
valueName = gotNode.name.text;
}
if (valueName === '') {
return;
}
let originalName: string = valueName;
let keyName = originalName + lineAndColum;
if (node.kind === SyntaxKind.Constructor && classMangledName.has(gotNode.name)) {
valueName = classMangledName.get(gotNode.name);
classInfoInMemberMethodCache.add(keyName);
}
let memberMethodCache = nameCache?.get(MEM_METHOD_CACHE);
if (memberMethodCache) {
(memberMethodCache as Map<string, string>).set(keyName, valueName);
}
}
function updateNameNode(node: Identifier): Node {
if (NodeUtils.isPropertyAccessNode(node)) {
return node;
}
if (NodeUtils.isNewTargetNode(node)) {
return node;
}
let sym: Symbol | undefined = NodeUtils.findSymbolOfIdentifier(checker, node, nodeSymbolMap);
let mangledPropertyNameOfNoSymbolImportExport = '';
if (!sym) {
if (shouldObfuscateNodeWithoutSymbol(node)) {
mangledPropertyNameOfNoSymbolImportExport = mangleNoSymbolImportExportPropertyName(node.text);
} else {
return node;
}
}
if (exportSymbolAliasMap.has(sym)) {
sym = exportSymbolAliasMap.get(sym);
}
const symbolInfo: MangledSymbolInfo = mangledSymbolNames.get(sym);
const identifierCache = nameCache?.get(IDENTIFIER_CACHE);
const lineAndColumn = identifierLineMap?.get(node);
const isFunction: boolean = sym ? Reflect.has(sym, 'isFunction') : false;
if (isFunction && symbolInfo && lineAndColumn) {
const originalName = symbolInfo.originalNameWithScope;
const pathWithLine: string = originalName + lineAndColumn;
(identifierCache as Map<string, string>).set(pathWithLine, symbolInfo.mangledName);
(identifierCache as Map<string, string>).delete(originalName);
}
let mangledName: string = mangledSymbolNames.get(sym)?.mangledName;
if (node?.parent.kind === SyntaxKind.ClassDeclaration) {
classMangledName.set(node, mangledName);
}
if (!mangledName && mangledPropertyNameOfNoSymbolImportExport !== '') {
mangledName = mangledPropertyNameOfNoSymbolImportExport;
}
if (!mangledName || mangledName === sym?.name) {
return node;
}
return factory.createIdentifier(mangledName);
}
function updatePropertyParameterNameNode(node: Identifier): Node {
let sym: Symbol | undefined = NodeUtils.findSymbolOfIdentifier(checker, node, nodeSymbolMap);
if (!sym || sym.valueDeclaration?.kind !== SyntaxKind.Parameter) {
return node;
}
let mangledName: string | undefined = mangledPropertyParameterSymbolNames.get(sym.valueDeclaration)?.mangledName;
if (!mangledName || mangledName === sym?.name) {
return node;
}
return factory.createIdentifier(mangledName);
}
function updateLabelNode(node: Identifier): Node {
let label: Label | undefined;
let labelName: string = '';
mangledLabelNames.forEach((value, key) => {
if (key.refs.includes(node)) {
label = key;
labelName = value;
}
});
return label ? factory.createIdentifier(labelName) : node;
}
* import {A as B} from 'modulename';
* import {C as D} from 'modulename';
* above A、C have no symbol, so deal with them specially.
*/
function mangleNoSymbolImportExportPropertyName(original: string): string {
const path: string = '#' + original;
const historyName: string = historyNameCache?.get(path);
let mangled = historyName ?? getPropertyMangledName(original, path);
if (nameCache && nameCache.get(IDENTIFIER_CACHE)) {
(nameCache.get(IDENTIFIER_CACHE) as Map<string, string>).set(path, mangled);
}
return mangled;
}
function trySearchImportExportSpecifier(node: Node): boolean {
while (node.parent) {
node = node.parent;
if ((isImportSpecifier(node) || isExportSpecifier(node)) && node.propertyName && isIdentifier(node.propertyName)) {
return true;
}
}
return false;
}
function shouldObfuscateNodeWithoutSymbol(node: Identifier): boolean {
if (exportObfuscation && exportElementsWithoutSymbol.has(node) && trySearchImportExportSpecifier(node)) {
let isGlobalNode: boolean = exportElementsWithoutSymbol.get(node);
if ((isGlobalNode && enableToplevel) || !isGlobalNode) {
return true;
}
}
return false;
}
}
function initWhitelist(): void {
if (isInitializedReservedList) {
return;
}
if (enablePropertyObf) {
const tmpReservedProps: string[] = profile?.mReservedProperties ?? [];
tmpReservedProps.forEach(item => {
PropCollections.reservedProperties.add(item);
});
addToSet(PropCollections.reservedProperties, AtKeepCollections.keepSymbol.propertyNames);
addToSet(PropCollections.reservedProperties, AtKeepCollections.keepAsConsumer.propertyNames);
addToSet(PropCollections.reservedProperties, AtIntentCollections.propertyNames);
if (profile?.mUniversalReservedProperties) {
PropCollections.universalReservedProperties = [...profile.mUniversalReservedProperties];
}
UnobfuscationCollections.reservedLangForTopLevel.forEach(element => {
UnobfuscationCollections.reservedLangForProperty.add(element);
});
UnobfuscationCollections.reservedExportName.forEach(element => {
UnobfuscationCollections.reservedExportNameAndProp.add(element);
});
UnobfuscationCollections.reservedSdkApiForGlobal.forEach(element => {
UnobfuscationCollections.reservedSdkApiForProp.add(element);
});
}
PropCollections.globalMangledNamesInCache = new Set(PropCollections.historyMangledTable?.values());
LocalVariableCollections.reservedConfig = new Set(profile?.mReservedNames ?? []);
profile?.mReservedToplevelNames?.forEach(item => PropCollections.reservedProperties.add(item));
addToSet(PropCollections.reservedProperties, AtKeepCollections.keepSymbol.globalNames);
addToSet(PropCollections.reservedProperties, AtKeepCollections.keepAsConsumer.globalNames);
addToSet(PropCollections.reservedProperties, AtIntentCollections.globalNames);
addToSet(UnobfuscationCollections.reservedSdkApiForProp, BytecodeObfuscationCollections.decoratorProp);
profile?.mUniversalReservedToplevelNames?.forEach(item => PropCollections.universalReservedProperties.push(item));
isInitializedReservedList = true;
}
};
function isSkippedGlobal(enableTopLevel: boolean, scope: Scope): boolean {
return !enableTopLevel && isGlobalScope(scope);
}
export let transformerPlugin: TransformPlugin = {
'name': 'renameIdentifierPlugin',
'order': TransformerOrder.RENAME_IDENTIFIER_TRANSFORMER,
'createTransformerFactory': createRenameIdentifierFactory
};
export let nameCache: Map<string, string | Map<string, string>> = new Map();
export let historyNameCache: Map<string, string> = undefined;
export let historyUnobfuscatedNamesMap: Map<string, string[]> = undefined;
export let identifierLineMap: Map<Identifier, string> = new Map();
export let classMangledName: Map<Node, string> = new Map();
export let classInfoInMemberMethodCache: Set<string> = new Set();
export let globalGenerator: INameGenerator;
export function ensureGlobalGenerator(nameGeneratorType: NameGeneratorType, options?: NameGeneratorOptions): void {
if (!globalGenerator) {
globalGenerator = getNameGenerator(nameGeneratorType, options ?? {});
}
}
export function clearCaches(): void {
nameCache.clear();
historyNameCache = undefined;
historyUnobfuscatedNamesMap = undefined;
identifierLineMap.clear();
classMangledName.clear();
classInfoInMemberMethodCache.clear();
UnobfuscationCollections.unobfuscatedNamesMap.clear();
}
}
export = secharmony;