aboutsummaryrefslogtreecommitdiff
path: root/lib/compress.js
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2017-03-03 18:13:07 +0800
committerGitHub <noreply@github.com>2017-03-03 18:13:07 +0800
commit07accd2fbb78ddbdb427774b3b5287a16fa95b5f (patch)
treeb1c07129a02b3e595af25e5e9daf8a3024d44fa3 /lib/compress.js
parent18059cc94fdc037e296a1cb1b08143d5e3aae570 (diff)
downloadtracifyjs-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.js90
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)) {