enhance pure_funcs (#4945)
This commit is contained in:
100
lib/compress.js
100
lib/compress.js
@@ -136,11 +136,23 @@ function Compressor(options, false_by_default) {
|
||||
this.pure_funcs = pure_funcs;
|
||||
} else if (typeof pure_funcs == "string") {
|
||||
this.pure_funcs = function(node) {
|
||||
return pure_funcs !== node.expression.print_to_string();
|
||||
var expr;
|
||||
if (node instanceof AST_Call) {
|
||||
expr = node.expression;
|
||||
} else if (node instanceof AST_Template) {
|
||||
expr = node.tag;
|
||||
}
|
||||
return !(expr && pure_funcs === expr.print_to_string());
|
||||
};
|
||||
} else if (Array.isArray(pure_funcs)) {
|
||||
this.pure_funcs = function(node) {
|
||||
return !member(node.expression.print_to_string(), pure_funcs);
|
||||
var expr;
|
||||
if (node instanceof AST_Call) {
|
||||
expr = node.expression;
|
||||
} else if (node instanceof AST_Template) {
|
||||
expr = node.tag;
|
||||
}
|
||||
return !(expr && member(expr.print_to_string(), pure_funcs));
|
||||
};
|
||||
} else {
|
||||
this.pure_funcs = return_true;
|
||||
@@ -2115,7 +2127,7 @@ merge(Compressor.prototype, {
|
||||
def = fn.definition();
|
||||
fn = fn.fixed_value();
|
||||
}
|
||||
if (!(fn instanceof AST_Lambda)) return true;
|
||||
if (!(fn instanceof AST_Lambda)) return !node.is_expr_pure(compressor);
|
||||
if (def && recursive_ref(compressor, def)) return true;
|
||||
if (fn.collapse_scanning) return false;
|
||||
fn.collapse_scanning = true;
|
||||
@@ -2175,7 +2187,7 @@ merge(Compressor.prototype, {
|
||||
var def = node.definition();
|
||||
return (in_try || def.scope.resolve() !== scope) && !can_drop_symbol(node);
|
||||
}
|
||||
if (node instanceof AST_Template) return node.tag && !is_raw_tag(compressor, node.tag);
|
||||
if (node instanceof AST_Template) return !node.is_expr_pure(compressor);
|
||||
if (node instanceof AST_VarDef) {
|
||||
if (check_destructured(node.name)) return true;
|
||||
return (node.value || parent instanceof AST_Let) && node.name.match_symbol(function(node) {
|
||||
@@ -4275,6 +4287,7 @@ merge(Compressor.prototype, {
|
||||
],
|
||||
String: [
|
||||
"fromCharCode",
|
||||
"raw",
|
||||
],
|
||||
});
|
||||
|
||||
@@ -4908,6 +4921,19 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
return compressor.option("annotations") && this.pure || !compressor.pure_funcs(this);
|
||||
});
|
||||
AST_Template.DEFMETHOD("is_expr_pure", function(compressor) {
|
||||
var tag = this.tag;
|
||||
if (!tag) return true;
|
||||
if (compressor.option("unsafe")) {
|
||||
if (is_undeclared_ref(tag) && global_pure_fns[tag.name]) return true;
|
||||
if (tag instanceof AST_Dot && is_undeclared_ref(tag.expression)) {
|
||||
var static_fn = static_fns[tag.expression.name];
|
||||
return static_fn && (static_fn[tag.property]
|
||||
|| tag.expression.name == "Math" && tag.property == "random");
|
||||
}
|
||||
}
|
||||
return !compressor.pure_funcs(this);
|
||||
});
|
||||
AST_Node.DEFMETHOD("is_call_pure", return_false);
|
||||
AST_Call.DEFMETHOD("is_call_pure", function(compressor) {
|
||||
if (!compressor.option("unsafe")) return false;
|
||||
@@ -5072,7 +5098,7 @@ merge(Compressor.prototype, {
|
||||
return !this.is_declared(compressor) || !can_drop_symbol(this, compressor);
|
||||
});
|
||||
def(AST_Template, function(compressor) {
|
||||
return this.tag && !is_raw_tag(compressor, this.tag) || any(this.expressions, compressor);
|
||||
return !this.is_expr_pure(compressor) || any(this.expressions, compressor);
|
||||
});
|
||||
def(AST_Try, function(compressor) {
|
||||
return any(this.body, compressor)
|
||||
@@ -7485,27 +7511,7 @@ merge(Compressor.prototype, {
|
||||
if (!rhs) return lhs;
|
||||
return make_sequence(this, [ lhs, rhs ]);
|
||||
});
|
||||
def(AST_Call, function(compressor, first_in_statement) {
|
||||
var self = this;
|
||||
if (self.is_expr_pure(compressor)) {
|
||||
if (self.pure) AST_Node.warn("Dropping __PURE__ call [{file}:{line},{col}]", self.start);
|
||||
var args = trim(self.args, compressor, first_in_statement, array_spread);
|
||||
return args && make_sequence(self, args.map(convert_spread));
|
||||
}
|
||||
var exp = self.expression;
|
||||
if (self.is_call_pure(compressor)) {
|
||||
var exprs = self.args.slice();
|
||||
exprs.unshift(exp.expression);
|
||||
exprs = trim(exprs, compressor, first_in_statement, array_spread);
|
||||
return exprs && make_sequence(self, exprs.map(convert_spread));
|
||||
}
|
||||
if (compressor.option("yields") && is_generator(exp)) {
|
||||
var call = self.clone();
|
||||
call.expression = make_node(AST_Function, exp, exp);
|
||||
call.expression.body = [];
|
||||
var opt = call.transform(compressor);
|
||||
if (opt !== call) return opt.drop_side_effect_free(compressor, first_in_statement);
|
||||
}
|
||||
function drop_returns(compressor, exp) {
|
||||
var drop_body = false;
|
||||
if (compressor.option("arrows") && is_arrow(exp)) {
|
||||
if (!exp.value) {
|
||||
@@ -7539,6 +7545,31 @@ merge(Compressor.prototype, {
|
||||
node.value = value.drop_side_effect_free(compressor);
|
||||
}
|
||||
});
|
||||
}
|
||||
return drop_body;
|
||||
}
|
||||
def(AST_Call, function(compressor, first_in_statement) {
|
||||
var self = this;
|
||||
if (self.is_expr_pure(compressor)) {
|
||||
if (self.pure) AST_Node.warn("Dropping __PURE__ call [{file}:{line},{col}]", self.start);
|
||||
var args = trim(self.args, compressor, first_in_statement, array_spread);
|
||||
return args && make_sequence(self, args.map(convert_spread));
|
||||
}
|
||||
var exp = self.expression;
|
||||
if (self.is_call_pure(compressor)) {
|
||||
var exprs = self.args.slice();
|
||||
exprs.unshift(exp.expression);
|
||||
exprs = trim(exprs, compressor, first_in_statement, array_spread);
|
||||
return exprs && make_sequence(self, exprs.map(convert_spread));
|
||||
}
|
||||
if (compressor.option("yields") && is_generator(exp)) {
|
||||
var call = self.clone();
|
||||
call.expression = make_node(AST_Function, exp, exp);
|
||||
call.expression.body = [];
|
||||
var opt = call.transform(compressor);
|
||||
if (opt !== call) return opt.drop_side_effect_free(compressor, first_in_statement);
|
||||
}
|
||||
if (drop_returns(compressor, exp)) {
|
||||
// always shallow clone to ensure stripping of negated IIFEs
|
||||
self = self.clone();
|
||||
self.expression = exp.clone();
|
||||
@@ -7734,10 +7765,21 @@ merge(Compressor.prototype, {
|
||||
return this.is_declared(compressor) && can_drop_symbol(this, compressor) ? null : this;
|
||||
});
|
||||
def(AST_Template, function(compressor, first_in_statement) {
|
||||
if (this.tag && !is_raw_tag(compressor, this.tag)) return this;
|
||||
var expressions = this.expressions;
|
||||
var self = this;
|
||||
if (self.is_expr_pure(compressor)) {
|
||||
var expressions = self.expressions;
|
||||
if (expressions.length == 0) return null;
|
||||
return make_sequence(this, expressions).drop_side_effect_free(compressor, first_in_statement);
|
||||
return make_sequence(self, expressions).drop_side_effect_free(compressor, first_in_statement);
|
||||
}
|
||||
var tag = self.tag;
|
||||
if (drop_returns(compressor, tag)) {
|
||||
// always shallow clone to signal internal changes
|
||||
self = self.clone();
|
||||
self.tag = tag.clone();
|
||||
// avoid extraneous traversal
|
||||
if (tag._squeezed) self.tag._squeezed = true;
|
||||
}
|
||||
return self;
|
||||
});
|
||||
def(AST_Unary, function(compressor, first_in_statement) {
|
||||
var exp = this.expression;
|
||||
|
||||
@@ -315,6 +315,21 @@ unsafe_side_effects: {
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
pure_funcs: {
|
||||
options = {
|
||||
pure_funcs: "Math.random",
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
Math.random`${console.log("PASS")}`;
|
||||
}
|
||||
expect: {
|
||||
console.log("PASS");
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
issue_4604: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
|
||||
Reference in New Issue
Block a user