Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
7 /* Implementation of xptiInterfaceInfoManager. */
9 #include "mozilla/XPTInterfaceInfoManager.h"
11 #include "mozilla/FileUtils.h"
12 #include "mozilla/MemoryReporting.h"
13 #include "mozilla/StaticPtr.h"
15 #include "xptiprivate.h"
16 #include "nsDependentString.h"
17 #include "nsString.h"
18 #include "nsISupportsArray.h"
19 #include "nsArrayEnumerator.h"
20 #include "nsDirectoryService.h"
21 #include "nsIMemoryReporter.h"
23 using namespace mozilla;
25 NS_IMPL_ISUPPORTS(
26 XPTInterfaceInfoManager,
27 nsIInterfaceInfoManager,
28 nsIMemoryReporter)
30 static StaticRefPtr<XPTInterfaceInfoManager> gInterfaceInfoManager;
32 size_t
33 XPTInterfaceInfoManager::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
34 {
35 size_t n = aMallocSizeOf(this);
36 ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor);
37 // The entries themselves are allocated out of an arena accounted
38 // for elsewhere, so don't measure them
39 n += mWorkingSet.mIIDTable.SizeOfExcludingThis(nullptr, aMallocSizeOf);
40 n += mWorkingSet.mNameTable.SizeOfExcludingThis(nullptr, aMallocSizeOf);
41 return n;
42 }
44 MOZ_DEFINE_MALLOC_SIZE_OF(XPTIMallocSizeOf)
46 NS_IMETHODIMP
47 XPTInterfaceInfoManager::CollectReports(nsIHandleReportCallback* aHandleReport,
48 nsISupports* aData)
49 {
50 size_t amount = SizeOfIncludingThis(XPTIMallocSizeOf);
52 // Measure gXPTIStructArena here, too. This is a bit grotty because it
53 // doesn't belong to the XPTIInterfaceInfoManager, but there's no
54 // obviously better place to measure it.
55 amount += XPT_SizeOfArena(gXPTIStructArena, XPTIMallocSizeOf);
57 return MOZ_COLLECT_REPORT(
58 "explicit/xpti-working-set", KIND_HEAP, UNITS_BYTES, amount,
59 "Memory used by the XPCOM typelib system.");
60 }
62 // static
63 XPTInterfaceInfoManager*
64 XPTInterfaceInfoManager::GetSingleton()
65 {
66 if (!gInterfaceInfoManager) {
67 gInterfaceInfoManager = new XPTInterfaceInfoManager();
68 gInterfaceInfoManager->InitMemoryReporter();
69 }
70 return gInterfaceInfoManager;
71 }
73 void
74 XPTInterfaceInfoManager::FreeInterfaceInfoManager()
75 {
76 gInterfaceInfoManager = nullptr;
77 }
79 XPTInterfaceInfoManager::XPTInterfaceInfoManager()
80 : mWorkingSet(),
81 mResolveLock("XPTInterfaceInfoManager.mResolveLock")
82 {
83 }
85 XPTInterfaceInfoManager::~XPTInterfaceInfoManager()
86 {
87 // We only do this on shutdown of the service.
88 mWorkingSet.InvalidateInterfaceInfos();
90 UnregisterWeakMemoryReporter(this);
91 }
93 void
94 XPTInterfaceInfoManager::InitMemoryReporter()
95 {
96 RegisterWeakMemoryReporter(this);
97 }
99 void
100 XPTInterfaceInfoManager::RegisterBuffer(char *buf, uint32_t length)
101 {
102 XPTState *state = XPT_NewXDRState(XPT_DECODE, buf, length);
103 if (!state)
104 return;
106 XPTCursor cursor;
107 if (!XPT_MakeCursor(state, XPT_HEADER, 0, &cursor)) {
108 XPT_DestroyXDRState(state);
109 return;
110 }
112 XPTHeader *header = nullptr;
113 if (XPT_DoHeader(gXPTIStructArena, &cursor, &header)) {
114 RegisterXPTHeader(header);
115 }
117 XPT_DestroyXDRState(state);
118 }
120 void
121 XPTInterfaceInfoManager::RegisterXPTHeader(XPTHeader* aHeader)
122 {
123 if (aHeader->major_version >= XPT_MAJOR_INCOMPATIBLE_VERSION) {
124 NS_ASSERTION(!aHeader->num_interfaces,"bad libxpt");
125 LOG_AUTOREG((" file is version %d.%d Type file of version %d.0 or higher can not be read.\n", (int)header->major_version, (int)header->minor_version, (int)XPT_MAJOR_INCOMPATIBLE_VERSION));
126 }
128 xptiTypelibGuts* typelib = xptiTypelibGuts::Create(aHeader);
130 ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor);
131 for(uint16_t k = 0; k < aHeader->num_interfaces; k++)
132 VerifyAndAddEntryIfNew(aHeader->interface_directory + k, k, typelib);
133 }
135 void
136 XPTInterfaceInfoManager::VerifyAndAddEntryIfNew(XPTInterfaceDirectoryEntry* iface,
137 uint16_t idx,
138 xptiTypelibGuts* typelib)
139 {
140 if (!iface->interface_descriptor)
141 return;
143 // The number of maximum methods is not arbitrary. It is the same value as
144 // in xpcom/reflect/xptcall/public/genstubs.pl; do not change this value
145 // without changing that one or you WILL see problems.
146 if (iface->interface_descriptor->num_methods > 250 &&
147 !(XPT_ID_IS_BUILTINCLASS(iface->interface_descriptor->flags))) {
148 NS_ASSERTION(0, "Too many methods to handle for the stub, cannot load");
149 fprintf(stderr, "ignoring too large interface: %s\n", iface->name);
150 return;
151 }
153 mWorkingSet.mTableReentrantMonitor.AssertCurrentThreadIn();
154 xptiInterfaceEntry* entry = mWorkingSet.mIIDTable.Get(iface->iid);
155 if (entry) {
156 // XXX validate this info to find possible inconsistencies
157 LOG_AUTOREG((" ignoring repeated interface: %s\n", iface->name));
158 return;
159 }
161 // Build a new xptiInterfaceEntry object and hook it up.
163 entry = xptiInterfaceEntry::Create(iface->name,
164 iface->iid,
165 iface->interface_descriptor,
166 typelib);
167 if (!entry)
168 return;
170 //XXX We should SetHeader too as part of the validation, no?
171 entry->SetScriptableFlag(XPT_ID_IS_SCRIPTABLE(iface->interface_descriptor->flags));
172 entry->SetBuiltinClassFlag(XPT_ID_IS_BUILTINCLASS(iface->interface_descriptor->flags));
174 mWorkingSet.mIIDTable.Put(entry->IID(), entry);
175 mWorkingSet.mNameTable.Put(entry->GetTheName(), entry);
177 typelib->SetEntryAt(idx, entry);
179 LOG_AUTOREG((" added interface: %s\n", iface->name));
180 }
182 // this is a private helper
183 static nsresult
184 EntryToInfo(xptiInterfaceEntry* entry, nsIInterfaceInfo **_retval)
185 {
186 if (!entry) {
187 *_retval = nullptr;
188 return NS_ERROR_FAILURE;
189 }
191 nsRefPtr<xptiInterfaceInfo> info = entry->InterfaceInfo();
192 info.forget(_retval);
193 return NS_OK;
194 }
196 xptiInterfaceEntry*
197 XPTInterfaceInfoManager::GetInterfaceEntryForIID(const nsIID *iid)
198 {
199 ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor);
200 return mWorkingSet.mIIDTable.Get(*iid);
201 }
203 /* nsIInterfaceInfo getInfoForIID (in nsIIDPtr iid); */
204 NS_IMETHODIMP
205 XPTInterfaceInfoManager::GetInfoForIID(const nsIID * iid, nsIInterfaceInfo **_retval)
206 {
207 NS_ASSERTION(iid, "bad param");
208 NS_ASSERTION(_retval, "bad param");
210 ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor);
211 xptiInterfaceEntry* entry = mWorkingSet.mIIDTable.Get(*iid);
212 return EntryToInfo(entry, _retval);
213 }
215 /* nsIInterfaceInfo getInfoForName (in string name); */
216 NS_IMETHODIMP
217 XPTInterfaceInfoManager::GetInfoForName(const char *name, nsIInterfaceInfo **_retval)
218 {
219 NS_ASSERTION(name, "bad param");
220 NS_ASSERTION(_retval, "bad param");
222 ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor);
223 xptiInterfaceEntry* entry = mWorkingSet.mNameTable.Get(name);
224 return EntryToInfo(entry, _retval);
225 }
227 /* nsIIDPtr getIIDForName (in string name); */
228 NS_IMETHODIMP
229 XPTInterfaceInfoManager::GetIIDForName(const char *name, nsIID * *_retval)
230 {
231 NS_ASSERTION(name, "bad param");
232 NS_ASSERTION(_retval, "bad param");
234 ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor);
235 xptiInterfaceEntry* entry = mWorkingSet.mNameTable.Get(name);
236 if (!entry) {
237 *_retval = nullptr;
238 return NS_ERROR_FAILURE;
239 }
241 return entry->GetIID(_retval);
242 }
244 /* string getNameForIID (in nsIIDPtr iid); */
245 NS_IMETHODIMP
246 XPTInterfaceInfoManager::GetNameForIID(const nsIID * iid, char **_retval)
247 {
248 NS_ASSERTION(iid, "bad param");
249 NS_ASSERTION(_retval, "bad param");
251 ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor);
252 xptiInterfaceEntry* entry = mWorkingSet.mIIDTable.Get(*iid);
253 if (!entry) {
254 *_retval = nullptr;
255 return NS_ERROR_FAILURE;
256 }
258 return entry->GetName(_retval);
259 }
261 static PLDHashOperator
262 xpti_ArrayAppender(const char* name, xptiInterfaceEntry* entry, void* arg)
263 {
264 nsCOMArray<nsIInterfaceInfo>* array = static_cast<nsCOMArray<nsIInterfaceInfo>*>(arg);
266 if (entry->GetScriptableFlag()) {
267 nsCOMPtr<nsIInterfaceInfo> ii = entry->InterfaceInfo();
268 array->AppendElement(ii);
269 }
270 return PL_DHASH_NEXT;
271 }
273 /* nsIEnumerator enumerateInterfaces (); */
274 void
275 XPTInterfaceInfoManager::GetScriptableInterfaces(nsCOMArray<nsIInterfaceInfo>& aInterfaces)
276 {
277 // I didn't want to incur the size overhead of using nsHashtable just to
278 // make building an enumerator easier. So, this code makes a snapshot of
279 // the table using an nsISupportsArray and builds an enumerator for that.
280 // We can afford this transient cost.
282 ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor);
283 aInterfaces.SetCapacity(mWorkingSet.mNameTable.Count());
284 mWorkingSet.mNameTable.EnumerateRead(xpti_ArrayAppender, &aInterfaces);
285 }
287 struct ArrayAndPrefix
288 {
289 nsISupportsArray* array;
290 const char* prefix;
291 uint32_t length;
292 };
294 static PLDHashOperator
295 xpti_ArrayPrefixAppender(const char* keyname, xptiInterfaceEntry* entry, void* arg)
296 {
297 ArrayAndPrefix* args = (ArrayAndPrefix*) arg;
299 const char* name = entry->GetTheName();
300 if (name != PL_strnstr(name, args->prefix, args->length))
301 return PL_DHASH_NEXT;
303 nsCOMPtr<nsIInterfaceInfo> ii;
304 if (NS_SUCCEEDED(EntryToInfo(entry, getter_AddRefs(ii))))
305 args->array->AppendElement(ii);
306 return PL_DHASH_NEXT;
307 }
309 /* nsIEnumerator enumerateInterfacesWhoseNamesStartWith (in string prefix); */
310 NS_IMETHODIMP
311 XPTInterfaceInfoManager::EnumerateInterfacesWhoseNamesStartWith(const char *prefix, nsIEnumerator **_retval)
312 {
313 nsCOMPtr<nsISupportsArray> array;
314 NS_NewISupportsArray(getter_AddRefs(array));
315 if (!array)
316 return NS_ERROR_UNEXPECTED;
318 ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor);
319 ArrayAndPrefix args = {array, prefix, static_cast<uint32_t>(strlen(prefix))};
320 mWorkingSet.mNameTable.EnumerateRead(xpti_ArrayPrefixAppender, &args);
322 return array->Enumerate(_retval);
323 }
325 /* void autoRegisterInterfaces (); */
326 NS_IMETHODIMP
327 XPTInterfaceInfoManager::AutoRegisterInterfaces()
328 {
329 return NS_ERROR_NOT_IMPLEMENTED;
330 }