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
This commit is contained in:
Alex Lam S.L
2017-03-03 18:13:07 +08:00
committed by GitHub
parent 18059cc94f
commit 07accd2fbb
8 changed files with 485 additions and 32 deletions

View File

@@ -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)) {