瀏覽代碼

Merge pull request #477 from javascript-obfuscator/transform-object-keys-return-object

Improved `transformObjectKeys` option to cover more object expressions
Timofey Kachalov 5 年之前
父節點
當前提交
84492b3420
共有 30 個文件被更改,包括 624 次插入101 次删除
  1. 4 0
      CHANGELOG.md
  2. 0 0
      dist/index.browser.js
  3. 0 0
      dist/index.cli.js
  4. 0 0
      dist/index.js
  5. 1 1
      package.json
  6. 1 0
      src/container/ServiceIdentifiers.ts
  7. 17 0
      src/container/modules/custom-nodes/CustomNodesModule.ts
  8. 5 0
      src/container/modules/node-transformers/ConvertingTransformersModule.ts
  9. 46 0
      src/custom-nodes/object-expression-keys-transformer-nodes/BasePropertiesExtractorObjectExpressionHostNode.ts
  10. 4 0
      src/enums/custom-nodes/ObjectExpressionKeysTransformerCustomNode.ts
  11. 1 0
      src/enums/node-transformers/converting-transformers/properties-extractors/PropertiesExtractor.ts
  12. 3 0
      src/node-transformers/control-flow-transformers/FunctionControlFlowTransformer.ts
  13. 41 3
      src/node-transformers/converting-transformers/ObjectExpressionKeysTransformer.ts
  14. 126 0
      src/node-transformers/converting-transformers/properties-extractors/BasePropertiesExtractor.ts
  15. 17 0
      src/node/NodeAppender.ts
  16. 5 0
      src/types/container/custom-nodes/TObjectExpressionKeysTransformerCustomNodeFactory.d.ts
  17. 11 5
      test/dev/dev.ts
  18. 255 92
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/ObjectExpressionKeysTransformer.spec.ts
  19. 0 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/integration-with-control-flow-flattening-1.js
  20. 7 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/integration-with-control-flow-flattening-2.js
  21. 14 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/nested-objects-3.js
  22. 6 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/object-expression-inside-array-expression.js
  23. 6 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/object-expression-inside-call-expression.js
  24. 6 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/return-statement.js
  25. 1 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/variable-declarator-with-object-call-3.js
  26. 0 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/variable-declarator-with-object-call-4.js
  27. 0 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/variable-declarator-with-object-call-5.js
  28. 23 0
      test/unit-tests/node/node-appender/NodeAppender.spec.ts
  29. 13 0
      test/unit-tests/node/node-appender/fixtures/insert-node-before-expected.js
  30. 11 0
      test/unit-tests/node/node-appender/fixtures/insert-node-before.js

+ 4 - 0
CHANGELOG.md

@@ -1,5 +1,9 @@
 Change Log
 
+v0.21.0
+---
+* Improved `transformObjectKeys` transformation to cover more cases
+
 v0.20.4
 ---
 * Fixed typings. Now string values correctly assignable to enum-like options

File diff suppressed because it is too large
+ 0 - 0
dist/index.browser.js


File diff suppressed because it is too large
+ 0 - 0
dist/index.cli.js


File diff suppressed because it is too large
+ 0 - 0
dist/index.js


+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "javascript-obfuscator",
-  "version": "0.20.4",
+  "version": "0.21.0",
   "description": "JavaScript obfuscator",
   "keywords": [
     "obfuscator",

+ 1 - 0
src/container/ServiceIdentifiers.ts

@@ -11,6 +11,7 @@ export enum ServiceIdentifiers {
     Factory__INodeTransformer = 'Factory<INodeTransformer[]>',
     Factory__IObfuscatedCode = 'Factory<IObfuscatedCode>',
     Factory__IObfuscatingReplacer = 'Factory<IObfuscatingReplacer>',
+    Factory__IObjectExpressionKeysTransformerCustomNode = 'Factory<IObjectExpressionKeysTransformerCustomNode>',
     Factory__IPropertiesExtractor = 'Factory<IPropertiesExtractor>',
     Factory__TControlFlowStorage = 'Factory<TControlFlowStorage>',
     IArrayUtils = 'IArrayUtils',

+ 17 - 0
src/container/modules/custom-nodes/CustomNodesModule.ts

@@ -9,6 +9,7 @@ import { ControlFlowCustomNode } from "../../../enums/custom-nodes/ControlFlowCu
 import { CustomNode } from '../../../enums/custom-nodes/CustomNode';
 import { CustomNodeGroup } from '../../../enums/custom-nodes/CustomNodeGroup';
 import { DeadCodeInjectionCustomNode } from '../../../enums/custom-nodes/DeadCodeInjectionCustomNode';
+import { ObjectExpressionKeysTransformerCustomNode } from '../../../enums/custom-nodes/ObjectExpressionKeysTransformerCustomNode';
 
 import { ConsoleOutputCustomNodeGroup } from '../../../custom-nodes/console-output-nodes/group/ConsoleOutputCustomNodeGroup';
 import { DebugProtectionCustomNodeGroup } from '../../../custom-nodes/debug-protection-nodes/group/DebugProtectionCustomNodeGroup';
@@ -16,6 +17,7 @@ import { DomainLockCustomNodeGroup } from '../../../custom-nodes/domain-lock-nod
 import { SelfDefendingCustomNodeGroup } from '../../../custom-nodes/self-defending-nodes/group/SelfDefendingCustomNodeGroup';
 import { StringArrayCustomNodeGroup } from '../../../custom-nodes/string-array-nodes/group/StringArrayCustomNodeGroup';
 
+import { BasePropertiesExtractorObjectExpressionHostNode } from '../../../custom-nodes/object-expression-keys-transformer-nodes/BasePropertiesExtractorObjectExpressionHostNode';
 import { BinaryExpressionFunctionNode } from '../../../custom-nodes/control-flow-flattening-nodes/BinaryExpressionFunctionNode';
 import { BlockStatementControlFlowFlatteningNode } from '../../../custom-nodes/control-flow-flattening-nodes/BlockStatementControlFlowFlatteningNode';
 import { BlockStatementDeadCodeInjectionNode } from '../../../custom-nodes/dead-code-injection-nodes/BlockStatementDeadCodeInjectionNode';
@@ -121,6 +123,11 @@ export const customNodesModule: interfaces.ContainerModule = new ContainerModule
         .toConstructor(BlockStatementDeadCodeInjectionNode)
         .whenTargetNamed(DeadCodeInjectionCustomNode.BlockStatementDeadCodeInjectionNode);
 
+    // object expression keys transformer nodes
+    bind<interfaces.Newable<ICustomNode>>(ServiceIdentifiers.Newable__ICustomNode)
+        .toConstructor(BasePropertiesExtractorObjectExpressionHostNode)
+        .whenTargetNamed(ObjectExpressionKeysTransformerCustomNode.BasePropertiesExtractorObjectExpressionHostNode);
+
     // node groups
     bind<ICustomNodeGroup>(ServiceIdentifiers.ICustomNodeGroup)
         .to(ConsoleOutputCustomNodeGroup)
@@ -167,6 +174,16 @@ export const customNodesModule: interfaces.ContainerModule = new ContainerModule
                 ServiceIdentifiers.IOptions
             ));
 
+    // object expression keys transformer customNode constructor factory
+    bind<ICustomNode>(ServiceIdentifiers.Factory__IObjectExpressionKeysTransformerCustomNode)
+        .toFactory<ICustomNode>(InversifyContainerFacade
+            .getConstructorFactory<ObjectExpressionKeysTransformerCustomNode, ICustomNode>(
+                ServiceIdentifiers.Newable__ICustomNode,
+                ServiceIdentifiers.Factory__IIdentifierNamesGenerator,
+                ServiceIdentifiers.IRandomGenerator,
+                ServiceIdentifiers.IOptions
+            ));
+
     // customNodeGroup factory
     bind<ICustomNodeGroup>(ServiceIdentifiers.Factory__ICustomNodeGroup)
         .toFactory<ICustomNodeGroup>(InversifyContainerFacade

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

@@ -9,6 +9,7 @@ import { NodeTransformer } from '../../../enums/node-transformers/NodeTransforme
 import { PropertiesExtractor } from '../../../enums/node-transformers/converting-transformers/properties-extractors/PropertiesExtractor';
 
 import { AssignmentExpressionPropertiesExtractor } from '../../../node-transformers/converting-transformers/properties-extractors/AssignmentExpressionPropertiesExtractor';
+import { BasePropertiesExtractor } from '../../../node-transformers/converting-transformers/properties-extractors/BasePropertiesExtractor';
 import { MemberExpressionTransformer } from '../../../node-transformers/converting-transformers/MemberExpressionTransformer';
 import { MethodDefinitionTransformer } from '../../../node-transformers/converting-transformers/MethodDefinitionTransformer';
 import { ObjectExpressionKeysTransformer } from '../../../node-transformers/converting-transformers/ObjectExpressionKeysTransformer';
@@ -48,6 +49,10 @@ export const convertingTransformersModule: interfaces.ContainerModule = new Cont
         .to(AssignmentExpressionPropertiesExtractor)
         .whenTargetNamed(PropertiesExtractor.AssignmentExpressionPropertiesExtractor);
 
+    bind<IPropertiesExtractor>(ServiceIdentifiers.IPropertiesExtractor)
+        .to(BasePropertiesExtractor)
+        .whenTargetNamed(PropertiesExtractor.BasePropertiesExtractor);
+
     bind<IPropertiesExtractor>(ServiceIdentifiers.IPropertiesExtractor)
         .to(VariableDeclaratorPropertiesExtractor)
         .whenTargetNamed(PropertiesExtractor.VariableDeclaratorPropertiesExtractor);

+ 46 - 0
src/custom-nodes/object-expression-keys-transformer-nodes/BasePropertiesExtractorObjectExpressionHostNode.ts

@@ -0,0 +1,46 @@
+import { inject, injectable, } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
+import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
+import { TStatement } from '../../types/node/TStatement';
+
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+
+import { AbstractCustomNode } from '../AbstractCustomNode';
+import { NodeFactory } from '../../node/NodeFactory';
+
+@injectable()
+export class BasePropertiesExtractorObjectExpressionHostNode extends AbstractCustomNode {
+    /**
+     * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {IRandomGenerator} randomGenerator
+     * @param {IOptions} options
+     */
+    constructor (
+        @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
+            identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        super(identifierNamesGeneratorFactory, randomGenerator, options);
+    }
+
+    public initialize (): void {}
+
+    /**
+     * @returns {TStatement[]}
+     */
+    protected getNodeStructure (): TStatement[] {
+        const structure: TStatement = NodeFactory.variableDeclarationNode([
+            NodeFactory.variableDeclaratorNode(
+                NodeFactory.identifierNode(
+                    this.identifierNamesGenerator.generate()
+                ),
+                NodeFactory.objectExpressionNode([])
+            )
+        ]);
+
+        return [structure];
+    }
+}

+ 4 - 0
src/enums/custom-nodes/ObjectExpressionKeysTransformerCustomNode.ts

@@ -0,0 +1,4 @@
+export enum ObjectExpressionKeysTransformerCustomNode {
+    BasePropertiesExtractorObjectExpressionHostNode =
+        'BasePropertiesExtractorObjectExpressionHostNode'
+}

+ 1 - 0
src/enums/node-transformers/converting-transformers/properties-extractors/PropertiesExtractor.ts

@@ -1,4 +1,5 @@
 export enum PropertiesExtractor {
     AssignmentExpressionPropertiesExtractor = 'AssignmentExpressionPropertiesExtractor',
+    BasePropertiesExtractor = 'BasePropertiesExtractor',
     VariableDeclaratorPropertiesExtractor = 'VariableDeclaratorPropertiesExtractor'
 }

+ 3 - 0
src/node-transformers/control-flow-transformers/FunctionControlFlowTransformer.ts

@@ -25,6 +25,7 @@ import { NodeAppender } from '../../node/NodeAppender';
 import { NodeGuards } from '../../node/NodeGuards';
 import { NodeMetadata } from '../../node/NodeMetadata';
 import { NodeStatementUtils } from '../../node/NodeStatementUtils';
+import { NodeUtils } from '../../node/NodeUtils';
 
 @injectable()
 export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
@@ -158,6 +159,8 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
         NodeAppender.prepend(hostNode, controlFlowStorageCustomNode.getNode());
         this.hostNodesWithControlFlowNode.add(hostNode);
 
+        NodeUtils.parentizeAst(functionNode);
+
         return functionNode;
     }
 

+ 41 - 3
src/node-transformers/converting-transformers/ObjectExpressionKeysTransformer.ts

@@ -53,6 +53,10 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
      * @returns {IVisitor | null}
      */
     public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        if (!this.options.transformObjectKeys) {
+            return null;
+        }
+
         if (transformationStage !== TransformationStage.Converting) {
             return null;
         }
@@ -60,12 +64,19 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
         return {
             enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
                 if (
-                    this.options.transformObjectKeys
-                    && parentNode
+                    parentNode
                     && NodeGuards.isObjectExpressionNode(node)
                 ) {
                     return this.transformNode(node, parentNode);
                 }
+            },
+            leave: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                if (
+                    parentNode
+                    && NodeGuards.isObjectExpressionNode(node)
+                ) {
+                    return this.transformNodeWithBaseExtractor(node, parentNode);
+                }
             }
         };
     }
@@ -82,7 +93,7 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
      *     object['foo'] = 1;
      *     object['bar'] = 2;
      *
-     * @param {MemberExpression} objectExpressionNode
+     * @param {ObjectExpression} objectExpressionNode
      * @param {Node} parentNode
      * @returns {NodeGuards}
      */
@@ -103,4 +114,31 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
 
         return propertiesExtractor.extract(objectExpressionNode, parentNode);
     }
+
+    /**
+     * replaces:
+     *     return {
+     *          foo: 1,
+     *          bar: 2
+     *     };
+     *
+     * on:
+     *     var object = {};
+     *     object['foo'] = 1;
+     *     object['bar'] = 2;
+     *     return object;
+     *
+     * @param {ObjectExpression} objectExpressionNode
+     * @param {Node} parentNode
+     * @returns {NodeGuards}
+     */
+    public transformNodeWithBaseExtractor (objectExpressionNode: ESTree.ObjectExpression, parentNode: ESTree.Node): ESTree.Node {
+        if (!objectExpressionNode.properties.length) {
+            return objectExpressionNode;
+        }
+
+        const propertiesExtractor: IPropertiesExtractor = this.propertiesExtractorFactory(PropertiesExtractor.BasePropertiesExtractor);
+
+        return propertiesExtractor.extract(objectExpressionNode, parentNode);
+    }
 }

+ 126 - 0
src/node-transformers/converting-transformers/properties-extractors/BasePropertiesExtractor.ts

@@ -0,0 +1,126 @@
+import { inject, injectable } from 'inversify';
+import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
+
+import * as ESTree from 'estree';
+
+import { TNodeWithStatements } from '../../../types/node/TNodeWithStatements';
+import { TObjectExpressionKeysTransformerCustomNodeFactory } from '../../../types/container/custom-nodes/TObjectExpressionKeysTransformerCustomNodeFactory';
+import { TStatement } from '../../../types/node/TStatement';
+
+import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../../interfaces/options/IOptions';
+import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
+
+import { ObjectExpressionKeysTransformerCustomNode } from '../../../enums/custom-nodes/ObjectExpressionKeysTransformerCustomNode';
+
+import { AbstractPropertiesExtractor } from './AbstractPropertiesExtractor';
+import { NodeAppender } from '../../../node/NodeAppender';
+import { NodeGuards } from '../../../node/NodeGuards';
+
+@injectable()
+export class BasePropertiesExtractor extends AbstractPropertiesExtractor {
+    /**
+     * @type {TObjectExpressionKeysTransformerCustomNodeFactory}
+     */
+    private readonly objectExpressionKeysTransformerCustomNodeFactory: TObjectExpressionKeysTransformerCustomNodeFactory;
+
+    /**
+     * @param {IRandomGenerator} randomGenerator
+     * @param {IOptions} options
+     * @param {TObjectExpressionKeysTransformerCustomNodeFactory} objectExpressionKeysTransformerCustomNodeFactory
+     */
+    constructor (
+        @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions,
+        @inject(ServiceIdentifiers.Factory__IObjectExpressionKeysTransformerCustomNode)
+            objectExpressionKeysTransformerCustomNodeFactory: TObjectExpressionKeysTransformerCustomNodeFactory,
+    ) {
+        super(randomGenerator, options);
+
+        this.objectExpressionKeysTransformerCustomNodeFactory = objectExpressionKeysTransformerCustomNodeFactory;
+    }
+
+    /**
+     * @param {ObjectExpression} objectExpressionNode
+     * @param {Node} parentNode
+     * @returns {Node}
+     */
+    public extract (
+        objectExpressionNode: ESTree.ObjectExpression,
+        parentNode: ESTree.Node
+    ): ESTree.Node {
+        return this.transformObjectExpressionNode(
+            objectExpressionNode,
+            parentNode
+        );
+    }
+
+    /**
+     * @param {ObjectExpression} objectExpressionNode
+     * @param {Node} parentNode
+     * @returns {Node}
+     */
+    protected transformObjectExpressionNode (
+        objectExpressionNode: ESTree.ObjectExpression,
+        parentNode: ESTree.Node
+    ): ESTree.Node {
+        const newObjectExpressionHostNode: ESTree.VariableDeclaration = this.getObjectExpressionHostNode();
+        const newObjectExpressionIdentifier: ESTree.Identifier = this.getObjectExpressionIdentifierNode(newObjectExpressionHostNode);
+
+        const properties: ESTree.Property[] = objectExpressionNode.properties;
+
+        const [expressionStatements, removablePropertyIds]: [ESTree.ExpressionStatement[], number[]] = this
+            .extractPropertiesToExpressionStatements(properties, newObjectExpressionIdentifier);
+        const statementsToInsert: TStatement[] = [
+            newObjectExpressionHostNode,
+            ...expressionStatements
+        ];
+
+        const hostStatement: ESTree.Statement = this.getHostStatement(objectExpressionNode);
+        const hostNodeWithStatements: TNodeWithStatements = this.getHostNodeWithStatements(
+            objectExpressionNode,
+            hostStatement
+        );
+
+        this.filterExtractedObjectExpressionProperties(objectExpressionNode, removablePropertyIds);
+        NodeAppender.insertBefore(hostNodeWithStatements, statementsToInsert, hostStatement);
+
+        return newObjectExpressionIdentifier;
+    }
+
+    /**
+     * @returns {VariableDeclaration}
+     */
+    private getObjectExpressionHostNode (): ESTree.VariableDeclaration {
+        const objectExpressionHostCustomNode: ICustomNode = this.objectExpressionKeysTransformerCustomNodeFactory(
+            ObjectExpressionKeysTransformerCustomNode.BasePropertiesExtractorObjectExpressionHostNode
+        );
+
+        objectExpressionHostCustomNode.initialize();
+
+        const statementNode: TStatement = objectExpressionHostCustomNode.getNode()[0];
+
+        if (
+            !statementNode
+            || !NodeGuards.isVariableDeclarationNode(statementNode)
+        ) {
+            throw new Error(`\`objectExpressionHostCustomNode.getNode()[0]\` should returns array with \`VariableDeclaration\` node`);
+        }
+
+        return statementNode;
+    }
+
+    /**
+     * @param {VariableDeclaration} objectExpressionHostNode
+     * @returns {Identifier}
+     */
+    private getObjectExpressionIdentifierNode (objectExpressionHostNode: ESTree.VariableDeclaration): ESTree.Identifier {
+        const newObjectExpressionIdentifierNode: ESTree.Pattern = objectExpressionHostNode.declarations[0].id;
+
+        if (!NodeGuards.isIdentifierNode(newObjectExpressionIdentifierNode)) {
+            throw new Error(`\`objectExpressionHostNode\` should contain \`VariableDeclarator\` node with \`Identifier\` id property`);
+        }
+
+        return newObjectExpressionIdentifierNode;
+    }
+}

+ 17 - 0
src/node/NodeAppender.ts

@@ -82,6 +82,23 @@ export class NodeAppender {
         }
     }
 
+    /**
+     * @param {TNodeWithStatements} nodeWithStatements
+     * @param {TStatement[]} statements
+     * @param {Node} target
+     */
+    public static insertBefore (
+        nodeWithStatements: TNodeWithStatements,
+        statements: TStatement[],
+        target: ESTree.Statement
+    ): void {
+        const indexInScopeStatement: number = NodeAppender
+            .getScopeStatements(nodeWithStatements)
+            .indexOf(target);
+
+        NodeAppender.insertAtIndex(nodeWithStatements, statements, indexInScopeStatement);
+    }
+
     /**
      * @param {TNodeWithStatements} nodeWithStatements
      * @param {TStatement[]} statements

+ 5 - 0
src/types/container/custom-nodes/TObjectExpressionKeysTransformerCustomNodeFactory.d.ts

@@ -0,0 +1,5 @@
+import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
+
+import { ObjectExpressionKeysTransformerCustomNode } from '../../../enums/custom-nodes/ObjectExpressionKeysTransformerCustomNode';
+
+export type TObjectExpressionKeysTransformerCustomNodeFactory = (objectExpressionKeysTransformerNodeName: ObjectExpressionKeysTransformerCustomNode) => ICustomNode;

+ 11 - 5
test/dev/dev.ts

@@ -7,15 +7,21 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
 
     let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
         `
-            async function testFunc(params) {
-                for await (let param of params) {
-                  
+            function foo () {
+                function foo () {
+                    return {
+                        bar: {
+                            foo: 1
+                        }
+                    };
                 }
             }
-            testFunc(['foo']);
         `,
         {
-            ...NO_ADDITIONAL_NODES_PRESET
+            ...NO_ADDITIONAL_NODES_PRESET,
+            transformObjectKeys: true,
+            controlFlowFlattening: true,
+            controlFlowFlatteningThreshold: 1
         }
     ).getObfuscatedCode();
 

+ 255 - 92
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/ObjectExpressionKeysTransformer.spec.ts

@@ -37,7 +37,119 @@ describe('ObjectExpressionKeysTransformer', () => {
             });
         });
 
-        describe('Variant #2: nested objects #1', () => {
+        describe('Variant #2: variable declaration without initialization', () => {
+            const match: string = `` +
+                `var *${variableMatch};` +
+                `${variableMatch} *= *{};` +
+                `${variableMatch}\\['foo'] *= *'bar';` +
+                `${variableMatch}\\['baz'] *= *'bark';` +
+            ``;
+            const regExp: RegExp = new RegExp(match);
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/variable-declaration-without-initialization.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        transformObjectKeys: true
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should transform object keys', () => {
+                assert.match(obfuscatedCode,  regExp);
+            });
+        });
+
+        describe('Variant #3: return statement', () => {
+            const match: string = `` +
+                `var *${variableMatch} *= *{};` +
+                `${variableMatch}\\['foo'] *= *'bar';` +
+                `${variableMatch}\\['baz'] *= *'bark';` +
+                `return *${variableMatch};` +
+            ``;
+            const regExp: RegExp = new RegExp(match);
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/return-statement.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        transformObjectKeys: true
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('shouldn transform object keys', () => {
+                assert.match(obfuscatedCode,  regExp);
+            });
+        });
+
+        describe('Variant #4: object expression inside array expression', () => {
+            const match: string = `` +
+                `var *${variableMatch} *= *{};` +
+                `${variableMatch}\\['foo'] *= *'bar';` +
+                `${variableMatch}\\['baz'] *= *'bark';` +
+                `var *${variableMatch} *= *\\[${variableMatch}];` +
+                ``;
+            const regExp: RegExp = new RegExp(match);
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/object-expression-inside-array-expression.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        transformObjectKeys: true
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('shouldn transform object keys', () => {
+                assert.match(obfuscatedCode,  regExp);
+            });
+        });
+
+        describe('Variant #5: object expression inside call expression', () => {
+            const match: string = `` +
+                `var *${variableMatch} *= *{};` +
+                `${variableMatch}\\['foo'] *= *'bar';` +
+                `${variableMatch}\\['baz'] *= *'bark';` +
+                `console\\['log']\\(${variableMatch}\\);` +
+            ``;
+            const regExp: RegExp = new RegExp(match);
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/object-expression-inside-call-expression.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        transformObjectKeys: true
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('shouldn transform object keys', () => {
+                assert.match(obfuscatedCode,  regExp);
+            });
+        });
+
+        describe('Variant #6: nested objects #1', () => {
             const match: string = `` +
                 `var *${variableMatch} *= *{};` +
                 `${variableMatch}\\['foo'] *= *'bar';` +
@@ -66,7 +178,7 @@ describe('ObjectExpressionKeysTransformer', () => {
             });
         });
 
-        describe('Variant #3: nested objects #2', () => {
+        describe('Variant #7: nested objects #2', () => {
             const match: string = `` +
                 `var *${variableMatch} *= *{};` +
                 `${variableMatch}\\['foo'] *= *'bar';` +
@@ -98,7 +210,42 @@ describe('ObjectExpressionKeysTransformer', () => {
             });
         });
 
-        describe('Variant #4: correct integration with control flow flattening object', () => {
+        describe('Variant #8: nested objects #3', () => {
+            const match: string = `` +
+                `var *${variableMatch} *= *{};` +
+                `${variableMatch}\\['hawk'] *= *'geek';` +
+                `var *${variableMatch} *= *{};` +
+                `${variableMatch}\\['baz'] *= *'bark';` +
+                `${variableMatch}\\['inner1'] *= *${variableMatch};` +
+                `${variableMatch}\\['cow'] *= *'bear';` +
+                `var *${variableMatch} *= *{};` +
+                `${variableMatch}\\['foo'] *= *'bar';` +
+                `${variableMatch}\\['inner'] *= *${variableMatch};` +
+                `${variableMatch}\\['ball'] *= *'door';` +
+                `return ${variableMatch};` +
+            ``;
+            const regExp: RegExp = new RegExp(match);
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/nested-objects-3.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        transformObjectKeys: true
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should correctly transform object keys', () => {
+                assert.match(obfuscatedCode,  regExp);
+            });
+        });
+
+        describe('Variant #9: correct integration with control flow flattening object #1', () => {
             const match: string = `` +
                 `var *${variableMatch} *= *{};` +
                 `${variableMatch}\\['\\w{5}'] *= *function *\\(${variableMatch}, *${variableMatch}\\) *{` +
@@ -111,7 +258,7 @@ describe('ObjectExpressionKeysTransformer', () => {
             let obfuscatedCode: string;
 
             before(() => {
-                const code: string = readFileAsString(__dirname + '/fixtures/integration-with-control-flow-flattening.js');
+                const code: string = readFileAsString(__dirname + '/fixtures/integration-with-control-flow-flattening-1.js');
 
                 obfuscatedCode = JavaScriptObfuscator.obfuscate(
                     code,
@@ -129,19 +276,112 @@ describe('ObjectExpressionKeysTransformer', () => {
             });
         });
 
-        describe('Variant #5: variable declaration without initialization', () => {
+        describe('Variant #10: correct integration with control flow flattening object #2', () => {
             const match: string = `` +
-                `var *${variableMatch};` +
-                `${variableMatch} *= *{};` +
+                `var *${variableMatch} *= *{};` +
+                `${variableMatch}\\['\\w{5}'] *= *function *\\(${variableMatch}, *${variableMatch}\\) *{` +
+                    `return *${variableMatch} *\\+ *${variableMatch};` +
+                `};` +
+                `var *${variableMatch} *= *{};` +
+                `${variableMatch}\\['foo'] *= *0x1;` +
+                `var *${variableMatch} *= *{};` +
+                `${variableMatch}\\['bar'] *= *0x2;` +
+                `var *${variableMatch} *= *${variableMatch}\\['\\w{5}']\\(${variableMatch}\\['foo'], *${variableMatch}\\['bar']\\);` +
+            ``;
+            const regExp: RegExp = new RegExp(match);
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/integration-with-control-flow-flattening-2.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        controlFlowFlattening: true,
+                        controlFlowFlatteningThreshold: 1,
+                        transformObjectKeys: true
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should correctly transform object keys', () => {
+                assert.match(obfuscatedCode,  regExp);
+            });
+        });
+
+        describe('Variant #11: variable declarator object call inside other variable declarator', () => {
+            describe('Variant #1', () => {
+                const match: string = `` +
+                    `var *${variableMatch} *= *{};` +
+                    `${variableMatch}\\['foo'] *= *'foo';` +
+                    `const *${variableMatch} *= *${variableMatch}, *` +
+                    `${variableMatch} *= *${variableMatch}\\['foo'];` +
+                ``;
+                const regExp: RegExp = new RegExp(match);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/variable-declarator-with-object-call-4.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            transformObjectKeys: true
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should transform object keys', () => {
+                    assert.match(obfuscatedCode,  regExp);
+                });
+            });
+
+            describe('Variant #2', () => {
+                const match: string = `` +
+                    `var *${variableMatch} *= *{};` +
+                    `${variableMatch}\\['foo'] *= *'foo';` +
+                    `const *${variableMatch} *= *${variableMatch}, *` +
+                    `${variableMatch} *= *\\[${variableMatch}\\['foo']];` +
+                ``;
+                const regExp: RegExp = new RegExp(match);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/variable-declarator-with-object-call-5.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            transformObjectKeys: true
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should transform object keys', () => {
+                    assert.match(obfuscatedCode,  regExp);
+                });
+            });
+        });
+
+        describe('Variant #12: assignment expression and member expression', () => {
+            const match: string = `` +
+                `var ${variableMatch}; *` +
+                `var ${variableMatch} *= *{}; *` +
                 `${variableMatch}\\['foo'] *= *'bar';` +
-                `${variableMatch}\\['baz'] *= *'bark';` +
+                `\\(${variableMatch} *= *${variableMatch}\\)\\['baz'] *= *${variableMatch}\\['foo'];` +
             ``;
             const regExp: RegExp = new RegExp(match);
 
             let obfuscatedCode: string;
 
             before(() => {
-                const code: string = readFileAsString(__dirname + '/fixtures/variable-declaration-without-initialization.js');
+                const code: string = readFileAsString(__dirname + '/fixtures/assignment-expression-and-member-expression.js');
 
                 obfuscatedCode = JavaScriptObfuscator.obfuscate(
                     code,
@@ -152,7 +392,7 @@ describe('ObjectExpressionKeysTransformer', () => {
                 ).getObfuscatedCode();
             });
 
-            it('shouldn\'t transform object keys', () => {
+            it('should transform object keys', () => {
                 assert.match(obfuscatedCode,  regExp);
             });
         });
@@ -388,10 +628,13 @@ describe('ObjectExpressionKeysTransformer', () => {
 
             describe('Variant #3: two objects', () => {
                 const match: string = `` +
+                    `var *${variableMatch} *= *{};` +
+                    `${variableMatch}\\['bar'] *= *'bar';` +
                     `const *${variableMatch} *= *{}, *` +
-                        `${variableMatch} *= *{'bar': *'bar'}, *` +
+                        `${variableMatch} *= *${variableMatch}, *` +
                         `${variableMatch} *= *${variableMatch}\\['bar']; *` +
                     `${variableMatch}\\['foo'] *= *'foo';` +
+                    `console\\['log']\\(${variableMatch}\\);` +
                 ``;
                 const regExp: RegExp = new RegExp(match);
 
@@ -409,7 +652,7 @@ describe('ObjectExpressionKeysTransformer', () => {
                     ).getObfuscatedCode();
                 });
 
-                it('should correctly transform first object keys and ignore second object keys', () => {
+                it('should correctly transform objects keys', () => {
                     assert.match(obfuscatedCode,  regExp);
                 });
             });
@@ -465,85 +708,5 @@ describe('ObjectExpressionKeysTransformer', () => {
                 assert.match(obfuscatedCode,  regExp);
             });
         });
-
-        describe('Variant #3: variable declarator object call inside other variable declarator', () => {
-            describe('Variant #1', () => {
-                const match: string = `` +
-                    `const *${variableMatch} *= *{'foo': *'foo'}, *` +
-                        `${variableMatch} *= *${variableMatch}\\['foo'];` +
-                ``;
-                const regExp: RegExp = new RegExp(match);
-
-                let obfuscatedCode: string;
-
-                before(() => {
-                    const code: string = readFileAsString(__dirname + '/fixtures/variable-declarator-with-object-call-ignore-1.js');
-
-                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
-                        code,
-                        {
-                            ...NO_ADDITIONAL_NODES_PRESET,
-                            transformObjectKeys: true
-                        }
-                    ).getObfuscatedCode();
-                });
-
-                it('shouldn\'t transform object keys', () => {
-                    assert.match(obfuscatedCode,  regExp);
-                });
-            });
-
-            describe('Variant #2', () => {
-                const match: string = `` +
-                    `const *${variableMatch} *= *{'foo': *'foo'}, *` +
-                        `${variableMatch} *= *\\[${variableMatch}\\['foo']];` +
-                ``;
-                const regExp: RegExp = new RegExp(match);
-
-                let obfuscatedCode: string;
-
-                before(() => {
-                    const code: string = readFileAsString(__dirname + '/fixtures/variable-declarator-with-object-call-ignore-2.js');
-
-                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
-                        code,
-                        {
-                            ...NO_ADDITIONAL_NODES_PRESET,
-                            transformObjectKeys: true
-                        }
-                    ).getObfuscatedCode();
-                });
-
-                it('shouldn\'t transform object keys', () => {
-                    assert.match(obfuscatedCode,  regExp);
-                });
-            });
-        });
-
-        describe('Variant #4: assignment expression and member expression', () => {
-            const match: string = `` +
-                `var ${variableMatch}; *` +
-                `\\(${variableMatch} *= *{'foo': *'bar'}\\)\\['baz'] *= *${variableMatch}\\['foo'];` +
-            ``;
-            const regExp: RegExp = new RegExp(match);
-
-            let obfuscatedCode: string;
-
-            before(() => {
-                const code: string = readFileAsString(__dirname + '/fixtures/assignment-expression-and-member-expression.js');
-
-                obfuscatedCode = JavaScriptObfuscator.obfuscate(
-                    code,
-                    {
-                        ...NO_ADDITIONAL_NODES_PRESET,
-                        transformObjectKeys: true
-                    }
-                ).getObfuscatedCode();
-            });
-
-            it('shouldn\'t transform object keys', () => {
-                assert.match(obfuscatedCode,  regExp);
-            });
-        });
     });
 });

+ 0 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/integration-with-control-flow-flattening.js → test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/integration-with-control-flow-flattening-1.js


+ 7 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/integration-with-control-flow-flattening-2.js

@@ -0,0 +1,7 @@
+(function () {
+    var variable = {
+        foo: 1
+    }.foo + {
+        bar: 2
+    }.bar;
+})();

+ 14 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/nested-objects-3.js

@@ -0,0 +1,14 @@
+(function(){
+    return {
+        foo: 'bar',
+        inner: {
+            baz: 'bark',
+            inner1: {
+                hawk: 'geek'
+            },
+            cow: 'bear',
+
+        },
+        ball: 'door',
+    };
+})();

+ 6 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/object-expression-inside-array-expression.js

@@ -0,0 +1,6 @@
+function foo () {
+    var bar = [{
+        foo: 'bar',
+        baz: 'bark'
+    }];
+}

+ 6 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/object-expression-inside-call-expression.js

@@ -0,0 +1,6 @@
+function foo () {
+    console.log({
+        foo: 'bar',
+        baz: 'bark'
+    });
+}

+ 6 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/return-statement.js

@@ -0,0 +1,6 @@
+function foo () {
+    return {
+        foo: 'bar',
+        baz: 'bark'
+    };
+}

+ 1 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/variable-declarator-with-object-call-3.js

@@ -2,4 +2,5 @@
     const object1 = {foo: 'foo'},
         object2 = {bar: 'bar'},
         variable = object2.bar;
+    console.log(variable);
 })();

+ 0 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/variable-declarator-with-object-call-ignore-1.js → test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/variable-declarator-with-object-call-4.js


+ 0 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/variable-declarator-with-object-call-ignore-2.js → test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/variable-declarator-with-object-call-5.js


+ 23 - 0
test/unit-tests/node/node-appender/NodeAppender.spec.ts

@@ -167,6 +167,29 @@ describe('NodeAppender', () => {
         });
     });
 
+    describe('insertBefore', () => {
+        let astTree: ESTree.Program,
+            expectedAstTree: ESTree.Program,
+            node: TStatement[],
+            targetStatement: ESTree.Statement;
+
+        before(() => {
+            node = convertCodeToStructure('/fixtures/simple-input.js');
+            astTree = convertCodeToAst('/fixtures/insert-node-before.js');
+            expectedAstTree = convertCodeToAst('/fixtures/insert-node-before-expected.js');
+            targetStatement = <ESTree.Statement>astTree.body[1];
+
+            astTree = NodeUtils.parentizeAst(astTree);
+            expectedAstTree = NodeUtils.parentizeAst(expectedAstTree);
+
+            NodeAppender.insertBefore(astTree, node, targetStatement);
+        });
+
+        it('should insert given node in `BlockStatement` node body before target statement', () => {
+            assert.deepEqual(astTree, expectedAstTree);
+        });
+    });
+
     describe('insertAfter', () => {
         let astTree: ESTree.Program,
             expectedAstTree: ESTree.Program,

+ 13 - 0
test/unit-tests/node/node-appender/fixtures/insert-node-before-expected.js

@@ -0,0 +1,13 @@
+var func1 = function () {
+    return true;
+};
+
+var test = 1;
+
+var func2 = function () {
+    return false;
+};
+
+var func3 = function () {
+    return 'string';
+};

+ 11 - 0
test/unit-tests/node/node-appender/fixtures/insert-node-before.js

@@ -0,0 +1,11 @@
+var func1 = function () {
+    return true;
+};
+
+var func2 = function () {
+    return false;
+};
+
+var func3 = function () {
+    return 'string';
+};

Some files were not shown because too many files changed in this diff