From: Mihai Bazon Date: Tue, 6 Nov 2012 09:39:41 +0000 (+0200) Subject: add option to mangle names even if eval/with is in use X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=a4f6d46118c0d9d1d84a9238d8ef4e57279caac3;p=UglifyJS.git add option to mangle names even if eval/with is in use (for more fair comparison to Closure compiler) --- diff --git a/bin/uglifyjs2 b/bin/uglifyjs2 index aadda7cf..1706629c 100755 --- a/bin/uglifyjs2 +++ b/bin/uglifyjs2 @@ -252,7 +252,7 @@ if (SCOPE_IS_NEEDED) { time_it("scope", function(){ TOPLEVEL.figure_out_scope(); if (MANGLE) { - TOPLEVEL.compute_char_frequency(); + TOPLEVEL.compute_char_frequency(MANGLE); } }); } diff --git a/lib/scope.js b/lib/scope.js index fcd9de18..27cd5259 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -43,7 +43,7 @@ "use strict"; -function SymbolDef(scope, orig) { +function SymbolDef(scope, index, orig) { this.name = orig.name; this.orig = [ orig ]; this.scope = scope; @@ -52,15 +52,18 @@ function SymbolDef(scope, orig) { this.mangled_name = null; this.undeclared = false; this.constant = false; + this.index = index; }; SymbolDef.prototype = { - unmangleable: function() { - return this.global || this.undeclared || this.scope.uses_eval || this.scope.uses_with; + unmangleable: function(options) { + return this.global + || this.undeclared + || (!options.eval && (this.scope.uses_eval || this.scope.uses_with)); }, - mangle: function() { - if (!this.mangled_name && !this.unmangleable()) - this.mangled_name = this.scope.next_mangled(); + mangle: function(options) { + if (!this.mangled_name && !this.unmangleable(options)) + this.mangled_name = this.scope.next_mangled(options); } }; @@ -76,13 +79,16 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){ var self = this; var scope = self.parent_scope = null; var labels = new Dictionary(); + var nesting = 0; var tw = new TreeWalker(function(node, descend){ if (node instanceof AST_Scope) { - node.init_scope_vars(); + node.init_scope_vars(nesting); var save_scope = node.parent_scope = scope; + ++nesting; scope = node; descend(); scope = save_scope; + --nesting; return true; // don't descend again in TreeWalker } if (node instanceof AST_Directive) { @@ -185,7 +191,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){ if (globals.has(name)) { g = globals.get(name); } else { - g = new SymbolDef(self, node); + g = new SymbolDef(self, globals.size(), node); g.undeclared = true; globals.set(name, g); } @@ -207,7 +213,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){ self.walk(tw); }); -AST_Scope.DEFMETHOD("init_scope_vars", function(){ +AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){ this.directives = []; // contains the directives defined in this scope, i.e. "use strict" this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions) this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope) @@ -216,6 +222,7 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(){ this.parent_scope = null; // the parent scope this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes this.cname = -1; // the current index for mangling functions/variables + this.nesting = nesting; // the nesting level of this scope (0 means toplevel) }); AST_Scope.DEFMETHOD("strict", function(){ @@ -223,7 +230,7 @@ AST_Scope.DEFMETHOD("strict", function(){ }); AST_Lambda.DEFMETHOD("init_scope_vars", function(){ - AST_Scope.prototype.init_scope_vars.call(this); + AST_Scope.prototype.init_scope_vars.apply(this, arguments); this.uses_arguments = false; }); @@ -236,6 +243,7 @@ AST_SymbolRef.DEFMETHOD("reference", function() { if (s === def.scope) break; s = s.parent_scope; } + this.frame = this.scope.nesting - def.scope.nesting; }); AST_SymbolDeclaration.DEFMETHOD("init_scope_vars", function(){ @@ -268,7 +276,7 @@ AST_Scope.DEFMETHOD("def_function", function(symbol){ AST_Scope.DEFMETHOD("def_variable", function(symbol){ var def; if (!this.variables.has(symbol.name)) { - def = new SymbolDef(this, symbol); + def = new SymbolDef(this, this.variables.size(), symbol); this.variables.set(symbol.name, def); def.global = !this.parent_scope; } else { @@ -278,7 +286,7 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol){ return symbol.thedef = def; }); -AST_Scope.DEFMETHOD("next_mangled", function(){ +AST_Scope.DEFMETHOD("next_mangled", function(options){ var ext = this.enclosed, n = ext.length; out: while (true) { var m = base54(++this.cname); @@ -288,7 +296,7 @@ AST_Scope.DEFMETHOD("next_mangled", function(){ // inner scopes. for (var i = n; --i >= 0;) { var sym = ext[i]; - var name = sym.mangled_name || (sym.unmangleable() && sym.name); + var name = sym.mangled_name || (sym.unmangleable(options) && sym.name); if (m == name) continue out; } return m; @@ -300,8 +308,8 @@ AST_Scope.DEFMETHOD("references", function(sym){ return this.enclosed.indexOf(sym) < 0 ? null : sym; }); -AST_Symbol.DEFMETHOD("unmangleable", function(){ - return this.definition().unmangleable(); +AST_Symbol.DEFMETHOD("unmangleable", function(options){ + return this.definition().unmangleable(options); }); // labels are always mangleable @@ -336,7 +344,8 @@ AST_Symbol.DEFMETHOD("global", function(){ AST_Toplevel.DEFMETHOD("mangle_names", function(options){ options = defaults(options, { - except : [] + except : [], + eval : false, }); // We only need to mangle declaration nodes. Special logic wired // into the code generator will display the mangled name if it's @@ -375,7 +384,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){ to_mangle.forEach(function(def){ def.mangle(options) }); }); -AST_Toplevel.DEFMETHOD("compute_char_frequency", function(){ +AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){ var tw = new TreeWalker(function(node){ if (node instanceof AST_Constant) base54.consider(node.print_to_string()); @@ -433,7 +442,7 @@ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(){ base54.consider("catch"); else if (node instanceof AST_Finally) base54.consider("finally"); - else if (node instanceof AST_Symbol && node.unmangleable()) + else if (node instanceof AST_Symbol && node.unmangleable(options)) base54.consider(node.name); else if (node instanceof AST_Unary || node instanceof AST_Binary) base54.consider(node.operator); diff --git a/lib/utils.js b/lib/utils.js index 27b79753..15eed9ba 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -247,16 +247,30 @@ function makePredicate(words) { function Dictionary() { this._values = Object.create(null); + this._size = 0; }; Dictionary.prototype = { - set: function(key, val) { return this._values["$" + key] = val, this }, + set: function(key, val) { + if (!this.has(key)) ++this._size; + this._values["$" + key] = val; + return this; + }, get: function(key) { return this._values["$" + key] }, - del: function(key) { return delete this._values["$" + key], this }, + del: function(key) { + if (this.has(key)) { + --this._size; + delete this._values["$" + key]; + } + return this; + }, has: function(key) { return ("$" + key) in this._values }, each: function(f) { for (var i in this._values) f(this._values[i], i.substr(1)); }, + size: function() { + return this._size; + }, map: function(f) { var ret = []; for (var i in this._values)