Przeglądaj źródła

Added more logic to simplify partially valid if statement branches

sanex3339 4 lat temu
rodzic
commit
937309a664

Plik diff jest za duży
+ 0 - 0
dist/index.browser.js


Plik diff jest za duży
+ 0 - 0
dist/index.cli.js


Plik diff jest za duży
+ 0 - 0
dist/index.js


+ 5 - 0
src/interfaces/node-transformers/minification-transformers/IIfStatementSimplifyData.ts

@@ -22,4 +22,9 @@ export interface IIfStatementSimplifyData {
      * @type {boolean}
      */
     hasReturnStatement: boolean;
+
+    /**
+     * @type {boolean}
+     */
+    hasSingleExpression: boolean;
 }

+ 60 - 17
src/node-transformers/minification-transformers/IfStatementSimplifyTransformer.ts

@@ -9,17 +9,26 @@ import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 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 { NodeGuards } from '../../node/NodeGuards';
 import { NodeFactory } from '../../node/NodeFactory';
+import { NodeUtils } from '../../node/NodeUtils';
 
 /**
  * Simplifies `IfStatement` node
  */
 @injectable()
 export class IfStatementSimplifyTransformer extends AbstractNodeTransformer {
+    /**
+     * @type {NodeTransformer[]}
+     */
+    public readonly runAfter: NodeTransformer[] = [
+        NodeTransformer.VariableDeclarationsMergeTransformer
+    ];
+
     /**
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
@@ -70,19 +79,23 @@ export class IfStatementSimplifyTransformer extends AbstractNodeTransformer {
             return ifStatementNode;
         }
 
-        // Variant #2: valid data for consequent expression only
+        let transformedNode: ESTree.Node;
+
         if (!ifStatementNode.alternate) {
-            return this.getConsequentNode(ifStatementNode, consequentSimplifyData);
-        }
+            // Variant #2: valid data for consequent expression only
+            transformedNode = this.getConsequentNode(ifStatementNode, consequentSimplifyData);
+        } else {
+            const alternateSimplifyData: IIfStatementSimplifyData | null = this.getIfStatementSimplifyData(ifStatementNode.alternate);
 
-        const alternateSimplifyData: IIfStatementSimplifyData | null = this.getIfStatementSimplifyData(ifStatementNode.alternate);
+            if (!alternateSimplifyData) {
+                return ifStatementNode;
+            }
 
-        if (!alternateSimplifyData) {
-            return ifStatementNode;
+            // Variant #3: valid data for consequent and alternate expressions
+            transformedNode = this.getConsequentAndAlternateNode(ifStatementNode, consequentSimplifyData, alternateSimplifyData);
         }
 
-        // Variant #3: valid data for consequent and alternate expressions
-        return this.getConsequentAndAlternateNode(ifStatementNode, consequentSimplifyData, alternateSimplifyData);
+        return NodeUtils.parentizeNode(transformedNode, parentNode);
     }
 
     /**
@@ -114,7 +127,7 @@ export class IfStatementSimplifyTransformer extends AbstractNodeTransformer {
         ) {
             return NodeFactory.ifStatementNode(
                 ifStatementNode.test,
-                this.getPartialBlockStatementNode(consequentSimplifyData)
+                this.getPartialIfStatementBranchNode(consequentSimplifyData)
             );
         }
 
@@ -186,8 +199,8 @@ export class IfStatementSimplifyTransformer extends AbstractNodeTransformer {
         ) {
             return NodeFactory.ifStatementNode(
                 ifStatementNode.test,
-                this.getPartialBlockStatementNode(consequentSimplifyData),
-                this.getPartialBlockStatementNode(alternateSimplifyData)
+                this.getPartialIfStatementBranchNode(consequentSimplifyData),
+                this.getPartialIfStatementBranchNode(alternateSimplifyData)
             );
         }
 
@@ -263,10 +276,19 @@ export class IfStatementSimplifyTransformer extends AbstractNodeTransformer {
     private getIfStatementSimplifyData (
         statementNode: ESTree.Statement | null | undefined
     ): IIfStatementSimplifyData | null {
-        if (!statementNode || !NodeGuards.isBlockStatementNode(statementNode)) {
+        if (!statementNode) {
             return null;
         }
 
+        if (!NodeGuards.isBlockStatementNode(statementNode)) {
+            return {
+                leadingStatements: [statementNode],
+                trailingStatement: null,
+                hasReturnStatement: false,
+                hasSingleExpression: false
+            };
+        }
+
         const {
             startIndex,
             unwrappedExpressions,
@@ -279,7 +301,8 @@ export class IfStatementSimplifyTransformer extends AbstractNodeTransformer {
             return {
                 leadingStatements,
                 trailingStatement: null,
-                hasReturnStatement
+                hasReturnStatement,
+                hasSingleExpression: false
             };
         }
 
@@ -299,7 +322,8 @@ export class IfStatementSimplifyTransformer extends AbstractNodeTransformer {
                 statement,
                 expression
             },
-            hasReturnStatement
+            hasReturnStatement,
+            hasSingleExpression
         };
     }
 
@@ -374,16 +398,35 @@ export class IfStatementSimplifyTransformer extends AbstractNodeTransformer {
      * @param {IIfStatementSimplifyData} ifStatementSimplifyData
      * @returns {ESTree.BlockStatement}
      */
-    private getPartialBlockStatementNode (ifStatementSimplifyData: IIfStatementSimplifyData): ESTree.Statement {
+    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
-        return NodeFactory.blockStatementNode([
-            ...ifStatementSimplifyData.leadingStatements,
+        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;
+
+    }
+
+    /**
+     * @param {ESTree.Statement} statement
+     * @returns {boolean}
+     */
+    private isProhibitedSingleStatementForIfStatementBranch (statement: ESTree.Statement): boolean {
+        // TODO: write tests
+        // function declaration is not allowed outside of block in `strict` mode
+        return NodeGuards.isFunctionDeclarationNode(statement)
+            // `IfStatement` may break the code
+            // TODO: catch this and write tests
+            || NodeGuards.isIfStatementNode(statement);
     }
 }

+ 8 - 10
test/dev/dev.ts

@@ -7,16 +7,14 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
 
     let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
         `
-           function foo () {
-                if (true) {
-                    console.log(3);
-                    console.log(2);
-            
-                    return 'abc';
-                }
-            }
-            
-            console.log(foo());
+               function foo () {
+                   if (true) {
+                      if (foo === 1) {
+                      }
+                   } else {
+                        function foo () {}
+                   }
+               }
         `,
         {
             ...NO_ADDITIONAL_NODES_PRESET,

+ 45 - 17
test/functional-tests/node-transformers/minification-transformers/if-statement-simplify-transformer/IfStatementSimplifyTransformer.spec.ts

@@ -352,9 +352,8 @@ describe('IfStatementSimplifyTransformer', () => {
             describe('No `ReturnStatement`', () => {
                 describe('Variant #1: single statement', () => {
                     const regExp: RegExp = new RegExp(
-                        'if *\\(!!\\[]\\) *{ *' +
-                            'const _0x([a-f0-9]){4,6} *= *baz\\(\\); *' +
-                        '}'
+                        'if *\\(!!\\[]\\) *' +
+                            'const _0x([a-f0-9]){4,6} *= *baz\\(\\);'
                     );
 
 
@@ -441,11 +440,10 @@ describe('IfStatementSimplifyTransformer', () => {
             describe('No `ReturnStatement`', () => {
                 describe('Variant #1: single statement', () => {
                     const regExp: RegExp = new RegExp(
-                        'if *\\(!!\\[]\\) *{ *' +
+                        'if *\\(!!\\[]\\) *' +
                             'const *_0x([a-f0-9]){4,6} *= *baz\\(\\); *' +
-                        '} *else *{ *' +
-                            'const *_0x([a-f0-9]){4,6} *= *hawk\\(\\); *' +
-                        '}'
+                        'else *' +
+                            'const *_0x([a-f0-9]){4,6} *= *hawk\\(\\);'
                     );
 
 
@@ -502,9 +500,9 @@ describe('IfStatementSimplifyTransformer', () => {
 
                 describe('Variant #3: mixed statements #1', () => {
                     const regExp: RegExp = new RegExp(
-                        'if *\\(!!\\[]\\) *{ *' +
+                        'if *\\(!!\\[]\\) *' +
                             'const *_0x([a-f0-9]){4,6} *= *baz\\(\\); *' +
-                        '} *else *{ *' +
+                        'else *{ *' +
                             'const *_0x([a-f0-9]){4,6} *= *hawk\\(\\); *' +
                             'eagle\\(\\), *dog\\(\\);' +
                         '}'
@@ -536,9 +534,8 @@ describe('IfStatementSimplifyTransformer', () => {
                             'const *_0x([a-f0-9]){4,6} *= *baz\\(\\), *' +
                                 '_0x([a-f0-9]){4,6} *= *hawk\\(\\); *' +
                             'eagle\\(\\), *pork\\(\\);' +
-                        '} *else *{ *' +
-                            'const *_0x([a-f0-9]){4,6} *= *cow\\(\\); *' +
-                        '}'
+                        '} *else *' +
+                            'const *_0x([a-f0-9]){4,6} *= *cow\\(\\);'
                     );
 
 
@@ -567,9 +564,8 @@ describe('IfStatementSimplifyTransformer', () => {
                     const regExp: RegExp = new RegExp(
                         'if *\\(!!\\[]\\) *' +
                             'return *bar\\(\\); *' +
-                        'else *{ *' +
-                            'const *_0x([a-f0-9]){4,6} *= *bark\\(\\); *' +
-                        '}'
+                        'else *' +
+                            'const *_0x([a-f0-9]){4,6} *= *bark\\(\\);'
                     );
 
 
@@ -628,9 +624,9 @@ describe('IfStatementSimplifyTransformer', () => {
             describe('With alternate `ReturnStatement`', () => {
                 describe('Variant #1: single statement', () => {
                     const regExp: RegExp = new RegExp(
-                        'if *\\(!!\\[]\\) *{ *' +
+                        'if *\\(!!\\[]\\) *' +
                             'const *_0x([a-f0-9]){4,6} *= *baz\\(\\); *' +
-                        '} *else *' +
+                        'else *' +
                             'return *bark\\(\\);'
                     );
 
@@ -723,4 +719,36 @@ describe('IfStatementSimplifyTransformer', () => {
             });
         });
     });
+
+    describe('Cases', () => {
+        describe('Variable declarations merge transformer integration', () => {
+            describe('Variant #1: three statements', () => {
+                const regExp: RegExp = new RegExp(
+                    'if *\\(!!\\[]\\) *' +
+                        'const _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,
+                            minify: true
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should simplify if statement', () => {
+                    assert.match(obfuscatedCode, regExp);
+                });
+            });
+        });
+    });
 });

+ 7 - 0
test/functional-tests/node-transformers/minification-transformers/if-statement-simplify-transformer/fixtures/variable-declarations-merge-transformer-integration-1.js

@@ -0,0 +1,7 @@
+function foo() {
+    if (true) {
+        const bar = function () {};
+        const baz = function () {};
+        const bark = function () {};
+    }
+}

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików