diff options
Diffstat (limited to 'node_modules/uglify-js/lib/compress.js')
-rw-r--r-- | node_modules/uglify-js/lib/compress.js | 252 |
1 files changed, 178 insertions, 74 deletions
diff --git a/node_modules/uglify-js/lib/compress.js b/node_modules/uglify-js/lib/compress.js index 1d9258cf6..d8a491ebc 100644 --- a/node_modules/uglify-js/lib/compress.js +++ b/node_modules/uglify-js/lib/compress.js @@ -84,6 +84,7 @@ function Compressor(options, false_by_default) { unsafe_comps : false, unsafe_math : false, unsafe_proto : false, + unsafe_regexp : false, unused : !false_by_default, warnings : true, }, true); @@ -271,6 +272,14 @@ merge(Compressor.prototype, { if (d.fixed === undefined || !is_safe(d) || is_modified(node, 0, node.fixed_value() instanceof AST_Lambda)) { d.fixed = false; + } else { + var parent = tw.parent(); + if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right + || parent instanceof AST_Call && node !== parent.expression + || parent instanceof AST_Return && node === parent.value && node.scope !== d.scope + || parent instanceof AST_VarDef && node === parent.value) { + d.escaped = true; + } } } if (node instanceof AST_SymbolCatch) { @@ -308,21 +317,55 @@ merge(Compressor.prototype, { safe_ids = save_ids; return true; } - var iife; - if (node instanceof AST_Function - && !node.name - && (iife = tw.parent()) instanceof AST_Call - && iife.expression === node) { - // Virtually turn IIFE parameters into variable definitions: - // (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})() - // So existing transformation rules can work on them. - node.argnames.forEach(function(arg, i) { - var d = arg.definition(); - d.fixed = function() { - return iife.args[i] || make_node(AST_Undefined, iife); - }; - mark(d, true); - }); + if (node instanceof AST_Function) { + push(); + var iife; + if (!node.name + && (iife = tw.parent()) instanceof AST_Call + && iife.expression === node) { + // Virtually turn IIFE parameters into variable definitions: + // (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})() + // So existing transformation rules can work on them. + node.argnames.forEach(function(arg, i) { + var d = arg.definition(); + if (!node.uses_arguments && d.fixed === undefined) { + d.fixed = function() { + return iife.args[i] || make_node(AST_Undefined, iife); + }; + mark(d, true); + } else { + d.fixed = false; + } + }); + } + descend(); + pop(); + return true; + } + if (node instanceof AST_Accessor) { + var save_ids = safe_ids; + safe_ids = Object.create(null); + descend(); + safe_ids = save_ids; + return true; + } + if (node instanceof AST_Binary + && (node.operator == "&&" || node.operator == "||")) { + node.left.walk(tw); + push(); + node.right.walk(tw); + pop(); + return true; + } + if (node instanceof AST_Conditional) { + node.condition.walk(tw); + push(); + node.consequent.walk(tw); + pop(); + push(); + node.alternative.walk(tw); + pop(); + return true; } if (node instanceof AST_If || node instanceof AST_DWLoop) { node.condition.walk(tw); @@ -359,7 +402,19 @@ merge(Compressor.prototype, { pop(); return true; } - if (node instanceof AST_Catch || node instanceof AST_SwitchBranch) { + if (node instanceof AST_Try) { + push(); + walk_body(node, tw); + pop(); + if (node.bcatch) { + push(); + node.bcatch.walk(tw); + pop(); + } + if (node.bfinally) node.bfinally.walk(tw); + return true; + } + if (node instanceof AST_SwitchBranch) { push(); descend(); pop(); @@ -393,7 +448,10 @@ merge(Compressor.prototype, { } function reset_def(def) { - if (toplevel || !def.global || def.orig[0] instanceof AST_SymbolConst) { + def.escaped = false; + if (def.scope.uses_eval) { + def.fixed = false; + } else if (toplevel || !def.global || def.orig[0] instanceof AST_SymbolConst) { def.fixed = undefined; } else { def.fixed = false; @@ -419,6 +477,14 @@ merge(Compressor.prototype, { return fixed(); }); + function is_reference_const(ref) { + if (!(ref instanceof AST_SymbolRef)) return false; + var orig = ref.definition().orig; + for (var i = orig.length; --i >= 0;) { + if (orig[i] instanceof AST_SymbolConst) return true; + } + } + function find_variable(compressor, name) { var scope, i = 0; while (scope = compressor.parent(i++)) { @@ -1160,12 +1226,12 @@ merge(Compressor.prototype, { && !node.expression.has_side_effects(compressor); } - // may_eq_null() - // returns true if this node may evaluate to null or undefined + // may_throw_on_access() + // returns true if this node may be null, undefined or contain `AST_Accessor` (function(def) { - AST_Node.DEFMETHOD("may_eq_null", function(compressor) { + AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) { var pure_getters = compressor.option("pure_getters"); - return !pure_getters || this._eq_null(pure_getters); + return !pure_getters || this._throw_on_access(pure_getters); }); function is_strict(pure_getters) { @@ -1177,7 +1243,12 @@ merge(Compressor.prototype, { def(AST_Undefined, return_true); def(AST_Constant, return_false); def(AST_Array, return_false); - def(AST_Object, return_false); + def(AST_Object, function(pure_getters) { + if (!is_strict(pure_getters)) return false; + for (var i = this.properties.length; --i >=0;) + if (this.properties[i].value instanceof AST_Accessor) return true; + return false; + }); def(AST_Function, return_false); def(AST_UnaryPostfix, return_false); def(AST_UnaryPrefix, function() { @@ -1186,33 +1257,33 @@ merge(Compressor.prototype, { def(AST_Binary, function(pure_getters) { switch (this.operator) { case "&&": - return this.left._eq_null(pure_getters); + return this.left._throw_on_access(pure_getters); case "||": - return this.left._eq_null(pure_getters) - && this.right._eq_null(pure_getters); + return this.left._throw_on_access(pure_getters) + && this.right._throw_on_access(pure_getters); default: return false; } }) def(AST_Assign, function(pure_getters) { return this.operator == "=" - && this.right._eq_null(pure_getters); + && this.right._throw_on_access(pure_getters); }) def(AST_Conditional, function(pure_getters) { - return this.consequent._eq_null(pure_getters) - || this.alternative._eq_null(pure_getters); + return this.consequent._throw_on_access(pure_getters) + || this.alternative._throw_on_access(pure_getters); }) def(AST_Seq, function(pure_getters) { - return this.cdr._eq_null(pure_getters); + return this.cdr._throw_on_access(pure_getters); }); def(AST_SymbolRef, function(pure_getters) { if (this.is_undefined) return true; if (!is_strict(pure_getters)) return false; var fixed = this.fixed_value(); - return !fixed || fixed._eq_null(pure_getters); + return !fixed || fixed._throw_on_access(pure_getters); }); })(function(node, func) { - node.DEFMETHOD("_eq_null", func); + node.DEFMETHOD("_throw_on_access", func); }); /* -----[ boolean/negation helpers ]----- */ @@ -1549,23 +1620,20 @@ merge(Compressor.prototype, { : ev(this.alternative, compressor); }); def(AST_SymbolRef, function(compressor){ - if (this._evaluating) throw def; + if (!compressor.option("reduce_vars") || this._evaluating) throw def; this._evaluating = true; try { var fixed = this.fixed_value(); - if (compressor.option("reduce_vars") && fixed) { - if (compressor.option("unsafe")) { - if (!HOP(fixed, "_evaluated")) { - fixed._evaluated = ev(fixed, compressor); - } - return fixed._evaluated; - } - return ev(fixed, compressor); - } + if (!fixed) throw def; + var value = ev(fixed, compressor); + if (!HOP(fixed, "_eval")) fixed._eval = function() { + return value; + }; + if (value && typeof value == "object" && this.definition().escaped) throw def; + return value; } finally { this._evaluating = false; } - throw def; }); def(AST_PropAccess, function(compressor){ if (compressor.option("unsafe")) { @@ -1755,11 +1823,11 @@ merge(Compressor.prototype, { return any(this.elements, compressor); }); def(AST_Dot, function(compressor){ - return this.expression.may_eq_null(compressor) + return this.expression.may_throw_on_access(compressor) || this.expression.has_side_effects(compressor); }); def(AST_Sub, function(compressor){ - return this.expression.may_eq_null(compressor) + return this.expression.may_throw_on_access(compressor) || this.expression.has_side_effects(compressor) || this.property.has_side_effects(compressor); }); @@ -1891,6 +1959,7 @@ merge(Compressor.prototype, { && node instanceof AST_Assign && node.operator == "=" && node.left instanceof AST_SymbolRef + && !is_reference_const(node.left) && scope === self) { node.right.walk(tw); return true; @@ -1980,7 +2049,7 @@ merge(Compressor.prototype, { } return node; } - if (drop_vars && node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) { + if (drop_vars && node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn && tt.parent().init === node)) { var def = node.definitions.filter(function(def){ if (def.value) def.value = def.value.transform(tt); var sym = def.name.definition(); @@ -2058,26 +2127,32 @@ merge(Compressor.prototype, { return maintain_this_binding(tt.parent(), node, node.right.transform(tt)); } } + // certain combination of unused name + side effect leads to: + // https://github.com/mishoo/UglifyJS2/issues/44 + // https://github.com/mishoo/UglifyJS2/issues/1830 + // that's an invalid AST. + // We fix it at this stage by moving the `var` outside the `for`. if (node instanceof AST_For) { descend(node, this); - if (node.init instanceof AST_BlockStatement) { - // certain combination of unused name + side effect leads to: - // https://github.com/mishoo/UglifyJS2/issues/44 - // that's an invalid AST. - // We fix it at this stage by moving the `var` outside the `for`. - - var body = node.init.body.slice(0, -1); - node.init = node.init.body.slice(-1)[0].body; - body.push(node); - - return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, { - body: body - }); + var block = node.init; + node.init = block.body.pop(); + block.body.push(node); + return in_list ? MAP.splice(block.body) : block; } else if (is_empty(node.init)) { node.init = null; - return node; } + return node; + } + if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) { + descend(node, this); + if (node.body instanceof AST_BlockStatement) { + var block = node.body; + node.body = block.body.pop(); + block.body.push(node); + return in_list ? MAP.splice(block.body) : block; + } + return node; } if (node instanceof AST_Scope && node !== self) return node; @@ -2256,6 +2331,7 @@ merge(Compressor.prototype, { var args = trim(this.args, compressor, first_in_statement); return args && AST_Seq.from_array(args); }); + def(AST_Accessor, return_null); def(AST_Function, return_null); def(AST_Binary, function(compressor, first_in_statement){ var right = this.right.drop_side_effect_free(compressor); @@ -2326,11 +2402,11 @@ merge(Compressor.prototype, { return values && AST_Seq.from_array(values); }); def(AST_Dot, function(compressor, first_in_statement){ - if (this.expression.may_eq_null(compressor)) return this; + 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){ - if (this.expression.may_eq_null(compressor)) return this; + 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); var property = this.property.drop_side_effect_free(compressor); @@ -2380,7 +2456,7 @@ merge(Compressor.prototype, { if (compressor.option("dead_code") && self instanceof AST_While) { var a = []; extract_declarations_from_unreachable_code(compressor, self.body, a); - return make_node(AST_BlockStatement, self, { body: a }); + return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor); } if (self instanceof AST_Do) { var has_loop_control = false; @@ -2389,7 +2465,8 @@ merge(Compressor.prototype, { if (node instanceof AST_LoopControl && tw.loopcontrol_target(node) === self) return has_loop_control = true; }); - self.walk(tw); + var parent = compressor.parent(); + (parent instanceof AST_LabeledStatement ? parent : self).walk(tw); if (!has_loop_control) return self.body; } } @@ -2459,7 +2536,7 @@ merge(Compressor.prototype, { })); } extract_declarations_from_unreachable_code(compressor, self.body, a); - return make_node(AST_BlockStatement, self, { body: a }); + return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor); } if (cond !== self.condition) { cond = make_node_from_constant(cond, self.condition).transform(compressor); @@ -2711,9 +2788,9 @@ merge(Compressor.prototype, { var body = []; if (self.bcatch) extract_declarations_from_unreachable_code(compressor, self.bcatch, body); if (self.bfinally) body = body.concat(self.bfinally.body); - return body.length > 0 ? make_node(AST_BlockStatement, self, { + return make_node(AST_BlockStatement, self, { body: body - }).optimize(compressor) : make_node(AST_EmptyStatement, self); + }).optimize(compressor); } return self; }); @@ -3041,7 +3118,8 @@ merge(Compressor.prototype, { } if (left && !(left instanceof AST_SymbolRef - && left.definition().orig[0] instanceof AST_SymbolLambda)) { + && (left.definition().orig[0] instanceof AST_SymbolLambda + || is_reference_const(left)))) { var parent, field; var cdr = self.cdr; while (true) { @@ -3595,29 +3673,55 @@ merge(Compressor.prototype, { return make_node(AST_Infinity, self).optimize(compressor); } } - if (compressor.option("evaluate") && compressor.option("reduce_vars")) { + if (compressor.option("evaluate") + && compressor.option("reduce_vars") + && is_lhs(self, compressor.parent()) !== self) { var d = self.definition(); var fixed = self.fixed_value(); if (fixed) { if (d.should_replace === undefined) { var init = fixed.evaluate(compressor); - if (init !== fixed) { + if (init !== fixed && (compressor.option("unsafe_regexp") || !(init instanceof RegExp))) { init = make_node_from_constant(init, fixed); - var value = best_of_expression(init.optimize(compressor), fixed).print_to_string().length; + var value = init.optimize(compressor).print_to_string().length; + var fn; + if (has_symbol_ref(fixed)) { + fn = function() { + var result = init.optimize(compressor); + return result === init ? result.clone(true) : result; + }; + } else { + value = Math.min(value, fixed.print_to_string().length); + fn = function() { + var result = best_of_expression(init.optimize(compressor), fixed); + return result === init || result === fixed ? result.clone(true) : result; + }; + } var name = d.name.length; - var freq = d.references.length; - var overhead = d.global || !freq ? 0 : (name + 2 + value) / freq; - d.should_replace = value <= name + overhead ? init : false; + var overhead = 0; + if (compressor.option("unused") && (!d.global || compressor.option("toplevel"))) { + overhead = (name + 2 + value) / d.references.length; + } + d.should_replace = value <= name + overhead ? fn : false; } else { d.should_replace = false; } } if (d.should_replace) { - return best_of_expression(d.should_replace.optimize(compressor), fixed).clone(true); + return d.should_replace(); } } } return self; + + function has_symbol_ref(value) { + var found; + value.walk(new TreeWalker(function(node) { + if (node instanceof AST_SymbolRef) found = true; + if (found) return true; + })); + return found; + } }); function is_atomic(lhs, self) { |