Skip to content

Instantly share code, notes, and snippets.

@edmorley
Created May 22, 2018 12:18
Show Gist options
  • Save edmorley/3ab9651d5eab1dcd22720e45f476b452 to your computer and use it in GitHub Desktop.
Save edmorley/3ab9651d5eab1dcd22720e45f476b452 to your computer and use it in GitHub Desktop.
Output of `0x ./node_modules/webpack/bin/webpack.js --mode production`
This file has been truncated, but you can view the full file.
Profiling(node:8760) ExperimentalWarning: The fs.promises API is experimental
.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
} else {
def = defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined);
}
if (!all(def.orig, function(sym) {
if (sym === node) return true;
if (node instanceof AST_SymbolBlockDeclaration) {
return sym instanceof AST_SymbolLambda;
}
return !(sym instanceof AST_SymbolLet || sym instanceof AST_SymbolConst);
})) {
js_error(
node.name + " redeclared",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
AST_SymbolDefun) {
// Careful here, the scope where this should be defined is
// the parent scope. The reason is that we enter a new
// scope when we encounter the AST_Defun node (which is
// instanceof AST_Scope) but we get to the symbol a bit
// later.
mark_export((node.scope = defun.parent_scope.get_defun_scope()).def_function(node, defun), 1);
}
else if (node instanceof AST_SymbolClass) {
mark_export(defun.def_variable(node, defun), 1);
}
else if (node instanceof AST_SymbolImport) {
scope.def_variable(node);
}
else if (node instanceof AST_SymbolDefClass) {
// This deals with the name of the class being available
// inside the class.
mark_export((node.scope = defun.parent_scope).def_function(node, defun), 1);
}
else if (node instanceof AST_SymbolVar
|| node instanceof AST_SymbolLet
|| node instanceof AST_SymbolConst) {
var def;
if (node instanceof AST_SymbolBlockDeclaration) {
def = scope.def_variable(node, null);
} else {
def = defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined);
}
if (!all(def.orig, function(sym) {
if (sym === node) return true;
if (node instanceof AST_SymbolBlockDeclaration) {
return sym instanceof AST_SymbolLambda;
}
return !(sym instanceof AST_SymbolLet || sym instanceof AST_SymbolConst);
})) {
js_error(
node.name + " redeclared",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
st);
})) {
js_error(
node.name + " redeclared",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
= true;
return;
}
if (node instanceof AST_Symbol) {
node.scope = scope;
}
if (node instanceof AST_Label) {
node.thedef = node;
node.references = [];
}
if (node instanceof AST_SymbolLambda) {
defun.def_function(node, node.name == "arguments" ? undefined : defun);
}
else if (node instanceof AST_SymbolDefun) {
// Careful here, the scope where this should be defined is
// the parent scope. The reason is that we enter a new
// scope when we encounter the AST_Defun node (which is
// instanceof AST_Scope) but we get to the symbol a bit
// later.
mark_export((node.scope = defun.parent_scope.get_defun_scope()).def_function(node, defun), 1);
}
else if (node instanceof AST_SymbolClass) {
mark_export(defun.def_variable(node, defun), 1);
}
else if (node instanceof AST_SymbolImport) {
scope.def_variable(node);
}
else if (node instanceof AST_SymbolDefClass) {
// This deals with the name of the class being available
// inside the class.
mark_export((node.scope = defun.parent_scope).def_function(node, defun), 1);
}
else if (node instanceof AST_SymbolVar
|| node instanceof AST_SymbolLet
|| node instanceof AST_SymbolConst) {
var def;
if (node instanceof AST_SymbolBlockDeclaration) {
def = scope.def_variable(node, null);
} else {
def = defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined);
}
if (!all(def.orig, function(sym) {
if (sym === node) return true;
if (node instanceof AST_SymbolBlockDeclaration) {
return sym instanceof AST_SymbolLambda;
}
return !(sym instanceof AST_SymbolLet || sym instanceof AST_SymbolConst);
})) {
js_error(
node.name + " redeclared",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
var def;
if (node instanceof AST_SymbolBlockDeclaration) {
def = scope.def_variable(node, null);
} else {
def = defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined);
}
if (!all(def.orig, function(sym) {
if (sym === node) return true;
if (node instanceof AST_SymbolBlockDeclaration) {
return sym instanceof AST_SymbolLambda;
}
return !(sym instanceof AST_SymbolLet || sym instanceof AST_SymbolConst);
})) {
js_error(
node.name + " redeclared",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
return !(sym instanceof AST_SymbolLet || sym instanceof AST_SymbolConst);
})) {
js_error(
node.name + " redeclared",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
Let || sym instanceof AST_SymbolConst);
})) {
js_error(
node.name + " redeclared",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
e.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
t: tok, end: tok });
break;
}
break;
}
next();
return ret;
}
--- END ---
name: def.name,
scope: node.expression.scope,
thedef: def
});
sym.reference({});
return sym;
}
}
function make_sym(key) {
var new_var = make_node(sym.CTOR, sym, {
name: self.make_var_name(sym.name + "_" + key),
scope: self
});
var def = self.def_variable(new_var);
defs.set(key, def);
self.enclosed.push(def);
return new_var;
}
}
--- END ---
.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
ile":
next();
return new AST_While({
condition : parenthesised(),
body : in_loop(statement)
});
case "for":
next();
return for_();
case "class":
next();
return class_(AST_DefClass);
case "function":
next();
return function_(AST_Defun, false, false, is_export_default);
case "if":
next();
return if_();
case "return":
if (S.in_function == 0 && !options.bare_returns)
croak("'return' outside of function");
next();
var value = null;
if (is("punc", ";")) {
next();
} else if (!can_insert_semicolon()) {
value = expression(true);
semicolon();
}
return new AST_Return({
value: value
});
case "switch":
next();
return new AST_Switch({
expression : parenthesised(),
body : in_loop(switch_body_)
});
case "throw":
next();
if (has_newline_before(S.token))
croak("Illegal newline after 'throw'");
var value = expression(true);
semicolon();
return new AST_Throw({
value: value
});
case "try":
next();
return try_();
case "var":
next();
var node = var_();
semicolon();
return node;
case "let":
next();
var node = let_();
semicolon();
return node;
case "const":
next();
var node = const_();
semicolon();
return node;
case "with":
if (S.input.has_directive("use strict")) {
croak("Strict mode may not include a with statement");
}
next();
return new AST_With({
expression : parenthesised(),
body : statement()
});
case "export":
if (!is_token(peek(), "punc", "(")) {
next();
return export_();
}
}
}
unexpected();
}
--- END ---
case "\x5cf": return "\x5c\x5cf";
case "\x5cx0B": return options.ie8 ? "\x5c\x5cx0B" : "\x5c\x5cv";
case "\x5cu2028": return "\x5c\x5cu2028";
case "\x5cu2029": return "\x5c\x5cu2029";
case "\x5cufeff": return "\x5c\x5cufeff";
case "\x5c0":
return /[0-9]/.test(get_full_char(str, i+1)) ? "\x5c\x5cx00" : "\x5c\x5c0";
}
return s;
});
function quote_single() {
return "'" + str.replace(/\x5cx27/g, "\x5c\x5c'") + "'";
}
function quote_double() {
return '"' + str.replace(/\x5cx22/g, '\x5c\x5c"') + '"';
}
function quote_template() {
return '`' + str.replace(/`/g, '\x5c\x5c`') + '`';
}
str = to_utf8(str);
if (quote === "`") return quote_template();
switch (options.quote_style) {
case 1:
return quote_single();
case 2:
return quote_double();
case 3:
return quote == "'" ? quote_single() : quote_double();
default:
return dq > sq ? quote_single() : quote_double();
}
};
function encode_string(str, quote) {
var ret = make_string(str, quote);
if (options.inline_script) {
ret = ret.replace(/<\x5cx2fscript([>\x5c/\x5ct\x5cn\x5cf\x5cr ])/gi, "<\x5c\x5c/script$1");
ret = ret.replace(/\x5cx3c!--/g, "\x5c\x5cx3c!--");
ret = ret.replace(/--\x5cx3e/g, "--\x5c\x5cx3e");
}
return ret;
};
function make_name(name) {
name = name.toString();
name = to_utf8(name, true);
return name;
};
function make_indent(back) {
return repeat_string(" ", options.indent_start + indentation - back * options.indent_level);
};
/* -----[ beautification/minification ]----- */
var might_need_space = false;
var might_need_semicolon = false;
var might_add_newline = 0;
var need_newline_indented = false;
var need_space = false;
var newline_insert = -1;
var last = "";
var mapping_token, mapping_name, mappings = options.source_map && [];
var do_add_mapping = mappings ? function() {
mappings.forEach(function(mapping) {
try {
options.source_map.add(
mapping.token.file,
mapping.line, mapping.col,
mapping.token.line, mapping.token.col,
!mapping.name && mapping.token.type == "name" ? mapping.token.value : mapping.name
);
} catch(ex) {
AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} \u2192 {cline},{ccol} [{name}]", {
file: mapping.token.file,
line: mapping.token.line,
col: mapping.token.col,
cline: mapping.line,
ccol: mapping.col,
name: mapping.name || ""
})
}
});
mappings = [];
} : noop;
var ensure_line_len = options.max_line_len ? function() {
if (current_col > options.max_line_len) {
if (might_add_newline) {
var left = OUTPUT.slice(0, might_add_newline);
var right = OUTPUT.slice(might_add_newline);
if (mappings) {
var delta = right.length - current_col;
mappings.forEach(function(mapping) {
mapping.line++;
mapping.col += delta;
});
}
OUTPUT = left + "\x5cn" + right;
current_line++;
current_pos++;
current_col = right.length;
}
if (current_col > options.max_line_len) {
AST_Node.warn("Output exceeds {max_line_len} characters", options);
}
}
if (might_add_newline) {
might_add_newline = 0;
do_add_mapping();
--- FUNCTION SOURCE (peek) id{61,-1} start{94549} ---
() { return S.peeked || (S.peeked = S.input()); }
--- END ---
}
} : noop;
var requireSemicolonChars = makePredicate("( [ + * / - , . `");
function print(str) {
str = String(str);
var ch = get_full_char(str, 0);
var prev = get_full_char(last, last.length - 1);
if (need_newline_indented && ch) {
need_newline_indented = false;
if (ch != "\x5cn") {
print("\x5cn");
indent();
}
}
if (need_space && ch) {
need_space = false;
if (!/[\x5cs;})]/.test(ch)) {
space();
}
}
newline_insert = -1;
var prev = last.charAt(last.length - 1);
if (might_need_semicolon) {
might_need_semicolon = false;
if (prev == ":" && ch == "}" || (!ch || ";}".indexOf(ch) < 0) && prev != ";") {
if (options.semicolons || requireSemicolonChars(ch)) {
OUTPUT += ";";
current_col++;
current_pos++;
} else {
ensure_line_len();
OUTPUT += "\x5cn";
current_pos++;
current_line++;
current_col = 0;
if (/^\x5cs+$/.test(str)) {
// reset the semicolon flag, since we didn't print one
// now and might still have to later
might_need_semicolon = true;
}
}
if (!options.beautify)
might_need_space = false;
}
}
if (!options.beautify && options.preserve_line && stack[stack.length - 1]) {
var target_line = stack[stack.length - 1].start.line;
while (current_line < target_line) {
ensure_line_len();
OUTPUT += "\x5cn";
current_pos++;
current_line++;
current_col = 0;
might_need_space = false;
}
}
if (might_need_space) {
if ((is_identifier_char(prev)
&& (is_identifier_char(ch) || ch == "\x5c\x5c"))
|| (ch == "/" && ch == prev)
|| ((ch == "+" || ch == "-") && ch == last))
{
OUTPUT += " ";
current_col++;
current_pos++;
}
might_need_space = false;
}
if (mapping_token) {
mappings.push({
token: mapping_token,
name: mapping_name,
line: current_line,
col: current_col
});
mapping_token = false;
if (!might_add_newline) do_add_mapping();
}
OUTPUT += str;
current_pos += str.length;
var a = str.split(/\x5cr?\x5cn/), n = a.length - 1;
current_line += n;
current_col += a[0].length;
if (n > 0) {
ensure_line_len();
current_col = a[n].length;
}
last = str;
};
var star = function(){
print("*");
}
var space = options.beautify ? function() {
print(" ");
} : function() {
might_need_space = true;
};
var indent = options.beautify ? function(half) {
if (options.beautify) {
print(make_indent(half ? 0.5 : 0));
}
} : noop;
var with_indent = options.beautify ? function(col, cont) {
if (col === true) col = next_indent();
var save_indentation = indentation;
indentation = col;
var ret = cont();
indentation = save_indentation;
return ret;
} : function(col, cont) { return cont() };
var newline = options.beautify ? function() {
if (newline_insert < 0) return print("\x5cn");
if (OUTPUT[newline_insert] != "\x5cn") {
OUTPUT = OUTPUT.slice(0, newline_insert) + "\x5cn" + OUTPUT.slice(newline_insert);
current_pos++;
current_line++;
}
newline_ins--- FUNCTION SOURCE (simple_statement) id{65,1} start{104198} ---
(tmp) {
return new AST_SimpleStatement({ body: (tmp = expression(true), semicolon(), tmp) });
}
--- END ---
ert++;
} : options.max_line_len ? function() {
ensure_line_len();
might_add_newline = OUTPUT.length;
} : noop;
var semicolon = options.beautify ? function() {
print(";");
} : function() {
might_need_semicolon = true;
};
function force_semicolon() {
might_need_semicolon = false;
print(";");
};
function next_indent() {
return indentation + options.indent_level;
};
function with_block(cont) {
var ret;
print("{");
newline();
with_indent(next_indent(), function(){
ret = cont();
});
indent();
print("}");
return ret;
};
function with_parens(cont) {
print("(");
//XXX: still nice to have that for argument lists
//var ret = with_indent(current_col, cont);
var ret = cont();
print(")");
return ret;
};
function with_square(cont) {
print("[");
//var ret = with_indent(current_col, cont);
var ret = cont();
print("]");
return ret;
};
function comma() {
print(",");
space();
};
function colon() {
print(":");
space();
};
var add_mapping = mappings ? function(token, name) {
mapping_token = token;
mapping_name = name;
} : noop;
function get() {
if (might_add_newline) {
ensure_line_len();
}
return OUTPUT;
};
function has_nlb() {
var index = OUTPUT.lastIndexOf("\x5cn");
return /^ *$/.test(OUTPUT.slice(index + 1));
}
function prepend_comments(node) {
var self = this;
var start = node.start;
if (!start) return;
if (start.comments_before && start.comments_before._dumped === self) return;
var comments = start.comments_before;
if (!comments) {
comments = start.comments_before = [];
}
comments._dumped = self;
if (node instanceof AST_Exit && node.value) {
var tw = new TreeWalker(function(node) {
var parent = tw.parent();
if (parent instanceof AST_Exit
|| parent instanceof AST_Binary && parent.left === node
|| parent.TYPE == "Call" && parent.expression === node
|| parent instanceof AST_Conditional && parent.condition === node
|| parent instanceof AST_Dot && parent.expression === node
|| parent instanceof AST_Sequence && parent.expressions[0] === node
|| parent instanceof AST_Sub && parent.expression === node
|| parent instanceof AST_UnaryPostfix) {
if (!node.start) return;
var text = node.start.comments_before;
if (text && text._dumped !== self) {
text._dumped = self;
comments = comments.concat(text);
}
} else {
return true;
}
});
tw.push(node);
node.value.walk(tw);
}
if (current_pos == 0) {
if (comments.length > 0 && options.shebang && comments[0].type == "comment5") {
print("#!" + comments.shift().value + "\x5cn");
indent();
}
var preamble = options.preamble;
if (preamble) {
print(preamble.replace(/\x5cr\x5cn?|[\x5cn\x5cu2028\x5cu2029]|\x5cs*$/g, "\x5cn"));
}
}
comments = comments.filter(comment_filter, node);
if (comments.length == 0) return;
var last_nlb = has_nlb();
comments.forEach(function(c, i) {
if (!last_nlb) {
if (c.nlb) {
print("\x5cn");
indent();
last_nlb = true;
} else if (i > 0) {
space();
}
}
if (/comment[134]/.test(c.type)) {
print("//" + c.value.replace(/[@#]__PURE__/g, ' ') + "\x5cn");
indent();
last_nlb = true;
} else if (c.type == "comment2") {
print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/");
last_nlb = false;
}
});
if (!last_nlb) {
if (start.nlb) {
print("\x5cn");
indent();
} else {
space();
}
}
}
function append_comments(node, tail) {
var self = this;
var token = node.end;
if (!token) return;
var comments = token[tail ? "comments_before" : "comments_after"];
if (!comments || comments._dumped === self) return;
if (!(node instanceof AST_Statement || all(comments, function(c) {
return !/comment[134]/.test(c.type);
}))) return;
comments._dumped = self;
var insert = OUTPUT.length;
comments.filter(comment_filter, node).forEach(function(c, i) {
need_space = false;
if (need_newline_indented) {
print("\x5cn");
indent();
need_newline_indented = false;
} else if (c.nlb && (i > 0 || !has_nlb())) {
print("\x5cn");
indent();
} else if (i > 0 || !tail) {
space();
}
if (/comment[134]/.test(c.type)) {
print("//" + c.value.replace(/[@#]__PURE__/g, ' '));
need_newline_indented = true;
} else if (c.type == "comment2") {
print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/");
need_space = true;
}
});
if (OUTPUT.length > insert) newline_insert = insert;
}
var stack = [];
return {
get : get,
toString : get,
indent : indent,
indentation : function() { return indentation },
current_width : function() { return current_col - indentation },
should_break : function() { return options.width && this.current_width() >= options.width },
newline : newline,
print : print,
star : star,
space : space,
comma : comma,
colon : colon,
last : function() { return last },
semicolon : semicolon,
force_semicolon : force_semicolon,
to_utf8 : to_utf8,
print_name : function(name) { print(make_name(name)) },
print_string : function(str, quote, escape_directive) {
var encoded = encode_string(str, quote);
if (escape_directive === true && encoded.indexOf("\x5c\x5c") === -1) {
// Insert semicolons to break directive prologue
if (!EXPECT_DIRECTIVE.test(OUTPUT)) {
force_semicolon();
}
force_semicolon();
}
print(encoded);
},
print_template_string_chars: function(str) {
var encoded = encode_string(str, '`').replace(/\x5c${/g, "\x5c\x5c${");
return print(encoded.substr(1, encoded.length - 2));
},
encode_string : encode_string,
next_indent : next_indent,
with_indent : with_indent,
with_block : with_block,
with_parens : with_parens,
with_square : with_square,
add_mapping : add_mapping,
option : function(opt) { return options[opt] },
prepend_comments: readonly ? noop : prepend_comments,
append_comments : readonly || comment_filter === return_false ? noop : append_comments,
line : function() { return current_line },
col : function() { return current_col },
pos : function() { return current_pos },
push_node : function(node) { stack.push(node) },
pop_node : function() { return stack.pop() },
parent : function(n) {
return stack[stack.length - 2 - (n || 0)];
}
};
}
--- END ---
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
}
initializations.add(node_def.id, node);
return true; // don't go in nested scopes
}
if (node instanceof AST_SymbolFunarg && scope === self) {
var_defs_by_id.add(node.definition().id, node);
}
if (node instanceof AST_Definitions && scope === self) {
var in_export = tw.parent() instanceof AST_Export;
node.definitions.forEach(function(def){
if (def.name instanceof AST_SymbolVar) {
var_defs_by_id.add(def.name.definition().id, def);
}
if (in_export || !drop_vars) {
def.name.walk(new TreeWalker(function(node) {
if (node instanceof AST_SymbolDeclaration) {
var def = node.definition();
if ((in_export || def.global) && !(def.id in in_use_ids)) {
in_use_ids[def.id] = true;
in_use.push(def);
}
}
}));
}
if (def.value) {
if (def.name instanceof AST_Destructuring) {
var destructuring_cache = destructuring_value;
destructuring_value = def.value;
def.walk(tw);
destructuring_value = destructuring_cache;
} else {
var node_def = def.name.definition();;
initializations.add(node_def.id, def.value);
if (def.name.fixed_value() === def.value) {
fixed_ids[node_def.id] = def;
}
}
if (def.value.has_side_effects(compressor)) {
def.value.walk(tw);
}
}
});
return true;
}
if (node.destructuring && destructuring_value) {
initializations.add(node.name, destructuring_value);
}
return scan_ref_scoped(node, descend);
}
--- END ---
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);
if (def.name instanceof AST_Destructuring) return tail.push(def);
var sym = def.name.definition();
if (drop_block && sym.global) return tail.push(def);
if (!(drop_vars || drop_block) || sym.id in in_use_ids) {
if (def.value && sym.id in fixed_ids && fixed_ids[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)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
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: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[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) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value);
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
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);
if (def.name instanceof AST_Destructuring) return tail.push(def);
var sym = def.name.definition();
if (drop_block && sym.global) return tail.push(def);
if (!(drop_vars || drop_block) || sym.id in in_use_ids) {
if (def.value && sym.id in fixed_ids && fixed_ids[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)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
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: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[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) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value);
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
e.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
anceof AST_SymbolConst);
})) {
js_error(
node.name + " redeclared",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
else if (expr instanceof AST_ForIn) {
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
--- END ---
def = defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined);
}
if (!all(def.orig, function(sym) {
if (sym === node) return true;
if (node instanceof AST_SymbolBlockDeclaration) {
return sym instanceof AST_SymbolLambda;
}
return !(sym instanceof AST_SymbolLet || sym instanceof AST_SymbolConst);
})) {
js_error(
node.name + " redeclared",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
e.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
e.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
{
var destructuring_cache = destructuring_value;
destructuring_value = def.value;
def.walk(tw);
destructuring_value = destructuring_cache;
} else {
var node_def = def.name.definition();;
initializations.add(node_def.id, def.value);
if (def.name.fixed_value() === def.value) {
fixed_ids[node_def.id] = def;
}
}
if (def.value.has_side_effects(compressor)) {
def.value.walk(tw);
}
}
});
return true;
}
if (node.destructuring && destructuring_value) {
initializations.add(node.name, destructuring_value);
}
return scan_ref_scoped(node, descend);
}
--- END ---
if (sym === node) return true;
if (node instanceof AST_SymbolBlockDeclaration) {
return sym instanceof AST_SymbolLambda;
}
return !(sym instanceof AST_SymbolLet || sym instanceof AST_SymbolConst);
})) {
js_error(
node.name + " redeclared",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
{ return cont() };
var newline = options.beautify ? function() {
if (newline_insert < 0) return print("\x5cn");
if (OUTPUT[newline_insert] != "\x5cn") {
OUTPUT = OUTPUT.slice(0, newline_insert) + "\x5cn" + OUTPUT.slice(newline_insert);
current_pos++;
current_line++;
}
newline_insert++;
} : options.max_line_len ? function() {
ensure_line_len();
might_add_newline = OUTPUT.length;
} : noop;
var semicolon = options.beautify ? function() {
print(";");
} : function() {
might_need_semicolon = true;
};
function force_semicolon() {
might_need_semicolon = false;
print(";");
};
function next_indent() {
return indentation + options.indent_level;
};
function with_block(cont) {
var ret;
print("{");
newline();
with_indent(next_indent(), function(){
ret = cont();
});
indent();
print("}");
return ret;
};
function with_parens(cont) {
print("(");
//XXX: still nice to have that for argument lists
//var ret = with_indent(current_col, cont);
var ret = cont();
print(")");
return ret;
};
function with_square(cont) {
print("[");
//var ret = with_indent(current_col, cont);
var ret = cont();
print("]");
return ret;
};
function comma() {
print(",");
space();
};
function colon() {
print(":");
space();
};
var add_mapping = mappings ? function(token, name) {
mapping_token = token;
mapping_name = name;
} : noop;
function get() {
if (might_add_newline) {
ensure_line_len();
}
return OUTPUT;
};
function has_nlb() {
var index = OUTPUT.lastIndexOf("\x5cn");
return /^ *$/.test(OUTPUT.slice(index + 1));
}
function prepend_comments(node) {
var self = this;
var start = node.start;
if (!start) return;
if (start.comments_before && start.comments_before._dumped === self) return;
var comments = start.comments_before;
if (!comments) {
comments = start.comments_before = [];
}
comments._dumped = self;
if (node instanceof AST_Exit && node.value) {
var tw = new TreeWalker(function(node) {
var parent = tw.parent();
if (parent instanceof AST_Exit
|| parent instanceof AST_Binary && parent.left === node
|| parent.TYPE == "Call" && parent.expression === node
|| parent instanceof AST_Conditional && parent.condition === node
|| parent instanceof AST_Dot && parent.expression === node
|| parent instanceof AST_Sequence && parent.expressions[0] === node
|| parent instanceof AST_Sub && parent.expression === node
|| parent instanceof AST_UnaryPostfix) {
if (!node.start) return;
var text = node.start.comments_before;
if (text && text._dumped !== self) {
text._dumped = self;
comments = comments.concat(text);
}
} else {
return true;
}
});
tw.push(node);
node.value.walk(tw);
}
if (current_pos == 0) {
if (comments.length > 0 && options.shebang && comments[0].type == "comment5") {
print("#!" + comments.shift().value + "\x5cn");
indent();
}
var preamble = options.preamble;
if (preamble) {
print(preamble.replace(/\x5cr\x5cn?|[\x5cn\x5cu2028\x5cu2029]|\x5cs*$/g, "\x5cn"));
}
}
comments = comments.filter(comment_filter, node);
if (comments.length == 0) return;
var last_nlb = has_nlb();
comments.forEach(function(c, i) {
if (!last_nlb) {
if (c.nlb) {
print("\x5cn");
indent();
last_nlb = true;
} else if (i > 0) {
space();
}
}
if (/comment[134]/.test(c.type)) {
print("//" + c.value.replace(/[@#]__PURE__/g, ' ') + "\x5cn");
indent();
last_nlb = true;
} else if (c.type == "comment2") {
print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/");
last_nlb = false;
}
});
if (!last_nlb) {
if (start.nlb) {
print("\x5cn");
indent();
} else {
space();
}
}
}
function append_comments(node, tail) {
var self = this;
var token = node.end;
if (!token) return;
var comments = token[tail ? "comments_before" : "comments_after"];
if (!comments || comments._dumped === self) return;
if (!(node instanceof AST_Statement || all(comments, function(c) {
return !/comment[134]/.test(c.type);
}))) return;
comments._dumped = self;
var insert = OUTPUT.length;
comments.filter(comment_filter, node).forEach(function(c, i) {
need_space = false;
if (need_newline_indented) {
print("\x5cn");
indent();
need_newline_indented = false;
} else if (c.nlb && (i > 0 || !has_nlb())) {
print("\x5cn");
indent();
} else if (i > 0 || !tail) {
space();
}
if (/comment[134]/.test(c.type)) {
print("//" + c.value.replace(/[@#]__PURE__/g, ' '));
need_newline_indented = true;
} else if (c.type == "comment2") {
print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/");
need_space = true;
}
});
if (OUTPUT.length > insert) newline_insert = insert;
}
var stack = [];
return {
get : get,
toString : get,
indent : indent,
indentation : function() { return indentation },
current_width : function() { return current_col - indentation },
should_break : function() { return options.width && this.current_width() >= options.width },
newline : newline,
print : print,
star : star,
space : space,
comma : comma,
colon : colon,
last : function() { return last },
semicolon : semicolon,
force_semicolon : force_semicolon,
to_utf8 : to_utf8,
print_name : function(name) { print(make_name(name)) },
print_string : function(str, quote, escape_directive) {
var encoded = encode_string(str, quote);
if (escape_directive === true && encoded.indexOf("\x5c\x5c") === -1) {
// Insert semicolons to break directive prologue
if (!EXPECT_DIRECTIVE.test(OUTPUT)) {
force_semicolon();
}
force_semicolon();
}
print(encoded);
},
print_template_string_chars: function(str) {
var encoded = encode_string(str, '`').replace(/\x5c${/g, "\x5c\x5c${");
return print(encoded.substr(1, encoded.length - 2));
},
encode_string : encode_string,
next_indent : next_indent,
with_indent : with_indent,
with_block : with_block,
with_parens : with_parens,
with_square : with_square,
add_mapping : add_mapping,
option : function(opt) { return options[opt] },
prepend_comments: readonly ? noop : prepend_comments,
append_comments : readonly || comment_filter === return_false ? noop : append_comments,
line : function() { return current_line },
col : function() { return current_col },
pos : function() { return current_pos },
push_node : function(node) { stack.push(node) },
pop_node : function() { return stack.pop() },
parent : function(n) {
return stack[stack.length - 2 - (n || 0)];
}
};
}
--- END ---
e.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
e.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
r node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
node = node.hoist_properties(this);
node = node.hoist_declarations(this);
was_scope = true;
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this);
// Existing code relies on how AST_Node.optimize() worked, and omitting the
// following replacement call would result in degraded efficiency of both
// output and performance.
descend(node, this);
var opt = node.optimize(this);
if (was_scope && opt instanceof AST_Scope) {
opt.drop_unused(this);
descend(opt, this);
}
if (opt === node) opt._squeezed = true;
return opt;
}
--- END ---
e.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
if ((in_export || def.global) && !(def.id in in_use_ids)) {
in_use_ids[def.id] = true;
in_use.push(def);
}
}
}));
}
if (def.value) {
if (def.name instanceof AST_Destructuring) {
var destructuring_cache = destructuring_value;
destructuring_value = def.value;
def.walk(tw);
destructuring_value = destructuring_cache;
} else {
var node_def = def.name.definition();;
initializations.add(node_def.id, def.value);
if (def.name.fixed_value() === def.value) {
fixed_ids[node_def.id] = def;
}
}
if (def.value.has_side_effects(compressor)) {
def.value.walk(tw);
}
}
});
return true;
}
if (node.destructuring && destructuring_value) {
initializations.add(node.name, destructuring_value);
}
return scan_ref_scoped(node, descend);
}
--- END ---
n_strict = compressor.has_directive("use strict");
if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
for (var i = len; --i >= 0;) {
var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue;
names[sym.name] = true;
if (sym instanceof AST_Expansion) {
var elements = iife.args.slice(i);
if (all(elements, function(arg) {
return !has_overlapping_symbol(fn, arg, fn_strict);
})) {
candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym.expression,
value: make_node(AST_Array, iife, {
elements: elements
})
}) ]);
}
} else {
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null;
if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym,
value: arg
}) ]);
}
}
}
}
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (!expr.left.has_side_effects(compressor)) {
candidates.push(hit_stack.slice());
}
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
extract_candidates(expr.right);
} else if (expr instanceof AST_Call) {
extract_candidates(expr.expression);
expr.args.forEach(extract_candidates);
} else if (expr instanceof AST_Case) {
extract_candidates(expr.expression);
} else if (expr instanceof AST_Conditional) {
extract_candidates(expr.condition);
extract_candidates(expr.consequent);
extract_candidates(expr.alternative);
} else if (expr instanceof AST_Definitions
&& (compressor.option("unused") || !(expr instanceof AST_Const))) {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_Exit) {
if (expr.value) extract_candidates(expr.value);
} else if (expr instanceof AST_For) {
if (expr.init) extract_candidates(expr.init);
if (expr.condition) extract_candidates(expr.condition);
if (expr.step) extract_candidates(expr.step);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
function mangleable_var(var_def) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return;
var def = value.definition();
if (def.undeclared) return;
return value_def = def;
}
function get_lhs(expr) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
return !is_ref_of(lhs, AST_SymbolConst) && lhs;
}
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var tw = new TreeWalker(function(node, descend) {
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
get_rvalue(expr).walk(tw);
return lvalues;
}
function lhs_or_def(node) {
if (node instanceof AST_VarDef) return node.value && node.name;
return is_lhs(node.left, node);
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var iife = compressor.parent(), argnames = compressor.self().argnames;
var index = argnames.indexOf(expr.name);
if (index < 0) {
iife.args.length = Math.min(iife.args.length, argnames.length - 1);
} else {
var args = iife.args;
if (args[index]) args[index] = make_node(AST_Number, args[index], {
value: 0
});
}
return true;
}
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
node.value = null;
return node;
}
return in_list ? MAP.skip : null;
}
}, function(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
}
}));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope.get_defun_scope() !== scope) return false;
return def.references.every(function(ref) {
return ref.scope.get_defun_scope() === scope;
});
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
}
}
--- END ---
e.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
case "undefined":
return make_node(AST_Undefined, self).optimize(compressor);
case "NaN":
return make_node(AST_NaN, self).optimize(compressor);
case "Infinity":
return make_node(AST_Infinity, self).optimize(compressor);
}
}
if (compressor.option("reduce_vars")
&& is_lhs(self, compressor.parent()) !== self) {
var d = self.definition();
var fixed = self.fixed_value();
var single_use = d.single_use;
if (single_use && (fixed instanceof AST_Lambda || fixed instanceof AST_Class)) {
if (d.scope !== self.scope
&& (!compressor.option("reduce_funcs") && fixed instanceof AST_Lambda
|| d.escaped == 1
|| fixed.inlined)) {
single_use = false;
} else if (recursive_ref(compressor, d)) {
single_use = false;
} else if (d.scope !== self.scope || d.orig[0] instanceof AST_SymbolFunarg) {
single_use = fixed.is_constant_expression(self.scope);
if (single_use == "f") {
var scope = self.scope;
do {
if (scope instanceof AST_Defun || is_func_expr(scope)) {
scope.inlined = true;
}
} while (scope = scope.parent_scope);
}
}
}
if (single_use && fixed) {
if (fixed instanceof AST_DefClass) {
fixed = make_node(AST_ClassExpression, fixed, fixed);
}
if (fixed instanceof AST_Defun) {
fixed._squeezed = true;
fixed = make_node(AST_Function, fixed, fixed);
}
var value;
if (d.recursive_refs > 0 && fixed.name instanceof AST_SymbolDefun) {
value = fixed.clone(true);
var defun_def = value.name.definition();
var lambda_def = value.variables.get(value.name.name);
var name = lambda_def && lambda_def.orig[0];
if (!(name instanceof AST_SymbolLambda)) {
name = make_node(AST_SymbolLambda, value.name, value.name);
name.scope = value;
value.name = name;
lambda_def = value.def_function(name);
}
value.walk(new TreeWalker(function(node) {
if (node instanceof AST_SymbolRef && node.definition() === defun_def) {
node.thedef = lambda_def;
lambda_def.references.push(node);
}
}));
} else {
value = fixed.optimize(compressor);
if (value === fixed) value = fixed.clone(true);
}
return value;
}
if (fixed && d.should_replace === undefined) {
var init;
if (fixed instanceof AST_This) {
if (!(d.orig[0] instanceof AST_SymbolFunarg)
&& all(d.references, function(ref) {
return d.scope === ref.scope;
})) {
init = fixed;
}
} else {
var ev = fixed.evaluate(compressor);
if (ev !== fixed && (compressor.option("unsafe_regexp") || !(ev instanceof RegExp))) {
init = make_node_from_constant(ev, fixed);
}
}
if (init) {
var value_length = init.optimize(compressor).print_to_string().length;
var fn;
if (has_symbol_ref(fixed)) {
fn = function() {
var result = init.optimize(compressor);
return result === init ? result.clone(true) : result;
};
} else {
value_length = Math.min(value_length, fixed.print_to_string().length);
fn = function() {
var result = best_of_expression(init.optimize(compressor), fixed);
return result === init || result === fixed ? result.clone(true) : result;
};
}
var name_length = d.name.length;
var overhead = 0;
if (compressor.option("unused") && !compressor.exposed(d)) {
overhead = (name_length + 2 + value_length) / d.references.length;
}
d.should_replace = value_length <= name_length + overhead ? fn : false;
} else {
d.should_replace = false;
}
}
if (d.should_replace) {
return d.should_replace();
}
}
return self;
function has_symbol_ref(value) {
var found;
value.walk(new TreeWalker(function(node) {
if (node instanceof AST_SymbolRef) found = true;
if (found) return true;
}));
return found;
}
}
--- END ---
--- F--- FUNCTION SOURCE (_walk) id{263,-1} start{38972} ---
(visitor) {
return visitor._visit(this, function(){
this.left._walk(visitor);
this.right._walk(visitor);
});
}
--- END ---
UNCTION SOURCE () id{223,-1} start{168775} ---
(self, tw){
self.condition = self.condition.transform(tw);
self.consequent = self.consequent.transform(tw);
self.alternative = self.alternative.transform(tw);
}
--- END ---
apsible assignments
var hit_stack = [];
extract_candidates(statements[stat_index]);
while (candidates.length > 0) {
hit_stack = candidates.pop();
var hit_index = 0;
var candidate = hit_stack[hit_stack.length - 1];
var value_def = null;
var stop_after = null;
var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue;
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
var replace_all = value_def;
if (!replace_all && lhs instanceof AST_SymbolRef) {
var def = lhs.definition();
if (def.references.length - def.replaced == (candidate instanceof AST_VarDef ? 1 : 2)) {
replace_all = true;
}
}
var side_effects = value_has_side_effects(candidate);
var may_throw = candidate.may_throw(compressor);
var funarg = candidate.name instanceof AST_SymbolFunarg;
var hit = funarg;
var abort = false, replaced = 0, can_replace = !args || !hit;
if (!can_replace) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) {
args[j].transform(scanner);
}
can_replace = true;
}
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(scanner);
}
if (value_def) {
var def = candidate.name.definition();
if (abort && def.references.length - def.replaced > replaced) replaced = false;
else {
abort = false;
hit_index = 0;
hit = funarg;
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(multi_replacer);
}
value_def.single_use = false;
}
}
if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
}
}
function has_overlapping_symbol(fn, arg, fn_strict) {
var found = false, scan_this = !(fn instanceof AST_Arrow);
arg.walk(new TreeWalker(function(node, descend) {
if (found) return true;
if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
var s = node.definition().scope;
if (s !== scope) while (s = s.parent_scope) {
if (s === scope) return true;
}
return found = true;
}
if ((fn_strict || scan_this) && node instanceof AST_This) {
return found = true;
}
if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) {
var prev = scan_this;
scan_this = false;
descend();
scan_this = prev;
return true;
}
}));
return found;
}
function extract_args() {
var iife, fn = compressor.self();
if (is_func_expr(fn)
&& !fn.name
--- FUNCTION SOURCE (walk) id{239,-1} start{15000} ---
(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
--- END ---
&& !fn.uses_arguments
&& !fn.uses_eval
&& (iife = compressor.parent()) instanceof AST_Call
&& iife.expression === fn
&& all(iife.args, function(arg) {
return !(arg instanceof AST_Expansion);
})) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
for (var i = len; --i >= 0;) {
var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue;
names[sym.name] = true;
if (sym instanceof AST_Expansion) {
var elements = iife.args.slice(i);
if (all(elements, function(arg) {
return !has_overlapping_symbol(fn, arg, fn_strict);
})) {
candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym.expression,
value: make_node(AST_Array, iife, {
elements: elements
})
}) ]);
}
} else {
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null;
if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym,
value: arg
}) ]);
}
}
}
}
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (!expr.left.has_side_effects(compressor)) {
candidates.push(hit_stack.slice());
}
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
extract_candidates(expr.right);
} else if (expr instanceof AST_Call) {
extract_candidates(expr.expression);
expr.args.forEach(extract_candidates);
} else if (expr instanceof AST_Case) {
extract_candidates(expr.expression);
} else if (expr instanceof AST_Conditional) {
extract_candidates(expr.condition);
extract_candidates(expr.consequent);
extract_candidates(expr.alternative);
} else if (expr instanceof AST_Definitions
&& (compressor.option("unused") || !(expr instanceof AST_Const))) {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_Exit) {
if (expr.value) extract_candidates(expr.value);
} else if (expr instanceof AST_For) {
if (expr.init) extract_candidates(expr.init);
if (expr.condition) extract_candidates(expr.condition);
if (expr.step) extract_candidates(expr.step);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
function mangleable_var(var_def) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return;
var def = value.definition();
if (def.undeclared) return;
return value_def = def;
}
function get_lhs(expr) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
return !is_ref_of(lhs, AST_SymbolConst) && lhs;
}
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var tw = new TreeWalker(function(node, descend) {
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
get_rvalue(expr).walk(tw);
return lvalues;
}
function lhs_or_def(node) {
if (node instanceof AST_VarDef) return node.value && node.name;
return is_lhs(node.left, node);
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var iife = compressor.parent(), argnames = compressor.self().argnames;
var index = argnames.indexOf(expr.name);
if (index < 0) {
iife.args.length = Math.min(iife.args.length, argnames.length - 1);
} else {
var args = iife.args;
if (args[index]) args[index] = make_node(AST_Number, args[index], {
value: 0
});
}
return true;
}
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
node.value = null;
return node;
}
return in_list ? MAP.skip : null;
}
}, function(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
}
}));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope.get_defun_scope() !== scope) return false;
return def.references.every(function(ref) {
return ref.scope.get_defun_scope() === scope;
});
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
}
}
--- END ---
hit_stack.slice());
}
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
extract_candidates(expr.right);
} else if (expr instanceof AST_Call) {
extract_candidates(expr.expression);
expr.args.forEach(extract_candidates);
} else if (expr instanceof AST_Case) {
extract_candidates(expr.expression);
} else if (expr instanceof AST_Conditional) {
extract_candidates(expr.condition);
extract_candidates(expr.consequent);
extract_candidates(expr.alternative);
} else if (expr instanceof AST_Definitions
&& (compressor.option("unused") || !(expr instanceof AST_Const))) {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_Exit) {
if (expr.value) extract_candidates(expr.value);
} else if (expr instanceof AST_For) {
if (expr.init) extract_candidates(expr.init);
if (expr.condition) extract_candidates(expr.condition);
if (expr.step) extract_candidates(expr.step);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
--- END ---
l.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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
n) {
return sym instanceof AST_SymbolLambda;
}
return !(sym instanceof AST_SymbolLet || sym instanceof AST_SymbolConst);
})) {
js_error(
node.name + " redeclared",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
dex = 0;
var candidate = hit_stack[hit_stack.length - 1];
var value_def = null;
var stop_after = null;
var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue;
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
var replace_all = value_def;
if (!replace_all && lhs instanceof AST_SymbolRef) {
var def = lhs.definition();
if (def.references.length - def.replaced == (candidate instanceof AST_VarDef ? 1 : 2)) {
replace_all = true;
}
}
var side_effects = value_has_side_effects(candidate);
var may_throw = candidate.may_throw(compressor);
var funarg = candidate.name instanceof AST_SymbolFunarg;
var hit = funarg;
var abort = false, replaced = 0, can_replace = !args || !hit;
if (!can_replace) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) {
args[j].transform(scanner);
}
can_replace = true;
}
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(scanner);
}
if (value_def) {
var def = candidate.name.definition();
if (abort && def.references.length - def.replaced > replaced) replaced = false;
else {
abort = false;
hit_index = 0;
hit = funarg;
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(multi_replacer);
}
value_def.single_use = false;
}
}
if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
}
}
function has_overlapping_symbol(fn, arg, fn_strict) {
var found = false, scan_this = !(fn instanceof AST_Arrow);
arg.walk(new TreeWalker(function(node, descend) {
if (found) return true;
if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
var s = node.definition().scope;
if (s !== scope) while (s = s.parent_scope) {
if (s === scope) return true;
}
return found = true;
}
if ((fn_strict || scan_this) && node instanceof AST_This) {
return found = true;
}
if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) {
var prev = scan_this;
scan_this = false;
descend();
scan_this = prev;
return true;
}
}));
return found;
}
function extract_args() {
var iife, fn = compressor.self();
if (is_func_expr(fn)
&& !fn.name
&& !fn.uses_arguments
&& !fn.uses_eval
&& (iife = compressor.parent()) instanceof AST_Call
&& iife.expression === fn
&& all(iife.args, function(arg) {
return !(arg instanceof AST_Expansion);
})) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
for (var i = len; --i >= 0;) {
var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue;
names[sym.name] = true;
if (sym instanceof AST_Expansion) {
var elements = iife.args.slice(i);
if (all(elements, function(arg) {
return !has_overlapping_symbol(fn, arg, fn_strict);
})) {
candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym.expression,
value: make_node(AST_Array, iife, {
elements: elements
})
}) ]);
}
} else {
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null;
if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym,
value: arg
}) ]);
}
}
}
}
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (!expr.left.has_side_effects(compressor)) {
candidates.push(hit_stack.slice());
}
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
extract_candidates(expr.right);
} else if (expr instanceof AST_Call) {
extract_candidates(expr.expression);
expr.args.forEach(extract_candidates);
} else if (expr instanceof AST_Case) {
extract_candidates(expr.expression);
} else if (expr instanceof AST_Conditional) {
extract_candidates(expr.condition);
extract_candidates(expr.consequent);
extract_candidates(expr.alternative);
} else if (expr instanceof AST_Definitions
&& (compressor.option("unused") || !(expr instanceof AST_Const))) {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_Exit) {
if (expr.value) extract_candidates(expr.value);
} else if (expr instanceof AST_For) {
if (expr.init) extract_candidates(expr.init);
if (expr.condition) extract_candidates(expr.condition);
if (expr.step) extract_candidates(expr.step);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
function mangleable_var(var_def) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return;
var def = value.definition();
if (def.undeclared) return;
return value_def = def;
}
function get_lhs(expr) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
return !is_ref_of(lhs, AST_SymbolConst) && lhs;
}
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var tw = new TreeWalker(function(node, descend) {
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
get_rvalue(expr).walk(tw);
return lvalues;
}
function lhs_or_def(node) {
if (node instanceof AST_VarDef) return node.value && node.name;
return is_lhs(node.left, node);
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var iife = compressor.parent(), argnames = compressor.self().argnames;
var index = argnames.indexOf(expr.name);
if (index < 0) {
iife.args.length = Math.min(iife.args.length, argnames.length - 1);
} else {
var args = iife.args;
if (args[index]) args[index] = make_node(AST_Number, args[index], {
value: 0
});
}
return true;
}
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
node.value = null;
return node;
}
return in_list ? MAP.skip : null;
}
}, function(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
}
}));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope.get_defun_scope() !== scope) return false;
return def.references.every(function(ref) {
return ref.scope.get_defun_scope() === scope;
});
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
}
}
--- END ---
effects = [];
node.definitions.forEach(function(def) {
if (def.value) def.value = def.value.transform(tt);
if (def.name instanceof AST_Destructuring) return tail.push(def);
var sym = def.name.definition();
if (drop_block && sym.global) return tail.push(def);
if (!(drop_vars || drop_block) || sym.id in in_use_ids) {
if (def.value && sym.id in fixed_ids && fixed_ids[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)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
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: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[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) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value);
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
sym.eliminated++;
}
});
if (head.length > 0 || tai--- FUNCTION SOURCE (option) id{190,9} start{267146} ---
(key) { return this.options[key] }
--- END ---
l.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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
l.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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
tack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
--- END ---
l.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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
) {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
--- END ---
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) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value);
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
);
self.transform(tt);
function scan_ref_scoped(node, descend) {
var node_def, 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.left.fixed_value() === node.right) {
fixed_ids[node_def.id] = node;
}
}
return true;
}
if (node instanceof AST_SymbolRef) {
node_def = node.definition();
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
if (node_def = node_def.redefined()) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
}
return true;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend();
scope = save_scope;
return true;
}
}
}
--- END ---
ly as possible
if (!hit) {
if (node !== hit_stack[hit_index]) return node;
hit_index++;
if (hit_index < hit_stack.length) return;
hit = true;
return node;
}
// Replace variable when found
if (node instanceof AST_SymbolRef
&& node.name == def.name) {
if (!--replaced) abort = true;
if (is_lhs(node, multi_replacer.parent())) return node;
def.replaced++;
value_def.replaced--;
return candidate.value;
}
// Skip (non-executed) functions and (leading) default case in switch statements
if (node instanceof AST_Default || node instanceof AST_Scope) return node;
});
while (--stat_index >= 0) {
// Treat parameters as collapsible in IIFE, i.e.
// function(a, b){ ... }(x());
// would be translated into equivalent assignments:
// var a = x(), b = undefined;
if (stat_index == 0 && compressor.option("unused")) extract_args();
// Find collapsible assignments
var hit_stack = [];
extract_candidates(statements[stat_index]);
while (candidates.length > 0) {
hit_stack = candidates.pop();
var hit_index = 0;
var candidate = hit_stack[hit_stack.length - 1];
var value_def = null;
var stop_after = null;
var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue;
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
var replace_all = value_def;
if (!replace_all && lhs instanceof AST_SymbolRef) {
var def = lhs.definition();
if (def.references.length - def.replaced == (candidate instanceof AST_VarDef ? 1 : 2)) {
replace_all = true;
}
}
var side_effects = value_has_side_effects(candidate);
var may_throw = candidate.may_throw(compressor);
var funarg = candidate.name instanceof AST_SymbolFunarg;
var hit = funarg;
var abort = false, replaced = 0, can_replace = !args || !hit;
if (!can_replace) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) {
args[j].transform(scanner);
}
can_replace = true;
}
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(scanner);
}
if (value_def) {
var def = candidate.name.definition();
if (abort && def.references.length - def.replaced > replaced) replaced = false;
else {
abort = false;
hit_index = 0;
hit = funarg;
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(multi_replacer);
}
value_def.single_use = false;
}
}
if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
}
}
function has_overlapping_symbol(fn, arg, fn_strict) {
var found = false, scan_this = !(fn instanceof AST_Arrow);
arg.walk(new TreeWalker(function(node, descend) {
if (found) return true;
if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
var s = node.definition().scope;
if (s !== scope) while (s = s.parent_scope) {
if (s === scope) return true;
}
return found = true;
}
if ((fn_strict || scan_this) && node instanceof AST_This) {
return found = true;
}
if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) {
var prev = scan_this;
scan_this = false;
descend();
scan_this = prev;
return true;
}
}));
return found;
}
function extract_args() {
var iife, fn = compressor.self();
if (is_func_expr(fn)
&& !fn.name
&& !fn.uses_arguments
&& !fn.uses_eval
&& (iife = compressor.parent()) instanceof AST_Call
&& iife.expression === fn
&& all(iife.args, function(arg) {
return !(arg instanceof AST_Expansion);
})) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
for (var i = len; --i >= 0;) {
var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue;
names[sym.name] = true;
if (sym instanceof AST_Expansion) {
var elements = iife.args.slice(i);
if (all(elements, function(arg) {
return !has_overlapping_symbol(fn, arg, fn_strict);
})) {
candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym.expression,
value: make_node(AST_Array, iife, {
elements: elements
})
}) ]);
}
} else {
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null;
if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym,
value: arg
}) ]);
}
}
}
}
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (!expr.left.has_side_effects(compressor)) {
candidates.push(hit_stack.slice());
}
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
extract_candidates(expr.right);
} else if (expr instanceof AST_Call) {
extract_candidates(expr.expression);
expr.args.forEach(extract_candidates);
} else if (expr instanceof AST_Case) {
extract_candidates(expr.expression);
} else if (expr instanceof AST_Conditional) {
extract_candidates(expr.condition);
extract_candidates(expr.consequent);
extract_candidates(expr.alternative);
} else if (expr instanceof AST_Definitions
&& (compressor.option("unused") || !(expr instanceof AST_Const))) {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_Exit) {
if (expr.value) extract_candidates(expr.value);
} else if (expr instanceof AST_For) {
if (expr.init) extract_candidates(expr.init);
if (expr.condition) extract_candidates(expr.condition);
if (expr.step) extract_candidates(expr.step);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
function mangleable_var(var_def) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return;
var def = value.definition();
if (def.undeclared) return;
return value_def = def;
}
function get_lhs(expr) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
return !is_ref_of(lhs, AST_SymbolConst) && lhs;
}
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var tw = new TreeWalker(function(node, descend) {
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
get_rvalue(expr).walk(tw);
return lvalues;
}
function lhs_or_def(node) {
if (node instanceof AST_VarDef) return node.value && node.name;
return is_lhs(node.left, node);
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var iife = compressor.parent(), argnames = compressor.self().argnames;
var index = argnames.indexOf(expr.name);
if (index < 0) {
iife.args.length = Math.min(iife.args.length, argnames.length - 1);
} else {
var args = iife.args;
if (args[index]) args[index] = make_node(AST_Number, args[index], {
value: 0
});
}
return true;
}
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
node.value = null;
return node;
}
return in_list ? MAP.skip : null;
}
}, function(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
}
}));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope.get_defun_scope() !== scope) return false;
return def.references.every(function(ref) {
return ref.scope.get_defun_scope() === scope;
});
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
}
}
function eliminate_spurious_blocks(statements) {
var seen_dirs = [];
for (var i = 0; i < statements.length;) {
var stat = statements[i];
if (stat instanceof AST_BlockStatement && all(stat.body, can_be_evicted_from_block)) {
CHANGED = true;
eliminate_spurious_blocks(stat.body);
[].splice.apply(statements, [i, 1].concat(stat.body));
i += stat.body.length;
} else if (stat instanceof AST_EmptyStatement) {
CHANGED = true;
statements.splice(i, 1);
} else if (stat instanceof AST_Directive) {
if (seen_dirs.indexOf(stat.value) < 0) {
i++;
seen_dirs.push(stat.value);
} else {
CHANGED = true;
statements.splice(i, 1);
}
} else i++;
}
}
function handle_if_return(statements, compressor) {
var self = compressor.self();
var multiple_if_returns = has_multiple_if_returns(statements);
var in_lambda = self instanceof AST_Lambda;
for (var i = statements.length; --i >= 0;) {
var stat = statements[i];
var j = next_index(i);
var next = statements[j];
if (in_lambda && !next && stat instanceof AST_Return) {
if (!stat.value) {
CHANGED = true;
statements.splice(i, 1);
continue;
}
if (stat.value instanceof AST_UnaryPrefix && stat.value.operator == "void") {
CHANGED = true;
statements[i] = make_node(AST_SimpleStatement, stat, {
body: stat.value.expression
});
continue;
}
}
if (stat instanceof AST_If) {
var ab = aborts(stat.body);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
var body = as_statement_array_with_return(stat.body, ab);
stat.body = make_node(AST_BlockStatement, stat, {
body: as_statement_array(stat.alternative).concat(extract_functions())
});
stat.alternative = make_node(AST_BlockStatement, stat, {
body: body
});
statements[i] = stat.transform(compressor);
continue;
}
var ab = aborts(stat.alternative);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
stat = stat.clone();
stat.body = make_node(AST_BlockStatement, stat.body, {
body: as_statement_array(stat.body).concat(extract_functions())
});
var body = as_statement_array_with_return(stat.alternative, ab);
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
body: body
});
statements[i] = stat.transform(compressor);
continue;
}
}
if (stat instanceof AST_If && stat.body instanceof AST_Return) {
var value = stat.body.value;
//---
// pretty silly case, but:
// if (foo()) return; return; ==> foo(); return;
if (!value && !stat.alternative
&& (in_lambda && !next || next instanceof AST_Return && !next.value)) {
CHANGED = true;
statements[i] = make_node(AST_SimpleStatement, stat.condition, {
body: stat.condition
});
continue;
}
//---
// if (foo()) return x; return y; ==> return foo() ? x : y;
if (value && !stat.alternative && next instanceof AST_Return && next.value) {
CHANGED = true;
stat = stat.clone();
stat.alternative = next;
statements.splice(i, 1, stat.transform(compressor));
statements.splice(j, 1);
continue;
}
//---
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
if (value && !stat.alternative
&& (!next && in_lambda && multiple_if_returns
|| next instanceof AST_Return)) {
CHANGED = true;
stat = stat.clone();
stat.alternative = next || make_node(AST_Return, stat, {
value: null
});
statements.splice(i, 1, stat.transform(compressor));
if (next) statements.splice(j, 1);
continue;
}
//---
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
//
// if sequences is not enabled, this can lead to an endless loop (issue #866).
// however, with sequences on this helps producing slightly better output for
// the example code.
var prev = statements[prev_index(i)];
if (compressor.option("sequences") && in_lambda && !stat.alternative
&& prev instanceof AST_If && prev.body instanceof AST_Return
&& next_index(j) == statements.length && next instanceof AST_SimpleStatement) {
CHANGED = true;
stat = stat.clone();
stat.alternative = make_node(AST_BlockStatement, next, {
body: [
next,
make_node(AST_Return, next, {
value: null
})
]
});
statements.splice(i, 1, stat.transform(compressor));
statements.splice(j, 1);
continue;
}
}
}
function has_multiple_if_returns(statements) {
var n = 0;
for (var i = statements.length; --i >= 0;) {
var stat = statements[i];
if (stat instanceof AST_If && stat.body instanceof AST_Return) {
if (++n > 1) return true;
}
}
return false;
}
function is_return_void(value) {
return !value || value instanceof AST_UnaryPrefix && value.operator == "void";
}
function can_merge_flow(ab) {
if (!ab) return false;
for (var j = i + 1, len = statements.length; j < len; j++) {
var stat = statements[j];
if (stat instanceof AST_Const || stat instanceof AST_Let) return false;
}
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
return ab instanceof AST_Return && in_lambda && is_return_void(ab.value)
|| ab instanceof AST_Continue && self === loop_body(lct)
|| ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct;
}
function extract_functions() {
var tail = statements.slice(i + 1);
statements.length = i + 1;
return tail.filter(function(stat) {
if (stat instanceof AST_Defun) {
statements.push(stat);
return false;
}
return true;
});
}
function as_statement_array_with_return(node, ab) {
var body = as_statement_array(node).slice(0, -1);
if (ab.value) {
body.push(make_node(AST_SimpleStatement, ab.value, {
body: ab.value.expression
}));
}
return body;
}
function next_index(i) {
for (var j = i + 1, len = statements.length; j < len; j++) {
var stat = statements[j];
if (!(stat instanceof AST_Var && declarations_only(stat))) {
break;
}
}
return j;
}
function prev_index(i) {
for (var j = i; --j >= 0;) {
var stat = statements[j];
if (!(stat instanceof AST_Var && declarations_only(stat))) {
break;
}
}
return j;
}
}
function eliminate_dead_code(statements, compressor) {
var has_quit;
var self = compressor.self();
for (var i = 0, n = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (stat instanceof AST_LoopControl) {
var lct = compressor.loopcontrol_target(stat);
if (stat instanceof AST_Break
&& !(lct instanceof AST_IterationStatement)
&& loop_body(lct) === self
|| stat instanceof AST_Continue
&& loop_body(lct) === self) {
if (stat.label) {
remove(stat.label.thedef.references, stat);
}
} else {
statements[n++] = stat;
}
} else {
statements[n++] = stat;
}
if (aborts(stat)) {
has_quit = statements.slice(i + 1);
break;
}
}
statements.length = n;
CHANGED = n != len;
if (has_quit) has_quit.forEach(function(stat) {
extract_declarations_from_unreachable_code(compressor, stat, statements);
});
}
function declarations_only(node) {
return all(node.definitions, function(var_def) {
return !var_def.value;
});
}
function sequencesize(statements, compressor) {
if (statements.length < 2) return;
var seq = [], n = 0;
function push_seq() {
if (!seq.length) return;
var body = make_sequence(seq[0], seq);
statements[n++] = make_node(AST_SimpleStatement, body, { body: body });
seq = [];
}
for (var i = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (stat instanceof AST_SimpleStatement) {
if (seq.length >= compressor.sequences_limit) push_seq();
var body = stat.body;
if (seq.length > 0) body = body.drop_side_effect_free(compressor);
if (body) merge_sequence(seq, body);
} else if (stat instanceof AST_Definitions && declarations_only(stat)
|| stat instanceof AST_Defun) {
statements[n++] = stat;
} else {
push_seq();
statements[n++] = stat;
}
}
push_seq();
statements.length = n;
if (n != len) CHANGED = true;
}
function to_simple_statement(block, decls) {
if (!(block instanceof AST_BlockStatement)) return block;
var stat = null;
for (var i = 0, len = block.body.length; i < len; i++) {
var line = block.body[i];
if (line instanceof AST_Var && declarations_only(line)) {
decls.push(line);
} else if (stat) {
return false;
} else {
stat = line;
}
}
return stat;
}
function sequencesize_2(statements, compressor) {
function cons_seq(right) {
n--;
CHANGED = true;
var left = prev.body;
return make_sequence(left, [ left, right ]).transform(compressor);
};
var n = 0, prev;
for (var i = 0; i < statements.length; i++) {
var stat = statements[i];
if (prev) {
if (stat instanceof AST_Exit) {
stat.value = cons_seq(stat.value || make_node(AST_Undefined, stat).transform(compressor));
} else if (stat instanceof AST_For) {
if (!(stat.init instanceof AST_Definitions)) {
var abort = false;
prev.body.walk(new TreeWalker(function(node) {
if (abort || node instanceof AST_Scope) return true;
if (node instanceof AST_Binary && node.operator == "in") {
abort = true;
return true;
}
}));
if (!abort) {
if (stat.init) stat.init = cons_seq(stat.init);
else {
stat.init = prev.body;
n--;
CHANGED = true;
}
}
}
} else if (stat instanceof AST_ForIn) {
if (!(stat.init instanceof AST_Const) && !(stat.init instanceof AST_Let)) {
stat.object = cons_seq(stat.object);
}
} else if (stat instanceof AST_If) {
stat.condition = cons_seq(stat.condition);
} else if (stat instanceof AST_Switch) {
stat.expression = cons_seq(stat.expression);
} else if (stat instanceof AST_With) {
stat.expression = cons_seq(stat.expression);
}
}
if (compressor.option("conditionals") && stat instanceof AST_If) {
var decls = [];
var body = to_simple_statement(stat.body, decls);
var alt = to_simple_statement(stat.alternative, decls);
if (body !== false && alt !== false && decls.length > 0) {
var len = decls.length;
decls.push(make_node(AST_If, stat, {
condition: stat.condition,
body: body || make_node(AST_EmptyStatement, stat.body),
alternative: alt
}));
decls.unshift(n, 1);
[].splice.apply(statements, decls);
i += len;
n += len + 1;
prev = null;
CHANGED = true;
continue;
}
}
statements[n++] = stat;
prev = stat instanceof AST_SimpleStatement ? stat : null;
}
statements.length = n;
}
function join_object_assignments(defn, body) {
if (!(defn instanceof AST_Definitions)) return;
var def = defn.definitions[defn.definitions.length - 1];
if (!(def.value instanceof AST_Object)) return;
var exprs;
if (body instanceof AST_Assign) {
exprs = [ body ];
} else if (body instanceof AST_Sequence) {
exprs = body.expressions.slice();
}
if (!exprs) return;
var trimmed = false;
do {
var node = exprs[0];
if (!(node instanceof AST_Assign)) break;
if (node.operator != "=") break;
if (!(node.left instanceof AST_PropAccess)) break;
var sym = node.left.expression;
if (!(sym instanceof AST_SymbolRef)) break;
if (def.name.name != sym.name) break;
if (!node.right.is_constant_expression(scope)) break;
var prop = node.left.property;
if (prop instanceof AST_Node) {
prop = prop.evaluate(compressor);
}
if (prop instanceof AST_Node) break;
prop = "" + prop;
if (compressor.option("ecma") < 6 && compressor.has_directive("use strict")) {
if (!all(def.value.properties, function(node) {
return node.key != prop && node.key.name != prop;
})) break;
}
def.value.properties.push(make_node(AST_ObjectKeyVal, node, {
key: prop,
value: node.right
}));
exprs.shift();
trimmed = true;
} while (exprs.length);
return trimmed && exprs;
}
function join_consecutive_vars(statements) {
var defs;
for (var i = 0, j = -1, len = statements.length; i < len; i++) {
var stat = statements[i];
var prev = statements[j];
if (stat instanceof AST_Definitions) {
if (prev && prev.TYPE == stat.TYPE) {
prev.definitions = prev.definitions.concat(stat.definitions);
CHANGED = true;
} else if (defs && defs.TYPE == stat.TYPE && declarations_only(stat)) {
defs.definitions = defs.definitions.concat(stat.definitions);
CHANGED = true;
} else {
statements[++j] = stat;
defs = stat;
}
} else if (stat instanceof AST_Exit) {
stat.value = extract_object_assignments(stat.value);
} else if (stat instanceof AST_For) {
var exprs = join_object_assignments(prev, stat.init);
if (exprs) {
CHANGED = true;
stat.init = exprs.length ? make_sequence(stat.init, exprs) : null;
statements[++j] = stat;
} else if (prev instanceof AST_Var && (!stat.init || stat.init.TYPE == prev.TYPE)) {
if (stat.init) {
prev.definitions = prev.definitions.concat(stat.init.definitions);
}
stat.init = prev;
statements[j] = stat;
CHANGED = true;
} else if (defs && stat.init && defs.TYPE == stat.init.TYPE && declarations_only(stat.init)) {
defs.definitions = defs.definitions.concat(stat.init.definitions);
stat.init = null;
statements[++j] = stat;
CHANGED = true;
} else {
statements[++j] = stat;
}
} else if (stat instanceof AST_ForIn) {
stat.object = extract_object_assignments(stat.object);
} else if (stat instanceof AST_If) {
stat.condition = extract_object_assignments(stat.condition);
} else if (stat instanceof AST_SimpleStatement) {
var exprs = join_object_assignments(prev, stat.body);
if (exprs) {
CHANGED = true;
if (!exprs.length) continue;
stat.body = make_sequence(stat.body, exprs);
}
statements[++j] = stat;
} else if (stat instanceof AST_Switch) {
stat.expression = extract_object_assignments(stat.expression);
} else if (stat instanceof AST_With) {
stat.expression = extract_object_assignments(stat.expression);
} else {
statements[++j] = stat;
}
}
statements.length = j + 1;
function extract_object_assignments(value) {
statements[++j] = stat;
var exprs = join_object_assignments(prev, value);
if (exprs) {
CHANGED = true;
if (exprs.length) {
return make_sequence(value, exprs);
} else if (value instanceof AST_Sequence) {
return value.tail_node().left;
} else {
return value.left;
}
}
return value;
}
}
}
--- END ---
dex = 0;
var candidate = hit_stack[hit_stack.length - 1];
var value_def = null;
var stop_after = null;
var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue;
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
var replace_all = value_def;
if (!replace_all && lhs instanceof AST_SymbolRef) {
var def = lhs.definition();
if (def.references.length - def.replaced == (candidate instanceof AST_VarDef ? 1 : 2)) {
replace_all = true;
}
}
var side_effects = value_has_side_effects(candidate);
var may_throw = candidate.may_throw(compressor);
var funarg = candidate.name instanceof AST_SymbolFunarg;
var hit = funarg;
var abort = false, replaced = 0, can_replace = !args || !hit;
if (!can_replace) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) {
args[j].transform(scanner);
}
can_replace = true;
}
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(scanner);
}
if (value_def) {
var def = candidate.name.definition();
if (abort && def.references.length - def.replaced > replaced) replaced = false;
else {
abort = false;
hit_index = 0;
hit = funarg;
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(multi_replacer);
}
value_def.single_use = false;
}
}
if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
}
}
function has_overlapping_symbol(fn, arg, fn_strict) {
var found = false, scan_this = !(fn instanceof AST_Arrow);
arg.walk(new TreeWalker(function(node, descend) {
if (found) return true;
if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
var s = node.definition().scope;
if (s !== scope) while (s = s.parent_scope) {
if (s === scope) return true;
}
return found = true;
}
if ((fn_strict || scan_this) && node instanceof AST_This) {
return found = true;
}
if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) {
var prev = scan_this;
scan_this = false;
descend();
scan_this = prev;
return true;
}
}));
return found;
}
function extract_args() {
var iife, fn = compressor.self();
if (is_func_expr(fn)
&& !fn.name
&& !fn.uses_arguments
&& !fn.uses_eval
&& (iife = compressor.parent()) instanceof AST_Call
&& iife.expression === fn
&& all(iife.args, function(arg) {
--- FUNCTION SOURCE (pop) id{297,7} start{51370} ---
() {
var node = this.stack.pop();
if (node instanceof AST_Lambda || node instanceof AST_Class) {
this.directives = Object.getPrototypeOf(this.directives);
}
}
--- END ---
return !(arg instanceof AST_Expansion);
})) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
for (var i = len; --i >= 0;) {
var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue;
names[sym.name] = true;
if (sym instanceof AST_Expansion) {
var elements = iife.args.slice(i);
if (all(elements, function(arg) {
return !has_overlapping_symbol(fn, arg, fn_strict);
})) {
candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym.expression,
value: make_node(AST_Array, iife, {
elements: elements
})
}) ]);
}
} else {
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null;
if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym,
value: arg
}) ]);
}
}
}
}
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (!expr.left.has_side_effects(compressor)) {
candidates.push(hit_stack.slice());
}
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
extract_candidates(expr.right);
} else if (expr instanceof AST_Call) {
extract_candidates(expr.expression);
expr.args.forEach(extract_candidates);
} else if (expr instanceof AST_Case) {
extract_candidates(expr.expression);
} else if (expr instanceof AST_Conditional) {
extract_candidates(expr.condition);
extract_candidates(expr.consequent);
extract_candidates(expr.alternative);
} else if (expr instanceof AST_Definitions
&& (compressor.option("unused") || !(expr instanceof AST_Const))) {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_Exit) {
if (expr.value) extract_candidates(expr.value);
} else if (expr instanceof AST_For) {
if (expr.init) extract_candidates(expr.init);
if (expr.condition) extract_candidates(expr.condition);
if (expr.step) extract_candidates(expr.step);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
function mangleable_var(var_def) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return;
var def = value.definition();
if (def.undeclared) return;
return value_def = def;
}
function get_lhs(expr) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
return !is_ref_of(lhs, AST_SymbolConst) && lhs;
}
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var tw = new TreeWalker(function(node, descen--- FUNCTION SOURCE (_walk) id{210,-1} start{16131} ---
(visitor) {
return visitor._visit(this, function(){
this.body._walk(visitor);
});
}
--- END ---
d) {
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
get_rvalue(expr).walk(tw);
return lvalues;
}
function lhs_or_def(node) {
if (node instanceof AST_VarDef) return node.value && node.name;
return is_lhs(node.left, node);
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var iife = compressor.parent(), argnames = compressor.self().argnames;
var index = argnames.indexOf(expr.name);
if (index < 0) {
iife.args.length = Math.min(iife.args.length, argnames.length - 1);
} else {
var args = iife.args;
if (args[index]) args[index] = make_node(AST_Number, args[index], {
value: 0
});
}
return true;
}
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
node.value = null;
return node;
}
return in_list ? MAP.skip : null;
}
}, function(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
}
}));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope.get_defun_scope() !== scope) return false;
return def.references.every(function(ref) {
return ref.scope.get_defun_scope() === scope;
});
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
}
}
--- END ---
dex = 0;
var candidate = hit_stack[hit_stack.length - 1];
var value_def = null;
var stop_after = null;
var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue;
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
var replace_all = value_def;
if (!replace_all && lhs instanceof AST_SymbolRef) {
var def = lhs.definition();
if (def.references.length - def.replaced == (candidate instanceof AST_VarDef ? 1 : 2)) {
replace_all = true;
}
}
var side_effects = value_has_side_effects(candidate);
var may_throw = candidate.may_throw(compressor);
var funarg = candidate.name instanceof AST_SymbolFunarg;
var hit = funarg;
var abort = false, replaced = 0, can_replace = !args || !hit;
if (!can_replace) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) {
args[j].transform(scanner);
}
can_replace = true;
}
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(scanner);
}
if (value_def) {
var def = candidate.name.definition();
if (abort && def.references.length - def.replaced > replaced) replaced = false;
else {
abort = false;
hit_index = 0;
hit = funarg;
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(multi_replacer);
}
value_def.single_use = false;
}
}
if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
}
}
function has_overlapping_symbol(fn, arg, fn_strict) {
var found = false, scan_this = !(fn instanceof AST_Arrow);
arg.walk(new TreeWalker(function(node, descend) {
if (found) return true;
if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
var s = node.definition().scope;
if (s !== scope) while (s = s.parent_scope) {
if (s === scope) return true;
}
return found = true;
}
if ((fn_strict || scan_this) && node instanceof AST_This) {
return found = true;
}
if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) {
var prev = scan_this;
scan_this = false;
descend();
scan_this = prev;
return true;
}
}));
return found;
}
function extract_args() {
var iife, fn = compressor.self();
if (is_func_expr(fn)
&& !fn.name
&& !fn.uses_arguments
&& !fn.uses_eval
&& (iife = compressor.parent()) instanceof AST_Call
&& iife.expression === fn
&& all(iife.args, function(arg) {
return !(arg instanceof AST_Expansion);
})) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
for (var i = len; --i >= 0;) {
var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue;
names[sym.name] = true;
if (sym instanceof AST_Expansion) {
var elements = iife.args.slice(i);
if (all(elements, function(arg) {
return !has_overlapping_symbol(fn, arg, fn_strict);
})) {
candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym.expression,
value: make_node(AST_Array, iife, {
elements: elements
})
}) ]);
}
} else {
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null;
if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym,
value: arg
}) ]);
}
}
}
}
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (!expr.left.has_side_effects(compressor)) {
candidates.push(hit_stack.slice());
}
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
extract_candidates(expr.right);
} else if (expr instanceof AST_Call) {
extract_candidates(expr.expression);
expr.args.forEach(extract_candidates);
} else if (expr instanceof AST_Case) {
extract_candidates(expr.expression);
} else if (expr instanceof AST_Conditional) {
extract_candidates(expr.condition);
extract_candidates(expr.consequent);
extract_candidates(expr.alternative);
} else if (expr instanceof AST_Definitions
&& (compressor.option("unused") || !(expr instanceof AST_Const))) {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_Exit) {
if (expr.value) extract_candidates(expr.value);
} else if (expr instanceof AST_For) {
if (expr.init) extract_candidates(expr.init);
if (expr.condition) extract_candidates(expr.condition);
if (expr.step) extract_candidates(expr.step);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
function mangleable_var(var_def) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return;
var def = value.definition();
if (def.undeclared) return;
return value_def = def;
}
function get_lhs(expr) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
return !is_ref_of(lhs, AST_SymbolConst) && lhs;
}
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var tw = new TreeWalker(function(node, descend) {
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
get_rvalue(expr).walk(tw);
return lvalues;
}
function lhs_or_def(node) {
if (node instanceof AST_VarDef) return node.value && node.name;
return is_lhs(node.left, node);
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var iife = compressor.parent(), argnames = compressor.self().argnames;
var index = argnames.indexOf(expr.name);
if (index < 0) {
iife.args.length = Math.min(iife.args.length, argnames.length - 1);
} else {
var args = iife.args;
if (args[index]) args[index] = make_node(AST_Number, args[index], {
value: 0
});
}
return true;
}
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
node.value = null;
return node;
}
return in_list ? MAP.skip : null;
}
}, function(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
}
}));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope.get_defun_scope() !== scope) return false;
return def.references.every(function(ref) {
return ref.scope.get_defun_scope() === scope;
});
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
}
}
--- END ---
if (def.value) {
if (def.name instanceof AST_Destructuring) {
var destructuring_cache = destructuring_value;
destructuring_value = def.value;
def.walk(tw);
destructuring_value = destructuring_cache;
} else {
var node_def = def.name.definition();;
initializations.add(node_def.id, def.value);
if (def.name.fixed_value() === def.value) {
fixed_ids[node_def.id] = def;
}
}
if (def.value.has_side_effects(compressor)) {
def.value.walk(tw);
}
}
});
return true;
}
if (node.destructuring && destructuring_value) {
initializations.add(node.name, destructuring_value);
}
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);
for (var i = 0; i < in_use.length; i++) {
var init = initializations.get(in_use[i].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) {
var sym = assign_as_unused(node);
if (sym instanceof AST_SymbolRef) {
var def = sym.definition();
var in_use = def.id in in_use_ids;
if (node instanceof AST_Assign) {
if (!in_use || def.id in fixed_ids && fixed_ids[def.id] !== node) {
return maintain_this_binding(parent, node, node.right.transform(tt));
}
} else if (!in_use) return make_node(AST_Number, node, {
value: 0
});
}
}
if (scope !== self) return;
if (node.name
&& (!compressor.option("keep_classnames") && node instanceof AST_ClassExpression
|| !compressor.option("keep_fnames") && node instanceof AST_Function)) {
var def = node.name.definition();
// any declarations with same name will overshadow
// name of this anonymous function and can therefore
// never be used anywhere
if (!(def.id in in_use_ids) || def.orig.length > 1)
node.name = null;
}
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
var trim = !compressor.option("keep_fargs");
for (var a = node.argnames, i = a.length; --i >= 0;) {
var sym = a[i];
if (sym instanceof AST_Expansion) {
sym = sym.expression;
}
if (sym instanceof AST_DefaultAssign) {
sym = sym.left;
}
// Do not drop destructuring arguments.
// They constitute a type assertion, so dropping
// them would stop that TypeError which would happen
// if someone called it with an incorrectly formatted
// parameter.
if (!(sym instanceof AST_Destructuring) && !(sym.definition().id in in_use_ids)) {
sym.__unused = true;
if (trim) {
a.pop();
compressor[sym.unreferenced() ? "warn" : "info"]("Dropping unused function argument {name} [{file}:{line},{col}]", template(sym));
}
}
else {
trim = false;
}
}
}
if ((node instanceof AST_Defun || node instanceof AST_DefClass) && node !== self) {
var def = node.name.definition();
var keep = (def.id in in_use_ids) || !drop_funcs && def.global;
if (!keep) {
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
def.eliminated++;
return 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);
if (def.name instanceof AST_Destructuring) return tail.push(def);
var sym = def.name.definition();
if (drop_block && sym.global) return tail.push(def);
if (!(drop_vars || drop_block) || sym.id in in_use_ids) {
if (def.value && sym.id in fixed_ids && fixed_ids[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)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
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: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[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) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value);
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
);
self.transform(tt);
function scan_ref_scoped(node, descend) {
var node_def, 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.left.fixed_value() === node.right) {
fixed_ids[node_def.id] = node;
}
}
return true;
}
if (node instanceof AST_SymbolRef) {
node_def = node.definition();
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
if (node_def = node_def.redefined()) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
}
return true;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend();
scope = save_scope;
return true;
}
}
}
--- END ---
if (!keep) {
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
def.eliminated++;
return 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);
if (def.name instanceof AST_Destructuring) return tail.push(def);
var sym = def.name.definition();
if (drop_block && sym.global) return tail.push(def);
if (!(drop_vars || drop_block) || sym.id in in_use_ids) {
if (def.value && sym.id in fixed_ids && fixed_ids[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)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
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: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[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) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value);
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
);
self.transform(tt);
function scan_ref_scoped(node, descend) {
var node_def, 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.left.fixed_value() === node.right) {
fixed_ids[node_def.id] = node;
}
}
return true;
}
if (node instanceof AST_SymbolRef) {
node_def = node.definition();
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
if (node_def = node_def.redefined()) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
}
return true;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend();
scope = save_scope;
return true;
}
}
}
--- END ---
effects = [];
node.definitions.forEach(function(def) {
if (def.value) def.value = def.value.transform(tt);
if (def.name instanceof AST_Destructuring) return tail.push(def);
var sym = def.name.definition();
if (drop_block && sym.global) return tail.push(def);
if (!(drop_vars || drop_block) || sym.id in in_use_ids) {
if (def.value && sym.id in fixed_ids && fixed_ids[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)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
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: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[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) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value);
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
}
}
function eliminate_spurious_blocks(statements) {
var seen_dirs = [];
for (var i = 0; i < statements.length;) {
var stat = statements[i];
if (stat instanceof AST_BlockStatement && all(stat.body, can_be_evicted_from_block)) {
CHANGED = true;
eliminate_spurious_blocks(stat.body);
[].splice.apply(statements, [i, 1].concat(stat.body));
i += stat.body.length;
} else if (stat instanceof AST_EmptyStatement) {
CHANGED = true;
statements.splice(i, 1);
} else if (stat instanceof AST_Directive) {
if (seen_dirs.indexOf(stat.value) < 0) {
i++;
seen_dirs.push(stat.value);
} else {
CHANGED = true;
statements.splice(i, 1);
}
} else i++;
}
}
function handle_if_return(statements, compressor) {
var self = compressor.self();
var multiple_if_returns = has_multiple_if_returns(statements);
var in_lambda = self instanceof AST_Lambda;
for (var i = statements.length; --i >= 0;) {
var stat = statements[i];
var j = next_index(i);
var next = statements[j];
if (in_lambda && !next && stat instanceof AST_Return) {
if (!stat.value) {
CHANGED = true;
statements.splice(i, 1);
continue;
}
if (stat.value instanceof AST_UnaryPrefix && stat.value.operator == "void") {
CHANGED = true;
statements[i] = make_node(AST_SimpleStatement, stat, {
body: stat.value.expression
});
continue;
}
}
if (stat instanceof AST_If) {
var ab = aborts(stat.body);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
var body = as_statement_array_with_return(stat.body, ab);
stat.body = make_node(AST_BlockStatement, stat, {
body: as_statement_array(stat.alternative).concat(extract_functions())
});
stat.alternative = make_node(AST_BlockStatement, stat, {
body: body
});
statements[i] = stat.transform(compressor);
continue;
}
var ab = aborts(stat.alternative);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
stat = stat.clone();
stat.body = make_node(AST_BlockStatement, stat.body, {
body: as_statement_array(stat.body).concat(extract_functions())
});
var body = as_statement_array_with_return(stat.alternative, ab);
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
body: body
});
statements[i] = stat.transform(compressor);
continue;
}
}
if (stat instanceof AST_If && stat.body instanceof AST_Return) {
var value = stat.body.value;
//---
// pretty silly case, but:
// if (foo()) return; return; ==> foo(); return;
if (!value && !stat.alternative
&& (in_lambda && !next || next instanceof AST_Return && !next.value)) {
CHANGED = true;
statements[i] = make_node(AST_SimpleStatement, stat.condition, {
body: stat.condition
});
continue;
}
//---
// if (foo()) return x; return y; ==> return foo() ? x : y;
if (value && !stat.alternative && next instanceof AST_Return && next.value) {
CHANGED = true;
stat = stat.clone();
stat.alternative = next;
statements.splice(i, 1, stat.transform(compressor));
statements.splice(j, 1);
continue;
}
//---
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
if (value && !stat.alternative
&& (!next && in_lambda && multiple_if_returns
|| next instanceof AST_Return)) {
CHANGED = true;
stat = stat.clone();
stat.alternative = next || make_node(AST_Return, stat, {
value: null
});
statements.splice(i, 1, stat.transform(compressor));
if (next) statements.splice(j, 1);
continue;
}
//---
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
//
// if sequences is not enabled, this can lead to an endless loop (issue #866).
// however, with sequences on this helps producing slightly better output for
// the example code.
var prev = statements[prev_index(i)];
if (compressor.option("sequences") && in_lambda && !stat.alternative
&& prev instanceof AST_If && prev.body instanceof AST_Return
&& next_index(j) == statements.length && next instanceof AST_SimpleStatement) {
CHANGED = true;
stat = stat.clone();
stat.alternative = make_node(AST_BlockStatement, next, {
body: [
next,
make_node(AST_Return, next, {
value: null
})
]
});
statements.splice(i--- FUNCTION SOURCE () id{266,-1} start{14471} ---
(node) {
if (node !== self) {
return node.clone(true);
}
}
--- END ---
, 1, stat.transform(compressor));
statements.splice(j, 1);
continue;
}
}
}
function has_multiple_if_returns(statements) {
var n = 0;
for (var i = statements.length; --i >= 0;) {
var stat = statements[i];
if (stat instanceof AST_If && stat.body instanceof AST_Return) {
if (++n > 1) return true;
}
}
return false;
}
function is_return_void(value) {
return !value || value instanceof AST_UnaryPrefix && value.operator == "void";
}
function can_merge_flow(ab) {
if (!ab) return false;
for (var j = i + 1, len = statements.length; j < len; j++) {
var stat = statements[j];
if (stat instanceof AST_Const || stat instanceof AST_Let) return false;
}
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
return ab instanceof AST_Return && in_lambda && is_return_void(ab.value)
|| ab instanceof AST_Continue && self === loop_body(lct)
|| ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct;
}
function extract_functions() {
var tail = statements.slice(i + 1);
statements.length = i + 1;
return tail.filter(function(stat) {
if (stat instanceof AST_Defun) {
statements.push(stat);
return false;
}
return true;
});
}
function as_statement_array_with_return(node, ab) {
var body = as_statement_array(node).slice(0, -1);
if (ab.value) {
body.push(make_node(AST_SimpleStatement, ab.value, {
body: ab.value.expression
}));
}
return body;
}
function next_index(i) {
for (var j = i + 1, len = statements.length; j < len; j++) {
var stat = statements[j];
if (!(stat instanceof AST_Var && declarations_only(stat))) {
break;
}
}
return j;
}
function prev_index(i) {
for (var j = i; --j >= 0;) {
var stat = statements[j];
if (!(stat instanceof AST_Var && declarations_only(stat))) {
break;
}
}
return j;
}
}
function eliminate_dead_code(statements, compressor) {
var has_quit;
var self = compressor.self();
for (var i = 0, n = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (stat instanceof AST_LoopControl) {
var lct = compressor.loopcontrol_target(stat);
if (stat instanceof AST_Break
&& !(lct instanceof AST_IterationStatement)
&& loop_body(lct) === self
|| stat instanceof AST_Continue
&& loop_body(lct) === self) {
if (stat.label) {
remove(stat.label.thedef.references, stat);
}
} else {
statements[n++] = stat;
}
} else {
statements[n++] = stat;
}
if (aborts(stat)) {
has_quit = statements.slice(i + 1);
break;
}
}
statements.length = n;
CHANGED = n != len;
if (has_quit) has_quit.forEach(function(stat) {
extract_declarations_from_unreachable_code(compressor, stat, statements);
});
}
function declarations_only(node) {
return all(node.definitions, function(var_def) {
return !var_def.value;
});
}
function sequencesize(statements, compressor) {
if (statements.length < 2) return;
var seq = [], n = 0;
function push_seq() {
if (!seq.length) return;
var body = make_sequence(seq[0], seq);
statements[n++] = make_node(AST_SimpleStatement, body, { body: body });
seq = [];
}
for (var i = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (stat instanceof AST_SimpleStatement) {
if (seq.length >= compressor.sequences_limit) push_seq();
var body = stat.body;
if (seq.length > 0) body = body.drop_side_effect_free(compressor);
if (body) merge_sequence(seq, body);
} else if (stat instanceof AST_Definitions && declarations_only(stat)
|| stat instanceof AST_Defun) {
statements[n++] = stat;
} else {
push_seq();
statements[n++] = stat;
}
}
push_seq();
statements.length = n;
if (n != len) CHANGED = true;
}
function to_simple_statement(block, decls) {
if (!(block instanceof AST_BlockStatement)) return block;
var stat = null;
for (var i = 0, len = block.body.length; i < len; i++) {
var line = block.body[i];
if (line instanceof AST_Var && declarations_only(line)) {
decls.push(line);
} else if (stat) {
return false;
} else {
stat = line;
}
}
return stat;
}
function sequencesize_2(statements, compressor) {
function cons_seq(right) {
n--;
CHANGED = true;
var left = prev.body;
return make_sequence(left, [ left, right ]).transform(compressor);
};
var n = 0, prev;
for (var i = 0; i < statements.length; i++) {
var stat = statements[i];
if (prev) {
if (stat instanceof AST_Exit) {
stat.value = cons_seq(stat.value || make_node(AST_Undefined, stat).transform(compressor));
} else if (stat instanceof AST_For) {
if (!(stat.init instanceof AST_Definitions)) {
var abort = false;
prev.body.walk(new TreeWalker(function(node) {
if (abort || node instanceof AST_Scope) return true;
if (node instanceof AST_Binary && node.operator == "in") {
abort = true;
return true;
}
}));
if (!abort) {
if (stat.init) stat.init = cons_seq(stat.init);
else {
stat.init = prev.body;
n--;
CHANGED = true;
}
}
}
} else if (stat instanceof AST_ForIn) {
if (!(stat.init instanceof AST_Const) && !(stat.init instanceof AST_Let)) {
stat.object = cons_seq(stat.object);
}
} else if (stat instanceof AST_If) {
stat.condition = cons_seq(stat.condition);
} else if (stat instanceof AST_Switch) {
stat.expression = cons_seq(stat.expression);
} else if (stat instanceof AST_With) {
stat.expression = cons_seq(stat.expression);
}
}
if (compressor.option("conditionals") && stat instanceof AST_If) {
var decls = [];
var body = to_simple_statement(stat.body, decls);
var alt = to_simple_statement(stat.alternative, decls);
if (body !== false && alt !== false && decls.length > 0) {
var len = decls.length;
decls.push(make_node(AST_If, stat, {
condition: stat.condition,
body: body || make_node(AST_EmptyStatement, stat.body),
alternative: alt
}));
decls.unshift(n, 1);
[].splice.apply(statements, decls);
i += len;
n += len + 1;
prev = null;
CHANGED = true;
continue;
}
}
statements[n++] = stat;
prev = stat instanceof AST_SimpleStatement ? stat : null;
}
statements.length = n;
}
function join_object_assignments(defn, body) {
if (!(defn instanceof AST_Definitions)) return;
var def = defn.definitions[defn.definitions.length - 1];
if (!(def.value instanceof AST_Object)) return;
var exprs;
if (body instanceof AST_Assign) {
exprs = [ body ];
} else if (body instanceof AST_Sequence) {
exprs = body.expressions.slice();
}
if (!exprs) return;
var trimmed = false;
do {
var node = exprs[0];
if (!(node instanceof AST_Assign)) break;
if (node.operator != "=") break;
if (!(node.left instanceof AST_PropAccess)) break;
var sym = node.left.expression;
if (!(sym instanceof AST_SymbolRef)) break;
if (def.name.name != sym.name) break;
if (!node.right.is_constant_expression(scope)) break;
var prop = node.left.property;
if (prop instanceof AST_Node) {
prop = prop.evaluate(compressor);
}
if (prop instanceof AST_Node) break;
prop = "" + prop;
if (compressor.option("ecma") < 6 && compressor.has_directive("use strict")) {
if (!all(def.value.properties, function(node) {
return node.key != prop && node.key.name != prop;
})) break;
}
def.value.properties.push(make_node(AST_ObjectKeyVal, node, {
key: prop,
value: node.right
}));
exprs.shift();
trimmed = true;
} while (exprs.length);
return trimmed && exprs;
}
function join_consecutive_vars(statements) {
var defs;
for (var i = 0, j = -1, len = statements.length; i < len; i++) {
var stat = statements[i];
var prev = statements[j];
if (stat instanceof AST_Definitions) {
if (prev && prev.TYPE == stat.TYPE) {
prev.definitions = prev.definitions.concat(stat.definitions);
CHANGED = true;
} else if (defs && defs.TYPE == stat.TYPE && declarations_only(stat)) {
defs.definitions = defs.definitions.concat(stat.definitions);
CHANGED = true;
} else {
statements[++j] = stat;
defs = stat;
}
} else if (stat instanceof AST_Exit) {
stat.value = extract_object_assignments(stat.value);
} else if (stat instanceof AST_For) {
var exprs = join_object_assignments(prev, stat.init);
if (exprs) {
CHANGED = true;
stat.init = exprs.length ? make_sequence(stat.init, exprs) : null;
statements[++j] = stat;
} else if (prev instanceof AST_Var && (!stat.init || stat.init.TYPE == prev.TYPE)) {
if (stat.init) {
prev.definitions = prev.definitions.concat(stat.init.definitions);
}
stat.init = prev;
statements[j] = stat;
CHANGED = true;
} else if (defs && stat.init && defs.TYPE == stat.init.TYPE && declarations_only(stat.init)) {
defs.definitions = defs.definitions.concat(stat.init.definitions);
stat.init = null;
statements[++j] = stat;
CHANGED = true;
} else {
statements[++j] = stat;
}
} else if (stat instanceof AST_ForIn) {
stat.object = extract_object_assignments(stat.object);
} else if (stat instanceof AST_If) {
stat.condition = extract_object_assignments(stat.condition);
} else if (stat instanceof AST_SimpleStatement) {
var exprs = join_object_assignments(prev, stat.body);
if (exprs) {
CHANGED = true;
if (!exprs.length) continue;
stat.body = make_sequence(stat.body, exprs);
}
statements[++j] = stat;
} else if (stat instanceof AST_Switch) {
stat.expression = extract_object_assignments(stat.expression);
} else if (stat instanceof AST_With) {
stat.expression = extract_object_assignments(stat.expression);
} else {
statements[++j] = stat;
}
}
statements.length = j + 1;
function extract_object_assignments(value) {
statements[++j] = stat;
var exprs = join_object_assignments(prev, value);
if (exprs) {
CHANGED = true;
if (exprs.length) {
return make_sequence(value, exprs);
} else if (value instanceof AST_Sequence) {
return value.tail_node().left;
} else {
return value.left;
}
}
return value;
}
}
}
--- END ---
}
--- END ---
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);
if (def.name instanceof AST_Destructuring) return tail.push(def);
var sym = def.name.definition();
if (drop_block && sym.global) return tail.push(def);
if (!(drop_vars || drop_block) || sym.id in in_use_ids) {
if (def.value && sym.id in fixed_ids && fixed_ids[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)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
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: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[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) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value);
} else {
compressor[def.name.unrefe--- FUNCTION SOURCE (push) id{233,2} start{50832} ---
(node) {
if (node instanceof AST_Lambda) {
this.directives = Object.create(this.directives);
} else if (node instanceof AST_Directive && !this.directives[node.value]) {
this.directives[node.value] = node;
} else if (node instanceof AST_Class) {
this.directives = Object.create(this.directives);
if (!this.directives["use strict"]) {
this.directives["use strict"] = node;
}
}
this.stack.push(node);
}
--- END ---
renced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
function mangleable_var(var_def) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return;
var def = value.definition();
if (def.undeclared) return;
return value_def = def;
}
function get_lhs(expr) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
return !is_ref_of(lhs, AST_SymbolConst) && lhs;
}
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var tw = new TreeWalker(function(node, descen--- FUNCTION SOURCE (push) id{236,2} start{50832} ---
(node) {
if (node instanceof AST_Lambda) {
this.directives = Object.create(this.directives);
} else if (node instanceof AST_Directive && !this.directives[node.value]) {
this.directives[node.value] = node;
} else if (node instanceof AST_Class) {
this.directives = Object.create(this.directives);
if (!this.directives["use strict"]) {
this.directives["use strict"] = node;
}
}
this.stack.push(node);
}
--- END ---
d) {
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
get_rvalue(expr).walk(tw);
return lvalues;
}
function lhs_or_def(node) {
if (node instanceof AST_VarDef) return node.value && node.name;
return is_lhs(node.left, node);
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var iife = compressor.parent(), argnames = compressor.self().argnames;
var index = argnames.indexOf(expr.name);
if (index < 0) {
iife.args.length = Math.min(iife.args.length, argnames.length - 1);
} else {
var args = iife.args;
if (args[index]) args[index] = make_node(AST_Number, args[index], {
value: 0
});
}
return true;
}
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
node.value = null;
return node;
}
return in_list ? MAP.skip : null;
}
}, function(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
}
}));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope.get_defun_scope() !== scope) return false;
return def.references.every(function(ref) {
return ref.scope.get_defun_scope() === scope;
});
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
}
}
--- END ---
effects = [];
node.definitions.forEach(function(def) {
if (def.value) def.value = def.value.transform(tt);
if (def.name instanceof AST_Destructuring) return tail.push(def);
var sym = def.name.definition();
if (drop_block && sym.global) return tail.push(def);
if (!(drop_vars || drop_block) || sym.id in in_use_ids) {
if (def.value && sym.id in fixed_ids && fixed_ids[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)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
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: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[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) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value);
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
sym.eliminated++;
}
});
if (head.length > 0 || tai--- FUNCTION SOURCE () id{246,1} start{413742} ---
(node, descend) {
if (node instanceof AST_Definitions && tt.parent() instanceof AST_Export) return node;
if (node instanceof AST_VarDef) {
var sym = node.name, def, value;
if (sym.scope === self
&& (def = sym.definition()).escaped != 1
&& !def.single_use
&& !def.direct_access
&& !compressor.exposed(def)
&& !top_retain(def)
&& (value = sym.fixed_value()) === node.value
&& value instanceof AST_Object) {
descend(node, this);
var defs = new Dictionary();
var assignments = [];
value.properties.forEach(function(prop) {
assignments.push(make_node(AST_VarDef, node, {
name: make_sym(prop.key),
value: prop.value
}));
});
defs_by_id[def.id] = defs;
return MAP.splice(assignments);
}
}
if (node instanceof AST_PropAccess && node.expression instanceof AST_SymbolRef) {
var defs = defs_by_id[node.expression.definition().id];
if (defs) {
var def = defs.get(get_value(node.property));
var sym = make_node(AST_SymbolRef, node, {
name: def.name,
scope: node.expression.scope,
thedef: def
});
sym.reference({});
return sym;
}
}
function make_sym(key) {
var new_var = make_node(sym.CTOR, sym, {
name: self.make_var_name(sym.name + "_" + key),
scope: self
});
var def = self.def_variable(new_var);
defs.set(key, def);
self.enclosed.push(def);
return new_var;
}
}
--- END ---
l.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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
this.operator);
}
}
--- END ---
function has_overlapping_symbol(fn, arg, fn_strict) {
var found = false, scan_this = !(fn instanceof AST_Arrow);
arg.walk(new TreeWalker(function(node, descend) {
if (found) return true;
if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
var s = node.definition().scope;
if (s !== scope) while (s = s.parent_scope) {
if (s === scope) return true;
}
return found = true;
}
if ((fn_strict || scan_this) && node instanceof AST_This) {
return found = true;
}
if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) {
var prev = scan_this;
scan_this = false;
descend();
scan_this = prev;
return true;
}
}));
return found;
}
function extract_args() {
var iife, fn = compressor.self();
if (is_func_expr(fn)
&& !fn.name
&& !fn.uses_arguments
&& !fn.uses_eval
&& (iife = compressor.parent()) instanceof AST_Call
&& iife.expression === fn
&& all(iife.args, function(arg) {
return !(arg instanceof AST_Expansion);
})) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
for (var i = len; --i >= 0;) {
var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue;
names[sym.name] = true;
if (sym instanceof AST_Expansion) {
var elements = iife.args.slice(i);
if (all(elements, function(arg) {
return !has_overlapping_symbol(fn, arg, fn_strict);
})) {
candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym.expression,
value: make_node(AST_Array, iife, {
elements: elements
})
}) ]);
}
} else {
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null;
if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym,
value: arg
}) ]);
}
}
}
}
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (!expr.left.has_side_effects(compressor)) {
candidates.push(hit_stack.slice());
}
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
extract_candidates(expr.right);
} else if (expr i--- FUNCTION SOURCE (parent) id{226,1} start{50740} ---
(n) {
return this.stack[this.stack.length - 2 - (n || 0)];
}
--- END ---
nstanceof AST_Call) {
extract_candidates(expr.expression);
expr.args.forEach(extract_candidates);
} else if (expr instanceof AST_Case) {
extract_candidates(expr.expression);
} else if (expr instanceof AST_Conditional) {
extract_candidates(expr.condition);
extract_candidates(expr.consequent);
extract_candidates(expr.alternative);
} else if (expr instanceof AST_Definitions
&& (compressor.option("unused") || !(expr instanceof AST_Const))) {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_Exit) {
if (expr.value) extract_candidates(expr.value);
} else if (expr instanceof AST_For) {
if (expr.init) extract_candidates(expr.init);
if (expr.condition) extract_candidates(expr.condition);
if (expr.step) extract_candidates(expr.step);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
function mangleable_var(var_d--- FUNCTION SOURCE () id{292,-1} start{167209} ---
(self, tw){
self.name = self.name.transform(tw);
if (self.value) self.value = self.value.transform(tw);
}
--- END ---
ef) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return;
var def = value.definition();
if (def.undeclared) return;
return value_def = def;
}
function get_lhs(expr) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
return !is_ref_of(lhs, AST_SymbolConst) && lhs;
}
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var tw = new TreeWalker(function(node, descend) {
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
get_rvalue(expr).walk(tw);
return lvalues;
}
function lhs_or_def(node) {
if (node instanceof AST_VarDef) return node.value && node.name;
return is_lhs(node.left, node);
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var iife = compressor.parent(), argnames = compressor.self().argnames;
var index = argnames.indexOf(expr.name);
if (index < 0) {
iife.args.length = Math.min(iife.args.length, argnames.length - 1);
} else {
var args = iife.args;
if (args[index]) args[index] = make_node(AST_Number, args[index], {
value: 0
});
}
return true;
}
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
node.value = null;
return node;
}
return in_list ? MAP.skip : null;
}
}, function(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
}
}));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope.get_defun_scope() !== scope) return false;
--- FUNCTION SOURCE (push) id{292,3} start{50832} ---
(node) {
if (node instanceof AST_Lambda) {
this.directives = Object.create(this.directives);
} else if (node instanceof AST_Directive && !this.directives[node.value]) {
this.directives[node.value] = node;
} else if (node instanceof AST_Class) {
this.directives = Object.create(this.directives);
if (!this.directives["use strict"]) {
this.directives["use strict"] = node;
}
}
this.stack.push(node);
}
--- END ---
return def.references.every(function(ref) {
return ref.scope.get_defun_scope() === scope;
});
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
}
}
function eliminate_spurious_blocks(statements) {
var seen_dirs = [];
for (var i = 0; i < statements.length;) {
var stat = statements[i];
if (stat instanceof AST_BlockStatement && all(stat.body, can_be_evicted_from_block)) {
CHANGED = true;
eliminate_spurious_blocks(stat.body);
[].splice.apply(statements, [i, 1].concat(stat.body));
i += stat.body.length;
} else if (stat instanceof AST_EmptyStatement) {
CHANGED = true;
statements.splice(i, 1);
} else if (stat instanceof AST_Directive) {
if (seen_dirs.indexOf(stat.value) < 0) {
i++;
seen_dirs.push(stat.value);
} else {
CHANGED = true;
statements.splice(i, 1);
}
} else i++;
}
}
function handle_if_return(statements, compressor) {
var self = compressor.self();
var multiple_if_returns = has_multiple_if_returns(statements);
var in_lambda = self instanceof AST_Lambda;
for (var i = statements.length; --i >= 0;) {
var stat = statements[i];
var j = next_index(i);
var next = statements[j];
if (in_lambda && !next && stat instanceof AST_Return) {
if (!stat.value) {
CHANGED = true;
statements.splice(i, 1);
continue;
}
if (stat.value instanceof AST_UnaryPrefix && stat.value.operator == "void") {
CHANGED = true;
statements[i] = make_node(AST_SimpleStatement, stat, {
body: stat.value.expression
});
continue;
}
}
if (stat instanceof AST_If) {
var ab = aborts(stat.body);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
var body = as_statement_array_with_return(stat.body, ab);
stat.body = make_node(AST_BlockStatement, stat, {
body: as_statement_array(stat.alternative).concat(extract_functions())
});
stat.alternative = make_node(AST_BlockSta--- FUNCTION SOURCE (AST_String) id{111,-1} start{51} ---
(props){ if (props) { this.end = props.end;this.start = props.start;this.quote = props.quote;this.value = props.value;}}
--- END ---
tement, stat, {
body: body
});
statements[i] = stat.transform(compressor);
continue;
}
var ab = aborts(stat.alternative);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
stat = stat.clone();
stat.body = make_node(AST_BlockStatement, stat.body, {
body: as_statement_array(stat.body).concat(extract_functions())
});
var body = as_statement_array_with_return(stat.alternative, ab);
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
body: body
});
statements[i] = stat.transform(compressor);
continue;
}
}
if (stat instanceof AST_If && stat.body instanceof AST_Return) {
var value = stat.body.value;
//---
// pretty silly case, but:
// if (foo()) return; return; ==> foo(); return;
if (!value && !stat.alternative
&& (in_lambda && !next || next instanceof AST_Return && !next.value)) {
CHANGED = true;
statements[i] = make_node(AST_SimpleStatement, stat.condition, {
body: stat.condition
});
continue;
}
//---
// if (foo()) return x; return y; ==> return foo() ? x : y;
if (value && !stat.alternative && next instanceof AST_Return && next.value) {
CHANGED = true;
stat = stat.clone();
stat.alternative = next;
statements.splice(i, 1, stat.transform(compressor));
statements.splice(j, 1);
continue;
}
//---
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
if (value && !stat.alternative
&& (!next && in_lambda && multiple_if_returns
|| next instanceof AST_Return)) {
CHANGED = true;
stat = stat.clone();
stat.alternative = next || make_node(AST_Return, stat, {
value: null
});
statements.splice(i, 1, stat.transform(compressor));
if (next) statements.splice(j, 1);
continue;
}
//---
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
//
// if sequences is not enabled, this can lead to an endless loop (issue #866).
// however, with sequences on this helps producing slightly better output for
// the example code.
var prev = statements[prev_index(i)];
if (compressor.option("sequences") && in_lambda && !stat.alternative
&& prev instanceof AST_If && prev.body instanceof AST_Return
&& next_index(j) == statements.length && next instanceof AST_SimpleStatement) {
CHANGED = true;
stat = stat.clone();
stat.alternative = make_node(AST_BlockStatement, next, {
body: [
next,
make_node(AST_Return, next, {
--- FUNCTION SOURCE (expression) id{112,7} start{160873} ---
(commas, no_in) {
var start = S.token;
var exprs = [];
while (true) {
exprs.push(maybe_assign(no_in));
if (!commas || !is("punc", ",")) break;
next();
commas = true;
}
return exprs.length == 1 ? exprs[0] : new AST_Sequence({
start : start,
expressions : exprs,
end : peek()
});
}
--- END ---
value: null
})
]
});
statements.splice(i, 1, stat.transform(compressor));
statements.splice(j, 1);
continue;
}
}
}
function has_multiple_if_returns(statements) {
var n = 0;
for (var i = statements.length; --i >= 0;) {
var stat = statements[i];
if (stat instanceof AST_If && stat.body instanceof AST_Return) {
if (++n > 1) return true;
}
}
return false;
}
function is_return_void(value) {
return !value || value instanceof AST_UnaryPrefix && value.operator == "void";
}
function can_merge_flow(ab) {
if (!ab) return false;
for (var j = i + 1, len = statements.length; j < len; j++) {
var stat = statements[j];
if (stat instanceof AST_Const || stat instanceof AST_Let) return false;
}
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
return ab instanceof AST_Return && in_lambda && is_return_void(ab.value)
|| ab instanceof AST_Continue && self === loop_body(lct)
|| ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct;
}
function extract_functions() {
var tail = statements.slice(i + 1);
statements.length = i + 1;
return tail.filter(function(stat) {
if (stat instanceof AST_Defun) {
statements.push(stat);
return false;
}
return true;
});
}
function as_statement_array_with_return(node, ab) {
var body = as_statement_array(node).slice(0, -1);
if (ab.value) {
body.push(make_node(AST_SimpleStatement, ab.value, {
body: ab.value.expression
}));
}
return body;
}
function next_index(i) {
for (var j = i + 1, len = statements.length; j < len; j++) {
var stat = statements[j];
if (!(stat instanceof AST_Var && declarations_only(stat))) {
break;
}
}
return j;
}
function prev_index(i) {
for (var j = i; --j >= 0;) {
var stat = statements[j];
if (!(stat instanceof AST_Var && declarations_only(stat))) {
break;
}
}
return j;
}
}
function eliminate_dead_code(statements, compressor) {
var has_quit;
var self = compressor.self();
for (var i = 0, n = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (stat instanceof AST_LoopControl) {
var lct = compressor.loopcontrol_target(stat);
if (stat instanceof AST_Break
&& !(lct instanceof AST_IterationStatement)
&& loop_body(lct) === self
|| stat instanceof AST_Continue
&& loop_body(lct) === self) {
if (stat.label) {
remove(stat.label.thedef.references, stat);
}
} else {
statements[n++] = stat;
}
} else {
statements[n++] = stat;
}
if (aborts(stat)) {
has_quit = statements.slice(i + 1);
break;
}
}
statements.length = n;
CHANGED = n != len;
if (has_quit) has_quit.forEach(function(stat) {
extract_declarations_from_unreachable_code(compressor, stat, statements);
});
}
function declarations_only(node) {
return all(node.definitions, function(var_def) {
return !var_def.value;
});
}
function sequencesize(statements, compressor) {
if (statements.length < 2) return;
var seq = [], n = 0;
function push_seq() {
if (!seq.length) return;
var body = make_sequence(seq[0], seq);
statements[n++] = make_node(AST_SimpleStatement, body, { body: body });
seq = [];
}
for (var i = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (stat instanceof AST_SimpleStatement) {
if (seq.length >= compressor.sequences_limit) push_seq();
var body = stat.body;
if (seq.length > 0) body = body.drop_side_effect_free(compressor);
if (body) merge_sequence(seq, body);
} else if (stat instanceof AST_Definitions && declarations_only(stat)
|| stat instanceof AST_Defun) {
statements[n++] = stat;
} else {
push_seq();
statements[n++] = stat;
}
}
push_seq();
statements.length = n;
if (n != len) CHANGED = true;
}
function to_simple_statement(block, decls) {
if (!(block instanceof AST_BlockStatement)) return block;
var stat = null;
for (var i = 0, len = block.body.length; i < len; i++) {
var line = block.body[i];
if (line instanceof AST_Var && declarations_only(line)) {
decls.push(line);
} else if (stat) {
return false;
} else {
stat = line;
}
}
return stat;
}
function sequencesize_2(statements, compressor) {
function cons_seq(right) {
n--;
CHANGED = true;
var left = prev.body;
return make_sequence(left, [ left, right ]).transform(compressor);
};
var n = 0, prev;
for (var i = 0; i < statements.length; i++) {
var stat = statements[i];
if (prev) {
if (stat instanceof AST_Exit) {
stat.value = cons_seq(stat.value || make_node(AST_Undefined, stat).transform(compressor));
} else if (stat instanceof AST_For) {
if (!(stat.init instanceof AST_Definitions)) {
var abort = false;
prev.body.walk(new TreeWalker(function(node) {
if (abort || node instanceof AST_Scope) return true;
if (node instanceof AST_Binary && node.operator == "in") {
abort = true;
return true;
}
}));
if (!abort) {
if (stat.init) stat.init = cons_seq(stat.init);
else {
stat.init = prev.body;
n--;
CHANGED = true;
}
}
}
} else if (stat instanceof AST_ForIn) {
if (!(stat.init instanceof AST_Const) && !(stat.init instanceof AST_Let)) {
stat.object = cons_seq(stat.object);
}
} else if (stat instanceof AST_If) {
stat.condition = cons_seq(stat.condition);
} else if (stat instanceof AST_Switch) {
stat.expression = cons_seq(stat.expression);
} else if (stat instanceof AST_With) {
stat.expression = cons_seq(stat.expression);
}
}
if (compressor.option("conditionals") && stat instanceof AST_If) {
var decls = [];
var body = to_simple_statement(stat.body, decls);
var alt = to_simple_statement(stat.alternative, decls);
if (body !== false && alt !== false && decls.length > 0) {
var len = decls.length;
decls.push(make_node(AST_If, stat, {
condition: stat.condition,
body: body || make_node(AST_EmptyStatement, stat.body),
alternative: alt
}));
decls.unshift(n, 1);
[].splice.apply(statements, decls);
i += len;
n += len + 1;
prev = null;
CHANGED = true;
continue;
}
}
statements[n++] = stat;
prev = stat instanceof AST_SimpleStatement ? stat : null;
}
statements.length = n;
}
function join_object_assignments(defn, body) {
if (!(defn instanceof AST_Definitions)) return;
var def = defn.definitions[defn.definitions.length - 1];
if (!(def.value instanceof AST_Object)) return;
var exprs;
if (body instanceof AST_Assign) {
exprs = [ body ];
} else if (body instanceof AST_Sequence) {
exprs = body.expressions.slice();
}
if (!exprs) return;
var trimmed = false;
do {
var node = exprs[0];
if (!(node instanceof AST_Assign)) break;
if (node.operator != "=") break;
if (!(node.left instanceof AST_PropAccess)) break;
var sym = node.left.expression;
if (!(sym instanceof AST_SymbolRef)) break;
if (def.name.name != sym.name) break;
if (!node.right.is_constant_expression(scope)) break;
var prop = node.left.property;
if (prop instanceof AST_Node) {
prop = prop.evaluate(compressor);
}
if (prop instanceof AST_Node) break;
prop = "" + prop;
if (compressor.option("ecma") < 6 && compressor.has_directive("use strict")) {
if (!all(def.value.properties, function(node) {
return node.key != prop && node.key.name != prop;
})) break;
}
def.value.properties.push(make_node(AST_ObjectKeyVal, node, {
key: prop,
value: node.right
}));
exprs.shift();
trimmed = true;
} while (exprs.length);
return trimmed && exprs;
}
function join_consecutive_vars(statements) {
var defs;
for (var i = 0, j = -1, len = statements.length; i < len; i++) {
var stat = statements[i];
var prev = statements[j];
if (stat instanceof AST_Definitions) {
if (prev && prev.TYPE == stat.TYPE) {
prev.definitions = prev.definitions.concat(stat.definitions);
CHANGED = true;
} else if (defs && defs.TYPE == stat.TYPE && declarations_only(stat)) {
defs.definitions = defs.definitions.concat(stat.definitions);
CHANGED = true;
} else {
statements[++j] = stat;
defs = stat;
}
} else if (stat instanceof AST_Exit) {
stat.value = extract_object_assignments(stat.value);
} else if (stat instanceof AST_For) {
var exprs = join_object_assignments(prev, stat.init);
if (exprs) {
CHANGED = true;
stat.init = exprs.length ? make_sequence(stat.init, exprs) : null;
statements[++j] = stat;
} else if (prev instanceof AST_Var && (!stat.init || stat.init.TYPE == prev.TYPE)) {
if (stat.init) {
prev.definitions = prev.definitions.concat(stat.init.definitions);
}
stat.init = prev;
statements[j] = stat;
CHANGED = true;
} else if (defs && stat.init && defs.TYPE == stat.init.TYPE && declarations_only(stat.init)) {
defs.definitions = defs.definitions.concat(stat.init.definitions);
stat.init = null;
statements[++j] = stat;
CHANGED = true;
} else {
statements[++j] = stat;
}
} else if (stat instanceof AST_ForIn) {
stat.object = extract_object_assignments(stat.object);
} else if (stat instanceof AST_If) {
stat.condition = extract_object_assignments(stat.condition);
} else if (stat instanceof AST_SimpleStatement) {
var exprs = join_object_assignments(prev, stat.body);
if (exprs) {
CHANGED = true;
if (!exprs.length) continue;
stat.body = make_sequence(stat.body, exprs);
}
statements[++j] = stat;
} else if (stat instanceof AST_Switch) {
stat.expression = extract_object_assignments(stat.expression);
} else if (stat instanceof AST_With) {
stat.expression = extract_object_assignments(stat.expression);
} else {
statements[++j] = stat;
}
}
statements.length = j + 1;
function extract_object_assignments(value) {
statements[++j] = stat;
var exprs = join_object_assignments(prev, value);
if (exprs) {
CHANGED = true;
if (exprs.length) {
return make_sequence(value, exprs);
} else if (value instanceof AST_Sequence) {
return value.tail_node().left;
} else {
return value.left;
}
}
return value;
}
}
}
--- END ---
ffects, 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);
if (def.name instanceof AST_Destructuring) return tail.push(def);
var sym = def.name.definition();
if (drop_block && sym.global) return tail.push(def);
if (!(drop_vars || drop_block) || sym.id in in_use_ids) {
if (def.value && sym.id in fixed_ids && fixed_ids[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)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
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: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[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) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value);
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
nceof AST_Export) return node;
if (node instanceof AST_VarDef) {
var sym = node.name, def, value;
if (sym.scope === self
&& (def = sym.definition()).escaped != 1
&& !def.single_use
&& !def.direct_access
&& !compressor.exposed(def)
&& !top_retain(def)
&& (value = sym.fixed_value()) === node.value
&& value instanceof AST_Object) {
descend(node, this);
var defs = new Dictionary();
var assignments = [];
value.properties.forEach(function(prop) {
assignments.push(make_node(AST_VarDef, node, {
name: make_sym(prop.key),
value: prop.value
}));
});
defs_by_id[def.id] = defs;
return MAP.splice(assignments);
}
}
if (node instanceof AST_PropAccess && node.expression instanceof AST_SymbolRef) {
var defs = defs_by_id[node.expression.definition().id];
if (defs) {
var def = defs.get(get_value(node.property));
var sym = make_node(AST_SymbolRef, node, {
name: def.name,
scope: node.expression.scope,
thedef: def
});
sym.reference({});
return sym;
}
}
function make_sym(key) {
var new_var = make_node(sym.CTOR, sym, {
name: self.make_var_name(sym.name + "_" + key),
scope: self
});
var def = self.def_variable(new_var);
defs.set(key, def);
self.enclosed.push(def);
return new_var;
}
}
--- END ---
signments.push(make_node(AST_VarDef, node, {
name: make_sym(prop.key),
value: prop.value
}));
});
defs_by_id[def.id] = defs;
return MAP.splice(assignments);
}
}
if (node instanceof AST_PropAccess && node.expression instanceof AST_SymbolRef) {
var defs = defs_by_id[node.expression.definition().id];
if (defs) {
var def = defs.get(get_value(node.property));
var sym = make_node(AST_SymbolRef, node, {
name: def.name,
scope: node.expression.scope,
thedef: def
});
sym.reference({});
return sym;
}
}
function make_sym(key) {
var new_var = make_node(sym.CTOR, sym, {
name: self.make_var_name(sym.name + "_" + key),
scope: self
});
var def = self.def_variable(new_var);
defs.set(key, def);
self.enclosed.push(def);
return new_var;
}
}
--- END ---
l.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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
walk(new TreeWalker(function(node, descend) {
if (found) return true;
if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
var s = node.definition().scope;
if (s !== scope) while (s = s.parent_scope) {
if (s === scope) return true;
}
return found = true;
}
if ((fn_strict || scan_this) && node instanceof AST_This) {
return found = true;
}
if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) {
var prev = scan_this;
scan_this = false;
descend();
scan_this = prev;
return true;
}
}));
return found;
}
function extract_args() {
var iife, fn = compressor.self();
if (is_func_expr(fn)
&& !fn.name
&& !fn.uses_arguments
&& !fn.uses_eval
&& (iife = compressor.parent()) instanceof AST_Call
&& iife.expression === fn
&& all(iife.args, function(arg) {
return !(arg instanceof AST_Expansion);
})) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
for (var i = len; --i >= 0;) {
var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue;
names[sym.name] = true;
if (sym instanceof AST_Expansion) {
var elements = iife.args.slice(i);
if (all(elements, function(arg) {
return !has_overlapping_symbol(fn, arg, fn_strict);
})) {
candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym.expression,
value: make_node(AST_Array, iife, {
elements: elements
})
}) ]);
}
} else {
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null;
if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym,
value: arg
}) ]);
}
}
}
}
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (!expr.left.has_side_effects(compressor)) {
candidates.push(hit_stack.slice());
}
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
extract_candidates(expr.right);
} else if (expr instanceof AST_Call) {
extract_candidates(expr.expression);
expr.args.forEach(extract_candidates);
} else if (expr instanceof AST_Case) {
extract_candidates(expr.expression);
} else if (expr instanceof AST_Conditional) {
extract_candidates(expr.condition);
extract_candidates(expr.consequent);
extract_candidates(expr.alternative);
} else if (expr instanceof AST_Definitions
&& (compressor.option("unused") || !(expr instanceof AST_Const))) {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_Exit) {
if (expr.value) extract_candidates(expr.value);
} else if (expr instanceof AST_For) {
if (expr.init) extract_candidates(expr.init);
if (expr.condition) extract_candidates(expr.condition);
if (expr.step) extract_candidates(expr.step);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
function mangleable_var(var_def) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return;
var def = value.definition();
if (def.undeclared) return;
return value_def = def;
}
function get_lhs(expr) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
return !is_ref_of(lhs, AST_SymbolConst) && lhs;
}
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var tw = new TreeWalker(function(node, descend) {
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
get_rvalue(expr).walk(tw);
return lvalues;
}
function lhs_or_def(node) {
if (node instanceof AST_VarDef) return node.value && node.name;
return is_lhs(node.left, node);
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var iife = compressor.parent(), argnames = compressor.self().argnames;
var index = argnames.indexOf(expr.name);
if (index < 0) {
iife.args.length = Math.min(iife.args.length, argnames.length - 1);
} else {
var args = iife.args;
if (args[index]) args[index] = make_node(AST_Number, args[index], {
value: 0
});
}
return true;
}
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
node.value = null;
return node;
}
return in_list ? MAP.skip : null;
}
}, function(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
}
}));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope.get_defun_scope() !== scope) return false;
return def.references.every(function(ref) {
return ref.scope.get_defun_scope() === scope;
});
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
}
}
function eliminate_spurious_blocks(statements) {
var seen_dirs = [];
for (var i = 0; i < statements.length;) {
var stat = statements[i];
if (stat instanceof AST_BlockStatement && all(stat.body, can_be_evicted_from_block)) {
CHANGED = true;
eliminate_spurious_blocks(stat.body);
[].splice.apply(statements, [i, 1].concat(stat.body));
i += stat.body.length;
} else if (stat instanceof AST_EmptyStatement) {
CHANGED = true;
statements.splice(i, 1);
} else if (stat instanceof AST_Directive) {
if (seen_dirs.indexOf(stat.value) < 0) {
i++;
seen_dirs.push(stat.value);
} else {
CHANGED = true;
statements.splice(i, 1);
}
} else i++;
}
}
function handle_if_return(statements, compressor) {
var self = compressor.self();
var multiple_if_returns = has_multiple_if_returns(statements);
var in_lambda = self instanceof AST_Lambda;
for (var i = statements.length; --i >= 0;) {
var stat = statements[i];
var j = next_index(i);
var next = statements[j];
if (in_lambda && !next && stat instanceof AST_Return) {
if (!stat.value) {
CHANGED = true;
statements.splice(i, 1);
continue;
}
if (stat.value instanceof AST_UnaryPrefix && stat.value.operator == "void") {
CHANGED = true;
statements[i] = make_node(AST_SimpleStatement, stat, {
body: stat.value.expression
});
continue;
}
}
if (stat instanceof AST_If) {
var ab = aborts(stat.body);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
var body = as_statement_array_with_return(stat.body, ab);
stat.body = make_node(AST_BlockStatement, stat, {
body: as_statement_array(stat.alternative).concat(extract_functions())
});
stat.alternative = make_node(AST_BlockStatement, stat, {
body: body
});
statements[i] = stat.transform(compressor);
continue;
}
var ab = aborts(stat.alternative);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
stat = stat.clone();
stat.body = make_node(AST_BlockStatement, stat.body, {
body: as_statement_array(stat.body).concat(extract_functions())
});
var body = as_statement_array_with_return(stat.alternative, ab);
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
body: body
});
statements[i] = stat.transform(compressor);
continue;
}
}
if (stat instanceof AST_If && stat.body instanceof AST_Return) {
var value = stat.body.value;
//---
// pretty silly case, but:
// if (foo()) return; return; ==> foo(); return;
if (!value && !stat.alternative
&& (in_lambda && !next || next instanceof AST_Return && !next.value)) {
CHANGED = true;
statements[i] = make_node(AST_SimpleStatement, stat.condition, {
body: stat.condition
});
continue;
}
//---
// if (foo()) return x; return y; ==> return foo() ? x : y;
if (value && !stat.alternative && next instanceof AST_Return && next.value) {
CHANGED = true;
stat = stat.clone();
stat.alternative = next;
statements.splice(i, 1, stat.transform(compressor));
statements.splice(j, 1);
continue;
}
//---
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
if (value && !stat.alternative
&& (!next && in_lambda && multiple_if_returns
|| next instanceof AST_Return)) {
CHANGED = true;
stat = stat.clone();
stat.alternative = next || make_node(AST_Return, stat, {
value: null
});
statements.splice(i, 1, stat.transform(compressor));
if (next) statements.splice(j, 1);
continue;
}
//---
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
//
// if sequences is not enabled, this can lead to an endless loop (issue #866).
// however, with sequences on this helps producing slightly better output for
// the example code.
var prev = statements[prev_index(i)];
if (compressor.option("sequences") && in_lambda && !stat.alternative
&& prev instanceof AST_If && prev.body instanceof AST_Return
&& next_index(j) == statements.length && next instanceof AST_SimpleStatement) {
CHANGED = true;
stat = stat.clone();
stat.alternative = make_node(AST_BlockStatement, next, {
body: [
next,
make_node(AST_Return, next, {
value: null
})
]
});
statements.splice(i, 1, stat.transform(compressor));
statements.splice(j, 1);
continue;
}
}
}
function has_multiple_if_returns(statements) {
var n = 0;
for (var i = statements.length; --i >= 0;) {
var stat = statements[i];
if (stat instanceof AST_If && stat.body instanceof AST_Return) {
if (++n > 1) return true;
}
}
return false;
}
function is_return_void(value) {
return !value || value instanceof AST_UnaryPrefix && value.operator == "void";
}
function can_merge_flow(ab) {
if (!ab) return false;
for (var j = i + 1, len = statements.length; j < len; j++) {
var stat = statements[j];
if (stat instanceof AST_Const || stat instanceof AST_Let) return false;
}
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
return ab instanceof AST_Return && in_lambda && is_return_void(ab.value)
|| ab instanceof AST_Continue && self === loop_body(lct)
|| ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct;
}
function extract_functions() {
var tail = statements.slice(i + 1);
statements.length = i + 1;
return tail.filter(function(stat) {
if (stat instanceof AST_Defun) {
statements.push(stat);
return false;
}
return true;
});
}
function as_statement_array_with_return(node, ab) {
var body = as_statement_array(node).slice(0, -1);
if (ab.value) {
body.push(make_node(AST_SimpleStatement, ab.value, {
body: ab.value.expression
}));
}
return body;
}
function next_index(i) {
for (var j = i + 1, len = statements.length; j < len; j++) {
var stat = statements[j];
if (!(stat instanceof AST_Var && declarations_only(stat))) {
break;
}
}
return j;
}
function prev_index(i) {
for (var j = i; --j >= 0;) {
var stat = statements[j];
if (!(stat instanceof AST_Var && declarations_only(stat))) {
break;
}
}
return j;
}
}
function eliminate_dead_code(statements, compressor) {
var has_quit;
var self = compressor.self();
for (var i = 0, n = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (stat instanceof AST_LoopControl) {
var lct = compressor.loopcontrol_target(stat);
if (stat instanceof AST_Break
&& !(lct instanceof AST_IterationStatement)
&& loop_body(lct) === self
|| stat instanceof AST_Continue
&& loop_body(lct) === self) {
if (stat.label) {
remove(stat.label.thedef.references, stat);
}
} else {
statements[n++] = stat;
}
} else {
statements[n++] = stat;
}
if (aborts(stat)) {
has_quit = statements.slice(i + 1);
break;
}
}
statements.length = n;
--- FUNCTION SOURCE () id{118,-1} start{175088} ---
(node, descend){
if (node.is_block_scope()) {
var save_scope = scope;
node.block_scope = scope = new AST_Scope(node);
scope.init_scope_vars(save_scope);
if (!(node instanceof AST_Scope)) {
scope.uses_with = save_scope.uses_with;
scope.uses_eval = save_scope.uses_eval;
scope.directives = save_scope.directives;
}
if (options.safari10) {
if (node instanceof AST_For || node instanceof AST_ForIn) {
for_scopes.push(scope);
}
}
descend();
scope = save_scope;
return true;
}
if (node instanceof AST_Destructuring) {
in_destructuring = node; // These don't nest
descend();
in_destructuring = null;
return true;
}
if (node instanceof AST_Scope) {
node.init_scope_vars(scope);
var save_scope = scope;
var save_defun = defun;
var save_labels = labels;
defun = scope = node;
labels = new Dictionary();
descend();
scope = save_scope;
defun = save_defun;
labels = save_labels;
return true; // don't descend again in TreeWalker
}
if (node instanceof AST_LabeledStatement) {
var l = node.label;
if (labels.has(l.name)) {
throw new Error(string_template("Label {name} defined twice", l));
}
labels.set(l.name, l);
descend();
labels.del(l.name);
return true; // no descend again
}
if (node instanceof AST_With) {
for (var s = scope; s; s = s.parent_scope)
s.uses_with = true;
return;
}
if (node instanceof AST_Symbol) {
node.scope = scope;
}
if (node instanceof AST_Label) {
node.thedef = node;
node.references = [];
}
if (node instanceof AST_SymbolLambda) {
defun.def_function(node, node.name == "arguments" ? undefined : defun);
}
else if (node instanceof AST_SymbolDefun) {
// Careful here, the scope where this should be defined is
// the parent scope. The reason is that we enter a new
// scope when we encounter the AST_Defun node (which is
// instanceof AST_Scope) but we get to the symbol a bit
// later.
mark_export((node.scope = defun.parent_scope.get_defun_scope()).def_function(node, defun), 1);
}
else if (node instanceof AST_SymbolClass) {
mark_export(defun.def_variable(node, defun), 1);
}
else if (node instanceof AST_SymbolImport) {
scope.def_variable(node);
}
else if (node instanceof AST_SymbolDefClass) {
// This deals with the name of the class being available
// inside the class.
mark_export((node.scope = defun.parent_scope).def_function(node, defun), 1);
}
else if (node instanceof AST_SymbolVar
|| node instanceof AST_SymbolLet
|| node instanceof AST_SymbolConst) {
var def;
if (node instanceof AST_SymbolBlockDeclaration) {
def = scope.def_variable(node, null);
} else {
def = defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined);
}
if (!all(def.orig, function(sym) {
if (sym === node) return true;
if (node instanceof AST_SymbolBlockDeclaration) {
return sym instanceof AST_SymbolLambda;
}
return !(sym instanceof AST_SymbolLet || sym instanceof AST_SymbolConst);
})) {
js_error(
node.name + " redeclared",
nodCHANGED = n != len;
if (has_quit) has_quit.forEach(function(stat) {
extract_declarations_from_unreachable_code(compressor, stat, statements);
});
}
function declarations_only(node) {
return all(node.definitions, function(var_def) {
return !var_def.value;
});
}
function sequencesize(statements, compressor) {
if (statements.length < 2) return;
var seq = [], n = 0;
function push_seq() {
if (!seq.length) return;
var body = make_sequence(seq[0], seq);
statements[n++] = make_node(AST_SimpleStatement, body, { body: body });
seq = [];
}
for (var i = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (stat instanceof AST_SimpleStatement) {
if (seq.length >= compressor.sequences_limit) push_seq();
var body = stat.body;
if (seq.length > 0) body = body.drop_side_effect_free(compressor);
if (body) merge_sequence(seq, body);
} else if (stat instanceof AST_Definitions && declarations_only(stat)
|| stat instanceof AST_Defun) {
statements[n++] = stat;
} else {
push_seq();
statements[n++] = stat;
}
}
push_seq();
statements.length = n;
if (n != len) CHANGED = true;
}
function to_simple_statement(block, decls) {
if (!(block instanceof AST_BlockStatement)) return block;
var stat = null;
for (var i = 0, len = block.body.length; i < len; i++) {
var line = block.body[i];
if (line instanceof AST_Var && declarations_only(line)) {
decls.push(line);
} else if (stat) {
return false;
} else {
stat = line;
}
}
return stat;
}
function sequencesize_2(statements, compressor) {
function cons_seq(right) {
n--;
CHANGED = true;
var left = prev.body;
return make_sequence(left, [ left, right ]).transform(compressor);
};
var n = 0, prev;
for (var i = 0; i < statements.length; i++) {
var stat = statements[i];
if (prev) {
if (stat instanceof AST_Exit) {
stat.value = cons_seq(stat.value || make_node(AST_Undefined, stat).transform(compressor));
} else if (stat instanceof AST_For) {
if (!(stat.init instanceof AST_Definitions)) {
var abort = false;
prev.body.walk(new TreeWalker(function(node) {
if (abort || node instanceof AST_Scope) return true;
if (node instanceof AST_Binary && node.operator == "in") {
abort = true;
return true;
}
}));
if (!abort) {
if (stat.init) stat.init = cons_seq(stat.init);
else {
stat.init = prev.body;
n--;
CHANGED = true;
}
}
}
} else if (stat instanceof AST_ForIn) {
if (!(stat.init instanceof AST_Const) && !(stat.init instanceof AST_Let)) {
stat.object = cons_seq(stat.object);
}
} else if (stat instanceof --- FUNCTION SOURCE () id{331,-1} start{16187} ---
(){
this.body._walk(visitor);
}
--- END ---
AST_If) {
stat.condition = cons_seq(stat.condition);
} else if (stat instanceof AST_Switch) {
stat.expression = cons_seq(stat.expression);
} else if (stat instanceof AST_With) {
stat.expression = cons_seq(stat.expression);
}
}
if (compressor.option("conditionals") && stat instanceof AST_If) {
var decls = [];
var body = to_simple_statement(stat.body, decls);
var alt = to_simple_statement(stat.alternative, decls);
if (body !== false && alt !== false && decls.length > 0) {
var len = decls.length;
decls.push(make_node(AST_If, stat, {
condition: stat.condition,
body: body || make_node(AST_EmptyStatement, stat.body),
alternative: alt
}));
decls.unshift(n, 1);
[].splice.apply(statements, decls);
i += len;
n += len + 1;
prev = null;
CHANGED = true;
continue;
}
}
statements[n++] = stat;
prev = stat instanceof AST_SimpleStatement ? stat : null;
}
statements.length = n;
}
function join_object_assignments(defn, body) {
if (!(defn instanceof AST_Definitions)) return;
var def = defn.definitions[defn.definitions.length - 1];
if (!(def.value instanceof AST_Object)) return;
var exprs;
if (body instanceof AST_Assign) {
exprs = [ body ];
} else if (body instanceof AST_Sequence) {
exprs = body.expressions.slice();
}
if (!exprs) return;
var trimmed = false;
do {
var node = exprs[0];
if (!(node instanceof AST_Assign)) break;
if (node.operator != "=") break;
if (!(node.left instanceof AST_PropAccess)) break;
var sym = node.left.expression;
if (!(sym instanceof AST_SymbolRef)) break;
if (def.name.name != sym.name) break;
if (!node.right.is_constant_expression(scope)) break;
var prop = node.left.property;
if (prop instanceof AST_Node) {
prop = prop.evaluate(compressor);
}
if (prop instanceof AST_Node) break;
prop = "" + prop;
if (compressor.option("ecma") < 6 && compressor.has_directive("use strict")) {
if (!all(def.value.properties, function(node) {
return node.key != prop && node.key.name != prop;
})) break;
}
def.value.properties.push(make_node(AST_ObjectKeyVal, node, {
key: prop,
value: node.right
}));
exprs.shift();
trimmed = true;
} while (exprs.length);
return trimmed && exprs;
}
function join_consecutive_vars(statements) {
var defs;
for (var i = 0, j = -1, len = statements.length; i < len; i++) {
var stat = statements[i];
var prev = statements[j];
if (stat instanceof AST_Definitions) {
if (prev && prev.TYPE == stat.TYPE) {
prev.definitions = prev.definitions.concat(stat.definitions);
CHANGED = true;
} else if (defs && defs.TYPE == stat.TYPE && declarations_only(stat)) {
defs.definitions = defs.definitions.concat(stat.definitions);
CHANGED = true;
--- FUNCTION SOURCE (TreeWalker) id{291,-1} start{50297} ---
(callback) {
this.visit = callback;
this.stack = [];
this.directives = Object.create(null);
}
--- END ---
} else {
statements[++j] = stat;
defs = stat;
}
} else if (stat instanceof AST_Exit) {
stat.value = extract_object_assignments(stat.value);
} else if (stat instanceof AST_For) {
var exprs = join_object_assignments(prev, stat.init);
if (exprs) {
CHANGED = true;
stat.init = exprs.length ? make_sequence(stat.init, exprs) : null;
statements[++j] = stat;
} else if (prev instanceof AST_Var && (!stat.init || stat.init.TYPE == prev.TYPE)) {
if (stat.init) {
prev.definitions = prev.definitions.concat(stat.init.definitions);
}
stat.init = prev;
statements[j] = stat;
CHANGED = true;
} else if (defs && stat.init && defs.TYPE == stat.init.TYPE && declarations_only(stat.init)) {
defs.definitions = defs.definitions.concat(stat.init.definitions);
stat.init = null;
statements[++j] = stat;
CHANGED = true;
} else {
statements[++j] = stat;
}
} else if (stat instanceof AST_ForIn) {
stat.object = extract_object_assignments(stat.object);
} else if (stat instanceof AST_If) {
stat.condition = extract_object_assignments(stat.condition);
} else if (stat instanceof AST_SimpleStatement) {
var exprs = join_object_assignments(prev, stat.body);
if (exprs) {
CHANGED = true;
if (!exprs.length) continue;
stat.body = make_sequence(stat.body, exprs);
}
statements[++j] = stat;
} else if (stat instanceof AST_Switch) {
stat.expression = extract_object_assignments(stat.expression);
} else if (stat instanceof AST_With) {
stat.expression = extract_object_assignments(stat.expression);
} else {
statements[++j] = stat;
}
}
statements.length = j + 1;
function extract_object_assignments(value) {
statements[++j] = stat;
var exprs = join_object_assignments(prev, value);
if (exprs) {
CHANGED = true;
if (exprs.length) {
return make_sequence(value, exprs);
} else if (value instanceof AST_Sequence) {
return value.tail_node().left;
} else {
return value.left;
}
}
return value;
}
}
}
--- END ---
e.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
e.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
e.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
) {
return node.left;
}
if (node instanceof AST_Unary && node.write_only) return node.expression;
}
--- END ---
ef = defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined);
}
if (!all(def.orig, function(sym) {
if (sym === node) return true;
if (node instanceof AST_SymbolBlockDeclaration) {
return sym instanceof AST_SymbolLambda;
}
return !(sym instanceof AST_SymbolLet || sym instanceof AST_SymbolConst);
})) {
js_error(
node.name + " redeclared",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
l.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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
e.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
if (!keep) {
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
def.eliminated++;
return 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);
if (def.name instanceof AST_Destructuring) return tail.push(def);
var sym = def.name.definition();
if (drop_block && sym.global) return tail.push(def);
if (!(drop_vars || drop_block) || sym.id in in_use_ids) {
if (def.value && sym.id in fixed_ids && fixed_ids[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)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
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: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[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);
--- FUNCTION SOURCE (MAP) id{301,-1} start{3884} ---
(a, f, backwards) {
var ret = [], top = [], i;
function doit() {
var val = f(a[i], i);
var is_last = val instanceof Last;
if (is_last) val = val.v;
if (val instanceof AtTop) {
val = val.v;
if (val instanceof Splice) {
top.push.apply(top, backwards ? val.v.slice().reverse() : val.v);
} else {
top.push(val);
}
}
else if (val !== skip) {
if (val instanceof Splice) {
ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v);
} else {
ret.push(val);
}
}
return is_last;
};
if (a instanceof Array) {
if (backwards) {
for (i = a.length; --i >= 0;) if (doit()) break;
ret.reverse();
top.reverse();
} else {
for (i = 0; i < a.length; ++i) if (doit()) break;
}
}
else {
for (i in a) if (HOP(a, i)) if (doit()) break;
}
return top.concat(ret);
}
--- END ---
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) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value);
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
);
self.transform(tt);
function scan_ref_scoped(node, descend) {
var node_def, 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.left.fixed_value() === node.right) {
fixed_ids[node_def.id] = node;
}
}
return true;
}
if (node instanceof AST_SymbolRef) {
node_def = node.definition();
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
if (node_def = node_def.redefined()) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
}
return true;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend();
scope = save_scope;
return true;
}
}
}
--- END ---
l.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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
a = x(), b = undefined;
if (stat_index == 0 && compressor.option("unused")) extract_args();
// Find collapsible assignments
var hit_stack = [];
extract_candidates(statements[stat_index]);
while (candidates.length > 0) {
hit_stack = candidates.pop();
var hit_index = 0;
var candidate = hit_stack[hit_stack.length - 1];
var value_def = null;
var stop_after = null;
var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue;
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
var replace_all = value_def;
if (!replace_all && lhs instanceof AST_SymbolRef) {
var def = lhs.definition();
if (def.references.length - def.replaced == (candidate instanceof AST_VarDef ? 1 : 2)) {
replace_all = true;
}
}
var side_effects = value_has_side_effects(candidate);
var may_throw = candidate.may_throw(compressor);
var funarg = candidate.name instanceof AST_SymbolFunarg;
var hit = funarg;
var abort = false, replaced = 0, can_replace = !args || !hit;
if (!can_replace) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) {
args[j].transform(scanner);
}
can_replace = true;
}
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(scanner);
}
if (value_def) {
var def = candidate.name.definition();
if (abort && def.references.length - def.replaced > replaced) replaced = false;
else {
abort = false;
hit_index = 0;
hit = funarg;
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(multi_replacer);
}
value_def.single_use = false;
}
}
if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
}
}
function has_overlapping_symbol(fn, arg, fn_strict) {
var found = false, scan_this = !(fn instanceof AST_Arrow);
arg.walk(new TreeWalker(function(node, descend) {
if (found) return true;
if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
var s = node.definition().scope;
if (s !== scope) while (s = s.parent_scope) {
if (s === scope) return true;
}
return found = true;
}
if ((fn_strict || scan_this) && node instanceof AST_This) {
return found = true;
}
if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) {
var prev = scan_this;
scan_this = false;
descend();
scan_this = prev;
return true;
}
}));
return found;
}
function extract_arg--- FUNCTION SOURCE () id{304,3} start{413742} ---
(node, descend) {
if (node instanceof AST_Definitions && tt.parent() instanceof AST_Export) return node;
if (node instanceof AST_VarDef) {
var sym = node.name, def, value;
if (sym.scope === self
&& (def = sym.definition()).escaped != 1
&& !def.single_use
&& !def.direct_access
&& !compressor.exposed(def)
&& !top_retain(def)
&& (value = sym.fixed_value()) === node.value
&& value instanceof AST_Object) {
descend(node, this);
var defs = new Dictionary();
var assignments = [];
value.properties.forEach(function(prop) {
assignments.push(make_node(AST_VarDef, node, {
name: make_sym(prop.key),
value: prop.value
}));
});
defs_by_id[def.id] = defs;
return MAP.splice(assignments);
}
}
if (node instanceof AST_PropAccess && node.expression instanceof AST_SymbolRef) {
var defs = defs_by_id[node.expression.definition().id];
if (defs) {
var def = defs.get(get_value(node.property));
var sym = make_node(AST_SymbolRef, node, {
name: def.name,
scope: node.expression.scope,
thedef: def
});
sym.reference({});
return sym;
}
}
function make_sym(key) {
var new_var = make_node(sym.CTOR, sym, {
name: self.make_var_name(sym.name + "_" + key),
scope: self
});
var def = self.def_variable(new_var);
defs.set(key, def);
self.enclosed.push(def);
return new_var;
}
}
--- END ---
s() {
var iife, fn = compressor.self();
if (is_func_expr(fn)
&& !fn.name
&& !fn.uses_arguments
&& !fn.uses_eval
&& (iife = compressor.parent()) instanceof AST_Call
&& iife.expression === fn
&& all(iife.args, function(arg) {
return !(arg instanceof AST_Expansion);
})) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
for (var i = len; --i >= 0;) {
var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue;
names[sym.name] = true;
if (sym instanceof AST_Expansion) {
var elements = iife.args.slice(i);
if (all(elements, function(arg) {
return !has_overlapping_symbol(fn, arg, fn_strict);
})) {
candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym.expression,
value: make_node(AST_Array, iife, {
elements: elements
})
}) ]);
}
} else {
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null;
if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym,
value: arg
}) ]);
}
}
}
}
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (!expr.left.has_side_effects(compressor)) {
candidates.push(hit_stack.slice());
}
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
extract_candidates(expr.right);
} else if (expr instanceof AST_Call) {
extract_candidates(expr.expression);
expr.args.forEach(extract_candidates);
} else if (expr instanceof AST_Case) {
extract_candidates(expr.expression);
} else if (expr instanceof AST_Conditional) {
extract_candidates(expr.condition);
extract_candidates(expr.consequent);
extract_candidates(expr.alternative);
} else if (expr instanceof AST_Definitions
&& (compressor.option("unused") || !(expr instanceof AST_Const))) {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_Exit) {
if (expr.value) extract_candidates(expr.value);
} else if (expr instanceof AST_For) {
if (expr.init) extract_candidates(expr.init);
if (expr.condition) extract_candidates(expr.condition);
if (expr.step) extract_candidates(expr.step);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
function mangleable_var(var_def) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return;
var def = value.definition();
if (def.undeclared) return;
return value_def = def;
}
function get_lhs(expr) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
return !is_ref_of(lhs, AST_SymbolConst) && lhs;
}
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var tw = new TreeWalker(function(node, descend) {
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
get_rvalue(expr).walk(tw);
return lvalues;
}
function lhs_or_def(node) {
if (node instanceof AST_VarDef) return node.value && node.name;
return is_lhs(node.left, node);
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var iife = compressor.parent(), argnames = compressor.self().argnames;
var index = argnames.indexOf(expr.name);
if (index < 0) {
iife.args.length = Math.min(iife.args.length, argnames.length - 1);
} else {
var args = iife.args;
if (args[index]) args[index] = make_node(AST_Number, args[index], {
value: 0
});
}
return true;
}
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
node.value = null;
return node;
}
return in_list ? MAP.skip : null;
}
}, function(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
}
}));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope.get_defun_scope() !== scope) return false;
return def.references.every(function(ref) {
return ref.scope.get_defun_scope() === scope;
});
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
}
}
--- END ---
minated)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
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: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[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) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value);
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
-- END ---
rm(compressor);
} else {
return maintain_this_binding(parent, node, value);
}
}
return make_node(AST_Assign, candidate, {
operator: "=",
left: make_node(AST_SymbolRef, candidate.name, candidate.name),
right: value
});
}
candidate.write_only = false;
return candidate;
}
// These node types have child nodes that execute sequentially,
// but are otherwise not safe to scan into or beyond them.
var sym;
if (node instanceof AST_Call
|| node instanceof AST_Exit
|| node instanceof AST_PropAccess
&& (side_effects || node.expression.may_throw_on_access(compressor))
|| node instanceof AST_SymbolRef
&& (lvalues[node.name]
|| side_effects && !references_in_scope(node.definition()))
|| (sym = lhs_or_def(node))
&& (sym instanceof AST_PropAccess || sym.name in lvalues)
|| may_throw
&& (in_try ? node.has_side_effects(compressor) : side_effects_external(node))
|| (side_effects || !replace_all)
&& (parent instanceof AST_Binary && lazy_op(parent.operator)
|| parent instanceof AST_Conditional
|| parent instanceof AST_If)) {
stop_after = node;
if (node instanceof AST_Scope) abort = true;
}
// Skip (non-executed) functions
if (node instanceof AST_Scope) return node;
}
--- END ---
turn true;
// (foo && bar)["prop"], (foo && bar).prop
if (p instanceof AST_PropAccess && p.expression === this)
return true;
// this deals with precedence: 3 * (2 + 1)
if (p instanceof AST_Binary) {
var po = p.operator, pp = PRECEDENCE[po];
var so = this.operator, sp = PRECEDENCE[so];
if (pp > sp
|| (pp == sp
&& this === p.right)) {
return true;
}
}
}
--- END ---
value_def.replaced--;
return candidate.value;
}
// Skip (non-executed) functions and (leading) default case in switch statements
if (node instanceof AST_Default || node instanceof AST_Scope) return node;
});
while (--stat_index >= 0) {
// Treat parameters as collapsible in IIFE, i.e.
// function(a, b){ ... }(x());
// would be translated into equivalent assignments:
// var a = x(), b = undefined;
if (stat_index == 0 && compressor.option("unused")) extract_args();
// Find collapsible assignments
var hit_stack = [];
extract_candidates(statements[stat_index]);
while (candidates.length > 0) {
hit_stack = candidates.pop();
var hit_index = 0;
var candidate = hit_stack[hit_stack.length - 1];
var value_def = null;
var stop_after = null;
var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue;
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
var replace_all = value_def;
if (!replace_all && lhs instanceof AST_SymbolRef) {
var def = lhs.definition();
if (def.references.length - def.replaced == (candidate instanceof AST_VarDef ? 1 : 2)) {
replace_all = true;
}
}
var side_effects = value_has_side_effects(candidate);
var may_throw = candidate.may_throw(compressor);
var funarg = candidate.name instanceof AST_SymbolFunarg;
var hit = funarg;
var abort = false, replaced = 0, can_replace = !args || !hit;
if (!can_replace) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) {
args[j].transform(scanner);
}
can_replace = true;
}
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(scanner);
}
if (value_def) {
var def = candidate.name.definition();
if (abort && def.references.length - def.replaced > replaced) replaced = false;
else {
abort = false;
hit_index = 0;
hit = funarg;
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(multi_replacer);
}
value_def.single_use = false;
}
}
if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
}
}
function has_overlapping_symbol(fn, arg, fn_strict) {
var found = false, scan_this = !(fn instanceof AST_Arrow);
arg.walk(new TreeWalker(function(node, descend) {
if (found) return true;
if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
var s = node.definition().scope;
if (s !== scope) while (s = s.parent_scope) {
if (s === scope) return true;
}
return found = true;
}
if ((fn_strict || scan_this) && node instanceof AST_This) {
return found = true;
}
if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) {
var prev = scan_this;
scan_this = false;
descend();
scan_this = prev;
return true;
}
}));
return found;
}
function extract_args() {
var iife, fn = compressor.self();
if (is_func_expr(fn)
&& !fn.name
&& !fn.uses_arguments
&& !fn.uses_eval
&& (iife = compressor.parent()) instanceof AST_Call
&& iife.expression === fn
&& all(iife.args, function(arg) {
return !(arg instanceof AST_Expansion);
})) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
for (var i = len; --i >= 0;) {
var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue;
names[sym.name] = true;
if (sym instanceof AST_Expansion) {
var elements = iife.args.slice(i);
if (all(elements, function(arg) {
return !has_overlapping_symbol(fn, arg, fn_strict);
})) {
candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym.expression,
value: make_node(AST_Array, iife, {
elements: elements
})
}) ]);
}
} else {
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null;
if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym,
value: arg
}) ]);
}
}
}
}
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (!expr.left.has_side_effects(compressor)) {
candidates.push(hit_stack.slice());
}
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
extract_candidates(expr.right);
} else if (expr instanceof AST_Call) {
extract_candidates(expr.expression);
expr.args.forEach(extract_candidates);
} else if (expr instanceof AST_Case) {
extract_candidates(expr.expression);
} else if (expr instanceof AST_Conditional) {
extract_candidates(expr.condition);
extract_candidates(expr.consequent);
extract_candidates(expr.alternative);
} else if (expr instanceof AST_Definitions
&& (compressor.option("unused") || !(expr instanceof AST_Const))) {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_Exit) {
if (expr.value) extract_candidates(expr.value);
} else if (expr instanceof AST_For) {
if (expr.init) extract_candidates(expr.init);
if (expr.condition) extract_candidates(expr.condition);
if (expr.step) extract_candidates(expr.step);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
function mangleable_var(var_def) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return;
var def = value.definition();
if (def.undeclared) return;
return value_def = def;
}
function get_lhs(expr) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
return !is_ref_of(lhs, AST_SymbolConst) && lhs;
}
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var tw = new TreeWalker(function(node, descend) {
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
get_rvalue(expr).walk(tw);
return lvalues;
}
function lhs_or_def(node) {
if (node instanceof AST_VarDef) return node.value && node.name;
return is_lhs(node.left, node);
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var iife = compressor.parent(), argnames = compressor.self().argnames;
var index = argnames.indexOf(expr.name);
if (index < 0) {
iife.args.length = Math.min(iife.args.length, argnames.length - 1);
} else {
var args = iife.args;
if (args[index]) args[index] = make_node(AST_Number, args[index], {
value: 0
});
}
return true;
}
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
node.value = null;
return node;
}
return in_list ? MAP.skip : null;
}
}, function(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
}
}));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope.get_defun_scope() !== scope) return false;
return def.references.every(function(ref) {
return ref.scope.get_defun_scope() === scope;
});
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_--- FUNCTION SOURCE () id{386,-1} start{167465} ---
(self, tw){
if (self.name) self.name = self.name.transform(tw);
self.argnames = do_list(self.argnames, tw);
if (self.body instanceof AST_Node) {
self.body = self.body.transform(tw);
} else {
self.body = do_list(self.body, tw);
}
}
--- END ---
VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
}
}
function eliminate_spurious_blocks(statements) {
var seen_dirs = [];
for (var i = 0; i < statements.length;) {
var stat = statements[i];
if (stat instanceof AST_BlockStatement && all(stat.body, can_be_evicted_from_block)) {
CHANGED = true;
eliminate_spurious_blocks(stat.body);
[].splice.apply(statements, [i, 1].concat(stat.body));
i += stat.body.length;
} else if (stat instanceof AST_EmptyStatement) {
CHANGED = true;
statements.splice(i, 1);
} else if (stat instanceof AST_Directive) {
if (seen_dirs.indexOf(stat.value) < 0) {
i++;
seen_dirs.push(stat.value);
} else {
CHANGED = true;
statements.splice(i, 1);
}
} else i++;
}
}
function handle_if_return(statements, compressor) {
var self = compressor.self();
var multiple_if_returns = has_multiple_if_returns(statements);
var in_lambda = self instanceof AST_Lambda;
for (var i = statements.length; --i >= 0;) {
var stat = statements[i];
var j = next_index(i);
var next = statements[j];
if (in_lambda && !next && stat instanceof AST_Return) {
if (!stat.value) {
CHANGED = true;
statements.splice(i, 1);
continue;
}
if (stat.value instanceof AST_UnaryPrefix && stat.value.operator == "void") {
CHANGED = true;
statements[i] = make_node(AST_SimpleStatement, stat, {
body: stat.value.expression
});
continue;
}
}
if (stat instanceof AST_If) {
var ab = aborts(stat.body);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
var body = as_statement_array_with_return(stat.body, ab);
stat.body = make_node(AST_BlockStatement, stat, {
body: as_statement_array(stat.alternative).concat(extract_functions())
});
stat.alternative = make_node(AST_BlockStatement, stat, {
body: body
});
statements[i] = stat.transform(compressor);
continue;
}
var ab = aborts(stat.alternative);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
stat = stat.clone();
stat.body = make_node(AST_BlockStatement, stat.body, {
body: as_statement_array(stat.body).concat(extract_functions())
});
var body = as_statement_array_with_return(stat.alternative, ab);
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
body: body
});
statements[i] = stat.transform(compressor);
continue;
}
}
if (stat instanceof AST_If && stat.body instanceof AST_Return) {
var value = stat.body.value;
//---
// pretty silly case, but:
// if (foo()) return; return; ==> foo(); return;
if (!value && !stat.alternative
&& (in_lambda && !next || next instanceof AST_Return && !next.value)) {
CHANGED = true;
statements[i] = make_node(AST_SimpleStatement, stat.condition, {
body: stat.condition
});
continue;
}
//---
// if (foo()) return x; return y; ==> return foo() ? x : y;
if (value && !stat.alternative && next instanceof AST_Return && next.value) {
CHANGED = true;
stat = stat.clone();
stat.alternative = next;
statements.splice(i, 1, stat.transform(compressor));
statements.splice(j, 1);
continue;
}
//---
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
if (value && !stat.alternative
&& (!next && in_lambda && multiple_if_returns
|| next instanceof AST_Return)) {
CHANGED = true;
stat = stat.clone();
stat.alternative = next || make_node(AST_Return, stat, {
value: null
});
statements.splice(i, 1, stat.transform(compressor));
if (next) statements.splice(j, 1);
continue;
}
//---
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
//
// if sequences is not enabled, this can lead to an endless loop (issue #866).
// however, with sequences on this helps producing slightly better output for
// the example code.
var prev = statements[prev_index(i)];
if (compressor.option("sequences") && in_lambda && !stat.alternative
&& prev instanceof AST_If && prev.body instanceof AST_Return
&& next_index(j) == statements.length && next instanceof AST_SimpleStatement) {
CHANGED = true;
stat = stat.clone();
stat.alternative = make_node(AST_BlockStatement, next, {
body: [
next,
make_node(AST_Return, next, {
value: null
})
]
});
statements.splice(i, 1, stat.transform(compressor));
statements.splice(j, 1);
continue;
}
}
}
function has_multiple_if_returns(statements) {
var n = 0;
for (var i = statements.length; --i >= 0;) {
var stat = statements[i];
if (stat instanceof AST_If && stat.body instanceof AST_Return) {
--- FUNCTION SOURCE (_walk) id{353,-1} start{38265} ---
(visitor) {
return visitor._visit(this, function(){
this.expression._walk(visitor);
});
}
--- END ---
if (++n > 1) return true;
}
}
return false;
}
function is_return_void(value) {
return !value || value instanceof AST_UnaryPrefix && value.operator == "void";
}
function can_merge_flow(ab) {
if (!ab) return false;
for (var j = i + 1, len = statements.length; j < len; j++) {
var stat = statements[j];
if (stat instanceof AST_Const || stat instanceof AST_Let) return false;
}
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
return ab instanceof AST_Return && in_lambda && is_return_void(ab.value)
|| ab instanceof AST_Continue && self === loop_body(lct)
|| ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct;
}
function extract_functions() {
var tail = statements.slice(i + 1);
statements.length = i + 1;
return tail.filter(function(stat) {
if (stat instanceof AST_Defun) {
statements.push(stat);
return false;
}
return true;
});
}
function as_statement_array_with_return(node, ab) {
var body = as_statement_array(node).slice(0, -1);
if (ab.value) {
body.push(make_node(AST_SimpleStatement, ab.value, {
body: ab.value.expression
}));
}
return body;
}
function next_index(i) {
for (var j = i + 1, len = statements.length; j < len; j++) {
var stat = statements[j];
if (!(stat instanceof AST_Var && declarations_only(stat))) {
break;
}
}
return j;
}
function prev_index(i) {
for (var j = i; --j >= 0;) {
var stat = statements[j];
if (!(stat instanceof AST_Var && declarations_only(stat))) {
break;
}
}
return j;
}
}
function eliminate_dead_code(statements, compressor) {
var has_quit;
var self = compressor.self();
for (var i = 0, n = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (stat instanceof AST_LoopControl) {
var lct = compressor.loopcontrol_target(stat);
if (stat instanceof AST_Break
&& !(lct instanceof AST_IterationStatement)
&& loop_body(lct) === self
|| stat instanceof AST_Continue
&& loop_body(lct) === self) {
if (stat.label) {
remove(stat.label.thedef.references, stat);
}
} else {
statements[n++] = stat;
}
} else {
statements[n++] = stat;
}
if (aborts(stat)) {
has_quit = statements.slice(i + 1);
break;
}
}
statements.length = n;
CHANGED = n != len;
if (has_quit) has_quit.forEach(function(stat) {
extract_declarations_from_unreachable_code(compressor, stat, statements);
});
}
function declarations_only(node) {
return all(node.definitions, function(var_def) {
return !var_def.value;
});
}
function sequencesize(statements, compressor) {
if (statements.length < 2) return;
var seq = [], n = 0;
function push_seq() {
if (!seq.length) return;
var body = make_sequence(seq[0], seq);
statements[n++] = make_node(AST_SimpleStatement, body, { body: body });
seq = [];
}
for (var i = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (stat instanceof AST_SimpleStatement) {
if (seq.length >= compressor.sequences_limit) push_seq();
var body = stat.body;
if (seq.length > 0) body = body.drop_side_effect_free(compressor);
if (body) merge_sequence(seq, body);
} else if (stat instanceof AST_Definitions && declarations_only(stat)
|| stat instanceof AST_Defun) {
statements[n++] = stat;
} else {
push_seq();
statements[n++] = stat;
}
}
push_seq();
statements.length = n;
if (n != len) CHANGED = true;
}
function to_simple_statement(block, decls) {
if (!(block instanceof AST_BlockStatement)) return block;
var stat = null;
for (var i = 0, len = block.body.length; i < len; i++) {
var line = block.body[i];
if (line instanceof AST_Var && declarations_only(line)) {
decls.push(line);
} else if (stat) {
return false;
} else {
stat = line;
}
}
return stat;
}
function sequencesize_2(statements, compressor) {
function cons_seq(right) {
n--;
CHANGED = true;
var left = prev.body;
return make_sequence(left, [ left, right ]).transform(compressor);
};
var n = 0, prev;
for (var i = 0; i < statements.length; i++) {
var stat = statements[i];
if (prev) {
if (stat instanceof AST_Exit) {
stat.value = cons_seq(stat.value || make_node(AST_Undefined, stat).transform(compressor));
} else if (stat instanceof AST_For) {
if (!(stat.init instanceof AST_Definitions)) {
var abort = false;
prev.body.walk(new TreeWalker(function(node) {
if (abort || node instanceof AST_Scope) return true;
if (node instanceof AST_Binary && node.operator == "in") {
abort = true;
return true;
}
}));
if (!abort) {
if (stat.init) stat.init = cons_seq(stat.init);
else {
stat.init = prev.body;
n--;
CHANGED = true;
}
}
}
} else if (stat instanceof AST_ForIn) {
if (!(stat.init instanceof AST_Const) && !(stat.init instanceof AST_Let)) {
stat.object = cons_seq(stat.object);
}
} else if (stat instanceof AST_If) {
stat.condition = cons_seq(stat.condition);
} else if (stat instanceof AST_Switch) {
stat.expression = cons_seq(stat.expression);
} else if (stat instanceof AST_With) {
stat.expression = cons_seq(stat.expression);
}
}
if (compressor.option("conditionals") && stat instanceof AST_If) {
var decls = [];
var body = to_simple_statement(stat.body, decls);
var alt = to_simple_statement(stat.alternative, decls);
if (body !== false && alt !== false && decls.length > 0) {
var len = decls.length;
decls.push(make_node(AST_If, stat, {
condition: stat.condition,
body: body || make_node(AST_EmptyStatement, stat.body),
alternative: alt
}));
decls.unshift(n, 1);
[].splice.apply(statements, decls);
i += len;
n += len + 1;
prev = null;
CHANGED = true;
continue;
}
}
statements[n++] = stat;
prev = stat instanceof AST_SimpleStatement ? stat : null;
}
statements.length = n;
}
function join_object_assignments(defn, body) {
if (!(defn instanceof AST_Definitions)) return;
var def = defn.definitions[defn.definitions.length - 1];
if (!(def.value instanceof AST_Object)) return;
var exprs;
if (body instanceof AST_Assign) {
exprs = [ body ];
} else if (body instanceof AST_Sequence) {
exprs = body.expressions.slice();
}
if (!exprs) return;
var trimmed = false;
do {
var node = exprs[0];
if (!(node instanceof AST_Assign)) break;
if (node.operator != "=") break;
if (!(node.left instanceof AST_PropAccess)) break;
var sym = node.left.expression;
if (!(sym instanceof AST_SymbolRef)) break;
if (def.name.name != sym.name) break;
if (!node.right.is_constant_expression(scope)) break;
var prop = node.left.property;
if (prop instanceof AST_Node) {
prop = prop.evaluate(compressor);
}
if (prop instanceof AST_Node) break;
prop = "" + prop;
if (compressor.option("ecma") < 6 && compressor.has_directive("use strict")) {
if (!all(def.value.properties, function(node) {
return node.key != prop && node.key.name != prop;
})) break;
}
def.value.properties.push(make_node(AST_ObjectKeyVal, node, {
key: prop,
value: node.right
}));
exprs.shift();
trimmed = true;
} while (exprs.length);
return trimmed && exprs;
}
function join_consecutive_vars(statements) {
var defs;
for (var i = 0, j = -1, len = statements.length; i < len; i++) {
var stat = statements[i];
var prev = statements[j];
if (stat instanceof AST_Definitions) {
if (prev && prev.TYPE == stat.TYPE) {
prev.definitions = prev.definitions.concat(stat.definitions);
CHANGED = true;
} else if (defs && defs.TYPE == stat.TYPE && declarations_only(stat)) {
defs.definitions = defs.definitions.concat(stat.definitions);
CHANGED = true;
} else {
statements[++j] = stat;
defs = stat;
}
} else if (stat instanceof AST_Exit) {
stat.value = extract_object_assignments(stat.value);
} else if (stat instanceof AST_For) {
var exprs = join_object_assignments(prev, stat.init);
if (exprs) {
CHANGED = true;
stat.init = exprs.length ? make_sequence(stat.init, exprs) : null;
statements[++j] = stat;
} else if (prev instanceof AST_Var && (!stat.init || stat.init.TYPE == prev.TYPE)) {
if (stat.init) {
prev.definitions = prev.definitions.concat(stat.init.definitions);
}
stat.init = prev;
statements[j] = stat;
CHANGED = true;
} else if (defs && stat.init && defs.TYPE == stat.init.TYPE && declarations_only(stat.init)) {
defs.definitions = defs.definitions.concat(stat.init.definitions);
stat.init = null;
statements[++j] = stat;
CHANGED = true;
} else {
statements[++j] = stat;
}
} else if (stat instanceof AST_ForIn) {
stat.object = extract_object_assignments(stat.object);
} else if (stat instanceof AST_If) {
stat.condition = extract_object_assignments(stat.condition);
} else if (stat instanceof AST_SimpleStatement) {
var exprs = join_object_assignments(prev, stat.body);
if (exprs) {
CHANGED = true;
if (!exprs.length) continue;
stat.body = make_sequence(stat.body, exprs);
}
statements[++j] = stat;
} else if (stat instanceof AST_Switch) {
stat.expression = extract_object_assignments(stat.expression);
} else if (stat instanceof AST_With) {
stat.expression = extract_object_assignments(stat.expression);
} else {
statements[++j] = stat;
}
}
statements.length = j + 1;
function extract_object_assignments(value) {
statements[++j] = stat;
var exprs = join_object_assignments(prev, value);
if (exprs) {
CHANGED = true;
if (exprs.length) {
return make_sequence(value, exprs);
} else if (value instanceof AST_Sequence) {
return value.tail_node().left;
} else {
return value.left;
}
}
return value;
}
}
}
--- END ---
if (def.value && sym.id in fixed_ids && fixed_ids[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)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
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: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[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) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value);
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
if (!keep) {
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
def.eliminated++;
return 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);
if (def.name instanceof AST_Destructuring) return tail.push(def);
var sym = def.name.definition();
if (drop_block && sym.global) return tail.push(def);
if (!(drop_vars || drop_block) || sym.id in in_use_ids) {
if (def.value && sym.id in fixed_ids && fixed_ids[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)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
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: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[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);
--- FUNCTION SOURCE () id{397,-1} start{32115} ---
(){
var definitions = this.definitions;
for (var i = 0, len = definitions.length; i < len; i++) {
definitions[i]._walk(visitor);
}
}
--- END ---
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) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value);
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
);
self.transform(tt);
function scan_ref_scoped(node, descend) {
var node_def, 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.left.fixed_value() === node.right) {
fixed_ids[node_def.id] = node;
}
}
return true;
}
if (node instanceof AST_SymbolRef) {
node_def = node.definition();
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
if (node_def = node_def.redefined()) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
}
return true;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend();
scope = save_scope;
return true;
}
}
}
--- END ---
}
return !(sym instanceof AST_SymbolLet || sym instanceof AST_SymbolConst);
})) {
js_error(
node.name + " redeclared",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
rm(compressor);
} else {
return maintain_this_binding(parent, node, value);
}
}
return make_node(AST_Assign, candidate, {
operator: "=",
left: make_node(AST_SymbolRef, candidate.name, candidate.name),
right: value
});
}
candidate.write_only = false;
return candidate;
}
// These node types have child nodes that execute sequentially,
// but are otherwise not safe to scan into or beyond them.
var sym;
if (node instanceof AST_Call
|| node instanceof AST_Exit
|| node instanceof AST_PropAccess
&& (side_effects || node.expression.may_throw_on_access(compressor))
|| node instanceof AST_SymbolRef
&& (lvalues[node.name]
|| side_effects && !references_in_scope(node.definition()))
|| (sym = lhs_or_def(node))
&& (sym instanceof AST_PropAccess || sym.name in lvalues)
|| may_throw
&& (in_try ? node.has_side_effects(compressor) : side_effects_external(node))
|| (side_effects || !replace_all)
&& (parent instanceof AST_Binary && lazy_op(parent.operator)
|| parent instanceof AST_Conditional
|| parent instanceof AST_If)) {
stop_after = node;
if (node instanceof AST_Scope) abort = true;
}
// Skip (non-executed) functions
if (node instanceof AST_Scope) return node;
}
--- END ---
n();
var value = candidate.value;
if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
def.replaced++;
if (funarg && is_identifier_atom(value)) {
return value.transform(compressor);
} else {
return maintain_this_binding(parent, node, value);
}
}
return make_node(AST_Assign, candidate, {
operator: "=",
left: make_node(AST_SymbolRef, candidate.name, candidate.name),
right: value
});
}
candidate.write_only = false;
return candidate;
}
// These node types have child nodes that execute sequentially,
// but are otherwise not safe to scan into or beyond them.
var sym;
if (node instanceof AST_Call
|| node instanceof AST_Exit
|| node instanceof AST_PropAccess
&& (side_effects || node.expression.may_throw_on_access(compressor))
|| node instanceof AST_SymbolRef
&& (lvalues[node.name]
|| side_effects && !references_in_scope(node.definition()))
|| (sym = lhs_or_def(node))
&& (sym instanceof AST_PropAccess || sym.name in lvalues)
|| may_throw
&& (in_try ? node.has_side_effects(compressor) : side_effects_external(node))
|| (side_effects || !replace_all)
&& (parent instanceof AST_Binary && lazy_op(parent.operator)
|| parent instanceof AST_Conditional
|| parent instanceof AST_If)) {
stop_after = node;
if (node instanceof AST_Scope) abort = true;
}
// Skip (non-executed) functions
if (node instanceof AST_Scope) return node;
}, function(node) {
if (!abort && stop_after === node) abort = true;
});
var multi_replacer = new TreeTransformer(function(node) {
if (abort) return node;
// Skip nodes before `candidate` as quickly as possible
if (!hit) {
if (node !== hit_stack[hit_index]) return node;
hit_index++;
if (hit_index < hit_stack.length) return;
hit = true;
return node;
}
// Replace variable when found
if (node instanceof AST_SymbolRef
&& node.name == def.name) {
if (!--replaced) abort = true;
if (is_lhs(node, multi_replacer.parent())) return node;
def.replaced++;
value_def.replaced--;
return candidate.value;
}
// Skip (non-executed) functions and (leading) default case in switch statements
if (node instanceof AST_Default || node instanceof AST_Scope) return node;
});
while (--stat_index >= 0) {
// Treat parameters as collapsible in IIFE, i.e.
// function(a, b){ ... }(x());
// would be translated into equivalent assignments:
// var a = x(), b = undefined;
if (stat_index == 0 && compressor.option("unused")) extract_args();
// Find collapsible assignments
var hit_stack = [];
extract_candidates(statements[stat_index]);
while (candidates.length > 0) {
hit_stack = candidates.pop();
var hit_index = 0;
var candidate = hit_stack[hit_stack.length - 1];
var value_def = null;
var stop_after = null;
var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue;
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
var replace_all = value_def;
if (!replace_all && lhs instanceof AST_SymbolRef) {
var def = lhs.definition();
if (def.references.length - def.replaced == (candidate instanceof AST_VarDef ? 1 : 2)) {
replace_all = true;
}
}
var side_effects = value_has_side_effects(candidate);
var may_throw = candidate.may_throw(compressor);
var funarg = candidate.name instanceof AST_SymbolFunarg;
var hit = funarg;
var abort = false, replaced = 0, can_replace = !args || !hit;
if (!can_replace) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) {
args[j].transform(scanner);
}
can_replace = true;
}
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(scanner);
}
if (value_def) {
var def = candidate.name.definition();
if (abort && def.references.length - def.replaced > replaced) replaced = false;
else {
abort = false;
hit_index = 0;
hit = funarg;
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(multi_replacer);
}
value_def.single_use = false;
}
}
if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
}
}
function has_overlapping_symbol(fn, arg, fn_strict) {
var found = false, scan_this = !(fn instanceof AST_Arrow);
arg.walk(new TreeWalker(function(node, descend) {
if (found) return true;
if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
var s = node.definition().scope;
if (s !== scope) while (s = s.parent_scope) {
if (s === scope) return true;
}
return found = true;
}
if ((fn_strict || scan_this) && node instanceof AST_This) {
return found = true;
}
if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) {
var prev = scan_this;
scan_this = false;
descend();
scan_this = prev;
return true;
}
}));
return found;
}
function extract_args() {
var iife, fn = compressor.self();
if (is_func_expr(fn)
&& !fn.name
&& !fn.uses_arguments
&& !fn.uses_eval
&& (iife = compressor.parent()) instanceof AST_Call
&& iife.expression === fn
&& all(iife.args, function(arg) {
return !(arg instanceof AST_Expansion);
})) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
for (var i = len; --i >= 0;) {
var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue;
names[sym.name] = true;
if (sym instanceof AST_Expansion) {
var elements = iife.args.slice(i);
if (all(elements, function(arg) {
return !has_overlapping_symbol(fn, arg, fn_strict);
})) {
candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym.expression,
value: make_node(AST_Array, iife, {
elements: elements
})
}) ]);
}
} else {
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null;
if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym,
value: arg
}) ]);
}
}
}
}
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (!expr.left.has_side_effects(compressor)) {
candidates.push(hit_stack.slice());
}
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
extract_candidates(expr.right);
} else if (expr instanceof AST_Call) {
extract_candidates(expr.expression);
expr.args.forEach(extract_candidates);
} else if (expr instanceof AST_Case) {
extract_candidates(expr.expression);
} else if (expr instanceof AST_Conditional) {
extract_candidates(expr.condition);
extract_candidates(expr.consequent);
extract_candidates(expr.alternative);
} else if (expr instanceof AST_Definitions
&& (compressor.option("unused") || !(expr instanceof AST_Const))) {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_Exit) {
if (expr.value) extract_candidates(expr.value);
} else if (expr instanceof AST_For) {
if (expr.init) extract_candidates(expr.init);
if (expr.condition) extract_candidates(expr.condition);
if (expr.step) extract_candidates(expr.step);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
function mangleable_var(var_def) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return;
var def = value.definition();
if (def.undeclared) return;
return value_def = def;
}
function get_lhs(expr) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
return !is_ref_of(lhs, AST_SymbolConst) && lhs;
}
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var tw = new TreeWalker(function(node, descen--- FUNCTION SOURCE (collapse) id{398,-1} start{299362} ---
(statements, compressor) {
if (scope.uses_eval || scope.uses_with) return statements;
var args;
var candidates = [];
var in_try = compressor.self() instanceof AST_Try;
var stat_index = statements.length;
var scanner = new TreeTransformer(function(node, descend) {
if (abort) return node;
// Scan case expressions first in a switch statement
if (node instanceof AST_Switch) {
if (!hit) {
if (node !== hit_stack[hit_index]) return node;
hit_index++;
}
node.expression = node.expression.transform(scanner);
for (var i = 0, len = node.body.length; !abort && i < len; i++) {
var branch = node.body[i];
if (branch instanceof AST_Case) {
if (!hit) {
if (branch !== hit_stack[hit_index]) continue;
hit_index++;
}
branch.expression = branch.expression.transform(scanner);
if (side_effects || !replace_all) break;
}
}
abort = true;
return node;
}
// Skip nodes before `candidate` as quickly as possible
if (!hit) {
if (node !== hit_stack[hit_index]) return node;
hit_index++;
if (hit_index < hit_stack.length) return;
hit = true;
stop_after = find_stop(node, 0);
if (stop_after === node) abort = true;
return node;
}
// Stop immediately if these node types are encountered
var parent = scanner.parent();
if (node instanceof AST_Assign && node.operator != "=" && lhs.equivalent_to(node.left)
|| node instanceof AST_Await
|| node instanceof AST_Call && lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression)
|| node instanceof AST_Debugger
|| node instanceof AST_Destructuring
|| node instanceof AST_IterationStatement && !(node instanceof AST_For)
|| node instanceof AST_Try
|| node instanceof AST_With
|| parent instanceof AST_For && node !== parent.init
|| (side_effects || !replace_all)
&& (node instanceof AST_SymbolRef && !node.is_declared(compressor))) {
abort = true;
return node;
}
// Replace variable with assignment when found
if (can_replace
&& !(node instanceof AST_SymbolDeclaration)
&& lhs.equivalent_to(node)) {
if (is_lhs(node, parent)) {
if (value_def) replaced++;
return node;
}
CHANGED = abort = true;
replaced++;
compressor.info("Collapsing {name} [{file}:{line},{col}]", {
name: node.print_to_string(),
file: node.start.file,
line: node.start.line,
col: node.start.col
});
if (candidate instanceof AST_UnaryPostfix) {
return make_node(AST_UnaryPrefix, candidate, candidate);
}
if (candidate instanceof AST_VarDef) {
if (value_def) {
abort = false;
return node;
}
var def = candidate.name.definition();
var value = candidate.value;
if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
def.replaced++;
if (funarg && is_identifier_atom(value)) {
return value.transform(compressor);
} else {
return maintain_this_binding(parent, node, value);
}
}
return make_node(AST_Assign, candidate, {
operator: "=",
left: make_node(AST_SymbolRef, candidate.name, candidate.name),
right: value
});
}
candidate.write_only = false;
return candidate;
}
// These node types have child nodes that execute sequentially,
// but are otherwise not safe to scan into or beyond them.
var sym;
if (node instanceof AST_Call
|| node instanceof AST_Exit
|| node instanceof AST_PropAccess
&& (side_effects || node.expression.may_throw_on_access(compressor))
|| node instanceof AST_SymbolRef
&& (lvalues[node.name]
|| side_effects && !references_in_scope(node.definition()))
|| (sym = lhs_or_def(node))
&& (sym instanceof AST_PropAccess || sym.name in lvalues)
|| may_throw
&& (in_try ? node.has_side_effects(compressor) : side_effects_external(node))
|| (side_effects || !replace_all)
&& (parent instanceof AST_Binary && lazy_op(parent.operator)
|| parent instanceof AST_Conditional
|| parent instanceof AST_If)) {
stop_after = node;
if (node instanceof AST_Scope) abort = true;
}
// Skip (non-executed) functions
if (node instanceof AST_Scope) return node;
}, function(node) {
if (!abort && stop_after === node) abort = true;
});
var multi_replacer = new TreeTransformer(function(node) {
if (abort) return node;
// Skip nodes before `candidate` as quickly as possible
if (!hit) {
if (node !== hit_stack[hit_index]) return node;
hit_index++;
if (hit_index < hit_stack.length) return;
hit = true;
return node;
}
// Replace variable when found
if (node instanceof AST_SymbolRef
&& node.name == def.name) {
if (!--replaced) abort = true;
if (is_lhs(node, multi_replacer.parent())) return node;
def.replaced++;
value_def.replaced--;
return candidate.value;
}
// Skip (non-executed) functions and (leading) default case in switch statements
if (node instanceof AST_Default || node instanceof AST_Scope) return node;
});
while (--stat_index >= 0) {
// Treat parameters as collapsible in IIFE, i.e.
// function(a, b){ ... }(x());
// would be translated into equivalent assignments:
// var a = x(), b = undefined;
if (stat_index == 0 && compressor.option("unused")) extract_args();
// Find collapsible assignments
var hit_stack = [];
extract_candidates(statements[stat_index]);
while (candidates.length > 0) {
hit_stack = candidates.pop();
var hit_index = 0;
var candidate = hit_stack[hit_stack.length - 1];
var value_def = null;
var stop_after = null;
var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue;
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
var replace_all = value_def;
if (!replace_all && lhs instanceof AST_SymbolRef) {
var def = lhs.definition();
if (def.references.length - def.replaced == (candidate instanceof AST_VarDef ? 1 : 2)) {
replace_all = true;
}
}
var side_effects = value_has_side_effects(candidate);
var may_throw = candidate.may_throw(compressor);
var funarg = candidate.name instanceof AST_SymbolFunarg;
var hit = funarg;
var abort = false, replaced = 0, can_replace = !args || !hit;
if (!can_replace) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) {
args[j].transform(scanner);
}
can_replace = true;
}
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(scanner);
}
if (value_def) {
var def = candidate.name.definition();
if (abort && def.references.length - def.replaced > replaced) replaced = false;
else {
abort = false;
hit_index = 0;
hit = funarg;
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(multi_replacer);
}
value_def.single_use = false;
}
}
if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
}
}
function has_overlapping_symbol(fn, arg, fn_strict) {
var found = false, scan_this = !(fn instanceof AST_Arrow);
arg.walk(new TreeWalker(function(node, descend) {
if (found) return true;
if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
var s = node.definition().scope;
if (s !== scope) while (s = s.parent_scope) {
if (s === scope) return true;
}
return found = true;
}
if ((fn_strict || scan_this) && node instanceof AST_This) {
return found = true;
}
if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) {
var prev = scan_this;
scan_this = false;
descend();
scan_this = prev;
return true;
}
}));
return found;
}
function extract_args() {
var iife, fn = compressor.self();
if (is_func_expr(fn)
&& !fn.name
&& !fn.uses_arguments
&& !fn.uses_eval
&& (iife = compressor.parent()) instanceof AST_Call
&& iife.expression === fn
&& all(iife.args, function(arg) {
--- FUNCTION SOURCE (clone) id{348,-1} start{14669} ---
(deep) {
return this._clone(deep);
}
--- END ---
return !(arg instanceof AST_Expansion);
})) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
for (var i = len; --i >= 0;) {
var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue;
names[sym.name] = true;
if (sym instanceof AST_Expansion) {
var elements = iife.args.slice(i);
if (all(elements, function(arg) {
return !has_overlapping_symbol(fn, arg, fn_strict);
})) {
candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym.expression,
value: make_node(AST_Array, iife, {
elements: elements
})
}) ]);
}
} else {
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null;
if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym,
value: arg
}) ]);
}
}
}
}
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (!expr.left.has_side_effects(compressor)) {
candidates.push(hit_stack.slice());
}
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
extract_candidates(expr.right);
} else if (expr instanceof AST_Call) {
extract_candidates(expr.expression);
expr.args.forEach(extract_candidates);
} else if (expr instanceof AST_Case) {
extract_candidates(expr.expression);
} else if (expr instanceof AST_Conditional) {
extract_candidates(expr.condition);
extract_candidates(expr.consequent);
extract_candidates(expr.alternative);
} else if (expr instanceof AST_Definitions
&& (compressor.option("unused") || !(expr instanceof AST_Const))) {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_Exit) {
if (expr.value) extract_candidates(expr.value);
} else if (expr instanceof AST_For) {
if (expr.init) extract_candidates(expr.init);
if (expr.condition) extract_candidates(expr.condition);
if (expr.step) extract_candidates(expr.step);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
function mangleable_var(var_def) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return;
var def = value.definition();
if (def.undeclared) return;
return value_def = def;
}
function get_lhs(expr) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
return !is_ref_of(lhs, AST_SymbolConst) && lhs;
}
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var tw = new TreeWalker(function(node, descen--- FUNCTION SOURCE () id{351,-1} start{168043} ---
(self, tw){
self.expression = self.expression.transform(tw);
}
--- END ---
d) {
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
get_rvalue(expr).walk(tw);
return lvalues;
}
function lhs_or_def(node) {
if (node instanceof AST_VarDef) return node.value && node.name;
return is_lhs(node.left, node);
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var iife = compressor.parent(), argnames = compressor.self().argnames;
var index = argnames.indexOf(expr.name);
if (index < 0) {
iife.args.length = Math.min(iife.args.length, argnames.length - 1);
} else {
var args = iife.args;
if (args[index]) args[index] = make_node(AST_Number, args[index], {
value: 0
});
}
return true;
}
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
node.value = null;
return node;
}
return in_list ? MAP.skip : null;
}
}, function(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
}
}));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope.get_defun_scope() !== scope) return false;
return def.references.every(function(ref) {
return ref.scope.get_defun_scope() === scope;
});
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
}
}
--- END ---
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
--- END ---
d) {
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
get_rvalue(expr).walk(tw);
return lvalues;
}
function lhs_or_def(node) {
if (node instanceof AST_VarDef) return node.value && node.name;
return is_lhs(node.left, node);
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var iife = compressor.parent(), argnames = compressor.self().argnames;
var index = argnames.indexOf(expr.name);
if (index < 0) {
iife.args.length = Math.min(iife.args.length, argnames.length - 1);
} else {
var args = iife.args;
if (args[index]) args[index] = make_node(AST_Number, args[index], {
value: 0
});
}
return true;
}
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
node.value = null;
return node;
}
return in_list ? MAP.skip : null;
}
}, function(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
}
}));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope.get_defun_scope() !== scope) return false;
return def.references.every(function(ref) {
return ref.scope.get_defun_scope() === scope;
});
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
}
}
--- END ---
l.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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
ST_SymbolLet || sym instanceof AST_SymbolConst);
})) {
js_error(
node.name + " redeclared",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
}
--- END ---
rt.line,
col : sym.start.col
};
}
}
);
self.transform(tt);
function scan_ref_scoped(node, descend) {
var node_def, 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.left.fixed_value() === node.right) {
fixed_ids[node_def.id] = node;
}
}
return true;
}
if (node instanceof AST_SymbolRef) {
node_def = node.definition();
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
if (node_def = node_def.redefined()) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
}
return true;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend();
scope = save_scope;
return true;
}
}
}
--- END ---
}
} : noop;
var requireSemicolonChars = makePredicate("( [ + * / - , . `");
function print(str) {
str = String(str);
var ch = get_full_char(str, 0);
var prev = get_full_char(last, last.length - 1);
if (need_newline_indented && ch) {
need_newline_indented = false;
if (ch != "\x5cn") {
print("\x5cn");
indent();
}
}
if (need_space && ch) {
need_space = false;
if (!/[\x5cs;})]/.test(ch)) {
space();
}
}
newline_insert = -1;
var prev = last.charAt(last.length - 1);
if (might_need_semicolon) {
might_need_semicolon = false;
if (prev == ":" && ch == "}" || (!ch || ";}".indexOf(ch) < 0) && prev != ";") {
if (options.semicolons || requireSemicolonChars(ch)) {
OUTPUT += ";";
current_col++;
current_pos++;
} else {
ensure_line_len();
OUTPUT += "\x5cn";
current_pos++;
current_line++;
current_col = 0;
if (/^\x5cs+$/.test(str)) {
// reset the semicolon flag, since we didn't print one
// now and might still have to later
might_need_semicolon = true;
}
}
if (!options.beautify)
might_need_space = false;
}
}
if (!options.beautify && options.preserve_line && stack[stack.length - 1]) {
var target_line = stack[stack.length - 1].start.line;
while (current_line < target_line) {
ensure_line_len();
OUTPUT += "\x5cn";
current_pos++;
current_line++;
current_col = 0;
might_need_space = false;
}
}
if (might_need_space) {
if ((is_identifier_char(prev)
&& (is_identifier_char(ch) || ch == "\x5c\x5c"))
|| (ch == "/" && ch == prev)
|| ((ch == "+" || ch == "-") && ch == last))
{
OUTPUT += " ";
current_col++;
current_pos++;
}
might_need_space = false;
}
if (mapping_token) {
mappings.push({
token: mapping_token,
name: mapping_name,
line: current_line,
col: current_col
});
mapping_token = false;
if (!might_add_newline) do_add_mapping();
}
OUTPUT += str;
current_pos += str.length;
var a = str.split(/\x5cr?\x5cn/), n = a.length - 1;
current_line += n;
current_col += a[0].length;
if (n > 0) {
ensure_line_len();
current_col = a[n].length;
}
last = str;
};
var star = function(){
print("*");
}
var space = options.beautify ? function() {
print(" ");
} : function() {
might_need_space = true;
};
var indent = options.beautify ? function(half) {
if (options.beautify) {
print(make_indent(half ? 0.5 : 0));
}
} : noop;
var with_indent = options.beautify ? function(col, cont) {
if (col === true) col = next_indent();
var save_indentation = indentation;
indentation = col;
var ret = cont();
indentation = save_indentation;
return ret;
} : function(col, cont) { return cont() };
var newline = options.beautify ? function() {
if (newline_insert < 0) return print("\x5cn");
if (OUTPUT[newline_insert] != "\x5cn") {
OUTPUT = OUTPUT.slice(0, newline_insert) + "\x5cn" + OUTPUT.slice(newline_insert);
current_pos++;
current_line++;
}
newline_insert++;
} : options.max_line_len ? function() {
ensure_line_len();
might_add_newline = OUTPUT.length;
} : noop;
var semicolon = options.beautify ? function() {
print(";");
} : function() {
might_need_semicolon = true;
};
function force_semicolon() {
might_need_semicolon = false;
print(";");
};
function next_indent() {
return indentation + options.indent_level;
};
function with_block(cont) {
var ret;
print("{");
newline();
with_indent(next_indent(), function(){
ret = cont();
});
indent();
print("}");
return ret;
};
function with_parens(cont) {
print("(");
//XXX: still nice to have that for argument lists
//var ret = with_indent(current_col, cont);
var ret = cont();
print(")");
return ret;
};
function with_square(cont) {
print("[");
//var ret = with_indent(current_col, cont);
var ret = cont();
print("]");
return ret;
};
function comma() {
print(",");
space();
};
function colon() {
print(":");
space();
};
var add_mapping = mappings ? function(token, name) {
mapping_token = token;
mapping_name = name;
} : noop;
function get() {
if (might_add_newline) {
ensure_line_len();
}
return OUTPUT;
};
function has_nlb() {
var index = OUTPUT.lastIndexOf("\x5cn");
return /^ *$/.test(OUTPUT.slice(index + 1));
}
function prepend_comments(node) {
var self = this;
var start = node.start;
if (!start) return;
if (start.comments_before && start.comments_before._dumped === self) return;
var comments = start.comments_before;
if (!comments) {
comments = start.comments_before = [];
}
comments._dumped = self;
if (node instanceof AST_Exit && node.value) {
var tw = new TreeWalker(function(node) {
var parent = tw.parent();
if (parent instanceof AST_Exit
|| parent instanceof AST_Binary && parent.left === node
|| parent.TYPE == "Call" && parent.expression === node
|| parent instanceof AST_Conditional && parent.condition === node
|| parent instanceof AST_Dot && parent.expression === node
|| parent instanceof AST_Sequence && parent.expressions[0] === node
|| parent instanceof AST_Sub && parent.expression === node
|| parent instanceof AST_UnaryPostfix) {
if (!node.start) return;
var text = node.start.comments_before;
if (text && text._dumped !== self) {
text._dumped = self;
comments = comments.concat(text);
}
} else {
return true;
}
});
tw.push(node);
node.value.walk(tw);
}
if (current_pos == 0) {
if (comments.length > 0 && options.shebang && comments[0].type == "comment5") {
print("#!" + comments.shift().value + "\x5cn");
indent();
}
var preamble = options.preamble;
if (preamble) {
print(preamble.replace(/\x5cr\x5cn?|[\x5cn\x5cu2028\x5cu2029]|\x5cs*$/g, "\x5cn"));
}
}
comments = comments.filter(comment_filter, node);
if (comments.length == 0) return;
var last_nlb = has_nlb();
comments.forEach(function(c, i) {
if (!last_nlb) {
if (c.nlb) {
print("\x5cn");
indent();
last_nlb = true;
} else if (i > 0) {
space();
}
}
if (/comment[134]/.test(c.type)) {
print("//" + c.value.replace(/[@#]__PURE__/g, ' ') + "\x5cn");
indent();
last_nlb = true;
} else if (c.type == "comment2") {
print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/");
last_nlb = false;
}
});
if (!last_nlb) {
if (start.nlb) {
print("\x5cn");
indent();
} else {
space();
}
}
}
function append_comments(node, tail) {
var self = this;
var token = node.end;
if (!token) return;
var comments = token[tail ? "comments_before" : "comments_after"];
if (!comments || comments._dumped === self) return;
if (!(node instanceof AST_Statement || all(comments, function(c) {
return !/comment[134]/.test(c.type);
}))) return;
comments._dumped = self;
var insert = OUTPUT.length;
comments.filter(comment_filter, node).forEach(function(c, i) {
need_space = false;
if (need_newline_indented) {
print("\x5cn");
indent();
need_newline_indented = false;
} else if (c.nlb && (i > 0 || !has_nlb())) {
print("\x5cn");
indent();
} else if (i > 0 || !tail) {
space();
}
if (/comment[134]/.test(c.type)) {
print("//" + c.value.replace(/[@#]__PURE__/g, ' '));
need_newline_indented = true;
} else if (c.type == "comment2") {
print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/");
need_space = true;
}
});
if (OUTPUT.length > insert) newline_insert = insert;
}
var stack = [];
return {
get : get,
toString : get,
indent : indent,
indentation : function() { return indentation },
current_width : function() { return current_col - indentation },
should_break : function() { return options.width && this.current_width() >= options.width },
newline : newline,
print : print,
star : star,
space : space,
comma : comma,
colon : colon,
last : function() { return last },
semicolon : semicolon,
force_semicolon : force_semicolon,
to_utf8 : to_utf8,
print_name : function(name) { print(make_name(name)) },
print_string : function(str, quote, escape_directive) {
var encoded = encode_string(str, quote);
if (escape_directive === true && encoded.indexOf("\x5c\x5c") === -1) {
// Insert semicolons to break directive prologue
if (!EXPECT_DIRECTIVE.test(OUTPUT)) {
force_semicolon();
}
force_semicolon();
}
print(encoded);
},
print_template_string_chars: function(str) {
var encoded = encode_string(str, '`').replace(/\x5c${/g, "\x5c\x5c${");
return print(encoded.substr(1, encoded.length - 2));
},
encode_string : encode_string,
next_indent : next_indent,
with_indent : with_indent,
with_block : with_block,
with_parens : with_parens,
with_square : with_square,
add_mapping : add_mapping,
option : function(opt) { return options[opt] },
prepend_comments: readonly ? noop : prepend_comments,
append_comments : readonly || comment_filter === return_false ? noop : append_comments,
line : function() { return current_line },
col : function() { return current_col },
pos : function() { return current_pos },
push_node : function(node) { stack.push(node) },
--- FUNCTION SOURCE (clone) id{414,-1} start{9428} ---
() {
var ret = new Dictionary();
for (var i in this._values)
ret._values[i] = this._values[i];
ret._size = this._size;
return ret;
}
--- END ---
pop_node : function() { return stack.pop() },
parent : function(n) {
return stack[stack.length - 2 - (n || 0)];
}
};
}
--- END ---
l.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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
n();
var value = candidate.value;
if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
def.replaced++;
if (funarg && is_identifier_atom(value)) {
return value.transform(compressor);
} else {
return maintain_this_binding(parent, node, value);
}
}
return make_node(AST_Assign, candidate, {
operator: "=",
left: make_node(AST_SymbolRef, candidate.name, candidate.name),
right: value
});
}
candidate.write_only = false;
return candidate;
}
// These node types have child nodes that execute sequentially,
// but are otherwise not safe to scan into or beyond them.
var sym;
if (node instanceof AST_Call
|| node instanceof AST_Exit
|| node instanceof AST_PropAccess
&& (side_effects || node.expression.may_throw_on_access(compressor))
|| node instanceof AST_SymbolRef
&& (lvalues[node.name]
|| side_effects && !references_in_scope(node.definition()))
|| (sym = lhs_or_def(node))
&& (sym instanceof AST_PropAccess || sym.name in lvalues)
|| may_throw
&& (in_try ? node.has_side_effects(compressor) : side_effects_external(node))
|| (side_effects || !replace_all)
&& (parent instanceof AST_Binary && lazy_op(parent.operator)
|| parent instanceof AST_Conditional
|| parent instanceof AST_If)) {
stop_after = node;
if (node instanceof AST_Scope) abort = true;
}
// Skip (non-executed) functions
if (node instanceof AST_Scope) return node;
}, function(node) {
if (!abort && stop_after === node) abort = true;
});
var multi_replacer = new TreeTransformer(function(node) {
if (abort) return node;
// Skip nodes before `candidate` as quickly as possible
if (!hit) {
if (node !== hit_stack[hit_index]) return node;
hit_index++;
if (hit_index < hit_stack.length) return;
hit = true;
return node;
}
// Replace variable when found
if (node instanceof AST_SymbolRef
&& node.name == def.name) {
if (!--replaced) abort = true;
if (is_lhs(node, multi_replacer.parent())) return node;
def.replaced++;
value_def.replaced--;
return candidate.value;
}
// Skip (non-executed) functions and (leading) default case in switch statements
if (node instanceof AST_Default || node instanceof AST_Scope) return node;
});
while (--stat_index >= 0) {
// Treat parameters as collapsible in IIFE, i.e.
// function(a, b){ ... }(x());
// would be translated into equivalent assignments:
// var a = x(), b = undefined;
if (stat_index == 0 && compressor.option("unused")) extract_args();
// Find collapsible assignments
var hit_stack = [];
extract_candidates(statements[stat_index]);
while (candidates.length > 0) {
hit_stack = candidates.pop();
var hit_index = 0;
var candidate = hit_stack[hit_stack.length - 1];
var value_def = null;
var stop_after = null;
var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue;
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
var replace_all = value_def;
if (!replace_all && lhs instanceof AST_SymbolRef) {
var def = lhs.definition();
if (def.references.length - def.replaced == (candidate instanceof AST_VarDef ? 1 : 2)) {
replace_all = true;
}
}
var side_effects = value_has_side_effects(candidate);
var may_throw = candidate.may_throw(compressor);
var funarg = candidate.name instanceof AST_SymbolFunarg;
var hit = funarg;
var abort = false, replaced = 0, can_replace = !args || !hit;
if (!can_replace) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) {
args[j].transform(scanner);
}
can_replace = true;
}
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(scanner);
}
if (value_def) {
var def = candidate.name.definition();
if (abort && def.references.length - def.replaced > replaced) replaced = false;
else {
abort = false;
hit_index = 0;
hit = funarg;
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(multi_replacer);
}
value_def.single_use = false;
}
}
if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
}
}
function has_overlapping_symbol(fn, arg, fn_strict) {
var found = false, scan_this = !(fn instanceof AST_Arrow);
arg.walk(new TreeWalker(function(node, descend) {
if (found) return true;
if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
var s = node.definition().scope;
if (s !== scope) while (s = s.parent_scope) {
if (s === scope) return true;
}
return found = true;
}
if ((fn_strict || scan_this) && node instanceof AST_This) {
return found = true;
}
if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) {
var prev = scan_this;
scan_this = false;
descend();
scan_this = prev;
return true;
}
}));
return found;
}
function extract_args() {
var iife, fn = compressor.self();
if (is_func_expr(fn)
&& !fn.name
&& !fn.uses_arguments
&& !fn.uses_eval
&& (iife = compressor.parent()) instanceof AST_Call
&& iife.expression === fn
&& all(iife.args, function(arg) {
return !(arg instanceof AST_Expansion);
})) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
for (var i = len; --i >= 0;) {
var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue;
names[sym.name] = true;
if (sym instanceof AST_Expansion) {
var elements = iife.args.slice(i);
if (all(elements, function(arg) {
return !has_overlapping_symbol(fn, arg, fn_strict);
})) {
candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym.expression,
value: make_node(AST_Array, iife, {
elements: elements
})
}) ]);
}
} else {
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null;
if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym,
value: arg
}) ]);
}
}
}
}
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (!expr.left.has_side_effects(compressor)) {
candidates.push(hit_stack.slice());
}
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
extract_candidates(expr.right);
} else if (expr instanceof AST_Call) {
extract_candidates(expr.expression);
expr.args.forEach(extract_candidates);
} else if (expr instanceof AST_Case) {
extract_candidates(expr.expression);
} else if (expr instanceof AST_Conditional) {
extract_candidates(expr.condition);
extract_candidates(expr.consequent);
extract_candidates(expr.alternative);
} else if (expr instanceof AST_Definitions
&& (compressor.option("unused") || !(expr instanceof AST_Const))) {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_Exit) {
if (expr.value) extract_candidates(expr.value);
} else if (expr instanceof AST_For) {
if (expr.init) extract_candidates(expr.init);
if (expr.condition) extract_candidates(expr.condition);
if (expr.step) extract_candidates(expr.step);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
--- FUNCTION SOURCE (make_num) id{367,1} start{258517} ---
(num) {
var str = num.toString(10), a = [ str.replace(/^0\x5c./, ".").replace('e+', 'e') ], m;
if (Math.floor(num) === num) {
if (num >= 0) {
a.push("0x" + num.toString(16).toLowerCase(), // probably pointless
"0" + num.toString(8)); // same.
} else {
a.push("-0x" + (-num).toString(16).toLowerCase(), // probably pointless
"-0" + (-num).toString(8)); // same.
}
if ((m = /^(.*?)(0+)$/.exec(num))) {
a.push(m[1] + "e" + m[2].length);
}
} else if ((m = /^0?\x5c.(0+)(.*)$/.exec(num))) {
a.push(m[2] + "e-" + (m[1].length + m[2].length),
str.substr(str.indexOf(".")));
}
return best_of(a);
}
--- END ---
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
function mangleable_var(var_def) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return;
var def = value.definition();
if (def.undeclared) return;
return value_def = def;
}
function get_lhs(expr) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
return !is_ref_of(lhs, AST_SymbolConst) && lhs;
}
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var tw = new TreeWalker(function(node, descend) {
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
get_rvalue(expr).walk(tw);
return lvalues;
}
function lhs_or_def(node) {
if (node instanceof AST_VarDef) return node.value && node.name;
return is_lhs(node.left, node);
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var iife = compressor.parent(), argnames = compressor.self().argnames;
var index = argnames.indexOf(expr.name);
if (index < 0) {
iife.args.length = Math.min(iife.args.length, argnames.length - 1);
} else {
var args = iife.args;
if (args[index]) args[index] = make_node(AST_Number, args[index], {
value: 0
});
}
return true;
}
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
node.value = null;
return node;
}
return in_list ? MAP.skip : null;
}
}, function(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
}
}));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope.get_defun_scope() !== scope) return false;
return def.references.every(function(ref) {
return ref.scope.get_defun_scope() === scope;
});
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
}
}
--- END ---
n();
var value = candidate.value;
if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
def.replaced++;
if (funarg && is_identifier_atom(value)) {
return value.transform(compressor);
} else {
return maintain_this_binding(parent, node, value);
}
}
return make_node(AST_Assign, candidate, {
operator: "=",
left: make_node(AST_SymbolRef, candidate.name, candidate.name),
right: value
});
}
candidate.write_only = false;
return candidate;
}
// These node types have child nodes that execute sequentially,
// but are otherwise not safe to scan into or beyond them.
var sym;
if (node instanceof AST_Call
|| node instanceof AST_Exit
|| node instanceof AST_PropAccess
&& (side_effects || node.expression.may_throw_on_access(compressor))
|| node instanceof AST_SymbolRef
&& (lvalues[node.name]
|| side_effects && !references_in_scope(node.definition()))
|| (sym = lhs_or_def(node))
&& (sym instanceof AST_PropAccess || sym.name in lvalues)
|| may_throw
&& (in_try ? node.has_side_effects(compressor) : side_effects_external(node))
|| (side_effects || !replace_all)
&& (parent instanceof AST_Binary && lazy_op(parent.operator)
|| parent instanceof AST_Conditional
|| parent instanceof AST_If)) {
stop_after = node;
if (node instanceof AST_Scope) abort = true;
}
// Skip (non-executed) functions
if (node instanceof AST_Scope) return node;
}, function(node) {
if (!abort && stop_after === node) abort = true;
});
var multi_replacer = new TreeTransformer(function(node) {
if (abort) return node;
// Skip nodes before `candidate` as quickly as possible
if (!hit) {
if (node !== hit_stack[hit_index]) return node;
hit_index++;
if (hit_index < hit_stack.length) return;
hit = true;
return node;
}
// Replace variable when found
if (node instanceof AST_SymbolRef
&& node.name == def.name) {
if (!--replaced) abort = true;
if (is_lhs(node, multi_replacer.parent())) return node;
def.replaced++;
value_def.replaced--;
return candidate.value;
}
// Skip (non-executed) functions and (leading) default case in switch statements
if (node instanceof AST_Default || node instanceof AST_Scope) return node;
});
while (--stat_index >= 0) {
// Treat parameters as collapsible in IIFE, i.e.
// function(a, b){ ... }(x());
// would be translated into equivalent assignments:
// var a = x(), b = undefined;
if (stat_index == 0 && compressor.option("unused")) extract_args();
// Find collapsible assignments
var hit_stack = [];
extract_candidates(statements[stat_index]);
while (candidates.length > 0) {
hit_stack = candidates.pop();
var hit_in--- FUNCTION SOURCE (before) id{385,-1} start{393300} ---
(node, descend, in_list) {
var parent = tt.parent();
if (drop_vars) {
var sym = assign_as_unused(node);
if (sym instanceof AST_SymbolRef) {
var def = sym.definition();
var in_use = def.id in in_use_ids;
if (node instanceof AST_Assign) {
if (!in_use || def.id in fixed_ids && fixed_ids[def.id] !== node) {
return maintain_this_binding(parent, node, node.right.transform(tt));
}
} else if (!in_use) return make_node(AST_Number, node, {
value: 0
});
}
}
if (scope !== self) return;
if (node.name
&& (!compressor.option("keep_classnames") && node instanceof AST_ClassExpression
|| !compressor.option("keep_fnames") && node instanceof AST_Function)) {
var def = node.name.definition();
// any declarations with same name will overshadow
// name of this anonymous function and can therefore
// never be used anywhere
if (!(def.id in in_use_ids) || def.orig.length > 1)
node.name = null;
}
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
var trim = !compressor.option("keep_fargs");
for (var a = node.argnames, i = a.length; --i >= 0;) {
var sym = a[i];
if (sym instanceof AST_Expansion) {
sym = sym.expression;
}
if (sym instanceof AST_DefaultAssign) {
sym = sym.left;
}
// Do not drop destructuring arguments.
// They constitute a type assertion, so dropping
// them would stop that TypeError which would happen
// if someone called it with an incorrectly formatted
// parameter.
if (!(sym instanceof AST_Destructuring) && !(sym.definition().id in in_use_ids)) {
sym.__unused = true;
if (trim) {
a.pop();
compressor[sym.unreferenced() ? "warn" : "info"]("Dropping unused function argument {name} [{file}:{line},{col}]", template(sym));
}
}
else {
trim = false;
}
}
}
if ((node instanceof AST_Defun || node instanceof AST_DefClass) && node !== self) {
var def = node.name.definition();
var keep = (def.id in in_use_ids) || !drop_funcs && def.global;
if (!keep) {
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
def.eliminated++;
return 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_dex = 0;
var candidate = hit_stack[hit_stack.length - 1];
var value_def = null;
var stop_after = null;
var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue;
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
var replace_all = value_def;
if (!replace_all && lhs instanceof AST_SymbolRef) {
var def = lhs.definition();
if (def.references.length - def.replaced == (candidate instanceof AST_VarDef ? 1 : 2)) {
replace_all = true;
}
}
var side_effects = value_has_side_effects(candidate);
var may_throw = candidate.may_throw(compressor);
var funarg = candidate.name instanceof AST_SymbolFunarg;
var hit = funarg;
var abort = false, replaced = 0, can_replace = !args || !hit;
if (!can_replace) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) {
args[j].transform(scanner);
}
can_replace = true;
}
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(scanner);
}
if (value_def) {
var def = candidate.name.definition();
if (abort && def.references.length - def.replaced > replaced) replaced = false;
else {
abort = false;
hit_index = 0;
hit = funarg;
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(multi_replacer);
}
value_def.single_use = false;
}
}
if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
}
}
function has_overlapping_symbol(fn, arg, fn_strict) {
var found = false, scan_this = !(fn instanceof AST_Arrow);
arg.walk(new TreeWalker(function(node, descend) {
if (found) return true;
if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
var s = node.definition().scope;
if (s !== scope) while (s = s.parent_scope) {
if (s === scope) return true;
}
return found = true;
}
if ((fn_strict || scan_this) && node instanceof AST_This) {
return found = true;
}
if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) {
var prev = scan_this;
scan_this = false;
descend();
scan_this = prev;
return true;
}
}));
return found;
}
function extract_args() {
var iife, fn = compressor.self();
if (is_func_expr(fn)
&& !fn.name
&& !fn.uses_arguments
&& !fn.uses_eval
&& (iife = compressor.parent()) instanceof AST_Call
&& iife.expression === fn
&& all(iife.args, function(arg) {
effects = [];
node.definitions.forEach(function(def) {
if (def.value) def.value = def.value.transform(tt);
if (def.name instanceof AST_Destructuring) return tail.push(def);
var sym = def.name.definition();
if (drop_block && sym.global) return tail.push(def);
if (!(drop_vars || drop_block) || sym.id in in_use_ids) {
if (def.value && sym.id in fixed_ids && fixed_ids[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)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
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: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[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) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value);
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
sym.eliminated++;
}
});
if (head.length > 0 || tai return !(arg instanceof AST_Expansion);
})) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
for (var i = len; --i >= 0;) {
var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue;
names[sym.name] = true;
if (sym instanceof AST_Expansion) {
var elements = iife.args.slice(i);
if (all(elements, function(arg) {
return !has_overlapping_symbol(fn, arg, fn_strict);
})) {
candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym.expression,
value: make_node(AST_Array, iife, {
elements: elements
})
}) ]);
}
} else {
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null;
if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym,
value: arg
}) ]);
}
}
}
}
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (!expr.left.has_side_effects(compressor)) {
candidates.push(hit_stack.slice());
}
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
extract_candidates(expr.right);
} else if (expr instanceof AST_Call) {
extract_candidates(expr.expression);
expr.args.forEach(extract_candidates);
} else if (expr instanceof AST_Case) {
extract_candidates(expr.expression);
} else if (expr instanceof AST_Conditional) {
extract_candidates(expr.condition);
extract_candidates(expr.consequent);
extract_candidates(expr.alternative);
} else if (expr instanceof AST_Definitions
&& (compressor.option("unused") || !(expr instanceof AST_Const))) {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_Exit) {
if (expr.value) extract_candidates(expr.value);
} else if (expr instanceof AST_For) {
if (expr.init) extract_candidates(expr.init);
if (expr.condition) extract_candidates(expr.condition);
if (expr.step) extract_candidates(expr.step);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
l.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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
function mangleable_var(var_def) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return;
var def = value.definition();
if (def.undeclared) return;
return value_def = def;
}
function get_lhs(expr) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
return !is_ref_of(lhs, AST_SymbolConst) && lhs;
}
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var tw = new TreeWalker(function(node, descend) {
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
get_rvalue(expr).walk(tw);
return lvalues;
}
function lhs_or_def(node) {
if (node instanceof AST_VarDef) return node.value && node.name;
return is_lhs(node.left, node);
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var iife = compressor.parent(), argnames = compressor.self().argnames;
var index = argnames.indexOf(expr.name);
if (index < 0) {
iife.args.length = Math.min(iife.args.length, argnames.length - 1);
} else {
var args = iife.args;
if (args[index]) args[index] = make_node(AST_Number, args[index], {
value: 0
});
}
return true;
}
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
node.value = null;
return node;
}
return in_list ? MAP.skip : null;
}
}, function(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
}
}));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope.get_defun_scope() !== scope) return false;
return def.references.every(function(ref) {
return ref.scope.get_defun_scope() === scope;
});
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
}
}
--- END ---
if (!keep) {
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
def.eliminated++;
return 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);
if (def.name instanceof AST_Destructuring) return tail.push(def);
var sym = def.name.definition();
if (drop_block && sym.global) return tail.push(def);
if (!(drop_vars || drop_block) || sym.id in in_use_ids) {
if (def.value && sym.id in fixed_ids && fixed_ids[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)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
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: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[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) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value);
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
if ((in_export || def.global) && !(def.id in in_use_ids)) {
in_use_ids[def.id] = true;
in_use.push(def);
}
}
}));
}
if (def.value) {
if (def.name instanceof AST_Destructuring) {
var destructuring_cache = destructuring_value;
destructuring_value = def.value;
def.walk(tw);
destructuring_value = destructuring_cache;
} else {
var node_def = def.name.definition();;
initializations.add(node_def.id, def.value);
if (def.name.fixed_value() === def.value) {
fixed_ids[node_def.id] = def;
}
}
if (def.value.has_side_effects(compressor)) {
def.value.walk(tw);
}
}
});
return true;
}
if (node.destructuring && destructuring_value) {
initializations.add(node.name, destructuring_value);
}
return scan_ref_scoped(node, descend);
}
--- END ---
de.name, def, value;
if (sym.scope === self
&& (def = sym.definition()).escaped != 1
&& !def.single_use
&& !def.direct_access
&& !compressor.exposed(def)
&& !top_retain(def)
&& (value = sym.fixed_value()) === node.value
&& value instanceof AST_Object) {
descend(node, this);
var defs = new Dictionary();
var assignments = [];
value.properties.forEach(function(prop) {
assignments.push(make_node(AST_VarDef, node, {
name: make_sym(prop.key),
value: prop.value
}));
});
defs_by_id[def.id] = defs;
return MAP.splice(assignments);
}
}
if (node instanceof AST_PropAccess && node.expression instanceof AST_SymbolRef) {
var defs = defs_by_id[node.expression.definition().id];
if (defs) {
var def = defs.get(get_value(node.property));
var sym = make_node(AST_SymbolRef, node, {
name: def.name,
scope: node.expression.scope,
thedef: def
});
sym.reference({});
return sym;
}
}
function make_sym(key) {
var new_var = make_node(sym.CTOR, sym, {
name: self.make_var_name(sym.name + "_" + key),
scope: self
});
var def = self.def_variable(new_var);
defs.set(key, def);
self.enclosed.push(def);
return new_var;
}
}
--- END ---
tatement) {
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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
e.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
}
// These node types have child nodes that execute sequentially,
// but are otherwise not safe to scan into or beyond them.
var sym;
if (node instanceof AST_Call
|| node instanceof AST_Exit
|| node instanceof AST_PropAccess
&& (side_effects || node.expression.may_throw_on_access(compressor))
|| node instanceof AST_SymbolRef
&& (lvalues[node.name]
|| side_effects && !references_in_scope(node.definition()))
|| (sym = lhs_or_def(node))
&& (sym instanceof AST_PropAccess || sym.name in lvalues)
|| may_throw
&& (in_try ? node.has_side_effects(compressor) : side_effects_external(node))
|| (side_effects || !replace_all)
&& (parent instanceof AST_Binary && lazy_op(parent.operator)
|| parent instanceof AST_Conditional
|| parent instanceof AST_If)) {
stop_after = node;
if (node instanceof AST_Scope) abort = true;
}
// Skip (non-executed) functions
if (node instanceof AST_Scope) return node;
}, function(node) {
if (!abort && stop_after === node) abort = true;
});
var multi_replacer = new TreeTransformer(function(node) {
if (abort) return node;
// Skip nodes before `candidate` as quickly as possible
if (!hit) {
if (node !== hit_stack[hit_index]) return node;
hit_index++;
if (hit_index < hit_stack.length) return;
hit = true;
return node;
}
// Replace variable when found
if (node instanceof AST_SymbolRef
&& node.name == def.name) {
if (!--replaced) abort = true;
if (is_lhs(node, multi_replacer.parent())) return node;
def.replaced++;
value_def.replaced--;
return candidate.value;
}
// Skip (non-executed) functions and (leading) default case in switch statements
if (node instanceof AST_Default || node instanceof AST_Scope) return node;
});
while (--stat_index >= 0) {
// Treat parameters as collapsible in IIFE, i.e.
// function(a, b){ ... }(x());
// would be translated into equivalent assignments:
// var a = x(), b = undefined;
if (stat_index == 0 && compressor.option("unused")) extract_args();
// Find collapsible assignments
var hit_stack = [];
extract_candidates(statements[stat_index]);
while (candidates.length > 0) {
hit_stack = candidates.pop();
var hit_index = 0;
var candidate = hit_stack[hit_stack.length - 1];
var value_def = null;
var stop_after = null;
var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue;
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
var replace_all = value_def;
if (!replace_all && lhs instanceof AST_SymbolRef) {
var def = lhs.definition();
if (def.references.length - def.replaced == (candidate instanceof AST_VarDef ? 1 : 2)) {
replace_all = true;
}
}
var side_effects = value_has_side_effects(candidate);
var may_throw = candidate.may_throw(compressor);
var funarg = candidate.name instanceof AST_SymbolFunarg;
var hit = funarg;
var abort = false, replaced = 0, can_replace = !args || !hit;
if (!can_replace) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) {
args[j].transform(scanner);
}
can_replace = true;
}
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(scanner);
}
if (value_def) {
var def = candidate.name.definition();
if (abort && def.references.length - def.replaced > replaced) replaced = false;
else {
abort = false;
hit_index = 0;
hit = funarg;
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(multi_replacer);
}
value_def.single_use = false;
}
}
if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
}
}
function has_overlapping_symbol(fn, arg, fn_strict) {
var found = false, scan_this = !(fn instanceof AST_Arrow);
arg.walk(new TreeWalker(function(node, descend) {
if (found) return true;
if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
var s = node.definition().scope;
if (s !== scope) while (s = s.parent_scope) {
if (s === scope) return true;
}
return found = true;
}
if ((fn_strict || scan_this) && node instanceof AST_This) {
return found = true;
}
if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) {
var prev = scan_this;
scan_this = false;
descend();
scan_this = prev;
return true;
}
}));
return found;
}
function extract_args() {
var iife, fn = compressor.self();
if (is_func_expr(fn)
&& !fn.name
&& !fn.uses_arguments
&& !fn.uses_eval
&& (iife = compressor.parent()) instanceof AST_Call
&& iife.expression === fn
&& all(iife.args, function(arg) {
return !(arg instanceof AST_Expansion);
})) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
for (var i = len; --i >= 0;) {
var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue;
names[sym.name] = true;
if (sym instanceof AST_Expansion) {
var elements = iife.args.slice(i);
if (all(elements, function(arg) {
return !has_overlapping_symbol(fn, arg, fn_strict);
})) {
candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym.expression,
value: make_node(AST_Array, iife, {
elements: elements
})
}) ]);
}
} else {
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null;
if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym,
value: arg
}) ]);
}
}
}
}
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (!expr.left.has_side_effects(compressor)) {
candidates.push(hit_stack.slice());
}
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
extract_candidates(expr.right);
} else if (expr instanceof AST_Call) {
extract_candidates(expr.expression);
expr.args.forEach(extract_candidates);
} else if (expr instanceof AST_Case) {
extract_candidates(expr.expression);
} else if (expr instanceof AST_Conditional) {
extract_candidates(expr.condition);
extract_candidates(expr.consequent);
extract_candidates(expr.alternative);
} else if (expr instanceof AST_Definitions
&& (compressor.option("unused") || !(expr instanceof AST_Const))) {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_Exit) {
if (expr.value) extract_candidates(expr.value);
} else if (expr instanceof AST_For) {
if (expr.init) extract_candidates(expr.init);
if (expr.condition) extract_candidates(expr.condition);
if (expr.step) extract_candidates(expr.step);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
function mangleable_var(var_def) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return;
var def = value.definition();
if (def.undeclared) return;
return value_def = def;
}
function get_lhs(expr) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
return !is_ref_of(lhs, AST_SymbolConst) && lhs;
}
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var tw = new TreeWalker(function(node, descend) {
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
get_rvalue(expr).walk(tw);
return lvalues;
}
function lhs_or_def(node) {
if (node instanceof AST_VarDef) return node.value && node.name;
return is_lhs(node.left, node);
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var iife = compressor.parent(), argnames = compressor.self().argnames;
var index = argnames.indexOf(expr.name);
if (index --- FUNCTION SOURCE (_walk) id{220,-1} start{41266} ---
(visitor) {
return visitor._visit(this, function(){
if (this.key instanceof AST_Node)
this.key._walk(visitor);
this.value._walk(visitor);
});
}
--- END ---
e.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
< 0) {
iife.args.length = Math.min(iife.args.length, argnames.length - 1);
} else {
var args = iife.args;
if (args[index]) args[index] = make_node(AST_Number, args[index], {
value: 0
});
}
return true;
}
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
node.value = null;
return node;
}
return in_list ? MAP.skip : null;
}
}, function(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
}
}));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope.get_defun_scope() !== scope) return false;
return def.references.every(function(ref) {
return ref.scope.get_defun_scope() === scope;
});
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
}
}
function eliminate_spurious_blocks(statements) {
var seen_dirs = [];
for (var i = 0; i < statements.length;) {
var stat = statements[i];
if (stat instanceof AST_BlockStatement && all(stat.body, can_be_evicted_from_block)) {
CHANGED = true;
eliminate_spurious_blocks(stat.body);
[].splice.apply(statements, [i, 1].concat(stat.body));
i += stat.body.length;
} else if (stat instanceof AST_EmptyStatement) {
CHANGED = true;
statements.splice(i, 1);
} else if (stat instanceof AST_Directive) {
if (seen_dirs.indexOf(stat.value) < 0) {
i++;
seen_dirs.push(stat.value);
} else {
CHANGED = true;
statements.splice(i, 1);
}
} else i++;
}
}
function handle_if_return(statements, compressor) {
var self = compressor.self();
var multiple_if_returns = has_multiple_if_returns(statements);
var in_lambda = self instanceof AST_Lambda;
for (var i = statements.length; --i >= 0;) {
var stat = statements[i];
var j = next_index(i);
var next = statements[j];
if (in_lambda && !next && stat instanceof AST_Return) {
if (!stat.value) {
CHANGED = true;
statements.splice(i, 1);
continue;
}
if (stat.value instanceof AST_UnaryPrefix && stat.value.operator == "void") {
CHANGED = true;
statements[i] = make_node(AST_SimpleStatement, stat, {
body: stat.value.expression
});
continue;
}
}
if (stat instanceof AST_If) {
var ab = aborts(stat.body);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
var body = as_statement_array_with_return(stat.body, ab);
stat.body = make_node(AST_BlockStatement, stat, {
body: as_statement_array(stat.alternative).concat(extract_functions())
});
stat.alternative = make_node(AST_BlockStatement, stat, {
body: body
});
statements[i] = stat.transform(compressor);
continue;
}
var ab = aborts(stat.alternative);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
stat = stat.clone();
stat.body = make_node(AST_BlockStatement, stat.body, {
body: as_statement_array(stat.body).concat(extract_functions())
});
var body = as_statement_array_with_return(stat.alternative, ab);
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
body: body
});
statements[i] = stat.transform(compressor);
continue;
}
}
if (stat instanceof AST_If && stat.body instanceof AST_Return) {
var value = stat.body.value;
//---
// pretty silly case, but:
// if (foo()) return; return; ==> foo(); return;
if (!value && !stat.alternative
&& (in_lambda && !next || next instanceof AST_Return && !next.value)) {
CHANGED = true;
statements[i] = make_node(AST_SimpleStatement, stat.condition, {
body: stat.condition
});
continue;
}
//---
// if (foo()) return x; return y; ==> return foo() ? x : y;
if (value && !stat.alternative && next instanceof AST_Return && next.value) {
CHANGED = true;
stat = stat.clone();
stat.alternative = next;
statements.splice(i, 1, stat.transform(compressor));
statements.splice(j, 1);
continue;
}
//---
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
if (value && !stat.alternative
&& (!next && in_lambda && multiple_if_returns
|| next instanceof AST_Return)) {
CHANGED = true;
stat = stat.clone();
stat.alternative = next || make_node(AST_Return, stat, {
value: null
});
statements.splice(i, 1, stat.transform(compressor));
if (next) statements.splice(j, 1);
continue;
}
//---
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
//
// if sequences is not enabled, this can lead to an endless loop (issue #866).
// however, with sequences on this helps producing slightly better output for
// the example code.
var prev = statements[prev_index(i)];
if (compressor.option("sequences") && in_lambda && !stat.alternative
&& prev instanceof AST_If && prev.body instanceof AST_Return
&& next_index(j) == statements.length && next instanceof AST_SimpleStatement) {
CHANGED = true;
stat = stat.clone();
stat.alternative = make_node(AST_BlockStatement, next, {
body: [
next,
make_node(AST_Return, next, {
value: null
})
]
});
statements.splice(i, 1, stat.transform(compressor));
statements.splice(j, 1);
continue;
}
}
}
function has_multiple_if_returns(statements) {
var n = 0;
for (var i = statements.length; --i >= 0;) {
var stat = statements[i];
if (stat instanceof AST_If && stat.body instanceof AST_Return) {
if (++n > 1) return true;
}
}
return false;
}
function is_return_void(value) {
return !value || value instanceof AST_UnaryPrefix && value.operator == "void";
}
function can_merge_flow(ab) {
if (!ab) return false;
for (var j = i + 1, len = statements.length; j < len; j++) {
var stat = statements[j];
if (stat instanceof AST_Const || stat instanceof AST_Let) return false;
}
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
return ab instanceof AST_Return && in_lambda && is_return_void(ab.value)
|| ab instanceof AST_Continue && self === loop_body(lct)
|| ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct;
}
function extract_functions() {
var tail = statements.slice(i + 1);
statements.length = i + 1;
return tail.filter(function(stat) {
if (stat instanceof AST_Defun) {
statements.push(stat);
return false;
}
return true;
});
}
function as_statement_array_with_return(node, ab) {
var body = as_statement_array(node).slice(0, -1);
if (ab.value) {
body.push(make_node(AST_SimpleStatement, ab.value, {
body: ab.value.expression
}));
}
return body;
}
function next_index(i) {
for (var j = i + 1, len = statements.length; j < len; j++) {
var stat = statements[j];
if (!(stat instanceof AST_Var && declarations_only(stat))) {
break;
}
}
return j;
}
function prev_index(i) {
for (var j = i; --j >= 0;) {
var stat = statements[j];
if (!(stat instanceof AST_Var && declarations_only(stat))) {
break;
}
}
return j;
}
}
function eliminate_dead_code(statements, compressor) {
var has_quit;
var self = compressor.self();
for (var i = 0, n = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (stat instanceof AST_LoopControl) {
var lct = compressor.loopcontrol_target(stat);
if (stat instanceof AST_Break
&& !(lct instanceof AST_IterationStatement)
&& loop_body(lct) === self
|| stat instanceof AST_Continue
&& loop_body(lct) === self) {
if (stat.label) {
remove(stat.label.thedef.references, stat);
}
} else {
statements[n++] = stat;
}
} else {
statements[n++] = stat;
}
if (aborts(stat)) {
has_quit = statements.slice(i + 1);
break;
}
}
statements.length = n;
CHANGED = n != len;
if (has_quit) has_quit.forEach(function(stat) {
extract_declarations_from_unreachable_code(compressor, stat, statements);
});
}
function declarations_only(node) {
return all(node.definitions, function(var_def) {
return !var_def.value;
});
}
function sequencesize(statements, compressor) {
if (statements.length < 2) return;
var seq = [], n = 0;
function push_seq() {
if (!seq.length) return;
var body = make_sequence(seq[0], seq);
statements[n++] = make_node(AST_SimpleStatement, body, { body: body });
seq = [];
}
for (var i = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (stat instanceof AST_SimpleStatement) {
if (seq.length >= compressor.sequences_limit) push_seq();
var body = stat.body;
if (seq.length > 0) body = body.drop_side_effect_free(compressor);
if (body) merge_sequence(seq, body);
} else if (stat instanceof AST_Definitions && declarations_only(stat)
|| stat instanceof AST_Defun) {
statements[n++] = stat;
} else {
push_seq();
statements[n++] = stat;
}
}
push_seq();
statements.length = n;
if (n != len) CHANGED = true;
}
function to_simple_statement(block, decls) {
if (!(block instanceof AST_BlockStatement)) return block;
var stat = null;
for (var i = 0, len = block.body.length; i < len; i++) {
var line = block.body[i];
if (line instanceof AST_Var && declarations_only(line)) {
decls.push(line);
} else if (stat) {
return false;
} else {
stat = line;
}
}
return stat;
}
function sequencesize_2(statements, compressor) {
function cons_seq(right) {
n--;
CHANGED = true;
var left = prev.body;
return make_sequence(left, [ left, right ]).transform(compressor);
};
var n = 0, prev;
for (var i = 0; i < statements.length; i++) {
var stat = statements[i];
if (prev) {
if (stat instanceof AST_Exit) {
stat.value = cons_seq(stat.value || make_node(AST_Undefined, stat).transform(compressor));
} else if (stat instanceof AST_For) {
if (!(stat.init instanceof AST_Definitions)) {
var abort = false;
prev.body.walk(new TreeWalker(function(node) {
if (abort || node instanceof AST_Scope) return true;
if (node instanceof AST_Binary && node.operator == "in") {
abort = true;
return true;
}
}));
if (!abort) {
if (stat.init) stat.init = cons_seq(stat.init);
else {
stat.init = prev.body;
n--;
CHANGED = true;
}
}
}
} else if (stat instanceof AST_ForIn) {
if (!(stat.init instanceof AST_Const) && !(stat.init instanceof AST_Let)) {
stat.object = cons_seq(stat.object);
}
} else if (stat instanceof AST_If) {
stat.condition = cons_seq(stat.condition);
} else if (stat instanceof AST_Switch) {
stat.expression = cons_seq(stat.expression);
} else if (stat instanceof AST_With) {
stat.expression = cons_seq(stat.expression);
}
}
if (compressor.option("conditionals") && stat instanceof AST_If) {
var decls = [];
var body = to_simple_statement(stat.body, decls);
var alt = to_simple_statement(stat.alternative, decls);
if (body !== false && alt !== false && decls.length > 0) {
var len = decls.length;
decls.push(make_node(AST_If, stat, {
condition: stat.condition,
body: body || make_node(AST_EmptyStatement, stat.body),
alternative: alt
}));
decls.unshift(n, 1);
[].splice.apply(statements, decls);
i += len;
n += len + 1;
prev = null;
CHANGED = true;
continue;
}
}
statements[n++] = stat;
prev = stat instanceof AST_SimpleStatement ? stat : null;
}
statements.length = n;
}
function join_object_assignments(defn, body) {
if (!(defn instanceof AST_Definitions)) return;
var def = defn.definitions[defn.definitions.length - 1];
if (!(def.value instanceof AST_Object)) return;
var exprs;
if (body instanceof AST_Assign) {
exprs = [ body ];
} else if (body instanceof AST_Sequence) {
exprs = body.expressions.slice();
}
if (!exprs) return;
var trimmed = false;
do {
var node = exprs[0];
if (!(node instanceof AST_Assign)) break;
if (node.operator != "=") break;
if (!(node.left instanceof AST_PropAcces--- FUNCTION SOURCE (push) id{222,-1} start{275307} ---
(tw) {
tw.safe_ids = Object.create(tw.safe_ids);
}
--- END ---
s)) break;
var sym = node.left.expression;
if (!(sym instanceof AST_SymbolRef)) break;
if (def.name.name != sym.name) break;
if (!node.right.is_constant_expression(scope)) break;
var prop = node.left.property;
if (prop instanceof AST_Node) {
prop = prop.evaluate(compressor);
}
if (prop instanceof AST_Node) break;
prop = "" + prop;
if (compressor.option("ecma") < 6 && compressor.has_directive("use strict")) {
if (!all(def.value.properties, function(node) {
return node.key != prop && node.key.name != prop;
})) break;
}
def.value.properties.push(make_node(AST_ObjectKeyVal, node, {
key: prop,
value: node.right
}));
exprs.shift();
trimmed = true;
} while (exprs.length);
return trimmed && exprs;
}
function join_consecutive_vars(statements) {
var defs;
for (var i = 0, j = -1, len = statements.length; i < len; i++) {
var stat = statements[i];
var prev = statements[j];
if (stat instanceof AST_Definitions) {
if (prev && prev.TYPE == stat.TYPE) {
prev.definitions = prev.definitions.concat(stat.definitions);
CHANGED = true;
} else if (defs && defs.TYPE == stat.TYPE && declarations_only(stat)) {
defs.definitions = defs.definitions.concat(stat.definitions);
CHANGED = true;
} else {
statements[++j] = stat;
defs = stat;
}
} else if (stat instanceof AST_Exit) {
stat.value = extract_object_assignments(stat.value);
} else if (stat instanceof AST_For) {
var exprs = join_object_assignments(prev, stat.init);
if (exprs) {
CHANGED = true;
stat.init = exprs.length ? make_sequence(stat.init, exprs) : null;
statements[++j] = stat;
} else if (prev instanceof AST_Var && (!stat.init || stat.init.TYPE == prev.TYPE)) {
if (stat.init) {
prev.definitions = prev.definitions.concat(stat.init.definitions);
}
stat.init = prev;
statements[j] = stat;
CHANGED = true;
} else if (defs && stat.init && defs.TYPE == stat.init.TYPE && declarations_only(stat.init)) {
defs.definitions = defs.definitions.concat(stat.init.definitions);
stat.init = null;
statements[++j] = stat;
CHANGED = true;
} else {
statements[++j] = stat;
}
} else if (stat instanceof AST_ForIn) {
stat.object = extract_object_assignments(stat.object);
} else if (stat instanceof AST_If) {
stat.condition = extract_object_assignments(stat.condition);
} else if (stat instanceof AST_SimpleStatement) {
var exprs = join_object_assignments(prev, stat.body);
if (exprs) {
CHANGED = true;
if (!exprs.length) continue;
stat.body = make_sequence(stat.body, exprs);
}
statements[++j] = stat;
} else if (stat instanceof AST_Switch) {
stat.expression = extract_object_assignments(stat.expression);
} else if (stat instanceof AST_With) {
--- FUNCTION SOURCE (exposed) id{221,4} start{267203} ---
(def) {
if (def.export) return true;
if (def.global) for (var i = 0, len = def.orig.length; i < len; i++)
if (!this.toplevel[def.orig[i] instanceof AST_SymbolDefun ? "funcs" : "vars"])
return true;
return false;
}
--- END ---
stat.expression = extract_object_assignments(stat.expression);
} else {
statements[++j] = stat;
}
}
statements.length = j + 1;
function extract_object_assignments(value) {
statements[++j] = stat;
var exprs = join_object_assignments(prev, value);
if (exprs) {
CHANGED = true;
if (exprs.length) {
return make_sequence(value, exprs);
} else if (value instanceof AST_Sequence) {
return value.tail_node().left;
} else {
return value.left;
}
}
return value;
}
}
}
--- END ---
e.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
stanceof 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);
if (def.name instanceof AST_Destructuring) return tail.push(def);
var sym = def.name.definition();
if (drop_block && sym.global) return tail.push(def);
if (!(drop_vars || drop_block) || sym.id in in_use_ids) {
if (def.value && sym.id in fixed_ids && fixed_ids[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)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
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: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[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) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side--- FUNCTION SOURCE () id{403,-1} start{186173} ---
(name){
if (name instanceof AST_Symbol) name = name.name;
return this.variables.get(name)
|| (this.parent_scope && this.parent_scope.find_variable(name));
}
--- END ---
_effects.push(value);
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
walk(new TreeWalker(function(node, descend) {
if (found) return true;
if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
var s = node.definition().scope;
if (s !== scope) while (s = s.parent_scope) {
if (s === scope) return true;
}
return found = true;
}
if ((fn_strict || scan_this) && node instanceof AST_This) {
return found = true;
}
if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) {
var prev = scan_this;
scan_this = false;
descend();
scan_this = prev;
return true;
}
}));
return found;
}
function extract_args() {
var iife, fn = compressor.self();
if (is_func_expr(fn)
&& !fn.name
&& !fn.uses_arguments
&& !fn.uses_eval
&& (iife = compressor.parent()) instanceof AST_Call
&& iife.expression === fn
&& all(iife.args, function(arg) {
return !(arg instanceof AST_Expansion);
})) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
for (var i = len; --i >= 0;) {
var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue;
names[sym.name] = true;
if (sym instanceof AST_Expansion) {
var elements = iife.args.slice(i);
if (all(elements, function(arg) {
return !has_overlapping_symbol(fn, arg, fn_strict);
})) {
candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym.expression,
value: make_node(AST_Array, iife, {
elements: elements
})
}) ]);
}
} else {
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null;
if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym,
value: arg
}) ]);
}
}
}
}
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (!expr.left.has_side_effects(compressor)) {
candidates.push(hit_stack.slice());
}
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
extract_candidates(expr.right);
} else if (expr instanceof AST_Call) {
extract_candidates(expr.expression);
expr.args.forEach(extract_candidates);
}--- FUNCTION SOURCE () id{404,1} start{189216} ---
(){
return this.thedef;
}
--- END ---
else if (expr instanceof AST_Case) {
extract_candidates(expr.expression);
} else if (expr instanceof AST_Conditional) {
extract_candidates(expr.condition);
extract_candidates(expr.consequent);
extract_candidates(expr.alternative);
} else if (expr instanceof AST_Definitions
&& (compressor.option("unused") || !(expr instanceof AST_Const))) {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_Exit) {
if (expr.value) extract_candidates(expr.value);
} else if (expr instanceof AST_For) {
if (expr.init) extract_candidates(expr.init);
if (expr.condition) extract_candidates(expr.condition);
if (expr.step) extract_candidates(expr.step);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
function mangleable_var(var_def) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return;
var def = value.definition();
if (def.undeclared) return;
return value_def = def;
}
function get_lhs(expr) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
return !is_ref_of(lhs, AST_SymbolConst) && lhs;
}
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var tw = new TreeWalker(function(node, descend) {
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
get_rvalue(expr).walk(tw);
return lvalues;
}
function lhs_or_def(node) {
if (node instanceof AST_VarDef) return node.value && node.name;
return is_lhs(node.left, node);
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var iife = compressor.parent(), argnames = compressor.self().argnames;
var index = argnames.indexOf(expr.name);
if (index < 0) {
iife.args.length = Math.min(iife.args.length, argnames.length - 1);
} else {
var args = iife.args;
if (args[index]) args[index] = make_node(AST_Number, args[index], {
value: 0
});
}
return true;
}
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
node.value = null;
return node;
}
return in_list ? MAP.skip : null;
}
}, function(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
}
}));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope.get_defun_scope() !== scope) return false;
return def.references.every(function(ref) {
return ref.scope.get_defun_scope() === scope;
});
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
}
}
function eliminate_spurious_blocks(statements) {
var seen_dirs = [];
for (var i = 0; i < statements.length;) {
var stat = statements[i];
if (stat instanceof AST_BlockStatement && all(stat.body, can_be_evicted_from_block)) {
CHANGED = true;
eliminate_spurious_blocks(stat.body);
[].splice.apply(statements, [i, 1].concat(stat.body));
i += stat.body.length;
} else if (stat instanceof AST_EmptyStatement) {
CHANGED = true;
statements.splice(i, 1);
} else if (stat instanceof AST_Directive) {
if (seen_dirs.indexOf(stat.value) < 0) {
i++;
seen_dirs.push(stat.value);
} else {
CHANGED = true;
statements.splice(i, 1);
}
} else i++;
}
}
function handle_if_return(statements, compressor) {
var self = compressor.self();
var multiple_if_returns = has_multiple_if_returns(statements);
var in_lambda = self instanceof AST_Lambda;
for (var i = statements.length; --i >= 0;) {
var stat = statements[i];
var j = next_index(i);
var next = statements[j];
if (in_lambda && !next && stat instanceof AST_Return) {
if (!stat.value) {
CHANGED = true;
statements.splice(i, 1);
continue;
}
if (stat.value instanceof AST_UnaryPrefix && stat.value.operator == "void") {
CHANGED = true;
statements[i] = make_node(AST_SimpleStatement, stat, {
body: stat.value.expression
});
continue;
}
}
if (stat instanceof AST_If) {
var ab = aborts(stat.body);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
var body = as_statement_array_with_return(stat.body, ab);
stat.body = make_node(AST_BlockStatement, stat, {
body: as_statement_array(stat.alternative).concat(extract_functions())
});
stat.alternative = make_node(AST_BlockStatement, stat, {
body: body
});
statements[i] = stat.transform(compressor);
continue;
}
var ab = aborts(stat.alternative);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
stat = stat.clone();
stat.body = make_node(AST_BlockStatement, stat.body, {
body: as_statement_array(stat.body).concat(extract_functions())
});
var body = as_statement_array_with_return(stat.alternative, ab);
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
body: body
});
statements[i] = stat.transform(compressor);
continue;
}
}
if (stat instanceof AST_If && stat.body instanceof AST_Return) {
var value = stat.body.value;
//---
// pretty silly case, but:
// if (foo()) return; return; ==> foo(); return;
if (!value && !stat.alternative
&& (in_lambda && !next || next instanceof AST_Return && !next.value)) {
CHANGED = true;
statements[i] = make_node(AST_SimpleStatement, stat.condition, {
body: stat.condition
});
continue;
}
//---
// if (foo()) return x; return y; ==> return foo() ? x : y;
if (value && !stat.alternative && next instanceof AST_Return && next.value) {
CHANGED = true;
stat = stat.clone();
stat.alternative = next;
statements.splice(i, 1, stat.transform(compressor));
statements.splice(j, 1);
continue;
}
//---
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
if (value && !stat.alternative
&& (!next && in_lambda && multiple_if_returns
|| next instanceof AST_Return)) {
CHANGED = true;
stat = stat.clone();
stat.alternative = next || make_node(AST_Return, stat, {
value: null
});
statements.splice(i, 1, stat.transform(compressor));
if (next) statements.splice(j, 1);
continue;
}
//---
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
//
// if sequences is not enabled, this can lead to an endless loop (issue #866).
// however, with sequences on this helps producing slightly better output for
// the example code.
var prev = statements[prev_index(i)];
if (compressor.option("sequences") && in_lambda && !stat.alternative
&& prev instanceof AST_If && prev.body instanceof AST_Return
&& next_index(j) == statements.length && next instanceof AST_SimpleStatement) {
CHANGED = true;
stat = stat.clone();
stat.alternative = make_node(AST_BlockStatement, next, {
body: [
next,
make_node(AST_Return, next, {
value: null
})
]
});
statements.splice(i, 1, stat.transform(compressor));
statements.splice(j, 1);
continue;
}
}
}
function has_multiple_if_returns(statements) {
var n = 0;
for (var i = statements.length; --i >= 0;) {
var stat = statements[i];
if (stat instanceof AST_If && stat.body instanceof AST_Return) {
if (++n > 1) return true;
}
}
return false;
}
function is_return_void(value) {
return !value || value instanceof AST_UnaryPrefix && value.operator == "void";
}
function can_merge_flow(ab) {
if (!ab) return false;
for (var j = i + 1, len = statements.length; j < len; j++) {
var stat = statements[j];
if (stat instanceof AST_Const || stat instanceof AST_Let) return false;
}
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
return ab instanceof AST_Return && in_lambda && is_return_void(ab.value)
|| ab instanceof AST_Continue && self === loop_body(lct)
|| ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct;
}
function extract_functions() {
var tail = statements.slice(i + 1);
statements.length = i + 1;
return tail.filter(function(stat) {
if (stat instanceof AST_Defun) {
statements.push(stat);
return false;
}
return true;
});
}
function as_statement_array_with_return(node, ab) {
var body = as_statement_array(node).slice(0, -1);
if (ab.value) {
body.push(make_node(AST_SimpleStatement, ab.value, {
body: ab.value.expression
}));
}
return body;
}
function next_index(i) {
for (var j = i + 1, len = statements.length; j < len; j++) {
var stat = statements[j];
if (!(stat instanceof AST_Var && declarations_only(stat))) {
break;
}
}
return j;
}
function prev_index(i) {
for (var j = i; --j >= 0;) {
var stat = statements[j];
if (!(stat instanceof AST_Var && declarations_only(stat))) {
break;
}
}
return j;
}
}
function eliminate_dead_code(statements, compressor) {
var has_quit;
var self = compressor.self();
for (var i = 0, n = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (stat instanceof AST_LoopControl) {
var lct = compressor.loopcontrol_target(stat);
if (stat instanceof AST_Break
&& !(lct instanceof AST_IterationStatement)
&& loop_body(lct) === self
|| stat instanceof AST_Continue
&& loop_body(lct) === self) {
if (stat.label) {
remove(stat.label.thedef.references, stat);
}
} else {
statements[n++] = stat;
}
} else {
statements[n++] = stat;
}
if (aborts(stat)) {
has_quit = statements.slice(i + 1);
break;
}
}
statements.length = n;
CHANGED = n != len;
if (has_quit) has_quit.forEach(function(stat) {
extract_declarations_from_unreachable_code(compressor, stat, statements);
});
}
function declarations_only(node) {
return all(node.definitions, function(var_def) {
return !var_def.value;
});
}
function sequencesize(statements, compressor) {
if (statements.length < 2) return;
var seq = [], n = 0;
function push_seq() {
if (!seq.length) return;
var body = make_sequence(seq[0], seq);
statements[n++] = make_node(AST_SimpleStatement, body, { body: body });
seq = [];
}
for (var i = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (stat instanceof AST_SimpleStatement) {
if (seq.length >= compressor.sequences_limit) push_seq();
var body = stat.body;
if (seq.length > 0) body = body.drop_side_effect_free(compressor);
if (body) merge_sequence(seq, body);
} else if (stat instanceof AST_Definitions && declarations_only(stat)
|| stat instanceof AST_Defun) {
statements[n++] = stat;
} else {
push_seq();
statements[n++] = stat;
}
}
push_seq();
statements.length = n;
if (n != len) CHANGED = true;
}
function to_simple_statement(block, decls) {
if (!(block instanceof AST_BlockStatement)) return block;
var stat = null;
for (var i = 0, len = block.body.length; i < len; i++) {
var line = block.body[i];
if (line instanceof AST_Var && declarations_only(line)) {
decls.push(line);
} else if (stat) {
return false;
} else {
stat = line;
}
}
return stat;
}
function sequencesize_2(statements, compressor) {
function cons_seq(right) {
n--;
CHANGED = true;
var left = prev.body;
return make_sequence(left, [ left, right ]).transform(compressor);
};
var n = 0, prev;
for (var i = 0; i < statements.length; i++) {
var stat = statements[i];
if (prev) {
if (stat instanceof AST_Exit) {
stat.value = cons_seq(stat.value || make_node(AST_Undefined, stat).transform(compressor));
} else if (stat instanceof AST_For) {
if (!(stat.init instanceof AST_Definitions)) {
var abort = false;
prev.body.walk(new TreeWalker(function(node) {
if (abort || node instanceof AST_Scope) return true;
if (node instanceof AST_Binary && node.operator == "in") {
abort = true;
return true;
}
}));
if (!abort) {
if (stat.init) stat.init = cons_seq(stat.init);
else {
stat.init = prev.body;
n--;
CHANGED = true;
}
}
}
} else if (stat instanceof AST_ForIn) {
if (!(stat.init instanceof AST_Const) && !(stat.init instanceof AST_Let)) {
stat.object = cons_seq(stat.object);
}
} else if (stat instanceof AST_If) {
stat.condition = cons_seq(stat.condition);
} else if (stat instanceof AST_Switch) {
stat.expression = cons_seq(stat.expression);
} else if (stat instanceof AST_With) {
stat.expression = cons_seq(stat.expression);
}
}
if (compressor.option("conditionals") && stat instanceof AST_If) {
var decls = [];
var body = to_simple_statement(stat.body, decls);
var alt = to_simple_statement(stat.alternative, decls);
if (body !== false && alt !== false && decls.length > 0) {
var len = decls.length;
decls.push(make_node(AST_If, stat, {
condition: stat.condition,
body: body || make_node(AST_EmptyStatement, stat.body),
alternative: alt
}));
decls.unshift(n, 1);
[].splice.apply(statements, decls);
i += len;
n += len + 1;
prev = null;
CHANGED = true;
continue;
}
}
statements[n++] = stat;
prev = stat instanceof AST_SimpleStatement ? stat : null;
}
statements.length = n;
}
function join_object_assignments(defn, body) {
if (!(defn instanceof AST_Definitions)) return;
var def = defn.definitions[defn.definitions.length - 1];
if (!(def.value instanceof AST_Object)) return;
var exprs;
if (body instanceof AST_Assign) {
exprs = [ body ];
} else if (body instanceof AST_Sequence) {
exprs = body.expressions.slice();
}
if (!exprs) return;
var trimmed = false;
do {
var node = exprs[0];
if (!(node instanceof AST_Assign)) break;
if (node.operator != "=") break;
if (!(node.left instanceof AST_PropAccess)) break;
var sym = node.left.expression;
if (!(sym instanceof AST_SymbolRef)) break;
if (def.name.name != sym.name) break;
if (!node.right.is_constant_expression(scope)) break;
var prop = node.left.property;
if (prop instanceof AST_Node) {
prop = prop.evaluate(compressor);
}
if (prop instanceof AST_Node) break;
prop = "" + prop;
if (compressor.option("ecma") < 6 && compressor.has_directive("use strict")) {
if (!all(def.value.properties, function(node) {
return node.key != prop && node.key.name != prop;
})) break;
}
def.value.properties.push(make_node(AST_ObjectKeyVal, node, {
key: prop,
value: node.right
}));
exprs.shift();
trimmed = true;
} while (exprs.length);
return trimmed && exprs;
}
function join_consecutive_vars(statements) {
var defs;
for (var i = 0, j = -1, len = statements.length; i < len; i++) {
var stat = statements[i];
var prev = statements[j];
if (stat instanceof AST_Definitions) {
if (prev && prev.TYPE == stat.TYPE) {
prev.definitions = prev.definitions.concat(stat.definitions);
CHANGED = true;
} else if (defs && defs.TYPE == stat.TYPE && declarations_only(stat)) {
defs.definitions = defs.definitions.concat(stat.definitions);
CHANGED = true;
} else {
statements[++j] = stat;
defs = stat;
}
} else if (stat instanceof AST_Exit) {
stat.value = extract_object_assignments(stat.value);
} else if (stat instanceof AST_For) {
var exprs = join_object_assignments(prev, stat.init);
if (exprs) {
CHANGED = true;
stat.init = exprs.length ? make_sequence(stat.init, exprs) : null;
statements[++j] = stat;
} else if (prev instanceof AST_Var && (!stat.init || stat.init.TYPE == prev.TYPE)) {
if (stat.init) {
prev.definitions = prev.definitions.concat(stat.init.definitions);
}
stat.init = prev;
statements[j] = stat;
CHANGED = true;
} else if (defs && stat.init && defs.TYPE == stat.init.TYPE && declarations_only(stat.init)) {
defs.definitions = defs.definitions.concat(stat.init.definitions);
stat.init = null;
statements[++j] = stat;
CHANGED = true;
} else {
statements[++j] = stat;
}
} else if (stat instanceof AST_ForIn) {
stat.object = extract_object_assignments(stat.object);
} else if (stat instanceof AST_If) {
stat.condition = extract_object_assignments(stat.condition);
} else if (stat instanceof AST_SimpleStatement) {
var exprs = join_object_assignments(prev, stat.body);
if (exprs) {
CHANGED = true;
if (!exprs.length) continue;
stat.body = make_sequence(stat.body, exprs);
}
statements[++j] = stat;
} else if (stat instanceof AST_Switch) {
stat.expression = extract_object_assignments(stat.expression);
} else if (stat instanceof AST_With) {
stat.expression = extract_object_assignments(stat.expression);
} else {
statements[++j] = stat;
}
}
statements.length = j + 1;
function extract_object_assignments(value) {
statements[++j] = stat;
var exprs = join_object_assignments(prev, value);
if (exprs) {
CHANGED = true;
if (exprs.length) {
return make_sequence(value, exprs);
} else if (value instanceof AST_Sequence) {
return value.tail_node().left;
} else {
return value.left;
}
}
return value;
}
}
}
--- END ---
| node instanceof AST_DefClass) && node !== self) {
var def = node.name.definition();
var keep = (def.id in in_use_ids) || !drop_funcs && def.global;
if (!keep) {
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
def.eliminated++;
return 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);
if (def.name instanceof AST_Destructuring) return tail.push(def);
var sym = def.name.definition();
if (drop_block && sym.global) return tail.push(def);
if (!(drop_vars || drop_block) || sym.id in in_use_ids) {
if (def.value && sym.id in fixed_ids && fixed_ids[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)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
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: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[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) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value);
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
walk(new TreeWalker(function(node, descend) {
if (found) return true;
if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
var s = node.definition().scope;
if (s !== scope) while (s = s.parent_scope) {
if (s === scope) return true;
}
return found = true;
}
if ((fn_strict || scan_this) && node instanceof AST_This) {
return found = true;
}
if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) {
var prev = scan_this;
scan_this = false;
descend();
scan_this = prev;
return true;
}
}));
return found;
}
function extract_args() {
var iife, fn = compressor.self();
if (is_func_expr(fn)
&& !fn.name
&& !fn.uses_arguments
&& !fn.uses_eval
&& (iife = compressor.parent()) instanceof AST_Call
&& iife.expression === fn
&& all(iife.args, function(arg) {
return !(arg instanceof AST_Expansion);
})) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
for (var i = len; --i >= 0;) {
var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue;
names[sym.name] = true;
if (sym instanceof AST_Expansion) {
var elements = iife.args.slice(i);
if (all(elements, function(arg) {
return !has_overlapping_symbol(fn, arg, fn_strict);
})) {
candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym.expression,
value: make_node(AST_Array, iife, {
elements: elements
})
}) ]);
}
} else {
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null;
if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym,
value: arg
}) ]);
}
}
}
}
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (!expr.left.has_side_effects(compressor)) {
candidates.push(hit_stack.slice());
}
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
extract_candidates(expr.right);
} else if (expr instanceof AST_Call) {
extract_candidates(expr.expression);
expr.args.forEach(extract_candidates);
} else if (expr instanceof AST_Case) {
extract_candidates(expr.expression);
} else if (expr instanceof AST_Conditional) {
extract_candidates(expr.condition);
extract_candidates(expr.consequent);
extract_candidates(expr.alternative);
} else if (expr instanceof AST_Definitions
&& (compressor.option("unused") || !(expr instanceof AST_Const))) {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_Exit) {
if (expr.value) extract_candidates(expr.value);
} else if (expr instanceof AST_For) {
if (expr.init) extract_candidates(expr.init);
if (expr.condition) extract_candidates(expr.condition);
if (expr.step) extract_candidates(expr.step);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
function mangleable_var(var_def) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return;
var def = value.definition();
if (def.undeclared) return;
return value_def = def;
}
function get_lhs(expr) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
return !is_ref_of(lhs, AST_SymbolConst) && lhs;
}
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var tw = new TreeWalker(function(node, descend) {
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
get_rvalue(expr).walk(tw);
return lvalues;
}
function lhs_or_def(node) {
if (node instanceof AST_VarDef) return node.value && node.name;
return is_lhs(node.left, node);
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var iife = compressor.parent(), argnames = compressor.self().argnames;
var index = argnames.indexOf(expr.name);
if (index < 0) {
iife.args.length = Math.min(iife.args.length, argnames.length - 1);
} else {
var args = iife.args;
if (args[index]) args[index] = make_node(AST_Number, args[index], {
value: 0
});
}
return true;
}
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
node.value = null;
return node;
}
return in_list ? MAP.skip : null;
}
}, function(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
}
}));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope.get_defun_scope() !== scope) return false;
return def.references.every(function(ref) {
return ref.scope.get_defun_scope() === scope;
});
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
}
}
function eliminate_spurious_blocks(statements) {
var seen_dirs = [];
for (var i = 0; i < statements.length;) {
var stat = statements[i];
if (stat instanceof AST_BlockStatement && all(stat.body, can_be_evicted_from_block)) {
CHANGED = true;
eliminate_spurious_blocks(stat.body);
[].splice.apply(statements, [i, 1].concat(stat.body));
i += stat.body.length;
} else if (stat instanceof AST_EmptyStatement) {
CHANGED = true;
statements.splice(i, 1);
} else if (stat instanceof AST_Directive) {
if (seen_dirs.indexOf(stat.value) < 0) {
i++;
seen_dirs.push(stat.value);
} else {
CHANGED = true;
statements.splice(i, 1);
}
} else i++;
}
}
function handle_if_return(statements, compressor) {
var self = compressor.self();
var multiple_if_returns = has_multiple_if_returns(statements);
var in_lambda = self instanceof AST_Lambda;
for (var i = statements.length; --i >= 0;) {
var stat = statements[i];
var j = next_index(i);
var next = statements[j];
if (in_lambda && !next && stat instanceof AST_Return) {
if (!stat.value) {
CHANGED = true;
statements.splice(i, 1);
continue;
}
if (stat.value instanceof AST_UnaryPrefix && stat.value.operator == "void") {
CHANGED = true;
statements[i] = make_node(AST_SimpleStatement, stat, {
body: stat.value.expression
});
continue;
}
}
if (stat instanceof AST_If) {
var ab = aborts(stat.body);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
var body = as_statement_array_with_return(stat.body, ab);
stat.body = make_node(AST_BlockStatement, stat, {
body: as_statement_array(stat.alternative).concat(extract_functions())
});
stat.alternative = make_node(AST_BlockStatement, stat, {
body: body
});
statements[i] = stat.transform(compressor);
continue;
}
var ab = aborts(stat.alternative);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
stat = stat.clone();
stat.body = make_node(AST_BlockStatement, stat.body, {
body: as_statement_array(stat.body).concat(extract_functions())
});
var body = as_statement_array_with_return(stat.alternative, ab);
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
body: body
});
statements[i] = stat.transform(compressor);
continue;
}
}
if (stat instanceof AST_If && stat.body instanceof AST_Return) {
var value = stat.body.value;
//---
// pretty silly case, but:
// if (foo()) return; return; ==> foo(); return;
if (!value && !stat.alternative
&& (in_lambda && !next || next instanceof AST_Return && !next.value)) {
CHANGED = true;
statements[i] = make_node(AST_SimpleStatement, stat.condition, {
body: stat.condition
});
continue;
}
//---
// if (foo()) return x; return y; ==> return foo() ? x : y;
if (value && !stat.alternative && next instanceof AST_Return && next.value) {
CHANGED = true;
stat = stat.clone();
stat.alternative = next;
statements.splice(i, 1, stat.transform(compressor));
statements.splice(j, 1);
continue;
}
//---
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
if (value && !stat.alternative
&& (!next && in_lambda && multiple_if_returns
|| next instanceof AST_Return)) {
CHANGED = true;
stat = stat.clone();
stat.alternative = next || make_node(AST_Return, stat, {
value: null
});
statements.splice(i, 1, stat.transform(compressor));
if (next) statements.splice(j, 1);
continue;
}
//---
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
//
// if sequences is not enabled, this can lead to an endless loop (issue #866).
// however, with sequences on this helps producing slightly better output for
// the example code.
var prev = statements[prev_index(i)];
if (compressor.option("sequences") && in_lambda && !stat.alternative
&& prev instanceof AST_If && prev.body instanceof AST_Return
&& next_index(j) == statements.length && next instanceof AST_SimpleStatement) {
CHANGED = true;
stat = stat.clone();
stat.alternative = make_node(AST_BlockStatement, next, {
body: [
next,
make_node(AST_Return, next, {
value: null
})
]
});
statements.splice(i, 1, stat.transform(compressor));
statements.splice(j, 1);
continue;
}
}
}
function has_multiple_if_returns(statements) {
var n = 0;
for (var i = statements.length; --i >= 0;) {
var stat = statements[i];
if (stat instanceof AST_If && stat.body instanceof AST_Return) {
if (++n > 1) return true;
}
}
return false;
}
function is_return_void(value) {
return !value || value instanceof AST_UnaryPrefix && value.operator == "void";
}
function can_merge_flow(ab) {
if (!ab) return false;
for (var j = i + 1, len = statements.length; j < len; j++) {
var stat = statements[j];
if (stat instanceof AST_Const || stat instanceof AST_Let) return false;
}
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
return ab instanceof AST_Return && in_lambda && is_return_void(ab.value)
|| ab instanceof AST_Continue && self === loop_body(lct)
|| ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct;
}
function extract_functions() {
var tail = statements.slice(i + 1);
statements.length = i + 1;
return tail.filter(function(stat) {
if (stat instanceof AST_Defun) {
statements.push(stat);
return false;
}
return true;
});
}
function as_statement_array_with_return(node, ab) {
var body = as_statement_array(node).slice(0, -1);
if (ab.value) {
body.push(make_node(AST_SimpleStatement, ab.value, {
body: ab.value.expression
}));
}
return body;
}
function next_index(i) {
for (var j = i + 1, len = statements.length; j < len; j++) {
var stat = statements[j];
if (!(stat instanceof AST_Var && declarations_only(stat))) {
break;
}
}
return j;
}
function prev_index(i) {
for (var j = i; --j >= 0;) {
var stat = statements[j];
if (!(stat instanceof AST_Var && declarations_only(stat))) {
break;
}
}
return j;
}
}
function eliminate_dead_code(statements, compressor) {
var has_quit;
var self = compressor.self();
for (var i = 0, n = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (stat instanceof AST_LoopControl) {
var lct = compressor.loopcontrol_target(stat);
if (stat instanceof AST_Break
&& !(lct instanceof AST_IterationStatement)
&& loop_body(lct) === self
|| stat instanceof AST_Continue
&& loop_body(lct) === self) {
if (stat.label) {
remove(stat.label.thedef.references, stat);
}
} else {
statements[n++] = stat;
}
} else {
statements[n++] = stat;
}
if (aborts(stat)) {
has_quit = statements.slice(i + 1);
break;
}
}
statements.length = n;
CHANGED = n != len;
if (has_quit) has_quit.forEach(function(stat) {
extract_declarations_from_unreachable_code(compressor, stat, statements);
});
}
function declarations_only(node) {
return all(node.definitions, function(var_def) {
return !var_def.value;
});
}
function sequencesize(statements, compressor) {
if (statements.length < 2) return;
var seq = [], n = 0;
function push_seq() {
if (!seq.length) return;
var body = make_sequence(seq[0], seq);
statements[n++] = make_node(AST_SimpleStatement, body, { body: body });
seq = [];
}
for (var i = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (stat instanceof AST_SimpleStatement) {
if (seq.length >= compressor.sequences_limit) push_seq();
var body = stat.body;
if (seq.length > 0) body = body.drop_side_effect_free(compressor);
if (body) merge_sequence(seq, body);
} else if (stat instanceof AST_Definitions && declarations_only(stat)
|| stat instanceof AST_Defun) {
statements[n++] = stat;
} else {
push_seq();
statements[n++] = stat;
}
}
push_seq();
statements.length = n;
if (n != len) CHANGED = true;
}
function to_simple_statement(block, decls) {
if (!(block instanceof AST_BlockStatement)) return block;
var stat = null;
for (var i = 0, len = block.body.length; i < len; i++) {
var line = block.body[i];
if (line instanceof AST_Var && declarations_only(line)) {
decls.push(line);
} else if (stat) {
return false;
} else {
stat = line;
}
}
return stat;
}
function sequencesize_2(statements, compressor) {
function cons_seq(right) {
n--;
CHANGED = true;
var left = prev.body;
return make_sequence(left, [ left, right ]).transform(compressor);
};
var n = 0, prev;
for (var i = 0; i < statements.length; i++) {
var stat = statements[i];
if (prev) {
if (stat instanceof AST_Exit) {
stat.value = cons_seq(stat.value || make_node(AST_Undefined, stat).transform(compressor));
} else if (stat instanceof AST_For) {
if (!(stat.init instanceof AST_Definitions)) {
var abort = false;
prev.body.walk(new TreeWalker(function(node) {
if (abort || node instanceof AST_Scope) return true;
if (node instanceof AST_Binary && node.operator == "in") {
abort = true;
return true;
}
}));
if (!abort) {
if (stat.init) stat.init = cons_seq(stat.init);
else {
stat.init = prev.body;
n--;
CHANGED = true;
}
}
}
} else if (stat instanceof AST_ForIn) {
if (!(stat.init instanceof AST_Const) && !(stat.init instanceof AST_Let)) {
stat.object = cons_seq(stat.object);
}
} else if (stat instanceof AST_If) {
stat.condition = cons_seq(stat.condition);
} else if (stat instanceof AST_Switch) {
stat.expression = cons_seq(stat.expression);
} else if (stat instanceof AST_With) {
stat.expression = cons_seq(stat.expression);
}
}
if (compressor.option("conditionals") && stat instanceof AST_If) {
var decls = [];
var body = to_simple_statement(stat.body, decls);
var alt = to_simple_statement(stat.alternative, decls);
if (body !== false && alt !== false && decls.length > 0) {
var len = decls.length;
decls.push(make_node(AST_If, stat, {
condition: stat.condition,
body: body || make_node(AST_EmptyStatement, stat.body),
alternative: alt
}));
decls.unshift(n, 1);
[].splice.apply(statements, decls);
i += len;
n += len + 1;
prev = null;
CHANGED = true;
continue;
}
}
statements[n++] = stat;
prev = stat instanceof AST_SimpleStatement ? stat : null;
}
statements.length = n;
}
function join_object_assignments(defn, body) {
if (!(defn instanceof AST_Definitions)) return;
var def = defn.definitions[defn.definitions.length - 1];
if (!(def.value instanceof AST_Object)) return;
var exprs;
if (body instanceof AST_Assign) {
exprs = [ body ];
} else if (body instanceof AST_Sequence) {
exprs = body.expressions.slice();
}
if (!exprs) return;
var trimmed = false;
do {
var node = exprs[0];
if (!(node instanceof AST_Assign)) break;
if (node.operator != "=") break;
if (!(node.left instanceof AST_PropAccess)) break;
var sym = node.left.expression;
if (!(sym instanceof AST_SymbolRef)) break;
if (def.name.name != sym.name) break;
if (!node.right.is_constant_expression(scope)) break;
var prop = node.left.property;
if (prop instanceof AST_Node) {
prop = prop.evaluate(compressor);
}
if (prop instanceof AST_Node) break;
prop = "" + prop;
if (compressor.option("ecma") < 6 && compressor.has_directive("use strict")) {
if (!all(def.value.properties, function(node) {
return node.key != prop && node.key.name != prop;
})) break;
}
def.value.properties.push(make_node(AST_ObjectKeyVal, node, {
key: prop,
value: node.right
}));
exprs.shift();
trimmed = true;
} while (exprs.length);
return trimmed && exprs;
}
function join_consecutive_vars(statements) {
var defs;
for (var i = 0, j = -1, len = statements.length; i < len; i++) {
var stat = statements[i];
var prev = statements[j];
if (stat instanceof AST_Definitions) {
if (prev && prev.TYPE == stat.TYPE) {
prev.definitions = prev.definitions.concat(stat.definitions);
CHANGED = true;
} else if (defs && defs.TYPE == stat.TYPE && declarations_only(stat)) {
defs.definitions = defs.definitions.concat(stat.definitions);
CHANGED = true;
} else {
statements[++j] = stat;
defs = stat;
}
} else if (stat instanceof AST_Exit) {
stat.value = extract_object_assignments(stat.value);
} else if (stat instanceof AST_For) {
var exprs = join_object_assignments(prev, stat.init);
if (exprs) {
CHANGED = true;
stat.init = exprs.length ? make_sequence(stat.init, exprs) : null;
statements[++j] = stat;
} else if (prev instanceof AST_Var && (!stat.init || stat.init.TYPE == prev.TYPE)) {
if (stat.init) {
prev.definitions = prev.definitions.concat(stat.init.definitions);
}
stat.init = prev;
statements[j] = stat;
CHANGED = true;
} else if (defs && stat.init && defs.TYPE == stat.init.TYPE && declarations_only(stat.init)) {
defs.definitions = defs.definitions.concat(stat.init.definitions);
stat.init = null;
statements[++j] = stat;
CHANGED = true;
} else {
statements[++j] = stat;
}
} else if (stat instanceof AST_ForIn) {
stat.object = extract_object_assignments(stat.object);
} else if (stat instanceof AST_If) {
stat.condition = extract_object_assignments(stat.condition);
} else if (stat instanceof AST_SimpleStatement) {
var exprs = join_object_assignments(prev, stat.body);
if (exprs) {
CHANGED = true;
if (!exprs.length) continue;
stat.body = make_sequence(stat.body, exprs);
}
statements[++j] = stat;
} else if (stat instanceof AST_Switch) {
stat.expression = extract_object_assignments(stat.expression);
} else if (stat instanceof AST_With) {
stat.expression = extract_object_assignments(stat.expression);
} else {
statements[++j] = stat;
}
}
statements.length = j + 1;
function extract_object_assignments(value) {
statements[++j] = stat;
var exprs = join_object_assignments(prev, value);
if (exprs) {
CHANGED = true;
if (exprs.length) {
return make_sequence(value, exprs);
} else if (value instanceof AST_Sequence) {
return value.tail_node().left;
} else {
return value.left;
}
}
return value;
}
}
}
--- END ---
tack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
--- END ---
}
else {
trim = false;
}
}
}
if ((node instanceof AST_Defun || node instanceof AST_DefClass) && node !== self) {
var def = node.name.definition();
var keep = (def.id in in_use_ids) || !drop_funcs && def.global;
if (!keep) {
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
def.eliminated++;
return 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);
if (def.name instanceof AST_Destructuring) return tail.push(def);
var sym = def.name.definition();
if (drop_block && sym.global) return tail.push(def);
if (!(drop_vars || drop_block) || sym.id in in_use_ids) {
if (def.value && sym.id in fixed_ids && fixed_ids[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)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
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: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[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) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value);
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
);
self.transform(tt);
function scan_ref_scoped(node, descend) {
var node_def, 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.left.fixed_value() === node.right) {
fixed_ids[node_def.id] = node;
}
}
return true;
}
if (node instanceof AST_SymbolRef) {
node_def = node.definition();
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
if (node_def = node_def.redefined()) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
}
return true;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend();
scope = save_scope;
return true;
}
}
}
--- END ---
nstanceof AST_SymbolBlockDeclaration) {
return sym instanceof AST_SymbolLambda;
}
return !(sym instanceof AST_SymbolLet || sym instanceof AST_SymbolConst);
})) {
js_error(
node.name + " redeclared",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
node.scope = scope;
}
if (node instanceof AST_Label) {
node.thedef = node;
node.references = [];
}
if (node instanceof AST_SymbolLambda) {
defun.def_function(node, node.name == "arguments" ? undefined : defun);
}
else if (node instanceof AST_SymbolDefun) {
// Careful here, the scope where this should be defined is
// the parent scope. The reason is that we enter a new
// scope when we encounter the AST_Defun node (which is
// instanceof AST_Scope) but we get to the symbol a bit
// later.
mark_export((node.scope = defun.parent_scope.get_defun_scope()).def_function(node, defun), 1);
}
else if (node instanceof AST_SymbolClass) {
mark_export(defun.def_variable(node, defun), 1);
}
else if (node instanceof AST_SymbolImport) {
scope.def_variable(node);
}
else if (node instanceof AST_SymbolDefClass) {
// This deals with the name of the class being available
// inside the class.
mark_export((node.scope = defun.parent_scope).def_function(node, defun), 1);
}
else if (node instanceof AST_SymbolVar
|| node instanceof AST_SymbolLet
|| node instanceof AST_SymbolConst) {
var def;
if (node instanceof AST_SymbolBlockDeclaration) {
def = scope.def_variable(node, null);
} else {
def = defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined);
}
if (!all(def.orig, function(sym) {
if (sym === node) return true;
if (node instanceof AST_SymbolBlockDeclaration) {
return sym instanceof AST_SymbolLambda;
}
return !(sym instanceof AST_SymbolLet || sym instanceof AST_SymbolConst);
})) {
js_error(
node.name + " redeclared",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
his.uses_eval = props.uses_eval;this.uses_with = props.uses_with;this.functions = props.functions;this.variables = props.variables;}}
--- END ---
ym instanceof AST_SymbolConst);
})) {
js_error(
node.name + " redeclared",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
s_declared(compressor))) {
abort = true;
return node;
}
// Replace variable with assignment when found
if (can_replace
&& !(node instanceof AST_SymbolDeclaration)
&& lhs.equivalent_to(node)) {
if (is_lhs(node, parent)) {
if (value_def) replaced++;
return node;
}
CHANGED = abort = true;
replaced++;
compressor.info("Collapsing {name} [{file}:{line},{col}]", {
name: node.print_to_string(),
file: node.start.file,
line: node.start.line,
col: node.start.col
});
if (candidate instanceof AST_UnaryPostfix) {
return make_node(AST_UnaryPrefix, candidate, candidate);
}
if (candidate instanceof AST_VarDef) {
if (value_def) {
abort = false;
return node;
}
var def = candidate.name.definition();
var value = candidate.value;
if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
def.replaced++;
if (funarg && is_identifier_atom(value)) {
return value.transform(compressor);
} else {
return maintain_this_binding(parent, node, value);
}
}
return make_node(AST_Assign, candidate, {
operator: "=",
left: make_node(AST_SymbolRef, candidate.name, candidate.name),
right: value
});
}
candidate.write_only = false;
return candidate;
}
// These node types have child nodes that execute sequentially,
// but are otherwise not safe to scan into or beyond them.
var sym;
if (node instanceof AST_Call
|| node instanceof AST_Exit
|| node instanceof AST_PropAccess
&& (side_effects || node.expression.may_throw_on_access(compressor))
|| node instanceof AST_SymbolRef
&& (lvalues[node.name]
|| side_effects && !references_in_scope(node.definition()))
|| (sym = lhs_or_def(node))
&& (sym instanceof AST_PropAccess || sym.name in lvalues)
|| may_throw
&& (in_try ? node.has_side_effects(compressor) : side_effects_external(node))
|| (side_effects || !replace_all)
&& (parent instanceof AST_Binary && lazy_op(parent.operator)
|| parent instanceof AST_Conditional
|| parent instanceof AST_If)) {
stop_after = node;
if (node instanceof AST_Scope) abort = true;
}
// Skip (non-executed) functions
if (node instanceof AST_Scope) return node;
}, function(node) {
if (!abort && stop_after === node) abort = true;
});
var multi_replacer = new TreeTransformer(function(node) {
if (abort) return node;
// Skip nodes before `candidate` as quickly as possible
if (!hit) {
if (node !== hit_stack[hit_index]) return node;
hit_index++;
if (hit_index < hit_stack.length) return;
hit = true;
return node;
}
// Replace variable when found
if (node instanceof AST_SymbolRef
&& node.name == def.name) {
if (!--replaced) abort = true;
if (is_lhs(node, multi_replacer.parent())) return node;
def.replaced++;
value_def.replaced--;
return candidate.value;
}
// Skip (non-executed) functions and (leading) default case in switch statements
if (node instanceof AST_Default || node instanceof AST_Scope) return node;
});
while (--stat_index >= 0) {
// Treat parameters as collapsible in IIFE, i.e.
// function(a, b){ ... }(x());
// would be translated into equivalent assignments:
// var a = x(), b = undefined;
if (stat_index == 0 && compressor.option("unused")) extract_args();
// Find collapsible assignments
var hit_stack = [];
extract_candidates(statements[stat_index]);
while (candidates.length > 0) {
hit_stack = candidates.pop();
var hit_index = 0;
var candidate = hit_stack[hit_stack.length - 1];
var value_def = null;
var stop_after = null;
var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue;
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
var replace_all = value_def;
if (!replace_all && lhs instanceof AST_SymbolRef) {
var def = lhs.definition();
if (def.references.length - def.replaced == (candidate instanceof AST_VarDef ? 1 : 2)) {
replace_all = true;
}
}
var side_effects = value_has_side_effects(candidate);
var may_throw = candidate.may_throw(compressor);
var funarg = candidate.name instanceof AST_SymbolFunarg;
var hit = funarg;
var abort = false, replaced = 0, can_replace = !args || !hit;
if (!can_replace) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) {
args[j].transform(scanner);
}
can_replace = true;
}
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(scanner);
}
if (value_def) {
var def = candidate.name.definition();
if (abort && def.references.length - def.replaced > replaced) replaced = false;
else {
abort = false;
hit_index = 0;
hit = funarg;
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(multi_replacer);
}
value_def.single_use = false;
}
}
if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
}
}
function has_overlapping_symbol(fn, arg, fn_strict) {
var found = false, scan_this = !(fn instanceof AST_Arrow);
arg.walk(new TreeWalker(function(node, descend) {
if (found) return true;
--- FUNCTION SOURCE () id{422,-1} start{184150} ---
(parent_scope){
this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
this.parent_scope = parent_scope; // the parent scope
this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes
this.cname = -1; // the current index for mangling functions/variables
}
--- END ---
if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
var s = node.definition().scope;
if (s !== scope) while (s = s.parent_scope) {
if (s === scope) return true;
}
return found = true;
}
if ((fn_strict || scan_this) && node instanceof AST_This) {
return found = true;
}
if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) {
var prev = scan_this;
scan_this = false;
descend();
scan_this = prev;
return true;
}
}));
return found;
}
function extract_args() {
var iife, fn = compressor.self();
if (is_func_expr(fn)
&& !fn.name
&& !fn.uses_arguments
&& !fn.uses_eval
&& (iife = compressor.parent()) instanceof AST_Call
&& iife.expression === fn
&& all(iife.args, function(arg) {
return !(arg instanceof AST_Expansion);
})) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
for (var i = len; --i >= 0;) {
var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue;
names[sym.name] = true;
if (sym instanceof AST_Expansion) {
var elements = iife.args.slice(i);
if (all(elements, function(arg) {
return !has_overlapping_symbol(fn, arg, fn_strict);
})) {
candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym.expression,
value: make_node(AST_Array, iife, {
elements: elements
})
}) ]);
}
} else {
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null;
if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym,
value: arg
}) ]);
}
}
}
}
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (!expr.left.has_side_effects(compressor)) {
candidates.push(hit_stack.slice());
}
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
extract_candidates(expr.right);
} else if (expr instanceof AST_Call) {
extract_candidates(expr.expression);
expr.args.forEach(extract_candidates);
} else if (expr instanceof AST_Case) {
extract_candidates(expr.expression);
--- FUNCTION SOURCE () id{423,-1} start{186632} ---
(symbol, init){
var def = this.variables.get(symbol.name);
if (def) {
def.orig.push(symbol);
if (def.init && (def.scope !== symbol.scope || def.init instanceof AST_Function)) {
def.init = init;
}
} else {
def = new SymbolDef(this, symbol, init);
this.variables.set(symbol.name, def);
def.global = !this.parent_scope;
}
return symbol.thedef = def;
}
--- END ---
} else if (expr instanceof AST_Conditional) {
extract_candidates(expr.condition);
extract_candidates(expr.consequent);
extract_candidates(expr.alternative);
} else if (expr instanceof AST_Definitions
&& (compressor.option("unused") || !(expr instanceof AST_Const))) {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_Exit) {
if (expr.value) extract_candidates(expr.value);
} else if (expr instanceof AST_For) {
if (expr.init) extract_candidates(expr.init);
if (expr.condition) extract_candidates(expr.condition);
if (expr.step) extract_candidates(expr.step);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
function mangleable_var(var_def) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return;
var def = value.definition();
if (def.undeclared) return;
--- FUNCTION SOURCE () id{424,-1} start{186173} ---
(name){
if (name instanceof AST_Symbol) name = name.name;
return this.variables.get(name)
|| (this.parent_scope && this.parent_scope.find_variable(name));
}
--- END ---
effects = [];
node.definitions.forEach(function(def) {
if (def.value) def.value = def.value.transform(tt);
if (def.name instanceof AST_Destructuring) return tail.push(def);
var sym = def.name.definition();
if (drop_block && sym.global) return tail.push(def);
if (!(drop_vars || drop_block) || sym.id in in_use_ids) {
if (def.value && sym.id in fixed_ids && fixed_ids[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)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
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: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[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) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value);
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
dex = 0;
var candidate = hit_stack[hit_stack.length - 1];
var value_def = null;
var stop_after = null;
var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue;
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
var replace_all = value_def;
if (!replace_all && lhs instanceof AST_SymbolRef) {
var def = lhs.definition();
if (def.references.length - def.replaced == (candidate instanceof AST_VarDef ? 1 : 2)) {
replace_all = true;
}
}
var side_effects = value_has_side_effects(candidate);
var may_throw = candidate.may_throw(compressor);
var funarg = candidate.name instanceof AST_SymbolFunarg;
var hit = funarg;
var abort = false, replaced = 0, can_replace = !args || !hit;
if (!can_replace) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) {
args[j].transform(scanner);
}
can_replace = true;
}
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(scanner);
}
if (value_def) {
var def = candidate.name.definition();
if (abort && def.references.length - def.replaced > replaced) replaced = false;
else {
abort = false;
hit_index = 0;
hit = funarg;
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(multi_replacer);
}
value_def.single_use = false;
}
}
if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
}
}
function has_overlapping_symbol(fn, arg, fn_strict) {
var found = false, scan_this = !(fn instanceof AST_Arrow);
arg.walk(new TreeWalker(function(node, descend) {
if (found) return true;
if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
var s = node.definition().scope;
if (s !== scope) while (s = s.parent_scope) {
if (s === scope) return true;
}
return found = true;
}
if ((fn_strict || scan_this) && node instanceof AST_This) {
return found = true;
}
if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) {
var prev = scan_this;
scan_this = false;
descend();
scan_this = prev;
return true;
}
}));
return found;
}
function extract_args() {
var iife, fn = compressor.self();
if (is_func_expr(fn)
&& !fn.name
&& !fn.uses_arguments
&& !fn.uses_eval
&& (iife = compressor.parent()) instanceof AST_Call
&& iife.expression === fn
&& all(iife.args, function(arg) {
--- FUNCTION SOURCE (is_lhs_read_only) id{449,6} start{291820} ---
(lhs) {
if (lhs instanceof AST_This) return true;
if (lhs instanceof AST_SymbolRef) return lhs.definition().orig[0] instanceof AST_SymbolLambda;
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 instanceof AST_RegExp) return false;
if (lhs instanceof AST_Constant) return true;
return is_lhs_read_only(lhs);
}
return false;
}
--- END ---
return !(arg instanceof AST_Expansion);
})) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
for (var i = len; --i >= 0;) {
var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue;
names[sym.name] = true;
if (sym instanceof AST_Expansion) {
var elements = iife.args.slice(i);
if (all(elements, function(arg) {
return !has_overlapping_symbol(fn, arg, fn_strict);
})) {
candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym.expression,
value: make_node(AST_Array, iife, {
elements: elements
})
}) ]);
}
} else {
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null;
if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym,
value: arg
}) ]);
}
}
}
}
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (!expr.left.has_side_effects(compressor)) {
candidates.push(hit_stack.slice());
}
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
extract_candidates(expr.right);
} else if (expr instanceof AST_Call) {
extract_candidates(expr.expression);
expr.args.forEach(extract_candidates);
} else if (expr instanceof AST_Case) {
extract_candidates(expr.expression);
} else if (expr instanceof AST_Conditional) {
extract_candidates(expr.condition);
extract_candidates(expr.consequent);
extract_candidates(expr.alternative);
} else if (expr instanceof AST_Definitions
&& (compressor.option("unused") || !(expr instanceof AST_Const))) {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_Exit) {
if (expr.value) extract_candidates(expr.value);
} else if (expr instanceof AST_For) {
if (expr.init) extract_candidates(expr.init);
if (expr.condition) extract_candidates(expr.condition);
if (expr.step) extract_candidates(expr.step);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
--- FUNCTION SOURCE () id{450,-1} start{495722} ---
(self, compressor){
var def = self.resolve_defines(compressor);
if (def) {
return def.optimize(compressor);
}
// testing against !self.scope.uses_with first is an optimization
if (!compressor.option("ie8")
&& is_undeclared_ref(self)
&& (!self.scope.uses_with || !compressor.find_parent(AST_With))) {
switch (self.name) {
case "undefined":
return make_node(AST_Undefined, self).optimize(compressor);
case "NaN":
return make_node(AST_NaN, self).optimize(compressor);
case "Infinity":
return make_node(AST_Infinity, self).optimize(compressor);
}
}
if (compressor.option("reduce_vars")
&& is_lhs(self, compressor.parent()) !== self) {
var d = self.definition();
var fixed = self.fixed_value();
var single_use = d.single_use;
if (single_use && (fixed instanceof AST_Lambda || fixed instanceof AST_Class)) {
if (d.scope !== self.scope
&& (!compressor.option("reduce_funcs") && fixed instanceof AST_Lambda
|| d.escaped == 1
|| fixed.inlined)) {
single_use = false;
} else if (recursive_ref(compressor, d)) {
single_use = false;
} else if (d.scope !== self.scope || d.orig[0] instanceof AST_SymbolFunarg) {
single_use = fixed.is_constant_expression(self.scope);
if (single_use == "f") {
var scope = self.scope;
do {
if (scope instanceof AST_Defun || is_func_expr(scope)) {
scope.inlined = true;
}
} while (scope = scope.parent_scope);
}
}
}
if (single_use && fixed) {
if (fixed instanceof AST_DefClass) {
fixed = make_node(AST_ClassExpression, fixed, fixed);
}
if (fixed instanceof AST_Defun) {
fixed._squeezed = true;
fixed = make_node(AST_Function, fixed, fixed);
}
var value;
if (d.recursive_refs > 0 && fixed.name instanceof AST_SymbolDefun) {
value = fixed.clone(true);
var defun_def = value.name.definition();
var lambda_def = value.variables.get(value.name.name);
var name = lambda_def && lambda_def.orig[0];
if (!(name instanceof AST_SymbolLambda)) {
name = make_node(AST_SymbolLambda, value.name, value.name);
name.scope = value;
value.name = name;
lambda_def = value.def_function(name);
}
value.walk(new TreeWalker(function(node) {
if (node instanceof AST_SymbolRef && node.definition() === defun_def) {
node.thedef = lambda_def;
lambda_def.references.push(node);
}
}));
} else {
value = fixed.optimize(compressor);
if (value === fixed) value = fixed.clone(true);
}
return value;
}
if (fixed && d.should_replace === undefined) {
var init;
if (fixed instanceof AST_This) {
if (!(d.orig[0] instanceof AST_SymbolFunarg)
&& all(d.references, function(ref) {
return d.scope === ref.scope;
})) {
init = fixed;
}
} else {
var ev = extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
function mangleable_var(var_def) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return;
var def = value.definition();
if (def.undeclared) return;
return value_def = def;
}
function get_lhs(expr) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
return !is_ref_of(lhs, AST_SymbolConst) && lhs;
}
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var tw = new TreeWalker(function(node, descen--- FUNCTION SOURCE (push) id{268,-1} start{50832} ---
(node) {
if (node instanceof AST_Lambda) {
this.directives = Object.create(this.directives);
} else if (node instanceof AST_Directive && !this.directives[node.value]) {
this.directives[node.value] = node;
} else if (node instanceof AST_Class) {
this.directives = Object.create(this.directives);
if (!this.directives["use strict"]) {
this.directives["use strict"] = node;
}
}
this.stack.push(node);
}
--- END ---
fixed.evaluate(compressor);
if (ev !== fixed && (compressor.option("unsafe_regexp") || !(ev instanceof RegExp))) {
init = make_node_from_constant(ev, fixed);
}
}
if (init) {
var value_length = init.optimize(compressor).print_to_string().length;
var fn;
if (has_symbol_ref(fixed)) {
fn = function() {
var result = init.optimize(compressor);
return result === init ? result.clone(true) : result;
};
} else {
value_length = Math.min(value_length, fixed.print_to_string().length);
fn = function() {
var result = best_of_expression(init.optimize(compressor), fixed);
return result === init || result === fixed ? result.clone(true) : result;
};
}
var name_length = d.name.length;
var overhead = 0;
if (compressor.option("unused") && !compressor.exposed(d)) {
overhead = (name_length + 2 + value_length) / d.references.length;
}
d.should_replace = value_length <= name_length + overhead ? fn : false;
} else {
d.should_replace = false;
}
}
if (d.should_replace) {
return d.should_replace();
}
}
return self;
function has_symbol_ref(value) {
var found;
value.walk(new TreeWalker(function(node) {
if (node instanceof AST_SymbolRef) found = true;
if (found) return true;
}));
return found;
}
}
--- END ---
d) {
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
get_rvalue(expr).walk(tw);
return lvalues;
}
function lhs_or_def(node) {
if (node instanceof AST_VarDef) return node.value && node.name;
return is_lhs(node.left, node);
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var iife = compressor.parent(), argnames = compressor.self().argnames;
var index = argnames.indexOf(expr.name);
if (index < 0) {
iife.args.length = Math.min(iife.args.length, argnames.length - 1);
} else {
var args = iife.args;
if (args[index]) args[index] = make_node(AST_Number, args[index], {
value: 0
});
}
return true;
}
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
node.value = null;
return node;
}
return in_list ? MAP.skip : null;
}
}, function(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
}
}));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope.get_defun_scope() !== scope) return false;
return def.references.every(function(ref) {
return ref.scope.get_defun_scope() === scope;
});
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
}
}
--- END ---
of AST_Scope) {
node.init_scope_vars(scope);
var save_scope = scope;
var save_defun = defun;
var save_labels = labels;
defun = scope = node;
labels = new Dictionary();
descend();
scope = save_scope;
defun = save_defun;
labels = save_labels;
return true; // don't descend again in TreeWalker
}
if (node instanceof AST_LabeledStatement) {
var l = node.label;
if (labels.has(l.name)) {
throw new Error(string_template("Label {name} defined twice", l));
}
labels.set(l.name, l);
descend();
labels.del(l.name);
return true; // no descend again
}
if (node instanceof AST_With) {
for (var s = scope; s; s = s.parent_scope)
s.uses_with = true;
return;
}
if (node instanceof AST_Symbol) {
node.scope = scope;
}
if (node instanceof AST_Label) {
node.thedef = node;
node.references = [];
}
if (node instanceof AST_SymbolLambda) {
defun.def_function(node, node.name == "arguments" ? undefined : defun);
}
else if (node instanceof AST_SymbolDefun) {
// Careful here, the scope where this should be defined is
// the parent scope. The reason is that we enter a new
// scope when we encounter the AST_Defun node (which is
// instanceof AST_Scope) but we get to the symbol a bit
// later.
mark_export((node.scope = defun.parent_scope.get_defun_scope()).def_function(node, defun), 1);
}
else if (node instanceof AST_SymbolClass) {
mark_export(defun.def_variable(node, defun), 1);
}
else if (node instanceof AST_SymbolImport) {
scope.def_variable(node);
}
else if (node instanceof AST_SymbolDefClass) {
// This deals with the name of the class being available
// inside the class.
mark_export((node.scope = defun.parent_scope).def_function(node, defun), 1);
}
else if (node instanceof AST_SymbolVar
|| node instanceof AST_SymbolLet
|| node instanceof AST_SymbolConst) {
var def;
if (node instanceof AST_SymbolBlockDeclaration) {
def = scope.def_variable(node, null);
} else {
def = defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined);
}
if (!all(def.orig, function(sym) {
if (sym === node) return true;
if (node instanceof AST_SymbolBlockDeclaration) {
return sym instanceof AST_SymbolLambda;
}
return !(sym instanceof AST_SymbolLet || sym instanceof AST_SymbolConst);
})) {
js_error(
node.name + " redeclared",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
e.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
e.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level);
def.export = node instanceof AST_Export;
}
}
--- END ---
l.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 && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
return !(arg instanceof AST_Expansion);
})) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
for (var i = len; --i >= 0;) {
var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue;
names[sym.name] = true;
if (sym instanceof AST_Expansion) {
var elements = iife.args.slice(i);
if (all(elements, function(arg) {
return !has_overlapping_symbol(fn, arg, fn_strict);
})) {
candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym.expression,
value: make_node(AST_Array, iife, {
elements: elements
})
}) ]);
}
} else {
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null;
if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
name: sym,
value: arg
}) ]);
}
}
}
}
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (!expr.left.has_side_effects(compressor)) {
candidates.push(hit_stack.slice());
}
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
extract_candidates(expr.right);
} else if (expr instanceof AST_Call) {
extract_candidates(expr.expression);
expr.args.forEach(extract_candidates);
} else if (expr instanceof AST_Case) {
extract_candidates(expr.expression);
} else if (expr instanceof AST_Conditional) {
extract_candidates(expr.condition);
extract_candidates(expr.consequent);
extract_candidates(expr.alternative);
} else if (expr instanceof AST_Definitions
&& (compressor.option("unused") || !(expr instanceof AST_Const))) {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_Exit) {
if (expr.value) extract_candidates(expr.value);
} else if (expr instanceof AST_For) {
if (expr.init) extract_candidates(expr.init);
if (expr.condition) extract_candidates(expr.condition);
if (expr.step) extract_candidates(expr.step);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
candidates.push(hit_stack.slice());
extract_candidates(expr.value);
}
}
hit_stack.pop();
}
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
function mangleable_var(var_def) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return;
var def = value.definition();
if (def.undeclared) return;
return value_def = def;
}
function get_lhs(expr) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
return !is_ref_of(lhs, AST_SymbolConst) && lhs;
}
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var tw = new TreeWalker(function(node, descend) {
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
get_rvalue(expr).walk(tw);
return lvalues;
}
function lhs_or_def(node) {
if (node instanceof AST_VarDef) return node.value && node.name;
return is_lhs(node.left, node);
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var iife = compressor.parent(), argnames = compressor.self().argnames;
var index = argnames.indexOf(expr.name);
if (index < 0) {
iife.args.length = Math.min(iife.args.length, argnames.length - 1);
} else {
var args = iife.args;
if (args[index]) args[index] = make_node(AST_Number, args[index], {
value: 0
});
}
return true;
}
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
node.value = null;
return node;
}
return in_list ? MAP.skip : null;
}
}, function(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
}
}));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope.get_defun_scope() !== scope) return false;
return def.references.every(function(ref) {
return ref.scope.get_defun_scope() === scope;
});
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
}
}
--- END ---
preamble;
if (preamble) {
print(preamble.replace(/\x5cr\x5cn?|[\x5cn\x5cu2028\x5cu2029]|\x5cs*$/g, "\x5cn"));
}
}
comments = comments.filter(comment_filter, node);
if (comments.length == 0) return;
var last_nlb = has_nlb();
comments.forEach(function(c, i) {
if (!last_nlb) {
if (c.nlb) {
print("\x5cn");
indent();
last_nlb = true;
} else if (i > 0) {
space();
}
}
if (/comment[134]/.test(c.type)) {
print("//" + c.value.replace(/[@#]__PURE__/g, ' ') + "\x5cn");
indent();
last_nlb = true;
} else if (c.type == "comment2") {
print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/");
last_nlb = false;
}
});
if (!last_nlb) {
if (start.nlb) {
print("\x5cn");
indent();
} else {
space();
}
}
}
--- END ---
this.right._eval(compressor, depth);
if (right === this.right) return this;
var result;
switch (this.operator) {
case "&&" : result = left && right; break;
case "||" : result = left || right; break;
case "|" : result = left | right; break;
case "&" : result = left & right; break;
case "^" : result = left ^ right; break;
case "+" : result = left + right; break;
case "*" : result = left * right; break;
case "**" : result = Math.pow(left, right); break;
case "/" : result = left / right; break;
case "%" : result = left % right; break;
case "-" : result = left - right; break;
case "<<" : result = left << right; break;
case ">>" : result = left >> right; break;
case ">>>" : result = left >>> right; break;
case "==" : result = left == right; break;
case "===" : result = left === right; break;
case "!=" : result = left != right; break;
case "!==" : result = left !== right; break;
case "<" : result = left < right; break;
case "<=" : result = left <= right; break;
case ">" : result = left > right; break;
case ">=" : result = left >= right; break;
default:
return this;
}
if (isNaN(result) && compressor.find_parent(AST_With)) {
// leave original expression as is
return this;
}
return result;
}
--- END ---
lue);
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) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value);
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
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.--- FUNCTION SOURCE (add) id{451,-1} start{8623} ---
(key, val) {
if (this.has(key)) {
this.get(key).push(val);
} else {
this.set(key, [ val ]);
}
return this;
}
--- END ---
body) : block;
}
return node;
}
if (node instanceof AST_BlockStatement) {
descend(node, this);
if (in_list && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
}
}
--- END ---
value: 0
});
}
}
if (scope !== self) return;
if (node.name
&& (!compressor.option("keep_classnames") && node instanceof AST_ClassExpression
|| !compressor.option("keep_fnames") && node instanceof AST_Function)) {
var def = node.name.definition();
// any declarations with same name will overshadow
// name of this anonymous function and can therefore
// never be used anywhere
if (!(def.id in in_use_ids) || def.orig.length > 1)
node.name = null;
}
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
var trim = !compressor.option("keep_fargs");
for (var a = node.argnames, i = a.length; --i >= 0;) {
var sym = a[i];
if (sym instanceof AST_Expansion) {
sym = sym.expression;
}
if (sym instanceof AST_DefaultAssign) {
sym = sym.left;
}
// Do not drop destructuring arguments.
// They constitute a type assertion, so dropping
// them would stop that TypeError which would happen
// if someone called it with an incorrectly formatted
// parameter.
if (!(sym instanceof AST_Destructuring) && !(sym.definition().id in in_use_ids)) {
sym.__unused = true;
if (trim) {
a.pop();
compressor[sym.unreferenced() ? "warn" : "info"]("Dropping unused function argument {name} [{file}:{line},{col}]", template(sym));
}
}
else {
trim = false;
}
}
}
if ((node instanceof AST_Defun || node instanceof AST_DefClass) && node !== self) {
var def = node.name.definition();
var keep = (def.id in in_use_ids) || !drop_funcs && def.global;
if (!keep) {
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
def.eliminated++;
return 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);
if (def.name instanceof AST_Destructuring) return tail.push(def);
var sym = def.name.definition();
if (drop_block && sym.global) return tail.push(def);
if (!(drop_vars || drop_block) || sym.id in in_use_ids) {
if (def.value && sym.id in fixed_ids && fixed_ids[sym.id] !== def) {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment