From 6d47bf2e5307913e403a44761a8ad990447208f6 Mon Sep 17 00:00:00 2001 From: Andreas Svensson Date: Wed, 30 May 2018 15:25:19 +0200 Subject: [PATCH] add `keep_quoted=strict` mode --- README.md | 3 ++ lib/ast.js | 5 ++- lib/compress.js | 3 +- lib/minify.js | 2 +- lib/propmangle.js | 30 ++++++++++--- test/compress/keep_quoted_strict.js | 69 +++++++++++++++++++++++++++++ test/run-tests.js | 4 +- 7 files changed, 105 insertions(+), 11 deletions(-) create mode 100644 test/compress/keep_quoted_strict.js diff --git a/README.md b/README.md index 6d1cdf8f..977df04b 100644 --- a/README.md +++ b/README.md @@ -807,6 +807,9 @@ UglifyJS.minify(code, { mangle: { toplevel: true } }).code; Pass an empty string `""` to enable, or a non-empty string to set the debug suffix. - `keep_quoted` (default: `false`) -— Only mangle unquoted property names. + - `true` -- quoted property names are reserved and any unquoted properties with the + same property name will not be mangled. + - `"strict"` -- advanced, all unquoted property names are mangled unless reserved. - `regex` (default: `null`) -— Pass a RegExp literal to only mangle property names matching the regular expression. diff --git a/lib/ast.js b/lib/ast.js index 331d340b..84e9679f 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -581,8 +581,11 @@ var AST_PropAccess = DEFNODE("PropAccess", "expression property", { } }); -var AST_Dot = DEFNODE("Dot", null, { +var AST_Dot = DEFNODE("Dot", "quote", { $documentation: "A dotted property access expression", + $propdoc: { + quote: "[string] the original quote character when transformed from AST_Sub", + }, _walk: function(visitor) { return visitor._visit(this, function(){ this.expression._walk(visitor); diff --git a/lib/compress.js b/lib/compress.js index 1832f7a8..1ce03031 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -6208,7 +6208,8 @@ merge(Compressor.prototype, { && property.length <= prop.print_to_string().length + 1) { return make_node(AST_Dot, self, { expression: expr, - property: property + property: property, + quote: prop.quote, }).optimize(compressor); } } diff --git a/lib/minify.js b/lib/minify.js index 399b8615..db5b2b5d 100644 --- a/lib/minify.js +++ b/lib/minify.js @@ -151,7 +151,7 @@ function minify(files, options) { } toplevel = options.parse.toplevel; } - if (quoted_props) { + if (quoted_props && options.mangle.properties.keep_quoted !== "strict") { reserve_quoted_keys(toplevel, quoted_props); } if (options.wrap) { diff --git a/lib/propmangle.js b/lib/propmangle.js index 1bca1ad4..fbe99049 100644 --- a/lib/propmangle.js +++ b/lib/propmangle.js @@ -135,20 +135,30 @@ function mangle_properties(ast, options) { var names_to_mangle = []; var unmangleable = []; + var keep_quoted_strict = options.keep_quoted === "strict"; + // step 1: find candidates to mangle ast.walk(new TreeWalker(function(node){ if (node instanceof AST_ObjectKeyVal) { - add(node.key); + if (!keep_quoted_strict || !node.quote) { + add(node.key); + } } else if (node instanceof AST_ObjectProperty) { // setter or getter, since KeyVal is handled above - add(node.key.name); + if (!keep_quoted_strict || !node.key.start.quote) { + add(node.key.name); + } } else if (node instanceof AST_Dot) { - add(node.property); + if (!keep_quoted_strict || !node.quote) { + add(node.property); + } } else if (node instanceof AST_Sub) { - addStrings(node.property, add); + if (!keep_quoted_strict) { + addStrings(node.property, add); + } } else if (node instanceof AST_Call && node.expression.print_to_string() == "Object.defineProperty") { @@ -159,14 +169,20 @@ function mangle_properties(ast, options) { // step 2: transform the tree, renaming properties return ast.transform(new TreeTransformer(function(node){ if (node instanceof AST_ObjectKeyVal) { - node.key = mangle(node.key); + if (!keep_quoted_strict || !node.quote) { + node.key = mangle(node.key); + } } else if (node instanceof AST_ObjectProperty) { // setter or getter - node.key.name = mangle(node.key.name); + if (!keep_quoted_strict || !node.key.start.quote) { + node.key.name = mangle(node.key.name); + } } else if (node instanceof AST_Dot) { - node.property = mangle(node.property); + if (!keep_quoted_strict || !node.quote) { + node.property = mangle(node.property); + } } else if (!options.keep_quoted && node instanceof AST_Sub) { node.property = mangleStrings(node.property); diff --git a/test/compress/keep_quoted_strict.js b/test/compress/keep_quoted_strict.js new file mode 100644 index 00000000..6201a245 --- /dev/null +++ b/test/compress/keep_quoted_strict.js @@ -0,0 +1,69 @@ +keep_quoted_strict: { + options = { + evaluate: true, + properties: true, + }, + mangle = { + properties: { + keep_quoted: "strict", + reserved: ["propc", "propd"], + }, + } + input: { + var a = { + propa: 1, + get propb() { return 2; }, + propc: 3, + get propd() { return 4; }, + }; + var b = { + "propa": 5, + get "propb"() { return 6; }, + "propc": 7, + get "propd"() { return 8; }, + }; + var c = {}; + Object.defineProperty(c, "propa", {"value": 9}); + Object.defineProperty(c, "propc", {"value": 10}); + console.log(a.propa, a.propb, a.propc, a["propc"], a.propd, a["propd"]); + console.log(b["propa"], b["propb"], b.propc, b["propc"], b.propd, b["propd"]); + console.log(c.propa, c["propc"]); + } + expect: { + var a = { + r: 1, + get p() { + return 2; + }, + propc: 3, + get propd() { + return 4; + } + }; + var b = { + propa: 5, + get propb() { + return 6; + }, + propc: 7, + get propd() { + return 8; + } + }; + var c = {}; + Object.defineProperty(c, "r", { + value: 9 + }); + Object.defineProperty(c, "propc", { + value: 10 + }); + console.log(a.r, a.p, a.propc, a.propc, a.propd, a.propd); + console.log(b.propa, b.propb, b.propc, b.propc, b.propd, b.propd); + console.log(c.r, c.propc); + } + expect_stdout: [ + "1 2 3 3 4 4", + "5 6 7 7 8 8", + "9 10", + ] +} diff --git a/test/run-tests.js b/test/run-tests.js index 6043f635..19926809 100755 --- a/test/run-tests.js +++ b/test/run-tests.js @@ -114,7 +114,9 @@ function run_compress_tests() { 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); + if (test.mangle.properties.keep_quoted !== "strict") { + U.reserve_quoted_keys(input, quoted_props); + } } if (test.rename) { input.figure_out_scope(test.mangle);