• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  isBlock,
19  isExpressionStatement,
20  isReturnStatement,
21  isSourceFile,
22  isVariableStatement,
23  setParentRecursive,
24  SyntaxKind,
25  visitEachChild,
26  isStringLiteral,
27} from 'typescript';
28
29import type {
30  ExpressionStatement,
31  Node,
32  NodeFlags,
33  Statement,
34  TransformationContext,
35  Transformer,
36  TransformerFactory,
37  VariableDeclaration,
38  VariableStatement,
39  ModifiersArray,
40  SourceFile,
41  Block,
42  Expression,
43} from 'typescript';
44
45import type { IOptions } from '../../configs/IOptions';
46import type { TransformPlugin } from '../TransformPlugin';
47import { TransformerOrder } from '../TransformPlugin';
48import { isCommentedNode, isSuperCallStatement } from '../../utils/TransformUtil';
49import { NodeUtils } from '../../utils/NodeUtils';
50
51namespace secharmony {
52  export let transformerPlugin: TransformPlugin = {
53    'name': 'simplifyPlugin',
54    'order': (1 << TransformerOrder.SIMPLIFY_TRANSFORMER),
55    'createTransformerFactory': createSimplifyFactory
56  };
57
58  export function createSimplifyFactory(option: IOptions): TransformerFactory<Node> {
59    if (!option.mSimplify) {
60      return null;
61    }
62
63    return simplifyFactory;
64
65    function simplifyFactory(context: TransformationContext): Transformer<Node> {
66      const MIN_STATEMENTS_LEN = 2;
67      let sourceFile: SourceFile;
68      return transformer;
69
70      function transformer(node: Node): Node {
71        if (!isSourceFile(node) || NodeUtils.isDeclarationFile(node)) {
72          return node;
73        }
74
75        sourceFile = node;
76        return setParentRecursive(visitStatements(node), true);
77      }
78
79      function visitStatements(node: Node): Node {
80        if (!isSourceFile(node) && !isBlock(node)) {
81          return visitEachChild(node, visitStatements, context);
82        }
83
84        const simplified: SourceFile | Block = visitEachChild(node, visitStatements, context);
85        if (node.statements.length < MIN_STATEMENTS_LEN) {
86          return node;
87        }
88
89        return simplifyStatements(simplified);
90      }
91
92      /**
93       * use variable statement merge and expression merge to simplify code
94       * @param node
95       */
96      function simplifyStatements(node: Node): Node {
97        if (!isSourceFile(node) && !isBlock(node)) {
98          return node;
99        }
100
101        let simplifiedStatements: Statement[] = [];
102        const continuousStatements: Statement[] = [];
103        let nodeFlag: NodeFlags = undefined;
104        let modifiers: ModifiersArray = undefined;
105        let preType: SyntaxKind | undefined = undefined;
106
107        function mergeArray(): void {
108          if (continuousStatements.length < MIN_STATEMENTS_LEN) {
109            simplifiedStatements = [...simplifiedStatements, ...continuousStatements];
110            return;
111          }
112
113          if (preType === SyntaxKind.VariableStatement) {
114            simplifiedStatements.push(mergeVariableStatements(continuousStatements as VariableStatement[]));
115            return;
116          }
117
118          if (preType === SyntaxKind.ExpressionStatement) {
119            simplifiedStatements.push(mergeExpression(continuousStatements as ExpressionStatement[]));
120          }
121        }
122
123        function doMerge(currentType: SyntaxKind | undefined, childArg: Statement | undefined): void {
124          if (preType === currentType) {
125            if (preType !== SyntaxKind.VariableStatement) {
126              return;
127            }
128
129            let child = childArg as VariableStatement;
130            if (nodeFlag === child.declarationList.flags) {
131              if (!modifiers && !child.modifiers) {
132                return;
133              }
134
135              let isSame: boolean = true;
136              if (modifiers && child.modifiers && modifiers.length === child.modifiers.length) {
137                modifiers.forEach((modifier, index) => {
138                  if (modifier.kind !== child.modifiers[index].kind) {
139                    isSame = false;
140                  }
141                });
142              } else {
143                isSame = false;
144              }
145
146              if (isSame) {
147                return;
148              }
149            }
150
151            mergeArray();
152            nodeFlag = (child as VariableStatement).declarationList.flags;
153            modifiers = child.modifiers;
154            continuousStatements.length = 0;
155            return;
156          }
157
158          if (preType === SyntaxKind.VariableStatement) {
159            nodeFlag = undefined;
160            modifiers = undefined;
161            mergeArray();
162          } else if (preType === SyntaxKind.ExpressionStatement) {
163            mergeArray();
164          }
165
166          continuousStatements.length = 0;
167          preType = currentType;
168        }
169
170        node.statements.forEach((child) => {
171          if (isCommentedNode(child, sourceFile) ||
172            (isExpressionStatement(child) && isStringLiteral(child.expression)) ||
173            isSuperCallStatement(child)
174          ) {
175            doMerge(undefined, undefined);
176            simplifiedStatements.push(child);
177            return;
178          }
179
180          if (isVariableStatement(child)) {
181            doMerge(SyntaxKind.VariableStatement, child);
182            continuousStatements.push(child);
183            nodeFlag = child.declarationList.flags;
184            modifiers = child.modifiers;
185            return;
186          }
187
188          if (isExpressionStatement(child)) {
189            doMerge(SyntaxKind.ExpressionStatement, child);
190            continuousStatements.push(child);
191            return;
192          }
193
194          if (isReturnStatement(child) && child.expression !== undefined) {
195            doMerge(SyntaxKind.ExpressionStatement, child);
196            continuousStatements.push(child);
197            doMerge(undefined, undefined);
198            return;
199          }
200
201          // do merge on continuous stopped
202          doMerge(undefined, child);
203          simplifiedStatements.push(child);
204        });
205
206        doMerge(undefined, undefined);
207
208        if (isSourceFile(node)) {
209          return factory.updateSourceFile(node, simplifiedStatements);
210        }
211
212        return factory.createBlock(simplifiedStatements, true);
213      }
214
215      /**
216       * merge variable statement, need same type and same modifier variable and continuous
217       * @param variableStatements
218       */
219      function mergeVariableStatements(variableStatements: VariableStatement[]): VariableStatement {
220        let variableDeclarations: VariableDeclaration[] = [];
221        variableStatements.forEach((statement) => {
222          variableDeclarations = [...variableDeclarations, ...statement.declarationList.declarations];
223        });
224
225        return factory.createVariableStatement(
226          variableStatements[0].modifiers,
227          factory.createVariableDeclarationList(
228            variableDeclarations,
229            variableStatements[0].declarationList.flags
230          )
231        );
232      }
233
234      /**
235       * merge expression, include:
236       *   - continuous expression like a=a+1; b=b+1;
237       */
238      function mergeExpression(expressionStatements: ExpressionStatement[]): Statement {
239        let pos: number = 1;
240        let expression: Expression = expressionStatements[0].expression;
241        const statementsLength: number = expressionStatements.length;
242        while (pos < statementsLength) {
243          expression = factory.createBinaryExpression(
244            expression,
245            SyntaxKind.CommaToken,
246            expressionStatements[pos].expression
247          );
248          pos += 1;
249        }
250
251        if (isReturnStatement(expressionStatements[statementsLength - 1])) {
252          return factory.createReturnStatement(expression);
253        }
254
255        return factory.createExpressionStatement(expression);
256      }
257    }
258  }
259}
260
261export = secharmony;
262