aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md3
-rw-r--r--lib/compress.js75
-rw-r--r--lib/output.js28
-rw-r--r--test/compress/conditionals.js8
-rw-r--r--test/compress/evaluate.js8
-rw-r--r--test/compress/issue-1105.js68
-rw-r--r--test/compress/issue-597.js32
-rw-r--r--test/compress/numbers.js2
-rw-r--r--test/compress/properties.js2
9 files changed, 170 insertions, 56 deletions
diff --git a/README.md b/README.md
index 2399e23f..d57a15ce 100644
--- a/README.md
+++ b/README.md
@@ -443,6 +443,9 @@ to set `true`; it's effectively a shortcut for `foo=true`).
integer argument larger than 1 to further reduce code size in some cases.
Note: raising the number of passes will increase uglify compress time.
+- `keep_infinity` -- default `false`. Pass `true` to prevent `Infinity` from
+ being compressed into `1/0`, which may cause performance issues on Chrome.
+
### The `unsafe` option
It enables some transformations that *might* break code logic in certain
diff --git a/lib/compress.js b/lib/compress.js
index a2332a9c..1d9cd401 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -66,6 +66,7 @@ function Compressor(options, false_by_default) {
join_vars : !false_by_default,
keep_fargs : true,
keep_fnames : false,
+ keep_infinity : false,
loops : !false_by_default,
negate_iife : !false_by_default,
passes : 1,
@@ -215,7 +216,12 @@ merge(Compressor.prototype, {
}) : make_node(AST_EmptyStatement, node);
}
return make_node(AST_SimpleStatement, node, {
- body: node.value || make_node(AST_Undefined, node)
+ body: node.value || make_node(AST_UnaryPrefix, node, {
+ operator: "void",
+ expression: make_node(AST_Number, node, {
+ value: 0
+ })
+ })
});
}
if (node instanceof AST_Lambda && node !== self) {
@@ -1123,8 +1129,12 @@ merge(Compressor.prototype, {
}));
};
- function is_undefined(node) {
- return node instanceof AST_Undefined || node.is_undefined;
+ function is_undefined(node, compressor) {
+ return node.is_undefined
+ || node instanceof AST_Undefined
+ || node instanceof AST_UnaryPrefix
+ && node.operator == "void"
+ && !node.expression.has_side_effects(compressor);
}
/* -----[ boolean/negation helpers ]----- */
@@ -1313,7 +1323,7 @@ merge(Compressor.prototype, {
return this;
}
});
- var unaryPrefix = makePredicate("! ~ - +");
+ var unaryPrefix = makePredicate("! ~ - + void");
AST_Node.DEFMETHOD("is_constant", function(){
// Accomodate when compress option evaluate=false
// as well as the common constant expressions !0 and -1
@@ -2971,7 +2981,7 @@ merge(Compressor.prototype, {
}
}
}
- if (is_undefined(self.cdr)) {
+ if (is_undefined(self.cdr, compressor)) {
return make_node(AST_UnaryPrefix, self, {
operator : "void",
expression : self.car
@@ -3010,7 +3020,7 @@ merge(Compressor.prototype, {
self.expression = e;
return self;
} else {
- return make_node(AST_Undefined, self).transform(compressor);
+ return make_node(AST_Undefined, self).optimize(compressor);
}
}
if (compressor.option("booleans") && compressor.in_boolean_context()) {
@@ -3034,6 +3044,9 @@ merge(Compressor.prototype, {
})).optimize(compressor);
}
}
+ if (self.operator == "-" && e instanceof AST_Infinity) {
+ e = e.transform(compressor);
+ }
if (e instanceof AST_Binary
&& (self.operator == "+" || self.operator == "-")
&& (e.operator == "*" || e.operator == "/" || e.operator == "%")) {
@@ -3043,8 +3056,7 @@ merge(Compressor.prototype, {
}
// avoids infinite recursion of numerals
if (self.operator != "-"
- || !(self.expression instanceof AST_Number
- || self.expression instanceof AST_Infinity)) {
+ || !(e instanceof AST_Number || e instanceof AST_Infinity)) {
var ev = self.evaluate(compressor);
if (ev !== self) {
ev = make_node_from_constant(ev, self).optimize(compressor);
@@ -3087,8 +3099,8 @@ merge(Compressor.prototype, {
OPT(AST_Binary, function(self, compressor){
function reversible() {
- return self.left instanceof AST_Constant
- || self.right instanceof AST_Constant
+ return self.left.is_constant()
+ || self.right.is_constant()
|| !self.left.has_side_effects(compressor)
&& !self.right.has_side_effects(compressor);
}
@@ -3101,8 +3113,8 @@ merge(Compressor.prototype, {
}
}
if (commutativeOperators(self.operator)) {
- if (self.right instanceof AST_Constant
- && !(self.left instanceof AST_Constant)) {
+ if (self.right.is_constant()
+ && !self.left.is_constant()) {
// if right is a constant, whatever side effects the
// left side might have could not influence the
// result. hence, force switch.
@@ -3464,9 +3476,9 @@ merge(Compressor.prototype, {
case "undefined":
return make_node(AST_Undefined, self).optimize(compressor);
case "NaN":
- return make_node(AST_NaN, self);
+ return make_node(AST_NaN, self).optimize(compressor);
case "Infinity":
- return make_node(AST_Infinity, self);
+ return make_node(AST_Infinity, self).optimize(compressor);
}
}
if (compressor.option("evaluate") && compressor.option("reduce_vars")) {
@@ -3508,7 +3520,38 @@ merge(Compressor.prototype, {
return ref;
}
}
- return self;
+ return make_node(AST_UnaryPrefix, self, {
+ operator: "void",
+ expression: make_node(AST_Number, self, {
+ value: 0
+ })
+ });
+ });
+
+ OPT(AST_Infinity, function(self, compressor){
+ var retain = compressor.option("keep_infinity")
+ && !compressor.find_parent(AST_Scope).find_variable("Infinity");
+ return retain ? self : make_node(AST_Binary, self, {
+ operator: "/",
+ left: make_node(AST_Number, self, {
+ value: 1
+ }),
+ right: make_node(AST_Number, self, {
+ value: 0
+ })
+ });
+ });
+
+ OPT(AST_NaN, function(self, compressor){
+ return compressor.find_parent(AST_Scope).find_variable("NaN") ? make_node(AST_Binary, self, {
+ operator: "/",
+ left: make_node(AST_Number, self, {
+ value: 0
+ }),
+ right: make_node(AST_Number, self, {
+ value: 0
+ })
+ }) : self;
});
var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ];
@@ -3809,7 +3852,7 @@ merge(Compressor.prototype, {
OPT(AST_RegExp, literals_in_boolean_context);
OPT(AST_Return, function(self, compressor){
- if (self.value && is_undefined(self.value)) {
+ if (self.value && is_undefined(self.value, compressor)) {
self.value = null;
}
return self;
diff --git a/lib/output.js b/lib/output.js
index d5c7304a..d0adbddf 100644
--- a/lib/output.js
+++ b/lib/output.js
@@ -586,21 +586,12 @@ function OutputStream(options) {
return first_in_statement(output);
});
- PARENS([ AST_Unary, AST_Undefined ], function(output){
+ PARENS(AST_Unary, function(output){
var p = output.parent();
return p instanceof AST_PropAccess && p.expression === this
|| p instanceof AST_Call && p.expression === this;
});
- PARENS([ AST_Infinity, AST_NaN ], function(output){
- var p = output.parent();
- return p instanceof AST_PropAccess && p.expression === this
- || p instanceof AST_Call && p.expression === this
- || p instanceof AST_Unary && p.operator != "+" && p.operator != "-"
- || p instanceof AST_Binary && p.right === this
- && (p.operator == "/" || p.operator == "%");
- });
-
PARENS(AST_Seq, function(output){
var p = output.parent();
return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4)
@@ -1258,24 +1249,7 @@ function OutputStream(options) {
var def = self.definition();
output.print_name(def ? def.mangled_name || def.name : self.name);
});
- DEFPRINT(AST_Undefined, function(self, output){
- output.print("void 0");
- });
DEFPRINT(AST_Hole, noop);
- DEFPRINT(AST_Infinity, function(self, output){
- output.print("1");
- output.space();
- output.print("/");
- output.space();
- output.print("0");
- });
- DEFPRINT(AST_NaN, function(self, output){
- output.print("0");
- output.space();
- output.print("/");
- output.space();
- output.print("0");
- });
DEFPRINT(AST_This, function(self, output){
output.print("this");
});
diff --git a/test/compress/conditionals.js b/test/compress/conditionals.js
index 54d4264d..e7ea2bb2 100644
--- a/test/compress/conditionals.js
+++ b/test/compress/conditionals.js
@@ -840,8 +840,8 @@ equality_conditionals_false: {
f(0, true, 0),
f(1, 2, 3),
f(1, null, 3),
- f(0/0),
- f(0/0, "foo");
+ f(NaN),
+ f(NaN, "foo");
}
expect_stdout: true
}
@@ -888,8 +888,8 @@ equality_conditionals_true: {
f(0, true, 0),
f(1, 2, 3),
f(1, null, 3),
- f(0/0),
- f(0/0, "foo");
+ f(NaN),
+ f(NaN, "foo");
}
expect_stdout: true
}
diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js
index 35b6b925..0d26e749 100644
--- a/test/compress/evaluate.js
+++ b/test/compress/evaluate.js
@@ -52,7 +52,7 @@ and: {
a = 7;
a = false;
- a = 0/0;
+ a = NaN;
a = 0;
a = void 0;
a = null;
@@ -67,7 +67,7 @@ and: {
a = 6 << condition && -4.5;
a = condition && false;
- a = console.log("b") && 0/0;
+ a = console.log("b") && NaN;
a = console.log("c") && 0;
a = 2 * condition && void 0;
a = condition + 3 && null;
@@ -149,7 +149,7 @@ or: {
a = 6 << condition || -4.5;
a = condition || false;
- a = console.log("b") || 0/0;
+ a = console.log("b") || NaN;
a = console.log("c") || 0;
a = 2 * condition || void 0;
a = condition + 3 || null;
@@ -533,7 +533,7 @@ unsafe_array: {
[1, 2, 3, a][0] + 1,
2,
3,
- 0/0,
+ NaN,
"1,21",
5,
(void 0)[1] + 1
diff --git a/test/compress/issue-1105.js b/test/compress/issue-1105.js
index f9412165..ea957930 100644
--- a/test/compress/issue-1105.js
+++ b/test/compress/issue-1105.js
@@ -193,6 +193,7 @@ assorted_Infinity_NaN_undefined_in_with_scope: {
cascade: true,
side_effects: true,
sequences: false,
+ keep_infinity: false,
}
input: {
var f = console.log;
@@ -224,10 +225,73 @@ assorted_Infinity_NaN_undefined_in_with_scope: {
};
if (o) {
f(void 0, void 0);
- f(0/0, 0/0);
+ f(NaN, NaN);
f(1/0, 1/0);
f(-1/0, -1/0);
- f(0/0, 0/0);
+ f(NaN, NaN);
+ }
+ with (o) {
+ f(undefined, void 0);
+ f(NaN, 0/0);
+ f(Infinity, 1/0);
+ f(-Infinity, -1/0);
+ f(9 + undefined, 9 + void 0);
+ }
+ }
+ expect_stdout: true
+}
+
+assorted_Infinity_NaN_undefined_in_with_scope_keep_infinity: {
+ options = {
+ unused: true,
+ evaluate: true,
+ dead_code: true,
+ conditionals: true,
+ comparisons: true,
+ booleans: true,
+ hoist_funs: true,
+ keep_fargs: true,
+ if_return: true,
+ join_vars: true,
+ cascade: true,
+ side_effects: true,
+ sequences: false,
+ keep_infinity: true,
+ }
+ input: {
+ var f = console.log;
+ var o = {
+ undefined : 3,
+ NaN : 4,
+ Infinity : 5,
+ };
+ if (o) {
+ f(undefined, void 0);
+ f(NaN, 0/0);
+ f(Infinity, 1/0);
+ f(-Infinity, -(1/0));
+ f(2 + 7 + undefined, 2 + 7 + void 0);
+ }
+ with (o) {
+ f(undefined, void 0);
+ f(NaN, 0/0);
+ f(Infinity, 1/0);
+ f(-Infinity, -(1/0));
+ f(2 + 7 + undefined, 2 + 7 + void 0);
+ }
+ }
+ expect: {
+ var f = console.log, o = {
+ undefined : 3,
+ NaN : 4,
+ Infinity : 5
+ };
+ if (o) {
+ f(void 0, void 0);
+ f(NaN, NaN);
+ f(Infinity, 1/0);
+ f(-Infinity, -1/0);
+ f(NaN, NaN);
}
with (o) {
f(undefined, void 0);
diff --git a/test/compress/issue-597.js b/test/compress/issue-597.js
index 987bcacc..143fcc22 100644
--- a/test/compress/issue-597.js
+++ b/test/compress/issue-597.js
@@ -6,7 +6,7 @@ NaN_and_Infinity_must_have_parens: {
}
expect: {
(1/0).toString();
- (0/0).toString();
+ NaN.toString();
}
}
@@ -24,6 +24,36 @@ NaN_and_Infinity_should_not_be_replaced_when_they_are_redefined: {
}
}
+NaN_and_Infinity_must_have_parens_evaluate: {
+ options = {
+ evaluate: true,
+ }
+ input: {
+ (123456789 / 0).toString();
+ (+"foo").toString();
+ }
+ expect: {
+ (1/0).toString();
+ NaN.toString();
+ }
+}
+
+NaN_and_Infinity_should_not_be_replaced_when_they_are_redefined_evaluate: {
+ options = {
+ evaluate: true,
+ }
+ input: {
+ var Infinity, NaN;
+ (123456789 / 0).toString();
+ (+"foo").toString();
+ }
+ expect: {
+ var Infinity, NaN;
+ (1/0).toString();
+ (0/0).toString();
+ }
+}
+
beautify_off_1: {
options = {
evaluate: true,
diff --git a/test/compress/numbers.js b/test/compress/numbers.js
index 946a7f2d..86545fba 100644
--- a/test/compress/numbers.js
+++ b/test/compress/numbers.js
@@ -186,7 +186,7 @@ unary_binary_parenthesis: {
});
}
expect: {
- var v = [ 0, 1, 0/0, 1/0, null, void 0, true, false, "", "foo", /foo/ ];
+ var v = [ 0, 1, NaN, 1/0, null, void 0, true, false, "", "foo", /foo/ ];
v.forEach(function(x) {
v.forEach(function(y) {
console.log(
diff --git a/test/compress/properties.js b/test/compress/properties.js
index 376fb9e2..29bdfe2a 100644
--- a/test/compress/properties.js
+++ b/test/compress/properties.js
@@ -77,7 +77,7 @@ sub_properties: {
a[3.14] = 3;
a.if = 4;
a["foo bar"] = 5;
- a[0/0] = 6;
+ a[NaN] = 6;
a[null] = 7;
a[void 0] = 8;
}