123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- import { injectable, inject } from 'inversify';
- import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
- import * as estraverse from 'estraverse';
- import * as ESTree from 'estree';
- import { IOptions } from '../../interfaces/options/IOptions';
- import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
- import { IVisitor } from '../../interfaces/IVisitor';
- import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
- import { Node } from '../../node/Node';
- import { Nodes } from '../../node/Nodes';
- import { NodeUtils } from '../../node/NodeUtils';
- @injectable()
- export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
- /**
- * @type {number}
- */
- private static readonly maxNestedBlockStatementsCount: number = 4;
- /**
- * @type {number}
- */
- private static readonly minCollectedBlockStatementsCount: number = 5;
- /**
- * @type {ESTree.BlockStatement[]}
- */
- private readonly collectedBlockStatements: ESTree.BlockStatement[] = [];
- /**
- * @param {IRandomGenerator} randomGenerator
- * @param {IOptions} options
- */
- constructor (
- @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
- @inject(ServiceIdentifiers.IOptions) options: IOptions
- ) {
- super(randomGenerator, options);
- }
- /**
- * @return {IVisitor}
- */
- public getVisitor (): IVisitor {
- return {
- leave: (node: ESTree.Node, parentNode: ESTree.Node) => {
- if (Node.isProgramNode(node)) {
- return this.transformNode(node, parentNode);
- }
- }
- };
- }
- /**
- * @param {Program} programNode
- * @param {Node} parentNode
- * @returns {Node}
- */
- public transformNode (programNode: ESTree.Program, parentNode: ESTree.Node): ESTree.Node {
- this.transformProgramNode(programNode);
- return programNode;
- }
- /**
- * @param {BlockStatement} blockStatementNode
- * @param {BlockStatement[]} collectedBlockStatements
- */
- private collectBlockStatementNodes (
- blockStatementNode: ESTree.BlockStatement,
- collectedBlockStatements: ESTree.BlockStatement[]
- ): void {
- const clonedBlockStatementNode: ESTree.BlockStatement = NodeUtils.clone(blockStatementNode);
- let nestedBlockStatementsCount: number = 0,
- isValidBlockStatementNode: boolean = true;
- estraverse.replace(clonedBlockStatementNode, {
- enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
- /**
- * First step: count nested block statements in current block statement
- */
- if (Node.isBlockStatementNode(node)) {
- nestedBlockStatementsCount++;
- }
- /**
- * If nested block statements count bigger then specified amount or current block statement
- * contains prohibited nodes - we will stop traversing and leave method
- */
- if (
- nestedBlockStatementsCount > DeadCodeInjectionTransformer.maxNestedBlockStatementsCount ||
- Node.isBreakStatementNode(node) ||
- Node.isContinueStatementNode(node)
- ) {
- isValidBlockStatementNode = false;
- return estraverse.VisitorOption.Break;
- }
- /**
- * Second step: rename all identifiers (except identifiers in member expressions)
- * in current block statement
- */
- if (Node.isIdentifierNode(node) && !Node.isMemberExpressionNode(parentNode)) {
- node.name = this.randomGenerator.getRandomVariableName(6);
- }
- return node;
- }
- });
- if (!isValidBlockStatementNode) {
- return;
- }
- collectedBlockStatements.push(clonedBlockStatementNode);
- }
- /**
- * @param {BlockStatement} blockStatementNode
- * @param {BlockStatement} randomBlockStatementNode
- * @returns {BlockStatement}
- */
- private replaceBlockStatementNode (
- blockStatementNode: ESTree.BlockStatement,
- randomBlockStatementNode: ESTree.BlockStatement
- ): ESTree.BlockStatement {
- const random1: boolean = this.randomGenerator.getMathRandom() > 0.5;
- const random2: boolean = this.randomGenerator.getMathRandom() > 0.5;
- const operator: ESTree.BinaryOperator = random1 ? '===' : '!==';
- const leftString: string = this.randomGenerator.getRandomString(3);
- const rightString: string = random2 ? leftString : this.randomGenerator.getRandomString(3);
- let consequent: ESTree.BlockStatement,
- alternate: ESTree.BlockStatement;
- if ((random1 && random2) || (!random1 && !random2)) {
- consequent = blockStatementNode;
- alternate = randomBlockStatementNode;
- } else {
- consequent = randomBlockStatementNode;
- alternate = blockStatementNode;
- }
- let newBlockStatementNode: ESTree.BlockStatement = Nodes.getBlockStatementNode([
- Nodes.getIfStatementNode(
- Nodes.getBinaryExpressionNode(
- operator,
- Nodes.getLiteralNode(leftString),
- Nodes.getLiteralNode(rightString)
- ),
- consequent,
- alternate
- )
- ]);
- newBlockStatementNode = NodeUtils.parentize(newBlockStatementNode);
- return newBlockStatementNode;
- }
- /**
- * @param {Program} programNode
- */
- private transformProgramNode (programNode: ESTree.Program): void {
- estraverse.traverse(programNode, {
- enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
- if (!Node.isBlockStatementNode(node)) {
- return;
- }
- this.collectBlockStatementNodes(node, this.collectedBlockStatements);
- }
- });
- if (this.collectedBlockStatements.length < DeadCodeInjectionTransformer.minCollectedBlockStatementsCount) {
- return;
- }
- estraverse.replace(programNode, {
- leave: (node: ESTree.Node, parentNode: ESTree.Node): any => {
- if (!this.collectedBlockStatements.length) {
- return estraverse.VisitorOption.Break;
- }
- if (
- !Node.isBlockStatementNode(node) ||
- this.randomGenerator.getMathRandom() > this.options.deadCodeInjectionThreshold
- ) {
- return node;
- }
- const minInteger: number = 0;
- const maxInteger: number = this.collectedBlockStatements.length - 1;
- const randomIndex: number = this.randomGenerator.getRandomInteger(minInteger, maxInteger);
- const randomBlockStatementNode: ESTree.BlockStatement = this.collectedBlockStatements.splice(randomIndex, 1)[0];
- if (randomBlockStatementNode === node) {
- return node;
- }
- return this.replaceBlockStatementNode(node, randomBlockStatementNode);
- }
- });
- }
- }
|