@redux-multipurpose/angular-cli
Version:
A multipurpose redux tools angular cli
1,275 lines (1,207 loc) • 410 kB
JavaScript
/***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS
-------------------------------- (C) ---------------------------------
Author: Mihai Bazon
<mihai.bazon@gmail.com>
http://mihai.bazon.net/blog
Distributed under the BSD license:
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
***********************************************************************/
"use strict";
function Compressor(options, false_by_default) {
if (!(this instanceof Compressor))
return new Compressor(options, false_by_default);
TreeTransformer.call(this, this.before, this.after);
this.options = defaults(options, {
arguments : !false_by_default,
assignments : !false_by_default,
booleans : !false_by_default,
collapse_vars : !false_by_default,
comparisons : !false_by_default,
conditionals : !false_by_default,
dead_code : !false_by_default,
directives : !false_by_default,
drop_console : false,
drop_debugger : !false_by_default,
evaluate : !false_by_default,
expression : false,
functions : !false_by_default,
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_fargs : false_by_default || "strict",
keep_fnames : false,
keep_infinity : false,
loops : !false_by_default,
merge_vars : !false_by_default,
negate_iife : !false_by_default,
objects : !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,
strings : !false_by_default,
switches : !false_by_default,
top_retain : null,
toplevel : !!(options && options["top_retain"]),
typeofs : !false_by_default,
unsafe : false,
unsafe_comps : false,
unsafe_Function : false,
unsafe_math : false,
unsafe_proto : false,
unsafe_regexp : false,
unsafe_undefined: false,
unused : !false_by_default,
varify : !false_by_default,
}, true);
var evaluate = this.options["evaluate"];
this.eval_threshold = /eager/.test(evaluate) ? 1 / 0 : +evaluate;
var global_defs = this.options["global_defs"];
if (typeof global_defs == "object") for (var key in global_defs) {
if (/^@/.test(key) && 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 keep_fargs = this.options["keep_fargs"];
this.drop_fargs = keep_fargs == "strict" ? function(lambda, parent) {
if (lambda.length_read) return false;
var name = lambda.name;
if (!name) return parent && parent.TYPE == "Call" && parent.expression === lambda;
if (name.fixed_value() !== lambda) return false;
var def = name.definition();
if (def.direct_access) return false;
var escaped = def.escaped;
return escaped && escaped.depth != 1;
} : keep_fargs ? return_false : return_true;
var pure_funcs = this.options["pure_funcs"];
if (typeof pure_funcs == "function") {
this.pure_funcs = pure_funcs;
} else if (typeof pure_funcs == "string") {
this.pure_funcs = function(node) {
return pure_funcs !== node.expression.print_to_string();
};
} else if (Array.isArray(pure_funcs)) {
this.pure_funcs = function(node) {
return !member(node.expression.print_to_string(), pure_funcs);
};
} else {
this.pure_funcs = return_true;
}
var sequences = this.options["sequences"];
this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
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 member(def.name, top_retain);
};
}
var toplevel = this.options["toplevel"];
this.toplevel = typeof toplevel == "string" ? {
funcs: /funcs/.test(toplevel),
vars: /vars/.test(toplevel)
} : {
funcs: toplevel,
vars: toplevel
};
}
Compressor.prototype = new TreeTransformer;
merge(Compressor.prototype, {
option: function(key) { return this.options[key] },
exposed: function(def) {
if (def.undeclared) return true;
if (!(def.global || def.scope.resolve() instanceof AST_Toplevel)) return false;
var toplevel = this.toplevel;
return !all(def.orig, function(sym) {
return toplevel[sym instanceof AST_SymbolDefun ? "funcs" : "vars"];
});
},
compress: function(node) {
node = node.resolve_defines(this);
if (this.option("expression")) {
node.process_expression(true);
}
var passes = +this.options.passes || 1;
var min_count = 1 / 0;
var stopping = false;
var mangle = { ie8: this.option("ie8") };
for (var pass = 0; pass < passes; pass++) {
node.figure_out_scope(mangle);
if (pass > 0 || this.option("reduce_vars"))
node.reset_opt_flags(this);
node = node.transform(this);
if (passes > 1) {
var count = 0;
node.walk(new TreeWalker(function() {
count++;
}));
AST_Node.info("pass " + pass + ": last_count: " + min_count + ", count: " + count);
if (count < min_count) {
min_count = count;
stopping = false;
} else if (stopping) {
break;
} else {
stopping = true;
}
}
}
if (this.option("expression")) {
node.process_expression(false);
}
return node;
},
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var is_scope = node instanceof AST_Scope;
if (is_scope) {
node.hoist_properties(this);
node.hoist_declarations(this);
node.process_boolean_returns(this);
}
// Before https://github.com/mishoo/UglifyJS/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
// Existing code relies on how AST_Node.optimize() worked, and omitting the
// following replacement call would result in degraded efficiency of both
// output and performance.
descend(node, this);
var opt = node.optimize(this);
if (is_scope && opt === node && !this.has_directive("use asm") && !opt.pinned()) {
opt.merge_variables(this);
opt.drop_unused(this);
descend(opt, this);
}
if (opt === node) opt._squeezed = true;
return opt;
}
});
(function(OPT) {
OPT(AST_Node, function(self, compressor) {
return self;
});
AST_Node.DEFMETHOD("equivalent_to", function(node) {
return this.TYPE == node.TYPE && this.print_to_string() == node.print_to_string();
});
AST_Scope.DEFMETHOD("process_expression", function(insert, transform) {
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) {
return transform ? transform(node) : make_node(AST_SimpleStatement, node, {
body: node.value || make_node(AST_UnaryPrefix, node, {
operator: "void",
expression: make_node(AST_Number, node, {
value: 0
})
})
});
}
if (node instanceof AST_Lambda && node !== self) {
return node;
}
if (node instanceof AST_Block) {
var index = node.body.length - 1;
if (index >= 0) {
node.body[index] = node.body[index].transform(tt);
}
} 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);
});
function read_property(obj, node) {
var key = node.getProperty();
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_Lambda) {
if (key == "length") {
obj.length_read = true;
return make_node_from_constant(obj.argnames.length, obj);
}
} 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;
}
function is_read_only_fn(value, name) {
if (value instanceof AST_Boolean) return native_fns.Boolean[name];
if (value instanceof AST_Number) return native_fns.Number[name];
if (value instanceof AST_String) return native_fns.String[name];
if (name == "valueOf") return false;
if (value instanceof AST_Array) return native_fns.Array[name];
if (value instanceof AST_Function) return native_fns.Function[name];
if (value instanceof AST_Object) return native_fns.Object[name];
if (value instanceof AST_RegExp) return native_fns.RegExp[name] && !value.value.global;
}
function is_modified(compressor, tw, node, value, level, immutable, recursive) {
var parent = tw.parent(level);
if (compressor.option("unsafe") && parent instanceof AST_Dot && is_read_only_fn(value, parent.property)) {
return;
}
var lhs = is_lhs(node, parent);
if (lhs) return lhs;
if (parent instanceof AST_Array) return is_modified(compressor, tw, parent, parent, level + 1);
if (parent instanceof AST_Call) {
return !immutable
&& parent.expression === node
&& !parent.is_expr_pure(compressor)
&& (!(value instanceof AST_Function)
|| !(parent instanceof AST_New) && value.contains_this());
}
if (parent instanceof AST_ForIn) return parent.init === node;
if (parent instanceof AST_ObjectKeyVal) {
if (parent.value !== node) return;
var obj = tw.parent(level + 1);
return is_modified(compressor, tw, obj, obj, level + 2);
}
if (parent instanceof AST_PropAccess) {
if (parent.expression !== node) return;
var prop = read_property(value, parent);
return (!immutable || recursive) && is_modified(compressor, tw, parent, prop, level + 1);
}
}
function is_arguments(def) {
if (def.name != "arguments") return false;
var orig = def.orig;
return orig.length == 1 && orig[0] instanceof AST_SymbolFunarg;
}
function cross_scope(def, sym) {
do {
if (def === sym) return false;
if (sym instanceof AST_Scope) return true;
} while (sym = sym.parent_scope);
}
(function(def) {
def(AST_Node, noop);
function reset_def(tw, compressor, def) {
def.assignments = 0;
def.bool_fn = 0;
def.cross_loop = false;
def.direct_access = false;
def.escaped = [];
def.fixed = !def.const_redefs
&& !def.scope.pinned()
&& !compressor.exposed(def)
&& !(def.init instanceof AST_Function && def.init !== def.scope)
&& def.init;
if (def.fixed instanceof AST_Defun && !all(def.references, function(ref) {
var scope = ref.scope.resolve();
do {
if (def.scope === scope) return true;
} while (scope instanceof AST_Function && (scope = scope.parent_scope.resolve()));
})) {
tw.defun_ids[def.id] = false;
}
def.recursive_refs = 0;
def.references = [];
def.should_replace = undefined;
def.single_use = undefined;
}
function reset_variables(tw, compressor, scope) {
scope.variables.each(function(def) {
reset_def(tw, compressor, def);
if (def.fixed === null) {
def.safe_ids = tw.safe_ids;
mark(tw, def);
} else if (def.fixed) {
tw.loop_ids[def.id] = tw.in_loop;
mark(tw, def);
}
});
scope.may_call_this = function() {
scope.may_call_this = noop;
if (!scope.contains_this()) return;
scope.functions.each(function(def) {
if (def.init instanceof AST_Defun && !(def.id in tw.defun_ids)) {
tw.defun_ids[def.id] = false;
}
});
};
if (compressor.option("ie8")) scope.variables.each(function(def) {
var d = def.orig[0].definition();
if (d !== def) d.fixed = false;
});
}
function mark_defun(tw, def) {
if (def.id in tw.defun_ids) {
var marker = tw.defun_ids[def.id];
if (!marker) return;
var visited = tw.defun_visited[def.id];
if (marker === tw.safe_ids) {
if (!visited) return def.fixed;
} else if (visited) {
def.init.enclosed.forEach(function(d) {
if (def.init.variables.get(d.name) === d) return;
if (!safe_to_read(tw, d)) d.fixed = false;
});
} else {
tw.defun_ids[def.id] = false;
}
} else {
if (!tw.in_loop) {
tw.defun_ids[def.id] = tw.safe_ids;
return def.fixed;
}
tw.defun_ids[def.id] = false;
}
}
function walk_defuns(tw, scope) {
scope.functions.each(function(def) {
if (def.init instanceof AST_Defun && !tw.defun_visited[def.id]) {
tw.defun_ids[def.id] = tw.safe_ids;
def.init.walk(tw);
}
});
}
function push(tw) {
tw.safe_ids = Object.create(tw.safe_ids);
}
function pop(tw) {
tw.safe_ids = Object.getPrototypeOf(tw.safe_ids);
}
function mark(tw, def) {
tw.safe_ids[def.id] = {};
}
function push_ref(def, ref) {
def.references.push(ref);
def.last_ref = ref;
}
function safe_to_read(tw, def) {
if (def.single_use == "m") return false;
var safe = tw.safe_ids[def.id];
if (safe) {
if (!HOP(tw.safe_ids, def.id)) safe.read = safe.read && safe.read !== tw.safe_ids ? true : tw.safe_ids;
if (def.fixed == null) {
if (is_arguments(def)) return false;
if (def.global && def.name == "arguments") return false;
tw.loop_ids[def.id] = null;
def.fixed = make_node(AST_Undefined, def.orig[0]);
return true;
}
return !safe.assign || safe.assign === tw.safe_ids;
}
return def.fixed instanceof AST_Defun;
}
function safe_to_assign(tw, def, declare) {
if (!(declare || all(def.orig, function(sym) {
return !(sym instanceof AST_SymbolConst);
}))) return false;
if (def.fixed === undefined) return declare || all(def.orig, function(sym) {
return !(sym instanceof AST_SymbolLet);
});
if (def.fixed === null && def.safe_ids) {
def.safe_ids[def.id] = false;
delete def.safe_ids;
return true;
}
if (def.fixed === false) return false;
var safe = tw.safe_ids[def.id];
if (!HOP(tw.safe_ids, def.id)) {
if (!safe) return false;
if (safe.read && def.scope !== tw.find_parent(AST_Scope)) return false;
safe.assign = safe.assign && safe.assign !== tw.safe_ids ? true : tw.safe_ids;
}
if (def.fixed != null && safe.read) {
if (safe.read !== tw.safe_ids) return false;
if (tw.loop_ids[def.id] !== tw.in_loop) return false;
}
return safe_to_read(tw, def) && all(def.orig, function(sym) {
return !(sym instanceof AST_SymbolLambda);
});
}
function make_ref(ref, fixed) {
var node = make_node(AST_SymbolRef, ref, ref);
node.fixed = fixed || make_node(AST_Undefined, ref);
return node;
}
function ref_once(compressor, def) {
return compressor.option("unused")
&& !def.scope.pinned()
&& def.single_use !== false
&& def.references.length - def.recursive_refs == 1;
}
function is_immutable(value) {
if (!value) return false;
return value.is_constant()
|| value instanceof AST_Lambda
|| value instanceof AST_This;
}
function has_escaped(d, node, parent) {
if (parent instanceof AST_Assign) return parent.operator == "=" && parent.right === node;
if (parent instanceof AST_Call) return parent.expression !== node || parent instanceof AST_New;
if (parent instanceof AST_Exit) return parent.value === node && node.scope !== d.scope;
if (parent instanceof AST_VarDef) return parent.value === node;
}
function value_in_use(node, parent) {
if (parent instanceof AST_Array) return true;
if (parent instanceof AST_Binary) return lazy_op[parent.operator];
if (parent instanceof AST_Conditional) return parent.condition !== node;
if (parent instanceof AST_Sequence) return parent.tail_node() === node;
}
function mark_escaped(tw, d, scope, node, value, level, depth) {
var parent = tw.parent(level);
if (value && value.is_constant()) return;
if (has_escaped(d, node, parent)) {
d.escaped.push(parent);
if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1;
if (!d.escaped.depth || d.escaped.depth > depth) d.escaped.depth = depth;
return;
} else if (value_in_use(node, parent)) {
mark_escaped(tw, d, scope, parent, parent, level + 1, depth);
} else if (parent instanceof AST_ObjectKeyVal && parent.value === node) {
var obj = tw.parent(level + 1);
mark_escaped(tw, d, scope, obj, obj, level + 2, depth);
} else if (parent instanceof AST_PropAccess && parent.expression === node) {
value = read_property(value, parent);
mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1);
if (value) return;
}
if (level > 0) return;
if (parent instanceof AST_Call && parent.expression === node) return;
if (parent instanceof AST_Sequence && parent.tail_node() !== node) return;
if (parent instanceof AST_SimpleStatement) return;
if (parent instanceof AST_Unary && !unary_side_effects[parent.operator]) return;
d.direct_access = true;
}
function mark_assignment_to_arguments(node) {
if (!(node instanceof AST_Sub)) return;
var expr = node.expression;
if (!(expr instanceof AST_SymbolRef)) return;
var def = expr.definition();
if (is_arguments(def) && node.property instanceof AST_Number) def.reassigned = true;
}
def(AST_Accessor, function(tw, descend, compressor) {
push(tw);
reset_variables(tw, compressor, this);
descend();
pop(tw);
walk_defuns(tw, this);
return true;
});
def(AST_Assign, function(tw, descend, compressor) {
var node = this;
var eq = node.operator == "=";
var sym = node.left;
if (eq && sym.equivalent_to(node.right) && !sym.has_side_effects(compressor)) {
node.right.walk(tw);
walk_prop(sym);
node.__drop = true;
return true;
}
if (!(sym instanceof AST_SymbolRef)) {
mark_assignment_to_arguments(sym);
return;
}
var d = sym.definition();
d.assignments++;
var fixed = d.fixed;
var value = eq ? node.right : node;
if (is_modified(compressor, tw, node, value, 0)) {
d.fixed = false;
return;
}
var safe = eq || safe_to_read(tw, d);
node.right.walk(tw);
if (safe && safe_to_assign(tw, d)) {
push_ref(d, sym);
mark(tw, d);
if (eq) {
tw.loop_ids[d.id] = tw.in_loop;
mark_escaped(tw, d, sym.scope, node, value, 0, 1);
sym.fixed = d.fixed = function() {
return node.right;
};
} else {
if (d.single_use) d.single_use = false;
sym.fixed = d.fixed = function() {
return make_node(AST_Binary, node, {
operator: node.operator.slice(0, -1),
left: make_ref(sym, fixed),
right: node.right
});
};
}
sym.fixed.assigns = eq || !fixed || !fixed.assigns ? [] : fixed.assigns.slice();
sym.fixed.assigns.push(node);
} else {
sym.walk(tw);
d.fixed = false;
}
return true;
function walk_prop(node) {
if (node instanceof AST_Dot) {
walk_prop(node.expression);
} else if (node instanceof AST_Sub) {
walk_prop(node.expression);
node.property.walk(tw);
} else if (node instanceof AST_SymbolRef) {
var d = node.definition();
push_ref(d, node);
node.fixed = d.fixed;
} else {
node.walk(tw);
}
}
});
def(AST_Binary, function(tw) {
if (!lazy_op[this.operator]) return;
this.left.walk(tw);
push(tw);
this.right.walk(tw);
pop(tw);
return true;
});
def(AST_BlockScope, function(tw, descend, compressor) {
this.variables.each(function(def) {
reset_def(tw, compressor, def);
});
});
def(AST_Call, function(tw, descend) {
tw.find_parent(AST_Scope).may_call_this();
var exp = this.expression;
if (exp instanceof AST_Function) {
this.args.forEach(function(arg) {
arg.walk(tw);
});
exp.walk(tw);
return true;
} else if (exp instanceof AST_SymbolRef) {
var def = exp.definition();
if (this.TYPE == "Call" && tw.in_boolean_context()) def.bool_fn++;
if (!(def.fixed instanceof AST_Defun)) return;
var defun = mark_defun(tw, def);
if (!defun) return;
descend();
defun.walk(tw);
return true;
} else if (this.TYPE == "Call"
&& exp instanceof AST_Assign
&& exp.operator == "="
&& exp.left instanceof AST_SymbolRef
&& tw.in_boolean_context()) {
exp.left.definition().bool_fn++;
}
});
def(AST_Conditional, function(tw) {
this.condition.walk(tw);
push(tw);
this.consequent.walk(tw);
pop(tw);
push(tw);
this.alternative.walk(tw);
pop(tw);
return true;
});
def(AST_Defun, function(tw, descend, compressor) {
var id = this.name.definition().id;
if (tw.defun_visited[id]) return true;
if (tw.defun_ids[id] !== tw.safe_ids) return true;
tw.defun_visited[id] = true;
this.inlined = false;
push(tw);
reset_variables(tw, compressor, this);
descend();
pop(tw);
walk_defuns(tw, this);
return true;
});
def(AST_Do, function(tw) {
var saved_loop = tw.in_loop;
tw.in_loop = this;
push(tw);
this.body.walk(tw);
if (has_loop_control(this, tw.parent())) {
pop(tw);
push(tw);
}
this.condition.walk(tw);
pop(tw);
tw.in_loop = saved_loop;
return true;
});
def(AST_For, function(tw, descend, compressor) {
this.variables.each(function(def) {
reset_def(tw, compressor, def);
});
if (this.init) this.init.walk(tw);
var saved_loop = tw.in_loop;
tw.in_loop = this;
push(tw);
if (this.condition) this.condition.walk(tw);
this.body.walk(tw);
if (this.step) {
if (has_loop_control(this, tw.parent())) {
pop(tw);
push(tw);
}
this.step.walk(tw);
}
pop(tw);
tw.in_loop = saved_loop;
return true;
});
def(AST_ForIn, function(tw, descend, compressor) {
this.variables.each(function(def) {
reset_def(tw, compressor, def);
});
this.object.walk(tw);
var saved_loop = tw.in_loop;
tw.in_loop = this;
push(tw);
var init = this.init;
init.walk(tw);
if (init instanceof AST_SymbolRef) {
init.definition().fixed = false;
} else if (init instanceof AST_Var) {
init.definitions[0].name.definition().fixed = false;
}
this.body.walk(tw);
pop(tw);
tw.in_loop = saved_loop;
return true;
});
def(AST_Function, function(tw, descend, compressor) {
var fn = this;
fn.inlined = false;
var iife;
if (!fn.name && (iife = tw.parent()) instanceof AST_Call && iife.expression === fn) {
var hit = false;
var aborts = false;
fn.walk(new TreeWalker(function(node) {
if (hit) return aborts = true;
if (node instanceof AST_Return) return hit = true;
if (node instanceof AST_Scope && node !== fn) return true;
}));
if (aborts) push(tw);
reset_variables(tw, compressor, fn);
// Virtually turn IIFE parameters into variable definitions:
// (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
// So existing transformation rules can work on them.
fn.argnames.forEach(function(arg, i) {
var d = arg.definition();
if (d.fixed === undefined && (!fn.uses_arguments || tw.has_directive("use strict"))) {
mark(tw, d);
tw.loop_ids[d.id] = tw.in_loop;
var value = iife.args[i];
d.fixed = function() {
var j = fn.argnames.indexOf(arg);
return (j < 0 ? value : iife.args[j]) || make_node(AST_Undefined, iife);
};
d.fixed.assigns = [ arg ];
} else {
d.fixed = false;
}
});
descend();
var safe_ids = tw.safe_ids;
pop(tw);
walk_defuns(tw, fn);
if (!aborts) tw.safe_ids = safe_ids;
} else {
push(tw);
reset_variables(tw, compressor, fn);
descend();
pop(tw);
walk_defuns(tw, fn);
}
return true;
});
def(AST_If, function(tw) {
this.condition.walk(tw);
push(tw);
this.body.walk(tw);
pop(tw);
if (this.alternative) {
push(tw);
this.alternative.walk(tw);
pop(tw);
}
return true;
});
def(AST_LabeledStatement, function(tw) {
push(tw);
this.body.walk(tw);
pop(tw);
return true;
});
def(AST_Switch, function(tw, descend, compressor) {
this.variables.each(function(def) {
reset_def(tw, compressor, def);
});
this.expression.walk(tw);
var first = true;
this.body.forEach(function(branch) {
if (branch instanceof AST_Default) return;
branch.expression.walk(tw);
if (first) {
first = false;
push(tw);
}
})
if (!first) pop(tw);
walk_body(this, tw);
return true;
});
def(AST_SwitchBranch, function(tw) {
push(tw);
walk_body(this, tw);
pop(tw);
return true;
});
def(AST_SymbolCatch, function() {
this.definition().fixed = false;
});
def(AST_SymbolRef, function(tw, descend, compressor) {
var d = this.definition();
push_ref(d, this);
if (d.references.length == 1
&& !d.fixed
&& d.orig[0] instanceof AST_SymbolDefun) {
tw.loop_ids[d.id] = tw.in_loop;
}
if (d.fixed === false) {
var redef = d.redefined();
if (redef && cross_scope(d.scope, this.scope)) redef.single_use = false;
} else if (d.fixed === undefined || !safe_to_read(tw, d)) {
d.fixed = false;
} else if (d.fixed) {
var value = this.fixed_value();
var recursive = recursive_ref(tw, d);
if (recursive) {
d.recursive_refs++;
} else if (value && ref_once(compressor, d)) {
d.in_loop = tw.loop_ids[d.id] !== tw.in_loop;
d.single_use = value instanceof AST_Lambda
&& !value.pinned()
&& (!d.in_loop || tw.parent() instanceof AST_Call)
|| !d.in_loop
&& d.scope === this.scope.resolve()
&& value.is_constant_expression();
} else {
d.single_use = false;
}
if (is_modified(compressor, tw, this, value, 0, is_immutable(value), recursive)) {
if (d.single_use) {
d.single_use = "m";
} else {
d.fixed = false;
}
}
if (d.fixed && tw.loop_ids[d.id] !== tw.in_loop) {
d.cross_loop = true;
}
mark_escaped(tw, d, this.scope, this, value, 0, 1);
}
this.fixed = d.fixed;
var parent;
if (d.fixed instanceof AST_Defun
&& !((parent = tw.parent()) instanceof AST_Call && parent.expression === this)) {
var defun = mark_defun(tw, d);
if (defun) defun.walk(tw);
}
});
def(AST_Toplevel, function(tw, descend, compressor) {
this.globals.each(function(def) {
reset_def(tw, compressor, def);
});
push(tw);
reset_variables(tw, compressor, this);
descend();
pop(tw);
walk_defuns(tw, this);
return true;
});
def(AST_Try, function(tw, descend, compressor) {
this.variables.each(function(def) {
reset_def(tw, compressor, def);
});
push(tw);
walk_body(this, tw);
pop(tw);
if (this.bcatch) {
push(tw);
this.bcatch.walk(tw);
pop(tw);
}
if (this.bfinally) this.bfinally.walk(tw);
return true;
});
def(AST_Unary, function(tw, descend) {
var node = this;
if (!unary_arithmetic[node.operator]) return;
var exp = node.expression;
if (!(exp instanceof AST_SymbolRef)) {
mark_assignment_to_arguments(exp);
return;
}
var d = exp.definition();
d.assignments++;
var fixed = d.fixed;
if (safe_to_read(tw, d) && safe_to_assign(tw, d)) {
push_ref(d, exp);
mark(tw, d);
if (d.single_use) d.single_use = false;
d.fixed = function() {
return make_node(AST_Binary, node, {
operator: node.operator.slice(0, -1),
left: make_node(AST_UnaryPrefix, node, {
operator: "+",
expression: make_ref(exp, fixed)
}),
right: make_node(AST_Number, node, {
value: 1
})
});
};
d.fixed.assigns = fixed && fixed.assigns ? fixed.assigns.slice() : [];
d.fixed.assigns.push(node);
if (node instanceof AST_UnaryPrefix) {
exp.fixed = d.fixed;
} else {
exp.fixed = function() {
return make_node(AST_UnaryPrefix, node, {
operator: "+",
expression: make_ref(exp, fixed)
});
};
exp.fixed.assigns = fixed && fixed.assigns;
}
} else {
exp.walk(tw);
d.fixed = false;
}
return true;
});
def(AST_VarDef, function(tw, descend) {
var node = this;
if (!node.value) return;
descend();
var d = node.name.definition();
if (safe_to_assign(tw, d, true)) {
mark(tw, d);
tw.loop_ids[d.id] = tw.in_loop;
d.fixed = function() {
return node.value;
};
d.fixed.assigns = [ node ];
if (node.name instanceof AST_SymbolConst && d.redefined()) d.single_use = false;
} else {
d.fixed = false;
}
return true;
});
def(AST_While, function(tw, descend) {
var saved_loop = tw.in_loop;
tw.in_loop = this;
push(tw);
descend();
pop(tw);
tw.in_loop = saved_loop;
return true;
});
})(function(node, func) {
node.DEFMETHOD("reduce_vars", func);
});
AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
var tw = new TreeWalker(compressor.option("reduce_vars") ? function(node, descend) {
reset_flags(node);
return node.reduce_vars(tw, descend, compressor);
} : reset_flags);
// Flow control for visiting `AST_Defun`s
tw.defun_ids = Object.create(null);
tw.defun_visited = Object.create(null);
// Record the loop body in which `AST_SymbolDeclaration` is first encountered
tw.in_loop = null;
tw.loop_ids = Object.create(null);
// Stack of look-up tables to keep track of whether a `SymbolDef` has been
// properly assigned before use:
// - `push()` & `pop()` when visiting conditional branches
// - backup & restore via `save_ids` when visiting out-of-order sections
tw.safe_ids = Object.create(null);
this.walk(tw);
function reset_flags(node) {
node._squeezed = false;
node._optimized = false;
delete node.fixed;
if (node instanceof AST_Scope) delete node._var_names;
}
});
AST_Symbol.DEFMETHOD("fixed_value", function() {
var fixed = this.definition().fixed;
if (!fixed) return fixed;
if (this.fixed) fixed = this.fixed;
return fixed instanceof AST_Node ? fixed : fixed();
});
AST_SymbolRef.DEFMETHOD("is_immutable", function() {
var def = this.definition();
if (def.orig.length != 1) return false;
var sym = def.orig[0];
return sym instanceof AST_SymbolLambda && def.scope.name === sym;
});
function find_scope(compressor) {
var level = 0, node;
while (node = compressor.parent(level++)) {
if (node.variables) return node;
}
}
function is_lhs_read_only(lhs, compressor) {
if (lhs instanceof AST_This) return true;
if (lhs instanceof AST_SymbolRef) {
var def = lhs.definition();
return def.lambda || compressor.exposed(def) && identifier_atom[def.name];
}
if (lhs instanceof AST_PropAccess) {
lhs = lhs.expression;
if (lhs instanceof AST_SymbolRef) {
if (lhs.is_immutable()) return false;
lhs = lhs.fixed_value();
}
if (!lhs) return true;
if (lhs.is_constant()) return true;
return is_lhs_read_only(lhs, compressor);
}
return false;
}
function make_node(ctor, orig, props) {
if (!props) props = {};
if (orig) {
if (!props.start) props.start = orig.start;
if (!props.end) props.end = orig.end;
}
return new ctor(props);
}
function make_sequence(orig, expressions) {
if (expressions.length == 1) return expressions[0];
return make_node(AST_Sequence, orig, {
expressions: expressions.reduce(merge_sequence, [])
});
}
function make_node_from_constant(val, orig) {
switch (typeof val) {
case "string":
return make_node(AST_String, orig, {
value: val
});
case "number":
if (isNaN(val)) return make_node(AST_NaN, orig);
if (isFinite(val)) {
return 1 / val < 0 ? make_node(AST_UnaryPrefix, orig, {
operator: "-",
expression: make_node(AST_Number, orig, { value: -val })
}) : make_node(AST_Number, orig, { value: val });
}
return val < 0 ? make_node(AST_UnaryPrefix, orig, {
operator: "-",
expression: make_node(AST_Infinity, orig)
}) : make_node(AST_Infinity, orig);
case "boolean":
return make_node(val ? AST_True : AST_False, orig);
case "undefined":
return make_node(AST_Undefined, orig);
default:
if (val === null) {
return make_node(AST_Null, orig, { value: null });
}
if (val instanceof RegExp) {
return make_node(AST_RegExp, orig, { value: val });
}
throw new Error(string_template("Can't handle constant of type: {type}", {
type: typeof val
}));
}
}
function needs_unbinding(compressor, val) {
return val instanceof AST_PropAccess
|| is_undeclared_ref(val) && val.name == "eval";
}
// we shouldn't compress (1,func)(something) to
// func(something) because that changes the meaning of
// the func (becomes lexical instead of global).
function maintain_this_binding(compressor, parent, orig, val) {
var wrap = false;
if (parent.TYPE == "Call") {
wrap = parent.expression === orig && needs_unbinding(compressor, val);
} else if (parent instanceof AST_UnaryPrefix) {
wrap = parent.operator == "delete"
|| parent.operator == "typeof" && is_undeclared_ref(val);
}
return wrap ? make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]) : val;
}
function merge_sequence(array, node) {
if (node instanceof AST_Sequence) {
array.push.apply(array, node.expressions);
} else {
array.push(node);
}
return array;
}
function as_statement_array(thing) {
if (thing === null) return [];
if (thing instanceof AST_BlockStatement) return thing.body;
if (thing instanceof AST_EmptyStatement) return [];
if (thing instanceof AST_Statement) return [ thing ];
throw new Error("Can't convert thing to statement array");
}
function is_empty(thing) {
if (thing === null) return true;
if (thing instanceof AST_EmptyStatement) return true;
if (thing instanceof AST_BlockStatement) return thing.body.length == 0;
return false;
}
function has_declarations_only(block) {
return all(block.body, function(stat) {
return is_empty(stat)
|| stat instanceof AST_Defun
|| stat instanceof AST_Var && all(stat.definitions, function(var_def) {
return !var_def.value;
});
});
}
function loop_body(x) {
if (x instanceof AST_IterationStatement) {
return x.body instanceof AST_BlockStatement ? x.body : x;
}
return x;
}
function root_expr(prop) {
while (prop instanceof AST_PropAccess) prop = prop.expression;
return prop;
}
function is_iife_call(node) {
if (node.TYPE != "Call") return false;
return node.expression instanceof AST_Function || is_iife_call(node.expression);
}
function is_undeclared_ref(node) {
return node instanceof AST_SymbolRef && node.definition().undeclared;
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
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.defined
|| !this.definition().undeclared
|| compressor.option("unsafe") && global_names[this.name];
});
var identifier_atom = makePredicate("Infinity NaN undefined");
function is_identifier_atom(node) {
return node instanceof AST_Infinity
|| node instanceof AST_NaN
|| node instanceof AST_Undefined;
}
function declarations_only(node) {
return all(node.definitions, function(var_def) {
return !var_def.value;
});
}
function is_declaration(stat) {
return stat instanceof AST_Defun || stat instanceof AST_Var && declarations_only(stat);
}
function tighten_body(statements, compressor) {
var in_loop, in_try, scope;
find_loop_scope_try();
var CHANGED, max_iter = 10;
do {
CHANGED = false;
eliminate_spurious_blocks(statements);
if (compressor.option("dead_code")) {
eliminate_dead_code(statements, compressor);
}
if (compressor.option("if_return")) {
handle_if_return(statements, compressor);
}
if (compressor.sequences_limit > 0) {
sequencesize(statements, compressor);
sequencesize_2(statements, compressor);
}
if (compressor.option("join_vars")) {
join_consecutive_vars(statements);
}
if (compressor.option("collapse_vars")) {
collapse(statements, compressor);
}
} while (CHANGED && max_iter-- > 0);
return statements;
function find_loop_scope_try() {
var node = compressor.self(), level = 0;
do {
if (node instanceof AST_Catch) {
if (!compressor.parent(level).bfinally) level++;
} else if (node instanceof AST_Finally) {
level++;
} else if (node instanceof AST_IterationStatement) {
in_loop = true;
} else if (node instanceof AST_Scope) {
scope = node;
break;