aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMihai Bazon <mihai@bazon.net>2013-10-02 19:33:45 +0300
committerMihai Bazon <mihai@bazon.net>2013-10-02 19:38:01 +0300
commit8cc86fee6016bc2d540a348f5eb4a174f006a02b (patch)
tree1a754198977df9deacb85f14445b2ef940e2a914
parent88fb83aa813d38e21abeab2b5e4f74f02ef582c1 (diff)
downloadtracifyjs-8cc86fee6016bc2d540a348f5eb4a174f006a02b.tar.gz
tracifyjs-8cc86fee6016bc2d540a348f5eb4a174f006a02b.zip
add `clean_getters` compressor option (default `false`)
allows one to specify if `foo.bar` is considered to have side effects.
-rw-r--r--README.md3
-rw-r--r--lib/compress.js101
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;