From: Alex Lam S.L Date: Fri, 23 Feb 2018 15:51:49 +0000 (+0800) Subject: improve `mangle` (#2948) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=f40f5eb228aa576f2555b5aaa20f3a676aaad147;p=UglifyJS.git improve `mangle` (#2948) --- 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); } } }); diff --git a/test/compress/issue-1704.js b/test/compress/issue-1704.js index 25e49522..043038e5 100644 --- a/test/compress/issue-1704.js +++ b/test/compress/issue-1704.js @@ -104,7 +104,7 @@ mangle_catch_toplevel: { } console.log(a); } - expect_exact: 'var o="FAIL";try{throw 1}catch(c){o="PASS"}console.log(o);' + expect_exact: 'var c="FAIL";try{throw 1}catch(o){c="PASS"}console.log(c);' expect_stdout: "PASS" } @@ -148,7 +148,7 @@ mangle_catch_var_toplevel: { } console.log(a); } - expect_exact: 'var o="FAIL";try{throw 1}catch(r){var o="PASS"}console.log(o);' + expect_exact: 'var r="FAIL";try{throw 1}catch(o){var r="PASS"}console.log(r);' expect_stdout: "PASS" } @@ -345,3 +345,95 @@ mangle_catch_redef_2_ie8_toplevel: { expect_exact: 'try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);' expect_stdout: "undefined" } + +mangle_catch_redef_3: { + mangle = { + ie8: false, + toplevel: false, + } + input: { + var o = "PASS"; + try { + throw 0; + } catch (o) { + (function() { + function f() { + o = "FAIL"; + } + f(), f(); + })(); + } + console.log(o); + } + expect_exact: 'var o="PASS";try{throw 0}catch(o){(function(){function c(){o="FAIL"}c(),c()})()}console.log(o);' + expect_stdout: "PASS" +} + +mangle_catch_redef_3_toplevel: { + mangle = { + ie8: false, + toplevel: true, + } + input: { + var o = "PASS"; + try { + throw 0; + } catch (o) { + (function() { + function f() { + o = "FAIL"; + } + f(), f(); + })(); + } + console.log(o); + } + expect_exact: 'var c="PASS";try{throw 0}catch(c){(function(){function o(){c="FAIL"}o(),o()})()}console.log(c);' + expect_stdout: "PASS" +} + +mangle_catch_redef_ie8_3: { + mangle = { + ie8: true, + toplevel: false, + } + input: { + var o = "PASS"; + try { + throw 0; + } catch (o) { + (function() { + function f() { + o = "FAIL"; + } + f(), f(); + })(); + } + console.log(o); + } + expect_exact: 'var o="PASS";try{throw 0}catch(o){(function(){function c(){o="FAIL"}c(),c()})()}console.log(o);' + expect_stdout: "PASS" +} + +mangle_catch_redef_3_ie8_toplevel: { + mangle = { + ie8: true, + toplevel: true, + } + input: { + var o = "PASS"; + try { + throw 0; + } catch (o) { + (function() { + function f() { + o = "FAIL"; + } + f(), f(); + })(); + } + console.log(o); + } + expect_exact: 'var c="PASS";try{throw 0}catch(c){(function(){function o(){c="FAIL"}o(),o()})()}console.log(c);' + expect_stdout: "PASS" +} diff --git a/test/compress/issue-1733.js b/test/compress/issue-1733.js index f1e576c7..15c4a899 100644 --- a/test/compress/issue-1733.js +++ b/test/compress/issue-1733.js @@ -15,7 +15,7 @@ function_iife_catch: { } f(); } - expect_exact: "function f(o){!function(){try{throw 0}catch(c){var o=1;console.log(c,o)}}()}f();" + expect_exact: "function f(o){!function(){try{throw 0}catch(o){var c=1;console.log(o,c)}}()}f();" expect_stdout: "0 1" } @@ -36,7 +36,7 @@ function_iife_catch_ie8: { } f(); } - expect_exact: "function f(o){!function(){try{throw 0}catch(o){var c=1;console.log(o,c)}}()}f();" + expect_exact: "function f(c){!function(){try{throw 0}catch(c){var o=1;console.log(c,o)}}()}f();" expect_stdout: "0 1" } @@ -61,7 +61,7 @@ function_catch_catch: { } f(); } - expect_exact: "var o=0;function f(){try{throw 1}catch(c){try{throw 2}catch(o){var o=3;console.log(o)}}console.log(o)}f();" + expect_exact: "var o=0;function f(){try{throw 1}catch(o){try{throw 2}catch(c){var c=3;console.log(c)}}console.log(c)}f();" expect_stdout: [ "3", "undefined", diff --git a/test/compress/rename.js b/test/compress/rename.js index defc6cf5..83187dff 100644 --- a/test/compress/rename.js +++ b/test/compress/rename.js @@ -109,7 +109,7 @@ mangle_catch_toplevel: { } console.log(a); } - expect_exact: 'var o="FAIL";try{throw 1}catch(c){o="PASS"}console.log(o);' + expect_exact: 'var c="FAIL";try{throw 1}catch(o){c="PASS"}console.log(c);' expect_stdout: "PASS" } @@ -155,7 +155,7 @@ mangle_catch_var_toplevel: { } console.log(a); } - expect_exact: 'var o="FAIL";try{throw 1}catch(r){var o="PASS"}console.log(o);' + expect_exact: 'var r="FAIL";try{throw 1}catch(o){var r="PASS"}console.log(r);' expect_stdout: "PASS" } @@ -451,7 +451,7 @@ function_iife_catch: { } f(); } - expect_exact: "function f(o){!function(){try{throw 0}catch(c){var o=1;console.log(c,o)}}()}f();" + expect_exact: "function f(o){!function(){try{throw 0}catch(o){var c=1;console.log(o,c)}}()}f();" expect_stdout: "0 1" } @@ -473,7 +473,7 @@ function_iife_catch_ie8: { } f(); } - expect_exact: "function f(o){!function(){try{throw 0}catch(o){var c=1;console.log(o,c)}}()}f();" + expect_exact: "function f(c){!function(){try{throw 0}catch(c){var o=1;console.log(c,o)}}()}f();" expect_stdout: "0 1" } @@ -499,7 +499,7 @@ function_catch_catch: { } f(); } - expect_exact: "var o=0;function f(){try{throw 1}catch(c){try{throw 2}catch(o){var o=3;console.log(o)}}console.log(o)}f();" + expect_exact: "var o=0;function f(){try{throw 1}catch(o){try{throw 2}catch(c){var c=3;console.log(c)}}console.log(c)}f();" expect_stdout: [ "3", "undefined", diff --git a/test/compress/screw-ie8.js b/test/compress/screw-ie8.js index 82152b7d..b4098b8e 100644 --- a/test/compress/screw-ie8.js +++ b/test/compress/screw-ie8.js @@ -102,12 +102,12 @@ dont_screw_try_catch: { }; } expect: { - bad = function(n){ - return function(t){ + bad = function(t){ + return function(n){ try{ - n() - } catch(n) { - t(n) + t() + } catch(t) { + n(t) } } }; @@ -349,11 +349,11 @@ issue_2254_1: { try { console.log(f("PASS")); } catch (e) {} - function f(e) { + function f(t) { try { throw "FAIL"; - } catch (t) { - return e; + } catch (e) { + return t; } } } diff --git a/test/mocha/minify.js b/test/mocha/minify.js index 1b0052a4..02180a59 100644 --- a/test/mocha/minify.js +++ b/test/mocha/minify.js @@ -46,10 +46,10 @@ describe("minify", function() { assert.strictEqual(compressed, [ "function n(n){return 3*n}", "function r(n){return n/2}", - "var o=console.log.bind(console);", - 'function c(n){o("Foo:",2*n)}', + "var c=console.log.bind(console);", + 'function o(o){c("Foo:",2*o)}', "var a=n(3),b=r(12);", - 'o("qux",a,b),c(11);', + 'c("qux",a,b),o(11);', ].join("")); assert.strictEqual(run_code(compressed), run_code(original)); }); @@ -79,10 +79,10 @@ describe("minify", function() { assert.strictEqual(compressed, [ "function n(n){return 3*n}", "function r(n){return n/2}", - "var o=console.log.bind(console);", - 'function c(n){o("Foo:",2*n)}', + "var c=console.log.bind(console);", + 'function o(o){c("Foo:",2*o)}', "var a=n(3),b=r(12);", - 'o("qux",a,b),c(11);', + 'c("qux",a,b),o(11);', ].join("")); assert.strictEqual(run_code(compressed), run_code(original)); });