mirror of
https://github.com/javascript-obfuscator/javascript-obfuscator
synced 2026-08-09 12:42:29 +00:00
173 lines
5.3 KiB
TypeScript
173 lines
5.3 KiB
TypeScript
import { injectable, inject } from 'inversify';
|
|
import { ServiceIdentifiers } from '../container/ServiceIdentifiers';
|
|
|
|
import * as estraverse from 'estraverse';
|
|
import * as ESTree from 'estree';
|
|
|
|
import { TCalleeDataExtractorFactory } from '../types/container/TCalleeDataExtractorFactory';
|
|
|
|
import { ICalleeData } from '../interfaces/stack-trace-analyzer/ICalleeData';
|
|
import { ICalleeDataExtractor } from '../interfaces/stack-trace-analyzer/ICalleeDataExtractor';
|
|
import { IStackTraceAnalyzer } from '../interfaces/stack-trace-analyzer/IStackTraceAnalyzer';
|
|
import { IStackTraceData } from '../interfaces/stack-trace-analyzer/IStackTraceData';
|
|
|
|
import { CalleeDataExtractors } from '../enums/container/CalleeDataExtractors';
|
|
|
|
import { Node } from '../node/Node';
|
|
import { NodeUtils } from '../node/NodeUtils';
|
|
|
|
/**
|
|
* This class generates a data with code stack trace functions calls
|
|
*
|
|
* For example:
|
|
*
|
|
* function Foo () {
|
|
* var baz = function () {
|
|
*
|
|
* }
|
|
*
|
|
* baz();
|
|
* }
|
|
*
|
|
* foo();
|
|
*
|
|
* Will generate a structure like:
|
|
*
|
|
* [
|
|
* {
|
|
* callee: FOO_FUNCTION_NODE
|
|
* name: 'Foo',
|
|
* trace: [
|
|
* {
|
|
* callee: BAZ_FUNCTION_NODE,
|
|
* name: 'baz,
|
|
* trace: []
|
|
* }
|
|
* ]
|
|
* }
|
|
* ]
|
|
*/
|
|
@injectable()
|
|
export class StackTraceAnalyzer implements IStackTraceAnalyzer {
|
|
/**
|
|
* @type {CalleeDataExtractors[]}
|
|
*/
|
|
private static readonly calleeDataExtractorsList: CalleeDataExtractors[] = [
|
|
CalleeDataExtractors.FunctionDeclarationCalleeDataExtractor,
|
|
CalleeDataExtractors.FunctionExpressionCalleeDataExtractor,
|
|
CalleeDataExtractors.ObjectExpressionCalleeDataExtractor
|
|
];
|
|
|
|
/**
|
|
* @type {number}
|
|
*/
|
|
private static readonly limitThresholdActivationLength: number = 25;
|
|
|
|
/**
|
|
* @type {number}
|
|
*/
|
|
private static readonly limitThreshold: number = 0.002;
|
|
|
|
/**
|
|
* @type {(calleeDataExtractorName: CalleeDataExtractors) => ICalleeDataExtractor}
|
|
*/
|
|
private calleeDataExtractorFactory: (calleeDataExtractorName: CalleeDataExtractors) => ICalleeDataExtractor;
|
|
|
|
constructor (
|
|
@inject(ServiceIdentifiers.Factory__ICalleeDataExtractor) calleeDataExtractorFactory: TCalleeDataExtractorFactory
|
|
) {
|
|
this.calleeDataExtractorFactory = calleeDataExtractorFactory;
|
|
}
|
|
|
|
/**
|
|
* @param blockScopeBodyLength
|
|
* @returns {number}
|
|
*/
|
|
public static getLimitIndex (blockScopeBodyLength: number): number {
|
|
const lastIndex: number = blockScopeBodyLength - 1;
|
|
const limitThresholdActivationIndex: number = StackTraceAnalyzer.limitThresholdActivationLength - 1;
|
|
|
|
let limitIndex: number = lastIndex;
|
|
|
|
if (lastIndex > limitThresholdActivationIndex) {
|
|
limitIndex = Math.round(
|
|
limitThresholdActivationIndex + (lastIndex * StackTraceAnalyzer.limitThreshold)
|
|
);
|
|
|
|
if (limitIndex > lastIndex) {
|
|
limitIndex = lastIndex;
|
|
}
|
|
}
|
|
|
|
return limitIndex;
|
|
}
|
|
|
|
/**
|
|
* @param blockScopeBody
|
|
* @returns {IStackTraceData[]}
|
|
*/
|
|
public analyze (blockScopeBody: ESTree.Node[]): IStackTraceData[] {
|
|
return this.analyzeRecursive(blockScopeBody);
|
|
}
|
|
|
|
/**
|
|
* @param blockScopeBody
|
|
* @returns {IStackTraceData[]}
|
|
*/
|
|
private analyzeRecursive (blockScopeBody: ESTree.Node[]): IStackTraceData[] {
|
|
const limitIndex: number = StackTraceAnalyzer.getLimitIndex(blockScopeBody.length);
|
|
const stackTraceData: IStackTraceData[] = [];
|
|
const blockScopeBodyLength: number = blockScopeBody.length;
|
|
|
|
for (let index: number = 0; index < blockScopeBodyLength; index++) {
|
|
if (index > limitIndex) {
|
|
break;
|
|
}
|
|
|
|
const blockScopeBodyNode: ESTree.Node = blockScopeBody[index];
|
|
|
|
estraverse.traverse(blockScopeBodyNode, {
|
|
enter: (node: ESTree.Node): any => {
|
|
if (!Node.isCallExpressionNode(node)) {
|
|
return;
|
|
}
|
|
|
|
if (blockScopeBodyNode.parentNode !== NodeUtils.getBlockScopesOfNode(node)[0]) {
|
|
return estraverse.VisitorOption.Skip;
|
|
}
|
|
|
|
this.analyzeCallExpressionNode(stackTraceData, blockScopeBody, node);
|
|
}
|
|
});
|
|
}
|
|
|
|
return stackTraceData;
|
|
}
|
|
|
|
/**
|
|
* @param stackTraceData
|
|
* @param blockScopeBody
|
|
* @param callExpressionNode
|
|
* @returns {IStackTraceData[]}
|
|
*/
|
|
private analyzeCallExpressionNode (
|
|
stackTraceData: IStackTraceData[],
|
|
blockScopeBody: ESTree.Node[],
|
|
callExpressionNode: ESTree.CallExpression
|
|
): void {
|
|
StackTraceAnalyzer.calleeDataExtractorsList.forEach((calleeDataExtractorName: CalleeDataExtractors) => {
|
|
const calleeData: ICalleeData | null = this.calleeDataExtractorFactory(calleeDataExtractorName)
|
|
.extract(blockScopeBody, callExpressionNode.callee);
|
|
|
|
if (!calleeData) {
|
|
return;
|
|
}
|
|
|
|
stackTraceData.push({
|
|
...calleeData,
|
|
stackTrace: this.analyzeRecursive(calleeData.callee.body)
|
|
});
|
|
});
|
|
}
|
|
}
|