diff --git a/AK/Platform.h b/AK/Platform.h index 95c7d3fd584..14a81609080 100644 --- a/AK/Platform.h +++ b/AK/Platform.h @@ -18,6 +18,12 @@ # define AK_ARCH_AARCH64 1 #endif +#if (defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 8) || defined(_WIN64) +# define AK_ARCH_64_BIT +#else +# define AK_ARCH_32_BIT +#endif + #if defined(__APPLE__) && defined(__MACH__) # define AK_OS_MACOS # define AK_OS_BSD_GENERIC diff --git a/Tests/LibJS/test-value-js.cpp b/Tests/LibJS/test-value-js.cpp index 2b0fbdd9599..eac8c4463d7 100644 --- a/Tests/LibJS/test-value-js.cpp +++ b/Tests/LibJS/test-value-js.cpp @@ -39,25 +39,18 @@ TEST_NULLPTR_INPUT(Accessor); #undef TEST_NULLPTR_INPUT -// Unfortunately we don't have a way to get the pointer without it being dereferenced -// so we just use the same logic, this is dangerous if Value is ever changed! -static u64 extract_pointer(u64 ptr) -{ - return (u64)(((i64)(ptr << 16)) >> 16); -} - TEST_CASE(valid_pointer_in_gives_same_pointer_out) { if (sizeof(void*) < sizeof(double)) return; -#define EXPECT_POINTER_TO_SURVIVE(input) \ - { \ - JS::Value value(reinterpret_cast(static_cast(input))); \ - EXPECT(value.is_object()); \ - EXPECT(!value.is_null()); \ - auto extracted_pointer = extract_pointer(value.encoded()); \ - EXPECT_EQ(static_cast(input), extracted_pointer); \ +#define EXPECT_POINTER_TO_SURVIVE(input) \ + { \ + JS::Value value(reinterpret_cast(static_cast(input))); \ + EXPECT(value.is_object()); \ + EXPECT(!value.is_null()); \ + auto extracted_pointer = JS::Value::extract_pointer_bits(value.encoded()); \ + EXPECT_EQ(static_cast(input), extracted_pointer); \ } EXPECT_POINTER_TO_SURVIVE(0x1); @@ -66,9 +59,18 @@ TEST_CASE(valid_pointer_in_gives_same_pointer_out) EXPECT_POINTER_TO_SURVIVE(0x00007fffffffffff); EXPECT_POINTER_TO_SURVIVE(0x0000700000000000); EXPECT_POINTER_TO_SURVIVE(0x0000100000000000); + +#if ARCH(X86_64) + // On x86-64, the top 16 bits of pointers are equal to bit 47. EXPECT_POINTER_TO_SURVIVE(0xffff800000000000); EXPECT_POINTER_TO_SURVIVE(0xffff800000000001); EXPECT_POINTER_TO_SURVIVE(0xffff800000000010); +#elif ARCH(AARCH64) + // ... but they should contain zeroes on AArch64. + EXPECT_POINTER_TO_SURVIVE(0x0000800000000000); + EXPECT_POINTER_TO_SURVIVE(0x0000800000000001); + EXPECT_POINTER_TO_SURVIVE(0x0000800000000010); +#endif #undef EXPECT_POINTER_TO_SURVIVE } diff --git a/Userland/Libraries/LibJS/Heap/Heap.cpp b/Userland/Libraries/LibJS/Heap/Heap.cpp index 1fef87d439a..97b416980aa 100644 --- a/Userland/Libraries/LibJS/Heap/Heap.cpp +++ b/Userland/Libraries/LibJS/Heap/Heap.cpp @@ -142,7 +142,7 @@ __attribute__((no_sanitize("address"))) void Heap::gather_conservative_roots(Has // match any pointer-backed tag, in that case we have to extract the pointer to its // canonical form and add that as a possible pointer. if ((data & SHIFTED_IS_CELL_PATTERN) == SHIFTED_IS_CELL_PATTERN) - possible_pointers.set((u64)(((i64)data << 16) >> 16)); + possible_pointers.set(Value::extract_pointer_bits(data)); else possible_pointers.set(data); } else { diff --git a/Userland/Libraries/LibJS/Runtime/Value.h b/Userland/Libraries/LibJS/Runtime/Value.h index d53cbf3ecbc..aadf403302a 100644 --- a/Userland/Libraries/LibJS/Runtime/Value.h +++ b/Userland/Libraries/LibJS/Runtime/Value.h @@ -398,6 +398,24 @@ public: template [[nodiscard]] ALWAYS_INLINE ThrowCompletionOr invoke(VM&, PropertyKey const& property_key, Args... args); + static constexpr FlatPtr extract_pointer_bits(u64 encoded) + { +#ifdef AK_ARCH_32_BIT + // For 32-bit system the pointer fully fits so we can just return it directly. + static_assert(sizeof(void*) == sizeof(u32)); + return static_cast(encoded & 0xffff'ffff); +#elif ARCH(X86_64) + // For x86_64 the top 16 bits should be sign extending the "real" top bit (47th). + // So first shift the top 16 bits away then using the right shift it sign extends the top 16 bits. + return static_cast((static_cast(encoded << 16)) >> 16); +#elif ARCH(AARCH64) + // For AArch64 the top 16 bits of the pointer should be zero. + return static_cast(encoded & 0xffff'ffff'ffffULL); +#else +# error "Unknown architecture. Don't know whether pointers need to be sign-extended." +#endif + } + private: Value(u64 tag, u64 val) { @@ -444,15 +462,7 @@ private: PointerType* extract_pointer() const { VERIFY(is_cell()); - - // For 32-bit system the pointer fully fits so we can just return it directly. - if constexpr (sizeof(PointerType*) < sizeof(u64)) - return reinterpret_cast(static_cast(m_value.encoded & 0xffffffff)); - - // For x86_64 the top 16 bits should be sign extending the "real" top bit (47th). - // So first shift the top 16 bits away then using the right shift it sign extends the top 16 bits. - u64 ptr_val = (u64)(((i64)(m_value.encoded << 16)) >> 16); - return reinterpret_cast(ptr_val); + return reinterpret_cast(extract_pointer_bits(m_value.encoded)); } [[nodiscard]] ThrowCompletionOr invoke_internal(VM&, PropertyKey const&, Optional> arguments);