Browse Source

Merge pull request #520 from javascript-obfuscator/object-expression-keys-transformer-rework

Object expression keys transformer rework
Timofey Kachalov 5 năm trước cách đây
mục cha
commit
03ab22ad5c
50 tập tin đã thay đổi với 1152 bổ sung727 xóa
  1. 1 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. 13 3
      src/analyzers/string-array-storage-analyzer/StringArrayStorageAnalyzer.ts
  7. 2 2
      src/container/ServiceIdentifiers.ts
  8. 3 3
      src/container/modules/custom-nodes/CustomNodesModule.ts
  9. 15 20
      src/container/modules/node-transformers/ConvertingTransformersModule.ts
  10. 12 3
      src/custom-nodes/object-expression-keys-transformer-nodes/ObjectExpressionVariableDeclarationHostNode.ts
  11. 2 2
      src/enums/custom-nodes/ObjectExpressionKeysTransformerCustomNode.ts
  12. 4 0
      src/enums/node-transformers/converting-transformers/properties-extractors/ObjectExpressionExtractor.ts
  13. 0 5
      src/enums/node-transformers/converting-transformers/properties-extractors/PropertiesExtractor.ts
  14. 15 0
      src/interfaces/node-transformers/converting-transformers/object-expression-extractors/IObjectExpressionExtractor.ts
  15. 18 0
      src/interfaces/node-transformers/converting-transformers/object-expression-extractors/IObjectExpressionExtractorResult.ts
  16. 0 13
      src/interfaces/node-transformers/converting-transformers/properties-extractors/IPropertiesExtractor.ts
  17. 180 64
      src/node-transformers/converting-transformers/ObjectExpressionKeysTransformer.ts
  18. 2 22
      src/node-transformers/converting-transformers/ObjectExpressionTransformer.ts
  19. 77 118
      src/node-transformers/converting-transformers/object-expression-extractors/BasePropertiesExtractor.ts
  20. 148 0
      src/node-transformers/converting-transformers/object-expression-extractors/ObjectExpressionToVariableDeclarationExtractor.ts
  21. 0 81
      src/node-transformers/converting-transformers/properties-extractors/AssignmentExpressionPropertiesExtractor.ts
  22. 0 129
      src/node-transformers/converting-transformers/properties-extractors/BasePropertiesExtractor.ts
  23. 0 118
      src/node-transformers/converting-transformers/properties-extractors/VariableDeclaratorPropertiesExtractor.ts
  24. 32 11
      src/node-transformers/obfuscating-transformers/LiteralTransformer.ts
  25. 2 15
      src/node-transformers/obfuscating-transformers/obfuscating-replacers/literal-obfuscating-replacers/StringLiteralObfuscatingReplacer.ts
  26. 17 0
      src/node/NodeFactory.ts
  27. 0 52
      src/node/NodeGuards.ts
  28. 22 0
      src/node/NodeLiteralUtils.ts
  29. 1 0
      src/storages/string-array/StringArrayStorage.ts
  30. 6 0
      src/types/container/node-transformers/TObjectExpressionExtractorFactory.ts
  31. 0 5
      src/types/container/node-transformers/TPropertiesExtractorFactory.ts
  32. 5 6
      test/dev/dev.ts
  33. 279 29
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/ObjectExpressionKeysTransformer.spec.ts
  34. 7 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/computed-key-member-expression.js
  35. 1 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/function-default-value-reference.js
  36. 1 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/function-default-values.js
  37. 7 9
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/nested-objects-1.js
  38. 11 13
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/nested-objects-2.js
  39. 2 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/object-expression-inside-variable-declaration-1.js
  40. 2 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/object-expression-inside-variable-declaration-2.js
  41. 3 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/object-expression-inside-variable-declaration-3.js
  42. 3 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/object-expression-inside-variable-declaration-4.js
  43. 4 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/variable-declarator-with-object-call-6.js
  44. 6 1
      test/functional-tests/node-transformers/converting-transformers/split-string-transformer/SplitStringTransformer.spec.ts
  45. 60 2
      test/functional-tests/node-transformers/obfuscating-transformers/literal-transformer/LiteralTransformer.spec.ts
  46. 1 0
      test/functional-tests/node-transformers/obfuscating-transformers/literal-transformer/fixtures/object-expression-computed-key-literal.js
  47. 1 0
      test/functional-tests/node-transformers/obfuscating-transformers/literal-transformer/fixtures/object-expression-key-literal.js
  48. 1 0
      test/index.spec.ts
  49. 49 0
      test/unit-tests/analyzers/string-array-storage-analyzer/StringArrayStorageAnalyzer.spec.ts
  50. 136 0
      test/unit-tests/node/node-literal-utils/NodeLiteralUtils.spec.ts

+ 1 - 0
CHANGELOG.md

@@ -6,6 +6,7 @@ v0.24.0
 * Dynamic import and `import.meta` support. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/505
 * Now usage of some browser-related options with `target: 'node'` will cause a validation error
 * **CLI:** a file path will be displayed on obfuscation error. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/513
+* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/516
 * Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/512
 * Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/496
 * **Internal:** switched from `awesome-typescript-loader` on `ts-loader`

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


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


+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "javascript-obfuscator",
-  "version": "0.24.0-rc.0",
+  "version": "0.24.0-rc.1",
   "description": "JavaScript obfuscator",
   "keywords": [
     "obfuscator",

+ 13 - 3
src/analyzers/string-array-storage-analyzer/StringArrayStorageAnalyzer.ts

@@ -12,6 +12,7 @@ import { IStringArrayStorageItemData } from '../../interfaces/storages/string-ar
 
 import { NodeGuards } from '../../node/NodeGuards';
 import { NodeMetadata } from '../../node/NodeMetadata';
+import { NodeLiteralUtils } from '../../node/NodeLiteralUtils';
 
 /**
  * Adds values of literal nodes to the string array storage
@@ -67,7 +68,11 @@ export class StringArrayStorageAnalyzer implements IStringArrayStorageAnalyzer {
         }
 
         estraverse.traverse(astTree, {
-            enter: (node: ESTree.Node): estraverse.VisitorOption | void => {
+            enter: (node: ESTree.Node, parentNode: ESTree.Node | null): estraverse.VisitorOption | void => {
+                if (!parentNode) {
+                    return;
+                }
+
                 if (NodeMetadata.isIgnoredNode(node)) {
                     return estraverse.VisitorOption.Skip;
                 }
@@ -76,7 +81,7 @@ export class StringArrayStorageAnalyzer implements IStringArrayStorageAnalyzer {
                     return;
                 }
 
-                this.analyzeLiteralNode(node);
+                this.analyzeLiteralNode(node, parentNode);
             }
         });
     }
@@ -91,12 +96,17 @@ export class StringArrayStorageAnalyzer implements IStringArrayStorageAnalyzer {
 
     /**
      * @param {Literal} literalNode
+     * @param {Node} parentNode
      */
-    private analyzeLiteralNode (literalNode: ESTree.Literal): void {
+    private analyzeLiteralNode (literalNode: ESTree.Literal, parentNode: ESTree.Node): void {
         if (typeof literalNode.value !== 'string') {
             return;
         }
 
+        if (NodeLiteralUtils.isProhibitedLiteralNode(literalNode, parentNode)) {
+            return;
+        }
+
         if (!this.shouldAddValueToStringArray(literalNode.value)) {
             return;
         }

+ 2 - 2
src/container/ServiceIdentifiers.ts

@@ -12,7 +12,7 @@ export enum ServiceIdentifiers {
     Factory__IObfuscatedCode = 'Factory<IObfuscatedCode>',
     Factory__IObfuscatingReplacer = 'Factory<IObfuscatingReplacer>',
     Factory__IObjectExpressionKeysTransformerCustomNode = 'Factory<IObjectExpressionKeysTransformerCustomNode>',
-    Factory__IPropertiesExtractor = 'Factory<IPropertiesExtractor>',
+    Factory__IObjectExpressionExtractor = 'Factory<IObjectExpressionExtractor>',
     Factory__TControlFlowStorage = 'Factory<TControlFlowStorage>',
     IArrayUtils = 'IArrayUtils',
     ICalleeDataExtractor = 'ICalleeDataExtractor',
@@ -37,7 +37,7 @@ export enum ServiceIdentifiers {
     IOptionsNormalizer = 'IOptionsNormalizer',
     IObfuscatingReplacer = 'IObfuscatingReplacer',
     IPrevailingKindOfVariablesAnalyzer = 'IPrevailingKindOfVariablesAnalyzer',
-    IPropertiesExtractor = 'IPropertiesExtractor',
+    IObjectExpressionExtractor = 'IObjectExpressionExtractor',
     IRandomGenerator = 'IRandomGenerator',
     ISourceCode = 'ISourceCode',
     ISourceMapCorrector = 'ISourceMapCorrector',

+ 3 - 3
src/container/modules/custom-nodes/CustomNodesModule.ts

@@ -18,7 +18,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 { ObjectExpressionVariableDeclarationHostNode } from '../../../custom-nodes/object-expression-keys-transformer-nodes/ObjectExpressionVariableDeclarationHostNode';
 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';
@@ -127,8 +127,8 @@ export const customNodesModule: interfaces.ContainerModule = new ContainerModule
 
     // object expression keys transformer nodes
     bind<interfaces.Newable<ICustomNode>>(ServiceIdentifiers.Newable__ICustomNode)
-        .toConstructor(BasePropertiesExtractorObjectExpressionHostNode)
-        .whenTargetNamed(ObjectExpressionKeysTransformerCustomNode.BasePropertiesExtractorObjectExpressionHostNode);
+        .toConstructor(ObjectExpressionVariableDeclarationHostNode)
+        .whenTargetNamed(ObjectExpressionKeysTransformerCustomNode.ObjectExpressionVariableDeclarationHostNode);
 
     // node groups
     bind<ICustomNodeGroup>(ServiceIdentifiers.ICustomNodeGroup)

+ 15 - 20
src/container/modules/node-transformers/ConvertingTransformersModule.ts

@@ -3,20 +3,19 @@ import { InversifyContainerFacade } from '../../InversifyContainerFacade';
 import { ServiceIdentifiers } from '../../ServiceIdentifiers';
 
 import { INodeTransformer } from '../../../interfaces/node-transformers/INodeTransformer';
-import { IPropertiesExtractor } from '../../../interfaces/node-transformers/converting-transformers/properties-extractors/IPropertiesExtractor';
+import { IObjectExpressionExtractor } from '../../../interfaces/node-transformers/converting-transformers/object-expression-extractors/IObjectExpressionExtractor';
 
 import { NodeTransformer } from '../../../enums/node-transformers/NodeTransformer';
-import { PropertiesExtractor } from '../../../enums/node-transformers/converting-transformers/properties-extractors/PropertiesExtractor';
+import { ObjectExpressionExtractor } from '../../../enums/node-transformers/converting-transformers/properties-extractors/ObjectExpressionExtractor';
 
-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 { BasePropertiesExtractor } from '../../../node-transformers/converting-transformers/object-expression-extractors/BasePropertiesExtractor';
 
 export const convertingTransformersModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
     // converting transformers
@@ -44,23 +43,19 @@ export const convertingTransformersModule: interfaces.ContainerModule = new Cont
         .to(TemplateLiteralTransformer)
         .whenTargetNamed(NodeTransformer.TemplateLiteralTransformer);
 
-    // properties extractors
-    bind<IPropertiesExtractor>(ServiceIdentifiers.IPropertiesExtractor)
-        .to(AssignmentExpressionPropertiesExtractor)
-        .whenTargetNamed(PropertiesExtractor.AssignmentExpressionPropertiesExtractor);
+    // object expression extractors
+    bind<IObjectExpressionExtractor>(ServiceIdentifiers.IObjectExpressionExtractor)
+        .to(ObjectExpressionToVariableDeclarationExtractor)
+        .whenTargetNamed(ObjectExpressionExtractor.ObjectExpressionToVariableDeclarationExtractor);
 
-    bind<IPropertiesExtractor>(ServiceIdentifiers.IPropertiesExtractor)
+    bind<IObjectExpressionExtractor>(ServiceIdentifiers.IObjectExpressionExtractor)
         .to(BasePropertiesExtractor)
-        .whenTargetNamed(PropertiesExtractor.BasePropertiesExtractor);
+        .whenTargetNamed(ObjectExpressionExtractor.BasePropertiesExtractor);
 
-    bind<IPropertiesExtractor>(ServiceIdentifiers.IPropertiesExtractor)
-        .to(VariableDeclaratorPropertiesExtractor)
-        .whenTargetNamed(PropertiesExtractor.VariableDeclaratorPropertiesExtractor);
-
-    // properties extractor factory
-    bind<IPropertiesExtractor>(ServiceIdentifiers.Factory__IPropertiesExtractor)
-        .toFactory<IPropertiesExtractor>(InversifyContainerFacade
-            .getCacheFactory<PropertiesExtractor, IPropertiesExtractor>(
-                ServiceIdentifiers.IPropertiesExtractor
+    // object expression extractor factory
+    bind<IObjectExpressionExtractor>(ServiceIdentifiers.Factory__IObjectExpressionExtractor)
+        .toFactory<IObjectExpressionExtractor>(InversifyContainerFacade
+            .getCacheFactory<ObjectExpressionExtractor, IObjectExpressionExtractor>(
+                ServiceIdentifiers.IObjectExpressionExtractor
             ));
 });

+ 12 - 3
src/custom-nodes/object-expression-keys-transformer-nodes/BasePropertiesExtractorObjectExpressionHostNode.ts → src/custom-nodes/object-expression-keys-transformer-nodes/ObjectExpressionVariableDeclarationHostNode.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';
 
@@ -12,7 +14,12 @@ import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeFactory } from '../../node/NodeFactory';
 
 @injectable()
-export class BasePropertiesExtractorObjectExpressionHostNode extends AbstractCustomNode {
+export class ObjectExpressionVariableDeclarationHostNode 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'

+ 2 - 2
src/enums/custom-nodes/ObjectExpressionKeysTransformerCustomNode.ts

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

+ 4 - 0
src/enums/node-transformers/converting-transformers/properties-extractors/ObjectExpressionExtractor.ts

@@ -0,0 +1,4 @@
+export enum ObjectExpressionExtractor {
+    BasePropertiesExtractor = 'BasePropertiesExtractor',
+    ObjectExpressionToVariableDeclarationExtractor = 'ObjectExpressionToVariableDeclarationExtractor'
+}

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

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

+ 15 - 0
src/interfaces/node-transformers/converting-transformers/object-expression-extractors/IObjectExpressionExtractor.ts

@@ -0,0 +1,15 @@
+import * as ESTree from 'estree';
+
+import { IObjectExpressionExtractorResult } from './IObjectExpressionExtractorResult';
+
+export interface IObjectExpressionExtractor {
+    /**
+     * @param {ObjectExpression} objectExpressionNode
+     * @param {Statement} hostStatement
+     * @returns {IObjectExpressionExtractorResult}
+     */
+    extract (
+        objectExpressionNode: ESTree.ObjectExpression,
+        hostStatement: ESTree.Statement
+    ): IObjectExpressionExtractorResult;
+}

+ 18 - 0
src/interfaces/node-transformers/converting-transformers/object-expression-extractors/IObjectExpressionExtractorResult.ts

@@ -0,0 +1,18 @@
+import * as ESTree from 'estree';
+
+export interface IObjectExpressionExtractorResult {
+    /**
+     * @type {Node}
+     */
+    nodeToReplace: ESTree.Node;
+
+    /**
+     * @type {Statement}
+     */
+    objectExpressionHostStatement: ESTree.Statement;
+
+    /**
+     * @type {ObjectExpression}
+     */
+    objectExpressionNode: ESTree.ObjectExpression;
+}

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

@@ -1,13 +0,0 @@
-import * as ESTree from 'estree';
-
-export interface IPropertiesExtractor {
-    /**
-     * @param {ObjectExpression} objectExpressionNode
-     * @param {Node} hostNode
-     * @returns {Node}
-     */
-    extract (
-        objectExpressionNode: ESTree.ObjectExpression,
-        hostNode: ESTree.Node
-    ): ESTree.Node;
-}

+ 180 - 64
src/node-transformers/converting-transformers/ObjectExpressionKeysTransformer.ts

@@ -1,51 +1,164 @@
 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 { TObjectExpressionExtractorFactory } from '../../types/container/node-transformers/TObjectExpressionExtractorFactory';
 
 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 { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeGuards } from '../../node/NodeGuards';
+import { NodeStatementUtils } from '../../node/NodeStatementUtils';
+import { ObjectExpressionExtractor } from '../../enums/node-transformers/converting-transformers/properties-extractors/ObjectExpressionExtractor';
 
 @injectable()
 export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
     /**
-     * @type {Map<string, PropertiesExtractor>}
+     * @type {ObjectExpressionExtractor[]}
      */
-    private static readonly propertiesExtractorsMap: Map <string, PropertiesExtractor> = new Map([
-        [NodeType.AssignmentExpression, PropertiesExtractor.AssignmentExpressionPropertiesExtractor],
-        [NodeType.VariableDeclarator, PropertiesExtractor.VariableDeclaratorPropertiesExtractor]
-    ]);
+    private static readonly objectExpressionExtractorNames: ObjectExpressionExtractor[] = [
+        ObjectExpressionExtractor.ObjectExpressionToVariableDeclarationExtractor,
+        ObjectExpressionExtractor.BasePropertiesExtractor
+    ];
 
     /**
-     * @type {TPropertiesExtractorFactory}
+     * @type {TObjectExpressionExtractorFactory}
      */
-    private readonly propertiesExtractorFactory: TPropertiesExtractorFactory;
+    private readonly objectExpressionExtractorFactory: TObjectExpressionExtractorFactory;
 
     /**
-     * @param {TPropertiesExtractorFactory} propertiesExtractorFactory
+     * @param {TObjectExpressionExtractorFactory} objectExpressionExtractorFactory
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
     constructor (
-        @inject(ServiceIdentifiers.Factory__IPropertiesExtractor)
-            propertiesExtractorFactory: TPropertiesExtractorFactory,
+        @inject(ServiceIdentifiers.Factory__IObjectExpressionExtractor)
+            objectExpressionExtractorFactory: TObjectExpressionExtractorFactory,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
         super(randomGenerator, options);
 
-        this.propertiesExtractorFactory = propertiesExtractorFactory;
+        this.objectExpressionExtractorFactory = objectExpressionExtractorFactory;
+    }
+
+    /**
+     * @param {ObjectExpression} objectExpressionNode
+     * @param {Statement} hostStatement
+     * @returns {boolean}
+     */
+    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;
+        }
+
+        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;
+        }
+
+        const hostNode: ESTree.Node | undefined = objectExpressionNode.parentNode;
+
+        if (!hostNode || !NodeGuards.isAssignmentPatternNode(hostNode)) {
+            return false;
+        }
+
+        return ObjectExpressionKeysTransformer.isReferencedIdentifierName(
+            objectExpressionNode,
+            hostStatement.params
+        );
+    }
+
+    /**
+     * @param {ObjectExpression} objectExpressionNode
+     * @param {Node[]} nodesToSearch
+     * @returns {boolean}
+     */
+    private static isReferencedIdentifierName (
+        objectExpressionNode: ESTree.ObjectExpression,
+        nodesToSearch: ESTree.Node[],
+    ): boolean {
+        if (nodesToSearch.length === 1) {
+            return false;
+        }
+
+        const identifierNamesSet: string[] = [];
+
+        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;
     }
 
     /**
@@ -57,28 +170,22 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
             return null;
         }
 
-        if (transformationStage !== TransformationStage.Converting) {
-            return null;
-        }
+        switch (transformationStage) {
+            case TransformationStage.Converting:
+                return {
+                    leave: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (
+                            parentNode
+                            && NodeGuards.isObjectExpressionNode(node)
+                        ) {
+                            return this.transformNode(node, parentNode);
+                        }
+                    }
+                };
 
-        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);
-                }
-            }
-        };
+            default:
+                return null;
+        }
     }
 
     /**
@@ -89,9 +196,10 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
      *     };
      *
      * on:
-     *     var object = {};
-     *     object['foo'] = 1;
-     *     object['bar'] = 2;
+     *     var _0xabc123 = {};
+     *     _0xabc123['foo'] = 1;
+     *     _0xabc123['bar'] = 2;
+     *     var object = _0xabc123;
      *
      * @param {ObjectExpression} objectExpressionNode
      * @param {Node} parentNode
@@ -102,43 +210,51 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
             return objectExpressionNode;
         }
 
-        const propertiesExtractorName: PropertiesExtractor | undefined = ObjectExpressionKeysTransformer
-            .propertiesExtractorsMap
-            .get(parentNode.type);
+        const hostStatement: ESTree.Statement = NodeStatementUtils.getRootStatementOfNode(objectExpressionNode);
 
-        if (!propertiesExtractorName) {
+        if (ObjectExpressionKeysTransformer.isProhibitedHostStatement(objectExpressionNode, hostStatement)) {
             return objectExpressionNode;
         }
 
-        const propertiesExtractor: IPropertiesExtractor = this.propertiesExtractorFactory(propertiesExtractorName);
-
-        return propertiesExtractor.extract(objectExpressionNode, parentNode);
+        return this.applyObjectExpressionKeysExtractorsRecursive(
+            ObjectExpressionKeysTransformer.objectExpressionExtractorNames,
+            objectExpressionNode,
+            hostStatement
+        );
     }
 
     /**
-     * replaces:
-     *     return {
-     *          foo: 1,
-     *          bar: 2
-     *     };
-     *
-     * on:
-     *     var object = {};
-     *     object['foo'] = 1;
-     *     object['bar'] = 2;
-     *     return object;
-     *
+     * @param {ObjectExpressionExtractor[]} objectExpressionExtractorNames
      * @param {ObjectExpression} objectExpressionNode
-     * @param {Node} parentNode
-     * @returns {NodeGuards}
+     * @param {Statement} hostStatement
+     * @returns {Node}
      */
-    public transformNodeWithBaseExtractor (objectExpressionNode: ESTree.ObjectExpression, parentNode: ESTree.Node): ESTree.Node {
-        if (!objectExpressionNode.properties.length) {
+    private applyObjectExpressionKeysExtractorsRecursive (
+        objectExpressionExtractorNames: ObjectExpressionExtractor[],
+        objectExpressionNode: ESTree.ObjectExpression,
+        hostStatement: ESTree.Statement
+    ): ESTree.Node {
+        const newObjectExpressionExtractorNames: ObjectExpressionExtractor[] = [...objectExpressionExtractorNames];
+        const objectExpressionExtractor: ObjectExpressionExtractor | undefined =
+            newObjectExpressionExtractorNames.shift();
+
+        if (!objectExpressionExtractor) {
             return objectExpressionNode;
         }
 
-        const propertiesExtractor: IPropertiesExtractor = this.propertiesExtractorFactory(PropertiesExtractor.BasePropertiesExtractor);
+        const {
+            nodeToReplace,
+            objectExpressionHostStatement: newObjectExpressionHostStatement,
+            objectExpressionNode: newObjectExpressionNode
+        } = this.objectExpressionExtractorFactory(objectExpressionExtractor)
+            .extract(objectExpressionNode, hostStatement);
+
+        this.applyObjectExpressionKeysExtractorsRecursive(
+            newObjectExpressionExtractorNames,
+            newObjectExpressionNode,
+            newObjectExpressionHostStatement
+        );
 
-        return propertiesExtractor.extract(objectExpressionNode, parentNode);
+        return nodeToReplace;
     }
 }

+ 2 - 22
src/node-transformers/converting-transformers/ObjectExpressionTransformer.ts

@@ -12,7 +12,6 @@ import { TransformationStage } from '../../enums/node-transformers/Transformatio
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeFactory } from '../../node/NodeFactory';
 import { NodeGuards } from '../../node/NodeGuards';
-import { IEscapeSequenceEncoder } from '../../interfaces/utils/IEscapeSequenceEncoder';
 
 /**
  * replaces:
@@ -24,23 +23,14 @@ import { IEscapeSequenceEncoder } from '../../interfaces/utils/IEscapeSequenceEn
 @injectable()
 export class ObjectExpressionTransformer extends AbstractNodeTransformer {
     /**
-     * @type {IEscapeSequenceEncoder}
-     */
-    private readonly escapeSequenceEncoder: IEscapeSequenceEncoder;
-
-    /**
-     * @param {IEscapeSequenceEncoder} escapeSequenceEncoder
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
     constructor (
-        @inject(ServiceIdentifiers.IEscapeSequenceEncoder) escapeSequenceEncoder: IEscapeSequenceEncoder,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
         super(randomGenerator, options);
-
-        this.escapeSequenceEncoder = escapeSequenceEncoder;
     }
 
     /**
@@ -93,7 +83,7 @@ export class ObjectExpressionTransformer extends AbstractNodeTransformer {
             return;
         }
 
-        property.key = NodeFactory.literalNode(this.getPropertyKeyValue(property.key.value));
+        property.key = NodeFactory.literalNode(property.key.value);
     }
 
     /**
@@ -108,16 +98,6 @@ export class ObjectExpressionTransformer extends AbstractNodeTransformer {
             return;
         }
 
-        property.key = NodeFactory.literalNode(this.getPropertyKeyValue(property.key.name));
-    }
-
-    /**
-     * @param {string} inputValue
-     * @returns {string}
-     */
-    private getPropertyKeyValue (inputValue: string): string {
-        return this.options.unicodeEscapeSequence
-            ? this.escapeSequenceEncoder.encode(inputValue, true)
-            : inputValue;
+        property.key = NodeFactory.literalNode(property.key.name);
     }
 }

+ 77 - 118
src/node-transformers/converting-transformers/properties-extractors/AbstractPropertiesExtractor.ts → src/node-transformers/converting-transformers/object-expression-extractors/BasePropertiesExtractor.ts

@@ -1,13 +1,11 @@
-import { inject, injectable } from 'inversify';
-import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
+import { injectable } from 'inversify';
 
 import * as ESTree from 'estree';
 
 import { TNodeWithStatements } from '../../../types/node/TNodeWithStatements';
+import { IObjectExpressionExtractorResult } from '../../../interfaces/node-transformers/converting-transformers/object-expression-extractors/IObjectExpressionExtractorResult';
 
-import { IOptions } from '../../../interfaces/options/IOptions';
-import { IPropertiesExtractor } from '../../../interfaces/node-transformers/converting-transformers/properties-extractors/IPropertiesExtractor';
-import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
+import { IObjectExpressionExtractor } from '../../../interfaces/node-transformers/converting-transformers/object-expression-extractors/IObjectExpressionExtractor';
 
 import { NodeAppender } from '../../../node/NodeAppender';
 import { NodeFactory } from '../../../node/NodeFactory';
@@ -15,48 +13,12 @@ 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;
-    }
-
+export class BasePropertiesExtractor implements IObjectExpressionExtractor {
     /**
      * @param {Property} propertyNode
      * @returns {string | null}
      */
-    protected static getPropertyNodeKeyName (propertyNode: ESTree.Property): string | null {
-        if (!propertyNode.key) {
-            return null;
-        }
-
+    private static getPropertyNodeKeyName (propertyNode: ESTree.Property): string | null {
         const propertyKeyNode: ESTree.Expression = propertyNode.key;
 
         if (
@@ -80,7 +42,7 @@ export abstract class AbstractPropertiesExtractor implements IPropertiesExtracto
      * @param {Node} node
      * @returns {propertyValueNode is Pattern}
      */
-    protected static isProhibitedPattern (node: ESTree.Node): node is ESTree.Pattern {
+    private static isProhibitedPattern (node: ESTree.Node): node is ESTree.Pattern {
         return !node
             || NodeGuards.isObjectPatternNode(node)
             || NodeGuards.isArrayPatternNode(node)
@@ -89,22 +51,82 @@ export abstract class AbstractPropertiesExtractor implements IPropertiesExtracto
     }
 
     /**
+     * extracts object expression properties:
+     *     var _0xabc123 = {
+     *          foo: 1,
+     *          bar: 2
+     *     };
+     *
+     * to:
+     *     var _0xabc123 = {};
+     *     _0xabc123['foo'] = 1;
+     *     _0xabc123['bar'] = 2;
+     *
      * @param {ObjectExpression} objectExpressionNode
-     * @param {Node} hostNode
-     * @returns {Node}
+     * @param {Statement} hostStatement
+     * @returns {IObjectExpressionExtractorResult}
      */
-    public abstract extract (
+    public extract (
         objectExpressionNode: ESTree.ObjectExpression,
-        hostNode: ESTree.Node
-    ): ESTree.Node;
+        hostStatement: ESTree.Statement
+    ): IObjectExpressionExtractorResult {
+        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 {IObjectExpressionExtractorResult}
+     */
+    private transformObjectExpressionNode (
+        objectExpressionNode: ESTree.ObjectExpression,
+        hostStatement: ESTree.Statement,
+        memberExpressionHostNode: ESTree.Expression
+    ): IObjectExpressionExtractorResult {
+        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[]]}
+     * @returns {[ExpressionStatement[], number[]]}
      */
-    protected extractPropertiesToExpressionStatements (
+    private extractPropertiesToExpressionStatements (
         properties: ESTree.Property[],
+        hostStatement: ESTree.Statement,
         memberExpressionHostNode: ESTree.Expression
     ): [ESTree.ExpressionStatement[], number[]] {
         const propertiesLength: number = properties.length;
@@ -116,14 +138,14 @@ export abstract class AbstractPropertiesExtractor implements IPropertiesExtracto
             const propertyValue: ESTree.Expression | ESTree.Pattern = property.value;
 
             // invalid property nodes
-            if (AbstractPropertiesExtractor.isProhibitedPattern(propertyValue)) {
+            if (BasePropertiesExtractor.isProhibitedPattern(propertyValue)) {
                 continue;
             }
 
             /**
              * Stage 1: extract property node key names
              */
-            const propertyKeyName: string | null = AbstractPropertiesExtractor.getPropertyNodeKeyName(property);
+            const propertyKeyName: string | null = BasePropertiesExtractor.getPropertyNodeKeyName(property);
 
             if (!propertyKeyName) {
                 continue;
@@ -147,7 +169,7 @@ export abstract class AbstractPropertiesExtractor implements IPropertiesExtracto
              * Stage 3: recursively processing nested object expressions
              */
             if (NodeGuards.isObjectExpressionNode(property.value)) {
-                this.transformObjectExpressionNode(property.value, memberExpressionNode);
+                this.transformObjectExpressionNode(property.value, hostStatement, memberExpressionNode);
             }
 
             /**
@@ -164,74 +186,11 @@ export abstract class AbstractPropertiesExtractor implements IPropertiesExtracto
      * @param {ObjectExpression} objectExpressionNode
      * @param {number[]} removablePropertyIds
      */
-    protected filterExtractedObjectExpressionProperties (
+    private 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 properties: ESTree.Property[] = objectExpressionNode.properties;
-        const [expressionStatements, removablePropertyIds]: [ESTree.ExpressionStatement[], number[]] = this
-            .extractPropertiesToExpressionStatements(properties, memberExpressionHostNode);
-
-        const hostStatement: ESTree.Statement = this.getHostStatement(objectExpressionNode);
-        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;
-    }
 }

+ 148 - 0
src/node-transformers/converting-transformers/object-expression-extractors/ObjectExpressionToVariableDeclarationExtractor.ts

@@ -0,0 +1,148 @@
+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 { IObjectExpressionExtractorResult } from '../../../interfaces/node-transformers/converting-transformers/object-expression-extractors/IObjectExpressionExtractorResult';
+import { TStatement } from '../../../types/node/TStatement';
+
+import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
+import { TInitialData } from '../../../types/TInitialData';
+import { IObjectExpressionExtractor } from '../../../interfaces/node-transformers/converting-transformers/object-expression-extractors/IObjectExpressionExtractor';
+
+import { ObjectExpressionKeysTransformerCustomNode } from '../../../enums/custom-nodes/ObjectExpressionKeysTransformerCustomNode';
+
+import { ObjectExpressionVariableDeclarationHostNode } from '../../../custom-nodes/object-expression-keys-transformer-nodes/ObjectExpressionVariableDeclarationHostNode';
+import { NodeAppender } from '../../../node/NodeAppender';
+import { NodeGuards } from '../../../node/NodeGuards';
+import { NodeStatementUtils } from '../../../node/NodeStatementUtils';
+import { NodeUtils } from '../../../node/NodeUtils';
+
+@injectable()
+export class ObjectExpressionToVariableDeclarationExtractor implements IObjectExpressionExtractor {
+    /**
+     * @type {TObjectExpressionKeysTransformerCustomNodeFactory}
+     */
+    private readonly objectExpressionKeysTransformerCustomNodeFactory: TObjectExpressionKeysTransformerCustomNodeFactory;
+
+    /**
+     * @param {TObjectExpressionKeysTransformerCustomNodeFactory} objectExpressionKeysTransformerCustomNodeFactory
+     */
+    constructor (
+        @inject(ServiceIdentifiers.Factory__IObjectExpressionKeysTransformerCustomNode)
+            objectExpressionKeysTransformerCustomNodeFactory: TObjectExpressionKeysTransformerCustomNodeFactory,
+    ) {
+        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 {Statement} hostStatement
+     * @returns {IObjectExpressionExtractorResult}
+     */
+    public extract (
+        objectExpressionNode: ESTree.ObjectExpression,
+        hostStatement: ESTree.Statement
+    ): IObjectExpressionExtractorResult {
+        return this.transformObjectExpressionToVariableDeclaration(
+            objectExpressionNode,
+            hostStatement
+        );
+    }
+
+    /**
+     * @param {ObjectExpression} objectExpressionNode
+     * @param {Statement} hostStatement
+     * @returns {Node}
+     */
+    private transformObjectExpressionToVariableDeclaration (
+        objectExpressionNode: ESTree.ObjectExpression,
+        hostStatement: ESTree.Statement
+    ): IObjectExpressionExtractorResult {
+        const properties: ESTree.Property[] = objectExpressionNode.properties;
+
+        const newObjectExpressionHostStatement: ESTree.VariableDeclaration = this.getObjectExpressionHostNode(properties);
+        const newObjectExpressionIdentifier: ESTree.Identifier = this.getObjectExpressionIdentifierNode(newObjectExpressionHostStatement);
+        const newObjectExpressionNode: ESTree.ObjectExpression = this.getObjectExpressionNode(newObjectExpressionHostStatement);
+
+        const statementsToInsert: TStatement[] = [newObjectExpressionHostStatement];
+        const hostNodeWithStatements: TNodeWithStatements = NodeStatementUtils.getScopeOfNode(hostStatement);
+
+        NodeAppender.insertBefore(hostNodeWithStatements, statementsToInsert, hostStatement);
+        NodeUtils.parentizeAst(newObjectExpressionHostStatement);
+        NodeUtils.parentizeNode(newObjectExpressionHostStatement, hostNodeWithStatements);
+
+        return {
+            nodeToReplace: newObjectExpressionIdentifier,
+            objectExpressionHostStatement: newObjectExpressionHostStatement,
+            objectExpressionNode: newObjectExpressionNode
+        };
+    }
+
+    /**
+     * @param {Property[]} properties
+     * @returns {VariableDeclaration}
+     */
+    private getObjectExpressionHostNode (properties: ESTree.Property[]): ESTree.VariableDeclaration {
+        const variableDeclarationHostNodeCustomNode: ICustomNode<TInitialData<ObjectExpressionVariableDeclarationHostNode>> =
+            this.objectExpressionKeysTransformerCustomNodeFactory(
+                ObjectExpressionKeysTransformerCustomNode.ObjectExpressionVariableDeclarationHostNode
+            );
+
+        variableDeclarationHostNodeCustomNode.initialize(properties);
+
+        const statementNode: TStatement = variableDeclarationHostNodeCustomNode.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;
+    }
+
+    /**
+     * @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 - 81
src/node-transformers/converting-transformers/properties-extractors/AssignmentExpressionPropertiesExtractor.ts

@@ -1,81 +0,0 @@
-import { inject, injectable } from 'inversify';
-import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
-
-import * as ESTree from 'estree';
-
-import { IOptions } from '../../../interfaces/options/IOptions';
-import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
-
-import { 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 {Node}
-     */
-    public extract (
-        objectExpressionNode: ESTree.ObjectExpression,
-        hostNode: ESTree.AssignmentExpression
-    ): ESTree.Node {
-        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 - 129
src/node-transformers/converting-transformers/properties-extractors/BasePropertiesExtractor.ts

@@ -1,129 +0,0 @@
-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 { TInitialData } from '../../../types/TInitialData';
-import { IOptions } from '../../../interfaces/options/IOptions';
-import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
-
-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';
-
-@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<TInitialData<BasePropertiesExtractorObjectExpressionHostNode>> =
-            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;
-    }
-}

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

@@ -1,118 +0,0 @@
-import { inject, injectable } from 'inversify';
-import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
-
-import * as estraverse from 'estraverse';
-import * as ESTree from 'estree';
-
-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 {Node}
-     */
-    public extract (
-        objectExpressionNode: ESTree.ObjectExpression,
-        hostNode: ESTree.VariableDeclarator
-    ): ESTree.Node {
-        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;
-    }
-}

+ 32 - 11
src/node-transformers/obfuscating-transformers/LiteralTransformer.ts

@@ -5,6 +5,7 @@ import * as ESTree from 'estree';
 
 import { TLiteralObfuscatingReplacerFactory } from '../../types/container/node-transformers/TLiteralObfuscatingReplacerFactory';
 
+import { IEscapeSequenceEncoder } from '../../interfaces/utils/IEscapeSequenceEncoder';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IStringArrayStorageAnalyzer } from '../../interfaces/analyzers/string-array-storage-analyzer/IStringArrayStorageAnalyzer';
@@ -14,12 +15,19 @@ import { LiteralObfuscatingReplacer } from '../../enums/node-transformers/obfusc
 import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
+import { NodeFactory } from '../../node/NodeFactory';
 import { NodeGuards } from '../../node/NodeGuards';
+import { NodeLiteralUtils } from '../../node/NodeLiteralUtils';
 import { NodeMetadata } from '../../node/NodeMetadata';
 import { NodeUtils } from '../../node/NodeUtils';
 
 @injectable()
 export class LiteralTransformer extends AbstractNodeTransformer {
+    /**
+     * @type {IEscapeSequenceEncoder}
+     */
+    private readonly escapeSequenceEncoder: IEscapeSequenceEncoder;
+
     /**
      * @type {TLiteralObfuscatingReplacerFactory}
      */
@@ -35,18 +43,21 @@ export class LiteralTransformer extends AbstractNodeTransformer {
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      * @param {IStringArrayStorageAnalyzer} stringArrayStorageAnalyzer
+     * @param {IEscapeSequenceEncoder} escapeSequenceEncoder
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IObfuscatingReplacer)
             literalObfuscatingReplacerFactory: TLiteralObfuscatingReplacerFactory,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions,
-        @inject(ServiceIdentifiers.IStringArrayStorageAnalyzer) stringArrayStorageAnalyzer: IStringArrayStorageAnalyzer
+        @inject(ServiceIdentifiers.IStringArrayStorageAnalyzer) stringArrayStorageAnalyzer: IStringArrayStorageAnalyzer,
+        @inject(ServiceIdentifiers.IEscapeSequenceEncoder) escapeSequenceEncoder: IEscapeSequenceEncoder
     ) {
         super(randomGenerator, options);
 
         this.literalObfuscatingReplacerFactory = literalObfuscatingReplacerFactory;
         this.stringArrayStorageAnalyzer = stringArrayStorageAnalyzer;
+        this.escapeSequenceEncoder = escapeSequenceEncoder;
     }
 
     /**
@@ -68,6 +79,15 @@ export class LiteralTransformer extends AbstractNodeTransformer {
                     }
                 };
 
+            case TransformationStage.Finalizing:
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (parentNode && NodeGuards.isLiteralNode(node)) {
+                            return this.encodeLiteralNodeToEscapeSequence(node, parentNode);
+                        }
+                    }
+                };
+
             default:
                 return null;
         }
@@ -83,7 +103,7 @@ export class LiteralTransformer extends AbstractNodeTransformer {
      * @returns {NodeGuards}
      */
     public transformNode (literalNode: ESTree.Literal, parentNode: ESTree.Node): ESTree.Node {
-        if (this.isProhibitedNode(literalNode, parentNode)) {
+        if (NodeLiteralUtils.isProhibitedLiteralNode(literalNode, parentNode)) {
             return literalNode;
         }
 
@@ -123,17 +143,18 @@ export class LiteralTransformer extends AbstractNodeTransformer {
     /**
      * @param {Literal} literalNode
      * @param {Node} parentNode
-     * @returns {boolean}
+     * @returns {Literal}
      */
-    private isProhibitedNode (literalNode: ESTree.Literal, parentNode: ESTree.Node): boolean {
-        if (NodeGuards.isPropertyNode(parentNode) && parentNode.key === literalNode) {
-            return true;
-        }
-
-        if (NodeGuards.isImportDeclarationNode(parentNode)) {
-            return true;
+    private encodeLiteralNodeToEscapeSequence (
+        literalNode: ESTree.Literal,
+        parentNode: ESTree.Node
+    ): ESTree.Literal {
+        if (typeof literalNode.value !== 'string') {
+            return literalNode;
         }
 
-        return false;
+        return NodeFactory.literalNode(
+            this.escapeSequenceEncoder.encode(literalNode.value, this.options.unicodeEscapeSequence)
+        );
     }
 }

+ 2 - 15
src/node-transformers/obfuscating-transformers/obfuscating-replacers/literal-obfuscating-replacers/StringLiteralObfuscatingReplacer.ts

@@ -3,7 +3,6 @@ import { ServiceIdentifiers } from '../../../../container/ServiceIdentifiers';
 
 import * as ESTree from 'estree';
 
-import { IEscapeSequenceEncoder } from '../../../../interfaces/utils/IEscapeSequenceEncoder';
 import { IInitializable } from '../../../../interfaces/IInitializable';
 import { IOptions } from '../../../../interfaces/options/IOptions';
 import { IStringArrayStorage } from '../../../../interfaces/storages/string-array-storage/IStringArrayStorage';
@@ -22,11 +21,6 @@ import { Utils } from '../../../../utils/Utils';
 
 @injectable()
 export class StringLiteralObfuscatingReplacer extends AbstractObfuscatingReplacer implements IInitializable {
-    /**
-     * @type {IEscapeSequenceEncoder}
-     */
-    private readonly escapeSequenceEncoder: IEscapeSequenceEncoder;
-
     /**
      * @type {Map<string, ESTree.Node>}
      */
@@ -51,20 +45,17 @@ export class StringLiteralObfuscatingReplacer extends AbstractObfuscatingReplace
     /**
      * @param {IStringArrayStorage} stringArrayStorage
      * @param {IStringArrayStorageAnalyzer} stringArrayStorageAnalyzer
-     * @param {IEscapeSequenceEncoder} escapeSequenceEncoder
      * @param {IOptions} options
      */
     constructor (
         @inject(ServiceIdentifiers.TStringArrayStorage) stringArrayStorage: IStringArrayStorage,
         @inject(ServiceIdentifiers.IStringArrayStorageAnalyzer) stringArrayStorageAnalyzer: IStringArrayStorageAnalyzer,
-        @inject(ServiceIdentifiers.IEscapeSequenceEncoder) escapeSequenceEncoder: IEscapeSequenceEncoder,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
         super(options);
 
         this.stringArrayStorage = stringArrayStorage;
         this.stringArrayStorageAnalyzer = stringArrayStorageAnalyzer;
-        this.escapeSequenceEncoder = escapeSequenceEncoder;
     }
 
     /**
@@ -138,9 +129,7 @@ export class StringLiteralObfuscatingReplacer extends AbstractObfuscatingReplace
      * @returns {Node}
      */
     private replaceWithLiteralNode (value: string): ESTree.Node {
-        return NodeFactory.literalNode(
-            this.escapeSequenceEncoder.encode(value, this.options.unicodeEscapeSequence)
-        );
+        return NodeFactory.literalNode(value);
     }
 
     /**
@@ -156,9 +145,7 @@ export class StringLiteralObfuscatingReplacer extends AbstractObfuscatingReplace
         ];
 
         if (decodeKey) {
-            callExpressionArgs.push(StringLiteralObfuscatingReplacer.getRc4KeyLiteralNode(
-                this.escapeSequenceEncoder.encode(decodeKey, this.options.unicodeEscapeSequence)
-            ));
+            callExpressionArgs.push(StringLiteralObfuscatingReplacer.getRc4KeyLiteralNode(decodeKey));
         }
 
         const stringArrayIdentifierNode: ESTree.Identifier = NodeFactory.identifierNode(this.stringArrayStorageCallsWrapperName);

+ 17 - 0
src/node/NodeFactory.ts

@@ -228,6 +228,23 @@ export class NodeFactory {
         };
     }
 
+    /**
+     * @param {(ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier)[]} specifiers
+     * @param {Literal} source
+     * @returns {ImportDeclaration}
+     */
+    public static importDeclarationNode (
+        specifiers: (ESTree.ImportSpecifier | ESTree.ImportDefaultSpecifier | ESTree.ImportNamespaceSpecifier)[],
+        source: ESTree.Literal
+    ): ESTree.ImportDeclaration {
+        return {
+            type: NodeType.ImportDeclaration,
+            specifiers,
+            source,
+            metadata: { ignoredNode: false }
+        };
+    }
+
     /**
      * @param {boolean | number | string} value
      * @param {string} raw

+ 0 - 52
src/node/NodeGuards.ts

@@ -80,14 +80,6 @@ export class NodeGuards {
         return node.type === NodeType.CallExpression;
     }
 
-    /**
-     * @param {Node} node
-     * @returns {boolean}
-     */
-    public static isCatchClauseNode (node: ESTree.Node): node is ESTree.CatchClause {
-        return node.type === NodeType.CatchClause;
-    }
-
     /**
      * @param {Node} node
      * @returns {boolean}
@@ -132,42 +124,6 @@ export class NodeGuards {
             && !('directive' in node);
     }
 
-    /**
-     * @param {Node} node
-     * @returns {boolean}
-     */
-    public static isForStatementTypeNode (
-        node: ESTree.Node
-    ): node is ESTree.ForStatement | ESTree.ForInStatement | ESTree.ForOfStatement {
-        return NodeGuards.isForStatementNode(node)
-            || NodeGuards.isForInStatementNode(node)
-            || NodeGuards.isForOfStatementNode(node);
-    }
-
-    /**
-     * @param {Node} node
-     * @returns {boolean}
-     */
-    public static isForStatementNode (node: ESTree.Node): node is ESTree.ForStatement {
-        return node.type === NodeType.ForStatement;
-    }
-
-    /**
-     * @param {Node} node
-     * @returns {boolean}
-     */
-    public static isForInStatementNode (node: ESTree.Node): node is ESTree.ForInStatement {
-        return node.type === NodeType.ForInStatement;
-    }
-
-    /**
-     * @param {Node} node
-     * @returns {boolean}
-     */
-    public static isForOfStatementNode (node: ESTree.Node): node is ESTree.ForOfStatement {
-        return node.type === NodeType.ForOfStatement;
-    }
-
     /**
      * @param {Node} node
      * @returns {boolean}
@@ -204,14 +160,6 @@ export class NodeGuards {
         return node.type === NodeType.Identifier;
     }
 
-    /**
-     * @param {Node} node
-     * @returns {boolean}
-     */
-    public static isIfStatementNode (node: ESTree.Node): node is ESTree.IfStatement {
-        return node.type === NodeType.IfStatement;
-    }
-
     /**
      * @param {Node} node
      * @returns {boolean}

+ 22 - 0
src/node/NodeLiteralUtils.ts

@@ -0,0 +1,22 @@
+import * as ESTree from 'estree';
+
+import { NodeGuards } from './NodeGuards';
+
+export class NodeLiteralUtils {
+    /**
+     * @param {Literal} literalNode
+     * @param {Node} parentNode
+     * @returns {boolean}
+     */
+    public static isProhibitedLiteralNode (literalNode: ESTree.Literal, parentNode: ESTree.Node): boolean {
+        if (NodeGuards.isPropertyNode(parentNode) && !parentNode.computed && parentNode.key === literalNode) {
+            return true;
+        }
+
+        if (NodeGuards.isImportDeclarationNode(parentNode)) {
+            return true;
+        }
+
+        return false;
+    }
+}

+ 1 - 0
src/storages/string-array/StringArrayStorage.ts

@@ -218,6 +218,7 @@ export class StringArrayStorage extends MapStorage <string, IStringArrayStorageI
         return Array
             .from(this.storage.values())
             .map((stringArrayStorageItemData: IStringArrayStorageItemData) => {
+                // we have to encode here, because of possible errors during `parse` of StringArrayCustomNode
                 return `'${this.escapeSequenceEncoder.encode(
                     stringArrayStorageItemData.encodedValue,
                     this.options.unicodeEscapeSequence

+ 6 - 0
src/types/container/node-transformers/TObjectExpressionExtractorFactory.ts

@@ -0,0 +1,6 @@
+import { IObjectExpressionExtractor } from '../../../interfaces/node-transformers/converting-transformers/object-expression-extractors/IObjectExpressionExtractor';
+
+import { ObjectExpressionExtractor } from '../../../enums/node-transformers/converting-transformers/properties-extractors/ObjectExpressionExtractor';
+
+export type TObjectExpressionExtractorFactory =
+    (objectExpressionExtractorName: ObjectExpressionExtractor) => IObjectExpressionExtractor;

+ 0 - 5
src/types/container/node-transformers/TPropertiesExtractorFactory.ts

@@ -1,5 +0,0 @@
-import { IPropertiesExtractor } from '../../../interfaces/node-transformers/converting-transformers/properties-extractors/IPropertiesExtractor';
-
-import { PropertiesExtractor } from '../../../enums/node-transformers/converting-transformers/properties-extractors/PropertiesExtractor';
-
-export type TPropertiesExtractorFactory = (propertiesExtractorName: PropertiesExtractor) => IPropertiesExtractor;

+ 5 - 6
test/dev/dev.ts

@@ -7,15 +7,14 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
 
     let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
         `
-            const foo = \`\${{
-                foo: 'bar',
-                baz: 'bark'
-            }.baz}\`;
-            console.log(foo);
+            var foo = {['foo']: 1};
         `,
         {
             ...NO_ADDITIONAL_NODES_PRESET,
-            transformObjectKeys: true
+            compact: false,
+            unicodeEscapeSequence: true,
+            stringArray: true,
+            stringArrayThreshold: 1
         }
     ).getObfuscatedCode();
 

+ 279 - 29
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);
 
@@ -151,11 +152,14 @@ describe('ObjectExpressionKeysTransformer', () => {
 
         describe('Variant #6: nested objects #1', () => {
             const match: string = `` +
+                `var *${variableMatch} *= *{};` +
+                `${variableMatch}\\['baz'] *= *'bark';` +
+                `var *${variableMatch} *= *{};` +
+                `${variableMatch}\\['inner1'] *= *${variableMatch};` +
                 `var *${variableMatch} *= *{};` +
                 `${variableMatch}\\['foo'] *= *'bar';` +
-                `${variableMatch}\\['inner'] *= *{};` +
-                `${variableMatch}\\['inner']\\['inner1'] *= *{};` +
-                `${variableMatch}\\['inner']\\['inner1']\\['baz'] *= *'bark';` +
+                `${variableMatch}\\['inner'] *= *${variableMatch};` +
+                `var object *= *${variableMatch};` +
             ``;
             const regExp: RegExp = new RegExp(match);
 
@@ -180,14 +184,17 @@ describe('ObjectExpressionKeysTransformer', () => {
 
         describe('Variant #7: nested objects #2', () => {
             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}\\['inner'] *= *${variableMatch};` +
                 `${variableMatch}\\['ball'] *= *'door';` +
-                `${variableMatch}\\['inner']\\['baz'] *= *'bark';` +
-                `${variableMatch}\\['inner']\\['inner1'] *= *{};` +
-                `${variableMatch}\\['inner']\\['cow'] *= *'bear';` +
-                `${variableMatch}\\['inner']\\['inner1']\\['hawk'] *= *'geek';` +
+                `var *object *= *${variableMatch};` +
             ``;
             const regExp: RegExp = new RegExp(match);
 
@@ -251,6 +258,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 +290,7 @@ describe('ObjectExpressionKeysTransformer', () => {
                 `${variableMatch}\\['\\w{5}'] *= *function *\\(${variableMatch}, *${variableMatch}\\) *{` +
                     `return *${variableMatch} *\\+ *${variableMatch};` +
                 `};` +
+                `var *${variableMatch} *= *${variableMatch};` +
                 `var *${variableMatch} *= *{};` +
                 `${variableMatch}\\['foo'] *= *0x1;` +
                 `var *${variableMatch} *= *{};` +
@@ -367,6 +376,31 @@ describe('ObjectExpressionKeysTransformer', () => {
                     assert.match(obfuscatedCode,  regExp);
                 });
             });
+
+            describe('Variant #3', () => {
+                const match: string = `` +
+                    `var *${variableMatch} *= *0x1, *${variableMatch} *= *{'foo' *: *${variableMatch}};` +
+                ``;
+                const regExp: RegExp = new RegExp(match);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/variable-declarator-with-object-call-6.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', () => {
@@ -452,15 +486,164 @@ describe('ObjectExpressionKeysTransformer', () => {
                 assert.match(obfuscatedCode,  regExp);
             });
         });
+
+        // issue https://github.com/javascript-obfuscator/javascript-obfuscator/issues/516
+        describe('Variant #15: function default values', () => {
+            const match: string = `` +
+                `var *${variableMatch} *= *{};` +
+                `${variableMatch}\\['value'] *= *0x1;` +
+                `function test *\\(${variableMatch} *= *0x1, *${variableMatch} *= *${variableMatch}\\) *{ *}` +
+            ``;
+            const regExp: RegExp = new RegExp(match);
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/function-default-values.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        transformObjectKeys: true
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('shouldn ignore default parameter object if it references other parameter', () => {
+                assert.match(obfuscatedCode,  regExp);
+            });
+        });
+
+        // issue https://github.com/javascript-obfuscator/javascript-obfuscator/issues/516
+        describe('Variant #16: object expression inside inside variable declaration', () => {
+            describe('Without reference on other property', () => {
+                describe('Variant #1: Single variable declarator and object expression parent node is expression node', () => {
+                    const match: string = `` +
+                        `var *passthrough *= *${variableMatch} *=> *${variableMatch};` +
+                        `var *${variableMatch} *= *{};` +
+                        `${variableMatch}\\['foo'] *= *0x1;` +
+                        `var foo *= *passthrough *\\(${variableMatch}\\);` +
+                    ``;
+                    const regExp: RegExp = new RegExp(match);
+
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/object-expression-inside-variable-declaration-1.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                transformObjectKeys: true
+                            }
+                        ).getObfuscatedCode();
+                    });
+
+                    it('should transform object expression keys', () => {
+                        assert.match(obfuscatedCode,  regExp);
+                    });
+                });
+
+                describe('Variant #2: Multiple variable declarators and object expression parent node is variable declarator node', () => {
+                    const match: string = `` +
+                        `var ${variableMatch} *= *{};` +
+                        `${variableMatch}\\['foo'] *= *0x1;` +
+                        `var ${variableMatch} *= *{};` +
+                        `${variableMatch}\\['bar'] *= *0x2;` +
+                        `var foo *= *${variableMatch}, *bar *= *${variableMatch};` +
+                    ``;
+                    const regExp: RegExp = new RegExp(match);
+
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/object-expression-inside-variable-declaration-2.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                transformObjectKeys: true
+                            }
+                        ).getObfuscatedCode();
+                    });
+
+                    it('should transform object expressions keys', () => {
+                        assert.match(obfuscatedCode,  regExp);
+                    });
+                });
+            });
+
+            describe('With reference on other property', () => {
+                describe('Variant #1: Object expression parent node is variable declarator node', () => {
+                    const match: string = `` +
+                        `var *passthrough *= *${variableMatch} *=> *${variableMatch};` +
+                        `var *${variableMatch} *= *{};` +
+                        `${variableMatch}\\['foo'] *= *0x1;` +
+                        `var foo *= *${variableMatch}, *bar *= *{'bar': *foo\\['foo']};` +
+                    ``;
+                    const regExp: RegExp = new RegExp(match);
+
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/object-expression-inside-variable-declaration-3.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                transformObjectKeys: true
+                            }
+                        ).getObfuscatedCode();
+                    });
+
+                    it('should transform first object expression keys and ignore second object expression keys', () => {
+                        assert.match(obfuscatedCode,  regExp);
+                    });
+                });
+
+                describe('Variant #2: Object expression parent node is any expression node', () => {
+                    const match: string = `` +
+                        `var *passthrough *= *${variableMatch} *=> *${variableMatch};` +
+                        `var *${variableMatch} *= *{};` +
+                        `${variableMatch}\\['foo'] *= *0x1;` +
+                        `var foo *= *${variableMatch}, *bar *= *passthrough *\\({ *'bar' *: *foo\\['foo'] *}\\);` +
+                    ``;
+                    const regExp: RegExp = new RegExp(match);
+
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/object-expression-inside-variable-declaration-4.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                transformObjectKeys: true
+                            }
+                        ).getObfuscatedCode();
+                    });
+
+                    it('should transform first object expression keys and ignore second object expression keys', () => {
+                        assert.match(obfuscatedCode,  regExp);
+                    });
+                });
+            });
+        });
     });
 
     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);
 
             let obfuscatedCode: string;
@@ -484,10 +667,11 @@ 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);
 
             let obfuscatedCode: string;
@@ -515,10 +699,11 @@ 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);
 
                 let obfuscatedCode: string;
@@ -606,8 +791,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);
@@ -666,8 +852,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);
@@ -726,8 +913,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);
@@ -787,6 +975,7 @@ describe('ObjectExpressionKeysTransformer', () => {
                     `while *\\(!!\\[]\\) *{` +
                         `var *${variableMatch} *= *{};` +
                         `${variableMatch}\\['foo'] *= *'bar';` +
+                        `var ${variableMatch} *= *${variableMatch};` +
                     `}` +
                 ``;
                 const regExp: RegExp = new RegExp(match);
@@ -873,6 +1062,7 @@ describe('ObjectExpressionKeysTransformer', () => {
                 `try *{` +
                     `var *${variableMatch} *= *{};` +
                     `${variableMatch}\\['foo'] *= *'bar';` +
+                    `var ${variableMatch} *= *${variableMatch};` +
                 `} *catch *\\(${variableMatch}\\) *{` +
                 `}` +
             ``;
@@ -903,6 +1093,7 @@ describe('ObjectExpressionKeysTransformer', () => {
                 `} *catch *\\(${variableMatch}\\) *{` +
                     `var *${variableMatch} *= *{};` +
                     `${variableMatch}\\['foo'] *= *'bar';` +
+                    `var ${variableMatch} *= *${variableMatch};` +
                 `}` +
             ``;
             const regExp: RegExp = new RegExp(match);
@@ -932,6 +1123,7 @@ describe('ObjectExpressionKeysTransformer', () => {
                     `case *!!\\[]:` +
                         `var *${variableMatch} *= *{};` +
                         `${variableMatch}\\['foo'] *= *'bar';` +
+                    `var ${variableMatch} *= *${variableMatch};` +
                 `}` +
             ``;
             const regExp: RegExp = new RegExp(match);
@@ -960,6 +1152,7 @@ describe('ObjectExpressionKeysTransformer', () => {
                 const match: string = `` +
                     `const *${variableMatch} *= *{}; *` +
                     `${variableMatch}\\['foo'] *= *'foo'; *` +
+                    `const ${variableMatch} *= *${variableMatch};` +
                     `const *${variableMatch} *= *${variableMatch}\\['foo'];` +
                 ``;
                 const regExp: RegExp = new RegExp(match);
@@ -985,10 +1178,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);
 
@@ -1013,12 +1207,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);
@@ -1176,5 +1371,60 @@ describe('ObjectExpressionKeysTransformer', () => {
                 assert.match(obfuscatedCode,  regExp);
             });
         });
+
+        describe('Variant #3: function default value reference', () => {
+            const match: string = `` +
+                `function test *\\(${variableMatch} *= *0x1, *${variableMatch} *= *{'value' *: *${variableMatch}}\\) *{ *}` +
+            ``;
+            const regExp: RegExp = new RegExp(match);
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/function-default-value-reference.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        transformObjectKeys: true
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('shouldn ignore default parameter object if it references other parameter', () => {
+                assert.match(obfuscatedCode,  regExp);
+            });
+        });
+
+        describe('Variant #4: member expression node as property key', () => {
+            const match: string = `` +
+                `var ${variableMatch} *= *{};` +
+                `${variableMatch}\\['foo'] *= *'test';` +
+                `var foo *= *${variableMatch};` +
+                `var ${variableMatch} *= *{\\[foo\\['foo']] *: *'1'};` +
+                `${variableMatch}\\['bar'] *= *'2';` +
+                `var bar *= *${variableMatch};` +
+            ``;
+            const regExp: RegExp = new RegExp(match);
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/computed-key-member-expression.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        transformObjectKeys: true
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('shouldn ignore extraction of property with member expression key', () => {
+                assert.match(obfuscatedCode,  regExp);
+            });
+        });
     });
 });

+ 7 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/computed-key-member-expression.js

@@ -0,0 +1,7 @@
+var foo = {
+    foo: 'test'
+};
+var bar = {
+    [foo.foo]: '1',
+    bar: '2'
+};

+ 1 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/function-default-value-reference.js

@@ -0,0 +1 @@
+function test (param1 = 1, param2 = {value: param1}) {}

+ 1 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/function-default-values.js

@@ -0,0 +1 @@
+function test (param1 = 1, param2 = {value: 1}) {}

+ 7 - 9
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/nested-objects-1.js

@@ -1,10 +1,8 @@
-(function(){
-    var object = {
-        foo: 'bar',
-        inner: {
-            inner1: {
-                baz: 'bark'
-            }
+var object = {
+    foo: 'bar',
+    inner: {
+        inner1: {
+            baz: 'bark'
         }
-    };
-})();
+    }
+};

+ 11 - 13
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/nested-objects-2.js

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

+ 2 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/object-expression-inside-variable-declaration-1.js

@@ -0,0 +1,2 @@
+var passthrough = (object) => object;
+var foo = passthrough({foo: 1});

+ 2 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/object-expression-inside-variable-declaration-2.js

@@ -0,0 +1,2 @@
+var foo = {foo: 1},
+    bar = {bar: 2};

+ 3 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/object-expression-inside-variable-declaration-3.js

@@ -0,0 +1,3 @@
+var passthrough = (object) => object;
+var foo = {foo: 1},
+    bar = {bar: foo.foo};

+ 3 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/object-expression-inside-variable-declaration-4.js

@@ -0,0 +1,3 @@
+var passthrough = (object) => object;
+var foo = {foo: 1},
+    bar = passthrough({bar: foo.foo});

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

@@ -0,0 +1,4 @@
+(function () {
+    var object = 1,
+        variable = {foo: object};
+})();

+ 6 - 1
test/functional-tests/node-transformers/converting-transformers/split-string-transformer/SplitStringTransformer.spec.ts

@@ -165,6 +165,11 @@ describe('SplitStringTransformer', () => {
 
     describe('Variant #10: Integration with `transformObjectKeys` option', () => {
         it('should correctly transform string when `transformObjectKeys` option is enabled', () => {
+            const regExp: RegExp = new RegExp(`` +
+                `var _0x[a-f0-9]{4,6} *= *{};` +
+                `*_0x[a-f0-9]{4,6}\\['ab' *\\+ *'cd' *\\+ *'ef' *\\+ *'g'] *= *'ab' *\\+ *'cd' *\\+ *'ef' *\\+ *'g';` +
+                `var test *= *_0x[a-f0-9]{4,6};` +
+            ``);
             const code: string = readFileAsString(__dirname + '/fixtures/object-string-literal.js');
 
             obfuscatedCode = JavaScriptObfuscator.obfuscate(
@@ -179,7 +184,7 @@ describe('SplitStringTransformer', () => {
 
             assert.match(
                 obfuscatedCode,
-                /^var *test *= *{}; *test\['ab' *\+ *'cd' *\+ *'ef' *\+ *'g'] *= *'ab' *\+ *'cd' *\+ *'ef' *\+ *'g';$/
+                regExp
             );
         });
     });

+ 60 - 2
test/functional-tests/node-transformers/obfuscating-transformers/literal-transformer/LiteralTransformer.spec.ts

@@ -135,7 +135,7 @@ describe('LiteralTransformer', () => {
 
         describe('Variant #6: `unicodeEscapeSequence` and `stringArray` options are enabled', () => {
             const stringArrayRegExp: RegExp = /^var *_0x([a-f0-9]){4} *= *\['\\x74\\x65\\x73\\x74'\];/;
-            const stringArrayCallRegExp: RegExp = /var *test *= *_0x([a-f0-9]){4}\('0x0'\);/;
+            const stringArrayCallRegExp: RegExp = /var *test *= *_0x([a-f0-9]){4}\('\\x30\\x78\\x30'\);/;
 
             let obfuscatedCode: string;
 
@@ -157,7 +157,7 @@ describe('LiteralTransformer', () => {
                 assert.match(obfuscatedCode, stringArrayRegExp);
             });
 
-            it('match #1: should replace literal node value with unicode escape sequence from string array', () => {
+            it('match #2: should replace literal node value with unicode escape sequence from string array', () => {
                 assert.match(obfuscatedCode, stringArrayCallRegExp);
             });
         });
@@ -518,6 +518,64 @@ describe('LiteralTransformer', () => {
                 });
             });
         });
+
+        describe('Variant #13: object expression key literal', () => {
+            describe('Variant #1: base key literal', () => {
+                const stringArrayRegExp: RegExp = /^var _0x([a-f0-9]){4} *= *\['bar'];/;
+                const objectExpressionRegExp: RegExp = /var test *= *{'foo' *: *_0x([a-f0-9]){4}\('0x0'\)};/;
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/object-expression-key-literal.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            stringArray: true,
+                            stringArrayThreshold: 1
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('match #1: should not add object expression key literal to the string array', () => {
+                    assert.match(obfuscatedCode, stringArrayRegExp);
+                });
+
+                it('match #2: should keep object expression key literal', () => {
+                    assert.match(obfuscatedCode, objectExpressionRegExp);
+                });
+            });
+
+            describe('Variant #2: computed key literal', () => {
+                const stringArrayRegExp: RegExp = /^var _0x([a-f0-9]){4} *= *\['foo', *'bar'];/;
+                const objectExpressionRegExp: RegExp = /var test *= *{\[_0x([a-f0-9]){4}\('0x0'\)] *: *_0x([a-f0-9]){4}\('0x1'\)};/;
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/object-expression-computed-key-literal.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            stringArray: true,
+                            stringArrayThreshold: 1
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('match #1: should add object expression computed key literal to the string array', () => {
+                    assert.match(obfuscatedCode, stringArrayRegExp);
+                });
+
+                it('match #2: should replace object expression computed key literal on call to the string array', () => {
+                    assert.match(obfuscatedCode, objectExpressionRegExp);
+                });
+            });
+        });
     });
 
     describe('transformation of literal node with boolean value', () => {

+ 1 - 0
test/functional-tests/node-transformers/obfuscating-transformers/literal-transformer/fixtures/object-expression-computed-key-literal.js

@@ -0,0 +1 @@
+var test = {['foo']: 'bar'};

+ 1 - 0
test/functional-tests/node-transformers/obfuscating-transformers/literal-transformer/fixtures/object-expression-key-literal.js

@@ -0,0 +1 @@
+var test = {'foo': 'bar'};

+ 1 - 0
test/index.spec.ts

@@ -28,6 +28,7 @@ import './unit-tests/node/node-appender/NodeAppender.spec';
 import './unit-tests/node/node-guards/NodeGuards.spec';
 import './unit-tests/node/node-metadata/NodeMetadata.spec';
 import './unit-tests/node/node-lexical-scope-utils/NodeLexicalScopeUtils.spec';
+import './unit-tests/node/node-literal-utils/NodeLiteralUtils.spec';
 import './unit-tests/node/node-statement-utils/NodeStatementUtils.spec';
 import './unit-tests/node/node-utils/NodeUtils.spec';
 import './unit-tests/node-transformers/preparing-transformers/ObfuscatingGuardsTransformer.spec';

+ 49 - 0
test/unit-tests/analyzers/string-array-storage-analyzer/StringArrayStorageAnalyzer.spec.ts

@@ -157,6 +157,55 @@ describe('StringArrayStorageAnalyzer', () => {
             });
         });
 
+        describe('Analyzes of the AST tree with prohibited string literal nodes', () => {
+            const literalNode1: ESTree.Literal = NodeFactory.literalNode('foo');
+            const literalNode2: ESTree.Literal = NodeFactory.literalNode('bar');
+
+            const expectedStringArrayStorageItemData1: IStringArrayStorageItemData = {
+                encodedValue: 'foo',
+                decodeKey: null,
+                index: 0,
+                value: 'foo'
+            };
+            const expectedStringArrayStorageItemData2: undefined = undefined;
+
+            let stringArrayStorageItemData1: IStringArrayStorageItemData | undefined;
+            let stringArrayStorageItemData2: IStringArrayStorageItemData | undefined;
+
+            before(() => {
+                stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
+                    stringArrayThreshold: 1
+                });
+
+                const astTree: ESTree.Program = NodeFactory.programNode([
+                    NodeFactory.expressionStatementNode(literalNode1),
+                    NodeFactory.variableDeclarationNode([
+                        NodeFactory.variableDeclaratorNode(
+                            NodeFactory.identifierNode('bar'),
+                            NodeFactory.objectExpressionNode([
+                                NodeFactory.propertyNode(
+                                    literalNode2,
+                                    NodeFactory.literalNode(1)
+                                )
+                            ])
+                        )
+                    ])
+                ]);
+
+                stringArrayStorageAnalyzer.analyze(astTree);
+                stringArrayStorageItemData1 = stringArrayStorageAnalyzer.getItemDataForLiteralNode(literalNode1);
+                stringArrayStorageItemData2 = stringArrayStorageAnalyzer.getItemDataForLiteralNode(literalNode2);
+            });
+
+            it('Variant #1: should return correct string array storage item data for literal node #1', () => {
+                assert.deepEqual(stringArrayStorageItemData1, expectedStringArrayStorageItemData1);
+            });
+
+            it('Variant #2: should return correct string array storage item data for literal node #1', () => {
+                assert.deepEqual(stringArrayStorageItemData2, expectedStringArrayStorageItemData2);
+            });
+        });
+
         describe('Analyzes of the AST tree with ignored nodes', () => {
             const literalNode1: ESTree.Literal = NodeFactory.literalNode('foo');
             const literalNode2: ESTree.Literal = NodeFactory.literalNode('bar');

+ 136 - 0
test/unit-tests/node/node-literal-utils/NodeLiteralUtils.spec.ts

@@ -0,0 +1,136 @@
+import * as ESTree from 'estree';
+
+import { assert } from 'chai';
+
+import { NodeFactory } from '../../../../src/node/NodeFactory';
+import { NodeLiteralUtils } from '../../../../src/node/NodeLiteralUtils';
+
+describe('NodeLiteralUtils', () => {
+    describe('isProhibitedLiteralNode', () => {
+        describe('String literal node', () => {
+            describe('Variant #1: base string literal node', () => {
+                const literalNode: ESTree.Literal = NodeFactory.literalNode('foo');
+
+                let statementNode: ESTree.Statement;
+
+                before(() => {
+                    statementNode = NodeFactory.expressionStatementNode(
+                        literalNode
+                    );
+
+                    literalNode.parentNode = statementNode;
+                });
+
+                it('should return false for base string literal node', () => {
+                    assert.equal(NodeLiteralUtils.isProhibitedLiteralNode(literalNode, statementNode), false);
+                });
+            });
+
+            describe('Variant #2: property literal node', () => {
+                describe('Variant #1: property key literal node', () => {
+                    const literalNode: ESTree.Literal = NodeFactory.literalNode('foo');
+
+                    let propertyNode: ESTree.Property;
+
+                    before(() => {
+                        propertyNode = NodeFactory.propertyNode(
+                            literalNode,
+                            NodeFactory.literalNode(1)
+                        );
+
+                        literalNode.parentNode = propertyNode;
+                    });
+
+                    it('should return false for property key literal node', () => {
+                        assert.equal(NodeLiteralUtils.isProhibitedLiteralNode(literalNode, propertyNode), true);
+                    });
+                });
+
+                describe('Variant #2: computed property key literal node', () => {
+                    const literalNode: ESTree.Literal = NodeFactory.literalNode('foo');
+
+                    let propertyNode: ESTree.Property;
+
+                    before(() => {
+                        propertyNode = NodeFactory.propertyNode(
+                            literalNode,
+                            NodeFactory.literalNode(1),
+                            true
+                        );
+
+                        literalNode.parentNode = propertyNode;
+                    });
+
+                    it('should return false for computed property key literal node', () => {
+                        assert.equal(NodeLiteralUtils.isProhibitedLiteralNode(literalNode, propertyNode), false);
+                    });
+                });
+
+                describe('Variant #3: property value literal node', () => {
+                    const literalNode: ESTree.Literal = NodeFactory.literalNode('foo');
+
+                    let propertyNode: ESTree.Property;
+
+                    before(() => {
+                        propertyNode = NodeFactory.propertyNode(
+                            NodeFactory.literalNode(1),
+                            literalNode
+                        );
+
+                        literalNode.parentNode = propertyNode;
+                    });
+
+                    it('should return false for property value literal node', () => {
+                        assert.equal(NodeLiteralUtils.isProhibitedLiteralNode(literalNode, propertyNode), false);
+                    });
+                });
+            });
+
+            describe('Variant #3: import declaration node', () => {
+                describe('Variant #1: base import declaration literal node', () => {
+                    const literalNode: ESTree.Literal = NodeFactory.literalNode('foo');
+
+                    let importDeclarationNode: ESTree.ImportDeclaration;
+
+                    before(() => {
+                        importDeclarationNode = NodeFactory.importDeclarationNode(
+                            [],
+                            literalNode
+                        );
+
+                        literalNode.parentNode = importDeclarationNode;
+                    });
+
+                    it('should return false for import declaration literal node', () => {
+                        assert.equal(NodeLiteralUtils.isProhibitedLiteralNode(literalNode, importDeclarationNode), true);
+                    });
+                });
+            });
+        });
+
+        describe('Number literal node', () => {
+            describe('Variant #1: base number literal node', () => {
+                const literalNode: ESTree.Literal = NodeFactory.literalNode(1);
+
+                let statementNode: ESTree.Statement;
+
+                before(() => {
+                    statementNode = NodeFactory.expressionStatementNode(
+                        literalNode
+                    );
+
+                    const blockStatementNode: ESTree.BlockStatement = NodeFactory.blockStatementNode([
+                        statementNode
+                    ]);
+
+                    statementNode.parentNode = blockStatementNode;
+                    literalNode.parentNode = statementNode;
+                });
+
+                it('should return false for base number literal node', () => {
+                    assert.equal(NodeLiteralUtils.isProhibitedLiteralNode(literalNode, statementNode), false);
+                });
+            });
+        });
+    });
+});

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