BlockStatementControlFlowTransformer.ts 3.2 KB

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