UNPKG

bot18

Version:

A high-frequency cryptocurrency trading bot by Zenbot creator @carlos8f

1,149 lines (1,077 loc) 179 kB
/*********************************************************************** A JavaScript tokenizer / parser / beautifier / compressor. https://github.com/mishoo/UglifyJS2 -------------------------------- (C) --------------------------------- Author: Mihai Bazon <mihai.bazon@gmail.com> http://mihai.bazon.net/blog Distributed under the BSD license: Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ "use strict"; function Compressor(options, false_by_default) { if (!(this instanceof Compressor)) return new Compressor(options, false_by_default); TreeTransformer.call(this, this.before, this.after); this.options = defaults(options, { angular : false, booleans : !false_by_default, cascade : !false_by_default, collapse_vars : !false_by_default, comparisons : !false_by_default, conditionals : !false_by_default, dead_code : !false_by_default, drop_console : false, drop_debugger : !false_by_default, evaluate : !false_by_default, expression : false, global_defs : {}, hoist_funs : !false_by_default, hoist_vars : false, if_return : !false_by_default, join_vars : !false_by_default, keep_fargs : true, keep_fnames : false, keep_infinity : false, loops : !false_by_default, negate_iife : !false_by_default, passes : 1, properties : !false_by_default, pure_getters : !false_by_default && "strict", pure_funcs : null, reduce_vars : !false_by_default, screw_ie8 : true, sequences : !false_by_default, side_effects : !false_by_default, switches : !false_by_default, top_retain : null, toplevel : !!(options && options["top_retain"]), unsafe : false, unsafe_comps : false, unsafe_math : false, unsafe_proto : false, unsafe_regexp : false, unused : !false_by_default, warnings : true, }, true); var pure_funcs = this.options["pure_funcs"]; if (typeof pure_funcs == "function") { this.pure_funcs = pure_funcs; } else { this.pure_funcs = pure_funcs ? function(node) { return pure_funcs.indexOf(node.expression.print_to_string()) < 0; } : return_true; } var top_retain = this.options["top_retain"]; if (top_retain instanceof RegExp) { this.top_retain = function(def) { return top_retain.test(def.name); }; } else if (typeof top_retain == "function") { this.top_retain = top_retain; } else if (top_retain) { if (typeof top_retain == "string") { top_retain = top_retain.split(/,/); } this.top_retain = function(def) { return top_retain.indexOf(def.name) >= 0; }; } var sequences = this.options["sequences"]; this.sequences_limit = sequences == 1 ? 200 : sequences | 0; this.warnings_produced = {}; }; Compressor.prototype = new TreeTransformer; merge(Compressor.prototype, { option: function(key) { return this.options[key] }, compress: function(node) { if (this.option("expression")) { node = node.process_expression(true); } var passes = +this.options.passes || 1; for (var pass = 0; pass < passes && pass < 3; ++pass) { if (pass > 0 || this.option("reduce_vars")) node.reset_opt_flags(this, true); node = node.transform(this); } if (this.option("expression")) { node = node.process_expression(false); } return node; }, info: function() { if (this.options.warnings == "verbose") { AST_Node.warn.apply(AST_Node, arguments); } }, warn: function(text, props) { if (this.options.warnings) { // only emit unique warnings var message = string_template(text, props); if (!(message in this.warnings_produced)) { this.warnings_produced[message] = true; AST_Node.warn.apply(AST_Node, arguments); } } }, clear_warnings: function() { this.warnings_produced = {}; }, before: function(node, descend, in_list) { if (node._squeezed) return node; var was_scope = false; if (node instanceof AST_Scope) { node = node.hoist_declarations(this); was_scope = true; } // Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize() // would call AST_Node.transform() if a different instance of AST_Node is // produced after OPT(). // This corrupts TreeWalker.stack, which cause AST look-ups to malfunction. // Migrate and defer all children's AST_Node.transform() to below, which // will now happen after this parent AST_Node has been properly substituted // thus gives a consistent AST snapshot. descend(node, this); // Existing code relies on how AST_Node.optimize() worked, and omitting the // following replacement call would result in degraded efficiency of both // output and performance. descend(node, this); var opt = node.optimize(this); if (was_scope && opt instanceof AST_Scope) { opt.drop_unused(this); descend(opt, this); } if (opt === node) opt._squeezed = true; return opt; } }); (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){ return self; }); AST_Node.DEFMETHOD("equivalent_to", function(node){ return this.TYPE == node.TYPE && this.print_to_string() == node.print_to_string(); }); AST_Node.DEFMETHOD("process_expression", function(insert, compressor) { var self = this; var tt = new TreeTransformer(function(node) { if (insert && node instanceof AST_SimpleStatement) { return make_node(AST_Return, node, { value: node.body }); } if (!insert && node instanceof AST_Return) { if (compressor) { var value = node.value && node.value.drop_side_effect_free(compressor, true); return value ? make_node(AST_SimpleStatement, node, { body: value }) : make_node(AST_EmptyStatement, node); } return make_node(AST_SimpleStatement, node, { body: node.value || make_node(AST_UnaryPrefix, node, { operator: "void", expression: make_node(AST_Number, node, { value: 0 }) }) }); } if (node instanceof AST_Lambda && node !== self) { return node; } if (node instanceof AST_Block) { var index = node.body.length - 1; if (index >= 0) { node.body[index] = node.body[index].transform(tt); } } if (node instanceof AST_If) { node.body = node.body.transform(tt); if (node.alternative) { node.alternative = node.alternative.transform(tt); } } if (node instanceof AST_With) { node.body = node.body.transform(tt); } return node; }); return self.transform(tt); }); AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan){ var reduce_vars = rescan && compressor.option("reduce_vars"); var toplevel = compressor.option("toplevel"); var safe_ids = Object.create(null); var suppressor = new TreeWalker(function(node) { if (node instanceof AST_Symbol) { var d = node.definition(); if (node instanceof AST_SymbolRef) d.references.push(node); d.fixed = false; } }); var tw = new TreeWalker(function(node, descend){ node._squeezed = false; node._optimized = false; if (reduce_vars) { if (node instanceof AST_Toplevel) node.globals.each(reset_def); if (node instanceof AST_Scope) node.variables.each(reset_def); if (node instanceof AST_SymbolRef) { var d = node.definition(); d.references.push(node); 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) { node.definition().fixed = false; } if (node instanceof AST_VarDef) { var d = node.name.definition(); if (d.fixed == null) { if (node.value) { d.fixed = function() { return node.value; }; mark(d, false); descend(); } else { d.fixed = null; } mark(d, true); return true; } else if (node.value) { d.fixed = false; } } if (node instanceof AST_Defun) { var d = node.name.definition(); if (!toplevel && d.global || is_safe(d)) { d.fixed = false; } else { d.fixed = node; mark(d, true); } var save_ids = safe_ids; safe_ids = Object.create(null); descend(); safe_ids = save_ids; return 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); push(); node.body.walk(tw); pop(); if (node.alternative) { push(); node.alternative.walk(tw); pop(); } return true; } if (node instanceof AST_LabeledStatement) { push(); node.body.walk(tw); pop(); return true; } if (node instanceof AST_For) { if (node.init) node.init.walk(tw); push(); if (node.condition) node.condition.walk(tw); node.body.walk(tw); if (node.step) node.step.walk(tw); pop(); return true; } if (node instanceof AST_ForIn) { node.init.walk(suppressor); node.object.walk(tw); push(); node.body.walk(tw); pop(); return true; } 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(); return true; } } }); this.walk(tw); function mark(def, safe) { safe_ids[def.id] = safe; } function is_safe(def) { if (safe_ids[def.id]) { if (def.fixed == null) { var orig = def.orig[0]; if (orig instanceof AST_SymbolFunarg || orig.name == "arguments") return false; def.fixed = make_node(AST_Undefined, orig); } return true; } } function push() { safe_ids = Object.create(safe_ids); } function pop() { safe_ids = Object.getPrototypeOf(safe_ids); } function reset_def(def) { 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; } def.references = []; def.should_replace = undefined; } function is_modified(node, level, func) { var parent = tw.parent(level); if (is_lhs(node, parent) || !func && parent instanceof AST_Call && parent.expression === node) { return true; } else if (parent instanceof AST_PropAccess && parent.expression === node) { return !func && is_modified(parent, level + 1); } } }); AST_SymbolRef.DEFMETHOD("fixed_value", function() { var fixed = this.definition().fixed; if (!fixed || fixed instanceof AST_Node) return fixed; 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++)) { if (scope instanceof AST_Scope) break; if (scope instanceof AST_Catch) { scope = scope.argname.definition().scope; break; } } return scope.find_variable(name); } function make_node(ctor, orig, props) { if (!props) props = {}; if (orig) { if (!props.start) props.start = orig.start; if (!props.end) props.end = orig.end; } return new ctor(props); }; function make_node_from_constant(val, orig) { switch (typeof val) { case "string": return make_node(AST_String, orig, { value: val }); case "number": if (isNaN(val)) return make_node(AST_NaN, orig); if (isFinite(val)) { return 1 / val < 0 ? make_node(AST_UnaryPrefix, orig, { operator: "-", expression: make_node(AST_Number, orig, { value: -val }) }) : make_node(AST_Number, orig, { value: val }); } return val < 0 ? make_node(AST_UnaryPrefix, orig, { operator: "-", expression: make_node(AST_Infinity, orig) }) : make_node(AST_Infinity, orig); case "boolean": return make_node(val ? AST_True : AST_False, orig); case "undefined": return make_node(AST_Undefined, orig); default: if (val === null) { return make_node(AST_Null, orig, { value: null }); } if (val instanceof RegExp) { return make_node(AST_RegExp, orig, { value: val }); } throw new Error(string_template("Can't handle constant of type: {type}", { type: typeof val })); } }; // we shouldn't compress (1,func)(something) to // func(something) because that changes the meaning of // the func (becomes lexical instead of global). function maintain_this_binding(parent, orig, val) { if (parent instanceof AST_UnaryPrefix && parent.operator == "delete" || parent instanceof AST_Call && parent.expression === orig && (val instanceof AST_PropAccess || val instanceof AST_SymbolRef && val.name == "eval")) { return make_node(AST_Seq, orig, { car: make_node(AST_Number, orig, { value: 0 }), cdr: val }); } return val; } function as_statement_array(thing) { if (thing === null) return []; if (thing instanceof AST_BlockStatement) return thing.body; 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_Switch) return x; if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) { return (x.body instanceof AST_BlockStatement ? x.body : x); } return x; }; function is_iife_call(node) { if (node instanceof AST_Call && !(node instanceof AST_New)) { return node.expression instanceof AST_Function || is_iife_call(node.expression); } return false; } function tighten_body(statements, compressor) { var CHANGED, max_iter = 10; do { CHANGED = false; if (compressor.option("angular")) { statements = process_for_angular(statements); } statements = eliminate_spurious_blocks(statements); if (compressor.option("dead_code")) { statements = eliminate_dead_code(statements, compressor); } if (compressor.option("if_return")) { statements = handle_if_return(statements, compressor); } if (compressor.sequences_limit > 0) { statements = sequencesize(statements, compressor); } if (compressor.option("join_vars")) { statements = join_consecutive_vars(statements, compressor); } if (compressor.option("collapse_vars")) { statements = collapse_single_use_vars(statements, compressor); } } while (CHANGED && max_iter-- > 0); return statements; function collapse_single_use_vars(statements, compressor) { // Iterate statements backwards looking for a statement with a var/const // declaration immediately preceding it. Grab the rightmost var definition // and if it has exactly one reference then attempt to replace its reference // in the statement with the var value and then erase the var definition. var self = compressor.self(); var var_defs_removed = false; var toplevel = compressor.option("toplevel"); for (var stat_index = statements.length; --stat_index >= 0;) { var stat = statements[stat_index]; if (stat instanceof AST_Definitions) continue; // Process child blocks of statement if present. [stat, stat.body, stat.alternative, stat.bcatch, stat.bfinally].forEach(function(node) { node && node.body && collapse_single_use_vars(node.body, compressor); }); // The variable definition must precede a statement. if (stat_index <= 0) break; var prev_stat_index = stat_index - 1; var prev_stat = statements[prev_stat_index]; if (!(prev_stat instanceof AST_Definitions)) continue; var var_defs = prev_stat.definitions; if (var_defs == null) continue; var var_names_seen = {}; var side_effects_encountered = false; var lvalues_encountered = false; var lvalues = {}; // Scan variable definitions from right to left. for (var var_defs_index = var_defs.length; --var_defs_index >= 0;) { // Obtain var declaration and var name with basic sanity check. var var_decl = var_defs[var_defs_index]; if (var_decl.value == null) break; var var_name = var_decl.name.name; if (!var_name || !var_name.length) break; // Bail if we've seen a var definition of same name before. if (var_name in var_names_seen) break; var_names_seen[var_name] = true; // Only interested in cases with just one reference to the variable. var def = self.find_variable && self.find_variable(var_name); if (!def || !def.references || def.references.length !== 1 || var_name == "arguments" || (!toplevel && def.global)) { side_effects_encountered = true; continue; } var ref = def.references[0]; // Don't replace ref if eval() or with statement in scope. if (ref.scope.uses_eval || ref.scope.uses_with) break; // Constant single use vars can be replaced in any scope. if (var_decl.value.is_constant()) { var ctt = new TreeTransformer(function(node) { var parent = ctt.parent(); if (parent instanceof AST_IterationStatement && (parent.condition === node || parent.init === node)) { return node; } if (node === ref) return replace_var(node, parent, true); }); stat.transform(ctt); continue; } // Restrict var replacement to constants if side effects encountered. if (side_effects_encountered |= lvalues_encountered) continue; var value_has_side_effects = var_decl.value.has_side_effects(compressor); // Non-constant single use vars can only be replaced in same scope. if (ref.scope !== self) { side_effects_encountered |= value_has_side_effects; continue; } // Detect lvalues in var value. var tw = new TreeWalker(function(node){ if (node instanceof AST_SymbolRef && is_lvalue(node, tw.parent())) { lvalues[node.name] = lvalues_encountered = true; } }); var_decl.value.walk(tw); // Replace the non-constant single use var in statement if side effect free. var unwind = false; var tt = new TreeTransformer( function preorder(node) { if (unwind) return node; var parent = tt.parent(); if (node instanceof AST_Lambda || node instanceof AST_Try || node instanceof AST_With || node instanceof AST_Case || node instanceof AST_IterationStatement || (parent instanceof AST_If && node !== parent.condition) || (parent instanceof AST_Conditional && node !== parent.condition) || (node instanceof AST_SymbolRef && value_has_side_effects && !are_references_in_scope(node.definition(), self)) || (parent instanceof AST_Binary && (parent.operator == "&&" || parent.operator == "||") && node === parent.right) || (parent instanceof AST_Switch && node !== parent.expression)) { return side_effects_encountered = unwind = true, node; } function are_references_in_scope(def, scope) { if (def.orig.length === 1 && def.orig[0] instanceof AST_SymbolDefun) return true; if (def.scope !== scope) return false; var refs = def.references; for (var i = 0, len = refs.length; i < len; i++) { if (refs[i].scope !== scope) return false; } return true; } }, function postorder(node) { if (unwind) return node; if (node === ref) return unwind = true, replace_var(node, tt.parent(), false); if (side_effects_encountered |= node.has_side_effects(compressor)) return unwind = true, node; if (lvalues_encountered && node instanceof AST_SymbolRef && node.name in lvalues) { side_effects_encountered = true; return unwind = true, node; } } ); stat.transform(tt); } } // Remove extraneous empty statments in block after removing var definitions. // Leave at least one statement in `statements`. if (var_defs_removed) for (var i = statements.length; --i >= 0;) { if (statements.length > 1 && statements[i] instanceof AST_EmptyStatement) statements.splice(i, 1); } return statements; function is_lvalue(node, parent) { return node instanceof AST_SymbolRef && is_lhs(node, parent); } function replace_var(node, parent, is_constant) { if (is_lvalue(node, parent)) return node; // Remove var definition and return its value to the TreeTransformer to replace. var value = maintain_this_binding(parent, node, var_decl.value); var_decl.value = null; var_defs.splice(var_defs_index, 1); if (var_defs.length === 0) { statements[prev_stat_index] = make_node(AST_EmptyStatement, self); var_defs_removed = true; } // Further optimize statement after substitution. stat.reset_opt_flags(compressor); compressor.info("Collapsing " + (is_constant ? "constant" : "variable") + " " + var_name + " [{file}:{line},{col}]", node.start); CHANGED = true; return value; } } function process_for_angular(statements) { function has_inject(comment) { return /@ngInject/.test(comment.value); } function make_arguments_names_list(func) { return func.argnames.map(function(sym){ return make_node(AST_String, sym, { value: sym.name }); }); } function make_array(orig, elements) { return make_node(AST_Array, orig, { elements: elements }); } function make_injector(func, name) { return make_node(AST_SimpleStatement, func, { body: make_node(AST_Assign, func, { operator: "=", left: make_node(AST_Dot, name, { expression: make_node(AST_SymbolRef, name, name), property: "$inject" }), right: make_array(func, make_arguments_names_list(func)) }) }); } function check_expression(body) { if (body && body.args) { // if this is a function call check all of arguments passed body.args.forEach(function(argument, index, array) { var comments = argument.start.comments_before; // if the argument is function preceded by @ngInject if (argument instanceof AST_Lambda && comments.length && has_inject(comments[0])) { // replace the function with an array of names of its parameters and function at the end array[index] = make_array(argument, make_arguments_names_list(argument).concat(argument)); } }); // if this is chained call check previous one recursively if (body.expression && body.expression.expression) { check_expression(body.expression.expression); } } } return statements.reduce(function(a, stat){ a.push(stat); if (stat.body && stat.body.args) { check_expression(stat.body); } else { var token = stat.start; var comments = token.comments_before; if (comments && comments.length > 0) { var last = comments.pop(); if (has_inject(last)) { // case 1: defun if (stat instanceof AST_Defun) { a.push(make_injector(stat, stat.name)); } else if (stat instanceof AST_Definitions) { stat.definitions.forEach(function(def) { if (def.value && def.value instanceof AST_Lambda) { a.push(make_injector(def.value, def.name)); } }); } else { compressor.warn("Unknown statement marked with @ngInject [{file}:{line},{col}]", token); } } } } return a; }, []); } function eliminate_spurious_blocks(statements) { var seen_dirs = []; return statements.reduce(function(a, stat){ if (stat instanceof AST_BlockStatement) { CHANGED = true; a.push.apply(a, eliminate_spurious_blocks(stat.body)); } else if (stat instanceof AST_EmptyStatement) { CHANGED = true; } else if (stat instanceof AST_Directive) { if (seen_dirs.indexOf(stat.value) < 0) { a.push(stat); seen_dirs.push(stat.value); } else { CHANGED = true; } } else { a.push(stat); } return a; }, []); }; function handle_if_return(statements, compressor) { var self = compressor.self(); var multiple_if_returns = has_multiple_if_returns(statements); var in_lambda = self instanceof AST_Lambda; var ret = []; // Optimized statements, build from tail to front loop: for (var i = statements.length; --i >= 0;) { var stat = statements[i]; switch (true) { case (in_lambda && stat instanceof AST_Return && !stat.value && ret.length == 0): CHANGED = true; // note, ret.length is probably always zero // because we drop unreachable code before this // step. nevertheless, it's good to check. continue loop; case stat instanceof AST_If: if (stat.body instanceof AST_Return) { //--- // pretty silly case, but: // if (foo()) return; return; ==> foo(); return; if (((in_lambda && ret.length == 0) || (ret[0] instanceof AST_Return && !ret[0].value)) && !stat.body.value && !stat.alternative) { CHANGED = true; var cond = make_node(AST_SimpleStatement, stat.condition, { body: stat.condition }); ret.unshift(cond); continue loop; } //--- // if (foo()) return x; return y; ==> return foo() ? x : y; if (ret[0] instanceof AST_Return && stat.body.value && ret[0].value && !stat.alternative) { CHANGED = true; stat = stat.clone(); stat.alternative = ret[0]; ret[0] = stat.transform(compressor); continue loop; } //--- // if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined; if (multiple_if_returns && (ret.length == 0 || ret[0] instanceof AST_Return) && stat.body.value && !stat.alternative && in_lambda) { CHANGED = true; stat = stat.clone(); stat.alternative = ret[0] || make_node(AST_Return, stat, { value: null }); ret[0] = stat.transform(compressor); continue loop; } //--- // if (foo()) return; [ else x... ]; y... ==> if (!foo()) { x...; y... } if (!stat.body.value && in_lambda) { CHANGED = true; stat = stat.clone(); stat.condition = stat.condition.negate(compressor); var body = as_statement_array(stat.alternative).concat(ret); var funs = extract_functions_from_statement_array(body); stat.body = make_node(AST_BlockStatement, stat, { body: body }); stat.alternative = null; ret = funs.concat([ stat.transform(compressor) ]); continue loop; } //--- // if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e; // // if sequences is not enabled, this can lead to an endless loop (issue #866). // however, with sequences on this helps producing slightly better output for // the example code. if (compressor.option("sequences") && i > 0 && statements[i - 1] instanceof AST_If && statements[i - 1].body instanceof AST_Return && ret.length == 1 && in_lambda && ret[0] instanceof AST_SimpleStatement && !stat.alternative) { CHANGED = true; ret.push(make_node(AST_Return, ret[0], { value: null }).transform(compressor)); ret.unshift(stat); continue loop; } } var ab = aborts(stat.body); var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null; if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda) || (ab instanceof AST_Continue && self === loop_body(lct)) || (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) { if (ab.label) { remove(ab.label.thedef.references, ab); } CHANGED = true; var body = as_statement_array(stat.body).slice(0, -1); stat = stat.clone(); stat.condition = stat.condition.negate(compressor); stat.body = make_node(AST_BlockStatement, stat, { body: as_statement_array(stat.alternative).concat(ret) }); stat.alternative = make_node(AST_BlockStatement, stat, { body: body }); ret = [ stat.transform(compressor) ]; continue loop; } var ab = aborts(stat.alternative); var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null; if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda) || (ab instanceof AST_Continue && self === loop_body(lct)) || (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) { if (ab.label) { remove(ab.label.thedef.references, ab); } CHANGED = true; stat = stat.clone(); stat.body = make_node(AST_BlockStatement, stat.body, { body: as_statement_array(stat.body).concat(ret) }); stat.alternative = make_node(AST_BlockStatement, stat.alternative, { body: as_statement_array(stat.alternative).slice(0, -1) }); ret = [ stat.transform(compressor) ]; continue loop; } ret.unshift(stat); break; default: ret.unshift(stat); break; } } return ret; function has_multiple_if_returns(statements) { var n = 0; for (var i = statements.length; --i >= 0;) { var stat = statements[i]; if (stat instanceof AST_If && stat.body instanceof AST_Return) { if (++n > 1) return true; } } return false; } }; function eliminate_dead_code(statements, compressor) { var has_quit = false; var orig = statements.length; var self = compressor.self(); statements = statements.reduce(function(a, stat){ if (has_quit) { extract_declarations_from_unreachable_code(compressor, stat, a); } else { if (stat instanceof AST_LoopControl) { var lct = compressor.loopcontrol_target(stat); if ((stat instanceof AST_Break && !(lct instanceof AST_IterationStatement) && loop_body(lct) === self) || (stat instanceof AST_Continue && loop_body(lct) === self)) { if (stat.label) { remove(stat.label.thedef.references, stat); } } else { a.push(stat); } } else { a.push(stat); } if (aborts(stat)) has_quit = true; } return a; }, []); CHANGED = statements.length != orig; return statements; }; function sequencesize(statements, compressor) { if (statements.length < 2) return statements; var seq = [], ret = []; function push_seq() { seq = AST_Seq.from_array(seq); if (seq) ret.push(make_node(AST_SimpleStatement, seq, { body: seq })); seq = []; }; statements.forEach(function(stat){ if (stat instanceof AST_SimpleStatement) { if (seqLength(seq) >= compressor.sequences_limit) push_seq(); var body = stat.body; if (seq.length > 0) body = body.drop_side_effect_free(compressor); if (body) seq.push(body); } else { push_seq(); ret.push(stat); } }); push_seq(); ret = sequencesize_2(ret, compressor); CHANGED = ret.length != statements.length; return ret; }; function seqLength(a) { for (var len = 0, i = 0; i < a.length; ++i) { var stat = a[i]; if (stat instanceof AST_Seq) { len += stat.len(); } else { len++; } } return len; }; function sequencesize_2(statements, compressor) { function cons_seq(right) { ret.pop(); var left = prev.body; if (left instanceof AST_Seq) { left.add(right); } else { left = AST_Seq.cons(left, right); } return left.transform(compressor); }; var ret = [], prev = null; statements.forEach(function(stat){ if (prev) { if (stat instanceof AST_For) { var opera = {}; try { prev.body.walk(new TreeWalker(function(node){ if (node instanceof AST_Binary && node.operator == "in") throw opera; })); if (stat.init && !(stat.init instanceof AST_Definitions)) { stat.init = cons_seq(stat.init); } else if (!stat.init) { stat.init = prev.body.drop_side_effect_free(compressor); ret.pop(); } } catch(ex) { if (ex !== opera) throw ex; } } else if (stat instanceof AST_If) { stat.condition = cons_seq(stat.condition); } else if (stat instanceof AST_With) { stat.expression = cons_seq(stat.expression); } else if (stat instanceof AST_Exit && stat.value) { stat.value = cons_seq(stat.value); } else if (stat instanceof AST_Exit) { stat.value = cons_seq(make_node(AST_Undefined, stat).transform(compressor)); } else if (stat instanceof AST_Switch) { stat.expression = cons_seq(