diff --git a/lib/ast.js b/lib/ast.js index 8700b4ba..2bebe7e0 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -1068,7 +1068,23 @@ var AST_Number = DEFNODE("Number", "value literal", { $propdoc: { value: "[number] the numeric value", literal: "[string] numeric value as string (optional)" - } + }, + _validate: function() { + if (typeof this.value != "number") throw new Error("value must be number"); + if (!isFinite(this.value)) throw new Error("value must be finite"); + if (this.value < 0) throw new Error("value cannot be negative"); + }, +}, AST_Constant); + +var AST_BigInt = DEFNODE("BigInt", "value", { + $documentation: "A BigInt literal", + $propdoc: { + value: "[string] the numeric representation", + }, + _validate: function() { + if (typeof this.value != "string") throw new Error("value must be string"); + if (this.value[0] == "-") throw new Error("value cannot be negative"); + }, }, AST_Constant); var AST_RegExp = DEFNODE("RegExp", "value", { diff --git a/lib/output.js b/lib/output.js index e4987179..01cd311d 100644 --- a/lib/output.js +++ b/lib/output.js @@ -865,13 +865,11 @@ function OutputStream(options) { }); PARENS(AST_Number, function(output){ + if (!output.option("galio")) return false; + // https://github.com/mishoo/UglifyJS/pull/1009 + var p = output.parent(); - if (p instanceof AST_PropAccess && p.expression === this) { - var value = this.getValue(); - if (value < 0 || /^0/.test(make_num(value))) { - return true; - } - } + return p instanceof AST_PropAccess && p.expression === this && /^0/.test(make_num(this.value)); }); PARENS([ AST_Assign, AST_Conditional ], function(output){ diff --git a/lib/parse.js b/lib/parse.js index d8cc9ac4..f937abc0 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -377,9 +377,8 @@ function tokenizer($TEXT, filename, html5_comments, shebang) { }; function read_while(pred) { - var ret = "", ch, i = 0; - while ((ch = peek()) && pred(ch, i++)) - ret += next(); + var ret = "", ch; + while ((ch = peek()) && pred(ch)) ret += next(); return ret; }; @@ -389,7 +388,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) { function read_num(prefix) { var has_e = false, after_e = false, has_x = false, has_dot = prefix == "."; - var num = read_while(function(ch, i){ + var num = read_while(function(ch){ var code = ch.charCodeAt(0); switch (code) { case 98: case 66: // bB @@ -399,9 +398,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) { return has_x ? false : (has_x = true); case 101: case 69: // eE return has_x ? true : has_e ? false : (has_e = after_e = true); - case 45: // - - return after_e || (i == 0 && !prefix); - case 43: // + + case 43: case 45: return after_e; case (after_e = false, 46): // . return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false; @@ -413,11 +410,9 @@ function tokenizer($TEXT, filename, html5_comments, shebang) { parse_error("Legacy octal literals are not allowed in strict mode"); } var valid = parse_js_number(num); - if (!isNaN(valid)) { - return token("num", valid); - } else { - parse_error("Invalid syntax: " + num); - } + if (isNaN(valid)) parse_error("Invalid syntax: " + num); + if (has_dot || has_e || peek() != "n") return token("num", valid); + return token("bigint", num.toLowerCase() + next()); }; function read_escaped_char(in_string) { @@ -847,7 +842,7 @@ var PRECEDENCE = (function(a, ret){ {} ); -var ATOMIC_START_TOKEN = makePredicate([ "atom", "num", "string", "regexp", "name" ]); +var ATOMIC_START_TOKEN = makePredicate([ "atom", "bigint", "num", "string", "regexp", "name" ]); /* -----[ Parser ]----- */ @@ -1003,6 +998,7 @@ function parse($TEXT, options) { return dir ? new AST_Directive(stat.body) : stat; case "template_head": case "num": + case "bigint": case "regexp": case "operator": case "atom": @@ -1935,6 +1931,9 @@ function parse($TEXT, options) { case "num": ret = new AST_Number({ start: tok, end: tok, value: tok.value }); break; + case "bigint": + ret = new AST_BigInt({ start: tok, end: tok, value: tok.value }); + break; case "string": ret = new AST_String({ start : tok, diff --git a/test/compress/arrays.js b/test/compress/arrays.js index d84a9a8e..380c2d84 100644 --- a/test/compress/arrays.js +++ b/test/compress/arrays.js @@ -15,7 +15,7 @@ holes_and_undefined: { } } -constant_join: { +constant_join_1: { options = { unsafe : true, evaluate : true @@ -37,7 +37,7 @@ constant_join: { var c5 = [ boo() + bar() + "foo", 1, 2, 3, "bar", bar() + "foo" ].join(); var c6 = [ 1, 2, null, undefined, "foo", "bar", baz() ].join(); var d = [ "foo", 1 + 2 + "bar", "baz" ].join("-"); - var e = [].join(foo + bar); + var e = (foo, bar, ""); var f = [].join(""); var g = [].join("foo"); } diff --git a/test/compress/bigint.js b/test/compress/bigint.js new file mode 100644 index 00000000..0d3d132b --- /dev/null +++ b/test/compress/bigint.js @@ -0,0 +1,46 @@ +arithmetic: { + input: { + console.log(((1n + 0x2n) * (0o3n - -4n)) >> (5n - 6n)); + } + expect_exact: "console.log((1n+0x2n)*(0o3n- -4n)>>5n-6n);" + expect_stdout: "42n" + node_version: ">=10" +} + +minus_dot: { + input: { + console.log(typeof -42n.toString(), typeof (-42n).toString()); + } + expect_exact: "console.log(typeof-42n.toString(),typeof(-42n).toString());" + expect_stdout: "number string" + node_version: ">=10" +} + +evaluate: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log((0xDEAD_BEEFn).toString(16)); + } + expect: { + console.log(0xdeadbeefn.toString(16)); + } + expect_stdout: "deadbeef" + node_version: ">=10" +} + +Number: { + options = { + unsafe: true, + } + input: { + console.log(Number(-0xfeed_dead_beef_badn)); + } + expect: { + console.log(+("" + -0xfeed_dead_beef_badn)); + } + expect_stdout: "-1148098955808013200" + node_version: ">=10" +} \ No newline at end of file diff --git a/test/compress/issue-269.js b/test/compress/issue-269.js index a29e7541..c938fbe2 100644 --- a/test/compress/issue-269.js +++ b/test/compress/issue-269.js @@ -13,7 +13,7 @@ issue_269_1: { } expect: { f( - x + '', +x, !!x, + '' + x, +('' + x), !!x, '', 0, false ); } diff --git a/test/compress/numbers.js b/test/compress/numbers.js index 86545fba..eeaa850b 100644 --- a/test/compress/numbers.js +++ b/test/compress/numbers.js @@ -120,7 +120,7 @@ evaluate_3: { console.log(1 + Number(x) + 2); } expect: { - console.log(3 + +x); + console.log(+("" + x) + 3); } }