diff options
author | Alex Lam S.L <alexlamsl@gmail.com> | 2017-03-03 18:13:07 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-03-03 18:13:07 +0800 |
commit | 07accd2fbb78ddbdb427774b3b5287a16fa95b5f (patch) | |
tree | b1c07129a02b3e595af25e5e9daf8a3024d44fa3 /lib/compress.js | |
parent | 18059cc94fdc037e296a1cb1b08143d5e3aae570 (diff) | |
download | tracifyjs-07accd2fbb78ddbdb427774b3b5287a16fa95b5f.tar.gz tracifyjs-07accd2fbb78ddbdb427774b3b5287a16fa95b5f.zip |
process code with implicit return statement (#1522)
Bookmarklet for instance implicitedly assumes a "completion value" without using `return`.
The `expression` option now supports such use cases.
Optimisations on IIFEs also enhanced.
fixes #354
fixes #543
fixes #625
fixes #628
fixes #640
closes #1293
Diffstat (limited to 'lib/compress.js')
-rw-r--r-- | lib/compress.js | 90 |
1 files changed, 70 insertions, 20 deletions
diff --git a/lib/compress.js b/lib/compress.js index ec1e7174..2cd79128 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -80,6 +80,7 @@ function Compressor(options, false_by_default) { screw_ie8 : true, drop_console : false, angular : false, + expression : false, warnings : true, global_defs : {}, passes : 1, @@ -116,12 +117,18 @@ Compressor.prototype = new TreeTransformer; merge(Compressor.prototype, { option: function(key) { return this.options[key] }, compress: function(node) { + if (this.option("expression")) { + node = node.process_expression(true); + } var passes = +this.options.passes || 1; for (var pass = 0; pass < passes && pass < 3; ++pass) { if (pass > 0 || this.option("reduce_vars")) node.reset_opt_flags(this, true); node = node.transform(this); } + if (this.option("expression")) { + node = node.process_expression(false); + } return node; }, warn: function(text, props) { @@ -178,6 +185,42 @@ merge(Compressor.prototype, { return this.print_to_string() == node.print_to_string(); }); + AST_Node.DEFMETHOD("process_expression", function(insert) { + var self = this; + var tt = new TreeTransformer(function(node) { + if (insert && node instanceof AST_SimpleStatement) { + return make_node(AST_Return, node, { + value: node.body + }); + } + if (!insert && node instanceof AST_Return) { + return make_node(AST_SimpleStatement, node, { + body: node.value || make_node(AST_Undefined, node) + }); + } + if (node instanceof AST_Lambda && node !== self) { + return node; + } + if (node instanceof AST_Block) { + var index = node.body.length - 1; + if (index >= 0) { + node.body[index] = node.body[index].transform(tt); + } + } + if (node instanceof AST_If) { + node.body = node.body.transform(tt); + if (node.alternative) { + node.alternative = node.alternative.transform(tt); + } + } + if (node instanceof AST_With) { + node.body = node.body.transform(tt); + } + return node; + }); + return self.transform(tt); + }); + AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan){ var reduce_vars = rescan && compressor.option("reduce_vars"); var safe_ids = []; @@ -2030,7 +2073,14 @@ merge(Compressor.prototype, { def(AST_Constant, return_null); def(AST_This, return_null); def(AST_Call, function(compressor, first_in_statement){ - if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) return this; + if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) { + if (this.expression instanceof AST_Function) { + var node = this.clone(); + node.expression = node.expression.process_expression(false); + return node; + } + return this; + } if (this.pure) { compressor.warn("Dropping __PURE__ call [{file}:{line},{col}]", this.start); this.pure.value = this.pure.value.replace(/[@#]__PURE__/g, ' '); @@ -2522,12 +2572,13 @@ merge(Compressor.prototype, { }); OPT(AST_Call, function(self, compressor){ + var exp = self.expression; if (compressor.option("unused") - && self.expression instanceof AST_Function - && !self.expression.uses_arguments - && !self.expression.uses_eval - && self.args.length > self.expression.argnames.length) { - var end = self.expression.argnames.length; + && exp instanceof AST_Function + && !exp.uses_arguments + && !exp.uses_eval + && self.args.length > exp.argnames.length) { + var end = exp.argnames.length; for (var i = end, len = self.args.length; i < len; i++) { var node = self.args[i].drop_side_effect_free(compressor); if (node) { @@ -2537,7 +2588,6 @@ merge(Compressor.prototype, { self.args.length = end; } if (compressor.option("unsafe")) { - var exp = self.expression; if (exp instanceof AST_SymbolRef && exp.undeclared()) { switch (exp.name) { case "Array": @@ -2711,16 +2761,22 @@ merge(Compressor.prototype, { return best_of(self, node); } } - if (compressor.option("side_effects")) { - if (self.expression instanceof AST_Function - && self.args.length == 0 - && !AST_Block.prototype.has_side_effects.call(self.expression, compressor)) { - return make_node(AST_Undefined, self).transform(compressor); + if (exp instanceof AST_Function) { + if (exp.body[0] instanceof AST_Return + && exp.body[0].value.is_constant()) { + var args = self.args.concat(exp.body[0].value); + return AST_Seq.from_array(args).transform(compressor); + } + if (compressor.option("side_effects")) { + if (!AST_Block.prototype.has_side_effects.call(exp, compressor)) { + var args = self.args.concat(make_node(AST_Undefined, self)); + return AST_Seq.from_array(args).transform(compressor); + } } } if (compressor.option("drop_console")) { - if (self.expression instanceof AST_PropAccess) { - var name = self.expression.expression; + if (exp instanceof AST_PropAccess) { + var name = exp.expression; while (name.expression) { name = name.expression; } @@ -2731,12 +2787,6 @@ merge(Compressor.prototype, { } } } - if (self.args.length == 0 - && self.expression instanceof AST_Function - && self.expression.body[0] instanceof AST_Return - && self.expression.body[0].value.is_constant()) { - return self.expression.body[0].value; - } if (compressor.option("negate_iife") && compressor.parent() instanceof AST_SimpleStatement && is_iife_call(self)) { |