diff options
author | Alex Lam S.L <alexlamsl@gmail.com> | 2018-02-23 23:51:49 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-02-23 23:51:49 +0800 |
commit | f40f5eb228aa576f2555b5aaa20f3a676aaad147 (patch) | |
tree | 6a6f176ee0afc1af3a0fc68d49087322a5e289a0 /lib/scope.js | |
parent | 604caa09e7181791621db6c96b92cb8ecc7ace24 (diff) | |
download | tracifyjs-f40f5eb228aa576f2555b5aaa20f3a676aaad147.tar.gz tracifyjs-f40f5eb228aa576f2555b5aaa20f3a676aaad147.zip |
improve `mangle` (#2948)
Diffstat (limited to 'lib/scope.js')
-rw-r--r-- | lib/scope.js | 144 |
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); } } }); |