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:
@@ -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)) {
|
||||
|
||||
Reference in New Issue
Block a user