});
}
}
- if (this.options["inline"] === true) this.options["inline"] = 3;
+ if (this.options["inline"] === true) this.options["inline"] = 4;
this.drop_fargs = this.options["keep_fargs"] ? return_false : function(lambda, parent) {
if (lambda.length_read) return false;
var name = lambda.name;
function make_node_from_constant(val, orig) {
switch (typeof val) {
case "string":
- return make_node(AST_String, orig, {
- value: val
- });
+ return make_node(AST_String, orig, { value: val });
case "number":
if (isNaN(val)) return make_node(AST_NaN, orig);
if (isFinite(val)) {
}
return val < 0 ? make_node(AST_UnaryPrefix, orig, {
operator: "-",
- expression: make_node(AST_Infinity, orig)
+ expression: make_node(AST_Infinity, orig),
}) : make_node(AST_Infinity, orig);
case "boolean":
return make_node(val ? AST_True : AST_False, orig);
return make_node(AST_Undefined, orig);
default:
if (val === null) {
- return make_node(AST_Null, orig, { value: null });
+ return make_node(AST_Null, orig);
}
if (val instanceof RegExp) {
return make_node(AST_RegExp, orig, { value: val });
}
function tighten_body(statements, compressor) {
+ var in_lambda = last_of(compressor, function(node) {
+ return node instanceof AST_Lambda;
+ });
var in_loop, in_try, scope;
find_loop_scope_try();
var changed, last_changed, max_iter = 10;
if (handle_if_return(statements, compressor)) changed = 3;
if (!changed && last_changed == 3) break;
}
- if (compressor.sequences_limit > 0) {
- if (sequencesize(statements, compressor)) changed = 4;
+ if (compressor.option("inline") >= 4) {
+ if (inline_last_iife(statements, compressor)) changed = 4;
if (!changed && last_changed == 4) break;
- if (sequencesize_2(statements, compressor)) changed = 5;
+ }
+ if (compressor.sequences_limit > 0) {
+ if (sequencesize(statements, compressor)) changed = 5;
if (!changed && last_changed == 5) break;
+ if (sequencesize_2(statements, compressor)) changed = 6;
+ if (!changed && last_changed == 6) break;
}
if (compressor.option("join_vars")) {
- if (join_consecutive_vars(statements)) changed = 6;
- if (!changed && last_changed == 6) break;
+ if (join_consecutive_vars(statements)) changed = 7;
+ if (!changed && last_changed == 7) break;
}
if (compressor.option("collapse_vars")) {
- if (collapse(statements, compressor)) changed = 7;
+ if (collapse(statements, compressor)) changed = 8;
}
} while (changed && max_iter-- > 0);
return statements;
+ function last_of(compressor, predicate) {
+ var block = compressor.self(), stat, level = 0;
+ do {
+ do {
+ if (predicate(block)) return true;
+ block = compressor.parent(level++);
+ } while (block instanceof AST_If && (stat = block));
+ } while ((block instanceof AST_BlockStatement || block instanceof AST_Scope)
+ && is_last_statement(block.body, stat));
+ }
+
function find_loop_scope_try() {
var node = compressor.self(), level = 0;
do {
function handle_if_return(statements, compressor) {
var changed = false;
- var self = compressor.self();
var parent = compressor.parent();
- var in_lambda = last_of(function(node) {
- return node instanceof AST_Lambda;
- });
var in_iife = in_lambda && parent && parent.TYPE == "Call";
var multiple_if_returns = has_multiple_if_returns(statements);
for (var i = statements.length; --i >= 0;) {
body = stat.value.clone();
body.expressions[body.length - 1] = tail.expression;
}
- statements[i] = make_node(AST_SimpleStatement, stat, {
- body: body,
- });
+ statements[i] = make_node(AST_SimpleStatement, stat, { body: body });
continue;
}
}
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
+ body: as_statement_array(stat.alternative).concat(extract_functions()),
});
+ stat.alternative = make_node(AST_BlockStatement, stat, { body: body });
statements[i] = stat;
statements[i] = stat.transform(compressor);
continue;
changed = true;
stat = stat.clone();
stat.body = make_node(AST_BlockStatement, stat.body, {
- body: as_statement_array(stat.body).concat(extract_functions())
+ body: as_statement_array(stat.body).concat(extract_functions()),
});
var body = as_statement_array_with_return(stat.alternative, alt);
- stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
- body: body
- });
+ stat.alternative = make_node(AST_BlockStatement, stat.alternative, { body: body });
statements[i] = stat;
statements[i] = stat.transform(compressor);
continue;
if (compressor.option("typeofs")) {
if (ab && !alt) {
- mark_locally_defined(stat.condition, null, make_node(AST_BlockStatement, self, {
- body: statements.slice(i + 1)
- }));
+ var stats = make_node(AST_BlockStatement, compressor.self(), {
+ body: statements.slice(i + 1),
+ });
+ mark_locally_defined(stat.condition, null, stats);
}
if (!ab && alt) {
- mark_locally_defined(stat.condition, make_node(AST_BlockStatement, self, {
- body: statements.slice(i + 1)
- }));
+ var stats = make_node(AST_BlockStatement, compressor.self(), {
+ body: statements.slice(i + 1),
+ });
+ mark_locally_defined(stat.condition, stats);
}
}
}
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
- });
+ statements[i] = make_node(AST_SimpleStatement, stat.condition, { body: stat.condition });
continue;
}
//---
if (!stat.alternative && !next && in_lambda && (in_bool || value && multiple_if_returns)) {
changed = true;
stat = stat.clone();
- stat.alternative = make_node(AST_Return, stat, {
- value: null
- });
+ stat.alternative = make_node(AST_Return, stat, { value: null });
statements.splice(i, 1, stat.transform(compressor));
continue;
}
stat.alternative = make_node(AST_BlockStatement, next, {
body: [
next,
- make_node(AST_Return, next, {
- value: null
- })
- ]
+ make_node(AST_Return, next, { value: null }),
+ ],
});
statements.splice(i, 1, stat.transform(compressor));
statements.splice(j, 1);
return !value || value instanceof AST_UnaryPrefix && value.operator == "void";
}
- function last_of(predicate) {
- var block = self, stat, level = 0;
- do {
- do {
- if (predicate(block)) return true;
- block = compressor.parent(level++);
- } while (block instanceof AST_If && (stat = block));
- } while ((block instanceof AST_BlockStatement || block instanceof AST_Scope)
- && is_last_statement(block.body, stat));
- }
-
function match_target(target) {
- return last_of(function(node) {
+ return last_of(compressor, function(node) {
return node === target;
});
}
return statements.length != len;
}
+ function inline_last_iife(statements, compressor) {
+ if (!in_lambda) return false;
+ var index = statements.length - 1;
+ var stat = statements[index];
+ if (!(stat instanceof AST_SimpleStatement)) return false;
+ var body = stat.body;
+ if (body instanceof AST_UnaryPrefix) {
+ if (unary_side_effects[body.operator]) return false;
+ body = body.expression;
+ }
+ var inlined = make_node(AST_UnaryPrefix, stat, {
+ operator: "void",
+ expression: body,
+ }).try_inline(compressor, scope);
+ if (!inlined) return false;
+ statements[index] = inlined;
+ return true;
+ }
+
function sequencesize(statements, compressor) {
if (statements.length < 2) return;
var seq = [], n = 0;
return avoid.length && makePredicate(avoid);
}
+ function safe_from_await_yield(fn, avoid) {
+ if (!avoid) return true;
+ var safe = true;
+ var tw = new TreeWalker(function(node) {
+ if (!safe) return true;
+ if (node instanceof AST_Scope) {
+ if (node === fn) return;
+ if (is_arrow(node)) {
+ for (var i = 0; safe && i < node.argnames.length; i++) node.argnames[i].walk(tw);
+ } else if (node instanceof AST_LambdaDefinition && avoid[node.name.name]) {
+ safe = false;
+ }
+ return true;
+ }
+ if (node instanceof AST_Symbol && avoid[node.name] && node !== fn.name) safe = false;
+ });
+ fn.walk(tw);
+ return safe;
+ }
+
OPT(AST_Call, function(self, compressor) {
var exp = self.expression;
var terminated = trim_optional_chain(self, compressor);
if (exp === fn
&& !fn.name
&& (!value || value.is_constant_expression())
- && safe_from_await_yield(fn, compressor.find_parent(AST_Scope))) {
+ && safe_from_await_yield(fn, avoid_await_yield(compressor.find_parent(AST_Scope)))) {
return make_sequence(self, convert_args(value)).optimize(compressor);
}
}
&& !(fn.name && fn instanceof AST_LambdaExpression)
&& (exp === fn || !recursive_ref(compressor, def = exp.definition(), fn)
&& fn.is_constant_expression(find_scope(compressor)))
- && !has_spread
&& (value = can_flatten_body(stat))
&& !fn.contains_this()) {
var replacing = exp === fn || def.single_use && def.references.length - def.replaced == 1;
&& all(fn.body, is_empty)
&& (fn === exp ? fn_name_unused(fn, compressor) : !has_default && !has_destructured && !fn.rest)
&& !(is_arrow(fn) && fn.value)
- && safe_from_await_yield(fn, compressor.find_parent(AST_Scope))) {
+ && safe_from_await_yield(fn, avoid_await_yield(compressor.find_parent(AST_Scope)))) {
return make_sequence(self, convert_args()).optimize(compressor);
}
}
return args;
}
- function safe_from_await_yield(node, scope) {
- var avoid = avoid_await_yield(scope);
- if (!avoid) return true;
- var safe = true;
- var tw = new TreeWalker(function(node) {
- if (!safe) return true;
- if (node instanceof AST_Scope) {
- if (node === fn) return;
- if (is_arrow(node)) {
- for (var i = 0; safe && i < node.argnames.length; i++) node.argnames[i].walk(tw);
- } else if (node instanceof AST_LambdaDefinition && avoid[node.name.name]) {
- safe = false;
- }
- return true;
- }
- if (node instanceof AST_Symbol && avoid[node.name] && node !== fn.name) safe = false;
- });
- node.walk(tw);
- return safe;
- }
-
function noop_value() {
return self.call_only ? make_node(AST_Number, self, { value: 0 }) : make_node(AST_Undefined, self);
}
}
function can_substitute_directly() {
- if (has_default || has_destructured || var_assigned || fn.rest) return;
+ if (has_default || has_destructured || has_spread || var_assigned || fn.rest) return;
if (compressor.option("inline") < 2 && fn.argnames.length) return;
if (!fn.variables.all(function(def) {
return def.references.length - def.replaced < 2 && def.orig[0] instanceof AST_SymbolFunarg;
} while (!(scope instanceof AST_Scope));
insert = scope.body.indexOf(child) + 1;
if (!insert) return false;
- if (!safe_from_await_yield(fn, scope)) return false;
+ if (!safe_from_await_yield(fn, avoid_await_yield(scope))) return false;
var safe_to_inject = exp !== fn || fn.parent_scope.resolve() === scope;
if (scope instanceof AST_Toplevel) {
if (compressor.toplevel.vars) {
function flatten_fn() {
var decls = [];
var expressions = [];
- if (has_default > 1 || has_destructured || fn.rest) {
+ if (has_default > 1 || has_destructured || has_spread || fn.rest) {
flatten_destructured(decls, expressions);
} else {
flatten_args(decls, expressions);
}
});
+ (function(def) {
+ def(AST_Node, noop);
+ function process(sym, argname) {
+ argname.definition().orig.push(sym);
+ }
+ def(AST_Call, function(compressor, scope) {
+ if (compressor.option("inline") < 4) return;
+ var call = this;
+ if (call.is_expr_pure(compressor)) return;
+ var fn = call.expression;
+ if (!(fn instanceof AST_Function)) return;
+ if (fn.name) return;
+ if (fn.uses_arguments) return;
+ if (fn.contains_this()) return;
+ if (!scope) scope = compressor.find_parent(AST_Scope);
+ var names = scope.var_names();
+ if (!fn.variables.all(function(def, name) {
+ if (!names.has(name)) return true;
+ if (name != "arguments") return false;
+ if (scope.uses_arguments) return false;
+ return def.references.length == def.replaced;
+ })) return;
+ var safe = true;
+ fn.each_argname(function(argname) {
+ if (!all(argname.definition().orig, function(sym) {
+ return !(sym instanceof AST_SymbolDefun);
+ })) safe = false;
+ });
+ if (!safe) return;
+ if (!safe_from_await_yield(fn, avoid_await_yield(scope))) return;
+ fn.functions.each(function(def, name) {
+ scope.functions.set(name, def);
+ });
+ fn.variables.each(function(def, name) {
+ scope.variables.set(name, def);
+ def.single_use = false;
+ });
+ if (fn.variables.has("NaN")) scope.transform(new TreeTransformer(function(node) {
+ if (node instanceof AST_NaN) return make_node(AST_Binary, node, {
+ operator: "/",
+ left: make_node(AST_Number, node, { value: 0 }),
+ right: make_node(AST_Number, node, { value: 0 }),
+ });
+ if (node instanceof AST_Scope && node !== scope) return node;
+ }));
+ var body = [];
+ if (fn.rest || !all(fn.argnames, function(argname) {
+ return argname instanceof AST_SymbolFunarg;
+ }) || !all(call.args, function(arg) {
+ return !(arg instanceof AST_Spread);
+ })) {
+ body.push(make_node(AST_Var, call, {
+ definitions: [ make_node(AST_VarDef, call, {
+ name: make_node(AST_DestructuredArray, call, {
+ elements: fn.argnames.map(function(argname) {
+ if (argname.unused) return make_node(AST_Hole, argname);
+ return argname.convert_symbol(AST_SymbolVar, process);
+ }),
+ rest: fn.rest && fn.rest.convert_symbol(AST_SymbolVar, process),
+ }),
+ value: make_node(AST_Array, call, { elements: call.args.slice() }),
+ }) ],
+ }));
+ } else {
+ var values = call.args.slice();
+ fn.argnames.forEach(function(argname) {
+ var value = values.shift();
+ if (argname.unused) {
+ if (value) body.push(make_node(AST_SimpleStatement, call, { body: value }));
+ return;
+ }
+ body.push(make_node(AST_Var, call, {
+ definitions: [ make_node(AST_VarDef, call, {
+ name: argname.convert_symbol(AST_SymbolVar, process),
+ value: value || make_node(AST_Undefined, call).optimize(compressor),
+ }) ],
+ }));
+ });
+ if (values.length) body.push(make_node(AST_SimpleStatement, call, {
+ body: make_sequence(call, values),
+ }));
+ }
+ return make_node(AST_BlockStatement, call, {
+ body: body.concat(fn.body, make_node(AST_Return, call, { value: null })),
+ });
+ });
+ def(AST_New, noop);
+ def(AST_Sequence, function(compressor, scope) {
+ var inlined = this.tail_node().try_inline(compressor, scope);
+ if (inlined) return make_node(AST_BlockStatement, this, {
+ body: [
+ make_node(AST_SimpleStatement, this, {
+ body: make_sequence(this, this.expressions.slice(0, -1)),
+ }),
+ inlined,
+ ],
+ });
+ });
+ def(AST_UnaryPrefix, function(compressor, scope) {
+ var self = this;
+ var op = self.operator;
+ if (unary_side_effects[op]) return;
+ var inlined = self.expression.try_inline(compressor, scope);
+ if (!inlined) return;
+ scan_local_returns(inlined, function(node) {
+ var value = node.value;
+ if (op == "void") {
+ if (!value) return;
+ if (is_undefined(value)) return;
+ }
+ node.value = make_node(AST_UnaryPrefix, self, {
+ operator: op,
+ expression: value || make_node(AST_Undefined, node),
+ }).optimize(compressor);
+ });
+ return inlined;
+ })
+ })(function(node, func) {
+ node.DEFMETHOD("try_inline", func);
+ });
+
OPT(AST_Return, function(self, compressor) {
+ var value = self.value;
+ if (!value) return self;
+ var inlined = value.try_inline(compressor);
+ if (inlined) return inlined;
if (compressor.option("side_effects")
- && self.value
- && is_undefined(self.value, compressor)
+ && is_undefined(value, compressor)
&& !in_async_generator(compressor.find_parent(AST_Scope))) {
self.value = null;
}