Jelajahi Sumber

Shifted indexes of string array wrappers #4: fixed wrappers calls and added tests

sanex 4 tahun lalu
induk
melakukan
835581db64

File diff ditekan karena terlalu besar
+ 0 - 0
dist/index.browser.js


File diff ditekan karena terlalu besar
+ 0 - 0
dist/index.cli.js


File diff ditekan karena terlalu besar
+ 0 - 0
dist/index.js


+ 54 - 24
src/node-transformers/string-array-transformers/StringArrayScopeCallsWrapperTransformer.ts

@@ -6,7 +6,6 @@ import * as ESTree from 'estree';
 import { TInitialData } from '../../types/TInitialData';
 import { TInitialData } from '../../types/TInitialData';
 import { TNodeWithLexicalScopeStatements } from '../../types/node/TNodeWithLexicalScopeStatements';
 import { TNodeWithLexicalScopeStatements } from '../../types/node/TNodeWithLexicalScopeStatements';
 import { TStatement } from '../../types/node/TStatement';
 import { TStatement } from '../../types/node/TStatement';
-import { TStringArrayEncoding } from '../../types/options/TStringArrayEncoding';
 import { TStringArrayScopeCallsWrapperNamesDataByEncoding } from '../../types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrapperNamesDataByEncoding';
 import { TStringArrayScopeCallsWrapperNamesDataByEncoding } from '../../types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrapperNamesDataByEncoding';
 import { TStringArrayTransformerCustomNodeFactory } from '../../types/container/custom-nodes/TStringArrayTransformerCustomNodeFactory';
 import { TStringArrayTransformerCustomNodeFactory } from '../../types/container/custom-nodes/TStringArrayTransformerCustomNodeFactory';
 
 
@@ -135,9 +134,6 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
 
 
         const stringArrayScopeCallsWrapperNamesDataList: (IStringArrayScopeCallsWrapperNamesData | undefined)[] =
         const stringArrayScopeCallsWrapperNamesDataList: (IStringArrayScopeCallsWrapperNamesData | undefined)[] =
             Object.values(stringArrayScopeCallsWrapperNamesDataByEncoding);
             Object.values(stringArrayScopeCallsWrapperNamesDataByEncoding);
-        const stringArrayScopeCallsWrapperShiftedIndex: number = this.options.stringArrayWrappersChainedCalls
-            ? stringArrayScopeCallsWrapperNamesLexicalScopeData.scopeShiftedIndex
-            : stringArrayScopeCallsWrapperNamesLexicalScopeData.resultShiftedIndex;
 
 
         // iterates over data for each encoding type
         // iterates over data for each encoding type
         for (const stringArrayScopeCallsWrapperNamesData of stringArrayScopeCallsWrapperNamesDataList) {
         for (const stringArrayScopeCallsWrapperNamesData of stringArrayScopeCallsWrapperNamesDataList) {
@@ -145,7 +141,7 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
                 continue;
                 continue;
             }
             }
 
 
-            const {encoding, names} = stringArrayScopeCallsWrapperNamesData;
+            const {names} = stringArrayScopeCallsWrapperNamesData;
             const namesLength: number = names.length;
             const namesLength: number = names.length;
 
 
             /**
             /**
@@ -154,12 +150,18 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
              */
              */
             for (let i = namesLength - 1; i >= 0; i--) {
             for (let i = namesLength - 1; i >= 0; i--) {
                 const stringArrayScopeCallsWrapperName: string = names[i];
                 const stringArrayScopeCallsWrapperName: string = names[i];
-                const upperStringArrayCallsWrapperName: string = this.getUpperStringArrayCallsWrapperName(encoding);
+                const [
+                    upperStringArrayCallsWrapperName,
+                    upperStringArrayCallsWrapperShiftedIndex,
+                ] = this.getUpperStringArrayCallsWrapperName(
+                    stringArrayScopeCallsWrapperNamesData,
+                    stringArrayScopeCallsWrapperNamesLexicalScopeData,
+                );
 
 
                 const stringArrayScopeCallsWrapperNode: TStatement[] = this.getStringArrayScopeCallsWrapperNode(
                 const stringArrayScopeCallsWrapperNode: TStatement[] = this.getStringArrayScopeCallsWrapperNode(
                     stringArrayScopeCallsWrapperName,
                     stringArrayScopeCallsWrapperName,
                     upperStringArrayCallsWrapperName,
                     upperStringArrayCallsWrapperName,
-                    stringArrayScopeCallsWrapperShiftedIndex
+                    upperStringArrayCallsWrapperShiftedIndex
                 );
                 );
 
 
                 NodeAppender.prepend(
                 NodeAppender.prepend(
@@ -173,40 +175,68 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
     }
     }
 
 
     /**
     /**
-     * @param {TStringArrayEncoding} encoding
-     * @returns {string}
+     * @param {IStringArrayScopeCallsWrapperNamesData} stringArrayScopeCallsWrapperNamesData
+     * @param {IStringArrayScopeCallsWrapperLexicalScopeData} stringArrayScopeCallsWrapperNamesLexicalScopeData
+     * @returns {[name: string, index: number]}
      */
      */
-    private getRootStringArrayCallsWrapperName (encoding: TStringArrayEncoding): string {
-        return this.stringArrayStorage.getStorageCallsWrapperName(encoding);
+    private getRootStringArrayCallsWrapperData (
+        stringArrayScopeCallsWrapperNamesData: IStringArrayScopeCallsWrapperNamesData,
+        stringArrayScopeCallsWrapperNamesLexicalScopeData: IStringArrayScopeCallsWrapperLexicalScopeData
+    ): [name: string, index: number] {
+        const {encoding} = stringArrayScopeCallsWrapperNamesData;
+        const {resultShiftedIndex} = stringArrayScopeCallsWrapperNamesLexicalScopeData;
+
+        return [
+            this.stringArrayStorage.getStorageCallsWrapperName(encoding),
+            resultShiftedIndex
+        ];
     }
     }
 
 
     /**
     /**
-     * @param {TStringArrayEncoding} encoding
-     * @returns {string}
+     * @param {IStringArrayScopeCallsWrapperNamesData} stringArrayScopeCallsWrapperNamesData
+     * @param {IStringArrayScopeCallsWrapperLexicalScopeData} stringArrayScopeCallsWrapperNamesLexicalScopeData
+     * @returns {[name: string, index: number]}
      */
      */
-    private getUpperStringArrayCallsWrapperName (encoding: TStringArrayEncoding): string {
-        const rootStringArrayCallsWrapperName: string = this.getRootStringArrayCallsWrapperName(encoding);
+    private getUpperStringArrayCallsWrapperName (
+        stringArrayScopeCallsWrapperNamesData: IStringArrayScopeCallsWrapperNamesData,
+        stringArrayScopeCallsWrapperNamesLexicalScopeData: IStringArrayScopeCallsWrapperLexicalScopeData
+    ): [name: string, index: number] {
+        const {encoding} = stringArrayScopeCallsWrapperNamesData;
+        const {scopeShiftedIndex} = stringArrayScopeCallsWrapperNamesLexicalScopeData;
+
+        const rootStringArrayCallsWrapperData = this.getRootStringArrayCallsWrapperData(
+            stringArrayScopeCallsWrapperNamesData,
+            stringArrayScopeCallsWrapperNamesLexicalScopeData
+        );
 
 
         if (!this.options.stringArrayWrappersChainedCalls) {
         if (!this.options.stringArrayWrappersChainedCalls) {
-            return rootStringArrayCallsWrapperName;
+            return rootStringArrayCallsWrapperData;
         }
         }
 
 
-        const parentLexicalScopeBodyNode: TNodeWithLexicalScopeStatements | undefined =
-            this.visitedLexicalScopeNodesStackStorage.getLastElement();
+        const parentLexicalScopeBodyNode: TNodeWithLexicalScopeStatements | null =
+            this.visitedLexicalScopeNodesStackStorage.getLastElement()
+            ?? null;
 
 
         if (!parentLexicalScopeBodyNode) {
         if (!parentLexicalScopeBodyNode) {
-            return rootStringArrayCallsWrapperName;
+            return rootStringArrayCallsWrapperData;
         }
         }
 
 
         const parentLexicalScopeNamesDataByEncoding: TStringArrayScopeCallsWrapperNamesDataByEncoding | null = this.stringArrayScopeCallsWrapperNamesDataStorage
         const parentLexicalScopeNamesDataByEncoding: TStringArrayScopeCallsWrapperNamesDataByEncoding | null = this.stringArrayScopeCallsWrapperNamesDataStorage
             .get(parentLexicalScopeBodyNode) ?? null;
             .get(parentLexicalScopeBodyNode) ?? null;
         const parentLexicalScopeNames: string[] | null = parentLexicalScopeNamesDataByEncoding?.[encoding]?.names ?? null;
         const parentLexicalScopeNames: string[] | null = parentLexicalScopeNamesDataByEncoding?.[encoding]?.names ?? null;
 
 
-        return parentLexicalScopeNames?.length
-            ? this.randomGenerator
-                .getRandomGenerator()
-                .pickone(parentLexicalScopeNames)
-            : rootStringArrayCallsWrapperName;
+        if (!parentLexicalScopeNames?.length) {
+            return rootStringArrayCallsWrapperData;
+        }
+
+        const upperStringArrayCallsWrapperName: string = this.randomGenerator
+            .getRandomGenerator()
+            .pickone(parentLexicalScopeNames);
+
+        return [
+            upperStringArrayCallsWrapperName,
+            scopeShiftedIndex
+        ];
     }
     }
 
 
     /**
     /**

+ 27 - 14
test/dev/dev.ts

@@ -3,37 +3,50 @@
 import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNodes';
 import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNodes';
 import { IdentifierNamesGenerator } from '../../src/enums/generators/identifier-names-generators/IdentifierNamesGenerator';
 import { IdentifierNamesGenerator } from '../../src/enums/generators/identifier-names-generators/IdentifierNamesGenerator';
 import { StringArrayWrappersType } from '../../src/enums/node-transformers/string-array-transformers/StringArrayWrappersType';
 import { StringArrayWrappersType } from '../../src/enums/node-transformers/string-array-transformers/StringArrayWrappersType';
+import { StringArrayEncoding } from '../../src/enums/node-transformers/string-array-transformers/StringArrayEncoding';
 
 
 (function () {
 (function () {
     const JavaScriptObfuscator: any = require('../../index');
     const JavaScriptObfuscator: any = require('../../index');
 
 
     let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
     let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
         `
         `
-            const foo = 'foo'
-            const bar = 'bar';
-            const baz = 'baz';
+            const foo = 'aaa';
+
+            function test (a, b) {
+                const bar = 'bbb';
+                const baz = 'ccc';
             
             
-            function test (arg = 'bark') {
-                const hawk = 'hawk';
-                const eagle = 'eagle';
-                
-                console.log(arg, hawk, eagle);
-            }
+                function test1 (a, b) {
+                    const bark = 'ddd';
+                    const hawk = 'eee';
+            
+                    function test2 (a, b) {
+                        const bark = 'ddd';
+                        const hawk = 'eee';
             
             
-            console.log(foo, bar, baz);
+                        return bark + hawk;
+                    }
+            
+                    return bark + hawk;
+                }
+            
+                return bar + baz + test1();
+            }
             
             
-            test();
+            foo + test();
         `,
         `,
         {
         {
             ...NO_ADDITIONAL_NODES_PRESET,
             ...NO_ADDITIONAL_NODES_PRESET,
             compact: false,
             compact: false,
             identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
             identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
-            renameGlobals: true,
             stringArray: true,
             stringArray: true,
-            transformObjectKeys: true,
             stringArrayThreshold: 1,
             stringArrayThreshold: 1,
+            stringArrayEncoding: [
+                StringArrayEncoding.None,
+                StringArrayEncoding.Rc4
+            ],
             stringArrayWrappersChainedCalls: true,
             stringArrayWrappersChainedCalls: true,
-            stringArrayWrappersCount: 2,
+            stringArrayWrappersCount: 5,
             stringArrayWrappersType: StringArrayWrappersType.Function
             stringArrayWrappersType: StringArrayWrappersType.Function
         }
         }
     ).getObfuscatedCode();
     ).getObfuscatedCode();

+ 292 - 1
test/functional-tests/node-transformers/string-array-transformers/string-array-scope-calls-wrapper-transformer/StringArrayScopeCallsWrapperTransformer.spec.ts

@@ -2,6 +2,7 @@ import { assert } from 'chai';
 
 
 import { IdentifierNamesGenerator } from '../../../../../src/enums/generators/identifier-names-generators/IdentifierNamesGenerator';
 import { IdentifierNamesGenerator } from '../../../../../src/enums/generators/identifier-names-generators/IdentifierNamesGenerator';
 import { StringArrayEncoding } from '../../../../../src/enums/node-transformers/string-array-transformers/StringArrayEncoding';
 import { StringArrayEncoding } from '../../../../../src/enums/node-transformers/string-array-transformers/StringArrayEncoding';
+import { StringArrayWrappersType } from '../../../../../src/enums/node-transformers/string-array-transformers/StringArrayWrappersType';
 
 
 import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes';
 import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes';
 
 
@@ -16,7 +17,7 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
         describe('Variant #1: root scope', () => {
         describe('Variant #1: root scope', () => {
             describe('Variant #1: option value value is lower then count `literal` nodes in the scope', () => {
             describe('Variant #1: option value value is lower then count `literal` nodes in the scope', () => {
                 const stringArrayCallRegExp: RegExp = new RegExp(
                 const stringArrayCallRegExp: RegExp = new RegExp(
-                    'return _0x([a-f0-9]){4,6};' +
+                        'return _0x([a-f0-9]){4,6};' +
                     '};' +
                     '};' +
                     'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
                     'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
                     'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
                     'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
@@ -572,6 +573,296 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                 });
                 });
             });
             });
         });
         });
+
+        describe('Variant #7: `stringArrayWrappersType` option has `Function` value', () => {
+            const hexadecimalIndexMatch: string = '0x[a-z0-9]{1,3}';
+
+            describe('Variant #1: base', () => {
+                    const stringArrayCallRegExp: RegExp = new RegExp(
+                        'const f *= *function *\\(c, *d\\) *{' +
+                            `return b\\(c *-(?: -)?'${hexadecimalIndexMatch}', *d\\);` +
+                        '};' +
+                        `const foo *= *f\\(-? *'${hexadecimalIndexMatch}'\\);` +
+                        `const bar *= *f\\(-? *'${hexadecimalIndexMatch}'\\);` +
+                        `const baz *= *f\\(-? *'${hexadecimalIndexMatch}'\\);` +
+                        'function test *\\( *\\) *{' +
+                            'const g *= *function *\\(c, *d\\) *{' +
+                                `return b\\(c *-(?: -)?'${hexadecimalIndexMatch}', *d\\);` +
+                            '};' +
+                            `const c *= *g\\(-? *'${hexadecimalIndexMatch}'\\);` +
+                            `const d *= *g\\(-? *'${hexadecimalIndexMatch}'\\);` +
+                            `const e *= *g\\(-? *'${hexadecimalIndexMatch}'\\);` +
+                        '}'
+                    );
+
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/wrappers-count-const.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
+                                stringArray: true,
+                                stringArrayThreshold: 1,
+                                stringArrayWrappersChainedCalls: false,
+                                stringArrayWrappersCount: 1,
+                                stringArrayWrappersType: StringArrayWrappersType.Function
+                            }
+                        ).getObfuscatedCode();
+                    });
+
+                    it('should add correct scope calls wrappers', () => {
+                        assert.match(obfuscatedCode, stringArrayCallRegExp);
+                    });
+                });
+
+            describe('Variant #2: correct chained calls', () => {
+                    const stringArrayCallRegExp: RegExp = new RegExp(
+                        'const f *= *function *\\(c, *d\\) *{' +
+                            `return b\\(c *-(?: -)?'${hexadecimalIndexMatch}', *d\\);` +
+                        '};' +
+                        `const foo *= *f\\(-? *'${hexadecimalIndexMatch}'\\);` +
+                        `const bar *= *f\\(-? *'${hexadecimalIndexMatch}'\\);` +
+                        `const baz *= *f\\(-? *'${hexadecimalIndexMatch}'\\);` +
+                        'function test *\\( *\\) *{' +
+                            'const g *= *function *\\(c, *d\\) *{' +
+                                `return f\\(c *-(?: -)?'${hexadecimalIndexMatch}', *d\\);` +
+                            '};' +
+                            `const c *= *g\\(-? *'${hexadecimalIndexMatch}'\\);` +
+                            `const d *= *g\\(-? *'${hexadecimalIndexMatch}'\\);` +
+                            `const e *= *g\\(-? *'${hexadecimalIndexMatch}'\\);` +
+                        '}'
+                    );
+
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/wrappers-count-const.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
+                                stringArray: true,
+                                stringArrayThreshold: 1,
+                                stringArrayWrappersChainedCalls: true,
+                                stringArrayWrappersCount: 1,
+                                stringArrayWrappersType: StringArrayWrappersType.Function
+                            }
+                        ).getObfuscatedCode();
+                    });
+
+                    it('should add correct scope calls wrappers', () => {
+                        assert.match(obfuscatedCode, stringArrayCallRegExp);
+                    });
+                });
+
+            describe('Variant #3: no wrappers on a root scope', () => {
+                    const stringArrayCallRegExp: RegExp = new RegExp(
+                            'return e;' +
+                        '};' +
+                        'function test *\\( *\\) *{' +
+                            'const f *= *function *\\(c, *d\\) *{' +
+                                `return b\\(c *-(?: -)?'${hexadecimalIndexMatch}', *d\\);` +
+                            '};' +
+                            `const c *= *f\\(-? *'${hexadecimalIndexMatch}'\\);` +
+                            `const d *= *f\\(-? *'${hexadecimalIndexMatch}'\\);` +
+                            `const e *= *f\\(-? *'${hexadecimalIndexMatch}'\\);` +
+                        '}'
+                    );
+
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/wrappers-count-const-no-root-wrappers.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
+                                stringArray: true,
+                                stringArrayThreshold: 1,
+                                stringArrayWrappersChainedCalls: true,
+                                stringArrayWrappersCount: 1,
+                                stringArrayWrappersType: StringArrayWrappersType.Function
+                            }
+                        ).getObfuscatedCode();
+                    });
+
+                    it('should add correct scope calls wrappers', () => {
+                        assert.match(obfuscatedCode, stringArrayCallRegExp);
+                    });
+                });
+
+            describe('Variant #4: correct evaluation of the string array wrappers chained calls', () => {
+                describe('Variant #1: base', () => {
+                    describe('Variant #1: `Hexadecimal` identifier names generator', () => {
+                        const samplesCount: number = 50;
+                        const expectedEvaluationResult: string = 'aaabbbcccdddeee';
+                        let isEvaluationSuccessful: boolean = true;
+
+                        before(() => {
+                            const code: string = readFileAsString(__dirname + '/fixtures/chained-calls-1.js');
+
+                            for (let i = 0; i < samplesCount; i++) {
+                                const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
+                                    code,
+                                    {
+                                        ...NO_ADDITIONAL_NODES_PRESET,
+                                        identifierNamesGenerator: IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator,
+                                        stringArray: true,
+                                        stringArrayThreshold: 1,
+                                        stringArrayEncoding: [
+                                            StringArrayEncoding.None
+                                        ],
+                                        stringArrayWrappersChainedCalls: true,
+                                        stringArrayWrappersCount: 5,
+                                        stringArrayWrappersType: StringArrayWrappersType.Function
+                                    }
+                                ).getObfuscatedCode();
+
+                                const evaluationResult: string = eval(obfuscatedCode);
+
+                                if (evaluationResult !== expectedEvaluationResult) {
+                                    isEvaluationSuccessful = false;
+                                    break;
+                                }
+                            }
+                        });
+
+                        it('should correctly evaluate string array wrappers chained calls', () => {
+                            assert.equal(isEvaluationSuccessful, true);
+                        });
+                    });
+
+                    describe('Variant #2: `Mangled` identifier names generator', () => {
+                        const samplesCount: number = 50;
+                        const expectedEvaluationResult: string = 'aaabbbcccdddeee';
+                        let isEvaluationSuccessful: boolean = true;
+
+                        before(() => {
+                            const code: string = readFileAsString(__dirname + '/fixtures/chained-calls-1.js');
+
+                            for (let i = 0; i < samplesCount; i++) {
+                                const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
+                                    code,
+                                    {
+                                        ...NO_ADDITIONAL_NODES_PRESET,
+                                        identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
+                                        stringArray: true,
+                                        stringArrayThreshold: 1,
+                                        stringArrayEncoding: [
+                                            StringArrayEncoding.None
+                                        ],
+                                        stringArrayWrappersChainedCalls: true,
+                                        stringArrayWrappersCount: 5,
+                                        stringArrayWrappersType: StringArrayWrappersType.Function
+                                    }
+                                ).getObfuscatedCode();
+
+                                const evaluationResult: string = eval(obfuscatedCode);
+
+                                if (evaluationResult !== expectedEvaluationResult) {
+                                    isEvaluationSuccessful = false;
+                                    break;
+                                }
+                            }
+                        });
+
+                        it('should correctly evaluate string array wrappers chained calls', () => {
+                            assert.equal(isEvaluationSuccessful, true);
+                        });
+                    });
+                });
+
+                describe('Variant #2: advanced', () => {
+                    describe('Variant #1: `Hexadecimal` identifier names generator', () => {
+                        const samplesCount: number = 50;
+                        const expectedEvaluationResult: string = 'aaabbbcccdddeee';
+                        let isEvaluationSuccessful: boolean = true;
+
+                        before(() => {
+                            const code: string = readFileAsString(__dirname + '/fixtures/chained-calls-2.js');
+
+                            for (let i = 0; i < samplesCount; i++) {
+                                const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
+                                    code,
+                                    {
+                                        ...NO_ADDITIONAL_NODES_PRESET,
+                                        identifierNamesGenerator: IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator,
+                                        stringArray: true,
+                                        stringArrayThreshold: 1,
+                                        stringArrayEncoding: [
+                                            StringArrayEncoding.None,
+                                            StringArrayEncoding.Rc4
+                                        ],
+                                        stringArrayWrappersChainedCalls: true,
+                                        stringArrayWrappersCount: 5,
+                                        stringArrayWrappersType: StringArrayWrappersType.Function
+                                    }
+                                ).getObfuscatedCode();
+
+                                const evaluationResult: string = eval(obfuscatedCode);
+
+                                if (evaluationResult !== expectedEvaluationResult) {
+                                    isEvaluationSuccessful = false;
+                                    break;
+                                }
+                            }
+                        });
+
+                        it('should correctly evaluate string array wrappers chained calls', () => {
+                            assert.equal(isEvaluationSuccessful, true);
+                        });
+                    });
+
+                    describe('Variant #2: `Mangled` identifier names generator', () => {
+                        const samplesCount: number = 50;
+                        const expectedEvaluationResult: string = 'aaabbbcccdddeee';
+                        let isEvaluationSuccessful: boolean = true;
+
+                        before(() => {
+                            const code: string = readFileAsString(__dirname + '/fixtures/chained-calls-2.js');
+
+                            for (let i = 0; i < samplesCount; i++) {
+                                const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
+                                    code,
+                                    {
+                                        ...NO_ADDITIONAL_NODES_PRESET,
+                                        identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
+                                        stringArray: true,
+                                        stringArrayThreshold: 1,
+                                        stringArrayEncoding: [
+                                            StringArrayEncoding.None,
+                                            StringArrayEncoding.Rc4
+                                        ],
+                                        stringArrayWrappersChainedCalls: true,
+                                        stringArrayWrappersCount: 5,
+                                        stringArrayWrappersType: StringArrayWrappersType.Function
+                                    }
+                                ).getObfuscatedCode();
+
+                                const evaluationResult: string = eval(obfuscatedCode);
+
+                                if (evaluationResult !== expectedEvaluationResult) {
+                                    isEvaluationSuccessful = false;
+                                    break;
+                                }
+                            }
+                        });
+
+                        it('should correctly evaluate string array wrappers chained calls', () => {
+                            assert.equal(isEvaluationSuccessful, true);
+                        });
+                    });
+                });
+            });
+        });
     });
     });
 
 
     describe('Variant #2: none and base64 encoding', () => {
     describe('Variant #2: none and base64 encoding', () => {

+ 5 - 0
test/functional-tests/node-transformers/string-array-transformers/string-array-scope-calls-wrapper-transformer/fixtures/wrappers-count-const-no-root-wrappers.js

@@ -0,0 +1,5 @@
+function test () {
+    const foo = 'foo'
+    const bar = 'bar';
+    const baz = 'baz';
+}

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini