Skip to content

Commit f01c0c4

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 18059cc commit f01c0c4

File tree

8 files changed

+485
-32
lines changed

8 files changed

+485
-32
lines changed

README.md

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

429+
- `expression` -- default `false`. Pass `true` to preserve completion values
430+
from terminal statements without `return`, e.g. in bookmarklets.
431+
429432
- `keep_fargs` -- default `true`. Prevents the
430433
compressor from discarding unused function arguments. You need this
431434
for code which relies on `Function.length`.

lib/compress.js

+70-20
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ function Compressor(options, false_by_default) {
8080
screw_ie8 : true,
8181
drop_console : false,
8282
angular : false,
83+
expression : false,
8384
warnings : true,
8485
global_defs : {},
8586
passes : 1,
@@ -116,12 +117,18 @@ Compressor.prototype = new TreeTransformer;
116117
merge(Compressor.prototype, {
117118
option: function(key) { return this.options[key] },
118119
compress: function(node) {
120+
if (this.option("expression")) {
121+
node = node.process_expression(true);
122+
}
119123
var passes = +this.options.passes || 1;
120124
for (var pass = 0; pass < passes && pass < 3; ++pass) {
121125
if (pass > 0 || this.option("reduce_vars"))
122126
node.reset_opt_flags(this, true);
123127
node = node.transform(this);
124128
}
129+
if (this.option("expression")) {
130+
node = node.process_expression(false);
131+
}
125132
return node;
126133
},
127134
warn: function(text, props) {
@@ -178,6 +185,42 @@ merge(Compressor.prototype, {
178185
return this.print_to_string() == node.print_to_string();
179186
});
180187

188+
AST_Node.DEFMETHOD("process_expression", function(insert) {
189+
var self = this;
190+
var tt = new TreeTransformer(function(node) {
191+
if (insert && node instanceof AST_SimpleStatement) {
192+
return make_node(AST_Return, node, {
193+
value: node.body
194+
});
195+
}
196+
if (!insert && node instanceof AST_Return) {
197+
return make_node(AST_SimpleStatement, node, {
198+
body: node.value || make_node(AST_Undefined, node)
199+
});
200+
}
201+
if (node instanceof AST_Lambda && node !== self) {
202+
return node;
203+
}
204+
if (node instanceof AST_Block) {
205+
var index = node.body.length - 1;
206+
if (index >= 0) {
207+
node.body[index] = node.body[index].transform(tt);
208+
}
209+
}
210+
if (node instanceof AST_If) {
211+
node.body = node.body.transform(tt);
212+
if (node.alternative) {
213+
node.alternative = node.alternative.transform(tt);
214+
}
215+
}
216+
if (node instanceof AST_With) {
217+
node.body = node.body.transform(tt);
218+
}
219+
return node;
220+
});
221+
return self.transform(tt);
222+
});
223+
181224
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan){
182225
var reduce_vars = rescan && compressor.option("reduce_vars");
183226
var safe_ids = [];
@@ -2030,7 +2073,14 @@ merge(Compressor.prototype, {
20302073
def(AST_Constant, return_null);
20312074
def(AST_This, return_null);
20322075
def(AST_Call, function(compressor, first_in_statement){
2033-
if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) return this;
2076+
if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) {
2077+
if (this.expression instanceof AST_Function) {
2078+
var node = this.clone();
2079+
node.expression = node.expression.process_expression(false);
2080+
return node;
2081+
}
2082+
return this;
2083+
}
20342084
if (this.pure) {
20352085
compressor.warn("Dropping __PURE__ call [{file}:{line},{col}]", this.start);
20362086
this.pure.value = this.pure.value.replace(/[@#]__PURE__/g, ' ');
@@ -2522,12 +2572,13 @@ merge(Compressor.prototype, {
25222572
});
25232573

25242574
OPT(AST_Call, function(self, compressor){
2575+
var exp = self.expression;
25252576
if (compressor.option("unused")
2526-
&& self.expression instanceof AST_Function
2527-
&& !self.expression.uses_arguments
2528-
&& !self.expression.uses_eval
2529-
&& self.args.length > self.expression.argnames.length) {
2530-
var end = self.expression.argnames.length;
2577+
&& exp instanceof AST_Function
2578+
&& !exp.uses_arguments
2579+
&& !exp.uses_eval
2580+
&& self.args.length > exp.argnames.length) {
2581+
var end = exp.argnames.length;
25312582
for (var i = end, len = self.args.length; i < len; i++) {
25322583
var node = self.args[i].drop_side_effect_free(compressor);
25332584
if (node) {
@@ -2537,7 +2588,6 @@ merge(Compressor.prototype, {
25372588
self.args.length = end;
25382589
}
25392590
if (compressor.option("unsafe")) {
2540-
var exp = self.expression;
25412591
if (exp instanceof AST_SymbolRef && exp.undeclared()) {
25422592
switch (exp.name) {
25432593
case "Array":
@@ -2711,16 +2761,22 @@ merge(Compressor.prototype, {
27112761
return best_of(self, node);
27122762
}
27132763
}
2714-
if (compressor.option("side_effects")) {
2715-
if (self.expression instanceof AST_Function
2716-
&& self.args.length == 0
2717-
&& !AST_Block.prototype.has_side_effects.call(self.expression, compressor)) {
2718-
return make_node(AST_Undefined, self).transform(compressor);
2764+
if (exp instanceof AST_Function) {
2765+
if (exp.body[0] instanceof AST_Return
2766+
&& exp.body[0].value.is_constant()) {
2767+
var args = self.args.concat(exp.body[0].value);
2768+
return AST_Seq.from_array(args).transform(compressor);
2769+
}
2770+
if (compressor.option("side_effects")) {
2771+
if (!AST_Block.prototype.has_side_effects.call(exp, compressor)) {
2772+
var args = self.args.concat(make_node(AST_Undefined, self));
2773+
return AST_Seq.from_array(args).transform(compressor);
2774+
}
27192775
}
27202776
}
27212777
if (compressor.option("drop_console")) {
2722-
if (self.expression instanceof AST_PropAccess) {
2723-
var name = self.expression.expression;
2778+
if (exp instanceof AST_PropAccess) {
2779+
var name = exp.expression;
27242780
while (name.expression) {
27252781
name = name.expression;
27262782
}
@@ -2731,12 +2787,6 @@ merge(Compressor.prototype, {
27312787
}
27322788
}
27332789
}
2734-
if (self.args.length == 0
2735-
&& self.expression instanceof AST_Function
2736-
&& self.expression.body[0] instanceof AST_Return
2737-
&& self.expression.body[0].value.is_constant()) {
2738-
return self.expression.body[0].value;
2739-
}
27402790
if (compressor.option("negate_iife")
27412791
&& compressor.parent() instanceof AST_SimpleStatement
27422792
&& is_iife_call(self)) {

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
}

test/compress/evaluate.js

+2-6
Original file line numberDiff line numberDiff line change
@@ -640,9 +640,7 @@ call_args: {
640640
expect: {
641641
const a = 1;
642642
console.log(1);
643-
+function(a) {
644-
return 1;
645-
}(1);
643+
+(1, 1);
646644
}
647645
}
648646

@@ -663,9 +661,7 @@ call_args_drop_param: {
663661
expect: {
664662
const a = 1;
665663
console.log(1);
666-
+function() {
667-
return 1;
668-
}(b);
664+
+(b, 1);
669665
}
670666
}
671667

test/compress/functions.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ iifes_returning_constants_keep_fargs_true: {
3535
console.log("okay");
3636
console.log(123);
3737
console.log(void 0);
38-
console.log(function(x,y,z){return 2}(1,2,3));
39-
console.log(function(x,y){return 6}(2,3));
40-
console.log(function(x, y){return 6}(2,3,a(),b()));
38+
console.log(2);
39+
console.log(6);
40+
console.log((a(), b(), 6));
4141
}
4242
}
4343

@@ -71,6 +71,6 @@ iifes_returning_constants_keep_fargs_false: {
7171
console.log(void 0);
7272
console.log(2);
7373
console.log(6);
74-
console.log(function(){return 6}(a(),b()));
74+
console.log((a(), b(), 6));
7575
}
7676
}

0 commit comments

Comments
 (0)