From 6864328c0334e03513a449f703f8b0482e765510 Mon Sep 17 00:00:00 2001 From: Timofey Kachalov Date: Wed, 28 Jan 2026 01:04:40 +0400 Subject: [PATCH] Fix identifiers generation (#1378) --- ...ctExpressionVariableDeclarationHostNode.ts | 4 +- .../AbstractIdentifierNamesGenerator.ts | 6 ++ .../DictionaryIdentifierNamesGenerator.ts | 37 +++++++--- .../HexadecimalIdentifierNamesGenerator.ts | 67 +++++++++++++------ .../MangledIdentifierNamesGenerator.ts | 46 ++++++++----- .../IIdentifierNamesGenerator.ts | 12 ++++ 6 files changed, 126 insertions(+), 46 deletions(-) diff --git a/src/custom-nodes/object-expression-keys-transformer-nodes/ObjectExpressionVariableDeclarationHostNode.ts b/src/custom-nodes/object-expression-keys-transformer-nodes/ObjectExpressionVariableDeclarationHostNode.ts index 3bb223c9..dfdd1a15 100644 --- a/src/custom-nodes/object-expression-keys-transformer-nodes/ObjectExpressionVariableDeclarationHostNode.ts +++ b/src/custom-nodes/object-expression-keys-transformer-nodes/ObjectExpressionVariableDeclarationHostNode.ts @@ -58,8 +58,10 @@ export class ObjectExpressionVariableDeclarationHostNode extends AbstractCustomN * @returns {TStatement[]} */ protected getNodeStructure(): TStatement[] { + // Use generateForGlobalScopeWithAllScopesValidation when in global scope + // to avoid shadowing variables in inner lexical scopes const variableDeclarationName: string = NodeGuards.isProgramNode(this.lexicalScopeNode) - ? this.identifierNamesGenerator.generateForGlobalScope() + ? this.identifierNamesGenerator.generateForGlobalScopeWithAllScopesValidation() : this.identifierNamesGenerator.generateForLexicalScope(this.lexicalScopeNode); const structure: TStatement = NodeFactory.variableDeclarationNode( diff --git a/src/generators/identifier-names-generators/AbstractIdentifierNamesGenerator.ts b/src/generators/identifier-names-generators/AbstractIdentifierNamesGenerator.ts index 161f1cc6..a9655ad1 100644 --- a/src/generators/identifier-names-generators/AbstractIdentifierNamesGenerator.ts +++ b/src/generators/identifier-names-generators/AbstractIdentifierNamesGenerator.ts @@ -150,6 +150,12 @@ export abstract class AbstractIdentifierNamesGenerator implements IIdentifierNam */ public abstract generateForGlobalScope(nameLength?: number): string; + /** + * @param {number} nameLength + * @returns {string} + */ + public abstract generateForGlobalScopeWithAllScopesValidation(nameLength?: number): string; + /** * @param {TNodeWithLexicalScope} lexicalScopeNode * @param {number} nameLength diff --git a/src/generators/identifier-names-generators/DictionaryIdentifierNamesGenerator.ts b/src/generators/identifier-names-generators/DictionaryIdentifierNamesGenerator.ts index 7d8b75a7..4db36fcc 100644 --- a/src/generators/identifier-names-generators/DictionaryIdentifierNamesGenerator.ts +++ b/src/generators/identifier-names-generators/DictionaryIdentifierNamesGenerator.ts @@ -81,18 +81,14 @@ export class DictionaryIdentifierNamesGenerator extends AbstractIdentifierNamesG * @returns {string} */ public generateForGlobalScope(): string { - const prefix: string = this.options.identifiersPrefix ? `${this.options.identifiersPrefix}` : ''; + return this.generateForGlobalScopeInternal((name) => this.isValidIdentifierName(name)); + } - const identifierName: string = this.generateNewDictionaryName((newIdentifierName: string) => { - const identifierNameWithPrefix: string = `${prefix}${newIdentifierName}`; - - return this.isValidIdentifierNameInAllScopes(identifierNameWithPrefix); - }); - const identifierNameWithPrefix = `${prefix}${identifierName}`; - - this.preserveName(identifierNameWithPrefix); - - return identifierNameWithPrefix; + /** + * @returns {string} + */ + public generateForGlobalScopeWithAllScopesValidation(): string { + return this.generateForGlobalScopeInternal((name) => this.isValidIdentifierNameInAllScopes(name)); } /** @@ -121,6 +117,25 @@ export class DictionaryIdentifierNamesGenerator extends AbstractIdentifierNamesG return this.generateNewDictionaryName(); } + /** + * @param {(name: string) => boolean} validationFn + * @returns {string} + */ + private generateForGlobalScopeInternal(validationFn: (name: string) => boolean): string { + const prefix: string = this.options.identifiersPrefix ? `${this.options.identifiersPrefix}` : ''; + + const identifierName: string = this.generateNewDictionaryName((newIdentifierName: string) => { + const identifierNameWithPrefix: string = `${prefix}${newIdentifierName}`; + + return validationFn(identifierNameWithPrefix); + }); + const identifierNameWithPrefix = `${prefix}${identifierName}`; + + this.preserveName(identifierNameWithPrefix); + + return identifierNameWithPrefix; + } + /** * @param {(newIdentifierName: string) => boolean} validationFunction * @returns {string} diff --git a/src/generators/identifier-names-generators/HexadecimalIdentifierNamesGenerator.ts b/src/generators/identifier-names-generators/HexadecimalIdentifierNamesGenerator.ts index c6f87933..59df523e 100644 --- a/src/generators/identifier-names-generators/HexadecimalIdentifierNamesGenerator.ts +++ b/src/generators/identifier-names-generators/HexadecimalIdentifierNamesGenerator.ts @@ -33,23 +33,7 @@ export class HexadecimalIdentifierNamesGenerator extends AbstractIdentifierNames * @returns {string} */ public generateNext(nameLength?: number): string { - const rangeMinInteger: number = 10000; - const rangeMaxInteger: number = 99_999_999; - const randomInteger: number = this.randomGenerator.getRandomInteger(rangeMinInteger, rangeMaxInteger); - const hexadecimalNumber: string = NumberUtils.toHex(randomInteger); - const prefixLength: number = Utils.hexadecimalPrefix.length; - const baseNameLength: number = - (nameLength ?? HexadecimalIdentifierNamesGenerator.baseIdentifierNameLength) + prefixLength; - const baseIdentifierName: string = hexadecimalNumber.slice(0, baseNameLength); - const identifierName: string = `_${baseIdentifierName}`; - - if (!this.isValidIdentifierNameInAllScopes(identifierName)) { - return this.generateNext(nameLength); - } - - this.preserveName(identifierName); - - return identifierName; + return this.generateNextName(nameLength, (name) => this.isValidIdentifierName(name)); } /** @@ -57,9 +41,15 @@ export class HexadecimalIdentifierNamesGenerator extends AbstractIdentifierNames * @returns {string} */ public generateForGlobalScope(nameLength?: number): string { - const identifierName: string = this.generateNext(nameLength); + return this.generateForGlobalScopeInternal(nameLength, (name) => this.isValidIdentifierName(name)); + } - return `${this.options.identifiersPrefix}${identifierName}`.replace('__', '_'); + /** + * @param {number} nameLength + * @returns {string} + */ + public generateForGlobalScopeWithAllScopesValidation(nameLength?: number): string { + return this.generateForGlobalScopeInternal(nameLength, (name) => this.isValidIdentifierNameInAllScopes(name)); } /** @@ -79,4 +69,43 @@ export class HexadecimalIdentifierNamesGenerator extends AbstractIdentifierNames public generateForLabel(label: string, nameLength?: number): string { return this.generateNext(nameLength); } + + /** + * @param {number} nameLength + * @param {(name: string) => boolean} validationFn + * @returns {string} + */ + private generateForGlobalScopeInternal( + nameLength: number | undefined, + validationFn: (name: string) => boolean + ): string { + const identifierName: string = this.generateNextName(nameLength, validationFn); + + return `${this.options.identifiersPrefix}${identifierName}`.replace('__', '_'); + } + + /** + * @param {number} nameLength + * @param {(name: string) => boolean} validationFn + * @returns {string} + */ + private generateNextName(nameLength: number | undefined, validationFn: (name: string) => boolean): string { + const rangeMinInteger: number = 10000; + const rangeMaxInteger: number = 99_999_999; + const randomInteger: number = this.randomGenerator.getRandomInteger(rangeMinInteger, rangeMaxInteger); + const hexadecimalNumber: string = NumberUtils.toHex(randomInteger); + const prefixLength: number = Utils.hexadecimalPrefix.length; + const baseNameLength: number = + (nameLength ?? HexadecimalIdentifierNamesGenerator.baseIdentifierNameLength) + prefixLength; + const baseIdentifierName: string = hexadecimalNumber.slice(0, baseNameLength); + const identifierName: string = `_${baseIdentifierName}`; + + if (!validationFn(identifierName)) { + return this.generateNextName(nameLength, validationFn); + } + + this.preserveName(identifierName); + + return identifierName; + } } diff --git a/src/generators/identifier-names-generators/MangledIdentifierNamesGenerator.ts b/src/generators/identifier-names-generators/MangledIdentifierNamesGenerator.ts index c5ca35c8..96d6c337 100644 --- a/src/generators/identifier-names-generators/MangledIdentifierNamesGenerator.ts +++ b/src/generators/identifier-names-generators/MangledIdentifierNamesGenerator.ts @@ -96,22 +96,15 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene * @returns {string} */ public generateForGlobalScope(nameLength?: number): string { - const prefix: string = this.options.identifiersPrefix ? `${this.options.identifiersPrefix}` : ''; + return this.generateForGlobalScopeInternal((name) => this.isValidIdentifierName(name)); + } - const identifierName: string = this.generateNewMangledName( - this.lastMangledName, - (newIdentifierName: string) => { - const identifierNameWithPrefix: string = `${prefix}${newIdentifierName}`; - - return this.isValidIdentifierNameInAllScopes(identifierNameWithPrefix); - } - ); - const identifierNameWithPrefix: string = `${prefix}${identifierName}`; - - this.updatePreviousMangledName(identifierName); - this.preserveName(identifierNameWithPrefix); - - return identifierNameWithPrefix; + /** + * @param {number} nameLength + * @returns {string} + */ + public generateForGlobalScopeWithAllScopesValidation(nameLength?: number): string { + return this.generateForGlobalScopeInternal((name) => this.isValidIdentifierNameInAllScopes(name)); } /** @@ -299,6 +292,29 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene return identifierName; } + /** + * @param {(name: string) => boolean} validationFn + * @returns {string} + */ + private generateForGlobalScopeInternal(validationFn: (name: string) => boolean): string { + const prefix: string = this.options.identifiersPrefix ? `${this.options.identifiersPrefix}` : ''; + + const identifierName: string = this.generateNewMangledName( + this.lastMangledName, + (newIdentifierName: string) => { + const identifierNameWithPrefix: string = `${prefix}${newIdentifierName}`; + + return validationFn(identifierNameWithPrefix); + } + ); + const identifierNameWithPrefix: string = `${prefix}${identifierName}`; + + this.updatePreviousMangledName(identifierName); + this.preserveName(identifierNameWithPrefix); + + return identifierNameWithPrefix; + } + /** * @param {TNodeWithLexicalScope[]} lexicalScopeNodes * @returns {string} diff --git a/src/interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator.ts b/src/interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator.ts index a5164d74..11dad39e 100644 --- a/src/interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator.ts +++ b/src/interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator.ts @@ -47,6 +47,18 @@ export interface IIdentifierNamesGenerator { */ isValidIdentifierNameInLexicalScopes(identifierName: string, lexicalScopeNodes: TNodeWithLexicalScope[]): boolean; + /** + * @param {string} identifierName + * @returns {boolean} + */ + isValidIdentifierNameInAllScopes(identifierName: string): boolean; + + /** + * @param {number} nameLength + * @returns {string} + */ + generateForGlobalScopeWithAllScopesValidation(nameLength?: number): string; + /** * @param {string} identifierName */