/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* vim: set ts=8 sts=4 et sw=4 tw=99: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* Private maps (hashtables). */ #ifndef xpcmaps_h___ #define xpcmaps_h___ #include "mozilla/MemoryReporting.h" #include "js/GCHashTable.h" // Maps... // Note that most of the declarations for hash table entries begin with // a pointer to something or another. This makes them look enough like // the PLDHashEntryStub struct that the default ops (PLDHashTable::StubOps()) // just do the right thing for most of our needs. // no virtuals in the maps - all the common stuff inlined // templates could be used to good effect here. /*************************/ class JSObject2WrappedJSMap { using Map = js::HashMap, nsXPCWrappedJS*, js::MovableCellHasher>, InfallibleAllocPolicy>; public: static JSObject2WrappedJSMap* newMap(int length) { auto* map = new JSObject2WrappedJSMap(); if (!map->mTable.init(length)) { // This is a decent estimate of the size of the hash table's // entry storage. The |2| is because on average the capacity is // twice the requested length. NS_ABORT_OOM(length * 2 * sizeof(Map::Entry)); } return map; } inline nsXPCWrappedJS* Find(JSObject* Obj) { NS_PRECONDITION(Obj,"bad param"); Map::Ptr p = mTable.lookup(Obj); return p ? p->value() : nullptr; } #ifdef DEBUG inline bool HasWrapper(nsXPCWrappedJS* wrapper) { for (auto r = mTable.all(); !r.empty(); r.popFront()) { if (r.front().value() == wrapper) return true; } return false; } #endif inline nsXPCWrappedJS* Add(JSContext* cx, nsXPCWrappedJS* wrapper) { NS_PRECONDITION(wrapper,"bad param"); JSObject* obj = wrapper->GetJSObjectPreserveColor(); Map::AddPtr p = mTable.lookupForAdd(obj); if (p) return p->value(); if (!mTable.add(p, obj, wrapper)) return nullptr; return wrapper; } inline void Remove(nsXPCWrappedJS* wrapper) { NS_PRECONDITION(wrapper,"bad param"); mTable.remove(wrapper->GetJSObjectPreserveColor()); } inline uint32_t Count() {return mTable.count();} inline void Dump(int16_t depth) { for (Map::Range r = mTable.all(); !r.empty(); r.popFront()) r.front().value()->DebugDump(depth); } void UpdateWeakPointersAfterGC(XPCJSContext* context); void ShutdownMarker(); size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; // Report the sum of SizeOfIncludingThis() for all wrapped JS in the map. // Each wrapped JS is only in one map. size_t SizeOfWrappedJS(mozilla::MallocSizeOf mallocSizeOf) const; private: JSObject2WrappedJSMap() {} Map mTable; }; /*************************/ class Native2WrappedNativeMap { public: struct Entry : public PLDHashEntryHdr { nsISupports* key; XPCWrappedNative* value; }; static Native2WrappedNativeMap* newMap(int length); inline XPCWrappedNative* Find(nsISupports* Obj) { NS_PRECONDITION(Obj,"bad param"); auto entry = static_cast(mTable.Search(Obj)); return entry ? entry->value : nullptr; } inline XPCWrappedNative* Add(XPCWrappedNative* wrapper) { NS_PRECONDITION(wrapper,"bad param"); nsISupports* obj = wrapper->GetIdentityObject(); MOZ_ASSERT(!Find(obj), "wrapper already in new scope!"); auto entry = static_cast(mTable.Add(obj, mozilla::fallible)); if (!entry) return nullptr; if (entry->key) return entry->value; entry->key = obj; entry->value = wrapper; return wrapper; } inline void Remove(XPCWrappedNative* wrapper) { NS_PRECONDITION(wrapper,"bad param"); #ifdef DEBUG XPCWrappedNative* wrapperInMap = Find(wrapper->GetIdentityObject()); MOZ_ASSERT(!wrapperInMap || wrapperInMap == wrapper, "About to remove a different wrapper with the same " "nsISupports identity! This will most likely cause serious " "problems!"); #endif mTable.Remove(wrapper->GetIdentityObject()); } inline uint32_t Count() { return mTable.EntryCount(); } PLDHashTable::Iterator Iter() { return mTable.Iter(); } size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; private: Native2WrappedNativeMap(); // no implementation explicit Native2WrappedNativeMap(int size); private: PLDHashTable mTable; }; /*************************/ class IID2WrappedJSClassMap { public: struct Entry : public PLDHashEntryHdr { const nsIID* key; nsXPCWrappedJSClass* value; static const struct PLDHashTableOps sOps; }; static IID2WrappedJSClassMap* newMap(int length); inline nsXPCWrappedJSClass* Find(REFNSIID iid) { auto entry = static_cast(mTable.Search(&iid)); return entry ? entry->value : nullptr; } inline nsXPCWrappedJSClass* Add(nsXPCWrappedJSClass* clazz) { NS_PRECONDITION(clazz,"bad param"); const nsIID* iid = &clazz->GetIID(); auto entry = static_cast(mTable.Add(iid, mozilla::fallible)); if (!entry) return nullptr; if (entry->key) return entry->value; entry->key = iid; entry->value = clazz; return clazz; } inline void Remove(nsXPCWrappedJSClass* clazz) { NS_PRECONDITION(clazz,"bad param"); mTable.Remove(&clazz->GetIID()); } inline uint32_t Count() { return mTable.EntryCount(); } #ifdef DEBUG PLDHashTable::Iterator Iter() { return mTable.Iter(); } #endif private: IID2WrappedJSClassMap(); // no implementation explicit IID2WrappedJSClassMap(int size); private: PLDHashTable mTable; }; /*************************/ class IID2NativeInterfaceMap { public: struct Entry : public PLDHashEntryHdr { const nsIID* key; XPCNativeInterface* value; static const struct PLDHashTableOps sOps; }; static IID2NativeInterfaceMap* newMap(int length); inline XPCNativeInterface* Find(REFNSIID iid) { auto entry = static_cast(mTable.Search(&iid)); return entry ? entry->value : nullptr; } inline XPCNativeInterface* Add(XPCNativeInterface* iface) { NS_PRECONDITION(iface,"bad param"); const nsIID* iid = iface->GetIID(); auto entry = static_cast(mTable.Add(iid, mozilla::fallible)); if (!entry) return nullptr; if (entry->key) return entry->value; entry->key = iid; entry->value = iface; return iface; } inline void Remove(XPCNativeInterface* iface) { NS_PRECONDITION(iface,"bad param"); mTable.Remove(iface->GetIID()); } inline uint32_t Count() { return mTable.EntryCount(); } PLDHashTable::Iterator Iter() { return mTable.Iter(); } size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; private: IID2NativeInterfaceMap(); // no implementation explicit IID2NativeInterfaceMap(int size); private: PLDHashTable mTable; }; /*************************/ class ClassInfo2NativeSetMap { public: struct Entry : public PLDHashEntryHdr { nsIClassInfo* key; XPCNativeSet* value; // strong reference static const PLDHashTableOps sOps; private: static bool Match(const PLDHashEntryHdr* aEntry, const void* aKey); static void Clear(PLDHashTable* aTable, PLDHashEntryHdr* aEntry); }; static ClassInfo2NativeSetMap* newMap(int length); inline XPCNativeSet* Find(nsIClassInfo* info) { auto entry = static_cast(mTable.Search(info)); return entry ? entry->value : nullptr; } inline XPCNativeSet* Add(nsIClassInfo* info, XPCNativeSet* set) { NS_PRECONDITION(info,"bad param"); auto entry = static_cast(mTable.Add(info, mozilla::fallible)); if (!entry) return nullptr; if (entry->key) return entry->value; entry->key = info; NS_ADDREF(entry->value = set); return set; } inline void Remove(nsIClassInfo* info) { NS_PRECONDITION(info,"bad param"); mTable.Remove(info); } inline uint32_t Count() { return mTable.EntryCount(); } // ClassInfo2NativeSetMap holds pointers to *some* XPCNativeSets. // So we don't want to count those XPCNativeSets, because they are better // counted elsewhere (i.e. in XPCJSContext::mNativeSetMap, which holds // pointers to *all* XPCNativeSets). Hence the "Shallow". size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); private: ClassInfo2NativeSetMap(); // no implementation explicit ClassInfo2NativeSetMap(int size); private: PLDHashTable mTable; }; /*************************/ class ClassInfo2WrappedNativeProtoMap { public: struct Entry : public PLDHashEntryHdr { nsIClassInfo* key; XPCWrappedNativeProto* value; }; static ClassInfo2WrappedNativeProtoMap* newMap(int length); inline XPCWrappedNativeProto* Find(nsIClassInfo* info) { auto entry = static_cast(mTable.Search(info)); return entry ? entry->value : nullptr; } inline XPCWrappedNativeProto* Add(nsIClassInfo* info, XPCWrappedNativeProto* proto) { NS_PRECONDITION(info,"bad param"); auto entry = static_cast(mTable.Add(info, mozilla::fallible)); if (!entry) return nullptr; if (entry->key) return entry->value; entry->key = info; entry->value = proto; return proto; } inline void Remove(nsIClassInfo* info) { NS_PRECONDITION(info,"bad param"); mTable.Remove(info); } inline uint32_t Count() { return mTable.EntryCount(); } PLDHashTable::Iterator Iter() { return mTable.Iter(); } size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; private: ClassInfo2WrappedNativeProtoMap(); // no implementation explicit ClassInfo2WrappedNativeProtoMap(int size); private: PLDHashTable mTable; }; /*************************/ class NativeSetMap { public: struct Entry : public PLDHashEntryHdr { XPCNativeSet* key_value; static bool Match(const PLDHashEntryHdr* entry, const void* key); static const struct PLDHashTableOps sOps; }; static NativeSetMap* newMap(int length); inline XPCNativeSet* Find(XPCNativeSetKey* key) { auto entry = static_cast(mTable.Search(key)); return entry ? entry->key_value : nullptr; } inline XPCNativeSet* Add(const XPCNativeSetKey* key, XPCNativeSet* set) { MOZ_ASSERT(key, "bad param"); MOZ_ASSERT(set, "bad param"); auto entry = static_cast(mTable.Add(key, mozilla::fallible)); if (!entry) return nullptr; if (entry->key_value) return entry->key_value; entry->key_value = set; return set; } bool AddNew(const XPCNativeSetKey* key, XPCNativeSet* set) { XPCNativeSet* set2 = Add(key, set); if (!set2) { return false; } #ifdef DEBUG XPCNativeSetKey key2(set); MOZ_ASSERT(key->Hash() == key2.Hash()); MOZ_ASSERT(set2 == set, "Should not have found an existing entry"); #endif return true; } inline void Remove(XPCNativeSet* set) { MOZ_ASSERT(set, "bad param"); XPCNativeSetKey key(set); mTable.Remove(&key); } inline uint32_t Count() { return mTable.EntryCount(); } PLDHashTable::Iterator Iter() { return mTable.Iter(); } size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; private: NativeSetMap(); // no implementation explicit NativeSetMap(int size); private: PLDHashTable mTable; }; /***************************************************************************/ class IID2ThisTranslatorMap { public: struct Entry : public PLDHashEntryHdr { nsIID key; nsCOMPtr value; static bool Match(const PLDHashEntryHdr* entry, const void* key); static void Clear(PLDHashTable* table, PLDHashEntryHdr* entry); static const struct PLDHashTableOps sOps; }; static IID2ThisTranslatorMap* newMap(int length); inline nsIXPCFunctionThisTranslator* Find(REFNSIID iid) { auto entry = static_cast(mTable.Search(&iid)); if (!entry) { return nullptr; } return entry->value; } inline nsIXPCFunctionThisTranslator* Add(REFNSIID iid, nsIXPCFunctionThisTranslator* obj) { auto entry = static_cast(mTable.Add(&iid, mozilla::fallible)); if (!entry) return nullptr; entry->value = obj; entry->key = iid; return obj; } inline void Remove(REFNSIID iid) { mTable.Remove(&iid); } inline uint32_t Count() { return mTable.EntryCount(); } private: IID2ThisTranslatorMap(); // no implementation explicit IID2ThisTranslatorMap(int size); private: PLDHashTable mTable; }; /***************************************************************************/ class XPCWrappedNativeProtoMap { public: typedef PLDHashEntryStub Entry; static XPCWrappedNativeProtoMap* newMap(int length); inline XPCWrappedNativeProto* Add(XPCWrappedNativeProto* proto) { NS_PRECONDITION(proto,"bad param"); auto entry = static_cast (mTable.Add(proto, mozilla::fallible)); if (!entry) return nullptr; if (entry->key) return (XPCWrappedNativeProto*) entry->key; entry->key = proto; return proto; } inline void Remove(XPCWrappedNativeProto* proto) { NS_PRECONDITION(proto,"bad param"); mTable.Remove(proto); } inline uint32_t Count() { return mTable.EntryCount(); } PLDHashTable::Iterator Iter() { return mTable.Iter(); } private: XPCWrappedNativeProtoMap(); // no implementation explicit XPCWrappedNativeProtoMap(int size); private: PLDHashTable mTable; }; /***************************************************************************/ class JSObject2JSObjectMap { using Map = JS::GCHashMap, JS::Heap, js::MovableCellHasher>, js::SystemAllocPolicy>; public: static JSObject2JSObjectMap* newMap(int length) { auto* map = new JSObject2JSObjectMap(); if (!map->mTable.init(length)) { // This is a decent estimate of the size of the hash table's // entry storage. The |2| is because on average the capacity is // twice the requested length. NS_ABORT_OOM(length * 2 * sizeof(Map::Entry)); } return map; } inline JSObject* Find(JSObject* key) { NS_PRECONDITION(key, "bad param"); if (Map::Ptr p = mTable.lookup(key)) return p->value(); return nullptr; } /* Note: If the entry already exists, return the old value. */ inline JSObject* Add(JSContext* cx, JSObject* key, JSObject* value) { NS_PRECONDITION(key,"bad param"); Map::AddPtr p = mTable.lookupForAdd(key); if (p) return p->value(); if (!mTable.add(p, key, value)) return nullptr; MOZ_ASSERT(xpc::CompartmentPrivate::Get(key)->scope->mWaiverWrapperMap == this); return value; } inline void Remove(JSObject* key) { NS_PRECONDITION(key,"bad param"); mTable.remove(key); } inline uint32_t Count() { return mTable.count(); } void Sweep() { mTable.sweep(); } private: JSObject2JSObjectMap() {} Map mTable; }; #endif /* xpcmaps_h___ */