mirror of
https://github.com/javascript-obfuscator/javascript-obfuscator
synced 2026-08-09 12:42:29 +00:00
Optimize performance
This commit is contained in:
+10
-6
@@ -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>
|
||||
|
||||
+3
-3
@@ -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);
|
||||
|
||||
+3
-1
@@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -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
@@ -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)];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user