javascript-obfuscator
Version:
JavaScript obfuscator
197 lines (168 loc) • 7.06 kB
text/typescript
import { inject, injectable, } from 'inversify';
import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
import * as ESTree from 'estree';
import { IIteratedStatementsSimplifyData } from '../../interfaces/node-transformers/simplifying-transformers/IIteratedStatementsSimplifyData';
import { IOptions } from '../../interfaces/options/IOptions';
import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
import { IStatementSimplifyData } from '../../interfaces/node-transformers/simplifying-transformers/IStatementSimplifyData';
import { NodeTransformer } from '../../enums/node-transformers/NodeTransformer';
import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
import { NodeGuards } from '../../node/NodeGuards';
import { NodeFactory } from '../../node/NodeFactory';
/**
* Simplifies `Statement` node
*/
()
export abstract class AbstractStatementSimplifyTransformer extends AbstractNodeTransformer {
/**
* @type {NodeTransformer[]}
*/
public readonly runAfter: NodeTransformer[] = [
NodeTransformer.ExpressionStatementsMergeTransformer,
NodeTransformer.VariableDeclarationsMergeTransformer
];
/**
* @param {IRandomGenerator} randomGenerator
* @param {IOptions} options
*/
protected constructor (
(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
(ServiceIdentifiers.IOptions) options: IOptions
) {
super(randomGenerator, options);
}
/**
* Returns IStatementSimplifyData based on `Statement` node
*
* @param {ESTree.Statement | null | undefined} statementNode
* @returns {IStatementSimplifyData | null}
*/
protected getStatementSimplifyData (
statementNode: ESTree.Statement | null | undefined
): IStatementSimplifyData | null {
if (!statementNode) {
return null;
}
if (!NodeGuards.isBlockStatementNode(statementNode)) {
return {
leadingStatements: [statementNode],
trailingStatement: null,
hasReturnStatement: false,
hasSingleExpression: false
};
}
const {
startIndex,
unwrappedExpressions,
hasReturnStatement
} = this.collectIteratedStatementsSimplifyData(statementNode);
const leadingStatements: ESTree.Statement[] = this.getLeadingStatements(statementNode, startIndex);
if (!unwrappedExpressions.length) {
return {
leadingStatements,
trailingStatement: null,
hasReturnStatement,
hasSingleExpression: false
};
}
const hasSingleExpression: boolean = unwrappedExpressions.length === 1;
const expression: ESTree.Expression = hasSingleExpression
? unwrappedExpressions[0]
: NodeFactory.sequenceExpressionNode(unwrappedExpressions);
const statement: ESTree.Statement = hasReturnStatement
? NodeFactory.returnStatementNode(expression)
: NodeFactory.expressionStatementNode(expression);
return {
leadingStatements,
trailingStatement: {
statement,
expression
},
hasReturnStatement,
hasSingleExpression
};
}
/**
* Iterates over `BlockStatement` node body and collects data
*
* @param {ESTree.Statement | null | undefined} statementNode
* @returns {IIteratedStatementsSimplifyData}
*/
protected collectIteratedStatementsSimplifyData (
statementNode: ESTree.BlockStatement
): IIteratedStatementsSimplifyData {
const statementNodeBodyLength: number = statementNode.body.length;
const unwrappedExpressions: ESTree.Expression[] = [];
let hasReturnStatement: boolean = false;
let startIndex: number | null = null;
for (let i = statementNodeBodyLength - 1; i >= 0; i--) {
const statementBodyStatementNode: ESTree.Statement = statementNode.body[i];
if (NodeGuards.isExpressionStatementNode(statementBodyStatementNode)) {
if (NodeGuards.isSequenceExpressionNode(statementBodyStatementNode.expression)) {
unwrappedExpressions.unshift(...statementBodyStatementNode.expression.expressions);
} else {
unwrappedExpressions.unshift(statementBodyStatementNode.expression);
}
startIndex = i;
continue;
}
if (
NodeGuards.isReturnStatementNode(statementBodyStatementNode)
&& statementBodyStatementNode.argument
) {
unwrappedExpressions.unshift(statementBodyStatementNode.argument);
hasReturnStatement = true;
startIndex = i;
continue;
}
break;
}
return {
startIndex,
unwrappedExpressions,
hasReturnStatement
};
}
/**
* Returns leading statements
*
* @param {ESTree.BlockStatement} statementNode
* @param {number | null} startIndex
* @returns {ESTree.Statement[]}
*/
protected getLeadingStatements (statementNode: ESTree.BlockStatement, startIndex: number | null): ESTree.Statement[] {
// variant #1: no valid statements inside `BlockStatement` are found
if (startIndex === null) {
return statementNode.body;
}
return startIndex === 0
// variant #2: all statements inside `BlockStatement` branch are valid
? []
// variant #3: only last N statements inside `BlockStatement` branch are valid
: statementNode.body.slice(0, startIndex);
}
/**
* @param {IStatementSimplifyData} statementSimplifyData
* @returns {ESTree.Statement}
*/
protected getPartialStatement (statementSimplifyData: IStatementSimplifyData): ESTree.Statement {
// variant #1: all statements inside `BlockStatement` branch are valid
if (!statementSimplifyData.leadingStatements.length && statementSimplifyData.trailingStatement) {
return statementSimplifyData.trailingStatement.statement;
}
// variant #2: only last N statements inside `BlockStatement` branch are valid
return NodeFactory.blockStatementNode([
...statementSimplifyData.leadingStatements.length ? statementSimplifyData.leadingStatements : [],
...statementSimplifyData.trailingStatement ? [statementSimplifyData.trailingStatement.statement] : []
]);
}
/**
* @param {ESTree.Statement} statementNode
* @param {ESTree.Node} parentNode
* @returns {ESTree.Node}
*/
public abstract transformNode (
statementNode: ESTree.Statement,
parentNode: ESTree.Node
): ESTree.Node;
}