浏览代码

IfStatementSimplifyTransformer prototype WIP

sanex3339 4 年之前
父节点
当前提交
770d52ccac

文件差异内容过多而无法显示
+ 0 - 0
dist/index.cli.js


文件差异内容过多而无法显示
+ 0 - 0
dist/index.js


+ 1 - 0
src/JavaScriptObfuscator.ts

@@ -68,6 +68,7 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
         NodeTransformer.DeadCodeInjectionTransformer,
         NodeTransformer.EvalCallExpressionTransformer,
         NodeTransformer.FunctionControlFlowTransformer,
+        NodeTransformer.IfStatementSimplifyTransformer,
         NodeTransformer.LabeledStatementTransformer,
         NodeTransformer.LiteralTransformer,
         NodeTransformer.RenamePropertiesTransformer,

+ 5 - 0
src/container/modules/node-transformers/MinificationTransformersModule.ts

@@ -5,10 +5,15 @@ import { INodeTransformer } from '../../../interfaces/node-transformers/INodeTra
 
 import { NodeTransformer } from '../../../enums/node-transformers/NodeTransformer';
 
+import { IfStatementSimplifyTransformer } from '../../../node-transformers/minification-transformers/IfStatementSimplifyTransformer';
 import { VariableDeclarationsMergeTransformer } from '../../../node-transformers/minification-transformers/VariableDeclarationsMergeTransformer';
 
 export const minificationTransformersModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
     // minification transformers
+    bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
+        .to(IfStatementSimplifyTransformer)
+        .whenTargetNamed(NodeTransformer.IfStatementSimplifyTransformer);
+
     bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
         .to(VariableDeclarationsMergeTransformer)
         .whenTargetNamed(NodeTransformer.VariableDeclarationsMergeTransformer);

+ 1 - 0
src/enums/node-transformers/NodeTransformer.ts

@@ -5,6 +5,7 @@ export enum NodeTransformer {
     DeadCodeInjectionTransformer = 'DeadCodeInjectionTransformer',
     EvalCallExpressionTransformer = 'EvalCallExpressionTransformer',
     FunctionControlFlowTransformer = 'FunctionControlFlowTransformer',
+    IfStatementSimplifyTransformer = 'IfStatementSimplifyTransformer',
     LabeledStatementTransformer = 'LabeledStatementTransformer',
     LiteralTransformer = 'LiteralTransformer',
     MemberExpressionTransformer = 'MemberExpressionTransformer',

+ 205 - 0
src/node-transformers/minification-transformers/IfStatementSimplifyTransformer.ts

@@ -0,0 +1,205 @@
+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 { IVisitor } from '../../interfaces/node-transformers/IVisitor';
+
+import { NodeTransformationStage } from '../../enums/node-transformers/NodeTransformationStage';
+
+import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
+import { NodeGuards } from '../../node/NodeGuards';
+import { NodeFactory } from '../../node/NodeFactory';
+
+/**
+ * Simplifies `IfStatement` node
+ */
+@injectable()
+export class IfStatementSimplifyTransformer extends AbstractNodeTransformer {
+    /**
+     * @type {WeakSet<ESTree.BlockStatement>}
+     */
+    private readonly ifStatementBlockStatementsWithReturnStatementSet: WeakSet<ESTree.BlockStatement> = new Set();
+
+    /**
+     * @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.Minification:
+                return {
+                    leave: (
+                        node: ESTree.Node,
+                        parentNode: ESTree.Node | null
+                    ): ESTree.Node | undefined => {
+                        if (parentNode && NodeGuards.isIfStatementNode(node)) {
+                            return this.transformNode(node, parentNode);
+                        }
+                    }
+                };
+
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * @param {ESTree.IfStatement} ifStatementNode
+     * @param {ESTree.Node} parentNode
+     * @returns {ESTree.IfStatement}
+     */
+    // eslint-disable-next-line complexity
+    public transformNode (
+        ifStatementNode: ESTree.IfStatement,
+        parentNode: ESTree.Node
+    ): ESTree.Node {
+        if (this.isValidIfStatementBlockStatementForConvert(ifStatementNode.consequent)) {
+            const consequentExpression: ESTree.Expression | null = this.toExpression(ifStatementNode.consequent.body);
+
+            if (!consequentExpression) {
+                return ifStatementNode;
+            }
+
+            if (ifStatementNode.alternate) {
+                if (this.isValidIfStatementBlockStatementForConvert(ifStatementNode.alternate)) {
+                    const alternateExpression: ESTree.Expression | null = this.toExpression(ifStatementNode.alternate.body);
+
+                    if (!alternateExpression) {
+                        return ifStatementNode;
+                    }
+
+                    if (
+                        this.ifStatementBlockStatementsWithReturnStatementSet.has(ifStatementNode.consequent)
+                        && this.ifStatementBlockStatementsWithReturnStatementSet.has(ifStatementNode.alternate)
+                    ) {
+                        return NodeFactory.returnStatementNode(
+                            NodeFactory.conditionalExpressionNode(
+                                ifStatementNode.test,
+                                consequentExpression,
+                                alternateExpression
+                            )
+                        );
+                    }
+
+                    if (this.ifStatementBlockStatementsWithReturnStatementSet.has(ifStatementNode.consequent)) {
+                        return NodeFactory.ifStatementNode(
+                            ifStatementNode.test,
+                            NodeFactory.returnStatementNode(consequentExpression),
+                            NodeFactory.expressionStatementNode(alternateExpression)
+                        );
+                    }
+
+                    if (this.ifStatementBlockStatementsWithReturnStatementSet.has(ifStatementNode.alternate)) {
+                        return NodeFactory.ifStatementNode(
+                            ifStatementNode.test,
+                            NodeFactory.expressionStatementNode(consequentExpression),
+                            NodeFactory.returnStatementNode(alternateExpression)
+                        );
+                    }
+
+                    return NodeFactory.expressionStatementNode(
+                        NodeFactory.conditionalExpressionNode(
+                            ifStatementNode.test,
+                            consequentExpression,
+                            alternateExpression
+                        )
+                    );
+                } else {
+                    return ifStatementNode;
+                }
+            }
+
+            return this.ifStatementBlockStatementsWithReturnStatementSet.has(ifStatementNode.consequent)
+                ? NodeFactory.ifStatementNode(
+                    ifStatementNode.test,
+                    NodeFactory.returnStatementNode(consequentExpression)
+                )
+                : NodeFactory.expressionStatementNode(
+                    NodeFactory.logicalExpressionNode(
+                        '&&',
+                        ifStatementNode.test,
+                        consequentExpression,
+                    )
+                );
+
+        }
+
+        return ifStatementNode;
+    }
+
+    /**
+     * @param {ESTree.Statement} statementNode
+     * @returns {statementNode is ESTree.BlockStatement & {body: (ESTree.ExpressionStatement | ESTree.ReturnStatement)[]}}
+     * @private
+     */
+    private isValidIfStatementBlockStatementForConvert (
+        statementNode: ESTree.Statement
+    ): statementNode is ESTree.BlockStatement & {body: (ESTree.ExpressionStatement | ESTree.ReturnStatement)[]} {
+        if (!NodeGuards.isBlockStatementNode(statementNode)) {
+            return false;
+        }
+
+        let isValidStatementNode: boolean = true;
+
+        for (const statement of statementNode.body) {
+            if (
+                !NodeGuards.isExpressionStatementNode(statement)
+                && !NodeGuards.isReturnStatementNode(statement)
+            ) {
+                isValidStatementNode = false;
+
+                break;
+            } else if (NodeGuards.isReturnStatementNode(statement)) {
+                this.ifStatementBlockStatementsWithReturnStatementSet.add(statementNode);
+            }
+        }
+
+        return isValidStatementNode;
+    }
+
+    /**
+     * @param {(ESTree.ExpressionStatement | ESTree.ReturnStatement)[]} statementNodes
+     * @returns {ESTree.Expression | null}
+     */
+    private toExpression (
+        statementNodes: (ESTree.ExpressionStatement | ESTree.ReturnStatement)[]
+    ): ESTree.Expression | null {
+        const unwrappedExpressions: ESTree.Expression[] = statementNodes
+            .map((statementNode: ESTree.ExpressionStatement | ESTree.ReturnStatement) => {
+                if (NodeGuards.isExpressionStatementNode(statementNode)) {
+                    return statementNode.expression;
+                }
+
+                if (NodeGuards.isReturnStatementNode(statementNode)) {
+                    return statementNode.argument;
+                }
+
+                return statementNode;
+            })
+            .filter((expressionNode: ESTree.Expression | null | undefined): expressionNode is ESTree.Expression =>
+                !!expressionNode
+            );
+
+        if (!unwrappedExpressions.length) {
+            return null;
+        }
+
+        return unwrappedExpressions.length === 1
+            ? unwrappedExpressions[0]
+            : NodeFactory.sequenceExpressionNode(unwrappedExpressions);
+    }
+}

+ 38 - 6
src/node/NodeFactory.ts

@@ -114,6 +114,26 @@ export class NodeFactory {
         };
     }
 
+    /**
+     * @param {ESTree.Expression} test
+     * @param {ESTree.Expression} consequent
+     * @param {ESTree.Expression} alternate
+     * @returns {ESTree.ConditionalExpression}
+     */
+    public static conditionalExpressionNode (
+        test: ESTree.Expression,
+        consequent: ESTree.Expression,
+        alternate: ESTree.Expression
+    ): ESTree.ConditionalExpression {
+        return {
+            type: NodeType.ConditionalExpression,
+            test,
+            consequent,
+            alternate,
+            metadata: { ignoredNode: false }
+        };
+    }
+
     /**
      * @param {Identifier} label
      * @returns {ContinueStatement}
@@ -195,15 +215,15 @@ export class NodeFactory {
     }
 
     /**
-     * @param {Expression} test
-     * @param {BlockStatement} consequent
-     * @param {BlockStatement} alternate
-     * @returns {IfStatement}
+     * @param {ESTree.Expression} test
+     * @param {ESTree.Statement} consequent
+     * @param {ESTree.Statement | null} alternate
+     * @returns {ESTree.IfStatement}
      */
     public static ifStatementNode (
         test: ESTree.Expression,
-        consequent: ESTree.BlockStatement,
-        alternate?: ESTree.BlockStatement
+        consequent: ESTree.Statement,
+        alternate?: ESTree.Statement | null
     ): ESTree.IfStatement {
         return {
             type: NodeType.IfStatement,
@@ -350,6 +370,18 @@ export class NodeFactory {
         };
     }
 
+    /**
+     * @param {ESTree.Expression[]} expressions
+     * @returns {ESTree.SequenceExpression}
+     */
+    public static sequenceExpressionNode (expressions: ESTree.Expression[]): ESTree.SequenceExpression {
+        return {
+            type: NodeType.SequenceExpression,
+            expressions,
+            metadata: { ignoredNode: false }
+        };
+    }
+
     /**
      * @param {Expression} discriminant
      * @param {SwitchCase[]} cases

+ 8 - 0
src/node/NodeGuards.ts

@@ -168,6 +168,14 @@ export class NodeGuards {
         return node.type === NodeType.Identifier;
     }
 
+    /**
+     * @param {Node} node
+     * @returns {boolean}
+     */
+    public static isIfStatementNode (node: ESTree.Node): node is ESTree.IfStatement {
+        return node.type === NodeType.IfStatement;
+    }
+
     /**
      * @param {Node} node
      * @returns {boolean}

+ 8 - 4
test/runtime-tests/JavaScriptObfuscatorRuntime.spec.ts

@@ -257,10 +257,14 @@ describe('JavaScriptObfuscator runtime eval', function () {
                         }
                     ).getObfuscatedCode();
 
-                    evaluationResult = eval(`
-                        ${getEnvironmentCode()}
-                        ${obfuscatedCode}
-                    `);
+                    try {
+                        evaluationResult = eval(`
+                            ${getEnvironmentCode()}
+                            ${obfuscatedCode}
+                        `);
+                    } catch (e) {
+                        throw new Error(`Evaluation error: ${e.message}. Code: ${obfuscatedCode}`);
+                    }
                 });
 
                 it('should obfuscate code without any runtime errors after obfuscation: Variant #4 webpack bootstrap', () => {

部分文件因为文件数量过多而无法显示