MangledIdentifierNamesGenerator.ts 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. import { inject, injectable } from 'inversify';
  2. import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
  3. import { TNodeWithLexicalScope } from '../../types/node/TNodeWithLexicalScope';
  4. import { IOptions } from '../../interfaces/options/IOptions';
  5. import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
  6. import { numbersString } from '../../constants/NumbersString';
  7. import { alphabetString } from '../../constants/AlphabetString';
  8. import { alphabetStringUppercase } from '../../constants/AlphabetStringUppercase';
  9. import { AbstractIdentifierNamesGenerator } from './AbstractIdentifierNamesGenerator';
  10. import { NodeLexicalScopeUtils } from '../../node/NodeLexicalScopeUtils';
  11. @injectable()
  12. export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGenerator {
  13. /**
  14. * @type {string}
  15. */
  16. private static readonly initMangledNameCharacter: string = '9';
  17. /**
  18. * @type {WeakMap<TNodeWithLexicalScope, string>}
  19. */
  20. private static readonly lastMangledNameInScopeMap: WeakMap <TNodeWithLexicalScope, string> = new WeakMap();
  21. /**
  22. * Reserved JS words with length of 2-4 symbols that can be possible generated with this replacer
  23. *
  24. * @type {Set<string>}
  25. */
  26. private static readonly reservedNamesSet: Set<string> = new Set([
  27. 'byte', 'case', 'char', 'do', 'else', 'enum', 'eval', 'for', 'goto',
  28. 'if', 'in', 'int', 'let', 'long', 'new', 'null', 'this', 'true', 'try',
  29. 'var', 'void', 'with'
  30. ]);
  31. /**
  32. * @type {string[]}
  33. */
  34. protected nameSequence: string[] = `${numbersString}${alphabetString}${alphabetStringUppercase}`.split('');
  35. /**
  36. * @type {string}
  37. */
  38. private previousMangledName: string = MangledIdentifierNamesGenerator.initMangledNameCharacter;
  39. /**
  40. * @param {IRandomGenerator} randomGenerator
  41. * @param {IOptions} options
  42. */
  43. public constructor (
  44. @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
  45. @inject(ServiceIdentifiers.IOptions) options: IOptions
  46. ) {
  47. super(randomGenerator, options);
  48. }
  49. /**
  50. * We can only ignore limited nameLength, it has no sense here
  51. * @param {number} nameLength
  52. * @returns {string}
  53. */
  54. public generateNext (nameLength?: number): string {
  55. const identifierName: string = this.generateNewMangledName(this.previousMangledName);
  56. this.previousMangledName = identifierName;
  57. this.preserveName(identifierName);
  58. return identifierName;
  59. }
  60. /**
  61. * @param {number} nameLength
  62. * @returns {string}
  63. */
  64. public generateForGlobalScope (nameLength?: number): string {
  65. const prefix: string = this.options.identifiersPrefix ?
  66. `${this.options.identifiersPrefix}`
  67. : '';
  68. const identifierName: string = this.generateNewMangledName(this.previousMangledName);
  69. const identifierNameWithPrefix: string = `${prefix}${identifierName}`;
  70. this.previousMangledName = identifierName;
  71. if (!this.isValidIdentifierName(identifierNameWithPrefix)) {
  72. return this.generateForGlobalScope(nameLength);
  73. }
  74. this.preserveName(identifierNameWithPrefix);
  75. return identifierNameWithPrefix;
  76. }
  77. /**
  78. * @param {TNodeWithLexicalScope} lexicalScopeNode
  79. * @param {number} nameLength
  80. * @returns {string}
  81. */
  82. public generateForLexicalScope (lexicalScopeNode: TNodeWithLexicalScope, nameLength?: number): string {
  83. const lexicalScopes: TNodeWithLexicalScope[] = [
  84. lexicalScopeNode,
  85. ...NodeLexicalScopeUtils.getLexicalScopes(lexicalScopeNode)
  86. ];
  87. const lastMangledNameForScope: string = this.getLastMangledNameForScopes(lexicalScopes);
  88. let identifierName: string = lastMangledNameForScope;
  89. do {
  90. identifierName = this.generateNewMangledName(identifierName);
  91. } while (!this.isValidIdentifierNameInLexicalScopes(identifierName, lexicalScopes));
  92. MangledIdentifierNamesGenerator.lastMangledNameInScopeMap.set(lexicalScopeNode, identifierName);
  93. this.preserveNameForLexicalScope(identifierName, lexicalScopeNode);
  94. return identifierName;
  95. }
  96. /**
  97. * @param {string} mangledName
  98. * @returns {boolean}
  99. */
  100. public isValidIdentifierName (mangledName: string): boolean {
  101. return super.isValidIdentifierName(mangledName)
  102. && !MangledIdentifierNamesGenerator.reservedNamesSet.has(mangledName);
  103. }
  104. /**
  105. * @param {string} previousMangledName
  106. * @returns {string}
  107. */
  108. private generateNewMangledName (previousMangledName: string): string {
  109. const generateNewMangledName: (name: string) => string = (name: string): string => {
  110. const nameSequence: string[] = this.nameSequence;
  111. const nameSequenceLength: number = nameSequence.length;
  112. const nameLength: number = name.length;
  113. const zeroSequence: (num: number) => string = (num: number): string => {
  114. return '0'.repeat(num);
  115. };
  116. let index: number = nameLength - 1;
  117. do {
  118. const character: string = name[index];
  119. const indexInSequence: number = nameSequence.indexOf(character);
  120. const lastNameSequenceIndex: number = nameSequenceLength - 1;
  121. if (indexInSequence !== lastNameSequenceIndex) {
  122. const previousNamePart: string = name.substring(0, index);
  123. const nextCharacter: string = nameSequence[indexInSequence + 1];
  124. const zeroSequenceLength: number = nameLength - (index + 1);
  125. const zeroSequenceCharacters: string = zeroSequence(zeroSequenceLength);
  126. return previousNamePart + nextCharacter + zeroSequenceCharacters;
  127. }
  128. --index;
  129. } while (index >= 0);
  130. return `a${zeroSequence(nameLength)}`;
  131. };
  132. let newMangledName: string = generateNewMangledName(previousMangledName);
  133. if (!this.isValidIdentifierName(newMangledName)) {
  134. newMangledName = this.generateNewMangledName(newMangledName);
  135. }
  136. return newMangledName;
  137. }
  138. /**
  139. * @param {TNodeWithLexicalScope[]} lexicalScopeNodes
  140. * @returns {string}
  141. */
  142. private getLastMangledNameForScopes (lexicalScopeNodes: TNodeWithLexicalScope[]): string {
  143. for (const lexicalScope of lexicalScopeNodes) {
  144. const lastMangledName: string | null = MangledIdentifierNamesGenerator.lastMangledNameInScopeMap
  145. .get(lexicalScope) ?? null;
  146. if (!lastMangledName) {
  147. continue;
  148. }
  149. return lastMangledName;
  150. }
  151. return MangledIdentifierNamesGenerator.initMangledNameCharacter;
  152. }
  153. }