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