From ed80b4a534083510082419b305e0b60b395b10c6 Mon Sep 17 00:00:00 2001 From: Mihai Bazon Date: Tue, 20 Aug 2013 17:45:52 +0300 Subject: [PATCH] Move support for `negate_iife` in the compressor, rather than code generator (the code generator doesn't maintain enough context to know whether the return value is important or discarded) Fixes #272 --- README.md | 7 ++-- lib/compress.js | 40 +++++++++++++++++++ lib/output.js | 19 ++++----- test/compress/negate-iife.js | 76 ++++++++++++++++++++++++++++++++++++ 4 files changed, 126 insertions(+), 16 deletions(-) create mode 100644 test/compress/negate-iife.js diff --git a/README.md b/README.md index b735eb6e..ad394b25 100644 --- a/README.md +++ b/README.md @@ -209,6 +209,9 @@ to set `true`; it's effectively a shortcut for `foo=true`). and `x = something(), x` into `x = something()` - `warnings` -- display warnings when dropping unreachable code or unused declarations etc. +- `negate_iife` -- negate "Immediately-Called Function Expressions" + where the return value is discarded, to avoid the parens that the + code generator would insert. ### The `unsafe` option @@ -296,10 +299,6 @@ can pass additional arguments that control the code output: you pass `false` then whenever possible we will use a newline instead of a semicolon, leading to more readable output of uglified code (size before gzip could be smaller; size after gzip insignificantly larger). -- `negate-iife` (default `!beautify`) -- prefer negation, rather than - parens, for "Immediately-Called Function Expressions". This defaults to - `true` when beautification is off, and `false` if beautification is on; - pass it manually to force a value. ### Keeping copyright notices or other comments diff --git a/lib/compress.js b/lib/compress.js index 8dfbc72b..8d936bac 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -66,6 +66,7 @@ function Compressor(options, false_by_default) { join_vars : !false_by_default, cascade : !false_by_default, side_effects : !false_by_default, + negate_iife : !false_by_default, screw_ie8 : false, warnings : true, @@ -214,6 +215,11 @@ merge(Compressor.prototype, { statements = join_consecutive_vars(statements, compressor); } } while (CHANGED); + + if (compressor.option("negate_iife")) { + negate_iifes(statements, compressor); + } + return statements; function eliminate_spurious_blocks(statements) { @@ -497,6 +503,40 @@ merge(Compressor.prototype, { }, []); }; + function negate_iifes(statements, compressor) { + statements.forEach(function(stat){ + if (stat instanceof AST_SimpleStatement) { + stat.body = (function transform(thing) { + return thing.transform(new TreeTransformer(function(node){ + if (node instanceof AST_Call && node.expression instanceof AST_Function) { + return make_node(AST_UnaryPrefix, node, { + operator: "!", + expression: node + }); + } + else if (node instanceof AST_Call) { + node.expression = transform(node.expression); + } + else if (node instanceof AST_Seq) { + node.car = transform(node.car); + } + else if (node instanceof AST_Conditional) { + var expr = transform(node.condition); + if (expr !== node.condition) { + // it has been negated, reverse + node.condition = expr; + var tmp = node.consequent; + node.consequent = node.alternative; + node.alternative = tmp; + } + } + return node; + })); + })(stat.body); + } + }); + }; + }; function extract_declarations_from_unreachable_code(compressor, stat, target) { diff --git a/lib/output.js b/lib/output.js index 6d0dac5a..b7bcd1e3 100644 --- a/lib/output.js +++ b/lib/output.js @@ -61,7 +61,6 @@ function OutputStream(options) { comments : false, preserve_line : false, screw_ie8 : false, - negate_iife : !(options && options.beautify), }, true); var indentation = 0; @@ -351,21 +350,17 @@ function OutputStream(options) { AST_Node.DEFMETHOD("print", function(stream, force_parens){ var self = this, generator = self._codegen; - stream.push_node(self); - var needs_parens = self.needs_parens(stream); - var fc = self instanceof AST_Function && stream.option("negate_iife"); - if (force_parens || (needs_parens && !fc)) { - stream.with_parens(function(){ - self.add_comments(stream); - self.add_source_map(stream); - generator(self, stream); - }); - } else { + function doit() { self.add_comments(stream); - if (needs_parens && fc) stream.print("!"); self.add_source_map(stream); generator(self, stream); } + stream.push_node(self); + if (force_parens || self.needs_parens(stream)) { + stream.with_parens(doit); + } else { + doit(); + } stream.pop_node(); }); diff --git a/test/compress/negate-iife.js b/test/compress/negate-iife.js new file mode 100644 index 00000000..0362ffce --- /dev/null +++ b/test/compress/negate-iife.js @@ -0,0 +1,76 @@ +negate_iife_1: { + options = { + negate_iife: true + }; + input: { + (function(){ stuff() })(); + } + expect: { + !function(){ stuff() }(); + } +} + +negate_iife_2: { + options = { + negate_iife: true + }; + input: { + (function(){ return {} })().x = 10; // should not transform this one + } + expect: { + (function(){ return {} })().x = 10; + } +} + +negate_iife_3: { + options = { + negate_iife: true, + }; + input: { + (function(){ return true })() ? console.log(true) : console.log(false); + } + expect: { + !function(){ return true }() ? console.log(false) : console.log(true); + } +} + +negate_iife_3: { + options = { + negate_iife: true, + sequences: true + }; + input: { + (function(){ return true })() ? console.log(true) : console.log(false); + (function(){ + console.log("something"); + })(); + } + expect: { + !function(){ return true }() ? console.log(false) : console.log(true), function(){ + console.log("something"); + }(); + } +} + +negate_iife_4: { + options = { + negate_iife: true, + sequences: true, + conditionals: true, + }; + input: { + if ((function(){ return true })()) { + console.log(true); + } else { + console.log(false); + } + (function(){ + console.log("something"); + })(); + } + expect: { + !function(){ return true }() ? console.log(false) : console.log(true), function(){ + console.log("something"); + }(); + } +} -- 2.34.1