• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  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  isSourceFile
33} from 'typescript';
34
35import type {
36  ComputedPropertyName,
37  Expression,
38  Identifier,
39  Node,
40  TransformationContext,
41  Transformer,
42  TransformerFactory,
43  ClassDeclaration,
44  ClassExpression,
45  StructDeclaration,
46  PropertyName
47} from 'typescript';
48
49import type {IOptions} from '../../configs/IOptions';
50import type {INameObfuscationOption} from '../../configs/INameObfuscationOption';
51import type {INameGenerator, NameGeneratorOptions} from '../../generator/INameGenerator';
52import {getNameGenerator, NameGeneratorType} from '../../generator/NameFactory';
53import type {TransformPlugin} from '../TransformPlugin';
54import {TransformerOrder} from '../TransformPlugin';
55import {NodeUtils} from '../../utils/NodeUtils';
56import {collectPropertyNamesAndStrings, isViewPUBasedClass} from '../../utils/OhsUtil';
57import { ArkObfuscator, performancePrinter } from '../../ArkObfuscator';
58import { EventList } from '../../utils/PrinterUtils';
59import { needToBeReserved } from '../../utils/TransformUtil';
60import {
61  classInfoInMemberMethodCache,
62  nameCache
63} from './RenameIdentifierTransformer';
64import { UpdateMemberMethodName } from '../../utils/NameCacheUtil';
65import { PropCollections } from '../../utils/CommonCollections';
66
67namespace secharmony {
68  /**
69   * Rename Properties Transformer
70   *
71   * @param option obfuscation options
72   */
73  const createRenamePropertiesFactory = function (option: IOptions): TransformerFactory<Node> {
74    let profile: INameObfuscationOption | undefined = option?.mNameObfuscation;
75
76    if (!profile || !profile.mEnable || !profile.mRenameProperties) {
77      return null;
78    }
79
80    return renamePropertiesFactory;
81
82    function renamePropertiesFactory(context: TransformationContext): Transformer<Node> {
83      let options: NameGeneratorOptions = {};
84      let generator: INameGenerator = getNameGenerator(profile.mNameGeneratorType, options);
85      let currentConstructorParams: Set<string> = new Set<string>();
86
87      return renamePropertiesTransformer;
88
89      function renamePropertiesTransformer(node: Node): Node {
90        if (isSourceFile(node) && ArkObfuscator.isKeptCurrentFile) {
91          return node;
92        }
93
94        collectReservedNames(node);
95
96        performancePrinter?.singleFilePrinter?.startEvent(EventList.PROPERTY_OBFUSCATION, performancePrinter.timeSumPrinter);
97        let ret: Node = renameProperties(node);
98        UpdateMemberMethodName(nameCache, PropCollections.globalMangledTable, classInfoInMemberMethodCache);
99        let parentNodes = setParentRecursive(ret, true);
100        performancePrinter?.singleFilePrinter?.endEvent(EventList.PROPERTY_OBFUSCATION, performancePrinter.timeSumPrinter);
101        return parentNodes;
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        if (isStringLiteralLike(node) && profile?.mKeepStringProperty) {
163          return node;
164        }
165
166        let original: string = node.text;
167        if (needToBeReserved(PropCollections.reservedProperties, PropCollections.universalReservedProperties, original)) {
168          return node;
169        }
170
171        let mangledName: string = getPropertyName(original);
172
173        if (isStringLiteralLike(node)) {
174          return factory.createStringLiteral(mangledName);
175        }
176
177        /**
178         * source demo:
179         * class A {
180         *   123 = 1; // it is NumericLiteral
181         *   [456] = 2; // it is NumericLiteral within ComputedPropertyName
182         * }
183         * obfuscation result:
184         * class A {
185         *   a = 1;
186         *   ['b'] = 2;
187         * }
188         */
189        if (isNumericLiteral(node)) {
190          return computeName ? factory.createStringLiteral(mangledName) : factory.createIdentifier(mangledName);
191        }
192
193        if (isIdentifier(node) || isNumericLiteral(node)) {
194          return factory.createIdentifier(mangledName);
195        }
196
197        return factory.createPrivateIdentifier('#' + mangledName);
198      }
199
200      function getPropertyName(original: string): string {
201        const historyName: string = PropCollections.historyMangledTable?.get(original);
202        let mangledName: string = historyName ? historyName : PropCollections.globalMangledTable.get(original);
203
204        while (!mangledName) {
205          let tmpName = generator.getName();
206          if (needToBeReserved(PropCollections.reservedProperties, PropCollections.universalReservedProperties, tmpName) ||
207            tmpName === original) {
208            continue;
209          }
210
211          if (PropCollections.newlyOccupiedMangledProps.has(tmpName) || PropCollections.mangledPropsInNameCache.has(tmpName)) {
212            continue;
213          }
214
215          mangledName = tmpName;
216        }
217        PropCollections.globalMangledTable.set(original, mangledName);
218        PropCollections.newlyOccupiedMangledProps.add(mangledName);
219        return mangledName;
220      }
221
222      function visitEnumInitializer(childNode: Node): void {
223        if (!isIdentifier(childNode)) {
224          forEachChild(childNode, visitEnumInitializer);
225          return;
226        }
227
228        if (NodeUtils.isPropertyNode(childNode)) {
229          return;
230        }
231
232        if (isTypeNode(childNode)) {
233          return;
234        }
235
236        PropCollections.reservedProperties.add(childNode.text);
237      }
238
239      // enum syntax has special scenarios
240      function collectReservedNames(node: Node): void {
241        // collect ViewPU class properties
242        if (isClassDeclaration(node) && isViewPUBasedClass(node)) {
243          getViewPUClassProperties(node, PropCollections.reservedProperties);
244          return;
245        }
246
247        // collect reserved name of enum
248        // example: enum H {A, B = A + 1}, enum H = {A, B= 1 + (A + 1)}; A is reserved
249        if (isEnumMember(node) && node.initializer) {
250          // collect enum properties
251          node.initializer.forEachChild(visitEnumInitializer);
252          return;
253        }
254
255        forEachChild(node, collectReservedNames);
256      }
257
258      function getViewPUClassProperties(classNode: ClassDeclaration | ClassExpression | StructDeclaration, propertySet: Set<string>): void {
259        if (!classNode || !classNode.members) {
260          return;
261        }
262
263        classNode.members.forEach((member) => {
264          const memberName: PropertyName = member.name;
265          if (!member || !memberName) {
266            return;
267          }
268          collectPropertyNamesAndStrings(memberName, propertySet);
269        });
270      }
271    }
272  };
273
274  export let transformerPlugin: TransformPlugin = {
275    'name': 'renamePropertiesPlugin',
276    'order': TransformerOrder.RENAME_PROPERTIES_TRANSFORMER,
277    'createTransformerFactory': createRenamePropertiesFactory
278  };
279}
280
281export = secharmony;
282