BlockStatementControlFlowTransformer.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import { injectable, inject } from 'inversify';
  2. import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
  3. import * as ESTree from 'estree';
  4. import { TCustomNodeFactory } from '../../types/container/custom-nodes/TCustomNodeFactory';
  5. import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
  6. import { IOptions } from '../../interfaces/options/IOptions';
  7. import { IVisitor } from '../../interfaces/IVisitor';
  8. import { CustomNode } from '../../enums/container/custom-nodes/CustomNode';
  9. import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
  10. import { Node } from '../../node/Node';
  11. import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
  12. import { Utils } from '../../utils/Utils';
  13. @injectable()
  14. export class BlockStatementControlFlowTransformer extends AbstractNodeTransformer {
  15. /**
  16. * @type {TCustomNodeFactory}
  17. */
  18. private readonly customNodeFactory: TCustomNodeFactory;
  19. /**
  20. * @param customNodeFactory
  21. * @param options
  22. */
  23. constructor (
  24. @inject(ServiceIdentifiers.Factory__ICustomNode) customNodeFactory: TCustomNodeFactory,
  25. @inject(ServiceIdentifiers.IOptions) options: IOptions
  26. ) {
  27. super(options);
  28. this.customNodeFactory = customNodeFactory;
  29. }
  30. /**
  31. * @param blockStatementNode
  32. * @return {boolean}
  33. */
  34. private static blockStatementHasProhibitedStatements (blockStatementNode: ESTree.BlockStatement): boolean {
  35. return blockStatementNode.body.some((statement: ESTree.Statement) => {
  36. const isBreakOrContinueStatement: boolean = Node.isBreakStatementNode(statement) || Node.isContinueStatementNode(statement);
  37. const isVariableDeclarationWithLetOrConstKind: boolean = Node.isVariableDeclarationNode(statement) &&
  38. (statement.kind === 'const' || statement.kind === 'let');
  39. return Node.isFunctionDeclarationNode(statement) || isBreakOrContinueStatement || isVariableDeclarationWithLetOrConstKind;
  40. });
  41. }
  42. /**
  43. * @return {IVisitor}
  44. */
  45. public getVisitor (): IVisitor {
  46. return {
  47. leave: (node: ESTree.Node, parentNode: ESTree.Node) => {
  48. if (Node.isBlockStatementNode(node)) {
  49. return this.transformNode(node, parentNode);
  50. }
  51. }
  52. };
  53. }
  54. /**
  55. * @param blockStatementNode
  56. * @param parentNode
  57. * @returns {ESTree.Node}
  58. */
  59. public transformNode (blockStatementNode: ESTree.BlockStatement, parentNode: ESTree.Node): ESTree.Node {
  60. if (
  61. RandomGeneratorUtils.getMathRandom() > this.options.controlFlowFlatteningThreshold ||
  62. BlockStatementControlFlowTransformer.blockStatementHasProhibitedStatements(blockStatementNode)
  63. ) {
  64. return blockStatementNode;
  65. }
  66. const blockStatementBody: ESTree.Statement[] = blockStatementNode.body;
  67. if (blockStatementBody.length <= 4) {
  68. return blockStatementNode;
  69. }
  70. const originalKeys: number[] = Utils.arrayRange(blockStatementBody.length);
  71. const shuffledKeys: number[] = Utils.arrayShuffle(originalKeys);
  72. const originalKeysIndexesInShuffledArray: number[] = originalKeys.map((key: number) => shuffledKeys.indexOf(key));
  73. const blockStatementControlFlowFlatteningCustomNode: ICustomNode = this.customNodeFactory(
  74. CustomNode.BlockStatementControlFlowFlatteningNode
  75. );
  76. blockStatementControlFlowFlatteningCustomNode.initialize(
  77. blockStatementBody,
  78. shuffledKeys,
  79. originalKeysIndexesInShuffledArray
  80. );
  81. return blockStatementControlFlowFlatteningCustomNode.getNode()[0];
  82. }
  83. }