mirror of
https://github.com/javascript-obfuscator/javascript-obfuscator
synced 2026-08-09 12:42:29 +00:00
Merge pull request #459 from javascript-obfuscator/node-transformers-topological-sort
Node transformers topological sort
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
Change Log
|
||||
|
||||
v0.19.3
|
||||
---
|
||||
* The `splitStrings` option now correctly works with `transformObjectKeys` option
|
||||
* Internal `TransformersRunner` rework to support topological sort of node transformers
|
||||
|
||||
v0.19.2
|
||||
---
|
||||
* The `splitStrings` option now correctly splits strings inside objects
|
||||
|
||||
Vendored
+3
-3
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "javascript-obfuscator",
|
||||
"version": "0.19.2",
|
||||
"version": "0.19.3",
|
||||
"description": "JavaScript obfuscator",
|
||||
"keywords": [
|
||||
"obfuscator",
|
||||
|
||||
@@ -23,9 +23,11 @@ export enum ServiceIdentifiers {
|
||||
IIdentifierNamesGenerator = 'IIdentifierNamesGenerator',
|
||||
IIdentifierObfuscatingReplacer = 'IIdentifierObfuscatingReplacer',
|
||||
IJavaScriptObfuscator = 'IJavaScriptObfuscator',
|
||||
ILevelledTopologicalSorter = 'ILevelledTopologicalSorter',
|
||||
ILogger = 'ILogger',
|
||||
INodeGuard = 'INodeGuard',
|
||||
INodeTransformer = 'INodeTransformer',
|
||||
INodeTransformerNamesGroupsBuilder = 'INodeTransformerNamesGroupsBuilder',
|
||||
IObfuscationEventEmitter = 'IObfuscationEventEmitter',
|
||||
IObfuscatedCode = 'IObfuscatedCode',
|
||||
IOptions = 'IOptions',
|
||||
|
||||
@@ -4,11 +4,15 @@ import { ServiceIdentifiers } from '../../ServiceIdentifiers';
|
||||
import { IArrayUtils } from '../../../interfaces/utils/IArrayUtils';
|
||||
import { ICryptUtils } from '../../../interfaces/utils/ICryptUtils';
|
||||
import { IEscapeSequenceEncoder } from '../../../interfaces/utils/IEscapeSequenceEncoder';
|
||||
import { ILevelledTopologicalSorter } from '../../../interfaces/utils/ILevelledTopologicalSorter';
|
||||
import { INodeTransformerNamesGroupsBuilder } from '../../../interfaces/utils/INodeTransformerNamesGroupsBuilder';
|
||||
import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
|
||||
|
||||
import { ArrayUtils } from '../../../utils/ArrayUtils';
|
||||
import { CryptUtils } from '../../../utils/CryptUtils';
|
||||
import { EscapeSequenceEncoder } from '../../../utils/EscapeSequenceEncoder';
|
||||
import { LevelledTopologicalSorter } from '../../../utils/LevelledTopologicalSorter';
|
||||
import { NodeTransformerNamesGroupsBuilder } from '../../../utils/NodeTransformerNamesGroupsBuilder';
|
||||
import { RandomGenerator } from '../../../utils/RandomGenerator';
|
||||
|
||||
export const utilsModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
|
||||
@@ -31,4 +35,13 @@ export const utilsModule: interfaces.ContainerModule = new ContainerModule((bind
|
||||
bind<IEscapeSequenceEncoder>(ServiceIdentifiers.IEscapeSequenceEncoder)
|
||||
.to(EscapeSequenceEncoder)
|
||||
.inSingletonScope();
|
||||
|
||||
// levelled topological sorter
|
||||
bind<ILevelledTopologicalSorter>(ServiceIdentifiers.ILevelledTopologicalSorter)
|
||||
.to(LevelledTopologicalSorter);
|
||||
|
||||
// node transformer names groups builder
|
||||
bind<INodeTransformerNamesGroupsBuilder>(ServiceIdentifiers.INodeTransformerNamesGroupsBuilder)
|
||||
.to(NodeTransformerNamesGroupsBuilder)
|
||||
.inSingletonScope();
|
||||
});
|
||||
|
||||
@@ -3,9 +3,15 @@ import * as ESTree from 'estree';
|
||||
|
||||
import { IVisitor } from './IVisitor';
|
||||
|
||||
import { NodeTransformer } from '../../enums/node-transformers/NodeTransformer';
|
||||
import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
|
||||
|
||||
export interface INodeTransformer {
|
||||
/**
|
||||
* @type {NodeTransformer[] | undefined}
|
||||
*/
|
||||
runAfter?: NodeTransformer[];
|
||||
|
||||
/**
|
||||
* @param {TransformationStage} transformationStage
|
||||
* @returns {IVisitor | null}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
export interface ILevelledTopologicalSorter <TValue extends string = string> {
|
||||
/**
|
||||
* @param {TValue} precedent
|
||||
* @param {TValue | null} consequent
|
||||
* @returns {this}
|
||||
*/
|
||||
add (
|
||||
precedent: TValue,
|
||||
consequent?: TValue | null
|
||||
): this;
|
||||
|
||||
/**
|
||||
* @returns {TValue[]}
|
||||
*/
|
||||
sort (): TValue[];
|
||||
|
||||
/**
|
||||
* @returns {TValue[][]}
|
||||
*/
|
||||
sortByGroups (): TValue[][];
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { TNormalizedNodeTransformers } from '../../types/node-transformers/TNormalizedNodeTransformers';
|
||||
|
||||
import { NodeTransformer } from '../../enums/node-transformers/NodeTransformer';
|
||||
|
||||
export interface INodeTransformerNamesGroupsBuilder {
|
||||
/**
|
||||
* @param {TNormalizedNodeTransformers} normalizedNodeTransformers
|
||||
* @returns {NodeTransformer[][]}
|
||||
*/
|
||||
build (normalizedNodeTransformers: TNormalizedNodeTransformers): NodeTransformer[][];
|
||||
}
|
||||
@@ -9,10 +9,16 @@ import { IOptions } from '../interfaces/options/IOptions';
|
||||
import { IRandomGenerator } from '../interfaces/utils/IRandomGenerator';
|
||||
import { IVisitor } from '../interfaces/node-transformers/IVisitor';
|
||||
|
||||
import { NodeTransformer } from '../enums/node-transformers/NodeTransformer';
|
||||
import { TransformationStage } from '../enums/node-transformers/TransformationStage';
|
||||
|
||||
@injectable()
|
||||
export abstract class AbstractNodeTransformer implements INodeTransformer {
|
||||
/**
|
||||
* @type {NodeTransformer[] | undefined}
|
||||
*/
|
||||
public readonly runAfter: NodeTransformer[] | undefined;
|
||||
|
||||
/**
|
||||
* @type {IOptions}
|
||||
*/
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
|
||||
import { ServiceIdentifiers } from '../container/ServiceIdentifiers';
|
||||
|
||||
import * as estraverse from 'estraverse';
|
||||
import * as ESTree from 'estree';
|
||||
|
||||
import { TNodeTransformerFactory } from '../types/container/node-transformers/TNodeTransformerFactory';
|
||||
import { TNormalizedNodeTransformers } from '../types/node-transformers/TNormalizedNodeTransformers';
|
||||
import { TTransformersRunnerData } from '../types/node-transformers/TTransformersRunnerData';
|
||||
import { TVisitorDirection } from '../types/node-transformers/TVisitorDirection';
|
||||
import { TVisitorFunction } from '../types/node-transformers/TVisitorFunction';
|
||||
import { TVisitorResult } from '../types/node-transformers/TVisitorResult';
|
||||
|
||||
import { INodeTransformer } from '../interfaces/node-transformers/INodeTransformer';
|
||||
import { INodeTransformerNamesGroupsBuilder } from '../interfaces/utils/INodeTransformerNamesGroupsBuilder';
|
||||
import { ITransformersRunner } from '../interfaces/node-transformers/ITransformersRunner';
|
||||
import { IVisitor } from '../interfaces/node-transformers/IVisitor';
|
||||
|
||||
@@ -21,69 +26,113 @@ import { NodeMetadata } from '../node/NodeMetadata';
|
||||
|
||||
@injectable()
|
||||
export class TransformersRunner implements ITransformersRunner {
|
||||
/**
|
||||
* @type {Map<NodeTransformer[], TTransformersRunnerData>}
|
||||
*/
|
||||
private readonly cachedNodeTransformersData: Map<NodeTransformer[], TTransformersRunnerData> = new Map();
|
||||
|
||||
/**
|
||||
* @type {TNodeTransformerFactory}
|
||||
*/
|
||||
private readonly nodeTransformerFactory: TNodeTransformerFactory;
|
||||
|
||||
/**
|
||||
* @type {INodeTransformerNamesGroupsBuilder}
|
||||
*/
|
||||
private readonly nodeTransformerNamesGroupsBuilder: INodeTransformerNamesGroupsBuilder;
|
||||
|
||||
/**
|
||||
* @param {TNodeTransformerFactory} nodeTransformerFactory
|
||||
* @param {INodeTransformerNamesGroupsBuilder} nodeTransformerNamesGroupsBuilder
|
||||
*/
|
||||
constructor (
|
||||
@inject(ServiceIdentifiers.Factory__INodeTransformer) nodeTransformerFactory: TNodeTransformerFactory,
|
||||
@inject(ServiceIdentifiers.Factory__INodeTransformer)
|
||||
nodeTransformerFactory: TNodeTransformerFactory,
|
||||
@inject(ServiceIdentifiers.INodeTransformerNamesGroupsBuilder)
|
||||
nodeTransformerNamesGroupsBuilder: INodeTransformerNamesGroupsBuilder,
|
||||
) {
|
||||
this.nodeTransformerFactory = nodeTransformerFactory;
|
||||
this.nodeTransformerNamesGroupsBuilder = nodeTransformerNamesGroupsBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {T} astTree
|
||||
* @param {NodeTransformer[]} nodeTransformers
|
||||
* @param {NodeTransformer[]} nodeTransformerNames
|
||||
* @param {TransformationStage} transformationStage
|
||||
* @returns {T}
|
||||
*/
|
||||
public transform <T extends ESTree.Node = ESTree.Program> (
|
||||
astTree: T,
|
||||
nodeTransformers: NodeTransformer[],
|
||||
nodeTransformerNames: NodeTransformer[],
|
||||
transformationStage: TransformationStage
|
||||
): T {
|
||||
if (!nodeTransformers.length) {
|
||||
if (!nodeTransformerNames.length) {
|
||||
return astTree;
|
||||
}
|
||||
|
||||
const enterVisitors: IVisitor[] = [];
|
||||
const leaveVisitors: IVisitor[] = [];
|
||||
const nodeTransformersLength: number = nodeTransformers.length;
|
||||
let normalizedNodeTransformers: TNormalizedNodeTransformers;
|
||||
let nodeTransformerNamesGroups: NodeTransformer[][];
|
||||
|
||||
let visitor: IVisitor | null;
|
||||
if (!this.cachedNodeTransformersData.has(nodeTransformerNames)) {
|
||||
normalizedNodeTransformers = this.buildNormalizedNodeTransformers(nodeTransformerNames);
|
||||
nodeTransformerNamesGroups = this.nodeTransformerNamesGroupsBuilder.build(normalizedNodeTransformers);
|
||||
this.cachedNodeTransformersData.set(nodeTransformerNames, [normalizedNodeTransformers, nodeTransformerNamesGroups]);
|
||||
} else {
|
||||
[
|
||||
normalizedNodeTransformers,
|
||||
nodeTransformerNamesGroups
|
||||
] = <TTransformersRunnerData>this.cachedNodeTransformersData.get(nodeTransformerNames);
|
||||
}
|
||||
|
||||
for (let i: number = 0; i < nodeTransformersLength; i++) {
|
||||
visitor = this.nodeTransformerFactory(nodeTransformers[i]).getVisitor(transformationStage);
|
||||
for (const nodeTransformerNamesGroup of nodeTransformerNamesGroups) {
|
||||
const enterVisitors: IVisitor[] = [];
|
||||
const leaveVisitors: IVisitor[] = [];
|
||||
|
||||
if (!visitor) {
|
||||
for (const nodeTransformerName of nodeTransformerNamesGroup) {
|
||||
const nodeTransformer: INodeTransformer = normalizedNodeTransformers[nodeTransformerName];
|
||||
const visitor: IVisitor | null = nodeTransformer.getVisitor(transformationStage);
|
||||
|
||||
if (!visitor) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (visitor.enter) {
|
||||
enterVisitors.push({ enter: visitor.enter });
|
||||
}
|
||||
|
||||
if (visitor.leave) {
|
||||
leaveVisitors.push({ leave: visitor.leave });
|
||||
}
|
||||
}
|
||||
|
||||
if (!enterVisitors.length && !leaveVisitors.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (visitor.enter) {
|
||||
enterVisitors.push({ enter: visitor.enter });
|
||||
}
|
||||
|
||||
if (visitor.leave) {
|
||||
leaveVisitors.push({ leave: visitor.leave });
|
||||
}
|
||||
estraverse.replace(astTree, {
|
||||
enter: this.mergeVisitorsForDirection(enterVisitors, VisitorDirection.Enter),
|
||||
leave: this.mergeVisitorsForDirection(leaveVisitors, VisitorDirection.Leave)
|
||||
});
|
||||
}
|
||||
|
||||
if (!enterVisitors.length && !leaveVisitors.length) {
|
||||
return astTree;
|
||||
}
|
||||
|
||||
estraverse.replace(astTree, {
|
||||
enter: this.mergeVisitorsForDirection(enterVisitors, VisitorDirection.Enter),
|
||||
leave: this.mergeVisitorsForDirection(leaveVisitors, VisitorDirection.Leave)
|
||||
});
|
||||
|
||||
return astTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {NodeTransformer[]} nodeTransformerNames
|
||||
* @returns {TNormalizedNodeTransformers}
|
||||
*/
|
||||
private buildNormalizedNodeTransformers (nodeTransformerNames: NodeTransformer[]): TNormalizedNodeTransformers {
|
||||
return nodeTransformerNames
|
||||
.reduce<TNormalizedNodeTransformers>(
|
||||
(acc: TNormalizedNodeTransformers, nodeTransformerName: NodeTransformer) => ({
|
||||
...acc,
|
||||
[nodeTransformerName]: this.nodeTransformerFactory(nodeTransformerName)
|
||||
}),
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {IVisitor[]} visitors
|
||||
* @param {TVisitorDirection} direction
|
||||
|
||||
@@ -7,6 +7,7 @@ import { IOptions } from '../../interfaces/options/IOptions';
|
||||
import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
|
||||
import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
|
||||
|
||||
import { NodeTransformer } from '../../enums/node-transformers/NodeTransformer';
|
||||
import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
|
||||
|
||||
import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
|
||||
@@ -18,6 +19,14 @@ import { NodeGuards } from '../../node/NodeGuards';
|
||||
*/
|
||||
@injectable()
|
||||
export class SplitStringTransformer extends AbstractNodeTransformer {
|
||||
/**
|
||||
* @type {NodeTransformer[]}
|
||||
*/
|
||||
public runAfter: NodeTransformer[] = [
|
||||
NodeTransformer.ObjectExpressionKeysTransformer,
|
||||
NodeTransformer.TemplateLiteralTransformer
|
||||
];
|
||||
|
||||
/**
|
||||
* @param {IRandomGenerator} randomGenerator
|
||||
* @param {IOptions} options
|
||||
@@ -59,7 +68,7 @@ export class SplitStringTransformer extends AbstractNodeTransformer {
|
||||
switch (transformationStage) {
|
||||
case TransformationStage.Converting:
|
||||
return {
|
||||
leave: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
|
||||
enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
|
||||
if (!this.options.splitStrings) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import { NodeTransformer } from '../../enums/node-transformers/NodeTransformer';
|
||||
|
||||
export type TNodeTransformersRelationEdge = [NodeTransformer, NodeTransformer | null];
|
||||
@@ -0,0 +1,5 @@
|
||||
import { TObject } from '../TObject';
|
||||
|
||||
import { INodeTransformer } from '../../interfaces/node-transformers/INodeTransformer';
|
||||
|
||||
export type TNormalizedNodeTransformers = TObject<INodeTransformer>;
|
||||
@@ -0,0 +1,5 @@
|
||||
import { TNormalizedNodeTransformers } from './TNormalizedNodeTransformers';
|
||||
|
||||
import { NodeTransformer } from '../../enums/node-transformers/NodeTransformer';
|
||||
|
||||
export type TTransformersRunnerData = [TNormalizedNodeTransformers, NodeTransformer[][]];
|
||||
@@ -0,0 +1,216 @@
|
||||
import { injectable } from 'inversify';
|
||||
|
||||
import { ILevelledTopologicalSorter } from '../interfaces/utils/ILevelledTopologicalSorter';
|
||||
|
||||
type TVisitMark = 'ok' | 'visiting';
|
||||
|
||||
interface IVisitMarks <TValue extends string> {
|
||||
[key: string]: TVisitMark;
|
||||
}
|
||||
|
||||
/**
|
||||
* Port and rework of https://github.com/loveencounterflow/ltsort
|
||||
*/
|
||||
@injectable()
|
||||
export class LevelledTopologicalSorter <TValue extends string = string> implements ILevelledTopologicalSorter<TValue> {
|
||||
/**
|
||||
* @type {Map<TValue, TValue[]}
|
||||
*/
|
||||
private readonly graph: Map<TValue, TValue[]> = new Map();
|
||||
|
||||
/**
|
||||
* @param {TValue} precedent
|
||||
* @param {TValue | null} consequent
|
||||
* @returns {this}
|
||||
*/
|
||||
public add (
|
||||
precedent: TValue,
|
||||
consequent: TValue | null = null
|
||||
): this {
|
||||
if (consequent !== null) {
|
||||
return this.link(precedent, consequent);
|
||||
}
|
||||
|
||||
return this.register(precedent);
|
||||
}
|
||||
|
||||
/**
|
||||
* As given in http://en.wikipedia.org/wiki/Topological_sorting
|
||||
*
|
||||
* @returns {TValue[]}
|
||||
*/
|
||||
public sort (): TValue[] {
|
||||
const consequents: TValue[] = Array.from(this.graph.keys());
|
||||
|
||||
const results: TValue[] = [];
|
||||
const marks: IVisitMarks<TValue> = {};
|
||||
|
||||
for (const consequent of consequents) {
|
||||
if (marks[consequent] !== undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.visit(results, marks, consequent);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {TValue[][]}
|
||||
*/
|
||||
public sortByGroups (): TValue[][] {
|
||||
this.sort();
|
||||
|
||||
const resultItemsGroups: TValue[][] = [];
|
||||
|
||||
while (this.hasNodes()) {
|
||||
const rootNodes: TValue[] = this.findRootNodes();
|
||||
|
||||
resultItemsGroups.push(rootNodes);
|
||||
|
||||
for (const rootNode of rootNodes) {
|
||||
this.delete(rootNode);
|
||||
}
|
||||
}
|
||||
|
||||
return resultItemsGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TValue} consequent
|
||||
*/
|
||||
private delete (consequent: TValue): void {
|
||||
const precedents: TValue[] = this.getPrecedents(consequent);
|
||||
|
||||
if (precedents.length) {
|
||||
throw new Error(`Unable to remove non-root node: ${consequent}`);
|
||||
}
|
||||
|
||||
this.graph.delete(consequent);
|
||||
|
||||
const precedentsGroups: string[][] = Array.from(this.graph.values());
|
||||
|
||||
for (const precedentsGroup of precedentsGroups) {
|
||||
const precedentsCount: number = precedentsGroup.length - 1;
|
||||
|
||||
for (let index: number = precedentsCount; index >= 0; index = index - 1) {
|
||||
if (precedentsGroup[index] !== consequent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
precedentsGroup.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {TValue[]}
|
||||
*/
|
||||
private findRootNodes (): TValue[] {
|
||||
const consequents: TValue[] = Array.from(this.graph.keys());
|
||||
const rootNodes: TValue[] = [];
|
||||
|
||||
for (const consequent of consequents) {
|
||||
if (!this.hasPrecedents(consequent)) {
|
||||
rootNodes.push(consequent);
|
||||
}
|
||||
}
|
||||
|
||||
return rootNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TValue} consequent
|
||||
* @returns {TValue[]}
|
||||
*/
|
||||
private getPrecedents (consequent: TValue): TValue[] {
|
||||
const precedents: TValue[] | undefined = this.graph.get(consequent);
|
||||
|
||||
if (!precedents) {
|
||||
throw new Error(`Unknown node: ${consequent}`);
|
||||
}
|
||||
|
||||
return precedents;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private hasNodes (): boolean {
|
||||
return this.graph.size > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TValue} consequent
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private hasPrecedents (consequent: TValue): boolean {
|
||||
return this.getPrecedents(consequent).length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TValue} precedent
|
||||
* @param {TValue} consequent
|
||||
* @returns {this}
|
||||
*/
|
||||
private link (precedent: TValue, consequent: TValue): this {
|
||||
this.register(precedent);
|
||||
this.register(consequent);
|
||||
|
||||
const target: TValue[] | undefined = this.graph.get(consequent);
|
||||
|
||||
if (target && !target.includes(precedent)) {
|
||||
target.push(precedent);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TValue} name
|
||||
* @returns {this}
|
||||
*/
|
||||
private register (name: TValue): this {
|
||||
if (!this.graph.has(name)) {
|
||||
this.graph.set(name, []);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TValue[]} results
|
||||
* @param {IVisitMarks<TValue>} marks
|
||||
* @param {TValue} name
|
||||
* @returns {null}
|
||||
*/
|
||||
private visit (
|
||||
results: TValue[],
|
||||
marks: IVisitMarks<TValue>,
|
||||
name: TValue
|
||||
): void {
|
||||
const mark: TVisitMark = marks[name];
|
||||
|
||||
if (mark === 'visiting') {
|
||||
throw new Error(`Detected cycle involving node: ${name}`);
|
||||
}
|
||||
|
||||
if (mark) {
|
||||
return;
|
||||
}
|
||||
|
||||
marks[name] = 'visiting';
|
||||
|
||||
const precedents: TValue[] = this.getPrecedents(name);
|
||||
|
||||
for (const precedent of precedents) {
|
||||
this.visit(results, marks, precedent);
|
||||
}
|
||||
|
||||
marks[name] = 'ok';
|
||||
results.push(name);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { ServiceIdentifiers } from '../container/ServiceIdentifiers';
|
||||
|
||||
import { TNodeTransformersRelationEdge } from '../types/node-transformers/TNodeTransformersRelationEdge';
|
||||
import { TNormalizedNodeTransformers } from '../types/node-transformers/TNormalizedNodeTransformers';
|
||||
|
||||
import { ILevelledTopologicalSorter } from '../interfaces/utils/ILevelledTopologicalSorter';
|
||||
import { INodeTransformer } from '../interfaces/node-transformers/INodeTransformer';
|
||||
import { INodeTransformerNamesGroupsBuilder } from '../interfaces/utils/INodeTransformerNamesGroupsBuilder';
|
||||
|
||||
import { NodeTransformer } from '../enums/node-transformers/NodeTransformer';
|
||||
|
||||
@injectable()
|
||||
export class NodeTransformerNamesGroupsBuilder implements INodeTransformerNamesGroupsBuilder {
|
||||
/**
|
||||
* @type {ILevelledTopologicalSorter<NodeTransformer>}
|
||||
*/
|
||||
private readonly levelledTopologicalSorter: ILevelledTopologicalSorter<NodeTransformer>;
|
||||
|
||||
constructor (
|
||||
@inject(ServiceIdentifiers.ILevelledTopologicalSorter)
|
||||
levelledTopologicalSorter: ILevelledTopologicalSorter<NodeTransformer>
|
||||
) {
|
||||
this.levelledTopologicalSorter = levelledTopologicalSorter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds sorted NodeTransformer names by topological sort with levels
|
||||
*
|
||||
* For example, if SplitString transformer has following dependencies inside `runAfter` property:
|
||||
* - NodeTransformer.ObjectExpressionKeysTransformer,
|
||||
* - NodeTransformer.TemplateLiteralTransformer
|
||||
*
|
||||
* Than result node transformer names groups will be like:
|
||||
* [
|
||||
* [
|
||||
* SomeTransformerA,
|
||||
* ObjectExpressionKeysTransformer,
|
||||
* TemplateLiteralTransformer,
|
||||
* SomeTransformerB
|
||||
* ],
|
||||
* [
|
||||
* SplitStringTransformer
|
||||
* ]
|
||||
* ]
|
||||
*
|
||||
* @param {TNormalizedNodeTransformers} normalizedNodeTransformers
|
||||
* @returns {NodeTransformer[][]}
|
||||
*/
|
||||
public build (normalizedNodeTransformers: TNormalizedNodeTransformers): NodeTransformer[][] {
|
||||
const nodeTransformerNames: NodeTransformer[] = <NodeTransformer[]>Object.keys(normalizedNodeTransformers);
|
||||
const relationEdges: TNodeTransformersRelationEdge[] = this.buildNodeTransformersRelationEdges(
|
||||
nodeTransformerNames,
|
||||
normalizedNodeTransformers
|
||||
);
|
||||
|
||||
for (const [precedent, consequent] of relationEdges) {
|
||||
this.levelledTopologicalSorter.add(precedent, consequent);
|
||||
}
|
||||
|
||||
return this.levelledTopologicalSorter.sortByGroups();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {NodeTransformer[]} nodeTransformerNames
|
||||
* @param {TNormalizedNodeTransformers} normalizedNodeTransformers
|
||||
* @returns {[NodeTransformer, NodeTransformer][]}
|
||||
*/
|
||||
private buildNodeTransformersRelationEdges (
|
||||
nodeTransformerNames: NodeTransformer[],
|
||||
normalizedNodeTransformers: TNormalizedNodeTransformers
|
||||
): TNodeTransformersRelationEdge[] {
|
||||
const relationEdges: TNodeTransformersRelationEdge[] = [];
|
||||
|
||||
for (const nodeTransformerName of nodeTransformerNames) {
|
||||
const nodeTransformer: INodeTransformer = normalizedNodeTransformers[nodeTransformerName];
|
||||
const runAfterRelations: NodeTransformer[] | undefined = nodeTransformer.runAfter;
|
||||
|
||||
if (!runAfterRelations || !runAfterRelations.length) {
|
||||
relationEdges.push([nodeTransformerName, null]);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const runAfterRelation of runAfterRelations) {
|
||||
const isUnknownRelation: boolean = !normalizedNodeTransformers[runAfterRelation];
|
||||
|
||||
if (isUnknownRelation) {
|
||||
relationEdges.push([nodeTransformerName, null]);
|
||||
continue;
|
||||
}
|
||||
|
||||
relationEdges.push([runAfterRelation, nodeTransformerName]);
|
||||
}
|
||||
}
|
||||
|
||||
return relationEdges;
|
||||
}
|
||||
}
|
||||
+4
-6
@@ -6,18 +6,16 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
|
||||
|
||||
let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
|
||||
`
|
||||
function hi() {
|
||||
console.log("Hello World!");
|
||||
}
|
||||
hi();
|
||||
var s = {
|
||||
'abcdefg': 'abcdefg'
|
||||
};
|
||||
`,
|
||||
{
|
||||
...NO_ADDITIONAL_NODES_PRESET,
|
||||
compact: false,
|
||||
splitStrings: true,
|
||||
splitStringsChunkLength: 4,
|
||||
controlFlowFlattening: true,
|
||||
controlFlowFlatteningThreshold: 1
|
||||
transformObjectKeys: true
|
||||
}
|
||||
).getObfuscatedCode();
|
||||
|
||||
|
||||
+21
@@ -162,4 +162,25 @@ describe('SplitStringTransformer', () => {
|
||||
assert.match(obfuscatedCode, /^var *test *= *{\['ab' *\+ *'cd' *\+ *'ef' *\+ *'g'] *: *0x1};$/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Variant #10: Integration with `transformObjectKeys` option', () => {
|
||||
it('should correctly transform string when `transformObjectKeys` option is enabled', () => {
|
||||
const code: string = readFileAsString(__dirname + '/fixtures/object-string-literal.js');
|
||||
|
||||
obfuscatedCode = JavaScriptObfuscator.obfuscate(
|
||||
code,
|
||||
{
|
||||
...NO_ADDITIONAL_NODES_PRESET,
|
||||
splitStrings: true,
|
||||
splitStringsChunkLength: 2,
|
||||
transformObjectKeys: true
|
||||
}
|
||||
).getObfuscatedCode();
|
||||
|
||||
assert.match(
|
||||
obfuscatedCode,
|
||||
/^var *test *= *{}; *test\['ab' *\+ *'cd' *\+ *'ef' *\+ *'g'] *= *'ab' *\+ *'cd' *\+ *'ef' *\+ *'g';$/
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -34,6 +34,7 @@ import './unit-tests/storages/MapStorage.spec';
|
||||
import './unit-tests/utils/ArrayUtils.spec';
|
||||
import './unit-tests/utils/CryptUtils.spec';
|
||||
import './unit-tests/utils/EscapeSequenceEncoder.spec';
|
||||
import './unit-tests/utils/LevelledTopologicalSorter.spec';
|
||||
import './unit-tests/utils/NumberUtils.spec';
|
||||
import './unit-tests/utils/Utils.spec';
|
||||
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
import 'reflect-metadata';
|
||||
|
||||
import { assert } from 'chai';
|
||||
|
||||
import { InversifyContainerFacade } from '../../../src/container/InversifyContainerFacade';
|
||||
import { ServiceIdentifiers } from '../../../src/container/ServiceIdentifiers';
|
||||
|
||||
import { IInversifyContainerFacade } from '../../../src/interfaces/container/IInversifyContainerFacade';
|
||||
import { ILevelledTopologicalSorter } from '../../../src/interfaces/utils/ILevelledTopologicalSorter';
|
||||
|
||||
|
||||
describe('EscapeSequenceEncoder', () => {
|
||||
describe('encode', () => {
|
||||
let levelledTopologicalSorter: ILevelledTopologicalSorter;
|
||||
|
||||
beforeEach(() => {
|
||||
const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade();
|
||||
|
||||
inversifyContainerFacade.load('', '', {});
|
||||
levelledTopologicalSorter = inversifyContainerFacade
|
||||
.get<ILevelledTopologicalSorter>(ServiceIdentifiers.ILevelledTopologicalSorter);
|
||||
});
|
||||
|
||||
describe('Base sort', () => {
|
||||
beforeEach(() => {
|
||||
levelledTopologicalSorter.add('A', 'B');
|
||||
levelledTopologicalSorter.add('B');
|
||||
levelledTopologicalSorter.add('C', 'B');
|
||||
levelledTopologicalSorter.add('D');
|
||||
levelledTopologicalSorter.add('E');
|
||||
levelledTopologicalSorter.add('F', 'A');
|
||||
levelledTopologicalSorter.add('F', 'E');
|
||||
});
|
||||
|
||||
describe('Variant #1: Base linear sort', () => {
|
||||
const expectedSortedItems: string[] = [
|
||||
'F',
|
||||
'A',
|
||||
'C',
|
||||
'B',
|
||||
'D',
|
||||
'E',
|
||||
];
|
||||
|
||||
let sortedItems: string[];
|
||||
|
||||
beforeEach(() => {
|
||||
sortedItems = levelledTopologicalSorter.sort();
|
||||
});
|
||||
|
||||
it('should topologically linear sort items', () => {
|
||||
assert.deepEqual(sortedItems, expectedSortedItems);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Variant #2: Base sort with grouping', () => {
|
||||
const expectedSortedItems: string[][] = [
|
||||
['C', 'D', 'F'],
|
||||
['A', 'E'],
|
||||
['B']
|
||||
];
|
||||
|
||||
let sortedItems: string[][];
|
||||
|
||||
beforeEach(() => {
|
||||
sortedItems = levelledTopologicalSorter.sortByGroups();
|
||||
});
|
||||
|
||||
it('should topologically linear sort items', () => {
|
||||
assert.deepEqual(sortedItems, expectedSortedItems);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Sort without relations', () => {
|
||||
beforeEach(() => {
|
||||
levelledTopologicalSorter.add('A');
|
||||
levelledTopologicalSorter.add('B');
|
||||
levelledTopologicalSorter.add('C');
|
||||
levelledTopologicalSorter.add('D');
|
||||
levelledTopologicalSorter.add('E');
|
||||
levelledTopologicalSorter.add('F');
|
||||
});
|
||||
|
||||
describe('Variant #1: Should sort items without relations', () => {
|
||||
const expectedSortedItems: string[] = [
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
'D',
|
||||
'E',
|
||||
'F'
|
||||
];
|
||||
|
||||
let sortedItems: string[];
|
||||
|
||||
beforeEach(() => {
|
||||
sortedItems = levelledTopologicalSorter.sort();
|
||||
});
|
||||
|
||||
it('should topologically linear sort items', () => {
|
||||
assert.deepEqual(sortedItems, expectedSortedItems);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Variant #2: Should sort items without relations with grouping', () => {
|
||||
const expectedSortedItems: string[][] = [
|
||||
['A', 'B', 'C', 'D', 'E', 'F']
|
||||
];
|
||||
|
||||
let sortedItems: string[][];
|
||||
|
||||
beforeEach(() => {
|
||||
sortedItems = levelledTopologicalSorter.sortByGroups();
|
||||
});
|
||||
|
||||
it('should topologically linear sort items', () => {
|
||||
assert.deepEqual(sortedItems, expectedSortedItems);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user