|
@@ -0,0 +1,116 @@
|
|
|
+import { inject, injectable } from 'inversify';
|
|
|
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
|
|
|
+
|
|
|
+import { IOptions } from '../../interfaces/options/IOptions';
|
|
|
+import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
|
|
|
+
|
|
|
+import { AbstractIdentifierNameGenerator } from './AbstractIdentifierNameGenerator';
|
|
|
+
|
|
|
+@injectable()
|
|
|
+export class MangledIdentifierNameGenerator extends AbstractIdentifierNameGenerator {
|
|
|
+ /**
|
|
|
+ * @type {string}
|
|
|
+ */
|
|
|
+ private static initMangledNameCharacter: string = '9';
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @type {string[]}
|
|
|
+ */
|
|
|
+ private static nameSequence: string[] = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Reserved JS words with length of 2-4 symbols that can be possible generated with this replacer
|
|
|
+ *
|
|
|
+ * @type {string[]}
|
|
|
+ */
|
|
|
+ private static reservedNames: string[] = [
|
|
|
+ 'byte', 'case', 'char', 'do', 'else', 'enum', 'eval', 'for', 'goto',
|
|
|
+ 'if', 'in', 'int', 'let', 'long', 'new', 'null', 'this', 'true', 'try',
|
|
|
+ 'var', 'void', 'with'
|
|
|
+ ];
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @type {string}
|
|
|
+ */
|
|
|
+ private previousMangledName: string = MangledIdentifierNameGenerator.initMangledNameCharacter;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param {IRandomGenerator} randomGenerator
|
|
|
+ * @param {IOptions} options
|
|
|
+ */
|
|
|
+ constructor (
|
|
|
+ @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
|
|
|
+ @inject(ServiceIdentifiers.IOptions) options: IOptions
|
|
|
+ ) {
|
|
|
+ super(randomGenerator, options);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param {string} previousMangledName
|
|
|
+ * @returns {string}
|
|
|
+ */
|
|
|
+ private static generateNewMangledName (previousMangledName: string): string {
|
|
|
+ const generateNewMangledName: (name: string) => string = (name: string): string => {
|
|
|
+ const nameSequence: string[] = MangledIdentifierNameGenerator.nameSequence;
|
|
|
+ const zeroSequenceCache: string[] = [];
|
|
|
+
|
|
|
+ const zeroSequence: (num: number) => string = (num: number): string => {
|
|
|
+ let result: string = zeroSequenceCache[num];
|
|
|
+
|
|
|
+ if (result !== undefined) {
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ result = '0'.repeat(num);
|
|
|
+ zeroSequenceCache[num] = result;
|
|
|
+
|
|
|
+ return result;
|
|
|
+ };
|
|
|
+
|
|
|
+ let cur: number = name.length - 1;
|
|
|
+
|
|
|
+ do {
|
|
|
+ let character: string, index: number;
|
|
|
+
|
|
|
+ character = name.charAt(cur);
|
|
|
+ index = nameSequence.indexOf(character);
|
|
|
+
|
|
|
+ if (index !== (nameSequence.length - 1)) {
|
|
|
+ return name.substring(0, cur) + nameSequence[index + 1] + zeroSequence(name.length - (cur + 1));
|
|
|
+ }
|
|
|
+
|
|
|
+ --cur;
|
|
|
+ } while (cur >= 0);
|
|
|
+
|
|
|
+ return `a${zeroSequence(name.length)}`;
|
|
|
+ };
|
|
|
+
|
|
|
+ let newMangledName: string = generateNewMangledName(previousMangledName);
|
|
|
+
|
|
|
+ if (!MangledIdentifierNameGenerator.validateMangledName(newMangledName)) {
|
|
|
+ newMangledName = MangledIdentifierNameGenerator.generateNewMangledName(newMangledName);
|
|
|
+ }
|
|
|
+
|
|
|
+ return newMangledName;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param {string} mangledName
|
|
|
+ * @returns {boolean}
|
|
|
+ */
|
|
|
+ private static validateMangledName (mangledName: string): boolean {
|
|
|
+ return !MangledIdentifierNameGenerator.reservedNames.includes(mangledName);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param {number} length
|
|
|
+ * @returns {string}
|
|
|
+ */
|
|
|
+ public generate (length: number): string {
|
|
|
+ const newName: string = MangledIdentifierNameGenerator.generateNewMangledName(this.previousMangledName);
|
|
|
+
|
|
|
+ this.previousMangledName = newName;
|
|
|
+
|
|
|
+ return newName;
|
|
|
+ }
|
|
|
+}
|