BlockStatementControlFlowFlatteningNode.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import { inject, injectable, } from 'inversify';
  2. import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
  3. import * as ESTree from 'estree';
  4. import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
  5. import { TStatement } from '../../types/node/TStatement';
  6. import { IOptions } from '../../interfaces/options/IOptions';
  7. import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
  8. import { initializable } from '../../decorators/Initializable';
  9. import { AbstractCustomNode } from '../AbstractCustomNode';
  10. import { NodeFactory } from '../../node/NodeFactory';
  11. import { NodeGuards } from '../../node/NodeGuards';
  12. import { NodeUtils } from '../../node/NodeUtils';
  13. @injectable()
  14. export class BlockStatementControlFlowFlatteningNode extends AbstractCustomNode {
  15. /**
  16. * @type {ESTree.Statement[]}
  17. */
  18. @initializable()
  19. private blockStatementBody!: ESTree.Statement[];
  20. /**
  21. * @type {number[]}
  22. */
  23. @initializable()
  24. private originalKeysIndexesInShuffledArray!: number[];
  25. /**
  26. * @type {number[]}
  27. */
  28. @initializable()
  29. private shuffledKeys!: number[];
  30. /**
  31. * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
  32. * @param {IRandomGenerator} randomGenerator
  33. * @param {IOptions} options
  34. */
  35. constructor (
  36. @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
  37. identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
  38. @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
  39. @inject(ServiceIdentifiers.IOptions) options: IOptions
  40. ) {
  41. super(identifierNamesGeneratorFactory, randomGenerator, options);
  42. }
  43. /**
  44. * @param {Statement[]} blockStatementBody
  45. * @param {number[]} shuffledKeys
  46. * @param {number[]} originalKeysIndexesInShuffledArray
  47. */
  48. public initialize (
  49. blockStatementBody: ESTree.Statement[],
  50. shuffledKeys: number[],
  51. originalKeysIndexesInShuffledArray: number[]
  52. ): void {
  53. this.blockStatementBody = blockStatementBody;
  54. this.shuffledKeys = shuffledKeys;
  55. this.originalKeysIndexesInShuffledArray = originalKeysIndexesInShuffledArray;
  56. }
  57. /**
  58. * @returns {TStatement[]}
  59. */
  60. protected getNodeStructure (): TStatement[] {
  61. const controllerIdentifierName: string = this.randomGenerator.getRandomString(6);
  62. const indexIdentifierName: string = this.randomGenerator.getRandomString(6);
  63. const structure: ESTree.BlockStatement = NodeFactory.blockStatementNode([
  64. NodeFactory.variableDeclarationNode([
  65. NodeFactory.variableDeclaratorNode(
  66. NodeFactory.identifierNode(controllerIdentifierName),
  67. NodeFactory.callExpressionNode(
  68. NodeFactory.memberExpressionNode(
  69. NodeFactory.literalNode(
  70. this.originalKeysIndexesInShuffledArray.join('|')
  71. ),
  72. NodeFactory.identifierNode('split')
  73. ),
  74. [
  75. NodeFactory.literalNode('|')
  76. ]
  77. )
  78. ),
  79. NodeFactory.variableDeclaratorNode(
  80. NodeFactory.identifierNode(indexIdentifierName),
  81. NodeFactory.literalNode(0)
  82. )
  83. ]),
  84. NodeFactory.whileStatementNode(
  85. NodeFactory.literalNode(true),
  86. NodeFactory.blockStatementNode([
  87. NodeFactory.switchStatementNode(
  88. NodeFactory.memberExpressionNode(
  89. NodeFactory.identifierNode(controllerIdentifierName),
  90. NodeFactory.updateExpressionNode(
  91. '++',
  92. NodeFactory.identifierNode(indexIdentifierName)
  93. ),
  94. true
  95. ),
  96. this.shuffledKeys.map((key: number, index: number) => {
  97. const statement: ESTree.Statement = this.blockStatementBody[key];
  98. const consequent: ESTree.Statement[] = [statement];
  99. /**
  100. * We shouldn't add continue statement after return statement
  101. * to prevent `unreachable code after return statement` warnings
  102. */
  103. if (!NodeGuards.isReturnStatementNode(statement)) {
  104. consequent.push(NodeFactory.continueStatement());
  105. }
  106. return NodeFactory.switchCaseNode(
  107. NodeFactory.literalNode(String(index)),
  108. consequent
  109. );
  110. })
  111. ),
  112. NodeFactory.breakStatement()
  113. ])
  114. )
  115. ]);
  116. NodeUtils.parentizeAst(structure);
  117. return [structure];
  118. }
  119. }