sanex 4 роки тому
батько
коміт
6d70a5abcf
100 змінених файлів з 1959 додано та 629 видалено
  1. 7 1
      CHANGELOG.md
  2. 84 1
      README.md
  3. 0 0
      dist/index.browser.js
  4. 304 167
      dist/index.cli.js
  5. 237 206
      dist/index.js
  6. 3 3
      index.d.ts
  7. 17 17
      package.json
  8. 14 13
      src/JavaScriptObfuscator.ts
  9. 5 5
      src/JavaScriptObfuscatorFacade.ts
  10. 33 18
      src/cli/JavaScriptObfuscatorCLI.ts
  11. 98 0
      src/cli/utils/IdentifierNamesCacheFileUtils.ts
  12. 1 1
      src/cli/utils/ObfuscatedCodeFileUtils.ts
  13. 13 13
      src/cli/utils/SourceCodeFileUtils.ts
  14. 11 11
      src/container/InversifyContainerFacade.ts
  15. 5 2
      src/container/ServiceIdentifiers.ts
  16. 11 0
      src/container/modules/node-transformers/RenameIdentifiersTransformersModule.ts
  17. 12 0
      src/container/modules/storages/StoragesModule.ts
  18. 1 1
      src/custom-code-helpers/calls-controller/CallsControllerFunctionCodeHelper.ts
  19. 1 1
      src/custom-code-helpers/console-output/ConsoleOutputDisableCodeHelper.ts
  20. 1 1
      src/custom-code-helpers/debug-protection/DebugProtectionFunctionCallCodeHelper.ts
  21. 1 1
      src/custom-code-helpers/debug-protection/DebugProtectionFunctionCodeHelper.ts
  22. 1 1
      src/custom-code-helpers/debug-protection/DebugProtectionFunctionIntervalCodeHelper.ts
  23. 1 1
      src/custom-code-helpers/domain-lock/DomainLockCodeHelper.ts
  24. 1 1
      src/custom-code-helpers/self-defending/SelfDefendingUnicodeCodeHelper.ts
  25. 1 1
      src/custom-code-helpers/string-array/StringArrayCallsWrapperBase64CodeHelper.ts
  26. 1 1
      src/custom-code-helpers/string-array/StringArrayCallsWrapperCodeHelper.ts
  27. 1 1
      src/custom-code-helpers/string-array/StringArrayCallsWrapperRc4CodeHelper.ts
  28. 1 1
      src/custom-code-helpers/string-array/StringArrayCodeHelper.ts
  29. 1 1
      src/custom-code-helpers/string-array/StringArrayRotateFunctionCodeHelper.ts
  30. 12 10
      src/custom-nodes/control-flow-flattening-nodes/CallExpressionFunctionNode.ts
  31. 1 1
      src/custom-nodes/dead-code-injection-nodes/BlockStatementDeadCodeInjectionNode.ts
  32. 1 0
      src/enums/node-transformers/NodeTransformer.ts
  33. 1 1
      src/generators/identifier-names-generators/MangledIdentifierNamesGenerator.ts
  34. 2 2
      src/generators/identifier-names-generators/MangledShuffledIdentifierNamesGenerator.ts
  35. 3 3
      src/interfaces/IJavaScriptObfsucator.ts
  36. 9 0
      src/interfaces/node-transformers/rename-identifiers-transformers/replacer/IThroughIdentifierReplacer.ts
  37. 1 0
      src/interfaces/node/IScopeThroughIdentifiersTraverserCallbackData.ts
  38. 1 0
      src/interfaces/options/ICLIOptions.ts
  39. 2 0
      src/interfaces/options/IOptions.ts
  40. 0 13
      src/interfaces/source-code/IObfuscatedCode.ts
  41. 19 0
      src/interfaces/source-code/IObfuscationResult.ts
  42. 7 0
      src/interfaces/storages/IMapStorage.ts
  43. 4 0
      src/interfaces/storages/identifier-names-cache/IGlobalIdentifierNamesCacheStorage.ts
  44. 4 0
      src/interfaces/storages/identifier-names-cache/IPropertyIdentifierNamesCacheStorage.ts
  45. 1 1
      src/node-transformers/converting-transformers/NumberLiteralTransformer.ts
  46. 1 1
      src/node-transformers/converting-transformers/SplitStringTransformer.ts
  47. 141 0
      src/node-transformers/dead-code-injection-transformers/DeadCodeInjectionIdentifiersTransformer.ts
  48. 2 2
      src/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.ts
  49. 1 1
      src/node-transformers/finalizing-transformers/DirectivePlacementTransformer.ts
  50. 1 1
      src/node-transformers/finalizing-transformers/EscapeSequenceTransformer.ts
  51. 2 2
      src/node-transformers/preparing-transformers/CustomCodeHelpersTransformer.ts
  52. 2 2
      src/node-transformers/preparing-transformers/EvalCallExpressionTransformer.ts
  53. 2 2
      src/node-transformers/preparing-transformers/MetadataTransformer.ts
  54. 2 2
      src/node-transformers/preparing-transformers/ObfuscatingGuardsTransformer.ts
  55. 1 1
      src/node-transformers/preparing-transformers/VariablePreserveTransformer.ts
  56. 22 39
      src/node-transformers/rename-identifiers-transformers/ScopeThroughIdentifiersTransformer.ts
  57. 17 2
      src/node-transformers/rename-identifiers-transformers/replacer/IdentifierReplacer.ts
  58. 64 0
      src/node-transformers/rename-identifiers-transformers/through-replacer/ThroughIdentifierReplacer.ts
  59. 22 2
      src/node-transformers/rename-properties-transformers/replacer/RenamePropertiesReplacer.ts
  60. 1 0
      src/node-transformers/rename-properties-transformers/replacer/ReservedDomProperties.json
  61. 2 2
      src/node-transformers/simplifying-transformers/AbstractStatementSimplifyTransformer.ts
  62. 1 1
      src/node-transformers/simplifying-transformers/BlockStatementSimplifyTransformer.ts
  63. 1 1
      src/node-transformers/simplifying-transformers/IfStatementSimplifyTransformer.ts
  64. 1 1
      src/node-transformers/string-array-transformers/StringArrayScopeCallsWrapperTransformer.ts
  65. 1 1
      src/node-transformers/string-array-transformers/StringArrayTransformer.ts
  66. 2 0
      src/node/ScopeIdentifiersTraverser.ts
  67. 8 0
      src/options/Options.ts
  68. 2 0
      src/options/OptionsNormalizer.ts
  69. 32 0
      src/options/normalizer-rules/IdentifierNamesCacheRule.ts
  70. 1 0
      src/options/presets/Default.ts
  71. 87 0
      src/options/validators/IsIdentifierNamesCache.ts
  72. 43 3
      src/source-code/ObfuscationResult.ts
  73. 8 0
      src/storages/MapStorage.ts
  74. 1 1
      src/storages/custom-code-helpers/CustomCodeHelperGroupStorage.ts
  75. 29 0
      src/storages/identifier-names-cache/GlobalIdentifierNamesCacheStorage.ts
  76. 29 0
      src/storages/identifier-names-cache/PropertyIdentifierNamesCacheStorage.ts
  77. 3 3
      src/storages/string-array-transformers/StringArrayStorage.ts
  78. 7 0
      src/types/TIdentifierNamesCache.ts
  79. 3 0
      src/types/TIdentifierNamesCacheDictionary.ts
  80. 2 2
      src/types/TObfuscationResultsObject.ts
  81. 0 3
      src/types/container/source-code/TObfuscatedCodeFactory.ts
  82. 3 0
      src/types/container/source-code/TObfuscationResultFactory.ts
  83. 2 2
      src/utils/CryptUtilsStringArray.ts
  84. 1 1
      test/fixtures/compile-performance.js
  85. 7 7
      test/functional-tests/custom-code-helpers/domain-lock/templates/DomainLockNodeTemplate.spec.ts
  86. 7 7
      test/functional-tests/custom-code-helpers/string-array/templates/string-array-calls-wrapper-node-template/StringArrayCallsWrapperTemplate.spec.ts
  87. 11 11
      test/functional-tests/custom-code-helpers/string-array/templates/string-array-rotate-function-template/StringArrayRotateFunctionTemplate.spec.ts
  88. 7 7
      test/functional-tests/custom-code-helpers/string-array/templates/string-array-template/StringArrayTemplate.spec.ts
  89. 141 15
      test/functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec.ts
  90. 9 0
      test/functional-tests/javascript-obfuscator/fixtures/identifier-names-cache-1.js
  91. 9 0
      test/functional-tests/javascript-obfuscator/fixtures/identifier-names-cache-2.js
  92. 67 3
      test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/call-expression-control-flow-replacer/CallExpressionControlFlowReplacer.spec.ts
  93. 0 0
      test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/call-expression-control-flow-replacer/fixtures/rest-as-last-call-argument.js
  94. 7 0
      test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/call-expression-control-flow-replacer/fixtures/rest-as-middle-call-argument.js
  95. 6 0
      test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/call-expression-control-flow-replacer/fixtures/rest-as-start-call-argument.js
  96. 98 0
      test/functional-tests/node-transformers/rename-identifiers-transformers/scope-through-identifiers-transformer/class-declaration/ClassDeclaration.spec.ts
  97. 3 0
      test/functional-tests/node-transformers/rename-identifiers-transformers/scope-through-identifiers-transformer/class-declaration/fixtures/class-call-with-declaration.js
  98. 1 0
      test/functional-tests/node-transformers/rename-identifiers-transformers/scope-through-identifiers-transformer/class-declaration/fixtures/class-call-without-declaration.js
  99. 98 0
      test/functional-tests/node-transformers/rename-identifiers-transformers/scope-through-identifiers-transformer/function-declaration/FunctionDeclaration.spec.ts
  100. 3 0
      test/functional-tests/node-transformers/rename-identifiers-transformers/scope-through-identifiers-transformer/function-declaration/fixtures/function-call-with-declaration.js

+ 7 - 1
CHANGELOG.md

@@ -1,11 +1,17 @@
 Change Log
 Change Log
 
 
-v2.14.0
+v2.15.0
 ---
 ---
 * Added support of `es2022` features: private identifiers and class properties
 * Added support of `es2022` features: private identifiers and class properties
 
 
+v2.14.0
+---
+* Added `identifierNamesCache` option for reading and writing identifier names cache. See `README.md`.
+* **CLI**:  Added `--identifier-names-cache-path` option for reading and writing identifier names cache. See `README.md`.
+
 v2.13.0
 v2.13.0
 ---
 ---
+* Fixed invalid code generation for start/middle rest arguments when `controlFlowFlattenig` option is enabled. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/920
 * **Internal**: Added support of `node@16` and dropped support of `node@10`. This should not affect obfuscated code
 * **Internal**: Added support of `node@16` and dropped support of `node@10`. This should not affect obfuscated code
 
 
 v2.12.0
 v2.12.0

+ 84 - 1
README.md

@@ -230,7 +230,8 @@ function _0x1054 (_0x14a2a4, _0x5a6b22) {
 Returns `ObfuscationResult` object which contains two public methods:
 Returns `ObfuscationResult` object which contains two public methods:
 
 
 * `getObfuscatedCode()` - returns `string` with obfuscated code;
 * `getObfuscatedCode()` - returns `string` with obfuscated code;
-* `getSourceMap()` - if [`sourceMap`](#sourcemap) option is enabled - returns `string` with source map or an empty string if [`sourceMapMode`](#sourcemapmode) option is set as `inline`.
+* `getSourceMap()` - if [`sourceMap`](#sourcemap) option is enabled - returns `string` with source map or an empty string if [`sourceMapMode`](#sourcemapmode) option is set as `inline`;
+* `getIdentifierNamesCache()` - returns object with identifier names cache if `identifierNamesCache` option is enabled, `null` overwise.
 
 
 Calling `toString()` for `ObfuscationResult` object will return `string` with obfuscated code.
 Calling `toString()` for `ObfuscationResult` object will return `string` with obfuscated code.
 
 
@@ -361,6 +362,7 @@ Following options are available for the JS Obfuscator:
     disableConsoleOutput: false,
     disableConsoleOutput: false,
     domainLock: [],
     domainLock: [],
     forceTransformStrings: [],
     forceTransformStrings: [],
+    identifierNamesCache: null,
     identifierNamesGenerator: 'hexadecimal',
     identifierNamesGenerator: 'hexadecimal',
     identifiersDictionary: [],
     identifiersDictionary: [],
     identifiersPrefix: '',
     identifiersPrefix: '',
@@ -421,6 +423,7 @@ Following options are available for the JS Obfuscator:
     --domain-lock '<list>' (comma separated)
     --domain-lock '<list>' (comma separated)
     --exclude '<list>' (comma separated)
     --exclude '<list>' (comma separated)
     --force-transform-strings '<list>' (comma separated)
     --force-transform-strings '<list>' (comma separated)
+    --identifier-names-cache-path <string>
     --identifier-names-generator <string> [dictionary, hexadecimal, mangled, mangled-shuffled]
     --identifier-names-generator <string> [dictionary, hexadecimal, mangled, mangled-shuffled]
     --identifiers-dictionary '<list>' (comma separated)
     --identifiers-dictionary '<list>' (comma separated)
     --identifiers-prefix <string>
     --identifiers-prefix <string>
@@ -712,6 +715,84 @@ Example:
 	}
 	}
 ```
 ```
 
 
+### `identifierNamesCache`
+Type: `Object | null` Default: `null`
+
+The main goal for this option is the ability to use the same identifier names during obfuscation of multiple sources/files.
+
+Currently the two types of the identifiers are supported:
+- Global identifiers:
+    * All global identifiers will be written to the cache;
+    * All matched **undeclared** global identifiers will be replaced by the values from the cache.
+- Property identifiers, only when `renameProperties` option is enabled:
+    * All property identifiers will be written to the cache;
+    * All matched property identifiers will be replaced by the values from the cache.
+
+#### Node.js API
+If a `null` value is passed, completely disables the cache.
+
+If an empty object (`{}`) is passed, enables the writing identifier names to the cache-object (`TIdentifierNamesCache` type). This cache-object will be accessed through the `getIdentifierNamesCache` method call of `ObfuscationResult` object.
+
+The resulting cache-object can be next used as `identifierNamesGenerator` option value for using these names during obfuscation of all matched identifier names of next sources.
+
+Example:
+```
+const source1ObfuscationResult = JavaScriptObfuscator.obfuscate(
+    `
+        function foo(arg) {
+           console.log(arg)
+        }
+        
+        function bar() {
+            var bark = 2;
+        }
+    `,
+    {
+        compact: false,
+        identifierNamesCache: {},
+        renameGlobals: true
+    }
+)
+
+console.log(source1ObfuscationResult.getIdentifierNamesCache());
+/*{ 
+    globalIdentifiers: {
+        foo: '_0x5de86d',
+        bar: '_0x2a943b'
+    }
+}*/
+
+
+
+const source2ObfuscationResult = JavaScriptObfuscator.obfuscate(
+    `
+        // Expecting that these global functions are defined in another obfuscated file
+        foo(1);
+        bar();
+        
+        // Expecting that this global function is defined in third-party package
+        baz();
+    `,
+    {
+        compact: false,
+        identifierNamesCache: source1ObfuscationResult.getIdentifierNamesCache(),
+        renameGlobals: true
+    }
+)
+
+console.log(source2ObfuscationResult.getObfuscatedCode());
+// _0x5de86d(0x1);
+// _0x2a943b();
+// baz();
+```
+
+#### CLI
+CLI has a different option `--identifier-names-cache-path` that allows defining a path to the existing `.json` file that will be used to read and write identifier names cache.
+
+If a path to the empty file will be passed - identifier names cache will be written to that file.
+
+This file with existing cache can be used again as `--identifier-names-cache-path` option value for using these names during obfuscation of all matched identifier names of the next files.
+
 ### `identifierNamesGenerator`
 ### `identifierNamesGenerator`
 Type: `string` Default: `hexadecimal`
 Type: `string` Default: `hexadecimal`
 
 
@@ -834,6 +915,8 @@ Specifies `renameProperties` option mode:
 * `safe` - default behaviour after `2.11.0` release. Trying to rename properties in a more safe way to prevent runtime errors. With this mode some properties will be excluded from renaming.
 * `safe` - default behaviour after `2.11.0` release. Trying to rename properties in a more safe way to prevent runtime errors. With this mode some properties will be excluded from renaming.
 * `unsafe` - default behaviour before `2.11.0` release. Renames properties in an unsafe way without any restrictions.
 * `unsafe` - default behaviour before `2.11.0` release. Renames properties in an unsafe way without any restrictions.
 
 
+If one file is using properties from other file, use [`identifierNamesCache`](#identifiernamescache) option to keep the same property names between these files.
+
 ### `reservedNames`
 ### `reservedNames`
 Type: `string[]` Default: `[]`
 Type: `string[]` Default: `[]`
 
 

Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/index.browser.js


Різницю між файлами не показано, бо вона завелика
+ 304 - 167
dist/index.cli.js


Різницю між файлами не показано, бо вона завелика
+ 237 - 206
dist/index.js


+ 3 - 3
index.d.ts

@@ -3,18 +3,18 @@ import { TInputOptions } from './src/types/options/TInputOptions';
 import { TObfuscationResultsObject } from './src/types/TObfuscationResultsObject';
 import { TObfuscationResultsObject } from './src/types/TObfuscationResultsObject';
 import { TOptionsPreset } from './src/types/options/TOptionsPreset';
 import { TOptionsPreset } from './src/types/options/TOptionsPreset';
 
 
-import { IObfuscatedCode } from './src/interfaces/source-code/IObfuscatedCode';
+import { IObfuscationResult } from './src/interfaces/source-code/IObfuscationResult';
 
 
 export type ObfuscatorOptions = TInputOptions;
 export type ObfuscatorOptions = TInputOptions;
 
 
-export interface ObfuscatedCode extends IObfuscatedCode {}
+export interface ObfuscationResult extends IObfuscationResult {}
 
 
 /**
 /**
  * @param {string} sourceCode
  * @param {string} sourceCode
  * @param {ObfuscatorOptions} inputOptions
  * @param {ObfuscatorOptions} inputOptions
  * @returns {ObfuscatedCode}
  * @returns {ObfuscatedCode}
  */
  */
-export function obfuscate (sourceCode: string, inputOptions?: ObfuscatorOptions): ObfuscatedCode;
+export function obfuscate (sourceCode: string, inputOptions?: ObfuscatorOptions): ObfuscationResult;
 
 
 /**
 /**
  * @param {TSourceCodesObject} sourceCodesObject
  * @param {TSourceCodesObject} sourceCodesObject

+ 17 - 17
package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "javascript-obfuscator",
   "name": "javascript-obfuscator",
-  "version": "2.14.0",
+  "version": "2.15.0",
   "description": "JavaScript obfuscator",
   "description": "JavaScript obfuscator",
   "keywords": [
   "keywords": [
     "obfuscator",
     "obfuscator",
@@ -24,7 +24,7 @@
     "@javascript-obfuscator/escodegen": "2.2.0",
     "@javascript-obfuscator/escodegen": "2.2.0",
     "@javascript-obfuscator/estraverse": "5.3.0",
     "@javascript-obfuscator/estraverse": "5.3.0",
     "@nuxtjs/opencollective": "0.3.2",
     "@nuxtjs/opencollective": "0.3.2",
-    "acorn": "8.2.4",
+    "acorn": "8.3.0",
     "assert": "2.0.0",
     "assert": "2.0.0",
     "chalk": "4.1.1",
     "chalk": "4.1.1",
     "chance": "1.1.7",
     "chance": "1.1.7",
@@ -47,7 +47,7 @@
   "devDependencies": {
   "devDependencies": {
     "@istanbuljs/nyc-config-typescript": "1.0.1",
     "@istanbuljs/nyc-config-typescript": "1.0.1",
     "@types/chai": "4.2.18",
     "@types/chai": "4.2.18",
-    "@types/chance": "1.1.1",
+    "@types/chance": "1.1.2",
     "@types/escodegen": "0.0.6",
     "@types/escodegen": "0.0.6",
     "@types/eslint-scope": "3.7.0",
     "@types/eslint-scope": "3.7.0",
     "@types/estraverse": "5.1.0",
     "@types/estraverse": "5.1.0",
@@ -57,35 +57,35 @@
     "@types/mkdirp": "1.0.1",
     "@types/mkdirp": "1.0.1",
     "@types/mocha": "8.2.2",
     "@types/mocha": "8.2.2",
     "@types/multimatch": "4.0.0",
     "@types/multimatch": "4.0.0",
-    "@types/node": "15.0.2",
+    "@types/node": "15.6.1",
     "@types/rimraf": "3.0.0",
     "@types/rimraf": "3.0.0",
-    "@types/sinon": "10.0.0",
+    "@types/sinon": "10.0.1",
     "@types/string-template": "1.0.2",
     "@types/string-template": "1.0.2",
     "@types/webpack-env": "1.16.0",
     "@types/webpack-env": "1.16.0",
-    "@typescript-eslint/eslint-plugin": "4.22.1",
-    "@typescript-eslint/parser": "4.22.1",
+    "@typescript-eslint/eslint-plugin": "4.25.0",
+    "@typescript-eslint/parser": "4.25.0",
     "chai": "4.3.4",
     "chai": "4.3.4",
     "chai-exclude": "2.0.3",
     "chai-exclude": "2.0.3",
     "cross-env": "7.0.3",
     "cross-env": "7.0.3",
-    "eslint": "7.26.0",
-    "eslint-plugin-import": "2.22.1",
-    "eslint-plugin-jsdoc": "33.1.0",
+    "eslint": "7.27.0",
+    "eslint-plugin-import": "2.23.4",
+    "eslint-plugin-jsdoc": "35.1.0",
     "eslint-plugin-no-null": "1.0.2",
     "eslint-plugin-no-null": "1.0.2",
     "eslint-plugin-prefer-arrow": "1.2.3",
     "eslint-plugin-prefer-arrow": "1.2.3",
-    "eslint-plugin-unicorn": "31.0.0",
+    "eslint-plugin-unicorn": "32.0.1",
     "fork-ts-checker-notifier-webpack-plugin": "4.0.0",
     "fork-ts-checker-notifier-webpack-plugin": "4.0.0",
-    "fork-ts-checker-webpack-plugin": "6.2.6",
+    "fork-ts-checker-webpack-plugin": "6.2.10",
     "mocha": "8.4.0",
     "mocha": "8.4.0",
     "nyc": "15.1.0",
     "nyc": "15.1.0",
     "pjson": "1.0.9",
     "pjson": "1.0.9",
     "pre-commit": "1.2.2",
     "pre-commit": "1.2.2",
     "rimraf": "3.0.2",
     "rimraf": "3.0.2",
-    "sinon": "10.0.0",
+    "sinon": "11.1.1",
     "threads": "1.6.4",
     "threads": "1.6.4",
-    "ts-loader": "9.1.2",
-    "ts-node": "9.1.1",
-    "typescript": "beta",
-    "webpack": "5.36.2",
+    "ts-loader": "9.2.2",
+    "ts-node": "10.0.0",
+    "typescript": "4.3.2",
+    "webpack": "5.38.1",
     "webpack-cli": "4.7.0",
     "webpack-cli": "4.7.0",
     "webpack-node-externals": "3.0.0"
     "webpack-node-externals": "3.0.0"
   },
   },

+ 14 - 13
src/JavaScriptObfuscator.ts

@@ -5,13 +5,13 @@ import * as acorn from 'acorn';
 import * as escodegen from '@javascript-obfuscator/escodegen';
 import * as escodegen from '@javascript-obfuscator/escodegen';
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
-import { TObfuscatedCodeFactory } from './types/container/source-code/TObfuscatedCodeFactory';
+import { TObfuscationResultFactory } from './types/container/source-code/TObfuscationResultFactory';
 
 
 import { ICodeTransformersRunner } from './interfaces/code-transformers/ICodeTransformersRunner';
 import { ICodeTransformersRunner } from './interfaces/code-transformers/ICodeTransformersRunner';
 import { IGeneratorOutput } from './interfaces/IGeneratorOutput';
 import { IGeneratorOutput } from './interfaces/IGeneratorOutput';
 import { IJavaScriptObfuscator } from './interfaces/IJavaScriptObfsucator';
 import { IJavaScriptObfuscator } from './interfaces/IJavaScriptObfsucator';
 import { ILogger } from './interfaces/logger/ILogger';
 import { ILogger } from './interfaces/logger/ILogger';
-import { IObfuscatedCode } from './interfaces/source-code/IObfuscatedCode';
+import { IObfuscationResult } from './interfaces/source-code/IObfuscationResult';
 import { IOptions } from './interfaces/options/IOptions';
 import { IOptions } from './interfaces/options/IOptions';
 import { IRandomGenerator } from './interfaces/utils/IRandomGenerator';
 import { IRandomGenerator } from './interfaces/utils/IRandomGenerator';
 import { INodeTransformersRunner } from './interfaces/node-transformers/INodeTransformersRunner';
 import { INodeTransformersRunner } from './interfaces/node-transformers/INodeTransformersRunner';
@@ -87,6 +87,7 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
         NodeTransformer.ObjectPatternPropertiesTransformer,
         NodeTransformer.ObjectPatternPropertiesTransformer,
         NodeTransformer.ParentificationTransformer,
         NodeTransformer.ParentificationTransformer,
         NodeTransformer.ScopeIdentifiersTransformer,
         NodeTransformer.ScopeIdentifiersTransformer,
+        NodeTransformer.ScopeThroughIdentifiersTransformer,
         NodeTransformer.SplitStringTransformer,
         NodeTransformer.SplitStringTransformer,
         NodeTransformer.StringArrayRotateFunctionTransformer,
         NodeTransformer.StringArrayRotateFunctionTransformer,
         NodeTransformer.StringArrayScopeCallsWrapperTransformer,
         NodeTransformer.StringArrayScopeCallsWrapperTransformer,
@@ -108,9 +109,9 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
     private readonly logger: ILogger;
     private readonly logger: ILogger;
 
 
     /**
     /**
-     * @type {TObfuscatedCodeFactory}
+     * @type {TObfuscationResultFactory}
      */
      */
-    private readonly obfuscatedCodeFactory: TObfuscatedCodeFactory;
+    private readonly obfuscationResultFactory: TObfuscationResultFactory;
 
 
     /**
     /**
      * @type {IOptions}
      * @type {IOptions}
@@ -131,7 +132,7 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
      * @param {ICodeTransformersRunner} codeTransformersRunner
      * @param {ICodeTransformersRunner} codeTransformersRunner
      * @param {INodeTransformersRunner} nodeTransformersRunner
      * @param {INodeTransformersRunner} nodeTransformersRunner
      * @param {IRandomGenerator} randomGenerator
      * @param {IRandomGenerator} randomGenerator
-     * @param {TObfuscatedCodeFactory} obfuscatedCodeFactory
+     * @param {TObfuscationResultFactory} obfuscatedCodeFactory
      * @param {ILogger} logger
      * @param {ILogger} logger
      * @param {IOptions} options
      * @param {IOptions} options
      */
      */
@@ -139,23 +140,23 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
         @inject(ServiceIdentifiers.ICodeTransformersRunner) codeTransformersRunner: ICodeTransformersRunner,
         @inject(ServiceIdentifiers.ICodeTransformersRunner) codeTransformersRunner: ICodeTransformersRunner,
         @inject(ServiceIdentifiers.INodeTransformersRunner) nodeTransformersRunner: INodeTransformersRunner,
         @inject(ServiceIdentifiers.INodeTransformersRunner) nodeTransformersRunner: INodeTransformersRunner,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
-        @inject(ServiceIdentifiers.Factory__IObfuscatedCode) obfuscatedCodeFactory: TObfuscatedCodeFactory,
+        @inject(ServiceIdentifiers.Factory__IObfuscationResult) obfuscatedCodeFactory: TObfuscationResultFactory,
         @inject(ServiceIdentifiers.ILogger) logger: ILogger,
         @inject(ServiceIdentifiers.ILogger) logger: ILogger,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
     ) {
         this.codeTransformersRunner = codeTransformersRunner;
         this.codeTransformersRunner = codeTransformersRunner;
         this.nodeTransformersRunner = nodeTransformersRunner;
         this.nodeTransformersRunner = nodeTransformersRunner;
         this.randomGenerator = randomGenerator;
         this.randomGenerator = randomGenerator;
-        this.obfuscatedCodeFactory = obfuscatedCodeFactory;
+        this.obfuscationResultFactory = obfuscatedCodeFactory;
         this.logger = logger;
         this.logger = logger;
         this.options = options;
         this.options = options;
     }
     }
 
 
     /**
     /**
      * @param {string} sourceCode
      * @param {string} sourceCode
-     * @returns {IObfuscatedCode}
+     * @returns {IObfuscationResult}
      */
      */
-    public obfuscate (sourceCode: string): IObfuscatedCode {
+    public obfuscate (sourceCode: string): IObfuscationResult {
         if (typeof sourceCode !== 'string') {
         if (typeof sourceCode !== 'string') {
             sourceCode = '';
             sourceCode = '';
         }
         }
@@ -183,7 +184,7 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
         const obfuscationTime: number = (Date.now() - timeStart) / 1000;
         const obfuscationTime: number = (Date.now() - timeStart) / 1000;
         this.logger.success(LoggingMessage.ObfuscationCompleted, obfuscationTime);
         this.logger.success(LoggingMessage.ObfuscationCompleted, obfuscationTime);
 
 
-        return this.getObfuscatedCode(generatorOutput);
+        return this.getObfuscationResult(generatorOutput);
     }
     }
 
 
     /**
     /**
@@ -268,10 +269,10 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
 
 
     /**
     /**
      * @param {IGeneratorOutput} generatorOutput
      * @param {IGeneratorOutput} generatorOutput
-     * @returns {IObfuscatedCode}
+     * @returns {IObfuscationResult}
      */
      */
-    private getObfuscatedCode (generatorOutput: IGeneratorOutput): IObfuscatedCode {
-        return this.obfuscatedCodeFactory(generatorOutput.code, generatorOutput.map);
+    private getObfuscationResult (generatorOutput: IGeneratorOutput): IObfuscationResult {
+        return this.obfuscationResultFactory(generatorOutput.code, generatorOutput.map);
     }
     }
 
 
     /**
     /**

+ 5 - 5
src/JavaScriptObfuscatorFacade.ts

@@ -9,7 +9,7 @@ import { TOptionsPreset } from './types/options/TOptionsPreset';
 
 
 import { IInversifyContainerFacade } from './interfaces/container/IInversifyContainerFacade';
 import { IInversifyContainerFacade } from './interfaces/container/IInversifyContainerFacade';
 import { IJavaScriptObfuscator } from './interfaces/IJavaScriptObfsucator';
 import { IJavaScriptObfuscator } from './interfaces/IJavaScriptObfsucator';
-import { IObfuscatedCode } from './interfaces/source-code/IObfuscatedCode';
+import { IObfuscationResult } from './interfaces/source-code/IObfuscationResult';
 
 
 import { InversifyContainerFacade } from './container/InversifyContainerFacade';
 import { InversifyContainerFacade } from './container/InversifyContainerFacade';
 import { Options } from './options/Options';
 import { Options } from './options/Options';
@@ -24,20 +24,20 @@ class JavaScriptObfuscatorFacade {
     /**
     /**
      * @param {string} sourceCode
      * @param {string} sourceCode
      * @param {TInputOptions} inputOptions
      * @param {TInputOptions} inputOptions
-     * @returns {IObfuscatedCode}
+     * @returns {IObfuscationResult}
      */
      */
-    public static obfuscate (sourceCode: string, inputOptions: TInputOptions = {}): IObfuscatedCode {
+    public static obfuscate (sourceCode: string, inputOptions: TInputOptions = {}): IObfuscationResult {
         const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade();
         const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade();
 
 
         inversifyContainerFacade.load(sourceCode, '', inputOptions);
         inversifyContainerFacade.load(sourceCode, '', inputOptions);
 
 
         const javaScriptObfuscator: IJavaScriptObfuscator = inversifyContainerFacade
         const javaScriptObfuscator: IJavaScriptObfuscator = inversifyContainerFacade
             .get<IJavaScriptObfuscator>(ServiceIdentifiers.IJavaScriptObfuscator);
             .get<IJavaScriptObfuscator>(ServiceIdentifiers.IJavaScriptObfuscator);
-        const obfuscatedCode: IObfuscatedCode = javaScriptObfuscator.obfuscate(sourceCode);
+        const obfuscationResult: IObfuscationResult = javaScriptObfuscator.obfuscate(sourceCode);
 
 
         inversifyContainerFacade.unload();
         inversifyContainerFacade.unload();
 
 
-        return obfuscatedCode;
+        return obfuscationResult;
     }
     }
 
 
     /**
     /**

+ 33 - 18
src/cli/JavaScriptObfuscatorCLI.ts

@@ -7,7 +7,7 @@ import { TInputOptions } from '../types/options/TInputOptions';
 
 
 import { IFileData } from '../interfaces/cli/IFileData';
 import { IFileData } from '../interfaces/cli/IFileData';
 import { IInitializable } from '../interfaces/IInitializable';
 import { IInitializable } from '../interfaces/IInitializable';
-import { IObfuscatedCode } from '../interfaces/source-code/IObfuscatedCode';
+import { IObfuscationResult } from '../interfaces/source-code/IObfuscationResult';
 
 
 import { initializable } from '../decorators/Initializable';
 import { initializable } from '../decorators/Initializable';
 
 
@@ -27,10 +27,11 @@ import { ArraySanitizer } from './sanitizers/ArraySanitizer';
 import { BooleanSanitizer } from './sanitizers/BooleanSanitizer';
 import { BooleanSanitizer } from './sanitizers/BooleanSanitizer';
 
 
 import { CLIUtils } from './utils/CLIUtils';
 import { CLIUtils } from './utils/CLIUtils';
+import { IdentifierNamesCacheFileUtils } from './utils/IdentifierNamesCacheFileUtils';
 import { JavaScriptObfuscator } from '../JavaScriptObfuscatorFacade';
 import { JavaScriptObfuscator } from '../JavaScriptObfuscatorFacade';
 import { Logger } from '../logger/Logger';
 import { Logger } from '../logger/Logger';
-import { ObfuscatedCodeWriter } from './utils/ObfuscatedCodeWriter';
-import { SourceCodeReader } from './utils/SourceCodeReader';
+import { ObfuscatedCodeFileUtils } from './utils/ObfuscatedCodeFileUtils';
+import { SourceCodeFileUtils } from './utils/SourceCodeFileUtils';
 import { Utils } from '../utils/Utils';
 import { Utils } from '../utils/Utils';
 
 
 export class JavaScriptObfuscatorCLI implements IInitializable {
 export class JavaScriptObfuscatorCLI implements IInitializable {
@@ -57,6 +58,12 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
     @initializable()
     @initializable()
     private commands!: commander.CommanderStatic;
     private commands!: commander.CommanderStatic;
 
 
+    /**
+     * @type {IdentifierNamesCacheFileUtils}
+     */
+    @initializable()
+    private identifierNamesCacheFileUtils!: IdentifierNamesCacheFileUtils;
+
     /**
     /**
      * @type {TInputCLIOptions}
      * @type {TInputCLIOptions}
      */
      */
@@ -70,16 +77,16 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
     private inputPath!: string;
     private inputPath!: string;
 
 
     /**
     /**
-     * @type {SourceCodeReader}
+     * @type {SourceCodeFileUtils}
      */
      */
     @initializable()
     @initializable()
-    private sourceCodeReader!: SourceCodeReader;
+    private sourceCodeFileUtils!: SourceCodeFileUtils;
 
 
     /**
     /**
-     * @type {ObfuscatedCodeWriter}
+     * @type {ObfuscatedCodeFileUtils}
      */
      */
     @initializable()
     @initializable()
-    private obfuscatedCodeWriter!: ObfuscatedCodeWriter;
+    private obfuscatedCodeFileUtils!: ObfuscatedCodeFileUtils;
 
 
     /**
     /**
      * @type {string[]}
      * @type {string[]}
@@ -144,14 +151,15 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
 
 
         this.inputPath = path.normalize(this.commands.args[0] || '');
         this.inputPath = path.normalize(this.commands.args[0] || '');
         this.inputCLIOptions = JavaScriptObfuscatorCLI.buildOptions(this.commands.opts());
         this.inputCLIOptions = JavaScriptObfuscatorCLI.buildOptions(this.commands.opts());
-        this.sourceCodeReader = new SourceCodeReader(
+        this.sourceCodeFileUtils = new SourceCodeFileUtils(
             this.inputPath,
             this.inputPath,
             this.inputCLIOptions
             this.inputCLIOptions
         );
         );
-        this.obfuscatedCodeWriter = new ObfuscatedCodeWriter(
+        this.obfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
             this.inputPath,
             this.inputPath,
             this.inputCLIOptions
             this.inputCLIOptions
         );
         );
+        this.identifierNamesCacheFileUtils = new IdentifierNamesCacheFileUtils(this.inputCLIOptions.identifierNamesCachePath);
     }
     }
 
 
     public run (): void {
     public run (): void {
@@ -163,7 +171,7 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
             return;
             return;
         }
         }
 
 
-        const sourceCodeData: IFileData[] = this.sourceCodeReader.readSourceCode();
+        const sourceCodeData: IFileData[] = this.sourceCodeFileUtils.readSourceCode();
 
 
         this.processSourceCodeData(sourceCodeData);
         this.processSourceCodeData(sourceCodeData);
     }
     }
@@ -238,6 +246,10 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
                 'Enables force transformation of string literals, which being matched by passed RegExp patterns (comma separated)',
                 'Enables force transformation of string literals, which being matched by passed RegExp patterns (comma separated)',
                 ArraySanitizer
                 ArraySanitizer
             )
             )
+            .option(
+                '--identifier-names-cache-path <string>',
+                'Sets path for identifier names cache'
+            )
             .option(
             .option(
                 '--identifier-names-generator <string>',
                 '--identifier-names-generator <string>',
                 'Sets identifier names generator. ' +
                 'Sets identifier names generator. ' +
@@ -430,7 +442,7 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
      */
      */
     private processSourceCodeData (sourceCodeData: IFileData[]): void {
     private processSourceCodeData (sourceCodeData: IFileData[]): void {
         sourceCodeData.forEach(({ filePath, content }: IFileData, index: number) => {
         sourceCodeData.forEach(({ filePath, content }: IFileData, index: number) => {
-            const outputCodePath: string = this.obfuscatedCodeWriter.getOutputCodePath(filePath);
+            const outputCodePath: string = this.obfuscatedCodeFileUtils.getOutputCodePath(filePath);
 
 
             try {
             try {
                 Logger.log(
                 Logger.log(
@@ -466,6 +478,7 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
     ): void {
     ): void {
         const options: TInputOptions = {
         const options: TInputOptions = {
             ...this.inputCLIOptions,
             ...this.inputCLIOptions,
+            identifierNamesCache: this.identifierNamesCacheFileUtils.readFile(),
             inputFileName: path.basename(inputCodePath),
             inputFileName: path.basename(inputCodePath),
             ...sourceCodeIndex !== null && {
             ...sourceCodeIndex !== null && {
                 identifiersPrefix: Utils.getIdentifiersPrefixForMultipleSources(
                 identifiersPrefix: Utils.getIdentifiersPrefixForMultipleSources(
@@ -492,9 +505,10 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
         outputCodePath: string,
         outputCodePath: string,
         options: TInputOptions
         options: TInputOptions
     ): void {
     ): void {
-        const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(sourceCode, options).getObfuscatedCode();
+        const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(sourceCode, options);
 
 
-        this.obfuscatedCodeWriter.writeFile(outputCodePath, obfuscatedCode);
+        this.obfuscatedCodeFileUtils.writeFile(outputCodePath, obfuscationResult.getObfuscatedCode());
+        this.identifierNamesCacheFileUtils.writeFile(obfuscationResult.getIdentifierNamesCache());
     }
     }
 
 
     /**
     /**
@@ -507,7 +521,7 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
         outputCodePath: string,
         outputCodePath: string,
         options: TInputOptions
         options: TInputOptions
     ): void {
     ): void {
-        const outputSourceMapPath: string = this.obfuscatedCodeWriter.getOutputSourceMapPath(
+        const outputSourceMapPath: string = this.obfuscatedCodeFileUtils.getOutputSourceMapPath(
             outputCodePath,
             outputCodePath,
             options.sourceMapFileName ?? ''
             options.sourceMapFileName ?? ''
         );
         );
@@ -517,12 +531,13 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
             sourceMapFileName: path.basename(outputSourceMapPath)
             sourceMapFileName: path.basename(outputSourceMapPath)
         };
         };
 
 
-        const obfuscatedCode: IObfuscatedCode = JavaScriptObfuscator.obfuscate(sourceCode, options);
+        const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(sourceCode, options);
 
 
-        this.obfuscatedCodeWriter.writeFile(outputCodePath, obfuscatedCode.getObfuscatedCode());
+        this.obfuscatedCodeFileUtils.writeFile(outputCodePath, obfuscationResult.getObfuscatedCode());
+        this.identifierNamesCacheFileUtils.writeFile(obfuscationResult.getIdentifierNamesCache());
 
 
-        if (options.sourceMapMode === SourceMapMode.Separate && obfuscatedCode.getSourceMap()) {
-            this.obfuscatedCodeWriter.writeFile(outputSourceMapPath, obfuscatedCode.getSourceMap());
+        if (options.sourceMapMode === SourceMapMode.Separate && obfuscationResult.getSourceMap()) {
+            this.obfuscatedCodeFileUtils.writeFile(outputSourceMapPath, obfuscationResult.getSourceMap());
         }
         }
     }
     }
 }
 }

+ 98 - 0
src/cli/utils/IdentifierNamesCacheFileUtils.ts

@@ -0,0 +1,98 @@
+import * as fs from 'fs';
+import * as path from 'path';
+
+import { TIdentifierNamesCache } from '../../types/TIdentifierNamesCache';
+
+import { IFileData } from '../../interfaces/cli/IFileData';
+
+import { JavaScriptObfuscatorCLI } from '../JavaScriptObfuscatorCLI';
+
+/**
+ * Utils to work with identifier names cache file
+ */
+export class IdentifierNamesCacheFileUtils {
+    /**
+     * @type {string}
+     */
+    private static readonly identifierNamesCacheExtension: string = '.json';
+
+    /**
+     * @type {string}
+     */
+    private readonly identifierNamesCachePath: string | undefined;
+
+    /**
+     * @param {string} identifierNamesCachePath
+     */
+    public constructor (identifierNamesCachePath: string | undefined) {
+        this.identifierNamesCachePath = identifierNamesCachePath;
+    }
+
+    /**
+     * @param {string} filePath
+     * @returns {boolean}
+     */
+    private static isValidFilePath (filePath: string): boolean {
+        try {
+            return fs.statSync(filePath).isFile()
+                && path.extname(filePath) === IdentifierNamesCacheFileUtils.identifierNamesCacheExtension;
+        } catch {
+            return false;
+        }
+    }
+
+    /**
+     * @param {string} filePath
+     * @returns {IFileData}
+     */
+    private static readFile (filePath: string): IFileData {
+        return {
+            filePath: path.normalize(filePath),
+            content: fs.readFileSync(filePath, JavaScriptObfuscatorCLI.encoding)
+        };
+    }
+
+    /**
+     * @returns {TIdentifierNamesCache | null}
+     */
+    public readFile (): TIdentifierNamesCache | null {
+        if (!this.identifierNamesCachePath) {
+            return null;
+        }
+
+        if (!IdentifierNamesCacheFileUtils.isValidFilePath(this.identifierNamesCachePath)) {
+            throw new ReferenceError(`Given identifier names cache path must be a valid ${
+                IdentifierNamesCacheFileUtils.identifierNamesCacheExtension
+            } file path`);
+        }
+
+        const fileData: IFileData = IdentifierNamesCacheFileUtils.readFile(this.identifierNamesCachePath);
+
+        if (!fileData.content) {
+            // Initial state of identifier names cache file
+            return {};
+        }
+
+        try {
+            // Already written identifier names cache file
+            return JSON.parse(fileData.content);
+        } catch {
+            throw new ReferenceError('Identifier names cache file must contains a json dictionary with identifier names');
+        }
+    }
+
+    /**
+     * @param {TIdentifierNamesCache} identifierNamesCache
+     */
+    public writeFile (identifierNamesCache: TIdentifierNamesCache): void {
+        if (!this.identifierNamesCachePath) {
+            return;
+        }
+
+        const identifierNamesCacheJson: string = JSON.stringify(identifierNamesCache);
+
+        fs.writeFileSync(this.identifierNamesCachePath, identifierNamesCacheJson, {
+            encoding: JavaScriptObfuscatorCLI.encoding
+        });
+    }
+}

+ 1 - 1
src/cli/utils/ObfuscatedCodeWriter.ts → src/cli/utils/ObfuscatedCodeFileUtils.ts

@@ -8,7 +8,7 @@ import { StringSeparator } from '../../enums/StringSeparator';
 
 
 import { JavaScriptObfuscatorCLI } from '../JavaScriptObfuscatorCLI';
 import { JavaScriptObfuscatorCLI } from '../JavaScriptObfuscatorCLI';
 
 
-export class ObfuscatedCodeWriter {
+export class ObfuscatedCodeFileUtils {
     /**
     /**
      * @type {string}
      * @type {string}
      */
      */

+ 13 - 13
src/cli/utils/SourceCodeReader.ts → src/cli/utils/SourceCodeFileUtils.ts

@@ -8,7 +8,7 @@ import { IFileData } from '../../interfaces/cli/IFileData';
 
 
 import { JavaScriptObfuscatorCLI } from '../JavaScriptObfuscatorCLI';
 import { JavaScriptObfuscatorCLI } from '../JavaScriptObfuscatorCLI';
 
 
-export class SourceCodeReader {
+export class SourceCodeFileUtils {
     /**
     /**
      * @type {string}
      * @type {string}
      */
      */
@@ -80,7 +80,7 @@ export class SourceCodeReader {
      * @returns {boolean}
      * @returns {boolean}
      */
      */
     private static isValidDirectory (directoryPath: string, excludePatterns: string[] = []): boolean {
     private static isValidDirectory (directoryPath: string, excludePatterns: string[] = []): boolean {
-        return !SourceCodeReader.isExcludedPath(directoryPath, excludePatterns);
+        return !SourceCodeFileUtils.isExcludedPath(directoryPath, excludePatterns);
     }
     }
 
 
     /**
     /**
@@ -91,7 +91,7 @@ export class SourceCodeReader {
     private static isValidFile (filePath: string, excludePatterns: string[] = []): boolean {
     private static isValidFile (filePath: string, excludePatterns: string[] = []): boolean {
         return JavaScriptObfuscatorCLI.availableInputExtensions.includes(path.extname(filePath))
         return JavaScriptObfuscatorCLI.availableInputExtensions.includes(path.extname(filePath))
             && !filePath.includes(JavaScriptObfuscatorCLI.obfuscatedFilePrefix)
             && !filePath.includes(JavaScriptObfuscatorCLI.obfuscatedFilePrefix)
-            && !SourceCodeReader.isExcludedPath(filePath, excludePatterns);
+            && !SourceCodeFileUtils.isExcludedPath(filePath, excludePatterns);
     }
     }
 
 
     /**
     /**
@@ -110,15 +110,15 @@ export class SourceCodeReader {
      */
      */
     public readSourceCode (): IFileData[] {
     public readSourceCode (): IFileData[] {
         if (
         if (
-            SourceCodeReader.isFilePath(this.inputPath)
-            && SourceCodeReader.isValidFile(this.inputPath, this.options.exclude)
+            SourceCodeFileUtils.isFilePath(this.inputPath)
+            && SourceCodeFileUtils.isValidFile(this.inputPath, this.options.exclude)
         ) {
         ) {
-            return [SourceCodeReader.readFile(this.inputPath)];
+            return [SourceCodeFileUtils.readFile(this.inputPath)];
         }
         }
 
 
         if (
         if (
-            SourceCodeReader.isDirectoryPath(this.inputPath)
-            && SourceCodeReader.isValidDirectory(this.inputPath, this.options.exclude)
+            SourceCodeFileUtils.isDirectoryPath(this.inputPath)
+            && SourceCodeFileUtils.isValidDirectory(this.inputPath, this.options.exclude)
         ) {
         ) {
             return this.readDirectoryRecursive(this.inputPath);
             return this.readDirectoryRecursive(this.inputPath);
         }
         }
@@ -142,8 +142,8 @@ export class SourceCodeReader {
                 const filePath: string = path.join(directoryPath, fileName);
                 const filePath: string = path.join(directoryPath, fileName);
 
 
                 if (
                 if (
-                    SourceCodeReader.isDirectoryPath(filePath)
-                    && SourceCodeReader.isValidDirectory(filePath, this.options.exclude)
+                    SourceCodeFileUtils.isDirectoryPath(filePath)
+                    && SourceCodeFileUtils.isValidDirectory(filePath, this.options.exclude)
                 ) {
                 ) {
                     filesData.push(...this.readDirectoryRecursive(filePath));
                     filesData.push(...this.readDirectoryRecursive(filePath));
 
 
@@ -151,10 +151,10 @@ export class SourceCodeReader {
                 }
                 }
 
 
                 if (
                 if (
-                    SourceCodeReader.isFilePath(filePath)
-                    && SourceCodeReader.isValidFile(filePath, this.options.exclude)
+                    SourceCodeFileUtils.isFilePath(filePath)
+                    && SourceCodeFileUtils.isValidFile(filePath, this.options.exclude)
                 ) {
                 ) {
-                    const fileData: IFileData = SourceCodeReader.readFile(filePath);
+                    const fileData: IFileData = SourceCodeFileUtils.readFile(filePath);
 
 
                     filesData.push(fileData);
                     filesData.push(fileData);
 
 

+ 11 - 11
src/container/InversifyContainerFacade.ts

@@ -28,7 +28,7 @@ import { ICodeTransformersRunner } from '../interfaces/code-transformers/ICodeTr
 import { IInversifyContainerFacade } from '../interfaces/container/IInversifyContainerFacade';
 import { IInversifyContainerFacade } from '../interfaces/container/IInversifyContainerFacade';
 import { IJavaScriptObfuscator } from '../interfaces/IJavaScriptObfsucator';
 import { IJavaScriptObfuscator } from '../interfaces/IJavaScriptObfsucator';
 import { ILogger } from '../interfaces/logger/ILogger';
 import { ILogger } from '../interfaces/logger/ILogger';
-import { IObfuscatedCode } from '../interfaces/source-code/IObfuscatedCode';
+import { IObfuscationResult } from '../interfaces/source-code/IObfuscationResult';
 import { ISourceCode } from '../interfaces/source-code/ISourceCode';
 import { ISourceCode } from '../interfaces/source-code/ISourceCode';
 import { INodeTransformersRunner } from '../interfaces/node-transformers/INodeTransformersRunner';
 import { INodeTransformersRunner } from '../interfaces/node-transformers/INodeTransformersRunner';
 
 
@@ -36,7 +36,7 @@ import { CodeTransformersRunner } from '../code-transformers/CodeTransformersRun
 import { JavaScriptObfuscator } from '../JavaScriptObfuscator';
 import { JavaScriptObfuscator } from '../JavaScriptObfuscator';
 import { Logger } from '../logger/Logger';
 import { Logger } from '../logger/Logger';
 import { NodeTransformersRunner } from '../node-transformers/NodeTransformersRunner';
 import { NodeTransformersRunner } from '../node-transformers/NodeTransformersRunner';
-import { ObfuscatedCode } from '../source-code/ObfuscatedCode';
+import { ObfuscationResult } from '../source-code/ObfuscationResult';
 import { SourceCode } from '../source-code/SourceCode';
 import { SourceCode } from '../source-code/SourceCode';
 
 
 export class InversifyContainerFacade implements IInversifyContainerFacade {
 export class InversifyContainerFacade implements IInversifyContainerFacade {
@@ -181,19 +181,19 @@ export class InversifyContainerFacade implements IInversifyContainerFacade {
             .inSingletonScope();
             .inSingletonScope();
 
 
         this.container
         this.container
-            .bind<IObfuscatedCode>(ServiceIdentifiers.IObfuscatedCode)
-            .to(ObfuscatedCode);
+            .bind<IObfuscationResult>(ServiceIdentifiers.IObfuscationResult)
+            .to(ObfuscationResult);
 
 
         this.container
         this.container
-            .bind<IObfuscatedCode>(ServiceIdentifiers.Factory__IObfuscatedCode)
-            .toFactory<IObfuscatedCode>((context: interfaces.Context) => {
-                return (obfuscatedCodeAsString: string, sourceMapAsString: string): IObfuscatedCode => {
-                    const obfuscatedCode: IObfuscatedCode = context.container
-                        .get<IObfuscatedCode>(ServiceIdentifiers.IObfuscatedCode);
+            .bind<IObfuscationResult>(ServiceIdentifiers.Factory__IObfuscationResult)
+            .toFactory<IObfuscationResult>((context: interfaces.Context) => {
+                return (obfuscatedCodeAsString: string, sourceMapAsString: string): IObfuscationResult => {
+                    const obfuscationResult: IObfuscationResult = context.container
+                        .get<IObfuscationResult>(ServiceIdentifiers.IObfuscationResult);
 
 
-                    obfuscatedCode.initialize(obfuscatedCodeAsString, sourceMapAsString);
+                    obfuscationResult.initialize(obfuscatedCodeAsString, sourceMapAsString);
 
 
-                    return obfuscatedCode;
+                    return obfuscationResult;
                 };
                 };
             });
             });
 
 

+ 5 - 2
src/container/ServiceIdentifiers.ts

@@ -9,7 +9,7 @@ export enum ServiceIdentifiers {
     Factory__IIdentifierNamesGenerator = 'Factory<IIdentifierNamesGenerator>',
     Factory__IIdentifierNamesGenerator = 'Factory<IIdentifierNamesGenerator>',
     Factory__INodeGuard = 'Factory<INodeGuard>',
     Factory__INodeGuard = 'Factory<INodeGuard>',
     Factory__INodeTransformer = 'Factory<INodeTransformer[]>',
     Factory__INodeTransformer = 'Factory<INodeTransformer[]>',
-    Factory__IObfuscatedCode = 'Factory<IObfuscatedCode>',
+    Factory__IObfuscationResult = 'Factory<IObfuscationResult>',
     Factory__IObjectExpressionKeysTransformerCustomNode = 'Factory<IObjectExpressionKeysTransformerCustomNode>',
     Factory__IObjectExpressionKeysTransformerCustomNode = 'Factory<IObjectExpressionKeysTransformerCustomNode>',
     Factory__IObjectExpressionExtractor = 'Factory<IObjectExpressionExtractor>',
     Factory__IObjectExpressionExtractor = 'Factory<IObjectExpressionExtractor>',
     Factory__IStringArrayCustomNode = 'Factory<IStringArrayCustomNode>',
     Factory__IStringArrayCustomNode = 'Factory<IStringArrayCustomNode>',
@@ -29,6 +29,7 @@ export enum ServiceIdentifiers {
     ICustomCodeHelperFormatter = 'ICustomCodeHelperFormatter',
     ICustomCodeHelperFormatter = 'ICustomCodeHelperFormatter',
     ICustomCodeHelperObfuscator = 'ICustomCodeHelperObfuscator',
     ICustomCodeHelperObfuscator = 'ICustomCodeHelperObfuscator',
     IEscapeSequenceEncoder = 'IEscapeSequenceEncoder',
     IEscapeSequenceEncoder = 'IEscapeSequenceEncoder',
+    IGlobalIdentifierNamesCacheStorage = 'IGlobalIdentifierNamesCacheStorage',
     IIdentifierNamesGenerator = 'IIdentifierNamesGenerator',
     IIdentifierNamesGenerator = 'IIdentifierNamesGenerator',
     IIdentifierReplacer = 'IIdentifierReplacer',
     IIdentifierReplacer = 'IIdentifierReplacer',
     IJavaScriptObfuscator = 'IJavaScriptObfuscator',
     IJavaScriptObfuscator = 'IJavaScriptObfuscator',
@@ -40,10 +41,11 @@ export enum ServiceIdentifiers {
     INodeTransformerNamesGroupsBuilder = 'INodeTransformerNamesGroupsBuilder',
     INodeTransformerNamesGroupsBuilder = 'INodeTransformerNamesGroupsBuilder',
     INodeTransformersRunner = 'INodeTransformersRunner',
     INodeTransformersRunner = 'INodeTransformersRunner',
     INumberNumericalExpressionAnalyzer = 'INumberNumericalExpressionAnalyzer',
     INumberNumericalExpressionAnalyzer = 'INumberNumericalExpressionAnalyzer',
-    IObfuscatedCode = 'IObfuscatedCode',
+    IObfuscationResult = 'IObfuscationResult',
     IOptions = 'IOptions',
     IOptions = 'IOptions',
     IOptionsNormalizer = 'IOptionsNormalizer',
     IOptionsNormalizer = 'IOptionsNormalizer',
     IPrevailingKindOfVariablesAnalyzer = 'IPrevailingKindOfVariablesAnalyzer',
     IPrevailingKindOfVariablesAnalyzer = 'IPrevailingKindOfVariablesAnalyzer',
+    IPropertyIdentifierNamesCacheStorage = 'IPropertyIdentifierNamesCacheStorage',
     IObjectExpressionExtractor = 'IObjectExpressionExtractor',
     IObjectExpressionExtractor = 'IObjectExpressionExtractor',
     IRandomGenerator = 'IRandomGenerator',
     IRandomGenerator = 'IRandomGenerator',
     IRenamePropertiesReplacer = 'IRenamePropertiesReplacer',
     IRenamePropertiesReplacer = 'IRenamePropertiesReplacer',
@@ -55,6 +57,7 @@ export enum ServiceIdentifiers {
     IStringArrayScopeCallsWrapperNamesDataStorage = 'IStringArrayScopeCallsWrapperNamesDataStorage',
     IStringArrayScopeCallsWrapperNamesDataStorage = 'IStringArrayScopeCallsWrapperNamesDataStorage',
     IStringArrayStorage = 'IStringArrayStorage',
     IStringArrayStorage = 'IStringArrayStorage',
     IStringArrayStorageAnalyzer = 'IStringArrayStorageAnalyzer',
     IStringArrayStorageAnalyzer = 'IStringArrayStorageAnalyzer',
+    IThroughIdentifierReplacer = 'IThroughIdentifierReplacer',
     IVisitedLexicalScopeNodesStackStorage = 'IVisitedLexicalScopeNodesStackStorage',
     IVisitedLexicalScopeNodesStackStorage = 'IVisitedLexicalScopeNodesStackStorage',
     Newable__ICustomNode = 'Newable<ICustomNode>',
     Newable__ICustomNode = 'Newable<ICustomNode>',
     Newable__TControlFlowStorage = 'Newable<TControlFlowStorage>',
     Newable__TControlFlowStorage = 'Newable<TControlFlowStorage>',

+ 11 - 0
src/container/modules/node-transformers/RenameIdentifiersTransformersModule.ts

@@ -6,13 +6,20 @@ import { INodeTransformer } from '../../../interfaces/node-transformers/INodeTra
 
 
 import { NodeTransformer } from '../../../enums/node-transformers/NodeTransformer';
 import { NodeTransformer } from '../../../enums/node-transformers/NodeTransformer';
 
 
+import { DeadCodeInjectionIdentifiersTransformer } from '../../../node-transformers/dead-code-injection-transformers/DeadCodeInjectionIdentifiersTransformer';
 import { IdentifierReplacer } from '../../../node-transformers/rename-identifiers-transformers/replacer/IdentifierReplacer';
 import { IdentifierReplacer } from '../../../node-transformers/rename-identifiers-transformers/replacer/IdentifierReplacer';
 import { LabeledStatementTransformer } from '../../../node-transformers/rename-identifiers-transformers/LabeledStatementTransformer';
 import { LabeledStatementTransformer } from '../../../node-transformers/rename-identifiers-transformers/LabeledStatementTransformer';
 import { ScopeIdentifiersTransformer } from '../../../node-transformers/rename-identifiers-transformers/ScopeIdentifiersTransformer';
 import { ScopeIdentifiersTransformer } from '../../../node-transformers/rename-identifiers-transformers/ScopeIdentifiersTransformer';
 import { ScopeThroughIdentifiersTransformer } from '../../../node-transformers/rename-identifiers-transformers/ScopeThroughIdentifiersTransformer';
 import { ScopeThroughIdentifiersTransformer } from '../../../node-transformers/rename-identifiers-transformers/ScopeThroughIdentifiersTransformer';
+import { ThroughIdentifierReplacer } from '../../../node-transformers/rename-identifiers-transformers/through-replacer/ThroughIdentifierReplacer';
+import { IThroughIdentifierReplacer } from '../../../interfaces/node-transformers/rename-identifiers-transformers/replacer/IThroughIdentifierReplacer';
 
 
 export const renameIdentifiersTransformersModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
 export const renameIdentifiersTransformersModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
     // rename identifiers transformers
     // rename identifiers transformers
+    bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
+        .to(DeadCodeInjectionIdentifiersTransformer)
+        .whenTargetNamed(NodeTransformer.DeadCodeInjectionIdentifiersTransformer);
+
     bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
     bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
         .to(LabeledStatementTransformer)
         .to(LabeledStatementTransformer)
         .whenTargetNamed(NodeTransformer.LabeledStatementTransformer);
         .whenTargetNamed(NodeTransformer.LabeledStatementTransformer);
@@ -29,4 +36,8 @@ export const renameIdentifiersTransformersModule: interfaces.ContainerModule = n
     bind<IIdentifierReplacer>(ServiceIdentifiers.IIdentifierReplacer)
     bind<IIdentifierReplacer>(ServiceIdentifiers.IIdentifierReplacer)
         .to(IdentifierReplacer)
         .to(IdentifierReplacer)
         .inSingletonScope();
         .inSingletonScope();
+
+    bind<IThroughIdentifierReplacer>(ServiceIdentifiers.IThroughIdentifierReplacer)
+        .to(ThroughIdentifierReplacer)
+        .inSingletonScope();
 });
 });

+ 12 - 0
src/container/modules/storages/StoragesModule.ts

@@ -4,8 +4,10 @@ import { ServiceIdentifiers } from '../../ServiceIdentifiers';
 import { TControlFlowStorage } from '../../../types/storages/TControlFlowStorage';
 import { TControlFlowStorage } from '../../../types/storages/TControlFlowStorage';
 import { TCustomCodeHelperGroupStorage } from '../../../types/storages/TCustomCodeHelperGroupStorage';
 import { TCustomCodeHelperGroupStorage } from '../../../types/storages/TCustomCodeHelperGroupStorage';
 
 
+import { IGlobalIdentifierNamesCacheStorage } from '../../../interfaces/storages/identifier-names-cache/IGlobalIdentifierNamesCacheStorage';
 import { ILiteralNodesCacheStorage } from '../../../interfaces/storages/string-array-transformers/ILiteralNodesCacheStorage';
 import { ILiteralNodesCacheStorage } from '../../../interfaces/storages/string-array-transformers/ILiteralNodesCacheStorage';
 import { IOptions } from '../../../interfaces/options/IOptions';
 import { IOptions } from '../../../interfaces/options/IOptions';
+import { IPropertyIdentifierNamesCacheStorage } from '../../../interfaces/storages/identifier-names-cache/IPropertyIdentifierNamesCacheStorage';
 import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
 import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
 import { IStringArrayScopeCallsWrapperLexicalScopeDataStorage } from '../../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperLexicalScopeDataStorage';
 import { IStringArrayScopeCallsWrapperLexicalScopeDataStorage } from '../../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperLexicalScopeDataStorage';
 import { IStringArrayScopeCallsWrapperNamesDataStorage } from '../../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperNamesDataStorage';
 import { IStringArrayScopeCallsWrapperNamesDataStorage } from '../../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperNamesDataStorage';
@@ -14,7 +16,9 @@ import { IVisitedLexicalScopeNodesStackStorage } from '../../../interfaces/stora
 
 
 import { ControlFlowStorage } from '../../../storages/custom-nodes/ControlFlowStorage';
 import { ControlFlowStorage } from '../../../storages/custom-nodes/ControlFlowStorage';
 import { CustomCodeHelperGroupStorage } from '../../../storages/custom-code-helpers/CustomCodeHelperGroupStorage';
 import { CustomCodeHelperGroupStorage } from '../../../storages/custom-code-helpers/CustomCodeHelperGroupStorage';
+import { GlobalIdentifierNamesCacheStorage } from '../../../storages/identifier-names-cache/GlobalIdentifierNamesCacheStorage';
 import { LiteralNodesCacheStorage } from '../../../storages/string-array-transformers/LiteralNodesCacheStorage';
 import { LiteralNodesCacheStorage } from '../../../storages/string-array-transformers/LiteralNodesCacheStorage';
+import { PropertyIdentifierNamesCacheStorage } from '../../../storages/identifier-names-cache/PropertyIdentifierNamesCacheStorage';
 import { StringArrayScopeCallsWrapperLexicalScopeDataStorage } from '../../../storages/string-array-transformers/StringArrayScopeCallsWrapperLexicalScopeDataStorage';
 import { StringArrayScopeCallsWrapperLexicalScopeDataStorage } from '../../../storages/string-array-transformers/StringArrayScopeCallsWrapperLexicalScopeDataStorage';
 import { StringArrayScopeCallsWrapperNamesDataStorage } from '../../../storages/string-array-transformers/StringArrayScopeCallsWrapperNamesDataStorage';
 import { StringArrayScopeCallsWrapperNamesDataStorage } from '../../../storages/string-array-transformers/StringArrayScopeCallsWrapperNamesDataStorage';
 import { StringArrayStorage } from '../../../storages/string-array-transformers/StringArrayStorage';
 import { StringArrayStorage } from '../../../storages/string-array-transformers/StringArrayStorage';
@@ -26,10 +30,18 @@ export const storagesModule: interfaces.ContainerModule = new ContainerModule((b
         .to(CustomCodeHelperGroupStorage)
         .to(CustomCodeHelperGroupStorage)
         .inSingletonScope();
         .inSingletonScope();
 
 
+    bind<IGlobalIdentifierNamesCacheStorage>(ServiceIdentifiers.IGlobalIdentifierNamesCacheStorage)
+        .to(GlobalIdentifierNamesCacheStorage)
+        .inSingletonScope();
+
     bind<ILiteralNodesCacheStorage>(ServiceIdentifiers.ILiteralNodesCacheStorage)
     bind<ILiteralNodesCacheStorage>(ServiceIdentifiers.ILiteralNodesCacheStorage)
         .to(LiteralNodesCacheStorage)
         .to(LiteralNodesCacheStorage)
         .inSingletonScope();
         .inSingletonScope();
 
 
+    bind<IPropertyIdentifierNamesCacheStorage>(ServiceIdentifiers.IPropertyIdentifierNamesCacheStorage)
+        .to(PropertyIdentifierNamesCacheStorage)
+        .inSingletonScope();
+
     bind<IStringArrayStorage>(ServiceIdentifiers.IStringArrayStorage)
     bind<IStringArrayStorage>(ServiceIdentifiers.IStringArrayStorage)
         .to(StringArrayStorage)
         .to(StringArrayStorage)
         .inSingletonScope();
         .inSingletonScope();

+ 1 - 1
src/custom-code-helpers/calls-controller/CallsControllerFunctionCodeHelper.ts

@@ -76,7 +76,7 @@ export class CallsControllerFunctionCodeHelper extends AbstractCustomCodeHelper
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    protected getCodeHelperTemplate (): string {
+    protected override getCodeHelperTemplate (): string {
         if (this.nodeTransformationStage === NodeTransformationStage.Finalizing) {
         if (this.nodeTransformationStage === NodeTransformationStage.Finalizing) {
             return this.customCodeHelperObfuscator.obfuscateTemplate(
             return this.customCodeHelperObfuscator.obfuscateTemplate(
                 this.customCodeHelperFormatter.formatTemplate(SingleCallControllerTemplate(), {
                 this.customCodeHelperFormatter.formatTemplate(SingleCallControllerTemplate(), {

+ 1 - 1
src/custom-code-helpers/console-output/ConsoleOutputDisableCodeHelper.ts

@@ -77,7 +77,7 @@ export class ConsoleOutputDisableCodeHelper extends AbstractCustomCodeHelper {
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    protected getCodeHelperTemplate (): string {
+    protected override getCodeHelperTemplate (): string {
         const globalVariableTemplate: string = this.options.target !== ObfuscationTarget.BrowserNoEval
         const globalVariableTemplate: string = this.options.target !== ObfuscationTarget.BrowserNoEval
             ? this.getGlobalVariableTemplate()
             ? this.getGlobalVariableTemplate()
             : GlobalVariableNoEvalTemplate();
             : GlobalVariableNoEvalTemplate();

+ 1 - 1
src/custom-code-helpers/debug-protection/DebugProtectionFunctionCallCodeHelper.ts

@@ -74,7 +74,7 @@ export class DebugProtectionFunctionCallCodeHelper extends AbstractCustomCodeHel
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    protected getCodeHelperTemplate (): string {
+    protected override getCodeHelperTemplate (): string {
         return this.customCodeHelperFormatter.formatTemplate(DebugProtectionFunctionCallTemplate(), {
         return this.customCodeHelperFormatter.formatTemplate(DebugProtectionFunctionCallTemplate(), {
             debugProtectionFunctionName: this.debugProtectionFunctionName,
             debugProtectionFunctionName: this.debugProtectionFunctionName,
             callControllerFunctionName: this.callsControllerFunctionName
             callControllerFunctionName: this.callsControllerFunctionName

+ 1 - 1
src/custom-code-helpers/debug-protection/DebugProtectionFunctionCodeHelper.ts

@@ -70,7 +70,7 @@ export class DebugProtectionFunctionCodeHelper extends AbstractCustomCodeHelper
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    protected getCodeHelperTemplate (): string {
+    protected override getCodeHelperTemplate (): string {
         const debuggerTemplate: string = this.options.target !== ObfuscationTarget.BrowserNoEval
         const debuggerTemplate: string = this.options.target !== ObfuscationTarget.BrowserNoEval
             ? DebuggerTemplate()
             ? DebuggerTemplate()
             : DebuggerTemplateNoEval();
             : DebuggerTemplateNoEval();

+ 1 - 1
src/custom-code-helpers/debug-protection/DebugProtectionFunctionIntervalCodeHelper.ts

@@ -66,7 +66,7 @@ export class DebugProtectionFunctionIntervalCodeHelper extends AbstractCustomCod
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    protected getCodeHelperTemplate (): string {
+    protected override getCodeHelperTemplate (): string {
         return this.customCodeHelperFormatter.formatTemplate(DebugProtectionFunctionIntervalTemplate(), {
         return this.customCodeHelperFormatter.formatTemplate(DebugProtectionFunctionIntervalTemplate(), {
             debugProtectionFunctionName: this.debugProtectionFunctionName
             debugProtectionFunctionName: this.debugProtectionFunctionName
         });
         });

+ 1 - 1
src/custom-code-helpers/domain-lock/DomainLockCodeHelper.ts

@@ -87,7 +87,7 @@ export class DomainLockCodeHelper extends AbstractCustomCodeHelper {
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    protected getCodeHelperTemplate (): string {
+    protected override getCodeHelperTemplate (): string {
         const domainsString: string = this.options.domainLock.join(';');
         const domainsString: string = this.options.domainLock.join(';');
         const [hiddenDomainsString, diff]: string[] = this.cryptUtils.hideString(
         const [hiddenDomainsString, diff]: string[] = this.cryptUtils.hideString(
             domainsString,
             domainsString,

+ 1 - 1
src/custom-code-helpers/self-defending/SelfDefendingUnicodeCodeHelper.ts

@@ -78,7 +78,7 @@ export class SelfDefendingUnicodeCodeHelper extends AbstractCustomCodeHelper {
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    protected getCodeHelperTemplate (): string {
+    protected override getCodeHelperTemplate (): string {
         const globalVariableTemplate: string = this.options.target !== ObfuscationTarget.BrowserNoEval
         const globalVariableTemplate: string = this.options.target !== ObfuscationTarget.BrowserNoEval
             ? this.getGlobalVariableTemplate()
             ? this.getGlobalVariableTemplate()
             : GlobalVariableNoEvalTemplate();
             : GlobalVariableNoEvalTemplate();

+ 1 - 1
src/custom-code-helpers/string-array/StringArrayCallsWrapperBase64CodeHelper.ts

@@ -10,7 +10,7 @@ export class StringArrayCallsWrapperBase64CodeHelper extends StringArrayCallsWra
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    protected getDecodeStringArrayTemplate (): string {
+    protected override getDecodeStringArrayTemplate (): string {
         const atobFunctionName: string = this.randomGenerator.getRandomString(6);
         const atobFunctionName: string = this.randomGenerator.getRandomString(6);
 
 
         const atobPolyfill: string = this.customCodeHelperFormatter.formatTemplate(AtobTemplate(), {
         const atobPolyfill: string = this.customCodeHelperFormatter.formatTemplate(AtobTemplate(), {

+ 1 - 1
src/custom-code-helpers/string-array/StringArrayCallsWrapperCodeHelper.ts

@@ -97,7 +97,7 @@ export class StringArrayCallsWrapperCodeHelper extends AbstractCustomCodeHelper
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    protected getCodeHelperTemplate (): string {
+    protected override getCodeHelperTemplate (): string {
         const decodeCodeHelperTemplate: string = this.getDecodeStringArrayTemplate();
         const decodeCodeHelperTemplate: string = this.getDecodeStringArrayTemplate();
 
 
         const preservedNames: string[] = [`^${this.stringArrayName}$`];
         const preservedNames: string[] = [`^${this.stringArrayName}$`];

+ 1 - 1
src/custom-code-helpers/string-array/StringArrayCallsWrapperRc4CodeHelper.ts

@@ -11,7 +11,7 @@ export class StringArrayCallsWrapperRc4CodeHelper extends StringArrayCallsWrappe
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    protected getDecodeStringArrayTemplate (): string {
+    protected override getDecodeStringArrayTemplate (): string {
         const atobFunctionName: string = this.randomGenerator.getRandomString(6);
         const atobFunctionName: string = this.randomGenerator.getRandomString(6);
 
 
         const atobPolyfill: string = this.customCodeHelperFormatter.formatTemplate(AtobTemplate(), {
         const atobPolyfill: string = this.customCodeHelperFormatter.formatTemplate(AtobTemplate(), {

+ 1 - 1
src/custom-code-helpers/string-array/StringArrayCodeHelper.ts

@@ -80,7 +80,7 @@ export class StringArrayCodeHelper extends AbstractCustomCodeHelper {
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    protected getCodeHelperTemplate (): string {
+    protected override getCodeHelperTemplate (): string {
         return this.customCodeHelperFormatter.formatTemplate(StringArrayTemplate(), {
         return this.customCodeHelperFormatter.formatTemplate(StringArrayTemplate(), {
             stringArrayName: this.stringArrayName,
             stringArrayName: this.stringArrayName,
             stringArrayStorageItems: this.getEncodedStringArrayStorageItems()
             stringArrayStorageItems: this.getEncodedStringArrayStorageItems()

+ 1 - 1
src/custom-code-helpers/string-array/StringArrayRotateFunctionCodeHelper.ts

@@ -87,7 +87,7 @@ export class StringArrayRotateFunctionCodeHelper extends AbstractCustomCodeHelpe
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    protected getCodeHelperTemplate (): string {
+    protected override getCodeHelperTemplate (): string {
         const comparisonExpressionCode: string = NodeUtils.convertStructureToCode([this.comparisonExpressionNode]);
         const comparisonExpressionCode: string = NodeUtils.convertStructureToCode([this.comparisonExpressionNode]);
 
 
         return this.customCodeHelperFormatter.formatTemplate(
         return this.customCodeHelperFormatter.formatTemplate(

+ 12 - 10
src/custom-nodes/control-flow-flattening-nodes/CallExpressionFunctionNode.ts

@@ -68,16 +68,18 @@ export class CallExpressionFunctionNode extends AbstractCustomNode {
 
 
             const baseIdentifierNode: ESTree.Identifier = NodeFactory.identifierNode(`param${i + 1}`);
             const baseIdentifierNode: ESTree.Identifier = NodeFactory.identifierNode(`param${i + 1}`);
 
 
-            params.push(
-                isSpreadCallArgument
-                    ? NodeFactory.restElementNode(baseIdentifierNode)
-                    : baseIdentifierNode
-            );
-            callArguments.push(
-                isSpreadCallArgument
-                    ? NodeFactory.spreadElementNode(baseIdentifierNode)
-                    : baseIdentifierNode
-            );
+            if (isSpreadCallArgument) {
+                params.push(NodeFactory.restElementNode(baseIdentifierNode));
+                callArguments.push(NodeFactory.spreadElementNode(baseIdentifierNode));
+
+                const isMiddleSpreadCallArgument: boolean = i < argumentsLength - 1;
+                if (isMiddleSpreadCallArgument) {
+                    break;
+                }
+            } else {
+                params.push(baseIdentifierNode);
+                callArguments.push(baseIdentifierNode);
+            }
         }
         }
 
 
         const structure: TStatement = NodeFactory.expressionStatementNode(
         const structure: TStatement = NodeFactory.expressionStatementNode(

+ 1 - 1
src/custom-nodes/dead-code-injection-nodes/BlockStatementDeadCodeInjectionNode.ts

@@ -64,7 +64,7 @@ export class BlockStatementDeadCodeInjectionNode extends AbstractCustomNode {
      *
      *
      * @returns {TStatement[]}
      * @returns {TStatement[]}
      */
      */
-    public getNode (): TStatement[] {
+    public override getNode (): TStatement[] {
         return this.getNodeStructure();
         return this.getNodeStructure();
     }
     }
 
 

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

@@ -4,6 +4,7 @@ export enum NodeTransformer {
     BlockStatementSimplifyTransformer = 'BlockStatementSimplifyTransformer',
     BlockStatementSimplifyTransformer = 'BlockStatementSimplifyTransformer',
     CommentsTransformer = 'CommentsTransformer',
     CommentsTransformer = 'CommentsTransformer',
     CustomCodeHelpersTransformer = 'CustomCodeHelpersTransformer',
     CustomCodeHelpersTransformer = 'CustomCodeHelpersTransformer',
+    DeadCodeInjectionIdentifiersTransformer = 'DeadCodeInjectionIdentifiersTransformer',
     DeadCodeInjectionTransformer = 'DeadCodeInjectionTransformer',
     DeadCodeInjectionTransformer = 'DeadCodeInjectionTransformer',
     DirectivePlacementTransformer = 'DirectivePlacementTransformer',
     DirectivePlacementTransformer = 'DirectivePlacementTransformer',
     EscapeSequenceTransformer = 'EscapeSequenceTransformer',
     EscapeSequenceTransformer = 'EscapeSequenceTransformer',

+ 1 - 1
src/generators/identifier-names-generators/MangledIdentifierNamesGenerator.ts

@@ -165,7 +165,7 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene
      * @param {string} mangledName
      * @param {string} mangledName
      * @returns {boolean}
      * @returns {boolean}
      */
      */
-    public isValidIdentifierName (mangledName: string): boolean {
+    public override isValidIdentifierName (mangledName: string): boolean {
         return super.isValidIdentifierName(mangledName)
         return super.isValidIdentifierName(mangledName)
             && !MangledIdentifierNamesGenerator.reservedNamesSet.has(mangledName);
             && !MangledIdentifierNamesGenerator.reservedNamesSet.has(mangledName);
     }
     }

+ 2 - 2
src/generators/identifier-names-generators/MangledShuffledIdentifierNamesGenerator.ts

@@ -58,7 +58,7 @@ export class MangledShuffledIdentifierNamesGenerator extends MangledIdentifierNa
     /**
     /**
      * @returns {string[]}
      * @returns {string[]}
      */
      */
-    protected getNameSequence (): string[] {
+    protected override getNameSequence (): string[] {
         return MangledShuffledIdentifierNamesGenerator.shuffledNameSequence;
         return MangledShuffledIdentifierNamesGenerator.shuffledNameSequence;
     }
     }
 
 
@@ -66,7 +66,7 @@ export class MangledShuffledIdentifierNamesGenerator extends MangledIdentifierNa
      * @param {string} previousMangledName
      * @param {string} previousMangledName
      * @returns {string}
      * @returns {string}
      */
      */
-    protected generateNewMangledName (previousMangledName: string): string {
+    protected override generateNewMangledName (previousMangledName: string): string {
         return super.generateNewMangledName(previousMangledName);
         return super.generateNewMangledName(previousMangledName);
     }
     }
 }
 }

+ 3 - 3
src/interfaces/IJavaScriptObfsucator.ts

@@ -1,9 +1,9 @@
-import { IObfuscatedCode } from './source-code/IObfuscatedCode';
+import { IObfuscationResult } from './source-code/IObfuscationResult';
 
 
 export interface IJavaScriptObfuscator {
 export interface IJavaScriptObfuscator {
     /**
     /**
      * @param sourceCode
      * @param sourceCode
-     * @returns IObfuscatedCode
+     * @returns IObfuscationResult
      */
      */
-    obfuscate (sourceCode: string): IObfuscatedCode;
+    obfuscate (sourceCode: string): IObfuscationResult;
 }
 }

+ 9 - 0
src/interfaces/node-transformers/rename-identifiers-transformers/replacer/IThroughIdentifierReplacer.ts

@@ -0,0 +1,9 @@
+import * as ESTree from 'estree';
+
+export interface IThroughIdentifierReplacer {
+    /**
+     * @param {Identifier} identifierNode
+     * @returns {Identifier}
+     */
+    replace (identifierNode: ESTree.Identifier): ESTree.Identifier;
+}

+ 1 - 0
src/interfaces/node/IScopeThroughIdentifiersTraverserCallbackData.ts

@@ -3,6 +3,7 @@ import * as eslintScope from 'eslint-scope';
 import { TNodeWithLexicalScope } from '../../types/node/TNodeWithLexicalScope';
 import { TNodeWithLexicalScope } from '../../types/node/TNodeWithLexicalScope';
 
 
 export interface IScopeThroughIdentifiersTraverserCallbackData {
 export interface IScopeThroughIdentifiersTraverserCallbackData {
+    isGlobalDeclaration: boolean;
     reference: eslintScope.Reference;
     reference: eslintScope.Reference;
     variableLexicalScopeNode: TNodeWithLexicalScope;
     variableLexicalScopeNode: TNodeWithLexicalScope;
 }
 }

+ 1 - 0
src/interfaces/options/ICLIOptions.ts

@@ -3,6 +3,7 @@ import { IOptions } from './IOptions';
 export interface ICLIOptions extends IOptions {
 export interface ICLIOptions extends IOptions {
     readonly config: string;
     readonly config: string;
     readonly exclude: string[];
     readonly exclude: string[];
+    readonly identifierNamesCachePath: string;
     readonly output: string;
     readonly output: string;
     readonly version: string;
     readonly version: string;
 }
 }

+ 2 - 0
src/interfaces/options/IOptions.ts

@@ -1,3 +1,4 @@
+import { TIdentifierNamesCache } from '../../types/TIdentifierNamesCache';
 import { TOptionsPreset } from '../../types/options/TOptionsPreset';
 import { TOptionsPreset } from '../../types/options/TOptionsPreset';
 import { TStringArrayIndexesType } from '../../types/options/TStringArrayIndexesType';
 import { TStringArrayIndexesType } from '../../types/options/TStringArrayIndexesType';
 import { TStringArrayEncoding } from '../../types/options/TStringArrayEncoding';
 import { TStringArrayEncoding } from '../../types/options/TStringArrayEncoding';
@@ -20,6 +21,7 @@ export interface IOptions {
     readonly disableConsoleOutput: boolean;
     readonly disableConsoleOutput: boolean;
     readonly domainLock: string[];
     readonly domainLock: string[];
     readonly forceTransformStrings: string[];
     readonly forceTransformStrings: string[];
+    readonly identifierNamesCache: TIdentifierNamesCache;
     readonly identifierNamesGenerator: TTypeFromEnum<typeof IdentifierNamesGenerator>;
     readonly identifierNamesGenerator: TTypeFromEnum<typeof IdentifierNamesGenerator>;
     readonly identifiersDictionary: string[];
     readonly identifiersDictionary: string[];
     readonly identifiersPrefix: string;
     readonly identifiersPrefix: string;

+ 0 - 13
src/interfaces/source-code/IObfuscatedCode.ts

@@ -1,13 +0,0 @@
-import { IInitializable } from '../IInitializable';
-
-export interface IObfuscatedCode extends IInitializable <[string, string]> {
-    /**
-     * @return {string}
-     */
-    getObfuscatedCode (): string;
-
-    /**
-     * @return {string}
-     */
-    getSourceMap (): string;
-}

+ 19 - 0
src/interfaces/source-code/IObfuscationResult.ts

@@ -0,0 +1,19 @@
+import { TIdentifierNamesCache } from '../../types/TIdentifierNamesCache';
+import { IInitializable } from '../IInitializable';
+
+export interface IObfuscationResult extends IInitializable <[string, string]> {
+    /**
+     * @returns {TIdentifierNamesCache}
+     */
+    getIdentifierNamesCache (): TIdentifierNamesCache;
+
+    /**
+     * @return {string}
+     */
+    getObfuscatedCode (): string;
+
+    /**
+     * @return {string}
+     */
+    getSourceMap (): string;
+}

+ 7 - 0
src/interfaces/storages/IMapStorage.ts

@@ -1,3 +1,5 @@
+import { TDictionary } from '../../types/TDictionary';
+
 import { IInitializable } from '../IInitializable';
 import { IInitializable } from '../IInitializable';
 
 
 export interface IMapStorage <K, V> extends IInitializable {
 export interface IMapStorage <K, V> extends IInitializable {
@@ -29,6 +31,11 @@ export interface IMapStorage <K, V> extends IInitializable {
      */
      */
     getStorage (): Map <K, V>;
     getStorage (): Map <K, V>;
 
 
+    /**
+     * @returns {TDictionary<V>}
+     */
+    getStorageAsDictionary (): TDictionary<V>;
+
     /**
     /**
      * @returns string
      * @returns string
      */
      */

+ 4 - 0
src/interfaces/storages/identifier-names-cache/IGlobalIdentifierNamesCacheStorage.ts

@@ -0,0 +1,4 @@
+import { IMapStorage } from '../IMapStorage';
+
+// eslint-disable-next-line
+export interface IGlobalIdentifierNamesCacheStorage extends IMapStorage <string, string> {}

+ 4 - 0
src/interfaces/storages/identifier-names-cache/IPropertyIdentifierNamesCacheStorage.ts

@@ -0,0 +1,4 @@
+import { IMapStorage } from '../IMapStorage';
+
+// eslint-disable-next-line
+export interface IPropertyIdentifierNamesCacheStorage extends IMapStorage <string, string> {}

+ 1 - 1
src/node-transformers/converting-transformers/NumberLiteralTransformer.ts

@@ -22,7 +22,7 @@ export class NumberLiteralTransformer extends AbstractNodeTransformer {
      *
      *
      * @type {NodeTransformer[]}
      * @type {NodeTransformer[]}
      */
      */
-    public readonly runAfter: NodeTransformer[] = [
+    public override readonly runAfter: NodeTransformer[] = [
         NodeTransformer.NumberToNumericalExpressionTransformer
         NodeTransformer.NumberToNumericalExpressionTransformer
     ];
     ];
 
 

+ 1 - 1
src/node-transformers/converting-transformers/SplitStringTransformer.ts

@@ -31,7 +31,7 @@ export class SplitStringTransformer extends AbstractNodeTransformer {
     /**
     /**
      * @type {NodeTransformer[]}
      * @type {NodeTransformer[]}
      */
      */
-    public runAfter: NodeTransformer[] = [
+    public override runAfter: NodeTransformer[] = [
         NodeTransformer.ObjectExpressionKeysTransformer,
         NodeTransformer.ObjectExpressionKeysTransformer,
         NodeTransformer.TemplateLiteralTransformer
         NodeTransformer.TemplateLiteralTransformer
     ];
     ];

+ 141 - 0
src/node-transformers/dead-code-injection-transformers/DeadCodeInjectionIdentifiersTransformer.ts

@@ -0,0 +1,141 @@
+import { inject, injectable, } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
+import * as eslintScope from 'eslint-scope';
+import * as ESTree from 'estree';
+
+import { TNodeWithLexicalScope } from '../../types/node/TNodeWithLexicalScope';
+
+import { IIdentifierReplacer } from '../../interfaces/node-transformers/rename-identifiers-transformers/replacer/IIdentifierReplacer';
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { IScopeIdentifiersTraverser } from '../../interfaces/node/IScopeIdentifiersTraverser';
+import { IScopeThroughIdentifiersTraverserCallbackData } from '../../interfaces/node/IScopeThroughIdentifiersTraverserCallbackData';
+import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
+
+import { NodeTransformationStage } from '../../enums/node-transformers/NodeTransformationStage';
+
+import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
+import { NodeGuards } from '../../node/NodeGuards';
+
+/**
+ * Renames all scope through identifiers for Dead Code Injection
+ */
+@injectable()
+export class DeadCodeInjectionIdentifiersTransformer extends AbstractNodeTransformer {
+    /**
+     * @type {IIdentifierReplacer}
+     */
+    private readonly identifierReplacer: IIdentifierReplacer;
+
+    /**
+     * @type {IScopeIdentifiersTraverser}
+     */
+    private readonly scopeIdentifiersTraverser: IScopeIdentifiersTraverser;
+
+    /**
+     * @param {IIdentifierReplacer} identifierReplacer
+     * @param {IRandomGenerator} randomGenerator
+     * @param {IOptions} options
+     * @param {IScopeIdentifiersTraverser} scopeIdentifiersTraverser
+     */
+    public constructor (
+        @inject(ServiceIdentifiers.IIdentifierReplacer) identifierReplacer: IIdentifierReplacer,
+        @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions,
+        @inject(ServiceIdentifiers.IScopeIdentifiersTraverser) scopeIdentifiersTraverser: IScopeIdentifiersTraverser
+    ) {
+        super(randomGenerator, options);
+
+        this.identifierReplacer = identifierReplacer;
+        this.scopeIdentifiersTraverser = scopeIdentifiersTraverser;
+    }
+
+    /**
+     * @param {NodeTransformationStage} nodeTransformationStage
+     * @returns {IVisitor | null}
+     */
+    public getVisitor (nodeTransformationStage: NodeTransformationStage): IVisitor | null {
+        switch (nodeTransformationStage) {
+            case NodeTransformationStage.RenameIdentifiers:
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node | undefined => {
+                        if (parentNode && NodeGuards.isProgramNode(node)) {
+                            return this.transformNode(node, parentNode);
+                        }
+                    }
+                };
+
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * @param {VariableDeclaration} programNode
+     * @param {NodeGuards} parentNode
+     * @returns {NodeGuards}
+     */
+    public transformNode (programNode: ESTree.Program, parentNode: ESTree.Node): ESTree.Node {
+        this.scopeIdentifiersTraverser.traverseScopeThroughIdentifiers(
+            programNode,
+            parentNode,
+            (data: IScopeThroughIdentifiersTraverserCallbackData) => {
+                const {
+                    reference,
+                    variableLexicalScopeNode
+                } = data;
+
+                this.transformScopeThroughIdentifiers(reference, variableLexicalScopeNode);
+            }
+        );
+
+        return programNode;
+    }
+
+    /**
+     * @param {Reference} reference
+     * @param {TNodeWithLexicalScope} lexicalScopeNode
+     */
+    private transformScopeThroughIdentifiers (
+        reference: eslintScope.Reference,
+        lexicalScopeNode: TNodeWithLexicalScope,
+    ): void {
+        if (reference.resolved) {
+            return;
+        }
+
+        const identifier: ESTree.Identifier = reference.identifier;
+
+        this.storeIdentifierName(identifier, lexicalScopeNode);
+        this.replaceIdentifierName(identifier, lexicalScopeNode, reference);
+    }
+
+    /**
+     * @param {Identifier} identifierNode
+     * @param {TNodeWithLexicalScope} lexicalScopeNode
+     */
+    private storeIdentifierName (
+        identifierNode: ESTree.Identifier,
+        lexicalScopeNode: TNodeWithLexicalScope
+    ): void {
+        this.identifierReplacer.storeLocalName(identifierNode, lexicalScopeNode);
+    }
+
+    /**
+     * @param {Identifier} identifierNode
+     * @param {TNodeWithLexicalScope} lexicalScopeNode
+     * @param {Variable} reference
+     */
+    private replaceIdentifierName (
+        identifierNode: ESTree.Identifier,
+        lexicalScopeNode: TNodeWithLexicalScope,
+        reference: eslintScope.Reference
+    ): void {
+        const newIdentifier: ESTree.Identifier = this.identifierReplacer
+            .replace(identifierNode, lexicalScopeNode);
+
+        // rename of identifier
+        reference.identifier.name = newIdentifier.name;
+    }
+}

+ 2 - 2
src/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.ts

@@ -47,9 +47,9 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
      * @type {NodeTransformer[]}
      * @type {NodeTransformer[]}
      */
      */
     private static readonly transformersToRenameBlockScopeIdentifiers: NodeTransformer[] = [
     private static readonly transformersToRenameBlockScopeIdentifiers: NodeTransformer[] = [
+        NodeTransformer.DeadCodeInjectionIdentifiersTransformer,
         NodeTransformer.LabeledStatementTransformer,
         NodeTransformer.LabeledStatementTransformer,
-        NodeTransformer.ScopeIdentifiersTransformer,
-        NodeTransformer.ScopeThroughIdentifiersTransformer
+        NodeTransformer.ScopeIdentifiersTransformer
     ];
     ];
 
 
     /**
     /**

+ 1 - 1
src/node-transformers/finalizing-transformers/DirectivePlacementTransformer.ts

@@ -28,7 +28,7 @@ export class DirectivePlacementTransformer extends AbstractNodeTransformer {
     /**
     /**
      * @type {NodeTransformer[]}
      * @type {NodeTransformer[]}
      */
      */
-    public readonly runAfter: NodeTransformer[] = [
+    public override readonly runAfter: NodeTransformer[] = [
         NodeTransformer.CustomCodeHelpersTransformer
         NodeTransformer.CustomCodeHelpersTransformer
     ];
     ];
 
 

+ 1 - 1
src/node-transformers/finalizing-transformers/EscapeSequenceTransformer.ts

@@ -22,7 +22,7 @@ export class EscapeSequenceTransformer extends AbstractNodeTransformer {
     /**
     /**
      * @type {NodeTransformer[]}
      * @type {NodeTransformer[]}
      */
      */
-    public readonly runAfter: NodeTransformer[] = [
+    public override readonly runAfter: NodeTransformer[] = [
         NodeTransformer.CustomCodeHelpersTransformer
         NodeTransformer.CustomCodeHelpersTransformer
     ];
     ];
 
 

+ 2 - 2
src/node-transformers/preparing-transformers/CustomCodeHelpersTransformer.ts

@@ -26,9 +26,9 @@ import { NodeGuards } from '../../node/NodeGuards';
 @injectable()
 @injectable()
 export class CustomCodeHelpersTransformer extends AbstractNodeTransformer {
 export class CustomCodeHelpersTransformer extends AbstractNodeTransformer {
     /**
     /**
-     * @type {NodeTransformer.ParentificationTransformer[]}
+     * @type {NodeTransformer[]}
      */
      */
-    public readonly runAfter: NodeTransformer[] = [
+    public override readonly runAfter: NodeTransformer[] = [
         NodeTransformer.ParentificationTransformer,
         NodeTransformer.ParentificationTransformer,
         NodeTransformer.VariablePreserveTransformer
         NodeTransformer.VariablePreserveTransformer
     ];
     ];

+ 2 - 2
src/node-transformers/preparing-transformers/EvalCallExpressionTransformer.ts

@@ -19,9 +19,9 @@ import { StringUtils } from '../../utils/StringUtils';
 @injectable()
 @injectable()
 export class EvalCallExpressionTransformer extends AbstractNodeTransformer {
 export class EvalCallExpressionTransformer extends AbstractNodeTransformer {
     /**
     /**
-     * @type {NodeTransformer.NodeTransformer[]}
+     * @type {NodeTransformer[]}
      */
      */
-    public readonly runAfter: NodeTransformer[] = [
+    public override readonly runAfter: NodeTransformer[] = [
         NodeTransformer.EscapeSequenceTransformer,
         NodeTransformer.EscapeSequenceTransformer,
         NodeTransformer.ParentificationTransformer,
         NodeTransformer.ParentificationTransformer,
         NodeTransformer.VariablePreserveTransformer
         NodeTransformer.VariablePreserveTransformer

+ 2 - 2
src/node-transformers/preparing-transformers/MetadataTransformer.ts

@@ -20,9 +20,9 @@ import { NodeMetadata } from '../../node/NodeMetadata';
 @injectable()
 @injectable()
 export class MetadataTransformer extends AbstractNodeTransformer {
 export class MetadataTransformer extends AbstractNodeTransformer {
     /**
     /**
-     * @type {NodeTransformer.ParentificationTransformer[]}
+     * @type {NodeTransformer[]}
      */
      */
-    public readonly runAfter: NodeTransformer[] = [
+    public override readonly runAfter: NodeTransformer[] = [
         NodeTransformer.ParentificationTransformer,
         NodeTransformer.ParentificationTransformer,
         NodeTransformer.VariablePreserveTransformer
         NodeTransformer.VariablePreserveTransformer
     ];
     ];

+ 2 - 2
src/node-transformers/preparing-transformers/ObfuscatingGuardsTransformer.ts

@@ -36,9 +36,9 @@ export class ObfuscatingGuardsTransformer extends AbstractNodeTransformer {
     ];
     ];
 
 
     /**
     /**
-     * @type {NodeTransformer.ParentificationTransformer[]}
+     * @type {NodeTransformer[]}
      */
      */
-    public readonly runAfter: NodeTransformer[] = [
+    public override readonly runAfter: NodeTransformer[] = [
         NodeTransformer.ParentificationTransformer,
         NodeTransformer.ParentificationTransformer,
         NodeTransformer.VariablePreserveTransformer
         NodeTransformer.VariablePreserveTransformer
     ];
     ];

+ 1 - 1
src/node-transformers/preparing-transformers/VariablePreserveTransformer.ts

@@ -26,7 +26,7 @@ export class VariablePreserveTransformer extends AbstractNodeTransformer {
     /**
     /**
      * @type {NodeTransformer.ParentificationTransformer[]}
      * @type {NodeTransformer.ParentificationTransformer[]}
      */
      */
-    public readonly runAfter: NodeTransformer[] = [
+    public override readonly runAfter: NodeTransformer[] = [
         NodeTransformer.ParentificationTransformer
         NodeTransformer.ParentificationTransformer
     ];
     ];
 
 

+ 22 - 39
src/node-transformers/rename-identifiers-transformers/ScopeThroughIdentifiersTransformer.ts

@@ -6,11 +6,11 @@ import * as ESTree from 'estree';
 
 
 import { TNodeWithLexicalScope } from '../../types/node/TNodeWithLexicalScope';
 import { TNodeWithLexicalScope } from '../../types/node/TNodeWithLexicalScope';
 
 
-import { IIdentifierReplacer } from '../../interfaces/node-transformers/rename-identifiers-transformers/replacer/IIdentifierReplacer';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IScopeIdentifiersTraverser } from '../../interfaces/node/IScopeIdentifiersTraverser';
 import { IScopeIdentifiersTraverser } from '../../interfaces/node/IScopeIdentifiersTraverser';
 import { IScopeThroughIdentifiersTraverserCallbackData } from '../../interfaces/node/IScopeThroughIdentifiersTraverserCallbackData';
 import { IScopeThroughIdentifiersTraverserCallbackData } from '../../interfaces/node/IScopeThroughIdentifiersTraverserCallbackData';
+import { IThroughIdentifierReplacer } from '../../interfaces/node-transformers/rename-identifiers-transformers/replacer/IThroughIdentifierReplacer';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
 
 import { NodeTransformationStage } from '../../enums/node-transformers/NodeTransformationStage';
 import { NodeTransformationStage } from '../../enums/node-transformers/NodeTransformationStage';
@@ -19,35 +19,35 @@ import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeGuards } from '../../node/NodeGuards';
 import { NodeGuards } from '../../node/NodeGuards';
 
 
 /**
 /**
- * Renames all through identifiers. Now used directly from Dead Code Injection transformer
+ * Renames all through identifiers
  */
  */
 @injectable()
 @injectable()
 export class ScopeThroughIdentifiersTransformer extends AbstractNodeTransformer {
 export class ScopeThroughIdentifiersTransformer extends AbstractNodeTransformer {
     /**
     /**
-     * @type {IIdentifierReplacer}
+     * @type {IScopeIdentifiersTraverser}
      */
      */
-    private readonly identifierReplacer: IIdentifierReplacer;
+    protected readonly scopeIdentifiersTraverser: IScopeIdentifiersTraverser;
 
 
     /**
     /**
-     * @type {IScopeIdentifiersTraverser}
+     * @type {IThroughIdentifierReplacer}
      */
      */
-    private readonly scopeIdentifiersTraverser: IScopeIdentifiersTraverser;
+    protected readonly throughIdentifierReplacer: IThroughIdentifierReplacer;
 
 
     /**
     /**
-     * @param {IIdentifierReplacer} identifierReplacer
+     * @param {IThroughIdentifierReplacer} throughIdentifierReplacer
+     * @param {IScopeIdentifiersTraverser} scopeIdentifiersTraverser
      * @param {IRandomGenerator} randomGenerator
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      * @param {IOptions} options
-     * @param {IScopeIdentifiersTraverser} scopeIdentifiersTraverser
      */
      */
     public constructor (
     public constructor (
-        @inject(ServiceIdentifiers.IIdentifierReplacer) identifierReplacer: IIdentifierReplacer,
+        @inject(ServiceIdentifiers.IThroughIdentifierReplacer) throughIdentifierReplacer: IThroughIdentifierReplacer,
+        @inject(ServiceIdentifiers.IScopeIdentifiersTraverser) scopeIdentifiersTraverser: IScopeIdentifiersTraverser,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
-        @inject(ServiceIdentifiers.IOptions) options: IOptions,
-        @inject(ServiceIdentifiers.IScopeIdentifiersTraverser) scopeIdentifiersTraverser: IScopeIdentifiersTraverser
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
     ) {
         super(randomGenerator, options);
         super(randomGenerator, options);
 
 
-        this.identifierReplacer = identifierReplacer;
+        this.throughIdentifierReplacer = throughIdentifierReplacer;
         this.scopeIdentifiersTraverser = scopeIdentifiersTraverser;
         this.scopeIdentifiersTraverser = scopeIdentifiersTraverser;
     }
     }
 
 
@@ -86,7 +86,10 @@ export class ScopeThroughIdentifiersTransformer extends AbstractNodeTransformer
                     variableLexicalScopeNode
                     variableLexicalScopeNode
                 } = data;
                 } = data;
 
 
-                this.transformScopeThroughIdentifiers(reference, variableLexicalScopeNode);
+                this.transformScopeThroughIdentifiers(
+                    reference,
+                    variableLexicalScopeNode
+                );
             }
             }
         );
         );
 
 
@@ -97,43 +100,23 @@ export class ScopeThroughIdentifiersTransformer extends AbstractNodeTransformer
      * @param {Reference} reference
      * @param {Reference} reference
      * @param {TNodeWithLexicalScope} lexicalScopeNode
      * @param {TNodeWithLexicalScope} lexicalScopeNode
      */
      */
-    private transformScopeThroughIdentifiers (
+    protected transformScopeThroughIdentifiers (
         reference: eslintScope.Reference,
         reference: eslintScope.Reference,
-        lexicalScopeNode: TNodeWithLexicalScope,
+        lexicalScopeNode: TNodeWithLexicalScope
     ): void {
     ): void {
         if (reference.resolved) {
         if (reference.resolved) {
             return;
             return;
         }
         }
 
 
-        const identifier: ESTree.Identifier = reference.identifier;
-
-        this.storeIdentifierName(identifier, lexicalScopeNode);
-        this.replaceIdentifierName(identifier, lexicalScopeNode, reference);
+        this.replaceIdentifierName(reference);
     }
     }
 
 
     /**
     /**
-     * @param {Identifier} identifierNode
-     * @param {TNodeWithLexicalScope} lexicalScopeNode
-     */
-    private storeIdentifierName (
-        identifierNode: ESTree.Identifier,
-        lexicalScopeNode: TNodeWithLexicalScope
-    ): void {
-        this.identifierReplacer.storeLocalName(identifierNode, lexicalScopeNode);
-    }
-
-    /**
-     * @param {Identifier} identifierNode
-     * @param {TNodeWithLexicalScope} lexicalScopeNode
      * @param {Variable} reference
      * @param {Variable} reference
      */
      */
-    private replaceIdentifierName (
-        identifierNode: ESTree.Identifier,
-        lexicalScopeNode: TNodeWithLexicalScope,
-        reference: eslintScope.Reference
-    ): void {
-        const newIdentifier: ESTree.Identifier = this.identifierReplacer
-            .replace(identifierNode, lexicalScopeNode);
+    protected replaceIdentifierName (reference: eslintScope.Reference): void {
+        const identifier: ESTree.Identifier = reference.identifier;
+        const newIdentifier: ESTree.Identifier = this.throughIdentifierReplacer.replace(identifier);
 
 
         // rename of identifier
         // rename of identifier
         reference.identifier.name = newIdentifier.name;
         reference.identifier.name = newIdentifier.name;

+ 17 - 2
src/node-transformers/rename-identifiers-transformers/replacer/IdentifierReplacer.ts

@@ -6,6 +6,7 @@ import * as ESTree from 'estree';
 import { TIdentifierNamesGeneratorFactory } from '../../../types/container/generators/TIdentifierNamesGeneratorFactory';
 import { TIdentifierNamesGeneratorFactory } from '../../../types/container/generators/TIdentifierNamesGeneratorFactory';
 import { TNodeWithLexicalScope } from '../../../types/node/TNodeWithLexicalScope';
 import { TNodeWithLexicalScope } from '../../../types/node/TNodeWithLexicalScope';
 
 
+import { IGlobalIdentifierNamesCacheStorage } from '../../../interfaces/storages/identifier-names-cache/IGlobalIdentifierNamesCacheStorage';
 import { IIdentifierNamesGenerator } from '../../../interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator';
 import { IIdentifierNamesGenerator } from '../../../interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator';
 import { IIdentifierReplacer } from '../../../interfaces/node-transformers/rename-identifiers-transformers/replacer/IIdentifierReplacer';
 import { IIdentifierReplacer } from '../../../interfaces/node-transformers/rename-identifiers-transformers/replacer/IIdentifierReplacer';
 import { IOptions } from '../../../interfaces/options/IOptions';
 import { IOptions } from '../../../interfaces/options/IOptions';
@@ -14,6 +15,11 @@ import { NodeFactory } from '../../../node/NodeFactory';
 
 
 @injectable()
 @injectable()
 export class IdentifierReplacer implements IIdentifierReplacer {
 export class IdentifierReplacer implements IIdentifierReplacer {
+    /**
+     * @type {IGlobalIdentifierNamesCacheStorage}
+     */
+    private readonly identifierNamesCacheStorage: IGlobalIdentifierNamesCacheStorage;
+
     /**
     /**
      * @type {IIdentifierNamesGenerator}
      * @type {IIdentifierNamesGenerator}
      */
      */
@@ -31,19 +37,23 @@ export class IdentifierReplacer implements IIdentifierReplacer {
 
 
     /**
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {IGlobalIdentifierNamesCacheStorage} identifierNamesCacheStorage
      * @param {IOptions} options
      * @param {IOptions} options
      */
      */
     public constructor (
     public constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.IGlobalIdentifierNamesCacheStorage)
+            identifierNamesCacheStorage: IGlobalIdentifierNamesCacheStorage,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
     ) {
         this.options = options;
         this.options = options;
+        this.identifierNamesCacheStorage = identifierNamesCacheStorage;
         this.identifierNamesGenerator = identifierNamesGeneratorFactory(options);
         this.identifierNamesGenerator = identifierNamesGeneratorFactory(options);
     }
     }
 
 
     /**
     /**
-     * Store `nodeName` of global identifiers as key in map with random name as value.
+     * Store identifier node `name` of global identifiers as key in map with random name as value.
      * Reserved name will be ignored.
      * Reserved name will be ignored.
      *
      *
      * @param {Node} identifierNode
      * @param {Node} identifierNode
@@ -65,10 +75,15 @@ export class IdentifierReplacer implements IIdentifierReplacer {
         const namesMap: Map<string, string> = <Map<string, string>>this.blockScopesMap.get(lexicalScopeNode);
         const namesMap: Map<string, string> = <Map<string, string>>this.blockScopesMap.get(lexicalScopeNode);
 
 
         namesMap.set(identifierName, newIdentifierName);
         namesMap.set(identifierName, newIdentifierName);
+
+        // Have to write all global identifier names to the identifier names cache storage
+        if (this.options.identifierNamesCache) {
+            this.identifierNamesCacheStorage.set(identifierName, newIdentifierName);
+        }
     }
     }
 
 
     /**
     /**
-     * Store `nodeName` of local identifier as key in map with random name as value.
+     * Store identifier node `name` of local identifier as key in map with random name as value.
      * Reserved name will be ignored.
      * Reserved name will be ignored.
      *
      *
      * @param {Identifier} identifierNode
      * @param {Identifier} identifierNode

+ 64 - 0
src/node-transformers/rename-identifiers-transformers/through-replacer/ThroughIdentifierReplacer.ts

@@ -0,0 +1,64 @@
+import { inject, injectable, } from 'inversify';
+import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
+
+import * as ESTree from 'estree';
+
+import { IGlobalIdentifierNamesCacheStorage } from '../../../interfaces/storages/identifier-names-cache/IGlobalIdentifierNamesCacheStorage';
+import { IOptions } from '../../../interfaces/options/IOptions';
+import { IThroughIdentifierReplacer } from '../../../interfaces/node-transformers/rename-identifiers-transformers/replacer/IThroughIdentifierReplacer';
+
+import { NodeFactory } from '../../../node/NodeFactory';
+
+@injectable()
+export class ThroughIdentifierReplacer implements IThroughIdentifierReplacer {
+    /**
+     * @type {IGlobalIdentifierNamesCacheStorage}
+     */
+    private readonly identifierNamesCacheStorage: IGlobalIdentifierNamesCacheStorage;
+
+    /**
+     * @type {IOptions}
+     */
+    private readonly options: IOptions;
+
+    /**
+     * @param {IGlobalIdentifierNamesCacheStorage} identifierNamesCacheStorage
+     * @param {IOptions} options
+     */
+    public constructor (
+        @inject(ServiceIdentifiers.IGlobalIdentifierNamesCacheStorage)
+            identifierNamesCacheStorage: IGlobalIdentifierNamesCacheStorage,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        this.identifierNamesCacheStorage = identifierNamesCacheStorage;
+        this.options = options;
+    }
+
+    /**
+     * @param {Identifier} identifierNode
+     * @returns {Identifier}
+     */
+    public replace (identifierNode: ESTree.Identifier): ESTree.Identifier {
+        const identifierName: string = identifierNode.name;
+        const newIdentifierName: string = this.options.identifierNamesCache && !this.isReservedName(identifierName)
+            ? this.identifierNamesCacheStorage.get(identifierName) ?? identifierName
+            : identifierName;
+
+        return NodeFactory.identifierNode(newIdentifierName);
+    }
+
+    /**
+     * @param {string} name
+     * @returns {boolean}
+     */
+    private isReservedName (name: string): boolean {
+        if (!this.options.reservedNames.length) {
+            return false;
+        }
+
+        return this.options.reservedNames
+            .some((reservedName: string) => {
+                return new RegExp(reservedName, 'g').exec(name) !== null;
+            });
+    }
+}

+ 22 - 2
src/node-transformers/rename-properties-transformers/replacer/RenamePropertiesReplacer.ts

@@ -7,8 +7,9 @@ import * as ESTree from 'estree';
 import { TIdentifierNamesGeneratorFactory } from '../../../types/container/generators/TIdentifierNamesGeneratorFactory';
 import { TIdentifierNamesGeneratorFactory } from '../../../types/container/generators/TIdentifierNamesGeneratorFactory';
 
 
 import { IIdentifierNamesGenerator } from '../../../interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator';
 import { IIdentifierNamesGenerator } from '../../../interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator';
-import { IRenamePropertiesReplacer } from '../../../interfaces/node-transformers/rename-properties-transformers/replacer/IRenamePropertiesReplacer';
 import { IOptions } from '../../../interfaces/options/IOptions';
 import { IOptions } from '../../../interfaces/options/IOptions';
+import { IPropertyIdentifierNamesCacheStorage } from '../../../interfaces/storages/identifier-names-cache/IPropertyIdentifierNamesCacheStorage';
+import { IRenamePropertiesReplacer } from '../../../interfaces/node-transformers/rename-properties-transformers/replacer/IRenamePropertiesReplacer';
 
 
 // eslint-disable-next-line import/no-internal-modules
 // eslint-disable-next-line import/no-internal-modules
 import ReservedDomProperties from './ReservedDomProperties.json';
 import ReservedDomProperties from './ReservedDomProperties.json';
@@ -38,6 +39,11 @@ export class RenamePropertiesReplacer implements IRenamePropertiesReplacer {
      */
      */
     private readonly excludedPropertyNames: Set<string> = new Set();
     private readonly excludedPropertyNames: Set<string> = new Set();
 
 
+    /**
+     * @type {IPropertyIdentifierNamesCacheStorage}
+     */
+    private readonly propertyIdentifierNamesCacheStorage: IPropertyIdentifierNamesCacheStorage;
+
     /**
     /**
      * @type {Map<string, string>}
      * @type {Map<string, string>}
      * @private
      * @private
@@ -51,14 +57,18 @@ export class RenamePropertiesReplacer implements IRenamePropertiesReplacer {
 
 
     /**
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {IPropertyIdentifierNamesCacheStorage} propertyIdentifierNamesCacheStorage
      * @param {IOptions} options
      * @param {IOptions} options
      */
      */
     public constructor (
     public constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.IPropertyIdentifierNamesCacheStorage)
+            propertyIdentifierNamesCacheStorage: IPropertyIdentifierNamesCacheStorage,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
     ) {
         this.identifierNamesGenerator = identifierNamesGeneratorFactory(options);
         this.identifierNamesGenerator = identifierNamesGeneratorFactory(options);
+        this.propertyIdentifierNamesCacheStorage = propertyIdentifierNamesCacheStorage;
         this.options = options;
         this.options = options;
     }
     }
 
 
@@ -99,7 +109,13 @@ export class RenamePropertiesReplacer implements IRenamePropertiesReplacer {
             return propertyName;
             return propertyName;
         }
         }
 
 
-        let renamedPropertyName: string | null = this.propertyNamesMap.get(propertyName) ?? null;
+        let renamedPropertyName: string | null = this.options.identifierNamesCache
+            ? this.propertyIdentifierNamesCacheStorage.get(propertyName) ?? null
+            : null;
+
+        renamedPropertyName = renamedPropertyName
+            ?? this.propertyNamesMap.get(propertyName)
+            ?? null;
 
 
         if (renamedPropertyName !== null) {
         if (renamedPropertyName !== null) {
             return renamedPropertyName;
             return renamedPropertyName;
@@ -108,6 +124,10 @@ export class RenamePropertiesReplacer implements IRenamePropertiesReplacer {
         renamedPropertyName = this.identifierNamesGenerator.generateNext();
         renamedPropertyName = this.identifierNamesGenerator.generateNext();
         this.propertyNamesMap.set(propertyName, renamedPropertyName);
         this.propertyNamesMap.set(propertyName, renamedPropertyName);
 
 
+        if (this.options.identifierNamesCache) {
+            this.propertyIdentifierNamesCacheStorage.set(propertyName, renamedPropertyName);
+        }
+
         return renamedPropertyName;
         return renamedPropertyName;
     }
     }
 
 

+ 1 - 0
src/node-transformers/rename-properties-transformers/replacer/ReservedDomProperties.json

@@ -1029,6 +1029,7 @@
     "EQUALPOWER",
     "EQUALPOWER",
     "ERROR",
     "ERROR",
     "EXPONENTIAL_DISTANCE",
     "EXPONENTIAL_DISTANCE",
+    "exports",
     "Element",
     "Element",
     "ElementInternals",
     "ElementInternals",
     "ElementQuery",
     "ElementQuery",

+ 2 - 2
src/node-transformers/simplifying-transformers/AbstractStatementSimplifyTransformer.ts

@@ -22,7 +22,7 @@ export abstract class AbstractStatementSimplifyTransformer extends AbstractNodeT
     /**
     /**
      * @type {NodeTransformer[]}
      * @type {NodeTransformer[]}
      */
      */
-    public readonly runAfter: NodeTransformer[] = [
+    public override readonly runAfter: NodeTransformer[] = [
         NodeTransformer.ExpressionStatementsMergeTransformer,
         NodeTransformer.ExpressionStatementsMergeTransformer,
         NodeTransformer.VariableDeclarationsMergeTransformer
         NodeTransformer.VariableDeclarationsMergeTransformer
     ];
     ];
@@ -189,7 +189,7 @@ export abstract class AbstractStatementSimplifyTransformer extends AbstractNodeT
      * @param {ESTree.Node} parentNode
      * @param {ESTree.Node} parentNode
      * @returns {ESTree.Node}
      * @returns {ESTree.Node}
      */
      */
-    public abstract transformNode (
+    public abstract override transformNode (
         statementNode: ESTree.Statement,
         statementNode: ESTree.Statement,
         parentNode: ESTree.Node
         parentNode: ESTree.Node
     ): ESTree.Node;
     ): ESTree.Node;

+ 1 - 1
src/node-transformers/simplifying-transformers/BlockStatementSimplifyTransformer.ts

@@ -24,7 +24,7 @@ export class BlockStatementSimplifyTransformer extends AbstractStatementSimplify
     /**
     /**
      * @type {NodeTransformer[]}
      * @type {NodeTransformer[]}
      */
      */
-    public readonly runAfter: NodeTransformer[] = [
+    public override readonly runAfter: NodeTransformer[] = [
         NodeTransformer.VariableDeclarationsMergeTransformer
         NodeTransformer.VariableDeclarationsMergeTransformer
     ];
     ];
 
 

+ 1 - 1
src/node-transformers/simplifying-transformers/IfStatementSimplifyTransformer.ts

@@ -262,7 +262,7 @@ export class IfStatementSimplifyTransformer extends AbstractStatementSimplifyTra
      * @param {IStatementSimplifyData} statementSimplifyData
      * @param {IStatementSimplifyData} statementSimplifyData
      * @returns {ESTree.Statement}
      * @returns {ESTree.Statement}
      */
      */
-    protected getPartialStatement (statementSimplifyData: IStatementSimplifyData): ESTree.Statement {
+    protected override getPartialStatement (statementSimplifyData: IStatementSimplifyData): ESTree.Statement {
         const partialStatement: ESTree.Statement = super.getPartialStatement(statementSimplifyData);
         const partialStatement: ESTree.Statement = super.getPartialStatement(statementSimplifyData);
 
 
         if (!NodeGuards.isBlockStatementNode(partialStatement)) {
         if (!NodeGuards.isBlockStatementNode(partialStatement)) {

+ 1 - 1
src/node-transformers/string-array-transformers/StringArrayScopeCallsWrapperTransformer.ts

@@ -38,7 +38,7 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
     /**
     /**
      * @type {NodeTransformer[]}
      * @type {NodeTransformer[]}
      */
      */
-    public readonly runAfter: NodeTransformer[] = [
+    public override readonly runAfter: NodeTransformer[] = [
         NodeTransformer.StringArrayRotateFunctionTransformer
         NodeTransformer.StringArrayRotateFunctionTransformer
     ];
     ];
 
 

+ 1 - 1
src/node-transformers/string-array-transformers/StringArrayTransformer.ts

@@ -53,7 +53,7 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
     /**
     /**
      * @type {NodeTransformer[]}
      * @type {NodeTransformer[]}
      */
      */
-    public readonly runAfter: NodeTransformer[] = [
+    public override readonly runAfter: NodeTransformer[] = [
         NodeTransformer.StringArrayRotateFunctionTransformer
         NodeTransformer.StringArrayRotateFunctionTransformer
     ];
     ];
 
 

+ 2 - 0
src/node/ScopeIdentifiersTraverser.ts

@@ -142,6 +142,7 @@ export class ScopeIdentifiersTraverser implements IScopeIdentifiersTraverser {
         const variableLexicalScopeNode: TNodeWithLexicalScope | null = NodeGuards.isNodeWithBlockLexicalScope(variableScope.block)
         const variableLexicalScopeNode: TNodeWithLexicalScope | null = NodeGuards.isNodeWithBlockLexicalScope(variableScope.block)
             ? variableScope.block
             ? variableScope.block
             : null;
             : null;
+        const isGlobalDeclaration: boolean = ScopeIdentifiersTraverser.globalScopeNames.includes(variableScope.type);
 
 
         if (!variableLexicalScopeNode) {
         if (!variableLexicalScopeNode) {
             return;
             return;
@@ -149,6 +150,7 @@ export class ScopeIdentifiersTraverser implements IScopeIdentifiersTraverser {
 
 
         for (const reference of currentScope.through) {
         for (const reference of currentScope.through) {
             callback({
             callback({
+                isGlobalDeclaration,
                 reference,
                 reference,
                 variableLexicalScopeNode
                 variableLexicalScopeNode
             });
             });

+ 8 - 0
src/options/Options.ts

@@ -18,6 +18,7 @@ import {
     ValidatorOptions
     ValidatorOptions
 } from 'class-validator';
 } from 'class-validator';
 
 
+import { TIdentifierNamesCache } from '../types/TIdentifierNamesCache';
 import { TInputOptions } from '../types/options/TInputOptions';
 import { TInputOptions } from '../types/options/TInputOptions';
 import { TOptionsPreset } from '../types/options/TOptionsPreset';
 import { TOptionsPreset } from '../types/options/TOptionsPreset';
 import { TRenamePropertiesMode } from '../types/options/TRenamePropertiesMode';
 import { TRenamePropertiesMode } from '../types/options/TRenamePropertiesMode';
@@ -45,6 +46,7 @@ import { HIGH_OBFUSCATION_PRESET } from './presets/HighObfuscation';
 
 
 import { ValidationErrorsFormatter } from './ValidationErrorsFormatter';
 import { ValidationErrorsFormatter } from './ValidationErrorsFormatter';
 import { IsAllowedForObfuscationTargets } from './validators/IsAllowedForObfuscationTargets';
 import { IsAllowedForObfuscationTargets } from './validators/IsAllowedForObfuscationTargets';
+import { IsIdentifierNamesCache } from './validators/IsIdentifierNamesCache';
 
 
 @injectable()
 @injectable()
 export class Options implements IOptions {
 export class Options implements IOptions {
@@ -142,6 +144,12 @@ export class Options implements IOptions {
     })
     })
     public readonly forceTransformStrings!: string[];
     public readonly forceTransformStrings!: string[];
 
 
+    /**
+     * @type {TIdentifierNamesCache}
+     */
+    @IsIdentifierNamesCache()
+    public readonly identifierNamesCache!: TIdentifierNamesCache;
+
     /**
     /**
      * @type {IdentifierNamesGenerator}
      * @type {IdentifierNamesGenerator}
      */
      */

+ 2 - 0
src/options/OptionsNormalizer.ts

@@ -9,6 +9,7 @@ import { ControlFlowFlatteningThresholdRule } from './normalizer-rules/ControlFl
 import { DeadCodeInjectionRule } from './normalizer-rules/DeadCodeInjectionRule';
 import { DeadCodeInjectionRule } from './normalizer-rules/DeadCodeInjectionRule';
 import { DeadCodeInjectionThresholdRule } from './normalizer-rules/DeadCodeInjectionThresholdRule';
 import { DeadCodeInjectionThresholdRule } from './normalizer-rules/DeadCodeInjectionThresholdRule';
 import { DomainLockRule } from './normalizer-rules/DomainLockRule';
 import { DomainLockRule } from './normalizer-rules/DomainLockRule';
+import { IdentifierNamesCacheRule } from './normalizer-rules/IdentifierNamesCacheRule';
 import { InputFileNameRule } from './normalizer-rules/InputFileNameRule';
 import { InputFileNameRule } from './normalizer-rules/InputFileNameRule';
 import { SeedRule } from './normalizer-rules/SeedRule';
 import { SeedRule } from './normalizer-rules/SeedRule';
 import { SelfDefendingRule } from './normalizer-rules/SelfDefendingRule';
 import { SelfDefendingRule } from './normalizer-rules/SelfDefendingRule';
@@ -29,6 +30,7 @@ export class OptionsNormalizer implements IOptionsNormalizer {
         DeadCodeInjectionRule,
         DeadCodeInjectionRule,
         DeadCodeInjectionThresholdRule,
         DeadCodeInjectionThresholdRule,
         DomainLockRule,
         DomainLockRule,
+        IdentifierNamesCacheRule,
         InputFileNameRule,
         InputFileNameRule,
         SeedRule,
         SeedRule,
         SelfDefendingRule,
         SelfDefendingRule,

+ 32 - 0
src/options/normalizer-rules/IdentifierNamesCacheRule.ts

@@ -0,0 +1,32 @@
+import { TOptionsNormalizerRule } from '../../types/options/TOptionsNormalizerRule';
+
+import { IOptions } from '../../interfaces/options/IOptions';
+
+/**
+ * @param {IOptions} options
+ * @returns {IOptions}
+ */
+export const IdentifierNamesCacheRule: TOptionsNormalizerRule = (options: IOptions): IOptions => {
+    let identifierNamesCache = options.identifierNamesCache;
+
+    if (identifierNamesCache && !identifierNamesCache.globalIdentifiers) {
+        identifierNamesCache = {
+            ...identifierNamesCache,
+            globalIdentifiers: {}
+        };
+    }
+
+    if (identifierNamesCache && !identifierNamesCache.propertyIdentifiers) {
+        identifierNamesCache = {
+            ...identifierNamesCache,
+            propertyIdentifiers: {}
+        };
+    }
+
+    options = {
+        ...options,
+        identifierNamesCache
+    };
+
+    return options;
+};

+ 1 - 0
src/options/presets/Default.ts

@@ -22,6 +22,7 @@ export const DEFAULT_PRESET: TInputOptions = Object.freeze({
     domainLock: [],
     domainLock: [],
     exclude: [],
     exclude: [],
     forceTransformStrings: [],
     forceTransformStrings: [],
+    identifierNamesCache: null,
     identifierNamesGenerator: IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator,
     identifierNamesGenerator: IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator,
     identifiersPrefix: '',
     identifiersPrefix: '',
     identifiersDictionary: [],
     identifiersDictionary: [],

+ 87 - 0
src/options/validators/IsIdentifierNamesCache.ts

@@ -0,0 +1,87 @@
+import equal from 'fast-deep-equal';
+import { registerDecorator, ValidationArguments, ValidationOptions } from 'class-validator';
+
+import { TIdentifierNamesCache } from '../../types/TIdentifierNamesCache';
+import { TIdentifierNamesCacheDictionary } from '../../types/TIdentifierNamesCacheDictionary';
+
+import { IOptions } from '../../interfaces/options/IOptions';
+
+import { DEFAULT_PRESET } from '../presets/Default';
+
+/**
+ * @param value
+ * @returns {boolean}
+ */
+const validateDictionary = (value: unknown | TIdentifierNamesCacheDictionary): boolean => {
+    if (value === undefined) {
+        return true;
+    }
+
+    if (typeof value !== 'object' || value === null) {
+        return false;
+    }
+
+    const objectValues: unknown[] = Object.values(value);
+
+    if (!objectValues.length) {
+        return true;
+    }
+
+    for (const objectValue of objectValues) {
+        if (typeof objectValue !== 'string') {
+            return false;
+        }
+    }
+
+    return true;
+};
+
+/**
+ * @param {ValidationOptions} validationOptions
+ * @returns {(options: IOptions, propertyName: keyof IOptions) => void}
+ */
+export function IsIdentifierNamesCache (
+    validationOptions?: ValidationOptions
+): (options: IOptions, propertyName: keyof IOptions) => void {
+    return (optionsObject: IOptions, propertyName: keyof IOptions): void => {
+        registerDecorator({
+            propertyName,
+            constraints: [],
+            name: 'IsIdentifierNamesCache',
+            options: validationOptions,
+            target: optionsObject.constructor,
+            validator: {
+                /**
+                 * @param value
+                 * @param {ValidationArguments} validationArguments
+                 * @returns {boolean}
+                 */
+                validate (value: unknown, validationArguments: ValidationArguments): boolean {
+                    const defaultValue: IOptions[keyof IOptions] | undefined = DEFAULT_PRESET[propertyName];
+                    const isDefaultValue: boolean = equal(value, defaultValue);
+
+                    if (isDefaultValue || value === null) {
+                        return true;
+                    }
+
+                    if (typeof value !== 'object') {
+                        return false;
+                    }
+
+                    if (!validateDictionary((<TIdentifierNamesCache>value)?.globalIdentifiers)) {
+                        return false;
+                    }
+
+                    return validateDictionary((<TIdentifierNamesCache>value)?.propertyIdentifiers);
+                },
+
+                /**
+                 * @returns {string}
+                 */
+                defaultMessage (): string {
+                    return 'Passed value must be an identifier names cache object or `null` value';
+                }
+            }
+        });
+    };
+}

+ 43 - 3
src/source-code/ObfuscatedCode.ts → src/source-code/ObfuscationResult.ts

@@ -1,15 +1,19 @@
 import { inject, injectable } from 'inversify';
 import { inject, injectable } from 'inversify';
 import { ServiceIdentifiers } from '../container/ServiceIdentifiers';
 import { ServiceIdentifiers } from '../container/ServiceIdentifiers';
 
 
+import { TIdentifierNamesCache } from '../types/TIdentifierNamesCache';
+
 import { ICryptUtils } from '../interfaces/utils/ICryptUtils';
 import { ICryptUtils } from '../interfaces/utils/ICryptUtils';
-import { IObfuscatedCode } from '../interfaces/source-code/IObfuscatedCode';
+import { IGlobalIdentifierNamesCacheStorage } from '../interfaces/storages/identifier-names-cache/IGlobalIdentifierNamesCacheStorage';
+import { IObfuscationResult } from '../interfaces/source-code/IObfuscationResult';
+import { IPropertyIdentifierNamesCacheStorage } from '../interfaces/storages/identifier-names-cache/IPropertyIdentifierNamesCacheStorage';
+import { IOptions } from '../interfaces/options/IOptions';
 
 
 import { initializable } from '../decorators/Initializable';
 import { initializable } from '../decorators/Initializable';
 import { SourceMapMode } from '../enums/source-map/SourceMapMode';
 import { SourceMapMode } from '../enums/source-map/SourceMapMode';
-import { IOptions } from '../interfaces/options/IOptions';
 
 
 @injectable()
 @injectable()
-export class ObfuscatedCode implements IObfuscatedCode {
+export class ObfuscationResult implements IObfuscationResult {
     /**
     /**
      * @type {string}
      * @type {string}
      */
      */
@@ -27,16 +31,38 @@ export class ObfuscatedCode implements IObfuscatedCode {
      */
      */
     private readonly cryptUtils: ICryptUtils;
     private readonly cryptUtils: ICryptUtils;
 
 
+    /**
+     * @type {IGlobalIdentifierNamesCacheStorage}
+     */
+    private readonly globalIdentifierNamesCacheStorage: IGlobalIdentifierNamesCacheStorage;
+
+    /**
+     * @type {IPropertyIdentifierNamesCacheStorage}
+     */
+    private readonly propertyIdentifierNamesCacheStorage: IPropertyIdentifierNamesCacheStorage;
+
     /**
     /**
      * @type {IOptions}
      * @type {IOptions}
      */
      */
     private readonly options: IOptions;
     private readonly options: IOptions;
 
 
+    /**
+     * @param {ICryptUtils} cryptUtils
+     * @param {IGlobalIdentifierNamesCacheStorage} globalIdentifierNamesCacheStorage
+     * @param {IPropertyIdentifierNamesCacheStorage} propertyIdentifierNamesCacheStorage
+     * @param {IOptions} options
+     */
     public constructor (
     public constructor (
         @inject(ServiceIdentifiers.ICryptUtils) cryptUtils: ICryptUtils,
         @inject(ServiceIdentifiers.ICryptUtils) cryptUtils: ICryptUtils,
+        @inject(ServiceIdentifiers.IGlobalIdentifierNamesCacheStorage)
+            globalIdentifierNamesCacheStorage: IGlobalIdentifierNamesCacheStorage,
+        @inject(ServiceIdentifiers.IPropertyIdentifierNamesCacheStorage)
+            propertyIdentifierNamesCacheStorage: IPropertyIdentifierNamesCacheStorage,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
     ) {
         this.cryptUtils = cryptUtils;
         this.cryptUtils = cryptUtils;
+        this.globalIdentifierNamesCacheStorage = globalIdentifierNamesCacheStorage;
+        this.propertyIdentifierNamesCacheStorage = propertyIdentifierNamesCacheStorage;
         this.options = options;
         this.options = options;
     }
     }
 
 
@@ -49,6 +75,20 @@ export class ObfuscatedCode implements IObfuscatedCode {
         this.sourceMap = sourceMap;
         this.sourceMap = sourceMap;
     }
     }
 
 
+    /**
+     * @returns {string}
+     */
+    public getIdentifierNamesCache (): TIdentifierNamesCache {
+        if (!this.options.identifierNamesCache) {
+            return null;
+        }
+
+        return {
+            globalIdentifiers: this.globalIdentifierNamesCacheStorage.getStorageAsDictionary(),
+            propertyIdentifiers: this.propertyIdentifierNamesCacheStorage.getStorageAsDictionary()
+        };
+    }
+
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */

+ 8 - 0
src/storages/MapStorage.ts

@@ -6,6 +6,7 @@ import { IOptions } from '../interfaces/options/IOptions';
 import { IRandomGenerator } from '../interfaces/utils/IRandomGenerator';
 import { IRandomGenerator } from '../interfaces/utils/IRandomGenerator';
 
 
 import { initializable } from '../decorators/Initializable';
 import { initializable } from '../decorators/Initializable';
+import { TDictionary } from '../types/TDictionary';
 
 
 @injectable()
 @injectable()
 export abstract class MapStorage <K, V> implements IMapStorage <K, V> {
 export abstract class MapStorage <K, V> implements IMapStorage <K, V> {
@@ -99,6 +100,13 @@ export abstract class MapStorage <K, V> implements IMapStorage <K, V> {
         return this.storage;
         return this.storage;
     }
     }
 
 
+    /**
+     * @returns {TDictionary<V>}
+     */
+    public getStorageAsDictionary (): TDictionary<V> {
+        return Object.fromEntries(this.storage);
+    }
+
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */

+ 1 - 1
src/storages/custom-code-helpers/CustomCodeHelperGroupStorage.ts

@@ -45,7 +45,7 @@ export class CustomCodeHelperGroupStorage extends MapStorage <string, ICustomCod
     }
     }
 
 
     @postConstruct()
     @postConstruct()
-    public initialize (): void {
+    public override initialize (): void {
         super.initialize();
         super.initialize();
 
 
         CustomCodeHelperGroupStorage.customCodeHelperGroupsList.forEach((customCodeHelperGroupName: CustomCodeHelperGroup) => {
         CustomCodeHelperGroupStorage.customCodeHelperGroupsList.forEach((customCodeHelperGroupName: CustomCodeHelperGroup) => {

+ 29 - 0
src/storages/identifier-names-cache/GlobalIdentifierNamesCacheStorage.ts

@@ -0,0 +1,29 @@
+import { inject, injectable, postConstruct } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
+import { IGlobalIdentifierNamesCacheStorage } from '../../interfaces/storages/identifier-names-cache/IGlobalIdentifierNamesCacheStorage';
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+
+import { MapStorage } from '../MapStorage';
+
+@injectable()
+export class GlobalIdentifierNamesCacheStorage extends MapStorage <string, string> implements IGlobalIdentifierNamesCacheStorage {
+   /**
+     * @param {IRandomGenerator} randomGenerator
+     * @param {IOptions} options
+     */
+    public constructor (
+        @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        super(randomGenerator, options);
+    }
+
+    @postConstruct()
+    public override initialize (): void {
+       super.initialize();
+
+        this.storage = new Map(Object.entries(this.options.identifierNamesCache?.globalIdentifiers ?? {}));
+    }
+}

+ 29 - 0
src/storages/identifier-names-cache/PropertyIdentifierNamesCacheStorage.ts

@@ -0,0 +1,29 @@
+import { inject, injectable, postConstruct } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IPropertyIdentifierNamesCacheStorage } from '../../interfaces/storages/identifier-names-cache/IPropertyIdentifierNamesCacheStorage';
+import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+
+import { MapStorage } from '../MapStorage';
+
+@injectable()
+export class PropertyIdentifierNamesCacheStorage extends MapStorage <string, string> implements IPropertyIdentifierNamesCacheStorage {
+   /**
+     * @param {IRandomGenerator} randomGenerator
+     * @param {IOptions} options
+     */
+    public constructor (
+        @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        super(randomGenerator, options);
+    }
+
+    @postConstruct()
+    public override initialize (): void {
+       super.initialize();
+
+        this.storage = new Map(Object.entries(this.options.identifierNamesCache?.propertyIdentifiers ?? {}));
+    }
+}

+ 3 - 3
src/storages/string-array-transformers/StringArrayStorage.ts

@@ -130,7 +130,7 @@ export class StringArrayStorage extends MapStorage <`${string}-${TStringArrayEnc
     }
     }
 
 
     @postConstruct()
     @postConstruct()
-    public initialize (): void {
+    public override initialize (): void {
         super.initialize();
         super.initialize();
 
 
         this.indexShiftAmount = this.options.stringArrayIndexShift
         this.indexShiftAmount = this.options.stringArrayIndexShift
@@ -150,7 +150,7 @@ export class StringArrayStorage extends MapStorage <`${string}-${TStringArrayEnc
     /**
     /**
      * @param {string} value
      * @param {string} value
      */
      */
-    public get (value: string): IStringArrayStorageItemData {
+    public override get (value: string): IStringArrayStorageItemData {
         return this.getOrSetIfDoesNotExist(value);
         return this.getOrSetIfDoesNotExist(value);
     }
     }
 
 
@@ -178,7 +178,7 @@ export class StringArrayStorage extends MapStorage <`${string}-${TStringArrayEnc
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    public getStorageId (): string {
+    public override getStorageId (): string {
         if (!this.stringArrayStorageName) {
         if (!this.stringArrayStorageName) {
             this.stringArrayStorageName = this.identifierNamesGenerator
             this.stringArrayStorageName = this.identifierNamesGenerator
                 .generateForGlobalScope(StringArrayStorage.stringArrayNameLength);
                 .generateForGlobalScope(StringArrayStorage.stringArrayNameLength);

+ 7 - 0
src/types/TIdentifierNamesCache.ts

@@ -0,0 +1,7 @@
+import { TIdentifierNamesCacheDictionary } from './TIdentifierNamesCacheDictionary';
+
+export type TIdentifierNamesCache = {
+    globalIdentifiers?: TIdentifierNamesCacheDictionary;
+    propertyIdentifiers?: TIdentifierNamesCacheDictionary;
+} | null;
+

+ 3 - 0
src/types/TIdentifierNamesCacheDictionary.ts

@@ -0,0 +1,3 @@
+import { TDictionary } from './TDictionary';
+
+export type TIdentifierNamesCacheDictionary = TDictionary<string>;

+ 2 - 2
src/types/TObfuscationResultsObject.ts

@@ -1,3 +1,3 @@
-import { IObfuscatedCode } from '../interfaces/source-code/IObfuscatedCode';
+import { IObfuscationResult } from '../interfaces/source-code/IObfuscationResult';
 
 
-export type TObfuscationResultsObject <TSourceCodesObject> = {[key in keyof TSourceCodesObject]: IObfuscatedCode};
+export type TObfuscationResultsObject <TSourceCodesObject> = {[key in keyof TSourceCodesObject]: IObfuscationResult};

+ 0 - 3
src/types/container/source-code/TObfuscatedCodeFactory.ts

@@ -1,3 +0,0 @@
-import { IObfuscatedCode } from '../../../interfaces/source-code/IObfuscatedCode';
-
-export type TObfuscatedCodeFactory = (obfuscatedCode: string, sourceMap: string) => IObfuscatedCode;

+ 3 - 0
src/types/container/source-code/TObfuscationResultFactory.ts

@@ -0,0 +1,3 @@
+import { IObfuscationResult } from '../../../interfaces/source-code/IObfuscationResult';
+
+export type TObfuscationResultFactory = (obfuscatedCode: string, sourceMap: string) => IObfuscationResult;

+ 2 - 2
src/utils/CryptUtilsStringArray.ts

@@ -13,7 +13,7 @@ export class CryptUtilsStringArray extends CryptUtils implements ICryptUtilsStri
     /**
     /**
      * @type {string}
      * @type {string}
      */
      */
-    protected readonly base64Alphabet: string = base64alphabetSwapped;
+    protected override readonly base64Alphabet: string = base64alphabetSwapped;
 
 
     /**
     /**
      * @param {IRandomGenerator} randomGenerator
      * @param {IRandomGenerator} randomGenerator
@@ -30,7 +30,7 @@ export class CryptUtilsStringArray extends CryptUtils implements ICryptUtilsStri
      * @param {string} string
      * @param {string} string
      * @returns {string}
      * @returns {string}
      */
      */
-    public btoa (string: string): string {
+    public override btoa (string: string): string {
         const output = super.btoa(string);
         const output = super.btoa(string);
 
 
         return output.replace(/=+$/, '');
         return output.replace(/=+$/, '');

+ 1 - 1
test/fixtures/compile-performance.js

@@ -33689,7 +33689,7 @@
         var _QueryWithRead = (function () {
         var _QueryWithRead = (function () {
             function _QueryWithRead(query, match) {
             function _QueryWithRead(query, match) {
                 this.query = query;
                 this.query = query;
-                this.read = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__facade_lang__["a" /* isPresent */])(query.meta.read) ? query.meta.read : match;
+                this.readFile = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__facade_lang__["a" /* isPresent */])(query.meta.read) ? query.meta.read : match;
             }
             }
             return _QueryWithRead;
             return _QueryWithRead;
         }());
         }());

+ 7 - 7
test/functional-tests/custom-code-helpers/domain-lock/templates/DomainLockNodeTemplate.spec.ts

@@ -8,7 +8,7 @@ import { ServiceIdentifiers } from '../../../../../src/container/ServiceIdentifi
 
 
 import { ICryptUtils } from '../../../../../src/interfaces/utils/ICryptUtils';
 import { ICryptUtils } from '../../../../../src/interfaces/utils/ICryptUtils';
 import { IInversifyContainerFacade } from '../../../../../src/interfaces/container/IInversifyContainerFacade';
 import { IInversifyContainerFacade } from '../../../../../src/interfaces/container/IInversifyContainerFacade';
-import { IObfuscatedCode } from '../../../../../src/interfaces/source-code/IObfuscatedCode';
+import { IObfuscationResult } from '../../../../../src/interfaces/source-code/IObfuscationResult';
 
 
 import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes';
 import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes';
 
 
@@ -631,7 +631,7 @@ describe('DomainLockTemplate', () => {
 
 
             beforeEach(() => {
             beforeEach(() => {
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-var.js');
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-var.js');
-                const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                     code,
                     code,
                     {
                     {
                         ...NO_ADDITIONAL_NODES_PRESET,
                         ...NO_ADDITIONAL_NODES_PRESET,
@@ -641,7 +641,7 @@ describe('DomainLockTemplate', () => {
                     }
                     }
                 );
                 );
 
 
-                obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
+                obfuscatedCode = obfuscationResult.getObfuscatedCode();
             });
             });
 
 
             it('Should return correct kind of variables for domain lock code', () => {
             it('Should return correct kind of variables for domain lock code', () => {
@@ -659,7 +659,7 @@ describe('DomainLockTemplate', () => {
 
 
             beforeEach(() => {
             beforeEach(() => {
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-const.js');
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-const.js');
-                const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                     code,
                     code,
                     {
                     {
                         ...NO_ADDITIONAL_NODES_PRESET,
                         ...NO_ADDITIONAL_NODES_PRESET,
@@ -669,7 +669,7 @@ describe('DomainLockTemplate', () => {
                     }
                     }
                 );
                 );
 
 
-                obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
+                obfuscatedCode = obfuscationResult.getObfuscatedCode();
             });
             });
 
 
             it('Should return correct kind of variables for domain lock code', () => {
             it('Should return correct kind of variables for domain lock code', () => {
@@ -687,7 +687,7 @@ describe('DomainLockTemplate', () => {
 
 
             beforeEach(() => {
             beforeEach(() => {
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-let.js');
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-let.js');
-                const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                     code,
                     code,
                     {
                     {
                         ...NO_ADDITIONAL_NODES_PRESET,
                         ...NO_ADDITIONAL_NODES_PRESET,
@@ -697,7 +697,7 @@ describe('DomainLockTemplate', () => {
                     }
                     }
                 );
                 );
 
 
-                obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
+                obfuscatedCode = obfuscationResult.getObfuscatedCode();
             });
             });
 
 
             it('Should return correct kind of variables for domain lock code', () => {
             it('Should return correct kind of variables for domain lock code', () => {

+ 7 - 7
test/functional-tests/custom-code-helpers/string-array/templates/string-array-calls-wrapper-node-template/StringArrayCallsWrapperTemplate.spec.ts

@@ -8,7 +8,7 @@ import { ServiceIdentifiers } from '../../../../../../src/container/ServiceIdent
 
 
 import { ICryptUtilsStringArray } from '../../../../../../src/interfaces/utils/ICryptUtilsStringArray';
 import { ICryptUtilsStringArray } from '../../../../../../src/interfaces/utils/ICryptUtilsStringArray';
 import { IInversifyContainerFacade } from '../../../../../../src/interfaces/container/IInversifyContainerFacade';
 import { IInversifyContainerFacade } from '../../../../../../src/interfaces/container/IInversifyContainerFacade';
-import { IObfuscatedCode } from '../../../../../../src/interfaces/source-code/IObfuscatedCode';
+import { IObfuscationResult } from '../../../../../../src/interfaces/source-code/IObfuscationResult';
 import { IRandomGenerator } from '../../../../../../src/interfaces/utils/IRandomGenerator';
 import { IRandomGenerator } from '../../../../../../src/interfaces/utils/IRandomGenerator';
 
 
 import { AtobTemplate } from '../../../../../../src/custom-code-helpers/string-array/templates/string-array-calls-wrapper/AtobTemplate';
 import { AtobTemplate } from '../../../../../../src/custom-code-helpers/string-array/templates/string-array-calls-wrapper/AtobTemplate';
@@ -277,7 +277,7 @@ describe('StringArrayCallsWrapperTemplate', () => {
 
 
             beforeEach(() => {
             beforeEach(() => {
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-var.js');
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-var.js');
-                const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                     code,
                     code,
                     {
                     {
                         ...NO_ADDITIONAL_NODES_PRESET,
                         ...NO_ADDITIONAL_NODES_PRESET,
@@ -286,7 +286,7 @@ describe('StringArrayCallsWrapperTemplate', () => {
                     }
                     }
                 );
                 );
 
 
-                obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
+                obfuscatedCode = obfuscationResult.getObfuscatedCode();
             });
             });
 
 
             it('Should return correct kind of variables for string array calls wrapper code', () => {
             it('Should return correct kind of variables for string array calls wrapper code', () => {
@@ -304,7 +304,7 @@ describe('StringArrayCallsWrapperTemplate', () => {
 
 
             beforeEach(() => {
             beforeEach(() => {
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-const.js');
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-const.js');
-                const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                     code,
                     code,
                     {
                     {
                         ...NO_ADDITIONAL_NODES_PRESET,
                         ...NO_ADDITIONAL_NODES_PRESET,
@@ -313,7 +313,7 @@ describe('StringArrayCallsWrapperTemplate', () => {
                     }
                     }
                 );
                 );
 
 
-                obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
+                obfuscatedCode = obfuscationResult.getObfuscatedCode();
             });
             });
 
 
             it('Should return correct kind of variables for string array calls wrapper code', () => {
             it('Should return correct kind of variables for string array calls wrapper code', () => {
@@ -331,7 +331,7 @@ describe('StringArrayCallsWrapperTemplate', () => {
 
 
             beforeEach(() => {
             beforeEach(() => {
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-let.js');
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-let.js');
-                const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                     code,
                     code,
                     {
                     {
                         ...NO_ADDITIONAL_NODES_PRESET,
                         ...NO_ADDITIONAL_NODES_PRESET,
@@ -340,7 +340,7 @@ describe('StringArrayCallsWrapperTemplate', () => {
                     }
                     }
                 );
                 );
 
 
-                obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
+                obfuscatedCode = obfuscationResult.getObfuscatedCode();
             });
             });
 
 
             it('Should return correct kind of variables for string array calls wrapper code', () => {
             it('Should return correct kind of variables for string array calls wrapper code', () => {

+ 11 - 11
test/functional-tests/custom-code-helpers/string-array/templates/string-array-rotate-function-template/StringArrayRotateFunctionTemplate.spec.ts

@@ -2,7 +2,7 @@ import 'reflect-metadata';
 
 
 import { assert } from 'chai';
 import { assert } from 'chai';
 
 
-import { IObfuscatedCode } from '../../../../../../src/interfaces/source-code/IObfuscatedCode';
+import { IObfuscationResult } from '../../../../../../src/interfaces/source-code/IObfuscationResult';
 
 
 import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../../src/options/presets/NoCustomNodes';
 import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../../src/options/presets/NoCustomNodes';
 
 
@@ -19,7 +19,7 @@ describe('StringArrayRotateFunctionTemplate', () => {
 
 
             beforeEach(() => {
             beforeEach(() => {
                 const code: string = readFileAsString(__dirname + '/fixtures/simple-input.js');
                 const code: string = readFileAsString(__dirname + '/fixtures/simple-input.js');
-                const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                     code,
                     code,
                     {
                     {
                         ...NO_ADDITIONAL_NODES_PRESET,
                         ...NO_ADDITIONAL_NODES_PRESET,
@@ -29,7 +29,7 @@ describe('StringArrayRotateFunctionTemplate', () => {
                     }
                     }
                 );
                 );
 
 
-                obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
+                obfuscatedCode = obfuscationResult.getObfuscatedCode();
             });
             });
 
 
             it('Should use computed member expression in `array.push` method', () => {
             it('Should use computed member expression in `array.push` method', () => {
@@ -49,7 +49,7 @@ describe('StringArrayRotateFunctionTemplate', () => {
 
 
             beforeEach(() => {
             beforeEach(() => {
                 const code: string = readFileAsString(__dirname + '/fixtures/simple-input.js');
                 const code: string = readFileAsString(__dirname + '/fixtures/simple-input.js');
-                const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                     code,
                     code,
                     {
                     {
                         ...NO_ADDITIONAL_NODES_PRESET,
                         ...NO_ADDITIONAL_NODES_PRESET,
@@ -59,7 +59,7 @@ describe('StringArrayRotateFunctionTemplate', () => {
                     }
                     }
                 );
                 );
 
 
-                obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
+                obfuscatedCode = obfuscationResult.getObfuscatedCode();
             });
             });
 
 
             it('Should use computed member expression in `array.shift` method', () => {
             it('Should use computed member expression in `array.shift` method', () => {
@@ -79,7 +79,7 @@ describe('StringArrayRotateFunctionTemplate', () => {
 
 
             beforeEach(() => {
             beforeEach(() => {
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-var.js');
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-var.js');
-                const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                     code,
                     code,
                     {
                     {
                         ...NO_ADDITIONAL_NODES_PRESET,
                         ...NO_ADDITIONAL_NODES_PRESET,
@@ -89,7 +89,7 @@ describe('StringArrayRotateFunctionTemplate', () => {
                     }
                     }
                 );
                 );
 
 
-                obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
+                obfuscatedCode = obfuscationResult.getObfuscatedCode();
             });
             });
 
 
             it('Should return correct kind of variables for string array rotate function', () => {
             it('Should return correct kind of variables for string array rotate function', () => {
@@ -107,7 +107,7 @@ describe('StringArrayRotateFunctionTemplate', () => {
 
 
             beforeEach(() => {
             beforeEach(() => {
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-const.js');
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-const.js');
-                const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                     code,
                     code,
                     {
                     {
                         ...NO_ADDITIONAL_NODES_PRESET,
                         ...NO_ADDITIONAL_NODES_PRESET,
@@ -117,7 +117,7 @@ describe('StringArrayRotateFunctionTemplate', () => {
                     }
                     }
                 );
                 );
 
 
-                obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
+                obfuscatedCode = obfuscationResult.getObfuscatedCode();
             });
             });
 
 
             it('Should return correct kind of variables for string array rotate function', () => {
             it('Should return correct kind of variables for string array rotate function', () => {
@@ -135,7 +135,7 @@ describe('StringArrayRotateFunctionTemplate', () => {
 
 
             beforeEach(() => {
             beforeEach(() => {
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-let.js');
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-let.js');
-                const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                     code,
                     code,
                     {
                     {
                         ...NO_ADDITIONAL_NODES_PRESET,
                         ...NO_ADDITIONAL_NODES_PRESET,
@@ -145,7 +145,7 @@ describe('StringArrayRotateFunctionTemplate', () => {
                     }
                     }
                 );
                 );
 
 
-                obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
+                obfuscatedCode = obfuscationResult.getObfuscatedCode();
             });
             });
 
 
             it('Should return correct kind of variables for string array rotate function', () => {
             it('Should return correct kind of variables for string array rotate function', () => {

+ 7 - 7
test/functional-tests/custom-code-helpers/string-array/templates/string-array-template/StringArrayTemplate.spec.ts

@@ -2,7 +2,7 @@ import 'reflect-metadata';
 
 
 import { assert } from 'chai';
 import { assert } from 'chai';
 
 
-import { IObfuscatedCode } from '../../../../../../src/interfaces/source-code/IObfuscatedCode';
+import { IObfuscationResult } from '../../../../../../src/interfaces/source-code/IObfuscationResult';
 
 
 import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../../src/options/presets/NoCustomNodes';
 import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../../src/options/presets/NoCustomNodes';
 
 
@@ -17,7 +17,7 @@ describe('StringArrayTemplate', () => {
 
 
             beforeEach(() => {
             beforeEach(() => {
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-var.js');
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-var.js');
-                const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                     code,
                     code,
                     {
                     {
                         ...NO_ADDITIONAL_NODES_PRESET,
                         ...NO_ADDITIONAL_NODES_PRESET,
@@ -26,7 +26,7 @@ describe('StringArrayTemplate', () => {
                     }
                     }
                 );
                 );
 
 
-                obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
+                obfuscatedCode = obfuscationResult.getObfuscatedCode();
             });
             });
 
 
             it('Should return correct kind of variables for string array', () => {
             it('Should return correct kind of variables for string array', () => {
@@ -44,7 +44,7 @@ describe('StringArrayTemplate', () => {
 
 
             beforeEach(() => {
             beforeEach(() => {
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-const.js');
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-const.js');
-                const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                     code,
                     code,
                     {
                     {
                         ...NO_ADDITIONAL_NODES_PRESET,
                         ...NO_ADDITIONAL_NODES_PRESET,
@@ -53,7 +53,7 @@ describe('StringArrayTemplate', () => {
                     }
                     }
                 );
                 );
 
 
-                obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
+                obfuscatedCode = obfuscationResult.getObfuscatedCode();
             });
             });
 
 
             it('Should return correct kind of variables for string array', () => {
             it('Should return correct kind of variables for string array', () => {
@@ -71,7 +71,7 @@ describe('StringArrayTemplate', () => {
 
 
             beforeEach(() => {
             beforeEach(() => {
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-let.js');
                 const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-let.js');
-                const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                     code,
                     code,
                     {
                     {
                         ...NO_ADDITIONAL_NODES_PRESET,
                         ...NO_ADDITIONAL_NODES_PRESET,
@@ -80,7 +80,7 @@ describe('StringArrayTemplate', () => {
                     }
                     }
                 );
                 );
 
 
-                obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
+                obfuscatedCode = obfuscationResult.getObfuscatedCode();
             });
             });
 
 
             it('Should return correct kind of variables for string array', () => {
             it('Should return correct kind of variables for string array', () => {

+ 141 - 15
test/functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec.ts

@@ -1,11 +1,12 @@
 import { assert } from 'chai';
 import { assert } from 'chai';
 
 
 import { TDictionary } from '../../../src/types/TDictionary';
 import { TDictionary } from '../../../src/types/TDictionary';
+import { TIdentifierNamesCache } from '../../../src/types/TIdentifierNamesCache';
 import { TInputOptions } from '../../../src/types/options/TInputOptions';
 import { TInputOptions } from '../../../src/types/options/TInputOptions';
 import { TOptionsPreset } from '../../../src/types/options/TOptionsPreset';
 import { TOptionsPreset } from '../../../src/types/options/TOptionsPreset';
 import { TTypeFromEnum } from '../../../src/types/utils/TTypeFromEnum';
 import { TTypeFromEnum } from '../../../src/types/utils/TTypeFromEnum';
 
 
-import { IObfuscatedCode } from '../../../src/interfaces/source-code/IObfuscatedCode';
+import { IObfuscationResult } from '../../../src/interfaces/source-code/IObfuscationResult';
 
 
 import { SourceMapMode } from '../../../src/enums/source-map/SourceMapMode';
 import { SourceMapMode } from '../../../src/enums/source-map/SourceMapMode';
 import { StringArrayEncoding } from '../../../src/enums/node-transformers/string-array-transformers/StringArrayEncoding';
 import { StringArrayEncoding } from '../../../src/enums/node-transformers/string-array-transformers/StringArrayEncoding';
@@ -32,15 +33,15 @@ describe('JavaScriptObfuscator', () => {
 
 
             beforeEach(() => {
             beforeEach(() => {
                 const code: string = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
                 const code: string = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
-                const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                     code,
                     code,
                     {
                     {
                         ...NO_ADDITIONAL_NODES_PRESET
                         ...NO_ADDITIONAL_NODES_PRESET
                     }
                     }
                 );
                 );
 
 
-                obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
-                sourceMap = obfuscatedCodeObject.getSourceMap();
+                obfuscatedCode = obfuscationResult.getObfuscatedCode();
+                sourceMap = obfuscationResult.getSourceMap();
             });
             });
 
 
             it('should return correct obfuscated code', () => {
             it('should return correct obfuscated code', () => {
@@ -111,7 +112,7 @@ describe('JavaScriptObfuscator', () => {
 
 
                 beforeEach(() => {
                 beforeEach(() => {
                     const code: string = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
                     const code: string = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
-                    const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                         code,
                         code,
                         {
                         {
                             ...NO_ADDITIONAL_NODES_PRESET,
                             ...NO_ADDITIONAL_NODES_PRESET,
@@ -119,8 +120,8 @@ describe('JavaScriptObfuscator', () => {
                         }
                         }
                     );
                     );
 
 
-                    obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
-                    sourceMap = JSON.parse(obfuscatedCodeObject.getSourceMap()).mappings;
+                    obfuscatedCode = obfuscationResult.getObfuscatedCode();
+                    sourceMap = JSON.parse(obfuscationResult.getSourceMap()).mappings;
                 });
                 });
 
 
                 it('should return correct obfuscated code', () => {
                 it('should return correct obfuscated code', () => {
@@ -140,7 +141,7 @@ describe('JavaScriptObfuscator', () => {
 
 
                 beforeEach(() => {
                 beforeEach(() => {
                     const code: string = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
                     const code: string = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
-                    const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                         code,
                         code,
                         {
                         {
                             ...NO_ADDITIONAL_NODES_PRESET,
                             ...NO_ADDITIONAL_NODES_PRESET,
@@ -149,8 +150,8 @@ describe('JavaScriptObfuscator', () => {
                         }
                         }
                     );
                     );
 
 
-                    obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
-                    sourceMap = JSON.parse(obfuscatedCodeObject.getSourceMap()).mappings;
+                    obfuscatedCode = obfuscationResult.getObfuscatedCode();
+                    sourceMap = JSON.parse(obfuscationResult.getSourceMap()).mappings;
                 });
                 });
 
 
                 it('should return correct obfuscated code', () => {
                 it('should return correct obfuscated code', () => {
@@ -174,16 +175,16 @@ describe('JavaScriptObfuscator', () => {
 
 
                 beforeEach(() => {
                 beforeEach(() => {
                     const code: string = readFileAsString(__dirname + '/fixtures/empty-input.js');
                     const code: string = readFileAsString(__dirname + '/fixtures/empty-input.js');
-                    const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                         code,
                         code,
                         {
                         {
                             sourceMap: true
                             sourceMap: true
                         }
                         }
                     );
                     );
 
 
-                    obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
+                    obfuscatedCode = obfuscationResult.getObfuscatedCode();
 
 
-                    const sourceMapObject: any = JSON.parse(obfuscatedCodeObject.getSourceMap());
+                    const sourceMapObject: any = JSON.parse(obfuscationResult.getSourceMap());
 
 
                     sourceMapNames = sourceMapObject.names;
                     sourceMapNames = sourceMapObject.names;
                     sourceMapSources = sourceMapObject.sources;
                     sourceMapSources = sourceMapObject.sources;
@@ -886,7 +887,7 @@ describe('JavaScriptObfuscator', () => {
                     obfuscatedCode = JavaScriptObfuscator.obfuscate(code).getObfuscatedCode();
                     obfuscatedCode = JavaScriptObfuscator.obfuscate(code).getObfuscatedCode();
                 });
                 });
 
 
-                it('Match #!: should correctly obfuscate a import', () => {
+                it('Match #1: should correctly obfuscate a import', () => {
                     assert.match(obfuscatedCode, importRegExp);
                     assert.match(obfuscatedCode, importRegExp);
                 });
                 });
 
 
@@ -912,6 +913,131 @@ describe('JavaScriptObfuscator', () => {
             });
             });
         });
         });
 
 
+        describe('identifier names cache generation', () => {
+            describe('Variant #1: `identifierNamesCache` and `renameGlobal` options are enabled. Existing cache is passed', () => {
+                const expectedIdentifierNamesCache: TIdentifierNamesCache = {
+                    globalIdentifiers: {
+                        foo: 'a',
+                        bar: 'b',
+                        baz: 'baz_value_from_cache'
+                    },
+                    propertyIdentifiers: {}
+                };
+
+                let identifierNamesCache: TIdentifierNamesCache;
+
+                beforeEach(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/identifier-names-cache-1.js');
+
+                    identifierNamesCache = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            renameGlobals: true,
+                            identifierNamesCache: {
+                                globalIdentifiers: {
+                                    baz: 'baz_value_from_cache'
+                                },
+                                propertyIdentifiers: {}
+                            },
+                            identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator
+                        }
+                    ).getIdentifierNamesCache();
+                });
+
+                it('Match #1: should correctly generate identifier names cache', () => {
+                    assert.deepEqual(identifierNamesCache, expectedIdentifierNamesCache);
+                });
+            });
+
+            describe('Variant #2: `identifierNamesCache` and `renameGlobal` options are enabled', () => {
+                const expectedIdentifierNamesCache: TIdentifierNamesCache = {
+                    globalIdentifiers: {
+                        foo: 'a',
+                        bar: 'b'
+                    },
+                    propertyIdentifiers: {}
+                };
+
+                let identifierNamesCache: TIdentifierNamesCache;
+
+                beforeEach(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/identifier-names-cache-1.js');
+
+                    identifierNamesCache = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            renameGlobals: true,
+                            identifierNamesCache: {
+                                globalIdentifiers: {},
+                                propertyIdentifiers: {}
+                            },
+                            identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator
+                        }
+                    ).getIdentifierNamesCache();
+                });
+
+                it('Match #1: should correctly generate identifier names cache', () => {
+                    assert.deepEqual(identifierNamesCache, expectedIdentifierNamesCache);
+                });
+            });
+
+            describe('Variant #3: `identifierNamesCache` and `renameGlobal` options are enabled. Source code without global variables', () => {
+                const expectedIdentifierNamesCache: TIdentifierNamesCache = {
+                    globalIdentifiers: {},
+                    propertyIdentifiers: {}
+                };
+
+                let identifierNamesCache: TIdentifierNamesCache;
+
+                beforeEach(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/identifier-names-cache-2.js');
+
+                    identifierNamesCache = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            renameGlobals: true,
+                            identifierNamesCache: {
+                                globalIdentifiers: {},
+                                propertyIdentifiers: {}
+                            },
+                            identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator
+                        }
+                    ).getIdentifierNamesCache();
+                });
+
+                it('Match #1: should correctly generate identifier names cache', () => {
+                    assert.deepEqual(identifierNamesCache, expectedIdentifierNamesCache);
+                });
+            });
+
+            describe('Variant #4: `identifierNamesCache` option is disabled', () => {
+                const expectedIdentifierNamesCache: TIdentifierNamesCache = null;
+
+                let identifierNamesCache: TIdentifierNamesCache;
+
+                beforeEach(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/identifier-names-cache-1.js');
+
+                    identifierNamesCache = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            renameGlobals: true,
+                            identifierNamesCache: null,
+                            identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator
+                        }
+                    ).getIdentifierNamesCache();
+                });
+
+                it('Match #1: should correctly generate identifier names cache', () => {
+                    assert.deepEqual(identifierNamesCache, expectedIdentifierNamesCache);
+                });
+            });
+        });
+
         describe('3.5k variables', function () {
         describe('3.5k variables', function () {
             this.timeout(200000);
             this.timeout(200000);
 
 
@@ -1224,7 +1350,7 @@ describe('JavaScriptObfuscator', () => {
         });
         });
 
 
         describe('invalid source codes object', () => {
         describe('invalid source codes object', () => {
-            let testFunc: () => TDictionary<IObfuscatedCode>;
+            let testFunc: () => TDictionary<IObfuscationResult>;
 
 
             beforeEach(() => {
             beforeEach(() => {
                 testFunc = () => JavaScriptObfuscator.obfuscateMultiple(
                 testFunc = () => JavaScriptObfuscator.obfuscateMultiple(

+ 9 - 0
test/functional-tests/javascript-obfuscator/fixtures/identifier-names-cache-1.js

@@ -0,0 +1,9 @@
+function foo(arg) {
+    console.log(arg)
+}
+
+function bar() {
+    var bark = 2;
+}
+
+baz();

+ 9 - 0
test/functional-tests/javascript-obfuscator/fixtures/identifier-names-cache-2.js

@@ -0,0 +1,9 @@
+(function () {
+    function foo(arg) {
+        console.log(arg)
+    }
+
+    function bar() {
+        var bark = 2;
+    }
+})();

+ 67 - 3
test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/call-expression-control-flow-replacer/CallExpressionControlFlowReplacer.spec.ts

@@ -118,7 +118,71 @@ describe('CallExpressionControlFlowReplacer', function () {
             });
             });
         });
         });
 
 
-        describe('Variant #4 - rest call argument', () => {
+        describe('Variant #4 - rest as start call argument', () => {
+            const controlFlowStorageCallRegExp: RegExp = /_0x([a-f0-9]){4,6}\['\w{5}']\(_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}\);/;
+            const controlFlowStorageNodeRegExp: RegExp = new RegExp(`` +
+                `'\\w{5}' *: *function *\\(_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\\) *\\{` +
+                    `return *_0x([a-f0-9]){4,6}\\(\.\.\._0x([a-f0-9]){4,6}\\);` +
+                `\\}` +
+            ``);
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/rest-as-start-call-argument.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        controlFlowFlattening: true,
+                        controlFlowFlatteningThreshold: 1
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should replace call expression node with call to control flow storage node', () => {
+                assert.match(obfuscatedCode, controlFlowStorageCallRegExp);
+            });
+
+            it('should keep rest parameter and rest call argument, but remove all function parameters after rest parameter', () => {
+                assert.match(obfuscatedCode, controlFlowStorageNodeRegExp);
+            });
+        });
+
+        describe('Variant #5 - rest as middle call argument', () => {
+            const controlFlowStorageCallRegExp: RegExp = /_0x([a-f0-9]){4,6}\['\w{5}']\(_0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}\);/;
+            const controlFlowStorageNodeRegExp: RegExp = new RegExp(`` +
+                `'\\w{5}' *: *function *\\(_0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\\) *\\{` +
+                    `return *_0x([a-f0-9]){4,6}\\(_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\\);` +
+                `\\}` +
+            ``);
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/rest-as-middle-call-argument.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        controlFlowFlattening: true,
+                        controlFlowFlatteningThreshold: 1
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should replace call expression node with call to control flow storage node', () => {
+                assert.match(obfuscatedCode, controlFlowStorageCallRegExp);
+            });
+
+            it('should keep rest parameter and rest call argument, but remove all function parameters after rest parameter', () => {
+                assert.match(obfuscatedCode, controlFlowStorageNodeRegExp);
+            });
+        });
+
+        describe('Variant #6 - rest as last call argument', () => {
             const controlFlowStorageCallRegExp: RegExp = /_0x([a-f0-9]){4,6}\['\w{5}']\(_0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\);/;
             const controlFlowStorageCallRegExp: RegExp = /_0x([a-f0-9]){4,6}\['\w{5}']\(_0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\);/;
             const controlFlowStorageNodeRegExp: RegExp = new RegExp(`` +
             const controlFlowStorageNodeRegExp: RegExp = new RegExp(`` +
                 `'\\w{5}' *: *function *\\(_0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\\) *\\{` +
                 `'\\w{5}' *: *function *\\(_0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\\) *\\{` +
@@ -129,7 +193,7 @@ describe('CallExpressionControlFlowReplacer', function () {
             let obfuscatedCode: string;
             let obfuscatedCode: string;
 
 
             before(() => {
             before(() => {
-                const code: string = readFileAsString(__dirname + '/fixtures/rest-call-argument.js');
+                const code: string = readFileAsString(__dirname + '/fixtures/rest-as-last-call-argument.js');
 
 
                 obfuscatedCode = JavaScriptObfuscator.obfuscate(
                 obfuscatedCode = JavaScriptObfuscator.obfuscate(
                     code,
                     code,
@@ -145,7 +209,7 @@ describe('CallExpressionControlFlowReplacer', function () {
                 assert.match(obfuscatedCode, controlFlowStorageCallRegExp);
                 assert.match(obfuscatedCode, controlFlowStorageCallRegExp);
             });
             });
 
 
-            it('should keep spread parameter and rest call argument inside control flow storage node function', () => {
+            it('should keep rest parameter and rest call argument inside control flow storage node function', () => {
                 assert.match(obfuscatedCode, controlFlowStorageNodeRegExp);
                 assert.match(obfuscatedCode, controlFlowStorageNodeRegExp);
             });
             });
         });
         });

+ 0 - 0
test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/call-expression-control-flow-replacer/fixtures/rest-call-argument.js → test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/call-expression-control-flow-replacer/fixtures/rest-as-last-call-argument.js


+ 7 - 0
test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/call-expression-control-flow-replacer/fixtures/rest-as-middle-call-argument.js

@@ -0,0 +1,7 @@
+(function () {
+    const log = console.log;
+    const first = 'foo';
+    const rest = ['bar', 'baz', 'bark'];
+    const last = 'hawk';
+    log(first, ...rest, last);
+})();

+ 6 - 0
test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/call-expression-control-flow-replacer/fixtures/rest-as-start-call-argument.js

@@ -0,0 +1,6 @@
+(function () {
+    const log = console.log;
+    const rest = ['foo', 'bar', 'baz'];
+    const last = 'bark';
+    log(...rest, last);
+})();

+ 98 - 0
test/functional-tests/node-transformers/rename-identifiers-transformers/scope-through-identifiers-transformer/class-declaration/ClassDeclaration.spec.ts

@@ -0,0 +1,98 @@
+import { assert } from 'chai';
+
+import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../../src/options/presets/NoCustomNodes';
+
+import { readFileAsString } from '../../../../../helpers/readFileAsString';
+
+import { JavaScriptObfuscator } from '../../../../../../src/JavaScriptObfuscatorFacade';
+
+describe('ScopeThroughIdentifiersTransformer ClassDeclaration identifiers', () => {
+    describe('Variant #1: class declaration is exist', () => {
+        const classNameIdentifierRegExp: RegExp = /class *(_0x[a-f0-9]{4,6}) *\{/;
+        const classCallIdentifierRegExp: RegExp = /new *(_0x[a-f0-9]{4,6}) *\( *\);/;
+
+        let obfuscatedCode: string;
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/class-call-with-declaration.js');
+
+            obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET,
+                    renameGlobals: true,
+                    identifierNamesCache: {
+                        globalIdentifiers: {
+                            'Foo': 'Foo_from_cache'
+                        },
+                        propertyIdentifiers: {}
+                    }
+                }
+            ).getObfuscatedCode();
+        });
+
+        it('should skip transformation of class name', () => {
+            assert.match(obfuscatedCode, classNameIdentifierRegExp);
+        });
+
+        it('should skip transformation of class name', () => {
+            assert.match(obfuscatedCode, classCallIdentifierRegExp);
+        });
+    });
+
+    describe('Variant #2: class declaration is missing', () => {
+        describe('Variant #1: transformation of class call identifier node name based on identifier names cache', () => {
+            const classCallIdentifierRegExp: RegExp = /new *Foo_from_cache *\( *\);/;
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/class-call-without-declaration.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        renameGlobals: true,
+                        identifierNamesCache: {
+                            globalIdentifiers: {
+                                'Foo': 'Foo_from_cache'
+                            },
+                            propertyIdentifiers: {}
+                        }
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should transform class name', () => {
+                assert.match(obfuscatedCode, classCallIdentifierRegExp);
+            });
+        });
+
+        describe('Variant #2: ignore transformation of class call identifier node name when no identifier names cache value is available', () => {
+            const classCallIdentifierRegExp: RegExp = /new *Foo *\( *\);/;
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/class-call-without-declaration.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        renameGlobals: true,
+                        identifierNamesCache: {
+                            globalIdentifiers: {},
+                            propertyIdentifiers: {}
+                        }
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should not transform class name', () => {
+                assert.match(obfuscatedCode, classCallIdentifierRegExp);
+            });
+        });
+    });
+});

+ 3 - 0
test/functional-tests/node-transformers/rename-identifiers-transformers/scope-through-identifiers-transformer/class-declaration/fixtures/class-call-with-declaration.js

@@ -0,0 +1,3 @@
+class Foo {}
+
+new Foo();

+ 1 - 0
test/functional-tests/node-transformers/rename-identifiers-transformers/scope-through-identifiers-transformer/class-declaration/fixtures/class-call-without-declaration.js

@@ -0,0 +1 @@
+new Foo();

+ 98 - 0
test/functional-tests/node-transformers/rename-identifiers-transformers/scope-through-identifiers-transformer/function-declaration/FunctionDeclaration.spec.ts

@@ -0,0 +1,98 @@
+import { assert } from 'chai';
+
+import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../../src/options/presets/NoCustomNodes';
+
+import { readFileAsString } from '../../../../../helpers/readFileAsString';
+
+import { JavaScriptObfuscator } from '../../../../../../src/JavaScriptObfuscatorFacade';
+
+describe('ScopeThroughIdentifiersTransformer FunctionDeclaration identifiers', () => {
+    describe('Variant #1: function declaration is exist', () => {
+        const functionNameIdentifierRegExp: RegExp = /function *(_0x[a-f0-9]{4,6}) *\(\) *\{/;
+        const functionCallIdentifierRegExp: RegExp = /(_0x[a-f0-9]{4,6}) *\( *\);/;
+
+        let obfuscatedCode: string;
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/function-call-with-declaration.js');
+
+            obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET,
+                    renameGlobals: true,
+                    identifierNamesCache: {
+                        globalIdentifiers: {
+                            'foo': 'foo_from_cache'
+                        },
+                        propertyIdentifiers: {}
+                    }
+                }
+            ).getObfuscatedCode();
+        });
+
+        it('should skip transformation of function name', () => {
+            assert.match(obfuscatedCode, functionNameIdentifierRegExp);
+        });
+
+        it('should skip transformation of function name', () => {
+            assert.match(obfuscatedCode, functionCallIdentifierRegExp);
+        });
+    });
+
+    describe('Variant #2: function declaration is missing', () => {
+        describe('Variant #1: transformation of function call identifier node name based on identifier names cache', () => {
+            const functionCallIdentifierRegExp: RegExp = /foo_from_cache *\( *\);/;
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/function-call-without-declaration.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        renameGlobals: true,
+                        identifierNamesCache: {
+                            globalIdentifiers: {
+                                'foo': 'foo_from_cache'
+                            },
+                            propertyIdentifiers: {}
+                        }
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should transform function name', () => {
+                assert.match(obfuscatedCode, functionCallIdentifierRegExp);
+            });
+        });
+
+        describe('Variant #2: ignore transformation of function call identifier node name when no identifier names cache value is available', () => {
+            const functionCallIdentifierRegExp: RegExp = /foo *\( *\);/;
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/function-call-without-declaration.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        renameGlobals: true,
+                        identifierNamesCache: {
+                            globalIdentifiers: {},
+                            propertyIdentifiers: {}
+                        }
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should not transform function name', () => {
+                assert.match(obfuscatedCode, functionCallIdentifierRegExp);
+            });
+        });
+    });
+});

+ 3 - 0
test/functional-tests/node-transformers/rename-identifiers-transformers/scope-through-identifiers-transformer/function-declaration/fixtures/function-call-with-declaration.js

@@ -0,0 +1,3 @@
+function foo () {}
+
+foo();

Деякі файли не було показано, через те що забагато файлів було змінено