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