UNPKG

vue-simple-range-slider

Version:

Change Your numeric value or numeric range value with dragging handles

1,319 lines (1,265 loc) 157 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. ***********************************************************************/ import { AST_Accessor, AST_Array, AST_Arrow, AST_Assign, AST_BigInt, AST_Binary, AST_Block, AST_BlockStatement, AST_Boolean, AST_Break, AST_Call, AST_Catch, AST_Chain, AST_Class, AST_ClassExpression, AST_ClassProperty, AST_ConciseMethod, AST_Conditional, AST_Const, AST_Constant, AST_Debugger, AST_Default, AST_DefaultAssign, AST_DefClass, AST_Definitions, AST_Defun, AST_Destructuring, AST_Directive, AST_Do, AST_Dot, AST_DWLoop, AST_EmptyStatement, AST_Exit, AST_Expansion, AST_Export, AST_False, AST_For, AST_ForIn, AST_Function, AST_Hole, AST_If, AST_Import, AST_Infinity, AST_LabeledStatement, AST_Lambda, AST_Let, AST_NaN, AST_New, AST_Node, AST_Null, AST_Number, AST_Object, AST_ObjectKeyVal, AST_ObjectProperty, AST_PrefixedTemplateString, AST_PropAccess, AST_RegExp, AST_Return, AST_Scope, AST_Sequence, AST_SimpleStatement, AST_Statement, AST_String, AST_Sub, AST_Switch, AST_SwitchBranch, AST_Symbol, AST_SymbolBlockDeclaration, AST_SymbolCatch, AST_SymbolClassProperty, AST_SymbolDeclaration, AST_SymbolDefun, AST_SymbolExport, AST_SymbolFunarg, AST_SymbolLambda, AST_SymbolLet, AST_SymbolMethod, AST_SymbolRef, AST_SymbolVar, AST_TemplateString, AST_This, AST_Toplevel, AST_True, AST_Try, AST_Unary, AST_UnaryPostfix, AST_UnaryPrefix, AST_Undefined, AST_Var, AST_VarDef, AST_While, AST_With, AST_Yield, TreeTransformer, TreeWalker, walk, walk_abort, _INLINE, _NOINLINE, _PURE } from "../ast.js"; import { defaults, HOP, keep_name, make_node, makePredicate, map_add, MAP, remove, return_false, return_true, regexp_source_fix, has_annotation, regexp_is_safe, } from "../utils/index.js"; import { first_in_statement } from "../utils/first_in_statement.js"; import { equivalent_to } from "../equivalent-to.js"; import { is_basic_identifier_string, JS_Parse_Error, parse, PRECEDENCE, } from "../parse.js"; import { OutputStream } from "../output.js"; import { base54, SymbolDef, } from "../scope.js"; import "../size.js"; import "./evaluate.js"; import "./drop-side-effect-free.js"; import "./reduce-vars.js"; import { is_undeclared_ref, lazy_op, is_nullish, is_undefined, is_lhs, aborts, } from "./inference.js"; import { SQUEEZED, OPTIMIZED, CLEAR_BETWEEN_PASSES, TOP, WRITE_ONLY, UNDEFINED, UNUSED, TRUTHY, FALSY, has_flag, set_flag, clear_flag, } from "./compressor-flags.js"; import { make_sequence, best_of, best_of_expression, make_node_from_constant, merge_sequence, get_simple_key, has_break_or_continue, maintain_this_binding, is_empty, is_identifier_atom, is_reachable, is_ref_of, can_be_evicted_from_block, as_statement_array, retain_top_func, is_func_expr, } from "./common.js"; import { tighten_body, trim_unreachable_code } from "./tighten-body.js"; import { inline_into_symbolref, inline_into_call } from "./inline.js"; class Compressor extends TreeWalker { constructor(options, { false_by_default = false, mangle_options = false }) { super(); if (options.defaults !== undefined && !options.defaults) false_by_default = true; this.options = defaults(options, { arguments : false, arrows : !false_by_default, booleans : !false_by_default, booleans_as_integers : false, collapse_vars : !false_by_default, comparisons : !false_by_default, computed_props: !false_by_default, conditionals : !false_by_default, dead_code : !false_by_default, defaults : true, directives : !false_by_default, drop_console : false, drop_debugger : !false_by_default, ecma : 5, evaluate : !false_by_default, expression : false, global_defs : false, hoist_funs : false, hoist_props : !false_by_default, hoist_vars : false, ie8 : false, if_return : !false_by_default, inline : !false_by_default, join_vars : !false_by_default, keep_classnames: false, keep_fargs : true, keep_fnames : false, keep_infinity : false, loops : !false_by_default, module : false, negate_iife : !false_by_default, passes : 1, properties : !false_by_default, pure_getters : !false_by_default && "strict", pure_funcs : null, reduce_funcs : !false_by_default, reduce_vars : !false_by_default, sequences : !false_by_default, side_effects : !false_by_default, switches : !false_by_default, top_retain : null, toplevel : !!(options && options["top_retain"]), typeofs : !false_by_default, unsafe : false, unsafe_arrows : false, unsafe_comps : false, unsafe_Function: false, unsafe_math : false, unsafe_symbols: false, unsafe_methods: false, unsafe_proto : false, unsafe_regexp : false, unsafe_undefined: false, unused : !false_by_default, warnings : false // legacy }, true); var global_defs = this.options["global_defs"]; if (typeof global_defs == "object") for (var key in global_defs) { if (key[0] === "@" && HOP(global_defs, key)) { global_defs[key.slice(1)] = parse(global_defs[key], { expression: true }); } } if (this.options["inline"] === true) this.options["inline"] = 3; 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.includes(node.expression.print_to_string()); } : 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.includes(def.name); }; } if (this.options["module"]) { this.directives["use strict"] = true; this.options["toplevel"] = true; } var toplevel = this.options["toplevel"]; this.toplevel = typeof toplevel == "string" ? { funcs: /funcs/.test(toplevel), vars: /vars/.test(toplevel) } : { funcs: toplevel, vars: toplevel }; var sequences = this.options["sequences"]; this.sequences_limit = sequences == 1 ? 800 : sequences | 0; this.evaluated_regexps = new Map(); this._toplevel = undefined; this.mangle_options = mangle_options; } option(key) { return this.options[key]; } exposed(def) { if (def.export) return true; if (def.global) for (var i = 0, len = def.orig.length; i < len; i++) if (!this.toplevel[def.orig[i] instanceof AST_SymbolDefun ? "funcs" : "vars"]) return true; return false; } in_boolean_context() { if (!this.option("booleans")) return false; var self = this.self(); for (var i = 0, p; p = this.parent(i); i++) { if (p instanceof AST_SimpleStatement || p instanceof AST_Conditional && p.condition === self || p instanceof AST_DWLoop && p.condition === self || p instanceof AST_For && p.condition === self || p instanceof AST_If && p.condition === self || p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self) { return true; } if ( p instanceof AST_Binary && ( p.operator == "&&" || p.operator == "||" || p.operator == "??" ) || p instanceof AST_Conditional || p.tail_node() === self ) { self = p; } else { return false; } } } get_toplevel() { return this._toplevel; } compress(toplevel) { toplevel = toplevel.resolve_defines(this); this._toplevel = toplevel; if (this.option("expression")) { this._toplevel.process_expression(true); } var passes = +this.options.passes || 1; var min_count = 1 / 0; var stopping = false; var nth_identifier = this.mangle_options && this.mangle_options.nth_identifier || base54; var mangle = { ie8: this.option("ie8"), nth_identifier: nth_identifier }; for (var pass = 0; pass < passes; pass++) { this._toplevel.figure_out_scope(mangle); if (pass === 0 && this.option("drop_console")) { // must be run before reduce_vars and compress pass this._toplevel = this._toplevel.drop_console(); } if (pass > 0 || this.option("reduce_vars")) { this._toplevel.reset_opt_flags(this); } this._toplevel = this._toplevel.transform(this); if (passes > 1) { let count = 0; walk(this._toplevel, () => { count++; }); if (count < min_count) { min_count = count; stopping = false; } else if (stopping) { break; } else { stopping = true; } } } if (this.option("expression")) { this._toplevel.process_expression(false); } toplevel = this._toplevel; this._toplevel = undefined; return toplevel; } before(node, descend) { if (has_flag(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; } // 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 def_optimize(). // 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) set_flag(opt, SQUEEZED); return opt; } } function def_optimize(node, optimizer) { node.DEFMETHOD("optimize", function(compressor) { var self = this; if (has_flag(self, OPTIMIZED)) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); set_flag(opt, OPTIMIZED); return opt; }); } def_optimize(AST_Node, function(self) { return self; }); AST_Toplevel.DEFMETHOD("drop_console", function() { return this.transform(new TreeTransformer(function(self) { if (self.TYPE == "Call") { var exp = self.expression; if (exp instanceof AST_PropAccess) { var name = exp.expression; while (name.expression) { name = name.expression; } if (is_undeclared_ref(name) && name.name == "console") { return make_node(AST_Undefined, self); } } } })); }); AST_Node.DEFMETHOD("equivalent_to", function(node) { return equivalent_to(this, node); }); AST_Scope.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_Class || 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); } } else if (node instanceof AST_If) { node.body = node.body.transform(tt); if (node.alternative) { node.alternative = node.alternative.transform(tt); } } else if (node instanceof AST_With) { node.body = node.body.transform(tt); } return node; }); self.transform(tt); }); AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) { const self = this; const reduce_vars = compressor.option("reduce_vars"); const preparation = new TreeWalker(function(node, descend) { clear_flag(node, CLEAR_BETWEEN_PASSES); if (reduce_vars) { if (compressor.top_retain && node instanceof AST_Defun // Only functions are retained && preparation.parent() === self ) { set_flag(node, TOP); } return node.reduce_vars(preparation, descend, compressor); } }); // Stack of look-up tables to keep track of whether a `SymbolDef` has been // properly assigned before use: // - `push()` & `pop()` when visiting conditional branches preparation.safe_ids = Object.create(null); preparation.in_loop = null; preparation.loop_ids = new Map(); preparation.defs_to_safe_ids = new Map(); self.walk(preparation); }); AST_Symbol.DEFMETHOD("fixed_value", function() { var fixed = this.thedef.fixed; if (!fixed || fixed instanceof AST_Node) return fixed; return fixed(); }); AST_SymbolRef.DEFMETHOD("is_immutable", function() { var orig = this.definition().orig; return orig.length == 1 && orig[0] instanceof AST_SymbolLambda; }); 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.argname) { scope = scope.argname.definition().scope; break; } } return scope.find_variable(name); } var global_names = makePredicate("Array Boolean clearInterval clearTimeout console Date decodeURI decodeURIComponent encodeURI encodeURIComponent Error escape eval EvalError Function isFinite isNaN JSON Math Number parseFloat parseInt RangeError ReferenceError RegExp Object setInterval setTimeout String SyntaxError TypeError unescape URIError"); AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) { return !this.definition().undeclared || compressor.option("unsafe") && global_names.has(this.name); }); /* -----[ optimizers ]----- */ var directives = new Set(["use asm", "use strict"]); def_optimize(AST_Directive, function(self, compressor) { if (compressor.option("directives") && (!directives.has(self.value) || compressor.has_directive(self.value) !== self)) { return make_node(AST_EmptyStatement, self); } return self; }); def_optimize(AST_Debugger, function(self, compressor) { if (compressor.option("drop_debugger")) return make_node(AST_EmptyStatement, self); return self; }); def_optimize(AST_LabeledStatement, function(self, compressor) { if (self.body instanceof AST_Break && compressor.loopcontrol_target(self.body) === self.body) { return make_node(AST_EmptyStatement, self); } return self.label.references.length == 0 ? self.body : self; }); def_optimize(AST_Block, function(self, compressor) { tighten_body(self.body, compressor); return self; }); function can_be_extracted_from_if_block(node) { return !( node instanceof AST_Const || node instanceof AST_Let || node instanceof AST_Class ); } def_optimize(AST_BlockStatement, function(self, compressor) { tighten_body(self.body, compressor); switch (self.body.length) { case 1: if (!compressor.has_directive("use strict") && compressor.parent() instanceof AST_If && can_be_extracted_from_if_block(self.body[0]) || can_be_evicted_from_block(self.body[0])) { return self.body[0]; } break; case 0: return make_node(AST_EmptyStatement, self); } return self; }); function opt_AST_Lambda(self, compressor) { tighten_body(self.body, compressor); if (compressor.option("side_effects") && self.body.length == 1 && self.body[0] === compressor.has_directive("use strict")) { self.body.length = 0; } return self; } def_optimize(AST_Lambda, opt_AST_Lambda); const r_keep_assign = /keep_assign/; AST_Scope.DEFMETHOD("drop_unused", function(compressor) { if (!compressor.option("unused")) return; if (compressor.has_directive("use asm")) return; var self = this; if (self.pinned()) return; var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs; var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars; const assign_as_unused = r_keep_assign.test(compressor.option("unused")) ? return_false : function(node) { if (node instanceof AST_Assign && !node.logical && (has_flag(node, WRITE_ONLY) || node.operator == "=") ) { return node.left; } if (node instanceof AST_Unary && has_flag(node, WRITE_ONLY)) { return node.expression; } }; var in_use_ids = new Map(); var fixed_ids = new Map(); if (self instanceof AST_Toplevel && compressor.top_retain) { self.variables.forEach(function(def) { if (compressor.top_retain(def) && !in_use_ids.has(def.id)) { in_use_ids.set(def.id, def); } }); } var var_defs_by_id = new Map(); var initializations = new Map(); // 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) { if (node instanceof AST_Lambda && node.uses_arguments && !tw.has_directive("use strict")) { node.argnames.forEach(function(argname) { if (!(argname instanceof AST_SymbolDeclaration)) return; var def = argname.definition(); if (!in_use_ids.has(def.id)) { in_use_ids.set(def.id, def); } }); } if (node === self) return; if (node instanceof AST_Defun || node instanceof AST_DefClass) { var node_def = node.name.definition(); const in_export = tw.parent() instanceof AST_Export; if (in_export || !drop_funcs && scope === self) { if (node_def.global && !in_use_ids.has(node_def.id)) { in_use_ids.set(node_def.id, node_def); } } if (node instanceof AST_DefClass) { if ( node.extends && (node.extends.has_side_effects(compressor) || node.extends.may_throw(compressor)) ) { node.extends.walk(tw); } for (const prop of node.properties) { if ( prop.has_side_effects(compressor) || prop.may_throw(compressor) ) { prop.walk(tw); } } } map_add(initializations, node_def.id, node); return true; // don't go in nested scopes } if (node instanceof AST_SymbolFunarg && scope === self) { map_add(var_defs_by_id, node.definition().id, node); } if (node instanceof AST_Definitions && scope === self) { const in_export = tw.parent() instanceof AST_Export; node.definitions.forEach(function(def) { if (def.name instanceof AST_SymbolVar) { map_add(var_defs_by_id, def.name.definition().id, def); } if (in_export || !drop_vars) { walk(def.name, node => { if (node instanceof AST_SymbolDeclaration) { const def = node.definition(); if ( (in_export || def.global) && !in_use_ids.has(def.id) ) { in_use_ids.set(def.id, def); } } }); } if (def.value) { if (def.name instanceof AST_Destructuring) { def.walk(tw); } else { var node_def = def.name.definition(); map_add(initializations, node_def.id, def.value); if (!node_def.chained && def.name.fixed_value() === def.value) { fixed_ids.set(node_def.id, def); } } if (def.value.has_side_effects(compressor)) { def.value.walk(tw); } } }); return true; } return scan_ref_scoped(node, descend); }); self.walk(tw); // pass 2: for every used symbol we need to walk its // initialization code to figure out if it uses other // symbols (that may not be in_use). tw = new TreeWalker(scan_ref_scoped); in_use_ids.forEach(function (def) { var init = initializations.get(def.id); if (init) init.forEach(function(init) { init.walk(tw); }); }); // pass 3: we should drop declarations not in_use var tt = new TreeTransformer( function before(node, descend, in_list) { var parent = tt.parent(); if (drop_vars) { const sym = assign_as_unused(node); if (sym instanceof AST_SymbolRef) { var def = sym.definition(); var in_use = in_use_ids.has(def.id); if (node instanceof AST_Assign) { if (!in_use || fixed_ids.has(def.id) && fixed_ids.get(def.id) !== node) { return maintain_this_binding(parent, node, node.right.transform(tt)); } } else if (!in_use) return in_list ? MAP.skip : make_node(AST_Number, node, { value: 0 }); } } if (scope !== self) return; var def; if (node.name && (node instanceof AST_ClassExpression && !keep_name(compressor.option("keep_classnames"), (def = node.name.definition()).name) || node instanceof AST_Function && !keep_name(compressor.option("keep_fnames"), (def = node.name.definition()).name))) { // any declarations with same name will overshadow // name of this anonymous function and can therefore // never be used anywhere if (!in_use_ids.has(def.id) || def.orig.length > 1) node.name = null; } if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) { var trim = !compressor.option("keep_fargs"); for (var a = node.argnames, i = a.length; --i >= 0;) { var sym = a[i]; if (sym instanceof AST_Expansion) { sym = sym.expression; } if (sym instanceof AST_DefaultAssign) { sym = sym.left; } // Do not drop destructuring arguments. // They constitute a type assertion, so dropping // them would stop that TypeError which would happen // if someone called it with an incorrectly formatted // parameter. if (!(sym instanceof AST_Destructuring) && !in_use_ids.has(sym.definition().id)) { set_flag(sym, UNUSED); if (trim) { a.pop(); } } else { trim = false; } } } if ((node instanceof AST_Defun || node instanceof AST_DefClass) && node !== self) { const def = node.name.definition(); let keep = def.global && !drop_funcs || in_use_ids.has(def.id); if (!keep) { def.eliminated++; if (node instanceof AST_DefClass) { // Classes might have extends with side effects const side_effects = node.drop_side_effect_free(compressor); if (side_effects) { return make_node(AST_SimpleStatement, node, { body: side_effects }); } } return in_list ? MAP.skip : make_node(AST_EmptyStatement, node); } } if (node instanceof AST_Definitions && !(parent instanceof AST_ForIn && parent.init === node)) { var drop_block = !(parent instanceof AST_Toplevel) && !(node instanceof AST_Var); // place uninitialized names at the start var body = [], head = [], tail = []; // for unused names whose initialization has // side effects, we can cascade the init. code // into the next one, or next statement. var side_effects = []; node.definitions.forEach(function(def) { if (def.value) def.value = def.value.transform(tt); var is_destructure = def.name instanceof AST_Destructuring; var sym = is_destructure ? new SymbolDef(null, { name: "<destructure>" }) /* fake SymbolDef */ : def.name.definition(); if (drop_block && sym.global) return tail.push(def); if (!(drop_vars || drop_block) || is_destructure && (def.name.names.length || def.name.is_array || compressor.option("pure_getters") != true) || in_use_ids.has(sym.id) ) { if (def.value && fixed_ids.has(sym.id) && fixed_ids.get(sym.id) !== def) { def.value = def.value.drop_side_effect_free(compressor); } if (def.name instanceof AST_SymbolVar) { var var_defs = var_defs_by_id.get(sym.id); if (var_defs.length > 1 && (!def.value || sym.orig.indexOf(def.name) > sym.eliminated)) { if (def.value) { var ref = make_node(AST_SymbolRef, def.name, def.name); sym.references.push(ref); var assign = make_node(AST_Assign, def, { operator: "=", logical: false, left: ref, right: def.value }); if (fixed_ids.get(sym.id) === def) { fixed_ids.set(sym.id, assign); } side_effects.push(assign.transform(tt)); } remove(var_defs, def); sym.eliminated++; return; } } if (def.value) { if (side_effects.length > 0) { if (tail.length > 0) { side_effects.push(def.value); def.value = make_sequence(def.value, side_effects); } else { body.push(make_node(AST_SimpleStatement, node, { body: make_sequence(node, side_effects) })); } side_effects = []; } tail.push(def); } else { head.push(def); } } else if (sym.orig[0] instanceof AST_SymbolCatch) { var value = def.value && def.value.drop_side_effect_free(compressor); if (value) side_effects.push(value); def.value = null; head.push(def); } else { var value = def.value && def.value.drop_side_effect_free(compressor); if (value) { side_effects.push(value); } sym.eliminated++; } }); if (head.length > 0 || tail.length > 0) { node.definitions = head.concat(tail); body.push(node); } if (side_effects.length > 0) { body.push(make_node(AST_SimpleStatement, node, { body: make_sequence(node, side_effects) })); } switch (body.length) { case 0: return in_list ? MAP.skip : make_node(AST_EmptyStatement, node); case 1: return body[0]; default: return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, { body: body }); } } // certain combination of unused name + side effect leads to: // https://github.com/mishoo/UglifyJS2/issues/44 // https://github.com/mishoo/UglifyJS2/issues/1830 // https://github.com/mishoo/UglifyJS2/issues/1838 // 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); var block; if (node.init instanceof AST_BlockStatement) { block = node.init; node.init = block.body.pop(); block.body.push(node); } if (node.init instanceof AST_SimpleStatement) { node.init = node.init.body; } else if (is_empty(node.init)) { node.init = null; } return !block ? node : in_list ? MAP.splice(block.body) : block; } 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_BlockStatement) { descend(node, this); if (in_list && node.body.every(can_be_evicted_from_block)) { return MAP.splice(node.body); } return node; } if (node instanceof AST_Scope) { const save_scope = scope; scope = node; descend(node, this); scope = save_scope; return node; } } ); self.transform(tt); function scan_ref_scoped(node, descend) { var node_def; const sym = assign_as_unused(node); if (sym instanceof AST_SymbolRef && !is_ref_of(node.left, AST_SymbolBlockDeclaration) && self.variables.get(sym.name) === (node_def = sym.definition()) ) { if (node instanceof AST_Assign) { node.right.walk(tw); if (!node_def.chained && node.left.fixed_value() === node.right) { fixed_ids.set(node_def.id, node); } } return true; } if (node instanceof AST_SymbolRef) { node_def = node.definition(); if (!in_use_ids.has(node_def.id)) { in_use_ids.set(node_def.id, node_def); if (node_def.orig[0] instanceof AST_SymbolCatch) { const redef = node_def.scope.is_block_scope() && node_def.scope.get_defun_scope().variables.get(node_def.name); if (redef) in_use_ids.set(redef.id, redef); } } return true; } if (node instanceof AST_Scope) { var save_scope = scope; scope = node; descend(); scope = save_scope; return true; } } }); AST_Scope.DEFMETHOD("hoist_declarations", function(compressor) { var self = this; if (compressor.has_directive("use asm")) return self; // Hoisting makes no sense in an arrow func if (!Array.isArray(self.body)) return self; 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 Map(), vars_found = 0, var_decl = 0; // let's count var_decl first, we seem to waste a lot of // space if we hoist `var` when there's only one. walk(self, node => { if (node instanceof AST_Scope && node !== self) return true; if (node instanceof AST_Var) { ++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() instanceof AST_Export) && tt.parent() === self) { hoisted.push(node); return make_node(AST_EmptyStatement, node); } if ( hoist_vars && node instanceof AST_Var && !node.definitions.some(def => def.name instanceof AST_Destructuring) ) { 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 = []; const is_lambda = self instanceof AST_Lambda; const args_as_names = is_lambda ? self.args_as_names() : null; vars.forEach((def, name) => { if (is_lambda && args_as_names.some((x) => x.name === def.name.name)) { vars.delete(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 (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) { self.body.splice(i, 1); continue; } if (self.body[i] instanceof AST_BlockStatement) { self.body.splice(i, 1, ...self.body[i].body); continue; } break; } defs = make_node(AST_Var, self, { definitions: defs }); hoisted.push(defs); } } self.body = dirs.concat(hoisted, self.body); } return self; }); AST_Scope.DEFMETHOD("hoist_properties", function(compressor) { 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 = new Map(); var hoister = new TreeTransformer(function(node, descend) { if (node instanceof AST_Definitions && hoister.parent() instanceof AST_Export) return node; if (node instanceof AST_VarDef) { const sym = node.name; let def; let value; if (sym.scope === self && (def = sym.definition()).escaped != 1 && !def.assignments && !def.direct_access && !def.single_use && !compressor.exposed(def) && !top_retain(def) && (value = sym.fixed_value()) === node.value && value instanceof AST_Object && !value.properties.some(prop => prop instanceof AST_Expansion || prop.computed_key() ) ) { descend(node, this); const defs = new Map(); const assignments = []; value.properties.forEach(({ key, value }) => { const scope = hoister.find_scope(); const symbol = self.create_symbol(sym.CTOR, { source: sym, scope, conflict_scopes: new Set([ scope, ...sym.definition().references.map(ref => ref.scope) ]), tentative_name: sym.name + "_" + key }); defs.set(String(key), symbol.definition()); assignments.push(make_node(AST_VarDef, node, { name: symbol, value })); }); defs_by_id.set(def.id, defs); return MAP.splice(assignments); } } else if (node instanceof AST_PropAccess && node.expression instanceof AST_SymbolRef ) { const defs = defs_by_id.get(node.expression.definition().id); if (defs) { const def = defs.get(String(get_simple_key(node.property))); const sym = make_node(AST_SymbolRef, node, { name: def.name, scope: node.expression.scope, thedef: def }); sym.reference({}); return sym; } } }); return self.transform(hoister); }); def_optimize(AST_SimpleStatement, function(self, compressor) { if (compressor.option("side_effects")) { var body = self.body; var node = body.drop_side_effect_free(compressor, true); if (!node) { return make_node(AST_EmptyStatement, self); } if (node !== body) { return make_node(AST_SimpleStatement, self, { body: node }); } } return self; }); def_optimize(AST_While, function(self, compressor) { return compressor.option("loops") ? make_node(AST_For, self, self).optimize(compressor) : self; }); def_optimize(AST_Do, function(self, compressor) { if (!compressor.option("loops")) return self; var cond = self.condition.tail_node().evaluate(compressor); if (!(cond instanceof AST_Node)) { if (cond) return make_node(AST_For, self, { body: make_node(AST_BlockStatement, self.body, { body: [ self.body, make_node(AST_SimpleStatement, self.condition, { body: self.condition }) ] }) }).optimize(compressor); if (!has_break_or_continue(self, compressor.parent())) { return make_node(AST_BlockStatement, self.body, { body: [ self.body, make_node(AST_SimpleStatement, self.condition, { body: self.condition }) ] }).optimize(compressor); } } return self; }); function if_break_in_loop(self, compressor) { var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body; if (compressor.option("dead_code") && is_break(first)) { var body = []; if (self.init instanceof AST_Statement) { body.push(self.init); } else if (self.init) { body.push(make_node(AST_SimpleStatement, self.init, { body: self.init })); } if (self.condition) { body.push(make_node(AST_SimpleStatement, self.condition, { body: self.condition })); } trim_unreachable_code(compressor, self.body, body); return make_node(AST_BlockStatement, self, { body: body }); } if (first instanceof AST_If) { if (is_break(first.body)) { if (self.condition) { self.condition = make_node(AST_Binary, self.condition, { left: self.condition, operator: "&&", right: first.condition.negate(compressor), }); } else { self.condition = first.co