Skip to content

Commit dfb2d43

Browse files
committed
process code with implicit return statement
Things like Angular Expression and Bookmarklet do not specify `return`, but implicitedly assumes the evaluated value from the final statement to be the return value. fixes mishoo#354 fixes mishoo#543 fixes mishoo#625 fixes mishoo#628 fixes mishoo#640 closes mishoo#1293
1 parent fb2b6c7 commit dfb2d43

File tree

3 files changed

+360
-1
lines changed

3 files changed

+360
-1
lines changed

lib/compress.js

+42
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,41 @@ 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 tt = new TreeTransformer(function(node) {
189+
if (insert && node instanceof AST_SimpleStatement) {
190+
return make_node(AST_Return, node, {
191+
value: node.body
192+
});
193+
}
194+
if (!insert && node instanceof AST_Return) {
195+
return make_node(AST_SimpleStatement, node, {
196+
body: node.value || make_node(AST_Undefined, node)
197+
});
198+
}
199+
if (node instanceof AST_Lambda) {
200+
return node;
201+
}
202+
if (node instanceof AST_Block) {
203+
var index = node.body.length - 1;
204+
if (index >= 0) {
205+
node.body[index] = node.body[index].transform(tt);
206+
}
207+
}
208+
if (node instanceof AST_If) {
209+
node.body = node.body.transform(tt);
210+
if (node.alternative) {
211+
node.alternative = node.alternative.transform(tt);
212+
}
213+
}
214+
if (node instanceof AST_With) {
215+
node.body = node.body.transform(tt);
216+
}
217+
return node;
218+
});
219+
return this.transform(tt);
220+
});
221+
180222
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan){
181223
var reduce_vars = rescan && compressor.option("reduce_vars");
182224
var unsafe = compressor.option("unsafe");

test/compress/issue-640.js

+317
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
cond_5: {
2+
options = {
3+
conditionals: true,
4+
expression: true,
5+
}
6+
input: {
7+
if (some_condition()) {
8+
if (some_other_condition()) {
9+
do_something();
10+
} else {
11+
alternate();
12+
}
13+
} else {
14+
alternate();
15+
}
16+
17+
if (some_condition()) {
18+
if (some_other_condition()) {
19+
do_something();
20+
}
21+
}
22+
}
23+
expect: {
24+
some_condition() && some_other_condition() ? do_something() : alternate();
25+
if (some_condition() && some_other_condition()) do_something();
26+
}
27+
}
28+
29+
dead_code_const_annotation_regex: {
30+
options = {
31+
booleans : true,
32+
conditionals : true,
33+
dead_code : true,
34+
evaluate : true,
35+
expression : true,
36+
loops : true,
37+
}
38+
input: {
39+
var unused;
40+
// @constraint this shouldn't be a constant
41+
var CONST_FOO_ANN = false;
42+
if (CONST_FOO_ANN) {
43+
console.log("reachable");
44+
}
45+
}
46+
expect: {
47+
var unused;
48+
var CONST_FOO_ANN = !1;
49+
if (CONST_FOO_ANN) console.log('reachable');
50+
}
51+
}
52+
53+
drop_console_2: {
54+
options = {
55+
drop_console: true,
56+
expression: true,
57+
}
58+
input: {
59+
console.log('foo');
60+
console.log.apply(console, arguments);
61+
}
62+
expect: {
63+
// with regular compression these will be stripped out as well
64+
void 0;
65+
void 0;
66+
}
67+
}
68+
69+
drop_value: {
70+
options = {
71+
expression: true,
72+
side_effects: true,
73+
}
74+
input: {
75+
(1, [2, foo()], 3, {a:1, b:bar()});
76+
}
77+
expect: {
78+
foo(), {a:1, b:bar()};
79+
}
80+
}
81+
82+
wrongly_optimized: {
83+
options = {
84+
conditionals: true,
85+
booleans: true,
86+
evaluate: true,
87+
expression: true,
88+
}
89+
input: {
90+
function func() {
91+
foo();
92+
}
93+
if (func() || true) {
94+
bar();
95+
}
96+
}
97+
expect: {
98+
function func() {
99+
foo();
100+
}
101+
// TODO: optimize to `func(), bar()`
102+
if (func(), !0) bar();
103+
}
104+
}
105+
106+
negate_iife_1: {
107+
options = {
108+
expression: true,
109+
negate_iife: true,
110+
}
111+
input: {
112+
(function(){ stuff() })();
113+
}
114+
expect: {
115+
(function(){ stuff() })();
116+
}
117+
}
118+
119+
negate_iife_3: {
120+
options = {
121+
conditionals: true,
122+
expression: true,
123+
negate_iife: true,
124+
}
125+
input: {
126+
(function(){ return true })() ? console.log(true) : console.log(false);
127+
}
128+
expect: {
129+
(function(){ return true })() ? console.log(true) : console.log(false);
130+
}
131+
}
132+
133+
negate_iife_3_off: {
134+
options = {
135+
conditionals: true,
136+
expression: true,
137+
negate_iife: false,
138+
}
139+
input: {
140+
(function(){ return true })() ? console.log(true) : console.log(false);
141+
}
142+
expect: {
143+
(function(){ return true })() ? console.log(true) : console.log(false);
144+
}
145+
}
146+
147+
negate_iife_4: {
148+
options = {
149+
conditionals: true,
150+
expression: true,
151+
negate_iife: true,
152+
sequences: true,
153+
}
154+
input: {
155+
(function(){ return true })() ? console.log(true) : console.log(false);
156+
(function(){
157+
console.log("something");
158+
})();
159+
}
160+
expect: {
161+
(function(){ return true })() ? console.log(true) : console.log(false), function(){
162+
console.log("something");
163+
}();
164+
}
165+
}
166+
167+
negate_iife_5: {
168+
options = {
169+
conditionals: true,
170+
expression: true,
171+
negate_iife: true,
172+
sequences: true,
173+
}
174+
input: {
175+
if ((function(){ return true })()) {
176+
foo(true);
177+
} else {
178+
bar(false);
179+
}
180+
(function(){
181+
console.log("something");
182+
})();
183+
}
184+
expect: {
185+
(function(){ return true })() ? foo(true) : bar(false), function(){
186+
console.log("something");
187+
}();
188+
}
189+
}
190+
191+
negate_iife_5_off: {
192+
options = {
193+
conditionals: true,
194+
expression: true,
195+
negate_iife: false,
196+
sequences: true,
197+
};
198+
input: {
199+
if ((function(){ return true })()) {
200+
foo(true);
201+
} else {
202+
bar(false);
203+
}
204+
(function(){
205+
console.log("something");
206+
})();
207+
}
208+
expect: {
209+
(function(){ return true })() ? foo(true) : bar(false), function(){
210+
console.log("something");
211+
}();
212+
}
213+
}
214+
215+
issue_1254_negate_iife_true: {
216+
options = {
217+
expression: true,
218+
negate_iife: true,
219+
}
220+
input: {
221+
(function() {
222+
return function() {
223+
console.log('test')
224+
};
225+
})()();
226+
}
227+
expect_exact: '(function(){return function(){console.log("test")}})()();'
228+
}
229+
230+
issue_1254_negate_iife_nested: {
231+
options = {
232+
expression: true,
233+
negate_iife: true,
234+
}
235+
input: {
236+
(function() {
237+
return function() {
238+
console.log('test')
239+
};
240+
})()()()()();
241+
}
242+
expect_exact: '(function(){return function(){console.log("test")}})()()()()();'
243+
}
244+
245+
conditional: {
246+
options = {
247+
expression: true,
248+
pure_funcs: [ "pure" ],
249+
side_effects: true,
250+
}
251+
input: {
252+
pure(1 | a() ? 2 & b() : 7 ^ c());
253+
pure(1 | a() ? 2 & b() : 5);
254+
pure(1 | a() ? 4 : 7 ^ c());
255+
pure(1 | a() ? 4 : 5);
256+
pure(3 ? 2 & b() : 7 ^ c());
257+
pure(3 ? 2 & b() : 5);
258+
pure(3 ? 4 : 7 ^ c());
259+
pure(3 ? 4 : 5);
260+
}
261+
expect: {
262+
1 | a() ? b() : c();
263+
1 | a() && b();
264+
1 | a() || c();
265+
a();
266+
3 ? b() : c();
267+
3 && b();
268+
3 || c();
269+
pure(3 ? 4 : 5);
270+
}
271+
}
272+
273+
limit_1: {
274+
options = {
275+
expression: true,
276+
sequences: 3,
277+
}
278+
input: {
279+
a;
280+
b;
281+
c;
282+
d;
283+
e;
284+
f;
285+
g;
286+
h;
287+
i;
288+
j;
289+
k;
290+
}
291+
expect: {
292+
// Turned into a single return statement
293+
// so it can no longer be split into lines
294+
a,b,c,d,e,f,g,h,i,j,k;
295+
}
296+
}
297+
298+
iife: {
299+
options = {
300+
expression: true,
301+
sequences: true,
302+
}
303+
input: {
304+
x = 42;
305+
(function a() {})();
306+
!function b() {}();
307+
~function c() {}();
308+
+function d() {}();
309+
-function e() {}();
310+
void function f() {}();
311+
typeof function g() {}();
312+
}
313+
expect: {
314+
x = 42, function a() {}(), function b() {}(), function c() {}(),
315+
function d() {}(), function e() {}(), function f() {}(), typeof function g() {}();
316+
}
317+
}

test/compress/sequences.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,6 @@ iife: {
248248
}
249249
expect: {
250250
x = 42, function a() {}(), function b() {}(), function c() {}(),
251-
function d() {}(), function e() {}(), function f() {}(), function g() {}()
251+
function d() {}(), function e() {}(), function f() {}(), function g() {}();
252252
}
253253
}

0 commit comments

Comments
 (0)