From 8cc86fee6016bc2d540a348f5eb4a174f006a02b Mon Sep 17 00:00:00 2001 From: Mihai Bazon Date: Wed, 2 Oct 2013 19:33:45 +0300 Subject: [PATCH] add `clean_getters` compressor option (default `false`) allows one to specify if `foo.bar` is considered to have side effects. --- README.md | 3 ++ lib/compress.js | 101 +++++++++++++++++++++++++----------------------- 2 files changed, 55 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 4356d4f2..6980878c 100644 --- a/README.md +++ b/README.md @@ -212,6 +212,9 @@ to set `true`; it's effectively a shortcut for `foo=true`). - `negate_iife` -- negate "Immediately-Called Function Expressions" where the return value is discarded, to avoid the parens that the code generator would insert. +- `clean_getters` -- the default is `false`. If you pass `true` for + this, UglifyJS will assume that object property access + (e.g. `foo.bar` or `foo["bar"]`) doesn't have any side effects. ### The `unsafe` option diff --git a/lib/compress.js b/lib/compress.js index 93f01482..76cbc748 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -66,6 +66,7 @@ function Compressor(options, false_by_default) { join_vars : !false_by_default, cascade : !false_by_default, side_effects : !false_by_default, + clean_getters : false, negate_iife : !false_by_default, screw_ie8 : false, @@ -802,70 +803,72 @@ merge(Compressor.prototype, { // determine if expression has side effects (function(def){ - def(AST_Node, function(){ return true }); + def(AST_Node, function(compressor){ return true }); - def(AST_EmptyStatement, function(){ return false }); - def(AST_Constant, function(){ return false }); - def(AST_This, function(){ return false }); + def(AST_EmptyStatement, function(compressor){ return false }); + def(AST_Constant, function(compressor){ return false }); + def(AST_This, function(compressor){ return false }); - def(AST_Block, function(){ + def(AST_Block, function(compressor){ for (var i = this.body.length; --i >= 0;) { - if (this.body[i].has_side_effects()) + if (this.body[i].has_side_effects(compressor)) return true; } return false; }); - def(AST_SimpleStatement, function(){ - return this.body.has_side_effects(); + def(AST_SimpleStatement, function(compressor){ + return this.body.has_side_effects(compressor); }); - def(AST_Defun, function(){ return true }); - def(AST_Function, function(){ return false }); - def(AST_Binary, function(){ - return this.left.has_side_effects() - || this.right.has_side_effects(); + def(AST_Defun, function(compressor){ return true }); + def(AST_Function, function(compressor){ return false }); + def(AST_Binary, function(compressor){ + return this.left.has_side_effects(compressor) + || this.right.has_side_effects(compressor); }); - def(AST_Assign, function(){ return true }); - def(AST_Conditional, function(){ - return this.condition.has_side_effects() - || this.consequent.has_side_effects() - || this.alternative.has_side_effects(); + def(AST_Assign, function(compressor){ return true }); + def(AST_Conditional, function(compressor){ + return this.condition.has_side_effects(compressor) + || this.consequent.has_side_effects(compressor) + || this.alternative.has_side_effects(compressor); }); - def(AST_Unary, function(){ + def(AST_Unary, function(compressor){ return this.operator == "delete" || this.operator == "++" || this.operator == "--" - || this.expression.has_side_effects(); + || this.expression.has_side_effects(compressor); }); - def(AST_SymbolRef, function(){ return false }); - def(AST_Object, function(){ + def(AST_SymbolRef, function(compressor){ return false }); + def(AST_Object, function(compressor){ for (var i = this.properties.length; --i >= 0;) - if (this.properties[i].has_side_effects()) + if (this.properties[i].has_side_effects(compressor)) return true; return false; }); - def(AST_ObjectProperty, function(){ - return this.value.has_side_effects(); + def(AST_ObjectProperty, function(compressor){ + return this.value.has_side_effects(compressor); }); - def(AST_Array, function(){ + def(AST_Array, function(compressor){ for (var i = this.elements.length; --i >= 0;) - if (this.elements[i].has_side_effects()) + if (this.elements[i].has_side_effects(compressor)) return true; return false; }); - // def(AST_Dot, function(){ - // return this.expression.has_side_effects(); - // }); - // def(AST_Sub, function(){ - // return this.expression.has_side_effects() - // || this.property.has_side_effects(); - // }); - def(AST_PropAccess, function(){ - return true; + def(AST_Dot, function(compressor){ + if (!compressor.option("clean_getters")) return true; + return this.expression.has_side_effects(compressor); }); - def(AST_Seq, function(){ - return this.car.has_side_effects() - || this.cdr.has_side_effects(); + def(AST_Sub, function(compressor){ + if (!compressor.option("clean_getters")) return true; + return this.expression.has_side_effects(compressor) + || this.property.has_side_effects(compressor); + }); + def(AST_PropAccess, function(compressor){ + return !compressor.option("clean_getters"); + }); + def(AST_Seq, function(compressor){ + return this.car.has_side_effects(compressor) + || this.cdr.has_side_effects(compressor); }); })(function(node, func){ node.DEFMETHOD("has_side_effects", func); @@ -949,7 +952,7 @@ merge(Compressor.prototype, { node.definitions.forEach(function(def){ if (def.value) { initializations.add(def.name.name, def.value); - if (def.value.has_side_effects()) { + if (def.value.has_side_effects(compressor)) { def.value.walk(tw); } } @@ -1026,7 +1029,7 @@ merge(Compressor.prototype, { line : def.name.start.line, col : def.name.start.col }; - if (def.value && def.value.has_side_effects()) { + if (def.value && def.value.has_side_effects(compressor)) { def._unused_side_effects = true; compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w); return true; @@ -1228,7 +1231,7 @@ merge(Compressor.prototype, { OPT(AST_SimpleStatement, function(self, compressor){ if (compressor.option("side_effects")) { - if (!self.body.has_side_effects()) { + if (!self.body.has_side_effects(compressor)) { compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start); return make_node(AST_EmptyStatement, self); } @@ -1741,7 +1744,7 @@ merge(Compressor.prototype, { if (compressor.option("side_effects")) { if (self.expression instanceof AST_Function && self.args.length == 0 - && !AST_Block.prototype.has_side_effects.call(self.expression)) { + && !AST_Block.prototype.has_side_effects.call(self.expression, compressor)) { return make_node(AST_Undefined, self).transform(compressor); } } @@ -1768,7 +1771,7 @@ merge(Compressor.prototype, { OPT(AST_Seq, function(self, compressor){ if (!compressor.option("side_effects")) return self; - if (!self.car.has_side_effects()) { + if (!self.car.has_side_effects(compressor)) { // we shouldn't compress (1,eval)(something) to // eval(something) because that changes the meaning of // eval (becomes lexical instead of global). @@ -1783,12 +1786,12 @@ merge(Compressor.prototype, { } if (compressor.option("cascade")) { if (self.car instanceof AST_Assign - && !self.car.left.has_side_effects() + && !self.car.left.has_side_effects(compressor) && self.car.left.equivalent_to(self.cdr)) { return self.car; } - if (!self.car.has_side_effects() - && !self.cdr.has_side_effects() + if (!self.car.has_side_effects(compressor) + && !self.cdr.has_side_effects(compressor) && self.car.equivalent_to(self.cdr)) { return self.car; } @@ -1850,7 +1853,7 @@ merge(Compressor.prototype, { } if (this.right instanceof AST_Seq && !(this.operator == "||" || this.operator == "&&") - && !this.left.has_side_effects()) { + && !this.left.has_side_effects(compressor)) { var seq = this.right; var x = seq.to_array(); this.right = x.pop(); @@ -1867,7 +1870,7 @@ merge(Compressor.prototype, { OPT(AST_Binary, function(self, compressor){ var reverse = compressor.has_directive("use asm") ? noop : function(op, force) { - if (force || !(self.left.has_side_effects() || self.right.has_side_effects())) { + if (force || !(self.left.has_side_effects(compressor) || self.right.has_side_effects(compressor))) { if (op) self.operator = op; var tmp = self.left; self.left = self.right; -- 2.34.1