浏览代码

Improved mangled identifier names generator. Improved variable preserving.

sanex3339 5 年之前
父节点
当前提交
002697caf5
共有 27 个文件被更改,包括 248 次插入157 次删除
  1. 1 0
      CHANGELOG.md
  2. 0 0
      dist/index.browser.js
  3. 0 0
      dist/index.cli.js
  4. 0 0
      dist/index.js
  5. 34 6
      src/custom-nodes/AbstractCustomNode.ts
  6. 4 13
      src/custom-nodes/node-calls-controller-nodes/NodeCallsControllerFunctionNode.ts
  7. 8 2
      src/custom-nodes/object-expression-keys-transformer-nodes/ObjectExpressionVariableDeclarationHostNode.ts
  8. 2 9
      src/custom-nodes/self-defending-nodes/SelfDefendingUnicodeNode.ts
  9. 4 13
      src/custom-nodes/string-array-nodes/StringArrayCallsWrapper.ts
  10. 6 11
      src/custom-nodes/string-array-nodes/StringArrayRotateFunctionNode.ts
  11. 22 11
      src/generators/identifier-names-generators/AbstractIdentifierNamesGenerator.ts
  12. 39 21
      src/generators/identifier-names-generators/DictionaryIdentifierNamesGenerator.ts
  13. 43 6
      src/generators/identifier-names-generators/MangledIdentifierNamesGenerator.ts
  14. 2 2
      src/interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator.ts
  15. 21 6
      src/node-transformers/converting-transformers/object-expression-extractors/ObjectExpressionToVariableDeclarationExtractor.ts
  16. 0 9
      src/node-transformers/preparing-transformers/VariablePreserveTransformer.ts
  17. 10 8
      test/dev/dev.ts
  18. 15 5
      test/functional-tests/generators/identifier-names-generators/mangled-identifier-names-generator/MangledIdentifierNamesGenerator.spec.ts
  19. 3 1
      test/functional-tests/generators/identifier-names-generators/mangled-identifier-names-generator/fixtures/string-array-storage-name-conflict-2.js
  20. 8 8
      test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/class-declaration/ClassDeclaration.spec.ts
  21. 0 0
      test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/class-declaration/fixtures/prevent-using-of-preserved-identifiers.js
  22. 6 6
      test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/function-declaration/FunctionDeclaration.spec.ts
  23. 0 0
      test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/function-declaration/fixtures/prevent-using-of-preserved-identifiers-1.js
  24. 18 18
      test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/variable-declaration/VariableDeclaration.spec.ts
  25. 0 0
      test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/variable-declaration/fixtures/prevent-using-of-preserved-identifiers-1.js
  26. 0 0
      test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/variable-declaration/fixtures/prevent-using-of-preserved-identifiers-2.js
  27. 2 2
      test/functional-tests/node-transformers/preparing-transformers/variable-preserve-transformer/VariablePreserveTransformer.spec.ts

+ 1 - 0
CHANGELOG.md

@@ -2,6 +2,7 @@ Change Log
 
 v0.25.0
 ---
+* Improved `mangled` identifier names generator logic
 * Fixed conflicts between generated names and names from untouched identifiers from source code. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/550. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/549
 * Prevented transformation of object keys in sequence expression that has `super` call
 

文件差异内容过多而无法显示
+ 0 - 0
dist/index.browser.js


文件差异内容过多而无法显示
+ 0 - 0
dist/index.cli.js


文件差异内容过多而无法显示
+ 0 - 0
dist/index.js


+ 34 - 6
src/custom-nodes/AbstractCustomNode.ts

@@ -2,6 +2,7 @@ import { inject, injectable } from 'inversify';
 import { ServiceIdentifiers } from '../container/ServiceIdentifiers';
 
 import { TIdentifierNamesGeneratorFactory } from '../types/container/generators/TIdentifierNamesGeneratorFactory';
+import { TInputOptions } from '../types/options/TInputOptions';
 import { TStatement } from '../types/node/TStatement';
 
 import { ICustomNode } from '../interfaces/custom-nodes/ICustomNode';
@@ -13,6 +14,10 @@ import { ICustomNodeFormatter } from '../interfaces/custom-nodes/ICustomNodeForm
 import { GlobalVariableTemplate1 } from '../templates/GlobalVariableTemplate1';
 import { GlobalVariableTemplate2 } from '../templates/GlobalVariableTemplate2';
 
+import { NO_ADDITIONAL_NODES_PRESET } from '../options/presets/NoCustomNodes';
+
+import { JavaScriptObfuscator } from '../JavaScriptObfuscatorFacade';
+
 @injectable()
 export abstract class AbstractCustomNode <
     TInitialData extends any[] = any[]
@@ -100,16 +105,39 @@ export abstract class AbstractCustomNode <
         return '';
     }
 
+    /**
+     * @param {string} template
+     * @param {TInputOptions} options
+     * @returns {string}
+     */
+    protected obfuscateTemplate (template: string, options: TInputOptions = {}): string {
+        const reservedNames: string[] = this.getPreservedNames(options.reservedNames);
+
+        return JavaScriptObfuscator.obfuscate(
+            template,
+            {
+                ...NO_ADDITIONAL_NODES_PRESET,
+                identifierNamesGenerator: this.options.identifierNamesGenerator,
+                identifiersDictionary: this.options.identifiersDictionary,
+                seed: this.randomGenerator.getRawSeed(),
+                ...options,
+                reservedNames
+            }
+        ).getObfuscatedCode();
+    }
+
     /**
      * @param {string[]} additionalNames
      * @returns {string[]}
      */
-    protected getPreservedNames (additionalNames: string[]): string[] {
-        return Array.from(new Set([
-            ...Array.from(this.identifierNamesGenerator.getPreservedNames().values()),
-            ...additionalNames
-        ]).values())
-        .map((preservedName: string) => `^${preservedName}$`);
+    private getPreservedNames (additionalNames: string[] = []): string[] {
+        return Array
+            .from(new Set([
+                ...Array.from(this.identifierNamesGenerator.getPreservedNames().values()),
+                ...additionalNames
+            ])
+            .values())
+            .map((preservedName: string) => `^${preservedName}$`);
     }
 
     /**

+ 4 - 13
src/custom-nodes/node-calls-controller-nodes/NodeCallsControllerFunctionNode.ts

@@ -4,6 +4,7 @@ import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
 import { TStatement } from '../../types/node/TStatement';
 
+import { ICustomNodeFormatter } from '../../interfaces/custom-nodes/ICustomNodeFormatter';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 
@@ -13,12 +14,8 @@ import { initializable } from '../../decorators/Initializable';
 
 import { SingleNodeCallControllerTemplate } from '../../templates/SingleNodeCallControllerTemplate';
 
-import { NO_ADDITIONAL_NODES_PRESET } from '../../options/presets/NoCustomNodes';
-
 import { AbstractCustomNode } from '../AbstractCustomNode';
-import { JavaScriptObfuscator } from '../../JavaScriptObfuscatorFacade';
 import { NodeUtils } from '../../node/NodeUtils';
-import { ICustomNodeFormatter } from '../../interfaces/custom-nodes/ICustomNodeFormatter';
 
 @injectable()
 export class NodeCallsControllerFunctionNode extends AbstractCustomNode {
@@ -72,17 +69,11 @@ export class NodeCallsControllerFunctionNode extends AbstractCustomNode {
      */
     protected getNodeTemplate (): string {
         if (this.appendEvent === ObfuscationEvent.AfterObfuscation) {
-            return JavaScriptObfuscator.obfuscate(
+            return this.obfuscateTemplate(
                 this.customNodeFormatter.formatTemplate(SingleNodeCallControllerTemplate(), {
                     singleNodeCallControllerFunctionName: this.callsControllerFunctionName
-                }),
-                {
-                    ...NO_ADDITIONAL_NODES_PRESET,
-                    identifierNamesGenerator: this.options.identifierNamesGenerator,
-                    identifiersDictionary: this.options.identifiersDictionary,
-                    seed: this.options.seed
-                }
-            ).getObfuscatedCode();
+                })
+            );
         }
 
         return this.customNodeFormatter.formatTemplate(SingleNodeCallControllerTemplate(), {

+ 8 - 2
src/custom-nodes/object-expression-keys-transformer-nodes/ObjectExpressionVariableDeclarationHostNode.ts

@@ -4,6 +4,7 @@ import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 import * as ESTree from 'estree';
 
 import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
+import { TNodeWithLexicalScope } from '../../types/node/TNodeWithLexicalScope';
 import { TStatement } from '../../types/node/TStatement';
 
 import { IOptions } from '../../interfaces/options/IOptions';
@@ -15,6 +16,10 @@ import { NodeFactory } from '../../node/NodeFactory';
 
 @injectable()
 export class ObjectExpressionVariableDeclarationHostNode extends AbstractCustomNode {
+    /**
+     * @type {TNodeWithLexicalScope}
+     */
+    private lexicalScopeNode!: TNodeWithLexicalScope;
     /**
      * @ type {Property}
      */
@@ -36,7 +41,8 @@ export class ObjectExpressionVariableDeclarationHostNode extends AbstractCustomN
         super(identifierNamesGeneratorFactory, customNodeFormatter, randomGenerator, options);
     }
 
-    public initialize (properties: ESTree.Property[]): void {
+    public initialize (lexicalScopeNode: TNodeWithLexicalScope, properties: ESTree.Property[]): void {
+        this.lexicalScopeNode = lexicalScopeNode;
         this.properties = properties;
     }
 
@@ -49,7 +55,7 @@ export class ObjectExpressionVariableDeclarationHostNode extends AbstractCustomN
             [
                 NodeFactory.variableDeclaratorNode(
                     NodeFactory.identifierNode(
-                        this.identifierNamesGenerator.generate()
+                        this.identifierNamesGenerator.generateForLexicalScope(this.lexicalScopeNode)
                     ),
                     NodeFactory.objectExpressionNode(this.properties)
                 )

+ 2 - 9
src/custom-nodes/self-defending-nodes/SelfDefendingUnicodeNode.ts

@@ -11,12 +11,9 @@ import { ICustomNodeFormatter } from '../../interfaces/custom-nodes/ICustomNodeF
 
 import { initializable } from '../../decorators/Initializable';
 
-import { NO_ADDITIONAL_NODES_PRESET } from '../../options/presets/NoCustomNodes';
-
 import { SelfDefendingTemplate } from '../../templates/self-defending-nodes/self-defending-unicode-node/SelfDefendingTemplate';
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
-import { JavaScriptObfuscator } from '../../JavaScriptObfuscatorFacade';
 import { NodeUtils } from '../../node/NodeUtils';
 
 @injectable()
@@ -71,18 +68,14 @@ export class SelfDefendingUnicodeNode extends AbstractCustomNode {
      * @returns {string}
      */
     protected getNodeTemplate (): string {
-        return JavaScriptObfuscator.obfuscate(
+        return this.obfuscateTemplate(
             this.customNodeFormatter.formatTemplate(SelfDefendingTemplate(this.escapeSequenceEncoder), {
                 selfDefendingFunctionName: this.identifierNamesGenerator.generate(),
                 singleNodeCallControllerFunctionName: this.callsControllerFunctionName
             }),
             {
-                ...NO_ADDITIONAL_NODES_PRESET,
-                identifierNamesGenerator: this.options.identifierNamesGenerator,
-                identifiersDictionary: this.options.identifiersDictionary,
-                seed: this.options.seed,
                 unicodeEscapeSequence: true
             }
-        ).getObfuscatedCode();
+        );
     }
 }

+ 4 - 13
src/custom-nodes/string-array-nodes/StringArrayCallsWrapper.ts

@@ -14,8 +14,6 @@ import { StringArrayEncoding } from '../../enums/StringArrayEncoding';
 
 import { initializable } from '../../decorators/Initializable';
 
-import { NO_ADDITIONAL_NODES_PRESET } from '../../options/presets/NoCustomNodes';
-
 import { AtobTemplate } from '../../templates/AtobTemplate';
 import { GlobalVariableNoEvalTemplate } from '../../templates/GlobalVariableNoEvalTemplate';
 import { Rc4Template } from '../../templates/Rc4Template';
@@ -25,7 +23,6 @@ import { StringArrayCallsWrapperTemplate } from '../../templates/string-array-no
 import { StringArrayRc4DecodeNodeTemplate } from '../../templates/string-array-nodes/string-array-calls-wrapper/StringArrayRC4DecodeNodeTemplate';
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
-import { JavaScriptObfuscator } from '../../JavaScriptObfuscatorFacade';
 import { NodeUtils } from '../../node/NodeUtils';
 
 @injectable()
@@ -93,24 +90,18 @@ export class StringArrayCallsWrapper extends AbstractCustomNode {
     protected getNodeTemplate (): string {
         const decodeNodeTemplate: string = this.getDecodeStringArrayTemplate();
 
-        const preservedNames: string[] = this.getPreservedNames([this.stringArrayName]);
+        const preservedNames: string[] = [this.stringArrayName];
 
-        return JavaScriptObfuscator.obfuscate(
+        return this.obfuscateTemplate(
             this.customNodeFormatter.formatTemplate(StringArrayCallsWrapperTemplate(), {
                 decodeNodeTemplate,
                 stringArrayCallsWrapperName: this.stringArrayCallsWrapperName,
                 stringArrayName: this.stringArrayName
             }),
             {
-                ...NO_ADDITIONAL_NODES_PRESET,
-                identifierNamesGenerator: this.options.identifierNamesGenerator,
-                identifiersDictionary: this.options.identifiersDictionary,
-                reservedNames: [
-                    ...preservedNames
-                ],
-                seed: this.randomGenerator.getRawSeed()
+                reservedNames: preservedNames
             }
-        ).getObfuscatedCode();
+        );
     }
 
     /**

+ 6 - 11
src/custom-nodes/string-array-nodes/StringArrayRotateFunctionNode.ts

@@ -11,13 +11,10 @@ import { ICustomNodeFormatter } from '../../interfaces/custom-nodes/ICustomNodeF
 
 import { initializable } from '../../decorators/Initializable';
 
-import { NO_ADDITIONAL_NODES_PRESET } from '../../options/presets/NoCustomNodes';
-
 import { SelfDefendingTemplate } from '../../templates/string-array-nodes/string-array-rotate-function-node/SelfDefendingTemplate';
 import { StringArrayRotateFunctionTemplate } from '../../templates/string-array-nodes/string-array-rotate-function-node/StringArrayRotateFunctionTemplate';
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
-import { JavaScriptObfuscator } from '../../JavaScriptObfuscatorFacade';
 import { NodeUtils } from '../../node/NodeUtils';
 import { NumberUtils } from '../../utils/NumberUtils';
 
@@ -86,6 +83,7 @@ export class StringArrayRotateFunctionNode extends AbstractCustomNode {
     protected getNodeTemplate (): string {
         const timesName: string = this.identifierNamesGenerator.generate();
         const whileFunctionName: string = this.identifierNamesGenerator.generate();
+        const preservedNames: string[] = [this.stringArrayName];
 
         let code: string = '';
 
@@ -98,20 +96,17 @@ export class StringArrayRotateFunctionNode extends AbstractCustomNode {
             code = `${whileFunctionName}(++${timesName})`;
         }
 
-        return JavaScriptObfuscator.obfuscate(
+        return this.obfuscateTemplate(
             this.customNodeFormatter.formatTemplate(StringArrayRotateFunctionTemplate(), {
                 code,
                 timesName,
+                whileFunctionName,
                 stringArrayName: this.stringArrayName,
-                stringArrayRotationAmount: NumberUtils.toHex(this.stringArrayRotationAmount),
-                whileFunctionName
+                stringArrayRotationAmount: NumberUtils.toHex(this.stringArrayRotationAmount)
             }),
             {
-                ...NO_ADDITIONAL_NODES_PRESET,
-                identifierNamesGenerator: this.options.identifierNamesGenerator,
-                identifiersDictionary: this.options.identifiersDictionary,
-                seed: this.randomGenerator.getRawSeed()
+                reservedNames: preservedNames
             }
-        ).getObfuscatedCode();
+        );
     }
 }

+ 22 - 11
src/generators/identifier-names-generators/AbstractIdentifierNamesGenerator.ts

@@ -45,7 +45,14 @@ export abstract class AbstractIdentifierNamesGenerator implements IIdentifierNam
      * @returns {Set<string>}
      */
     public getPreservedNames (): Set<string> {
-        return this.preservedNamesSet;
+        const lexicalScopesPreservedNames: Set<string>[] = Array.from(this.lexicalScopesPreservedNamesMap.values());
+        const preservedNames: string[] = Array.from(this.preservedNamesSet);
+
+        for (const lexicalScopePreservedNames of lexicalScopesPreservedNames) {
+            preservedNames.push(...lexicalScopePreservedNames);
+        }
+
+        return new Set(preservedNames);
     }
 
     /**
@@ -60,8 +67,6 @@ export abstract class AbstractIdentifierNamesGenerator implements IIdentifierNam
      * @param {TNodeWithLexicalScope} lexicalScopeNode
      */
     public preserveNameForLexicalScope (name: string, lexicalScopeNode: TNodeWithLexicalScope): void {
-        this.preservedNamesSet.add(name);
-
         const preservedNamesForLexicalScopeSet: Set<string> =
             this.lexicalScopesPreservedNamesMap.get(lexicalScopeNode) ?? new Set();
 
@@ -80,22 +85,28 @@ export abstract class AbstractIdentifierNamesGenerator implements IIdentifierNam
 
     /**
      * @param {string} name
-     * @param {TNodeWithLexicalScope} lexicalScopeNode
+     * @param {TNodeWithLexicalScope[]} lexicalScopeNodes
      * @returns {boolean}
      */
-    public isValidIdentifierNameInLexicalScope (name: string, lexicalScopeNode: TNodeWithLexicalScope): boolean {
-        if (!this.notReservedName(name)) {
+    public isValidIdentifierNameInLexicalScopes (name: string, lexicalScopeNodes: TNodeWithLexicalScope[]): boolean {
+        if (!this.isValidIdentifierName(name)) {
             return false;
         }
 
-        const preservedNamesForLexicalScopeSet: Set<string> | null =
-            this.lexicalScopesPreservedNamesMap.get(lexicalScopeNode) ?? null;
+        for (const lexicalScope of lexicalScopeNodes) {
+            const preservedNamesForLexicalScopeSet: Set<string> | null =
+                this.lexicalScopesPreservedNamesMap.get(lexicalScope) ?? null;
+
+            if (!preservedNamesForLexicalScopeSet) {
+                continue;
+            }
 
-        if (!preservedNamesForLexicalScopeSet) {
-            return true;
+            if (preservedNamesForLexicalScopeSet.has(name)) {
+                return false;
+            }
         }
 
-        return !preservedNamesForLexicalScopeSet.has(name);
+        return true;
     }
 
     /**

+ 39 - 21
src/generators/identifier-names-generators/DictionaryIdentifierNamesGenerator.ts

@@ -7,6 +7,7 @@ import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 
 import { AbstractIdentifierNamesGenerator } from './AbstractIdentifierNamesGenerator';
 import { TNodeWithLexicalScope } from '../../types/node/TNodeWithLexicalScope';
+import { NodeLexicalScopeUtils } from '../../node/NodeLexicalScopeUtils';
 
 @injectable()
 export class DictionaryIdentifierNamesGenerator extends AbstractIdentifierNamesGenerator {
@@ -69,26 +70,11 @@ export class DictionaryIdentifierNamesGenerator extends AbstractIdentifierNamesG
     }
 
     public generate (): string {
-        if (!this.identifierNamesSet.size) {
-            throw new Error('Too many identifiers in the code, add more words to identifiers dictionary');
-        }
+        const identifierName: string = this.generateNewDictionaryName();
 
-        const iteratorResult: IteratorResult<string> = this.identifiersIterator.next();
+        this.preserveName(identifierName);
 
-        if (!iteratorResult.done) {
-            const identifierName: string =iteratorResult.value;
-
-            if (!this.isValidIdentifierName(identifierName)) {
-                return this.generate();
-            }
-
-            return iteratorResult.value;
-        }
-
-        this.identifierNamesSet = new Set(this.getIncrementedIdentifierNames([...this.identifierNamesSet]));
-        this.identifiersIterator = this.identifierNamesSet.values();
-
-        return this.generate();
+        return identifierName;
     }
 
     /**
@@ -96,12 +82,18 @@ export class DictionaryIdentifierNamesGenerator extends AbstractIdentifierNamesG
      * @returns {string}
      */
     public generateForLexicalScope (lexicalScopeNode: TNodeWithLexicalScope): string {
-        const identifierName: string = this.generate();
+        const lexicalScopes: TNodeWithLexicalScope[] = [
+            lexicalScopeNode,
+            ...NodeLexicalScopeUtils.getLexicalScopes(lexicalScopeNode)
+        ];
+        const identifierName: string = this.generateNewDictionaryName();
 
-        if (!this.isValidIdentifierNameInLexicalScope(identifierName, lexicalScopeNode)) {
+        if (!this.isValidIdentifierNameInLexicalScopes(identifierName, lexicalScopes)) {
             return this.generateForLexicalScope(lexicalScopeNode);
         }
 
+        this.preserveNameForLexicalScope(identifierName, lexicalScopeNode);
+
         return identifierName;
     }
 
@@ -112,7 +104,7 @@ export class DictionaryIdentifierNamesGenerator extends AbstractIdentifierNamesG
         const prefix: string = this.options.identifiersPrefix ?
             `${this.options.identifiersPrefix}`
             : '';
-        const identifierName: string = this.generate();
+        const identifierName: string = this.generateNewDictionaryName();
         const identifierNameWithPrefix: string = `${prefix}${identifierName}`;
 
         if (!this.isValidIdentifierName(identifierNameWithPrefix)) {
@@ -124,6 +116,32 @@ export class DictionaryIdentifierNamesGenerator extends AbstractIdentifierNamesG
         return identifierNameWithPrefix;
     }
 
+    /**
+     * @returns {string}
+     */
+    private generateNewDictionaryName (): string {
+        if (!this.identifierNamesSet.size) {
+            throw new Error('Too many identifiers in the code, add more words to identifiers dictionary');
+        }
+
+        const iteratorResult: IteratorResult<string> = this.identifiersIterator.next();
+
+        if (!iteratorResult.done) {
+            const identifierName: string =iteratorResult.value;
+
+            if (!this.isValidIdentifierName(identifierName)) {
+                return this.generateNewDictionaryName();
+            }
+
+            return iteratorResult.value;
+        }
+
+        this.identifierNamesSet = new Set(this.getIncrementedIdentifierNames([...this.identifierNamesSet]));
+        this.identifiersIterator = this.identifierNamesSet.values();
+
+        return this.generateNewDictionaryName();
+    }
+
     /**
      * @param {string[]} identifierNames
      * @returns {string[]}

+ 43 - 6
src/generators/identifier-names-generators/MangledIdentifierNamesGenerator.ts

@@ -7,6 +7,7 @@ import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 
 import { AbstractIdentifierNamesGenerator } from './AbstractIdentifierNamesGenerator';
+import { NodeLexicalScopeUtils } from '../../node/NodeLexicalScopeUtils';
 
 @injectable()
 export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGenerator {
@@ -15,6 +16,11 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene
      */
     private static readonly initMangledNameCharacter: string = '9';
 
+    /**
+     * @type {Map<TNodeWithLexicalScope, string>}
+     */
+    private static readonly lastMangledNameInScopeMap: Map <TNodeWithLexicalScope, string> = new Map();
+
     /**
      * @type {string[]}
      */
@@ -56,6 +62,7 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene
         const identifierName: string = this.generateNewMangledName(this.previousMangledName);
 
         this.previousMangledName = identifierName;
+        this.preserveName(identifierName);
 
         return identifierName;
     }
@@ -66,13 +73,22 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene
      * @returns {string}
      */
     public generateForLexicalScope (lexicalScopeNode: TNodeWithLexicalScope, nameLength?: number): string {
-        const identifierName: string = this.generateNewMangledName(this.previousMangledName);
+        const lexicalScopes: TNodeWithLexicalScope[] = [
+            lexicalScopeNode,
+            ...NodeLexicalScopeUtils.getLexicalScopes(lexicalScopeNode)
+        ];
 
-        if (!this.isValidIdentifierNameInLexicalScope(identifierName, lexicalScopeNode)) {
-            return this.generateForLexicalScope(lexicalScopeNode, nameLength);
-        }
+        const lastMangledNameForScope: string = this.getLastMangledNameForScopes(lexicalScopes);
 
-        this.previousMangledName = identifierName;
+        let identifierName: string = lastMangledNameForScope;
+
+        do {
+            identifierName = this.generateNewMangledName(identifierName);
+        } while (!this.isValidIdentifierNameInLexicalScopes(identifierName, lexicalScopes));
+
+        MangledIdentifierNamesGenerator.lastMangledNameInScopeMap.set(lexicalScopeNode, identifierName);
+
+        this.preserveNameForLexicalScope(identifierName, lexicalScopeNode);
 
         return identifierName;
     }
@@ -85,9 +101,11 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene
         const prefix: string = this.options.identifiersPrefix ?
             `${this.options.identifiersPrefix}`
             : '';
-        const identifierName: string = this.generate(nameLength);
+        const identifierName: string = this.generateNewMangledName(this.previousMangledName);
         const identifierNameWithPrefix: string = `${prefix}${identifierName}`;
 
+        this.previousMangledName = identifierName;
+
         if (!this.isValidIdentifierName(identifierNameWithPrefix)) {
             return this.generateWithPrefix(nameLength);
         }
@@ -150,4 +168,23 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene
 
         return newMangledName;
     }
+
+    /**
+     * @param {TNodeWithLexicalScope[]} lexicalScopeNodes
+     * @returns {string}
+     */
+    private getLastMangledNameForScopes (lexicalScopeNodes: TNodeWithLexicalScope[]): string {
+        for (const lexicalScope of lexicalScopeNodes) {
+            const lastMangledName: string | null = MangledIdentifierNamesGenerator.lastMangledNameInScopeMap
+                .get(lexicalScope) ?? null;
+
+            if (!lastMangledName) {
+                continue;
+            }
+
+            return lastMangledName;
+        }
+
+        return MangledIdentifierNamesGenerator.initMangledNameCharacter;
+    }
 }

+ 2 - 2
src/interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator.ts

@@ -34,10 +34,10 @@ export interface IIdentifierNamesGenerator {
 
     /**
      * @param {string} identifierName
-     * @param {TNodeWithLexicalScope} lexicalScopeNode
+     * @param {TNodeWithLexicalScope[]} lexicalScopeNodes
      * @returns {boolean}
      */
-    isValidIdentifierNameInLexicalScope (identifierName: string, lexicalScopeNode: TNodeWithLexicalScope): boolean;
+    isValidIdentifierNameInLexicalScopes (identifierName: string, lexicalScopeNodes: TNodeWithLexicalScope[]): boolean;
 
     /**
      * @param {string} identifierName

+ 21 - 6
src/node-transformers/converting-transformers/object-expression-extractors/ObjectExpressionToVariableDeclarationExtractor.ts

@@ -19,6 +19,8 @@ import { NodeAppender } from '../../../node/NodeAppender';
 import { NodeGuards } from '../../../node/NodeGuards';
 import { NodeStatementUtils } from '../../../node/NodeStatementUtils';
 import { NodeUtils } from '../../../node/NodeUtils';
+import { TNodeWithLexicalScope } from '../../../types/node/TNodeWithLexicalScope';
+import { NodeLexicalScopeUtils } from '../../../node/NodeLexicalScopeUtils';
 
 @injectable()
 export class ObjectExpressionToVariableDeclarationExtractor implements IObjectExpressionExtractor {
@@ -74,12 +76,18 @@ export class ObjectExpressionToVariableDeclarationExtractor implements IObjectEx
         objectExpressionNode: ESTree.ObjectExpression,
         hostStatement: ESTree.Statement
     ): IObjectExpressionExtractorResult {
-        const properties: ESTree.Property[] = objectExpressionNode.properties;
+        const lexicalScopeNode: TNodeWithLexicalScope | null = NodeLexicalScopeUtils.getLexicalScope(hostStatement) ?? null;
 
-        const newObjectExpressionHostStatement: ESTree.VariableDeclaration = this.getObjectExpressionHostNode(properties);
-        const newObjectExpressionIdentifier: ESTree.Identifier = this.getObjectExpressionIdentifierNode(newObjectExpressionHostStatement);
-        const newObjectExpressionNode: ESTree.ObjectExpression = this.getObjectExpressionNode(newObjectExpressionHostStatement);
+        if (!lexicalScopeNode) {
+            throw new Error('Cannot find lexical scope node for the host statement node');
+        }
+
+        const properties: ESTree.Property[] = objectExpressionNode.properties;
 
+        const newObjectExpressionHostStatement: ESTree.VariableDeclaration = this.getObjectExpressionHostNode(
+            lexicalScopeNode,
+            properties
+        );
         const statementsToInsert: TStatement[] = [newObjectExpressionHostStatement];
         const hostNodeWithStatements: TNodeWithStatements = NodeStatementUtils.getScopeOfNode(hostStatement);
 
@@ -87,6 +95,9 @@ export class ObjectExpressionToVariableDeclarationExtractor implements IObjectEx
         NodeUtils.parentizeAst(newObjectExpressionHostStatement);
         NodeUtils.parentizeNode(newObjectExpressionHostStatement, hostNodeWithStatements);
 
+        const newObjectExpressionIdentifier: ESTree.Identifier = this.getObjectExpressionIdentifierNode(newObjectExpressionHostStatement);
+        const newObjectExpressionNode: ESTree.ObjectExpression = this.getObjectExpressionNode(newObjectExpressionHostStatement);
+
         return {
             nodeToReplace: newObjectExpressionIdentifier,
             objectExpressionHostStatement: newObjectExpressionHostStatement,
@@ -95,16 +106,20 @@ export class ObjectExpressionToVariableDeclarationExtractor implements IObjectEx
     }
 
     /**
+     * @param {TNodeWithLexicalScope} lexicalScopeNode
      * @param {Property[]} properties
      * @returns {VariableDeclaration}
      */
-    private getObjectExpressionHostNode (properties: ESTree.Property[]): ESTree.VariableDeclaration {
+    private getObjectExpressionHostNode (
+        lexicalScopeNode: TNodeWithLexicalScope,
+        properties: ESTree.Property[]
+    ): ESTree.VariableDeclaration {
         const variableDeclarationHostNodeCustomNode: ICustomNode<TInitialData<ObjectExpressionVariableDeclarationHostNode>> =
             this.objectExpressionKeysTransformerCustomNodeFactory(
                 ObjectExpressionKeysTransformerCustomNode.ObjectExpressionVariableDeclarationHostNode
             );
 
-        variableDeclarationHostNodeCustomNode.initialize(properties);
+        variableDeclarationHostNodeCustomNode.initialize(lexicalScopeNode, properties);
 
         const statementNode: TStatement = variableDeclarationHostNodeCustomNode.getNode()[0];
 

+ 0 - 9
src/node-transformers/preparing-transformers/VariablePreserveTransformer.ts

@@ -117,15 +117,6 @@ export class VariablePreserveTransformer extends AbstractNodeTransformer {
         identifierNode: ESTree.Identifier,
         parentNode: ESTree.Node
     ): void {
-        if (
-            !NodeGuards.parentNodeIsPropertyNode(identifierNode, parentNode)
-            && !NodeGuards.parentNodeIsMemberExpressionNode(identifierNode, parentNode)
-            && !NodeGuards.parentNodeIsMethodDefinitionNode(identifierNode, parentNode)
-            && !NodeGuards.isLabelIdentifierNode(identifierNode, parentNode)
-        ) {
-            return;
-        }
-
         for (const lexicalScope of this.enteredLexicalScopesStack) {
             this.identifierObfuscatingReplacer.preserveNameForLexicalScope(identifierNode, lexicalScope);
         }

+ 10 - 8
test/dev/dev.ts

@@ -7,21 +7,23 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
 
     let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
         `
-            function foo () {
-                const testA = 'abc';
-                const testB = 'abc';
-                const testC = 'abc';
-                const testD = 'abc';
+            function a (a, b) {
+               
+            }
+            
+            function b (b, c) {
+               
             }
         `,
         {
             ...NO_ADDITIONAL_NODES_PRESET,
             compact: false,
-            identifierNamesGenerator: 'dictionary',
-            identifiersDictionary: ['a', 'b', 'aa'],
+            identifierNamesGenerator: 'mangled',
             identifiersPrefix: 'a',
+            transformObjectKeys: true,
             stringArray: true,
-            stringArrayThreshold: 1
+            stringArrayThreshold: 1,
+            log: true
         }
     ).getObfuscatedCode();
 

+ 15 - 5
test/functional-tests/generators/identifier-names-generators/mangled-identifier-names-generator/MangledIdentifierNamesGenerator.spec.ts

@@ -78,6 +78,7 @@ describe('MangledIdentifierNamesGenerator', () => {
         describe('Variant #2: should not generate same prefixed name for identifier in code as prefixed name of string array', () => {
             describe('Variant #1: `renameGlobals` option is disabled', () => {
                 const stringArrayStorageRegExp: RegExp = /const aa *= *\['abc', *'last'];/;
+                const functionDeclarationIdentifierNameRegExp: RegExp = /function foo *\(\) *{/;
                 const lastVariableDeclarationIdentifierNameRegExp: RegExp = /const ac *= *ab\('0x1'\);/;
 
                 let obfuscatedCode: string;
@@ -98,18 +99,23 @@ describe('MangledIdentifierNamesGenerator', () => {
                     ).getObfuscatedCode();
                 });
 
-                it('Match #1: should generate correct identifier for string array', () => {
+                it('Match #1: should generate correct identifier name for string array', () => {
                     assert.match(obfuscatedCode, stringArrayStorageRegExp);
                 });
 
-                it('Match #2: should keep identifier name for last variable declaration', () => {
+                it('Match #2: should keep identifier name for function declaration', () => {
+                    assert.match(obfuscatedCode, functionDeclarationIdentifierNameRegExp);
+                });
+
+                it('Match #3: should keep identifier name for last variable declaration', () => {
                     assert.match(obfuscatedCode, lastVariableDeclarationIdentifierNameRegExp);
                 });
             });
 
             describe('Variant #2: `renameGlobals` option is enabled', () => {
                 const stringArrayStorageRegExp: RegExp = /const aa *= *\['abc', *'last'];/;
-                const lastVariableDeclarationIdentifierNameRegExp: RegExp = /const ae *= *ab\('0x1'\);/;
+                const functionDeclarationIdentifierNameRegExp: RegExp = /function ac *\(\) *{/;
+                const lastVariableDeclarationIdentifierNameRegExp: RegExp = /const ad *= *ab\('0x1'\);/;
 
                 let obfuscatedCode: string;
 
@@ -130,11 +136,15 @@ describe('MangledIdentifierNamesGenerator', () => {
                     ).getObfuscatedCode();
                 });
 
-                it('Match #1: should generate correct identifier for string array', () => {
+                it('Match #1: should generate correct identifier name for string array', () => {
                     assert.match(obfuscatedCode, stringArrayStorageRegExp);
                 });
 
-                it('Match #2: should keep identifier name for last variable declaration', () => {
+                it('Match #2: should generate correct identifier name for function declaration', () => {
+                    assert.match(obfuscatedCode, functionDeclarationIdentifierNameRegExp);
+                });
+
+                it('Match #3: should keep identifier name for last variable declaration', () => {
                     assert.match(obfuscatedCode, lastVariableDeclarationIdentifierNameRegExp);
                 });
             });

+ 3 - 1
test/functional-tests/generators/identifier-names-generators/mangled-identifier-names-generator/fixtures/string-array-storage-name-conflict-2.js

@@ -59,5 +59,7 @@ function foo () {
     const test32 = 'abc';
     const test33 = 'abc';
     const test34 = 'abc';
-    const test35 = 'last';
+    const test35 = 'abc';
+    const test36 = 'abc';
+    const test38 = 'last';
 }

+ 8 - 8
test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/class-declaration/ClassDeclaration.spec.ts

@@ -482,15 +482,15 @@ describe('ScopeIdentifiersTransformer ClassDeclaration identifiers', () => {
             });
         });
 
-        describe('Variant #3: already renamed identifiers shouldn\'t be renamed twice', () => {
-            const classDeclarationRegExp: RegExp = /class *b *{/;
-            const variableDeclarationsRegExp: RegExp = /let c, *d, *e, *f;/;
-            const classReferenceRegExp: RegExp = /new b\(\);/;
+        describe('Variant #3: preserved identifier names shouldn\'t be used as identifier names', () => {
+            const classDeclarationRegExp: RegExp = /class *f *{/;
+            const variableDeclarationsRegExp: RegExp = /let g, *h, *i, *j;/;
+            const classReferenceRegExp: RegExp = /new f\(\);/;
 
             let obfuscatedCode: string;
 
             before(() => {
-                const code: string = readFileAsString(__dirname + '/fixtures/prevent-renaming-of-renamed-identifiers.js');
+                const code: string = readFileAsString(__dirname + '/fixtures/prevent-using-of-preserved-identifiers.js');
 
                 obfuscatedCode = JavaScriptObfuscator.obfuscate(
                     code,
@@ -501,15 +501,15 @@ describe('ScopeIdentifiersTransformer ClassDeclaration identifiers', () => {
                 ).getObfuscatedCode();
             });
 
-            it('Match #1: shouldn\'t rename twice class declaration name', () => {
+            it('Match #1: shouldn\'t use preserved identifier name as class declaration name', () => {
                 assert.match(obfuscatedCode, classDeclarationRegExp);
             });
 
-            it('Match #2: should correctly rename variable declarations', () => {
+            it('Match #2: shouldn\'t use preserved identifier name as variable declarations', () => {
                 assert.match(obfuscatedCode, variableDeclarationsRegExp);
             });
 
-            it('Match #3: should correctly rename class reference identifier', () => {
+            it('Match #3: shouldn\'t use preserved identifier name as class reference identifier', () => {
                 assert.match(obfuscatedCode, classReferenceRegExp);
             });
         });

+ 0 - 0
test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/class-declaration/fixtures/prevent-renaming-of-renamed-identifiers.js → test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/class-declaration/fixtures/prevent-using-of-preserved-identifiers.js


+ 6 - 6
test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/function-declaration/FunctionDeclaration.spec.ts

@@ -143,15 +143,15 @@ describe('ScopeIdentifiersTransformer FunctionDeclaration identifiers', () => {
             });
         });
 
-        describe('Variant #5: already renamed identifiers shouldn\'t be renamed twice', () => {
+        describe('Variant #5: preserved identifier names shouldn\'t be used as identifier names', () => {
             describe('Variant #1', () => {
-                const functionDeclarationRegExp: RegExp = /function *a\(\) *{/;
-                const variableDeclarationsRegExp: RegExp = /let c, *d, *e, *f;/;
+                const functionDeclarationRegExp: RegExp = /function *e\(\) *{/;
+                const variableDeclarationsRegExp: RegExp = /let g, *h, *i, *j;/;
 
                 let obfuscatedCode: string;
 
                 before(() => {
-                    const code: string = readFileAsString(__dirname + '/fixtures/prevent-renaming-of-renamed-identifiers-1.js');
+                    const code: string = readFileAsString(__dirname + '/fixtures/prevent-using-of-preserved-identifiers-1.js');
 
                     obfuscatedCode = JavaScriptObfuscator.obfuscate(
                         code,
@@ -162,11 +162,11 @@ describe('ScopeIdentifiersTransformer FunctionDeclaration identifiers', () => {
                     ).getObfuscatedCode();
                 });
 
-                it('Match #1: shouldn\'t rename twice function declaration name', () => {
+                it('Match #1: shouldn\'t use preserved identifier name as function declaration name', () => {
                     assert.match(obfuscatedCode, functionDeclarationRegExp);
                 });
 
-                it('Match #2: should correctly rename variable declarations', () => {
+                it('Match #2: shouldn\'t use preserved identifier name as variable declarations', () => {
                     assert.match(obfuscatedCode, variableDeclarationsRegExp);
                 });
             });

+ 0 - 0
test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/function-declaration/fixtures/prevent-renaming-of-renamed-identifiers-1.js → test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/function-declaration/fixtures/prevent-using-of-preserved-identifiers-1.js


+ 18 - 18
test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/variable-declaration/VariableDeclaration.spec.ts

@@ -454,18 +454,18 @@ describe('ScopeIdentifiersTransformer VariableDeclaration identifiers', () => {
         });
     });
 
-    describe('Variant #13: already renamed identifiers shouldn\'t be renamed twice', () => {
+    describe('Variant #13: preserved identifier names shouldn\'t be used as identifier names', () => {
         describe('Variant #1', () => {
-            const variableDeclarationRegExp: RegExp = /var b *= *0x1;/;
-            const functionDeclarationRegExp1: RegExp = /function *c *\(\) *{}/;
-            const functionDeclarationRegExp2: RegExp = /function *d *\(\) *{}/;
-            const functionDeclarationRegExp3: RegExp = /function *e *\(\) *{}/;
-            const functionDeclarationRegExp4: RegExp = /function *f *\(\) *{}/;
+            const variableDeclarationRegExp: RegExp = /var f *= *0x1;/;
+            const functionDeclarationRegExp1: RegExp = /function *g *\(\) *{}/;
+            const functionDeclarationRegExp2: RegExp = /function *h *\(\) *{}/;
+            const functionDeclarationRegExp3: RegExp = /function *i *\(\) *{}/;
+            const functionDeclarationRegExp4: RegExp = /function *j *\(\) *{}/;
 
             let obfuscatedCode: string;
 
             before(() => {
-                const code: string = readFileAsString(__dirname + '/fixtures/prevent-renaming-of-renamed-identifiers-1.js');
+                const code: string = readFileAsString(__dirname + '/fixtures/prevent-using-of-preserved-identifiers-1.js');
 
                 obfuscatedCode = JavaScriptObfuscator.obfuscate(
                     code,
@@ -476,23 +476,23 @@ describe('ScopeIdentifiersTransformer VariableDeclaration identifiers', () => {
                 ).getObfuscatedCode();
             });
 
-            it('Match #1: shouldn\'t rename twice variable declaration name', () => {
+            it('Match #1: shouldn\'t use preserved identifier name as variable declaration name', () => {
                 assert.match(obfuscatedCode, variableDeclarationRegExp);
             });
 
-            it('Match #2: should correctly rename function declaration name', () => {
+            it('Match #2: shouldn\'t use preserved identifier name as function declaration name', () => {
                 assert.match(obfuscatedCode, functionDeclarationRegExp1);
             });
 
-            it('Match #3: should correctly rename function declaration name', () => {
+            it('Match #3: shouldn\'t use preserved identifier name as function declaration name', () => {
                 assert.match(obfuscatedCode, functionDeclarationRegExp2);
             });
 
-            it('Match #4: should correctly rename function declaration name', () => {
+            it('Match #4: shouldn\'t use preserved identifier name as function declaration name', () => {
                 assert.match(obfuscatedCode, functionDeclarationRegExp3);
             });
 
-            it('Match #5: should correctly rename function declaration name', () => {
+            it('Match #5: shouldn\'t use preserved identifier name as function declaration name', () => {
                 assert.match(obfuscatedCode, functionDeclarationRegExp4);
             });
         });
@@ -501,12 +501,12 @@ describe('ScopeIdentifiersTransformer VariableDeclaration identifiers', () => {
             const variableDeclarationRegExp1: RegExp = /var b *= *0x1;/;
             const variableDeclarationRegExp2: RegExp = /var c;/;
             const functionDeclarationRegExp: RegExp = /function *d *\(\) *{/;
-            const variableDeclarationRegExp3: RegExp = /var e *= *function *\(\) *{}/;
+            const variableDeclarationRegExp3: RegExp = /var f *= *function *\(\) *{}/;
 
             let obfuscatedCode: string;
 
             before(() => {
-                const code: string = readFileAsString(__dirname + '/fixtures/prevent-renaming-of-renamed-identifiers-2.js');
+                const code: string = readFileAsString(__dirname + '/fixtures/prevent-using-of-preserved-identifiers-2.js');
 
                 obfuscatedCode = JavaScriptObfuscator.obfuscate(
                     code,
@@ -517,19 +517,19 @@ describe('ScopeIdentifiersTransformer VariableDeclaration identifiers', () => {
                 ).getObfuscatedCode();
             });
 
-            it('Match #1: shouldn\'t rename twice variable declaration name', () => {
+            it('Match #1: shouldn\'t use preserved identifier name as variable declaration name', () => {
                 assert.match(obfuscatedCode, variableDeclarationRegExp1);
             });
 
-            it('Match #2: shouldn\'t rename twice variable declaration name', () => {
+            it('Match #2: shouldn\'t use preserved identifier name as variable declaration name', () => {
                 assert.match(obfuscatedCode, variableDeclarationRegExp2);
             });
 
-            it('Match #3: should correctly rename function declaration name', () => {
+            it('Match #3: shouldn\'t use preserved identifier name as function declaration name', () => {
                 assert.match(obfuscatedCode, functionDeclarationRegExp);
             });
 
-            it('Match #4: should correctly rename variable declaration name', () => {
+            it('Match #4: shouldn\'t use preserved identifier name as variable declaration name', () => {
                 assert.match(obfuscatedCode, variableDeclarationRegExp3);
             });
         });

+ 0 - 0
test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/variable-declaration/fixtures/prevent-renaming-of-renamed-identifiers-1.js → test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/variable-declaration/fixtures/prevent-using-of-preserved-identifiers-1.js


+ 0 - 0
test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/variable-declaration/fixtures/prevent-renaming-of-renamed-identifiers-2.js → test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/variable-declaration/fixtures/prevent-using-of-preserved-identifiers-2.js


+ 2 - 2
test/functional-tests/node-transformers/preparing-transformers/variable-preserve-transformer/VariablePreserveTransformer.spec.ts

@@ -98,8 +98,8 @@ describe('VariablePreserveTransformer', () => {
         });
 
         describe('Variant #2: `renameGlobals` option is enabled', () => {
-            const transformObjectKeysNameRegExp: RegExp = /const c *= *{};/;
-            const identifierNameRegExp: RegExp = /const d *= *c;/;
+            const transformObjectKeysNameRegExp: RegExp = /const b *= *{};/;
+            const identifierNameRegExp: RegExp = /const c *= *b;/;
 
             let obfuscatedCode: string;
 

部分文件因为文件数量过多而无法显示