Преглед изворни кода

additional ~10-15% performance boost

sanex3339 пре 8 година
родитељ
комит
7b308b331e

+ 27 - 15
dist/index.js

@@ -947,9 +947,11 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
 var tslib_1 = __webpack_require__(3);
 var inversify_1 = __webpack_require__(2);
 var ServiceIdentifiers_1 = __webpack_require__(4);
+var RandomGeneratorUtils_1 = __webpack_require__(8);
 var AbstractNodeTransformer = function AbstractNodeTransformer(options) {
     (0, _classCallCheck3.default)(this, AbstractNodeTransformer);
 
+    this.nodeIdentifier = RandomGeneratorUtils_1.RandomGeneratorUtils.getRandomInteger(0, 10000);
     this.options = options;
 };
 AbstractNodeTransformer = tslib_1.__decorate([inversify_1.injectable(), tslib_1.__param(0, inversify_1.inject(ServiceIdentifiers_1.ServiceIdentifiers.IOptions)), tslib_1.__metadata("design:paramtypes", [Object])], AbstractNodeTransformer);
@@ -4739,7 +4741,6 @@ var NodeType_1 = __webpack_require__(16);
 var AbstractNodeTransformer_1 = __webpack_require__(17);
 var Node_1 = __webpack_require__(12);
 var NodeUtils_1 = __webpack_require__(9);
-var RandomGeneratorUtils_1 = __webpack_require__(8);
 var CatchClauseObfuscator = function (_AbstractNodeTransfor) {
     (0, _inherits3.default)(CatchClauseObfuscator, _AbstractNodeTransfor);
 
@@ -4755,7 +4756,7 @@ var CatchClauseObfuscator = function (_AbstractNodeTransfor) {
     (0, _createClass3.default)(CatchClauseObfuscator, [{
         key: "transformNode",
         value: function transformNode(catchClauseNode) {
-            var nodeIdentifier = RandomGeneratorUtils_1.RandomGeneratorUtils.getRandomString(7);
+            var nodeIdentifier = this.nodeIdentifier++;
             this.storeCatchClauseParam(catchClauseNode, nodeIdentifier);
             this.replaceCatchClauseParam(catchClauseNode, nodeIdentifier);
         }
@@ -4835,7 +4836,6 @@ var NodeType_1 = __webpack_require__(16);
 var AbstractNodeTransformer_1 = __webpack_require__(17);
 var Node_1 = __webpack_require__(12);
 var NodeUtils_1 = __webpack_require__(9);
-var RandomGeneratorUtils_1 = __webpack_require__(8);
 var FunctionDeclarationObfuscator = function (_AbstractNodeTransfor) {
     (0, _inherits3.default)(FunctionDeclarationObfuscator, _AbstractNodeTransfor);
 
@@ -4852,7 +4852,7 @@ var FunctionDeclarationObfuscator = function (_AbstractNodeTransfor) {
     (0, _createClass3.default)(FunctionDeclarationObfuscator, [{
         key: "transformNode",
         value: function transformNode(functionDeclarationNode, parentNode) {
-            var nodeIdentifier = RandomGeneratorUtils_1.RandomGeneratorUtils.getRandomString(7);
+            var nodeIdentifier = this.nodeIdentifier++;
             var blockScopeOfFunctionDeclarationNode = NodeUtils_1.NodeUtils.getBlockScopesOfNode(functionDeclarationNode)[0];
             if (blockScopeOfFunctionDeclarationNode.type === NodeType_1.NodeType.Program) {
                 return;
@@ -4965,7 +4965,6 @@ var NodeType_1 = __webpack_require__(16);
 var AbstractNodeTransformer_1 = __webpack_require__(17);
 var Node_1 = __webpack_require__(12);
 var NodeUtils_1 = __webpack_require__(9);
-var RandomGeneratorUtils_1 = __webpack_require__(8);
 var FunctionObfuscator = function (_AbstractNodeTransfor) {
     (0, _inherits3.default)(FunctionObfuscator, _AbstractNodeTransfor);
 
@@ -4981,7 +4980,7 @@ var FunctionObfuscator = function (_AbstractNodeTransfor) {
     (0, _createClass3.default)(FunctionObfuscator, [{
         key: "transformNode",
         value: function transformNode(functionNode) {
-            var nodeIdentifier = RandomGeneratorUtils_1.RandomGeneratorUtils.getRandomString(7);
+            var nodeIdentifier = this.nodeIdentifier++;
             this.storeFunctionParams(functionNode, nodeIdentifier);
             this.replaceFunctionParams(functionNode, nodeIdentifier);
         }
@@ -5063,7 +5062,6 @@ var NodeType_1 = __webpack_require__(16);
 var AbstractNodeTransformer_1 = __webpack_require__(17);
 var Node_1 = __webpack_require__(12);
 var NodeUtils_1 = __webpack_require__(9);
-var RandomGeneratorUtils_1 = __webpack_require__(8);
 var LabeledStatementObfuscator = function (_AbstractNodeTransfor) {
     (0, _inherits3.default)(LabeledStatementObfuscator, _AbstractNodeTransfor);
 
@@ -5079,7 +5077,7 @@ var LabeledStatementObfuscator = function (_AbstractNodeTransfor) {
     (0, _createClass3.default)(LabeledStatementObfuscator, [{
         key: "transformNode",
         value: function transformNode(labeledStatementNode) {
-            var nodeIdentifier = RandomGeneratorUtils_1.RandomGeneratorUtils.getRandomString(7);
+            var nodeIdentifier = this.nodeIdentifier++;
             this.storeLabeledStatementName(labeledStatementNode, nodeIdentifier);
             this.replaceLabeledStatementName(labeledStatementNode, nodeIdentifier);
         }
@@ -5516,7 +5514,6 @@ var NodeType_1 = __webpack_require__(16);
 var AbstractNodeTransformer_1 = __webpack_require__(17);
 var Node_1 = __webpack_require__(12);
 var NodeUtils_1 = __webpack_require__(9);
-var RandomGeneratorUtils_1 = __webpack_require__(8);
 var VariableDeclarationObfuscator = function (_AbstractNodeTransfor) {
     (0, _inherits3.default)(VariableDeclarationObfuscator, _AbstractNodeTransfor);
 
@@ -5537,7 +5534,7 @@ var VariableDeclarationObfuscator = function (_AbstractNodeTransfor) {
             if (blockScopeOfVariableDeclarationNode.type === NodeType_1.NodeType.Program) {
                 return;
             }
-            var nodeIdentifier = RandomGeneratorUtils_1.RandomGeneratorUtils.getRandomString(7);
+            var nodeIdentifier = this.nodeIdentifier++;
             var scopeNode = variableDeclarationNode.kind === 'var' ? blockScopeOfVariableDeclarationNode : parentNode;
             this.storeVariableNames(variableDeclarationNode, nodeIdentifier);
             this.replaceVariableNames(scopeNode, nodeIdentifier);
@@ -5717,7 +5714,7 @@ var IdentifierReplacer = function (_AbstractReplacer_1$A) {
     (0, _createClass3.default)(IdentifierReplacer, [{
         key: "replace",
         value: function replace(nodeValue, nodeIdentifier) {
-            var mapKey = nodeValue + "-" + nodeIdentifier;
+            var mapKey = nodeValue + "-" + String(nodeIdentifier);
             if (!this.namesMap.has(mapKey)) {
                 return nodeValue;
             }
@@ -5727,7 +5724,7 @@ var IdentifierReplacer = function (_AbstractReplacer_1$A) {
         key: "storeNames",
         value: function storeNames(nodeName, nodeIdentifier) {
             if (!this.isReservedName(nodeName)) {
-                this.namesMap.set(nodeName + "-" + nodeIdentifier, RandomGeneratorUtils_1.RandomGeneratorUtils.getRandomVariableName());
+                this.namesMap.set(nodeName + "-" + String(nodeIdentifier), RandomGeneratorUtils_1.RandomGeneratorUtils.getRandomVariableName());
             }
         }
     }, {
@@ -5750,6 +5747,10 @@ exports.IdentifierReplacer = IdentifierReplacer;
 "use strict";
 
 
+var _map = __webpack_require__(11);
+
+var _map2 = _interopRequireDefault(_map);
+
 var _getPrototypeOf = __webpack_require__(5);
 
 var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
@@ -5782,16 +5783,27 @@ var NumberLiteralReplacer = function (_AbstractReplacer_1$A) {
 
     function NumberLiteralReplacer(options) {
         (0, _classCallCheck3.default)(this, NumberLiteralReplacer);
-        return (0, _possibleConstructorReturn3.default)(this, (NumberLiteralReplacer.__proto__ || (0, _getPrototypeOf2.default)(NumberLiteralReplacer)).call(this, options));
+
+        var _this = (0, _possibleConstructorReturn3.default)(this, (NumberLiteralReplacer.__proto__ || (0, _getPrototypeOf2.default)(NumberLiteralReplacer)).call(this, options));
+
+        _this.numberLiteralCache = new _map2.default();
+        return _this;
     }
 
     (0, _createClass3.default)(NumberLiteralReplacer, [{
         key: "replace",
         value: function replace(nodeValue) {
+            if (this.numberLiteralCache.has(nodeValue)) {
+                return this.numberLiteralCache.get(nodeValue);
+            }
+            var result = void 0;
             if (!Utils_1.Utils.isCeilNumber(nodeValue)) {
-                return String(nodeValue);
+                result = String(nodeValue);
+            } else {
+                result = "" + Utils_1.Utils.hexadecimalPrefix + Utils_1.Utils.decToHex(nodeValue);
             }
-            return "" + Utils_1.Utils.hexadecimalPrefix + Utils_1.Utils.decToHex(nodeValue);
+            this.numberLiteralCache.set(nodeValue, result);
+            return result;
         }
     }]);
     return NumberLiteralReplacer;

+ 3 - 1
src/interfaces/node-transformers/IObfuscatorReplacer.d.ts

@@ -1,7 +1,9 @@
+import * as ESTree from 'estree';
+
 export interface IObfuscatorReplacer {
     /**
      * @param nodeValue
      * @param nodeIdentifier
      */
-    replace (nodeValue: any, nodeIdentifier?: string): string;
+    replace (nodeValue: any, nodeIdentifier?: number): string;
 }

+ 3 - 1
src/interfaces/node-transformers/IObfuscatorReplacerWithStorage.d.ts

@@ -1,3 +1,5 @@
+import * as ESTree from 'estree';
+
 import { IObfuscatorReplacer } from './IObfuscatorReplacer';
 
 export interface IObfuscatorReplacerWithStorage extends IObfuscatorReplacer {
@@ -5,5 +7,5 @@ export interface IObfuscatorReplacerWithStorage extends IObfuscatorReplacer {
      * @param nodeValue
      * @param nodeIdentifier
      */
-    storeNames (nodeValue: any, nodeIdentifier: string): void;
+    storeNames (nodeValue: any, nodeIdentifier: number): void;
 }

+ 8 - 1
src/node-transformers/AbstractNodeTransformer.ts

@@ -1,13 +1,20 @@
 import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../container/ServiceIdentifiers';
 
 import * as ESTree from 'estree';
 
 import { INodeTransformer } from '../interfaces/node-transformers/INodeTransformer';
 import { IOptions } from '../interfaces/options/IOptions';
-import { ServiceIdentifiers } from '../container/ServiceIdentifiers';
+
+import { RandomGeneratorUtils } from '../utils/RandomGeneratorUtils';
 
 @injectable()
 export abstract class AbstractNodeTransformer implements INodeTransformer {
+    /**
+     * @type {number}
+     */
+    protected nodeIdentifier: number = RandomGeneratorUtils.getRandomInteger(0, 10000);
+
     /**
      * @type {IOptions}
      */

+ 3 - 4
src/node-transformers/node-obfuscators/CatchClauseObfuscator.ts

@@ -14,7 +14,6 @@ import { NodeType } from '../../enums/NodeType';
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { Node } from '../../node/Node';
 import { NodeUtils } from '../../node/NodeUtils';
-import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
 
 /**
  * replaces:
@@ -48,7 +47,7 @@ export class CatchClauseObfuscator extends AbstractNodeTransformer {
      * @param catchClauseNode
      */
     public transformNode (catchClauseNode: ESTree.CatchClause): void {
-        const nodeIdentifier: string = RandomGeneratorUtils.getRandomString(7);
+        const nodeIdentifier: number = this.nodeIdentifier++;
 
         this.storeCatchClauseParam(catchClauseNode, nodeIdentifier);
         this.replaceCatchClauseParam(catchClauseNode, nodeIdentifier);
@@ -58,7 +57,7 @@ export class CatchClauseObfuscator extends AbstractNodeTransformer {
      * @param catchClauseNode
      * @param nodeIdentifier
      */
-    private storeCatchClauseParam (catchClauseNode: ESTree.CatchClause, nodeIdentifier: string): void {
+    private storeCatchClauseParam (catchClauseNode: ESTree.CatchClause, nodeIdentifier: number): void {
         NodeUtils.typedTraverse(catchClauseNode.param, NodeType.Identifier, {
             enter: (node: ESTree.Identifier) => this.identifierReplacer.storeNames(node.name, nodeIdentifier)
         });
@@ -68,7 +67,7 @@ export class CatchClauseObfuscator extends AbstractNodeTransformer {
      * @param catchClauseNode
      * @param nodeIdentifier
      */
-    private replaceCatchClauseParam (catchClauseNode: ESTree.CatchClause, nodeIdentifier: string): void {
+    private replaceCatchClauseParam (catchClauseNode: ESTree.CatchClause, nodeIdentifier: number): void {
         estraverse.replace(catchClauseNode, {
             enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
                 if (Node.isReplaceableIdentifierNode(node, parentNode)) {

+ 3 - 4
src/node-transformers/node-obfuscators/FunctionDeclarationObfuscator.ts

@@ -16,7 +16,6 @@ import { NodeType } from '../../enums/NodeType';
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { Node } from '../../node/Node';
 import { NodeUtils } from '../../node/NodeUtils';
-import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
 
 /**
  * replaces:
@@ -57,7 +56,7 @@ export class FunctionDeclarationObfuscator extends AbstractNodeTransformer {
      * @param parentNode
      */
     public transformNode (functionDeclarationNode: ESTree.FunctionDeclaration, parentNode: ESTree.Node): void {
-        const nodeIdentifier: string = RandomGeneratorUtils.getRandomString(7);
+        const nodeIdentifier: number = this.nodeIdentifier++;
         const blockScopeOfFunctionDeclarationNode: TNodeWithBlockStatement = NodeUtils
             .getBlockScopesOfNode(functionDeclarationNode)[0];
 
@@ -73,7 +72,7 @@ export class FunctionDeclarationObfuscator extends AbstractNodeTransformer {
      * @param functionDeclarationNode
      * @param nodeIdentifier
      */
-    private storeFunctionName (functionDeclarationNode: ESTree.FunctionDeclaration, nodeIdentifier: string): void {
+    private storeFunctionName (functionDeclarationNode: ESTree.FunctionDeclaration, nodeIdentifier: number): void {
         NodeUtils.typedTraverse(functionDeclarationNode.id, NodeType.Identifier, {
             enter: (node: ESTree.Identifier) => this.identifierReplacer.storeNames(node.name, nodeIdentifier)
         });
@@ -83,7 +82,7 @@ export class FunctionDeclarationObfuscator extends AbstractNodeTransformer {
      * @param scopeNode
      * @param nodeIdentifier
      */
-    private replaceFunctionName (scopeNode: ESTree.Node, nodeIdentifier: string): void {
+    private replaceFunctionName (scopeNode: ESTree.Node, nodeIdentifier: number): void {
         let replaceableIdentifiersForCurrentScope: ESTree.Identifier[];
 
         // check for cached identifiers for current scope node. If exist - loop through them.

+ 3 - 4
src/node-transformers/node-obfuscators/FunctionObfuscator.ts

@@ -14,7 +14,6 @@ import { NodeType } from '../../enums/NodeType';
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { Node } from '../../node/Node';
 import { NodeUtils } from '../../node/NodeUtils';
-import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
 
 /**
  * replaces:
@@ -48,7 +47,7 @@ export class FunctionObfuscator extends AbstractNodeTransformer {
      * @param functionNode
      */
     public transformNode (functionNode: ESTree.Function): void {
-        const nodeIdentifier: string = RandomGeneratorUtils.getRandomString(7);
+        const nodeIdentifier: number = this.nodeIdentifier++;
 
         this.storeFunctionParams(functionNode, nodeIdentifier);
         this.replaceFunctionParams(functionNode, nodeIdentifier);
@@ -58,7 +57,7 @@ export class FunctionObfuscator extends AbstractNodeTransformer {
      * @param functionNode
      * @param nodeIdentifier
      */
-    private storeFunctionParams (functionNode: ESTree.Function, nodeIdentifier: string): void {
+    private storeFunctionParams (functionNode: ESTree.Function, nodeIdentifier: number): void {
         functionNode.params
             .forEach((paramsNode: ESTree.Node) => {
                 NodeUtils.typedTraverse(paramsNode, NodeType.Identifier, {
@@ -71,7 +70,7 @@ export class FunctionObfuscator extends AbstractNodeTransformer {
      * @param functionNode
      * @param nodeIdentifier
      */
-    private replaceFunctionParams (functionNode: ESTree.Function, nodeIdentifier: string): void {
+    private replaceFunctionParams (functionNode: ESTree.Function, nodeIdentifier: number): void {
         const traverseVisitor: estraverse.Visitor = {
             enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
                 if (Node.isReplaceableIdentifierNode(node, parentNode)) {

+ 3 - 4
src/node-transformers/node-obfuscators/LabeledStatementObfuscator.ts

@@ -14,7 +14,6 @@ import { NodeType } from '../../enums/NodeType';
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { Node } from '../../node/Node';
 import { NodeUtils } from '../../node/NodeUtils';
-import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
 
 /**
  * replaces:
@@ -56,7 +55,7 @@ export class LabeledStatementObfuscator extends AbstractNodeTransformer {
      * @param labeledStatementNode
      */
     public transformNode (labeledStatementNode: ESTree.LabeledStatement): void {
-        const nodeIdentifier: string = RandomGeneratorUtils.getRandomString(7);
+        const nodeIdentifier: number = this.nodeIdentifier++;
 
         this.storeLabeledStatementName(labeledStatementNode, nodeIdentifier);
         this.replaceLabeledStatementName(labeledStatementNode, nodeIdentifier);
@@ -66,7 +65,7 @@ export class LabeledStatementObfuscator extends AbstractNodeTransformer {
      * @param labeledStatementNode
      * @param nodeIdentifier
      */
-    private storeLabeledStatementName (labeledStatementNode: ESTree.LabeledStatement, nodeIdentifier: string): void {
+    private storeLabeledStatementName (labeledStatementNode: ESTree.LabeledStatement, nodeIdentifier: number): void {
         NodeUtils.typedTraverse(labeledStatementNode.label, NodeType.Identifier, {
             enter: (node: ESTree.Identifier) => this.identifierReplacer.storeNames(node.name, nodeIdentifier)
         });
@@ -76,7 +75,7 @@ export class LabeledStatementObfuscator extends AbstractNodeTransformer {
      * @param labeledStatementNode
      * @param nodeIdentifier
      */
-    private replaceLabeledStatementName (labeledStatementNode: ESTree.LabeledStatement, nodeIdentifier: string): void {
+    private replaceLabeledStatementName (labeledStatementNode: ESTree.LabeledStatement, nodeIdentifier: number): void {
         estraverse.replace(labeledStatementNode, {
             enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
                 if (Node.isLabelIdentifierNode(node, parentNode)) {

+ 3 - 4
src/node-transformers/node-obfuscators/VariableDeclarationObfuscator.ts

@@ -16,7 +16,6 @@ import { NodeType } from '../../enums/NodeType';
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { Node } from '../../node/Node';
 import { NodeUtils } from '../../node/NodeUtils';
-import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
 
 /**
  * replaces:
@@ -65,7 +64,7 @@ export class VariableDeclarationObfuscator extends AbstractNodeTransformer {
             return;
         }
 
-        const nodeIdentifier: string = RandomGeneratorUtils.getRandomString(7);
+        const nodeIdentifier: number = this.nodeIdentifier++;
         const scopeNode: ESTree.Node = variableDeclarationNode.kind === 'var'
             ? blockScopeOfVariableDeclarationNode
             : parentNode;
@@ -78,7 +77,7 @@ export class VariableDeclarationObfuscator extends AbstractNodeTransformer {
      * @param variableDeclarationNode
      * @param nodeIdentifier
      */
-    private storeVariableNames (variableDeclarationNode: ESTree.VariableDeclaration, nodeIdentifier: string): void {
+    private storeVariableNames (variableDeclarationNode: ESTree.VariableDeclaration, nodeIdentifier: number): void {
         variableDeclarationNode.declarations
             .forEach((declarationNode: ESTree.VariableDeclarator) => {
                 NodeUtils.typedTraverse(declarationNode.id, NodeType.Identifier, {
@@ -91,7 +90,7 @@ export class VariableDeclarationObfuscator extends AbstractNodeTransformer {
      * @param scopeNode
      * @param nodeIdentifier
      */
-    private replaceVariableNames (scopeNode: ESTree.Node, nodeIdentifier: string): void {
+    private replaceVariableNames (scopeNode: ESTree.Node, nodeIdentifier: number): void {
         let replaceableIdentifiersForCurrentScope: ESTree.Identifier[];
 
         // check for cached identifiers for current scope node. If exist - loop through them.

+ 1 - 1
src/node-transformers/node-obfuscators/replacers/AbstractReplacer.ts

@@ -25,5 +25,5 @@ export abstract class AbstractReplacer implements IObfuscatorReplacer {
      * @param nodeIdentifier
      * @returns {string}
      */
-    public abstract replace (nodeValue: any, nodeIdentifier?: string): string;
+    public abstract replace (nodeValue: any, nodeIdentifier?: number): string;
 }

+ 5 - 5
src/node-transformers/node-obfuscators/replacers/IdentifierReplacer.ts

@@ -28,8 +28,8 @@ export class IdentifierReplacer extends AbstractReplacer implements IObfuscatorR
      * @param nodeIdentifier
      * @returns {string}
      */
-    public replace (nodeValue: string, nodeIdentifier: string): string {
-        const mapKey: string = `${nodeValue}-${nodeIdentifier}`;
+    public replace (nodeValue: string, nodeIdentifier: number): string {
+        const mapKey: string = `${nodeValue}-${String(nodeIdentifier)}`;
 
         if (!this.namesMap.has(mapKey)) {
             return nodeValue;
@@ -39,15 +39,15 @@ export class IdentifierReplacer extends AbstractReplacer implements IObfuscatorR
     }
 
     /**
-     * Store all identifiers names as keys in given `namesMap` with random names as value.
+     * Store all `nodeIdentifier`'s as keys in given `namesMap` with random names as value.
      * Reserved names will be ignored.
      *
      * @param nodeName
      * @param nodeIdentifier
      */
-    public storeNames (nodeName: string, nodeIdentifier: string): void {
+    public storeNames (nodeName: string, nodeIdentifier: number): void {
         if (!this.isReservedName(nodeName)) {
-            this.namesMap.set(`${nodeName}-${nodeIdentifier}`, RandomGeneratorUtils.getRandomVariableName());
+            this.namesMap.set(`${nodeName}-${String(nodeIdentifier)}`, RandomGeneratorUtils.getRandomVariableName());
         }
     }
 

+ 17 - 2
src/node-transformers/node-obfuscators/replacers/NumberLiteralReplacer.ts

@@ -8,6 +8,11 @@ import { Utils } from '../../../utils/Utils';
 
 @injectable()
 export class NumberLiteralReplacer extends AbstractReplacer {
+    /**
+     * @type {Map<string, string>}
+     */
+    private readonly numberLiteralCache: Map <number, string> = new Map();
+
     /**
      * @param options
      */
@@ -22,10 +27,20 @@ export class NumberLiteralReplacer extends AbstractReplacer {
      * @returns {string}
      */
     public replace (nodeValue: number): string {
+        if (this.numberLiteralCache.has(nodeValue)) {
+            return <string>this.numberLiteralCache.get(nodeValue);
+        }
+
+        let result: string;
+
         if (!Utils.isCeilNumber(nodeValue)) {
-            return String(nodeValue);
+            result = String(nodeValue);
+        } else {
+            result = `${Utils.hexadecimalPrefix}${Utils.decToHex(nodeValue)}`;
         }
 
-        return `${Utils.hexadecimalPrefix}${Utils.decToHex(nodeValue)}`;
+        this.numberLiteralCache.set(nodeValue, result);
+
+        return result;
     }
 }