enhance booleans (#5365)
This commit is contained in:
331
lib/compress.js
331
lib/compress.js
@@ -189,7 +189,7 @@ Compressor.prototype = new TreeTransformer(function(node, descend, in_list) {
|
||||
if (is_scope) {
|
||||
node.hoist_properties(this);
|
||||
node.hoist_declarations(this);
|
||||
node.process_boolean_returns(this);
|
||||
node.process_returns(this);
|
||||
}
|
||||
// Before https://github.com/mishoo/UglifyJS/pull/1602 AST_Node.optimize()
|
||||
// would call AST_Node.transform() if a different instance of AST_Node is
|
||||
@@ -475,7 +475,8 @@ Compressor.prototype.compress = function(node) {
|
||||
|
||||
function reset_def(tw, compressor, def) {
|
||||
def.assignments = 0;
|
||||
def.bool_fn = 0;
|
||||
def.bool_return = 0;
|
||||
def.drop_return = 0;
|
||||
def.cross_loop = false;
|
||||
def.direct_access = false;
|
||||
def.escaped = [];
|
||||
@@ -1057,12 +1058,11 @@ Compressor.prototype.compress = function(node) {
|
||||
if (iife) delete exp.reduce_vars;
|
||||
return true;
|
||||
}
|
||||
if (node.TYPE == "Call" && tw.in_boolean_context()) {
|
||||
if (exp instanceof AST_SymbolRef) {
|
||||
exp.definition().bool_fn++;
|
||||
} else if (exp instanceof AST_Assign && exp.operator == "=" && exp.left instanceof AST_SymbolRef) {
|
||||
exp.left.definition().bool_fn++;
|
||||
}
|
||||
if (node.TYPE == "Call") switch (tw.in_boolean_context()) {
|
||||
case "d":
|
||||
var drop = true;
|
||||
case true:
|
||||
mark_refs(exp, drop);
|
||||
}
|
||||
exp.walk(tw);
|
||||
var optional = node.optional;
|
||||
@@ -1078,6 +1078,25 @@ Compressor.prototype.compress = function(node) {
|
||||
tw.find_parent(AST_Scope).may_call_this();
|
||||
}
|
||||
return true;
|
||||
|
||||
function mark_refs(node, drop) {
|
||||
if (node instanceof AST_Assign) {
|
||||
if (node.operator != "=") return;
|
||||
mark_refs(node.left, drop);
|
||||
mark_refs(node.right, drop);
|
||||
} else if (node instanceof AST_Binary) {
|
||||
if (!lazy_op[node.operator]) return;
|
||||
mark_refs(node.left, drop);
|
||||
mark_refs(node.right, drop);
|
||||
} else if (node instanceof AST_Conditional) {
|
||||
mark_refs(node.consequent, drop);
|
||||
mark_refs(node.alternative, drop);
|
||||
} else if (node instanceof AST_SymbolRef) {
|
||||
var def = node.definition();
|
||||
def.bool_return++;
|
||||
if (drop) def.drop_return++;
|
||||
}
|
||||
}
|
||||
});
|
||||
def(AST_Class, function(tw, descend, compressor) {
|
||||
var node = this;
|
||||
@@ -7923,7 +7942,7 @@ Compressor.prototype.compress = function(node) {
|
||||
}));
|
||||
}
|
||||
|
||||
function map_bool_returns(fn) {
|
||||
function map_self_returns(fn) {
|
||||
var map = Object.create(null);
|
||||
scan_local_returns(fn, function(node) {
|
||||
var value = node.value;
|
||||
@@ -7936,9 +7955,14 @@ Compressor.prototype.compress = function(node) {
|
||||
return map;
|
||||
}
|
||||
|
||||
function all_bool(def, bool_returns, compressor) {
|
||||
return def.bool_fn + (bool_returns[def.id] || 0) === def.references.length - def.replaced
|
||||
&& !compressor.exposed(def);
|
||||
function can_trim_returns(def, self_returns, compressor) {
|
||||
if (compressor.exposed(def)) return false;
|
||||
switch (def.references.length - def.replaced - (self_returns[def.id] || 0)) {
|
||||
case def.drop_return:
|
||||
return "d";
|
||||
case def.bool_return:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function process_boolean_returns(fn, compressor) {
|
||||
@@ -7964,39 +7988,74 @@ Compressor.prototype.compress = function(node) {
|
||||
});
|
||||
}
|
||||
|
||||
AST_Scope.DEFMETHOD("process_boolean_returns", noop);
|
||||
AST_Defun.DEFMETHOD("process_boolean_returns", function(compressor) {
|
||||
AST_Scope.DEFMETHOD("process_returns", noop);
|
||||
AST_Defun.DEFMETHOD("process_returns", function(compressor) {
|
||||
if (!compressor.option("booleans")) return;
|
||||
var bool_returns = map_bool_returns(this);
|
||||
if (!all_bool(this.name.definition(), bool_returns, compressor)) return;
|
||||
if (compressor.parent() instanceof AST_ExportDefault) return;
|
||||
process_boolean_returns(this, compressor);
|
||||
switch (can_trim_returns(this.name.definition(), map_self_returns(this), compressor)) {
|
||||
case "d":
|
||||
drop_returns(compressor, this, true);
|
||||
break;
|
||||
case true:
|
||||
process_boolean_returns(this, compressor);
|
||||
break;
|
||||
}
|
||||
});
|
||||
AST_Function.DEFMETHOD("process_boolean_returns", function(compressor) {
|
||||
AST_Function.DEFMETHOD("process_returns", function(compressor) {
|
||||
if (!compressor.option("booleans")) return;
|
||||
var bool_returns = map_bool_returns(this);
|
||||
if (this.name && !all_bool(this.name.definition(), bool_returns, compressor)) return;
|
||||
var drop = true;
|
||||
var self_returns = map_self_returns(this);
|
||||
if (this.name && !can_trim(this.name.definition())) return;
|
||||
var parent = compressor.parent();
|
||||
if (parent instanceof AST_Assign) {
|
||||
if (parent.operator != "=") return;
|
||||
var sym = parent.left;
|
||||
if (!(sym instanceof AST_SymbolRef)) return;
|
||||
if (!all_bool(sym.definition(), bool_returns, compressor)) return;
|
||||
if (!can_trim(sym.definition())) return;
|
||||
} else if (parent instanceof AST_Call && parent.expression !== this) {
|
||||
var exp = parent.expression;
|
||||
if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
|
||||
if (!(exp instanceof AST_Lambda)) return;
|
||||
if (exp.uses_arguments || exp.pinned()) return;
|
||||
var sym = exp.argnames[parent.args.indexOf(this)];
|
||||
var args = parent.args, sym;
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
var arg = args[i];
|
||||
if (arg === this) {
|
||||
sym = exp.argnames[i];
|
||||
if (!sym && exp.rest) return;
|
||||
break;
|
||||
}
|
||||
if (arg instanceof AST_Spread) return;
|
||||
}
|
||||
if (sym instanceof AST_DefaultValue) sym = sym.name;
|
||||
if (sym instanceof AST_SymbolFunarg && !all_bool(sym.definition(), bool_returns, compressor)) return;
|
||||
if (sym instanceof AST_SymbolFunarg && !can_trim(sym.definition())) return;
|
||||
} else if (parent.TYPE == "Call") {
|
||||
compressor.pop();
|
||||
var in_bool = compressor.in_boolean_context();
|
||||
compressor.push(this);
|
||||
if (!in_bool) return;
|
||||
switch (in_bool) {
|
||||
case true:
|
||||
drop = false;
|
||||
case "d":
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
} else return;
|
||||
process_boolean_returns(this, compressor);
|
||||
if (drop) {
|
||||
drop_returns(compressor, this, true);
|
||||
} else {
|
||||
process_boolean_returns(this, compressor);
|
||||
}
|
||||
|
||||
function can_trim(def) {
|
||||
switch (can_trim_returns(def, self_returns, compressor)) {
|
||||
case true:
|
||||
drop = false;
|
||||
case "d":
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
AST_BlockScope.DEFMETHOD("var_names", function() {
|
||||
@@ -8155,6 +8214,118 @@ Compressor.prototype.compress = function(node) {
|
||||
});
|
||||
}
|
||||
|
||||
function drop_returns(compressor, exp, ignore_name) {
|
||||
if (!(exp instanceof AST_Lambda)) return;
|
||||
var arrow = is_arrow(exp);
|
||||
var async = is_async(exp);
|
||||
var changed = false;
|
||||
var drop_body = false;
|
||||
if (arrow && compressor.option("arrows")) {
|
||||
if (!exp.value) {
|
||||
drop_body = true;
|
||||
} else if (!async || is_primitive(compressor, exp.value)) {
|
||||
var dropped = exp.value.drop_side_effect_free(compressor);
|
||||
if (dropped !== exp.value) {
|
||||
changed = true;
|
||||
exp.value = dropped;
|
||||
}
|
||||
}
|
||||
} else if (!is_generator(exp)) {
|
||||
if (!ignore_name && exp.name) {
|
||||
var def = exp.name.definition();
|
||||
drop_body = def.references.length == def.replaced;
|
||||
} else {
|
||||
drop_body = true;
|
||||
}
|
||||
}
|
||||
if (drop_body) {
|
||||
exp.process_expression(false, function(node) {
|
||||
var value = node.value;
|
||||
if (value) {
|
||||
if (async && !is_primitive(compressor, value)) return node;
|
||||
value = value.drop_side_effect_free(compressor, true);
|
||||
}
|
||||
changed = true;
|
||||
if (!value) return make_node(AST_EmptyStatement, node);
|
||||
return make_node(AST_SimpleStatement, node, { body: value });
|
||||
});
|
||||
scan_local_returns(exp, function(node) {
|
||||
var value = node.value;
|
||||
if (value) {
|
||||
if (async && !is_primitive(compressor, value)) return;
|
||||
var dropped = value.drop_side_effect_free(compressor);
|
||||
if (dropped !== value) {
|
||||
changed = true;
|
||||
node.value = dropped;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (async && compressor.option("awaits")) {
|
||||
if (drop_body) exp.process_expression("awaits", function(node) {
|
||||
var body = node.body;
|
||||
if (body instanceof AST_Await) {
|
||||
if (is_primitive(compressor, body.expression)) {
|
||||
changed = true;
|
||||
body = body.expression.drop_side_effect_free(compressor, true);
|
||||
if (!body) return make_node(AST_EmptyStatement, node);
|
||||
node.body = body;
|
||||
}
|
||||
} else if (body instanceof AST_Sequence) {
|
||||
var exprs = body.expressions;
|
||||
for (var i = exprs.length; --i >= 0;) {
|
||||
var tail = exprs[i];
|
||||
if (!(tail instanceof AST_Await)) break;
|
||||
var value = tail.expression;
|
||||
if (!is_primitive(compressor, value)) break;
|
||||
changed = true;
|
||||
if (exprs[i] = value.drop_side_effect_free(compressor)) break;
|
||||
}
|
||||
switch (i) {
|
||||
case -1:
|
||||
return make_node(AST_EmptyStatement, node);
|
||||
case 0:
|
||||
node.body = exprs[0];
|
||||
break;
|
||||
default:
|
||||
exprs.length = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return node;
|
||||
});
|
||||
var abort = !drop_body && exp.name || arrow && exp.value && !is_primitive(compressor, exp.value);
|
||||
var tw = new TreeWalker(function(node) {
|
||||
if (abort) return true;
|
||||
if (tw.parent() === exp && node.may_throw(compressor)) return abort = true;
|
||||
if (node instanceof AST_Await) return abort = true;
|
||||
if (node instanceof AST_ForAwaitOf) return abort = true;
|
||||
if (node instanceof AST_Return) {
|
||||
if (node.value && !is_primitive(compressor, node.value)) return abort = true;
|
||||
return;
|
||||
}
|
||||
if (node instanceof AST_Scope && node !== exp) return true;
|
||||
});
|
||||
exp.walk(tw);
|
||||
if (!abort) {
|
||||
var ctor;
|
||||
switch (exp.CTOR) {
|
||||
case AST_AsyncArrow:
|
||||
ctor = AST_Arrow;
|
||||
break;
|
||||
case AST_AsyncFunction:
|
||||
ctor = AST_Function;
|
||||
break;
|
||||
case AST_AsyncGeneratorFunction:
|
||||
ctor = AST_GeneratorFunction;
|
||||
break;
|
||||
}
|
||||
return make_node(ctor, exp, exp);
|
||||
}
|
||||
}
|
||||
return changed && exp.clone();
|
||||
}
|
||||
|
||||
// drop_side_effect_free()
|
||||
// remove side-effect-free parts which only affects return value
|
||||
(function(def) {
|
||||
@@ -8261,116 +8432,6 @@ Compressor.prototype.compress = function(node) {
|
||||
if (!rhs) return lhs;
|
||||
return make_sequence(this, [ lhs, rhs ]);
|
||||
});
|
||||
function drop_returns(compressor, exp) {
|
||||
var arrow = is_arrow(exp);
|
||||
var async = is_async(exp);
|
||||
var changed = false;
|
||||
var drop_body = false;
|
||||
if (arrow && compressor.option("arrows")) {
|
||||
if (!exp.value) {
|
||||
drop_body = true;
|
||||
} else if (!async || is_primitive(compressor, exp.value)) {
|
||||
var dropped = exp.value.drop_side_effect_free(compressor);
|
||||
if (dropped !== exp.value) {
|
||||
changed = true;
|
||||
exp.value = dropped;
|
||||
}
|
||||
}
|
||||
} else if (exp instanceof AST_AsyncFunction || exp instanceof AST_Function) {
|
||||
if (exp.name) {
|
||||
var def = exp.name.definition();
|
||||
drop_body = def.references.length == def.replaced;
|
||||
} else {
|
||||
drop_body = true;
|
||||
}
|
||||
}
|
||||
if (drop_body) {
|
||||
exp.process_expression(false, function(node) {
|
||||
var value = node.value;
|
||||
if (value) {
|
||||
if (async && !is_primitive(compressor, value)) return node;
|
||||
value = value.drop_side_effect_free(compressor, true);
|
||||
}
|
||||
changed = true;
|
||||
if (!value) return make_node(AST_EmptyStatement, node);
|
||||
return make_node(AST_SimpleStatement, node, { body: value });
|
||||
});
|
||||
scan_local_returns(exp, function(node) {
|
||||
var value = node.value;
|
||||
if (value) {
|
||||
if (async && !is_primitive(compressor, value)) return;
|
||||
var dropped = value.drop_side_effect_free(compressor);
|
||||
if (dropped !== value) {
|
||||
changed = true;
|
||||
node.value = dropped;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (async && compressor.option("awaits")) {
|
||||
if (drop_body) exp.process_expression("awaits", function(node) {
|
||||
var body = node.body;
|
||||
if (body instanceof AST_Await) {
|
||||
if (is_primitive(compressor, body.expression)) {
|
||||
changed = true;
|
||||
body = body.expression.drop_side_effect_free(compressor, true);
|
||||
if (!body) return make_node(AST_EmptyStatement, node);
|
||||
node.body = body;
|
||||
}
|
||||
} else if (body instanceof AST_Sequence) {
|
||||
var exprs = body.expressions;
|
||||
for (var i = exprs.length; --i >= 0;) {
|
||||
var tail = exprs[i];
|
||||
if (!(tail instanceof AST_Await)) break;
|
||||
var value = tail.expression;
|
||||
if (!is_primitive(compressor, value)) break;
|
||||
changed = true;
|
||||
if (exprs[i] = value.drop_side_effect_free(compressor)) break;
|
||||
}
|
||||
switch (i) {
|
||||
case -1:
|
||||
return make_node(AST_EmptyStatement, node);
|
||||
case 0:
|
||||
node.body = exprs[0];
|
||||
break;
|
||||
default:
|
||||
exprs.length = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return node;
|
||||
});
|
||||
var abort = !drop_body && exp.name || arrow && exp.value && !is_primitive(compressor, exp.value);
|
||||
var tw = new TreeWalker(function(node) {
|
||||
if (abort) return true;
|
||||
if (tw.parent() === exp && node.may_throw(compressor)) return abort = true;
|
||||
if (node instanceof AST_Await) return abort = true;
|
||||
if (node instanceof AST_ForAwaitOf) return abort = true;
|
||||
if (node instanceof AST_Return) {
|
||||
if (node.value && !is_primitive(compressor, node.value)) return abort = true;
|
||||
return;
|
||||
}
|
||||
if (node instanceof AST_Scope && node !== exp) return true;
|
||||
});
|
||||
exp.walk(tw);
|
||||
if (!abort) {
|
||||
var ctor;
|
||||
switch (exp.CTOR) {
|
||||
case AST_AsyncArrow:
|
||||
ctor = AST_Arrow;
|
||||
break;
|
||||
case AST_AsyncFunction:
|
||||
ctor = AST_Function;
|
||||
break;
|
||||
case AST_AsyncGeneratorFunction:
|
||||
ctor = AST_GeneratorFunction;
|
||||
break;
|
||||
}
|
||||
return make_node(ctor, exp, exp);
|
||||
}
|
||||
}
|
||||
return changed && exp.clone();
|
||||
}
|
||||
function assign_this_only(fn, compressor) {
|
||||
fn.new = true;
|
||||
var result = all(fn.body, function(stat) {
|
||||
|
||||
Reference in New Issue
Block a user