DeadCodeInjectionTransformer.ts 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import { injectable, inject } from 'inversify';
  2. import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
  3. import * as estraverse from 'estraverse';
  4. import * as ESTree from 'estree';
  5. import { IOptions } from '../../interfaces/options/IOptions';
  6. import { IVisitor } from '../../interfaces/IVisitor';
  7. import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
  8. import { Node } from '../../node/Node';
  9. import { Nodes } from '../../node/Nodes';
  10. import { NodeUtils } from '../../node/NodeUtils';
  11. import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
  12. @injectable()
  13. export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
  14. /**
  15. * @type {ESTree.BlockStatement[]}
  16. */
  17. private collectedBlockStatements: ESTree.BlockStatement[] = [];
  18. /**
  19. * @param options
  20. */
  21. constructor (
  22. @inject(ServiceIdentifiers.IOptions) options: IOptions
  23. ) {
  24. super(options);
  25. }
  26. /**
  27. * @param targetNode
  28. * @param collectedBlockStatements
  29. */
  30. private static collectBlockStatementNodes (targetNode: ESTree.Node, collectedBlockStatements: ESTree.BlockStatement[]): void {
  31. if (!Node.isBlockStatementNode(targetNode) || !DeadCodeInjectionTransformer.isValidBlockStatementNode(targetNode)) {
  32. return;
  33. }
  34. const clonedBlockStatementNode: ESTree.BlockStatement = <ESTree.BlockStatement>NodeUtils.clone(targetNode);
  35. estraverse.replace(clonedBlockStatementNode, {
  36. enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
  37. if (Node.isIdentifierNode(node)) {
  38. node.name = RandomGeneratorUtils.getRandomVariableName(6);
  39. }
  40. return node;
  41. }
  42. });
  43. collectedBlockStatements.push(clonedBlockStatementNode);
  44. }
  45. /**
  46. * @param blockStatementNode
  47. * @param randomBlockStatementNode
  48. * @return {ESTree.BlockStatement}
  49. */
  50. private static replaceBlockStatementNodes (
  51. blockStatementNode: ESTree.BlockStatement,
  52. randomBlockStatementNode: ESTree.BlockStatement
  53. ): ESTree.BlockStatement {
  54. const leftString: string = RandomGeneratorUtils.getRandomString(3);
  55. let operator: ESTree.BinaryOperator,
  56. rightString: string,
  57. consequent: ESTree.BlockStatement,
  58. alternate: ESTree.BlockStatement;
  59. if (RandomGeneratorUtils.getMathRandom() > 0.5) {
  60. operator = '===';
  61. if (RandomGeneratorUtils.getMathRandom() > 0.5) {
  62. rightString = leftString;
  63. consequent = blockStatementNode;
  64. alternate = randomBlockStatementNode;
  65. } else {
  66. rightString = RandomGeneratorUtils.getRandomString(3);
  67. consequent = randomBlockStatementNode;
  68. alternate = blockStatementNode;
  69. }
  70. } else {
  71. operator = '!==';
  72. if (RandomGeneratorUtils.getMathRandom() > 0.5) {
  73. rightString = leftString;
  74. consequent = randomBlockStatementNode;
  75. alternate = blockStatementNode;
  76. } else {
  77. rightString = RandomGeneratorUtils.getRandomString(3);
  78. consequent = blockStatementNode;
  79. alternate = randomBlockStatementNode;
  80. }
  81. }
  82. let newBlockStatementNode: ESTree.BlockStatement = Nodes.getBlockStatementNode([
  83. Nodes.getIfStatementNode(
  84. Nodes.getBinaryExpressionNode(
  85. operator,
  86. Nodes.getLiteralNode(leftString),
  87. Nodes.getLiteralNode(rightString)
  88. ),
  89. consequent,
  90. alternate
  91. )
  92. ]);
  93. newBlockStatementNode = NodeUtils.parentize(newBlockStatementNode);
  94. return newBlockStatementNode;
  95. }
  96. /**
  97. * @param blockStatementNode
  98. * @return {boolean}
  99. */
  100. private static isValidBlockStatementNode (blockStatementNode: ESTree.BlockStatement): boolean {
  101. let isValidBlockStatementNode: boolean = true;
  102. estraverse.traverse(blockStatementNode, {
  103. enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
  104. if (
  105. (node !== blockStatementNode && Node.isBlockStatementNode(node)) ||
  106. Node.isBreakStatementNode(node) ||
  107. Node.isContinueStatementNode(node)
  108. ) {
  109. isValidBlockStatementNode = false;
  110. }
  111. }
  112. });
  113. return isValidBlockStatementNode;
  114. }
  115. /**
  116. * @return {IVisitor}
  117. */
  118. public getVisitor (): IVisitor {
  119. return {
  120. leave: (node: ESTree.Node, parentNode: ESTree.Node) => {
  121. if (Node.isProgramNode(node)) {
  122. return this.transformNode(node, parentNode);
  123. }
  124. }
  125. };
  126. }
  127. /**
  128. * @param programNode
  129. * @param parentNode
  130. * @returns {ESTree.Node}
  131. */
  132. public transformNode (programNode: ESTree.Program, parentNode: ESTree.Node): ESTree.Node {
  133. this.transformProgramNode(programNode);
  134. return programNode;
  135. }
  136. /**
  137. * @param programNode
  138. */
  139. private transformProgramNode (programNode: ESTree.Program): void {
  140. estraverse.traverse(programNode, {
  141. enter: (node: ESTree.Node, parentNode: ESTree.Node): any =>
  142. DeadCodeInjectionTransformer.collectBlockStatementNodes(node, this.collectedBlockStatements)
  143. });
  144. estraverse.replace(programNode, {
  145. leave: (node: ESTree.Node, parentNode: ESTree.Node): any => {
  146. if (!Node.isBlockStatementNode(node) || !this.collectedBlockStatements.length) {
  147. return node;
  148. }
  149. if (RandomGeneratorUtils.getMathRandom() > this.options.deadCodeInjectionThreshold) {
  150. return node;
  151. }
  152. const randomIndex: number = RandomGeneratorUtils.getRandomInteger(0, this.collectedBlockStatements.length - 1);
  153. const randomBlockStatementNode: ESTree.BlockStatement = this.collectedBlockStatements.splice(randomIndex, 1)[0];
  154. if (randomBlockStatementNode === node) {
  155. return node;
  156. }
  157. return DeadCodeInjectionTransformer.replaceBlockStatementNodes(node, randomBlockStatementNode);
  158. }
  159. });
  160. }
  161. }