From: Alex Lam S.L Date: Sun, 23 Aug 2020 00:45:39 +0000 (+0100) Subject: enhance `mangle.properties` (#4064) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=da85d102e38e84925db37b543a29224ed0cc5231;p=UglifyJS.git enhance `mangle.properties` (#4064) --- diff --git a/lib/minify.js b/lib/minify.js index 3817b634..c264f695 100644 --- a/lib/minify.js +++ b/lib/minify.js @@ -197,9 +197,7 @@ function minify(files, options) { toplevel.mangle_names(options.mangle); } if (timings) timings.properties = Date.now(); - if (options.mangle && options.mangle.properties) { - toplevel = mangle_properties(toplevel, options.mangle.properties); - } + if (options.mangle && options.mangle.properties) mangle_properties(toplevel, options.mangle.properties); if (timings) timings.output = Date.now(); var result = {}; if (options.output.ast) { diff --git a/lib/propmangle.js b/lib/propmangle.js index ebd1f4e9..04194393 100644 --- a/lib/propmangle.js +++ b/lib/propmangle.js @@ -43,7 +43,8 @@ "use strict"; -function find_builtins(reserved) { +var builtins = function() { + var names = []; // NaN will be included due to Number.NaN [ "null", @@ -67,19 +68,21 @@ function find_builtins(reserved) { ].forEach(function(ctor) { Object.getOwnPropertyNames(ctor).map(add); if (ctor.prototype) { + Object.getOwnPropertyNames(new ctor()).map(add); Object.getOwnPropertyNames(ctor.prototype).map(add); } }); + return makePredicate(names); function add(name) { - push_uniq(reserved, name); + names.push(name); } -} +}(); function reserve_quoted_keys(ast, reserved) { ast.walk(new TreeWalker(function(node) { - if (node instanceof AST_ObjectKeyVal && node.quote) { - add(node.key); + if (node instanceof AST_ObjectKeyVal) { + if (node.quote) add(node.key); } else if (node instanceof AST_Sub) { addStrings(node.property, add); } @@ -91,17 +94,14 @@ function reserve_quoted_keys(ast, reserved) { } function addStrings(node, add) { - node.walk(new TreeWalker(function(node) { - if (node instanceof AST_Sequence) { - addStrings(node.tail_node(), 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; - })); + if (node instanceof AST_Conditional) { + addStrings(node.consequent, add); + addStrings(node.alternative, add); + } else if (node instanceof AST_Sequence) { + addStrings(node.tail_node(), add); + } else if (node instanceof AST_String) { + add(node.value); + } } function mangle_properties(ast, options) { @@ -115,16 +115,17 @@ function mangle_properties(ast, options) { reserved: null, }, true); - var reserved = options.reserved; - if (!Array.isArray(reserved)) reserved = []; - if (!options.builtins) find_builtins(reserved); + var reserved = Object.create(options.builtins ? null : builtins); + if (Array.isArray(options.reserved)) options.reserved.forEach(function(name) { + reserved[name] = true; + }); var cname = -1; var cache; if (options.cache) { cache = options.cache.props; - cache.each(function(mangled_name) { - push_uniq(reserved, mangled_name); + cache.each(function(name) { + reserved[name] = true; }); } else { cache = new Dictionary(); @@ -139,62 +140,93 @@ function mangle_properties(ast, options) { var debug_suffix; if (debug) debug_suffix = options.debug === true ? "" : options.debug; - var names_to_mangle = []; - var unmangleable = []; + var names_to_mangle = Object.create(null); + var unmangleable = Object.create(reserved); // step 1: find candidates to mangle ast.walk(new TreeWalker(function(node) { - if (node instanceof AST_ObjectKeyVal) { + if (node instanceof AST_Binary) { + if (node.operator == "in") addStrings(node.left, add); + } else if (node.TYPE == "Call") { + var exp = node.expression; + if (exp instanceof AST_Dot) switch (exp.property) { + case "defineProperty": + case "getOwnPropertyDescriptor": + if (node.args.length < 2) break; + exp = exp.expression; + if (!(exp instanceof AST_SymbolRef)) break; + if (exp.name != "Object") break; + if (!exp.definition().undeclared) break; + addStrings(node.args[1], add); + break; + case "hasOwnProperty": + if (node.args.length < 1) break; + addStrings(node.args[0], add); + break; + } + } else if (node instanceof AST_Dot) { + add(node.property); + } else if (node instanceof AST_ObjectKeyVal) { add(node.key); } else if (node instanceof AST_ObjectProperty) { // setter or getter, since KeyVal is handled above add(node.key.name); - } else if (node instanceof AST_Dot) { - add(node.property); } else if (node instanceof AST_Sub) { addStrings(node.property, add); - } else if (node instanceof AST_Call - && node.expression.print_to_string() == "Object.defineProperty") { - addStrings(node.args[1], add); } })); - // step 2: transform the tree, renaming properties - return ast.transform(new TreeTransformer(function(node) { - if (node instanceof AST_ObjectKeyVal) { + // step 2: renaming properties + ast.walk(new TreeWalker(function(node) { + if (node instanceof AST_Binary) { + if (node.operator == "in") mangleStrings(node.left); + } else if (node.TYPE == "Call") { + var exp = node.expression; + if (exp instanceof AST_Dot) switch (exp.property) { + case "defineProperty": + case "getOwnPropertyDescriptor": + if (node.args.length < 2) break; + exp = exp.expression; + if (!(exp instanceof AST_SymbolRef)) break; + if (exp.name != "Object") break; + if (!exp.definition().undeclared) break; + mangleStrings(node.args[1]); + break; + case "hasOwnProperty": + if (node.args.length < 1) break; + mangleStrings(node.args[0]); + break; + } + } else if (node instanceof AST_Dot) { + node.property = mangle(node.property); + } else if (node instanceof AST_ObjectKeyVal) { node.key = mangle(node.key); } else if (node instanceof AST_ObjectProperty) { // setter or getter node.key.name = mangle(node.key.name); - } else if (node instanceof AST_Dot) { - node.property = mangle(node.property); - } else if (!options.keep_quoted && node instanceof AST_Sub) { - node.property = mangleStrings(node.property); - } else if (node instanceof AST_Call - && node.expression.print_to_string() == "Object.defineProperty") { - node.args[1] = mangleStrings(node.args[1]); + } else if (node instanceof AST_Sub) { + if (!options.keep_quoted) mangleStrings(node.property); } })); // only function declarations after this line function can_mangle(name) { - if (unmangleable.indexOf(name) >= 0) return false; - if (reserved.indexOf(name) >= 0) return false; + if (unmangleable[name]) return false; if (options.only_cache) return cache.has(name); if (/^-?[0-9]+(\.[0-9]+)?(e[+-][0-9]+)?$/.test(name)) return false; return true; } function should_mangle(name) { + if (reserved[name]) return false; if (regex && !regex.test(name)) return false; - if (reserved.indexOf(name) >= 0) return false; - return cache.has(name) || names_to_mangle.indexOf(name) >= 0; + return cache.has(name) || names_to_mangle[name]; } function add(name) { - if (can_mangle(name)) push_uniq(names_to_mangle, name); - if (!should_mangle(name)) push_uniq(unmangleable, name); + if (can_mangle(name)) names_to_mangle[name] = true; + if (!should_mangle(name)) unmangleable[name] = true; } function mangle(name) { @@ -218,17 +250,13 @@ function mangle_properties(ast, options) { } function mangleStrings(node) { - return node.transform(new TreeTransformer(function(node) { - if (node instanceof AST_Sequence) { - var last = node.expressions.length - 1; - node.expressions[last] = mangleStrings(node.expressions[last]); - } else if (node instanceof AST_String) { - node.value = mangle(node.value); - } else if (node instanceof AST_Conditional) { - node.consequent = mangleStrings(node.consequent); - node.alternative = mangleStrings(node.alternative); - } - return node; - })); + if (node instanceof AST_Sequence) { + mangleStrings(node.expressions.tail_node()); + } else if (node instanceof AST_String) { + node.value = mangle(node.value); + } else if (node instanceof AST_Conditional) { + mangleStrings(node.consequent); + mangleStrings(node.alternative); + } } } diff --git a/test/compress.js b/test/compress.js index 0afd98a9..77b09f0d 100644 --- a/test/compress.js +++ b/test/compress.js @@ -312,9 +312,7 @@ function test_case(test) { if (test.mangle) { output.compute_char_frequency(test.mangle); output.mangle_names(test.mangle); - if (test.mangle.properties) { - output = U.mangle_properties(output, test.mangle.properties); - } + if (test.mangle.properties) U.mangle_properties(output, test.mangle.properties); } var output_code = make_code(output, output_options); if (expect != output_code) { diff --git a/test/compress/properties.js b/test/compress/properties.js index af57c6b9..169dcd11 100644 --- a/test/compress/properties.js +++ b/test/compress/properties.js @@ -130,7 +130,7 @@ evaluate_string_length: { } } -mangle_properties: { +mangle_properties_1: { mangle = { properties: { keep_quoted: false, @@ -152,6 +152,53 @@ mangle_properties: { } } +mangle_properties_2: { + mangle = { + properties: { + reserved: [ + "value", + ] + }, + } + input: { + var o = { + prop1: 1, + }; + Object.defineProperty(o, "prop2", { + value: 2, + }); + Object.defineProperties(o, { + prop3: { + value: 3, + }, + }); + console.log("prop1", o.prop1, "prop1" in o); + console.log("prop2", o.prop2, o.hasOwnProperty("prop2")); + console.log("prop3", o.prop3, Object.getOwnPropertyDescriptor(o, "prop3").value); + } + expect: { + var o = { + o: 1, + }; + Object.defineProperty(o, "p", { + value: 2, + }); + Object.defineProperties(o, { + r: { + value: 3, + }, + }); + console.log("prop1", o.o, "o" in o); + console.log("prop2", o.p, o.hasOwnProperty("p")); + console.log("prop3", o.r, Object.getOwnPropertyDescriptor(o, "r").value); + } + expect_stdout: [ + "prop1 1 true", + "prop2 2 true", + "prop3 3 3", + ] +} + mangle_unquoted_properties: { options = { evaluate: true,