Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim: set ts=8 sts=4 et sw=4 tw=99: */
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 : /* Manage the shared info about interfaces for use by wrappedNatives. */
8 :
9 : #include "xpcprivate.h"
10 : #include "jswrapper.h"
11 :
12 : #include "mozilla/MemoryReporting.h"
13 : #include "mozilla/XPTInterfaceInfoManager.h"
14 : #include "nsIScriptError.h"
15 : #include "nsPrintfCString.h"
16 :
17 : using namespace JS;
18 : using namespace mozilla;
19 :
20 : /***************************************************************************/
21 :
22 : // XPCNativeMember
23 :
24 : // static
25 : bool
26 11152 : XPCNativeMember::GetCallInfo(JSObject* funobj,
27 : RefPtr<XPCNativeInterface>* pInterface,
28 : XPCNativeMember** pMember)
29 : {
30 11152 : funobj = js::UncheckedUnwrap(funobj);
31 : Value memberVal =
32 : js::GetFunctionNativeReserved(funobj,
33 11152 : XPC_FUNCTION_NATIVE_MEMBER_SLOT);
34 :
35 11152 : *pMember = static_cast<XPCNativeMember*>(memberVal.toPrivate());
36 11152 : *pInterface = (*pMember)->GetInterface();
37 :
38 11152 : return true;
39 : }
40 :
41 : bool
42 2755 : XPCNativeMember::NewFunctionObject(XPCCallContext& ccx,
43 : XPCNativeInterface* iface, HandleObject parent,
44 : Value* pval)
45 : {
46 2755 : MOZ_ASSERT(!IsConstant(), "Only call this if you're sure this is not a constant!");
47 :
48 2755 : return Resolve(ccx, iface, parent, pval);
49 : }
50 :
51 : bool
52 2954 : XPCNativeMember::Resolve(XPCCallContext& ccx, XPCNativeInterface* iface,
53 : HandleObject parent, Value* vp)
54 : {
55 2954 : MOZ_ASSERT(iface == GetInterface());
56 2954 : if (IsConstant()) {
57 398 : RootedValue resultVal(ccx);
58 398 : nsXPIDLCString name;
59 199 : if (NS_FAILED(iface->GetInterfaceInfo()->GetConstant(mIndex, &resultVal,
60 : getter_Copies(name))))
61 0 : return false;
62 :
63 199 : *vp = resultVal;
64 :
65 199 : return true;
66 : }
67 : // else...
68 :
69 : // This is a method or attribute - we'll be needing a function object
70 :
71 : int argc;
72 : JSNative callback;
73 :
74 2755 : if (IsMethod()) {
75 : const nsXPTMethodInfo* info;
76 1379 : if (NS_FAILED(iface->GetInterfaceInfo()->GetMethodInfo(mIndex, &info)))
77 0 : return false;
78 :
79 : // Note: ASSUMES that retval is last arg.
80 1379 : argc = (int) info->GetParamCount();
81 1379 : if (argc && info->GetParam((uint8_t)(argc-1)).IsRetval())
82 906 : argc-- ;
83 :
84 1379 : callback = XPC_WN_CallMethod;
85 : } else {
86 1376 : argc = 0;
87 1376 : callback = XPC_WN_GetterSetter;
88 : }
89 :
90 2755 : JSFunction* fun = js::NewFunctionByIdWithReserved(ccx, callback, argc, 0, GetName());
91 2755 : if (!fun)
92 0 : return false;
93 :
94 2755 : JSObject* funobj = JS_GetFunctionObject(fun);
95 2755 : if (!funobj)
96 0 : return false;
97 :
98 : js::SetFunctionNativeReserved(funobj, XPC_FUNCTION_NATIVE_MEMBER_SLOT,
99 2755 : PrivateValue(this));
100 : js::SetFunctionNativeReserved(funobj, XPC_FUNCTION_PARENT_OBJECT_SLOT,
101 2755 : ObjectValue(*parent));
102 :
103 2755 : vp->setObject(*funobj);
104 :
105 2755 : return true;
106 : }
107 :
108 : /***************************************************************************/
109 : // XPCNativeInterface
110 :
111 3114 : XPCNativeInterface::~XPCNativeInterface()
112 : {
113 1557 : XPCJSRuntime::Get()->GetIID2NativeInterfaceMap()->Remove(this);
114 1557 : }
115 :
116 : // static
117 : already_AddRefed<XPCNativeInterface>
118 13594 : XPCNativeInterface::GetNewOrUsed(const nsIID* iid)
119 : {
120 27188 : RefPtr<XPCNativeInterface> iface;
121 13594 : XPCJSRuntime* rt = XPCJSRuntime::Get();
122 :
123 13594 : IID2NativeInterfaceMap* map = rt->GetIID2NativeInterfaceMap();
124 13594 : if (!map)
125 0 : return nullptr;
126 :
127 13594 : iface = map->Find(*iid);
128 :
129 13594 : if (iface)
130 12143 : return iface.forget();
131 :
132 2902 : nsCOMPtr<nsIInterfaceInfo> info;
133 1451 : XPTInterfaceInfoManager::GetSingleton()->GetInfoForIID(iid, getter_AddRefs(info));
134 1451 : if (!info)
135 1176 : return nullptr;
136 :
137 275 : iface = NewInstance(info);
138 275 : if (!iface)
139 0 : return nullptr;
140 :
141 275 : XPCNativeInterface* iface2 = map->Add(iface);
142 275 : if (!iface2) {
143 0 : NS_ERROR("failed to add our interface!");
144 0 : iface = nullptr;
145 275 : } else if (iface2 != iface) {
146 0 : iface = iface2;
147 : }
148 :
149 275 : return iface.forget();
150 : }
151 :
152 : // static
153 : already_AddRefed<XPCNativeInterface>
154 1802 : XPCNativeInterface::GetNewOrUsed(nsIInterfaceInfo* info)
155 : {
156 3604 : RefPtr<XPCNativeInterface> iface;
157 :
158 : const nsIID* iid;
159 1802 : if (NS_FAILED(info->GetIIDShared(&iid)) || !iid)
160 0 : return nullptr;
161 :
162 1802 : XPCJSRuntime* rt = XPCJSRuntime::Get();
163 :
164 1802 : IID2NativeInterfaceMap* map = rt->GetIID2NativeInterfaceMap();
165 1802 : if (!map)
166 0 : return nullptr;
167 :
168 1802 : iface = map->Find(*iid);
169 :
170 1802 : if (iface)
171 252 : return iface.forget();
172 :
173 1550 : iface = NewInstance(info);
174 1550 : if (!iface)
175 0 : return nullptr;
176 :
177 3100 : RefPtr<XPCNativeInterface> iface2 = map->Add(iface);
178 1550 : if (!iface2) {
179 0 : NS_ERROR("failed to add our interface!");
180 0 : iface = nullptr;
181 1550 : } else if (iface2 != iface) {
182 0 : iface = iface2;
183 : }
184 :
185 1550 : return iface.forget();
186 : }
187 :
188 : // static
189 : already_AddRefed<XPCNativeInterface>
190 3 : XPCNativeInterface::GetNewOrUsed(const char* name)
191 : {
192 6 : nsCOMPtr<nsIInterfaceInfo> info;
193 3 : XPTInterfaceInfoManager::GetSingleton()->GetInfoForName(name, getter_AddRefs(info));
194 6 : return info ? GetNewOrUsed(info) : nullptr;
195 : }
196 :
197 : // static
198 : already_AddRefed<XPCNativeInterface>
199 2970 : XPCNativeInterface::GetISupports()
200 : {
201 : // XXX We should optimize this to cache this common XPCNativeInterface.
202 2970 : return GetNewOrUsed(&NS_GET_IID(nsISupports));
203 : }
204 :
205 : // static
206 : already_AddRefed<XPCNativeInterface>
207 1825 : XPCNativeInterface::NewInstance(nsIInterfaceInfo* aInfo)
208 : {
209 3650 : AutoJSContext cx;
210 : static const uint16_t MAX_LOCAL_MEMBER_COUNT = 16;
211 3650 : XPCNativeMember local_members[MAX_LOCAL_MEMBER_COUNT];
212 3650 : RefPtr<XPCNativeInterface> obj;
213 1825 : XPCNativeMember* members = nullptr;
214 :
215 : int i;
216 1825 : bool failed = false;
217 : uint16_t constCount;
218 : uint16_t methodCount;
219 : uint16_t totalCount;
220 1825 : uint16_t realTotalCount = 0;
221 : XPCNativeMember* cur;
222 3650 : RootedString str(cx);
223 3650 : RootedId interfaceName(cx);
224 :
225 : // XXX Investigate lazy init? This is a problem given the
226 : // 'placement new' scheme - we need to at least know how big to make
227 : // the object. We might do a scan of methods to determine needed size,
228 : // then make our object, but avoid init'ing *any* members until asked?
229 : // Find out how often we create these objects w/o really looking at
230 : // (or using) the members.
231 :
232 : bool canScript;
233 1825 : if (NS_FAILED(aInfo->IsScriptable(&canScript)) || !canScript)
234 0 : return nullptr;
235 :
236 : bool mainProcessScriptableOnly;
237 1825 : if (NS_FAILED(aInfo->IsMainProcessScriptableOnly(&mainProcessScriptableOnly)))
238 0 : return nullptr;
239 1825 : if (mainProcessScriptableOnly && !XRE_IsParentProcess()) {
240 0 : nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
241 0 : if (console) {
242 : const char* intfNameChars;
243 0 : aInfo->GetNameShared(&intfNameChars);
244 0 : nsPrintfCString errorMsg("Use of %s in content process is deprecated.", intfNameChars);
245 :
246 0 : nsAutoString filename;
247 0 : uint32_t lineno = 0, column = 0;
248 0 : nsJSUtils::GetCallingLocation(cx, filename, &lineno, &column);
249 0 : nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
250 0 : error->Init(NS_ConvertUTF8toUTF16(errorMsg),
251 0 : filename, EmptyString(),
252 0 : lineno, column, nsIScriptError::warningFlag, "chrome javascript");
253 0 : console->LogMessage(error);
254 : }
255 : }
256 :
257 3650 : if (NS_FAILED(aInfo->GetMethodCount(&methodCount)) ||
258 1825 : NS_FAILED(aInfo->GetConstantCount(&constCount)))
259 0 : return nullptr;
260 :
261 : // If the interface does not have nsISupports in its inheritance chain
262 : // then we know we can't reflect its methods. However, some interfaces that
263 : // are used just to reflect constants are declared this way. We need to
264 : // go ahead and build the thing. But, we'll ignore whatever methods it may
265 : // have.
266 1825 : if (!nsXPConnect::IsISupportsDescendant(aInfo))
267 0 : methodCount = 0;
268 :
269 1825 : totalCount = methodCount + constCount;
270 :
271 1825 : if (totalCount > MAX_LOCAL_MEMBER_COUNT) {
272 468 : members = new XPCNativeMember[totalCount];
273 234 : if (!members)
274 0 : return nullptr;
275 : } else {
276 1591 : members = local_members;
277 : }
278 :
279 : // NOTE: since getters and setters share a member, we might not use all
280 : // of the member objects.
281 :
282 16070 : for (i = 0; i < methodCount; i++) {
283 : const nsXPTMethodInfo* info;
284 14245 : if (NS_FAILED(aInfo->GetMethodInfo(i, &info))) {
285 0 : failed = true;
286 0 : break;
287 : }
288 :
289 : // don't reflect Addref or Release
290 14245 : if (i == 1 || i == 2)
291 7845 : continue;
292 :
293 10595 : if (!XPCConvert::IsMethodReflectable(*info))
294 545 : continue;
295 :
296 10050 : str = JS_AtomizeAndPinString(cx, info->GetName());
297 10050 : if (!str) {
298 0 : NS_ERROR("bad method name");
299 0 : failed = true;
300 0 : break;
301 : }
302 10050 : jsid name = INTERNED_STRING_TO_JSID(cx, str);
303 :
304 10050 : if (info->IsSetter()) {
305 550 : MOZ_ASSERT(realTotalCount,"bad setter");
306 : // Note: ASSUMES Getter/Setter pairs are next to each other
307 : // This is a rule of the typelib spec.
308 550 : cur = &members[realTotalCount-1];
309 550 : MOZ_ASSERT(cur->GetName() == name,"bad setter");
310 550 : MOZ_ASSERT(cur->IsReadOnlyAttribute(),"bad setter");
311 550 : MOZ_ASSERT(cur->GetIndex() == i-1,"bad setter");
312 550 : cur->SetWritableAttribute();
313 : } else {
314 : // XXX need better way to find dups
315 : // MOZ_ASSERT(!LookupMemberByID(name),"duplicate method name");
316 9500 : if (realTotalCount == XPCNativeMember::GetMaxIndexInInterface()) {
317 0 : NS_WARNING("Too many members in interface");
318 0 : failed = true;
319 0 : break;
320 : }
321 9500 : cur = &members[realTotalCount];
322 9500 : cur->SetName(name);
323 9500 : if (info->IsGetter())
324 1903 : cur->SetReadOnlyAttribute(i);
325 : else
326 7597 : cur->SetMethod(i);
327 9500 : cur->SetIndexInInterface(realTotalCount);
328 9500 : ++realTotalCount;
329 : }
330 : }
331 :
332 1825 : if (!failed) {
333 5640 : for (i = 0; i < constCount; i++) {
334 7630 : RootedValue constant(cx);
335 7630 : nsXPIDLCString namestr;
336 3815 : if (NS_FAILED(aInfo->GetConstant(i, &constant, getter_Copies(namestr)))) {
337 0 : failed = true;
338 0 : break;
339 : }
340 :
341 3815 : str = JS_AtomizeAndPinString(cx, namestr);
342 3815 : if (!str) {
343 0 : NS_ERROR("bad constant name");
344 0 : failed = true;
345 0 : break;
346 : }
347 3815 : jsid name = INTERNED_STRING_TO_JSID(cx, str);
348 :
349 : // XXX need better way to find dups
350 : //MOZ_ASSERT(!LookupMemberByID(name),"duplicate method/constant name");
351 3815 : if (realTotalCount == XPCNativeMember::GetMaxIndexInInterface()) {
352 0 : NS_WARNING("Too many members in interface");
353 0 : failed = true;
354 0 : break;
355 : }
356 3815 : cur = &members[realTotalCount];
357 3815 : cur->SetName(name);
358 3815 : cur->SetConstant(i);
359 3815 : cur->SetIndexInInterface(realTotalCount);
360 3815 : ++realTotalCount;
361 : }
362 : }
363 :
364 1825 : if (!failed) {
365 : const char* bytes;
366 7300 : if (NS_FAILED(aInfo->GetNameShared(&bytes)) || !bytes ||
367 7300 : nullptr == (str = JS_AtomizeAndPinString(cx, bytes))) {
368 0 : failed = true;
369 : }
370 1825 : interfaceName = INTERNED_STRING_TO_JSID(cx, str);
371 : }
372 :
373 1825 : if (!failed) {
374 : // Use placement new to create an object with the right amount of space
375 : // to hold the members array
376 1825 : int size = sizeof(XPCNativeInterface);
377 1825 : if (realTotalCount > 1)
378 1804 : size += (realTotalCount - 1) * sizeof(XPCNativeMember);
379 3650 : void* place = new char[size];
380 1825 : if (place)
381 1825 : obj = new(place) XPCNativeInterface(aInfo, interfaceName);
382 :
383 1825 : if (obj) {
384 1825 : obj->mMemberCount = realTotalCount;
385 : // copy valid members
386 1825 : if (realTotalCount)
387 1825 : memcpy(obj->mMembers, members,
388 1825 : realTotalCount * sizeof(XPCNativeMember));
389 : }
390 : }
391 :
392 1825 : if (members && members != local_members)
393 234 : delete [] members;
394 :
395 1825 : return obj.forget();
396 : }
397 :
398 : // static
399 : void
400 1557 : XPCNativeInterface::DestroyInstance(XPCNativeInterface* inst)
401 : {
402 1557 : inst->~XPCNativeInterface();
403 1557 : delete [] (char*) inst;
404 1557 : }
405 :
406 : size_t
407 0 : XPCNativeInterface::SizeOfIncludingThis(MallocSizeOf mallocSizeOf)
408 : {
409 0 : return mallocSizeOf(this);
410 : }
411 :
412 : void
413 0 : XPCNativeInterface::DebugDump(int16_t depth)
414 : {
415 : #ifdef DEBUG
416 0 : depth--;
417 0 : XPC_LOG_ALWAYS(("XPCNativeInterface @ %p", this));
418 0 : XPC_LOG_INDENT();
419 0 : XPC_LOG_ALWAYS(("name is %s", GetNameString()));
420 0 : XPC_LOG_ALWAYS(("mMemberCount is %d", mMemberCount));
421 0 : XPC_LOG_ALWAYS(("mInfo @ %p", mInfo.get()));
422 0 : XPC_LOG_OUTDENT();
423 : #endif
424 0 : }
425 :
426 : /***************************************************************************/
427 : // XPCNativeSetKey
428 :
429 : static PLDHashNumber
430 7094 : HashPointer(const void* ptr)
431 : {
432 7094 : return NS_PTR_TO_UINT32(ptr) >> 2;
433 : }
434 :
435 : PLDHashNumber
436 3745 : XPCNativeSetKey::Hash() const
437 : {
438 3745 : PLDHashNumber h = 0;
439 :
440 3745 : if (mBaseSet) {
441 1665 : XPCNativeInterface** current = mBaseSet->GetInterfaceArray();
442 1665 : uint16_t count = mBaseSet->GetInterfaceCount();
443 5020 : for (uint16_t i = 0; i < count; i++) {
444 3355 : h ^= HashPointer(*(current++));
445 : }
446 : } else {
447 : // A newly created set will contain nsISupports first...
448 3390 : RefPtr<XPCNativeInterface> isupp = XPCNativeInterface::GetISupports();
449 2080 : h ^= HashPointer(isupp);
450 :
451 : // ...but no more than once.
452 2080 : if (isupp == mAddition)
453 770 : return h;
454 : }
455 :
456 2975 : if (mAddition) {
457 1659 : h ^= HashPointer(mAddition);
458 : }
459 :
460 2975 : return h;
461 : }
462 :
463 : /***************************************************************************/
464 : // XPCNativeSet
465 :
466 752 : XPCNativeSet::~XPCNativeSet()
467 : {
468 : // Remove |this| before we clear the interfaces to ensure that the
469 : // hashtable look up is correct.
470 376 : XPCJSRuntime::Get()->GetNativeSetMap()->Remove(this);
471 :
472 1143 : for (int i = 0; i < mInterfaceCount; i++) {
473 767 : NS_RELEASE(mInterfaces[i]);
474 : }
475 376 : }
476 :
477 : // static
478 : already_AddRefed<XPCNativeSet>
479 374 : XPCNativeSet::GetNewOrUsed(const nsIID* iid)
480 : {
481 : RefPtr<XPCNativeInterface> iface =
482 748 : XPCNativeInterface::GetNewOrUsed(iid);
483 374 : if (!iface)
484 0 : return nullptr;
485 :
486 748 : XPCNativeSetKey key(iface);
487 :
488 374 : XPCJSRuntime* xpcrt = XPCJSRuntime::Get();
489 374 : NativeSetMap* map = xpcrt->GetNativeSetMap();
490 374 : if (!map)
491 0 : return nullptr;
492 :
493 748 : RefPtr<XPCNativeSet> set = map->Find(&key);
494 :
495 374 : if (set)
496 371 : return set.forget();
497 :
498 3 : set = NewInstance({iface.forget()});
499 3 : if (!set)
500 0 : return nullptr;
501 :
502 3 : if (!map->AddNew(&key, set)) {
503 0 : NS_ERROR("failed to add our set!");
504 0 : set = nullptr;
505 : }
506 :
507 3 : return set.forget();
508 : }
509 :
510 : // static
511 : already_AddRefed<XPCNativeSet>
512 1824 : XPCNativeSet::GetNewOrUsed(nsIClassInfo* classInfo)
513 : {
514 1824 : XPCJSRuntime* xpcrt = XPCJSRuntime::Get();
515 1824 : ClassInfo2NativeSetMap* map = xpcrt->GetClassInfo2NativeSetMap();
516 1824 : if (!map)
517 0 : return nullptr;
518 :
519 3648 : RefPtr<XPCNativeSet> set = map->Find(classInfo);
520 :
521 1824 : if (set)
522 755 : return set.forget();
523 :
524 1069 : nsIID** iidArray = nullptr;
525 1069 : uint32_t iidCount = 0;
526 :
527 1069 : if (NS_FAILED(classInfo->GetInterfaces(&iidCount, &iidArray))) {
528 : // Note: I'm making it OK for this call to fail so that one can add
529 : // nsIClassInfo to classes implemented in script without requiring this
530 : // method to be implemented.
531 :
532 : // Make sure these are set correctly...
533 0 : iidArray = nullptr;
534 0 : iidCount = 0;
535 : }
536 :
537 1069 : MOZ_ASSERT((iidCount && iidArray) || !(iidCount || iidArray), "GetInterfaces returned bad array");
538 :
539 : // !!! from here on we only exit through the 'out' label !!!
540 :
541 1069 : if (iidCount) {
542 1900 : nsTArray<RefPtr<XPCNativeInterface>> interfaceArray(iidCount);
543 950 : nsIID** currentIID = iidArray;
544 :
545 2864 : for (uint32_t i = 0; i < iidCount; i++) {
546 1914 : nsIID* iid = *(currentIID++);
547 1914 : if (!iid) {
548 0 : NS_ERROR("Null found in classinfo interface list");
549 0 : continue;
550 : }
551 :
552 : RefPtr<XPCNativeInterface> iface =
553 2652 : XPCNativeInterface::GetNewOrUsed(iid);
554 :
555 1914 : if (!iface) {
556 : // XXX warn here
557 1176 : continue;
558 : }
559 :
560 738 : interfaceArray.AppendElement(iface.forget());
561 : }
562 :
563 950 : if (interfaceArray.Length() > 0) {
564 695 : set = NewInstance(Move(interfaceArray));
565 695 : if (set) {
566 695 : NativeSetMap* map2 = xpcrt->GetNativeSetMap();
567 695 : if (!map2)
568 0 : goto out;
569 :
570 1390 : XPCNativeSetKey key(set);
571 :
572 695 : XPCNativeSet* set2 = map2->Add(&key, set);
573 695 : if (!set2) {
574 0 : NS_ERROR("failed to add our set!");
575 0 : set = nullptr;
576 0 : goto out;
577 : }
578 : // It is okay to find an existing entry here because
579 : // we did not look for one before we called Add().
580 695 : if (set2 != set) {
581 329 : set = set2;
582 : }
583 : }
584 : } else
585 255 : set = GetNewOrUsed(&NS_GET_IID(nsISupports));
586 : } else
587 119 : set = GetNewOrUsed(&NS_GET_IID(nsISupports));
588 :
589 1069 : if (set) {
590 : #ifdef DEBUG
591 : XPCNativeSet* set2 =
592 : #endif
593 1069 : map->Add(classInfo, set);
594 1069 : MOZ_ASSERT(set2, "failed to add our set!");
595 1069 : MOZ_ASSERT(set2 == set, "hashtables inconsistent!");
596 : }
597 :
598 : out:
599 1069 : if (iidArray)
600 950 : NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(iidCount, iidArray);
601 :
602 1069 : return set.forget();
603 : }
604 :
605 : // static
606 : void
607 0 : XPCNativeSet::ClearCacheEntryForClassInfo(nsIClassInfo* classInfo)
608 : {
609 0 : XPCJSRuntime* xpcrt = nsXPConnect::GetRuntimeInstance();
610 0 : ClassInfo2NativeSetMap* map = xpcrt->GetClassInfo2NativeSetMap();
611 0 : if (map)
612 0 : map->Remove(classInfo);
613 0 : }
614 :
615 : // static
616 : already_AddRefed<XPCNativeSet>
617 1568 : XPCNativeSet::GetNewOrUsed(XPCNativeSetKey* key)
618 : {
619 1568 : NativeSetMap* map = XPCJSRuntime::Get()->GetNativeSetMap();
620 1568 : if (!map)
621 0 : return nullptr;
622 :
623 3136 : RefPtr<XPCNativeSet> set = map->Find(key);
624 :
625 1568 : if (set)
626 1326 : return set.forget();
627 :
628 242 : if (key->GetBaseSet())
629 57 : set = NewInstanceMutate(key);
630 : else
631 185 : set = NewInstance({key->GetAddition()});
632 :
633 242 : if (!set)
634 0 : return nullptr;
635 :
636 242 : if (!map->AddNew(key, set)) {
637 0 : NS_ERROR("failed to add our set!");
638 0 : set = nullptr;
639 : }
640 :
641 242 : return set.forget();
642 : }
643 :
644 : // static
645 : already_AddRefed<XPCNativeSet>
646 2766 : XPCNativeSet::GetNewOrUsed(XPCNativeSet* firstSet,
647 : XPCNativeSet* secondSet,
648 : bool preserveFirstSetOrder)
649 : {
650 : // Figure out how many interfaces we'll need in the new set.
651 2766 : uint32_t uniqueCount = firstSet->mInterfaceCount;
652 9873 : for (uint32_t i = 0; i < secondSet->mInterfaceCount; ++i) {
653 7107 : if (!firstSet->HasInterface(secondSet->mInterfaces[i]))
654 549 : uniqueCount++;
655 : }
656 :
657 : // If everything in secondSet was a duplicate, we can just use the first
658 : // set.
659 2766 : if (uniqueCount == firstSet->mInterfaceCount)
660 2367 : return RefPtr<XPCNativeSet>(firstSet).forget();
661 :
662 : // If the secondSet is just a superset of the first, we can use it provided
663 : // that the caller doesn't care about ordering.
664 399 : if (!preserveFirstSetOrder && uniqueCount == secondSet->mInterfaceCount)
665 398 : return RefPtr<XPCNativeSet>(secondSet).forget();
666 :
667 : // Ok, darn. Now we have to make a new set.
668 : //
669 : // It would be faster to just create the new set all at once, but that
670 : // would involve wrangling with some pretty hairy code - especially since
671 : // a lot of stuff assumes that sets are created by adding one interface to an
672 : // existing set. So let's just do the slow and easy thing and hope that the
673 : // above optimizations handle the common cases.
674 2 : RefPtr<XPCNativeSet> currentSet = firstSet;
675 4 : for (uint32_t i = 0; i < secondSet->mInterfaceCount; ++i) {
676 3 : XPCNativeInterface* iface = secondSet->mInterfaces[i];
677 3 : if (!currentSet->HasInterface(iface)) {
678 : // Create a new augmented set, inserting this interface at the end.
679 4 : XPCNativeSetKey key(currentSet, iface);
680 2 : currentSet = XPCNativeSet::GetNewOrUsed(&key);
681 2 : if (!currentSet)
682 0 : return nullptr;
683 : }
684 : }
685 :
686 : // We've got the union set. Hand it back to the caller.
687 1 : MOZ_ASSERT(currentSet->mInterfaceCount == uniqueCount);
688 1 : return currentSet.forget();
689 : }
690 :
691 : // static
692 : already_AddRefed<XPCNativeSet>
693 883 : XPCNativeSet::NewInstance(nsTArray<RefPtr<XPCNativeInterface>>&& array)
694 : {
695 883 : if (array.Length() == 0)
696 0 : return nullptr;
697 :
698 : // We impose the invariant:
699 : // "All sets have exactly one nsISupports interface and it comes first."
700 : // This is the place where we impose that rule - even if given inputs
701 : // that don't exactly follow the rule.
702 :
703 1766 : RefPtr<XPCNativeInterface> isup = XPCNativeInterface::GetISupports();
704 883 : uint16_t slots = array.Length() + 1;
705 :
706 1809 : for (auto key = array.begin(); key != array.end(); key++) {
707 926 : if (*key == isup)
708 3 : slots--;
709 : }
710 :
711 : // Use placement new to create an object with the right amount of space
712 : // to hold the members array
713 883 : int size = sizeof(XPCNativeSet);
714 883 : if (slots > 1)
715 880 : size += (slots - 1) * sizeof(XPCNativeInterface*);
716 1766 : void* place = new char[size];
717 1766 : RefPtr<XPCNativeSet> obj = new(place) XPCNativeSet();
718 :
719 : // Stick the nsISupports in front and skip additional nsISupport(s)
720 883 : XPCNativeInterface** outp = (XPCNativeInterface**) &obj->mInterfaces;
721 883 : uint16_t memberCount = 1; // for the one member in nsISupports
722 :
723 883 : NS_ADDREF(*(outp++) = isup);
724 :
725 1809 : for (auto key = array.begin(); key != array.end(); key++) {
726 1849 : RefPtr<XPCNativeInterface> cur = key->forget();
727 926 : if (isup == cur)
728 3 : continue;
729 923 : memberCount += cur->GetMemberCount();
730 923 : *(outp++) = cur.forget().take();
731 : }
732 883 : obj->mMemberCount = memberCount;
733 883 : obj->mInterfaceCount = slots;
734 :
735 883 : return obj.forget();
736 : }
737 :
738 : // static
739 : already_AddRefed<XPCNativeSet>
740 57 : XPCNativeSet::NewInstanceMutate(XPCNativeSetKey* key)
741 : {
742 57 : XPCNativeSet* otherSet = key->GetBaseSet();
743 57 : XPCNativeInterface* newInterface = key->GetAddition();
744 :
745 57 : MOZ_ASSERT(otherSet);
746 :
747 57 : if (!newInterface)
748 0 : return nullptr;
749 :
750 : // Use placement new to create an object with the right amount of space
751 : // to hold the members array
752 57 : int size = sizeof(XPCNativeSet);
753 57 : size += otherSet->mInterfaceCount * sizeof(XPCNativeInterface*);
754 114 : void* place = new char[size];
755 114 : RefPtr<XPCNativeSet> obj = new(place) XPCNativeSet();
756 :
757 114 : obj->mMemberCount = otherSet->GetMemberCount() +
758 57 : newInterface->GetMemberCount();
759 57 : obj->mInterfaceCount = otherSet->mInterfaceCount + 1;
760 :
761 57 : XPCNativeInterface** src = otherSet->mInterfaces;
762 57 : XPCNativeInterface** dest = obj->mInterfaces;
763 187 : for (uint16_t i = 0; i < otherSet->mInterfaceCount; i++) {
764 130 : NS_ADDREF(*dest++ = *src++);
765 : }
766 57 : NS_ADDREF(*dest++ = newInterface);
767 :
768 57 : return obj.forget();
769 : }
770 :
771 : // static
772 : void
773 376 : XPCNativeSet::DestroyInstance(XPCNativeSet* inst)
774 : {
775 376 : inst->~XPCNativeSet();
776 376 : delete [] (char*) inst;
777 376 : }
778 :
779 : size_t
780 0 : XPCNativeSet::SizeOfIncludingThis(MallocSizeOf mallocSizeOf)
781 : {
782 0 : return mallocSizeOf(this);
783 : }
784 :
785 : void
786 0 : XPCNativeSet::DebugDump(int16_t depth)
787 : {
788 : #ifdef DEBUG
789 0 : depth--;
790 0 : XPC_LOG_ALWAYS(("XPCNativeSet @ %p", this));
791 0 : XPC_LOG_INDENT();
792 :
793 0 : XPC_LOG_ALWAYS(("mInterfaceCount of %d", mInterfaceCount));
794 0 : if (depth) {
795 0 : for (uint16_t i = 0; i < mInterfaceCount; i++)
796 0 : mInterfaces[i]->DebugDump(depth);
797 : }
798 0 : XPC_LOG_ALWAYS(("mMemberCount of %d", mMemberCount));
799 0 : XPC_LOG_OUTDENT();
800 : #endif
801 0 : }
|