aboutsummaryrefslogtreecommitdiff
path: root/lib/compress.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compress.js')
-rw-r--r--lib/compress.js597
1 files changed, 295 insertions, 302 deletions
diff --git a/lib/compress.js b/lib/compress.js
index 1832f7a8..ad01e48c 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -136,7 +136,7 @@ function Compressor(options, false_by_default) {
var sequences = this.options["sequences"];
this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
this.warnings_produced = {};
-};
+}
Compressor.prototype = new TreeTransformer;
merge(Compressor.prototype, {
@@ -201,11 +201,10 @@ merge(Compressor.prototype, {
},
before: function(node, descend, in_list) {
if (node._squeezed) return node;
- var was_scope = false;
- if (node instanceof AST_Scope) {
- node = node.hoist_properties(this);
- node = node.hoist_declarations(this);
- was_scope = true;
+ var is_scope = node instanceof AST_Scope;
+ if (is_scope) {
+ node.hoist_properties(this);
+ node.hoist_declarations(this);
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
@@ -220,7 +219,7 @@ merge(Compressor.prototype, {
// output and performance.
descend(node, this);
var opt = node.optimize(this);
- if (was_scope && opt instanceof AST_Scope) {
+ if (is_scope) {
opt.drop_unused(this);
descend(opt, this);
}
@@ -229,24 +228,12 @@ merge(Compressor.prototype, {
}
});
-(function(){
-
- function OPT(node, optimizer) {
- node.DEFMETHOD("optimize", function(compressor){
- var self = this;
- if (self._optimized) return self;
- if (compressor.has_directive("use asm")) return self;
- var opt = optimizer(self, compressor);
- opt._optimized = true;
- return opt;
- });
- };
-
- OPT(AST_Node, function(self, compressor){
+(function(OPT) {
+ OPT(AST_Node, function(self, compressor) {
return self;
});
- AST_Node.DEFMETHOD("equivalent_to", function(node){
+ AST_Node.DEFMETHOD("equivalent_to", function(node) {
return this.TYPE == node.TYPE && this.print_to_string() == node.print_to_string();
});
@@ -821,7 +808,7 @@ merge(Compressor.prototype, {
tw.in_loop = saved_loop;
return true;
});
- })(function(node, func){
+ })(function(node, func) {
node.DEFMETHOD("reduce_vars", func);
});
@@ -894,7 +881,7 @@ merge(Compressor.prototype, {
if (!props.end) props.end = orig.end;
}
return new ctor(props);
- };
+ }
function make_sequence(orig, expressions) {
if (expressions.length == 1) return expressions[0];
@@ -971,21 +958,21 @@ merge(Compressor.prototype, {
if (thing instanceof AST_EmptyStatement) return [];
if (thing instanceof AST_Statement) return [ thing ];
throw new Error("Can't convert thing to statement array");
- };
+ }
function is_empty(thing) {
if (thing === null) return true;
if (thing instanceof AST_EmptyStatement) return true;
if (thing instanceof AST_BlockStatement) return thing.body.length == 0;
return false;
- };
+ }
function loop_body(x) {
if (x instanceof AST_IterationStatement) {
return x.body instanceof AST_BlockStatement ? x.body : x;
}
return x;
- };
+ }
function root_expr(prop) {
while (prop instanceof AST_PropAccess) prop = prop.expression;
@@ -1406,7 +1393,10 @@ merge(Compressor.prototype, {
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
- candidates.push(hit_stack.slice());
+ var def = expr.name.definition();
+ if (def.references.length > def.replaced) {
+ candidates.push(hit_stack.slice());
+ }
extract_candidates(expr.value);
}
}
@@ -1476,7 +1466,6 @@ merge(Compressor.prototype, {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var referenced = def.references.length - def.replaced;
- if (!referenced) return;
var declared = def.orig.length - def.eliminated;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
@@ -1947,7 +1936,7 @@ merge(Compressor.prototype, {
CHANGED = true;
var left = prev.body;
return make_sequence(left, [ left, right ]).transform(compressor);
- };
+ }
var n = 0, prev;
for (var i = 0; i < statements.length; i++) {
var stat = statements[i];
@@ -2149,7 +2138,7 @@ merge(Compressor.prototype, {
if (!(stat instanceof AST_Defun)) {
compressor.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
}
- stat.walk(new TreeWalker(function(node){
+ stat.walk(new TreeWalker(function(node) {
if (node instanceof AST_Definitions) {
compressor.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start);
node.remove_initializers();
@@ -2164,7 +2153,7 @@ merge(Compressor.prototype, {
return true;
}
}));
- };
+ }
function get_value(key) {
if (key instanceof AST_Constant) {
@@ -2272,83 +2261,83 @@ merge(Compressor.prototype, {
/* -----[ boolean/negation helpers ]----- */
// methods to determine whether an expression has a boolean result type
- (function(def){
+ (function(def) {
var unary_bool = makePredicate("! delete");
var binary_bool = makePredicate("in instanceof == != === !== < <= >= >");
def(AST_Node, return_false);
- def(AST_UnaryPrefix, function(){
+ def(AST_UnaryPrefix, function() {
return unary_bool[this.operator];
});
- def(AST_Binary, function(){
+ def(AST_Binary, function() {
return binary_bool[this.operator]
|| lazy_op[this.operator]
&& this.left.is_boolean()
&& this.right.is_boolean();
});
- def(AST_Conditional, function(){
+ def(AST_Conditional, function() {
return this.consequent.is_boolean() && this.alternative.is_boolean();
});
- def(AST_Assign, function(){
+ def(AST_Assign, function() {
return this.operator == "=" && this.right.is_boolean();
});
- def(AST_Sequence, function(){
+ def(AST_Sequence, function() {
return this.tail_node().is_boolean();
});
def(AST_True, return_true);
def(AST_False, return_true);
- })(function(node, func){
+ })(function(node, func) {
node.DEFMETHOD("is_boolean", func);
});
// methods to determine if an expression has a numeric result type
- (function(def){
+ (function(def) {
def(AST_Node, return_false);
def(AST_Number, return_true);
var unary = makePredicate("+ - ~ ++ --");
- def(AST_Unary, function(){
+ def(AST_Unary, function() {
return unary[this.operator];
});
var binary = makePredicate("- * / % & | ^ << >> >>>");
- def(AST_Binary, function(compressor){
+ def(AST_Binary, function(compressor) {
return binary[this.operator] || this.operator == "+"
&& this.left.is_number(compressor)
&& this.right.is_number(compressor);
});
- def(AST_Assign, function(compressor){
+ def(AST_Assign, function(compressor) {
return binary[this.operator.slice(0, -1)]
|| this.operator == "=" && this.right.is_number(compressor);
});
- def(AST_Sequence, function(compressor){
+ def(AST_Sequence, function(compressor) {
return this.tail_node().is_number(compressor);
});
- def(AST_Conditional, function(compressor){
+ def(AST_Conditional, function(compressor) {
return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
});
- })(function(node, func){
+ })(function(node, func) {
node.DEFMETHOD("is_number", func);
});
// methods to determine if an expression has a string result type
- (function(def){
+ (function(def) {
def(AST_Node, return_false);
def(AST_String, return_true);
- def(AST_UnaryPrefix, function(){
+ def(AST_UnaryPrefix, function() {
return this.operator == "typeof";
});
- def(AST_Binary, function(compressor){
+ def(AST_Binary, function(compressor) {
return this.operator == "+" &&
(this.left.is_string(compressor) || this.right.is_string(compressor));
});
- def(AST_Assign, function(compressor){
+ def(AST_Assign, function(compressor) {
return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
});
- def(AST_Sequence, function(compressor){
+ def(AST_Sequence, function(compressor) {
return this.tail_node().is_string(compressor);
});
- def(AST_Conditional, function(compressor){
+ def(AST_Conditional, function(compressor) {
return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
});
- })(function(node, func){
+ })(function(node, func) {
node.DEFMETHOD("is_string", func);
});
@@ -2360,7 +2349,7 @@ merge(Compressor.prototype, {
if (parent instanceof AST_Assign && parent.left === node) return node;
}
- (function(def){
+ (function(def) {
AST_Node.DEFMETHOD("resolve_defines", function(compressor) {
if (!compressor.option("global_defs")) return;
var def = this._find_defs(compressor, "");
@@ -2399,10 +2388,10 @@ merge(Compressor.prototype, {
return make_node_from_constant(value, orig);
}
def(AST_Node, noop);
- def(AST_Dot, function(compressor, suffix){
+ def(AST_Dot, function(compressor, suffix) {
return this.expression._find_defs(compressor, "." + this.property + suffix);
});
- def(AST_SymbolRef, function(compressor, suffix){
+ def(AST_SymbolRef, function(compressor, suffix) {
if (!this.global()) return;
var name;
var defines = compressor.option("global_defs");
@@ -2418,7 +2407,7 @@ merge(Compressor.prototype, {
return node;
}
});
- })(function(node, func){
+ })(function(node, func) {
node.DEFMETHOD("_find_defs", func);
});
@@ -2533,13 +2522,13 @@ merge(Compressor.prototype, {
convert_to_predicate(static_fns);
// methods to evaluate a constant expression
- (function(def){
+ (function(def) {
// If the node has been successfully reduced to a constant,
// then its value is returned; otherwise the element itself
// is returned.
// They can be distinguished as constant value is never a
// descendant of AST_Node.
- AST_Node.DEFMETHOD("evaluate", function(compressor){
+ AST_Node.DEFMETHOD("evaluate", function(compressor) {
if (!compressor.option("evaluate")) return this;
var cached = [];
var val = this._eval(compressor, cached, 1);
@@ -2551,7 +2540,7 @@ merge(Compressor.prototype, {
return val;
});
var unaryPrefix = makePredicate("! ~ - + void");
- AST_Node.DEFMETHOD("is_constant", function(){
+ AST_Node.DEFMETHOD("is_constant", function() {
// Accomodate when compress option evaluate=false
// as well as the common constant expressions !0 and -1
if (this instanceof AST_Constant) {
@@ -2562,12 +2551,12 @@ merge(Compressor.prototype, {
&& unaryPrefix[this.operator];
}
});
- def(AST_Statement, function(){
+ def(AST_Statement, function() {
throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
});
def(AST_Lambda, return_this);
def(AST_Node, return_this);
- def(AST_Constant, function(){
+ def(AST_Constant, function() {
return this.getValue();
});
def(AST_Function, function(compressor) {
@@ -2811,12 +2800,12 @@ merge(Compressor.prototype, {
return this;
});
def(AST_New, return_this);
- })(function(node, func){
+ })(function(node, func) {
node.DEFMETHOD("_eval", func);
});
// method to negate an expression
- (function(def){
+ (function(def) {
function basic_negation(exp) {
return make_node(AST_UnaryPrefix, exp, {
operator: "!",
@@ -2833,32 +2822,32 @@ merge(Compressor.prototype, {
}
return best_of_expression(negated, alt);
}
- def(AST_Node, function(){
+ def(AST_Node, function() {
return basic_negation(this);
});
- def(AST_Statement, function(){
+ def(AST_Statement, function() {
throw new Error("Cannot negate a statement");
});
- def(AST_Function, function(){
+ def(AST_Function, function() {
return basic_negation(this);
});
- def(AST_UnaryPrefix, function(){
+ def(AST_UnaryPrefix, function() {
if (this.operator == "!")
return this.expression;
return basic_negation(this);
});
- def(AST_Sequence, function(compressor){
+ def(AST_Sequence, function(compressor) {
var expressions = this.expressions.slice();
expressions.push(expressions.pop().negate(compressor));
return make_sequence(this, expressions);
});
- def(AST_Conditional, function(compressor, first_in_statement){
+ def(AST_Conditional, function(compressor, first_in_statement) {
var self = this.clone();
self.consequent = self.consequent.negate(compressor);
self.alternative = self.alternative.negate(compressor);
return best(this, self, first_in_statement);
});
- def(AST_Binary, function(compressor, first_in_statement){
+ def(AST_Binary, function(compressor, first_in_statement) {
var self = this.clone(), op = this.operator;
if (compressor.option("unsafe_comps")) {
switch (op) {
@@ -2886,8 +2875,8 @@ merge(Compressor.prototype, {
}
return basic_negation(this);
});
- })(function(node, func){
- node.DEFMETHOD("negate", function(compressor, first_in_statement){
+ })(function(node, func) {
+ node.DEFMETHOD("negate", function(compressor, first_in_statement) {
return func.call(this, compressor, first_in_statement);
});
});
@@ -2926,7 +2915,7 @@ merge(Compressor.prototype, {
});
// determine if expression has side effects
- (function(def){
+ (function(def) {
def(AST_Node, return_true);
def(AST_EmptyStatement, return_false);
@@ -2940,10 +2929,10 @@ merge(Compressor.prototype, {
return false;
}
- def(AST_Block, function(compressor){
+ def(AST_Block, function(compressor) {
return any(this.body, compressor);
});
- def(AST_Call, function(compressor){
+ def(AST_Call, function(compressor) {
if (!this.is_expr_pure(compressor)
&& (!this.expression.is_call_pure(compressor)
|| this.expression.has_side_effects(compressor))) {
@@ -2951,82 +2940,82 @@ merge(Compressor.prototype, {
}
return any(this.args, compressor);
});
- def(AST_Switch, function(compressor){
+ def(AST_Switch, function(compressor) {
return this.expression.has_side_effects(compressor)
|| any(this.body, compressor);
});
- def(AST_Case, function(compressor){
+ def(AST_Case, function(compressor) {
return this.expression.has_side_effects(compressor)
|| any(this.body, compressor);
});
- def(AST_Try, function(compressor){
+ def(AST_Try, function(compressor) {
return any(this.body, compressor)
|| this.bcatch && this.bcatch.has_side_effects(compressor)
|| this.bfinally && this.bfinally.has_side_effects(compressor);
});
- def(AST_If, function(compressor){
+ def(AST_If, function(compressor) {
return this.condition.has_side_effects(compressor)
|| this.body && this.body.has_side_effects(compressor)
|| this.alternative && this.alternative.has_side_effects(compressor);
});
- def(AST_LabeledStatement, function(compressor){
+ def(AST_LabeledStatement, function(compressor) {
return this.body.has_side_effects(compressor);
});
- def(AST_SimpleStatement, function(compressor){
+ def(AST_SimpleStatement, function(compressor) {
return this.body.has_side_effects(compressor);
});
def(AST_Lambda, return_false);
- def(AST_Binary, function(compressor){
+ def(AST_Binary, function(compressor) {
return this.left.has_side_effects(compressor)
|| this.right.has_side_effects(compressor);
});
def(AST_Assign, return_true);
- def(AST_Conditional, function(compressor){
+ 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(compressor){
+ def(AST_Unary, function(compressor) {
return unary_side_effects[this.operator]
|| this.expression.has_side_effects(compressor);
});
- def(AST_SymbolRef, function(compressor){
+ def(AST_SymbolRef, function(compressor) {
return !this.is_declared(compressor);
});
def(AST_SymbolDeclaration, return_false);
- def(AST_Object, function(compressor){
+ def(AST_Object, function(compressor) {
return any(this.properties, compressor);
});
- def(AST_ObjectProperty, function(compressor){
+ def(AST_ObjectProperty, function(compressor) {
return this.value.has_side_effects(compressor);
});
- def(AST_Array, function(compressor){
+ def(AST_Array, function(compressor) {
return any(this.elements, compressor);
});
- def(AST_Dot, function(compressor){
+ def(AST_Dot, function(compressor) {
return this.expression.may_throw_on_access(compressor)
|| this.expression.has_side_effects(compressor);
});
- def(AST_Sub, function(compressor){
+ def(AST_Sub, function(compressor) {
return this.expression.may_throw_on_access(compressor)
|| this.expression.has_side_effects(compressor)
|| this.property.has_side_effects(compressor);
});
- def(AST_Sequence, function(compressor){
+ def(AST_Sequence, function(compressor) {
return any(this.expressions, compressor);
});
- def(AST_Definitions, function(compressor){
+ def(AST_Definitions, function(compressor) {
return any(this.definitions, compressor);
});
- def(AST_VarDef, function(compressor){
+ def(AST_VarDef, function(compressor) {
return this.value;
});
- })(function(node, func){
+ })(function(node, func) {
node.DEFMETHOD("has_side_effects", func);
});
// determine if expression may throw
- (function(def){
+ (function(def) {
def(AST_Node, return_true);
def(AST_Constant, return_false);
@@ -3042,10 +3031,10 @@ merge(Compressor.prototype, {
return false;
}
- def(AST_Array, function(compressor){
+ def(AST_Array, function(compressor) {
return any(this.elements, compressor);
});
- def(AST_Assign, function(compressor){
+ def(AST_Assign, function(compressor) {
if (this.right.may_throw(compressor)) return true;
if (!compressor.has_directive("use strict")
&& this.operator == "="
@@ -3054,90 +3043,90 @@ merge(Compressor.prototype, {
}
return this.left.may_throw(compressor);
});
- def(AST_Binary, function(compressor){
+ def(AST_Binary, function(compressor) {
return this.left.may_throw(compressor)
|| this.right.may_throw(compressor);
});
- def(AST_Block, function(compressor){
+ def(AST_Block, function(compressor) {
return any(this.body, compressor);
});
- def(AST_Call, function(compressor){
+ def(AST_Call, function(compressor) {
if (any(this.args, compressor)) return true;
if (this.is_expr_pure(compressor)) return false;
if (this.expression.may_throw(compressor)) return true;
return !(this.expression instanceof AST_Lambda)
|| any(this.expression.body, compressor);
});
- def(AST_Case, function(compressor){
+ def(AST_Case, function(compressor) {
return this.expression.may_throw(compressor)
|| any(this.body, compressor);
});
- def(AST_Conditional, function(compressor){
+ def(AST_Conditional, function(compressor) {
return this.condition.may_throw(compressor)
|| this.consequent.may_throw(compressor)
|| this.alternative.may_throw(compressor);
});
- def(AST_Definitions, function(compressor){
+ def(AST_Definitions, function(compressor) {
return any(this.definitions, compressor);
});
- def(AST_Dot, function(compressor){
+ def(AST_Dot, function(compressor) {
return this.expression.may_throw_on_access(compressor)
|| this.expression.may_throw(compressor);
});
- def(AST_If, function(compressor){
+ def(AST_If, function(compressor) {
return this.condition.may_throw(compressor)
|| this.body && this.body.may_throw(compressor)
|| this.alternative && this.alternative.may_throw(compressor);
});
- def(AST_LabeledStatement, function(compressor){
+ def(AST_LabeledStatement, function(compressor) {
return this.body.may_throw(compressor);
});
- def(AST_Object, function(compressor){
+ def(AST_Object, function(compressor) {
return any(this.properties, compressor);
});
- def(AST_ObjectProperty, function(compressor){
+ def(AST_ObjectProperty, function(compressor) {
return this.value.may_throw(compressor);
});
- def(AST_Return, function(compressor){
+ def(AST_Return, function(compressor) {
return this.value && this.value.may_throw(compressor);
});
- def(AST_Sequence, function(compressor){
+ def(AST_Sequence, function(compressor) {
return any(this.expressions, compressor);
});
- def(AST_SimpleStatement, function(compressor){
+ def(AST_SimpleStatement, function(compressor) {
return this.body.may_throw(compressor);
});
- def(AST_Sub, function(compressor){
+ def(AST_Sub, function(compressor) {
return this.expression.may_throw_on_access(compressor)
|| this.expression.may_throw(compressor)
|| this.property.may_throw(compressor);
});
- def(AST_Switch, function(compressor){
+ def(AST_Switch, function(compressor) {
return this.expression.may_throw(compressor)
|| any(this.body, compressor);
});
- def(AST_SymbolRef, function(compressor){
+ def(AST_SymbolRef, function(compressor) {
return !this.is_declared(compressor);
});
- def(AST_Try, function(compressor){
+ def(AST_Try, function(compressor) {
return this.bcatch ? this.bcatch.may_throw(compressor) : any(this.body, compressor)
|| this.bfinally && this.bfinally.may_throw(compressor);
});
- def(AST_Unary, function(compressor){
+ def(AST_Unary, function(compressor) {
if (this.operator == "typeof" && this.expression instanceof AST_SymbolRef)
return false;
return this.expression.may_throw(compressor);
});
- def(AST_VarDef, function(compressor){
+ def(AST_VarDef, function(compressor) {
if (!this.value) return false;
return this.value.may_throw(compressor);
});
- })(function(node, func){
+ })(function(node, func) {
node.DEFMETHOD("may_throw", func);
});
// determine if expression is constant
- (function(def){
+ (function(def) {
function all(list) {
for (var i = list.length; --i >= 0;)
if (!list[i].is_constant_expression())
@@ -3146,7 +3135,7 @@ merge(Compressor.prototype, {
}
def(AST_Node, return_false);
def(AST_Constant, return_true);
- def(AST_Lambda, function(scope){
+ def(AST_Lambda, function(scope) {
var self = this;
var result = true;
self.walk(new TreeWalker(function(node) {
@@ -3173,61 +3162,61 @@ merge(Compressor.prototype, {
}));
return result;
});
- def(AST_Unary, function(){
+ def(AST_Unary, function() {
return this.expression.is_constant_expression();
});
- def(AST_Binary, function(){
+ def(AST_Binary, function() {
return this.left.is_constant_expression() && this.right.is_constant_expression();
});
- def(AST_Array, function(){
+ def(AST_Array, function() {
return all(this.elements);
});
- def(AST_Object, function(){
+ def(AST_Object, function() {
return all(this.properties);
});
- def(AST_ObjectProperty, function(){
+ def(AST_ObjectProperty, function() {
return this.value.is_constant_expression();
});
- })(function(node, func){
+ })(function(node, func) {
node.DEFMETHOD("is_constant_expression", func);
});
// tell me if a statement aborts
function aborts(thing) {
return thing && thing.aborts();
- };
- (function(def){
+ }
+ (function(def) {
def(AST_Statement, return_null);
def(AST_Jump, return_this);
- function block_aborts(){
+ function block_aborts() {
var n = this.body.length;
return n > 0 && aborts(this.body[n - 1]);
- };
+ }
def(AST_BlockStatement, block_aborts);
def(AST_SwitchBranch, block_aborts);
- def(AST_If, function(){
+ def(AST_If, function() {
return this.alternative && aborts(this.body) && aborts(this.alternative) && this;
});
- })(function(node, func){
+ })(function(node, func) {
node.DEFMETHOD("aborts", func);
});
/* -----[ optimizers ]----- */
- OPT(AST_Directive, function(self, compressor){
+ OPT(AST_Directive, function(self, compressor) {
if (compressor.has_directive(self.value) !== self) {
return make_node(AST_EmptyStatement, self);
}
return self;
});
- OPT(AST_Debugger, function(self, compressor){
+ OPT(AST_Debugger, function(self, compressor) {
if (compressor.option("drop_debugger"))
return make_node(AST_EmptyStatement, self);
return self;
});
- OPT(AST_LabeledStatement, function(self, compressor){
+ OPT(AST_LabeledStatement, function(self, compressor) {
if (self.body instanceof AST_Break
&& compressor.loopcontrol_target(self.body) === self.body) {
return make_node(AST_EmptyStatement, self);
@@ -3235,12 +3224,12 @@ merge(Compressor.prototype, {
return self.label.references.length == 0 ? self.body : self;
});
- OPT(AST_Block, function(self, compressor){
+ OPT(AST_Block, function(self, compressor) {
tighten_body(self.body, compressor);
return self;
});
- OPT(AST_BlockStatement, function(self, compressor){
+ OPT(AST_BlockStatement, function(self, compressor) {
tighten_body(self.body, compressor);
switch (self.body.length) {
case 1: return self.body[0];
@@ -3249,7 +3238,7 @@ merge(Compressor.prototype, {
return self;
});
- OPT(AST_Lambda, function(self, compressor){
+ OPT(AST_Lambda, function(self, compressor) {
tighten_body(self.body, compressor);
if (compressor.option("side_effects")
&& self.body.length == 1
@@ -3259,7 +3248,7 @@ merge(Compressor.prototype, {
return self;
});
- AST_Scope.DEFMETHOD("drop_unused", function(compressor){
+ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
if (!compressor.option("unused")) return;
if (compressor.has_directive("use asm")) return;
var self = this;
@@ -3299,7 +3288,7 @@ merge(Compressor.prototype, {
// pass 1: find out which symbols are directly used in
// this scope (not in nested scopes).
var scope = this;
- var tw = new TreeWalker(function(node, descend){
+ var tw = new TreeWalker(function(node, descend) {
if (node === self) return;
if (node instanceof AST_Defun) {
var node_def = node.name.definition();
@@ -3316,7 +3305,7 @@ merge(Compressor.prototype, {
var_defs_by_id.add(node.definition().id, node);
}
if (node instanceof AST_Definitions && scope === self) {
- node.definitions.forEach(function(def){
+ node.definitions.forEach(function(def) {
var node_def = def.name.definition();
if (def.name instanceof AST_SymbolVar) {
var_defs_by_id.add(node_def.id, def);
@@ -3607,135 +3596,128 @@ merge(Compressor.prototype, {
}
});
- AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){
- var self = this;
- if (compressor.has_directive("use asm")) return self;
+ AST_Scope.DEFMETHOD("hoist_declarations", function(compressor) {
+ if (compressor.has_directive("use asm")) return;
var hoist_funs = compressor.option("hoist_funs");
var hoist_vars = compressor.option("hoist_vars");
- if (hoist_funs || hoist_vars) {
- var dirs = [];
- var hoisted = [];
- var vars = new Dictionary(), vars_found = 0, var_decl = 0;
+ var self = this;
+ if (hoist_vars) {
// let's count var_decl first, we seem to waste a lot of
// space if we hoist `var` when there's only one.
- self.walk(new TreeWalker(function(node){
- if (node instanceof AST_Scope && node !== self)
- return true;
+ var var_decl = 0;
+ self.walk(new TreeWalker(function(node) {
+ if (var_decl > 1) return true;
+ if (node instanceof AST_Scope && node !== self) return true;
if (node instanceof AST_Var) {
- ++var_decl;
+ var_decl++;
return true;
}
}));
- hoist_vars = hoist_vars && var_decl > 1;
- var tt = new TreeTransformer(
- function before(node) {
- if (node !== self) {
- if (node instanceof AST_Directive) {
- dirs.push(node);
- return make_node(AST_EmptyStatement, node);
- }
- if (hoist_funs && node instanceof AST_Defun
- && (tt.parent() === self || !compressor.has_directive("use strict"))) {
- hoisted.push(node);
- return make_node(AST_EmptyStatement, node);
- }
- if (hoist_vars && node instanceof AST_Var) {
- node.definitions.forEach(function(def){
- vars.set(def.name.name, def);
- ++vars_found;
- });
- var seq = node.to_assignments(compressor);
- var p = tt.parent();
- if (p instanceof AST_ForIn && p.init === node) {
- if (seq == null) {
- var def = node.definitions[0].name;
- return make_node(AST_SymbolRef, def, def);
- }
- return seq;
- }
- if (p instanceof AST_For && p.init === node) {
- return seq;
- }
- if (!seq) return make_node(AST_EmptyStatement, node);
- return make_node(AST_SimpleStatement, node, {
- body: seq
- });
- }
- if (node instanceof AST_Scope)
- return node; // to avoid descending in nested scopes
- }
- }
- );
- self = self.transform(tt);
- if (vars_found > 0) {
- // collect only vars which don't show up in self's arguments list
- var defs = [];
- vars.each(function(def, name){
- if (self instanceof AST_Lambda
- && !all(self.argnames, function(argname) {
- return argname.name != name;
- })) {
- vars.del(name);
- } else {
- def = def.clone();
- def.value = null;
- defs.push(def);
- vars.set(name, def);
- }
+ if (var_decl <= 1) hoist_vars = false;
+ }
+ if (!hoist_funs && !hoist_vars) return;
+ var dirs = [];
+ var hoisted = [];
+ var vars = new Dictionary(), vars_found = 0;
+ var tt = new TreeTransformer(function(node) {
+ if (node === self) return;
+ if (node instanceof AST_Directive) {
+ dirs.push(node);
+ return make_node(AST_EmptyStatement, node);
+ }
+ if (hoist_funs && node instanceof AST_Defun
+ && (tt.parent() === self || !compressor.has_directive("use strict"))) {
+ hoisted.push(node);
+ return make_node(AST_EmptyStatement, node);
+ }
+ if (hoist_vars && node instanceof AST_Var) {
+ node.definitions.forEach(function(def) {
+ vars.set(def.name.name, def);
+ ++vars_found;
});
- if (defs.length > 0) {
- // try to merge in assignments
- for (var i = 0; i < self.body.length;) {
- if (self.body[i] instanceof AST_SimpleStatement) {
- var expr = self.body[i].body, sym, assign;
- if (expr instanceof AST_Assign
- && expr.operator == "="
- && (sym = expr.left) instanceof AST_Symbol
- && vars.has(sym.name))
- {
- var def = vars.get(sym.name);
- if (def.value) break;
- def.value = expr.right;
- remove(defs, def);
- defs.push(def);
- self.body.splice(i, 1);
- continue;
- }
- if (expr instanceof AST_Sequence
- && (assign = expr.expressions[0]) instanceof AST_Assign
- && assign.operator == "="
- && (sym = assign.left) instanceof AST_Symbol
- && vars.has(sym.name))
- {
- var def = vars.get(sym.name);
- if (def.value) break;
- def.value = assign.right;
- remove(defs, def);
- defs.push(def);
- self.body[i].body = make_sequence(expr, expr.expressions.slice(1));
- continue;
- }
- }
- if (self.body[i] instanceof AST_EmptyStatement) {
+ var seq = node.to_assignments(compressor);
+ var p = tt.parent();
+ if (p instanceof AST_ForIn && p.init === node) {
+ if (seq) return seq;
+ var def = node.definitions[0].name;
+ return make_node(AST_SymbolRef, def, def);
+ }
+ if (p instanceof AST_For && p.init === node) return seq;
+ if (!seq) return make_node(AST_EmptyStatement, node);
+ return make_node(AST_SimpleStatement, node, {
+ body: seq
+ });
+ }
+ if (node instanceof AST_Scope) return node;
+ });
+ self.transform(tt);
+ if (vars_found > 0) {
+ // collect only vars which don't show up in self's arguments list
+ var defs = [];
+ vars.each(function(def, name) {
+ if (self instanceof AST_Lambda
+ && !all(self.argnames, function(argname) {
+ return argname.name != name;
+ })) {
+ vars.del(name);
+ } else {
+ def = def.clone();
+ def.value = null;
+ defs.push(def);
+ vars.set(name, def);
+ }
+ });
+ if (defs.length > 0) {
+ // try to merge in assignments
+ for (var i = 0; i < self.body.length;) {
+ if (self.body[i] instanceof AST_SimpleStatement) {
+ var expr = self.body[i].body, sym, assign;
+ if (expr instanceof AST_Assign
+ && expr.operator == "="
+ && (sym = expr.left) instanceof AST_Symbol
+ && vars.has(sym.name))
+ {
+ var def = vars.get(sym.name);
+ if (def.value) break;
+ def.value = expr.right;
+ remove(defs, def);
+ defs.push(def);
self.body.splice(i, 1);
continue;
}
- if (self.body[i] instanceof AST_BlockStatement) {
- var tmp = [ i, 1 ].concat(self.body[i].body);
- self.body.splice.apply(self.body, tmp);
+ if (expr instanceof AST_Sequence
+ && (assign = expr.expressions[0]) instanceof AST_Assign
+ && assign.operator == "="
+ && (sym = assign.left) instanceof AST_Symbol
+ && vars.has(sym.name))
+ {
+ var def = vars.get(sym.name);
+ if (def.value) break;
+ def.value = assign.right;
+ remove(defs, def);
+ defs.push(def);
+ self.body[i].body = make_sequence(expr, expr.expressions.slice(1));
continue;
}
- break;
}
- defs = make_node(AST_Var, self, {
- definitions: defs
- });
- hoisted.push(defs);
- };
+ if (self.body[i] instanceof AST_EmptyStatement) {
+ self.body.splice(i, 1);
+ continue;
+ }
+ if (self.body[i] instanceof AST_BlockStatement) {
+ var tmp = [ i, 1 ].concat(self.body[i].body);
+ self.body.splice.apply(self.body, tmp);
+ continue;
+ }
+ break;
+ }
+ defs = make_node(AST_Var, self, {
+ definitions: defs
+ });
+ hoisted.push(defs);
}
- self.body = dirs.concat(hoisted, self.body);
}
- return self;
+ self.body = dirs.concat(hoisted, self.body);
});
AST_Scope.DEFMETHOD("var_names", function() {
@@ -3761,12 +3743,12 @@ merge(Compressor.prototype, {
return name;
});
- AST_Scope.DEFMETHOD("hoist_properties", function(compressor){
+ AST_Scope.DEFMETHOD("hoist_properties", function(compressor) {
+ if (!compressor.option("hoist_props") || compressor.has_directive("use asm")) return;
var self = this;
- if (!compressor.option("hoist_props") || compressor.has_directive("use asm")) return self;
var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false;
var defs_by_id = Object.create(null);
- return self.transform(new TreeTransformer(function(node, descend) {
+ self.transform(new TreeTransformer(function(node, descend) {
if (node instanceof AST_Assign
&& node.operator == "="
&& node.write_only
@@ -3832,6 +3814,7 @@ merge(Compressor.prototype, {
if (def.assignments != count) return;
if (def.direct_access) return;
if (def.escaped == 1) return;
+ if (def.references.length == count) return;
if (def.single_use) return;
if (top_retain(def)) return;
if (sym.fixed_value() !== right) return;
@@ -3853,7 +3836,7 @@ merge(Compressor.prototype, {
// drop_side_effect_free()
// remove side-effect-free parts which only affects return value
- (function(def){
+ (function(def) {
// Drop side-effect-free elements from an array of expressions.
// Returns an array of expressions with side-effects or null
// if all elements were dropped. Note: original array may be
@@ -3876,7 +3859,7 @@ merge(Compressor.prototype, {
def(AST_Node, return_this);
def(AST_Constant, return_null);
def(AST_This, return_null);
- def(AST_Call, function(compressor, first_in_statement){
+ def(AST_Call, function(compressor, first_in_statement) {
if (!this.is_expr_pure(compressor)) {
if (this.expression.is_call_pure(compressor)) {
var exprs = this.args.slice();
@@ -3908,7 +3891,7 @@ merge(Compressor.prototype, {
});
def(AST_Accessor, return_null);
def(AST_Function, return_null);
- def(AST_Binary, function(compressor, first_in_statement){
+ def(AST_Binary, function(compressor, first_in_statement) {
var right = this.right.drop_side_effect_free(compressor);
if (!right) return this.left.drop_side_effect_free(compressor, first_in_statement);
if (lazy_op[this.operator]) {
@@ -3922,7 +3905,7 @@ merge(Compressor.prototype, {
return make_sequence(this, [ left, right ]);
}
});
- def(AST_Assign, function(compressor){
+ def(AST_Assign, function(compressor) {
var left = this.left;
if (left.has_side_effects(compressor)
|| compressor.has_directive("use strict")
@@ -3936,7 +3919,7 @@ merge(Compressor.prototype, {
}
return this;
});
- def(AST_Conditional, function(compressor){
+ def(AST_Conditional, function(compressor) {
var consequent = this.consequent.drop_side_effect_free(compressor);
var alternative = this.alternative.drop_side_effect_free(compressor);
if (consequent === this.consequent && alternative === this.alternative) return this;
@@ -3955,7 +3938,7 @@ merge(Compressor.prototype, {
node.alternative = alternative;
return node;
});
- def(AST_Unary, function(compressor, first_in_statement){
+ def(AST_Unary, function(compressor, first_in_statement) {
if (unary_side_effects[this.operator]) {
this.write_only = !this.expression.has_side_effects(compressor);
return this;
@@ -3971,22 +3954,22 @@ merge(Compressor.prototype, {
def(AST_SymbolRef, function(compressor) {
return this.is_declared(compressor) ? null : this;
});
- def(AST_Object, function(compressor, first_in_statement){
+ def(AST_Object, function(compressor, first_in_statement) {
var values = trim(this.properties, compressor, first_in_statement);
return values && make_sequence(this, values);
});
- def(AST_ObjectProperty, function(compressor, first_in_statement){
+ def(AST_ObjectProperty, function(compressor, first_in_statement) {
return this.value.drop_side_effect_free(compressor, first_in_statement);
});
- def(AST_Array, function(compressor, first_in_statement){
+ def(AST_Array, function(compressor, first_in_statement) {
var values = trim(this.elements, compressor, first_in_statement);
return values && make_sequence(this, values);
});
- def(AST_Dot, function(compressor, first_in_statement){
+ def(AST_Dot, function(compressor, first_in_statement) {
if (this.expression.may_throw_on_access(compressor)) return this;
return this.expression.drop_side_effect_free(compressor, first_in_statement);
});
- def(AST_Sub, function(compressor, first_in_statement){
+ def(AST_Sub, function(compressor, first_in_statement) {
if (this.expression.may_throw_on_access(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);
@@ -3994,7 +3977,7 @@ merge(Compressor.prototype, {
if (!property) return expression;
return make_sequence(this, [ expression, property ]);
});
- def(AST_Sequence, function(compressor){
+ def(AST_Sequence, function(compressor) {
var last = this.tail_node();
var expr = last.drop_side_effect_free(compressor);
if (expr === last) return this;
@@ -4002,11 +3985,11 @@ merge(Compressor.prototype, {
if (expr) expressions.push(expr);
return make_sequence(this, expressions);
});
- })(function(node, func){
+ })(function(node, func) {
node.DEFMETHOD("drop_side_effect_free", func);
});
- OPT(AST_SimpleStatement, function(self, compressor){
+ OPT(AST_SimpleStatement, function(self, compressor) {
if (compressor.option("side_effects")) {
var body = self.body;
var node = body.drop_side_effect_free(compressor, true);
@@ -4021,7 +4004,7 @@ merge(Compressor.prototype, {
return self;
});
- OPT(AST_While, function(self, compressor){
+ OPT(AST_While, function(self, compressor) {
return compressor.option("loops") ? make_node(AST_For, self, self).optimize(compressor) : self;
});
@@ -4039,7 +4022,7 @@ merge(Compressor.prototype, {
return found;
}
- OPT(AST_Do, function(self, compressor){
+ OPT(AST_Do, function(self, compressor) {
if (!compressor.option("loops")) return self;
var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
if (!(cond instanceof AST_Node)) {
@@ -4142,7 +4125,7 @@ merge(Compressor.prototype, {
}
}
- OPT(AST_For, function(self, compressor){
+ OPT(AST_For, function(self, compressor) {
if (!compressor.option("loops")) return self;
if (compressor.option("side_effects") && self.init) {
self.init = self.init.drop_side_effect_free(compressor);
@@ -4191,7 +4174,7 @@ merge(Compressor.prototype, {
return if_break_in_loop(self, compressor);
});
- OPT(AST_If, function(self, compressor){
+ OPT(AST_If, function(self, compressor) {
if (is_empty(self.alternative)) self.alternative = null;
if (!compressor.option("conditionals")) return self;
@@ -4337,7 +4320,7 @@ merge(Compressor.prototype, {
return self;
});
- OPT(AST_Switch, function(self, compressor){
+ OPT(AST_Switch, function(self, compressor) {
if (!compressor.option("switches")) return self;
var branch;
var value = self.expression.evaluate(compressor);
@@ -4443,7 +4426,7 @@ merge(Compressor.prototype, {
}
});
- OPT(AST_Try, function(self, compressor){
+ OPT(AST_Try, function(self, compressor) {
tighten_body(self.body, compressor);
if (self.bcatch && self.bfinally && all(self.bfinally.body, is_empty)) self.bfinally = null;
if (compressor.option("dead_code") && all(self.body, is_empty)) {
@@ -4468,13 +4451,15 @@ merge(Compressor.prototype, {
return self;
});
- AST_Definitions.DEFMETHOD("remove_initializers", function(){
- this.definitions.forEach(function(def){ def.value = null });
+ AST_Definitions.DEFMETHOD("remove_initializers", function() {
+ this.definitions.forEach(function(def) {
+ def.value = null;
+ });
});
- AST_Definitions.DEFMETHOD("to_assignments", function(compressor){
+ AST_Definitions.DEFMETHOD("to_assignments", function(compressor) {
var reduce_vars = compressor.option("reduce_vars");
- var assignments = this.definitions.reduce(function(a, def){
+ var assignments = this.definitions.reduce(function(a, def) {
if (def.value) {
var name = make_node(AST_SymbolRef, def.name, def.name);
a.push(make_node(AST_Assign, def, {
@@ -4493,7 +4478,7 @@ merge(Compressor.prototype, {
return make_sequence(this, assignments);
});
- OPT(AST_Definitions, function(self, compressor){
+ OPT(AST_Definitions, function(self, compressor) {
if (self.definitions.length == 0)
return make_node(AST_EmptyStatement, self);
return self;
@@ -4669,7 +4654,7 @@ merge(Compressor.prototype, {
} else {
first = make_node(AST_String, self, { value: "" });
}
- return elements.reduce(function(prev, el){
+ return elements.reduce(function(prev, el) {
return make_node(AST_Binary, el, {
operator : "+",
left : prev,
@@ -5048,7 +5033,7 @@ merge(Compressor.prototype, {
return self;
});
- OPT(AST_Sequence, function(self, compressor){
+ OPT(AST_Sequence, function(self, compressor) {
if (!compressor.option("side_effects")) return self;
var expressions = [];
filter_for_side_effects();
@@ -5086,7 +5071,7 @@ merge(Compressor.prototype, {
}
});
- AST_Unary.DEFMETHOD("lift_sequences", function(compressor){
+ AST_Unary.DEFMETHOD("lift_sequences", function(compressor) {
if (compressor.option("sequences") && this.expression instanceof AST_Sequence) {
var x = this.expression.expressions.slice();
var e = this.clone();
@@ -5097,11 +5082,11 @@ merge(Compressor.prototype, {
return this;
});
- OPT(AST_UnaryPostfix, function(self, compressor){
+ OPT(AST_UnaryPostfix, function(self, compressor) {
return self.lift_sequences(compressor);
});
- OPT(AST_UnaryPrefix, function(self, compressor){
+ OPT(AST_UnaryPrefix, function(self, compressor) {
var e = self.expression;
if (self.operator == "delete"
&& !(e instanceof AST_SymbolRef
@@ -5177,7 +5162,7 @@ merge(Compressor.prototype, {
return self;
});
- AST_Binary.DEFMETHOD("lift_sequences", function(compressor){
+ AST_Binary.DEFMETHOD("lift_sequences", function(compressor) {
if (compressor.option("sequences")) {
if (this.left instanceof AST_Sequence) {
var x = this.left.expressions.slice();
@@ -5218,7 +5203,7 @@ merge(Compressor.prototype, {
|| node instanceof AST_Object;
}
- OPT(AST_Binary, function(self, compressor){
+ OPT(AST_Binary, function(self, compressor) {
function reversible() {
return self.left.is_constant()
|| self.right.is_constant()
@@ -5666,7 +5651,7 @@ merge(Compressor.prototype, {
return node;
}
- OPT(AST_SymbolRef, function(self, compressor){
+ OPT(AST_SymbolRef, function(self, compressor) {
var def = self.resolve_defines(compressor);
if (def) {
return def.optimize(compressor);
@@ -5799,7 +5784,7 @@ merge(Compressor.prototype, {
return lhs instanceof AST_SymbolRef || lhs.TYPE === self.TYPE;
}
- OPT(AST_Undefined, function(self, compressor){
+ OPT(AST_Undefined, function(self, compressor) {
if (compressor.option("unsafe_undefined")) {
var undef = find_variable(compressor, "undefined");
if (undef) {
@@ -5822,7 +5807,7 @@ merge(Compressor.prototype, {
});
});
- OPT(AST_Infinity, function(self, compressor){
+ OPT(AST_Infinity, function(self, compressor) {
var lhs = is_lhs(compressor.self(), compressor.parent());
if (lhs && is_atomic(lhs, self)) return self;
if (compressor.option("keep_infinity")
@@ -5840,7 +5825,7 @@ merge(Compressor.prototype, {
});
});
- OPT(AST_NaN, function(self, compressor){
+ OPT(AST_NaN, function(self, compressor) {
var lhs = is_lhs(compressor.self(), compressor.parent());
if (lhs && !is_atomic(lhs, self)
|| find_variable(compressor, "NaN")) {
@@ -5880,7 +5865,7 @@ merge(Compressor.prototype, {
var ASSIGN_OPS = makePredicate("+ - / * % >> << >>> | ^ &");
var ASSIGN_OPS_COMMUTATIVE = makePredicate("* | ^ &");
- OPT(AST_Assign, function(self, compressor){
+ OPT(AST_Assign, function(self, compressor) {
var def;
if (compressor.option("dead_code")
&& self.left instanceof AST_SymbolRef
@@ -5940,7 +5925,7 @@ merge(Compressor.prototype, {
}
});
- OPT(AST_Conditional, function(self, compressor){
+ OPT(AST_Conditional, function(self, compressor) {
if (!compressor.option("conditionals")) return self;
// This looks like lift_sequences(), should probably be under "sequences"
if (self.condition instanceof AST_Sequence) {
@@ -6160,7 +6145,7 @@ merge(Compressor.prototype, {
}
});
- OPT(AST_Boolean, function(self, compressor){
+ OPT(AST_Boolean, function(self, compressor) {
if (!compressor.option("booleans")) return self;
if (compressor.in_boolean_context()) return make_node(AST_Number, self, {
value: +self.value
@@ -6186,7 +6171,7 @@ merge(Compressor.prototype, {
});
});
- OPT(AST_Sub, function(self, compressor){
+ OPT(AST_Sub, function(self, compressor) {
var expr = self.expression;
var prop = self.property;
if (compressor.option("properties")) {
@@ -6330,7 +6315,7 @@ merge(Compressor.prototype, {
}
});
- OPT(AST_Dot, function(self, compressor){
+ OPT(AST_Dot, function(self, compressor) {
if (self.property == "arguments" || self.property == "caller") {
compressor.warn("Function.protoype.{prop} not supported [{file}:{line},{col}]", {
prop: self.property,
@@ -6392,19 +6377,27 @@ merge(Compressor.prototype, {
return self;
});
- OPT(AST_Return, function(self, compressor){
+ OPT(AST_Return, function(self, compressor) {
if (self.value && is_undefined(self.value, compressor)) {
self.value = null;
}
return self;
});
- OPT(AST_VarDef, function(self, compressor){
+ OPT(AST_VarDef, function(self, compressor) {
var defines = compressor.option("global_defs");
if (defines && HOP(defines, self.name.name)) {
compressor.warn('global_defs ' + self.name.name + ' redefined [{file}:{line},{col}]', self.start);
}
return self;
});
-
-})();
+})(function(node, optimizer) {
+ node.DEFMETHOD("optimize", function(compressor) {
+ var self = this;
+ if (self._optimized) return self;
+ if (compressor.has_directive("use asm")) return self;
+ var opt = optimizer(self, compressor);
+ opt._optimized = true;
+ return opt;
+ });
+});