|
@@ -6,6 +6,7 @@ import * as ESTree from 'estree';
|
|
|
|
|
|
import { TDeadNodeInjectionCustomNodeFactory } from '../../types/container/custom-nodes/TDeadNodeInjectionCustomNodeFactory';
|
|
import { TDeadNodeInjectionCustomNodeFactory } from '../../types/container/custom-nodes/TDeadNodeInjectionCustomNodeFactory';
|
|
import { TNodeWithBlockScope } from '../../types/node/TNodeWithBlockScope';
|
|
import { TNodeWithBlockScope } from '../../types/node/TNodeWithBlockScope';
|
|
|
|
+import { TNodeWithScope } from '../../types/node/TNodeWithScope';
|
|
|
|
|
|
import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
|
|
import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
|
|
import { IOptions } from '../../interfaces/options/IOptions';
|
|
import { IOptions } from '../../interfaces/options/IOptions';
|
|
@@ -97,15 +98,62 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * @param {Node} blockStatementNode
|
|
|
|
|
|
+ * @param {Node} targetNode
|
|
* @returns {boolean}
|
|
* @returns {boolean}
|
|
*/
|
|
*/
|
|
- private static isValidCollectedBlockStatementNode (blockStatementNode: ESTree.Node): boolean {
|
|
|
|
- const isProhibitedNode: (node: ESTree.Node) => boolean =
|
|
|
|
- (node: ESTree.Node): boolean => NodeGuards.isBreakStatementNode(node) ||
|
|
|
|
- NodeGuards.isContinueStatementNode(node) ||
|
|
|
|
- NodeGuards.isAwaitExpressionNode(node) ||
|
|
|
|
- NodeGuards.isSuperNode(node);
|
|
|
|
|
|
+ private static isProhibitedNodeInsideCollectedBlockStatement (targetNode: ESTree.Node): boolean {
|
|
|
|
+ return NodeGuards.isBreakStatementNode(targetNode)
|
|
|
|
+ || NodeGuards.isContinueStatementNode(targetNode)
|
|
|
|
+ || NodeGuards.isAwaitExpressionNode(targetNode)
|
|
|
|
+ || NodeGuards.isSuperNode(targetNode);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @param {Node} targetNode
|
|
|
|
+ * @returns {boolean}
|
|
|
|
+ */
|
|
|
|
+ private static isScopeHoistingFunctionDeclaration (targetNode: ESTree.Node): boolean {
|
|
|
|
+ if (!NodeGuards.isFunctionDeclarationNode(targetNode)) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const scopeNode: TNodeWithScope = NodeUtils.getScopeOfNode(targetNode);
|
|
|
|
+ const scopeBody: ESTree.Statement[] = !NodeGuards.isSwitchCaseNode(scopeNode)
|
|
|
|
+ ? <ESTree.Statement[]>scopeNode.body
|
|
|
|
+ : scopeNode.consequent;
|
|
|
|
+ const indexInScope: number = scopeBody.indexOf(targetNode);
|
|
|
|
+
|
|
|
|
+ if (indexInScope === 0) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const slicedBody: ESTree.Statement[] = scopeBody.slice(0, indexInScope);
|
|
|
|
+ const hostBlockStatementNode: ESTree.BlockStatement = Nodes.getBlockStatementNode(slicedBody);
|
|
|
|
+ const functionDeclarationName: string = targetNode.id.name;
|
|
|
|
+
|
|
|
|
+ let isScopeHoistedFunctionDeclaration: boolean = false;
|
|
|
|
+
|
|
|
|
+ estraverse.traverse(hostBlockStatementNode, {
|
|
|
|
+ enter: (node: ESTree.Node): estraverse.VisitorOption | void => {
|
|
|
|
+ if (NodeGuards.isIdentifierNode(node) && node.name === functionDeclarationName) {
|
|
|
|
+ isScopeHoistedFunctionDeclaration = true;
|
|
|
|
+
|
|
|
|
+ return estraverse.VisitorOption.Break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ return isScopeHoistedFunctionDeclaration;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @param {BlockStatement} blockStatementNode
|
|
|
|
+ * @returns {boolean}
|
|
|
|
+ */
|
|
|
|
+ private static isValidCollectedBlockStatementNode (blockStatementNode: ESTree.BlockStatement): boolean {
|
|
|
|
+ if (!blockStatementNode.body.length) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
|
|
let nestedBlockStatementsCount: number = 0;
|
|
let nestedBlockStatementsCount: number = 0;
|
|
let isValidBlockStatementNode: boolean = true;
|
|
let isValidBlockStatementNode: boolean = true;
|
|
@@ -117,8 +165,9 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
|
|
}
|
|
}
|
|
|
|
|
|
if (
|
|
if (
|
|
- nestedBlockStatementsCount > DeadCodeInjectionTransformer.maxNestedBlockStatementsCount ||
|
|
|
|
- isProhibitedNode(node)
|
|
|
|
|
|
+ nestedBlockStatementsCount > DeadCodeInjectionTransformer.maxNestedBlockStatementsCount
|
|
|
|
+ || DeadCodeInjectionTransformer.isProhibitedNodeInsideCollectedBlockStatement(node)
|
|
|
|
+ || DeadCodeInjectionTransformer.isScopeHoistingFunctionDeclaration(node)
|
|
) {
|
|
) {
|
|
isValidBlockStatementNode = false;
|
|
isValidBlockStatementNode = false;
|
|
|
|
|
|
@@ -209,7 +258,10 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
|
|
* @param {NodeGuards} parentNode
|
|
* @param {NodeGuards} parentNode
|
|
* @returns {NodeGuards | VisitorOption}
|
|
* @returns {NodeGuards | VisitorOption}
|
|
*/
|
|
*/
|
|
- public transformNode (blockStatementNode: ESTree.BlockStatement, parentNode: ESTree.Node): ESTree.Node | estraverse.VisitorOption {
|
|
|
|
|
|
+ public transformNode (
|
|
|
|
+ blockStatementNode: ESTree.BlockStatement,
|
|
|
|
+ parentNode: ESTree.Node
|
|
|
|
+ ): ESTree.Node | estraverse.VisitorOption {
|
|
const canBreakTraverse: boolean = !this.collectedBlockStatements.length
|
|
const canBreakTraverse: boolean = !this.collectedBlockStatements.length
|
|
|| this.collectedBlockStatementsTotalLength < DeadCodeInjectionTransformer.minCollectedBlockStatementsCount;
|
|
|| this.collectedBlockStatementsTotalLength < DeadCodeInjectionTransformer.minCollectedBlockStatementsCount;
|
|
|
|
|
|
@@ -235,10 +287,9 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
|
|
const maxInteger: number = this.collectedBlockStatements.length - 1;
|
|
const maxInteger: number = this.collectedBlockStatements.length - 1;
|
|
const randomIndex: number = this.randomGenerator.getRandomInteger(minInteger, maxInteger);
|
|
const randomIndex: number = this.randomGenerator.getRandomInteger(minInteger, maxInteger);
|
|
const randomBlockStatementNode: ESTree.BlockStatement = this.collectedBlockStatements.splice(randomIndex, 1)[0];
|
|
const randomBlockStatementNode: ESTree.BlockStatement = this.collectedBlockStatements.splice(randomIndex, 1)[0];
|
|
- const isInvalidRandomBlockStatementNode: boolean = randomBlockStatementNode === blockStatementNode
|
|
|
|
- || !randomBlockStatementNode.body.length;
|
|
|
|
|
|
+ const isDuplicateBlockStatementNodes: boolean = randomBlockStatementNode === blockStatementNode;
|
|
|
|
|
|
- if (isInvalidRandomBlockStatementNode) {
|
|
|
|
|
|
+ if (isDuplicateBlockStatementNodes) {
|
|
return blockStatementNode;
|
|
return blockStatementNode;
|
|
}
|
|
}
|
|
|
|
|