From f7588409fcfdf2ddf4f3b4e958f5223338126da5 Mon Sep 17 00:00:00 2001 From: sanex3339 Date: Tue, 16 Dec 2025 21:09:53 +0400 Subject: [PATCH] Optimize performance --- .../BlockStatementControlFlowTransformer.ts | 16 +++++--- .../AbstractControlFlowReplacer.ts | 6 +-- .../TemplateLiteralTransformer.ts | 41 +++++++++++-------- .../BasePropertiesExtractor.ts | 4 +- ...StringArrayScopeCallsWrapperTransformer.ts | 7 ++-- .../StringArrayStorage.ts | 34 +++++++-------- src/utils/ArrayUtils.ts | 21 +++++----- 7 files changed, 68 insertions(+), 61 deletions(-) diff --git a/src/node-transformers/control-flow-transformers/BlockStatementControlFlowTransformer.ts b/src/node-transformers/control-flow-transformers/BlockStatementControlFlowTransformer.ts index 54a9356c..ca7dff71 100644 --- a/src/node-transformers/control-flow-transformers/BlockStatementControlFlowTransformer.ts +++ b/src/node-transformers/control-flow-transformers/BlockStatementControlFlowTransformer.ts @@ -77,6 +77,11 @@ export class BlockStatementControlFlowTransformer extends AbstractNodeTransforme * @returns {boolean} */ private static canTransformBlockStatementNode(blockStatementNode: ESTree.BlockStatement): boolean { + // Early short-circuit: check length first to avoid expensive traversal + if (blockStatementNode.body.length <= 4) { + return false; + } + let canTransform: boolean = true; estraverse.traverse(blockStatementNode, { @@ -91,10 +96,6 @@ export class BlockStatementControlFlowTransformer extends AbstractNodeTransforme } }); - if (blockStatementNode.body.length <= 4) { - canTransform = false; - } - return canTransform; } @@ -138,8 +139,11 @@ export class BlockStatementControlFlowTransformer extends AbstractNodeTransforme const blockStatementBody: ESTree.Statement[] = blockStatementNode.body; const originalKeys: number[] = this.arrayUtils.createWithRange(blockStatementBody.length); const shuffledKeys: number[] = this.arrayUtils.shuffle(originalKeys); - const originalKeysIndexesInShuffledArray: number[] = originalKeys.map((key: number) => - shuffledKeys.indexOf(key) + const shuffledKeyToIndex: Map = new Map( + shuffledKeys.map((key: number, index: number) => [key, index]) + ); + const originalKeysIndexesInShuffledArray: number[] = originalKeys.map( + (key: number) => shuffledKeyToIndex.get(key)! ); const blockStatementControlFlowFlatteningCustomNode: ICustomNode< TInitialData diff --git a/src/node-transformers/control-flow-transformers/control-flow-replacers/AbstractControlFlowReplacer.ts b/src/node-transformers/control-flow-transformers/control-flow-replacers/AbstractControlFlowReplacer.ts index e26e2fee..da1a1ca2 100644 --- a/src/node-transformers/control-flow-transformers/control-flow-replacers/AbstractControlFlowReplacer.ts +++ b/src/node-transformers/control-flow-transformers/control-flow-replacers/AbstractControlFlowReplacer.ts @@ -68,10 +68,10 @@ export abstract class AbstractControlFlowReplacer implements IControlFlowReplace * @returns {string} */ public generateStorageKey(controlFlowStorage: IControlFlowStorage): string { - const key: string = this.randomGenerator.getRandomString(5); + let key: string = this.randomGenerator.getRandomString(5); - if (controlFlowStorage.has(key)) { - return this.generateStorageKey(controlFlowStorage); + while (controlFlowStorage.has(key)) { + key = this.randomGenerator.getRandomString(5); } return key; diff --git a/src/node-transformers/converting-transformers/TemplateLiteralTransformer.ts b/src/node-transformers/converting-transformers/TemplateLiteralTransformer.ts index ae684a59..9de8392a 100644 --- a/src/node-transformers/converting-transformers/TemplateLiteralTransformer.ts +++ b/src/node-transformers/converting-transformers/TemplateLiteralTransformer.ts @@ -83,53 +83,60 @@ export class TemplateLiteralTransformer extends AbstractNodeTransformer { ): ESTree.Expression { const templateLiteralExpressions: ESTree.Expression[] = templateLiteralNode.expressions; - let nodes: ESTree.Expression[] = []; + const nodes: ESTree.Expression[] = []; + + const quasis: ESTree.TemplateElement[] = templateLiteralNode.quasis; + const quasisLength: number = quasis.length; + + for (let i: number = 0; i < quasisLength; i++) { + const templateElement: ESTree.TemplateElement = quasis[i]; - templateLiteralNode.quasis.forEach((templateElement: ESTree.TemplateElement) => { if (templateElement.value.cooked === undefined || templateElement.value.cooked === null) { - return; + continue; } nodes.push(NodeFactory.literalNode(templateElement.value.cooked)); - const expression: ESTree.Expression | undefined = templateLiteralExpressions.shift(); + const expression: ESTree.Expression | undefined = templateLiteralExpressions[i]; if (!expression) { - return; + continue; } nodes.push(expression); - }); + } - nodes = nodes.filter((node: ESTree.Literal | ESTree.Expression) => { + const filteredNodes: ESTree.Expression[] = nodes.filter((node: ESTree.Literal | ESTree.Expression) => { return !(NodeGuards.isLiteralNode(node) && node.value === ''); }); // since `+` is left-to-right associative // ensure the first node is a string if first/second isn't if ( - !TemplateLiteralTransformer.isLiteralNodeWithStringValue(nodes[0]) && - !TemplateLiteralTransformer.isLiteralNodeWithStringValue(nodes[1]) + !TemplateLiteralTransformer.isLiteralNodeWithStringValue(filteredNodes[0]) && + !TemplateLiteralTransformer.isLiteralNodeWithStringValue(filteredNodes[1]) ) { - nodes.unshift(NodeFactory.literalNode('')); + filteredNodes.unshift(NodeFactory.literalNode('')); } let transformedNode: ESTree.Node; - if (nodes.length > 1) { + if (filteredNodes.length > 1) { let root: ESTree.BinaryExpression = NodeFactory.binaryExpressionNode( '+', - nodes.shift(), - nodes.shift() + filteredNodes[0], + filteredNodes[1] ); - nodes.forEach((node: ESTree.Literal | ESTree.Expression) => { - root = NodeFactory.binaryExpressionNode('+', root, node); - }); + // Start from index 2 since we already used 0 and 1 + const filteredNodesLength: number = filteredNodes.length; + for (let i: number = 2; i < filteredNodesLength; i++) { + root = NodeFactory.binaryExpressionNode('+', root, filteredNodes[i]); + } transformedNode = root; } else { - transformedNode = nodes[0]; + transformedNode = filteredNodes[0]; } NodeUtils.parentizeAst(transformedNode); diff --git a/src/node-transformers/converting-transformers/object-expression-extractors/BasePropertiesExtractor.ts b/src/node-transformers/converting-transformers/object-expression-extractors/BasePropertiesExtractor.ts index 4eea6ae2..b4c90440 100644 --- a/src/node-transformers/converting-transformers/object-expression-extractors/BasePropertiesExtractor.ts +++ b/src/node-transformers/converting-transformers/object-expression-extractors/BasePropertiesExtractor.ts @@ -216,8 +216,10 @@ export class BasePropertiesExtractor implements IObjectExpressionExtractor { objectExpressionNode: ESTree.ObjectExpression, removablePropertyIds: number[] ): void { + const removablePropertyIdsSet: Set = new Set(removablePropertyIds); + objectExpressionNode.properties = objectExpressionNode.properties.filter( - (property: ESTree.Property | ESTree.SpreadElement, index: number) => !removablePropertyIds.includes(index) + (property: ESTree.Property | ESTree.SpreadElement, index: number) => !removablePropertyIdsSet.has(index) ); } } diff --git a/src/node-transformers/string-array-transformers/StringArrayScopeCallsWrapperTransformer.ts b/src/node-transformers/string-array-transformers/StringArrayScopeCallsWrapperTransformer.ts index 68a7eb3e..5aa19210 100644 --- a/src/node-transformers/string-array-transformers/StringArrayScopeCallsWrapperTransformer.ts +++ b/src/node-transformers/string-array-transformers/StringArrayScopeCallsWrapperTransformer.ts @@ -140,15 +140,16 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo const { scopeCallsWrappersData } = stringArrayScopeCallsWrappersData; const scopeCallsWrappersDataLength: number = scopeCallsWrappersData.length; + const upperStringArrayCallsWrapperData = this.getUpperStringArrayCallsWrapperData( + stringArrayScopeCallsWrappersData + ); + /** * Iterates over each name of scope wrapper name * Reverse iteration appends wrappers at index `0` at the correct order */ for (let i = scopeCallsWrappersDataLength - 1; i >= 0; i--) { const stringArrayScopeCallsWrapperData = scopeCallsWrappersData[i]; - const upperStringArrayCallsWrapperData = this.getUpperStringArrayCallsWrapperData( - stringArrayScopeCallsWrappersData - ); this.getAndAppendStringArrayScopeCallsWrapperNode( lexicalScopeBodyNode, diff --git a/src/storages/string-array-transformers/StringArrayStorage.ts b/src/storages/string-array-transformers/StringArrayStorage.ts index ebdd0407..a60dca37 100644 --- a/src/storages/string-array-transformers/StringArrayStorage.ts +++ b/src/storages/string-array-transformers/StringArrayStorage.ts @@ -78,9 +78,9 @@ export class StringArrayStorage private readonly rc4Keys: string[]; /** - * @type {Map} + * @type {Map>} */ - private readonly rc4EncodedValuesSourcesCache: Map = new Map(); + private readonly rc4EncodedValuesSourcesCache: Map> = new Map(); /** * @type {number} @@ -224,19 +224,13 @@ export class StringArrayStorage this.storage = new Map( this.arrayUtils .shuffle(Array.from(this.storage.entries())) - .map<[`${string}-${TStringArrayEncoding}`, IStringArrayStorageItemData]>( - ([value, stringArrayStorageItemData], index: number) => { - stringArrayStorageItemData.index = index; + .map< + [`${string}-${TStringArrayEncoding}`, IStringArrayStorageItemData] + >(([value, stringArrayStorageItemData], index: number) => { + stringArrayStorageItemData.index = index; - return [value, stringArrayStorageItemData]; - } - ) - .sort( - ( - [, stringArrayStorageItemDataA]: [string, IStringArrayStorageItemData], - [, stringArrayStorageItemDataB]: [string, IStringArrayStorageItemData] - ) => stringArrayStorageItemDataA.index - stringArrayStorageItemDataB.index - ) + return [value, stringArrayStorageItemData]; + }) ); } @@ -301,20 +295,20 @@ export class StringArrayStorage this.cryptUtilsStringArray.rc4(value, decodeKey) ); - const encodedValueSources: string[] = this.rc4EncodedValuesSourcesCache.get(encodedValue) ?? []; - let encodedValueSourcesLength: number = encodedValueSources.length; + const encodedValueSources: Set = + this.rc4EncodedValuesSourcesCache.get(encodedValue) ?? new Set(); + const encodedValueSourcesSize: number = encodedValueSources.size; const shouldAddValueToSourcesCache: boolean = - !encodedValueSourcesLength || !encodedValueSources.includes(value); + !encodedValueSourcesSize || !encodedValueSources.has(value); if (shouldAddValueToSourcesCache) { - encodedValueSources.push(value); - encodedValueSourcesLength++; + encodedValueSources.add(value); } this.rc4EncodedValuesSourcesCache.set(encodedValue, encodedValueSources); - if (encodedValueSourcesLength > 1) { + if (encodedValueSources.size > 1) { return this.getEncodedValue(value); } diff --git a/src/utils/ArrayUtils.ts b/src/utils/ArrayUtils.ts index 718ee229..4c1c2557 100644 --- a/src/utils/ArrayUtils.ts +++ b/src/utils/ArrayUtils.ts @@ -103,7 +103,9 @@ export class ArrayUtils implements IArrayUtils { * @returns {T[]} */ public rotate(array: T[], times: number): T[] { - if (!array.length) { + const arrayLength: number = array.length; + + if (!arrayLength) { throw new ReferenceError('Cannot rotate empty array.'); } @@ -111,19 +113,16 @@ export class ArrayUtils implements IArrayUtils { return array; } - const newArray: T[] = array; + // Normalize rotation amount to avoid unnecessary full rotations + // O(N) algorithm using slice instead of O(N*R) with pop/unshift + const normalizedTimes: number = times % arrayLength; - let temp: T | undefined; - - while (times--) { - temp = newArray.pop(); - - if (temp) { - newArray.unshift(temp); - } + if (normalizedTimes === 0) { + return [...array]; } - return newArray; + // Right rotation: take last `normalizedTimes` elements and put them at the front + return [...array.slice(-normalizedTimes), ...array.slice(0, -normalizedTimes)]; } /**