Просмотр исходного кода

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
serenitydev 3 лет назад
Родитель
Сommit
23c72c6728

+ 6 - 8
AK/JsonParser.cpp

@@ -8,6 +8,7 @@
 #include <AK/JsonArray.h>
 #include <AK/JsonObject.h>
 #include <AK/JsonParser.h>
+#include <math.h>
 
 namespace AK {
 
@@ -251,17 +252,14 @@ ErrorOr<JsonValue> 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<u64>();
         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<double>(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<double>(fraction_buffer.size()));
+        value = JsonValue((double)whole + sign * (fraction / divider));
     } else {
 #endif
         auto to_unsigned_result = number_string.to_uint<u64>();

+ 6 - 0
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);
+}

+ 7 - 0
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);
+});