From 06cdb74279d01ed9b4b625200882611482333825 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Thu, 6 Apr 2017 11:18:59 +0800 Subject: [PATCH] improve `pure_getters` (#1786) - property access to `null` & `undefined` always has side effects - utilise `reduce_vars` to determine safe property access - may-be cases treated as side effects unless `unsafe` --- lib/compress.js | 34 +++++++++++--- test/compress/collapse_vars.js | 1 + test/compress/pure_getters.js | 85 ++++++++++++++++++++++++++++++++++ test/ufuzz.json | 1 - 4 files changed, 113 insertions(+), 8 deletions(-) create mode 100644 test/compress/pure_getters.js diff --git a/lib/compress.js b/lib/compress.js index ef7f0441..22c79b81 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -1160,6 +1160,26 @@ merge(Compressor.prototype, { && !node.expression.has_side_effects(compressor); } + (function(def) { + def(AST_Node, return_false); + def(AST_Null, return_true); + def(AST_Undefined, return_true); + def(AST_UnaryPrefix, function() { + return this.operator == "void"; + }); + def(AST_PropAccess, function(compressor) { + return !compressor.option("unsafe"); + }); + def(AST_SymbolRef, function(compressor) { + if (this.is_undefined) return true; + if (compressor.option("unsafe")) return false; + var fixed = this.fixed_value(); + return !fixed || fixed.may_eq_null(compressor); + }); + })(function(node, func) { + node.DEFMETHOD("may_eq_null", func); + }); + /* -----[ boolean/negation helpers ]----- */ // methods to determine whether an expression has a boolean result type @@ -1688,7 +1708,7 @@ merge(Compressor.prototype, { || this.expression.has_side_effects(compressor); }); def(AST_SymbolRef, function(compressor){ - return this.global() && this.undeclared(); + return this.undeclared(); }); def(AST_Object, function(compressor){ return any(this.properties, compressor); @@ -1701,16 +1721,15 @@ merge(Compressor.prototype, { }); def(AST_Dot, function(compressor){ if (!compressor.option("pure_getters")) return true; - return this.expression.has_side_effects(compressor); + return this.expression.may_eq_null(compressor) + || this.expression.has_side_effects(compressor); }); def(AST_Sub, function(compressor){ if (!compressor.option("pure_getters")) return true; - return this.expression.has_side_effects(compressor) + return this.expression.may_eq_null(compressor) + || this.expression.has_side_effects(compressor) || this.property.has_side_effects(compressor); }); - def(AST_PropAccess, function(compressor){ - return !compressor.option("pure_getters"); - }); def(AST_Seq, function(compressor){ return this.car.has_side_effects(compressor) || this.cdr.has_side_effects(compressor); @@ -2275,10 +2294,12 @@ merge(Compressor.prototype, { }); def(AST_Dot, function(compressor, first_in_statement){ if (!compressor.option("pure_getters")) return this; + if (this.expression.may_eq_null(compressor)) return this; return this.expression.drop_side_effect_free(compressor, first_in_statement); }); def(AST_Sub, function(compressor, first_in_statement){ if (!compressor.option("pure_getters")) return this; + if (this.expression.may_eq_null(compressor)) return this; var expression = this.expression.drop_side_effect_free(compressor, first_in_statement); if (!expression) return this.property.drop_side_effect_free(compressor, first_in_statement); var property = this.property.drop_side_effect_free(compressor); @@ -3509,7 +3530,6 @@ merge(Compressor.prototype, { // testing against !self.scope.uses_with first is an optimization if (compressor.option("screw_ie8") && self.undeclared() - && !isLHS(self, compressor.parent()) && (!self.scope.uses_with || !compressor.find_parent(AST_With))) { switch (self.name) { case "undefined": diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index 4107707b..2264783d 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -1573,6 +1573,7 @@ var_side_effects_3: { options = { collapse_vars: true, pure_getters: true, + unsafe: true, } input: { var print = console.log.bind(console); diff --git a/test/compress/pure_getters.js b/test/compress/pure_getters.js new file mode 100644 index 00000000..338f8639 --- /dev/null +++ b/test/compress/pure_getters.js @@ -0,0 +1,85 @@ +side_effects: { + options = { + pure_getters: true, + reduce_vars: false, + side_effects: true, + toplevel: true, + unsafe: false, + } + input: { + var a, b = null, c = {}; + a.prop; + b.prop; + c.prop; + d.prop; + null.prop; + (void 0).prop; + undefined.prop; + } + expect: { + var a, b = null, c = {}; + a.prop; + b.prop; + c.prop; + d.prop; + null.prop; + (void 0).prop; + (void 0).prop; + } +} + +side_effects_reduce_vars: { + options = { + pure_getters: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unsafe: false, + } + input: { + var a, b = null, c = {}; + a.prop; + b.prop; + c.prop; + d.prop; + null.prop; + (void 0).prop; + undefined.prop; + } + expect: { + var a, b = null, c = {}; + a.prop; + b.prop; + d.prop; + null.prop; + (void 0).prop; + (void 0).prop; + } +} + +side_effects_unsafe: { + options = { + pure_getters: true, + reduce_vars: false, + side_effects: true, + toplevel: true, + unsafe: true, + } + input: { + var a, b = null, c = {}; + a.prop; + b.prop; + c.prop; + d.prop; + null.prop; + (void 0).prop; + undefined.prop; + } + expect: { + var a, b = null, c = {}; + d; + null.prop; + (void 0).prop; + (void 0).prop; + } +} diff --git a/test/ufuzz.json b/test/ufuzz.json index fd1084a4..4523795c 100644 --- a/test/ufuzz.json +++ b/test/ufuzz.json @@ -35,7 +35,6 @@ "compress": { "keep_fargs": false, "passes": 3, - "pure_getters": true, "warnings": false } } -- 2.34.1