Browse Source

* The `splitStrings` option now correctly works with `transformObjectKeys` option
* Internal `TransformersRunner` rework to support topological sort of node transformers

sanex3339 5 năm trước cách đây
mục cha
commit
c313802011

+ 5 - 0
CHANGELOG.md

@@ -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

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/index.browser.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/index.cli.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/index.js


+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "javascript-obfuscator",
-  "version": "0.19.2",
+  "version": "0.19.3",
   "description": "JavaScript obfuscator",
   "keywords": [
     "obfuscator",

+ 2 - 0
src/container/ServiceIdentifiers.ts

@@ -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',

+ 13 - 0
src/container/modules/utils/UtilsModule.ts

@@ -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();
 });

+ 6 - 0
src/interfaces/node-transformers/INodeTransformer.d.ts

@@ -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}

+ 21 - 0
src/interfaces/utils/ILevelledTopologicalSorter.d.ts

@@ -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[][];
+}

+ 11 - 0
src/interfaces/utils/INodeTransformerNamesGroupsBuilder.d.ts

@@ -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[][];
+}

+ 6 - 0
src/node-transformers/AbstractNodeTransformer.ts

@@ -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}
      */

+ 74 - 25
src/node-transformers/TransformersRunner.ts

@@ -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[][];
+
+        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);
+        }
 
-        let visitor: IVisitor | null;
+        for (const nodeTransformerNamesGroup of nodeTransformerNamesGroups) {
+            const enterVisitors: IVisitor[] = [];
+            const leaveVisitors: IVisitor[] = [];
 
-        for (let i: number = 0; i < nodeTransformersLength; i++) {
-            visitor = this.nodeTransformerFactory(nodeTransformers[i]).getVisitor(transformationStage);
+            for (const nodeTransformerName of nodeTransformerNamesGroup) {
+                const nodeTransformer: INodeTransformer = normalizedNodeTransformers[nodeTransformerName];
+                const visitor: IVisitor | null = nodeTransformer.getVisitor(transformationStage);
 
-            if (!visitor) {
-                continue;
-            }
+                if (!visitor) {
+                    continue;
+                }
 
-            if (visitor.enter) {
-                enterVisitors.push({ enter: visitor.enter });
+                if (visitor.enter) {
+                    enterVisitors.push({ enter: visitor.enter });
+                }
+
+                if (visitor.leave) {
+                    leaveVisitors.push({ leave: visitor.leave });
+                }
             }
 
-            if (visitor.leave) {
-                leaveVisitors.push({ leave: visitor.leave });
+            if (!enterVisitors.length && !leaveVisitors.length) {
+                continue;
             }
-        }
 
-        if (!enterVisitors.length && !leaveVisitors.length) {
-            return astTree;
+            estraverse.replace(astTree, {
+                enter: this.mergeVisitorsForDirection(enterVisitors, VisitorDirection.Enter),
+                leave: this.mergeVisitorsForDirection(leaveVisitors, VisitorDirection.Leave)
+            });
         }
 
-        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

+ 10 - 1
src/node-transformers/converting-transformers/SplitStringTransformer.ts

@@ -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;
                         }

+ 3 - 0
src/types/node-transformers/TNodeTransformersRelationEdge.d.ts

@@ -0,0 +1,3 @@
+import { NodeTransformer } from '../../enums/node-transformers/NodeTransformer';
+
+export type TNodeTransformersRelationEdge = [NodeTransformer, NodeTransformer | null];

+ 5 - 0
src/types/node-transformers/TNormalizedNodeTransformers.d.ts

@@ -0,0 +1,5 @@
+import { TObject } from '../TObject';
+
+import { INodeTransformer } from '../../interfaces/node-transformers/INodeTransformer';
+
+export type TNormalizedNodeTransformers = TObject<INodeTransformer>;

+ 5 - 0
src/types/node-transformers/TTransformersRunnerData.d.ts

@@ -0,0 +1,5 @@
+import { TNormalizedNodeTransformers } from './TNormalizedNodeTransformers';
+
+import { NodeTransformer } from '../../enums/node-transformers/NodeTransformer';
+
+export type TTransformersRunnerData = [TNormalizedNodeTransformers, NodeTransformer[][]];

+ 216 - 0
src/utils/LevelledTopologicalSorter.ts

@@ -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 precedents: 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.precedents.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} name
+     */
+    private delete (name: TValue): void {
+        const precedents: TValue[] = this.getPrecedents(name);
+
+        if (precedents.length) {
+            throw new Error(`Unable to remove non-root node: ${name}`);
+        }
+
+        this.precedents.delete(name);
+
+        const precedentsGroups: string[][] = Array.from(this.precedents.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] !== name) {
+                    continue;
+                }
+
+                precedentsGroup.splice(index, 1);
+            }
+        }
+    }
+
+    /**
+     * @returns {TValue[]}
+     */
+    private findRootNodes (): TValue[] {
+        const precedents: TValue[] = Array.from(this.precedents.keys());
+        const rootNodes: TValue[] = [];
+
+        for (const name of precedents) {
+            if (!this.hasPrecedents(name)) {
+                rootNodes.push(name);
+            }
+        }
+
+        return rootNodes;
+    }
+
+    /**
+     * @param {TValue} name
+     * @returns {TValue[]}
+     */
+    private getPrecedents (name: TValue): TValue[] {
+        const precedents: TValue[] | undefined = this.precedents.get(name);
+
+        if (!precedents) {
+            throw new Error(`Unknown node: ${name}`);
+        }
+
+        return precedents;
+    }
+
+    /**
+     * @returns {boolean}
+     */
+    private hasNodes (): boolean {
+        return this.precedents.size > 0;
+    }
+
+    /**
+     * @param {TValue} name
+     * @returns {boolean}
+     */
+    private hasPrecedents (name: TValue): boolean {
+        return this.getPrecedents(name).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.precedents.get(consequent);
+
+        if (target && !target.includes(precedent)) {
+            target.push(precedent);
+        }
+
+        return this;
+    }
+
+    /**
+     * @param {TValue} name
+     * @returns {this}
+     */
+    private register (name: TValue): this {
+        if (!this.precedents.has(name)) {
+            this.precedents.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 references: TValue[] = this.getPrecedents(name);
+
+        for (const precedent of references) {
+            this.visit(results, marks, precedent);
+        }
+
+        marks[name] = 'ok';
+        results.push(name);
+
+        return;
+    }
+}

+ 81 - 0
src/utils/NodeTransformerNamesGroupsBuilder.ts

@@ -0,0 +1,81 @@
+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
+     *
+     * @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
test/dev/dev.ts

@@ -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 - 0
test/functional-tests/node-transformers/converting-transformers/split-string-transformer/SplitStringTransformer.spec.ts

@@ -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';$/
+            );
+        });
+    });
 });

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác