add option to mangle names even if eval/with is in use

(for more fair comparison to Closure compiler)
This commit is contained in:
Mihai Bazon
2012-11-06 11:39:41 +02:00
parent 7f5f4d60b7
commit a4f6d46118
3 changed files with 44 additions and 21 deletions

View File

@@ -252,7 +252,7 @@ if (SCOPE_IS_NEEDED) {
time_it("scope", function(){ time_it("scope", function(){
TOPLEVEL.figure_out_scope(); TOPLEVEL.figure_out_scope();
if (MANGLE) { if (MANGLE) {
TOPLEVEL.compute_char_frequency(); TOPLEVEL.compute_char_frequency(MANGLE);
} }
}); });
} }

View File

@@ -43,7 +43,7 @@
"use strict"; "use strict";
function SymbolDef(scope, orig) { function SymbolDef(scope, index, orig) {
this.name = orig.name; this.name = orig.name;
this.orig = [ orig ]; this.orig = [ orig ];
this.scope = scope; this.scope = scope;
@@ -52,15 +52,18 @@ function SymbolDef(scope, orig) {
this.mangled_name = null; this.mangled_name = null;
this.undeclared = false; this.undeclared = false;
this.constant = false; this.constant = false;
this.index = index;
}; };
SymbolDef.prototype = { SymbolDef.prototype = {
unmangleable: function() { unmangleable: function(options) {
return this.global || this.undeclared || this.scope.uses_eval || this.scope.uses_with; return this.global
|| this.undeclared
|| (!options.eval && (this.scope.uses_eval || this.scope.uses_with));
}, },
mangle: function() { mangle: function(options) {
if (!this.mangled_name && !this.unmangleable()) if (!this.mangled_name && !this.unmangleable(options))
this.mangled_name = this.scope.next_mangled(); this.mangled_name = this.scope.next_mangled(options);
} }
}; };
@@ -76,13 +79,16 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
var self = this; var self = this;
var scope = self.parent_scope = null; var scope = self.parent_scope = null;
var labels = new Dictionary(); var labels = new Dictionary();
var nesting = 0;
var tw = new TreeWalker(function(node, descend){ var tw = new TreeWalker(function(node, descend){
if (node instanceof AST_Scope) { if (node instanceof AST_Scope) {
node.init_scope_vars(); node.init_scope_vars(nesting);
var save_scope = node.parent_scope = scope; var save_scope = node.parent_scope = scope;
++nesting;
scope = node; scope = node;
descend(); descend();
scope = save_scope; scope = save_scope;
--nesting;
return true; // don't descend again in TreeWalker return true; // don't descend again in TreeWalker
} }
if (node instanceof AST_Directive) { if (node instanceof AST_Directive) {
@@ -185,7 +191,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
if (globals.has(name)) { if (globals.has(name)) {
g = globals.get(name); g = globals.get(name);
} else { } else {
g = new SymbolDef(self, node); g = new SymbolDef(self, globals.size(), node);
g.undeclared = true; g.undeclared = true;
globals.set(name, g); globals.set(name, g);
} }
@@ -207,7 +213,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
self.walk(tw); 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.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.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) 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.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.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.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(){ AST_Scope.DEFMETHOD("strict", function(){
@@ -223,7 +230,7 @@ AST_Scope.DEFMETHOD("strict", function(){
}); });
AST_Lambda.DEFMETHOD("init_scope_vars", 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; this.uses_arguments = false;
}); });
@@ -236,6 +243,7 @@ AST_SymbolRef.DEFMETHOD("reference", function() {
if (s === def.scope) break; if (s === def.scope) break;
s = s.parent_scope; s = s.parent_scope;
} }
this.frame = this.scope.nesting - def.scope.nesting;
}); });
AST_SymbolDeclaration.DEFMETHOD("init_scope_vars", function(){ 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){ AST_Scope.DEFMETHOD("def_variable", function(symbol){
var def; var def;
if (!this.variables.has(symbol.name)) { 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); this.variables.set(symbol.name, def);
def.global = !this.parent_scope; def.global = !this.parent_scope;
} else { } else {
@@ -278,7 +286,7 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol){
return symbol.thedef = def; return symbol.thedef = def;
}); });
AST_Scope.DEFMETHOD("next_mangled", function(){ AST_Scope.DEFMETHOD("next_mangled", function(options){
var ext = this.enclosed, n = ext.length; var ext = this.enclosed, n = ext.length;
out: while (true) { out: while (true) {
var m = base54(++this.cname); var m = base54(++this.cname);
@@ -288,7 +296,7 @@ AST_Scope.DEFMETHOD("next_mangled", function(){
// inner scopes. // inner scopes.
for (var i = n; --i >= 0;) { for (var i = n; --i >= 0;) {
var sym = ext[i]; 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; if (m == name) continue out;
} }
return m; return m;
@@ -300,8 +308,8 @@ AST_Scope.DEFMETHOD("references", function(sym){
return this.enclosed.indexOf(sym) < 0 ? null : sym; return this.enclosed.indexOf(sym) < 0 ? null : sym;
}); });
AST_Symbol.DEFMETHOD("unmangleable", function(){ AST_Symbol.DEFMETHOD("unmangleable", function(options){
return this.definition().unmangleable(); return this.definition().unmangleable(options);
}); });
// labels are always mangleable // labels are always mangleable
@@ -336,7 +344,8 @@ AST_Symbol.DEFMETHOD("global", function(){
AST_Toplevel.DEFMETHOD("mangle_names", function(options){ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
options = defaults(options, { options = defaults(options, {
except : [] except : [],
eval : false,
}); });
// We only need to mangle declaration nodes. Special logic wired // We only need to mangle declaration nodes. Special logic wired
// into the code generator will display the mangled name if it's // 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) }); 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){ var tw = new TreeWalker(function(node){
if (node instanceof AST_Constant) if (node instanceof AST_Constant)
base54.consider(node.print_to_string()); base54.consider(node.print_to_string());
@@ -433,7 +442,7 @@ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(){
base54.consider("catch"); base54.consider("catch");
else if (node instanceof AST_Finally) else if (node instanceof AST_Finally)
base54.consider("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); base54.consider(node.name);
else if (node instanceof AST_Unary || node instanceof AST_Binary) else if (node instanceof AST_Unary || node instanceof AST_Binary)
base54.consider(node.operator); base54.consider(node.operator);

View File

@@ -247,16 +247,30 @@ function makePredicate(words) {
function Dictionary() { function Dictionary() {
this._values = Object.create(null); this._values = Object.create(null);
this._size = 0;
}; };
Dictionary.prototype = { 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] }, 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 }, has: function(key) { return ("$" + key) in this._values },
each: function(f) { each: function(f) {
for (var i in this._values) for (var i in this._values)
f(this._values[i], i.substr(1)); f(this._values[i], i.substr(1));
}, },
size: function() {
return this._size;
},
map: function(f) { map: function(f) {
var ret = []; var ret = [];
for (var i in this._values) for (var i in this._values)