blob: f95aef3365e2438935f11cd2723d9191f9be534a [file]
/*
* 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*));