From 23c72c67285743c9815f7ea82bd35b94aa179893 Mon Sep 17 00:00:00 2001 From: serenitydev Date: Fri, 11 Feb 2022 03:29:08 -0500 Subject: [PATCH] AK: Fix userland parsing of rounded floating point numbers Parse JSON floating point literals properly, No longer throwing a SyntaxError when the decimal portion of the number exceeds the capacity of u32. Added tests to AK/TestJSON and LibJS/builtins/JSON/JSON.parse --- AK/JsonParser.cpp | 14 ++++++-------- Tests/AK/TestJSON.cpp | 6 ++++++ .../LibJS/Tests/builtins/JSON/JSON.parse.js | 7 +++++++ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/AK/JsonParser.cpp b/AK/JsonParser.cpp index bf27305938f..46f931668be 100644 --- a/AK/JsonParser.cpp +++ b/AK/JsonParser.cpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace AK { @@ -251,17 +252,14 @@ ErrorOr JsonParser::parse_number() } StringView fraction_string(fraction_buffer.data(), fraction_buffer.size()); - auto fraction_string_uint = fraction_string.to_uint(); + auto fraction_string_uint = fraction_string.to_uint(); if (!fraction_string_uint.has_value()) return Error::from_string_literal("JsonParser: Error while parsing number"sv); - int fraction = fraction_string_uint.value(); - fraction *= (whole < 0) ? -1 : 1; + auto fraction = static_cast(fraction_string_uint.value()); + double sign = (whole < 0) ? -1 : 1; - auto divider = 1; - for (size_t i = 0; i < fraction_buffer.size(); ++i) { - divider *= 10; - } - value = JsonValue((double)whole + ((double)fraction / divider)); + auto divider = pow(10.0, static_cast(fraction_buffer.size())); + value = JsonValue((double)whole + sign * (fraction / divider)); } else { #endif auto to_unsigned_result = number_string.to_uint(); diff --git a/Tests/AK/TestJSON.cpp b/Tests/AK/TestJSON.cpp index 7f0cbab2969..0627b110c1d 100644 --- a/Tests/AK/TestJSON.cpp +++ b/Tests/AK/TestJSON.cpp @@ -128,3 +128,9 @@ TEST_CASE(json_parse_empty_string) auto value = JsonValue::from_string(""); EXPECT_EQ(value.value().is_null(), true); } + +TEST_CASE(json_parse_long_decimals) +{ + auto value = JsonValue::from_string("1644452550.6489999294281"); + EXPECT_EQ(value.value().as_double(), 1644452550.6489999294281); +} diff --git a/Userland/Libraries/LibJS/Tests/builtins/JSON/JSON.parse.js b/Userland/Libraries/LibJS/Tests/builtins/JSON/JSON.parse.js index 6cc501f51a3..a05e579e2a4 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/JSON/JSON.parse.js +++ b/Userland/Libraries/LibJS/Tests/builtins/JSON/JSON.parse.js @@ -43,3 +43,10 @@ test("negative zero", () => { expect(JSON.parse(-0)).toEqual(0); }); + +// The underlying parser resolves decimal numbers by storing the decimal portion in an integer +// This test handles a regression where the decimal portion was only using a u32 vs. u64 +// and would fail to parse. +test("long decimal parse", () => { + expect(JSON.parse("1644452550.6489999294281")).toEqual(1644452550.6489999294281); +});