Skip to content

Commit

Permalink
process code with implicit return statement
Browse files Browse the repository at this point in the history
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 mishoo#354
fixes mishoo#543
fixes mishoo#625
fixes mishoo#628
fixes mishoo#640
closes mishoo#1293
  • Loading branch information
alexlamsl committed Mar 2, 2017
1 parent ee3b39b commit 91f29e8
Showing 6 changed files with 434 additions and 5 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -423,6 +423,9 @@ to set `true`; it's effectively a shortcut for `foo=true`).
such as `console.info` and/or retain side effects from function arguments
after dropping the function call then use `pure_funcs` instead.

- `expression` -- default `false`. Pass `true` to preserve completion values
from terminal statements without `return`, e.g. in bookmarklets.

- `keep_fargs` -- default `true`. Prevents the
compressor from discarding unused function arguments. You need this
for code which relies on `Function.length`.
56 changes: 53 additions & 3 deletions lib/compress.js
Original file line number Diff line number Diff line change
@@ -79,6 +79,7 @@ function Compressor(options, false_by_default) {
screw_ie8 : true,
drop_console : false,
angular : false,
expression : false,
warnings : true,
global_defs : {},
passes : 1,
@@ -115,12 +116,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) {
@@ -177,6 +184,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 = [];
@@ -1989,7 +2032,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, ' ');
@@ -2672,9 +2722,9 @@ merge(Compressor.prototype, {
}
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);
var args = self.args.concat(make_node(AST_Undefined, self));
return AST_Seq.from_array(args).transform(compressor);
}
}
if (compressor.option("drop_console")) {
2 changes: 1 addition & 1 deletion test/compress/drop-unused.js
Original file line number Diff line number Diff line change
@@ -632,7 +632,7 @@ iife: {
}
expect: {
function f() {
~function() {}(b);
b;
}
}
}
Loading

0 comments on commit 91f29e8

Please sign in to comment.