aboutsummaryrefslogtreecommitdiff
path: root/lib/scope.js
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2018-02-23 23:51:49 +0800
committerGitHub <noreply@github.com>2018-02-23 23:51:49 +0800
commitf40f5eb228aa576f2555b5aaa20f3a676aaad147 (patch)
tree6a6f176ee0afc1af3a0fc68d49087322a5e289a0 /lib/scope.js
parent604caa09e7181791621db6c96b92cb8ecc7ace24 (diff)
downloadtracifyjs-f40f5eb228aa576f2555b5aaa20f3a676aaad147.tar.gz
tracifyjs-f40f5eb228aa576f2555b5aaa20f3a676aaad147.zip
improve `mangle` (#2948)
Diffstat (limited to 'lib/scope.js')
-rw-r--r--lib/scope.js144
1 files changed, 80 insertions, 64 deletions
diff --git a/lib/scope.js b/lib/scope.js
index 6c883c66..87bfe0d6 100644
--- a/lib/scope.js
+++ b/lib/scope.js
@@ -83,8 +83,9 @@ SymbolDef.prototype = {
var def;
if (def = this.redefined()) {
this.mangled_name = def.mangled_name || def.name;
- } else
- this.mangled_name = s.next_mangled(options, this);
+ } else {
+ this.mangled_name = next_mangled_name(s, options, this);
+ }
if (this.global && cache) {
cache.set(this.name, this.mangled_name);
}
@@ -168,8 +169,8 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
- node.reference(options);
}
+ node.reference(options);
}
}
else if (node instanceof AST_SymbolCatch) {
@@ -325,56 +326,59 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init){
return symbol.thedef = def;
});
-function next_mangled(scope, options) {
- var ext = scope.enclosed;
- out: while (true) {
- var m = base54(++scope.cname);
- if (!is_identifier(m)) continue; // skip over "do"
-
- // https://github.com/mishoo/UglifyJS2/issues/242 -- do not
- // shadow a name reserved from mangling.
- if (member(m, options.reserved)) continue;
-
- // we must ensure that the mangled name does not shadow a name
- // from some parent scope that is referenced in this or in
- // inner scopes.
- for (var i = ext.length; --i >= 0;) {
- var sym = ext[i];
- var name = sym.mangled_name || (sym.unmangleable(options) && sym.name);
- if (m == name) continue out;
- }
- return m;
+function names_in_use(scope, options) {
+ var names = scope.names_in_use;
+ if (!names) {
+ scope.names_in_use = names = Object.create(scope.mangled_names || null);
+ scope.cname_holes = [];
+ scope.enclosed.forEach(function(def) {
+ if (def.unmangleable(options)) names[def.name] = true;
+ });
}
+ return names;
}
-AST_Scope.DEFMETHOD("next_mangled", function(options){
- return next_mangled(this, options);
-});
-
-AST_Toplevel.DEFMETHOD("next_mangled", function(options){
- var name;
- do {
- name = next_mangled(this, options);
- } while (member(name, this.mangled_names));
- return name;
-});
-
-AST_Function.DEFMETHOD("next_mangled", function(options, def){
+function next_mangled_name(scope, options, def) {
+ var in_use = names_in_use(scope, options);
+ var holes = scope.cname_holes;
+ var names = Object.create(null);
// #179, #326
// in Safari strict mode, something like (function x(x){...}) is a syntax error;
// a function expression's argument cannot shadow the function expression's name
-
- var tricky_def = def.orig[0] instanceof AST_SymbolFunarg && this.name && this.name.definition();
-
- // the function's mangled_name is null when keep_fnames is true
- var tricky_name = tricky_def ? tricky_def.mangled_name || tricky_def.name : null;
-
+ if (scope instanceof AST_Function && scope.name && def.orig[0] instanceof AST_SymbolFunarg) {
+ var tricky_def = scope.name.definition();
+ // the function's mangled_name is null when keep_fnames is true
+ names[tricky_def.mangled_name || tricky_def.name] = true;
+ }
+ var scopes = [ scope ];
+ def.references.forEach(function(sym) {
+ var scope = sym.scope;
+ do {
+ if (scopes.indexOf(scope) < 0) {
+ for (var name in names_in_use(scope, options)) {
+ names[name] = true;
+ }
+ scopes.push(scope);
+ } else break;
+ } while (scope = scope.parent_scope);
+ });
+ var name;
+ for (var i = 0, len = holes.length; i < len; i++) {
+ name = base54(holes[i]);
+ if (names[name]) continue;
+ holes.splice(i, 1);
+ scope.names_in_use[name] = true;
+ return name;
+ }
while (true) {
- var name = next_mangled(this, options);
- if (!tricky_name || tricky_name != name)
- return name;
+ name = base54(++scope.cname);
+ if (in_use[name] || !is_identifier(name) || member(name, options.reserved)) continue;
+ if (!names[name]) break;
+ holes.push(scope.cname);
}
-});
+ scope.names_in_use[name] = true;
+ return name;
+}
AST_Symbol.DEFMETHOD("unmangleable", function(options){
var def = this.definition();
@@ -419,18 +423,15 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
// present (and for AST_SymbolRef-s it'll use the mangled name of
// the AST_SymbolDeclaration that it points to).
var lname = -1;
- var to_mangle = [];
-
- var mangled_names = this.mangled_names = [];
- if (options.cache) {
- this.globals.each(collect);
- if (options.cache.props) {
- options.cache.props.each(function(mangled_name) {
- push_uniq(mangled_names, mangled_name);
- });
- }
+
+ if (options.cache && options.cache.props) {
+ var mangled_names = this.mangled_names = Object.create(null);
+ options.cache.props.each(function(mangled_name) {
+ mangled_names[mangled_name] = true;
+ });
}
+ var redefined = [];
var tw = new TreeWalker(function(node, descend){
if (node instanceof AST_LabeledStatement) {
// lname is incremented when we get to the AST_Label
@@ -440,8 +441,12 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
return true; // don't descend again in TreeWalker
}
if (node instanceof AST_Scope) {
- node.variables.each(collect);
- return;
+ descend();
+ if (options.cache && node instanceof AST_Toplevel) {
+ node.globals.each(mangle);
+ }
+ node.variables.each(mangle);
+ return true;
}
if (node instanceof AST_Label) {
var name;
@@ -449,17 +454,28 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
node.mangled_name = name;
return true;
}
- if (!options.ie8 && node instanceof AST_SymbolCatch) {
- to_mangle.push(node.definition());
- return;
+ if (!options.ie8 && node instanceof AST_Catch) {
+ var def = node.argname.definition();
+ var redef = def.redefined();
+ if (redef) {
+ redefined.push(def);
+ def.references.forEach(function(ref) {
+ ref.thedef = redef;
+ ref.reference(options);
+ ref.thedef = def;
+ });
+ }
+ descend();
+ if (!redef) mangle(def);
+ return true;
}
});
this.walk(tw);
- to_mangle.forEach(function(def){ def.mangle(options) });
+ redefined.forEach(mangle);
- function collect(symbol) {
- if (!member(symbol.name, options.reserved)) {
- to_mangle.push(symbol);
+ function mangle(def) {
+ if (!member(def.name, options.reserved)) {
+ def.mangle(options);
}
}
});