summaryrefslogtreecommitdiff
path: root/xpcom/glue/nsISupportsImpl.h
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/glue/nsISupportsImpl.h')
-rw-r--r--xpcom/glue/nsISupportsImpl.h1090
1 files changed, 1090 insertions, 0 deletions
diff --git a/xpcom/glue/nsISupportsImpl.h b/xpcom/glue/nsISupportsImpl.h
new file mode 100644
index 0000000000..26db3c525d
--- /dev/null
+++ b/xpcom/glue/nsISupportsImpl.h
@@ -0,0 +1,1090 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+// IWYU pragma: private, include "nsISupports.h"
+
+
+#ifndef nsISupportsImpl_h__
+#define nsISupportsImpl_h__
+
+#include "nscore.h"
+#include "nsISupportsBase.h"
+#include "nsISupportsUtils.h"
+
+
+#if !defined(XPCOM_GLUE_AVOID_NSPR)
+#include "prthread.h" /* needed for thread-safety checks */
+#endif // !XPCOM_GLUE_AVOID_NSPR
+
+#include "nsDebug.h"
+#include "nsXPCOM.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Compiler.h"
+#include "mozilla/Likely.h"
+#include "mozilla/MacroArgs.h"
+#include "mozilla/MacroForEach.h"
+#include "mozilla/TypeTraits.h"
+
+#define MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(X) \
+ static_assert(!mozilla::IsDestructible<X>::value, \
+ "Reference-counted class " #X " should not have a public destructor. " \
+ "Make this class's destructor non-public");
+
+inline nsISupports*
+ToSupports(nsISupports* aSupports)
+{
+ return aSupports;
+}
+
+inline nsISupports*
+ToCanonicalSupports(nsISupports* aSupports)
+{
+ return nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Macros to help detect thread-safety:
+
+#ifdef MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED
+
+class nsAutoOwningThread
+{
+public:
+ nsAutoOwningThread() { mThread = PR_GetCurrentThread(); }
+ void* GetThread() const { return mThread; }
+
+private:
+ void* mThread;
+};
+
+#define NS_DECL_OWNINGTHREAD nsAutoOwningThread _mOwningThread;
+#define NS_ASSERT_OWNINGTHREAD_AGGREGATE(agg, _class) \
+ NS_CheckThreadSafe(agg->_mOwningThread.GetThread(), #_class " not thread-safe")
+#define NS_ASSERT_OWNINGTHREAD(_class) NS_ASSERT_OWNINGTHREAD_AGGREGATE(this, _class)
+#else // !DEBUG && !(NIGHTLY_BUILD && !MOZ_PROFILING)
+
+#define NS_DECL_OWNINGTHREAD /* nothing */
+#define NS_ASSERT_OWNINGTHREAD_AGGREGATE(agg, _class) ((void)0)
+#define NS_ASSERT_OWNINGTHREAD(_class) ((void)0)
+
+#endif // DEBUG || (NIGHTLY_BUILD && !MOZ_PROFILING)
+
+
+// Macros for reference-count and constructor logging
+
+#if defined(NS_BUILD_REFCNT_LOGGING)
+
+#define NS_LOG_ADDREF(_p, _rc, _type, _size) \
+ NS_LogAddRef((_p), (_rc), (_type), (uint32_t) (_size))
+
+#define NS_LOG_RELEASE(_p, _rc, _type) \
+ NS_LogRelease((_p), (_rc), (_type))
+
+#include "mozilla/TypeTraits.h"
+#define MOZ_ASSERT_CLASSNAME(_type) \
+ static_assert(mozilla::IsClass<_type>::value, \
+ "Token '" #_type "' is not a class type.")
+
+// Note that the following constructor/destructor logging macros are redundant
+// for refcounted objects that log via the NS_LOG_ADDREF/NS_LOG_RELEASE macros.
+// Refcount logging is preferred.
+#define MOZ_COUNT_CTOR(_type) \
+do { \
+ MOZ_ASSERT_CLASSNAME(_type); \
+ NS_LogCtor((void*)this, #_type, sizeof(*this)); \
+} while (0)
+
+#define MOZ_COUNT_CTOR_INHERITED(_type, _base) \
+do { \
+ MOZ_ASSERT_CLASSNAME(_type); \
+ MOZ_ASSERT_CLASSNAME(_base); \
+ NS_LogCtor((void*)this, #_type, sizeof(*this) - sizeof(_base)); \
+} while (0)
+
+#define MOZ_LOG_CTOR(_ptr, _name, _size) \
+do { \
+ NS_LogCtor((void*)_ptr, _name, _size); \
+} while (0)
+
+#define MOZ_COUNT_DTOR(_type) \
+do { \
+ MOZ_ASSERT_CLASSNAME(_type); \
+ NS_LogDtor((void*)this, #_type, sizeof(*this)); \
+} while (0)
+
+#define MOZ_COUNT_DTOR_INHERITED(_type, _base) \
+do { \
+ MOZ_ASSERT_CLASSNAME(_type); \
+ MOZ_ASSERT_CLASSNAME(_base); \
+ NS_LogDtor((void*)this, #_type, sizeof(*this) - sizeof(_base)); \
+} while (0)
+
+#define MOZ_LOG_DTOR(_ptr, _name, _size) \
+do { \
+ NS_LogDtor((void*)_ptr, _name, _size); \
+} while (0)
+
+/* nsCOMPtr.h allows these macros to be defined by clients
+ * These logging functions require dynamic_cast<void*>, so they don't
+ * do anything useful if we don't have dynamic_cast<void*>.
+ * Note: The explicit comparison to nullptr is needed to avoid warnings
+ * when _p is a nullptr itself. */
+#define NSCAP_LOG_ASSIGNMENT(_c, _p) \
+ if (_p != nullptr) \
+ NS_LogCOMPtrAddRef((_c),static_cast<nsISupports*>(_p))
+
+#define NSCAP_LOG_RELEASE(_c, _p) \
+ if (_p) \
+ NS_LogCOMPtrRelease((_c), static_cast<nsISupports*>(_p))
+
+#else /* !NS_BUILD_REFCNT_LOGGING */
+
+#define NS_LOG_ADDREF(_p, _rc, _type, _size)
+#define NS_LOG_RELEASE(_p, _rc, _type)
+#define MOZ_COUNT_CTOR(_type)
+#define MOZ_COUNT_CTOR_INHERITED(_type, _base)
+#define MOZ_LOG_CTOR(_ptr, _name, _size)
+#define MOZ_COUNT_DTOR(_type)
+#define MOZ_COUNT_DTOR_INHERITED(_type, _base)
+#define MOZ_LOG_DTOR(_ptr, _name, _size)
+
+#endif /* NS_BUILD_REFCNT_LOGGING */
+
+
+// Support for ISupports classes which interact with cycle collector.
+
+#define NS_NUMBER_OF_FLAGS_IN_REFCNT 2
+#define NS_IN_PURPLE_BUFFER (1 << 0)
+#define NS_IS_PURPLE (1 << 1)
+#define NS_REFCOUNT_CHANGE (1 << NS_NUMBER_OF_FLAGS_IN_REFCNT)
+#define NS_REFCOUNT_VALUE(_val) (_val >> NS_NUMBER_OF_FLAGS_IN_REFCNT)
+
+class nsCycleCollectingAutoRefCnt
+{
+public:
+ nsCycleCollectingAutoRefCnt() : mRefCntAndFlags(0) {}
+
+ explicit nsCycleCollectingAutoRefCnt(uintptr_t aValue)
+ : mRefCntAndFlags(aValue << NS_NUMBER_OF_FLAGS_IN_REFCNT)
+ {
+ }
+
+ nsCycleCollectingAutoRefCnt(const nsCycleCollectingAutoRefCnt&) = delete;
+ void operator=(const nsCycleCollectingAutoRefCnt&) = delete;
+
+ MOZ_ALWAYS_INLINE uintptr_t incr(nsISupports* aOwner)
+ {
+ return incr(aOwner, nullptr);
+ }
+
+ MOZ_ALWAYS_INLINE uintptr_t incr(void* aOwner,
+ nsCycleCollectionParticipant* aCp)
+ {
+ mRefCntAndFlags += NS_REFCOUNT_CHANGE;
+ mRefCntAndFlags &= ~NS_IS_PURPLE;
+ // For incremental cycle collection, use the purple buffer to track objects
+ // that have been AddRef'd.
+ if (!IsInPurpleBuffer()) {
+ mRefCntAndFlags |= NS_IN_PURPLE_BUFFER;
+ // Refcount isn't zero, so Suspect won't delete anything.
+ MOZ_ASSERT(get() > 0);
+ NS_CycleCollectorSuspect3(aOwner, aCp, this, nullptr);
+ }
+ return NS_REFCOUNT_VALUE(mRefCntAndFlags);
+ }
+
+ MOZ_ALWAYS_INLINE void stabilizeForDeletion()
+ {
+ // Set refcnt to 1 and mark us to be in the purple buffer.
+ // This way decr won't call suspect again.
+ mRefCntAndFlags = NS_REFCOUNT_CHANGE | NS_IN_PURPLE_BUFFER;
+ }
+
+ MOZ_ALWAYS_INLINE uintptr_t decr(nsISupports* aOwner,
+ bool* aShouldDelete = nullptr)
+ {
+ return decr(aOwner, nullptr, aShouldDelete);
+ }
+
+ MOZ_ALWAYS_INLINE uintptr_t decr(void* aOwner,
+ nsCycleCollectionParticipant* aCp,
+ bool* aShouldDelete = nullptr)
+ {
+ MOZ_ASSERT(get() > 0);
+ if (!IsInPurpleBuffer()) {
+ mRefCntAndFlags -= NS_REFCOUNT_CHANGE;
+ mRefCntAndFlags |= (NS_IN_PURPLE_BUFFER | NS_IS_PURPLE);
+ uintptr_t retval = NS_REFCOUNT_VALUE(mRefCntAndFlags);
+ // Suspect may delete 'aOwner' and 'this'!
+ NS_CycleCollectorSuspect3(aOwner, aCp, this, aShouldDelete);
+ return retval;
+ }
+ mRefCntAndFlags -= NS_REFCOUNT_CHANGE;
+ mRefCntAndFlags |= (NS_IN_PURPLE_BUFFER | NS_IS_PURPLE);
+ return NS_REFCOUNT_VALUE(mRefCntAndFlags);
+ }
+
+ MOZ_ALWAYS_INLINE void RemovePurple()
+ {
+ MOZ_ASSERT(IsPurple(), "must be purple");
+ mRefCntAndFlags &= ~NS_IS_PURPLE;
+ }
+
+ MOZ_ALWAYS_INLINE void RemoveFromPurpleBuffer()
+ {
+ MOZ_ASSERT(IsInPurpleBuffer());
+ mRefCntAndFlags &= ~(NS_IS_PURPLE | NS_IN_PURPLE_BUFFER);
+ }
+
+ MOZ_ALWAYS_INLINE bool IsPurple() const
+ {
+ return !!(mRefCntAndFlags & NS_IS_PURPLE);
+ }
+
+ MOZ_ALWAYS_INLINE bool IsInPurpleBuffer() const
+ {
+ return !!(mRefCntAndFlags & NS_IN_PURPLE_BUFFER);
+ }
+
+ MOZ_ALWAYS_INLINE nsrefcnt get() const
+ {
+ return NS_REFCOUNT_VALUE(mRefCntAndFlags);
+ }
+
+ MOZ_ALWAYS_INLINE operator nsrefcnt() const
+ {
+ return get();
+ }
+
+private:
+ uintptr_t mRefCntAndFlags;
+};
+
+class nsAutoRefCnt
+{
+public:
+ nsAutoRefCnt() : mValue(0) {}
+ explicit nsAutoRefCnt(nsrefcnt aValue) : mValue(aValue) {}
+
+ nsAutoRefCnt(const nsAutoRefCnt&) = delete;
+ void operator=(const nsAutoRefCnt&) = delete;
+
+ // only support prefix increment/decrement
+ nsrefcnt operator++() { return ++mValue; }
+ nsrefcnt operator--() { return --mValue; }
+
+ nsrefcnt operator=(nsrefcnt aValue) { return (mValue = aValue); }
+ operator nsrefcnt() const { return mValue; }
+ nsrefcnt get() const { return mValue; }
+
+ static const bool isThreadSafe = false;
+private:
+ nsrefcnt operator++(int) = delete;
+ nsrefcnt operator--(int) = delete;
+ nsrefcnt mValue;
+};
+
+namespace mozilla {
+class ThreadSafeAutoRefCnt
+{
+public:
+ ThreadSafeAutoRefCnt() : mValue(0) {}
+ explicit ThreadSafeAutoRefCnt(nsrefcnt aValue) : mValue(aValue) {}
+
+ ThreadSafeAutoRefCnt(const ThreadSafeAutoRefCnt&) = delete;
+ void operator=(const ThreadSafeAutoRefCnt&) = delete;
+
+ // only support prefix increment/decrement
+ MOZ_ALWAYS_INLINE nsrefcnt operator++() { return ++mValue; }
+ MOZ_ALWAYS_INLINE nsrefcnt operator--() { return --mValue; }
+
+ MOZ_ALWAYS_INLINE nsrefcnt operator=(nsrefcnt aValue)
+ {
+ return (mValue = aValue);
+ }
+ MOZ_ALWAYS_INLINE operator nsrefcnt() const { return mValue; }
+ MOZ_ALWAYS_INLINE nsrefcnt get() const { return mValue; }
+
+ static const bool isThreadSafe = true;
+private:
+ nsrefcnt operator++(int) = delete;
+ nsrefcnt operator--(int) = delete;
+ // In theory, RelaseAcquire consistency (but no weaker) is sufficient for
+ // the counter. Making it weaker could speed up builds on ARM (but not x86),
+ // but could break pre-existing code that assumes sequential consistency.
+ Atomic<nsrefcnt> mValue;
+};
+} // namespace mozilla
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Declare the reference count variable and the implementations of the
+ * AddRef and QueryInterface methods.
+ */
+
+#define NS_DECL_ISUPPORTS \
+public: \
+ NS_IMETHOD QueryInterface(REFNSIID aIID, \
+ void** aInstancePtr) override; \
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override; \
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) override; \
+ typedef mozilla::FalseType HasThreadSafeRefCnt; \
+protected: \
+ nsAutoRefCnt mRefCnt; \
+ NS_DECL_OWNINGTHREAD \
+public:
+
+#define NS_DECL_THREADSAFE_ISUPPORTS \
+public: \
+ NS_IMETHOD QueryInterface(REFNSIID aIID, \
+ void** aInstancePtr) override; \
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override; \
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) override; \
+ typedef mozilla::TrueType HasThreadSafeRefCnt; \
+protected: \
+ ::mozilla::ThreadSafeAutoRefCnt mRefCnt; \
+ NS_DECL_OWNINGTHREAD \
+public:
+
+#define NS_DECL_CYCLE_COLLECTING_ISUPPORTS \
+public: \
+ NS_IMETHOD QueryInterface(REFNSIID aIID, \
+ void** aInstancePtr) override; \
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override; \
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) override; \
+ NS_IMETHOD_(void) DeleteCycleCollectable(void); \
+ typedef mozilla::FalseType HasThreadSafeRefCnt; \
+protected: \
+ nsCycleCollectingAutoRefCnt mRefCnt; \
+ NS_DECL_OWNINGTHREAD \
+public:
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Implementation of AddRef and Release for non-nsISupports (ie "native")
+ * cycle-collected classes that use the purple buffer to avoid leaks.
+ */
+
+#define NS_IMPL_CC_NATIVE_ADDREF_BODY(_class) \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
+ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ nsrefcnt count = \
+ mRefCnt.incr(static_cast<void*>(this), \
+ _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()); \
+ NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
+ return count;
+
+#define NS_IMPL_CC_NATIVE_RELEASE_BODY(_class) \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ nsrefcnt count = \
+ mRefCnt.decr(static_cast<void*>(this), \
+ _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()); \
+ NS_LOG_RELEASE(this, count, #_class); \
+ return count;
+
+#define NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(_class) \
+NS_METHOD_(MozExternalRefCountType) _class::AddRef(void) \
+{ \
+ NS_IMPL_CC_NATIVE_ADDREF_BODY(_class) \
+}
+
+#define NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE_WITH_LAST_RELEASE(_class, _last) \
+NS_METHOD_(MozExternalRefCountType) _class::Release(void) \
+{ \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ bool shouldDelete = false; \
+ nsrefcnt count = \
+ mRefCnt.decr(static_cast<void*>(this), \
+ _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant(), \
+ &shouldDelete); \
+ NS_LOG_RELEASE(this, count, #_class); \
+ if (count == 0) { \
+ mRefCnt.incr(static_cast<void*>(this), \
+ _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()); \
+ _last; \
+ mRefCnt.decr(static_cast<void*>(this), \
+ _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()); \
+ if (shouldDelete) { \
+ mRefCnt.stabilizeForDeletion(); \
+ DeleteCycleCollectable(); \
+ } \
+ } \
+ return count; \
+}
+
+#define NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE(_class) \
+NS_METHOD_(MozExternalRefCountType) _class::Release(void) \
+{ \
+ NS_IMPL_CC_NATIVE_RELEASE_BODY(_class) \
+}
+
+#define NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(_class) \
+public: \
+ NS_METHOD_(MozExternalRefCountType) AddRef(void) { \
+ NS_IMPL_CC_NATIVE_ADDREF_BODY(_class) \
+ } \
+ NS_METHOD_(MozExternalRefCountType) Release(void) { \
+ NS_IMPL_CC_NATIVE_RELEASE_BODY(_class) \
+ } \
+ typedef mozilla::FalseType HasThreadSafeRefCnt; \
+protected: \
+ nsCycleCollectingAutoRefCnt mRefCnt; \
+ NS_DECL_OWNINGTHREAD \
+public:
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Use this macro to declare and implement the AddRef & Release methods for a
+ * given non-XPCOM <i>_class</i>.
+ *
+ * @param _class The name of the class implementing the method
+ * @param _destroy A statement that is executed when the object's
+ * refcount drops to zero.
+ * @param optional override Mark the AddRef & Release methods as overrides.
+ */
+#define NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(_class, _destroy, ...) \
+public: \
+ NS_METHOD_(MozExternalRefCountType) AddRef(void) __VA_ARGS__ { \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
+ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ ++mRefCnt; \
+ NS_LOG_ADDREF(this, mRefCnt, #_class, sizeof(*this)); \
+ return mRefCnt; \
+ } \
+ NS_METHOD_(MozExternalRefCountType) Release(void) __VA_ARGS__ { \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ --mRefCnt; \
+ NS_LOG_RELEASE(this, mRefCnt, #_class); \
+ if (mRefCnt == 0) { \
+ mRefCnt = 1; /* stabilize */ \
+ _destroy; \
+ return 0; \
+ } \
+ return mRefCnt; \
+ } \
+ typedef mozilla::FalseType HasThreadSafeRefCnt; \
+protected: \
+ nsAutoRefCnt mRefCnt; \
+ NS_DECL_OWNINGTHREAD \
+public:
+
+/**
+ * Use this macro to declare and implement the AddRef & Release methods for a
+ * given non-XPCOM <i>_class</i>.
+ *
+ * @param _class The name of the class implementing the method
+ * @param optional override Mark the AddRef & Release methods as overrides.
+ */
+#define NS_INLINE_DECL_REFCOUNTING(_class, ...) \
+ NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(_class, delete(this), __VA_ARGS__)
+
+#define NS_INLINE_DECL_THREADSAFE_REFCOUNTING_META(_class, _decl, ...) \
+public: \
+ _decl(MozExternalRefCountType) AddRef(void) __VA_ARGS__ { \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
+ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
+ nsrefcnt count = ++mRefCnt; \
+ NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
+ return (nsrefcnt) count; \
+ } \
+ _decl(MozExternalRefCountType) Release(void) __VA_ARGS__ { \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ nsrefcnt count = --mRefCnt; \
+ NS_LOG_RELEASE(this, count, #_class); \
+ if (count == 0) { \
+ delete (this); \
+ return 0; \
+ } \
+ return count; \
+ } \
+ typedef mozilla::TrueType HasThreadSafeRefCnt; \
+protected: \
+ ::mozilla::ThreadSafeAutoRefCnt mRefCnt; \
+public:
+
+/**
+ * Use this macro to declare and implement the AddRef & Release methods for a
+ * given non-XPCOM <i>_class</i> in a threadsafe manner.
+ *
+ * DOES NOT DO REFCOUNT STABILIZATION!
+ *
+ * @param _class The name of the class implementing the method
+ */
+#define NS_INLINE_DECL_THREADSAFE_REFCOUNTING(_class, ...) \
+NS_INLINE_DECL_THREADSAFE_REFCOUNTING_META(_class, NS_METHOD_, __VA_ARGS__)
+
+/**
+ * Like NS_INLINE_DECL_THREADSAFE_REFCOUNTING with AddRef & Release declared
+ * virtual.
+ */
+#define NS_INLINE_DECL_THREADSAFE_VIRTUAL_REFCOUNTING(_class, ...) \
+NS_INLINE_DECL_THREADSAFE_REFCOUNTING_META(_class, NS_IMETHOD_, __VA_ARGS__)
+
+/**
+ * Use this macro to implement the AddRef method for a given <i>_class</i>
+ * @param _class The name of the class implementing the method
+ */
+#define NS_IMPL_ADDREF(_class) \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::AddRef(void) \
+{ \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
+ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
+ if (!mRefCnt.isThreadSafe) \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ nsrefcnt count = ++mRefCnt; \
+ NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
+ return count; \
+}
+
+/**
+ * Use this macro to implement the AddRef method for a given <i>_class</i>
+ * implemented as a wholly owned aggregated object intended to implement
+ * interface(s) for its owner
+ * @param _class The name of the class implementing the method
+ * @param _aggregator the owning/containing object
+ */
+#define NS_IMPL_ADDREF_USING_AGGREGATOR(_class, _aggregator) \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::AddRef(void) \
+{ \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
+ NS_PRECONDITION(_aggregator, "null aggregator"); \
+ return (_aggregator)->AddRef(); \
+}
+
+/**
+ * Use this macro to implement the Release method for a given
+ * <i>_class</i>.
+ * @param _class The name of the class implementing the method
+ * @param _destroy A statement that is executed when the object's
+ * refcount drops to zero.
+ *
+ * For example,
+ *
+ * NS_IMPL_RELEASE_WITH_DESTROY(Foo, Destroy(this))
+ *
+ * will cause
+ *
+ * Destroy(this);
+ *
+ * to be invoked when the object's refcount drops to zero. This
+ * allows for arbitrary teardown activity to occur (e.g., deallocation
+ * of object allocated with placement new).
+ */
+#define NS_IMPL_RELEASE_WITH_DESTROY(_class, _destroy) \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::Release(void) \
+{ \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ if (!mRefCnt.isThreadSafe) \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ nsrefcnt count = --mRefCnt; \
+ NS_LOG_RELEASE(this, count, #_class); \
+ if (count == 0) { \
+ mRefCnt = 1; /* stabilize */ \
+ _destroy; \
+ return 0; \
+ } \
+ return count; \
+}
+
+/**
+ * Use this macro to implement the Release method for a given <i>_class</i>
+ * @param _class The name of the class implementing the method
+ *
+ * A note on the 'stabilization' of the refcnt to one. At that point,
+ * the object's refcount will have gone to zero. The object's
+ * destructor may trigger code that attempts to QueryInterface() and
+ * Release() 'this' again. Doing so will temporarily increment and
+ * decrement the refcount. (Only a logic error would make one try to
+ * keep a permanent hold on 'this'.) To prevent re-entering the
+ * destructor, we make sure that no balanced refcounting can return
+ * the refcount to |0|.
+ */
+#define NS_IMPL_RELEASE(_class) \
+ NS_IMPL_RELEASE_WITH_DESTROY(_class, delete (this))
+
+/**
+ * Use this macro to implement the Release method for a given <i>_class</i>
+ * implemented as a wholly owned aggregated object intended to implement
+ * interface(s) for its owner
+ * @param _class The name of the class implementing the method
+ * @param _aggregator the owning/containing object
+ */
+#define NS_IMPL_RELEASE_USING_AGGREGATOR(_class, _aggregator) \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::Release(void) \
+{ \
+ NS_PRECONDITION(_aggregator, "null aggregator"); \
+ return (_aggregator)->Release(); \
+}
+
+
+#define NS_IMPL_CYCLE_COLLECTING_ADDREF(_class) \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::AddRef(void) \
+{ \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
+ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this); \
+ nsrefcnt count = mRefCnt.incr(base); \
+ NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
+ return count; \
+}
+
+#define NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(_class, _destroy) \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::Release(void) \
+{ \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this); \
+ nsrefcnt count = mRefCnt.decr(base); \
+ NS_LOG_RELEASE(this, count, #_class); \
+ return count; \
+} \
+NS_IMETHODIMP_(void) _class::DeleteCycleCollectable(void) \
+{ \
+ _destroy; \
+}
+
+#define NS_IMPL_CYCLE_COLLECTING_RELEASE(_class) \
+ NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(_class, delete (this))
+
+// _LAST_RELEASE can be useful when certain resources should be released
+// as soon as we know the object will be deleted.
+#define NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(_class, _last) \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::Release(void) \
+{ \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ bool shouldDelete = false; \
+ nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this); \
+ nsrefcnt count = mRefCnt.decr(base, &shouldDelete); \
+ NS_LOG_RELEASE(this, count, #_class); \
+ if (count == 0) { \
+ mRefCnt.incr(base); \
+ _last; \
+ mRefCnt.decr(base); \
+ if (shouldDelete) { \
+ mRefCnt.stabilizeForDeletion(); \
+ DeleteCycleCollectable(); \
+ } \
+ } \
+ return count; \
+} \
+NS_IMETHODIMP_(void) _class::DeleteCycleCollectable(void) \
+{ \
+ delete this; \
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * There are two ways of implementing QueryInterface, and we use both:
+ *
+ * Table-driven QueryInterface uses a static table of IID->offset mappings
+ * and a shared helper function. Using it tends to reduce codesize and improve
+ * runtime performance (due to processor cache hits).
+ *
+ * Macro-driven QueryInterface generates a QueryInterface function directly
+ * using common macros. This is necessary if special QueryInterface features
+ * are being used (such as tearoffs and conditional interfaces).
+ *
+ * These methods can be combined into a table-driven function call followed
+ * by custom code for tearoffs and conditionals.
+ */
+
+struct QITableEntry
+{
+ const nsIID* iid; // null indicates end of the QITableEntry array
+ int32_t offset;
+};
+
+nsresult NS_FASTCALL
+NS_TableDrivenQI(void* aThis, REFNSIID aIID,
+ void** aInstancePtr, const QITableEntry* aEntries);
+
+/**
+ * Implement table-driven queryinterface
+ */
+
+#define NS_INTERFACE_TABLE_HEAD(_class) \
+NS_IMETHODIMP _class::QueryInterface(REFNSIID aIID, void** aInstancePtr) \
+{ \
+ NS_ASSERTION(aInstancePtr, \
+ "QueryInterface requires a non-NULL destination!"); \
+ nsresult rv = NS_ERROR_FAILURE;
+
+#define NS_INTERFACE_TABLE_BEGIN \
+ static const QITableEntry table[] = {
+
+#define NS_INTERFACE_TABLE_ENTRY(_class, _interface) \
+ { &NS_GET_IID(_interface), \
+ int32_t(reinterpret_cast<char*>( \
+ static_cast<_interface*>((_class*) 0x1000)) - \
+ reinterpret_cast<char*>((_class*) 0x1000)) \
+ },
+
+#define NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, _interface, _implClass) \
+ { &NS_GET_IID(_interface), \
+ int32_t(reinterpret_cast<char*>( \
+ static_cast<_interface*>( \
+ static_cast<_implClass*>( \
+ (_class*) 0x1000))) - \
+ reinterpret_cast<char*>((_class*) 0x1000)) \
+ },
+
+/*
+ * XXX: we want to use mozilla::ArrayLength (or equivalent,
+ * MOZ_ARRAY_LENGTH) in this condition, but some versions of GCC don't
+ * see that the static_assert condition is actually constant in those
+ * cases, even with constexpr support (?).
+ */
+#define NS_INTERFACE_TABLE_END_WITH_PTR(_ptr) \
+ { nullptr, 0 } }; \
+ static_assert((sizeof(table)/sizeof(table[0])) > 1, "need at least 1 interface"); \
+ rv = NS_TableDrivenQI(static_cast<void*>(_ptr), \
+ aIID, aInstancePtr, table);
+
+#define NS_INTERFACE_TABLE_END \
+ NS_INTERFACE_TABLE_END_WITH_PTR(this)
+
+#define NS_INTERFACE_TABLE_TAIL \
+ return rv; \
+}
+
+#define NS_INTERFACE_TABLE_TAIL_INHERITING(_baseclass) \
+ if (NS_SUCCEEDED(rv)) \
+ return rv; \
+ return _baseclass::QueryInterface(aIID, aInstancePtr); \
+}
+
+#define NS_INTERFACE_TABLE_TAIL_USING_AGGREGATOR(_aggregator) \
+ if (NS_SUCCEEDED(rv)) \
+ return rv; \
+ NS_ASSERTION(_aggregator, "null aggregator"); \
+ return _aggregator->QueryInterface(aIID, aInstancePtr) \
+}
+
+/**
+ * This implements query interface with two assumptions: First, the
+ * class in question implements nsISupports and its own interface and
+ * nothing else. Second, the implementation of the class's primary
+ * inheritance chain leads to its own interface.
+ *
+ * @param _class The name of the class implementing the method
+ * @param _classiiddef The name of the #define symbol that defines the IID
+ * for the class (e.g. NS_ISUPPORTS_IID)
+ */
+
+#define NS_IMPL_QUERY_HEAD(_class) \
+NS_IMETHODIMP _class::QueryInterface(REFNSIID aIID, void** aInstancePtr) \
+{ \
+ NS_ASSERTION(aInstancePtr, \
+ "QueryInterface requires a non-NULL destination!"); \
+ nsISupports* foundInterface;
+
+#define NS_IMPL_QUERY_BODY(_interface) \
+ if ( aIID.Equals(NS_GET_IID(_interface)) ) \
+ foundInterface = static_cast<_interface*>(this); \
+ else
+
+#define NS_IMPL_QUERY_BODY_CONDITIONAL(_interface, condition) \
+ if ( (condition) && aIID.Equals(NS_GET_IID(_interface))) \
+ foundInterface = static_cast<_interface*>(this); \
+ else
+
+#define NS_IMPL_QUERY_BODY_AMBIGUOUS(_interface, _implClass) \
+ if ( aIID.Equals(NS_GET_IID(_interface)) ) \
+ foundInterface = static_cast<_interface*>( \
+ static_cast<_implClass*>(this)); \
+ else
+
+#define NS_IMPL_QUERY_BODY_AGGREGATED(_interface, _aggregate) \
+ if ( aIID.Equals(NS_GET_IID(_interface)) ) \
+ foundInterface = static_cast<_interface*>(_aggregate); \
+ else
+
+#define NS_IMPL_QUERY_TAIL_GUTS \
+ foundInterface = 0; \
+ nsresult status; \
+ if ( !foundInterface ) \
+ { \
+ /* nsISupports should be handled by this point. If not, fail. */ \
+ MOZ_ASSERT(!aIID.Equals(NS_GET_IID(nsISupports))); \
+ status = NS_NOINTERFACE; \
+ } \
+ else \
+ { \
+ NS_ADDREF(foundInterface); \
+ status = NS_OK; \
+ } \
+ *aInstancePtr = foundInterface; \
+ return status; \
+}
+
+#define NS_IMPL_QUERY_TAIL_INHERITING(_baseclass) \
+ foundInterface = 0; \
+ nsresult status; \
+ if ( !foundInterface ) \
+ status = _baseclass::QueryInterface(aIID, (void**)&foundInterface); \
+ else \
+ { \
+ NS_ADDREF(foundInterface); \
+ status = NS_OK; \
+ } \
+ *aInstancePtr = foundInterface; \
+ return status; \
+}
+
+#define NS_IMPL_QUERY_TAIL_USING_AGGREGATOR(_aggregator) \
+ foundInterface = 0; \
+ nsresult status; \
+ if ( !foundInterface ) { \
+ NS_ASSERTION(_aggregator, "null aggregator"); \
+ status = _aggregator->QueryInterface(aIID, (void**)&foundInterface); \
+ } else \
+ { \
+ NS_ADDREF(foundInterface); \
+ status = NS_OK; \
+ } \
+ *aInstancePtr = foundInterface; \
+ return status; \
+}
+
+#define NS_IMPL_QUERY_TAIL(_supports_interface) \
+ NS_IMPL_QUERY_BODY_AMBIGUOUS(nsISupports, _supports_interface) \
+ NS_IMPL_QUERY_TAIL_GUTS
+
+
+/*
+ This is the new scheme. Using this notation now will allow us to switch to
+ a table driven mechanism when it's ready. Note the difference between this
+ and the (currently) underlying NS_IMPL_QUERY_INTERFACE mechanism. You must
+ explicitly mention |nsISupports| when using the interface maps.
+*/
+#define NS_INTERFACE_MAP_BEGIN(_implClass) NS_IMPL_QUERY_HEAD(_implClass)
+#define NS_INTERFACE_MAP_ENTRY(_interface) NS_IMPL_QUERY_BODY(_interface)
+#define NS_INTERFACE_MAP_ENTRY_CONDITIONAL(_interface, condition) \
+ NS_IMPL_QUERY_BODY_CONDITIONAL(_interface, condition)
+#define NS_INTERFACE_MAP_ENTRY_AGGREGATED(_interface,_aggregate) \
+ NS_IMPL_QUERY_BODY_AGGREGATED(_interface,_aggregate)
+
+#define NS_INTERFACE_MAP_END NS_IMPL_QUERY_TAIL_GUTS
+#define NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(_interface, _implClass) \
+ NS_IMPL_QUERY_BODY_AMBIGUOUS(_interface, _implClass)
+#define NS_INTERFACE_MAP_END_INHERITING(_baseClass) \
+ NS_IMPL_QUERY_TAIL_INHERITING(_baseClass)
+#define NS_INTERFACE_MAP_END_AGGREGATED(_aggregator) \
+ NS_IMPL_QUERY_TAIL_USING_AGGREGATOR(_aggregator)
+
+#define NS_INTERFACE_TABLE0(_class) \
+ NS_INTERFACE_TABLE_BEGIN \
+ NS_INTERFACE_TABLE_ENTRY(_class, nsISupports) \
+ NS_INTERFACE_TABLE_END
+
+#define NS_INTERFACE_TABLE(aClass, ...) \
+ MOZ_STATIC_ASSERT_VALID_ARG_COUNT(__VA_ARGS__); \
+ NS_INTERFACE_TABLE_BEGIN \
+ MOZ_FOR_EACH(NS_INTERFACE_TABLE_ENTRY, (aClass,), (__VA_ARGS__)) \
+ NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(aClass, nsISupports, \
+ MOZ_ARG_1(__VA_ARGS__)) \
+ NS_INTERFACE_TABLE_END
+
+#define NS_IMPL_QUERY_INTERFACE0(_class) \
+ NS_INTERFACE_TABLE_HEAD(_class) \
+ NS_INTERFACE_TABLE0(_class) \
+ NS_INTERFACE_TABLE_TAIL
+
+#define NS_IMPL_QUERY_INTERFACE(aClass, ...) \
+ NS_INTERFACE_TABLE_HEAD(aClass) \
+ NS_INTERFACE_TABLE(aClass, __VA_ARGS__) \
+ NS_INTERFACE_TABLE_TAIL
+
+/**
+ * Declare that you're going to inherit from something that already
+ * implements nsISupports, but also implements an additional interface, thus
+ * causing an ambiguity. In this case you don't need another mRefCnt, you
+ * just need to forward the definitions to the appropriate superclass. E.g.
+ *
+ * class Bar : public Foo, public nsIBar { // both provide nsISupports
+ * public:
+ * NS_DECL_ISUPPORTS_INHERITED
+ * ...other nsIBar and Bar methods...
+ * };
+ */
+#define NS_DECL_ISUPPORTS_INHERITED \
+public: \
+ NS_IMETHOD QueryInterface(REFNSIID aIID, \
+ void** aInstancePtr) override; \
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override; \
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) override; \
+
+/**
+ * These macros can be used in conjunction with NS_DECL_ISUPPORTS_INHERITED
+ * to implement the nsISupports methods, forwarding the invocations to a
+ * superclass that already implements nsISupports.
+ *
+ * Note that I didn't make these inlined because they're virtual methods.
+ */
+
+#define NS_IMPL_ADDREF_INHERITED(Class, Super) \
+NS_IMETHODIMP_(MozExternalRefCountType) Class::AddRef(void) \
+{ \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(Class) \
+ nsrefcnt r = Super::AddRef(); \
+ NS_LOG_ADDREF(this, r, #Class, sizeof(*this)); \
+ return r; \
+}
+
+#define NS_IMPL_RELEASE_INHERITED(Class, Super) \
+NS_IMETHODIMP_(MozExternalRefCountType) Class::Release(void) \
+{ \
+ nsrefcnt r = Super::Release(); \
+ NS_LOG_RELEASE(this, r, #Class); \
+ return r; \
+}
+
+/**
+ * As above but not logging the addref/release; needed if the base
+ * class might be aggregated.
+ */
+#define NS_IMPL_NONLOGGING_ADDREF_INHERITED(Class, Super) \
+NS_IMETHODIMP_(MozExternalRefCountType) Class::AddRef(void) \
+{ \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(Class) \
+ return Super::AddRef(); \
+}
+
+#define NS_IMPL_NONLOGGING_RELEASE_INHERITED(Class, Super) \
+NS_IMETHODIMP_(MozExternalRefCountType) Class::Release(void) \
+{ \
+ return Super::Release(); \
+}
+
+#define NS_INTERFACE_TABLE_INHERITED0(Class) /* Nothing to do here */
+
+#define NS_INTERFACE_TABLE_INHERITED(aClass, ...) \
+ MOZ_STATIC_ASSERT_VALID_ARG_COUNT(__VA_ARGS__); \
+ NS_INTERFACE_TABLE_BEGIN \
+ MOZ_FOR_EACH(NS_INTERFACE_TABLE_ENTRY, (aClass,), (__VA_ARGS__)) \
+ NS_INTERFACE_TABLE_END
+
+#define NS_IMPL_QUERY_INTERFACE_INHERITED(aClass, aSuper, ...) \
+ NS_INTERFACE_TABLE_HEAD(aClass) \
+ NS_INTERFACE_TABLE_INHERITED(aClass, __VA_ARGS__) \
+ NS_INTERFACE_TABLE_TAIL_INHERITING(aSuper)
+
+/**
+ * Convenience macros for implementing all nsISupports methods for
+ * a simple class.
+ * @param _class The name of the class implementing the method
+ * @param _classiiddef The name of the #define symbol that defines the IID
+ * for the class (e.g. NS_ISUPPORTS_IID)
+ */
+
+#define NS_IMPL_ISUPPORTS0(_class) \
+ NS_IMPL_ADDREF(_class) \
+ NS_IMPL_RELEASE(_class) \
+ NS_IMPL_QUERY_INTERFACE0(_class)
+
+#define NS_IMPL_ISUPPORTS(aClass, ...) \
+ NS_IMPL_ADDREF(aClass) \
+ NS_IMPL_RELEASE(aClass) \
+ NS_IMPL_QUERY_INTERFACE(aClass, __VA_ARGS__)
+
+#define NS_IMPL_ISUPPORTS_INHERITED0(aClass, aSuper) \
+ NS_INTERFACE_TABLE_HEAD(aClass) \
+ NS_INTERFACE_TABLE_TAIL_INHERITING(aSuper) \
+ NS_IMPL_ADDREF_INHERITED(aClass, aSuper) \
+ NS_IMPL_RELEASE_INHERITED(aClass, aSuper) \
+
+#define NS_IMPL_ISUPPORTS_INHERITED(aClass, aSuper, ...) \
+ NS_IMPL_QUERY_INTERFACE_INHERITED(aClass, aSuper, __VA_ARGS__) \
+ NS_IMPL_ADDREF_INHERITED(aClass, aSuper) \
+ NS_IMPL_RELEASE_INHERITED(aClass, aSuper)
+
+/*
+ * Macro to glue together a QI that starts with an interface table
+ * and segues into an interface map (e.g. it uses singleton classinfo
+ * or tearoffs).
+ */
+#define NS_INTERFACE_TABLE_TO_MAP_SEGUE \
+ if (rv == NS_OK) return rv; \
+ nsISupports* foundInterface;
+
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ *
+ * Threadsafe implementations of the ISupports convenience macros.
+ *
+ * @note These are not available when linking against the standalone glue,
+ * because the implementation requires PR_ symbols.
+ */
+#define NS_INTERFACE_MAP_END_THREADSAFE NS_IMPL_QUERY_TAIL_GUTS
+
+/**
+ * Macro to generate nsIClassInfo methods for classes which do not have
+ * corresponding nsIFactory implementations.
+ */
+#define NS_IMPL_THREADSAFE_CI(_class) \
+NS_IMETHODIMP \
+_class::GetInterfaces(uint32_t* _count, nsIID*** _array) \
+{ \
+ return NS_CI_INTERFACE_GETTER_NAME(_class)(_count, _array); \
+} \
+ \
+NS_IMETHODIMP \
+_class::GetScriptableHelper(nsIXPCScriptable** _retval) \
+{ \
+ *_retval = nullptr; \
+ return NS_OK; \
+} \
+ \
+NS_IMETHODIMP \
+_class::GetContractID(char** _contractID) \
+{ \
+ *_contractID = nullptr; \
+ return NS_OK; \
+} \
+ \
+NS_IMETHODIMP \
+_class::GetClassDescription(char** _classDescription) \
+{ \
+ *_classDescription = nullptr; \
+ return NS_OK; \
+} \
+ \
+NS_IMETHODIMP \
+_class::GetClassID(nsCID** _classID) \
+{ \
+ *_classID = nullptr; \
+ return NS_OK; \
+} \
+ \
+NS_IMETHODIMP \
+_class::GetFlags(uint32_t* _flags) \
+{ \
+ *_flags = nsIClassInfo::THREADSAFE; \
+ return NS_OK; \
+} \
+ \
+NS_IMETHODIMP \
+_class::GetClassIDNoAlloc(nsCID* _classIDNoAlloc) \
+{ \
+ return NS_ERROR_NOT_AVAILABLE; \
+}
+
+#endif