improve mangle (#2948)

This commit is contained in:
Alex Lam S.L
2018-02-23 23:51:49 +08:00
committed by GitHub
parent 604caa09e7
commit f40f5eb228
6 changed files with 196 additions and 88 deletions

View File

@@ -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();
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
var tricky_name = tricky_def ? tricky_def.mangled_name || tricky_def.name : null;
while (true) {
var name = next_mangled(this, options);
if (!tricky_name || tricky_name != name)
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) {
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) {
if (options.cache && options.cache.props) {
var mangled_names = this.mangled_names = Object.create(null);
options.cache.props.each(function(mangled_name) {
push_uniq(mangled_names, 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);
}
}
});

View File

@@ -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"
}

View File

@@ -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",

View File

@@ -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",

View File

@@ -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;
}
}
}

View File

@@ -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));
});