Kaynağa Gözat

LibJS: Don't treat 2^32 - 1 as numeric PropertyName

    10.4.2 Array Exotic Objects
    https://tc39.es/ecma262/#sec-array-exotic-objects

    A String property name P is an array index if and only if
    ToString(ToUint32(P)) equals P and ToUint32(P) is not the same value
    as 𝔽(2^32 - 1).
Linus Groh 4 yıl önce
ebeveyn
işleme
83f61748a5

+ 4 - 4
Userland/Libraries/LibJS/Runtime/PropertyName.h

@@ -31,7 +31,7 @@ public:
             return {};
         if (value.is_symbol())
             return value.as_symbol();
-        if (value.is_integral_number() && value.as_double() >= 0 && value.as_double() <= NumericLimits<u32>::max())
+        if (value.is_integral_number() && value.as_double() >= 0 && value.as_double() < NumericLimits<u32>::max())
             return value.as_u32();
         auto string = value.to_string(global_object);
         if (string.is_null())
@@ -49,8 +49,8 @@ public:
         // FIXME: Replace this with requires(IsUnsigned<T>)?
         //        Needs changes in various places using `int` (but not actually being in the negative range)
         VERIFY(index >= 0);
-        if constexpr (NumericLimits<T>::max() > NumericLimits<u32>::max())
-            VERIFY(index <= NumericLimits<u32>::max());
+        if constexpr (NumericLimits<T>::max() >= NumericLimits<u32>::max())
+            VERIFY(index < NumericLimits<u32>::max());
     }
 
     PropertyName(char const* chars)
@@ -131,7 +131,7 @@ public:
         }
 
         auto property_index = m_string.to_uint(TrimWhitespace::No);
-        if (!property_index.has_value()) {
+        if (!property_index.has_value() || property_index.value() == NumericLimits<u32>::max()) {
             m_string_may_be_number = false;
             return false;
         }

+ 30 - 0
Userland/Libraries/LibJS/Tests/object-expression-numeric-property.js

@@ -0,0 +1,30 @@
+test("numeric properties", () => {
+    const i32Max = 2 ** 31 - 1;
+    const u32Max = 2 ** 32 - 1;
+    const o = {
+        [-1]: "foo",
+        0: "foo",
+        1: "foo",
+        [i32Max - 1]: "foo",
+        [i32Max]: "foo",
+        [i32Max + 1]: "foo",
+        [u32Max - 1]: "foo",
+        [u32Max]: "foo",
+        [u32Max + 1]: "foo",
+    };
+    // Numeric properties come first in Object.getOwnPropertyNames()'s output,
+    // which means we can test what each is treated as internally.
+    expect(Object.getOwnPropertyNames(o)).toEqual([
+        // Numeric properties
+        "0",
+        "1",
+        "2147483646",
+        "2147483647",
+        "2147483648",
+        "4294967294",
+        // Non-numeric properties
+        "-1",
+        "4294967295", // >= 2^32 - 1
+        "4294967296", // >= 2^32 - 1
+    ]);
+});