Browse Source

Refactoring. Added `mangle` option

sanex3339 8 years ago
parent
commit
26d580d832

+ 12 - 2
README.md

@@ -1,12 +1,12 @@
 <!--
 <!--
   Title: JavaScript Obfuscator
   Title: JavaScript Obfuscator
-  Description: JavaScript obfuscator for Node.js.
+  Description: A powerful obfuscator for JavaScript and Node.js.
   Author: sanex3339
   Author: sanex3339
   -->
   -->
 
 
 # JavaScript obfuscator for Node.js
 # JavaScript obfuscator for Node.js
 
 
-JavaScript obfuscator for Node.js is a free obfuscator with a wide number of features which provides protection for your source code.
+JavaScript obfuscator is a powerful free obfuscator for JavaScript and Node.js with a wide number of features which provides protection for your source code.
 
 
 * has no limits or restrictions
 * has no limits or restrictions
 * runs on your local machine - does not send data to a server;
 * runs on your local machine - does not send data to a server;
@@ -181,6 +181,7 @@ Following options are available for the JS Obfuscator:
     debugProtection: false,
     debugProtection: false,
     debugProtectionInterval: false,
     debugProtectionInterval: false,
     disableConsoleOutput: true,
     disableConsoleOutput: true,
+    mangle: false,
     reservedNames: [],
     reservedNames: [],
     rotateStringArray: true,
     rotateStringArray: true,
     seed: 0,
     seed: 0,
@@ -211,6 +212,7 @@ Following options are available for the JS Obfuscator:
     --debugProtection <boolean>
     --debugProtection <boolean>
     --debugProtectionInterval <boolean>
     --debugProtectionInterval <boolean>
     --disableConsoleOutput <boolean>
     --disableConsoleOutput <boolean>
+    --mangle <boolean>
     --reservedNames <list> (comma separated)
     --reservedNames <list> (comma separated)
     --rotateStringArray <boolean>
     --rotateStringArray <boolean>
     --seed <number>
     --seed <number>
@@ -334,6 +336,11 @@ Locks the obfuscated source code so it only runs on specific domains and/or sub-
 ##### Multiple domains and sub-domains
 ##### Multiple domains and sub-domains
 It's possible to lock your code to more than one domain or sub-domain. For instance, to lock it so the code only runs on **www.example.com** add `www.example.com`, to make it work on any sub-domain from example.com, use `.example.com`.
 It's possible to lock your code to more than one domain or sub-domain. For instance, to lock it so the code only runs on **www.example.com** add `www.example.com`, to make it work on any sub-domain from example.com, use `.example.com`.
 
 
+### `mangle`
+Type: `boolean` Default: `false`
+
+Enables mangling of variable names.
+
 ### `reservedNames`
 ### `reservedNames`
 Type: `string[]` Default: `[]`
 Type: `string[]` Default: `[]`
 
 
@@ -470,6 +477,7 @@ Performance will 50-100% slower than without obfuscation
     debugProtection: true,
     debugProtection: true,
     debugProtectionInterval: true,
     debugProtectionInterval: true,
     disableConsoleOutput: true,
     disableConsoleOutput: true,
+	mangle: false,
     rotateStringArray: true,
     rotateStringArray: true,
     selfDefending: true,
     selfDefending: true,
     stringArray: true,
     stringArray: true,
@@ -493,6 +501,7 @@ Performance will 30-35% slower than without obfuscation
     debugProtection: false,
     debugProtection: false,
     debugProtectionInterval: false,
     debugProtectionInterval: false,
     disableConsoleOutput: true,
     disableConsoleOutput: true,
+    mangle: false,
     rotateStringArray: true,
     rotateStringArray: true,
     selfDefending: true,
     selfDefending: true,
     stringArray: true,
     stringArray: true,
@@ -514,6 +523,7 @@ Performance will slightly slower than without obfuscation
     debugProtection: false,
     debugProtection: false,
     debugProtectionInterval: false,
     debugProtectionInterval: false,
     disableConsoleOutput: true,
     disableConsoleOutput: true,
+    mangle: true,
     rotateStringArray: true,
     rotateStringArray: true,
     selfDefending: true,
     selfDefending: true,
     stringArray: true,
     stringArray: true,

+ 76 - 55
dist/index.js

@@ -441,6 +441,7 @@ exports.RandomGeneratorUtils = RandomGeneratorUtils;
 "use strict";
 "use strict";
 
 
 
 
+var tslib_1 = __webpack_require__(1);
 function initializable() {
 function initializable() {
     var initializeMethodKey = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'initialize';
     var initializeMethodKey = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'initialize';
 
 
@@ -452,21 +453,21 @@ function initializable() {
         };
         };
         var initializeMethod = target[initializeMethodKey];
         var initializeMethod = target[initializeMethodKey];
         if (!initializeMethod || typeof initializeMethod !== 'function') {
         if (!initializeMethod || typeof initializeMethod !== 'function') {
-            throw new Error('`' + initializeMethodKey + '` method with initialization logic not found. `@' + decoratorName + '` decorator requires `' + initializeMethodKey + '` method');
+            throw new Error("`" + initializeMethodKey + "` method with initialization logic not found. `@" + decoratorName + "` decorator requires `" + initializeMethodKey + "` method");
         }
         }
-        var metadataPropertyKey = '_' + propertyKey;
+        var metadataPropertyKey = "_" + propertyKey;
         var propertyDescriptor = Object.getOwnPropertyDescriptor(target, metadataPropertyKey) || descriptor;
         var propertyDescriptor = Object.getOwnPropertyDescriptor(target, metadataPropertyKey) || descriptor;
         var methodDescriptor = Object.getOwnPropertyDescriptor(target, initializeMethodKey) || descriptor;
         var methodDescriptor = Object.getOwnPropertyDescriptor(target, initializeMethodKey) || descriptor;
         var originalMethod = methodDescriptor.value;
         var originalMethod = methodDescriptor.value;
-        Object.defineProperty(target, propertyKey, Object.assign({}, propertyDescriptor, { get: function get() {
+        Object.defineProperty(target, propertyKey, tslib_1.__assign({}, propertyDescriptor, { get: function get() {
                 if (this[metadataPropertyKey] === undefined) {
                 if (this[metadataPropertyKey] === undefined) {
-                    throw new Error('Property `' + propertyKey + '` is not initialized! Initialize it first!');
+                    throw new Error("Property `" + propertyKey + "` is not initialized! Initialize it first!");
                 }
                 }
                 return this[metadataPropertyKey];
                 return this[metadataPropertyKey];
             }, set: function set(newVal) {
             }, set: function set(newVal) {
                 this[metadataPropertyKey] = newVal;
                 this[metadataPropertyKey] = newVal;
             } }));
             } }));
-        Object.defineProperty(target, initializeMethodKey, Object.assign({}, methodDescriptor, { value: function value() {
+        Object.defineProperty(target, initializeMethodKey, tslib_1.__assign({}, methodDescriptor, { value: function value() {
                 originalMethod.apply(this, arguments);
                 originalMethod.apply(this, arguments);
                 if (this[propertyKey]) {}
                 if (this[propertyKey]) {}
             } }));
             } }));
@@ -1306,7 +1307,7 @@ var _createClass = (function () { function defineProperties(target, props) { for
 
 
 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
 
 
-__webpack_require__(132);
+__webpack_require__(133);
 var ServiceIdentifiers_1 = __webpack_require__(2);
 var ServiceIdentifiers_1 = __webpack_require__(2);
 var InversifyContainerFacade_1 = __webpack_require__(45);
 var InversifyContainerFacade_1 = __webpack_require__(45);
 var JavaScriptObfuscatorCLI_1 = __webpack_require__(44);
 var JavaScriptObfuscatorCLI_1 = __webpack_require__(44);
@@ -1541,6 +1542,7 @@ exports.NO_CUSTOM_NODES_PRESET = Object.freeze({
     debugProtectionInterval: false,
     debugProtectionInterval: false,
     disableConsoleOutput: false,
     disableConsoleOutput: false,
     domainLock: [],
     domainLock: [],
+    mangle: false,
     reservedNames: [],
     reservedNames: [],
     rotateStringArray: false,
     rotateStringArray: false,
     seed: 0,
     seed: 0,
@@ -1957,6 +1959,7 @@ exports.DEFAULT_PRESET = Object.freeze({
     debugProtectionInterval: false,
     debugProtectionInterval: false,
     disableConsoleOutput: true,
     disableConsoleOutput: true,
     domainLock: [],
     domainLock: [],
+    mangle: false,
     reservedNames: [],
     reservedNames: [],
     rotateStringArray: true,
     rotateStringArray: true,
     seed: 0,
     seed: 0,
@@ -2121,6 +2124,7 @@ var inversify_1 = __webpack_require__(0);
 var ServiceIdentifiers_1 = __webpack_require__(2);
 var ServiceIdentifiers_1 = __webpack_require__(2);
 var esprima = __webpack_require__(36);
 var esprima = __webpack_require__(36);
 var escodegen = __webpack_require__(23);
 var escodegen = __webpack_require__(23);
+var esmangle = __webpack_require__(129);
 var JavaScriptObfuscatorInternal = JavaScriptObfuscatorInternal_1 = function () {
 var JavaScriptObfuscatorInternal = JavaScriptObfuscatorInternal_1 = function () {
     function JavaScriptObfuscatorInternal(obfuscator, sourceMapCorrector, options) {
     function JavaScriptObfuscatorInternal(obfuscator, sourceMapCorrector, options) {
         _classCallCheck(this, JavaScriptObfuscatorInternal);
         _classCallCheck(this, JavaScriptObfuscatorInternal);
@@ -2146,10 +2150,12 @@ var JavaScriptObfuscatorInternal = JavaScriptObfuscatorInternal_1 = function ()
                 escodegenParams.sourceMap = 'sourceMap';
                 escodegenParams.sourceMap = 'sourceMap';
                 escodegenParams.sourceContent = sourceCode;
                 escodegenParams.sourceContent = sourceCode;
             }
             }
-            escodegenParams.format = {
-                compact: this.options.compact
-            };
-            var generatorOutput = escodegen.generate(astTree, escodegenParams);
+            if (this.options.mangle) {
+                astTree = esmangle.mangle(astTree);
+            }
+            var generatorOutput = escodegen.generate(astTree, Object.assign({}, escodegenParams, { format: {
+                    compact: this.options.compact
+                } }));
             generatorOutput.map = generatorOutput.map ? generatorOutput.map.toString() : '';
             generatorOutput.map = generatorOutput.map ? generatorOutput.map.toString() : '';
             return generatorOutput;
             return generatorOutput;
         }
         }
@@ -2267,7 +2273,8 @@ var Obfuscator = Obfuscator_1 = function () {
                 _this.obfuscationEventEmitter.once(customNodeGroup.getAppendEvent(), customNodeGroup.appendCustomNodes.bind(customNodeGroup));
                 _this.obfuscationEventEmitter.once(customNodeGroup.getAppendEvent(), customNodeGroup.appendCustomNodes.bind(customNodeGroup));
             });
             });
             this.obfuscationEventEmitter.emit(ObfuscationEvents_1.ObfuscationEvents.BeforeObfuscation, astTree, stackTraceData);
             this.obfuscationEventEmitter.emit(ObfuscationEvents_1.ObfuscationEvents.BeforeObfuscation, astTree, stackTraceData);
-            astTree = this.transformAstTree(astTree, [].concat(_toConsumableArray(this.options.deadCodeInjection ? Obfuscator_1.deadCodeInjectionTransformersList : []), _toConsumableArray(this.options.controlFlowFlattening ? Obfuscator_1.controlFlowTransformersList : [])));
+            astTree = this.transformAstTree(astTree, [].concat(_toConsumableArray(this.options.deadCodeInjection ? Obfuscator_1.deadCodeInjectionTransformersList : [])));
+            astTree = this.transformAstTree(astTree, [].concat(_toConsumableArray(this.options.controlFlowFlattening ? Obfuscator_1.controlFlowTransformersList : [])));
             astTree = this.transformAstTree(astTree, [].concat(_toConsumableArray(Obfuscator_1.convertingTransformersList), _toConsumableArray(Obfuscator_1.obfuscatingTransformersList)));
             astTree = this.transformAstTree(astTree, [].concat(_toConsumableArray(Obfuscator_1.convertingTransformersList), _toConsumableArray(Obfuscator_1.obfuscatingTransformersList)));
             this.obfuscationEventEmitter.emit(ObfuscationEvents_1.ObfuscationEvents.AfterObfuscation, astTree, stackTraceData);
             this.obfuscationEventEmitter.emit(ObfuscationEvents_1.ObfuscationEvents.AfterObfuscation, astTree, stackTraceData);
             return astTree;
             return astTree;
@@ -2404,8 +2411,8 @@ var _createClass = (function () { function defineProperties(target, props) { for
 
 
 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
 
 
-var fs = __webpack_require__(130);
-var mkdirp = __webpack_require__(131);
+var fs = __webpack_require__(131);
+var mkdirp = __webpack_require__(132);
 var path = __webpack_require__(37);
 var path = __webpack_require__(37);
 
 
 var CLIUtils = function () {
 var CLIUtils = function () {
@@ -2546,7 +2553,7 @@ var JavaScriptObfuscatorCLI = function () {
         value: function configureCommands() {
         value: function configureCommands() {
             this.commands = new commander.Command().version(JavaScriptObfuscatorCLI.getBuildVersion(), '-v, --version').usage('<inputPath> [options]').option('-o, --output <path>', 'Output path for obfuscated code').option('--compact <boolean>', 'Disable one line output code compacting', JavaScriptObfuscatorCLI.parseBoolean).option('--controlFlowFlattening <boolean>', 'Enables control flow flattening', JavaScriptObfuscatorCLI.parseBoolean).option('--controlFlowFlatteningThreshold <number>', 'The probability that the control flow flattening transformation will be applied to the node', parseFloat).option('--deadCodeInjection <boolean>', 'Enables dead code injection', JavaScriptObfuscatorCLI.parseBoolean).option('--deadCodeInjectionThreshold <number>', 'The probability that the dead code injection transformation will be applied to the node', parseFloat).option('--debugProtection <boolean>', 'Disable browser Debug panel (can cause DevTools enabled browser freeze)', JavaScriptObfuscatorCLI.parseBoolean).option('--debugProtectionInterval <boolean>', 'Disable browser Debug panel even after page was loaded (can cause DevTools enabled browser freeze)', JavaScriptObfuscatorCLI.parseBoolean).option('--disableConsoleOutput <boolean>', 'Allow console.log, console.info, console.error and console.warn messages output into browser console', JavaScriptObfuscatorCLI.parseBoolean).option('--domainLock <list>', 'Blocks the execution of the code in domains that do not match the passed RegExp patterns (comma separated)', function (value) {
             this.commands = new commander.Command().version(JavaScriptObfuscatorCLI.getBuildVersion(), '-v, --version').usage('<inputPath> [options]').option('-o, --output <path>', 'Output path for obfuscated code').option('--compact <boolean>', 'Disable one line output code compacting', JavaScriptObfuscatorCLI.parseBoolean).option('--controlFlowFlattening <boolean>', 'Enables control flow flattening', JavaScriptObfuscatorCLI.parseBoolean).option('--controlFlowFlatteningThreshold <number>', 'The probability that the control flow flattening transformation will be applied to the node', parseFloat).option('--deadCodeInjection <boolean>', 'Enables dead code injection', JavaScriptObfuscatorCLI.parseBoolean).option('--deadCodeInjectionThreshold <number>', 'The probability that the dead code injection transformation will be applied to the node', parseFloat).option('--debugProtection <boolean>', 'Disable browser Debug panel (can cause DevTools enabled browser freeze)', JavaScriptObfuscatorCLI.parseBoolean).option('--debugProtectionInterval <boolean>', 'Disable browser Debug panel even after page was loaded (can cause DevTools enabled browser freeze)', JavaScriptObfuscatorCLI.parseBoolean).option('--disableConsoleOutput <boolean>', 'Allow console.log, console.info, console.error and console.warn messages output into browser console', JavaScriptObfuscatorCLI.parseBoolean).option('--domainLock <list>', 'Blocks the execution of the code in domains that do not match the passed RegExp patterns (comma separated)', function (value) {
                 return value.split(',');
                 return value.split(',');
-            }).option('--reservedNames <list>', 'Disable obfuscation of variable names, function names and names of function parameters that match the passed RegExp patterns (comma separated)', function (value) {
+            }).option('--mangle <boolean>', 'Enables mangling of variable names', JavaScriptObfuscatorCLI.parseBoolean).option('--reservedNames <list>', 'Disable obfuscation of variable names, function names and names of function parameters that match the passed RegExp patterns (comma separated)', function (value) {
                 return value.split(',');
                 return value.split(',');
             }).option('--rotateStringArray <boolean>', 'Disable rotation of unicode array values during obfuscation', JavaScriptObfuscatorCLI.parseBoolean).option('--seed <number>', 'Sets seed for random generator. This is useful for creating repeatable results.', parseFloat).option('--selfDefending <boolean>', 'Disables self-defending for obfuscated code', JavaScriptObfuscatorCLI.parseBoolean).option('--sourceMap <boolean>', 'Enables source map generation', JavaScriptObfuscatorCLI.parseBoolean).option('--sourceMapBaseUrl <string>', 'Sets base url to the source map import url when `--sourceMapMode=separate`').option('--sourceMapFileName <string>', 'Sets file name for output source map when `--sourceMapMode=separate`').option('--sourceMapMode <string> [inline, separate]', 'Specify source map output mode', JavaScriptObfuscatorCLI.parseSourceMapMode).option('--stringArray <boolean>', 'Disables gathering of all literal strings into an array and replacing every literal string with an array call', JavaScriptObfuscatorCLI.parseBoolean).option('--stringArrayEncoding <boolean|string> [true, false, base64, rc4]', 'Encodes all strings in strings array using base64 or rc4 (this option can slow down your code speed', JavaScriptObfuscatorCLI.parseStringArrayEncoding).option('--stringArrayThreshold <number>', 'The probability that the literal string will be inserted into stringArray (Default: 0.8, Min: 0, Max: 1)', parseFloat).option('--unicodeEscapeSequence <boolean>', 'Allows to enable/disable string conversion to unicode escape sequence', JavaScriptObfuscatorCLI.parseBoolean).parse(this.rawArguments);
             }).option('--rotateStringArray <boolean>', 'Disable rotation of unicode array values during obfuscation', JavaScriptObfuscatorCLI.parseBoolean).option('--seed <number>', 'Sets seed for random generator. This is useful for creating repeatable results.', parseFloat).option('--selfDefending <boolean>', 'Disables self-defending for obfuscated code', JavaScriptObfuscatorCLI.parseBoolean).option('--sourceMap <boolean>', 'Enables source map generation', JavaScriptObfuscatorCLI.parseBoolean).option('--sourceMapBaseUrl <string>', 'Sets base url to the source map import url when `--sourceMapMode=separate`').option('--sourceMapFileName <string>', 'Sets file name for output source map when `--sourceMapMode=separate`').option('--sourceMapMode <string> [inline, separate]', 'Specify source map output mode', JavaScriptObfuscatorCLI.parseSourceMapMode).option('--stringArray <boolean>', 'Disables gathering of all literal strings into an array and replacing every literal string with an array call', JavaScriptObfuscatorCLI.parseBoolean).option('--stringArrayEncoding <boolean|string> [true, false, base64, rc4]', 'Encodes all strings in strings array using base64 or rc4 (this option can slow down your code speed', JavaScriptObfuscatorCLI.parseStringArrayEncoding).option('--stringArrayThreshold <number>', 'The probability that the literal string will be inserted into stringArray (Default: 0.8, Min: 0, Max: 1)', parseFloat).option('--unicodeEscapeSequence <boolean>', 'Allows to enable/disable string conversion to unicode escape sequence', JavaScriptObfuscatorCLI.parseBoolean).parse(this.rawArguments);
             this.commands.on('--help', function () {
             this.commands.on('--help', function () {
@@ -4045,7 +4052,7 @@ var NodeCallsControllerFunctionNode = function (_AbstractCustomNode_) {
             if (this.appendEvent === ObfuscationEvents_1.ObfuscationEvents.AfterObfuscation) {
             if (this.appendEvent === ObfuscationEvents_1.ObfuscationEvents.AfterObfuscation) {
                 return JavaScriptObfuscator_1.JavaScriptObfuscator.obfuscate(format(SingleNodeCallControllerTemplate_1.SingleNodeCallControllerTemplate(), {
                 return JavaScriptObfuscator_1.JavaScriptObfuscator.obfuscate(format(SingleNodeCallControllerTemplate_1.SingleNodeCallControllerTemplate(), {
                     singleNodeCallControllerFunctionName: this.callsControllerFunctionName
                     singleNodeCallControllerFunctionName: this.callsControllerFunctionName
-                }), Object.assign({}, NoCustomNodes_1.NO_CUSTOM_NODES_PRESET, { seed: this.options.seed })).getObfuscatedCode();
+                }), tslib_1.__assign({}, NoCustomNodes_1.NO_CUSTOM_NODES_PRESET, { seed: this.options.seed })).getObfuscatedCode();
             }
             }
             return format(SingleNodeCallControllerTemplate_1.SingleNodeCallControllerTemplate(), {
             return format(SingleNodeCallControllerTemplate_1.SingleNodeCallControllerTemplate(), {
                 singleNodeCallControllerFunctionName: this.callsControllerFunctionName
                 singleNodeCallControllerFunctionName: this.callsControllerFunctionName
@@ -4259,7 +4266,7 @@ var StringArrayCallsWrapper = function (_AbstractCustomNode_) {
                 decodeNodeTemplate: decodeNodeTemplate,
                 decodeNodeTemplate: decodeNodeTemplate,
                 stringArrayCallsWrapperName: this.stringArrayCallsWrapperName,
                 stringArrayCallsWrapperName: this.stringArrayCallsWrapperName,
                 stringArrayName: this.stringArrayName
                 stringArrayName: this.stringArrayName
-            }), Object.assign({}, NoCustomNodes_1.NO_CUSTOM_NODES_PRESET, { seed: this.options.seed })).getObfuscatedCode();
+            }), tslib_1.__assign({}, NoCustomNodes_1.NO_CUSTOM_NODES_PRESET, { seed: this.options.seed })).getObfuscatedCode();
         }
         }
     }, {
     }, {
         key: "getDecodeStringArrayTemplate",
         key: "getDecodeStringArrayTemplate",
@@ -4440,7 +4447,7 @@ var StringArrayRotateFunctionNode = function (_AbstractCustomNode_) {
                 stringArrayName: this.stringArrayName,
                 stringArrayName: this.stringArrayName,
                 stringArrayRotateValue: Utils_1.Utils.decToHex(this.stringArrayRotateValue),
                 stringArrayRotateValue: Utils_1.Utils.decToHex(this.stringArrayRotateValue),
                 whileFunctionName: whileFunctionName
                 whileFunctionName: whileFunctionName
-            }), Object.assign({}, NoCustomNodes_1.NO_CUSTOM_NODES_PRESET, { seed: this.options.seed })).getObfuscatedCode();
+            }), tslib_1.__assign({}, NoCustomNodes_1.NO_CUSTOM_NODES_PRESET, { seed: this.options.seed })).getObfuscatedCode();
         }
         }
     }]);
     }]);
 
 
@@ -4573,7 +4580,7 @@ function _inherits(subClass, superClass) { if (typeof superClass !== "function"
 
 
 var tslib_1 = __webpack_require__(1);
 var tslib_1 = __webpack_require__(1);
 var inversify_1 = __webpack_require__(0);
 var inversify_1 = __webpack_require__(0);
-var events_1 = __webpack_require__(129);
+var events_1 = __webpack_require__(130);
 inversify_1.decorate(inversify_1.injectable(), events_1.EventEmitter);
 inversify_1.decorate(inversify_1.injectable(), events_1.EventEmitter);
 var ObfuscationEventEmitter = function (_events_1$EventEmitte) {
 var ObfuscationEventEmitter = function (_events_1$EventEmitte) {
     _inherits(ObfuscationEventEmitter, _events_1$EventEmitte);
     _inherits(ObfuscationEventEmitter, _events_1$EventEmitte);
@@ -5199,6 +5206,8 @@ var MethodDefinitionTransformer_1;
 "use strict";
 "use strict";
 
 
 
 
+var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
+
 var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
 var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
 
 
 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
@@ -5255,11 +5264,17 @@ var TemplateLiteralTransformer = TemplateLiteralTransformer_1 = function (_Abstr
                 nodes.unshift(Nodes_1.Nodes.getLiteralNode(''));
                 nodes.unshift(Nodes_1.Nodes.getLiteralNode(''));
             }
             }
             if (nodes.length > 1) {
             if (nodes.length > 1) {
-                var root = Nodes_1.Nodes.getBinaryExpressionNode('+', nodes.shift(), nodes.shift());
-                nodes.forEach(function (node) {
-                    root = Nodes_1.Nodes.getBinaryExpressionNode('+', root, node);
-                });
-                return root;
+                var _ret = function () {
+                    var root = Nodes_1.Nodes.getBinaryExpressionNode('+', nodes.shift(), nodes.shift());
+                    nodes.forEach(function (node) {
+                        root = Nodes_1.Nodes.getBinaryExpressionNode('+', root, node);
+                    });
+                    return {
+                        v: root
+                    };
+                }();
+
+                if ((typeof _ret === "undefined" ? "undefined" : _typeof(_ret)) === "object") return _ret.v;
             }
             }
             return nodes[0];
             return nodes[0];
         }
         }
@@ -5338,14 +5353,20 @@ var DeadCodeInjectionTransformer = DeadCodeInjectionTransformer_1 = function (_A
 
 
             estraverse.traverse(programNode, {
             estraverse.traverse(programNode, {
                 enter: function enter(node, parentNode) {
                 enter: function enter(node, parentNode) {
-                    return DeadCodeInjectionTransformer_1.collectBlockStatementNodes(node, _this3.collectedBlockStatements);
+                    if (!Node_1.Node.isBlockStatementNode(node)) {
+                        return;
+                    }
+                    DeadCodeInjectionTransformer_1.collectBlockStatementNodes(node, _this3.collectedBlockStatements);
                 }
                 }
             });
             });
-            if (this.collectedBlockStatements.length < 10) {
+            if (this.collectedBlockStatements.length < DeadCodeInjectionTransformer_1.minCollectedBlockStatementsCount) {
                 return;
                 return;
             }
             }
             estraverse.replace(programNode, {
             estraverse.replace(programNode, {
                 leave: function leave(node, parentNode) {
                 leave: function leave(node, parentNode) {
+                    if (!_this3.collectedBlockStatements.length) {
+                        return estraverse.VisitorOption.Break;
+                    }
                     if (!Node_1.Node.isBlockStatementNode(node) || RandomGeneratorUtils_1.RandomGeneratorUtils.getMathRandom() > _this3.options.deadCodeInjectionThreshold) {
                     if (!Node_1.Node.isBlockStatementNode(node) || RandomGeneratorUtils_1.RandomGeneratorUtils.getMathRandom() > _this3.options.deadCodeInjectionThreshold) {
                         return node;
                         return node;
                     }
                     }
@@ -5354,47 +5375,39 @@ var DeadCodeInjectionTransformer = DeadCodeInjectionTransformer_1 = function (_A
                     if (randomBlockStatementNode === node) {
                     if (randomBlockStatementNode === node) {
                         return node;
                         return node;
                     }
                     }
-                    return DeadCodeInjectionTransformer_1.replaceBlockStatementNodes(node, randomBlockStatementNode);
+                    return DeadCodeInjectionTransformer_1.replaceBlockStatementNode(node, randomBlockStatementNode);
                 }
                 }
             });
             });
         }
         }
     }], [{
     }], [{
         key: "collectBlockStatementNodes",
         key: "collectBlockStatementNodes",
-        value: function collectBlockStatementNodes(targetNode, collectedBlockStatements) {
-            if (!Node_1.Node.isBlockStatementNode(targetNode) || !DeadCodeInjectionTransformer_1.isValidBlockStatementNode(targetNode)) {
-                return;
-            }
-            var clonedBlockStatementNode = NodeUtils_1.NodeUtils.clone(targetNode);
+        value: function collectBlockStatementNodes(blockStatementNode, collectedBlockStatements) {
+            var clonedBlockStatementNode = NodeUtils_1.NodeUtils.clone(blockStatementNode);
+            var nestedBlockStatementsCount = 0,
+                isValidBlockStatementNode = true;
             estraverse.replace(clonedBlockStatementNode, {
             estraverse.replace(clonedBlockStatementNode, {
                 enter: function enter(node, parentNode) {
                 enter: function enter(node, parentNode) {
-                    if (Node_1.Node.isIdentifierNode(node)) {
+                    if (Node_1.Node.isBlockStatementNode(node)) {
+                        nestedBlockStatementsCount++;
+                    }
+                    if (nestedBlockStatementsCount > DeadCodeInjectionTransformer_1.maxNestedBlockStatementsCount || Node_1.Node.isBreakStatementNode(node) || Node_1.Node.isContinueStatementNode(node)) {
+                        isValidBlockStatementNode = false;
+                        return estraverse.VisitorOption.Break;
+                    }
+                    if (Node_1.Node.isIdentifierNode(node) && !Node_1.Node.isMemberExpressionNode(parentNode)) {
                         node.name = RandomGeneratorUtils_1.RandomGeneratorUtils.getRandomVariableName(6);
                         node.name = RandomGeneratorUtils_1.RandomGeneratorUtils.getRandomVariableName(6);
                     }
                     }
                     return node;
                     return node;
                 }
                 }
             });
             });
+            if (!isValidBlockStatementNode) {
+                return;
+            }
             collectedBlockStatements.push(clonedBlockStatementNode);
             collectedBlockStatements.push(clonedBlockStatementNode);
         }
         }
     }, {
     }, {
-        key: "isValidBlockStatementNode",
-        value: function isValidBlockStatementNode(blockStatementNode) {
-            var blockStatementsCount = 0,
-                isValidBlockStatementNode = true;
-            estraverse.traverse(blockStatementNode, {
-                enter: function enter(node, parentNode) {
-                    if (blockStatementNode !== node && Node_1.Node.isBlockStatementNode(node)) {
-                        blockStatementsCount++;
-                    }
-                    if (blockStatementsCount > DeadCodeInjectionTransformer_1.maxNestedBlockStatementsCount || Node_1.Node.isBreakStatementNode(node) || Node_1.Node.isContinueStatementNode(node)) {
-                        isValidBlockStatementNode = false;
-                    }
-                }
-            });
-            return isValidBlockStatementNode;
-        }
-    }, {
-        key: "replaceBlockStatementNodes",
-        value: function replaceBlockStatementNodes(blockStatementNode, randomBlockStatementNode) {
+        key: "replaceBlockStatementNode",
+        value: function replaceBlockStatementNode(blockStatementNode, randomBlockStatementNode) {
             var random1 = RandomGeneratorUtils_1.RandomGeneratorUtils.getMathRandom() > 0.5;
             var random1 = RandomGeneratorUtils_1.RandomGeneratorUtils.getMathRandom() > 0.5;
             var random2 = RandomGeneratorUtils_1.RandomGeneratorUtils.getMathRandom() > 0.5;
             var random2 = RandomGeneratorUtils_1.RandomGeneratorUtils.getMathRandom() > 0.5;
             var operator = random1 ? '===' : '!==';
             var operator = random1 ? '===' : '!==';
@@ -5418,6 +5431,7 @@ var DeadCodeInjectionTransformer = DeadCodeInjectionTransformer_1 = function (_A
     return DeadCodeInjectionTransformer;
     return DeadCodeInjectionTransformer;
 }(AbstractNodeTransformer_1.AbstractNodeTransformer);
 }(AbstractNodeTransformer_1.AbstractNodeTransformer);
 DeadCodeInjectionTransformer.maxNestedBlockStatementsCount = 4;
 DeadCodeInjectionTransformer.maxNestedBlockStatementsCount = 4;
+DeadCodeInjectionTransformer.minCollectedBlockStatementsCount = 5;
 DeadCodeInjectionTransformer = DeadCodeInjectionTransformer_1 = tslib_1.__decorate([inversify_1.injectable(), tslib_1.__param(0, inversify_1.inject(ServiceIdentifiers_1.ServiceIdentifiers.IOptions)), tslib_1.__metadata("design:paramtypes", [Object])], DeadCodeInjectionTransformer);
 DeadCodeInjectionTransformer = DeadCodeInjectionTransformer_1 = tslib_1.__decorate([inversify_1.injectable(), tslib_1.__param(0, inversify_1.inject(ServiceIdentifiers_1.ServiceIdentifiers.IOptions)), tslib_1.__metadata("design:paramtypes", [Object])], DeadCodeInjectionTransformer);
 exports.DeadCodeInjectionTransformer = DeadCodeInjectionTransformer;
 exports.DeadCodeInjectionTransformer = DeadCodeInjectionTransformer;
 var DeadCodeInjectionTransformer_1;
 var DeadCodeInjectionTransformer_1;
@@ -6387,6 +6401,7 @@ tslib_1.__decorate([class_validator_1.IsBoolean(), tslib_1.__metadata("design:ty
 tslib_1.__decorate([class_validator_1.IsArray(), class_validator_1.ArrayUnique(), class_validator_1.IsString({
 tslib_1.__decorate([class_validator_1.IsArray(), class_validator_1.ArrayUnique(), class_validator_1.IsString({
     each: true
     each: true
 }), tslib_1.__metadata("design:type", Array)], Options.prototype, "domainLock", void 0);
 }), tslib_1.__metadata("design:type", Array)], Options.prototype, "domainLock", void 0);
+tslib_1.__decorate([class_validator_1.IsBoolean(), tslib_1.__metadata("design:type", Boolean)], Options.prototype, "mangle", void 0);
 tslib_1.__decorate([class_validator_1.IsArray(), class_validator_1.ArrayUnique(), class_validator_1.IsString({
 tslib_1.__decorate([class_validator_1.IsArray(), class_validator_1.ArrayUnique(), class_validator_1.IsString({
     each: true
     each: true
 }), tslib_1.__metadata("design:type", Array)], Options.prototype, "reservedNames", void 0);
 }), tslib_1.__metadata("design:type", Array)], Options.prototype, "reservedNames", void 0);
@@ -6738,7 +6753,7 @@ var StackTraceAnalyzer = StackTraceAnalyzer_1 = function () {
                 if (!calleeData) {
                 if (!calleeData) {
                     return;
                     return;
                 }
                 }
-                stackTraceData.push(Object.assign({}, calleeData, { stackTrace: _this2.analyzeRecursive(calleeData.callee.body) }));
+                stackTraceData.push(tslib_1.__assign({}, calleeData, { stackTrace: _this2.analyzeRecursive(calleeData.callee.body) }));
             });
             });
         }
         }
     }], [{
     }], [{
@@ -7500,24 +7515,30 @@ module.exports = require("commander");
 /* 129 */
 /* 129 */
 /***/ (function(module, exports) {
 /***/ (function(module, exports) {
 
 
-module.exports = require("events");
+module.exports = require("esmangle");
 
 
 /***/ }),
 /***/ }),
 /* 130 */
 /* 130 */
 /***/ (function(module, exports) {
 /***/ (function(module, exports) {
 
 
-module.exports = require("fs");
+module.exports = require("events");
 
 
 /***/ }),
 /***/ }),
 /* 131 */
 /* 131 */
 /***/ (function(module, exports) {
 /***/ (function(module, exports) {
 
 
-module.exports = require("mkdirp");
+module.exports = require("fs");
 
 
 /***/ }),
 /***/ }),
 /* 132 */
 /* 132 */
 /***/ (function(module, exports) {
 /***/ (function(module, exports) {
 
 
+module.exports = require("mkdirp");
+
+/***/ }),
+/* 133 */
+/***/ (function(module, exports) {
+
 module.exports = require("reflect-metadata");
 module.exports = require("reflect-metadata");
 
 
 /***/ })
 /***/ })

+ 1 - 0
package.json

@@ -23,6 +23,7 @@
     "class-validator": "0.7.0",
     "class-validator": "0.7.0",
     "commander": "2.9.0",
     "commander": "2.9.0",
     "escodegen": "1.8.1",
     "escodegen": "1.8.1",
+    "esmangle": "^1.0.1",
     "esprima": "3.1.3",
     "esprima": "3.1.3",
     "estraverse": "4.2.0",
     "estraverse": "4.2.0",
     "inversify": "3.3.0",
     "inversify": "3.3.0",

+ 10 - 4
src/JavaScriptObfuscatorInternal.ts

@@ -3,6 +3,7 @@ import { ServiceIdentifiers } from './container/ServiceIdentifiers';
 
 
 import * as esprima from 'esprima';
 import * as esprima from 'esprima';
 import * as escodegen from 'escodegen';
 import * as escodegen from 'escodegen';
+import * as esmangle from 'esmangle';
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
 import { IGeneratorOutput } from './interfaces/IGeneratorOutput';
 import { IGeneratorOutput } from './interfaces/IGeneratorOutput';
@@ -83,11 +84,16 @@ export class JavaScriptObfuscatorInternal implements IJavaScriptObfuscator {
             escodegenParams.sourceContent = sourceCode;
             escodegenParams.sourceContent = sourceCode;
         }
         }
 
 
-        escodegenParams.format = {
-            compact: this.options.compact
-        };
+        if (this.options.mangle) {
+            astTree = esmangle.mangle(astTree);
+        }
 
 
-        const generatorOutput: IGeneratorOutput = escodegen.generate(astTree, escodegenParams);
+        const generatorOutput: IGeneratorOutput = escodegen.generate(astTree, {
+            ...escodegenParams,
+            format: {
+                compact: this.options.compact
+            }
+        });
 
 
         generatorOutput.map = generatorOutput.map ? generatorOutput.map.toString() : '';
         generatorOutput.map = generatorOutput.map ? generatorOutput.map.toString() : '';
 
 

+ 7 - 3
src/Obfuscator.ts

@@ -136,13 +136,17 @@ export class Obfuscator implements IObfuscator {
 
 
         this.obfuscationEventEmitter.emit(ObfuscationEvents.BeforeObfuscation, astTree, stackTraceData);
         this.obfuscationEventEmitter.emit(ObfuscationEvents.BeforeObfuscation, astTree, stackTraceData);
 
 
-        // first pass transformers: dead code injection and control flow flattening transformers
+        // first pass transformers: dead code injection transformer
+        astTree = this.transformAstTree(astTree, [
+            ...this.options.deadCodeInjection ? Obfuscator.deadCodeInjectionTransformersList : []
+        ]);
+
+        // second pass transformers: control flow flattening transformers
         astTree = this.transformAstTree(astTree, [
         astTree = this.transformAstTree(astTree, [
-            ...this.options.deadCodeInjection ? Obfuscator.deadCodeInjectionTransformersList : [],
             ...this.options.controlFlowFlattening ? Obfuscator.controlFlowTransformersList : []
             ...this.options.controlFlowFlattening ? Obfuscator.controlFlowTransformersList : []
         ]);
         ]);
 
 
-        // second pass: nodes obfuscation
+        // third pass: converting and obfuscating transformers
         astTree = this.transformAstTree(astTree, [
         astTree = this.transformAstTree(astTree, [
             ...Obfuscator.convertingTransformersList,
             ...Obfuscator.convertingTransformersList,
             ...Obfuscator.obfuscatingTransformersList
             ...Obfuscator.obfuscatingTransformersList

+ 4 - 0
src/cli/JavaScriptObfuscatorCLI.ts

@@ -194,6 +194,10 @@ export class JavaScriptObfuscatorCLI {
                 'Blocks the execution of the code in domains that do not match the passed RegExp patterns (comma separated)',
                 'Blocks the execution of the code in domains that do not match the passed RegExp patterns (comma separated)',
                 (value: string) => value.split(',')
                 (value: string) => value.split(',')
             )
             )
+            .option(
+                '--mangle <boolean>', 'Enables mangling of variable names',
+                JavaScriptObfuscatorCLI.parseBoolean
+            )
             .option(
             .option(
                 '--reservedNames <list>',
                 '--reservedNames <list>',
                 'Disable obfuscation of variable names, function names and names of function parameters that match the passed RegExp patterns (comma separated)',
                 'Disable obfuscation of variable names, function names and names of function parameters that match the passed RegExp patterns (comma separated)',

+ 5 - 0
src/declarations/esmangle.d.ts

@@ -0,0 +1,5 @@
+declare module "esmangle" {
+    import * as ESTree from 'estree';
+
+    function mangle(ast: ESTree.Program): ESTree.Program;
+}

+ 1 - 0
src/interfaces/options/IOptions.d.ts

@@ -11,6 +11,7 @@ export interface IOptions {
     readonly debugProtectionInterval: boolean;
     readonly debugProtectionInterval: boolean;
     readonly disableConsoleOutput: boolean;
     readonly disableConsoleOutput: boolean;
     readonly domainLock: string[];
     readonly domainLock: string[];
+    readonly mangle: boolean;
     readonly reservedNames: string[];
     readonly reservedNames: string[];
     readonly rotateStringArray: boolean;
     readonly rotateStringArray: boolean;
     readonly seed: number;
     readonly seed: number;

+ 56 - 38
src/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.ts

@@ -18,12 +18,17 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
     /**
     /**
      * @type {number}
      * @type {number}
      */
      */
-    private static maxNestedBlockStatementsCount: number = 4;
+    private static readonly maxNestedBlockStatementsCount: number = 4;
+
+    /**
+     * @type {number}
+     */
+    private static readonly minCollectedBlockStatementsCount: number = 5;
 
 
     /**
     /**
      * @type {ESTree.BlockStatement[]}
      * @type {ESTree.BlockStatement[]}
      */
      */
-    private collectedBlockStatements: ESTree.BlockStatement[] = [];
+    private readonly collectedBlockStatements: ESTree.BlockStatement[] = [];
 
 
     /**
     /**
      * @param options
      * @param options
@@ -35,54 +40,58 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
     }
     }
 
 
     /**
     /**
-     * @param targetNode
+     * @param blockStatementNode
      * @param collectedBlockStatements
      * @param collectedBlockStatements
      */
      */
-    private static collectBlockStatementNodes (targetNode: ESTree.Node, collectedBlockStatements: ESTree.BlockStatement[]): void {
-        if (!Node.isBlockStatementNode(targetNode) || !DeadCodeInjectionTransformer.isValidBlockStatementNode(targetNode)) {
-            return;
-        }
-
-        const clonedBlockStatementNode: ESTree.BlockStatement = <ESTree.BlockStatement>NodeUtils.clone(targetNode);
-
-        estraverse.replace(clonedBlockStatementNode, {
-            enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
-                if (Node.isIdentifierNode(node)) {
-                    node.name = RandomGeneratorUtils.getRandomVariableName(6);
-                }
-
-                return node;
-            }
-        });
-
-        collectedBlockStatements.push(clonedBlockStatementNode);
-    }
+    private static collectBlockStatementNodes (
+        blockStatementNode: ESTree.BlockStatement,
+        collectedBlockStatements: ESTree.BlockStatement[]
+    ): void {
+        const clonedBlockStatementNode: ESTree.BlockStatement = NodeUtils.clone(blockStatementNode);
 
 
-    /**
-     * @param blockStatementNode
-     * @return {boolean}
-     */
-    private static isValidBlockStatementNode (blockStatementNode: ESTree.BlockStatement): boolean {
-        let blockStatementsCount: number = 0,
+        let nestedBlockStatementsCount: number = 0,
             isValidBlockStatementNode: boolean = true;
             isValidBlockStatementNode: boolean = true;
 
 
-        estraverse.traverse(blockStatementNode, {
+        estraverse.replace(clonedBlockStatementNode, {
             enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
             enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
-                if (blockStatementNode !== node && Node.isBlockStatementNode(node)) {
-                    blockStatementsCount++;
+                /**
+                 * First step: count nested block statements in current block statement
+                 */
+                if (Node.isBlockStatementNode(node)) {
+                    nestedBlockStatementsCount++;
                 }
                 }
 
 
+                /**
+                 * If nested block statements count bigger then specified amount or current block statement
+                 * contains prohibited nodes - we will stop traversing and leave method
+                 */
                 if (
                 if (
-                    blockStatementsCount > DeadCodeInjectionTransformer.maxNestedBlockStatementsCount ||
+                    nestedBlockStatementsCount > DeadCodeInjectionTransformer.maxNestedBlockStatementsCount ||
                     Node.isBreakStatementNode(node) ||
                     Node.isBreakStatementNode(node) ||
                     Node.isContinueStatementNode(node)
                     Node.isContinueStatementNode(node)
                 ) {
                 ) {
                     isValidBlockStatementNode = false;
                     isValidBlockStatementNode = false;
+
+                    return estraverse.VisitorOption.Break;
                 }
                 }
+
+                /**
+                 * Second step: rename all identifiers (except identifiers in member expressions)
+                 * in current block statement
+                 */
+                if (Node.isIdentifierNode(node) && !Node.isMemberExpressionNode(parentNode)) {
+                    node.name = RandomGeneratorUtils.getRandomVariableName(6);
+                }
+
+                return node;
             }
             }
         });
         });
 
 
-        return isValidBlockStatementNode;
+        if (!isValidBlockStatementNode) {
+            return;
+        }
+
+        collectedBlockStatements.push(clonedBlockStatementNode);
     }
     }
 
 
     /**
     /**
@@ -90,7 +99,7 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
      * @param randomBlockStatementNode
      * @param randomBlockStatementNode
      * @return {ESTree.BlockStatement}
      * @return {ESTree.BlockStatement}
      */
      */
-    private static replaceBlockStatementNodes (
+    private static replaceBlockStatementNode (
         blockStatementNode: ESTree.BlockStatement,
         blockStatementNode: ESTree.BlockStatement,
         randomBlockStatementNode: ESTree.BlockStatement
         randomBlockStatementNode: ESTree.BlockStatement
     ): ESTree.BlockStatement {
     ): ESTree.BlockStatement {
@@ -158,16 +167,25 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
      */
      */
     private transformProgramNode (programNode: ESTree.Program): void {
     private transformProgramNode (programNode: ESTree.Program): void {
         estraverse.traverse(programNode, {
         estraverse.traverse(programNode, {
-            enter: (node: ESTree.Node, parentNode: ESTree.Node): any =>
-                DeadCodeInjectionTransformer.collectBlockStatementNodes(node, this.collectedBlockStatements)
+            enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
+                if (!Node.isBlockStatementNode(node)) {
+                    return;
+                }
+
+                DeadCodeInjectionTransformer.collectBlockStatementNodes(node, this.collectedBlockStatements);
+            }
         });
         });
 
 
-        if (this.collectedBlockStatements.length < 10) {
+        if (this.collectedBlockStatements.length < DeadCodeInjectionTransformer.minCollectedBlockStatementsCount) {
             return;
             return;
         }
         }
 
 
         estraverse.replace(programNode, {
         estraverse.replace(programNode, {
             leave: (node: ESTree.Node, parentNode: ESTree.Node): any => {
             leave: (node: ESTree.Node, parentNode: ESTree.Node): any => {
+                if (!this.collectedBlockStatements.length) {
+                    return estraverse.VisitorOption.Break;
+                }
+
                 if (
                 if (
                     !Node.isBlockStatementNode(node) ||
                     !Node.isBlockStatementNode(node) ||
                     RandomGeneratorUtils.getMathRandom() > this.options.deadCodeInjectionThreshold
                     RandomGeneratorUtils.getMathRandom() > this.options.deadCodeInjectionThreshold
@@ -182,7 +200,7 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
                     return node;
                     return node;
                 }
                 }
 
 
-                return DeadCodeInjectionTransformer.replaceBlockStatementNodes(node, randomBlockStatementNode);
+                return DeadCodeInjectionTransformer.replaceBlockStatementNode(node, randomBlockStatementNode);
             }
             }
         });
         });
     }
     }

+ 3 - 3
src/node/NodeUtils.ts

@@ -44,8 +44,8 @@ export class NodeUtils {
      * @param astTree
      * @param astTree
      * @return {ESTree.Node}
      * @return {ESTree.Node}
      */
      */
-    public static clone (astTree: ESTree.Node): ESTree.Node {
-        const cloneRecursive: (node: ESTree.Node) => ESTree.Node = (node: ESTree.Node) => {
+    public static clone <T extends ESTree.Node> (astTree: T): T {
+        const cloneRecursive: (node: T) => T = (node: T) => {
             const copy: {[key: string]: any} = {};
             const copy: {[key: string]: any} = {};
 
 
             Object
             Object
@@ -69,7 +69,7 @@ export class NodeUtils {
                     copy[property] = clonedValue;
                     copy[property] = clonedValue;
                 });
                 });
 
 
-            return <ESTree.Node>copy;
+            return <T>copy;
         };
         };
 
 
         return NodeUtils.parentize(cloneRecursive(astTree));
         return NodeUtils.parentize(cloneRecursive(astTree));

+ 6 - 0
src/options/Options.ts

@@ -99,6 +99,12 @@ export class Options implements IOptions {
     })
     })
     public readonly domainLock: string[];
     public readonly domainLock: string[];
 
 
+    /**
+     * @type {boolean}
+     */
+    @IsBoolean()
+    public readonly mangle: boolean;
+
     /**
     /**
      * @type {string[]}
      * @type {string[]}
      */
      */

+ 1 - 0
src/options/presets/Default.ts

@@ -12,6 +12,7 @@ export const DEFAULT_PRESET: TInputOptions = Object.freeze({
     debugProtectionInterval: false,
     debugProtectionInterval: false,
     disableConsoleOutput: true,
     disableConsoleOutput: true,
     domainLock: [],
     domainLock: [],
+    mangle: false,
     reservedNames: [],
     reservedNames: [],
     rotateStringArray: true,
     rotateStringArray: true,
     seed: 0,
     seed: 0,

+ 1 - 0
src/options/presets/NoCustomNodes.ts

@@ -12,6 +12,7 @@ export const NO_CUSTOM_NODES_PRESET: TInputOptions = Object.freeze({
     debugProtectionInterval: false,
     debugProtectionInterval: false,
     disableConsoleOutput: false,
     disableConsoleOutput: false,
     domainLock: [],
     domainLock: [],
+    mangle: false,
     reservedNames: [],
     reservedNames: [],
     rotateStringArray: false,
     rotateStringArray: false,
     seed: 0,
     seed: 0,

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

@@ -199,6 +199,17 @@ describe('JavaScriptObfuscator', () => {
             assert.notEqual(obfuscationResult3.getObfuscatedCode(), obfuscationResult4.getObfuscatedCode());
             assert.notEqual(obfuscationResult3.getObfuscatedCode(), obfuscationResult4.getObfuscatedCode());
         });
         });
 
 
+        it('should mangle obfuscated code', () => {
+            const code: string = readFileAsString(__dirname + '/fixtures/mangle.js');
+
+            const obfuscationResult1: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code, { mangle: true }
+            );
+            const mangleMatch: RegExp = /var *a *= *0x1/;
+
+            assert.match(obfuscationResult1.getObfuscatedCode(), mangleMatch);
+        });
+
         afterEach(() => {
         afterEach(() => {
             RandomGeneratorUtils.initializeRandomGenerator(0);
             RandomGeneratorUtils.initializeRandomGenerator(0);
         });
         });

+ 3 - 0
test/functional-tests/javascript-obfuscator/fixtures/mangle.js

@@ -0,0 +1,3 @@
+var foo = function () {
+    var test = 1;
+};