#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wc++11-extensions"
#include <darwintest.h>
#include <darwintest_utils.h>
#include <stdio.h>
#include <assert.h>
#include <typeinfo>
#if 0
# define OSPTR_LOG T_LOG
#elif 0
# define OSPTR_LOG printf
#else
# define OSPTR_LOG(x...) do { } while(0)
#endif
T_GLOBAL_META(
T_META_NAMESPACE("osptr"),
T_META_CHECK_LEAKS(false),
T_META_RUN_CONCURRENTLY(true)
);
static int num_instances = 0;
static int num_retains = 0;
static int num_releases = 0;
class OSMetaClassBase
{
static int id_counter;
static OSMetaClassBase *freelist;
public:
int inst_id;
mutable int refcount;
mutable OSMetaClassBase *next;
static void *type_id;
OSMetaClassBase() : refcount(1), next(nullptr)
{
inst_id = id_counter++;
num_instances++;
OSPTR_LOG("[%p, %d] constructed\n", this, inst_id);
}
virtual ~OSMetaClassBase()
{
OSPTR_LOG("[%p, %d] destroyed\n", this, inst_id);
}
virtual void
retain() const
{
T_QUIET; T_EXPECT_GT_INT(refcount, 0, "Instance resurrected");
refcount++;
num_retains++;
OSPTR_LOG("[%p, %d] retain, refcount=%d\n", this, inst_id, refcount);
}
virtual void
release() const
{
T_QUIET; T_EXPECT_GT_INT(refcount, 0, "Double free");
refcount--;
num_releases++;
OSPTR_LOG("[%p, %d] release, refcount=%d\n", this, inst_id, refcount);
if (refcount == 0) {
num_instances--;
this->next = freelist;
freelist = const_cast<OSMetaClassBase *>(this);
}
}
virtual void
taggedRetain(void *tag) const
{
OSPTR_LOG("tag[%p] ", tag);
retain();
}
virtual void
taggedRelease(void *tag) const
{
OSPTR_LOG("tag[%p] ", tag);
release();
}
};
int OSMetaClassBase::id_counter;
OSMetaClassBase *OSMetaClassBase::freelist;
void *OSMetaClassBase::type_id;
#define OSTypeID(T) T::type_id
#define OSTypeAlloc(T) new T
#define OSDynamicCast(T, p) dynamic_cast<T *>(p)
#define LIBKERN_SMART_POINTERS
#include <libkern/c++/OSPtr.h>
class Base : public OSMetaClassBase {
public:
Base() : OSMetaClassBase()
{
}
};
class Derived : public Base {
public:
Derived() : Base()
{
}
};
class Other : public OSMetaClassBase {
public:
Other() : OSMetaClassBase()
{
}
};
typedef OSPtr<Base> BasePtr;
typedef OSPtr<Derived> DerivedPtr;
typedef OSPtr<Other> OtherPtr;
static void
default_constructor()
{
BasePtr a;
T_ASSERT_NULL(a.get(), "Default NULL construction");
T_ASSERT_EQ_INT(num_instances, 0, "No instances created");
}
static void
null_constructor()
{
BasePtr a(nullptr);
T_ASSERT_NULL(a.get(), "Default NULL construction");
T_ASSERT_EQ_INT(num_instances, 0, "No instances created");
}
static void
raw_constructor()
{
Base *a = new Base();
T_ASSERT_EQ_INT(num_instances, 1, "Created instance");
{
BasePtr p(a);
T_ASSERT_EQ_INT(num_instances, 1, "No new instance");
T_ASSERT_EQ_PTR(p.get(), a, "osptr bound to correct object");
T_ASSERT_EQ_INT(a->refcount, 2, "Object refcount incremented");
}
T_ASSERT_EQ_INT(a->refcount, 1, "Object refcount decremented");
a->release();
T_ASSERT_EQ_INT(num_instances, 0, "All instances released");
}
static void
alloc()
{
BasePtr a = BasePtr::alloc();
T_ASSERT_NOTNULL(a.get(), "osptr seated");
T_ASSERT_EQ_INT(num_instances, 1, "Instance created");
T_ASSERT_EQ_INT(a->refcount, 1, "Reference created");
}
static void
destroy()
{
{
BasePtr a = BasePtr::alloc();
T_ASSERT_EQ_INT(num_instances, 1, "Instance created");
}
T_ASSERT_EQ_INT(num_instances, 0, "All instances released");
}
static void
copy()
{
BasePtr a = BasePtr::alloc();
BasePtr b;
int a_id = a->inst_id;
BasePtr a_copy(a);
T_ASSERT_EQ_INT(a_copy->inst_id, a_id, NULL);
T_ASSERT_EQ_INT(a->refcount, 2, NULL);
T_ASSERT_EQ_INT(a_copy->refcount, 2, NULL);
T_ASSERT_EQ_INT(num_instances, 1, NULL);
T_EXPECT_EQ_INT(num_retains, 1, NULL);
BasePtr b_copy(b);
T_ASSERT_NULL(b_copy.get(), "Copy null osptr");
T_ASSERT_EQ_INT(num_instances, 1, NULL);
T_EXPECT_EQ_INT(num_retains, 1, NULL);
BasePtr a_copy2 = a;
T_ASSERT_EQ_PTR(a_copy2.get(), a.get(), NULL);
T_ASSERT_EQ_INT(num_instances, 1, NULL);
T_EXPECT_EQ_INT(num_retains, 2, NULL);
T_EXPECT_EQ_INT(num_releases, 0, NULL);
}
static void
copy_subclass()
{
auto a = DerivedPtr::alloc();
BasePtr b(a);
T_ASSERT_EQ_PTR(a.get(), b.get(), NULL);
T_ASSERT_EQ_INT(b->refcount, 2, NULL);
T_ASSERT_EQ_INT(num_instances, 1, NULL);
a = nullptr;
T_ASSERT_NOTNULL(b.get(), NULL);
T_ASSERT_EQ_INT(b->refcount, 1, NULL);
T_ASSERT_EQ_INT(num_instances, 1, NULL);
}
static void
assign()
{
int a_id, b_id;
BasePtr p;
BasePtr a = BasePtr::alloc();
BasePtr b = BasePtr::alloc();
a_id = a->inst_id;
b_id = b->inst_id;
p = a;
T_ASSERT_EQ_PTR(p.get(), a.get(), "Assigned osptr references same object");
T_ASSERT_EQ_INT(p->inst_id, a_id, NULL);
T_ASSERT_EQ_INT(a->refcount, 2, "Assigned osptr bumps refcount");
T_QUIET; T_ASSERT_TRUE(b->refcount == 1, NULL);
p = b;
T_ASSERT_EQ_PTR(p.get(), b.get(), "Assigned osptr references same object");
T_ASSERT_EQ_INT(p->inst_id, b_id, NULL);
T_ASSERT_EQ_INT(a->refcount, 1, "Previous assignee drops reference");
T_ASSERT_EQ_INT(b->refcount, 2, "New assignee bumps reference");
T_ASSERT_EQ_INT(a->inst_id, a_id, NULL);
T_ASSERT_EQ_INT(b->inst_id, b_id, NULL);
a = nullptr;
T_ASSERT_EQ_INT(num_instances, 1, "Assignment to null releases object");
b = nullptr;
p = nullptr;
T_ASSERT_EQ_INT(num_instances, 0, "All instances released");
}
static void
assign_raw()
{
Base *a1 = new Base();
Base *a2 = new Base();
{
BasePtr p;
p = a1;
T_ASSERT_EQ_PTR(p.get(), a1, NULL);
T_ASSERT_EQ_INT(a1->refcount, 2, NULL);
T_ASSERT_EQ_INT(a2->refcount, 1, NULL);
p = a2;
T_ASSERT_EQ_PTR(p.get(), a2, NULL);
T_ASSERT_EQ_INT(a1->refcount, 1, NULL);
T_ASSERT_EQ_INT(a2->refcount, 2, NULL);
}
T_ASSERT_EQ_INT(a1->refcount, 1, NULL);
T_ASSERT_EQ_INT(a2->refcount, 1, NULL);
a1->release();
a2->release();
T_ASSERT_EQ_INT(num_instances, 0, "All instances released");
}
static void
assign_null()
{
BasePtr a = BasePtr::alloc();
T_ASSERT_EQ_INT(num_instances, 1, NULL);
a = nullptr;
T_ASSERT_NULL(a.get(), NULL);
T_ASSERT_EQ_INT(num_instances, 0, "No instances created");
a = BasePtr::alloc();
BasePtr b(a.get());
T_ASSERT_EQ_INT(a->refcount, 2, NULL);
T_ASSERT_EQ_INT(num_instances, 1, NULL);
b = nullptr;
T_ASSERT_EQ_INT(a->refcount, 1, NULL);
T_ASSERT_EQ_INT(num_instances, 1, NULL);
a = nullptr;
T_ASSERT_EQ_INT(num_instances, 0, "All instances released");
}
static void
assign_subclass()
{
int a_id, b_id;
OSPtr<OSMetaClassBase> base;
BasePtr a = BasePtr::alloc();
BasePtr b = BasePtr::alloc();
a_id = a->inst_id;
b_id = b->inst_id;
base = a;
T_ASSERT_TRUE(base.get() == static_cast<OSMetaClassBase *>(a.get()), NULL);
T_ASSERT_TRUE(base->inst_id == a_id, NULL);
T_ASSERT_TRUE(a->refcount == 2, NULL);
T_ASSERT_TRUE(b->refcount == 1, NULL);
base = b;
T_ASSERT_TRUE(base.get() == static_cast<OSMetaClassBase *>(b.get()), NULL);
T_ASSERT_TRUE(base->inst_id == b_id, NULL);
T_ASSERT_TRUE(a->refcount == 1, NULL);
T_ASSERT_TRUE(b->refcount == 2, NULL);
T_ASSERT_TRUE(a->inst_id == a_id, NULL);
T_ASSERT_TRUE(b->inst_id == b_id, NULL);
a = nullptr;
T_ASSERT_TRUE(num_instances == 1, NULL);
b = nullptr;
base = nullptr;
T_ASSERT_EQ_INT(num_instances, 0, "All instances released");
}
static void
assign_compatible()
{
OSPtr<Base> a = OSPtr<Base>::alloc();
OSPtr<const Base> b = a;
T_ASSERT_EQ_PTR(a.get(), b.get(), NULL);
OSPtr<Derived> c = OSPtr<Derived>::alloc();
OSPtr<Base> d = c;
T_ASSERT_EQ_PTR(c.get(), d.get(), NULL);
}
static void
move()
{
OSPtr<const Base> a = OSPtr<const Base>::alloc();
int a_id = a->inst_id;
OSPtr<const Base> b(os::move(a));
T_ASSERT_TRUE(a.get() == NULL, NULL);
T_ASSERT_TRUE(b->inst_id == a_id, NULL);
T_ASSERT_TRUE(b->refcount == 1, NULL);
T_ASSERT_TRUE(num_instances == 1, NULL);
T_EXPECT_EQ_INT(num_retains, 0, NULL);
}
static void
move_assign()
{
OSPtr<const Base> a = OSPtr<const Base>::alloc();
OSPtr<const Base> b = OSPtr<const Base>::alloc();
int a_id = a->inst_id;
int b_id = b->inst_id;
OSPtr<const Base> d;
d = os::move(a);
T_ASSERT_TRUE(a.get() == NULL, NULL);
T_ASSERT_TRUE(d->inst_id == a_id, NULL);
T_ASSERT_TRUE(d->refcount == 1, NULL);
T_ASSERT_TRUE(num_instances == 2, NULL);
d = os::move(b);
T_ASSERT_TRUE(a.get() == NULL, NULL);
T_ASSERT_TRUE(b.get() == NULL, NULL);
T_ASSERT_TRUE(d->inst_id == b_id, NULL);
T_ASSERT_TRUE(d->refcount == 1, NULL);
T_ASSERT_TRUE(num_instances == 1, NULL);
T_EXPECT_EQ_INT(num_retains, 0, NULL);
}
static void
move_assign_null()
{
BasePtr a = BasePtr::alloc();
BasePtr b = a;
T_EXPECT_EQ_INT(num_retains, 1, NULL);
a = os::move(nullptr);
T_ASSERT_TRUE(a.get() == NULL, NULL);
T_ASSERT_TRUE(b->refcount == 1, NULL);
b = os::move(nullptr);
T_ASSERT_EQ_INT(num_instances, 0, "All instances released");
T_EXPECT_EQ_INT(num_retains, 1, NULL);
}
static void
move_assign_raw()
{
BasePtr a = BasePtr::alloc();
Base *b = new Base;
Base *tmp = b;
T_ASSERT_EQ_INT(num_instances, 2, NULL);
a = os::move(tmp);
T_ASSERT_EQ_INT(num_instances, 1, NULL);
T_ASSERT_NULL(tmp, NULL);
T_ASSERT_EQ_PTR(a.get(), b, NULL);
T_ASSERT_EQ_INT(a->refcount, 2, NULL);
b->release();
T_ASSERT_EQ_INT(a->refcount, 1, NULL);
}
static void
move_assign_subclass()
{
auto a = DerivedPtr::alloc();
BasePtr b;
b = os::move(a);
T_ASSERT_NULL(a.get(), NULL);
T_ASSERT_NOTNULL(b.get(), NULL);
T_ASSERT_EQ_INT(b->refcount, 1, NULL);
T_ASSERT_EQ_INT(num_instances, 1, NULL);
}
static void
move_assign_self()
{
OSPtr<const Base> a = OSPtr<const Base>::alloc();
int a_id = a->inst_id;
a = os::move(a);
T_ASSERT_NOTNULL(a.get(), "osptr seated");
T_ASSERT_TRUE(a->inst_id == a_id, NULL);
T_ASSERT_TRUE(a->refcount == 1, NULL);
T_ASSERT_TRUE(num_instances == 1, NULL);
T_EXPECT_EQ_INT(num_retains, 0, NULL);
}
static void
test_const_cast()
{
OSPtr<const Base> a = OSPtr<const Base>::alloc();
OSPtr<Base> b;
b = a.const_pointer_cast<Base>();
T_ASSERT_TRUE(a.get() == b.get(), NULL);
T_ASSERT_TRUE(a->refcount == 2, NULL);
T_ASSERT_TRUE(b->refcount == 2, NULL);
T_ASSERT_TRUE(num_instances == 1, NULL);
T_EXPECT_EQ_INT(num_retains, 1, NULL);
}
static void
const_cast_move()
{
OSPtr<const Base> a = OSPtr<const Base>::alloc();
int a_id = a->inst_id;
OSPtr<Base> b;
b = os::move(a).const_pointer_cast<Base>();
T_ASSERT_TRUE(a.get() == NULL, NULL);
T_ASSERT_TRUE(b->inst_id == a_id, NULL);
T_ASSERT_TRUE(b->refcount == 1, NULL);
T_ASSERT_TRUE(num_instances == 1, NULL);
T_EXPECT_EQ_INT(num_retains, 0, NULL);
}
static void
const_cast_move_self()
{
BasePtr a = BasePtr::alloc();
int a_id = a->inst_id;
a = os::move(a).const_pointer_cast<Base>();
T_ASSERT_NOTNULL(a.get(), "osptr seated");
T_ASSERT_TRUE(a->inst_id == a_id, NULL);
T_ASSERT_TRUE(a->refcount == 1, NULL);
T_ASSERT_TRUE(num_instances == 1, NULL);
T_ASSERT_TRUE(num_retains == 0, NULL);
}
static void
test_static_cast()
{
DerivedPtr a = DerivedPtr::alloc();
BasePtr b;
b = a.static_pointer_cast<Base>();
T_ASSERT_TRUE(a.get() == b.get(), NULL);
T_ASSERT_TRUE(a->refcount == 2, NULL);
T_ASSERT_TRUE(b->refcount == 2, NULL);
T_ASSERT_TRUE(num_instances == 1, NULL);
T_EXPECT_TRUE(num_retains == 1, NULL);
}
static void
static_cast_move()
{
DerivedPtr a = DerivedPtr::alloc();
int a_id = a->inst_id;
BasePtr b;
b = os::move(a).static_pointer_cast<Base>();
T_ASSERT_NULL(a.get(), NULL);
T_ASSERT_EQ_INT(b->inst_id, a_id, NULL);
T_ASSERT_EQ_INT(b->refcount, 1, NULL);
T_ASSERT_EQ_INT(num_instances, 1, NULL);
T_EXPECT_EQ_INT(num_retains, 0, NULL);
}
static void
static_cast_move_self()
{
BasePtr a = BasePtr::alloc();
int a_id = a->inst_id;
a = os::move(a).static_pointer_cast<Base>();
T_ASSERT_NOTNULL(a.get(), "osptr seated");
T_ASSERT_TRUE(a->inst_id == a_id, NULL);
T_ASSERT_TRUE(a->refcount == 1, NULL);
T_ASSERT_TRUE(num_instances == 1, NULL);
T_ASSERT_TRUE(num_retains == 0, NULL);
}
static void
tagged_ptr()
{
OSTaggedPtr<Base, Derived> a;
auto b = OSTaggedPtr<Derived, Base>::alloc();
T_ASSERT_NULL(a.get(), NULL);
T_ASSERT_NOTNULL(b.get(), NULL);
T_ASSERT_TRUE(typeid(a.get()) == typeid(Base *), NULL);
T_ASSERT_TRUE(typeid(b.get()) == typeid(Derived *), NULL);
}
static void
attach()
{
Base *a = new Base();
BasePtr b;
b.attach(os::move(a));
T_ASSERT_NULL(a, NULL);
T_ASSERT_NOTNULL(b.get(), NULL);
T_ASSERT_EQ_INT(b->refcount, 1, NULL);
T_ASSERT_EQ_INT(num_instances, 1, NULL);
T_ASSERT_EQ_INT(num_retains, 0, NULL);
b.attach(new Base);
T_ASSERT_NOTNULL(b.get(), NULL);
T_ASSERT_EQ_INT(b->refcount, 1, NULL);
T_ASSERT_EQ_INT(num_instances, 1, NULL);
T_ASSERT_EQ_INT(num_retains, 0, NULL);
T_ASSERT_EQ_INT(num_releases, 1, NULL);
}
static void
detach()
{
BasePtr a = BasePtr::alloc();
Base *p = a.detach();
T_ASSERT_NULL(a.get(), NULL);
T_ASSERT_NOTNULL(p, NULL);
T_ASSERT_EQ_INT(p->refcount, 1, NULL);
T_ASSERT_EQ_INT(num_instances, 1, NULL);
T_ASSERT_EQ_INT(num_retains, 0, NULL);
T_ASSERT_EQ_INT(num_releases, 0, NULL);
BasePtr b(os::move(p), os::no_retain); }
static void
foreign()
{
auto a = OSPtr<Base>::alloc();
auto b = OSTaggedPtr<Base, Derived>::alloc();
void *a_ptr = a.get();
void *b_ptr = b.get();
a.swap(b);
T_ASSERT_EQ_PTR(b.get(), a_ptr, NULL);
T_ASSERT_EQ_PTR(a.get(), b_ptr, NULL);
T_ASSERT_EQ_INT(a->refcount, 1, NULL);
T_ASSERT_EQ_INT(b->refcount, 1, NULL);
T_ASSERT_EQ_INT(num_instances, 2, NULL);
T_ASSERT_GE_INT(num_retains, 2, NULL);
}
static void
test_dynamic_cast()
{
auto a = DerivedPtr::alloc();
T_ASSERT_NOTNULL(a.get(), NULL);
BasePtr b = a;
auto c = b.dynamic_pointer_cast<Derived>();
T_ASSERT_NOTNULL(c.get(), NULL);
T_ASSERT_EQ_INT(c->refcount, 3, NULL);
T_ASSERT_EQ_INT(num_instances, 1, NULL);
auto d = OtherPtr::alloc();
auto e = d.dynamic_pointer_cast<Derived>();
auto f = OSDynamicCastPtr<Derived>(OtherPtr::alloc());
T_ASSERT_NULL(e.get(), NULL);
T_ASSERT_NULL(f.get(), NULL);
T_ASSERT_EQ_INT(num_instances, 2, NULL);
T_ASSERT_EQ_INT(d->refcount, 1, NULL);
auto g = OSDynamicCastPtr<Base>(DerivedPtr::alloc());
T_ASSERT_EQ_INT(num_instances, 3, NULL);
T_ASSERT_EQ_INT(g->refcount, 1, NULL);
}
#define OSPTR_TEST_DECL(name) \
T_DECL(name, #name) { \
num_instances = 0; \
num_retains = 0; \
num_releases = 0; \
name(); \
T_QUIET; T_ASSERT_EQ_INT(num_instances, 0, "Instance leak"); \
}
OSPTR_TEST_DECL(default_constructor)
OSPTR_TEST_DECL(null_constructor)
OSPTR_TEST_DECL(raw_constructor)
OSPTR_TEST_DECL(alloc)
OSPTR_TEST_DECL(destroy)
OSPTR_TEST_DECL(copy)
OSPTR_TEST_DECL(copy_subclass)
OSPTR_TEST_DECL(assign)
OSPTR_TEST_DECL(assign_raw)
OSPTR_TEST_DECL(assign_null)
OSPTR_TEST_DECL(assign_subclass)
OSPTR_TEST_DECL(assign_compatible)
OSPTR_TEST_DECL(move)
OSPTR_TEST_DECL(move_assign)
OSPTR_TEST_DECL(move_assign_null)
OSPTR_TEST_DECL(move_assign_raw)
OSPTR_TEST_DECL(move_assign_subclass)
OSPTR_TEST_DECL(move_assign_self)
OSPTR_TEST_DECL(test_const_cast)
OSPTR_TEST_DECL(const_cast_move)
OSPTR_TEST_DECL(const_cast_move_self)
OSPTR_TEST_DECL(test_static_cast)
OSPTR_TEST_DECL(static_cast_move)
OSPTR_TEST_DECL(static_cast_move_self)
OSPTR_TEST_DECL(tagged_ptr)
OSPTR_TEST_DECL(attach)
OSPTR_TEST_DECL(detach)
OSPTR_TEST_DECL(foreign)
OSPTR_TEST_DECL(test_dynamic_cast)
struct Complex {
uintptr_t val;
Complex() : val(71)
{
}
~Complex()
{
}
};
struct Trivial {
uintptr_t val;
Trivial() : val(42)
{
}
~Trivial()
{
}
} __attribute__((trivial_abi));
__BEGIN_DECLS
extern uintptr_t pass_trivial(Trivial);
extern uintptr_t pass_complex(Complex);
__END_DECLS
Trivial return_trivial(uintptr_t);
Complex return_complex(uintptr_t);
T_DECL(trivial_abi, "Test trivial_abi classes are passed by value")
{
Trivial a;
uintptr_t x = pass_trivial(a);
T_EXPECT_EQ_ULONG(a.val, x, "Trivial class argument passed by-value");
Complex b;
uintptr_t y = pass_complex(b);
T_EXPECT_NE_ULONG(b.val, y, "Non-trivial class argument passed by-reference");
Trivial c = return_trivial(55);
T_EXPECT_EQ_ULONG(c.val, 55UL, "Trivial class returned by-value");
Complex d = return_complex(99);
T_EXPECT_NE_ULONG(d.val, 99UL, "Non-trivial class returned by-reference");
}
#pragma clang diagnostic pop