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  isBindingElement,
19  isObjectBindingPattern,
20  isShorthandPropertyAssignment,
21  isSourceFile,
22  isStructDeclaration,
23  setParentRecursive,
24  visitEachChild,
25  isConstructorDeclaration
26} from 'typescript';
27
28import type {
29  BindingElement,
30  ClassElement,
31  Expression,
32  Identifier,
33  Node,
34  SourceFile,
35  StructDeclaration,
36  TransformationContext,
37  Transformer,
38  TransformerFactory
39} from 'typescript';
40
41import type {INameObfuscationOption} from '../../configs/INameObfuscationOption';
42import type {TransformPlugin} from '../TransformPlugin';
43import {TransformerOrder} from '../TransformPlugin';
44import type {IOptions} from '../../configs/IOptions';
45import {NodeUtils} from '../../utils/NodeUtils';
46import {ArkObfuscator, performancePrinter} from '../../ArkObfuscator';
47import {EventList} from '../../utils/PrinterUtils';
48
49namespace secharmony {
50  const createShorthandPropertyTransformerFactory = function (option: IOptions): TransformerFactory<Node> {
51    let profile: INameObfuscationOption = option.mNameObfuscation;
52    if (!profile || !profile.mEnable) {
53      return null;
54    }
55
56    return shorthandPropertyTransformFactory;
57
58    function shorthandPropertyTransformFactory(context: TransformationContext): Transformer<Node> {
59      return shorthandPropertyTransformer;
60
61      function shorthandPropertyTransformer(node: Node): Node {
62        if (isSourceFile(node) && ArkObfuscator.isKeptCurrentFile) {
63          return node;
64        }
65
66        performancePrinter?.singleFilePrinter?.startEvent(EventList.SHORT_HAND_OBFUSCATION, performancePrinter.timeSumPrinter);
67        let ret = transformShortHandProperty(node);
68        let parentNodes = setParentRecursive(ret, true);
69        performancePrinter?.singleFilePrinter?.endEvent(EventList.SHORT_HAND_OBFUSCATION, performancePrinter.timeSumPrinter);
70        return parentNodes;
71      }
72
73      function transformShortHandProperty(node: Node): Node {
74        /**
75         * example:
76         * `let name1 = 'hello';`
77         * `let info = {name1};`
78         * obfuscated example:
79         * `let name1 = 'hello';`;
80         * `let info = {name1: name1};`
81         */
82        if (isShorthandPropertyAssignment((node))) {
83          let initializer = node.objectAssignmentInitializer;
84          let expression: Expression = node.name;
85          if (initializer) {
86            expression = factory.createBinaryExpression(node.name, node.equalsToken, initializer);
87          }
88
89          let identifier = factory.createIdentifier(node.name.text);
90          return factory.createPropertyAssignment(identifier, expression);
91        }
92
93        /**
94         * exclude grammar instance: let [name2, age2] = ['akira', 22];
95         * 
96         * grammar: {name1, ...rest}= {'name1': 'akira', age : 22};
97         * an alias will be created for name1.
98         * no alias will be created for rest.
99         * 
100         * include grammars:
101         * orinal ObjectBinding():
102         * const { name3, age3 } = foo3();
103         * const { name4, addr4: { contry, place} } = foo4();
104         * obfuscated ObjectBinding:
105         * `const { name3: name3, age3: age3 } = foo3();`
106         * `const { name4: name4, addr4: { contry: contry, place: place}  } = { name4: 4, addr4: { contry:5, place:6} };`
107         */
108        if (isElementsInObjectBindingPattern(node) && !node.propertyName && !node.dotDotDotToken) {
109          return factory.createBindingElement(node.dotDotDotToken, factory.createIdentifier((node.name as Identifier).text),
110            node.name, node.initializer);
111        }
112
113        return visitEachChild(node, transformShortHandProperty, context);
114      }
115
116      function isElementsInObjectBindingPattern(node: Node): node is BindingElement {
117        return node.parent && isObjectBindingPattern(node.parent) && isBindingElement(node);
118      }
119    }
120  };
121
122  export let transformerPlugin: TransformPlugin = {
123    'name': 'ShortHandPropertyTransformer',
124    'order': TransformerOrder.SHORTHAND_PROPERTY_TRANSFORMER,
125    'createTransformerFactory': createShorthandPropertyTransformerFactory,
126  };
127}
128
129export = secharmony;
130