improve mangle (#2948)
This commit is contained in:
136
lib/scope.js
136
lib/scope.js
@@ -83,8 +83,9 @@ SymbolDef.prototype = {
|
|||||||
var def;
|
var def;
|
||||||
if (def = this.redefined()) {
|
if (def = this.redefined()) {
|
||||||
this.mangled_name = def.mangled_name || def.name;
|
this.mangled_name = def.mangled_name || def.name;
|
||||||
} else
|
} else {
|
||||||
this.mangled_name = s.next_mangled(options, this);
|
this.mangled_name = next_mangled_name(s, options, this);
|
||||||
|
}
|
||||||
if (this.global && cache) {
|
if (this.global && cache) {
|
||||||
cache.set(this.name, this.mangled_name);
|
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);
|
var def = scope.find_variable(node);
|
||||||
if (node.thedef !== def) {
|
if (node.thedef !== def) {
|
||||||
node.thedef = def;
|
node.thedef = def;
|
||||||
node.reference(options);
|
|
||||||
}
|
}
|
||||||
|
node.reference(options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolCatch) {
|
else if (node instanceof AST_SymbolCatch) {
|
||||||
@@ -325,56 +326,59 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init){
|
|||||||
return symbol.thedef = def;
|
return symbol.thedef = def;
|
||||||
});
|
});
|
||||||
|
|
||||||
function next_mangled(scope, options) {
|
function names_in_use(scope, options) {
|
||||||
var ext = scope.enclosed;
|
var names = scope.names_in_use;
|
||||||
out: while (true) {
|
if (!names) {
|
||||||
var m = base54(++scope.cname);
|
scope.names_in_use = names = Object.create(scope.mangled_names || null);
|
||||||
if (!is_identifier(m)) continue; // skip over "do"
|
scope.cname_holes = [];
|
||||||
|
scope.enclosed.forEach(function(def) {
|
||||||
// https://github.com/mishoo/UglifyJS2/issues/242 -- do not
|
if (def.unmangleable(options)) names[def.name] = true;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("next_mangled", function(options){
|
function next_mangled_name(scope, options, def) {
|
||||||
return next_mangled(this, options);
|
var in_use = names_in_use(scope, options);
|
||||||
});
|
var holes = scope.cname_holes;
|
||||||
|
var names = Object.create(null);
|
||||||
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){
|
|
||||||
// #179, #326
|
// #179, #326
|
||||||
// in Safari strict mode, something like (function x(x){...}) is a syntax error;
|
// 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
|
// a function expression's argument cannot shadow the function expression's name
|
||||||
|
if (scope instanceof AST_Function && scope.name && def.orig[0] instanceof AST_SymbolFunarg) {
|
||||||
var tricky_def = def.orig[0] instanceof AST_SymbolFunarg && this.name && this.name.definition();
|
var tricky_def = scope.name.definition();
|
||||||
|
|
||||||
// the function's mangled_name is null when keep_fnames is true
|
// 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;
|
names[tricky_def.mangled_name || tricky_def.name] = true;
|
||||||
|
}
|
||||||
while (true) {
|
var scopes = [ scope ];
|
||||||
var name = next_mangled(this, options);
|
def.references.forEach(function(sym) {
|
||||||
if (!tricky_name || tricky_name != name)
|
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;
|
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){
|
AST_Symbol.DEFMETHOD("unmangleable", function(options){
|
||||||
var def = this.definition();
|
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
|
// present (and for AST_SymbolRef-s it'll use the mangled name of
|
||||||
// the AST_SymbolDeclaration that it points to).
|
// the AST_SymbolDeclaration that it points to).
|
||||||
var lname = -1;
|
var lname = -1;
|
||||||
var to_mangle = [];
|
|
||||||
|
|
||||||
var mangled_names = this.mangled_names = [];
|
if (options.cache && options.cache.props) {
|
||||||
if (options.cache) {
|
var mangled_names = this.mangled_names = Object.create(null);
|
||||||
this.globals.each(collect);
|
|
||||||
if (options.cache.props) {
|
|
||||||
options.cache.props.each(function(mangled_name) {
|
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){
|
var tw = new TreeWalker(function(node, descend){
|
||||||
if (node instanceof AST_LabeledStatement) {
|
if (node instanceof AST_LabeledStatement) {
|
||||||
// lname is incremented when we get to the AST_Label
|
// 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
|
return true; // don't descend again in TreeWalker
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Scope) {
|
if (node instanceof AST_Scope) {
|
||||||
node.variables.each(collect);
|
descend();
|
||||||
return;
|
if (options.cache && node instanceof AST_Toplevel) {
|
||||||
|
node.globals.each(mangle);
|
||||||
|
}
|
||||||
|
node.variables.each(mangle);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Label) {
|
if (node instanceof AST_Label) {
|
||||||
var name;
|
var name;
|
||||||
@@ -449,17 +454,28 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
|||||||
node.mangled_name = name;
|
node.mangled_name = name;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!options.ie8 && node instanceof AST_SymbolCatch) {
|
if (!options.ie8 && node instanceof AST_Catch) {
|
||||||
to_mangle.push(node.definition());
|
var def = node.argname.definition();
|
||||||
return;
|
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);
|
this.walk(tw);
|
||||||
to_mangle.forEach(function(def){ def.mangle(options) });
|
redefined.forEach(mangle);
|
||||||
|
|
||||||
function collect(symbol) {
|
function mangle(def) {
|
||||||
if (!member(symbol.name, options.reserved)) {
|
if (!member(def.name, options.reserved)) {
|
||||||
to_mangle.push(symbol);
|
def.mangle(options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ mangle_catch_toplevel: {
|
|||||||
}
|
}
|
||||||
console.log(a);
|
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"
|
expect_stdout: "PASS"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +148,7 @@ mangle_catch_var_toplevel: {
|
|||||||
}
|
}
|
||||||
console.log(a);
|
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"
|
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_exact: 'try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);'
|
||||||
expect_stdout: "undefined"
|
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"
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ function_iife_catch: {
|
|||||||
}
|
}
|
||||||
f();
|
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"
|
expect_stdout: "0 1"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ function_iife_catch_ie8: {
|
|||||||
}
|
}
|
||||||
f();
|
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"
|
expect_stdout: "0 1"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ function_catch_catch: {
|
|||||||
}
|
}
|
||||||
f();
|
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: [
|
expect_stdout: [
|
||||||
"3",
|
"3",
|
||||||
"undefined",
|
"undefined",
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ mangle_catch_toplevel: {
|
|||||||
}
|
}
|
||||||
console.log(a);
|
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"
|
expect_stdout: "PASS"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,7 +155,7 @@ mangle_catch_var_toplevel: {
|
|||||||
}
|
}
|
||||||
console.log(a);
|
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"
|
expect_stdout: "PASS"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -451,7 +451,7 @@ function_iife_catch: {
|
|||||||
}
|
}
|
||||||
f();
|
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"
|
expect_stdout: "0 1"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,7 +473,7 @@ function_iife_catch_ie8: {
|
|||||||
}
|
}
|
||||||
f();
|
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"
|
expect_stdout: "0 1"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -499,7 +499,7 @@ function_catch_catch: {
|
|||||||
}
|
}
|
||||||
f();
|
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: [
|
expect_stdout: [
|
||||||
"3",
|
"3",
|
||||||
"undefined",
|
"undefined",
|
||||||
|
|||||||
@@ -102,12 +102,12 @@ dont_screw_try_catch: {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
bad = function(n){
|
bad = function(t){
|
||||||
return function(t){
|
return function(n){
|
||||||
try{
|
try{
|
||||||
n()
|
t()
|
||||||
} catch(n) {
|
} catch(t) {
|
||||||
t(n)
|
n(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -349,11 +349,11 @@ issue_2254_1: {
|
|||||||
try {
|
try {
|
||||||
console.log(f("PASS"));
|
console.log(f("PASS"));
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
function f(e) {
|
function f(t) {
|
||||||
try {
|
try {
|
||||||
throw "FAIL";
|
throw "FAIL";
|
||||||
} catch (t) {
|
} catch (e) {
|
||||||
return e;
|
return t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,10 +46,10 @@ describe("minify", function() {
|
|||||||
assert.strictEqual(compressed, [
|
assert.strictEqual(compressed, [
|
||||||
"function n(n){return 3*n}",
|
"function n(n){return 3*n}",
|
||||||
"function r(n){return n/2}",
|
"function r(n){return n/2}",
|
||||||
"var o=console.log.bind(console);",
|
"var c=console.log.bind(console);",
|
||||||
'function c(n){o("Foo:",2*n)}',
|
'function o(o){c("Foo:",2*o)}',
|
||||||
"var a=n(3),b=r(12);",
|
"var a=n(3),b=r(12);",
|
||||||
'o("qux",a,b),c(11);',
|
'c("qux",a,b),o(11);',
|
||||||
].join(""));
|
].join(""));
|
||||||
assert.strictEqual(run_code(compressed), run_code(original));
|
assert.strictEqual(run_code(compressed), run_code(original));
|
||||||
});
|
});
|
||||||
@@ -79,10 +79,10 @@ describe("minify", function() {
|
|||||||
assert.strictEqual(compressed, [
|
assert.strictEqual(compressed, [
|
||||||
"function n(n){return 3*n}",
|
"function n(n){return 3*n}",
|
||||||
"function r(n){return n/2}",
|
"function r(n){return n/2}",
|
||||||
"var o=console.log.bind(console);",
|
"var c=console.log.bind(console);",
|
||||||
'function c(n){o("Foo:",2*n)}',
|
'function o(o){c("Foo:",2*o)}',
|
||||||
"var a=n(3),b=r(12);",
|
"var a=n(3),b=r(12);",
|
||||||
'o("qux",a,b),c(11);',
|
'c("qux",a,b),o(11);',
|
||||||
].join(""));
|
].join(""));
|
||||||
assert.strictEqual(run_code(compressed), run_code(original));
|
assert.strictEqual(run_code(compressed), run_code(original));
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user