output optimal representations of NaN & Infinity (#1723)
- move these optimisations out from `Compressor` to `OutputStream` - fixes behaviour inconsistency when running uglified code from global or module levels due to redefinition
This commit is contained in:
@@ -413,18 +413,17 @@ merge(Compressor.prototype, {
|
|||||||
value: val
|
value: val
|
||||||
});
|
});
|
||||||
case "number":
|
case "number":
|
||||||
if (isNaN(val)) {
|
if (isNaN(val)) return make_node(AST_NaN, orig);
|
||||||
return make_node(AST_NaN, orig);
|
if (isFinite(val)) {
|
||||||
}
|
return 1 / val < 0 ? make_node(AST_UnaryPrefix, orig, {
|
||||||
|
|
||||||
if ((1 / val) < 0) {
|
|
||||||
return make_node(AST_UnaryPrefix, orig, {
|
|
||||||
operator: "-",
|
operator: "-",
|
||||||
expression: make_node(AST_Number, orig, { value: -val })
|
expression: make_node(AST_Number, orig, { value: -val })
|
||||||
});
|
}) : make_node(AST_Number, orig, { value: val });
|
||||||
}
|
}
|
||||||
|
return val < 0 ? make_node(AST_UnaryPrefix, orig, {
|
||||||
return make_node(AST_Number, orig, { value: val });
|
operator: "-",
|
||||||
|
expression: make_node(AST_Infinity, orig)
|
||||||
|
}) : make_node(AST_Infinity, orig);
|
||||||
case "boolean":
|
case "boolean":
|
||||||
return make_node(val ? AST_True : AST_False, orig);
|
return make_node(val ? AST_True : AST_False, orig);
|
||||||
case "undefined":
|
case "undefined":
|
||||||
@@ -3023,7 +3022,9 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// avoids infinite recursion of numerals
|
// avoids infinite recursion of numerals
|
||||||
if (self.operator != "-" || !(self.expression instanceof AST_Number)) {
|
if (self.operator != "-"
|
||||||
|
|| !(self.expression instanceof AST_Number
|
||||||
|
|| self.expression instanceof AST_Infinity)) {
|
||||||
var ev = self.evaluate(compressor);
|
var ev = self.evaluate(compressor);
|
||||||
if (ev !== self) {
|
if (ev !== self) {
|
||||||
ev = make_node_from_constant(ev, self).optimize(compressor);
|
ev = make_node_from_constant(ev, self).optimize(compressor);
|
||||||
@@ -3455,9 +3456,9 @@ merge(Compressor.prototype, {
|
|||||||
case "undefined":
|
case "undefined":
|
||||||
return make_node(AST_Undefined, self).optimize(compressor);
|
return make_node(AST_Undefined, self).optimize(compressor);
|
||||||
case "NaN":
|
case "NaN":
|
||||||
return make_node(AST_NaN, self).optimize(compressor);
|
return make_node(AST_NaN, self);
|
||||||
case "Infinity":
|
case "Infinity":
|
||||||
return make_node(AST_Infinity, self).optimize(compressor);
|
return make_node(AST_Infinity, self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (compressor.option("evaluate") && compressor.option("reduce_vars")) {
|
if (compressor.option("evaluate") && compressor.option("reduce_vars")) {
|
||||||
@@ -3485,14 +3486,6 @@ merge(Compressor.prototype, {
|
|||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
OPT(AST_Infinity, function (self, compressor) {
|
|
||||||
return make_node(AST_Binary, self, {
|
|
||||||
operator : '/',
|
|
||||||
left : make_node(AST_Number, self, {value: 1}),
|
|
||||||
right : make_node(AST_Number, self, {value: 0})
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
OPT(AST_Undefined, function(self, compressor){
|
OPT(AST_Undefined, function(self, compressor){
|
||||||
if (compressor.option("unsafe")) {
|
if (compressor.option("unsafe")) {
|
||||||
var scope = compressor.find_parent(AST_Scope);
|
var scope = compressor.find_parent(AST_Scope);
|
||||||
|
|||||||
@@ -592,6 +592,13 @@ function OutputStream(options) {
|
|||||||
|| p instanceof AST_Call && 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 != "-";
|
||||||
|
});
|
||||||
|
|
||||||
PARENS(AST_Seq, function(output){
|
PARENS(AST_Seq, function(output){
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4)
|
return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4)
|
||||||
@@ -1254,10 +1261,18 @@ function OutputStream(options) {
|
|||||||
});
|
});
|
||||||
DEFPRINT(AST_Hole, noop);
|
DEFPRINT(AST_Hole, noop);
|
||||||
DEFPRINT(AST_Infinity, function(self, output){
|
DEFPRINT(AST_Infinity, function(self, output){
|
||||||
output.print("Infinity");
|
output.print("1");
|
||||||
|
output.space();
|
||||||
|
output.print("/");
|
||||||
|
output.space();
|
||||||
|
output.print("0");
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_NaN, function(self, output){
|
DEFPRINT(AST_NaN, function(self, output){
|
||||||
output.print("NaN");
|
output.print("0");
|
||||||
|
output.space();
|
||||||
|
output.print("/");
|
||||||
|
output.space();
|
||||||
|
output.print("0");
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_This, function(self, output){
|
DEFPRINT(AST_This, function(self, output){
|
||||||
output.print("this");
|
output.print("this");
|
||||||
|
|||||||
@@ -840,8 +840,8 @@ equality_conditionals_false: {
|
|||||||
f(0, true, 0),
|
f(0, true, 0),
|
||||||
f(1, 2, 3),
|
f(1, 2, 3),
|
||||||
f(1, null, 3),
|
f(1, null, 3),
|
||||||
f(NaN),
|
f(0/0),
|
||||||
f(NaN, "foo");
|
f(0/0, "foo");
|
||||||
}
|
}
|
||||||
expect_stdout: true
|
expect_stdout: true
|
||||||
}
|
}
|
||||||
@@ -888,8 +888,8 @@ equality_conditionals_true: {
|
|||||||
f(0, true, 0),
|
f(0, true, 0),
|
||||||
f(1, 2, 3),
|
f(1, 2, 3),
|
||||||
f(1, null, 3),
|
f(1, null, 3),
|
||||||
f(NaN),
|
f(0/0),
|
||||||
f(NaN, "foo");
|
f(0/0, "foo");
|
||||||
}
|
}
|
||||||
expect_stdout: true
|
expect_stdout: true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ and: {
|
|||||||
a = 7;
|
a = 7;
|
||||||
|
|
||||||
a = false;
|
a = false;
|
||||||
a = NaN;
|
a = 0/0;
|
||||||
a = 0;
|
a = 0;
|
||||||
a = void 0;
|
a = void 0;
|
||||||
a = null;
|
a = null;
|
||||||
@@ -67,7 +67,7 @@ and: {
|
|||||||
a = 6 << condition && -4.5;
|
a = 6 << condition && -4.5;
|
||||||
|
|
||||||
a = condition && false;
|
a = condition && false;
|
||||||
a = console.log("b") && NaN;
|
a = console.log("b") && 0/0;
|
||||||
a = console.log("c") && 0;
|
a = console.log("c") && 0;
|
||||||
a = 2 * condition && void 0;
|
a = 2 * condition && void 0;
|
||||||
a = condition + 3 && null;
|
a = condition + 3 && null;
|
||||||
@@ -149,7 +149,7 @@ or: {
|
|||||||
a = 6 << condition || -4.5;
|
a = 6 << condition || -4.5;
|
||||||
|
|
||||||
a = condition || false;
|
a = condition || false;
|
||||||
a = console.log("b") || NaN;
|
a = console.log("b") || 0/0;
|
||||||
a = console.log("c") || 0;
|
a = console.log("c") || 0;
|
||||||
a = 2 * condition || void 0;
|
a = 2 * condition || void 0;
|
||||||
a = condition + 3 || null;
|
a = condition + 3 || null;
|
||||||
@@ -196,8 +196,8 @@ negative_zero: {
|
|||||||
console.log(
|
console.log(
|
||||||
-0,
|
-0,
|
||||||
0,
|
0,
|
||||||
1 / (-0),
|
-1/0,
|
||||||
1 / (-0)
|
-1/0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
expect_stdout: true
|
expect_stdout: true
|
||||||
@@ -217,8 +217,8 @@ positive_zero: {
|
|||||||
console.log(
|
console.log(
|
||||||
0,
|
0,
|
||||||
-0,
|
-0,
|
||||||
1 / (0),
|
1/0,
|
||||||
1 / (0)
|
1/0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
expect_stdout: true
|
expect_stdout: true
|
||||||
@@ -533,7 +533,7 @@ unsafe_array: {
|
|||||||
[1, 2, 3, a][0] + 1,
|
[1, 2, 3, a][0] + 1,
|
||||||
2,
|
2,
|
||||||
3,
|
3,
|
||||||
NaN,
|
0/0,
|
||||||
"1,21",
|
"1,21",
|
||||||
5,
|
5,
|
||||||
(void 0)[1] + 1
|
(void 0)[1] + 1
|
||||||
|
|||||||
@@ -195,11 +195,12 @@ assorted_Infinity_NaN_undefined_in_with_scope: {
|
|||||||
sequences: false,
|
sequences: false,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
|
var f = console.log;
|
||||||
var o = {
|
var o = {
|
||||||
undefined : 3,
|
undefined : 3,
|
||||||
NaN : 4,
|
NaN : 4,
|
||||||
Infinity : 5,
|
Infinity : 5,
|
||||||
}
|
};
|
||||||
if (o) {
|
if (o) {
|
||||||
f(undefined, void 0);
|
f(undefined, void 0);
|
||||||
f(NaN, 0/0);
|
f(NaN, 0/0);
|
||||||
@@ -216,25 +217,25 @@ assorted_Infinity_NaN_undefined_in_with_scope: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
var o = {
|
var f = console.log, o = {
|
||||||
undefined : 3,
|
undefined : 3,
|
||||||
NaN : 4,
|
NaN : 4,
|
||||||
Infinity : 5
|
Infinity : 5
|
||||||
}
|
};
|
||||||
if (o) {
|
if (o) {
|
||||||
f(void 0, void 0);
|
f(void 0, void 0);
|
||||||
f(NaN, NaN);
|
f(0/0, 0/0);
|
||||||
f(1/0, 1/0);
|
f(1/0, 1/0);
|
||||||
f(-(1/0), -(1/0));
|
f(-1/0, -1/0);
|
||||||
f(NaN, NaN);
|
f(0/0, 0/0);
|
||||||
}
|
}
|
||||||
with (o) {
|
with (o) {
|
||||||
f(undefined, void 0);
|
f(undefined, void 0);
|
||||||
f(NaN, 0/0);
|
f(NaN, 0/0);
|
||||||
f(Infinity, 1/0);
|
f(Infinity, 1/0);
|
||||||
f(-Infinity, -(1/0));
|
f(-Infinity, -1/0);
|
||||||
f(9 + undefined, 9 + void 0);
|
f(9 + undefined, 9 + void 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
expect_stdout: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ NaN_and_Infinity_must_have_parens: {
|
|||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
(1/0).toString();
|
(1/0).toString();
|
||||||
NaN.toString(); // transformation to 0/0 dropped
|
(0/0).toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,3 +23,87 @@ NaN_and_Infinity_should_not_be_replaced_when_they_are_redefined: {
|
|||||||
NaN.toString();
|
NaN.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
beautify_off_1: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
}
|
||||||
|
beautify = {
|
||||||
|
beautify: false,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var NaN;
|
||||||
|
console.log(
|
||||||
|
null,
|
||||||
|
undefined,
|
||||||
|
Infinity,
|
||||||
|
NaN,
|
||||||
|
Infinity * undefined,
|
||||||
|
Infinity.toString(),
|
||||||
|
NaN.toString(),
|
||||||
|
(Infinity * undefined).toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect_exact: "var NaN;console.log(null,void 0,1/0,NaN,0/0,(1/0).toString(),NaN.toString(),(0/0).toString());"
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
beautify_off_2: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
}
|
||||||
|
beautify = {
|
||||||
|
beautify: false,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(
|
||||||
|
null.toString(),
|
||||||
|
undefined.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect_exact: "console.log(null.toString(),(void 0).toString());"
|
||||||
|
}
|
||||||
|
|
||||||
|
beautify_on_1: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
}
|
||||||
|
beautify = {
|
||||||
|
beautify: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var NaN;
|
||||||
|
console.log(
|
||||||
|
null,
|
||||||
|
undefined,
|
||||||
|
Infinity,
|
||||||
|
NaN,
|
||||||
|
Infinity * undefined,
|
||||||
|
Infinity.toString(),
|
||||||
|
NaN.toString(),
|
||||||
|
(Infinity * undefined).toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect_exact: [
|
||||||
|
"var NaN;",
|
||||||
|
"",
|
||||||
|
"console.log(null, void 0, 1 / 0, NaN, 0 / 0, (1 / 0).toString(), NaN.toString(), (0 / 0).toString());",
|
||||||
|
]
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
beautify_on_2: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
}
|
||||||
|
beautify = {
|
||||||
|
beautify: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(
|
||||||
|
null.toString(),
|
||||||
|
undefined.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect_exact: "console.log(null.toString(), (void 0).toString());"
|
||||||
|
}
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ sub_properties: {
|
|||||||
a[3.14] = 3;
|
a[3.14] = 3;
|
||||||
a.if = 4;
|
a.if = 4;
|
||||||
a["foo bar"] = 5;
|
a["foo bar"] = 5;
|
||||||
a[NaN] = 6;
|
a[0/0] = 6;
|
||||||
a[null] = 7;
|
a[null] = 7;
|
||||||
a[void 0] = 8;
|
a[void 0] = 8;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user