diff options
author | Alex Lam S.L <alexlamsl@gmail.com> | 2021-05-20 00:09:47 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-20 07:09:47 +0800 |
commit | e0695ef5492ac227c3c381683347f939f99a1021 (patch) | |
tree | 0cb1c425161ff56523abd64d5bca8d78993f401f | |
parent | d6152e6a76908223cace651898e12a3b3c9a445d (diff) | |
download | tracifyjs-e0695ef5492ac227c3c381683347f939f99a1021.tar.gz tracifyjs-e0695ef5492ac227c3c381683347f939f99a1021.zip |
enhance `pure_funcs` (#4945)
-rw-r--r-- | lib/compress.js | 102 | ||||
-rw-r--r-- | test/compress/templates.js | 15 |
2 files changed, 87 insertions, 30 deletions
diff --git a/lib/compress.js b/lib/compress.js index 9a6d0191..7b8e17b8 100644 --- a/lib/compress.js +++ b/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; - if (expressions.length == 0) return null; - return make_sequence(this, expressions).drop_side_effect_free(compressor, first_in_statement); + var self = this; + if (self.is_expr_pure(compressor)) { + var expressions = self.expressions; + if (expressions.length == 0) return null; + 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; diff --git a/test/compress/templates.js b/test/compress/templates.js index 3df3efb7..3c1661fe 100644 --- a/test/compress/templates.js +++ b/test/compress/templates.js @@ -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, |