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 factory, 18 isComputedPropertyName, 19 isConstructorDeclaration, 20 isElementAccessExpression, 21 isIdentifier, 22 isNumericLiteral, 23 isPrivateIdentifier, 24 isStringLiteralLike, 25 setParentRecursive, 26 visitEachChild, 27 isSourceFile 28} from 'typescript'; 29 30import type { 31 ComputedPropertyName, 32 Expression, 33 Identifier, 34 Node, 35 TransformationContext, 36 Transformer, 37 TransformerFactory, 38} from 'typescript'; 39 40import type {IOptions} from '../../configs/IOptions'; 41import type { INameObfuscationOption } from '../../configs/INameObfuscationOption'; 42import type {INameGenerator, NameGeneratorOptions} from '../../generator/INameGenerator'; 43import {getNameGenerator, NameGeneratorType} from '../../generator/NameFactory'; 44import type {TransformPlugin} from '../TransformPlugin'; 45import {TransformerOrder} from '../TransformPlugin'; 46import {NodeUtils} from '../../utils/NodeUtils'; 47import { ArkObfuscator, performancePrinter } from '../../ArkObfuscator'; 48import { EventList } from '../../utils/PrinterUtils'; 49import { 50 isInPropertyWhitelist, 51 isReservedProperty, 52 needToRecordProperty 53} from '../../utils/TransformUtil'; 54import { 55 classInfoInMemberMethodCache, 56 nameCache 57} from './RenameIdentifierTransformer'; 58import { UpdateMemberMethodName } from '../../utils/NameCacheUtil'; 59import { PropCollections, UnobfuscationCollections } from '../../utils/CommonCollections'; 60 61namespace secharmony { 62 /** 63 * Rename Properties Transformer 64 * 65 * @param option obfuscation options 66 */ 67 const createRenamePropertiesFactory = function (option: IOptions): TransformerFactory<Node> { 68 let profile: INameObfuscationOption | undefined = option?.mNameObfuscation; 69 70 if (!profile || !profile.mEnable || !profile.mRenameProperties) { 71 return null; 72 } 73 74 return renamePropertiesFactory; 75 76 function renamePropertiesFactory(context: TransformationContext): Transformer<Node> { 77 let options: NameGeneratorOptions = {}; 78 let generator: INameGenerator = getNameGenerator(profile.mNameGeneratorType, options); 79 let currentConstructorParams: Set<string> = new Set<string>(); 80 81 return renamePropertiesTransformer; 82 83 function renamePropertiesTransformer(node: Node): Node { 84 if (isSourceFile(node) && ArkObfuscator.isKeptCurrentFile) { 85 return node; 86 } 87 88 performancePrinter?.singleFilePrinter?.startEvent(EventList.PROPERTY_OBFUSCATION, performancePrinter.timeSumPrinter); 89 let ret: Node = renameProperties(node); 90 UpdateMemberMethodName(nameCache, PropCollections.globalMangledTable, classInfoInMemberMethodCache); 91 let parentNodes = setParentRecursive(ret, true); 92 performancePrinter?.singleFilePrinter?.endEvent(EventList.PROPERTY_OBFUSCATION, performancePrinter.timeSumPrinter); 93 return parentNodes; 94 } 95 96 function renameProperties(node: Node): Node { 97 if (isConstructorDeclaration(node)) { 98 currentConstructorParams.clear(); 99 } 100 101 if (NodeUtils.isClassPropertyInConstructorParams(node)) { 102 currentConstructorParams.add((node as Identifier).escapedText.toString()); 103 return renameProperty(node, false); 104 } 105 106 if (NodeUtils.isClassPropertyInConstructorBody(node, currentConstructorParams)) { 107 if (currentConstructorParams.has((node as Identifier).escapedText.toString())) { 108 return renameProperty(node, false); 109 } 110 } 111 112 if (!NodeUtils.isPropertyNode(node)) { 113 return visitEachChild(node, renameProperties, context); 114 } 115 116 if (isElementAccessExpression(node.parent)) { 117 return renameElementAccessProperty(node); 118 } 119 120 if (isComputedPropertyName(node)) { 121 return renameComputedProperty(node); 122 } 123 124 return renameProperty(node, false); 125 } 126 127 function renameElementAccessProperty(node: Node): Node { 128 if (isStringLiteralLike(node)) { 129 return renameProperty(node, false); 130 } 131 return visitEachChild(node, renameProperties, context); 132 } 133 134 function renameComputedProperty(node: ComputedPropertyName): ComputedPropertyName { 135 if (isStringLiteralLike(node.expression) || isNumericLiteral(node.expression)) { 136 let prop: Node = renameProperty(node.expression, true); 137 if (prop !== node.expression) { 138 return factory.createComputedPropertyName(prop as Expression); 139 } 140 } 141 142 if (isIdentifier(node.expression)) { 143 return node; 144 } 145 146 return visitEachChild(node, renameProperties, context); 147 } 148 149 function renameProperty(node: Node, computeName: boolean): Node { 150 if (!isStringLiteralLike(node) && !isIdentifier(node) && !isPrivateIdentifier(node) && !isNumericLiteral(node)) { 151 return visitEachChild(node, renameProperties, context); 152 } 153 154 if (isStringLiteralLike(node) && profile?.mKeepStringProperty) { 155 if (UnobfuscationCollections.printKeptName) { 156 needToRecordProperty(node.text, UnobfuscationCollections.unobfuscatedPropMap); 157 } 158 return node; 159 } 160 161 let original: string = node.text; 162 if (isInPropertyWhitelist(original, UnobfuscationCollections.unobfuscatedPropMap)) { 163 return node; 164 } 165 166 let mangledName: string = getPropertyName(original); 167 168 if (isStringLiteralLike(node)) { 169 return factory.createStringLiteral(mangledName); 170 } 171 172 /** 173 * source demo: 174 * class A { 175 * 123 = 1; // it is NumericLiteral 176 * [456] = 2; // it is NumericLiteral within ComputedPropertyName 177 * } 178 * obfuscation result: 179 * class A { 180 * a = 1; 181 * ['b'] = 2; 182 * } 183 */ 184 if (isNumericLiteral(node)) { 185 return computeName ? factory.createStringLiteral(mangledName) : factory.createIdentifier(mangledName); 186 } 187 188 if (isIdentifier(node) || isNumericLiteral(node)) { 189 return factory.createIdentifier(mangledName); 190 } 191 192 return factory.createPrivateIdentifier('#' + mangledName); 193 } 194 195 function getPropertyName(original: string): string { 196 const historyName: string = PropCollections.historyMangledTable?.get(original); 197 let mangledName: string = historyName ? historyName : PropCollections.globalMangledTable.get(original); 198 while (!mangledName) { 199 let tmpName = generator.getName(); 200 if (isReservedProperty(tmpName) || 201 tmpName === original) { 202 continue; 203 } 204 205 if (PropCollections.newlyOccupiedMangledProps.has(tmpName) || PropCollections.mangledPropsInNameCache.has(tmpName)) { 206 continue; 207 } 208 209 mangledName = tmpName; 210 } 211 PropCollections.globalMangledTable.set(original, mangledName); 212 PropCollections.newlyOccupiedMangledProps.add(mangledName); 213 return mangledName; 214 } 215 } 216 }; 217 218 export let transformerPlugin: TransformPlugin = { 219 'name': 'renamePropertiesPlugin', 220 'order': TransformerOrder.RENAME_PROPERTIES_TRANSFORMER, 221 'createTransformerFactory': createRenamePropertiesFactory 222 }; 223} 224 225export = secharmony; 226