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.
		
		
		
		
		
			
		
			
				
					346 lines
				
				11 KiB
			
		
		
			
		
	
	
					346 lines
				
				11 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_Array,
							 | 
						||
| 
								 | 
							
								    AST_Arrow,
							 | 
						||
| 
								 | 
							
								    AST_BlockStatement,
							 | 
						||
| 
								 | 
							
								    AST_Call,
							 | 
						||
| 
								 | 
							
								    AST_Class,
							 | 
						||
| 
								 | 
							
								    AST_Const,
							 | 
						||
| 
								 | 
							
								    AST_Constant,
							 | 
						||
| 
								 | 
							
								    AST_DefClass,
							 | 
						||
| 
								 | 
							
								    AST_Defun,
							 | 
						||
| 
								 | 
							
								    AST_EmptyStatement,
							 | 
						||
| 
								 | 
							
								    AST_Export,
							 | 
						||
| 
								 | 
							
								    AST_False,
							 | 
						||
| 
								 | 
							
								    AST_Function,
							 | 
						||
| 
								 | 
							
								    AST_Import,
							 | 
						||
| 
								 | 
							
								    AST_Infinity,
							 | 
						||
| 
								 | 
							
								    AST_LabeledStatement,
							 | 
						||
| 
								 | 
							
								    AST_Lambda,
							 | 
						||
| 
								 | 
							
								    AST_Let,
							 | 
						||
| 
								 | 
							
								    AST_LoopControl,
							 | 
						||
| 
								 | 
							
								    AST_NaN,
							 | 
						||
| 
								 | 
							
								    AST_Node,
							 | 
						||
| 
								 | 
							
								    AST_Null,
							 | 
						||
| 
								 | 
							
								    AST_Number,
							 | 
						||
| 
								 | 
							
								    AST_Object,
							 | 
						||
| 
								 | 
							
								    AST_ObjectKeyVal,
							 | 
						||
| 
								 | 
							
								    AST_PropAccess,
							 | 
						||
| 
								 | 
							
								    AST_RegExp,
							 | 
						||
| 
								 | 
							
								    AST_Scope,
							 | 
						||
| 
								 | 
							
								    AST_Sequence,
							 | 
						||
| 
								 | 
							
								    AST_SimpleStatement,
							 | 
						||
| 
								 | 
							
								    AST_Statement,
							 | 
						||
| 
								 | 
							
								    AST_String,
							 | 
						||
| 
								 | 
							
								    AST_SymbolRef,
							 | 
						||
| 
								 | 
							
								    AST_True,
							 | 
						||
| 
								 | 
							
								    AST_UnaryPrefix,
							 | 
						||
| 
								 | 
							
								    AST_Undefined,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    TreeWalker,
							 | 
						||
| 
								 | 
							
								    walk,
							 | 
						||
| 
								 | 
							
								    walk_abort,
							 | 
						||
| 
								 | 
							
								    walk_parent,
							 | 
						||
| 
								 | 
							
								} from "../ast.js";
							 | 
						||
| 
								 | 
							
								import { make_node, regexp_source_fix, string_template, makePredicate } from "../utils/index.js";
							 | 
						||
| 
								 | 
							
								import { first_in_statement } from "../utils/first_in_statement.js";
							 | 
						||
| 
								 | 
							
								import { has_flag, TOP } from "./compressor-flags.js";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export function merge_sequence(array, node) {
							 | 
						||
| 
								 | 
							
								    if (node instanceof AST_Sequence) {
							 | 
						||
| 
								 | 
							
								        array.push(...node.expressions);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        array.push(node);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return array;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export function make_sequence(orig, expressions) {
							 | 
						||
| 
								 | 
							
								    if (expressions.length == 1) return expressions[0];
							 | 
						||
| 
								 | 
							
								    if (expressions.length == 0) throw new Error("trying to create a sequence with length zero!");
							 | 
						||
| 
								 | 
							
								    return make_node(AST_Sequence, orig, {
							 | 
						||
| 
								 | 
							
								        expressions: expressions.reduce(merge_sequence, [])
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export 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: {
							 | 
						||
| 
								 | 
							
								                    source: regexp_source_fix(val.source),
							 | 
						||
| 
								 | 
							
								                    flags: val.flags
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            });
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        throw new Error(string_template("Can't handle constant of type: {type}", {
							 | 
						||
| 
								 | 
							
								            type: typeof val
							 | 
						||
| 
								 | 
							
								        }));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export function best_of_expression(ast1, ast2) {
							 | 
						||
| 
								 | 
							
								    return ast1.size() > ast2.size() ? ast2 : ast1;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export function best_of_statement(ast1, ast2) {
							 | 
						||
| 
								 | 
							
								    return best_of_expression(
							 | 
						||
| 
								 | 
							
								        make_node(AST_SimpleStatement, ast1, {
							 | 
						||
| 
								 | 
							
								            body: ast1
							 | 
						||
| 
								 | 
							
								        }),
							 | 
						||
| 
								 | 
							
								        make_node(AST_SimpleStatement, ast2, {
							 | 
						||
| 
								 | 
							
								            body: ast2
							 | 
						||
| 
								 | 
							
								        })
							 | 
						||
| 
								 | 
							
								    ).body;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/** Find which node is smaller, and return that */
							 | 
						||
| 
								 | 
							
								export function best_of(compressor, ast1, ast2) {
							 | 
						||
| 
								 | 
							
								    if (first_in_statement(compressor)) {
							 | 
						||
| 
								 | 
							
								        return best_of_statement(ast1, ast2);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        return best_of_expression(ast1, ast2);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/** Simplify an object property's key, if possible */
							 | 
						||
| 
								 | 
							
								export function get_simple_key(key) {
							 | 
						||
| 
								 | 
							
								    if (key instanceof AST_Constant) {
							 | 
						||
| 
								 | 
							
								        return key.getValue();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (key instanceof AST_UnaryPrefix
							 | 
						||
| 
								 | 
							
								        && key.operator == "void"
							 | 
						||
| 
								 | 
							
								        && key.expression instanceof AST_Constant) {
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return key;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export function read_property(obj, key) {
							 | 
						||
| 
								 | 
							
								    key = get_simple_key(key);
							 | 
						||
| 
								 | 
							
								    if (key instanceof AST_Node) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var value;
							 | 
						||
| 
								 | 
							
								    if (obj instanceof AST_Array) {
							 | 
						||
| 
								 | 
							
								        var elements = obj.elements;
							 | 
						||
| 
								 | 
							
								        if (key == "length") return make_node_from_constant(elements.length, obj);
							 | 
						||
| 
								 | 
							
								        if (typeof key == "number" && key in elements) value = elements[key];
							 | 
						||
| 
								 | 
							
								    } else if (obj instanceof AST_Object) {
							 | 
						||
| 
								 | 
							
								        key = "" + key;
							 | 
						||
| 
								 | 
							
								        var props = obj.properties;
							 | 
						||
| 
								 | 
							
								        for (var i = props.length; --i >= 0;) {
							 | 
						||
| 
								 | 
							
								            var prop = props[i];
							 | 
						||
| 
								 | 
							
								            if (!(prop instanceof AST_ObjectKeyVal)) return;
							 | 
						||
| 
								 | 
							
								            if (!value && props[i].key === key) value = props[i].value;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return value instanceof AST_SymbolRef && value.fixed_value() || value;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export function has_break_or_continue(loop, parent) {
							 | 
						||
| 
								 | 
							
								    var found = false;
							 | 
						||
| 
								 | 
							
								    var tw = new TreeWalker(function(node) {
							 | 
						||
| 
								 | 
							
								        if (found || node instanceof AST_Scope) return true;
							 | 
						||
| 
								 | 
							
								        if (node instanceof AST_LoopControl && tw.loopcontrol_target(node) === loop) {
							 | 
						||
| 
								 | 
							
								            return found = true;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								    if (parent instanceof AST_LabeledStatement) tw.push(parent);
							 | 
						||
| 
								 | 
							
								    tw.push(loop);
							 | 
						||
| 
								 | 
							
								    loop.body.walk(tw);
							 | 
						||
| 
								 | 
							
								    return found;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// we shouldn't compress (1,func)(something) to
							 | 
						||
| 
								 | 
							
								// func(something) because that changes the meaning of
							 | 
						||
| 
								 | 
							
								// the func (becomes lexical instead of global).
							 | 
						||
| 
								 | 
							
								export 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"
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								    ) {
							 | 
						||
| 
								 | 
							
								        const zero = make_node(AST_Number, orig, { value: 0 });
							 | 
						||
| 
								 | 
							
								        return make_sequence(orig, [ zero, val ]);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        return val;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export function is_func_expr(node) {
							 | 
						||
| 
								 | 
							
								    return node instanceof AST_Arrow || node instanceof AST_Function;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export function is_iife_call(node) {
							 | 
						||
| 
								 | 
							
								    // Used to determine whether the node can benefit from negation.
							 | 
						||
| 
								 | 
							
								    // Not the case with arrow functions (you need an extra set of parens).
							 | 
						||
| 
								 | 
							
								    if (node.TYPE != "Call") return false;
							 | 
						||
| 
								 | 
							
								    return node.expression instanceof AST_Function || is_iife_call(node.expression);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export 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;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export const identifier_atom = makePredicate("Infinity NaN undefined");
							 | 
						||
| 
								 | 
							
								export function is_identifier_atom(node) {
							 | 
						||
| 
								 | 
							
								    return node instanceof AST_Infinity
							 | 
						||
| 
								 | 
							
								        || node instanceof AST_NaN
							 | 
						||
| 
								 | 
							
								        || node instanceof AST_Undefined;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/** Check if this is a SymbolRef node which has one def of a certain AST type */
							 | 
						||
| 
								 | 
							
								export function is_ref_of(ref, type) {
							 | 
						||
| 
								 | 
							
								    if (!(ref instanceof AST_SymbolRef)) return false;
							 | 
						||
| 
								 | 
							
								    var orig = ref.definition().orig;
							 | 
						||
| 
								 | 
							
								    for (var i = orig.length; --i >= 0;) {
							 | 
						||
| 
								 | 
							
								        if (orig[i] instanceof type) return true;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**Can we turn { block contents... } into just the block contents ?
							 | 
						||
| 
								 | 
							
								 * Not if one of these is inside.
							 | 
						||
| 
								 | 
							
								 **/
							 | 
						||
| 
								 | 
							
								export function can_be_evicted_from_block(node) {
							 | 
						||
| 
								 | 
							
								    return !(
							 | 
						||
| 
								 | 
							
								        node instanceof AST_DefClass ||
							 | 
						||
| 
								 | 
							
								        node instanceof AST_Defun ||
							 | 
						||
| 
								 | 
							
								        node instanceof AST_Let ||
							 | 
						||
| 
								 | 
							
								        node instanceof AST_Const ||
							 | 
						||
| 
								 | 
							
								        node instanceof AST_Export ||
							 | 
						||
| 
								 | 
							
								        node instanceof AST_Import
							 | 
						||
| 
								 | 
							
								    );
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export 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");
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export function is_reachable(scope_node, defs) {
							 | 
						||
| 
								 | 
							
								    const find_ref = node => {
							 | 
						||
| 
								 | 
							
								        if (node instanceof AST_SymbolRef && defs.includes(node.definition())) {
							 | 
						||
| 
								 | 
							
								            return walk_abort;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return walk_parent(scope_node, (node, info) => {
							 | 
						||
| 
								 | 
							
								        if (node instanceof AST_Scope && node !== scope_node) {
							 | 
						||
| 
								 | 
							
								            var parent = info.parent();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (
							 | 
						||
| 
								 | 
							
								                parent instanceof AST_Call
							 | 
						||
| 
								 | 
							
								                && parent.expression === node
							 | 
						||
| 
								 | 
							
								                // Async/Generators aren't guaranteed to sync evaluate all of
							 | 
						||
| 
								 | 
							
								                // their body steps, so it's possible they close over the variable.
							 | 
						||
| 
								 | 
							
								                && !(node.async || node.is_generator)
							 | 
						||
| 
								 | 
							
								            ) {
							 | 
						||
| 
								 | 
							
								                return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (walk(node, find_ref)) return walk_abort;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return true;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/** Check if a ref refers to the name of a function/class it's defined within */
							 | 
						||
| 
								 | 
							
								export function is_recursive_ref(compressor, def) {
							 | 
						||
| 
								 | 
							
								    var node;
							 | 
						||
| 
								 | 
							
								    for (var i = 0; node = compressor.parent(i); i++) {
							 | 
						||
| 
								 | 
							
								        if (node instanceof AST_Lambda || node instanceof AST_Class) {
							 | 
						||
| 
								 | 
							
								            var name = node.name;
							 | 
						||
| 
								 | 
							
								            if (name && name.definition() === def) {
							 | 
						||
| 
								 | 
							
								                return true;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return false;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// TODO this only works with AST_Defun, shouldn't it work for other ways of defining functions?
							 | 
						||
| 
								 | 
							
								export function retain_top_func(fn, compressor) {
							 | 
						||
| 
								 | 
							
								    return compressor.top_retain
							 | 
						||
| 
								 | 
							
								        && fn instanceof AST_Defun
							 | 
						||
| 
								 | 
							
								        && has_flag(fn, TOP)
							 | 
						||
| 
								 | 
							
								        && fn.name
							 | 
						||
| 
								 | 
							
								        && compressor.top_retain(fn.name);
							 | 
						||
| 
								 | 
							
								}
							 |