1/* 2 * Copyright (c) 2023 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 forEachChild, 19 isComputedPropertyName, 20 isConstructorDeclaration, 21 isElementAccessExpression, 22 isEnumMember, 23 isIdentifier, 24 isClassDeclaration, 25 isNumericLiteral, 26 isPrivateIdentifier, 27 isStringLiteralLike, 28 isTypeNode, 29 setParentRecursive, 30 visitEachChild, 31 isStringLiteral 32} from 'typescript'; 33 34import type { 35 ComputedPropertyName, 36 Expression, 37 Identifier, 38 Node, 39 TransformationContext, 40 Transformer, 41 TransformerFactory, 42 ClassDeclaration, 43 ClassExpression, 44 StructDeclaration, 45 PropertyName 46} from 'typescript'; 47 48import type {IOptions} from '../../configs/IOptions'; 49import type {INameObfuscationOption} from '../../configs/INameObfuscationOption'; 50import type {INameGenerator, NameGeneratorOptions} from '../../generator/INameGenerator'; 51import {getNameGenerator, NameGeneratorType} from '../../generator/NameFactory'; 52import type {TransformPlugin} from '../TransformPlugin'; 53import {NodeUtils} from '../../utils/NodeUtils'; 54import {collectPropertyNamesAndStrings, isViewPUBasedClass} from '../../utils/OhsUtil'; 55 56namespace secharmony { 57 /** 58 * global mangled properties table used by all files in a project 59 */ 60 export let globalMangledTable: Map<string, string> = undefined; 61 62 // used for property cache 63 export let historyMangledTable: Map<string, string> = undefined; 64 65 /** 66 * Rename Properties Transformer 67 * 68 * @param option obfuscation options 69 */ 70 const createRenamePropertiesFactory = function (option: IOptions): TransformerFactory<Node> { 71 let profile: INameObfuscationOption | undefined = option?.mNameObfuscation; 72 73 if (!profile || !profile.mEnable || !profile.mRenameProperties) { 74 return null; 75 } 76 77 return renamePropertiesFactory; 78 79 function renamePropertiesFactory(context: TransformationContext): Transformer<Node> { 80 let options: NameGeneratorOptions = {}; 81 if (profile.mNameGeneratorType === NameGeneratorType.HEX) { 82 options.hexWithPrefixSuffix = true; 83 } 84 85 let generator: INameGenerator = getNameGenerator(profile.mNameGeneratorType, options); 86 87 let tmpReservedProps: string[] = profile?.mReservedProperties ?? []; 88 let reservedProperties: Set<string> = new Set<string>(tmpReservedProps); 89 90 let currentConstructorParams: Set<string> = new Set<string>(); 91 92 return renamePropertiesTransformer; 93 94 function renamePropertiesTransformer(node: Node): Node { 95 collectReservedNames(node); 96 if (globalMangledTable === undefined) { 97 globalMangledTable = new Map<string, string>(); 98 } 99 100 let ret: Node = renameProperties(node); 101 return setParentRecursive(ret, true); 102 } 103 104 function renameProperties(node: Node): Node { 105 if (isConstructorDeclaration(node)) { 106 currentConstructorParams.clear(); 107 } 108 109 if (NodeUtils.isClassPropertyInConstructorParams(node)) { 110 currentConstructorParams.add((node as Identifier).escapedText.toString()); 111 return renameProperty(node, false); 112 } 113 114 if (NodeUtils.isClassPropertyInConstructorBody(node, currentConstructorParams)) { 115 if (currentConstructorParams.has((node as Identifier).escapedText.toString())) { 116 return renameProperty(node, false); 117 } 118 } 119 120 if (!NodeUtils.isPropertyNode(node)) { 121 return visitEachChild(node, renameProperties, context); 122 } 123 124 if (isElementAccessExpression(node.parent)) { 125 return renameElementAccessProperty(node); 126 } 127 128 if (isComputedPropertyName(node)) { 129 return renameComputedProperty(node); 130 } 131 132 return renameProperty(node, false); 133 } 134 135 function renameElementAccessProperty(node: Node): Node { 136 if (isStringLiteralLike(node)) { 137 return renameProperty(node, false); 138 } 139 return visitEachChild(node, renameProperties, context); 140 } 141 142 function renameComputedProperty(node: ComputedPropertyName): ComputedPropertyName { 143 if (isStringLiteralLike(node.expression) || isNumericLiteral(node.expression)) { 144 let prop: Node = renameProperty(node.expression, true); 145 if (prop !== node.expression) { 146 return factory.createComputedPropertyName(prop as Expression); 147 } 148 } 149 150 if (isIdentifier(node.expression)) { 151 return node; 152 } 153 154 return visitEachChild(node, renameProperties, context); 155 } 156 157 function renameProperty(node: Node, computeName: boolean): Node { 158 if (!isStringLiteralLike(node) && !isIdentifier(node) && !isPrivateIdentifier(node) && !isNumericLiteral(node)) { 159 return visitEachChild(node, renameProperties, context); 160 } 161 162 let original: string = node.text; 163 if (reservedProperties.has(original)) { 164 return node; 165 } 166 167 let mangledName: string = getPropertyName(original); 168 169 if (isStringLiteralLike(node)) { 170 return factory.createStringLiteral(mangledName); 171 } 172 173 if (isNumericLiteral(node)) { 174 return computeName ? factory.createStringLiteral(mangledName) : factory.createIdentifier(mangledName); 175 176 } 177 178 if (isIdentifier(node) || isNumericLiteral(node)) { 179 return factory.createIdentifier(mangledName); 180 } 181 182 return factory.createPrivateIdentifier('#' + mangledName); 183 } 184 185 function getPropertyName(original: string): string { 186 if (reservedProperties.has(original)) { 187 return original; 188 } 189 190 const historyName: string = historyMangledTable?.get(original); 191 let mangledName: string = historyName ? historyName : globalMangledTable.get(original); 192 193 while (!mangledName) { 194 mangledName = generator.getName(); 195 if (mangledName === original || reservedProperties.has(mangledName)) { 196 mangledName = null; 197 continue; 198 } 199 200 let reserved: string[] = [...globalMangledTable.values()]; 201 if (historyMangledTable) { 202 reserved = [...reserved, ...historyMangledTable.values()]; 203 } 204 205 if (reserved.includes(mangledName)) { 206 mangledName = null; 207 } 208 } 209 globalMangledTable.set(original, mangledName); 210 return mangledName; 211 } 212 213 function visitEnumInitializer(childNode: Node): void { 214 if (!isIdentifier(childNode)) { 215 forEachChild(childNode, visitEnumInitializer); 216 return; 217 } 218 219 if (NodeUtils.isPropertyNode(childNode)) { 220 return; 221 } 222 223 if (isTypeNode(childNode)) { 224 return; 225 } 226 227 reservedProperties.add(childNode.text); 228 } 229 230 // enum syntax has special scenarios 231 function collectReservedNames(node: Node): void { 232 // collect ViewPU class properties 233 if (isClassDeclaration(node) && isViewPUBasedClass(node)) { 234 getViewPUClassProperties(node, reservedProperties); 235 return; 236 } 237 238 // collect reserved name of enum 239 // example: enum H {A, B = A + 1}, enum H = {A, B= 1 + (A + 1)}; A is reserved 240 if (isEnumMember(node) && node.initializer) { 241 // collect enum properties 242 node.initializer.forEachChild(visitEnumInitializer); 243 return; 244 } 245 246 forEachChild(node, collectReservedNames); 247 } 248 249 function getViewPUClassProperties(classNode: ClassDeclaration | ClassExpression | StructDeclaration, propertySet: Set<string>): void { 250 if (!classNode || !classNode.members) { 251 return; 252 } 253 254 classNode.members.forEach((member) => { 255 const memberName: PropertyName = member.name; 256 if (!member || !memberName) { 257 return; 258 } 259 collectPropertyNamesAndStrings(memberName, propertySet); 260 }); 261 } 262 } 263 }; 264 265 const TRANSFORMER_ORDER: number = 6; 266 export let transformerPlugin: TransformPlugin = { 267 'name': 'renamePropertiesPlugin', 268 'order': (1 << TRANSFORMER_ORDER), 269 'createTransformerFactory': createRenamePropertiesFactory 270 }; 271} 272 273export = secharmony; 274