Browse Source

Identifier names cache option prototype

sanex 4 năm trước cách đây
mục cha
commit
f087100c26

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


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


+ 17 - 2
src/cli/JavaScriptObfuscatorCLI.ts

@@ -27,6 +27,7 @@ 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 { IdentifierNamesCacheUtils } from './utils/IdentifierNamesCacheUtils';
 import { JavaScriptObfuscator } from '../JavaScriptObfuscatorFacade';
 import { JavaScriptObfuscator } from '../JavaScriptObfuscatorFacade';
 import { Logger } from '../logger/Logger';
 import { Logger } from '../logger/Logger';
 import { ObfuscatedCodeWriter } from './utils/ObfuscatedCodeWriter';
 import { ObfuscatedCodeWriter } from './utils/ObfuscatedCodeWriter';
@@ -57,6 +58,12 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
     @initializable()
     @initializable()
     private commands!: commander.CommanderStatic;
     private commands!: commander.CommanderStatic;
 
 
+    /**
+     * @type {IdentifierNamesCacheUtils}
+     */
+    @initializable()
+    private identifierNamesCacheUtils!: IdentifierNamesCacheUtils;
+
     /**
     /**
      * @type {TInputCLIOptions}
      * @type {TInputCLIOptions}
      */
      */
@@ -152,6 +159,7 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
             this.inputPath,
             this.inputPath,
             this.inputCLIOptions
             this.inputCLIOptions
         );
         );
+        this.identifierNamesCacheUtils = new IdentifierNamesCacheUtils(this.inputCLIOptions.identifierNamesCachePath);
     }
     }
 
 
     public run (): void {
     public run (): void {
@@ -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. ' +
@@ -466,6 +478,7 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
     ): void {
     ): void {
         const options: TInputOptions = {
         const options: TInputOptions = {
             ...this.inputCLIOptions,
             ...this.inputCLIOptions,
+            identifierNamesCache: this.identifierNamesCacheUtils.read(),
             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 obfuscatedCode: IObfuscatedCode = JavaScriptObfuscator.obfuscate(sourceCode, options);
 
 
-        this.obfuscatedCodeWriter.writeFile(outputCodePath, obfuscatedCode);
+        this.obfuscatedCodeWriter.writeFile(outputCodePath, obfuscatedCode.getObfuscatedCode());
+        this.identifierNamesCacheUtils.write(obfuscatedCode.getIdentifierNamesCache());
     }
     }
 
 
     /**
     /**
@@ -520,6 +534,7 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
         const obfuscatedCode: IObfuscatedCode = JavaScriptObfuscator.obfuscate(sourceCode, options);
         const obfuscatedCode: IObfuscatedCode = JavaScriptObfuscator.obfuscate(sourceCode, options);
 
 
         this.obfuscatedCodeWriter.writeFile(outputCodePath, obfuscatedCode.getObfuscatedCode());
         this.obfuscatedCodeWriter.writeFile(outputCodePath, obfuscatedCode.getObfuscatedCode());
+        this.identifierNamesCacheUtils.write(obfuscatedCode.getIdentifierNamesCache());
 
 
         if (options.sourceMapMode === SourceMapMode.Separate && obfuscatedCode.getSourceMap()) {
         if (options.sourceMapMode === SourceMapMode.Separate && obfuscatedCode.getSourceMap()) {
             this.obfuscatedCodeWriter.writeFile(outputSourceMapPath, obfuscatedCode.getSourceMap());
             this.obfuscatedCodeWriter.writeFile(outputSourceMapPath, obfuscatedCode.getSourceMap());

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

@@ -0,0 +1,98 @@
+import * as fs from 'fs';
+import * as path from 'path';
+
+import { TIdentifierNamesCache } from '../../types/caches/TIdentifierNamesCache';
+
+import { IFileData } from '../../interfaces/cli/IFileData';
+
+import { JavaScriptObfuscatorCLI } from '../JavaScriptObfuscatorCLI';
+
+/**
+ * Utils to work with identifier names cache file
+ */
+export class IdentifierNamesCacheUtils {
+    /**
+     * @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) === IdentifierNamesCacheUtils.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 read (): TIdentifierNamesCache | null {
+        if (!this.identifierNamesCachePath) {
+            return null;
+        }
+
+        if (!IdentifierNamesCacheUtils.isValidFilePath(this.identifierNamesCachePath)) {
+            throw new ReferenceError(`Given identifier names cache path must be a valid ${
+                IdentifierNamesCacheUtils.identifierNamesCacheExtension
+            } file path`);
+        }
+
+        const fileData: IFileData = IdentifierNamesCacheUtils.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 write (identifierNamesCache: TIdentifierNamesCache): void {
+        if (!this.identifierNamesCachePath) {
+            return;
+        }
+
+        const identifierNamesCacheJson: string = JSON.stringify(identifierNamesCache);
+
+        fs.writeFileSync(this.identifierNamesCachePath, identifierNamesCacheJson, {
+            encoding: JavaScriptObfuscatorCLI.encoding
+        });
+    }
+}

+ 1 - 0
src/container/ServiceIdentifiers.ts

@@ -29,6 +29,7 @@ export enum ServiceIdentifiers {
     ICustomCodeHelperFormatter = 'ICustomCodeHelperFormatter',
     ICustomCodeHelperFormatter = 'ICustomCodeHelperFormatter',
     ICustomCodeHelperObfuscator = 'ICustomCodeHelperObfuscator',
     ICustomCodeHelperObfuscator = 'ICustomCodeHelperObfuscator',
     IEscapeSequenceEncoder = 'IEscapeSequenceEncoder',
     IEscapeSequenceEncoder = 'IEscapeSequenceEncoder',
+    IIdentifierNamesCacheStorage = 'IIdentifierNamesCacheStorage',
     IIdentifierNamesGenerator = 'IIdentifierNamesGenerator',
     IIdentifierNamesGenerator = 'IIdentifierNamesGenerator',
     IIdentifierReplacer = 'IIdentifierReplacer',
     IIdentifierReplacer = 'IIdentifierReplacer',
     IJavaScriptObfuscator = 'IJavaScriptObfuscator',
     IJavaScriptObfuscator = 'IJavaScriptObfuscator',

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

@@ -4,6 +4,7 @@ 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 { IIdentifierNamesCacheStorage } from '../../../interfaces/storages/identifier-names-cache/IIdentifierNamesCacheStorage';
 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 { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
 import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
@@ -14,6 +15,7 @@ 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 { IdentifierNamesCacheStorage } from '../../../storages/identifier-names-cache/IdentifierNamesCacheStorage';
 import { LiteralNodesCacheStorage } from '../../../storages/string-array-transformers/LiteralNodesCacheStorage';
 import { LiteralNodesCacheStorage } from '../../../storages/string-array-transformers/LiteralNodesCacheStorage';
 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';
@@ -26,6 +28,10 @@ export const storagesModule: interfaces.ContainerModule = new ContainerModule((b
         .to(CustomCodeHelperGroupStorage)
         .to(CustomCodeHelperGroupStorage)
         .inSingletonScope();
         .inSingletonScope();
 
 
+    bind<IIdentifierNamesCacheStorage>(ServiceIdentifiers.IIdentifierNamesCacheStorage)
+        .to(IdentifierNamesCacheStorage)
+        .inSingletonScope();
+
     bind<ILiteralNodesCacheStorage>(ServiceIdentifiers.ILiteralNodesCacheStorage)
     bind<ILiteralNodesCacheStorage>(ServiceIdentifiers.ILiteralNodesCacheStorage)
         .to(LiteralNodesCacheStorage)
         .to(LiteralNodesCacheStorage)
         .inSingletonScope();
         .inSingletonScope();

+ 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/caches/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 | null;
     readonly identifierNamesGenerator: TTypeFromEnum<typeof IdentifierNamesGenerator>;
     readonly identifierNamesGenerator: TTypeFromEnum<typeof IdentifierNamesGenerator>;
     readonly identifiersDictionary: string[];
     readonly identifiersDictionary: string[];
     readonly identifiersPrefix: string;
     readonly identifiersPrefix: string;

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

@@ -1,6 +1,12 @@
+import { TIdentifierNamesCache } from '../../types/caches/TIdentifierNamesCache';
 import { IInitializable } from '../IInitializable';
 import { IInitializable } from '../IInitializable';
 
 
 export interface IObfuscatedCode extends IInitializable <[string, string]> {
 export interface IObfuscatedCode extends IInitializable <[string, string]> {
+    /**
+     * @returns {TIdentifierNamesCache}
+     */
+    getIdentifierNamesCache (): TIdentifierNamesCache;
+
     /**
     /**
      * @return {string}
      * @return {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 {
@@ -7,6 +9,11 @@ export interface IMapStorage <K, V> extends IInitializable {
      */
      */
     get (key: K): V | undefined;
     get (key: K): V | undefined;
 
 
+    /**
+     * @returns {TDictionary<V>}
+     */
+    getAsDictionary (): TDictionary<V>;
+
     /**
     /**
      * @param {K} key
      * @param {K} key
      * @returns {V}
      * @returns {V}

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

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

+ 22 - 1
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 { IIdentifierNamesCacheStorage } from '../../../interfaces/storages/identifier-names-cache/IIdentifierNamesCacheStorage';
 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 {IIdentifierNamesCacheStorage}
+     */
+    private readonly identifierNamesCacheStorage: IIdentifierNamesCacheStorage;
+
     /**
     /**
      * @type {IIdentifierNamesGenerator}
      * @type {IIdentifierNamesGenerator}
      */
      */
@@ -31,14 +37,18 @@ export class IdentifierReplacer implements IIdentifierReplacer {
 
 
     /**
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {IIdentifierNamesCacheStorage} 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.IIdentifierNamesCacheStorage)
+            identifierNamesCacheStorage: IIdentifierNamesCacheStorage,
         @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);
     }
     }
 
 
@@ -56,7 +66,14 @@ export class IdentifierReplacer implements IIdentifierReplacer {
             return;
             return;
         }
         }
 
 
-        const newIdentifierName: string = this.identifierNamesGenerator.generateForGlobalScope();
+        const valueFromIdentifierNamesCache: string | null = this.identifierNamesCacheStorage.get(identifierName) ?? null;
+        let newIdentifierName: string;
+
+        if (valueFromIdentifierNamesCache) {
+            newIdentifierName = valueFromIdentifierNamesCache;
+        } else {
+            newIdentifierName = this.identifierNamesGenerator.generateForGlobalScope();
+        }
 
 
         if (!this.blockScopesMap.has(lexicalScopeNode)) {
         if (!this.blockScopesMap.has(lexicalScopeNode)) {
             this.blockScopesMap.set(lexicalScopeNode, new Map());
             this.blockScopesMap.set(lexicalScopeNode, new Map());
@@ -65,6 +82,10 @@ 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);
+
+        if (valueFromIdentifierNamesCache !== newIdentifierName) {
+            this.identifierNamesCacheStorage.set(identifierName, newIdentifierName);
+        }
     }
     }
 
 
     /**
     /**

+ 8 - 0
src/options/Options.ts

@@ -18,6 +18,7 @@ import {
     ValidatorOptions
     ValidatorOptions
 } from 'class-validator';
 } from 'class-validator';
 
 
+import { TIdentifierNamesCache } from '../types/caches/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 { IsPrimitiveDictionary } from './validators/IsPrimitiveDictionary';
 
 
 @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}
+     */
+    @IsPrimitiveDictionary('string')
+    public readonly identifierNamesCache!: TIdentifierNamesCache;
+
     /**
     /**
      * @type {IdentifierNamesGenerator}
      * @type {IdentifierNamesGenerator}
      */
      */

+ 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: [],

+ 66 - 0
src/options/validators/IsPrimitiveDictionary.ts

@@ -0,0 +1,66 @@
+import equal from 'fast-deep-equal';
+import { registerDecorator, ValidationArguments, ValidationOptions } from 'class-validator';
+
+import { IOptions } from '../../interfaces/options/IOptions';
+
+import { DEFAULT_PRESET } from '../presets/Default';
+
+/**
+ * @param {"string" | "number"} valuesType
+ * @param {ValidationOptions} validationOptions
+ * @returns {(options: IOptions, propertyName: keyof IOptions) => void}
+ */
+export function IsPrimitiveDictionary (
+    valuesType: 'string' | 'number',
+    validationOptions?: ValidationOptions
+): (options: IOptions, propertyName: keyof IOptions) => void {
+    return (optionsObject: IOptions, propertyName: keyof IOptions): void => {
+        registerDecorator({
+            propertyName,
+            constraints: [valuesType],
+            name: 'IsPrimitiveDictionary',
+            options: validationOptions,
+            target: optionsObject.constructor,
+            validator: {
+                /**
+                 * @param value
+                 * @param {ValidationArguments} validationArguments
+                 * @returns {boolean}
+                 */
+                validate (value: IOptions[keyof IOptions], 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;
+                    }
+
+                    const objectValues: unknown[] = Object.values<unknown>(value);
+
+                    if (!objectValues.length) {
+                        return true;
+                    }
+
+                    for (const objectValue of objectValues) {
+                        if (typeof objectValue !== 'string') {
+                            return false;
+                        }
+                    }
+
+                    return true;
+                },
+
+                /**
+                 * @returns {string}
+                 */
+                defaultMessage (): string {
+                    return `Passed value must be a dictionary with \`${valuesType}\` values or \`null\` value`;
+                }
+            }
+        });
+    };
+}

+ 24 - 1
src/source-code/ObfuscatedCode.ts

@@ -1,12 +1,15 @@
 import { inject, injectable } from 'inversify';
 import { inject, injectable } from 'inversify';
 import { ServiceIdentifiers } from '../container/ServiceIdentifiers';
 import { ServiceIdentifiers } from '../container/ServiceIdentifiers';
 
 
+import { TIdentifierNamesCache } from '../types/caches/TIdentifierNamesCache';
+
 import { ICryptUtils } from '../interfaces/utils/ICryptUtils';
 import { ICryptUtils } from '../interfaces/utils/ICryptUtils';
+import { IIdentifierNamesCacheStorage } from '../interfaces/storages/identifier-names-cache/IIdentifierNamesCacheStorage';
 import { IObfuscatedCode } from '../interfaces/source-code/IObfuscatedCode';
 import { IObfuscatedCode } from '../interfaces/source-code/IObfuscatedCode';
+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 ObfuscatedCode implements IObfuscatedCode {
@@ -27,16 +30,29 @@ export class ObfuscatedCode implements IObfuscatedCode {
      */
      */
     private readonly cryptUtils: ICryptUtils;
     private readonly cryptUtils: ICryptUtils;
 
 
+    /**
+     * @type {IIdentifierNamesCacheStorage}
+     */
+    private readonly identifierNamesCacheStorage: IIdentifierNamesCacheStorage;
+
     /**
     /**
      * @type {IOptions}
      * @type {IOptions}
      */
      */
     private readonly options: IOptions;
     private readonly options: IOptions;
 
 
+    /**
+     * @param {ICryptUtils} cryptUtils
+     * @param {IIdentifierNamesCacheStorage} identifierNamesCacheStorage
+     * @param {IOptions} options
+     */
     public constructor (
     public constructor (
         @inject(ServiceIdentifiers.ICryptUtils) cryptUtils: ICryptUtils,
         @inject(ServiceIdentifiers.ICryptUtils) cryptUtils: ICryptUtils,
+        @inject(ServiceIdentifiers.IIdentifierNamesCacheStorage)
+            identifierNamesCacheStorage: IIdentifierNamesCacheStorage,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
     ) {
         this.cryptUtils = cryptUtils;
         this.cryptUtils = cryptUtils;
+        this.identifierNamesCacheStorage = identifierNamesCacheStorage;
         this.options = options;
         this.options = options;
     }
     }
 
 
@@ -49,6 +65,13 @@ export class ObfuscatedCode implements IObfuscatedCode {
         this.sourceMap = sourceMap;
         this.sourceMap = sourceMap;
     }
     }
 
 
+    /**
+     * @returns {string}
+     */
+    public getIdentifierNamesCache (): TIdentifierNamesCache {
+        return this.identifierNamesCacheStorage.getAsDictionary();
+    }
+
     /**
     /**
      * @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> {
@@ -57,6 +58,13 @@ export abstract class MapStorage <K, V> implements IMapStorage <K, V> {
         return this.storage.get(key);
         return this.storage.get(key);
     }
     }
 
 
+    /**
+     * @returns {TDictionary<V>}
+     */
+    public getAsDictionary (): TDictionary<V> {
+        return Object.fromEntries(this.storage);
+    }
+
     /**
     /**
      * @param {K} key
      * @param {K} key
      * @returns {V}
      * @returns {V}

+ 31 - 0
src/storages/identifier-names-cache/IdentifierNamesCacheStorage.ts

@@ -0,0 +1,31 @@
+import { inject, injectable, postConstruct } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
+import { IIdentifierNamesCacheStorage } from '../../interfaces/storages/identifier-names-cache/IIdentifierNamesCacheStorage';
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+
+import { MapStorage } from '../MapStorage';
+
+@injectable()
+export class IdentifierNamesCacheStorage extends MapStorage <string, string> implements IIdentifierNamesCacheStorage {
+    /**
+     * @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();
+
+       if (this.options.identifierNamesCache) {
+           this.storage = new Map(Object.entries(this.options.identifierNamesCache));
+       }
+    }
+}

+ 3 - 0
src/types/caches/TIdentifierNamesCache.ts

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

+ 26 - 14
test/dev/dev.ts

@@ -1,31 +1,43 @@
 'use strict';
 'use strict';
 
 
+import { TIdentifierNamesCache } from '../../src/types/caches/TIdentifierNamesCache';
+
 import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNodes';
 import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNodes';
 
 
 (function () {
 (function () {
     const JavaScriptObfuscator: any = require('../../index');
     const JavaScriptObfuscator: any = require('../../index');
 
 
-    let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
+    const existingIdentifierNamesCache: TIdentifierNamesCache | null = {
+        foo: 'foo_existing',
+        bar: 'bar_existing'
+    };
+
+    let obfuscationResult = JavaScriptObfuscator.obfuscate(
         `
         `
-            function foo(a, b, c, d) {
-              console.log(a, b, c, d)
-            }
-            
-            function bar(...args) {
-              foo(...args, 5)
-            }
-            
-            bar(...[1, 2, 3], 4)
+            (() => {
+                function foo(a, b, c, d) {
+                  console.log(a, b, c, d)
+                }
+                
+                function bar(...args) {
+                  foo(...args, 5)
+                }
+                
+                bar(...[1, 2, 3], 4)
+            })();
         `,
         `,
         {
         {
             ...NO_ADDITIONAL_NODES_PRESET,
             ...NO_ADDITIONAL_NODES_PRESET,
             compact: false,
             compact: false,
-            controlFlowFlattening: true,
-            controlFlowFlatteningThreshold: 1,
-            identifierNamesGenerator: 'mangled'
+            identifierNamesCache: existingIdentifierNamesCache,
+            identifierNamesGenerator: 'mangled-shuffled'
         }
         }
-    ).getObfuscatedCode();
+    );
+
+    let obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
+    let identifierNamesCache = obfuscationResult.getIdentifierNamesCache();
 
 
     console.log(obfuscatedCode);
     console.log(obfuscatedCode);
     console.log(eval(obfuscatedCode));
     console.log(eval(obfuscatedCode));
+    console.log(identifierNamesCache);
 })();
 })();

+ 2 - 2
tsconfig.json

@@ -6,10 +6,10 @@
     "emitDecoratorMetadata": true,
     "emitDecoratorMetadata": true,
     "experimentalDecorators": true,
     "experimentalDecorators": true,
     "lib": [
     "lib": [
-      "es2017",
+      "es2019",
       "dom"
       "dom"
     ],
     ],
-    "target": "es2017",
+    "target": "es2018",
     "module": "commonjs",
     "module": "commonjs",
     "resolveJsonModule": true,
     "resolveJsonModule": true,
     "esModuleInterop": true,
     "esModuleInterop": true,

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