From 58ac5b9bd5e5d37a4afd2c435973dd8c56449ffb Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sun, 4 Oct 2020 17:05:03 +0100 Subject: [PATCH] extend support for numeral literals (#4176) --- lib/parse.js | 31 +++++++++--------- test/mocha/number-literal.js | 61 ++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 17 deletions(-) diff --git a/lib/parse.js b/lib/parse.js index d57baccb..3989ae65 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -60,8 +60,9 @@ KEYWORDS_ATOM = makePredicate(KEYWORDS_ATOM); var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^")); -var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i; -var RE_OCT_NUMBER = /^0[0-7]+$/; +var RE_BIN_NUMBER = /^0b([01]+)$/i; +var RE_HEX_NUMBER = /^0x([0-9a-f]+)$/i; +var RE_OCT_NUMBER = /^0o?([0-7]+)$/i; var OPERATORS = makePredicate([ "in", @@ -147,10 +148,6 @@ function is_digit(code) { return code >= 48 && code <= 57; } -function is_alphanumeric_char(code) { - return is_digit(code) || is_letter(code); -} - function is_unicode_digit(code) { return UNICODE.digit.test(String.fromCharCode(code)); } @@ -184,14 +181,12 @@ function is_identifier_string(str) { } function parse_js_number(num) { - if (RE_HEX_NUMBER.test(num)) { - return parseInt(num.substr(2), 16); - } else if (RE_OCT_NUMBER.test(num)) { - return parseInt(num.substr(1), 8); - } else { - var val = parseFloat(num); - if (val == num) return val; - } + var match; + if (match = RE_BIN_NUMBER.exec(num)) return parseInt(match[1], 2); + if (match = RE_HEX_NUMBER.exec(num)) return parseInt(match[1], 16); + if (match = RE_OCT_NUMBER.exec(num)) return parseInt(match[1], 8); + var val = parseFloat(num); + if (val == num) return val; } function JS_Parse_Error(message, filename, line, col, pos) { @@ -347,11 +342,13 @@ function tokenizer($TEXT, filename, html5_comments, shebang) { case (after_e = false, 46): // . return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false; } - return is_alphanumeric_char(code); + return is_digit(code) || is_letter(code) || ch == "_"; }); if (prefix) num = prefix + num; - if (RE_OCT_NUMBER.test(num) && next_token.has_directive("use strict")) { - parse_error("Legacy octal literals are not allowed in strict mode"); + if (/^0[0-7_]+$/.test(num)) { + if (next_token.has_directive("use strict")) parse_error("Legacy octal literals are not allowed in strict mode"); + } else { + num = num.replace(has_x ? /([1-9a-f]|.0)_(?=[0-9a-f])/gi : /([1-9]|.0)_(?=[0-9])/gi, "$1"); } var valid = parse_js_number(num); if (!isNaN(valid)) return token("num", valid); diff --git a/test/mocha/number-literal.js b/test/mocha/number-literal.js index b87c88bb..a2b16a9e 100644 --- a/test/mocha/number-literal.js +++ b/test/mocha/number-literal.js @@ -28,4 +28,65 @@ describe("Number literals", function() { assert.throws(test(inputs[i]), error, inputs[i]); } }); + it("Should parse binary, hexadecimal, octal and underscore correctly", function() { + [ + "42", + "4_2", + "052", + "0o52", + "0O52", + "0o5_2", + "0x2a", + "0X2A", + "0x2_a", + "0b101010", + "0B101010", + "0b101_010", + "0.0000000042e+10", + "0.0000000042E+10", + "0.0_000000042e+10", + "0.0000000042e+1_0", + "0.000_000_004_2e+1_0", + "0.000_000_004_2e+1_0-0B101_010+0x2_A-0o5_2+4_2", + ].forEach(function(code) { + var result = UglifyJS.minify(code, { + compress: { + expression: true, + }, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, "42;"); + }); + }); + it("Should reject invalid use of underscore", function() { + [ + "_42", + "_+42", + "+_42", + ].forEach(function(code) { + var node = UglifyJS.parse(code, { + expression: true, + }); + assert.ok(!node.is_constant(), code); + assert.ok(!(node instanceof UglifyJS.AST_Statement), code); + }); + [ + "42_", + "4__2", + "0_52", + "05_2", + "0_o52", + "0o_52", + "0.0000000042_e10", + "0.0000000042e_10", + "0.0000000042e_+10", + "0.0000000042e+_10", + ].forEach(function(code) { + assert.throws(function() { + UglifyJS.parse(code); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error; + }, code); + }); + }); }); -- 2.34.1