|
@@ -20,10 +20,8 @@ import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
|
|
|
import { Node } from '../../node/Node';
|
|
|
import { NodeAppender } from '../../node/NodeAppender';
|
|
|
import { NodeControlFlowReplacers } from '../../enums/container/NodeControlFlowReplacers';
|
|
|
-import { Nodes } from '../../node/Nodes';
|
|
|
import { NodeUtils } from '../../node/NodeUtils';
|
|
|
import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
|
|
|
-import { Utils } from '../../utils/Utils';
|
|
|
|
|
|
@injectable()
|
|
|
export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
|
|
@@ -32,6 +30,7 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
|
|
|
*/
|
|
|
private static readonly controlFlowReplacersMap: Map <string, NodeControlFlowReplacers> = new Map([
|
|
|
[NodeType.BinaryExpression, NodeControlFlowReplacers.BinaryExpressionControlFlowReplacer],
|
|
|
+ [NodeType.BlockStatement, NodeControlFlowReplacers.BlockStatementControlFlowReplacer],
|
|
|
[NodeType.CallExpression, NodeControlFlowReplacers.CallExpressionControlFlowReplacer],
|
|
|
[NodeType.LogicalExpression, NodeControlFlowReplacers.LogicalExpressionControlFlowReplacer]
|
|
|
]);
|
|
@@ -90,18 +89,6 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
|
|
|
this.customNodeFactory = customNodeFactory;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * @param node
|
|
|
- * @return {boolean}
|
|
|
- */
|
|
|
- private static functionHasProhibitedStatements (node: ESTree.Node): boolean {
|
|
|
- const isBreakOrContinueStatement: boolean = Node.isBreakStatementNode(node) || Node.isContinueStatementNode(node);
|
|
|
- const isVariableDeclarationWithLetOrConstKind: boolean = Node.isVariableDeclarationNode(node) &&
|
|
|
- (node.kind === 'const' || node.kind === 'let');
|
|
|
-
|
|
|
- return Node.isFunctionDeclarationNode(node) || isBreakOrContinueStatement || isVariableDeclarationWithLetOrConstKind;
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* @param functionNode
|
|
|
* @returns {TNodeWithBlockStatement}
|
|
@@ -138,23 +125,8 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
|
|
|
const hostNode: TNodeWithBlockStatement = FunctionControlFlowTransformer.getHostNode(functionNode);
|
|
|
const controlFlowStorage: IStorage<ICustomNode> = this.getControlFlowStorage(hostNode);
|
|
|
|
|
|
- let functionHasProhibitedStatements: boolean = false;
|
|
|
-
|
|
|
this.controlFlowData.set(hostNode, controlFlowStorage);
|
|
|
-
|
|
|
- estraverse.replace(functionNode.body, {
|
|
|
- enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
|
|
|
- if (!functionHasProhibitedStatements) {
|
|
|
- functionHasProhibitedStatements = FunctionControlFlowTransformer.functionHasProhibitedStatements(node);
|
|
|
- }
|
|
|
-
|
|
|
- return this.transformFunctionNodes(node, parentNode, controlFlowStorage);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- if (!functionHasProhibitedStatements) {
|
|
|
- this.transformFunctionStatements(functionNode);
|
|
|
- }
|
|
|
+ this.transformFunctionBody(functionNode.body, controlFlowStorage);
|
|
|
|
|
|
if (!controlFlowStorage.getLength()) {
|
|
|
return functionNode;
|
|
@@ -190,108 +162,29 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * @param node
|
|
|
- * @param parentNode
|
|
|
+ * @param functionNodeBody
|
|
|
* @param controlFlowStorage
|
|
|
* @return {ESTree.Node}
|
|
|
*/
|
|
|
- private transformFunctionNodes (
|
|
|
- node: ESTree.Node,
|
|
|
- parentNode: ESTree.Node,
|
|
|
- controlFlowStorage: IStorage<ICustomNode>
|
|
|
- ): ESTree.Node {
|
|
|
- if (!FunctionControlFlowTransformer.controlFlowReplacersMap.has(node.type)) {
|
|
|
- return node;
|
|
|
- }
|
|
|
-
|
|
|
- if (RandomGeneratorUtils.getRandomFloat(0, 1) > this.options.controlFlowFlatteningThreshold) {
|
|
|
- return node;
|
|
|
- }
|
|
|
-
|
|
|
- const controlFlowReplacerName: NodeControlFlowReplacers = <NodeControlFlowReplacers>FunctionControlFlowTransformer
|
|
|
- .controlFlowReplacersMap.get(node.type);
|
|
|
-
|
|
|
- return {
|
|
|
- ...this.controlFlowReplacerFactory(controlFlowReplacerName).replace(node, parentNode, controlFlowStorage),
|
|
|
- parentNode
|
|
|
- };
|
|
|
- }
|
|
|
+ private transformFunctionBody (functionNodeBody: ESTree.BlockStatement, controlFlowStorage: IStorage<ICustomNode>): void {
|
|
|
+ estraverse.replace(functionNodeBody, {
|
|
|
+ enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
|
|
|
+ if (!FunctionControlFlowTransformer.controlFlowReplacersMap.has(node.type)) {
|
|
|
+ return node;
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- * @param functionNode
|
|
|
- */
|
|
|
- private transformFunctionStatements (functionNode: ESTree.FunctionExpression | ESTree.FunctionDeclaration): void {
|
|
|
- if (RandomGeneratorUtils.getRandomFloat(0, 1) > this.options.controlFlowFlatteningThreshold) {
|
|
|
- return;
|
|
|
- }
|
|
|
+ if (RandomGeneratorUtils.getRandomFloat(0, 1) > this.options.controlFlowFlatteningThreshold) {
|
|
|
+ return node;
|
|
|
+ }
|
|
|
|
|
|
- const functionStatements: ESTree.Statement[] = functionNode.body.body;
|
|
|
- const functionStatementsObject: any = Object.assign({}, functionStatements);
|
|
|
- const originalKeys: number[] = Object.keys(functionStatementsObject).map((key: string) => parseInt(key, 10));
|
|
|
- const shuffledKeys: number[] = Utils.arrayShuffle(originalKeys);
|
|
|
- const originalKeysIndexesInShuffledArray: number[] = originalKeys.map((key: number) => shuffledKeys.indexOf(key));
|
|
|
-
|
|
|
- if (functionStatements.length <= 4) {
|
|
|
- return;
|
|
|
- } else if (!functionStatements.length) {
|
|
|
- functionStatements.push(
|
|
|
- Nodes.getReturnStatementNode(
|
|
|
- Nodes.getLiteralNode(true)
|
|
|
- )
|
|
|
- );
|
|
|
- }
|
|
|
+ const controlFlowReplacerName: NodeControlFlowReplacers = <NodeControlFlowReplacers>FunctionControlFlowTransformer
|
|
|
+ .controlFlowReplacersMap.get(node.type);
|
|
|
|
|
|
- const controllerIdentifierName: string = RandomGeneratorUtils.getRandomString(3);
|
|
|
- const indexIdentifierName: string = RandomGeneratorUtils.getRandomString(3);
|
|
|
-
|
|
|
- functionNode.body.body = [
|
|
|
- Nodes.getVariableDeclarationNode([
|
|
|
- Nodes.getVariableDeclaratorNode(
|
|
|
- Nodes.getIdentifierNode(controllerIdentifierName),
|
|
|
- Nodes.getCallExpressionNode(
|
|
|
- Nodes.getMemberExpressionNode(
|
|
|
- Nodes.getLiteralNode(
|
|
|
- originalKeysIndexesInShuffledArray.join('|')
|
|
|
- ),
|
|
|
- Nodes.getIdentifierNode('split')
|
|
|
- ),
|
|
|
- [
|
|
|
- Nodes.getLiteralNode('|')
|
|
|
- ]
|
|
|
- )
|
|
|
- ),
|
|
|
- Nodes.getVariableDeclaratorNode(
|
|
|
- Nodes.getIdentifierNode(indexIdentifierName),
|
|
|
- Nodes.getLiteralNode(0)
|
|
|
- )
|
|
|
- ]),
|
|
|
- Nodes.getWhileStatementNode(
|
|
|
- Nodes.getLiteralNode(true),
|
|
|
- Nodes.getBlockStatementNode([
|
|
|
- Nodes.getSwitchStatementNode(
|
|
|
- Nodes.getMemberExpressionNode(
|
|
|
- Nodes.getIdentifierNode(controllerIdentifierName),
|
|
|
- Nodes.getUpdateExpressionNode(
|
|
|
- '++',
|
|
|
- Nodes.getIdentifierNode(indexIdentifierName)
|
|
|
- ),
|
|
|
- true
|
|
|
- ),
|
|
|
- shuffledKeys.map((key: number, index: number) => {
|
|
|
- return Nodes.getSwitchCaseNode(
|
|
|
- Nodes.getLiteralNode(String(index)),
|
|
|
- [
|
|
|
- functionStatementsObject[key],
|
|
|
- Nodes.getContinueStatement()
|
|
|
- ]
|
|
|
- );
|
|
|
- })
|
|
|
- ),
|
|
|
- Nodes.getBreakStatement()
|
|
|
- ])
|
|
|
- )
|
|
|
- ];
|
|
|
-
|
|
|
- NodeUtils.parentize(functionNode.body);
|
|
|
+ return {
|
|
|
+ ...this.controlFlowReplacerFactory(controlFlowReplacerName).replace(node, parentNode, controlFlowStorage),
|
|
|
+ parentNode
|
|
|
+ };
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
}
|