mirror of
https://github.com/javascript-obfuscator/javascript-obfuscator
synced 2026-08-09 12:42:29 +00:00
Optimize performance (#1355)
This commit is contained in:
+9
-6
@@ -77,6 +77,10 @@ export class BlockStatementControlFlowTransformer extends AbstractNodeTransforme
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private static canTransformBlockStatementNode(blockStatementNode: ESTree.BlockStatement): boolean {
|
||||
if (blockStatementNode.body.length <= 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let canTransform: boolean = true;
|
||||
|
||||
estraverse.traverse(blockStatementNode, {
|
||||
@@ -91,10 +95,6 @@ export class BlockStatementControlFlowTransformer extends AbstractNodeTransforme
|
||||
}
|
||||
});
|
||||
|
||||
if (blockStatementNode.body.length <= 4) {
|
||||
canTransform = false;
|
||||
}
|
||||
|
||||
return canTransform;
|
||||
}
|
||||
|
||||
@@ -138,8 +138,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>
|
||||
|
||||
+18
-8
@@ -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;
|
||||
@@ -91,9 +91,21 @@ export abstract class AbstractControlFlowReplacer implements IControlFlowReplace
|
||||
usingExistingIdentifierChance: number
|
||||
): string {
|
||||
const controlFlowStorageId: string = controlFlowStorage.getStorageId();
|
||||
const storageKeysById: Map<string | number, string[]> =
|
||||
this.replacerDataByControlFlowStorageId.get(controlFlowStorageId) ?? new Map<string, string[]>();
|
||||
const storageKeysForCurrentId: string[] = storageKeysById.get(replacerId) ?? [];
|
||||
|
||||
let storageKeysById: Map<string | number, string[]> | undefined =
|
||||
this.replacerDataByControlFlowStorageId.get(controlFlowStorageId);
|
||||
|
||||
if (!storageKeysById) {
|
||||
storageKeysById = new Map<string | number, string[]>();
|
||||
this.replacerDataByControlFlowStorageId.set(controlFlowStorageId, storageKeysById);
|
||||
}
|
||||
|
||||
let storageKeysForCurrentId: string[] | undefined = storageKeysById.get(replacerId);
|
||||
|
||||
if (!storageKeysForCurrentId) {
|
||||
storageKeysForCurrentId = [];
|
||||
storageKeysById.set(replacerId, storageKeysForCurrentId);
|
||||
}
|
||||
|
||||
const shouldPickFromStorageKeysById =
|
||||
this.randomGenerator.getMathRandom() < usingExistingIdentifierChance && storageKeysForCurrentId.length;
|
||||
@@ -105,8 +117,6 @@ export abstract class AbstractControlFlowReplacer implements IControlFlowReplace
|
||||
const storageKey: string = this.generateStorageKey(controlFlowStorage);
|
||||
|
||||
storageKeysForCurrentId.push(storageKey);
|
||||
storageKeysById.set(replacerId, storageKeysForCurrentId);
|
||||
this.replacerDataByControlFlowStorageId.set(controlFlowStorageId, storageKeysById);
|
||||
controlFlowStorage.set(storageKey, customNode);
|
||||
|
||||
return storageKey;
|
||||
|
||||
@@ -31,9 +31,9 @@ import { NodeGuards } from '../../node/NodeGuards';
|
||||
@injectable()
|
||||
export class ClassFieldTransformer extends AbstractNodeTransformer {
|
||||
/**
|
||||
* @type {string[]}
|
||||
* @type {string}
|
||||
*/
|
||||
private static readonly ignoredNames: string[] = ['constructor'];
|
||||
private static readonly ignoredName: string = 'constructor';
|
||||
|
||||
/**
|
||||
* @param {IRandomGenerator} randomGenerator
|
||||
@@ -98,7 +98,7 @@ export class ClassFieldTransformer extends AbstractNodeTransformer {
|
||||
classFieldNode: ESTree.MethodDefinition | ESTree.PropertyDefinition,
|
||||
keyNode: ESTree.Identifier
|
||||
): ESTree.MethodDefinition | ESTree.PropertyDefinition {
|
||||
if (!ClassFieldTransformer.ignoredNames.includes(keyNode.name) && !classFieldNode.computed) {
|
||||
if (keyNode.name !== ClassFieldTransformer.ignoredName && !classFieldNode.computed) {
|
||||
classFieldNode.computed = true;
|
||||
classFieldNode.key = NodeFactory.literalNode(keyNode.name);
|
||||
}
|
||||
@@ -117,7 +117,7 @@ export class ClassFieldTransformer extends AbstractNodeTransformer {
|
||||
): ESTree.MethodDefinition | ESTree.PropertyDefinition {
|
||||
if (
|
||||
typeof keyNode.value === 'string' &&
|
||||
!ClassFieldTransformer.ignoredNames.includes(keyNode.value) &&
|
||||
keyNode.value !== ClassFieldTransformer.ignoredName &&
|
||||
!classFieldNode.computed
|
||||
) {
|
||||
classFieldNode.computed = true;
|
||||
|
||||
@@ -53,6 +53,61 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
|
||||
this.objectExpressionExtractorFactory = objectExpressionExtractorFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combined prohibition check result
|
||||
*/
|
||||
private static checkProhibitedPatterns(
|
||||
objectExpressionNode: ESTree.ObjectExpression,
|
||||
objectExpressionHostNode: ESTree.Node
|
||||
): { hasReferencedIdentifier: boolean; hasCallExpression: boolean } {
|
||||
const identifierNamesSet: Set<string> = new Set();
|
||||
|
||||
let hasReferencedIdentifier: boolean = false;
|
||||
let hasCallExpression: boolean = false;
|
||||
let isInsideObjectExpression: boolean = false;
|
||||
|
||||
estraverse.traverse(objectExpressionHostNode, {
|
||||
// eslint-disable-next-line complexity
|
||||
enter: (node: ESTree.Node): void | estraverse.VisitorOption => {
|
||||
if (node === objectExpressionNode) {
|
||||
isInsideObjectExpression = true;
|
||||
}
|
||||
|
||||
if (isInsideObjectExpression && !hasCallExpression) {
|
||||
if (NodeGuards.isCallExpressionNode(node) || NodeGuards.isNewExpressionNode(node)) {
|
||||
hasCallExpression = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (NodeGuards.isIdentifierNode(node) || NodeGuards.isThisExpressionNode(node)) {
|
||||
const identifierName: string = NodeGuards.isIdentifierNode(node)
|
||||
? node.name
|
||||
: ObjectExpressionKeysTransformer.thisIdentifierName;
|
||||
|
||||
if (!isInsideObjectExpression) {
|
||||
identifierNamesSet.add(identifierName);
|
||||
} else if (identifierNamesSet.has(identifierName)) {
|
||||
hasReferencedIdentifier = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasReferencedIdentifier && hasCallExpression) {
|
||||
return estraverse.VisitorOption.Break;
|
||||
}
|
||||
},
|
||||
leave: (node: ESTree.Node): void | estraverse.VisitorOption => {
|
||||
if (node === objectExpressionNode) {
|
||||
isInsideObjectExpression = false;
|
||||
if (hasReferencedIdentifier || hasCallExpression) {
|
||||
return estraverse.VisitorOption.Break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { hasReferencedIdentifier, hasCallExpression };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ObjectExpression} objectExpressionNode
|
||||
* @param {Node} objectExpressionParentNode
|
||||
@@ -64,86 +119,25 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
|
||||
objectExpressionParentNode: ESTree.Node,
|
||||
objectExpressionHostStatement: ESTree.Statement
|
||||
): boolean {
|
||||
return (
|
||||
ObjectExpressionKeysTransformer.isReferencedIdentifierName(
|
||||
objectExpressionNode,
|
||||
objectExpressionHostStatement
|
||||
) ||
|
||||
if (
|
||||
ObjectExpressionKeysTransformer.isProhibitedArrowFunctionExpression(
|
||||
objectExpressionNode,
|
||||
objectExpressionParentNode
|
||||
) ||
|
||||
ObjectExpressionKeysTransformer.isObjectExpressionWithCallExpression(objectExpressionNode) ||
|
||||
ObjectExpressionKeysTransformer.isProhibitedSequenceExpression(
|
||||
objectExpressionNode,
|
||||
objectExpressionHostStatement
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Identifier | ThisExpression} node
|
||||
* @returns {string}
|
||||
*/
|
||||
private static getReferencedIdentifierName(node: ESTree.Identifier | ESTree.ThisExpression): string {
|
||||
if (NodeGuards.isIdentifierNode(node)) {
|
||||
return node.name;
|
||||
} else {
|
||||
return ObjectExpressionKeysTransformer.thisIdentifierName;
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ObjectExpression} objectExpressionNode
|
||||
* @param {Node} objectExpressionHostNode
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private static isReferencedIdentifierName(
|
||||
objectExpressionNode: ESTree.ObjectExpression,
|
||||
objectExpressionHostNode: ESTree.Node
|
||||
): boolean {
|
||||
const identifierNamesSet: Set<string> = new Set();
|
||||
const { hasReferencedIdentifier, hasCallExpression } = ObjectExpressionKeysTransformer.checkProhibitedPatterns(
|
||||
objectExpressionNode,
|
||||
objectExpressionHostStatement
|
||||
);
|
||||
|
||||
let isReferencedIdentifierName: boolean = false;
|
||||
let isCurrentNode: boolean = false;
|
||||
|
||||
// should mark node as prohibited if identifier of node is referenced somewhere inside other nodes
|
||||
estraverse.traverse(objectExpressionHostNode, {
|
||||
enter: (node: ESTree.Node): void | estraverse.VisitorOption => {
|
||||
if (node === objectExpressionNode) {
|
||||
isCurrentNode = true;
|
||||
}
|
||||
|
||||
if (!NodeGuards.isIdentifierNode(node) && !NodeGuards.isThisExpressionNode(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isCurrentNode) {
|
||||
identifierNamesSet.add(ObjectExpressionKeysTransformer.getReferencedIdentifierName(node));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const hasReferencedIdentifierName: boolean = identifierNamesSet.has(
|
||||
ObjectExpressionKeysTransformer.getReferencedIdentifierName(node)
|
||||
);
|
||||
|
||||
if (hasReferencedIdentifierName) {
|
||||
isReferencedIdentifierName = true;
|
||||
|
||||
return estraverse.VisitorOption.Break;
|
||||
}
|
||||
},
|
||||
leave: (node: ESTree.Node): void | estraverse.VisitorOption => {
|
||||
if (node === objectExpressionNode) {
|
||||
isCurrentNode = false;
|
||||
|
||||
return estraverse.VisitorOption.Break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return isReferencedIdentifierName;
|
||||
return hasReferencedIdentifier || hasCallExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,31 +155,6 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ObjectExpression} objectExpressionNode
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private static isObjectExpressionWithCallExpression(objectExpressionNode: ESTree.ObjectExpression): boolean {
|
||||
let isCallExpressionLikeNodeFound: boolean = false;
|
||||
|
||||
estraverse.traverse(objectExpressionNode, {
|
||||
enter: (node: ESTree.Node): void | estraverse.VisitorOption => {
|
||||
const isCallExpressionLikeNode =
|
||||
NodeGuards.isCallExpressionNode(node) || NodeGuards.isNewExpressionNode(node);
|
||||
|
||||
if (!isCallExpressionLikeNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
isCallExpressionLikeNodeFound = true;
|
||||
|
||||
return estraverse.VisitorOption.Break;
|
||||
}
|
||||
});
|
||||
|
||||
return isCallExpressionLikeNodeFound;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ObjectExpression} objectExpressionNode
|
||||
* @param {Node} objectExpressionHostNode
|
||||
@@ -263,32 +232,28 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
|
||||
return objectExpressionNode;
|
||||
}
|
||||
|
||||
return this.applyObjectExpressionKeysExtractorsRecursive(
|
||||
ObjectExpressionKeysTransformer.objectExpressionExtractorNames,
|
||||
objectExpressionNode,
|
||||
hostStatement
|
||||
);
|
||||
return this.applyObjectExpressionKeysExtractorsRecursive(objectExpressionNode, hostStatement, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ObjectExpressionExtractor[]} objectExpressionExtractorNames
|
||||
* @param {ObjectExpression} objectExpressionNode
|
||||
* @param {Statement} hostStatement
|
||||
* @param {number} extractorIndex
|
||||
* @returns {Node}
|
||||
*/
|
||||
private applyObjectExpressionKeysExtractorsRecursive(
|
||||
objectExpressionExtractorNames: ObjectExpressionExtractor[],
|
||||
objectExpressionNode: ESTree.ObjectExpression,
|
||||
hostStatement: ESTree.Statement
|
||||
hostStatement: ESTree.Statement,
|
||||
extractorIndex: number
|
||||
): ESTree.Node {
|
||||
const newObjectExpressionExtractorNames: ObjectExpressionExtractor[] = [...objectExpressionExtractorNames];
|
||||
const objectExpressionExtractor: ObjectExpressionExtractor | undefined =
|
||||
newObjectExpressionExtractorNames.shift();
|
||||
const objectExpressionExtractorNames = ObjectExpressionKeysTransformer.objectExpressionExtractorNames;
|
||||
|
||||
if (!objectExpressionExtractor) {
|
||||
if (extractorIndex >= objectExpressionExtractorNames.length) {
|
||||
return objectExpressionNode;
|
||||
}
|
||||
|
||||
const objectExpressionExtractor: ObjectExpressionExtractor = objectExpressionExtractorNames[extractorIndex];
|
||||
|
||||
const {
|
||||
nodeToReplace,
|
||||
objectExpressionHostStatement: newObjectExpressionHostStatement,
|
||||
@@ -299,9 +264,9 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
|
||||
);
|
||||
|
||||
this.applyObjectExpressionKeysExtractorsRecursive(
|
||||
newObjectExpressionExtractorNames,
|
||||
newObjectExpressionNode,
|
||||
newObjectExpressionHostStatement
|
||||
newObjectExpressionHostStatement,
|
||||
extractorIndex + 1
|
||||
);
|
||||
|
||||
return nodeToReplace;
|
||||
|
||||
@@ -152,26 +152,25 @@ export class SplitStringTransformer extends AbstractNodeTransformer {
|
||||
* @returns {BinaryExpression}
|
||||
*/
|
||||
private transformStringChunksToBinaryExpressionNode(chunks: string[]): ESTree.BinaryExpression {
|
||||
const firstChunk: string | undefined = chunks.shift();
|
||||
const secondChunk: string | undefined = chunks.shift();
|
||||
const chunksLength: number = chunks.length;
|
||||
|
||||
if (!firstChunk || !secondChunk) {
|
||||
if (chunksLength < 2) {
|
||||
throw new Error('First and second chunks values should not be empty');
|
||||
}
|
||||
|
||||
const initialBinaryExpressionNode: ESTree.BinaryExpression = NodeFactory.binaryExpressionNode(
|
||||
'+',
|
||||
NodeFactory.literalNode(firstChunk),
|
||||
NodeFactory.literalNode(secondChunk)
|
||||
NodeFactory.literalNode(chunks[0]),
|
||||
NodeFactory.literalNode(chunks[1])
|
||||
);
|
||||
|
||||
return chunks.reduce<ESTree.BinaryExpression>(
|
||||
(binaryExpressionNode: ESTree.BinaryExpression, chunk: string) => {
|
||||
const chunkLiteralNode: ESTree.Literal = NodeFactory.literalNode(chunk);
|
||||
let result: ESTree.BinaryExpression = initialBinaryExpressionNode;
|
||||
|
||||
return NodeFactory.binaryExpressionNode('+', binaryExpressionNode, chunkLiteralNode);
|
||||
},
|
||||
initialBinaryExpressionNode
|
||||
);
|
||||
// Start from index 2 since we already used 0 and 1
|
||||
for (let i: number = 2; i < chunksLength; i++) {
|
||||
result = NodeFactory.binaryExpressionNode('+', result, NodeFactory.literalNode(chunks[i]));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,21 +127,9 @@ export class DirectivePlacementTransformer extends AbstractNodeTransformer {
|
||||
// append new directive node at the top of lexical scope statements
|
||||
NodeAppender.prepend(nodeWithLexicalScopeStatements, [newDirectiveNode]);
|
||||
|
||||
// remove found directive node
|
||||
let isDirectiveNodeRemoved: boolean = false;
|
||||
estraverse.replace(nodeWithLexicalScopeStatements, {
|
||||
enter: (node: ESTree.Node): estraverse.VisitorOption | undefined => {
|
||||
if (isDirectiveNodeRemoved) {
|
||||
return estraverse.VisitorOption.Break;
|
||||
}
|
||||
|
||||
if (node === directiveNode) {
|
||||
isDirectiveNodeRemoved = true;
|
||||
|
||||
return estraverse.VisitorOption.Remove;
|
||||
}
|
||||
}
|
||||
});
|
||||
nodeWithLexicalScopeStatements.body = nodeWithLexicalScopeStatements.body.filter(
|
||||
(node) => node !== directiveNode
|
||||
);
|
||||
}
|
||||
|
||||
return nodeWithLexicalScopeStatements;
|
||||
|
||||
+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,
|
||||
|
||||
@@ -328,18 +328,24 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
|
||||
const nextScopeCallsWrapperParameterIndexesData: IStringArrayScopeCallsWrapperParameterIndexesData | null =
|
||||
this.getStringArrayCallsWrapperParameterIndexesData();
|
||||
|
||||
stringArrayScopeCallsWrappersDataByEncoding[encoding] = {
|
||||
encoding,
|
||||
scopeCallsWrappersData: [
|
||||
...stringArrayScopeCallsWrappersData,
|
||||
{
|
||||
name: nextScopeCallsWrapperName,
|
||||
index: nextScopeCallsWrapperShiftedIndex,
|
||||
parameterIndexesData: nextScopeCallsWrapperParameterIndexesData
|
||||
}
|
||||
]
|
||||
const newWrapperData: IStringArrayScopeCallsWrapperData = {
|
||||
name: nextScopeCallsWrapperName,
|
||||
index: nextScopeCallsWrapperShiftedIndex,
|
||||
parameterIndexesData: nextScopeCallsWrapperParameterIndexesData
|
||||
};
|
||||
|
||||
let encodingData = stringArrayScopeCallsWrappersDataByEncoding[encoding];
|
||||
|
||||
if (!encodingData) {
|
||||
encodingData = {
|
||||
encoding,
|
||||
scopeCallsWrappersData: [newWrapperData]
|
||||
};
|
||||
stringArrayScopeCallsWrappersDataByEncoding[encoding] = encodingData;
|
||||
} else {
|
||||
encodingData.scopeCallsWrappersData.push(newWrapperData);
|
||||
}
|
||||
|
||||
this.stringArrayScopeCallsWrappersDataStorage.set(
|
||||
currentLexicalScopeBodyNode,
|
||||
stringArrayScopeCallsWrappersDataByEncoding
|
||||
|
||||
@@ -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];
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -296,29 +290,36 @@ export class StringArrayStorage
|
||||
* if collision will happen, just try to encode value again
|
||||
*/
|
||||
case StringArrayEncoding.Rc4: {
|
||||
const decodeKey: string = this.randomGenerator.getRandomGenerator().pickone(this.rc4Keys);
|
||||
const encodedValue: string = this.cryptUtilsStringArray.btoa(
|
||||
this.cryptUtilsStringArray.rc4(value, decodeKey)
|
||||
);
|
||||
const maxRetryAttempts: number = 50;
|
||||
|
||||
const encodedValueSources: string[] = this.rc4EncodedValuesSourcesCache.get(encodedValue) ?? [];
|
||||
let encodedValueSourcesLength: number = encodedValueSources.length;
|
||||
for (let attempt: number = 0; attempt < maxRetryAttempts; attempt++) {
|
||||
const decodeKey: string = this.randomGenerator.getRandomGenerator().pickone(this.rc4Keys);
|
||||
const encodedValue: string = this.cryptUtilsStringArray.btoa(
|
||||
this.cryptUtilsStringArray.rc4(value, decodeKey)
|
||||
);
|
||||
|
||||
const shouldAddValueToSourcesCache: boolean =
|
||||
!encodedValueSourcesLength || !encodedValueSources.includes(value);
|
||||
const encodedValueSources: Set<string> =
|
||||
this.rc4EncodedValuesSourcesCache.get(encodedValue) ?? new Set();
|
||||
|
||||
if (shouldAddValueToSourcesCache) {
|
||||
encodedValueSources.push(value);
|
||||
encodedValueSourcesLength++;
|
||||
const shouldAddValueToSourcesCache: boolean =
|
||||
encodedValueSources.size === 0 || !encodedValueSources.has(value);
|
||||
|
||||
if (shouldAddValueToSourcesCache) {
|
||||
encodedValueSources.add(value);
|
||||
}
|
||||
|
||||
this.rc4EncodedValuesSourcesCache.set(encodedValue, encodedValueSources);
|
||||
|
||||
if (encodedValueSources.size <= 1) {
|
||||
return { encodedValue, encoding, decodeKey };
|
||||
}
|
||||
}
|
||||
|
||||
this.rc4EncodedValuesSourcesCache.set(encodedValue, encodedValueSources);
|
||||
|
||||
if (encodedValueSourcesLength > 1) {
|
||||
return this.getEncodedValue(value);
|
||||
}
|
||||
|
||||
return { encodedValue, encoding, decodeKey };
|
||||
return {
|
||||
encodedValue: this.cryptUtilsStringArray.btoa(value),
|
||||
encoding: StringArrayEncoding.Base64,
|
||||
decodeKey: null
|
||||
};
|
||||
}
|
||||
|
||||
case StringArrayEncoding.Base64: {
|
||||
|
||||
+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)];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,11 @@ export class EscapeSequenceEncoder implements IEscapeSequenceEncoder {
|
||||
*/
|
||||
private static readonly forceEscapeCharactersRegExp: RegExp = /[\x00-\x1F\x7F-\x9F'"\\\s]/;
|
||||
|
||||
/**
|
||||
* @type {RegExp}
|
||||
*/
|
||||
private static readonly replaceRegExp: RegExp = /[\s\S]/g;
|
||||
|
||||
/**
|
||||
* @type {Map<string, string>}
|
||||
*/
|
||||
@@ -37,12 +42,11 @@ export class EscapeSequenceEncoder implements IEscapeSequenceEncoder {
|
||||
}
|
||||
|
||||
const radix: number = 16;
|
||||
const replaceRegExp: RegExp = new RegExp('[\\s\\S]', 'g');
|
||||
|
||||
let prefix: string;
|
||||
let template: string;
|
||||
|
||||
const result: string = string.replace(replaceRegExp, (character: string): string => {
|
||||
const result: string = string.replace(EscapeSequenceEncoder.replaceRegExp, (character: string): string => {
|
||||
const shouldEncodeCharacter: boolean =
|
||||
encodeAllSymbols || EscapeSequenceEncoder.forceEscapeCharactersRegExp.test(character);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user