diff options
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | lib/compress.js | 88 | ||||
-rw-r--r-- | test/compress/collapse_vars.js | 2 | ||||
-rw-r--r-- | test/compress/evaluate.js | 2 | ||||
-rw-r--r-- | test/compress/functions.js | 4 | ||||
-rw-r--r-- | test/compress/issue-1787.js | 1 | ||||
-rw-r--r-- | test/compress/issue-281.js | 433 | ||||
-rw-r--r-- | test/compress/negate-iife.js | 6 | ||||
-rw-r--r-- | test/compress/reduce_vars.js | 15 | ||||
-rw-r--r-- | test/mocha/glob.js | 2 | ||||
-rw-r--r-- | test/mocha/minify.js | 2 |
11 files changed, 528 insertions, 29 deletions
@@ -616,6 +616,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u - `if_return` -- optimizations for if/return and if/continue +- `inline` -- embed simple functions + - `join_vars` -- join consecutive `var` statements - `cascade` -- small optimization for sequences, transform `x, x` into `x` diff --git a/lib/compress.js b/lib/compress.js index effa6b11..969acd61 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -63,6 +63,7 @@ function Compressor(options, false_by_default) { hoist_vars : false, ie8 : false, if_return : !false_by_default, + inline : !false_by_default, join_vars : !false_by_default, keep_fargs : true, keep_fnames : false, @@ -2943,24 +2944,9 @@ merge(Compressor.prototype, { OPT(AST_Call, function(self, compressor){ var exp = self.expression; - if (compressor.option("reduce_vars") - && exp instanceof AST_SymbolRef) { - var def = exp.definition(); + if (compressor.option("reduce_vars") && exp instanceof AST_SymbolRef) { var fixed = exp.fixed_value(); - if (fixed instanceof AST_Defun) { - def.fixed = fixed = make_node(AST_Function, fixed, fixed).clone(true); - } - if (fixed instanceof AST_Function) { - exp = fixed; - if (compressor.option("unused") - && def.references.length == 1 - && !(def.scope.uses_arguments - && def.orig[0] instanceof AST_SymbolFunarg) - && !def.scope.uses_eval - && compressor.find_parent(AST_Scope) === exp.parent_scope) { - self.expression = exp; - } - } + if (fixed instanceof AST_Function) exp = fixed; } if (compressor.option("unused") && exp instanceof AST_Function @@ -3171,13 +3157,61 @@ merge(Compressor.prototype, { } } if (exp instanceof AST_Function) { - if (exp.body[0] instanceof AST_Return) { - var value = exp.body[0].value; + var stat = exp.body[0]; + if (compressor.option("inline") && stat instanceof AST_Return) { + var value = stat && stat.value; if (!value || value.is_constant_expression()) { var args = self.args.concat(value || make_node(AST_Undefined, self)); return make_sequence(self, args).transform(compressor); } } + if (compressor.option("inline") + && !exp.name + && exp.body.length == 1 + && !exp.uses_arguments + && !exp.uses_eval + && !self.has_pure_annotation(compressor)) { + var body; + if (stat instanceof AST_Return) { + body = stat.value.clone(true); + } else if (stat instanceof AST_SimpleStatement) { + body = []; + merge_sequence(body, stat.body.clone(true)); + merge_sequence(body, make_node(AST_Undefined, self)); + body = make_sequence(self, body); + } + if (body) { + var fn = exp.clone(); + fn.argnames = []; + fn.body = [ + make_node(AST_Var, self, { + definitions: exp.argnames.map(function(sym, i) { + return make_node(AST_VarDef, sym, { + name: sym, + value: self.args[i] || make_node(AST_Undefined, self) + }); + }) + }) + ]; + if (self.args.length > exp.argnames.length) { + fn.body.push(make_node(AST_SimpleStatement, self, { + body: make_sequence(self, self.args.slice(exp.argnames.length)) + })); + } + fn.body.push(make_node(AST_Return, self, { + value: body + })); + body = fn.transform(compressor).body; + if (body.length == 0) return make_node(AST_Undefined, self); + if (body.length == 1 && body[0] instanceof AST_Return) { + if (!body[0].value) return make_node(AST_Undefined, self); + body = best_of(compressor, body[0].value, self); + } else { + body = self; + } + if (body !== self) return body; + } + } if (compressor.option("side_effects") && all(exp.body, is_empty)) { var args = self.args.concat(make_node(AST_Undefined, self)); return make_sequence(self, args).transform(compressor); @@ -3836,12 +3870,22 @@ merge(Compressor.prototype, { return make_node(AST_Infinity, self).optimize(compressor); } } - if (compressor.option("evaluate") - && compressor.option("reduce_vars") + if (compressor.option("reduce_vars") && is_lhs(self, compressor.parent()) !== self) { var d = self.definition(); var fixed = self.fixed_value(); - if (fixed) { + if (fixed instanceof AST_Defun) { + d.fixed = fixed = make_node(AST_Function, fixed, fixed).clone(true); + } + if (compressor.option("unused") + && fixed instanceof AST_Function + && d.references.length == 1 + && !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg) + && !d.scope.uses_eval + && compressor.find_parent(AST_Scope) === fixed.parent_scope) { + return fixed; + } + if (compressor.option("evaluate") && fixed) { if (d.should_replace === undefined) { var init = fixed.evaluate(compressor); if (init !== fixed && (compressor.option("unsafe_regexp") || !(init instanceof RegExp))) { diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index 90d7ac93..158d1b4a 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -1146,7 +1146,7 @@ collapse_vars_constants: { function f3(x) { var b = x.prop; sideeffect1(); - return b + -9; + return b + (function() { return -9; })(); } } } diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js index 99245d0d..27d08d47 100644 --- a/test/compress/evaluate.js +++ b/test/compress/evaluate.js @@ -644,6 +644,7 @@ unsafe_prototype_function: { call_args: { options = { evaluate: true, + inline: true, reduce_vars: true, toplevel: true, } @@ -665,6 +666,7 @@ call_args: { call_args_drop_param: { options = { evaluate: true, + inline: true, keep_fargs: false, reduce_vars: true, toplevel: true, diff --git a/test/compress/functions.js b/test/compress/functions.js index a9ca23f8..180bb11a 100644 --- a/test/compress/functions.js +++ b/test/compress/functions.js @@ -21,6 +21,7 @@ iifes_returning_constants_keep_fargs_true: { join_vars : true, reduce_vars : true, cascade : true, + inline : true, } input: { (function(){ return -1.23; }()); @@ -56,6 +57,7 @@ iifes_returning_constants_keep_fargs_false: { join_vars : true, reduce_vars : true, cascade : true, + inline : true, } input: { (function(){ return -1.23; }()); @@ -82,6 +84,7 @@ issue_485_crashing_1530: { conditionals: true, dead_code: true, evaluate: true, + inline: true, } input: { (function(a) { @@ -154,6 +157,7 @@ function_returning_constant_literal: { evaluate: true, cascade: true, unused: true, + inline: true, } input: { function greeter() { diff --git a/test/compress/issue-1787.js b/test/compress/issue-1787.js index 02fa0f91..2b5372be 100644 --- a/test/compress/issue-1787.js +++ b/test/compress/issue-1787.js @@ -1,6 +1,7 @@ unary_prefix: { options = { evaluate: true, + inline: true, reduce_vars: true, unused: true, } diff --git a/test/compress/issue-281.js b/test/compress/issue-281.js new file mode 100644 index 00000000..6b1b4b40 --- /dev/null +++ b/test/compress/issue-281.js @@ -0,0 +1,433 @@ +collapse_vars_constants: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + reduce_vars: true, + unused: true, + } + input: { + function f1(x) { + var a = 4, b = x.prop, c = 5, d = sideeffect1(), e = sideeffect2(); + return b + (function() { return d - a * e - c; })(); + } + function f2(x) { + var a = 4, b = x.prop, c = 5, not_used = sideeffect1(), e = sideeffect2(); + return b + (function() { return -a * e - c; })(); + } + } + expect: { + function f1(x) { + var b = x.prop, d = sideeffect1(), e = sideeffect2(); + return b + (d - 4 * e - 5); + } + function f2(x) { + var b = x.prop; + sideeffect1(); + return b + (-4 * sideeffect2() - 5); + } + } +} + +modified: { + options = { + collapse_vars: true, + inline: true, + unused: true, + } + input: { + function f5(b) { + var a = function() { + return b; + }(); + return b++ + a; + } + console.log(f5(1)); + } + expect: { + function f5(b) { + var a = b; + return b++ + a; + } + console.log(f5(1)); + } + expect_stdout: "2" +} + +ref_scope: { + options = { + collapse_vars: true, + inline: true, + unused: true, + } + input: { + console.log(function() { + var a = 1, b = 2, c = 3; + var a = c++, b = b /= a; + return function() { + return a; + }() + b; + }()); + } + expect: { + console.log(function() { + var a = 1, b = 2, c = 3; + b = b /= a = c++; + return a + b; + }()); + } + expect_stdout: true +} + +safe_undefined: { + options = { + conditionals: true, + if_return: true, + inline: true, + unsafe: false, + unused: true, + } + mangle = {} + input: { + var a, c; + console.log(function(undefined) { + return function() { + if (a) + return b; + if (c) + return d; + }; + }(1)()); + } + expect: { + var a, c; + console.log(a ? b : c ? d : void 0); + } + expect_stdout: true +} + +negate_iife_3: { + options = { + conditionals: true, + expression: true, + inline: true, + negate_iife: true, + } + input: { + (function(){ return t })() ? console.log(true) : console.log(false); + } + expect: { + t ? console.log(true) : console.log(false); + } +} + +negate_iife_3_off: { + options = { + conditionals: true, + expression: true, + inline: true, + negate_iife: false, + } + input: { + (function(){ return t })() ? console.log(true) : console.log(false); + } + expect: { + t ? console.log(true) : console.log(false); + } +} + +negate_iife_4: { + options = { + conditionals: true, + expression: true, + inline: true, + negate_iife: true, + sequences: true, + } + input: { + (function(){ return t })() ? console.log(true) : console.log(false); + (function(){ + console.log("something"); + })(); + } + expect: { + t ? console.log(true) : console.log(false), console.log("something"), void 0; + } +} + +negate_iife_5: { + options = { + conditionals: true, + expression: true, + inline: true, + negate_iife: true, + sequences: true, + } + input: { + if ((function(){ return t })()) { + foo(true); + } else { + bar(false); + } + (function(){ + console.log("something"); + })(); + } + expect: { + t ? foo(true) : bar(false), console.log("something"), void 0; + } +} + +negate_iife_5_off: { + options = { + conditionals: true, + expression: true, + inline: true, + negate_iife: false, + sequences: true, + }; + input: { + if ((function(){ return t })()) { + foo(true); + } else { + bar(false); + } + (function(){ + console.log("something"); + })(); + } + expect: { + t ? foo(true) : bar(false), console.log("something"), void 0; + } +} + +issue_1254_negate_iife_true: { + options = { + expression: true, + inline: true, + negate_iife: true, + } + input: { + (function() { + return function() { + console.log('test') + }; + })()(); + } + expect_exact: 'console.log("test"),void 0;' + expect_stdout: true +} + +issue_1254_negate_iife_nested: { + options = { + expression: true, + inline: true, + negate_iife: true, + } + input: { + (function() { + return function() { + console.log('test') + }; + })()()()()(); + } + expect_exact: '(console.log("test"),void 0)()()();' +} + +negate_iife_issue_1073: { + options = { + conditionals: true, + evaluate: true, + inline: true, + negate_iife: true, + reduce_vars: true, + sequences: true, + unused: true, + }; + input: { + new (function(a) { + return function Foo() { + this.x = a; + console.log(this); + }; + }(7))(); + } + expect: { + new function() { + this.x = 7, + console.log(this); + }(); + } + expect_stdout: true +} + +issue_1288_side_effects: { + options = { + conditionals: true, + evaluate: true, + inline: true, + negate_iife: true, + reduce_vars: true, + side_effects: true, + unused: true, + }; + input: { + if (w) ; + else { + (function f() {})(); + } + if (!x) { + (function() { + x = {}; + })(); + } + if (y) + (function() {})(); + else + (function(z) { + return z; + })(0); + } + expect: { + w; + x || (x = {}); + y; + } +} + +inner_var_for_in_1: { + options = { + evaluate: true, + inline: true, + reduce_vars: true, + } + input: { + function f() { + var a = 1, b = 2; + for (b in (function() { + return x(a, b, c); + })()) { + var c = 3, d = 4; + x(a, b, c, d); + } + x(a, b, c, d); + } + } + expect: { + function f() { + var a = 1, b = 2; + for (b in x(1, b, c)) { + var c = 3, d = 4; + x(1, b, c, d); + } + x(1, b, c, d); + } + } +} + +issue_1595_3: { + options = { + evaluate: true, + inline: true, + passes: 2, + reduce_vars: true, + unused: true, + } + input: { + (function f(a) { + return g(a + 1); + })(2); + } + expect: { + g(3); + } +} + +issue_1758: { + options = { + inline: true, + sequences: true, + side_effects: true, + } + input: { + console.log(function(c) { + var undefined = 42; + return function() { + c--; + c--, c.toString(); + return; + }(); + }()); + } + expect: { + console.log(function(c) { + var undefined = 42; + return c--, c--, void c.toString(); + }()); + } + expect_stdout: "undefined" +} +wrap_iife: { + options = { + inline: true, + negate_iife: false, + } + beautify = { + wrap_iife: true, + } + input: { + (function() { + return function() { + console.log('test') + }; + })()(); + } + expect_exact: 'console.log("test"),void 0;' +} + +wrap_iife_in_expression: { + options = { + inline: true, + negate_iife: false, + } + beautify = { + wrap_iife: true, + } + input: { + foo = (function () { + return bar(); + })(); + } + expect_exact: 'foo=bar();' +} + +wrap_iife_in_return_call: { + options = { + inline: true, + negate_iife: false, + } + beautify = { + wrap_iife: true, + } + input: { + (function() { + return (function() { + console.log('test') + })(); + })()(); + } + expect_exact: '(console.log("test"),void 0)();' +} + +pure_annotation: { + options = { + inline: true, + side_effects: true, + } + input: { + /*@__PURE__*/(function() { + console.log("hello"); + }()); + } + expect_exact: "" +} diff --git a/test/compress/negate-iife.js b/test/compress/negate-iife.js index 514a15c7..66d24270 100644 --- a/test/compress/negate-iife.js +++ b/test/compress/negate-iife.js @@ -22,7 +22,8 @@ negate_iife_1_off: { negate_iife_2: { options = { - negate_iife: true + inline: true, + negate_iife: true, }; input: { (function(){ return {} })().x = 10; @@ -32,6 +33,7 @@ negate_iife_2: { negate_iife_2_side_effects: { options = { + inline: true, negate_iife: true, side_effects: true, } @@ -58,6 +60,7 @@ negate_iife_3_evaluate: { options = { conditionals: true, evaluate: true, + inline: true, negate_iife: true, } input: { @@ -100,6 +103,7 @@ negate_iife_3_off_evaluate: { options = { conditionals: true, evaluate: true, + inline: true, negate_iife: false, } input: { diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index 078de82d..cef29832 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -2,6 +2,7 @@ reduce_vars: { options = { conditionals : true, evaluate : true, + inline : true, global_defs : { C : 0 }, @@ -1032,6 +1033,7 @@ defun_inline_2: { defun_inline_3: { options = { evaluate: true, + inline: true, passes: 2, reduce_vars: true, side_effects: true, @@ -1054,6 +1056,7 @@ defun_inline_3: { defun_call: { options = { + inline: true, reduce_vars: true, unused: true, } @@ -1080,6 +1083,7 @@ defun_call: { defun_redefine: { options = { + inline: true, reduce_vars: true, unused: true, } @@ -1112,6 +1116,7 @@ defun_redefine: { func_inline: { options = { + inline: true, reduce_vars: true, unused: true, } @@ -1138,6 +1143,7 @@ func_inline: { func_modified: { options = { + inline: true, reduce_vars: true, unused: true, } @@ -1340,10 +1346,9 @@ iife_func_side_effects: { console.log("z"); } (function(a, b, c) { - function y() { + return function() { console.log("FAIL"); - } - return y + b(); + } + b(); })(x(), function() { return y(); }, z()); @@ -1716,6 +1721,7 @@ redefine_arguments_1: { redefine_arguments_2: { options = { evaluate: true, + inline: true, keep_fargs: false, reduce_vars: true, side_effects: true, @@ -1752,6 +1758,7 @@ redefine_arguments_2: { redefine_arguments_3: { options = { evaluate: true, + inline: true, keep_fargs: false, passes: 3, reduce_vars: true, @@ -1828,6 +1835,7 @@ redefine_farg_1: { redefine_farg_2: { options = { evaluate: true, + inline: true, keep_fargs: false, reduce_vars: true, side_effects: true, @@ -1864,6 +1872,7 @@ redefine_farg_2: { redefine_farg_3: { options = { evaluate: true, + inline: true, keep_fargs: false, passes: 3, reduce_vars: true, diff --git a/test/mocha/glob.js b/test/mocha/glob.js index 56d3f82a..8567ecb3 100644 --- a/test/mocha/glob.js +++ b/test/mocha/glob.js @@ -31,7 +31,7 @@ describe("bin/uglifyjs with input file globs", function() { exec(command, function(err, stdout) { if (err) throw err; - assert.strictEqual(stdout, 'var print=console.log.bind(console),a=function(n){return 3*n}(3),b=function(n){return n/2}(12);print("qux",a,b),function(n){print("Foo:",2*n)}(11);\n'); + assert.strictEqual(stdout, 'var print=console.log.bind(console);print("qux",9,6),print("Foo:",2*11);\n'); done(); }); }); diff --git a/test/mocha/minify.js b/test/mocha/minify.js index 77b798ec..519b725c 100644 --- a/test/mocha/minify.js +++ b/test/mocha/minify.js @@ -106,7 +106,7 @@ describe("minify", function() { content: "inline" } }); - assert.strictEqual(result.code, "var bar=function(){function foo(bar){return bar}return foo}();"); + assert.strictEqual(result.code, "var bar=function(){return function(bar){return bar}}();"); assert.strictEqual(warnings.length, 1); assert.strictEqual(warnings[0], "inline source map not found"); } finally { |