BlockStatementControlFlowFlatteningNode.ts 5.7 KB

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