FunctionControlFlowTransformer.ts 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  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 * as _ from 'lodash';
  6. import { TControlFlowReplacerFactory } from '../../types/container/TControlFlowReplacerFactory';
  7. import { TControlFlowStorageFactory } from '../../types/container/TControlFlowStorageFactory';
  8. import { TCustomNodeFactory } from '../../types/container/TCustomNodeFactory';
  9. import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';
  10. import { TStatement } from '../../types/node/TStatement';
  11. import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
  12. import { IOptions } from '../../interfaces/options/IOptions';
  13. import { IStorage } from '../../interfaces/storages/IStorage';
  14. import { CustomNodes } from '../../enums/container/CustomNodes';
  15. import { NodeType } from '../../enums/NodeType';
  16. import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
  17. import { Node } from '../../node/Node';
  18. import { NodeAppender } from '../../node/NodeAppender';
  19. import { NodeControlFlowReplacers } from '../../enums/container/NodeControlFlowReplacers';
  20. import { NodeUtils } from '../../node/NodeUtils';
  21. import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
  22. @injectable()
  23. export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
  24. /**
  25. * @type {Map <string, NodeControlFlowReplacers>}
  26. */
  27. private static readonly controlFlowReplacersMap: Map <string, NodeControlFlowReplacers> = new Map([
  28. [NodeType.BinaryExpression, NodeControlFlowReplacers.BinaryExpressionControlFlowReplacer]
  29. ]);
  30. /**
  31. * @type {number}
  32. */
  33. private static readonly controlFlowReplacersThreshold: number = 0.75;
  34. /**
  35. * @type {number}
  36. */
  37. private static readonly hostNodeSearchMinDepth: number = 2;
  38. /**
  39. * @type {number}
  40. */
  41. private static readonly hostNodeSearchMaxDepth: number = 10;
  42. /**
  43. * @type {Map<ESTree.Node, IStorage<ICustomNode>>}
  44. */
  45. private controlFlowData: Map <ESTree.Node, IStorage<ICustomNode>> = new Map();
  46. /**
  47. * @type {TStatement[][]}
  48. */
  49. private readonly controlFlowNodesList: TStatement[][] = [];
  50. /**
  51. * @type {TControlFlowReplacerFactory}
  52. */
  53. private readonly controlFlowReplacerFactory: TControlFlowReplacerFactory;
  54. /**
  55. * @type {TControlFlowStorageFactory}
  56. */
  57. private readonly controlFlowStorageFactory: TControlFlowStorageFactory;
  58. /**
  59. * @type {TCustomNodeFactory}
  60. */
  61. private readonly customNodeFactory: TCustomNodeFactory;
  62. /**
  63. * @param controlFlowStorageFactory
  64. * @param controlFlowReplacerFactory
  65. * @param customNodeFactory
  66. * @param options
  67. */
  68. constructor (
  69. @inject(ServiceIdentifiers['Factory<IStorage<ICustomNode>>']) controlFlowStorageFactory: TControlFlowStorageFactory,
  70. @inject(ServiceIdentifiers['Factory<IControlFlowReplacer>']) controlFlowReplacerFactory: TControlFlowReplacerFactory,
  71. @inject(ServiceIdentifiers['Factory<ICustomNode>']) customNodeFactory: TCustomNodeFactory,
  72. @inject(ServiceIdentifiers.IOptions) options: IOptions
  73. ) {
  74. super(options);
  75. this.controlFlowStorageFactory = controlFlowStorageFactory;
  76. this.controlFlowReplacerFactory = controlFlowReplacerFactory;
  77. this.customNodeFactory = customNodeFactory;
  78. }
  79. /**
  80. * @param functionNode
  81. * @returns {TNodeWithBlockStatement}
  82. */
  83. private static getHostNode (functionNode: ESTree.FunctionDeclaration | ESTree.FunctionExpression): TNodeWithBlockStatement {
  84. const blockScopesOfNode: TNodeWithBlockStatement[] = NodeUtils.getBlockScopesOfNode(functionNode);
  85. if (blockScopesOfNode.length === 1) {
  86. return functionNode.body;
  87. } else {
  88. blockScopesOfNode.pop();
  89. }
  90. if (blockScopesOfNode.length > FunctionControlFlowTransformer.hostNodeSearchMinDepth) {
  91. blockScopesOfNode.splice(0, FunctionControlFlowTransformer.hostNodeSearchMinDepth);
  92. }
  93. if (blockScopesOfNode.length > FunctionControlFlowTransformer.hostNodeSearchMaxDepth) {
  94. blockScopesOfNode.length = FunctionControlFlowTransformer.hostNodeSearchMaxDepth;
  95. }
  96. return RandomGeneratorUtils.getRandomGenerator().pickone(blockScopesOfNode);
  97. }
  98. /**
  99. * @param hostNodeBody
  100. * @param controlFlowNodesList
  101. */
  102. private static removeOldControlFlowNodeFromHostNodeBody (
  103. hostNodeBody: TStatement[],
  104. controlFlowNodesList: TStatement[][]
  105. ): TStatement[] {
  106. for (let controlFlowNode of controlFlowNodesList) {
  107. const firstIndexOfNode: number = hostNodeBody.indexOf(controlFlowNode[0]);
  108. if (firstIndexOfNode === -1) {
  109. continue;
  110. }
  111. return _.difference(hostNodeBody, controlFlowNode);
  112. }
  113. return hostNodeBody;
  114. }
  115. /**
  116. * @param functionNode
  117. */
  118. public transformNode (functionNode: ESTree.Function): void {
  119. this.changeFunctionBodyControlFlow(functionNode);
  120. }
  121. /**
  122. * @param functionNode
  123. */
  124. private changeFunctionBodyControlFlow (functionNode: ESTree.Function): void {
  125. if (Node.isArrowFunctionExpressionNode(functionNode)) {
  126. return;
  127. }
  128. const controlFlowStorage: IStorage <ICustomNode> = this.controlFlowStorageFactory();
  129. const hostNode: TNodeWithBlockStatement = FunctionControlFlowTransformer.getHostNode(functionNode);
  130. if (!this.controlFlowData.has(hostNode)) {
  131. this.controlFlowData.set(hostNode, controlFlowStorage);
  132. } else {
  133. hostNode.body = <ESTree.Statement[]>FunctionControlFlowTransformer
  134. .removeOldControlFlowNodeFromHostNodeBody(hostNode.body, this.controlFlowNodesList);
  135. const hostControlFlowStorage: IStorage<ICustomNode> = <IStorage<ICustomNode>>this.controlFlowData.get(hostNode);
  136. controlFlowStorage.mergeWith(hostControlFlowStorage, true);
  137. this.controlFlowData.set(hostNode, controlFlowStorage);
  138. }
  139. estraverse.replace(functionNode.body, {
  140. enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
  141. if (RandomGeneratorUtils.getRandomFloat(0, 1) > FunctionControlFlowTransformer.controlFlowReplacersThreshold) {
  142. return;
  143. }
  144. const controlFlowReplacerName: NodeControlFlowReplacers | undefined = FunctionControlFlowTransformer
  145. .controlFlowReplacersMap.get(node.type);
  146. if (controlFlowReplacerName === undefined) {
  147. return;
  148. }
  149. return {
  150. ...this.controlFlowReplacerFactory(controlFlowReplacerName)
  151. .replace(node, parentNode, controlFlowStorage),
  152. parentNode
  153. };
  154. }
  155. });
  156. if (!controlFlowStorage.getLength()) {
  157. return;
  158. }
  159. const controlFlowStorageCustomNode: ICustomNode = this.customNodeFactory(CustomNodes.ControlFlowStorageNode);
  160. controlFlowStorageCustomNode.initialize(controlFlowStorage);
  161. const controlFlowStorageNode: TStatement[] = controlFlowStorageCustomNode.getNode();
  162. this.controlFlowNodesList.push(controlFlowStorageNode);
  163. NodeAppender.prependNode(hostNode, controlFlowStorageNode);
  164. }
  165. }