Skip to content

Commit 91f29e8

Browse files
committed
process code with implicit return statement
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
1 parent ee3b39b commit 91f29e8

File tree

6 files changed

+434
-5
lines changed

6 files changed

+434
-5
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,9 @@ to set `true`; it's effectively a shortcut for `foo=true`).
423423
such as `console.info` and/or retain side effects from function arguments
424424
after dropping the function call then use `pure_funcs` instead.
425425

426+
- `expression` -- default `false`. Pass `true` to preserve completion values
427+
from terminal statements without `return`, e.g. in bookmarklets.
428+
426429
- `keep_fargs` -- default `true`. Prevents the
427430
compressor from discarding unused function arguments. You need this
428431
for code which relies on `Function.length`.

lib/compress.js

+53-3
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ function Compressor(options, false_by_default) {
7979
screw_ie8 : true,
8080
drop_console : false,
8181
angular : false,
82+
expression : false,
8283
warnings : true,
8384
global_defs : {},
8485
passes : 1,
@@ -115,12 +116,18 @@ Compressor.prototype = new TreeTransformer;
115116
merge(Compressor.prototype, {
116117
option: function(key) { return this.options[key] },
117118
compress: function(node) {
119+
if (this.option("expression")) {
120+
node = node.process_expression(true);
121+
}
118122
var passes = +this.options.passes || 1;
119123
for (var pass = 0; pass < passes && pass < 3; ++pass) {
120124
if (pass > 0 || this.option("reduce_vars"))
121125
node.reset_opt_flags(this, true);
122126
node = node.transform(this);
123127
}
128+
if (this.option("expression")) {
129+
node = node.process_expression(false);
130+
}
124131
return node;
125132
},
126133
warn: function(text, props) {
@@ -177,6 +184,42 @@ merge(Compressor.prototype, {
177184
return this.print_to_string() == node.print_to_string();
178185
});
179186

187+
AST_Node.DEFMETHOD("process_expression", function(insert) {
188+
var self = this;
189+
var tt = new TreeTransformer(function(node) {
190+
if (insert && node instanceof AST_SimpleStatement) {
191+
return make_node(AST_Return, node, {
192+
value: node.body
193+
});
194+
}
195+
if (!insert && node instanceof AST_Return) {
196+
return make_node(AST_SimpleStatement, node, {
197+
body: node.value || make_node(AST_Undefined, node)
198+
});
199+
}
200+
if (node instanceof AST_Lambda && node !== self) {
201+
return node;
202+
}
203+
if (node instanceof AST_Block) {
204+
var index = node.body.length - 1;
205+
if (index >= 0) {
206+
node.body[index] = node.body[index].transform(tt);
207+
}
208+
}
209+
if (node instanceof AST_If) {
210+
node.body = node.body.transform(tt);
211+
if (node.alternative) {
212+
node.alternative = node.alternative.transform(tt);
213+
}
214+
}
215+
if (node instanceof AST_With) {
216+
node.body = node.body.transform(tt);
217+
}
218+
return node;
219+
});
220+
return self.transform(tt);
221+
});
222+
180223
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan){
181224
var reduce_vars = rescan && compressor.option("reduce_vars");
182225
var safe_ids = [];
@@ -1989,7 +2032,14 @@ merge(Compressor.prototype, {
19892032
def(AST_Constant, return_null);
19902033
def(AST_This, return_null);
19912034
def(AST_Call, function(compressor, first_in_statement){
1992-
if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) return this;
2035+
if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) {
2036+
if (this.expression instanceof AST_Function) {
2037+
var node = this.clone();
2038+
node.expression = node.expression.process_expression(false);
2039+
return node;
2040+
}
2041+
return this;
2042+
}
19932043
if (this.pure) {
19942044
compressor.warn("Dropping __PURE__ call [{file}:{line},{col}]", this.start);
19952045
this.pure.value = this.pure.value.replace(/[@#]__PURE__/g, ' ');
@@ -2672,9 +2722,9 @@ merge(Compressor.prototype, {
26722722
}
26732723
if (compressor.option("side_effects")) {
26742724
if (self.expression instanceof AST_Function
2675-
&& self.args.length == 0
26762725
&& !AST_Block.prototype.has_side_effects.call(self.expression, compressor)) {
2677-
return make_node(AST_Undefined, self).transform(compressor);
2726+
var args = self.args.concat(make_node(AST_Undefined, self));
2727+
return AST_Seq.from_array(args).transform(compressor);
26782728
}
26792729
}
26802730
if (compressor.option("drop_console")) {

test/compress/drop-unused.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,7 @@ iife: {
632632
}
633633
expect: {
634634
function f() {
635-
~function() {}(b);
635+
b;
636636
}
637637
}
638638
}

0 commit comments

Comments
 (0)