Skip to content

Commit

Permalink
output optimal representations of NaN & Infinity (#1723)
Browse files Browse the repository at this point in the history
- move these optimisations out from `Compressor` to `OutputStream`
- fixes behaviour inconsistency when running uglified code from global or module levels due to redefinition
  • Loading branch information
alexlamsl authored Mar 29, 2017
1 parent fef0bf9 commit 09f77c7
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 44 deletions.
33 changes: 13 additions & 20 deletions lib/compress.js
Original file line number Diff line number Diff line change
Expand Up @@ -413,18 +413,17 @@ merge(Compressor.prototype, {
value: val
});
case "number":
if (isNaN(val)) {
return make_node(AST_NaN, orig);
}

if ((1 / val) < 0) {
return make_node(AST_UnaryPrefix, orig, {
if (isNaN(val)) return make_node(AST_NaN, orig);
if (isFinite(val)) {
return 1 / val < 0 ? make_node(AST_UnaryPrefix, orig, {
operator: "-",
expression: make_node(AST_Number, orig, { value: -val })
});
}) : make_node(AST_Number, orig, { value: val });
}

return make_node(AST_Number, orig, { value: val });
return val < 0 ? make_node(AST_UnaryPrefix, orig, {
operator: "-",
expression: make_node(AST_Infinity, orig)
}) : make_node(AST_Infinity, orig);
case "boolean":
return make_node(val ? AST_True : AST_False, orig);
case "undefined":
Expand Down Expand Up @@ -3023,7 +3022,9 @@ merge(Compressor.prototype, {
}
}
// 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);
if (ev !== self) {
ev = make_node_from_constant(ev, self).optimize(compressor);
Expand Down Expand Up @@ -3455,9 +3456,9 @@ merge(Compressor.prototype, {
case "undefined":
return make_node(AST_Undefined, self).optimize(compressor);
case "NaN":
return make_node(AST_NaN, self).optimize(compressor);
return make_node(AST_NaN, self);
case "Infinity":
return make_node(AST_Infinity, self).optimize(compressor);
return make_node(AST_Infinity, self);
}
}
if (compressor.option("evaluate") && compressor.option("reduce_vars")) {
Expand Down Expand Up @@ -3485,14 +3486,6 @@ merge(Compressor.prototype, {
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){
if (compressor.option("unsafe")) {
var scope = compressor.find_parent(AST_Scope);
Expand Down
19 changes: 17 additions & 2 deletions lib/output.js
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,13 @@ function OutputStream(options) {
|| 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){
var p = output.parent();
return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4)
Expand Down Expand Up @@ -1254,10 +1261,18 @@ function OutputStream(options) {
});
DEFPRINT(AST_Hole, noop);
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){
output.print("NaN");
output.print("0");
output.space();
output.print("/");
output.space();
output.print("0");
});
DEFPRINT(AST_This, function(self, output){
output.print("this");
Expand Down
8 changes: 4 additions & 4 deletions test/compress/conditionals.js
Original file line number Diff line number Diff line change
Expand Up @@ -840,8 +840,8 @@ equality_conditionals_false: {
f(0, true, 0),
f(1, 2, 3),
f(1, null, 3),
f(NaN),
f(NaN, "foo");
f(0/0),
f(0/0, "foo");
}
expect_stdout: true
}
Expand Down Expand Up @@ -888,8 +888,8 @@ equality_conditionals_true: {
f(0, true, 0),
f(1, 2, 3),
f(1, null, 3),
f(NaN),
f(NaN, "foo");
f(0/0),
f(0/0, "foo");
}
expect_stdout: true
}
Expand Down
16 changes: 8 additions & 8 deletions test/compress/evaluate.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ and: {
a = 7;

a = false;
a = NaN;
a = 0/0;
a = 0;
a = void 0;
a = null;
Expand All @@ -67,7 +67,7 @@ and: {
a = 6 << condition && -4.5;

a = condition && false;
a = console.log("b") && NaN;
a = console.log("b") && 0/0;
a = console.log("c") && 0;
a = 2 * condition && void 0;
a = condition + 3 && null;
Expand Down Expand Up @@ -149,7 +149,7 @@ or: {
a = 6 << condition || -4.5;

a = condition || false;
a = console.log("b") || NaN;
a = console.log("b") || 0/0;
a = console.log("c") || 0;
a = 2 * condition || void 0;
a = condition + 3 || null;
Expand Down Expand Up @@ -196,8 +196,8 @@ negative_zero: {
console.log(
-0,
0,
1 / (-0),
1 / (-0)
-1/0,
-1/0
);
}
expect_stdout: true
Expand All @@ -217,8 +217,8 @@ positive_zero: {
console.log(
0,
-0,
1 / (0),
1 / (0)
1/0,
1/0
);
}
expect_stdout: true
Expand Down Expand Up @@ -533,7 +533,7 @@ unsafe_array: {
[1, 2, 3, a][0] + 1,
2,
3,
NaN,
0/0,
"1,21",
5,
(void 0)[1] + 1
Expand Down
17 changes: 9 additions & 8 deletions test/compress/issue-1105.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,11 +195,12 @@ assorted_Infinity_NaN_undefined_in_with_scope: {
sequences: false,
}
input: {
var f = console.log;
var o = {
undefined : 3,
NaN : 4,
Infinity : 5,
}
};
if (o) {
f(undefined, void 0);
f(NaN, 0/0);
Expand All @@ -216,25 +217,25 @@ assorted_Infinity_NaN_undefined_in_with_scope: {
}
}
expect: {
var o = {
var f = console.log, o = {
undefined : 3,
NaN : 4,
Infinity : 5
}
};
if (o) {
f(void 0, void 0);
f(NaN, NaN);
f(0/0, 0/0);
f(1/0, 1/0);
f(-(1/0), -(1/0));
f(NaN, NaN);
f(-1/0, -1/0);
f(0/0, 0/0);
}
with (o) {
f(undefined, void 0);
f(NaN, 0/0);
f(Infinity, 1/0);
f(-Infinity, -(1/0));
f(-Infinity, -1/0);
f(9 + undefined, 9 + void 0);
}
}
expect_stdout: true
}

86 changes: 85 additions & 1 deletion test/compress/issue-597.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ NaN_and_Infinity_must_have_parens: {
}
expect: {
(1/0).toString();
NaN.toString(); // transformation to 0/0 dropped
(0/0).toString();
}
}

Expand All @@ -23,3 +23,87 @@ NaN_and_Infinity_should_not_be_replaced_when_they_are_redefined: {
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());"
}
2 changes: 1 addition & 1 deletion test/compress/properties.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ sub_properties: {
a[3.14] = 3;
a.if = 4;
a["foo bar"] = 5;
a[NaN] = 6;
a[0/0] = 6;
a[null] = 7;
a[void 0] = 8;
}
Expand Down

1 comment on commit 09f77c7

@alfaproject
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change NaN to 0/0. It's 3 bytes as well, and 0/0 needs parenthesis sometimes? Doesn't look consistent to me either.

Please sign in to comment.