1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef nsIClassInfoImpl_h__
8 #define nsIClassInfoImpl_h__
9 
10 #include "mozilla/Alignment.h"
11 #include "mozilla/Assertions.h"
12 #include "mozilla/MacroArgs.h"
13 #include "mozilla/MacroForEach.h"
14 #include "nsIClassInfo.h"
15 #include "nsISupportsImpl.h"
16 
17 #include <new>
18 
19 /**
20  * This header file provides macros which help you make your class implement
21  * nsIClassInfo.  Implementing nsIClassInfo is particularly helpful if you have
22  * a C++ class which implements multiple interfaces and which you access from
23  * JavaScript.  If that class implements nsIClassInfo, the JavaScript code
24  * won't have to call QueryInterface on instances of the class; all methods
25  * from all interfaces returned by GetInterfaces() will be available
26  * automagically.
27  *
28  * Here's all you need to do.  Given a class
29  *
30  *   class nsFooBar : public nsIFoo, public nsIBar { };
31  *
32  * you should already have the following nsISupports implementation in its cpp
33  * file:
34  *
35  *   NS_IMPL_ISUPPORTS(nsFooBar, nsIFoo, nsIBar).
36  *
37  * Change this to
38  *
39  *   NS_IMPL_CLASSINFO(nsFooBar, nullptr, 0, NS_FOOBAR_CID)
40  *   NS_IMPL_ISUPPORTS_CI(nsFooBar, nsIFoo, nsIBar)
41  *
42  * If nsFooBar is threadsafe, change the 0 above to nsIClassInfo::THREADSAFE.
43  * If it's a singleton, use nsIClassInfo::SINGLETON.  The full list of flags is
44  * in nsIClassInfo.idl.
45  *
46  * The nullptr parameter is there so you can pass a function for converting
47  * from an XPCOM object to a scriptable helper.  Unless you're doing
48  * specialized JS work, you can probably leave this as nullptr.
49  *
50  * This file also defines the NS_IMPL_QUERY_INTERFACE_CI macro, which you can
51  * use to replace NS_IMPL_QUERY_INTERFACE, if you use that instead of
52  * NS_IMPL_ISUPPORTS.
53  *
54  * That's it!  The rest is gory details.
55  *
56  *
57  * Notice that nsFooBar didn't need to inherit from nsIClassInfo in order to
58  * "implement" it.  However, after adding these macros to nsFooBar, you you can
59  * QueryInterface an instance of nsFooBar to nsIClassInfo.  How can this be?
60  *
61  * The answer lies in the NS_IMPL_ISUPPORTS_CI macro.  It modifies nsFooBar's
62  * QueryInterface implementation such that, if we ask to QI to nsIClassInfo, it
63  * returns a singleton object associated with the class.  (That singleton is
64  * defined by NS_IMPL_CLASSINFO.)  So all nsFooBar instances will return the
65  * same object when QI'ed to nsIClassInfo.  (You can see this in
66  * NS_IMPL_QUERY_CLASSINFO below.)
67  *
68  * This hack breaks XPCOM's rules, since if you take an instance of nsFooBar,
69  * QI it to nsIClassInfo, and then try to QI to nsIFoo, that will fail.  On the
70  * upside, implementing nsIClassInfo doesn't add a vtable pointer to instances
71  * of your class.
72  *
73  * In principal, you can also implement nsIClassInfo by inheriting from the
74  * interface.  But some code expects that when it QI's an object to
75  * nsIClassInfo, it gets back a singleton which isn't attached to any
76  * particular object.  If a class were to implement nsIClassInfo through
77  * inheritance, that code might QI to nsIClassInfo and keep the resulting
78  * object alive, thinking it was only keeping alive the classinfo singleton,
79  * but in fact keeping a whole instance of the class alive.  See, e.g., bug
80  * 658632.
81  *
82  * Unless you specifically need to have a different nsIClassInfo instance for
83  * each instance of your class, you should probably just implement nsIClassInfo
84  * as a singleton.
85  */
86 
87 class GenericClassInfo : public nsIClassInfo {
88  public:
89   struct ClassInfoData {
90     // This function pointer uses NS_CALLBACK_ because it's always set to an
91     // NS_IMETHOD function, which uses __stdcall on Win32.
92     typedef NS_CALLBACK_(nsresult, GetInterfacesProc)(nsTArray<nsIID>& aArray);
93     GetInterfacesProc getinterfaces;
94 
95     // This function pointer doesn't use NS_CALLBACK_ because it's always set to
96     // a vanilla function.
97     typedef nsresult (*GetScriptableHelperProc)(nsIXPCScriptable** aHelper);
98     GetScriptableHelperProc getscriptablehelper;
99 
100     uint32_t flags;
101     nsCID cid;
102   };
103 
104   NS_DECL_ISUPPORTS_INHERITED
105   NS_DECL_NSICLASSINFO
106 
GenericClassInfo(const ClassInfoData * aData)107   explicit GenericClassInfo(const ClassInfoData* aData) : mData(aData) {}
108 
109  private:
110   const ClassInfoData* mData;
111 };
112 
113 #define NS_CLASSINFO_NAME(_class) g##_class##_classInfoGlobal
114 #define NS_CI_INTERFACE_GETTER_NAME(_class) _class##_GetInterfacesHelper
115 #define NS_DECL_CI_INTERFACE_GETTER(_class)                                  \
116   extern NS_IMETHODIMP NS_CI_INTERFACE_GETTER_NAME(_class)(nsTArray<nsIID> & \
117                                                            array);
118 
119 #define NS_IMPL_CLASSINFO(_class, _getscriptablehelper, _flags, _cid)       \
120   NS_DECL_CI_INTERFACE_GETTER(_class)                                       \
121   static const GenericClassInfo::ClassInfoData k##_class##ClassInfoData = { \
122       NS_CI_INTERFACE_GETTER_NAME(_class),                                  \
123       _getscriptablehelper,                                                 \
124       _flags | nsIClassInfo::SINGLETON_CLASSINFO,                           \
125       _cid,                                                                 \
126   };                                                                        \
127   mozilla::AlignedStorage2<GenericClassInfo> k##_class##ClassInfoDataPlace; \
128   nsIClassInfo* NS_CLASSINFO_NAME(_class) = nullptr;
129 
130 #define NS_IMPL_QUERY_CLASSINFO(_class)                                      \
131   if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {                               \
132     if (!NS_CLASSINFO_NAME(_class))                                          \
133       NS_CLASSINFO_NAME(_class) = new (k##_class##ClassInfoDataPlace.addr()) \
134           GenericClassInfo(&k##_class##ClassInfoData);                       \
135     foundInterface = NS_CLASSINFO_NAME(_class);                              \
136   } else
137 
138 #define NS_CLASSINFO_HELPER_BEGIN(_class, _c)                    \
139   NS_IMETHODIMP                                                  \
140   NS_CI_INTERFACE_GETTER_NAME(_class)(nsTArray<nsIID> & array) { \
141     array.Clear();                                               \
142     array.SetCapacity(_c);
143 
144 #define NS_CLASSINFO_HELPER_ENTRY(_interface) \
145   array.AppendElement(NS_GET_IID(_interface));
146 
147 #define NS_CLASSINFO_HELPER_END \
148   return NS_OK;                 \
149   }
150 
151 #define NS_IMPL_CI_INTERFACE_GETTER(aClass, ...)                       \
152   static_assert(MOZ_ARG_COUNT(__VA_ARGS__) > 0,                        \
153                 "Need more arguments to NS_IMPL_CI_INTERFACE_GETTER"); \
154   NS_CLASSINFO_HELPER_BEGIN(aClass, MOZ_ARG_COUNT(__VA_ARGS__))        \
155   MOZ_FOR_EACH(NS_CLASSINFO_HELPER_ENTRY, (), (__VA_ARGS__))           \
156   NS_CLASSINFO_HELPER_END
157 
158 #define NS_IMPL_CI_INTERFACE_GETTER0(aClass) \
159   NS_CLASSINFO_HELPER_BEGIN(aClass, 0)       \
160   NS_CLASSINFO_HELPER_END
161 
162 // Note that this macro is an internal implementation of this header and
163 // should not be used outside it. It does not end the interface map as this
164 // is done in NS_IMPL_QUERY_INTERFACE_CI or the _INHERITED variant.
165 #define NS_IMPL_QUERY_INTERFACE_CI_GUTS(aClass, ...)                      \
166   static_assert(MOZ_ARG_COUNT(__VA_ARGS__) > 0,                           \
167                 "Need more arguments to NS_IMPL_QUERY_INTERFACE_CI");     \
168   NS_INTERFACE_MAP_BEGIN(aClass)                                          \
169     MOZ_FOR_EACH(NS_INTERFACE_MAP_ENTRY, (), (__VA_ARGS__))               \
170     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, MOZ_ARG_1(__VA_ARGS__)) \
171     NS_IMPL_QUERY_CLASSINFO(aClass)
172 
173 #define NS_IMPL_QUERY_INTERFACE_CI(aClass, ...)        \
174   NS_IMPL_QUERY_INTERFACE_CI_GUTS(aClass, __VA_ARGS__) \
175   NS_INTERFACE_MAP_END
176 
177 #define NS_IMPL_QUERY_INTERFACE_CI_INHERITED(aClass, aSuper, ...) \
178   NS_IMPL_QUERY_INTERFACE_CI_GUTS(aClass, __VA_ARGS__)            \
179   NS_INTERFACE_MAP_END_INHERITING                                 \
180   (aSuper)
181 
182 #define NS_IMPL_QUERY_INTERFACE_CI_INHERITED0(aClass, aSuper) \
183   NS_INTERFACE_MAP_BEGIN(aClass)                              \
184     NS_IMPL_QUERY_CLASSINFO(aClass)                           \
185   NS_INTERFACE_MAP_END_INHERITING(aSuper)
186 
187 #define NS_IMPL_ISUPPORTS_CI(aClass, ...)         \
188   NS_IMPL_ADDREF(aClass)                          \
189   NS_IMPL_RELEASE(aClass)                         \
190   NS_IMPL_QUERY_INTERFACE_CI(aClass, __VA_ARGS__) \
191   NS_IMPL_CI_INTERFACE_GETTER(aClass, __VA_ARGS__)
192 
193 #endif  // nsIClassInfoImpl_h__
194