From 4b6ca5e742787c59969b9b00442cf85bbec19ed5 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Thu, 6 Jul 2017 21:51:58 +0800 Subject: [PATCH] inline property access of object literal (#2209) - only if property value is side-effect-free - guard by `unsafe` fixes #2208 --- lib/compress.js | 25 ++++++++ test/compress/evaluate.js | 92 ++++++++++++++++------------ test/compress/global_defs.js | 5 +- test/compress/properties.js | 113 +++++++++++++++++++++++++++++++++++ 4 files changed, 195 insertions(+), 40 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index a2acd53f..76eb6918 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -4307,6 +4307,17 @@ merge(Compressor.prototype, { return self; }); + AST_Lambda.DEFMETHOD("contains_this", function() { + var result; + var self = this; + self.walk(new TreeWalker(function(node) { + if (result) return true; + if (node instanceof AST_This) return result = true; + if (node !== self && node instanceof AST_Scope) return true; + })); + return result; + }); + OPT(AST_Dot, function(self, compressor){ var def = self.resolve_defines(compressor); if (def) { @@ -4321,6 +4332,20 @@ merge(Compressor.prototype, { }) }).optimize(compressor); } + if (compressor.option("unsafe") && self.expression instanceof AST_Object) { + var values = self.expression.properties; + for (var i = values.length; --i >= 0;) { + if (values[i].key === prop) { + var value = values[i].value; + if (value instanceof AST_Function ? !value.contains_this() : !value.has_side_effects(compressor)) { + var obj = self.expression.clone(); + obj.properties = obj.properties.slice(); + obj.properties.splice(i, 1); + return make_sequence(self, [ obj, value ]).optimize(compressor); + } + } + } + } if (compressor.option("unsafe_proto") && self.expression instanceof AST_Dot && self.expression.property == "prototype") { diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js index 27d08d47..69ea8c19 100644 --- a/test/compress/evaluate.js +++ b/test/compress/evaluate.js @@ -250,22 +250,26 @@ unsafe_constant: { unsafe_object: { options = { - evaluate : true, - unsafe : true + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, } input: { + var o = { a: 1 }; console.log( - ({a:1}) + 1, - ({a:1}).a + 1, - ({a:1}).b + 1, - ({a:1}).a.b + 1 + o + 1, + o.a + 1, + o.b + 1, + o.a.b + 1 ); } expect: { + var o = { a: 1 }; console.log( - ({a:1}) + 1, + o + 1, 2, - ({a:1}).b + 1, + o.b + 1, 1..b + 1 ); } @@ -274,22 +278,26 @@ unsafe_object: { unsafe_object_nested: { options = { - evaluate : true, - unsafe : true + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, } input: { + var o = { a: { b: 1 } }; console.log( - ({a:{b:1}}) + 1, - ({a:{b:1}}).a + 1, - ({a:{b:1}}).b + 1, - ({a:{b:1}}).a.b + 1 + o + 1, + o.a + 1, + o.b + 1, + o.a.b + 1 ); } expect: { + var o = { a: { b: 1 } }; console.log( - ({a:{b:1}}) + 1, - ({a:{b:1}}).a + 1, - ({a:{b:1}}).b + 1, + o + 1, + o.a + 1, + o.b + 1, 2 ); } @@ -298,21 +306,25 @@ unsafe_object_nested: { unsafe_object_complex: { options = { - evaluate : true, - unsafe : true + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, } input: { + var o = { a: { b: 1 }, b: 1 }; console.log( - ({a:{b:1},b:1}) + 1, - ({a:{b:1},b:1}).a + 1, - ({a:{b:1},b:1}).b + 1, - ({a:{b:1},b:1}).a.b + 1 + o + 1, + o.a + 1, + o.b + 1, + o.a.b + 1 ); } expect: { + var o = { a: { b: 1 }, b: 1 }; console.log( - ({a:{b:1},b:1}) + 1, - ({a:{b:1},b:1}).a + 1, + o + 1, + o.a + 1, 2, 2 ); @@ -322,22 +334,26 @@ unsafe_object_complex: { unsafe_object_repeated: { options = { - evaluate : true, - unsafe : true + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, } input: { + var o = { a: { b: 1 }, a: 1 }; console.log( - ({a:{b:1},a:1}) + 1, - ({a:{b:1},a:1}).a + 1, - ({a:{b:1},a:1}).b + 1, - ({a:{b:1},a:1}).a.b + 1 + o + 1, + o.a + 1, + o.b + 1, + o.a.b + 1 ); } expect: { + var o = { a: { b: 1 }, a: 1 }; console.log( - ({a:{b:1},a:1}) + 1, + o + 1, 2, - ({a:{b:1},a:1}).b + 1, + o.b + 1, 1..b + 1 ); } @@ -386,9 +402,9 @@ unsafe_function: { expect: { console.log( ({a:{b:1},b:function(){}}) + 1, - ({a:{b:1},b:function(){}}).a + 1, - ({a:{b:1},b:function(){}}).b + 1, - ({a:{b:1},b:function(){}}).a.b + 1 + ({b:function(){}}, {b:1}) + 1, + ({a:{b:1}}, function(){}) + 1, + ({b:function(){}}, {b:1}).b + 1 ); } expect_stdout: true @@ -636,8 +652,8 @@ unsafe_prototype_function: { var d = ({toString: 0}) + ""; var e = (({valueOf: 0}) + "")[2]; var f = (({toString: 0}) + "")[2]; - var g = ({valueOf: 0}).valueOf(); - var h = "" + ({toString: 0}); + var g = ({}, 0)(); + var h = ({}, 0)(); } } diff --git a/test/compress/global_defs.js b/test/compress/global_defs.js index 74147ded..bd791e2d 100644 --- a/test/compress/global_defs.js +++ b/test/compress/global_defs.js @@ -37,6 +37,7 @@ object: { VALUE: 42, }, }, + side_effects: true, unsafe: true, } input: { @@ -140,9 +141,9 @@ mixed: { console.log(CONFIG); } expect_warnings: [ - 'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:126,22]', 'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:127,22]', - 'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:129,8]', + 'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:128,22]', + 'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:130,8]', ] } diff --git a/test/compress/properties.js b/test/compress/properties.js index 8126d6c6..a5527de3 100644 --- a/test/compress/properties.js +++ b/test/compress/properties.js @@ -657,3 +657,116 @@ accessor_this: { expect_exact: 'var a=1;var b={get this(){return a},set this(c){a=c}};console.log(b.this,b.this=2,b.this);' expect_stdout: "1 2 2" } + +issue_2208_1: { + options = { + inline: true, + side_effects: true, + unsafe: true, + } + input: { + console.log({ + p: function() { + return 42; + } + }.p()); + } + expect: { + console.log(42); + } + expect_stdout: "42" +} + +issue_2208_2: { + options = { + inline: true, + side_effects: true, + unsafe: true, + } + input: { + console.log({ + a: 42, + p: function() { + return this.a; + } + }.p()); + } + expect: { + console.log({ + a: 42, + p: function() { + return this.a; + } + }.p()); + } + expect_stdout: "42" +} + +issue_2208_3: { + options = { + inline: true, + side_effects: true, + unsafe: true, + } + input: { + a = 42; + console.log({ + p: function() { + return function() { + return this.a; + }(); + } + }.p()); + } + expect: { + a = 42; + console.log(function() { + return this.a; + }()); + } + expect_stdout: "42" +} + +issue_2208_4: { + options = { + inline: true, + side_effects: true, + unsafe: true, + } + input: { + function foo() {} + console.log({ + a: foo(), + p: function() { + return 42; + } + }.p()); + } + expect: { + function foo() {} + console.log((foo(), function() { + return 42; + })()); + } + expect_stdout: "42" +} + +issue_2208_5: { + options = { + inline: true, + side_effects: true, + unsafe: true, + } + input: { + console.log({ + p: "FAIL", + p: function() { + return 42; + } + }.p()); + } + expect: { + console.log(42); + } + expect_stdout: "42" +} -- 2.34.1