Merge branch 'master' into harmony

This commit is contained in:
alexlamsl
2017-07-30 01:57:34 +08:00
11 changed files with 175 additions and 122 deletions

View File

@@ -8,7 +8,14 @@
**Uglify version (`uglifyjs -V`)** **Uglify version (`uglifyjs -V`)**
**JavaScript input** <!-- ideally as small as possible --> **JavaScript input**
<!--
A complete parsable JS program exhibiting the issue with
UglifyJS alone - without third party tools or libraries.
Ideally the input should be as small as possible.
Post a link to a gist if necessary.
-->
**The `uglifyjs` CLI command executed or `minify()` options used.** **The `uglifyjs` CLI command executed or `minify()` options used.**

View File

@@ -70,6 +70,7 @@ function minify(files, options) {
set_shorthand("keep_fnames", options, [ "compress", "mangle" ]); set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
set_shorthand("toplevel", options, [ "compress", "mangle" ]); set_shorthand("toplevel", options, [ "compress", "mangle" ]);
set_shorthand("warnings", options, [ "compress" ]); set_shorthand("warnings", options, [ "compress" ]);
var quoted_props;
if (options.mangle) { if (options.mangle) {
options.mangle = defaults(options.mangle, { options.mangle = defaults(options.mangle, {
cache: options.nameCache && (options.nameCache.vars || {}), cache: options.nameCache && (options.nameCache.vars || {}),
@@ -82,11 +83,16 @@ function minify(files, options) {
safari10: false, safari10: false,
toplevel: false, toplevel: false,
}, true); }, true);
if (options.nameCache && options.mangle.properties) { if (options.mangle.properties) {
if (typeof options.mangle.properties != "object") { if (typeof options.mangle.properties != "object") {
options.mangle.properties = {}; options.mangle.properties = {};
} }
if (!("cache" in options.mangle.properties)) { if (options.mangle.properties.keep_quoted) {
quoted_props = options.mangle.properties.reserved;
if (!Array.isArray(quoted_props)) quoted_props = [];
options.mangle.properties.reserved = quoted_props;
}
if (options.nameCache && !("cache" in options.mangle.properties)) {
options.mangle.properties.cache = options.nameCache.props || {}; options.mangle.properties.cache = options.nameCache.props || {};
} }
} }
@@ -129,6 +135,9 @@ function minify(files, options) {
} }
toplevel = options.parse.toplevel; toplevel = options.parse.toplevel;
} }
if (quoted_props) {
reserve_quoted_keys(toplevel, quoted_props);
}
if (options.wrap) { if (options.wrap) {
toplevel = toplevel.wrap_commonjs(options.wrap); toplevel = toplevel.wrap_commonjs(options.wrap);
} }

View File

@@ -85,6 +85,34 @@ function find_builtins(reserved) {
} }
} }
function reserve_quoted_keys(ast, reserved) {
function add(name) {
push_uniq(reserved, name);
}
ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_ObjectKeyVal && node.quote) {
add(node.key);
} else if (node instanceof AST_Sub) {
addStrings(node.property, add);
}
}));
}
function addStrings(node, add) {
node.walk(new TreeWalker(function(node) {
if (node instanceof AST_Sequence) {
addStrings(node.expressions[node.expressions.length - 1], add);
} else if (node instanceof AST_String) {
add(node.value);
} else if (node instanceof AST_Conditional) {
addStrings(node.consequent, add);
addStrings(node.alternative, add);
}
return true;
}));
}
function mangle_properties(ast, options) { function mangle_properties(ast, options) {
options = defaults(options, { options = defaults(options, {
builtins: false, builtins: false,
@@ -109,7 +137,6 @@ function mangle_properties(ast, options) {
} }
var regex = options.regex; var regex = options.regex;
var keep_quoted = options.keep_quoted;
// note debug is either false (disabled), or a string of the debug suffix to use (enabled). // note debug is either false (disabled), or a string of the debug suffix to use (enabled).
// note debug may be enabled as an empty string, which is falsey. Also treat passing 'true' // note debug may be enabled as an empty string, which is falsey. Also treat passing 'true'
@@ -122,12 +149,11 @@ function mangle_properties(ast, options) {
var names_to_mangle = []; var names_to_mangle = [];
var unmangleable = []; var unmangleable = [];
var to_keep = {};
// step 1: find candidates to mangle // step 1: find candidates to mangle
ast.walk(new TreeWalker(function(node){ ast.walk(new TreeWalker(function(node){
if (node instanceof AST_ObjectKeyVal) { if (node instanceof AST_ObjectKeyVal) {
add(node.key, keep_quoted && node.quote); add(node.key);
} }
else if (node instanceof AST_ConciseMethod && node.key && node.key.name) { else if (node instanceof AST_ConciseMethod && node.key && node.key.name) {
add(node.key.name, keep_quoted && node.quote); add(node.key.name, keep_quoted && node.quote);
@@ -140,15 +166,14 @@ function mangle_properties(ast, options) {
add(node.property); add(node.property);
} }
else if (node instanceof AST_Sub) { else if (node instanceof AST_Sub) {
addStrings(node.property, keep_quoted); addStrings(node.property, add);
} }
})); }));
// step 2: transform the tree, renaming properties // step 2: transform the tree, renaming properties
return ast.transform(new TreeTransformer(function(node){ return ast.transform(new TreeTransformer(function(node){
if (node instanceof AST_ObjectKeyVal) { if (node instanceof AST_ObjectKeyVal) {
if (!(keep_quoted && node.quote)) node.key = mangle(node.key);
node.key = mangle(node.key);
} }
else if (node instanceof AST_ConciseMethod && node.name && node.key.name) { else if (node instanceof AST_ConciseMethod && node.name && node.key.name) {
if (!(keep_quoted && node.quote) && should_mangle(node.key.name)) { if (!(keep_quoted && node.quote) && should_mangle(node.key.name)) {
@@ -162,22 +187,9 @@ function mangle_properties(ast, options) {
else if (node instanceof AST_Dot) { else if (node instanceof AST_Dot) {
node.property = mangle(node.property); node.property = mangle(node.property);
} }
else if (node instanceof AST_Sub) { else if (!options.keep_quoted && node instanceof AST_Sub) {
if (!keep_quoted) node.property = mangleStrings(node.property);
node.property = mangleStrings(node.property);
} }
// else if (node instanceof AST_String) {
// if (should_mangle(node.value)) {
// AST_Node.warn(
// "Found \"{prop}\" property candidate for mangling in an arbitrary string [{file}:{line},{col}]", {
// file : node.start.file,
// line : node.start.line,
// col : node.start.col,
// prop : node.value
// }
// );
// }
// }
})); }));
// only function declarations after this line // only function declarations after this line
@@ -193,19 +205,13 @@ function mangle_properties(ast, options) {
} }
function should_mangle(name) { function should_mangle(name) {
if (keep_quoted && name in to_keep) return false;
if (regex && !regex.test(name)) return false; if (regex && !regex.test(name)) return false;
if (reserved.indexOf(name) >= 0) return false; if (reserved.indexOf(name) >= 0) return false;
return cache.props.has(name) return cache.props.has(name)
|| names_to_mangle.indexOf(name) >= 0; || names_to_mangle.indexOf(name) >= 0;
} }
function add(name, keep) { function add(name) {
if (keep) {
to_keep[name] = true;
return;
}
if (can_mangle(name)) if (can_mangle(name))
push_uniq(names_to_mangle, name); push_uniq(names_to_mangle, name);
@@ -225,19 +231,16 @@ function mangle_properties(ast, options) {
// debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_. // debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_.
var debug_mangled = "_$" + name + "$" + debug_name_suffix + "_"; var debug_mangled = "_$" + name + "$" + debug_name_suffix + "_";
if (can_mangle(debug_mangled) && !(keep_quoted && debug_mangled in to_keep)) { if (can_mangle(debug_mangled)) {
mangled = debug_mangled; mangled = debug_mangled;
} }
} }
// either debug mode is off, or it is on and we could not use the mangled name // either debug mode is off, or it is on and we could not use the mangled name
if (!mangled) { if (!mangled) {
// Note: `can_mangle()` does not check if the name collides with the `to_keep` set
// (filled with quoted properties when `keep_quoted` is set). Make sure we add this
// check so we don't collide with a quoted name.
do { do {
mangled = base54(++cache.cname); mangled = base54(++cache.cname);
} while (!can_mangle(mangled) || keep_quoted && mangled in to_keep); } while (!can_mangle(mangled));
} }
cache.props.set(name, mangled); cache.props.set(name, mangled);
@@ -245,32 +248,6 @@ function mangle_properties(ast, options) {
return mangled; return mangled;
} }
function addStrings(node, keep) {
var out = {};
try {
(function walk(node){
node.walk(new TreeWalker(function(node){
if (node instanceof AST_Sequence) {
walk(node.expressions[node.expressions.length - 1]);
return true;
}
if (node instanceof AST_String) {
add(node.value, keep);
return true;
}
if (node instanceof AST_Conditional) {
walk(node.consequent);
walk(node.alternative);
return true;
}
throw out;
}));
})(node);
} catch(ex) {
if (ex !== out) throw ex;
}
}
function mangleStrings(node) { function mangleStrings(node) {
return node.transform(new TreeTransformer(function(node){ return node.transform(new TreeTransformer(function(node){
if (node instanceof AST_Sequence) { if (node instanceof AST_Sequence) {

View File

@@ -4,7 +4,7 @@
"homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony", "homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)", "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"version": "3.0.26", "version": "3.0.27",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },

View File

@@ -1,6 +1,8 @@
issue_1321_no_debug: { issue_1321_no_debug: {
mangle_props = { mangle = {
keep_quoted: true properties: {
keep_quoted: true,
},
} }
input: { input: {
var x = {}; var x = {};
@@ -10,17 +12,19 @@ issue_1321_no_debug: {
} }
expect: { expect: {
var x = {}; var x = {};
x.o = 1; x.x = 1;
x["a"] = 2 * x.o; x["a"] = 2 * x.x;
console.log(x.o, x["a"]); console.log(x.x, x["a"]);
} }
expect_stdout: true expect_stdout: true
} }
issue_1321_debug: { issue_1321_debug: {
mangle_props = { mangle = {
keep_quoted: true, properties: {
debug: "" debug: "",
keep_quoted: true,
},
} }
input: { input: {
var x = {}; var x = {};
@@ -30,16 +34,18 @@ issue_1321_debug: {
} }
expect: { expect: {
var x = {}; var x = {};
x.o = 1; x.x = 1;
x["_$foo$_"] = 2 * x.o; x["_$foo$_"] = 2 * x.x;
console.log(x.o, x["_$foo$_"]); console.log(x.x, x["_$foo$_"]);
} }
expect_stdout: true expect_stdout: true
} }
issue_1321_with_quoted: { issue_1321_with_quoted: {
mangle_props = { mangle = {
keep_quoted: false properties: {
keep_quoted: false,
},
} }
input: { input: {
var x = {}; var x = {};
@@ -49,9 +55,9 @@ issue_1321_with_quoted: {
} }
expect: { expect: {
var x = {}; var x = {};
x.o = 1; x.x = 1;
x["x"] = 2 * x.o; x["o"] = 2 * x.x;
console.log(x.o, x["x"]); console.log(x.x, x["o"]);
} }
expect_stdout: true expect_stdout: true
} }

View File

@@ -1,5 +1,7 @@
mangle_props: { mangle_props: {
mangle_props = {} mangle = {
properties: true,
}
input: { input: {
var obj = { var obj = {
undefined: 1, undefined: 1,
@@ -54,10 +56,12 @@ mangle_props: {
} }
numeric_literal: { numeric_literal: {
mangle = {
properties: true,
}
beautify = { beautify = {
beautify: true, beautify: true,
} }
mangle_props = {}
input: { input: {
var obj = { var obj = {
0: 0, 0: 0,

View File

@@ -1,6 +1,8 @@
dont_reuse_prop: { dont_reuse_prop: {
mangle_props = { mangle = {
regex: /asd/ properties: {
regex: /asd/,
},
} }
input: { input: {
"aaaaaaaaaabbbbb"; "aaaaaaaaaabbbbb";
@@ -20,8 +22,10 @@ dont_reuse_prop: {
} }
unmangleable_props_should_always_be_reserved: { unmangleable_props_should_always_be_reserved: {
mangle_props = { mangle = {
regex: /asd/ properties: {
regex: /asd/,
},
} }
input: { input: {
"aaaaaaaaaabbbbb"; "aaaaaaaaaabbbbb";

View File

@@ -128,9 +128,11 @@ evaluate_string_length: {
} }
mangle_properties: { mangle_properties: {
mangle_props = { mangle = {
keep_quoted: false properties: {
}; keep_quoted: false,
},
}
input: { input: {
a["foo"] = "bar"; a["foo"] = "bar";
a.color = "red"; a.color = "red";
@@ -139,11 +141,11 @@ mangle_properties: {
a['run']({color: "blue", foo: "baz"}); a['run']({color: "blue", foo: "baz"});
} }
expect: { expect: {
a["o"] = "bar"; a["a"] = "bar";
a.a = "red"; a.b = "red";
x = {r: 10}; x = {o: 10};
a.b(x.r, a.o); a.r(x.o, a.a);
a['b']({a: "blue", o: "baz"}); a['r']({b: "blue", a: "baz"});
} }
} }
@@ -151,9 +153,11 @@ mangle_unquoted_properties: {
options = { options = {
properties: false properties: false
} }
mangle_props = { mangle = {
builtins: true, properties: {
keep_quoted: true builtins: true,
keep_quoted: true,
},
} }
beautify = { beautify = {
beautify: false, beautify: false,
@@ -182,24 +186,26 @@ mangle_unquoted_properties: {
function f1() { function f1() {
a["foo"] = "bar"; a["foo"] = "bar";
a.color = "red"; a.color = "red";
a.o = 2; a.r = 2;
x = {"bar": 10, f: 7}; x = {"bar": 10, b: 7};
a.f = 9; a.b = 9;
} }
function f2() { function f2() {
a.foo = "bar"; a.foo = "bar";
a['color'] = "red"; a['color'] = "red";
x = {bar: 10, f: 7}; x = {bar: 10, b: 7};
a.f = 9; a.b = 9;
a.o = 3; a.r = 3;
} }
} }
} }
mangle_debug: { mangle_debug: {
mangle_props = { mangle = {
debug: "" properties: {
}; debug: "",
},
}
input: { input: {
a.foo = "bar"; a.foo = "bar";
x = { baz: "ban" }; x = { baz: "ban" };
@@ -211,9 +217,11 @@ mangle_debug: {
} }
mangle_debug_true: { mangle_debug_true: {
mangle_props = { mangle = {
debug: true properties: {
}; debug: true,
},
}
input: { input: {
a.foo = "bar"; a.foo = "bar";
x = { baz: "ban" }; x = { baz: "ban" };
@@ -225,9 +233,11 @@ mangle_debug_true: {
} }
mangle_debug_suffix: { mangle_debug_suffix: {
mangle_props = { mangle = {
debug: "XYZ" properties: {
}; debug: "XYZ",
},
}
input: { input: {
a.foo = "bar"; a.foo = "bar";
x = { baz: "ban" }; x = { baz: "ban" };
@@ -242,11 +252,13 @@ mangle_debug_suffix_keep_quoted: {
options = { options = {
properties: false properties: false
} }
mangle_props = { mangle = {
builtins: true, properties: {
keep_quoted: true, builtins: true,
debug: "XYZ", debug: "XYZ",
reserved: [] keep_quoted: true,
reserved: [],
},
} }
beautify = { beautify = {
beautify: false, beautify: false,
@@ -904,3 +916,21 @@ methods_keep_quoted_false: {
} }
expect_exact: "class C{o(){}d(){}}f1({o(){},d(){},e:3});f2({o(){}});f3({o(){}});" expect_exact: "class C{o(){}d(){}}f1({o(){},d(){},e:3});f2({o(){}});f3({o(){}});"
} }
issue_2256: {
options = {
side_effects: true,
}
mangle = {
properties: {
keep_quoted: true,
},
}
input: {
({ "keep": 1 });
g.keep = g.change;
}
expect: {
g.keep = g.g;
}
}

View File

@@ -8,6 +8,7 @@ exports["defaults"] = defaults;
exports["mangle_properties"] = mangle_properties; exports["mangle_properties"] = mangle_properties;
exports["minify"] = minify; exports["minify"] = minify;
exports["parse"] = parse; exports["parse"] = parse;
exports["reserve_quoted_keys"] = reserve_quoted_keys;
exports["string_template"] = string_template; exports["string_template"] = string_template;
exports["tokenizer"] = tokenizer; exports["tokenizer"] = tokenizer;
exports["is_identifier"] = is_identifier; exports["is_identifier"] = is_identifier;

View File

@@ -124,6 +124,17 @@ describe("minify", function() {
assert.strictEqual(result.code, assert.strictEqual(result.code,
'a["foo"]="bar",a.a="red",x={"bar":10};'); 'a["foo"]="bar",a.a="red",x={"bar":10};');
}); });
it("Should not mangle quoted property within dead code", function() {
var result = Uglify.minify('({ "keep": 1 }); g.keep = g.change;', {
mangle: {
properties: {
keep_quoted: true
}
}
});
if (result.error) throw result.error;
assert.strictEqual(result.code, "g.keep=g.g;");
});
}); });
describe("inSourceMap", function() { describe("inSourceMap", function() {

View File

@@ -111,18 +111,22 @@ function run_compress_tests() {
}; };
if (!options.warnings) options.warnings = true; if (!options.warnings) options.warnings = true;
} }
if (test.mangle && test.mangle.properties && test.mangle.properties.keep_quoted) {
var quoted_props = test.mangle.properties.reserved;
if (!Array.isArray(quoted_props)) quoted_props = [];
test.mangle.properties.reserved = quoted_props;
U.reserve_quoted_keys(input, quoted_props);
}
var cmp = new U.Compressor(options, true); var cmp = new U.Compressor(options, true);
var output = cmp.compress(input); var output = cmp.compress(input);
output.figure_out_scope(test.mangle); output.figure_out_scope(test.mangle);
if (test.mangle || test.mangle_props) { if (test.mangle) {
U.base54.reset(); U.base54.reset();
output.compute_char_frequency(test.mangle); output.compute_char_frequency(test.mangle);
}
if (test.mangle) {
output.mangle_names(test.mangle); output.mangle_names(test.mangle);
} if (test.mangle.properties) {
if (test.mangle_props) { output = U.mangle_properties(output, test.mangle.properties);
output = U.mangle_properties(output, test.mangle_props); }
} }
output = make_code(output, output_options); output = make_code(output, output_options);
if (expect != output) { if (expect != output) {