Browse Source

Object expression keys transformer rework

sanex3339 5 năm trước cách đây
mục cha
commit
007af5f52b
16 tập tin đã thay đổi với 466 bổ sung813 xóa
  1. 0 0
      dist/index.cli.js
  2. 0 0
      dist/index.js
  3. 4 14
      src/container/modules/node-transformers/ConvertingTransformersModule.ts
  4. 11 2
      src/custom-nodes/object-expression-keys-transformer-nodes/BasePropertiesExtractorObjectExpressionHostNode.ts
  5. 1 3
      src/enums/node-transformers/converting-transformers/properties-extractors/PropertiesExtractor.ts
  6. 2 2
      src/interfaces/node-transformers/converting-transformers/properties-extractors/IPropertiesExtractor.ts
  7. 145 98
      src/node-transformers/converting-transformers/ObjectExpressionKeysTransformer.ts
  8. 196 0
      src/node-transformers/converting-transformers/object-expression-extractors/BasePropertiesExtractor.ts
  9. 55 42
      src/node-transformers/converting-transformers/object-expression-extractors/ObjectExpressionToVariableDeclarationExtractor.ts
  10. 0 287
      src/node-transformers/converting-transformers/properties-extractors/AbstractPropertiesExtractor.ts
  11. 0 83
      src/node-transformers/converting-transformers/properties-extractors/AssignmentExpressionPropertiesExtractor.ts
  12. 0 123
      src/node-transformers/converting-transformers/properties-extractors/AssignmentPatternPropertiesExtractor.ts
  13. 0 120
      src/node-transformers/converting-transformers/properties-extractors/VariableDeclaratorPropertiesExtractor.ts
  14. 5 3
      src/types/node-transformers/TPropertiesExtractorResult.ts
  15. 1 4
      test/dev/dev.ts
  16. 46 32
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/ObjectExpressionKeysTransformer.spec.ts

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/index.cli.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/index.js


+ 4 - 14
src/container/modules/node-transformers/ConvertingTransformersModule.ts

@@ -8,16 +8,14 @@ import { IPropertiesExtractor } from '../../../interfaces/node-transformers/conv
 import { NodeTransformer } from '../../../enums/node-transformers/NodeTransformer';
 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 { ObjectExpressionToVariableDeclarationExtractor } from '../../../node-transformers/converting-transformers/object-expression-extractors/ObjectExpressionToVariableDeclarationExtractor';
 import { MemberExpressionTransformer } from '../../../node-transformers/converting-transformers/MemberExpressionTransformer';
 import { MethodDefinitionTransformer } from '../../../node-transformers/converting-transformers/MethodDefinitionTransformer';
 import { ObjectExpressionKeysTransformer } from '../../../node-transformers/converting-transformers/ObjectExpressionKeysTransformer';
 import { ObjectExpressionTransformer } from '../../../node-transformers/converting-transformers/ObjectExpressionTransformer';
 import { SplitStringTransformer } from '../../../node-transformers/converting-transformers/SplitStringTransformer';
 import { TemplateLiteralTransformer } from '../../../node-transformers/converting-transformers/TemplateLiteralTransformer';
-import { VariableDeclaratorPropertiesExtractor } from '../../../node-transformers/converting-transformers/properties-extractors/VariableDeclaratorPropertiesExtractor';
-import { AssignmentPatternPropertiesExtractor } from '../../../node-transformers/converting-transformers/properties-extractors/AssignmentPatternPropertiesExtractor';
+import { BasePropertiesExtractor } from '../../../node-transformers/converting-transformers/object-expression-extractors/BasePropertiesExtractor';
 
 export const convertingTransformersModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
     // converting transformers
@@ -47,21 +45,13 @@ export const convertingTransformersModule: interfaces.ContainerModule = new Cont
 
     // properties extractors
     bind<IPropertiesExtractor>(ServiceIdentifiers.IPropertiesExtractor)
-        .to(AssignmentExpressionPropertiesExtractor)
-        .whenTargetNamed(PropertiesExtractor.AssignmentExpressionPropertiesExtractor);
-
-    bind<IPropertiesExtractor>(ServiceIdentifiers.IPropertiesExtractor)
-        .to(AssignmentPatternPropertiesExtractor)
-        .whenTargetNamed(PropertiesExtractor.AssignmentPatternPropertiesExtractor);
+        .to(ObjectExpressionToVariableDeclarationExtractor)
+        .whenTargetNamed(PropertiesExtractor.ObjectExpressionToVariableDeclarationExtractor);
 
     bind<IPropertiesExtractor>(ServiceIdentifiers.IPropertiesExtractor)
         .to(BasePropertiesExtractor)
         .whenTargetNamed(PropertiesExtractor.BasePropertiesExtractor);
 
-    bind<IPropertiesExtractor>(ServiceIdentifiers.IPropertiesExtractor)
-        .to(VariableDeclaratorPropertiesExtractor)
-        .whenTargetNamed(PropertiesExtractor.VariableDeclaratorPropertiesExtractor);
-
     // properties extractor factory
     bind<IPropertiesExtractor>(ServiceIdentifiers.Factory__IPropertiesExtractor)
         .toFactory<IPropertiesExtractor>(InversifyContainerFacade

+ 11 - 2
src/custom-nodes/object-expression-keys-transformer-nodes/BasePropertiesExtractorObjectExpressionHostNode.ts

@@ -1,6 +1,8 @@
 import { inject, injectable, } from 'inversify';
 import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
+import * as ESTree from 'estree';
+
 import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
 import { TStatement } from '../../types/node/TStatement';
 
@@ -13,6 +15,11 @@ import { NodeFactory } from '../../node/NodeFactory';
 
 @injectable()
 export class BasePropertiesExtractorObjectExpressionHostNode extends AbstractCustomNode {
+    /**
+     * @ type {Property}
+     */
+    private properties!: ESTree.Property[];
+
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
      * @param {ICustomNodeFormatter} customNodeFormatter
@@ -29,7 +36,9 @@ export class BasePropertiesExtractorObjectExpressionHostNode extends AbstractCus
         super(identifierNamesGeneratorFactory, customNodeFormatter, randomGenerator, options);
     }
 
-    public initialize (): void {}
+    public initialize (properties: ESTree.Property[]): void {
+        this.properties = properties;
+    }
 
     /**
      * @param {string} nodeTemplate
@@ -42,7 +51,7 @@ export class BasePropertiesExtractorObjectExpressionHostNode extends AbstractCus
                     NodeFactory.identifierNode(
                         this.identifierNamesGenerator.generate()
                     ),
-                    NodeFactory.objectExpressionNode([])
+                    NodeFactory.objectExpressionNode(this.properties)
                 )
             ],
             'const'

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

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

+ 2 - 2
src/interfaces/node-transformers/converting-transformers/properties-extractors/IPropertiesExtractor.ts

@@ -5,11 +5,11 @@ import { TPropertiesExtractorResult } from '../../../../types/node-transformers/
 export interface IPropertiesExtractor {
     /**
      * @param {ObjectExpression} objectExpressionNode
-     * @param {Node} hostNode
+     * @param {Statement} hostStatement
      * @returns {TPropertiesExtractorResult}
      */
     extract (
         objectExpressionNode: ESTree.ObjectExpression,
-        hostNode: ESTree.Node
+        hostStatement: ESTree.Statement
     ): TPropertiesExtractorResult;
 }

+ 145 - 98
src/node-transformers/converting-transformers/ObjectExpressionKeysTransformer.ts

@@ -1,40 +1,24 @@
 import { inject, injectable } from 'inversify';
 import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
+import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
 import { TPropertiesExtractorFactory } from '../../types/container/node-transformers/TPropertiesExtractorFactory';
-import { TPropertiesExtractorResult } from '../../types/node-transformers/TPropertiesExtractorResult';
 
 import { IOptions } from '../../interfaces/options/IOptions';
-import { IPropertiesExtractor } from '../../interfaces/node-transformers/converting-transformers/properties-extractors/IPropertiesExtractor';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
-import { NodeType } from '../../enums/node/NodeType';
-import { PropertiesExtractor } from '../../enums/node-transformers/converting-transformers/properties-extractors/PropertiesExtractor';
-import { PropertiesExtractorFlag } from '../../enums/node-transformers/converting-transformers/properties-extractors/PropertiesExtractorResult';
 import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeGuards } from '../../node/NodeGuards';
+import { NodeStatementUtils } from '../../node/NodeStatementUtils';
+import { PropertiesExtractor } from '../../enums/node-transformers/converting-transformers/properties-extractors/PropertiesExtractor';
 
 @injectable()
 export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
-    /**
-     * @type {Set<ESTree.Node>}
-     */
-    private static readonly objectExpressionNodesToSkipSet: Set<ESTree.Node> = new Set();
-
-    /**
-     * @type {Map<string, PropertiesExtractor>}
-     */
-    private static readonly propertiesExtractorsMap: Map <string, PropertiesExtractor> = new Map([
-        [NodeType.AssignmentExpression, PropertiesExtractor.AssignmentExpressionPropertiesExtractor],
-        [NodeType.AssignmentPattern, PropertiesExtractor.AssignmentPatternPropertiesExtractor],
-        [NodeType.VariableDeclarator, PropertiesExtractor.VariableDeclaratorPropertiesExtractor]
-    ]);
-
     /**
      * @type {TPropertiesExtractorFactory}
      */
@@ -57,122 +41,185 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
     }
 
     /**
-     * @param {TransformationStage} transformationStage
-     * @returns {IVisitor | null}
+     * @param {ObjectExpression} objectExpressionNode
+     * @param {Statement} hostStatement
+     * @returns {boolean}
      */
-    public getVisitor (transformationStage: TransformationStage): IVisitor | null {
-        if (!this.options.transformObjectKeys) {
-            return null;
+    private static isProhibitedHostStatement (
+        objectExpressionNode: ESTree.ObjectExpression,
+        hostStatement: ESTree.Statement
+    ): boolean {
+        return ObjectExpressionKeysTransformer.isProhibitedVariableDeclarationHostStatement(objectExpressionNode, hostStatement)
+            || ObjectExpressionKeysTransformer.isProhibitedFunctionHostStatement(objectExpressionNode, hostStatement);
+    }
+
+    /**
+     * Fix of https://github.com/javascript-obfuscator/javascript-obfuscator/issues/516
+     * If object expression is placed inside any expression inside variable declaration with 2+ declarators
+     * - should mark host node as prohibited
+     *
+     * @param {ObjectExpression} objectExpressionNode
+     * @param {Statement} hostStatement
+     * @returns {boolean}
+     */
+    private static isProhibitedVariableDeclarationHostStatement (
+        objectExpressionNode: ESTree.ObjectExpression,
+        hostStatement: ESTree.Statement
+    ): boolean {
+        if (!NodeGuards.isVariableDeclarationNode(hostStatement) || !hostStatement.declarations.length) {
+            return false;
         }
 
-        if (transformationStage !== TransformationStage.Converting) {
-            return null;
+        return ObjectExpressionKeysTransformer.isReferencedIdentifierName(
+            objectExpressionNode,
+            hostStatement.declarations
+        );
+    }
+
+    /**
+     * @param {ObjectExpression} objectExpressionNode
+     * @param {Statement} hostStatement
+     * @returns {boolean}
+     */
+    private static isProhibitedFunctionHostStatement (
+        objectExpressionNode: ESTree.ObjectExpression,
+        hostStatement: ESTree.Statement
+    ): boolean {
+        if (!NodeGuards.isFunctionNode(hostStatement) || !hostStatement.params.length) {
+            return false;
         }
 
-        return {
-            enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
-                if (
-                    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);
-                }
-            }
-        };
+        const hostNode: ESTree.Node | undefined = objectExpressionNode.parentNode;
+
+        if (!hostNode || !NodeGuards.isAssignmentPatternNode(hostNode)) {
+            return false;
+        }
+
+        return ObjectExpressionKeysTransformer.isReferencedIdentifierName(
+            objectExpressionNode,
+            hostStatement.params
+        );
     }
 
     /**
-     * replaces:
-     *     var object = {
-     *          foo: 1,
-     *          bar: 2
-     *     };
-     *
-     * on:
-     *     var object = {};
-     *     object['foo'] = 1;
-     *     object['bar'] = 2;
-     *
      * @param {ObjectExpression} objectExpressionNode
-     * @param {Node} parentNode
-     * @returns {NodeGuards}
+     * @param {Node[]} nodesToSearch
+     * @returns {boolean}
      */
-    public transformNode (objectExpressionNode: ESTree.ObjectExpression, parentNode: ESTree.Node): ESTree.Node {
-        if (!objectExpressionNode.properties.length) {
-            return objectExpressionNode;
+    private static isReferencedIdentifierName (
+        objectExpressionNode: ESTree.ObjectExpression,
+        nodesToSearch: ESTree.Node[],
+    ): boolean {
+        if (nodesToSearch.length === 1) {
+            return false;
         }
 
-        const propertiesExtractorName: PropertiesExtractor | undefined = ObjectExpressionKeysTransformer
-            .propertiesExtractorsMap
-            .get(parentNode.type);
+        const identifierNamesSet: string[] = [];
 
-        if (!propertiesExtractorName) {
-            return objectExpressionNode;
+        let isReferencedIdentifierName: boolean = false;
+        let isCurrentNode: boolean = false;
+
+        // should mark node as prohibited if identifier of node is referenced somewhere inside other nodes
+        for (const nodeToSearch of nodesToSearch) {
+            const identifierNamesSetForCurrentNode: string[] = [];
+
+            estraverse.traverse(nodeToSearch, {
+                enter: (node: ESTree.Node): void | estraverse.VisitorOption => {
+                    if (node === objectExpressionNode) {
+                        isCurrentNode = true;
+                    }
+
+                    if (!NodeGuards.isIdentifierNode(node)) {
+                        return;
+                    }
+
+                    if (!isCurrentNode) {
+                        identifierNamesSetForCurrentNode.push(node.name);
+                    } else if (identifierNamesSet.includes(node.name)) {
+                        isReferencedIdentifierName = true;
+
+                        return estraverse.VisitorOption.Break;
+                    }
+                }
+            });
+
+            if (isCurrentNode || isReferencedIdentifierName) {
+                break;
+            } else {
+                identifierNamesSet.push(...identifierNamesSetForCurrentNode);
+            }
+        }
+
+        return isReferencedIdentifierName;
+    }
+
+    /**
+     * @param {TransformationStage} transformationStage
+     * @returns {IVisitor | null}
+     */
+    public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        if (!this.options.transformObjectKeys) {
+            return null;
         }
 
-        return this.transformNodeWithExtractor(objectExpressionNode, parentNode, propertiesExtractorName);
+        switch (transformationStage) {
+            case TransformationStage.Converting:
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (
+                            parentNode
+                            && NodeGuards.isObjectExpressionNode(node)
+                        ) {
+                            return this.transformNode(node, parentNode);
+                        }
+                    }
+                };
+
+            default:
+                return null;
+        }
     }
 
     /**
      * replaces:
-     *     return {
+     *     var object = {
      *          foo: 1,
      *          bar: 2
      *     };
      *
      * on:
-     *     var object = {};
-     *     object['foo'] = 1;
-     *     object['bar'] = 2;
-     *     return object;
+     *     var _0xabc123 = {};
+     *     _0xabc123['foo'] = 1;
+     *     _0xabc123['bar'] = 2;
+     *     var object = _0xabc123;
      *
      * @param {ObjectExpression} objectExpressionNode
      * @param {Node} parentNode
      * @returns {NodeGuards}
      */
-    public transformNodeWithBaseExtractor (objectExpressionNode: ESTree.ObjectExpression, parentNode: ESTree.Node): ESTree.Node {
+    public transformNode (objectExpressionNode: ESTree.ObjectExpression, parentNode: ESTree.Node): ESTree.Node {
         if (!objectExpressionNode.properties.length) {
             return objectExpressionNode;
         }
 
-        if (ObjectExpressionKeysTransformer.objectExpressionNodesToSkipSet.has(objectExpressionNode)) {
+        const hostStatement: ESTree.Statement = NodeStatementUtils.getRootStatementOfNode(objectExpressionNode);
+
+        if (ObjectExpressionKeysTransformer.isProhibitedHostStatement(objectExpressionNode, hostStatement)) {
             return objectExpressionNode;
         }
 
-        return this.transformNodeWithExtractor(objectExpressionNode, parentNode, PropertiesExtractor.BasePropertiesExtractor);
-    }
-
-    /**
-     * @param {ObjectExpression} objectExpressionNode
-     * @param {Node} parentNode
-     * @param {PropertiesExtractor} propertiesExtractorName
-     * @returns {Node}
-     */
-    private transformNodeWithExtractor (
-        objectExpressionNode: ESTree.ObjectExpression,
-        parentNode: ESTree.Node,
-        propertiesExtractorName: PropertiesExtractor
-    ): ESTree.Node {
-        const propertiesExtractor: IPropertiesExtractor = this.propertiesExtractorFactory(propertiesExtractorName);
-        const extractedResult: TPropertiesExtractorResult =
-            propertiesExtractor.extract(objectExpressionNode, parentNode);
-
-        switch (extractedResult) {
-            case PropertiesExtractorFlag.Skip:
-                ObjectExpressionKeysTransformer.objectExpressionNodesToSkipSet.add(objectExpressionNode);
+        const {
+            nodeToReplace: newObjectExpressionIdentifierReference,
+            objectExpressionHostStatement: newObjectExpressionHostStatement,
+            objectExpressionNode: newObjectExpressionNode
+        } = this
+            .propertiesExtractorFactory(PropertiesExtractor.ObjectExpressionToVariableDeclarationExtractor)
+            .extract(objectExpressionNode, hostStatement);
 
-                return objectExpressionNode;
+        this
+            .propertiesExtractorFactory(PropertiesExtractor.BasePropertiesExtractor)
+            .extract(newObjectExpressionNode, newObjectExpressionHostStatement);
 
-            default:
-                return extractedResult;
-        }
+        return newObjectExpressionIdentifierReference;
     }
 }

+ 196 - 0
src/node-transformers/converting-transformers/object-expression-extractors/BasePropertiesExtractor.ts

@@ -0,0 +1,196 @@
+import { injectable } from 'inversify';
+
+import * as ESTree from 'estree';
+
+import { TNodeWithStatements } from '../../../types/node/TNodeWithStatements';
+import { TPropertiesExtractorResult } from '../../../types/node-transformers/TPropertiesExtractorResult';
+
+import { IPropertiesExtractor } from '../../../interfaces/node-transformers/converting-transformers/properties-extractors/IPropertiesExtractor';
+
+import { NodeAppender } from '../../../node/NodeAppender';
+import { NodeFactory } from '../../../node/NodeFactory';
+import { NodeGuards } from '../../../node/NodeGuards';
+import { NodeStatementUtils } from '../../../node/NodeStatementUtils';
+
+@injectable()
+export class BasePropertiesExtractor implements IPropertiesExtractor {
+    /**
+     * @param {Property} propertyNode
+     * @returns {string | null}
+     */
+    private static getPropertyNodeKeyName (propertyNode: ESTree.Property): string | null {
+        const propertyKeyNode: ESTree.Expression = propertyNode.key;
+
+        if (
+            NodeGuards.isLiteralNode(propertyKeyNode)
+            && (
+                typeof propertyKeyNode.value === 'string'
+                || typeof propertyKeyNode.value === 'number'
+            )
+        ) {
+            return propertyKeyNode.value.toString();
+        }
+
+        if (NodeGuards.isIdentifierNode(propertyKeyNode)) {
+            return propertyKeyNode.name;
+        }
+
+        return null;
+    }
+
+    /**
+     * @param {Node} node
+     * @returns {propertyValueNode is Pattern}
+     */
+    private static isProhibitedPattern (node: ESTree.Node): node is ESTree.Pattern {
+        return !node
+            || NodeGuards.isObjectPatternNode(node)
+            || NodeGuards.isArrayPatternNode(node)
+            || NodeGuards.isAssignmentPatternNode(node)
+            || NodeGuards.isRestElementNode(node);
+    }
+
+    /**
+     * extracts object expression properties:
+     *     var _0xabc123 = {
+     *          foo: 1,
+     *          bar: 2
+     *     };
+     *
+     * to:
+     *     var _0xabc123 = {};
+     *     _0xabc123['foo'] = 1;
+     *     _0xabc123['bar'] = 2;
+     *
+     * @param {ObjectExpression} objectExpressionNode
+     * @param {Statement} hostStatement
+     * @returns {TPropertiesExtractorResult}
+     */
+    public extract (
+        objectExpressionNode: ESTree.ObjectExpression,
+        hostStatement: ESTree.Statement
+    ): TPropertiesExtractorResult {
+        const hostNode: ESTree.Node | undefined = objectExpressionNode.parentNode;
+
+        if (
+            hostNode
+            && NodeGuards.isVariableDeclaratorNode(hostNode)
+            && NodeGuards.isIdentifierNode(hostNode.id)
+        ) {
+            return this.transformObjectExpressionNode(objectExpressionNode, hostStatement, hostNode.id);
+        }
+
+        return {
+            nodeToReplace: objectExpressionNode,
+            objectExpressionHostStatement: hostStatement,
+            objectExpressionNode: objectExpressionNode
+        };
+    }
+
+    /**
+     * @param {ObjectExpression} objectExpressionNode
+     * @param {Statement} hostStatement
+     * @param {Expression} memberExpressionHostNode
+     * @returns {TPropertiesExtractorResult}
+     */
+    private transformObjectExpressionNode (
+        objectExpressionNode: ESTree.ObjectExpression,
+        hostStatement: ESTree.Statement,
+        memberExpressionHostNode: ESTree.Expression
+    ): TPropertiesExtractorResult {
+        const properties: ESTree.Property[] = objectExpressionNode.properties;
+        const [expressionStatements, removablePropertyIds]: [ESTree.ExpressionStatement[], number[]] = this
+            .extractPropertiesToExpressionStatements(
+                properties,
+                hostStatement,
+                memberExpressionHostNode
+            );
+
+        const hostNodeWithStatements: TNodeWithStatements = NodeStatementUtils.getScopeOfNode(hostStatement);
+
+        this.filterExtractedObjectExpressionProperties(objectExpressionNode, removablePropertyIds);
+        NodeAppender.insertAfter(hostNodeWithStatements, expressionStatements, hostStatement);
+
+        return {
+            nodeToReplace: objectExpressionNode,
+            objectExpressionHostStatement: hostStatement,
+            objectExpressionNode: objectExpressionNode
+        };
+    }
+
+    /**
+     * @param {Property[]} properties
+     * @param {Statement} hostStatement
+     * @param {Expression} memberExpressionHostNode
+     * @returns {[ExpressionStatement[], number[]]}
+     */
+    private extractPropertiesToExpressionStatements (
+        properties: ESTree.Property[],
+        hostStatement: ESTree.Statement,
+        memberExpressionHostNode: ESTree.Expression
+    ): [ESTree.ExpressionStatement[], number[]] {
+        const propertiesLength: number = properties.length;
+        const expressionStatements: ESTree.ExpressionStatement[] = [];
+        const removablePropertyIds: number[] = [];
+
+        for (let i: number = 0; i < propertiesLength; i++) {
+            const property: ESTree.Property = properties[i];
+            const propertyValue: ESTree.Expression | ESTree.Pattern = property.value;
+
+            // invalid property nodes
+            if (BasePropertiesExtractor.isProhibitedPattern(propertyValue)) {
+                continue;
+            }
+
+            /**
+             * Stage 1: extract property node key names
+             */
+            const propertyKeyName: string | null = BasePropertiesExtractor.getPropertyNodeKeyName(property);
+
+            if (!propertyKeyName) {
+                continue;
+            }
+
+            /**
+             * Stage 2: creating new expression statement node with member expression based on removed property
+             */
+            const shouldCreateLiteralNode: boolean = !property.computed
+                || (property.computed && !!property.key && NodeGuards.isLiteralNode(property.key));
+            const memberExpressionProperty: ESTree.Expression = shouldCreateLiteralNode
+                ? NodeFactory.literalNode(propertyKeyName)
+                : NodeFactory.identifierNode(propertyKeyName);
+            const memberExpressionNode: ESTree.MemberExpression = NodeFactory
+                .memberExpressionNode(memberExpressionHostNode, memberExpressionProperty, true);
+            const expressionStatementNode: ESTree.ExpressionStatement = NodeFactory.expressionStatementNode(
+                NodeFactory.assignmentExpressionNode('=', memberExpressionNode, propertyValue)
+            );
+
+            /**
+             * Stage 3: recursively processing nested object expressions
+             */
+            if (NodeGuards.isObjectExpressionNode(property.value)) {
+                this.transformObjectExpressionNode(property.value, hostStatement, memberExpressionNode);
+            }
+
+            /**
+             * Stage 4: filling arrays
+             */
+            expressionStatements.push(expressionStatementNode);
+            removablePropertyIds.push(i);
+        }
+
+        return [expressionStatements, removablePropertyIds];
+    }
+
+    /**
+     * @param {ObjectExpression} objectExpressionNode
+     * @param {number[]} removablePropertyIds
+     */
+    private filterExtractedObjectExpressionProperties (
+        objectExpressionNode: ESTree.ObjectExpression,
+        removablePropertyIds: number[]
+    ): void {
+        objectExpressionNode.properties = objectExpressionNode.properties
+            .filter((property: ESTree.Property, index: number) => !removablePropertyIds.includes(index));
+    }
+}

+ 55 - 42
src/node-transformers/converting-transformers/properties-extractors/BasePropertiesExtractor.ts → src/node-transformers/converting-transformers/object-expression-extractors/ObjectExpressionToVariableDeclarationExtractor.ts

@@ -10,102 +10,101 @@ import { TStatement } from '../../../types/node/TStatement';
 
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
 import { TInitialData } from '../../../types/TInitialData';
-import { IOptions } from '../../../interfaces/options/IOptions';
-import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
+import { IPropertiesExtractor } from '../../../interfaces/node-transformers/converting-transformers/properties-extractors/IPropertiesExtractor';
 
 import { ObjectExpressionKeysTransformerCustomNode } from '../../../enums/custom-nodes/ObjectExpressionKeysTransformerCustomNode';
 
-import { AbstractPropertiesExtractor } from './AbstractPropertiesExtractor';
 import { BasePropertiesExtractorObjectExpressionHostNode } from '../../../custom-nodes/object-expression-keys-transformer-nodes/BasePropertiesExtractorObjectExpressionHostNode';
 import { NodeAppender } from '../../../node/NodeAppender';
 import { NodeGuards } from '../../../node/NodeGuards';
+import { NodeStatementUtils } from '../../../node/NodeStatementUtils';
+import { NodeUtils } from '../../../node/NodeUtils';
 
 @injectable()
-export class BasePropertiesExtractor extends AbstractPropertiesExtractor {
+export class ObjectExpressionToVariableDeclarationExtractor implements IPropertiesExtractor {
     /**
      * @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;
     }
 
     /**
+     * extracts object expression:
+     *     var object = {
+     *          foo: 1,
+     *          bar: 2
+     *     };
+     *
+     * to:
+     *     var _0xabc123 = {
+     *          foo: 1,
+     *          bar: 2
+     *     };
+     *     var object = _0xabc123;
+     *
      * @param {ObjectExpression} objectExpressionNode
-     * @param {Node} parentNode
+     * @param {Statement} hostStatement
      * @returns {TPropertiesExtractorResult}
      */
     public extract (
         objectExpressionNode: ESTree.ObjectExpression,
-        parentNode: ESTree.Node
+        hostStatement: ESTree.Statement
     ): TPropertiesExtractorResult {
-        return this.transformObjectExpressionNode(
+        return this.transformObjectExpressionToVariableDeclaration(
             objectExpressionNode,
-            parentNode
+            hostStatement
         );
     }
 
     /**
      * @param {ObjectExpression} objectExpressionNode
-     * @param {Node} parentNode
+     * @param {Statement} hostStatement
      * @returns {Node}
      */
-    protected transformObjectExpressionNode (
+    private transformObjectExpressionToVariableDeclaration (
         objectExpressionNode: ESTree.ObjectExpression,
-        parentNode: ESTree.Node
-    ): ESTree.Node {
-        const hostStatement: ESTree.Statement = this.getHostStatement(objectExpressionNode);
-
-        if (AbstractPropertiesExtractor.isProhibitedHostStatement(objectExpressionNode, hostStatement)) {
-            return objectExpressionNode;
-        }
-
-        const newObjectExpressionHostNode: ESTree.VariableDeclaration = this.getObjectExpressionHostNode();
-        const newObjectExpressionIdentifier: ESTree.Identifier = this.getObjectExpressionIdentifierNode(newObjectExpressionHostNode);
-
+        hostStatement: ESTree.Statement
+    ): TPropertiesExtractorResult {
         const properties: ESTree.Property[] = objectExpressionNode.properties;
 
-        const [expressionStatements, removablePropertyIds]: [ESTree.ExpressionStatement[], number[]] = this
-            .extractPropertiesToExpressionStatements(properties, newObjectExpressionIdentifier);
-        const statementsToInsert: TStatement[] = [
-            newObjectExpressionHostNode,
-            ...expressionStatements
-        ];
+        const newObjectExpressionHostStatement: ESTree.VariableDeclaration = this.getObjectExpressionHostNode(properties);
+        const newObjectExpressionIdentifier: ESTree.Identifier = this.getObjectExpressionIdentifierNode(newObjectExpressionHostStatement);
+        const newObjectExpressionNode: ESTree.ObjectExpression = this.getObjectExpressionNode(newObjectExpressionHostStatement);
 
-        const hostNodeWithStatements: TNodeWithStatements = this.getHostNodeWithStatements(
-            objectExpressionNode,
-            hostStatement
-        );
+        const statementsToInsert: TStatement[] = [newObjectExpressionHostStatement];
+        const hostNodeWithStatements: TNodeWithStatements = NodeStatementUtils.getScopeOfNode(hostStatement);
 
-        this.filterExtractedObjectExpressionProperties(objectExpressionNode, removablePropertyIds);
         NodeAppender.insertBefore(hostNodeWithStatements, statementsToInsert, hostStatement);
-
-        return newObjectExpressionIdentifier;
+        NodeUtils.parentizeAst(newObjectExpressionHostStatement);
+        NodeUtils.parentizeNode(newObjectExpressionHostStatement, hostNodeWithStatements);
+
+        return {
+            nodeToReplace: newObjectExpressionIdentifier,
+            objectExpressionHostStatement: newObjectExpressionHostStatement,
+            objectExpressionNode: newObjectExpressionNode
+        };
     }
 
     /**
+     * @param {Property[]} properties
      * @returns {VariableDeclaration}
      */
-    private getObjectExpressionHostNode (): ESTree.VariableDeclaration {
+    private getObjectExpressionHostNode (properties: ESTree.Property[]): ESTree.VariableDeclaration {
         const objectExpressionHostCustomNode: ICustomNode<TInitialData<BasePropertiesExtractorObjectExpressionHostNode>> =
             this.objectExpressionKeysTransformerCustomNodeFactory(
                 ObjectExpressionKeysTransformerCustomNode.BasePropertiesExtractorObjectExpressionHostNode
             );
 
-        objectExpressionHostCustomNode.initialize();
+        objectExpressionHostCustomNode.initialize(properties);
 
         const statementNode: TStatement = objectExpressionHostCustomNode.getNode()[0];
 
@@ -132,4 +131,18 @@ export class BasePropertiesExtractor extends AbstractPropertiesExtractor {
 
         return newObjectExpressionIdentifierNode;
     }
+
+    /**
+     * @param {VariableDeclaration} objectExpressionHostNode
+     * @returns {Identifier}
+     */
+    private getObjectExpressionNode (objectExpressionHostNode: ESTree.VariableDeclaration): ESTree.ObjectExpression {
+        const newObjectExpressionNode: ESTree.Expression | null = objectExpressionHostNode.declarations[0].init ?? null;
+
+        if (!newObjectExpressionNode || !NodeGuards.isObjectExpressionNode(newObjectExpressionNode)) {
+            throw new Error(`\`objectExpressionHostNode\` should contain \`VariableDeclarator\` node with \`ObjectExpression\` init property`);
+        }
+
+        return newObjectExpressionNode;
+    }
 }

+ 0 - 287
src/node-transformers/converting-transformers/properties-extractors/AbstractPropertiesExtractor.ts

@@ -1,287 +0,0 @@
-import { inject, injectable } from 'inversify';
-import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
-
-import * as ESTree from 'estree';
-
-import { TNodeWithStatements } from '../../../types/node/TNodeWithStatements';
-import { TPropertiesExtractorResult } from '../../../types/node-transformers/TPropertiesExtractorResult';
-
-import { IOptions } from '../../../interfaces/options/IOptions';
-import { IPropertiesExtractor } from '../../../interfaces/node-transformers/converting-transformers/properties-extractors/IPropertiesExtractor';
-import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
-
-import { NodeAppender } from '../../../node/NodeAppender';
-import { NodeFactory } from '../../../node/NodeFactory';
-import { NodeGuards } from '../../../node/NodeGuards';
-import { NodeStatementUtils } from '../../../node/NodeStatementUtils';
-
-@injectable()
-export abstract class AbstractPropertiesExtractor implements IPropertiesExtractor {
-    /**
-     * @type {Map<ESTree.ObjectExpression, TNodeWithStatements>}
-     */
-    protected readonly cachedHostNodesWithStatementsMap: Map <ESTree.ObjectExpression, TNodeWithStatements> = new Map();
-
-    /**
-     * @type {Map<ESTree.ObjectExpression, ESTree.Statement>}
-     */
-    protected readonly cachedHostStatementsMap: Map <ESTree.ObjectExpression, ESTree.Statement> = new Map();
-
-    /**
-     * @type {IOptions}
-     */
-    protected readonly options: IOptions;
-
-    /**
-     * @type {IRandomGenerator}
-     */
-    protected readonly randomGenerator: IRandomGenerator;
-
-    /**
-     * @param {IRandomGenerator} randomGenerator
-     * @param {IOptions} options
-     */
-    constructor (
-        @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
-        @inject(ServiceIdentifiers.IOptions) options: IOptions
-    ) {
-        this.randomGenerator = randomGenerator;
-        this.options = options;
-    }
-
-    /**
-     * @param {Property} propertyNode
-     * @returns {string | null}
-     */
-    protected static getPropertyNodeKeyName (propertyNode: ESTree.Property): string | null {
-        if (!propertyNode.key) {
-            return null;
-        }
-
-        const propertyKeyNode: ESTree.Expression = propertyNode.key;
-
-        if (
-            NodeGuards.isLiteralNode(propertyKeyNode)
-            && (
-                typeof propertyKeyNode.value === 'string'
-                || typeof propertyKeyNode.value === 'number'
-            )
-        ) {
-            return propertyKeyNode.value.toString();
-        }
-
-        if (NodeGuards.isIdentifierNode(propertyKeyNode)) {
-            return propertyKeyNode.name;
-        }
-
-        return null;
-    }
-
-    /**
-     * @param {Node} node
-     * @returns {propertyValueNode is Pattern}
-     */
-    protected static isProhibitedPattern (node: ESTree.Node): node is ESTree.Pattern {
-        return !node
-            || NodeGuards.isObjectPatternNode(node)
-            || NodeGuards.isArrayPatternNode(node)
-            || NodeGuards.isAssignmentPatternNode(node)
-            || NodeGuards.isRestElementNode(node);
-    }
-
-    /**
-     * @param {ObjectExpression} objectExpressionNode
-     * @param {Statement} hostStatement
-     * @returns {boolean}
-     */
-    protected static isProhibitedHostStatement (
-        objectExpressionNode: ESTree.ObjectExpression,
-        hostStatement: ESTree.Statement
-    ): boolean {
-        if (AbstractPropertiesExtractor.isProhibitedVariableDeclarationHostStatement(objectExpressionNode, hostStatement)) {
-            return true;
-        }
-
-        return false;
-    }
-
-    /**
-     * Fix of https://github.com/javascript-obfuscator/javascript-obfuscator/issues/516
-     * If object expression is placed inside any expression inside variable declaration with 2+ declarators
-     * - should mark host node as prohibited
-     *
-     * @param {ObjectExpression} objectExpressionNode
-     * @param {Statement} hostStatement
-     * @returns {boolean}
-     */
-    protected static isProhibitedVariableDeclarationHostStatement (
-        objectExpressionNode: ESTree.ObjectExpression,
-        hostStatement: ESTree.Statement
-    ): boolean {
-        if (!NodeGuards.isVariableDeclarationNode(hostStatement)) {
-            return false;
-        }
-
-        if (objectExpressionNode.parentNode && NodeGuards.isVariableDeclaratorNode(objectExpressionNode.parentNode)) {
-            return false;
-        }
-
-        if (hostStatement.declarations.length <= 1) {
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * @param {ObjectExpression} objectExpressionNode
-     * @param {Node} hostNode
-     * @returns {TPropertiesExtractorResult}
-     */
-    public abstract extract (
-        objectExpressionNode: ESTree.ObjectExpression,
-        hostNode: ESTree.Node
-    ): TPropertiesExtractorResult;
-
-    /**
-     * @param {Property[]} properties
-     * @param {Expression} memberExpressionHostNode
-     * @returns {[ExpressionStatement[] , number[]]}
-     */
-    protected extractPropertiesToExpressionStatements (
-        properties: ESTree.Property[],
-        memberExpressionHostNode: ESTree.Expression
-    ): [ESTree.ExpressionStatement[], number[]] {
-        const propertiesLength: number = properties.length;
-        const expressionStatements: ESTree.ExpressionStatement[] = [];
-        const removablePropertyIds: number[] = [];
-
-        for (let i: number = 0; i < propertiesLength; i++) {
-            const property: ESTree.Property = properties[i];
-            const propertyValue: ESTree.Expression | ESTree.Pattern = property.value;
-
-            // invalid property nodes
-            if (AbstractPropertiesExtractor.isProhibitedPattern(propertyValue)) {
-                continue;
-            }
-
-            /**
-             * Stage 1: extract property node key names
-             */
-            const propertyKeyName: string | null = AbstractPropertiesExtractor.getPropertyNodeKeyName(property);
-
-            if (!propertyKeyName) {
-                continue;
-            }
-
-            /**
-             * Stage 2: creating new expression statement node with member expression based on removed property
-             */
-            const shouldCreateLiteralNode: boolean = !property.computed
-                || (property.computed && !!property.key && NodeGuards.isLiteralNode(property.key));
-            const memberExpressionProperty: ESTree.Expression = shouldCreateLiteralNode
-                ? NodeFactory.literalNode(propertyKeyName)
-                : NodeFactory.identifierNode(propertyKeyName);
-            const memberExpressionNode: ESTree.MemberExpression = NodeFactory
-                .memberExpressionNode(memberExpressionHostNode, memberExpressionProperty, true);
-            const expressionStatementNode: ESTree.ExpressionStatement = NodeFactory.expressionStatementNode(
-                NodeFactory.assignmentExpressionNode('=', memberExpressionNode, propertyValue)
-            );
-
-            /**
-             * Stage 3: recursively processing nested object expressions
-             */
-            if (NodeGuards.isObjectExpressionNode(property.value)) {
-                this.transformObjectExpressionNode(property.value, memberExpressionNode);
-            }
-
-            /**
-             * Stage 4: filling arrays
-             */
-            expressionStatements.push(expressionStatementNode);
-            removablePropertyIds.push(i);
-        }
-
-        return [expressionStatements, removablePropertyIds];
-    }
-
-    /**
-     * @param {ObjectExpression} objectExpressionNode
-     * @param {number[]} removablePropertyIds
-     */
-    protected filterExtractedObjectExpressionProperties (
-        objectExpressionNode: ESTree.ObjectExpression,
-        removablePropertyIds: number[]
-    ): void {
-        objectExpressionNode.properties = objectExpressionNode.properties
-            .filter((property: ESTree.Property, index: number) => !removablePropertyIds.includes(index));
-    }
-
-    /**
-     * @param {ObjectExpression} objectExpressionNode
-     * @param {Expression} memberExpressionHostNode
-     * @returns {Node}
-     */
-    protected transformObjectExpressionNode (
-        objectExpressionNode: ESTree.ObjectExpression,
-        memberExpressionHostNode: ESTree.Expression
-    ): ESTree.Node {
-        const hostStatement: ESTree.Statement = this.getHostStatement(objectExpressionNode);
-
-        if (AbstractPropertiesExtractor.isProhibitedHostStatement(objectExpressionNode, hostStatement)) {
-            return objectExpressionNode;
-        }
-
-        const properties: ESTree.Property[] = objectExpressionNode.properties;
-        const [expressionStatements, removablePropertyIds]: [ESTree.ExpressionStatement[], number[]] = this
-            .extractPropertiesToExpressionStatements(properties, memberExpressionHostNode);
-
-        const hostNodeWithStatements: TNodeWithStatements = this.getHostNodeWithStatements(
-            objectExpressionNode,
-            hostStatement
-        );
-
-        this.filterExtractedObjectExpressionProperties(objectExpressionNode, removablePropertyIds);
-        NodeAppender.insertAfter(hostNodeWithStatements, expressionStatements, hostStatement);
-
-        return objectExpressionNode;
-    }
-
-    /**
-     * @param {ObjectExpression} objectExpressionNode
-     * @param {Statement} hostStatement
-     * @returns {TNodeWithStatements}
-     */
-    protected getHostNodeWithStatements (
-        objectExpressionNode: ESTree.ObjectExpression,
-        hostStatement: ESTree.Statement
-    ): TNodeWithStatements {
-        if (this.cachedHostNodesWithStatementsMap.has(objectExpressionNode)) {
-            return <TNodeWithStatements>this.cachedHostNodesWithStatementsMap.get(objectExpressionNode);
-        }
-
-        const nodeWithStatements: TNodeWithStatements = NodeStatementUtils.getScopeOfNode(hostStatement);
-
-        this.cachedHostNodesWithStatementsMap.set(objectExpressionNode, nodeWithStatements);
-
-        return nodeWithStatements;
-    }
-
-    /**
-     * Returns host statement of object expression node
-     *
-     * @param {NodeGuards} objectExpressionNode
-     * @returns {Node}
-     */
-    protected getHostStatement (objectExpressionNode: ESTree.ObjectExpression): ESTree.Statement {
-        if (this.cachedHostStatementsMap.has(objectExpressionNode)) {
-            return <ESTree.Statement>this.cachedHostStatementsMap.get(objectExpressionNode);
-        }
-
-        const hostStatement: ESTree.Statement = NodeStatementUtils.getRootStatementOfNode(objectExpressionNode);
-
-        this.cachedHostStatementsMap.set(objectExpressionNode, hostStatement);
-
-        return hostStatement;
-    }
-}

+ 0 - 83
src/node-transformers/converting-transformers/properties-extractors/AssignmentExpressionPropertiesExtractor.ts

@@ -1,83 +0,0 @@
-import { inject, injectable } from 'inversify';
-import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
-
-import * as ESTree from 'estree';
-
-import { TPropertiesExtractorResult } from '../../../types/node-transformers/TPropertiesExtractorResult';
-
-import { IOptions } from '../../../interfaces/options/IOptions';
-import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
-
-import { AbstractPropertiesExtractor } from './AbstractPropertiesExtractor';
-import { NodeGuards } from '../../../node/NodeGuards';
-
-@injectable()
-export class AssignmentExpressionPropertiesExtractor extends AbstractPropertiesExtractor {
-    /**
-     * @param {IRandomGenerator} randomGenerator
-     * @param {IOptions} options
-     */
-    constructor (
-        @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
-        @inject(ServiceIdentifiers.IOptions) options: IOptions
-    ) {
-        super(randomGenerator, options);
-    }
-
-    /**
-     * @param {Node} node
-     * @returns {propertyValueNode is Pattern}
-     */
-    private static isProhibitedHostParent (node: ESTree.Node): node is ESTree.Pattern {
-        if (NodeGuards.isMemberExpressionNode(node)) {
-            return true;
-        }
-
-        if (AssignmentExpressionPropertiesExtractor.isProhibitedStatementNode(node)) {
-            return true;
-        }
-
-        // statements without block statement
-        return NodeGuards.isExpressionStatementNode(node)
-            && !!node.parentNode
-            && AssignmentExpressionPropertiesExtractor.isProhibitedStatementNode(node.parentNode);
-    }
-
-    /**
-     * @param {Node} node
-     * @returns {boolean}
-     */
-    private static isProhibitedStatementNode (node: ESTree.Node): boolean {
-        return NodeGuards.isIfStatementNode(node)
-            || NodeGuards.isForStatementTypeNode(node)
-            || NodeGuards.isWhileStatementNode(node);
-    }
-
-    /**
-     * @param {ObjectExpression} objectExpressionNode
-     * @param {AssignmentExpression} hostNode
-     * @returns {TPropertiesExtractorResult}
-     */
-    public extract (
-        objectExpressionNode: ESTree.ObjectExpression,
-        hostNode: ESTree.AssignmentExpression
-    ): TPropertiesExtractorResult {
-        const hostParentNode: ESTree.Node | undefined = hostNode.parentNode;
-        const leftNode: ESTree.MemberExpression | ESTree.Pattern = hostNode.left;
-
-        // left node shouldn't be as Pattern node
-        if (AbstractPropertiesExtractor.isProhibitedPattern(leftNode)) {
-            return objectExpressionNode;
-        }
-
-        // left node shouldn't be as prohibited node
-        if (hostParentNode && AssignmentExpressionPropertiesExtractor.isProhibitedHostParent(hostParentNode)) {
-            return objectExpressionNode;
-        }
-
-        return this.transformObjectExpressionNode(
-            objectExpressionNode,
-            leftNode
-        );
-    }
-}

+ 0 - 123
src/node-transformers/converting-transformers/properties-extractors/AssignmentPatternPropertiesExtractor.ts

@@ -1,123 +0,0 @@
-import { inject, injectable } from 'inversify';
-import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
-
-import * as estraverse from 'estraverse';
-import * as ESTree from 'estree';
-
-import { TObjectExpressionKeysTransformerCustomNodeFactory } from '../../../types/container/custom-nodes/TObjectExpressionKeysTransformerCustomNodeFactory';
-import { TPropertiesExtractorResult } from '../../../types/node-transformers/TPropertiesExtractorResult';
-
-import { IOptions } from '../../../interfaces/options/IOptions';
-import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
-
-import { PropertiesExtractorFlag } from '../../../enums/node-transformers/converting-transformers/properties-extractors/PropertiesExtractorResult';
-
-import { AbstractPropertiesExtractor } from './AbstractPropertiesExtractor';
-import { BasePropertiesExtractor } from './BasePropertiesExtractor';
-import { NodeGuards } from '../../../node/NodeGuards';
-
-@injectable()
-export class AssignmentPatternPropertiesExtractor extends BasePropertiesExtractor {
-    /**
-     * @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, objectExpressionKeysTransformerCustomNodeFactory);
-    }
-
-    /**
-     * @param {AssignmentPattern} hostNode
-     * @returns {boolean}
-     */
-    private static isProhibitedHostParent (hostNode: ESTree.AssignmentPattern): boolean {
-        const hostParentNode: ESTree.Node | undefined = hostNode.parentNode;
-
-        if (!hostParentNode) {
-            return true;
-        }
-
-        if (NodeGuards.isFunctionNode(hostParentNode)) {
-            return AssignmentPatternPropertiesExtractor.isProhibitedHostParentFunction(hostNode, hostParentNode);
-        }
-
-        return false;
-    }
-
-    /**
-     * @param {AssignmentExpression} hostNode
-     * @param {Function} hostParentNode
-     * @returns {boolean}
-     */
-    private static isProhibitedHostParentFunction (
-        hostNode: ESTree.AssignmentPattern,
-        hostParentNode: ESTree.Function
-    ): boolean {
-        const { params } = hostParentNode;
-        const indexOfParameter: number = params.indexOf(hostNode);
-        const isFirstParameter: boolean = indexOfParameter === 0;
-
-        const parametersBeforeCurrentParameter: ESTree.Pattern[] = params.slice(0, indexOfParameter + 1);
-        const parametersIdentifierNamesSet: Set<string> = new Set();
-
-        let isProhibitedNode: boolean = false;
-
-        // should mark node as prohibited if identifier of node using inside previous function parameters
-        for (const parameter of parametersBeforeCurrentParameter) {
-            estraverse.traverse(parameter, {
-                enter: (node: ESTree.Node): void | estraverse.VisitorOption => {
-                    if (!NodeGuards.isIdentifierNode(node)) {
-                        return;
-                    }
-
-                    if (!isFirstParameter && parametersIdentifierNamesSet.has(node.name)) {
-                        isProhibitedNode = true;
-
-                        return estraverse.VisitorOption.Break;
-                    }
-
-                    parametersIdentifierNamesSet.add(node.name);
-                }
-            });
-
-            if (isProhibitedNode) {
-                break;
-            }
-        }
-
-        return isProhibitedNode;
-    }
-
-    /**
-     * @param {ObjectExpression} objectExpressionNode
-     * @param {AssignmentPattern} hostNode
-     * @returns {TPropertiesExtractorResult}
-     */
-    public extract (
-        objectExpressionNode: ESTree.ObjectExpression,
-        hostNode: ESTree.AssignmentPattern
-    ): TPropertiesExtractorResult {
-        const leftNode: ESTree.Pattern = hostNode.left;
-
-        // left node shouldn't be as Pattern node
-        if (AbstractPropertiesExtractor.isProhibitedPattern(leftNode)) {
-            return objectExpressionNode;
-        }
-
-        // left node shouldn't be as prohibited node
-        if (AssignmentPatternPropertiesExtractor.isProhibitedHostParent(hostNode)) {
-            return PropertiesExtractorFlag.Skip;
-        }
-
-        return this.transformObjectExpressionNode(
-            objectExpressionNode,
-            leftNode
-        );
-    }
-}

+ 0 - 120
src/node-transformers/converting-transformers/properties-extractors/VariableDeclaratorPropertiesExtractor.ts

@@ -1,120 +0,0 @@
-import { inject, injectable } from 'inversify';
-import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
-
-import * as estraverse from 'estraverse';
-import * as ESTree from 'estree';
-
-import { TPropertiesExtractorResult } from '../../../types/node-transformers/TPropertiesExtractorResult';
-
-import { IOptions } from '../../../interfaces/options/IOptions';
-import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
-
-import { AbstractPropertiesExtractor } from './AbstractPropertiesExtractor';
-import { NodeGuards } from '../../../node/NodeGuards';
-
-@injectable()
-export class VariableDeclaratorPropertiesExtractor extends AbstractPropertiesExtractor {
-    /**
-     * @param {IRandomGenerator} randomGenerator
-     * @param {IOptions} options
-     */
-    constructor (
-        @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
-        @inject(ServiceIdentifiers.IOptions) options: IOptions
-    ) {
-        super(randomGenerator, options);
-    }
-
-    /**
-     * @param {ObjectExpression} objectExpressionNode
-     * @param {VariableDeclarator} hostNode
-     * @returns {TPropertiesExtractorResult}
-     */
-    public extract (
-        objectExpressionNode: ESTree.ObjectExpression,
-        hostNode: ESTree.VariableDeclarator
-    ): TPropertiesExtractorResult {
-        if (
-            !NodeGuards.isIdentifierNode(hostNode.id)
-            || this.isProhibitedObjectExpressionNode(objectExpressionNode, hostNode.id)
-        ) {
-            return objectExpressionNode;
-        }
-
-        return this.transformObjectExpressionNode(objectExpressionNode, hostNode.id);
-    }
-
-    /**
-     * @param {ObjectExpression} objectExpressionNode
-     * @returns {VariableDeclarator}
-     */
-    private getHostVariableDeclaratorNode (objectExpressionNode: ESTree.ObjectExpression): ESTree.VariableDeclarator | never {
-        const { parentNode } = objectExpressionNode;
-
-        if (!parentNode || !NodeGuards.isVariableDeclaratorNode(parentNode)) {
-            throw new Error('Cannot get `VariableDeclarator` node for `ObjectExpression` node');
-        }
-
-        return parentNode;
-    }
-
-    /**
-     * @param {VariableDeclarator} variableDeclaratorNode
-     * @returns {VariableDeclaration}
-     */
-    private getHostVariableDeclarationNode (variableDeclaratorNode: ESTree.VariableDeclarator): ESTree.VariableDeclaration | never {
-        const { parentNode } = variableDeclaratorNode;
-
-        if (!parentNode || !NodeGuards.isVariableDeclarationNode(parentNode)) {
-            throw new Error('Cannot get `VariableDeclaration` node for `VariableDeclarator` node');
-        }
-
-        return parentNode;
-    }
-
-    /**
-     * @param {ObjectExpression} objectExpressionNode
-     * @param {Identifier} memberExpressionHostNode
-     * @returns {boolean}
-     */
-    private isProhibitedObjectExpressionNode (
-        objectExpressionNode: ESTree.ObjectExpression,
-        memberExpressionHostNode: ESTree.Identifier
-    ): boolean {
-        const hostVariableDeclarator: ESTree.VariableDeclarator = this.getHostVariableDeclaratorNode(objectExpressionNode);
-        const hostVariableDeclaration: ESTree.VariableDeclaration = this.getHostVariableDeclarationNode(hostVariableDeclarator);
-        const { declarations } = hostVariableDeclaration;
-        const indexOfDeclarator: number = declarations.indexOf(hostVariableDeclarator);
-        const isLastDeclarator: boolean = indexOfDeclarator === (declarations.length - 1);
-
-        // avoid unnecessary checks
-        if (isLastDeclarator) {
-            return false;
-        }
-
-        const declaratorsAfterCurrentDeclarator: ESTree.VariableDeclarator[] = declarations.slice(indexOfDeclarator);
-
-        let isProhibitedObjectExpressionNode: boolean = false;
-
-        // should mark node as prohibited if that node using inside other variable declarators
-        declaratorsAfterCurrentDeclarator.forEach((variableDeclarator: ESTree.VariableDeclarator) => {
-            estraverse.traverse(variableDeclarator, {
-                enter: (node: ESTree.Node): estraverse.VisitorOption | ESTree.Node => {
-                    if (
-                        NodeGuards.isMemberExpressionNode(node)
-                        && NodeGuards.isIdentifierNode(node.object)
-                        && node.object.name === memberExpressionHostNode.name
-                    ) {
-                        isProhibitedObjectExpressionNode = true;
-
-                        return estraverse.VisitorOption.Break;
-                    }
-
-                    return node;
-                }
-            });
-        });
-
-        return isProhibitedObjectExpressionNode;
-    }
-}

+ 5 - 3
src/types/node-transformers/TPropertiesExtractorResult.ts

@@ -1,5 +1,7 @@
 import * as ESTree from 'estree';
 
-import { PropertiesExtractorFlag } from '../../enums/node-transformers/converting-transformers/properties-extractors/PropertiesExtractorResult';
-
-export type TPropertiesExtractorResult = ESTree.Node | PropertiesExtractorFlag;
+export type TPropertiesExtractorResult = {
+    nodeToReplace: ESTree.Node;
+    objectExpressionHostStatement: ESTree.Statement;
+    objectExpressionNode: ESTree.ObjectExpression;
+};

+ 1 - 4
test/dev/dev.ts

@@ -7,10 +7,7 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
 
     let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
         `
-            var passthrough = (object) => object;
-            var foo = {foo: 1},\t
-                bar = passthrough({bar: foo.foo});
-            console.log(bar.bar);
+            var foo = {foo: 1};
         `,
         {
             ...NO_ADDITIONAL_NODES_PRESET,

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

@@ -39,10 +39,11 @@ describe('ObjectExpressionKeysTransformer', () => {
 
         describe('Variant #2: variable declaration without initialization', () => {
             const match: string = `` +
-                `var *${variableMatch};` +
-                `${variableMatch} *= *{};` +
+                `var ${variableMatch};` +
+                `var ${variableMatch} *= *{};` +
                 `${variableMatch}\\['foo'] *= *'bar';` +
                 `${variableMatch}\\['baz'] *= *'bark';` +
+                `${variableMatch} *= *${variableMatch};` +
             ``;
             const regExp: RegExp = new RegExp(match);
 
@@ -212,16 +213,14 @@ describe('ObjectExpressionKeysTransformer', () => {
 
         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}\\['inner'] *= *{};` +
                 `${variableMatch}\\['ball'] *= *'door';` +
+                `${variableMatch}\\['inner']\\['baz'] *= *'bark';` +
+                `${variableMatch}\\['inner']\\['inner1'] *= *{};` +
+                `${variableMatch}\\['inner']\\['cow'] *= *'bear';` +
+                `${variableMatch}\\['inner']\\['inner1']\\['hawk'] *= *'geek';` +
                 `return ${variableMatch};` +
             ``;
             const regExp: RegExp = new RegExp(match);
@@ -251,6 +250,7 @@ describe('ObjectExpressionKeysTransformer', () => {
                 `${variableMatch}\\['\\w{5}'] *= *function *\\(${variableMatch}, *${variableMatch}\\) *{` +
                     `return *${variableMatch} *\\+ *${variableMatch};` +
                 `};` +
+                `var *${variableMatch} *= *${variableMatch};` +
                 `var *${variableMatch} *= *${variableMatch}\\['\\w{5}']\\(0x1, *0x2\\);` +
             ``;
             const regExp: RegExp = new RegExp(match);
@@ -282,6 +282,7 @@ describe('ObjectExpressionKeysTransformer', () => {
                 `${variableMatch}\\['\\w{5}'] *= *function *\\(${variableMatch}, *${variableMatch}\\) *{` +
                     `return *${variableMatch} *\\+ *${variableMatch};` +
                 `};` +
+                `var *${variableMatch} *= *${variableMatch};` +
                 `var *${variableMatch} *= *{};` +
                 `${variableMatch}\\['foo'] *= *0x1;` +
                 `var *${variableMatch} *= *{};` +
@@ -370,8 +371,7 @@ describe('ObjectExpressionKeysTransformer', () => {
 
             describe('Variant #3', () => {
                 const match: string = `` +
-                    `var *${variableMatch} *= *0x1, *${variableMatch} *= *{};` +
-                    `${variableMatch}\\['foo'] *= *${variableMatch};` +
+                    `var *${variableMatch} *= *0x1, *${variableMatch} *= *{'foo' *: *${variableMatch}};` +
                 ``;
                 const regExp: RegExp = new RegExp(match);
 
@@ -540,9 +540,11 @@ describe('ObjectExpressionKeysTransformer', () => {
 
                 describe('Variant #2: Multiple variable declarators and object expression parent node is variable declarator node', () => {
                     const match: string = `` +
-                        `var foo *= *{}, *bar *= *{};` +
-                        `bar\\['bar'] *= *0x2;` +
-                        `foo\\['foo'] *= *0x1;` +
+                        `var ${variableMatch} *= *{};` +
+                        `${variableMatch}\\['foo'] *= *0x1;` +
+                        `var ${variableMatch} *= *{};` +
+                        `${variableMatch}\\['bar'] *= *0x2;` +
+                        `var foo *= *${variableMatch}, *bar *= *${variableMatch};` +
                     ``;
                     const regExp: RegExp = new RegExp(match);
 
@@ -572,8 +574,7 @@ describe('ObjectExpressionKeysTransformer', () => {
                         `var *passthrough *= *${variableMatch} *=> *${variableMatch};` +
                         `var *${variableMatch} *= *{};` +
                         `${variableMatch}\\['foo'] *= *0x1;` +
-                        `var foo *= *${variableMatch}, *bar *= *{};` +
-                        `bar\\['bar'] *= *foo.foo;` +
+                        `var foo *= *${variableMatch}, *bar *= *{'bar': *foo\\['foo']};` +
                     ``;
                     const regExp: RegExp = new RegExp(match);
 
@@ -630,9 +631,10 @@ describe('ObjectExpressionKeysTransformer', () => {
     describe('member expression as host of object expression', () => {
         describe('Variant #1: simple', () => {
             const match: string = `` +
-                `this\\['state'] *= *{};` +
-                `this\\['state']\\['foo'] *= *'bar';` +
-                `this\\['state']\\['baz'] *= *'bark';` +
+                `var ${variableMatch} *= *{};` +
+                `${variableMatch}\\['foo'] *= *'bar';` +
+                `${variableMatch}\\['baz'] *= *'bark';` +
+                `this\\['state'] *= *${variableMatch};` +
             ``;
             const regExp: RegExp = new RegExp(match);
 
@@ -657,9 +659,10 @@ describe('ObjectExpressionKeysTransformer', () => {
 
         describe('Variant #2: long members chain', () => {
             const match: string = `` +
-                `this\\['state']\\['foo'] *= *{};` +
-                `this\\['state']\\['foo']\\['foo'] *= *'bar';` +
-                `this\\['state']\\['foo']\\['baz'] *= *'bark';` +
+                `var ${variableMatch} *= *{};` +
+                `${variableMatch}\\['foo'] *= *'bar';` +
+                `${variableMatch}\\['baz'] *= *'bark';` +
+                `this\\['state']\\['foo'] *= *${variableMatch};` +
             ``;
             const regExp: RegExp = new RegExp(match);
 
@@ -688,8 +691,9 @@ describe('ObjectExpressionKeysTransformer', () => {
             describe('Variant #1: with block statement', () => {
                 const match: string = `` +
                     `if *\\(!!\\[]\\) *{` +
-                    `var *${variableMatch} *= *{};` +
-                    `${variableMatch}\\['foo'] *= *'bar';` +
+                        `var ${variableMatch} *= *{};` +
+                        `${variableMatch}\\['foo'] *= *'bar';` +
+                        `var ${variableMatch} *= *${variableMatch};` +
                     `}` +
                 ``;
                 const regExp: RegExp = new RegExp(match);
@@ -779,8 +783,9 @@ describe('ObjectExpressionKeysTransformer', () => {
             describe('Variant #1: with block statement', () => {
                 const match: string = `` +
                     `for *\\(var *${variableMatch} *= *0x0; *${variableMatch} *< *0xa; *${variableMatch}\\+\\+\\) *{` +
-                        `var *${variableMatch} *= *{};` +
+                        `var ${variableMatch} *= *{};` +
                         `${variableMatch}\\['foo'] *= *'bar';` +
+                        `var ${variableMatch} *= *${variableMatch};` +
                     `}` +
                 ``;
                 const regExp: RegExp = new RegExp(match);
@@ -839,8 +844,9 @@ describe('ObjectExpressionKeysTransformer', () => {
                 const match: string = `` +
                     `var ${variableMatch} *= *{};` +
                     `for *\\(var *${variableMatch} in *${variableMatch}\\) *{` +
-                        `${variableMatch} *= *{};` +
+                        `var ${variableMatch} *= *{};` +
                         `${variableMatch}\\['bar'] *= *'bar';` +
+                        `${variableMatch} *= *${variableMatch};` +
                     `}` +
                 ``;
                 const regExp: RegExp = new RegExp(match);
@@ -899,8 +905,9 @@ describe('ObjectExpressionKeysTransformer', () => {
                 const match: string = `` +
                     `var ${variableMatch} *= *\\[];` +
                     `for *\\(var *${variableMatch} of *${variableMatch}\\) *{` +
-                        `${variableMatch} *= *{};` +
+                        `var ${variableMatch} *= *{};` +
                         `${variableMatch}\\['bar'] *= *'bar';` +
+                        `${variableMatch} *= *${variableMatch};` +
                     `}` +
                 ``;
                 const regExp: RegExp = new RegExp(match);
@@ -960,6 +967,7 @@ describe('ObjectExpressionKeysTransformer', () => {
                     `while *\\(!!\\[]\\) *{` +
                         `var *${variableMatch} *= *{};` +
                         `${variableMatch}\\['foo'] *= *'bar';` +
+                        `var ${variableMatch} *= *${variableMatch};` +
                     `}` +
                 ``;
                 const regExp: RegExp = new RegExp(match);
@@ -1046,6 +1054,7 @@ describe('ObjectExpressionKeysTransformer', () => {
                 `try *{` +
                     `var *${variableMatch} *= *{};` +
                     `${variableMatch}\\['foo'] *= *'bar';` +
+                    `var ${variableMatch} *= *${variableMatch};` +
                 `} *catch *\\(${variableMatch}\\) *{` +
                 `}` +
             ``;
@@ -1076,6 +1085,7 @@ describe('ObjectExpressionKeysTransformer', () => {
                 `} *catch *\\(${variableMatch}\\) *{` +
                     `var *${variableMatch} *= *{};` +
                     `${variableMatch}\\['foo'] *= *'bar';` +
+                    `var ${variableMatch} *= *${variableMatch};` +
                 `}` +
             ``;
             const regExp: RegExp = new RegExp(match);
@@ -1105,6 +1115,7 @@ describe('ObjectExpressionKeysTransformer', () => {
                     `case *!!\\[]:` +
                         `var *${variableMatch} *= *{};` +
                         `${variableMatch}\\['foo'] *= *'bar';` +
+                    `var ${variableMatch} *= *${variableMatch};` +
                 `}` +
             ``;
             const regExp: RegExp = new RegExp(match);
@@ -1133,6 +1144,7 @@ describe('ObjectExpressionKeysTransformer', () => {
                 const match: string = `` +
                     `const *${variableMatch} *= *{}; *` +
                     `${variableMatch}\\['foo'] *= *'foo'; *` +
+                    `const ${variableMatch} *= *${variableMatch};` +
                     `const *${variableMatch} *= *${variableMatch}\\['foo'];` +
                 ``;
                 const regExp: RegExp = new RegExp(match);
@@ -1158,10 +1170,11 @@ describe('ObjectExpressionKeysTransformer', () => {
 
             describe('Variant #2', () => {
                 const match: string = `` +
-                    `const *${variableMatch} *= *0x1, *` +
-                        `${variableMatch} *= *{}; *` +
+                    `const ${variableMatch} *= *{}; *` +
                     `${variableMatch}\\['foo'] *= *'foo'; *` +
-                    `const *${variableMatch} *= *${variableMatch}\\['foo'];` +
+                    `const ${variableMatch} *= *0x1, *` +
+                        `${variableMatch} *= *${variableMatch}; *` +
+                    `const ${variableMatch} *= *${variableMatch}\\['foo'];` +
                 ``;
                 const regExp: RegExp = new RegExp(match);
 
@@ -1186,12 +1199,13 @@ describe('ObjectExpressionKeysTransformer', () => {
 
             describe('Variant #3: two objects', () => {
                 const match: string = `` +
+                    `var *${variableMatch} *= *{};` +
+                    `${variableMatch}\\['foo'] *= *'foo';` +
                     `var *${variableMatch} *= *{};` +
                     `${variableMatch}\\['bar'] *= *'bar';` +
-                    `var *${variableMatch} *= *{}, *` +
+                    `var *${variableMatch} *= *${variableMatch}, *` +
                         `${variableMatch} *= *${variableMatch}, *` +
                         `${variableMatch} *= *${variableMatch}\\['bar']; *` +
-                    `${variableMatch}\\['foo'] *= *'foo';` +
                     `console\\['log']\\(${variableMatch}\\);` +
                 ``;
                 const regExp: RegExp = new RegExp(match);

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác