aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/compress.js142
-rw-r--r--lib/output.js25
-rw-r--r--lib/utils.js23
-rw-r--r--test/compress/negate-iife.js148
-rw-r--r--test/compress/sequences.js38
5 files changed, 277 insertions, 99 deletions
diff --git a/lib/compress.js b/lib/compress.js
index 536b7518..b66c5582 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -278,6 +278,15 @@ merge(Compressor.prototype, {
return x;
};
+ var readOnlyPrefix = makePredicate("! ~ + - void typeof");
+ function statement_to_expression(stat) {
+ if (stat.body instanceof AST_UnaryPrefix && readOnlyPrefix(stat.body.operator)) {
+ return stat.body.expression;
+ } else {
+ return stat.body;
+ }
+ }
+
function tighten_body(statements, compressor) {
var CHANGED, max_iter = 10;
do {
@@ -303,10 +312,6 @@ merge(Compressor.prototype, {
}
} while (CHANGED && max_iter-- > 0);
- if (compressor.option("negate_iife")) {
- negate_iifes(statements, compressor);
- }
-
return statements;
function collapse_single_use_vars(statements, compressor) {
@@ -753,7 +758,7 @@ merge(Compressor.prototype, {
if (seqLength(seq) >= compressor.sequences_limit) {
push_seq();
}
- seq.push(stat.body);
+ seq.push(seq.length > 0 ? statement_to_expression(stat) : stat.body);
} else {
push_seq();
ret.push(stat);
@@ -802,7 +807,7 @@ merge(Compressor.prototype, {
stat.init = cons_seq(stat.init);
}
else if (!stat.init) {
- stat.init = prev.body;
+ stat.init = statement_to_expression(prev);
ret.pop();
}
} catch(ex) {
@@ -859,50 +864,6 @@ merge(Compressor.prototype, {
}, []);
};
- function negate_iifes(statements, compressor) {
- function is_iife_call(node) {
- if (node instanceof AST_Call) {
- return node.expression instanceof AST_Function || is_iife_call(node.expression);
- }
- return false;
- }
-
- 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_New) {
- return node;
- }
- if (is_iife_call(node)) {
- 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_functions_from_statement_array(statements) {
@@ -1007,7 +968,15 @@ merge(Compressor.prototype, {
return ast1.print_to_string().length >
ast2.print_to_string().length
? ast2 : ast1;
- };
+ }
+
+ function best_of_statement(ast1, ast2) {
+ return best_of(make_node(AST_SimpleStatement, ast1, {
+ body: ast1
+ }), make_node(AST_SimpleStatement, ast2, {
+ body: ast2
+ })).body;
+ }
// methods to evaluate a constant expression
(function (def){
@@ -1227,7 +1196,17 @@ merge(Compressor.prototype, {
operator: "!",
expression: exp
});
- };
+ }
+ function best(orig, alt, first_in_statement) {
+ var negated = basic_negation(orig);
+ if (first_in_statement) {
+ var stat = make_node(AST_SimpleStatement, alt, {
+ body: alt
+ });
+ return best_of(negated, stat) === stat ? alt : negated;
+ }
+ return best_of(negated, alt);
+ }
def(AST_Node, function(){
return basic_negation(this);
});
@@ -1247,13 +1226,13 @@ merge(Compressor.prototype, {
self.cdr = self.cdr.negate(compressor);
return self;
});
- def(AST_Conditional, function(compressor){
+ def(AST_Conditional, function(compressor, first_in_statement){
var self = this.clone();
self.consequent = self.consequent.negate(compressor);
self.alternative = self.alternative.negate(compressor);
- return best_of(basic_negation(this), self);
+ return best(this, self, first_in_statement);
});
- def(AST_Binary, function(compressor){
+ def(AST_Binary, function(compressor, first_in_statement){
var self = this.clone(), op = this.operator;
if (compressor.option("unsafe_comps")) {
switch (op) {
@@ -1270,20 +1249,20 @@ merge(Compressor.prototype, {
case "!==": self.operator = "==="; return self;
case "&&":
self.operator = "||";
- self.left = self.left.negate(compressor);
+ self.left = self.left.negate(compressor, first_in_statement);
self.right = self.right.negate(compressor);
- return best_of(basic_negation(this), self);
+ return best(this, self, first_in_statement);
case "||":
self.operator = "&&";
- self.left = self.left.negate(compressor);
+ self.left = self.left.negate(compressor, first_in_statement);
self.right = self.right.negate(compressor);
- return best_of(basic_negation(this), self);
+ return best(this, self, first_in_statement);
}
return basic_negation(this);
});
})(function(node, func){
- node.DEFMETHOD("negate", function(compressor){
- return func.call(this, compressor);
+ node.DEFMETHOD("negate", function(compressor, first_in_statement){
+ return func.call(this, compressor, first_in_statement);
});
});
@@ -1954,8 +1933,8 @@ merge(Compressor.prototype, {
return make_node(AST_SimpleStatement, self, {
body: make_node(AST_Conditional, self, {
condition : self.condition,
- consequent : self.body.body,
- alternative : self.alternative.body
+ consequent : statement_to_expression(self.body),
+ alternative : statement_to_expression(self.alternative)
})
}).transform(compressor);
}
@@ -1971,14 +1950,14 @@ merge(Compressor.prototype, {
body: make_node(AST_Binary, self, {
operator : "||",
left : negated,
- right : self.body.body
+ right : statement_to_expression(self.body)
})
}).transform(compressor);
return make_node(AST_SimpleStatement, self, {
body: make_node(AST_Binary, self, {
operator : "&&",
left : self.condition,
- right : self.body.body
+ right : statement_to_expression(self.body)
})
}).transform(compressor);
}
@@ -1989,7 +1968,7 @@ merge(Compressor.prototype, {
body: make_node(AST_Binary, self, {
operator : "||",
left : self.condition,
- right : self.alternative.body
+ right : statement_to_expression(self.alternative)
})
}).transform(compressor);
}
@@ -2372,7 +2351,19 @@ merge(Compressor.prototype, {
}
}
}
- return self.evaluate(compressor)[0];
+ if (compressor.option("negate_iife")
+ && compressor.parent() instanceof AST_SimpleStatement
+ && is_iife_call(self)) {
+ return self.negate(compressor, true);
+ }
+ return self;
+
+ function is_iife_call(node) {
+ if (node instanceof AST_Call && !(node instanceof AST_New)) {
+ return node.expression instanceof AST_Function || is_iife_call(node.expression);
+ }
+ return false;
+ }
});
OPT(AST_New, function(self, compressor){
@@ -2459,6 +2450,10 @@ merge(Compressor.prototype, {
// !!foo ==> foo, if we're in boolean context
return e.expression;
}
+ if (e instanceof AST_Binary) {
+ var statement = first_in_statement(compressor);
+ self = (statement ? best_of_statement : best_of)(self, e.negate(compressor, statement));
+ }
break;
case "typeof":
// typeof always returns a non-empty string, thus it's
@@ -2472,9 +2467,6 @@ merge(Compressor.prototype, {
}
return make_node(AST_True, self);
}
- if (e instanceof AST_Binary && self.operator == "!") {
- self = best_of(self, e.negate(compressor));
- }
}
return self.evaluate(compressor)[0];
});
@@ -2651,11 +2643,12 @@ merge(Compressor.prototype, {
if (compressor.option("comparisons") && self.is_boolean()) {
if (!(compressor.parent() instanceof AST_Binary)
|| compressor.parent() instanceof AST_Assign) {
+ var statement = first_in_statement(compressor);
var negated = make_node(AST_UnaryPrefix, self, {
operator: "!",
- expression: self.negate(compressor)
+ expression: self.negate(compressor, statement)
});
- self = best_of(self, negated);
+ self = (statement ? best_of_statement : best_of)(self, negated);
}
if (compressor.option("unsafe_comps")) {
switch (self.operator) {
@@ -2859,8 +2852,9 @@ merge(Compressor.prototype, {
return maintain_this_binding(compressor.parent(), self, self.alternative);
}
}
- var negated = cond[0].negate(compressor);
- if (best_of(cond[0], negated) === negated) {
+ var statement = first_in_statement(compressor);
+ var negated = cond[0].negate(compressor, statement);
+ if ((statement ? best_of_statement : best_of)(cond[0], negated) === negated) {
self = make_node(AST_Conditional, self, {
condition: negated,
consequent: self.alternative,
diff --git a/lib/output.js b/lib/output.js
index 50e5aa43..b6f0a873 100644
--- a/lib/output.js
+++ b/lib/output.js
@@ -425,7 +425,6 @@ function OutputStream(options) {
pos : function() { return current_pos },
push_node : function(node) { stack.push(node) },
pop_node : function() { return stack.pop() },
- stack : function() { return stack },
parent : function(n) {
return stack[stack.length - 2 - (n || 0)];
}
@@ -1334,30 +1333,6 @@ function OutputStream(options) {
}
};
- // return true if the node at the top of the stack (that means the
- // innermost node in the current output) is lexically the first in
- // a statement.
- function first_in_statement(output) {
- var a = output.stack(), i = a.length, node = a[--i], p = a[--i];
- while (i > 0) {
- if (p instanceof AST_Statement && p.body === node)
- return true;
- if ((p instanceof AST_Seq && p.car === node ) ||
- (p instanceof AST_Call && p.expression === node && !(p instanceof AST_New) ) ||
- (p instanceof AST_Dot && p.expression === node ) ||
- (p instanceof AST_Sub && p.expression === node ) ||
- (p instanceof AST_Conditional && p.condition === node ) ||
- (p instanceof AST_Binary && p.left === node ) ||
- (p instanceof AST_UnaryPostfix && p.expression === node ))
- {
- node = p;
- p = a[--i];
- } else {
- return false;
- }
- }
- };
-
// self should be AST_New. decide if we want to show parens or not.
function need_constructor_parens(self, output) {
// Always print parentheses with arguments
diff --git a/lib/utils.js b/lib/utils.js
index d0a21ac9..a0571d65 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -320,3 +320,26 @@ Dictionary.fromObject = function(obj) {
function HOP(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
+
+// return true if the node at the top of the stack (that means the
+// innermost node in the current output) is lexically the first in
+// a statement.
+function first_in_statement(stack) {
+ var node = stack.parent(-1);
+ for (var i = 0, p; p = stack.parent(i); i++) {
+ if (p instanceof AST_Statement && p.body === node)
+ return true;
+ if ((p instanceof AST_Seq && p.car === node ) ||
+ (p instanceof AST_Call && p.expression === node && !(p instanceof AST_New) ) ||
+ (p instanceof AST_Dot && p.expression === node ) ||
+ (p instanceof AST_Sub && p.expression === node ) ||
+ (p instanceof AST_Conditional && p.condition === node ) ||
+ (p instanceof AST_Binary && p.left === node ) ||
+ (p instanceof AST_UnaryPostfix && p.expression === node ))
+ {
+ node = p;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/test/compress/negate-iife.js b/test/compress/negate-iife.js
index 0c111604..312e0f28 100644
--- a/test/compress/negate-iife.js
+++ b/test/compress/negate-iife.js
@@ -10,6 +10,16 @@ negate_iife_1: {
}
}
+negate_iife_1_off: {
+ options = {
+ negate_iife: false,
+ };
+ input: {
+ (function(){ stuff() })();
+ }
+ expect_exact: '(function(){stuff()})();'
+}
+
negate_iife_2: {
options = {
negate_iife: true
@@ -25,6 +35,20 @@ negate_iife_2: {
negate_iife_3: {
options = {
negate_iife: true,
+ conditionals: true
+ };
+ input: {
+ (function(){ return true })() ? console.log(true) : console.log(false);
+ }
+ expect: {
+ !function(){ return true }() ? console.log(false) : console.log(true);
+ }
+}
+
+negate_iife_3_off: {
+ options = {
+ negate_iife: false,
+ conditionals: true,
};
input: {
(function(){ return true })() ? console.log(true) : console.log(false);
@@ -37,6 +61,7 @@ negate_iife_3: {
negate_iife_3: {
options = {
negate_iife: true,
+ conditionals: true,
sequences: true
};
input: {
@@ -52,6 +77,41 @@ negate_iife_3: {
}
}
+sequence_off: {
+ options = {
+ negate_iife: false,
+ conditionals: true,
+ sequences: true,
+ passes: 2,
+ };
+ input: {
+ function f() {
+ (function(){ return true })() ? console.log(true) : console.log(false);
+ (function(){
+ console.log("something");
+ })();
+ }
+ function g() {
+ (function(){
+ console.log("something");
+ })();
+ (function(){ return true })() ? console.log(true) : console.log(false);
+ }
+ }
+ expect: {
+ function f() {
+ !function(){ return true }() ? console.log(false) : console.log(true), function(){
+ console.log("something");
+ }();
+ }
+ function g() {
+ (function(){
+ console.log("something");
+ })(), function(){ return true }() ? console.log(true) : console.log(false);
+ }
+ }
+}
+
negate_iife_4: {
options = {
negate_iife: true,
@@ -75,6 +135,29 @@ negate_iife_4: {
}
}
+negate_iife_4_off: {
+ options = {
+ negate_iife: false,
+ sequences: true,
+ conditionals: true,
+ };
+ input: {
+ if ((function(){ return true })()) {
+ foo(true);
+ } else {
+ bar(false);
+ }
+ (function(){
+ console.log("something");
+ })();
+ }
+ expect: {
+ !function(){ return true }() ? bar(false) : foo(true), function(){
+ console.log("something");
+ }();
+ }
+}
+
negate_iife_nested: {
options = {
negate_iife: true,
@@ -107,6 +190,38 @@ negate_iife_nested: {
}
}
+negate_iife_nested_off: {
+ options = {
+ negate_iife: false,
+ sequences: true,
+ conditionals: true,
+ };
+ input: {
+ function Foo(f) {
+ this.f = f;
+ }
+ new Foo(function() {
+ (function(x) {
+ (function(y) {
+ console.log(y);
+ })(x);
+ })(7);
+ }).f();
+ }
+ expect: {
+ function Foo(f) {
+ this.f = f;
+ }
+ new Foo(function() {
+ (function(x) {
+ (function(y) {
+ console.log(y);
+ })(x);
+ })(7);
+ }).f();
+ }
+}
+
negate_iife_issue_1073: {
options = {
negate_iife: true,
@@ -172,3 +287,36 @@ issue_1254_negate_iife_nested: {
}
expect_exact: '!function(){return function(){console.log("test")}}()()()()();'
}
+
+issue_1288: {
+ options = {
+ negate_iife: true,
+ conditionals: true,
+ };
+ input: {
+ if (w) ;
+ else {
+ (function f() {})();
+ }
+ if (!x) {
+ (function() {
+ x = {};
+ })();
+ }
+ if (y)
+ (function() {})();
+ else
+ (function(z) {
+ return z;
+ })(0);
+ }
+ expect: {
+ w || function f() {}();
+ x || function() {
+ x = {};
+ }();
+ y ? function() {}() : function(z) {
+ return z;
+ }(0);
+ }
+}
diff --git a/test/compress/sequences.js b/test/compress/sequences.js
index 8a3ffe89..d93f5237 100644
--- a/test/compress/sequences.js
+++ b/test/compress/sequences.js
@@ -213,3 +213,41 @@ limit_2: {
i, j, k;
}
}
+
+negate_iife_for: {
+ options = {
+ sequences: true,
+ negate_iife: true,
+ };
+ input: {
+ (function() {})();
+ for (i = 0; i < 5; i++) console.log(i);
+
+ (function() {})();
+ for (; i < 5; i++) console.log(i);
+ }
+ expect: {
+ for (!function() {}(), i = 0; i < 5; i++) console.log(i);
+ for (function() {}(); i < 5; i++) console.log(i);
+ }
+}
+
+iife: {
+ options = {
+ sequences: true,
+ };
+ input: {
+ x = 42;
+ (function a() {})();
+ !function b() {}();
+ ~function c() {}();
+ +function d() {}();
+ -function e() {}();
+ void function f() {}();
+ typeof function g() {}();
+ }
+ expect: {
+ x = 42, function a() {}(), function b() {}(), function c() {}(),
+ function d() {}(), function e() {}(), function f() {}(), function g() {}()
+ }
+}