You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					483 lines
				
				20 KiB
			
		
		
			
		
	
	
					483 lines
				
				20 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								/***********************************************************************
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  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_Assign,
							 | 
						||
| 
								 | 
							
								    AST_BlockStatement,
							 | 
						||
| 
								 | 
							
								    AST_Class,
							 | 
						||
| 
								 | 
							
								    AST_ClassExpression,
							 | 
						||
| 
								 | 
							
								    AST_DefaultAssign,
							 | 
						||
| 
								 | 
							
								    AST_DefClass,
							 | 
						||
| 
								 | 
							
								    AST_Definitions,
							 | 
						||
| 
								 | 
							
								    AST_Defun,
							 | 
						||
| 
								 | 
							
								    AST_Destructuring,
							 | 
						||
| 
								 | 
							
								    AST_EmptyStatement,
							 | 
						||
| 
								 | 
							
								    AST_Expansion,
							 | 
						||
| 
								 | 
							
								    AST_Export,
							 | 
						||
| 
								 | 
							
								    AST_For,
							 | 
						||
| 
								 | 
							
								    AST_ForIn,
							 | 
						||
| 
								 | 
							
								    AST_Function,
							 | 
						||
| 
								 | 
							
								    AST_LabeledStatement,
							 | 
						||
| 
								 | 
							
								    AST_Lambda,
							 | 
						||
| 
								 | 
							
								    AST_Number,
							 | 
						||
| 
								 | 
							
								    AST_Scope,
							 | 
						||
| 
								 | 
							
								    AST_SimpleStatement,
							 | 
						||
| 
								 | 
							
								    AST_SymbolBlockDeclaration,
							 | 
						||
| 
								 | 
							
								    AST_SymbolCatch,
							 | 
						||
| 
								 | 
							
								    AST_SymbolDeclaration,
							 | 
						||
| 
								 | 
							
								    AST_SymbolFunarg,
							 | 
						||
| 
								 | 
							
								    AST_SymbolRef,
							 | 
						||
| 
								 | 
							
								    AST_SymbolVar,
							 | 
						||
| 
								 | 
							
								    AST_Toplevel,
							 | 
						||
| 
								 | 
							
								    AST_Unary,
							 | 
						||
| 
								 | 
							
								    AST_Var,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    TreeTransformer,
							 | 
						||
| 
								 | 
							
								    TreeWalker,
							 | 
						||
| 
								 | 
							
								    walk,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _INLINE,
							 | 
						||
| 
								 | 
							
								    _NOINLINE,
							 | 
						||
| 
								 | 
							
								    _PURE
							 | 
						||
| 
								 | 
							
								} from "../ast.js";
							 | 
						||
| 
								 | 
							
								import {
							 | 
						||
| 
								 | 
							
								    keep_name,
							 | 
						||
| 
								 | 
							
								    make_node,
							 | 
						||
| 
								 | 
							
								    map_add,
							 | 
						||
| 
								 | 
							
								    MAP,
							 | 
						||
| 
								 | 
							
								    remove,
							 | 
						||
| 
								 | 
							
								    return_false,
							 | 
						||
| 
								 | 
							
								} from "../utils/index.js";
							 | 
						||
| 
								 | 
							
								import { SymbolDef } from "../scope.js";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import {
							 | 
						||
| 
								 | 
							
								    WRITE_ONLY,
							 | 
						||
| 
								 | 
							
								    UNUSED,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    has_flag,
							 | 
						||
| 
								 | 
							
								    set_flag,
							 | 
						||
| 
								 | 
							
								} from "./compressor-flags.js";
							 | 
						||
| 
								 | 
							
								import {
							 | 
						||
| 
								 | 
							
								    make_sequence,
							 | 
						||
| 
								 | 
							
								    maintain_this_binding,
							 | 
						||
| 
								 | 
							
								    is_empty,
							 | 
						||
| 
								 | 
							
								    is_ref_of,
							 | 
						||
| 
								 | 
							
								    can_be_evicted_from_block,
							 | 
						||
| 
								 | 
							
								} from "./common.js";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const r_keep_assign = /keep_assign/;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/** Drop unused variables from this scope */
							 | 
						||
| 
								 | 
							
								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 (def.global && !in_use_ids.has(def.id)) {
							 | 
						||
| 
								 | 
							
								                                in_use_ids.set(def.id, def);
							 | 
						||
| 
								 | 
							
								                            }
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								                    });
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                if (def.name instanceof AST_Destructuring) {
							 | 
						||
| 
								 | 
							
								                    def.walk(tw);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                if (def.name instanceof AST_SymbolDeclaration && def.value) {
							 | 
						||
| 
								 | 
							
								                    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 of sorts
							 | 
						||
| 
								 | 
							
								                    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();
							 | 
						||
| 
								 | 
							
								                const keep = def.global && !drop_funcs || in_use_ids.has(def.id);
							 | 
						||
| 
								 | 
							
								                // Class "extends" and static blocks may have side effects
							 | 
						||
| 
								 | 
							
								                const has_side_effects = !keep
							 | 
						||
| 
								 | 
							
								                    && node instanceof AST_Class
							 | 
						||
| 
								 | 
							
								                    && node.has_side_effects(compressor);
							 | 
						||
| 
								 | 
							
								                if (!keep && !has_side_effects) {
							 | 
						||
| 
								 | 
							
								                    def.eliminated++;
							 | 
						||
| 
								 | 
							
								                    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;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								
							 |