mirror of
https://github.com/javascript-obfuscator/javascript-obfuscator
synced 2026-08-09 12:42:29 +00:00
Block statement simplify transformer
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
Change Log
|
||||
|
||||
v1.7.0
|
||||
---
|
||||
* `simplify` option now affects all block statements
|
||||
|
||||
v1.6.0
|
||||
---
|
||||
* **New option:** `numbersToExpressions` enables numbers conversion to expressions
|
||||
|
||||
Vendored
+7
-7
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "javascript-obfuscator",
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.0",
|
||||
"description": "JavaScript obfuscator",
|
||||
"keywords": [
|
||||
"obfuscator",
|
||||
|
||||
@@ -63,6 +63,7 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
|
||||
*/
|
||||
private static readonly nodeTransformersList: NodeTransformer[] = [
|
||||
NodeTransformer.BlockStatementControlFlowTransformer,
|
||||
NodeTransformer.BlockStatementSimplifyTransformer,
|
||||
NodeTransformer.CommentsTransformer,
|
||||
NodeTransformer.CustomCodeHelpersTransformer,
|
||||
NodeTransformer.DeadCodeInjectionTransformer,
|
||||
|
||||
@@ -5,11 +5,16 @@ import { INodeTransformer } from '../../../interfaces/node-transformers/INodeTra
|
||||
|
||||
import { NodeTransformer } from '../../../enums/node-transformers/NodeTransformer';
|
||||
|
||||
import { BlockStatementSimplifyTransformer } from '../../../node-transformers/simplifying-transformers/BlockStatementSimplifyTransformer';
|
||||
import { IfStatementSimplifyTransformer } from '../../../node-transformers/simplifying-transformers/IfStatementSimplifyTransformer';
|
||||
import { VariableDeclarationsMergeTransformer } from '../../../node-transformers/simplifying-transformers/VariableDeclarationsMergeTransformer';
|
||||
|
||||
export const simplifyingTransformersModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
|
||||
// simplifying transformers
|
||||
bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
|
||||
.to(BlockStatementSimplifyTransformer)
|
||||
.whenTargetNamed(NodeTransformer.BlockStatementSimplifyTransformer);
|
||||
|
||||
bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
|
||||
.to(IfStatementSimplifyTransformer)
|
||||
.whenTargetNamed(NodeTransformer.IfStatementSimplifyTransformer);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export enum NodeTransformer {
|
||||
BlockStatementControlFlowTransformer = 'BlockStatementControlFlowTransformer',
|
||||
BlockStatementSimplifyTransformer = 'BlockStatementSimplifyTransformer',
|
||||
CommentsTransformer = 'CommentsTransformer',
|
||||
CustomCodeHelpersTransformer = 'CustomCodeHelpersTransformer',
|
||||
DeadCodeInjectionTransformer = 'DeadCodeInjectionTransformer',
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import * as ESTree from 'estree';
|
||||
|
||||
export interface IIfStatementIteratedStatementsData {
|
||||
export interface IIteratedStatementsSimplifyData {
|
||||
/**
|
||||
* @type {number | null}
|
||||
*/
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import * as ESTree from 'estree';
|
||||
|
||||
export interface IIfStatementSimplifyData {
|
||||
export interface IStatementSimplifyData {
|
||||
/**
|
||||
* @type {ESTree.Statement[]}
|
||||
*/
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
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
|
||||
*/
|
||||
@injectable()
|
||||
export abstract class AbstractStatementSimplifyTransformer extends AbstractNodeTransformer {
|
||||
/**
|
||||
* @type {NodeTransformer[]}
|
||||
*/
|
||||
public readonly runAfter: NodeTransformer[] = [
|
||||
NodeTransformer.VariableDeclarationsMergeTransformer
|
||||
];
|
||||
|
||||
/**
|
||||
* @param {IRandomGenerator} randomGenerator
|
||||
* @param {IOptions} options
|
||||
*/
|
||||
protected constructor (
|
||||
@inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
|
||||
@inject(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 = 0;
|
||||
|
||||
for (let i = 0; i < statementNodeBodyLength; i++) {
|
||||
const statementBodyStatementNode: ESTree.Statement = statementNode.body[i];
|
||||
|
||||
if (startIndex === null) {
|
||||
startIndex = i;
|
||||
}
|
||||
|
||||
if (NodeGuards.isExpressionStatementNode(statementBodyStatementNode)) {
|
||||
unwrappedExpressions.push(statementBodyStatementNode.expression);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
NodeGuards.isReturnStatementNode(statementBodyStatementNode)
|
||||
&& statementBodyStatementNode.argument
|
||||
) {
|
||||
unwrappedExpressions.push(statementBodyStatementNode.argument);
|
||||
hasReturnStatement = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
startIndex = null;
|
||||
unwrappedExpressions.length = 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import { inject, injectable, } from 'inversify';
|
||||
import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
|
||||
|
||||
import * as ESTree from 'estree';
|
||||
|
||||
import { IOptions } from '../../interfaces/options/IOptions';
|
||||
import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
|
||||
import { IStatementSimplifyData } from '../../interfaces/node-transformers/simplifying-transformers/IStatementSimplifyData';
|
||||
import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
|
||||
|
||||
import { NodeTransformer } from '../../enums/node-transformers/NodeTransformer';
|
||||
import { NodeTransformationStage } from '../../enums/node-transformers/NodeTransformationStage';
|
||||
|
||||
import { AbstractStatementSimplifyTransformer } from './AbstractStatementSimplifyTransformer';
|
||||
import { NodeGuards } from '../../node/NodeGuards';
|
||||
import { NodeFactory } from '../../node/NodeFactory';
|
||||
import { NodeUtils } from '../../node/NodeUtils';
|
||||
|
||||
/**
|
||||
* Simplifies `BlockStatement` node
|
||||
*/
|
||||
@injectable()
|
||||
export class BlockStatementSimplifyTransformer extends AbstractStatementSimplifyTransformer {
|
||||
/**
|
||||
* @type {NodeTransformer[]}
|
||||
*/
|
||||
public readonly runAfter: NodeTransformer[] = [
|
||||
NodeTransformer.VariableDeclarationsMergeTransformer
|
||||
];
|
||||
|
||||
/**
|
||||
* @param {IRandomGenerator} randomGenerator
|
||||
* @param {IOptions} options
|
||||
*/
|
||||
public constructor (
|
||||
@inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
|
||||
@inject(ServiceIdentifiers.IOptions) options: IOptions
|
||||
) {
|
||||
super(randomGenerator, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {NodeTransformationStage} nodeTransformationStage
|
||||
* @returns {IVisitor | null}
|
||||
*/
|
||||
public getVisitor (nodeTransformationStage: NodeTransformationStage): IVisitor | null {
|
||||
switch (nodeTransformationStage) {
|
||||
case NodeTransformationStage.Simplifying:
|
||||
return {
|
||||
leave: (
|
||||
node: ESTree.Node,
|
||||
parentNode: ESTree.Node | null
|
||||
): ESTree.Node | undefined => {
|
||||
if (parentNode && NodeGuards.isBlockStatementNode(node)) {
|
||||
return this.transformNode(node, parentNode);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ESTree.Statement} statementNode
|
||||
* @param {ESTree.Node} parentNode
|
||||
* @returns {ESTree.Node}
|
||||
*/
|
||||
public transformNode (
|
||||
statementNode: ESTree.Statement,
|
||||
parentNode: ESTree.Node
|
||||
): ESTree.Node {
|
||||
const simplifyData: IStatementSimplifyData | null = this.getStatementSimplifyData(statementNode);
|
||||
|
||||
if (!simplifyData) {
|
||||
return statementNode;
|
||||
}
|
||||
|
||||
const partialStatementNode: ESTree.Statement = this.getPartialStatement(simplifyData);
|
||||
const transformedNode: ESTree.Node = NodeGuards.isBlockStatementNode(partialStatementNode)
|
||||
? partialStatementNode
|
||||
: NodeFactory.blockStatementNode([partialStatementNode]);
|
||||
|
||||
return NodeUtils.parentizeNode(transformedNode, parentNode);
|
||||
}
|
||||
}
|
||||
@@ -3,16 +3,14 @@ import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
|
||||
|
||||
import * as ESTree from 'estree';
|
||||
|
||||
import { IIfStatementSimplifyData } from '../../interfaces/node-transformers/simplifying-transformers/IIfStatementSimplifyData';
|
||||
import { IIfStatementIteratedStatementsData } from '../../interfaces/node-transformers/simplifying-transformers/IIfStatementIteratedStatementsData';
|
||||
import { IOptions } from '../../interfaces/options/IOptions';
|
||||
import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
|
||||
import { IStatementSimplifyData } from '../../interfaces/node-transformers/simplifying-transformers/IStatementSimplifyData';
|
||||
import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
|
||||
|
||||
import { NodeTransformer } from '../../enums/node-transformers/NodeTransformer';
|
||||
import { NodeTransformationStage } from '../../enums/node-transformers/NodeTransformationStage';
|
||||
|
||||
import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
|
||||
import { AbstractStatementSimplifyTransformer } from './AbstractStatementSimplifyTransformer';
|
||||
import { NodeGuards } from '../../node/NodeGuards';
|
||||
import { NodeFactory } from '../../node/NodeFactory';
|
||||
import { NodeUtils } from '../../node/NodeUtils';
|
||||
@@ -21,14 +19,7 @@ import { NodeUtils } from '../../node/NodeUtils';
|
||||
* Simplifies `IfStatement` node
|
||||
*/
|
||||
@injectable()
|
||||
export class IfStatementSimplifyTransformer extends AbstractNodeTransformer {
|
||||
/**
|
||||
* @type {NodeTransformer[]}
|
||||
*/
|
||||
public readonly runAfter: NodeTransformer[] = [
|
||||
NodeTransformer.VariableDeclarationsMergeTransformer
|
||||
];
|
||||
|
||||
export class IfStatementSimplifyTransformer extends AbstractStatementSimplifyTransformer {
|
||||
/**
|
||||
* @param {IRandomGenerator} randomGenerator
|
||||
* @param {IOptions} options
|
||||
@@ -72,7 +63,7 @@ export class IfStatementSimplifyTransformer extends AbstractNodeTransformer {
|
||||
ifStatementNode: ESTree.IfStatement,
|
||||
parentNode: ESTree.Node
|
||||
): ESTree.Node {
|
||||
const consequentSimplifyData: IIfStatementSimplifyData | null = this.getIfStatementSimplifyData(ifStatementNode.consequent);
|
||||
const consequentSimplifyData: IStatementSimplifyData | null = this.getStatementSimplifyData(ifStatementNode.consequent);
|
||||
|
||||
// Variant #1: no valid consequent expression data
|
||||
if (!consequentSimplifyData) {
|
||||
@@ -85,7 +76,7 @@ export class IfStatementSimplifyTransformer extends AbstractNodeTransformer {
|
||||
// Variant #2: valid data for consequent expression only
|
||||
transformedNode = this.getConsequentNode(ifStatementNode, consequentSimplifyData);
|
||||
} else {
|
||||
const alternateSimplifyData: IIfStatementSimplifyData | null = this.getIfStatementSimplifyData(ifStatementNode.alternate);
|
||||
const alternateSimplifyData: IStatementSimplifyData | null = this.getStatementSimplifyData(ifStatementNode.alternate);
|
||||
|
||||
if (!alternateSimplifyData) {
|
||||
return ifStatementNode;
|
||||
@@ -100,12 +91,12 @@ export class IfStatementSimplifyTransformer extends AbstractNodeTransformer {
|
||||
|
||||
/**
|
||||
* @param {ESTree.IfStatement} ifStatementNode
|
||||
* @param {IIfStatementSimplifyData} consequentSimplifyData
|
||||
* @param {IStatementSimplifyData} consequentSimplifyData
|
||||
* @returns {ESTree.Node}
|
||||
*/
|
||||
private getConsequentNode (
|
||||
protected getConsequentNode (
|
||||
ifStatementNode: ESTree.IfStatement,
|
||||
consequentSimplifyData: IIfStatementSimplifyData
|
||||
consequentSimplifyData: IStatementSimplifyData
|
||||
): ESTree.Node {
|
||||
/**
|
||||
* Converts:
|
||||
@@ -127,7 +118,7 @@ export class IfStatementSimplifyTransformer extends AbstractNodeTransformer {
|
||||
) {
|
||||
return NodeFactory.ifStatementNode(
|
||||
ifStatementNode.test,
|
||||
this.getPartialIfStatementBranchNode(consequentSimplifyData)
|
||||
this.getPartialStatement(consequentSimplifyData)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -168,14 +159,14 @@ export class IfStatementSimplifyTransformer extends AbstractNodeTransformer {
|
||||
|
||||
/**
|
||||
* @param {ESTree.IfStatement} ifStatementNode
|
||||
* @param {IIfStatementSimplifyData} consequentSimplifyData
|
||||
* @param {IIfStatementSimplifyData} alternateSimplifyData
|
||||
* @param {IStatementSimplifyData} consequentSimplifyData
|
||||
* @param {IStatementSimplifyData} alternateSimplifyData
|
||||
* @returns {ESTree.Node}
|
||||
*/
|
||||
private getConsequentAndAlternateNode (
|
||||
protected getConsequentAndAlternateNode (
|
||||
ifStatementNode: ESTree.IfStatement,
|
||||
consequentSimplifyData: IIfStatementSimplifyData,
|
||||
alternateSimplifyData: IIfStatementSimplifyData
|
||||
consequentSimplifyData: IStatementSimplifyData,
|
||||
alternateSimplifyData: IStatementSimplifyData
|
||||
): ESTree.Node {
|
||||
/**
|
||||
* Converts:
|
||||
@@ -199,8 +190,8 @@ export class IfStatementSimplifyTransformer extends AbstractNodeTransformer {
|
||||
) {
|
||||
return NodeFactory.ifStatementNode(
|
||||
ifStatementNode.test,
|
||||
this.getPartialIfStatementBranchNode(consequentSimplifyData),
|
||||
this.getPartialIfStatementBranchNode(alternateSimplifyData)
|
||||
this.getPartialStatement(consequentSimplifyData),
|
||||
this.getPartialStatement(alternateSimplifyData)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -268,152 +259,20 @@ export class IfStatementSimplifyTransformer extends AbstractNodeTransformer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns IIfStatementSimplifyData based on `IfStatement` node body
|
||||
*
|
||||
* @param {ESTree.Statement | null | undefined} statementNode
|
||||
* @returns {IIfStatementSimplifyData | null}
|
||||
* @param {IStatementSimplifyData} statementSimplifyData
|
||||
* @returns {ESTree.Statement}
|
||||
*/
|
||||
private getIfStatementSimplifyData (
|
||||
statementNode: ESTree.Statement | null | undefined
|
||||
): IIfStatementSimplifyData | null {
|
||||
if (!statementNode) {
|
||||
return null;
|
||||
protected getPartialStatement (statementSimplifyData: IStatementSimplifyData): ESTree.Statement {
|
||||
const partialStatement: ESTree.Statement = super.getPartialStatement(statementSimplifyData);
|
||||
|
||||
if (!NodeGuards.isBlockStatementNode(partialStatement)) {
|
||||
return partialStatement;
|
||||
}
|
||||
|
||||
if (!NodeGuards.isBlockStatementNode(statementNode)) {
|
||||
return {
|
||||
leadingStatements: [statementNode],
|
||||
trailingStatement: null,
|
||||
hasReturnStatement: false,
|
||||
hasSingleExpression: false
|
||||
};
|
||||
}
|
||||
|
||||
const {
|
||||
startIndex,
|
||||
unwrappedExpressions,
|
||||
hasReturnStatement
|
||||
} = this.collectIteratedStatementsData(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 `IfStatement` node body and collects data
|
||||
*
|
||||
* @param {ESTree.Statement | null | undefined} statementNode
|
||||
* @returns {IIfStatementIteratedStatementsData}
|
||||
*/
|
||||
private collectIteratedStatementsData (
|
||||
statementNode: ESTree.BlockStatement
|
||||
): IIfStatementIteratedStatementsData {
|
||||
const statementNodeBodyLength: number = statementNode.body.length;
|
||||
const unwrappedExpressions: ESTree.Expression[] = [];
|
||||
|
||||
let hasReturnStatement: boolean = false;
|
||||
let startIndex: number | null = 0;
|
||||
|
||||
for (let i = 0; i < statementNodeBodyLength; i++) {
|
||||
const statementBodyStatementNode: ESTree.Statement = statementNode.body[i];
|
||||
|
||||
if (startIndex === null) {
|
||||
startIndex = i;
|
||||
}
|
||||
|
||||
if (NodeGuards.isExpressionStatementNode(statementBodyStatementNode)) {
|
||||
unwrappedExpressions.push(statementBodyStatementNode.expression);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
NodeGuards.isReturnStatementNode(statementBodyStatementNode)
|
||||
&& statementBodyStatementNode.argument
|
||||
) {
|
||||
unwrappedExpressions.push(statementBodyStatementNode.argument);
|
||||
hasReturnStatement = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
startIndex = null;
|
||||
unwrappedExpressions.length = 0;
|
||||
}
|
||||
|
||||
return {
|
||||
startIndex,
|
||||
unwrappedExpressions,
|
||||
hasReturnStatement
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns leading statements for IIfStatementSimplifyData
|
||||
*
|
||||
* @param {ESTree.BlockStatement} statementNode
|
||||
* @param {number | null} startIndex
|
||||
* @returns {ESTree.Statement[]}
|
||||
*/
|
||||
private getLeadingStatements (statementNode: ESTree.BlockStatement, startIndex: number | null): ESTree.Statement[] {
|
||||
// variant #1: no valid statements inside `IfStatement` are found
|
||||
if (startIndex === null) {
|
||||
return statementNode.body;
|
||||
}
|
||||
|
||||
return startIndex === 0
|
||||
// variant #2: all statements inside `IfStatement` branch are valid
|
||||
? []
|
||||
// variant #3: only last N statements inside `IfStatement` branch are valid
|
||||
: statementNode.body.slice(0, startIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {IIfStatementSimplifyData} ifStatementSimplifyData
|
||||
* @returns {ESTree.BlockStatement}
|
||||
*/
|
||||
private getPartialIfStatementBranchNode (ifStatementSimplifyData: IIfStatementSimplifyData): ESTree.Statement {
|
||||
// variant #1: all statements inside `IfStatement` branch are valid
|
||||
if (!ifStatementSimplifyData.leadingStatements.length && ifStatementSimplifyData.trailingStatement) {
|
||||
return ifStatementSimplifyData.trailingStatement.statement;
|
||||
}
|
||||
|
||||
// variant #2: only last N statements inside `IfStatement` branch are valid
|
||||
const blockStatementNode: ESTree.BlockStatement = NodeFactory.blockStatementNode([
|
||||
...ifStatementSimplifyData.leadingStatements.length ? ifStatementSimplifyData.leadingStatements : [],
|
||||
...ifStatementSimplifyData.trailingStatement ? [ifStatementSimplifyData.trailingStatement.statement] : []
|
||||
]);
|
||||
|
||||
return blockStatementNode.body.length === 1
|
||||
&& !this.isProhibitedSingleStatementForIfStatementBranch(blockStatementNode.body[0])
|
||||
? blockStatementNode.body[0]
|
||||
: blockStatementNode;
|
||||
return partialStatement.body.length === 1
|
||||
&& !this.isProhibitedSingleStatementForIfStatementBranch(partialStatement.body[0])
|
||||
? partialStatement.body[0]
|
||||
: partialStatement;
|
||||
|
||||
}
|
||||
|
||||
@@ -421,7 +280,7 @@ export class IfStatementSimplifyTransformer extends AbstractNodeTransformer {
|
||||
* @param {ESTree.Statement} statement
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private isProhibitedSingleStatementForIfStatementBranch (statement: ESTree.Statement): boolean {
|
||||
protected isProhibitedSingleStatementForIfStatementBranch (statement: ESTree.Statement): boolean {
|
||||
/**
|
||||
* Function declaration is not allowed outside of block in `strict` mode
|
||||
*/
|
||||
|
||||
+10
-7
@@ -7,17 +7,20 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
|
||||
|
||||
let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
|
||||
`
|
||||
const foo = 0..toString();
|
||||
const bar = 'abc';
|
||||
|
||||
console.log(foo, bar);
|
||||
function foo () {
|
||||
console.log(1);
|
||||
|
||||
const bar = 123;
|
||||
console.log(2);
|
||||
console.log(3);
|
||||
|
||||
return bar;
|
||||
}
|
||||
`,
|
||||
{
|
||||
...NO_ADDITIONAL_NODES_PRESET,
|
||||
compact: false,
|
||||
numbersToExpressions: true,
|
||||
stringArray: true,
|
||||
stringArrayThreshold: 1
|
||||
simplify: true
|
||||
}
|
||||
).getObfuscatedCode();
|
||||
|
||||
|
||||
+245
@@ -0,0 +1,245 @@
|
||||
import { assert } from 'chai';
|
||||
|
||||
import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes';
|
||||
|
||||
import { readFileAsString } from '../../../../helpers/readFileAsString';
|
||||
|
||||
import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade';
|
||||
|
||||
describe('BlockStatementSimplifyTransformer', () => {
|
||||
describe('Full `BlockStatement` simplify cases', () => {
|
||||
describe('No `ReturnStatement`', () => {
|
||||
describe('Variant #1: single statement', () => {
|
||||
const regExp: RegExp = new RegExp(
|
||||
'function foo *\\(\\) *{ *' +
|
||||
'bar\\(\\); *' +
|
||||
'}'
|
||||
);
|
||||
|
||||
|
||||
let obfuscatedCode: string;
|
||||
|
||||
before(() => {
|
||||
const code: string = readFileAsString(__dirname + '/fixtures/full-no-return-single-statement.js');
|
||||
|
||||
obfuscatedCode = JavaScriptObfuscator.obfuscate(
|
||||
code,
|
||||
{
|
||||
...NO_ADDITIONAL_NODES_PRESET,
|
||||
simplify: true
|
||||
}
|
||||
).getObfuscatedCode();
|
||||
});
|
||||
|
||||
it('should not simplify block statement', () => {
|
||||
assert.match(obfuscatedCode, regExp);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Variant #2: multiple statements', () => {
|
||||
const regExp: RegExp = new RegExp(
|
||||
'function foo *\\(\\) *{ *' +
|
||||
'bar\\(\\), *baz\\(\\), *bark\\(\\); *' +
|
||||
'}'
|
||||
);
|
||||
|
||||
|
||||
let obfuscatedCode: string;
|
||||
|
||||
before(() => {
|
||||
const code: string = readFileAsString(__dirname + '/fixtures/full-no-return-multiple-statements.js');
|
||||
|
||||
obfuscatedCode = JavaScriptObfuscator.obfuscate(
|
||||
code,
|
||||
{
|
||||
...NO_ADDITIONAL_NODES_PRESET,
|
||||
simplify: true
|
||||
}
|
||||
).getObfuscatedCode();
|
||||
});
|
||||
|
||||
it('should simplify block statement', () => {
|
||||
assert.match(obfuscatedCode, regExp);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('With `ReturnStatement`', () => {
|
||||
describe('Variant #1: single statement', () => {
|
||||
const regExp: RegExp = new RegExp(
|
||||
'function foo *\\(\\) *{ *' +
|
||||
'return bar\\(\\); *' +
|
||||
'}'
|
||||
);
|
||||
|
||||
|
||||
let obfuscatedCode: string;
|
||||
|
||||
before(() => {
|
||||
const code: string = readFileAsString(__dirname + '/fixtures/full-return-single-statement.js');
|
||||
|
||||
obfuscatedCode = JavaScriptObfuscator.obfuscate(
|
||||
code,
|
||||
{
|
||||
...NO_ADDITIONAL_NODES_PRESET,
|
||||
simplify: true
|
||||
}
|
||||
).getObfuscatedCode();
|
||||
});
|
||||
|
||||
it('should not simplify block statement', () => {
|
||||
assert.match(obfuscatedCode, regExp);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Variant #2: multiple statements', () => {
|
||||
const regExp: RegExp = new RegExp(
|
||||
'function foo *\\(\\) *{ *' +
|
||||
'return bar\\(\\), *baz\\(\\), *bark\\(\\); *' +
|
||||
'}'
|
||||
);
|
||||
|
||||
|
||||
let obfuscatedCode: string;
|
||||
|
||||
before(() => {
|
||||
const code: string = readFileAsString(__dirname + '/fixtures/full-return-multiple-statements.js');
|
||||
|
||||
obfuscatedCode = JavaScriptObfuscator.obfuscate(
|
||||
code,
|
||||
{
|
||||
...NO_ADDITIONAL_NODES_PRESET,
|
||||
simplify: true
|
||||
}
|
||||
).getObfuscatedCode();
|
||||
});
|
||||
|
||||
it('should simplify block statement', () => {
|
||||
assert.match(obfuscatedCode, regExp);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Partial `BlockStatement` simplify cases', () => {
|
||||
describe('No `ReturnStatement`', () => {
|
||||
describe('Variant #1: single statement', () => {
|
||||
const regExp: RegExp = new RegExp(
|
||||
'function foo *\\(\\) *{ *' +
|
||||
'var _0x([a-f0-9]){4,6} *= *baz\\(\\);' +
|
||||
'}'
|
||||
);
|
||||
|
||||
|
||||
let obfuscatedCode: string;
|
||||
|
||||
before(() => {
|
||||
const code: string = readFileAsString(__dirname + '/fixtures/partial-no-return-single-statement.js');
|
||||
|
||||
obfuscatedCode = JavaScriptObfuscator.obfuscate(
|
||||
code,
|
||||
{
|
||||
...NO_ADDITIONAL_NODES_PRESET,
|
||||
simplify: true
|
||||
}
|
||||
).getObfuscatedCode();
|
||||
});
|
||||
|
||||
it('should not simplify block statement', () => {
|
||||
assert.match(obfuscatedCode, regExp);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Variant #2: multiple statements', () => {
|
||||
const regExp: RegExp = new RegExp(
|
||||
'function foo *\\(\\) *{ *' +
|
||||
'const _0x([a-f0-9]){4,6} *= *baz\\(\\); *' +
|
||||
'bark\\(\\), *hawk\\(\\);' +
|
||||
'}'
|
||||
);
|
||||
|
||||
|
||||
let obfuscatedCode: string;
|
||||
|
||||
before(() => {
|
||||
const code: string = readFileAsString(__dirname + '/fixtures/partial-no-return-multiple-statements.js');
|
||||
|
||||
obfuscatedCode = JavaScriptObfuscator.obfuscate(
|
||||
code,
|
||||
{
|
||||
...NO_ADDITIONAL_NODES_PRESET,
|
||||
simplify: true
|
||||
}
|
||||
).getObfuscatedCode();
|
||||
});
|
||||
|
||||
it('should simplify block statement', () => {
|
||||
assert.match(obfuscatedCode, regExp);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('With `ReturnStatement`', () => {
|
||||
describe('Variant #1: multiple statements', () => {
|
||||
const regExp: RegExp = new RegExp(
|
||||
'function foo *\\(\\) *{ *' +
|
||||
'const _0x([a-f0-9]){4,6} *= *baz\\(\\); *' +
|
||||
'return bark\\(\\), *hawk\\(\\);' +
|
||||
'}'
|
||||
);
|
||||
|
||||
|
||||
let obfuscatedCode: string;
|
||||
|
||||
before(() => {
|
||||
const code: string = readFileAsString(__dirname + '/fixtures/partial-return-multiple-statements.js');
|
||||
|
||||
obfuscatedCode = JavaScriptObfuscator.obfuscate(
|
||||
code,
|
||||
{
|
||||
...NO_ADDITIONAL_NODES_PRESET,
|
||||
simplify: true
|
||||
}
|
||||
).getObfuscatedCode();
|
||||
});
|
||||
|
||||
it('should simplify block statement', () => {
|
||||
assert.match(obfuscatedCode, regExp);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Cases', () => {
|
||||
describe('Variable declarations merge transformer integration', () => {
|
||||
describe('Variant #1: three statements', () => {
|
||||
const regExp: RegExp = new RegExp(
|
||||
'function foo *\\(\\) *{ *' +
|
||||
'var _0x([a-f0-9]){4,6} *= *function *\\(\\) *{}, *' +
|
||||
'_0x([a-f0-9]){4,6} *= *function *\\(\\) *{}, *' +
|
||||
'_0x([a-f0-9]){4,6} *= *function *\\(\\) *{}; *' +
|
||||
'}'
|
||||
);
|
||||
|
||||
|
||||
let obfuscatedCode: string;
|
||||
|
||||
before(() => {
|
||||
const code: string = readFileAsString(__dirname + '/fixtures/variable-declarations-merge-transformer-integration-1.js');
|
||||
|
||||
obfuscatedCode = JavaScriptObfuscator.obfuscate(
|
||||
code,
|
||||
{
|
||||
...NO_ADDITIONAL_NODES_PRESET,
|
||||
simplify: true
|
||||
}
|
||||
).getObfuscatedCode();
|
||||
});
|
||||
|
||||
it('should simplify block statement', () => {
|
||||
assert.match(obfuscatedCode, regExp);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
function foo () {
|
||||
bar();
|
||||
baz();
|
||||
bark();
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
function foo () {
|
||||
bar();
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
function foo () {
|
||||
bar();
|
||||
baz();
|
||||
|
||||
return bark();
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
function foo () {
|
||||
return bar();
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
function foo () {
|
||||
const bar = baz();
|
||||
|
||||
bark();
|
||||
hawk();
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
function foo () {
|
||||
var bar = baz();
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
function foo () {
|
||||
const bar = baz();
|
||||
|
||||
bark();
|
||||
|
||||
return hawk();
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
function foo() {
|
||||
var bar = function () {};
|
||||
var baz = function () {};
|
||||
var bark = function () {};
|
||||
}
|
||||
Reference in New Issue
Block a user