diff options
Diffstat (limited to 'lib/compress.js')
-rw-r--r-- | lib/compress.js | 597 |
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; + }); +}); |