Optimize performance

This commit is contained in:
sanex3339
2025-12-16 21:52:33 +04:00
parent 6866396e9a
commit f7588409fc
7 changed files with 68 additions and 61 deletions
@@ -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<number, number> = new Map(
shuffledKeys.map((key: number, index: number) => [key, index])
);
const originalKeysIndexesInShuffledArray: number[] = originalKeys.map(
(key: number) => shuffledKeyToIndex.get(key)!
);
const blockStatementControlFlowFlatteningCustomNode: ICustomNode<
TInitialData<BlockStatementControlFlowFlatteningNode>
@@ -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;
@@ -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(
'+',
<ESTree.Literal>nodes.shift(),
<ESTree.Expression>nodes.shift()
<ESTree.Literal>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);
@@ -216,8 +216,10 @@ export class BasePropertiesExtractor implements IObjectExpressionExtractor {
objectExpressionNode: ESTree.ObjectExpression,
removablePropertyIds: number[]
): void {
const removablePropertyIdsSet: Set<number> = 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)
);
}
}
@@ -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,
@@ -78,9 +78,9 @@ export class StringArrayStorage
private readonly rc4Keys: string[];
/**
* @type {Map<string, string[]>}
* @type {Map<string, Set<string>>}
*/
private readonly rc4EncodedValuesSourcesCache: Map<string, string[]> = new Map();
private readonly rc4EncodedValuesSourcesCache: Map<string, Set<string>> = 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<string> =
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);
}
+10 -11
View File
@@ -103,7 +103,9 @@ export class ArrayUtils implements IArrayUtils {
* @returns {T[]}
*/
public rotate<T>(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)];
}
/**