adoptRef in WebKit constructs Ref/RefPtr so treat it as such in isCtorOfRefCounted. Also removed the support for makeRef and makeRefPtr as they don't exist any more.
243 lines
6.4 KiB
C++
243 lines
6.4 KiB
C++
// RUN: %clang_analyze_cc1 -analyzer-checker=webkit.RefCntblBaseVirtualDtor -verify %s
|
|
|
|
#include "mock-types.h"
|
|
|
|
namespace Detail {
|
|
|
|
template<typename Out, typename... In>
|
|
class CallableWrapperBase {
|
|
public:
|
|
virtual ~CallableWrapperBase() { }
|
|
virtual Out call(In...) = 0;
|
|
};
|
|
|
|
template<typename, typename, typename...> class CallableWrapper;
|
|
|
|
template<typename CallableType, typename Out, typename... In>
|
|
class CallableWrapper : public CallableWrapperBase<Out, In...> {
|
|
public:
|
|
explicit CallableWrapper(CallableType&& callable)
|
|
: m_callable(WTFMove(callable)) { }
|
|
CallableWrapper(const CallableWrapper&) = delete;
|
|
CallableWrapper& operator=(const CallableWrapper&) = delete;
|
|
Out call(In... in) final;
|
|
private:
|
|
CallableType m_callable;
|
|
};
|
|
|
|
} // namespace Detail
|
|
|
|
template<typename> class Function;
|
|
|
|
template<typename Out, typename... In> Function<Out(In...)> adopt(Detail::CallableWrapperBase<Out, In...>*);
|
|
|
|
template <typename Out, typename... In>
|
|
class Function<Out(In...)> {
|
|
public:
|
|
using Impl = Detail::CallableWrapperBase<Out, In...>;
|
|
|
|
Function() = default;
|
|
|
|
template<typename FunctionType>
|
|
Function(FunctionType f);
|
|
|
|
Out operator()(In... in) const;
|
|
explicit operator bool() const { return !!m_callableWrapper; }
|
|
|
|
private:
|
|
enum AdoptTag { Adopt };
|
|
Function(Impl* impl, AdoptTag)
|
|
: m_callableWrapper(impl)
|
|
{
|
|
}
|
|
|
|
friend Function adopt<Out, In...>(Impl*);
|
|
|
|
Impl* m_callableWrapper;
|
|
};
|
|
|
|
template<typename Out, typename... In> Function<Out(In...)> adopt(Detail::CallableWrapperBase<Out, In...>* impl)
|
|
{
|
|
return Function<Out(In...)>(impl, Function<Out(In...)>::Adopt);
|
|
}
|
|
|
|
enum class DestructionThread : unsigned char { Any, Main, MainRunLoop };
|
|
void ensureOnMainThread(Function<void()>&&); // Sync if called on main thread, async otherwise.
|
|
void ensureOnMainRunLoop(Function<void()>&&); // Sync if called on main run loop, async otherwise.
|
|
|
|
class ThreadSafeRefCountedBase {
|
|
public:
|
|
ThreadSafeRefCountedBase() = default;
|
|
|
|
void ref() const
|
|
{
|
|
++m_refCount;
|
|
}
|
|
|
|
bool hasOneRef() const
|
|
{
|
|
return refCount() == 1;
|
|
}
|
|
|
|
unsigned refCount() const
|
|
{
|
|
return m_refCount;
|
|
}
|
|
|
|
protected:
|
|
bool derefBase() const
|
|
{
|
|
if (!--m_refCount) {
|
|
m_refCount = 1;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
mutable unsigned m_refCount { 1 };
|
|
};
|
|
|
|
template<class T, DestructionThread destructionThread = DestructionThread::Any> class ThreadSafeRefCounted : public ThreadSafeRefCountedBase {
|
|
public:
|
|
void deref() const
|
|
{
|
|
if (!derefBase())
|
|
return;
|
|
|
|
if constexpr (destructionThread == DestructionThread::Any) {
|
|
delete static_cast<const T*>(this);
|
|
} else if constexpr (destructionThread == DestructionThread::Main) {
|
|
ensureOnMainThread([this] {
|
|
delete static_cast<const T*>(this);
|
|
});
|
|
} else if constexpr (destructionThread == DestructionThread::MainRunLoop) {
|
|
auto deleteThis = [this] {
|
|
delete static_cast<const T*>(this);
|
|
};
|
|
ensureOnMainThread(deleteThis);
|
|
}
|
|
}
|
|
|
|
protected:
|
|
ThreadSafeRefCounted() = default;
|
|
};
|
|
|
|
class FancyRefCountedClass final : public ThreadSafeRefCounted<FancyRefCountedClass, DestructionThread::Main> {
|
|
public:
|
|
static Ref<FancyRefCountedClass> create()
|
|
{
|
|
return adoptRef(*new FancyRefCountedClass());
|
|
}
|
|
|
|
virtual ~FancyRefCountedClass();
|
|
|
|
private:
|
|
FancyRefCountedClass();
|
|
};
|
|
|
|
template<class T, DestructionThread destructionThread = DestructionThread::Any> class BadThreadSafeRefCounted : public ThreadSafeRefCountedBase {
|
|
public:
|
|
void deref() const
|
|
{
|
|
if (!derefBase())
|
|
return;
|
|
|
|
[this] {
|
|
delete static_cast<const T*>(this);
|
|
};
|
|
}
|
|
|
|
protected:
|
|
BadThreadSafeRefCounted() = default;
|
|
};
|
|
|
|
class FancyRefCountedClass2 final : public ThreadSafeRefCounted<FancyRefCountedClass, DestructionThread::Main> {
|
|
// expected-warning@-1{{Class 'ThreadSafeRefCounted<FancyRefCountedClass, DestructionThread::Main>' is used as a base of class 'FancyRefCountedClass2' but doesn't have virtual destructor}}
|
|
public:
|
|
static Ref<FancyRefCountedClass2> create()
|
|
{
|
|
return adoptRef(*new FancyRefCountedClass2());
|
|
}
|
|
|
|
virtual ~FancyRefCountedClass2();
|
|
|
|
private:
|
|
FancyRefCountedClass2();
|
|
};
|
|
|
|
template<class T, DestructionThread destructionThread = DestructionThread::Any> class NestedThreadSafeRefCounted : public ThreadSafeRefCountedBase {
|
|
public:
|
|
void deref() const
|
|
{
|
|
if (!derefBase())
|
|
return;
|
|
ensureOnMainRunLoop([&] {
|
|
auto destroyThis = [&] {
|
|
delete static_cast<const T*>(this);
|
|
};
|
|
destroyThis();
|
|
});
|
|
}
|
|
|
|
protected:
|
|
NestedThreadSafeRefCounted() = default;
|
|
};
|
|
|
|
class FancyRefCountedClass3 final : public NestedThreadSafeRefCounted<FancyRefCountedClass3, DestructionThread::Main> {
|
|
public:
|
|
static Ref<FancyRefCountedClass3> create()
|
|
{
|
|
return adoptRef(*new FancyRefCountedClass3());
|
|
}
|
|
|
|
virtual ~FancyRefCountedClass3();
|
|
|
|
private:
|
|
FancyRefCountedClass3();
|
|
};
|
|
|
|
template<class T, DestructionThread destructionThread = DestructionThread::Any> class BadNestedThreadSafeRefCounted : public ThreadSafeRefCountedBase {
|
|
public:
|
|
void deref() const
|
|
{
|
|
if (!derefBase())
|
|
return;
|
|
ensureOnMainThread([&] {
|
|
auto destroyThis = [&] {
|
|
delete static_cast<const T*>(this);
|
|
};
|
|
});
|
|
}
|
|
|
|
protected:
|
|
BadNestedThreadSafeRefCounted() = default;
|
|
};
|
|
|
|
class FancyRefCountedClass4 final : public BadNestedThreadSafeRefCounted<FancyRefCountedClass4, DestructionThread::Main> {
|
|
// expected-warning@-1{{Class 'BadNestedThreadSafeRefCounted<FancyRefCountedClass4, DestructionThread::Main>' is used as a base of class 'FancyRefCountedClass4' but doesn't have virtual destructor}}
|
|
public:
|
|
static Ref<FancyRefCountedClass4> create()
|
|
{
|
|
return adoptRef(*new FancyRefCountedClass4());
|
|
}
|
|
|
|
virtual ~FancyRefCountedClass4();
|
|
|
|
private:
|
|
FancyRefCountedClass4();
|
|
};
|
|
|
|
class FancyRefCountedClass5 final : public ThreadSafeRefCounted<FancyRefCountedClass5, DestructionThread::MainRunLoop> {
|
|
public:
|
|
static Ref<FancyRefCountedClass5> create()
|
|
{
|
|
return adoptRef(*new FancyRefCountedClass5());
|
|
}
|
|
|
|
virtual ~FancyRefCountedClass5();
|
|
|
|
private:
|
|
FancyRefCountedClass5();
|
|
};
|