| /* |
| * Copyright 2026 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "include/private/SkAssert.h" |
| #include "include/private/SkTypeTraits.h" |
| #include "src/partition_alloc/raw_ptr.h" |
| #include "src/partition_alloc/raw_ref.h" |
| #include "tests/Test.h" |
| |
| #include <memory> |
| #include <set> |
| #include <tuple> |
| #include <type_traits> |
| #include <unordered_set> |
| #include <utility> |
| |
| #if defined(SK_USE_PARTITION_ALLOC) |
| #include <partition_alloc/buildflags.h> |
| #if PA_BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) |
| #include <partition_alloc/dangling_raw_ptr_checks.h> |
| |
| namespace { |
| class ScopedDanglingPointerTracker { |
| public: |
| ScopedDanglingPointerTracker() |
| : fOldDetectedFn(partition_alloc::GetDanglingRawPtrDetectedFn()) |
| , fOldReleasedFn(partition_alloc::GetDanglingRawPtrReleasedFn()) { |
| gDetected = false; |
| gReleased = false; |
| partition_alloc::SetDanglingRawPtrDetectedFn([](uintptr_t) { gDetected = true; }); |
| partition_alloc::SetDanglingRawPtrReleasedFn([](uintptr_t) { gReleased = true; }); |
| } |
| |
| ~ScopedDanglingPointerTracker() { |
| partition_alloc::SetDanglingRawPtrDetectedFn(fOldDetectedFn); |
| partition_alloc::SetDanglingRawPtrReleasedFn(fOldReleasedFn); |
| } |
| |
| bool detected() const { return gDetected; } |
| bool released() const { return gReleased; } |
| |
| private: |
| static inline bool gDetected = false; |
| static inline bool gReleased = false; |
| |
| partition_alloc::DanglingRawPtrDetectedFn* fOldDetectedFn; |
| partition_alloc::DanglingRawPtrReleasedFn* fOldReleasedFn; |
| }; |
| } // namespace |
| |
| // This test temporarily affects global PartitionAlloc state by replacing |
| // detection and release handlers, so it cannot run in parallel. |
| DEF_SERIAL_TEST(RawPtr_DanglingPointerDetected, reporter) { |
| ScopedDanglingPointerTracker tracker; |
| |
| std::unique_ptr<int> owner = std::make_unique<int>(42); |
| raw_ptr<int> ptr = owner.get(); |
| REPORTER_ASSERT(reporter, !tracker.detected()); |
| REPORTER_ASSERT(reporter, !tracker.released()); |
| |
| owner.reset(); |
| REPORTER_ASSERT(reporter, tracker.detected()); |
| REPORTER_ASSERT(reporter, !tracker.released()); |
| |
| ptr = nullptr; |
| REPORTER_ASSERT(reporter, tracker.detected()); |
| REPORTER_ASSERT(reporter, tracker.released()); |
| } |
| #endif |
| #endif |
| |
| namespace { |
| struct Base { |
| virtual ~Base() = default; |
| int base_val = 1; |
| }; |
| |
| struct Derived : public Base { |
| int derived_val = 2; |
| }; |
| } // namespace |
| |
| DEF_TEST(RawPtr_DisableDanglingPtrDetection, reporter) { |
| std::unique_ptr<int> owner = std::make_unique<int>(42); |
| raw_ptr<int, DisableDanglingPtrDetection> ptr = owner.get(); |
| std::ignore = ptr; |
| owner.reset(); |
| } |
| |
| static_assert(sk_is_trivially_relocatable<raw_ptr<int>>::value); |
| static_assert(sk_is_trivially_relocatable<raw_ptr<int, DisableDanglingPtrDetection>>::value); |
| static_assert(sk_is_trivially_relocatable<raw_ref<int>>::value); |
| static_assert(sk_is_trivially_relocatable<raw_ref<int, DisableDanglingPtrDetection>>::value); |
| DEF_TEST(RawPtr_DefaultConstructor, reporter) { |
| raw_ptr<int> p; |
| REPORTER_ASSERT(reporter, p == nullptr); |
| REPORTER_ASSERT(reporter, !p); |
| } |
| |
| DEF_TEST(RawPtr_NullptrConstructor, reporter) { |
| raw_ptr<int> p = nullptr; |
| REPORTER_ASSERT(reporter, p == nullptr); |
| REPORTER_ASSERT(reporter, !p); |
| } |
| |
| DEF_TEST(RawPtr_RawPointerConstructor, reporter) { |
| int val = 42; |
| raw_ptr<int> p = &val; |
| REPORTER_ASSERT(reporter, p == &val); |
| REPORTER_ASSERT(reporter, p); |
| REPORTER_ASSERT(reporter, *p == 42); |
| } |
| |
| DEF_TEST(RawPtr_CopyAndMove, reporter) { |
| int val = 42; |
| raw_ptr<int> p1 = &val; |
| |
| // Copy. |
| raw_ptr<int> p2 = p1; |
| REPORTER_ASSERT(reporter, p2 == &val); |
| REPORTER_ASSERT(reporter, p1 == &val); |
| |
| // Move. |
| raw_ptr<int> p3 = std::move(p2); |
| REPORTER_ASSERT(reporter, p3 == &val); |
| REPORTER_ASSERT(reporter, p2 == nullptr); // NOLINT(bugprone-use-after-move) |
| } |
| |
| DEF_TEST(RawPtr_Assignment, reporter) { |
| int val1 = 42; |
| int val2 = 84; |
| raw_ptr<int> p1 = &val1; |
| raw_ptr<int> p2 = &val2; |
| |
| p1 = p2; |
| REPORTER_ASSERT(reporter, p1 == &val2); |
| |
| p1 = nullptr; |
| REPORTER_ASSERT(reporter, p1 == nullptr); |
| |
| p1 = &val1; |
| REPORTER_ASSERT(reporter, p1 == &val1); |
| } |
| |
| DEF_TEST(RawPtr_SelfAssignment, reporter) { |
| int val = 42; |
| raw_ptr<int> p = &val; |
| |
| // Self-copy-assignment. |
| raw_ptr<int>* p_ptr = &p; |
| p = *p_ptr; |
| REPORTER_ASSERT(reporter, p == &val); |
| |
| // Self-move-assignment. |
| p = std::move(*p_ptr); |
| REPORTER_ASSERT(reporter, p == &val); |
| } |
| |
| DEF_TEST(RawPtr_Upcasting, reporter) { |
| Derived derived; |
| raw_ptr<Derived> p_derived = &derived; |
| |
| // Copy construct Derived -> Base. |
| raw_ptr<Base> p_base(p_derived); |
| REPORTER_ASSERT(reporter, p_base == &derived); |
| REPORTER_ASSERT(reporter, p_base->base_val == 1); |
| |
| // Assignment Derived -> Base. |
| raw_ptr<Base> p_base2; |
| p_base2 = p_derived; |
| REPORTER_ASSERT(reporter, p_base2 == &derived); |
| |
| // Move construct Derived -> Base. |
| raw_ptr<Derived> p_derived_move = &derived; |
| raw_ptr<Base> p_base_move(std::move(p_derived_move)); |
| REPORTER_ASSERT(reporter, p_base_move == &derived); |
| REPORTER_ASSERT(reporter, p_derived_move == nullptr); // NOLINT(bugprone-use-after-move) |
| |
| // Move assignment Derived -> Base. |
| raw_ptr<Derived> p_derived_move2 = &derived; |
| raw_ptr<Base> p_base_move2; |
| p_base_move2 = std::move(p_derived_move2); |
| REPORTER_ASSERT(reporter, p_base_move2 == &derived); |
| REPORTER_ASSERT(reporter, p_derived_move2 == nullptr); // NOLINT(bugprone-use-after-move) |
| } |
| |
| DEF_TEST(RawPtr_DereferenceAndMemberAccess, reporter) { |
| struct Foo { |
| int x = 42; |
| }; |
| Foo foo; |
| raw_ptr<Foo> p = &foo; |
| REPORTER_ASSERT(reporter, p->x == 42); |
| REPORTER_ASSERT(reporter, (*p).x == 42); |
| REPORTER_ASSERT(reporter, p == &foo); |
| } |
| |
| DEF_TEST(RawPtr_PointerArithmetic, reporter) { |
| int arr[] = {10, 20, 30}; |
| int* expected = arr; |
| raw_ptr<int, AllowPtrArithmetic> p = arr; |
| |
| // Verify the initial state. |
| REPORTER_ASSERT(reporter, p == expected); |
| |
| // Test pre-increment. |
| ++expected; |
| raw_ptr<int, AllowPtrArithmetic>& p_ref = ++p; |
| REPORTER_ASSERT(reporter, &p_ref == &p); |
| REPORTER_ASSERT(reporter, p == expected); |
| |
| // Test post-increment. |
| int* expected_old = expected++; |
| raw_ptr<int, AllowPtrArithmetic> p_old = p++; |
| REPORTER_ASSERT(reporter, p_old == expected_old); |
| REPORTER_ASSERT(reporter, p == expected); |
| |
| // Test pre-decrement. |
| --expected; |
| raw_ptr<int, AllowPtrArithmetic>& p_ref2 = --p; |
| REPORTER_ASSERT(reporter, &p_ref2 == &p); |
| REPORTER_ASSERT(reporter, p == expected); |
| |
| // Test post-decrement. |
| expected_old = expected--; |
| p_old = p--; |
| REPORTER_ASSERT(reporter, p_old == expected_old); |
| REPORTER_ASSERT(reporter, p == expected); |
| |
| // Test addition. |
| REPORTER_ASSERT(reporter, (p + 1) == (expected + 1)); |
| REPORTER_ASSERT(reporter, (p + 2) == (expected + 2)); |
| |
| // Test subtraction. |
| raw_ptr<int, AllowPtrArithmetic> p2 = p + 2; |
| int* expected2 = expected + 2; |
| REPORTER_ASSERT(reporter, (p2 - 1) == (expected2 - 1)); |
| REPORTER_ASSERT(reporter, (p2 - 2) == (expected2 - 2)); |
| |
| // Test difference. |
| REPORTER_ASSERT(reporter, (p2 - p) == (expected2 - expected)); |
| } |
| |
| DEF_TEST(RawPtr_EqualityComparisons, reporter) { |
| int val1 = 42; |
| int val2 = 84; |
| raw_ptr<int> p1 = &val1; |
| raw_ptr<int> p2 = &val2; |
| raw_ptr<int> null_p = nullptr; |
| |
| REPORTER_ASSERT(reporter, p1 == p1); |
| REPORTER_ASSERT(reporter, !(p1 == p2)); |
| REPORTER_ASSERT(reporter, p1 != p2); |
| |
| REPORTER_ASSERT(reporter, p1 == &val1); |
| REPORTER_ASSERT(reporter, &val1 == p1); |
| REPORTER_ASSERT(reporter, !(p1 == &val2)); |
| REPORTER_ASSERT(reporter, !(&val2 == p1)); |
| |
| REPORTER_ASSERT(reporter, null_p == nullptr); |
| REPORTER_ASSERT(reporter, nullptr == null_p); |
| REPORTER_ASSERT(reporter, !(p1 == nullptr)); |
| REPORTER_ASSERT(reporter, !(nullptr == p1)); |
| } |
| |
| DEF_TEST(RawPtr_RelationalComparisons, reporter) { |
| int arr[2] = {42, 84}; |
| raw_ptr<int> p0 = &arr[0]; |
| raw_ptr<int> p1_arr = &arr[1]; |
| |
| REPORTER_ASSERT(reporter, p0 < p1_arr); |
| REPORTER_ASSERT(reporter, p0 <= p1_arr); |
| REPORTER_ASSERT(reporter, p1_arr > p0); |
| REPORTER_ASSERT(reporter, p1_arr >= p0); |
| |
| REPORTER_ASSERT(reporter, !(p1_arr < p0)); |
| REPORTER_ASSERT(reporter, !(p1_arr <= p0)); |
| REPORTER_ASSERT(reporter, !(p0 > p1_arr)); |
| REPORTER_ASSERT(reporter, !(p0 >= p1_arr)); |
| |
| REPORTER_ASSERT(reporter, p0 < &arr[1]); |
| REPORTER_ASSERT(reporter, p0 <= &arr[1]); |
| REPORTER_ASSERT(reporter, &arr[1] > p0); |
| REPORTER_ASSERT(reporter, &arr[1] >= p0); |
| } |
| |
| DEF_TEST(RawPtr_Void, reporter) { |
| int val = 42; |
| raw_ptr<void> p = &val; |
| REPORTER_ASSERT(reporter, p == &val); |
| REPORTER_ASSERT(reporter, p); |
| |
| raw_ptr<const void> cp = &val; |
| REPORTER_ASSERT(reporter, cp == &val); |
| } |
| |
| DEF_TEST(RawPtr_Stl, reporter) { |
| int a = 1; |
| int b = 2; |
| raw_ptr<int> pa = &a; |
| raw_ptr<int> pb = &b; |
| |
| // std::set. |
| std::set<raw_ptr<int>> ptr_set; |
| ptr_set.insert(pa); |
| ptr_set.insert(pb); |
| REPORTER_ASSERT(reporter, ptr_set.size() == 2); |
| REPORTER_ASSERT(reporter, ptr_set.find(pa) != ptr_set.end()); |
| REPORTER_ASSERT(reporter, ptr_set.find(&a) != ptr_set.end()); |
| |
| // std::unordered_set. |
| std::unordered_set<raw_ptr<int>> ptr_uset; |
| ptr_uset.insert(pa); |
| ptr_uset.insert(pb); |
| REPORTER_ASSERT(reporter, ptr_uset.size() == 2); |
| REPORTER_ASSERT(reporter, ptr_uset.find(pa) != ptr_uset.end()); |
| |
| // std::to_address. |
| REPORTER_ASSERT(reporter, std::to_address(pa) == &a); |
| } |
| |
| static_assert(sizeof(raw_ptr<int>) == sizeof(int*)); |