From: Alex Lam S.L Date: Sat, 29 Jul 2017 15:02:04 +0000 (+0800) Subject: improve `mangle.properties` (#2261) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=a845897758aa2cbccf71f37643d31b08cdd22a0e;p=UglifyJS.git improve `mangle.properties` (#2261) - include dead code when `keep_quoted` - unify `keep_quoted` & `reserved` - make `test/run-tests.js` consistent with `minify()` fixes #2256 --- diff --git a/lib/minify.js b/lib/minify.js index b4bfe451..773e953a 100644 --- a/lib/minify.js +++ b/lib/minify.js @@ -68,6 +68,7 @@ function minify(files, options) { set_shorthand("keep_fnames", options, [ "compress", "mangle" ]); set_shorthand("toplevel", options, [ "compress", "mangle" ]); set_shorthand("warnings", options, [ "compress" ]); + var quoted_props; if (options.mangle) { options.mangle = defaults(options.mangle, { cache: options.nameCache && (options.nameCache.vars || {}), @@ -78,11 +79,16 @@ function minify(files, options) { reserved: [], toplevel: false, }, true); - if (options.nameCache && options.mangle.properties) { + if (options.mangle.properties) { if (typeof options.mangle.properties != "object") { 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 || {}; } } @@ -125,6 +131,9 @@ function minify(files, options) { } toplevel = options.parse.toplevel; } + if (quoted_props) { + reserve_quoted_keys(toplevel, quoted_props); + } if (options.wrap) { toplevel = toplevel.wrap_commonjs(options.wrap); } diff --git a/lib/propmangle.js b/lib/propmangle.js index 49490755..36a67e80 100644 --- a/lib/propmangle.js +++ b/lib/propmangle.js @@ -67,6 +67,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) { options = defaults(options, { builtins: false, @@ -91,7 +119,6 @@ function mangle_properties(ast, options) { } 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 may be enabled as an empty string, which is falsey. Also treat passing 'true' @@ -104,12 +131,11 @@ function mangle_properties(ast, options) { var names_to_mangle = []; var unmangleable = []; - var to_keep = {}; // step 1: find candidates to mangle ast.walk(new TreeWalker(function(node){ if (node instanceof AST_ObjectKeyVal) { - add(node.key, keep_quoted && node.quote); + add(node.key); } else if (node instanceof AST_ObjectProperty) { // setter or getter, since KeyVal is handled above @@ -119,15 +145,14 @@ function mangle_properties(ast, options) { add(node.property); } else if (node instanceof AST_Sub) { - addStrings(node.property, keep_quoted); + addStrings(node.property, add); } })); // step 2: transform the tree, renaming properties return ast.transform(new TreeTransformer(function(node){ 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_ObjectProperty) { // setter or getter @@ -136,22 +161,9 @@ function mangle_properties(ast, options) { else if (node instanceof AST_Dot) { node.property = mangle(node.property); } - else if (node instanceof AST_Sub) { - if (!keep_quoted) - node.property = mangleStrings(node.property); + else if (!options.keep_quoted && node instanceof AST_Sub) { + 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 @@ -167,19 +179,13 @@ function mangle_properties(ast, options) { } function should_mangle(name) { - if (keep_quoted && name in to_keep) return false; if (regex && !regex.test(name)) return false; if (reserved.indexOf(name) >= 0) return false; return cache.props.has(name) || names_to_mangle.indexOf(name) >= 0; } - function add(name, keep) { - if (keep) { - to_keep[name] = true; - return; - } - + function add(name) { if (can_mangle(name)) push_uniq(names_to_mangle, name); @@ -199,19 +205,16 @@ function mangle_properties(ast, options) { // debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_. 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; } } // either debug mode is off, or it is on and we could not use the mangled name 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 { mangled = base54(++cache.cname); - } while (!can_mangle(mangled) || keep_quoted && mangled in to_keep); + } while (!can_mangle(mangled)); } cache.props.set(name, mangled); @@ -219,32 +222,6 @@ function mangle_properties(ast, options) { 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) { return node.transform(new TreeTransformer(function(node){ if (node instanceof AST_Sequence) { diff --git a/test/compress/issue-1321.js b/test/compress/issue-1321.js index 6b92291d..e55696d0 100644 --- a/test/compress/issue-1321.js +++ b/test/compress/issue-1321.js @@ -1,6 +1,8 @@ issue_1321_no_debug: { - mangle_props = { - keep_quoted: true + mangle = { + properties: { + keep_quoted: true, + }, } input: { var x = {}; @@ -10,17 +12,19 @@ issue_1321_no_debug: { } expect: { var x = {}; - x.o = 1; - x["a"] = 2 * x.o; - console.log(x.o, x["a"]); + x.x = 1; + x["a"] = 2 * x.x; + console.log(x.x, x["a"]); } expect_stdout: true } issue_1321_debug: { - mangle_props = { - keep_quoted: true, - debug: "" + mangle = { + properties: { + debug: "", + keep_quoted: true, + }, } input: { var x = {}; @@ -30,16 +34,18 @@ issue_1321_debug: { } expect: { var x = {}; - x.o = 1; - x["_$foo$_"] = 2 * x.o; - console.log(x.o, x["_$foo$_"]); + x.x = 1; + x["_$foo$_"] = 2 * x.x; + console.log(x.x, x["_$foo$_"]); } expect_stdout: true } issue_1321_with_quoted: { - mangle_props = { - keep_quoted: false + mangle = { + properties: { + keep_quoted: false, + }, } input: { var x = {}; @@ -49,9 +55,9 @@ issue_1321_with_quoted: { } expect: { var x = {}; - x.o = 1; - x["x"] = 2 * x.o; - console.log(x.o, x["x"]); + x.x = 1; + x["o"] = 2 * x.x; + console.log(x.x, x["o"]); } expect_stdout: true } diff --git a/test/compress/issue-1770.js b/test/compress/issue-1770.js index f296a403..f63f8453 100644 --- a/test/compress/issue-1770.js +++ b/test/compress/issue-1770.js @@ -1,5 +1,7 @@ mangle_props: { - mangle_props = {} + mangle = { + properties: true, + } input: { var obj = { undefined: 1, @@ -54,10 +56,12 @@ mangle_props: { } numeric_literal: { + mangle = { + properties: true, + } beautify = { beautify: true, } - mangle_props = {} input: { var obj = { 0: 0, @@ -105,7 +109,9 @@ numeric_literal: { } identifier: { - mangle_props = {} + mangle = { + properties: true, + } input: { var obj = { abstract: 1, diff --git a/test/compress/issue-747.js b/test/compress/issue-747.js index 1f7079c2..e074305a 100644 --- a/test/compress/issue-747.js +++ b/test/compress/issue-747.js @@ -1,6 +1,8 @@ dont_reuse_prop: { - mangle_props = { - regex: /asd/ + mangle = { + properties: { + regex: /asd/, + }, } input: { "aaaaaaaaaabbbbb"; @@ -20,8 +22,10 @@ dont_reuse_prop: { } unmangleable_props_should_always_be_reserved: { - mangle_props = { - regex: /asd/ + mangle = { + properties: { + regex: /asd/, + }, } input: { "aaaaaaaaaabbbbb"; diff --git a/test/compress/properties.js b/test/compress/properties.js index dda2e74f..c2c43f69 100644 --- a/test/compress/properties.js +++ b/test/compress/properties.js @@ -128,9 +128,11 @@ evaluate_string_length: { } mangle_properties: { - mangle_props = { - keep_quoted: false - }; + mangle = { + properties: { + keep_quoted: false, + }, + } input: { a["foo"] = "bar"; a.color = "red"; @@ -139,11 +141,11 @@ mangle_properties: { a['run']({color: "blue", foo: "baz"}); } expect: { - a["o"] = "bar"; - a.a = "red"; - x = {r: 10}; - a.b(x.r, a.o); - a['b']({a: "blue", o: "baz"}); + a["a"] = "bar"; + a.b = "red"; + x = {o: 10}; + a.r(x.o, a.a); + a['r']({b: "blue", a: "baz"}); } } @@ -151,8 +153,10 @@ mangle_unquoted_properties: { options = { properties: false } - mangle_props = { - keep_quoted: true + mangle = { + properties: { + keep_quoted: true, + }, } beautify = { beautify: false, @@ -181,24 +185,26 @@ mangle_unquoted_properties: { function f1() { a["foo"] = "bar"; a.color = "red"; - a.o = 2; - x = {"bar": 10, f: 7}; - a.f = 9; + a.r = 2; + x = {"bar": 10, b: 7}; + a.b = 9; } function f2() { a.foo = "bar"; a['color'] = "red"; - x = {bar: 10, f: 7}; - a.f = 9; - a.o = 3; + x = {bar: 10, b: 7}; + a.b = 9; + a.r = 3; } } } mangle_debug: { - mangle_props = { - debug: "" - }; + mangle = { + properties: { + debug: "", + }, + } input: { a.foo = "bar"; x = { baz: "ban" }; @@ -210,9 +216,11 @@ mangle_debug: { } mangle_debug_true: { - mangle_props = { - debug: true - }; + mangle = { + properties: { + debug: true, + }, + } input: { a.foo = "bar"; x = { baz: "ban" }; @@ -224,9 +232,11 @@ mangle_debug_true: { } mangle_debug_suffix: { - mangle_props = { - debug: "XYZ" - }; + mangle = { + properties: { + debug: "XYZ", + }, + } input: { a.foo = "bar"; x = { baz: "ban" }; @@ -241,10 +251,12 @@ mangle_debug_suffix_keep_quoted: { options = { properties: false } - mangle_props = { - keep_quoted: true, - debug: "XYZ", - reserved: [] + mangle = { + properties: { + debug: "XYZ", + keep_quoted: true, + reserved: [], + }, } beautify = { beautify: false, @@ -774,3 +786,21 @@ issue_2208_5: { } expect_stdout: "42" } + +issue_2256: { + options = { + side_effects: true, + } + mangle = { + properties: { + keep_quoted: true, + }, + } + input: { + ({ "keep": 1 }); + g.keep = g.change; + } + expect: { + g.keep = g.g; + } +} diff --git a/test/exports.js b/test/exports.js index f50b772c..997ad513 100644 --- a/test/exports.js +++ b/test/exports.js @@ -8,6 +8,7 @@ exports["defaults"] = defaults; exports["mangle_properties"] = mangle_properties; exports["minify"] = minify; exports["parse"] = parse; +exports["reserve_quoted_keys"] = reserve_quoted_keys; exports["string_template"] = string_template; exports["tokenizer"] = tokenizer; exports["is_identifier"] = is_identifier; diff --git a/test/mocha/minify.js b/test/mocha/minify.js index 88e9c4eb..fc7332fb 100644 --- a/test/mocha/minify.js +++ b/test/mocha/minify.js @@ -124,6 +124,17 @@ describe("minify", function() { assert.strictEqual(result.code, '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() { diff --git a/test/run-tests.js b/test/run-tests.js index 6b8c9ddf..0051c6c2 100755 --- a/test/run-tests.js +++ b/test/run-tests.js @@ -111,18 +111,22 @@ function run_compress_tests() { }; 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 output = cmp.compress(input); output.figure_out_scope(test.mangle); - if (test.mangle || test.mangle_props) { + if (test.mangle) { U.base54.reset(); output.compute_char_frequency(test.mangle); - } - if (test.mangle) { output.mangle_names(test.mangle); - } - if (test.mangle_props) { - output = U.mangle_properties(output, test.mangle_props); + if (test.mangle.properties) { + output = U.mangle_properties(output, test.mangle.properties); + } } output = make_code(output, output_options); if (expect != output) {