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: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /* Implementation of xptiInterfaceEntry and xptiInterfaceInfo. */
8 #include "xptiprivate.h"
9 #include "mozilla/XPTInterfaceInfoManager.h"
11 using namespace mozilla;
13 /***************************************************************************/
14 // Debug Instrumentation...
16 #ifdef SHOW_INFO_COUNT_STATS
17 static int DEBUG_TotalInfos = 0;
18 static int DEBUG_CurrentInfos = 0;
19 static int DEBUG_MaxInfos = 0;
20 static int DEBUG_ReentrantMonitorEntryCount = 0;
22 #define LOG_INFO_CREATE(t) \
23 DEBUG_TotalInfos++; \
24 DEBUG_CurrentInfos++; \
25 if(DEBUG_MaxInfos < DEBUG_CurrentInfos) \
26 DEBUG_MaxInfos = DEBUG_CurrentInfos /* no ';' */
28 #define LOG_INFO_DESTROY(t) \
29 DEBUG_CurrentInfos-- /* no ';' */
31 #define LOG_INFO_MONITOR_ENTRY \
32 DEBUG_ReentrantMonitorEntryCount++ /* no ';' */
34 #else /* SHOW_INFO_COUNT_STATS */
36 #define LOG_INFO_CREATE(t) ((void)0)
37 #define LOG_INFO_DESTROY(t) ((void)0)
38 #define LOG_INFO_MONITOR_ENTRY ((void)0)
39 #endif /* SHOW_INFO_COUNT_STATS */
41 /* static */ xptiInterfaceEntry*
42 xptiInterfaceEntry::Create(const char* name, const nsID& iid,
43 XPTInterfaceDescriptor* aDescriptor,
44 xptiTypelibGuts* aTypelib)
45 {
46 int namelen = strlen(name);
47 return new (XPT_MALLOC(gXPTIStructArena,
48 sizeof(xptiInterfaceEntry) + namelen))
49 xptiInterfaceEntry(name, namelen, iid, aDescriptor, aTypelib);
50 }
52 xptiInterfaceEntry::xptiInterfaceEntry(const char* name,
53 size_t nameLength,
54 const nsID& iid,
55 XPTInterfaceDescriptor* aDescriptor,
56 xptiTypelibGuts* aTypelib)
57 : mIID(iid)
58 , mDescriptor(aDescriptor)
59 , mMethodBaseIndex(0)
60 , mConstantBaseIndex(0)
61 , mTypelib(aTypelib)
62 , mParent(nullptr)
63 , mInfo(nullptr)
64 , mFlags(0)
65 {
66 memcpy(mName, name, nameLength);
67 SetResolvedState(PARTIALLY_RESOLVED);
68 }
70 bool
71 xptiInterfaceEntry::Resolve()
72 {
73 MutexAutoLock lock(XPTInterfaceInfoManager::GetResolveLock());
74 return ResolveLocked();
75 }
77 bool
78 xptiInterfaceEntry::ResolveLocked()
79 {
80 int resolvedState = GetResolveState();
82 if(resolvedState == FULLY_RESOLVED)
83 return true;
84 if(resolvedState == RESOLVE_FAILED)
85 return false;
87 NS_ASSERTION(GetResolveState() == PARTIALLY_RESOLVED, "bad state!");
89 // Finish out resolution by finding parent and Resolving it so
90 // we can set the info we get from it.
92 uint16_t parent_index = mDescriptor->parent_interface;
94 if(parent_index)
95 {
96 xptiInterfaceEntry* parent =
97 mTypelib->GetEntryAt(parent_index - 1);
99 if(!parent || !parent->EnsureResolvedLocked())
100 {
101 SetResolvedState(RESOLVE_FAILED);
102 return false;
103 }
105 mParent = parent;
107 mMethodBaseIndex =
108 parent->mMethodBaseIndex +
109 parent->mDescriptor->num_methods;
111 mConstantBaseIndex =
112 parent->mConstantBaseIndex +
113 parent->mDescriptor->num_constants;
115 }
116 LOG_RESOLVE(("+ complete resolve of %s\n", mName));
118 SetResolvedState(FULLY_RESOLVED);
119 return true;
120 }
122 /**************************************************/
123 // These non-virtual methods handle the delegated nsIInterfaceInfo methods.
125 nsresult
126 xptiInterfaceEntry::GetName(char **name)
127 {
128 // It is not necessary to Resolve because this info is read from manifest.
129 *name = (char*) nsMemory::Clone(mName, strlen(mName)+1);
130 return *name ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
131 }
133 nsresult
134 xptiInterfaceEntry::GetIID(nsIID **iid)
135 {
136 // It is not necessary to Resolve because this info is read from manifest.
137 *iid = (nsIID*) nsMemory::Clone(&mIID, sizeof(nsIID));
138 return *iid ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
139 }
141 nsresult
142 xptiInterfaceEntry::IsScriptable(bool* result)
143 {
144 // It is not necessary to Resolve because this info is read from manifest.
145 *result = GetScriptableFlag();
146 return NS_OK;
147 }
149 nsresult
150 xptiInterfaceEntry::IsFunction(bool* result)
151 {
152 if(!EnsureResolved())
153 return NS_ERROR_UNEXPECTED;
155 *result = XPT_ID_IS_FUNCTION(mDescriptor->flags);
156 return NS_OK;
157 }
159 nsresult
160 xptiInterfaceEntry::GetMethodCount(uint16_t* count)
161 {
162 if(!EnsureResolved())
163 return NS_ERROR_UNEXPECTED;
165 *count = mMethodBaseIndex +
166 mDescriptor->num_methods;
167 return NS_OK;
168 }
170 nsresult
171 xptiInterfaceEntry::GetConstantCount(uint16_t* count)
172 {
173 if(!EnsureResolved())
174 return NS_ERROR_UNEXPECTED;
176 *count = mConstantBaseIndex +
177 mDescriptor->num_constants;
178 return NS_OK;
179 }
181 nsresult
182 xptiInterfaceEntry::GetMethodInfo(uint16_t index, const nsXPTMethodInfo** info)
183 {
184 if(!EnsureResolved())
185 return NS_ERROR_UNEXPECTED;
187 if(index < mMethodBaseIndex)
188 return mParent->GetMethodInfo(index, info);
190 if(index >= mMethodBaseIndex +
191 mDescriptor->num_methods)
192 {
193 NS_ERROR("bad param");
194 *info = nullptr;
195 return NS_ERROR_INVALID_ARG;
196 }
198 // else...
199 *info = reinterpret_cast<nsXPTMethodInfo*>
200 (&mDescriptor->method_descriptors[index - mMethodBaseIndex]);
201 return NS_OK;
202 }
204 nsresult
205 xptiInterfaceEntry::GetMethodInfoForName(const char* methodName, uint16_t *index,
206 const nsXPTMethodInfo** result)
207 {
208 if(!EnsureResolved())
209 return NS_ERROR_UNEXPECTED;
211 // This is a slow algorithm, but this is not expected to be called much.
212 for(uint16_t i = 0; i < mDescriptor->num_methods; ++i)
213 {
214 const nsXPTMethodInfo* info;
215 info = reinterpret_cast<nsXPTMethodInfo*>
216 (&mDescriptor->
217 method_descriptors[i]);
218 if (PL_strcmp(methodName, info->GetName()) == 0) {
219 *index = i + mMethodBaseIndex;
220 *result = info;
221 return NS_OK;
222 }
223 }
225 if(mParent)
226 return mParent->GetMethodInfoForName(methodName, index, result);
227 else
228 {
229 *index = 0;
230 *result = 0;
231 return NS_ERROR_INVALID_ARG;
232 }
233 }
235 nsresult
236 xptiInterfaceEntry::GetConstant(uint16_t index, const nsXPTConstant** constant)
237 {
238 if(!EnsureResolved())
239 return NS_ERROR_UNEXPECTED;
241 if(index < mConstantBaseIndex)
242 return mParent->GetConstant(index, constant);
244 if(index >= mConstantBaseIndex +
245 mDescriptor->num_constants)
246 {
247 NS_PRECONDITION(0, "bad param");
248 *constant = nullptr;
249 return NS_ERROR_INVALID_ARG;
250 }
252 // else...
253 *constant =
254 reinterpret_cast<nsXPTConstant*>
255 (&mDescriptor->
256 const_descriptors[index -
257 mConstantBaseIndex]);
258 return NS_OK;
259 }
261 // this is a private helper
263 nsresult
264 xptiInterfaceEntry::GetEntryForParam(uint16_t methodIndex,
265 const nsXPTParamInfo * param,
266 xptiInterfaceEntry** entry)
267 {
268 if(!EnsureResolved())
269 return NS_ERROR_UNEXPECTED;
271 if(methodIndex < mMethodBaseIndex)
272 return mParent->GetEntryForParam(methodIndex, param, entry);
274 if(methodIndex >= mMethodBaseIndex +
275 mDescriptor->num_methods)
276 {
277 NS_ERROR("bad param");
278 return NS_ERROR_INVALID_ARG;
279 }
281 const XPTTypeDescriptor *td = ¶m->type;
283 while (XPT_TDP_TAG(td->prefix) == TD_ARRAY) {
284 td = &mDescriptor->additional_types[td->type.additional_type];
285 }
287 if(XPT_TDP_TAG(td->prefix) != TD_INTERFACE_TYPE) {
288 NS_ERROR("not an interface");
289 return NS_ERROR_INVALID_ARG;
290 }
292 xptiInterfaceEntry* theEntry = mTypelib->
293 GetEntryAt(td->type.iface - 1);
295 // This can happen if a declared interface is not available at runtime.
296 if(!theEntry)
297 {
298 NS_WARNING("Declared InterfaceInfo not found");
299 *entry = nullptr;
300 return NS_ERROR_FAILURE;
301 }
303 *entry = theEntry;
304 return NS_OK;
305 }
307 nsresult
308 xptiInterfaceEntry::GetInfoForParam(uint16_t methodIndex,
309 const nsXPTParamInfo *param,
310 nsIInterfaceInfo** info)
311 {
312 xptiInterfaceEntry* entry;
313 nsresult rv = GetEntryForParam(methodIndex, param, &entry);
314 if(NS_FAILED(rv))
315 return rv;
317 *info = entry->InterfaceInfo().take();
319 return NS_OK;
320 }
322 nsresult
323 xptiInterfaceEntry::GetIIDForParam(uint16_t methodIndex,
324 const nsXPTParamInfo* param, nsIID** iid)
325 {
326 xptiInterfaceEntry* entry;
327 nsresult rv = GetEntryForParam(methodIndex, param, &entry);
328 if(NS_FAILED(rv))
329 return rv;
330 return entry->GetIID(iid);
331 }
333 nsresult
334 xptiInterfaceEntry::GetIIDForParamNoAlloc(uint16_t methodIndex,
335 const nsXPTParamInfo * param,
336 nsIID *iid)
337 {
338 xptiInterfaceEntry* entry;
339 nsresult rv = GetEntryForParam(methodIndex, param, &entry);
340 if(NS_FAILED(rv))
341 return rv;
342 *iid = entry->mIID;
343 return NS_OK;
344 }
346 // this is a private helper
347 nsresult
348 xptiInterfaceEntry::GetTypeInArray(const nsXPTParamInfo* param,
349 uint16_t dimension,
350 const XPTTypeDescriptor** type)
351 {
352 NS_ASSERTION(IsFullyResolved(), "bad state");
354 const XPTTypeDescriptor *td = ¶m->type;
355 const XPTTypeDescriptor *additional_types =
356 mDescriptor->additional_types;
358 for (uint16_t i = 0; i < dimension; i++) {
359 if(XPT_TDP_TAG(td->prefix) != TD_ARRAY) {
360 NS_ERROR("bad dimension");
361 return NS_ERROR_INVALID_ARG;
362 }
363 td = &additional_types[td->type.additional_type];
364 }
366 *type = td;
367 return NS_OK;
368 }
370 nsresult
371 xptiInterfaceEntry::GetTypeForParam(uint16_t methodIndex,
372 const nsXPTParamInfo* param,
373 uint16_t dimension,
374 nsXPTType* type)
375 {
376 if(!EnsureResolved())
377 return NS_ERROR_UNEXPECTED;
379 if(methodIndex < mMethodBaseIndex)
380 return mParent->
381 GetTypeForParam(methodIndex, param, dimension, type);
383 if(methodIndex >= mMethodBaseIndex +
384 mDescriptor->num_methods)
385 {
386 NS_ERROR("bad index");
387 return NS_ERROR_INVALID_ARG;
388 }
390 const XPTTypeDescriptor *td;
392 if(dimension) {
393 nsresult rv = GetTypeInArray(param, dimension, &td);
394 if(NS_FAILED(rv))
395 return rv;
396 }
397 else
398 td = ¶m->type;
400 *type = nsXPTType(td->prefix);
401 return NS_OK;
402 }
404 nsresult
405 xptiInterfaceEntry::GetSizeIsArgNumberForParam(uint16_t methodIndex,
406 const nsXPTParamInfo* param,
407 uint16_t dimension,
408 uint8_t* argnum)
409 {
410 if(!EnsureResolved())
411 return NS_ERROR_UNEXPECTED;
413 if(methodIndex < mMethodBaseIndex)
414 return mParent->
415 GetSizeIsArgNumberForParam(methodIndex, param, dimension, argnum);
417 if(methodIndex >= mMethodBaseIndex +
418 mDescriptor->num_methods)
419 {
420 NS_ERROR("bad index");
421 return NS_ERROR_INVALID_ARG;
422 }
424 const XPTTypeDescriptor *td;
426 if(dimension) {
427 nsresult rv = GetTypeInArray(param, dimension, &td);
428 if(NS_FAILED(rv))
429 return rv;
430 }
431 else
432 td = ¶m->type;
434 // verify that this is a type that has size_is
435 switch (XPT_TDP_TAG(td->prefix)) {
436 case TD_ARRAY:
437 case TD_PSTRING_SIZE_IS:
438 case TD_PWSTRING_SIZE_IS:
439 break;
440 default:
441 NS_ERROR("not a size_is");
442 return NS_ERROR_INVALID_ARG;
443 }
445 *argnum = td->argnum;
446 return NS_OK;
447 }
449 nsresult
450 xptiInterfaceEntry::GetInterfaceIsArgNumberForParam(uint16_t methodIndex,
451 const nsXPTParamInfo* param,
452 uint8_t* argnum)
453 {
454 if(!EnsureResolved())
455 return NS_ERROR_UNEXPECTED;
457 if(methodIndex < mMethodBaseIndex)
458 return mParent->
459 GetInterfaceIsArgNumberForParam(methodIndex, param, argnum);
461 if(methodIndex >= mMethodBaseIndex +
462 mDescriptor->num_methods)
463 {
464 NS_ERROR("bad index");
465 return NS_ERROR_INVALID_ARG;
466 }
468 const XPTTypeDescriptor *td = ¶m->type;
470 while (XPT_TDP_TAG(td->prefix) == TD_ARRAY) {
471 td = &mDescriptor->
472 additional_types[td->type.additional_type];
473 }
475 if(XPT_TDP_TAG(td->prefix) != TD_INTERFACE_IS_TYPE) {
476 NS_ERROR("not an iid_is");
477 return NS_ERROR_INVALID_ARG;
478 }
480 *argnum = td->argnum;
481 return NS_OK;
482 }
484 /* bool isIID (in nsIIDPtr IID); */
485 nsresult
486 xptiInterfaceEntry::IsIID(const nsIID * IID, bool *_retval)
487 {
488 // It is not necessary to Resolve because this info is read from manifest.
489 *_retval = mIID.Equals(*IID);
490 return NS_OK;
491 }
493 /* void getNameShared ([shared, retval] out string name); */
494 nsresult
495 xptiInterfaceEntry::GetNameShared(const char **name)
496 {
497 // It is not necessary to Resolve because this info is read from manifest.
498 *name = mName;
499 return NS_OK;
500 }
502 /* void getIIDShared ([shared, retval] out nsIIDPtrShared iid); */
503 nsresult
504 xptiInterfaceEntry::GetIIDShared(const nsIID * *iid)
505 {
506 // It is not necessary to Resolve because this info is read from manifest.
507 *iid = &mIID;
508 return NS_OK;
509 }
511 /* bool hasAncestor (in nsIIDPtr iid); */
512 nsresult
513 xptiInterfaceEntry::HasAncestor(const nsIID * iid, bool *_retval)
514 {
515 *_retval = false;
517 for(xptiInterfaceEntry* current = this;
518 current;
519 current = current->mParent)
520 {
521 if(current->mIID.Equals(*iid))
522 {
523 *_retval = true;
524 break;
525 }
526 if(!current->EnsureResolved())
527 return NS_ERROR_UNEXPECTED;
528 }
530 return NS_OK;
531 }
533 /***************************************************/
535 already_AddRefed<xptiInterfaceInfo>
536 xptiInterfaceEntry::InterfaceInfo()
537 {
538 #ifdef DEBUG
539 XPTInterfaceInfoManager::GetSingleton()->mWorkingSet.mTableReentrantMonitor.
540 AssertCurrentThreadIn();
541 #endif
542 LOG_INFO_MONITOR_ENTRY;
544 if(!mInfo)
545 {
546 mInfo = new xptiInterfaceInfo(this);
547 }
549 nsRefPtr<xptiInterfaceInfo> info = mInfo;
550 return info.forget();
551 }
553 void
554 xptiInterfaceEntry::LockedInvalidateInterfaceInfo()
555 {
556 if(mInfo)
557 {
558 mInfo->Invalidate();
559 mInfo = nullptr;
560 }
561 }
563 bool
564 xptiInterfaceInfo::BuildParent()
565 {
566 mozilla::ReentrantMonitorAutoEnter monitor(XPTInterfaceInfoManager::GetSingleton()->
567 mWorkingSet.mTableReentrantMonitor);
568 NS_ASSERTION(mEntry &&
569 mEntry->IsFullyResolved() &&
570 !mParent &&
571 mEntry->Parent(),
572 "bad BuildParent call");
573 mParent = mEntry->Parent()->InterfaceInfo().take();
574 return true;
575 }
577 /***************************************************************************/
579 NS_IMPL_QUERY_INTERFACE(xptiInterfaceInfo, nsIInterfaceInfo)
581 xptiInterfaceInfo::xptiInterfaceInfo(xptiInterfaceEntry* entry)
582 : mEntry(entry), mParent(nullptr)
583 {
584 LOG_INFO_CREATE(this);
585 }
587 xptiInterfaceInfo::~xptiInterfaceInfo()
588 {
589 LOG_INFO_DESTROY(this);
590 NS_IF_RELEASE(mParent);
591 NS_ASSERTION(!mEntry, "bad state in dtor");
592 }
594 MozExternalRefCountType
595 xptiInterfaceInfo::AddRef(void)
596 {
597 nsrefcnt cnt = ++mRefCnt;
598 NS_LOG_ADDREF(this, cnt, "xptiInterfaceInfo", sizeof(*this));
599 return cnt;
600 }
602 MozExternalRefCountType
603 xptiInterfaceInfo::Release(void)
604 {
605 xptiInterfaceEntry* entry = mEntry;
606 nsrefcnt cnt = --mRefCnt;
607 NS_LOG_RELEASE(this, cnt, "xptiInterfaceInfo");
608 if(!cnt)
609 {
610 mozilla::ReentrantMonitorAutoEnter monitor(XPTInterfaceInfoManager::
611 GetSingleton()->mWorkingSet.
612 mTableReentrantMonitor);
613 LOG_INFO_MONITOR_ENTRY;
615 // If InterfaceInfo added and *released* a reference before we
616 // acquired the monitor then 'this' might already be dead. In that
617 // case we would not want to try to access any instance data. We
618 // would want to bail immediately. If 'this' is already dead then the
619 // entry will no longer have a pointer to 'this'. So, we can protect
620 // ourselves from danger without more aggressive locking.
621 if(entry && !entry->InterfaceInfoEquals(this))
622 return 0;
624 // If InterfaceInfo added a reference before we acquired the monitor
625 // then we want to bail out of here without destorying the object.
626 if(mRefCnt)
627 return 1;
629 if(mEntry)
630 {
631 mEntry->LockedInterfaceInfoDeathNotification();
632 mEntry = nullptr;
633 }
635 delete this;
636 return 0;
637 }
638 return cnt;
639 }
641 /***************************************************************************/