|
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 /* Implementation of xptiInterfaceInfoManager. */ |
|
8 |
|
9 #include "mozilla/XPTInterfaceInfoManager.h" |
|
10 |
|
11 #include "mozilla/FileUtils.h" |
|
12 #include "mozilla/MemoryReporting.h" |
|
13 #include "mozilla/StaticPtr.h" |
|
14 |
|
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" |
|
22 |
|
23 using namespace mozilla; |
|
24 |
|
25 NS_IMPL_ISUPPORTS( |
|
26 XPTInterfaceInfoManager, |
|
27 nsIInterfaceInfoManager, |
|
28 nsIMemoryReporter) |
|
29 |
|
30 static StaticRefPtr<XPTInterfaceInfoManager> gInterfaceInfoManager; |
|
31 |
|
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 } |
|
43 |
|
44 MOZ_DEFINE_MALLOC_SIZE_OF(XPTIMallocSizeOf) |
|
45 |
|
46 NS_IMETHODIMP |
|
47 XPTInterfaceInfoManager::CollectReports(nsIHandleReportCallback* aHandleReport, |
|
48 nsISupports* aData) |
|
49 { |
|
50 size_t amount = SizeOfIncludingThis(XPTIMallocSizeOf); |
|
51 |
|
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); |
|
56 |
|
57 return MOZ_COLLECT_REPORT( |
|
58 "explicit/xpti-working-set", KIND_HEAP, UNITS_BYTES, amount, |
|
59 "Memory used by the XPCOM typelib system."); |
|
60 } |
|
61 |
|
62 // static |
|
63 XPTInterfaceInfoManager* |
|
64 XPTInterfaceInfoManager::GetSingleton() |
|
65 { |
|
66 if (!gInterfaceInfoManager) { |
|
67 gInterfaceInfoManager = new XPTInterfaceInfoManager(); |
|
68 gInterfaceInfoManager->InitMemoryReporter(); |
|
69 } |
|
70 return gInterfaceInfoManager; |
|
71 } |
|
72 |
|
73 void |
|
74 XPTInterfaceInfoManager::FreeInterfaceInfoManager() |
|
75 { |
|
76 gInterfaceInfoManager = nullptr; |
|
77 } |
|
78 |
|
79 XPTInterfaceInfoManager::XPTInterfaceInfoManager() |
|
80 : mWorkingSet(), |
|
81 mResolveLock("XPTInterfaceInfoManager.mResolveLock") |
|
82 { |
|
83 } |
|
84 |
|
85 XPTInterfaceInfoManager::~XPTInterfaceInfoManager() |
|
86 { |
|
87 // We only do this on shutdown of the service. |
|
88 mWorkingSet.InvalidateInterfaceInfos(); |
|
89 |
|
90 UnregisterWeakMemoryReporter(this); |
|
91 } |
|
92 |
|
93 void |
|
94 XPTInterfaceInfoManager::InitMemoryReporter() |
|
95 { |
|
96 RegisterWeakMemoryReporter(this); |
|
97 } |
|
98 |
|
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; |
|
105 |
|
106 XPTCursor cursor; |
|
107 if (!XPT_MakeCursor(state, XPT_HEADER, 0, &cursor)) { |
|
108 XPT_DestroyXDRState(state); |
|
109 return; |
|
110 } |
|
111 |
|
112 XPTHeader *header = nullptr; |
|
113 if (XPT_DoHeader(gXPTIStructArena, &cursor, &header)) { |
|
114 RegisterXPTHeader(header); |
|
115 } |
|
116 |
|
117 XPT_DestroyXDRState(state); |
|
118 } |
|
119 |
|
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 } |
|
127 |
|
128 xptiTypelibGuts* typelib = xptiTypelibGuts::Create(aHeader); |
|
129 |
|
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 } |
|
134 |
|
135 void |
|
136 XPTInterfaceInfoManager::VerifyAndAddEntryIfNew(XPTInterfaceDirectoryEntry* iface, |
|
137 uint16_t idx, |
|
138 xptiTypelibGuts* typelib) |
|
139 { |
|
140 if (!iface->interface_descriptor) |
|
141 return; |
|
142 |
|
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 } |
|
152 |
|
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 } |
|
160 |
|
161 // Build a new xptiInterfaceEntry object and hook it up. |
|
162 |
|
163 entry = xptiInterfaceEntry::Create(iface->name, |
|
164 iface->iid, |
|
165 iface->interface_descriptor, |
|
166 typelib); |
|
167 if (!entry) |
|
168 return; |
|
169 |
|
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)); |
|
173 |
|
174 mWorkingSet.mIIDTable.Put(entry->IID(), entry); |
|
175 mWorkingSet.mNameTable.Put(entry->GetTheName(), entry); |
|
176 |
|
177 typelib->SetEntryAt(idx, entry); |
|
178 |
|
179 LOG_AUTOREG((" added interface: %s\n", iface->name)); |
|
180 } |
|
181 |
|
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 } |
|
190 |
|
191 nsRefPtr<xptiInterfaceInfo> info = entry->InterfaceInfo(); |
|
192 info.forget(_retval); |
|
193 return NS_OK; |
|
194 } |
|
195 |
|
196 xptiInterfaceEntry* |
|
197 XPTInterfaceInfoManager::GetInterfaceEntryForIID(const nsIID *iid) |
|
198 { |
|
199 ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor); |
|
200 return mWorkingSet.mIIDTable.Get(*iid); |
|
201 } |
|
202 |
|
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"); |
|
209 |
|
210 ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor); |
|
211 xptiInterfaceEntry* entry = mWorkingSet.mIIDTable.Get(*iid); |
|
212 return EntryToInfo(entry, _retval); |
|
213 } |
|
214 |
|
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"); |
|
221 |
|
222 ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor); |
|
223 xptiInterfaceEntry* entry = mWorkingSet.mNameTable.Get(name); |
|
224 return EntryToInfo(entry, _retval); |
|
225 } |
|
226 |
|
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"); |
|
233 |
|
234 ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor); |
|
235 xptiInterfaceEntry* entry = mWorkingSet.mNameTable.Get(name); |
|
236 if (!entry) { |
|
237 *_retval = nullptr; |
|
238 return NS_ERROR_FAILURE; |
|
239 } |
|
240 |
|
241 return entry->GetIID(_retval); |
|
242 } |
|
243 |
|
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"); |
|
250 |
|
251 ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor); |
|
252 xptiInterfaceEntry* entry = mWorkingSet.mIIDTable.Get(*iid); |
|
253 if (!entry) { |
|
254 *_retval = nullptr; |
|
255 return NS_ERROR_FAILURE; |
|
256 } |
|
257 |
|
258 return entry->GetName(_retval); |
|
259 } |
|
260 |
|
261 static PLDHashOperator |
|
262 xpti_ArrayAppender(const char* name, xptiInterfaceEntry* entry, void* arg) |
|
263 { |
|
264 nsCOMArray<nsIInterfaceInfo>* array = static_cast<nsCOMArray<nsIInterfaceInfo>*>(arg); |
|
265 |
|
266 if (entry->GetScriptableFlag()) { |
|
267 nsCOMPtr<nsIInterfaceInfo> ii = entry->InterfaceInfo(); |
|
268 array->AppendElement(ii); |
|
269 } |
|
270 return PL_DHASH_NEXT; |
|
271 } |
|
272 |
|
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. |
|
281 |
|
282 ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor); |
|
283 aInterfaces.SetCapacity(mWorkingSet.mNameTable.Count()); |
|
284 mWorkingSet.mNameTable.EnumerateRead(xpti_ArrayAppender, &aInterfaces); |
|
285 } |
|
286 |
|
287 struct ArrayAndPrefix |
|
288 { |
|
289 nsISupportsArray* array; |
|
290 const char* prefix; |
|
291 uint32_t length; |
|
292 }; |
|
293 |
|
294 static PLDHashOperator |
|
295 xpti_ArrayPrefixAppender(const char* keyname, xptiInterfaceEntry* entry, void* arg) |
|
296 { |
|
297 ArrayAndPrefix* args = (ArrayAndPrefix*) arg; |
|
298 |
|
299 const char* name = entry->GetTheName(); |
|
300 if (name != PL_strnstr(name, args->prefix, args->length)) |
|
301 return PL_DHASH_NEXT; |
|
302 |
|
303 nsCOMPtr<nsIInterfaceInfo> ii; |
|
304 if (NS_SUCCEEDED(EntryToInfo(entry, getter_AddRefs(ii)))) |
|
305 args->array->AppendElement(ii); |
|
306 return PL_DHASH_NEXT; |
|
307 } |
|
308 |
|
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; |
|
317 |
|
318 ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor); |
|
319 ArrayAndPrefix args = {array, prefix, static_cast<uint32_t>(strlen(prefix))}; |
|
320 mWorkingSet.mNameTable.EnumerateRead(xpti_ArrayPrefixAppender, &args); |
|
321 |
|
322 return array->Enumerate(_retval); |
|
323 } |
|
324 |
|
325 /* void autoRegisterInterfaces (); */ |
|
326 NS_IMETHODIMP |
|
327 XPTInterfaceInfoManager::AutoRegisterInterfaces() |
|
328 { |
|
329 return NS_ERROR_NOT_IMPLEMENTED; |
|
330 } |