From c8b6f4733d35db48b5b7e2373264db0d99eb299f Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sat, 4 Nov 2017 00:31:37 +0800 Subject: [PATCH] reduce `this` within functions (#2421) - only replace same-scope usages - augment `test/ufuzz.js` to test for `this` fixes #2420 --- lib/compress.js | 75 +++++++++++++++++++------------- test/compress/reduce_vars.js | 84 ++++++++++++++++++++++++++++++++++++ test/ufuzz.js | 1 + 3 files changed, 129 insertions(+), 31 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 99ab7b7a..f09f2b9b 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -285,7 +285,7 @@ merge(Compressor.prototype, { self.transform(tt); }); - AST_Node.DEFMETHOD("reset_opt_flags", function(compressor) { + AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) { var reduce_vars = compressor.option("reduce_vars"); var unused = compressor.option("unused"); // Stack of look-up tables to keep track of whether a `SymbolDef` has been @@ -564,7 +564,10 @@ merge(Compressor.prototype, { } function is_immutable(value) { - return value && (value.is_constant() || value instanceof AST_Lambda); + if (!value) return false; + return value.is_constant() + || value instanceof AST_Lambda + || value instanceof AST_This; } function read_property(obj, key) { @@ -4211,39 +4214,49 @@ merge(Compressor.prototype, { var value = fixed.optimize(compressor); return value === fixed ? fixed.clone(true) : value; } - if (compressor.option("evaluate") && fixed) { - if (d.should_replace === undefined) { - var init = fixed.evaluate(compressor); - if (init !== fixed && (compressor.option("unsafe_regexp") || !(init instanceof RegExp))) { - init = make_node_from_constant(init, fixed); - var value_length = init.optimize(compressor).print_to_string().length; - var fn; - if (has_symbol_ref(fixed)) { - fn = function() { - var result = init.optimize(compressor); - return result === init ? result.clone(true) : result; - }; - } else { - value_length = Math.min(value_length, fixed.print_to_string().length); - fn = function() { - var result = best_of_expression(init.optimize(compressor), fixed); - return result === init || result === fixed ? result.clone(true) : result; - }; - } - var name_length = d.name.length; - var overhead = 0; - if (compressor.option("unused") && !compressor.exposed(d)) { - overhead = (name_length + 2 + value_length) / d.references.length; - } - d.should_replace = value_length <= name_length + overhead ? fn : false; - } else { - d.should_replace = false; + if (fixed && d.should_replace === undefined) { + var init; + if (fixed instanceof AST_This) { + if (!(d.orig[0] instanceof AST_SymbolFunarg) + && all(d.references, function(ref) { + return d.scope === ref.scope; + })) { + init = fixed; + } + } else { + var ev = fixed.evaluate(compressor); + if (ev !== fixed && (compressor.option("unsafe_regexp") || !(ev instanceof RegExp))) { + init = make_node_from_constant(ev, fixed); } } - if (d.should_replace) { - return d.should_replace(); + if (init) { + var value_length = init.optimize(compressor).print_to_string().length; + var fn; + if (has_symbol_ref(fixed)) { + fn = function() { + var result = init.optimize(compressor); + return result === init ? result.clone(true) : result; + }; + } else { + value_length = Math.min(value_length, fixed.print_to_string().length); + fn = function() { + var result = best_of_expression(init.optimize(compressor), fixed); + return result === init || result === fixed ? result.clone(true) : result; + }; + } + var name_length = d.name.length; + var overhead = 0; + if (compressor.option("unused") && !compressor.exposed(d)) { + overhead = (name_length + 2 + value_length) / d.references.length; + } + d.should_replace = value_length <= name_length + overhead ? fn : false; + } else { + d.should_replace = false; } } + if (d.should_replace) { + return d.should_replace(); + } } return self; diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index d7fdee18..e4d22e9b 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -3295,3 +3295,87 @@ escaped_prop: { } expect_stdout: "2" } + +issue_2420_1: { + options = { + reduce_vars: true, + unused: true, + } + input: { + function run() { + var self = this; + if (self.count++) + self.foo(); + else + self.bar(); + } + var o = { + count: 0, + foo: function() { console.log("foo"); }, + bar: function() { console.log("bar"); }, + }; + run.call(o); + run.call(o); + } + expect: { + function run() { + if (this.count++) + this.foo(); + else + this.bar(); + } + var o = { + count: 0, + foo: function() { console.log("foo"); }, + bar: function() { console.log("bar"); }, + }; + run.call(o); + run.call(o); + } + expect_stdout: [ + "bar", + "foo", + ] +} + +issue_2420_2: { + options = { + reduce_vars: true, + unused: true, + } + input: { + function f() { + var that = this; + if (that.bar) + that.foo(); + else + !function(that, self) { + console.log(this === that, self === this, that === self); + }(that, this); + } + f.call({ + bar: 1, + foo: function() { console.log("foo", this.bar); }, + }); + f.call({}); + } + expect: { + function f() { + if (this.bar) + this.foo(); + else + !function(that, self) { + console.log(this === that, self === this, that === self); + }(this, this); + } + f.call({ + bar: 1, + foo: function() { console.log("foo", this.bar); }, + }); + f.call({}); + } + expect_stdout: [ + "foo 1", + "false false true", + ] +} diff --git a/test/ufuzz.js b/test/ufuzz.js index e38ffa2f..d6a2a45b 100644 --- a/test/ufuzz.js +++ b/test/ufuzz.js @@ -162,6 +162,7 @@ var VALUES = [ '"object"', '"number"', '"function"', + 'this', ]; var BINARY_OPS_NO_COMMA = [ -- 2.34.1