瀏覽代碼

Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/60

sanex3339 7 年之前
父節點
當前提交
1712174533

+ 1 - 0
CHANGELOG.md

@@ -10,6 +10,7 @@ v0.10.0
 * increased runtime performance with `rc4` `stringArrayEncoding`.
 * added support for async functions
 * fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/54
+* fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/60
 * fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/57
 * fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/58
 

+ 88 - 55
README.md

@@ -60,9 +60,13 @@ var obfuscationResult = JavaScriptObfuscator.obfuscate(
             var variable1 = '5' - 3;
             var variable2 = '5' + 3;
             var variable3 = '5' + - '2';
+            var variable4 = ['10','10','10','10','10'].map(parseInt);
+            var variable5 = 'foo ' + 1 + 1;
             console.log(variable1);
             console.log(variable2);
             console.log(variable3);
+            console.log(variable4);
+            console.log(variable5);
         })();
     `,
     {
@@ -74,55 +78,79 @@ var obfuscationResult = JavaScriptObfuscator.obfuscate(
 
 console.log(obfuscationResult.getObfuscatedCode());
 /*
-var _0x6b22 = [
-    '\x67\x63\x73',
-    '\x32\x7c\x34\x7c\x30\x7c\x35\x7c\x31\x7c\x33',
-    '\x73\x70\x6c\x69\x74',
-    '\x6c\x6f\x67',
-    '\x68\x49\x4d'
+var _0x2218 = [
+    '8|3|1|2|0|4|6|9|7|5',
+    'bqndd',
+    'dySIh',
+    'kTiiG',
+    'log',
+    'tuvgv'
 ];
-(function (_0xe12ddf, _0x9bc9d1) {
-    var _0x3a950b = function (_0x78e498) {
-        while (--_0x78e498) {
-            _0xe12ddf['\x70\x75\x73\x68'](_0xe12ddf['\x73\x68\x69\x66\x74']());
+(function (_0x38b423, _0x1d6bd4) {
+    var _0x39a849 = function (_0x5794c7) {
+        while (--_0x5794c7) {
+            _0x38b423['push'](_0x38b423['shift']());
         }
     };
-    _0x3a950b(++_0x9bc9d1);
-}(_0x6b22, 0x1cd));
-var _0x26b2 = function (_0x348b1e, _0x346c2a) {
-    _0x348b1e = _0x348b1e - 0x0;
-    var _0x45ae32 = _0x6b22[_0x348b1e];
-    return _0x45ae32;
+    _0x39a849(++_0x1d6bd4);
+}(_0x2218, 0x114));
+var _0x8221 = function (_0xcac13e, _0x3627d7) {
+    _0xcac13e = _0xcac13e - 0x0;
+    var _0x1eae4d = _0x2218[_0xcac13e];
+    return _0x1eae4d;
 };
 (function () {
-    var _0x3a3615 = {
-        '\x68\x49\x4d': function _0x4c002e(_0x5a880a, _0xe710e3) {
-            return _0x5a880a - _0xe710e3;
+    var _0x5336d5 = {
+        'bqndd': _0x8221('0x0'),
+        'islRd': function _0x2abb6c(_0x2f45f8, _0x4d47b0) {
+            return _0x2f45f8 + _0x4d47b0;
+        },
+        'kTiiG': function _0x32525a(_0x44ba8d, _0x2c5e0c) {
+            return _0x44ba8d + _0x2c5e0c;
         },
-        '\x67\x63\x73': function _0x4cbf4b(_0xe1f02d, _0x5d1157) {
-            return _0xe1f02d + _0x5d1157;
+        'dySIh': 'foo\x20',
+        'tuvgv': function _0x28d015(_0x35d81a, _0x2d2463) {
+            return _0x35d81a - _0x2d2463;
         }
     };
-    var _0x26dbf0 = _0x26b2('0x0')[_0x26b2('0x1')]('\x7c'), _0x345ed7 = 0x0;
+    var _0x5000ba = _0x5336d5[_0x8221('0x1')]['split']('|'), _0x5c972f = 0x0;
     while (!![]) {
-        switch (_0x26dbf0[_0x345ed7++]) {
-        case '\x30':
-            var _0x5eb388 = '\x35' + -'\x32';
+        switch (_0x5000ba[_0x5c972f++]) {
+        case '0':
+            var _0x586faa = _0x5336d5['islRd'](_0x5336d5['kTiiG'](_0x5336d5[_0x8221('0x2')], 0x1), 0x1);
+            continue;
+        case '1':
+            var _0xab6a82 = _0x5336d5[_0x8221('0x3')]('5', -'2');
+            continue;
+        case '2':
+            var _0x19ab9d = [
+                '10',
+                '10',
+                '10',
+                '10',
+                '10'
+            ]['map'](parseInt);
             continue;
-        case '\x31':
-            console[_0x26b2('0x2')](_0x52a502);
+        case '3':
+            var _0x321653 = _0x5336d5[_0x8221('0x3')]('5', 0x3);
             continue;
-        case '\x32':
-            var _0xd18cf9 = _0x3a3615[_0x26b2('0x3')]('\x35', 0x3);
+        case '4':
+            console['log'](_0x2c1b0c);
             continue;
-        case '\x33':
-            console[_0x26b2('0x2')](_0x5eb388);
+        case '5':
+            console[_0x8221('0x4')](_0x586faa);
             continue;
-        case '\x34':
-            var _0x52a502 = _0x3a3615[_0x26b2('0x4')]('\x35', 0x3);
+        case '6':
+            console[_0x8221('0x4')](_0x321653);
             continue;
-        case '\x35':
-            console[_0x26b2('0x2')](_0xd18cf9);
+        case '7':
+            console[_0x8221('0x4')](_0x19ab9d);
+            continue;
+        case '8':
+            var _0x2c1b0c = _0x5336d5[_0x8221('0x5')]('5', 0x3);
+            continue;
+        case '9':
+            console[_0x8221('0x4')](_0xab6a82);
             continue;
         }
         break;
@@ -256,46 +284,51 @@ Example:
             console.log(6);
         }
     }
+    
+    foo()();
 })();
 
 // output
 (function () {
-    function _0x425898() {
+    function _0x3bfc5c() {
         return function () {
-            var _0x2b972d = {
-                '\x42\x6c\x67': function _0x160d18(_0xdc9f31, _0x3741dd) {
-                    return _0xdc9f31 + _0x3741dd;
+            var _0x3260a5 = {
+                'WtABe': '4|0|6|5|3|2|1',
+                'GokKo': function _0xf87260(_0x427a8e, _0x43354c) {
+                    return _0x427a8e + _0x43354c;
                 }
             };
-            var _0x170490 = '\x35\x7c\x34\x7c\x33\x7c\x32\x7c\x30\x7c\x36\x7c\x31'['\x73\x70\x6c\x69\x74']('\x7c'), _0x4f3437 = 0x0;
+            var _0x1ad4d6 = _0x3260a5['WtABe']['split']('|'), _0x1a7b12 = 0x0;
             while (!![]) {
-                switch (_0x170490[_0x4f3437++]) {
-                case '\x30':
-                    console['\x6c\x6f\x67'](0x4);
+                switch (_0x1ad4d6[_0x1a7b12++]) {
+                case '0':
+                    console['log'](0x1);
                     continue;
-                case '\x31':
-                    console['\x6c\x6f\x67'](0x6);
+                case '1':
+                    console['log'](0x6);
                     continue;
-                case '\x32':
-                    console['\x6c\x6f\x67'](0x3);
+                case '2':
+                    console['log'](0x5);
                     continue;
-                case '\x33':
-                    console['\x6c\x6f\x67'](0x2);
+                case '3':
+                    console['log'](0x4);
                     continue;
-                case '\x34':
-                    console['\x6c\x6f\x67'](0x1);
+                case '4':
+                    var _0x1f2f2f = _0x3260a5['GokKo'](0x1, 0x2);
                     continue;
-                case '\x35':
-                    var _0x476f51 = _0x2b972d['\x42\x6c\x67'](0x1, 0x2);
+                case '5':
+                    console['log'](0x3);
                     continue;
-                case '\x36':
-                    console['\x6c\x6f\x67'](0x5);
+                case '6':
+                    console['log'](0x2);
                     continue;
                 }
                 break;
             }
         };
     }
+
+	_0x3bfc5c()();
 }());
 ```
 

File diff suppressed because it is too large
+ 0 - 0
dist/index.js


+ 7 - 7
package.json

@@ -1,6 +1,6 @@
 {
   "name": "javascript-obfuscator",
-  "version": "0.10.0-beta.6",
+  "version": "0.10.0-beta.7",
   "description": "JavaScript obfuscator",
   "keywords": [
     "obfuscator",
@@ -21,12 +21,12 @@
   "dependencies": {
     "chance": "1.0.10",
     "class-validator": "0.7.2",
-    "commander": "2.10.0",
+    "commander": "2.11.0",
     "escodegen-wallaby": "1.6.11",
     "esmangle": "1.0.1",
     "esprima": "4.0.0",
     "estraverse": "4.2.0",
-    "inversify": "4.1.1",
+    "inversify": "4.2.0",
     "md5": "2.2.1",
     "mkdirp": "0.5.1",
     "opencollective": "1.0.3",
@@ -46,7 +46,7 @@
     "@types/md5": "2.1.32",
     "@types/mkdirp": "0.3.29",
     "@types/mocha": "2.2.41",
-    "@types/node": "8.0.5",
+    "@types/node": "8.0.8",
     "@types/sinon": "2.3.2",
     "@types/string-template": "1.0.2",
     "awesome-typescript-loader": "3.2.1",
@@ -60,11 +60,11 @@
     "mocha": "3.4.2",
     "pre-commit": "1.2.2",
     "sinon": "2.3.6",
-    "ts-node": "3.1.0",
-    "tslint": "5.4.3",
+    "ts-node": "3.2.0",
+    "tslint": "5.5.0",
     "tslint-loader": "3.5.3",
     "typescript": "2.4.1",
-    "webpack": "3.0.0",
+    "webpack": "3.1.0",
     "webpack-node-externals": "1.6.0"
   },
   "repository": {

+ 4 - 4
src/node-transformers/control-flow-transformers/control-flow-replacers/AbstractControlFlowReplacer.ts

@@ -104,15 +104,15 @@ export abstract class AbstractControlFlowReplacer implements IControlFlowReplace
         }
 
         const generateStorageKey: (length: number) => string = (length: number) => {
-            const storageKey: string = this.randomGenerator.getRandomString(length);
+            const key: string = this.randomGenerator.getRandomString(length);
 
-            if (controlFlowStorage.getStorage().has(storageKey)) {
+            if (controlFlowStorage.getStorage().has(key)) {
                 return generateStorageKey(length);
             }
 
-            return storageKey;
+            return key;
         };
-        const storageKey: string = generateStorageKey(3);
+        const storageKey: string = generateStorageKey(5);
 
         storageKeysById.set(replacerId, [storageKey]);
         this.replacerDataByControlFlowStorageId.set(controlFlowStorageId, storageKeysById);

+ 3 - 1
src/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.ts

@@ -195,7 +195,9 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
                     return node;
                 }
 
-                const randomIndex: number = this.randomGenerator.getRandomInteger(0, this.collectedBlockStatements.length - 1);
+                const minInteger: number = 0;
+                const maxInteger: number = this.collectedBlockStatements.length - 1;
+                const randomIndex: number = this.randomGenerator.getRandomInteger(minInteger, maxInteger);
                 const randomBlockStatementNode: ESTree.BlockStatement = this.collectedBlockStatements.splice(randomIndex, 1)[0];
 
                 if (randomBlockStatementNode === node) {

+ 3 - 5
src/utils/RandomGenerator.ts

@@ -119,11 +119,9 @@ export class RandomGenerator implements IRandomGenerator {
         const prefix: string = `_${Utils.hexadecimalPrefix}`;
         const rangeMinInteger: number = 10000;
         const rangeMaxInteger: number = 99999999;
-        const randomVariableName: string = `${prefix}${(
-            Utils.decToHex(
-                this.getRandomInteger(rangeMinInteger, rangeMaxInteger)
-            )
-        ).substr(0, length)}`;
+        const randomInteger: number = this.getRandomInteger(rangeMinInteger, rangeMaxInteger);
+        const hexadecimalNumber: string = Utils.decToHex(randomInteger);
+        const randomVariableName: string = `${prefix}${hexadecimalNumber.substr(0, length)}`;
 
         if (this.randomVariableNameSet.has(randomVariableName)) {
             return this.getRandomVariableName(length);

+ 36 - 0
test/functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec.ts

@@ -6,6 +6,7 @@ import { JavaScriptObfuscator } from '../../../src/JavaScriptObfuscator';
 
 import { NO_CUSTOM_NODES_PRESET } from '../../../src/options/presets/NoCustomNodes';
 
+import { buildLargeCode } from '../../helpers/buildLargeCode';
 import { getRegExpMatch } from '../../helpers/getRegExpMatch';
 import { readFileAsString } from '../../helpers/readFileAsString';
 
@@ -459,5 +460,40 @@ describe('JavaScriptObfuscator', () => {
                 assert.match(obfuscatedCode, regExp);
             });
         });
+
+        describe('3.5k variables', function () {
+            this.timeout(100000);
+
+            const expectedValue: number = 3500;
+
+            let result: number;
+
+            beforeEach(() => {
+                const code: string = buildLargeCode(expectedValue);
+                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        compact: true,
+                        controlFlowFlattening: true,
+                        controlFlowFlatteningThreshold: 1,
+                        deadCodeInjection: true,
+                        deadCodeInjectionThreshold: 1,
+                        disableConsoleOutput: false,
+                        rotateStringArray: true,
+                        stringArray: true,
+                        stringArrayEncoding: 'rc4',
+                        stringArrayThreshold: 1,
+                        unicodeEscapeSequence: false
+                    }
+                );
+
+                const obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
+                result = eval(obfuscatedCode);
+            });
+
+            it('should correctly obfuscate 3.5k variables', () => {
+                assert.equal(result, expectedValue);
+            });
+        });
     });
 });

+ 3 - 3
test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/binary-expression-control-flow-replacer/BinaryExpressionControlFlowReplacer.spec.ts

@@ -13,7 +13,7 @@ describe('BinaryExpressionControlFlowReplacer', function () {
 
     describe('replace (binaryExpressionNode: ESTree.BinaryExpression,parentNode: ESTree.Node,controlFlowStorage: IStorage <ICustomNode>)', () => {
         describe('variant #1 - single binary expression', () => {
-            const controlFlowStorageCallRegExp: RegExp = /var *_0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\['\w{3}'\]\(0x1, *0x2\);/;
+            const controlFlowStorageCallRegExp: RegExp = /var *_0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\['\w{5}'\]\(0x1, *0x2\);/;
 
             let obfuscatedCode: string;
 
@@ -43,8 +43,8 @@ describe('BinaryExpressionControlFlowReplacer', function () {
             const samplesCount: number = 1000;
             const delta: number = 0.1;
 
-            const controlFlowStorageCallRegExp1: RegExp = /var *_0x(?:[a-f0-9]){4,6} *= *(_0x([a-f0-9]){4,6}\['\w{3}'\])\(0x1, *0x2\);/;
-            const controlFlowStorageCallRegExp2: RegExp = /var *_0x(?:[a-f0-9]){4,6} *= *(_0x([a-f0-9]){4,6}\['\w{3}'\])\(0x2, *0x3\);/;
+            const controlFlowStorageCallRegExp1: RegExp = /var *_0x(?:[a-f0-9]){4,6} *= *(_0x([a-f0-9]){4,6}\['\w{5}'\])\(0x1, *0x2\);/;
+            const controlFlowStorageCallRegExp2: RegExp = /var *_0x(?:[a-f0-9]){4,6} *= *(_0x([a-f0-9]){4,6}\['\w{5}'\])\(0x2, *0x3\);/;
 
             let matchErrorsCount: number = 0,
                 usingExistingIdentifierChance: number;

+ 3 - 3
test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/call-expression-control-flow-replacer/CallExpressionControlFlowReplacer.spec.ts

@@ -13,7 +13,7 @@ describe('CallExpressionControlFlowReplacer', function () {
 
     describe('replace (callExpressionNode: ESTree.CallExpression,parentNode: ESTree.Node,controlFlowStorage: IStorage <ICustomNode>)', () => {
         describe('variant #1 - single call expression', () => {
-            const controlFlowStorageCallRegExp: RegExp = /var *_0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\['\w{3}'\]\(_0x([a-f0-9]){4,6}, *0x1, *0x2\);/;
+            const controlFlowStorageCallRegExp: RegExp = /var *_0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\['\w{5}'\]\(_0x([a-f0-9]){4,6}, *0x1, *0x2\);/;
 
             let obfuscatedCode: string;
 
@@ -43,8 +43,8 @@ describe('CallExpressionControlFlowReplacer', function () {
             const samplesCount: number = 1000;
             const delta: number = 0.1;
 
-            const controlFlowStorageCallRegExp1: RegExp = /var *_0x(?:[a-f0-9]){4,6} *= *(_0x([a-f0-9]){4,6}\['\w{3}'\])\(_0x([a-f0-9]){4,6}, *0x1, *0x2\);/;
-            const controlFlowStorageCallRegExp2: RegExp = /var *_0x(?:[a-f0-9]){4,6} *= *(_0x([a-f0-9]){4,6}\['\w{3}'\])\(_0x([a-f0-9]){4,6}, *0x2, *0x3\);/;
+            const controlFlowStorageCallRegExp1: RegExp = /var *_0x(?:[a-f0-9]){4,6} *= *(_0x([a-f0-9]){4,6}\['\w{5}'\])\(_0x([a-f0-9]){4,6}, *0x1, *0x2\);/;
+            const controlFlowStorageCallRegExp2: RegExp = /var *_0x(?:[a-f0-9]){4,6} *= *(_0x([a-f0-9]){4,6}\['\w{5}'\])\(_0x([a-f0-9]){4,6}, *0x2, *0x3\);/;
 
             let matchErrorsCount: number = 0,
                 usingExistingIdentifierChance: number;

+ 4 - 4
test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/logical-expression-control-flow-replacer/LogicalExpressionControlFlowReplacer.spec.ts

@@ -13,7 +13,7 @@ describe('LogicalExpressionControlFlowReplacer', function () {
 
     describe('replace (logicalExpressionNode: ESTree.LogicalExpression,parentNode: ESTree.Node,controlFlowStorage: IStorage <ICustomNode>)', () => {
         describe('variant #1 - single logical expression', () => {
-            const controlFlowStorageCallRegExp: RegExp = /var *_0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\['\w{3}'\]\(!!\[\], *!\[\]\);/;
+            const controlFlowStorageCallRegExp: RegExp = /var *_0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\['\w{5}'\]\(!!\[\], *!\[\]\);/;
 
             let obfuscatedCode: string;
 
@@ -43,8 +43,8 @@ describe('LogicalExpressionControlFlowReplacer', function () {
             const samplesCount: number = 1000;
             const delta: number = 0.1;
 
-            const controlFlowStorageCallRegExp1: RegExp = /var *_0x(?:[a-f0-9]){4,6} *= *(_0x([a-f0-9]){4,6}\['\w{3}'\])\(!!\[\], *!\[\]\);/;
-            const controlFlowStorageCallRegExp2: RegExp = /var *_0x(?:[a-f0-9]){4,6} *= *(_0x([a-f0-9]){4,6}\['\w{3}'\])\(!\[\], *!!\[\]\);/;
+            const controlFlowStorageCallRegExp1: RegExp = /var *_0x(?:[a-f0-9]){4,6} *= *(_0x([a-f0-9]){4,6}\['\w{5}'\])\(!!\[\], *!\[\]\);/;
+            const controlFlowStorageCallRegExp2: RegExp = /var *_0x(?:[a-f0-9]){4,6} *= *(_0x([a-f0-9]){4,6}\['\w{5}'\])\(!\[\], *!!\[\]\);/;
 
             let matchErrorsCount: number = 0,
                 usingExistingIdentifierChance: number;
@@ -102,7 +102,7 @@ describe('LogicalExpressionControlFlowReplacer', function () {
         });
 
         describe('variant #3 - single logical expression with unary expression', () => {
-            const controlFlowStorageCallRegExp: RegExp = /var *_0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\['\w{3}'\]\(!_0x([a-f0-9]){4,6}, *!_0x([a-f0-9]){4,6}\);/;
+            const controlFlowStorageCallRegExp: RegExp = /var *_0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\['\w{5}'\]\(!_0x([a-f0-9]){4,6}, *!_0x([a-f0-9]){4,6}\);/;
 
             let obfuscatedCode: string;
 

+ 2 - 2
test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/string-litertal-control-flow-replacer/StringLiteralControlFlowReplacer.spec.ts

@@ -10,8 +10,8 @@ import { JavaScriptObfuscator } from '../../../../../../src/JavaScriptObfuscator
 
 describe('StringLiteralControlFlowReplacer', () => {
     describe('replace (literalNode: ESTree.Literal,parentNode: ESTree.Node,controlFlowStorage: IStorage <ICustomNode>)', () => {
-        const controlFlowStorageStringLiteralRegExp: RegExp = /var *_0x([a-f0-9]){4,6} *= *\{'\w{3}' *: *'test'\};/;
-        const controlFlowStorageCallRegExp: RegExp = /var *_0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\['\w{3}'\];/;
+        const controlFlowStorageStringLiteralRegExp: RegExp = /var *_0x([a-f0-9]){4,6} *= *\{'\w{5}' *: *'test'\};/;
+        const controlFlowStorageCallRegExp: RegExp = /var *_0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\['\w{5}'\];/;
 
         let obfuscatedCode: string;
 

+ 6 - 6
test/functional-tests/node-transformers/control-flow-transformers/function-control-flow-transformer/FunctionControlFlowTransformer.spec.ts

@@ -14,15 +14,15 @@ describe('FunctionControlFlowTransformer', function () {
     const variableMatch: string = '_0x([a-f0-9]){4,6}';
     const rootControlFlowStorageNodeMatch: string = `` +
         `var *${variableMatch} *= *\\{` +
-            `'\\w{3}' *: *function *${variableMatch} *\\(${variableMatch}, *${variableMatch}\\) *\\{` +
+            `'\\w{5}' *: *function *${variableMatch} *\\(${variableMatch}, *${variableMatch}\\) *\\{` +
                 `return *${variableMatch} *\\+ *${variableMatch};` +
             `\\}` +
         `\\};` +
     ``;
     const innerControlFlowStorageNodeMatch: string = `` +
         `var *${variableMatch} *= *\\{` +
-            `'\\w{3}' *: *function *${variableMatch} *\\(${variableMatch}, *${variableMatch}\\) *\\{` +
-                `return *${variableMatch}\\['\\w{3}'\\]\\(${variableMatch}, *${variableMatch}\\);` +
+            `'\\w{5}' *: *function *${variableMatch} *\\(${variableMatch}, *${variableMatch}\\) *\\{` +
+                `return *${variableMatch}\\['\\w{5}'\\]\\(${variableMatch}, *${variableMatch}\\);` +
             `\\}` +
         `\\};` +
     ``;
@@ -107,10 +107,10 @@ describe('FunctionControlFlowTransformer', function () {
         describe('variant #3 - single `control flow storage` node with multiple items', () => {
             const regexp: RegExp = new RegExp(
                 `var *${variableMatch} *= *\\{` +
-                    `'\\w{3}' *: *function *${variableMatch} *\\(${variableMatch}, *${variableMatch}\\) *\\{` +
+                    `'\\w{5}' *: *function *${variableMatch} *\\(${variableMatch}, *${variableMatch}\\) *\\{` +
                         `return *${variableMatch} *\\+ *${variableMatch};` +
                     `\\}, *` +
-                    `'\\w{3}' *: *function *${variableMatch} *\\(${variableMatch}, *${variableMatch}\\) *\\{` +
+                    `'\\w{5}' *: *function *${variableMatch} *\\(${variableMatch}, *${variableMatch}\\) *\\{` +
                         `return *${variableMatch} *- *${variableMatch};` +
                     `\\}` +
                 `\\};`
@@ -167,7 +167,7 @@ describe('FunctionControlFlowTransformer', function () {
 
             const regExp: RegExp = new RegExp(
                 `var *[a-zA-Z]{6} *= *\\{` +
-                    `'\\w{3}' *: *function *_0x[0-9] *\\(${variableMatch}, *${variableMatch}\\) *\\{` +
+                    `'\\w{5}' *: *function *_0x[0-9] *\\(${variableMatch}, *${variableMatch}\\) *\\{` +
                         `return *${variableMatch} *\\+ *${variableMatch};` +
                     `\\}` +
                 `\\};`

+ 83 - 0
test/helpers/buildLargeCode.ts

@@ -0,0 +1,83 @@
+export function buildLargeCode (linesOfCode: number): string {
+    return new LargeCodeBuilder(linesOfCode).build();
+}
+
+class LargeCodeBuilder {
+    /**
+     * @type {string}
+     */
+    private code: string = '';
+
+    /**
+     * @type {number}
+     */
+    private readonly linesOfCode: number;
+
+    /**
+     * @param {number} linesOfCode
+     */
+    constructor (linesOfCode: number) {
+        this.linesOfCode = linesOfCode;
+    }
+
+    public build (): string {
+        const lastLineIndex: number = this.linesOfCode - 1;
+
+        let funcIndex: number = 0,
+            isFuncWasOpened: boolean = false;
+
+        this.addLine(`function Foo () {`);
+        this.addLine(`var var0 = 0;`);
+
+        for (let index = 0; index < this.linesOfCode; index++) {
+            const step: number = index % 10;
+            const newIndex: number = index + 1;
+            const isLastLine: boolean = index === lastLineIndex;
+
+            if (step === 3) {
+                funcIndex = index;
+                isFuncWasOpened = true;
+                this.addLine(`function func${funcIndex} () {`);
+                this.addLine(`var var${newIndex} = var${index};`);
+            }
+
+            if (step === 4) {
+                this.addLine(`if (true) {`);
+            }
+
+            if (step === 6) {
+                this.addLine(`if (true) {`);
+            }
+
+            this.addLine(`var var${newIndex} = var${index};`);
+            this.addLine(`var${newIndex}++;`);
+
+            if (step === 9 || (isLastLine && isFuncWasOpened)) {
+                isFuncWasOpened = false;
+                this.addLine(`}`);
+                this.addLine(`}`);
+                this.addLine(`return var${newIndex};`);
+                this.addLine(`}`);
+                this.addLine(`var var${newIndex} = func${funcIndex}();`);
+            }
+
+            if (isLastLine) {
+                this.addLine(`result = var${newIndex};`);
+            }
+        }
+
+        this.addLine(`return result;`);
+        this.addLine(`}`);
+        this.addLine(`Foo();`);
+
+        return this.code;
+    }
+
+    /**
+     * @param {string} line
+     * @returns {string}
+     */
+    private addLine (line: string): void {
+        this.code += `\n${line}`;
+    }
+}

+ 60 - 26
yarn.lock

@@ -36,7 +36,7 @@
   version "0.0.35"
   resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.35.tgz#8999974b34028686a8d61a719e61c138d3755107"
 
-"@types/md5@^2.1.32":
+"@types/[email protected]":
   version "2.1.32"
   resolved "https://registry.yarnpkg.com/@types/md5/-/md5-2.1.32.tgz#93e23437fcd17a7b9ca98d02aa6002e835842fe8"
   dependencies:
@@ -51,12 +51,12 @@
   resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.41.tgz#e27cf0817153eb9f2713b2d3f6c68f1e1c3ca608"
 
 "@types/node@*":
-  version "8.0.4"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.4.tgz#d0ca03fa4a3d7ab66c1f4e78a0fd06e30e46a7a9"
+  version "8.0.7"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.7.tgz#fb0ad04b5b6f6eabe0372a32a8f1fbba5c130cae"
 
-"@types/[email protected].5":
-  version "8.0.5"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.5.tgz#71ac2a2c33a4231dd2427f1b5a5b66ad2b185b5f"
+"@types/[email protected].8":
+  version "8.0.8"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.8.tgz#0dc4ca2c6f6fc69baee16c5e928c4a627f517ada"
 
 "@types/[email protected]":
   version "2.3.2"
@@ -127,6 +127,12 @@ ansi-styles@^2.2.1:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
 
+ansi-styles@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.1.0.tgz#09c202d5c917ec23188caa5c9cb9179cd9547750"
+  dependencies:
+    color-convert "^1.0.0"
+
 anymatch@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507"
@@ -924,6 +930,14 @@ [email protected], chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1:
     strip-ansi "^3.0.0"
     supports-color "^2.0.0"
 
+chalk@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.0.1.tgz#dbec49436d2ae15f536114e76d14656cdbc0f44d"
+  dependencies:
+    ansi-styles "^3.1.0"
+    escape-string-regexp "^1.0.5"
+    supports-color "^4.0.0"
+
 [email protected]:
   version "1.0.10"
   resolved "https://registry.yarnpkg.com/chance/-/chance-1.0.10.tgz#03500b04ad94e778dd2891b09ec73a6ad87b1996"
@@ -1015,6 +1029,16 @@ collection-visit@^0.2.1:
     map-visit "^0.1.5"
     object-visit "^0.3.4"
 
+color-convert@^1.0.0:
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a"
+  dependencies:
+    color-name "^1.1.1"
+
+color-name@^1.1.1:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.2.tgz#5c8ab72b64bd2215d617ae9559ebb148475cf98d"
+
 colors@^1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
@@ -1025,11 +1049,9 @@ combined-stream@^1.0.5, combined-stream@~1.0.5:
   dependencies:
     delayed-stream "~1.0.0"
 
[email protected], commander@^2.8.1, commander@^2.9.0:
-  version "2.10.0"
-  resolved "https://registry.yarnpkg.com/commander/-/commander-2.10.0.tgz#e1f5d3245de246d1a5ca04702fa1ad1bd7e405fe"
-  dependencies:
-    graceful-readlink ">= 1.0.0"
[email protected], commander@^2.8.1, commander@^2.9.0:
+  version "2.11.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563"
 
 [email protected]:
   version "2.9.0"
@@ -1816,6 +1838,10 @@ has-flag@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
 
+has-flag@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51"
+
 has-unicode@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
@@ -1947,9 +1973,9 @@ invariant@^2.2.0:
   dependencies:
     loose-envify "^1.0.0"
 
-inversify@4.1.1:
-  version "4.1.1"
-  resolved "https://registry.yarnpkg.com/inversify/-/inversify-4.1.1.tgz#a95edb34ae082bb1175336d51dcd5f011134c625"
+inversify@4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/inversify/-/inversify-4.2.0.tgz#71966a5005b1ef735ba2709e66c5b5f7765087e3"
 
 invert-kv@^1.0.0:
   version "1.0.0"
@@ -2471,7 +2497,7 @@ map-visit@^0.1.5:
     lazy-cache "^2.0.1"
     object-visit "^0.3.4"
 
-md5@^2.2.1:
[email protected]:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9"
   dependencies:
@@ -3565,6 +3591,12 @@ supports-color@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
 
+supports-color@^4.0.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.1.0.tgz#92cc14bb3dad8928ca5656c33e19a19f20af5c7a"
+  dependencies:
+    has-flag "^2.0.0"
+
 tapable@^0.2.5, tapable@~0.2.5:
   version "0.2.6"
   resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.6.tgz#206be8e188860b514425375e6f1ae89bfb01fd8d"
@@ -3657,12 +3689,12 @@ trim-right@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
 
-ts-node@3.1.0:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-3.1.0.tgz#a75ec5aeb48f3058b1b945dba765f1150ba88f8c"
+ts-node@3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-3.2.0.tgz#9814f0c0141784900cf12fef1197ad4b7f4d23d1"
   dependencies:
     arrify "^1.0.0"
-    chalk "^1.1.1"
+    chalk "^2.0.0"
     diff "^3.1.0"
     make-error "^1.1.1"
     minimist "^1.2.0"
@@ -3693,9 +3725,9 @@ [email protected]:
     rimraf "^2.4.4"
     semver "^5.3.0"
 
-tslint@5.4.3:
-  version "5.4.3"
-  resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.4.3.tgz#761c8402b80e347b7733a04390a757b253580467"
+tslint@5.5.0:
+  version "5.5.0"
+  resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.5.0.tgz#10e8dab3e3061fa61e9442e8cee3982acf20a6aa"
   dependencies:
     babel-code-frame "^6.22.0"
     colors "^1.1.2"
@@ -3706,11 +3738,13 @@ [email protected]:
     resolve "^1.3.2"
     semver "^5.3.0"
     tslib "^1.7.1"
-    tsutils "^2.3.0"
+    tsutils "^2.5.1"
 
-tsutils@^2.3.0:
-  version "2.3.0"
-  resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.3.0.tgz#96e661d7c2363f31adc8992ac67bbe7b7fc175e5"
+tsutils@^2.5.1:
+  version "2.5.1"
+  resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.5.1.tgz#c2001390c79eec1a5ccfa7ac12d599639683e0cf"
+  dependencies:
+    tslib "^1.7.1"
 
 [email protected]:
   version "0.0.0"

Some files were not shown because too many files changed in this diff