mirror of
https://github.com/javascript-obfuscator/javascript-obfuscator
synced 2026-08-09 12:42:29 +00:00
158 lines
6.0 KiB
TypeScript
158 lines
6.0 KiB
TypeScript
import { inject, injectable, } from 'inversify';
|
|
import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
|
|
|
|
import * as estraverse from 'estraverse';
|
|
import * as ESTree from 'estree';
|
|
|
|
import { TControlFlowCustomNodeFactory } from '../../types/container/custom-nodes/TControlFlowCustomNodeFactory';
|
|
import { TStatement } from '../../types/node/TStatement';
|
|
|
|
import { IArrayUtils } from '../../interfaces/utils/IArrayUtils';
|
|
import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
|
|
import { IOptions } from '../../interfaces/options/IOptions';
|
|
import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
|
|
import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
|
|
|
|
import { ControlFlowCustomNode } from '../../enums/custom-nodes/ControlFlowCustomNode';
|
|
import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
|
|
|
|
import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
|
|
import { NodeGuards } from '../../node/NodeGuards';
|
|
import { NodeUtils } from '../../node/NodeUtils';
|
|
|
|
@injectable()
|
|
export class BlockStatementControlFlowTransformer extends AbstractNodeTransformer {
|
|
/**
|
|
* @type {IArrayUtils}
|
|
*/
|
|
private readonly arrayUtils: IArrayUtils;
|
|
|
|
/**
|
|
* @type {TControlFlowCustomNodeFactory}
|
|
*/
|
|
private readonly controlFlowCustomNodeFactory: TControlFlowCustomNodeFactory;
|
|
|
|
/**
|
|
* @param {TControlFlowCustomNodeFactory} controlFlowCustomNodeFactory
|
|
* @param {IArrayUtils} arrayUtils
|
|
* @param {IRandomGenerator} randomGenerator
|
|
* @param {IOptions} options
|
|
*/
|
|
constructor (
|
|
@inject(ServiceIdentifiers.Factory__IControlFlowCustomNode)
|
|
controlFlowCustomNodeFactory: TControlFlowCustomNodeFactory,
|
|
@inject(ServiceIdentifiers.IArrayUtils) arrayUtils: IArrayUtils,
|
|
@inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
|
|
@inject(ServiceIdentifiers.IOptions) options: IOptions
|
|
) {
|
|
super(randomGenerator, options);
|
|
|
|
this.controlFlowCustomNodeFactory = controlFlowCustomNodeFactory;
|
|
this.arrayUtils = arrayUtils;
|
|
}
|
|
|
|
/**
|
|
* @param {BlockStatement} blockStatementNode
|
|
* @returns {boolean}
|
|
*/
|
|
private static blockStatementHasProhibitedStatements (blockStatementNode: ESTree.BlockStatement): boolean {
|
|
return blockStatementNode.body.some((statement: ESTree.Statement) => {
|
|
const isBreakOrContinueStatement: boolean = NodeGuards.isBreakStatementNode(statement)
|
|
|| NodeGuards.isContinueStatementNode(statement);
|
|
const isVariableDeclarationWithLetOrConstKind: boolean = NodeGuards.isVariableDeclarationNode(statement)
|
|
&& (statement.kind === 'const' || statement.kind === 'let');
|
|
|
|
return NodeGuards.isFunctionDeclarationNode(statement)
|
|
|| isBreakOrContinueStatement
|
|
|| isVariableDeclarationWithLetOrConstKind;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {BlockStatement} blockStatementNode
|
|
* @returns {boolean}
|
|
*/
|
|
private static canTransformBlockStatementNode (blockStatementNode: ESTree.BlockStatement): boolean {
|
|
let canTransform: boolean = true;
|
|
|
|
estraverse.traverse(blockStatementNode, {
|
|
enter: (node: ESTree.Node): any => {
|
|
if (NodeGuards.isWhileStatementNode(node)) {
|
|
return estraverse.VisitorOption.Skip;
|
|
}
|
|
|
|
if (
|
|
NodeGuards.isBlockStatementNode(node)
|
|
&& BlockStatementControlFlowTransformer.blockStatementHasProhibitedStatements(node)
|
|
) {
|
|
canTransform = false;
|
|
}
|
|
}
|
|
});
|
|
|
|
if (blockStatementNode.body.length <= 4) {
|
|
canTransform = false;
|
|
}
|
|
|
|
return canTransform;
|
|
}
|
|
|
|
/**
|
|
* @param {TransformationStage} transformationStage
|
|
* @returns {IVisitor | null}
|
|
*/
|
|
public getVisitor (transformationStage: TransformationStage): IVisitor | null {
|
|
if (!this.options.controlFlowFlattening) {
|
|
return null;
|
|
}
|
|
|
|
switch (transformationStage) {
|
|
case TransformationStage.ControlFlowFlattening:
|
|
return {
|
|
leave: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
|
|
if (parentNode && NodeGuards.isBlockStatementNode(node)) {
|
|
return this.transformNode(node, parentNode);
|
|
}
|
|
}
|
|
};
|
|
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {BlockStatement} blockStatementNode
|
|
* @param {NodeGuards} parentNode
|
|
* @returns {NodeGuards}
|
|
*/
|
|
public transformNode (blockStatementNode: ESTree.BlockStatement, parentNode: ESTree.Node): ESTree.Node {
|
|
if (
|
|
this.randomGenerator.getMathRandom() > this.options.controlFlowFlatteningThreshold ||
|
|
!BlockStatementControlFlowTransformer.canTransformBlockStatementNode(blockStatementNode)
|
|
) {
|
|
return blockStatementNode;
|
|
}
|
|
|
|
const blockStatementBody: ESTree.Statement[] = blockStatementNode.body;
|
|
const originalKeys: number[] = this.arrayUtils.arrayRange(blockStatementBody.length);
|
|
const shuffledKeys: number[] = this.arrayUtils.arrayShuffle(originalKeys);
|
|
const originalKeysIndexesInShuffledArray: number[] = originalKeys.map((key: number) => shuffledKeys.indexOf(key));
|
|
const blockStatementControlFlowFlatteningCustomNode: ICustomNode = this.controlFlowCustomNodeFactory(
|
|
ControlFlowCustomNode.BlockStatementControlFlowFlatteningNode
|
|
);
|
|
|
|
blockStatementControlFlowFlatteningCustomNode.initialize(
|
|
blockStatementBody,
|
|
shuffledKeys,
|
|
originalKeysIndexesInShuffledArray
|
|
);
|
|
|
|
const newBlockStatementNode: TStatement = blockStatementControlFlowFlatteningCustomNode.getNode()[0];
|
|
|
|
NodeUtils.parentizeNode(newBlockStatementNode, parentNode);
|
|
|
|
return newBlockStatementNode;
|
|
}
|
|
}
|