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