IfStatementSimplifyTransformer.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. import { inject, injectable, } from 'inversify';
  2. import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
  3. import * as ESTree from 'estree';
  4. import { IOptions } from '../../interfaces/options/IOptions';
  5. import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
  6. import { IStatementSimplifyData } from '../../interfaces/node-transformers/simplifying-transformers/IStatementSimplifyData';
  7. import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
  8. import { NodeTransformationStage } from '../../enums/node-transformers/NodeTransformationStage';
  9. import { AbstractStatementSimplifyTransformer } from './AbstractStatementSimplifyTransformer';
  10. import { NodeGuards } from '../../node/NodeGuards';
  11. import { NodeFactory } from '../../node/NodeFactory';
  12. import { NodeUtils } from '../../node/NodeUtils';
  13. /**
  14. * Simplifies `IfStatement` node
  15. */
  16. @injectable()
  17. export class IfStatementSimplifyTransformer extends AbstractStatementSimplifyTransformer {
  18. /**
  19. * @param {IRandomGenerator} randomGenerator
  20. * @param {IOptions} options
  21. */
  22. public constructor (
  23. @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
  24. @inject(ServiceIdentifiers.IOptions) options: IOptions
  25. ) {
  26. super(randomGenerator, options);
  27. }
  28. /**
  29. * @param {NodeTransformationStage} nodeTransformationStage
  30. * @returns {IVisitor | null}
  31. */
  32. public getVisitor (nodeTransformationStage: NodeTransformationStage): IVisitor | null {
  33. switch (nodeTransformationStage) {
  34. case NodeTransformationStage.Simplifying:
  35. return {
  36. leave: (
  37. node: ESTree.Node,
  38. parentNode: ESTree.Node | null
  39. ): ESTree.Node | undefined => {
  40. if (parentNode && NodeGuards.isIfStatementNode(node)) {
  41. return this.transformNode(node, parentNode);
  42. }
  43. }
  44. };
  45. default:
  46. return null;
  47. }
  48. }
  49. /**
  50. * @param {ESTree.IfStatement} ifStatementNode
  51. * @param {ESTree.Node} parentNode
  52. * @returns {ESTree.IfStatement}
  53. */
  54. public transformNode (
  55. ifStatementNode: ESTree.IfStatement,
  56. parentNode: ESTree.Node
  57. ): ESTree.Node {
  58. const consequentSimplifyData: IStatementSimplifyData | null = this.getStatementSimplifyData(ifStatementNode.consequent);
  59. // Variant #1: no valid consequent expression data
  60. if (!consequentSimplifyData) {
  61. return ifStatementNode;
  62. }
  63. let transformedNode: ESTree.Node;
  64. if (!ifStatementNode.alternate) {
  65. // Variant #2: valid data for consequent expression only
  66. transformedNode = this.getConsequentNode(ifStatementNode, consequentSimplifyData);
  67. } else {
  68. const alternateSimplifyData: IStatementSimplifyData | null = this.getStatementSimplifyData(ifStatementNode.alternate);
  69. if (!alternateSimplifyData) {
  70. return ifStatementNode;
  71. }
  72. // Variant #3: valid data for consequent and alternate expressions
  73. transformedNode = this.getConsequentAndAlternateNode(ifStatementNode, consequentSimplifyData, alternateSimplifyData);
  74. }
  75. return NodeUtils.parentizeNode(transformedNode, parentNode);
  76. }
  77. /**
  78. * @param {ESTree.IfStatement} ifStatementNode
  79. * @param {IStatementSimplifyData} consequentSimplifyData
  80. * @returns {ESTree.Node}
  81. */
  82. protected getConsequentNode (
  83. ifStatementNode: ESTree.IfStatement,
  84. consequentSimplifyData: IStatementSimplifyData
  85. ): ESTree.Node {
  86. /**
  87. * Converts:
  88. * if (true) {
  89. * const foo = 1;
  90. * console.log(1);
  91. * return 1;
  92. * }
  93. *
  94. * to:
  95. * if (true) {
  96. * const foo = 1;
  97. * return console.log(1), 1;
  98. * }
  99. */
  100. if (
  101. consequentSimplifyData.leadingStatements.length
  102. || !consequentSimplifyData.trailingStatement
  103. ) {
  104. return NodeFactory.ifStatementNode(
  105. ifStatementNode.test,
  106. this.getPartialStatement(consequentSimplifyData)
  107. );
  108. }
  109. /**
  110. * Converts:
  111. * if (true) {
  112. * return 1;
  113. * }
  114. *
  115. * to:
  116. * if (true)
  117. * return 1;
  118. */
  119. if (consequentSimplifyData.hasReturnStatement) {
  120. return NodeFactory.ifStatementNode(
  121. ifStatementNode.test,
  122. consequentSimplifyData.trailingStatement.statement
  123. );
  124. }
  125. /**
  126. * Converts:
  127. * if (true) {
  128. * console.log(1);
  129. * }
  130. *
  131. * to:
  132. * true && console.log(1);
  133. */
  134. return NodeFactory.expressionStatementNode(
  135. NodeFactory.logicalExpressionNode(
  136. '&&',
  137. ifStatementNode.test,
  138. consequentSimplifyData.trailingStatement.expression
  139. )
  140. );
  141. }
  142. /**
  143. * @param {ESTree.IfStatement} ifStatementNode
  144. * @param {IStatementSimplifyData} consequentSimplifyData
  145. * @param {IStatementSimplifyData} alternateSimplifyData
  146. * @returns {ESTree.Node}
  147. */
  148. protected getConsequentAndAlternateNode (
  149. ifStatementNode: ESTree.IfStatement,
  150. consequentSimplifyData: IStatementSimplifyData,
  151. alternateSimplifyData: IStatementSimplifyData
  152. ): ESTree.Node {
  153. /**
  154. * Converts:
  155. * if (true) {
  156. * const foo = 1;
  157. * console.log(1);
  158. * return 1;
  159. * }
  160. *
  161. * to:
  162. * if (true) {
  163. * const foo = 1;
  164. * return console.log(1), 1;
  165. * }
  166. */
  167. if (
  168. consequentSimplifyData.leadingStatements.length
  169. || alternateSimplifyData.leadingStatements.length
  170. || !consequentSimplifyData.trailingStatement
  171. || !alternateSimplifyData.trailingStatement
  172. ) {
  173. return NodeFactory.ifStatementNode(
  174. ifStatementNode.test,
  175. this.getPartialStatement(consequentSimplifyData),
  176. this.getPartialStatement(alternateSimplifyData)
  177. );
  178. }
  179. /**
  180. * Converts:
  181. * if (true) {
  182. * return 1;
  183. * } else {
  184. * return 2;
  185. * }
  186. *
  187. * to:
  188. * return true ? 1 : 2;
  189. */
  190. if (consequentSimplifyData.hasReturnStatement && alternateSimplifyData.hasReturnStatement) {
  191. return NodeFactory.returnStatementNode(
  192. NodeFactory.conditionalExpressionNode(
  193. ifStatementNode.test,
  194. consequentSimplifyData.trailingStatement.expression,
  195. alternateSimplifyData.trailingStatement.expression
  196. )
  197. );
  198. }
  199. /**
  200. * Converts:
  201. * if (true) {
  202. * return 1;
  203. * } else {
  204. * console.log(2);
  205. * }
  206. *
  207. * to:
  208. * if (true)
  209. * return 1;
  210. * else
  211. * console.log(2);
  212. */
  213. if (consequentSimplifyData.hasReturnStatement || alternateSimplifyData.hasReturnStatement) {
  214. return NodeFactory.ifStatementNode(
  215. ifStatementNode.test,
  216. consequentSimplifyData.trailingStatement.statement,
  217. alternateSimplifyData.trailingStatement.statement
  218. );
  219. }
  220. /**
  221. * Converts:
  222. * if (true) {
  223. * console.log(1);
  224. * } else {
  225. * console.log(2);
  226. * }
  227. *
  228. * to:
  229. * true ? console.log(1) : console.log(2);
  230. */
  231. return NodeFactory.expressionStatementNode(
  232. NodeFactory.conditionalExpressionNode(
  233. ifStatementNode.test,
  234. consequentSimplifyData.trailingStatement.expression,
  235. alternateSimplifyData.trailingStatement.expression
  236. )
  237. );
  238. }
  239. /**
  240. * @param {IStatementSimplifyData} statementSimplifyData
  241. * @returns {ESTree.Statement}
  242. */
  243. protected getPartialStatement (statementSimplifyData: IStatementSimplifyData): ESTree.Statement {
  244. const partialStatement: ESTree.Statement = super.getPartialStatement(statementSimplifyData);
  245. if (!NodeGuards.isBlockStatementNode(partialStatement)) {
  246. return partialStatement;
  247. }
  248. return partialStatement.body.length === 1
  249. && !this.isProhibitedSingleStatementForIfStatementBranch(partialStatement.body[0])
  250. ? partialStatement.body[0]
  251. : partialStatement;
  252. }
  253. /**
  254. * @param {ESTree.Statement} statement
  255. * @returns {boolean}
  256. */
  257. protected isProhibitedSingleStatementForIfStatementBranch (statement: ESTree.Statement): boolean {
  258. /**
  259. * Function declaration is not allowed outside of block in `strict` mode
  260. */
  261. return NodeGuards.isFunctionDeclarationNode(statement)
  262. /**
  263. * Have to ignore all `IfStatement` nodes
  264. * Also have to ignore any nodes with a single statement as a `body`
  265. * Without ignore it can break following code:
  266. * Input:
  267. * if (condition1) {
  268. * if (condition2) {
  269. * var foo = bar();
  270. * }
  271. * } else {
  272. * var baz = bark();
  273. * }
  274. *
  275. * Invalid output:
  276. * if (condition1)
  277. * if (condition2)
  278. * var foo = bar();
  279. * else
  280. * var baz = bark();
  281. *
  282. * See issue: https://github.com/javascript-obfuscator/javascript-obfuscator/issues/860
  283. */
  284. || NodeGuards.isIfStatementNode(statement)
  285. || NodeGuards.isNodeWithSingleStatementBody(statement)
  286. /**
  287. * `let` and `const` variable declarations are not allowed outside of `IfStatement` block statement
  288. * Input:
  289. * if (condition1) {
  290. * const foo = 1;
  291. * }
  292. *
  293. * Invalid output with runtime error:
  294. * if (condition1)
  295. * const foo = 1;
  296. */
  297. || (NodeGuards.isVariableDeclarationNode(statement) && statement.kind !== 'var');
  298. }
  299. }