/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/AllocPolicy.h" #include "mozilla/MemoryReporting.h" #include "mozilla/HashTable.h" #include "js/GCHashTable.h" /***************************************************************************/ // default initial sizes for maps (hashtables) #define XPC_JS_MAP_LENGTH 32 #define XPC_NATIVE_MAP_LENGTH 8 #define XPC_NATIVE_PROTO_MAP_LENGTH 8 #define XPC_DYING_NATIVE_PROTO_MAP_LENGTH 8 #define XPC_NATIVE_INTERFACE_MAP_LENGTH 32 #define XPC_NATIVE_SET_MAP_LENGTH 32 #define XPC_WRAPPER_MAP_LENGTH 8 /*************************/ class JSObject2WrappedJSMap { using Map = js::HashMap, nsXPCWrappedJS*, js::StableCellHasher>, InfallibleAllocPolicy>; public: JSObject2WrappedJSMap() = default; inline nsXPCWrappedJS* Find(JSObject* Obj) { MOZ_ASSERT(Obj, "bad param"); Map::Ptr p = mTable.lookup(Obj); return p ? p->value() : nullptr; } #ifdef DEBUG inline bool HasWrapper(nsXPCWrappedJS* wrapper) { for (auto iter = mTable.iter(); !iter.done(); iter.next()) { if (iter.get().value() == wrapper) { return true; } } return false; } #endif inline nsXPCWrappedJS* Add(JSContext* cx, nsXPCWrappedJS* wrapper) { MOZ_ASSERT(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) { MOZ_ASSERT(wrapper, "bad param"); mTable.remove(wrapper->GetJSObjectPreserveColor()); } inline uint32_t Count() { return mTable.count(); } inline void Dump(int16_t depth) { for (auto iter = mTable.iter(); !iter.done(); iter.next()) { iter.get().value()->DebugDump(depth); } } void UpdateWeakPointersAfterGC(JSTracer* trc); 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: Map mTable{XPC_JS_MAP_LENGTH}; }; /*************************/ class Native2WrappedNativeMap { using Map = mozilla::HashMap, mozilla::MallocAllocPolicy>; public: Native2WrappedNativeMap(); XPCWrappedNative* Find(nsISupports* obj) const { MOZ_ASSERT(obj, "bad param"); Map::Ptr ptr = mMap.lookup(obj); return ptr ? ptr->value() : nullptr; } XPCWrappedNative* Add(XPCWrappedNative* wrapper) { MOZ_ASSERT(wrapper, "bad param"); nsISupports* obj = wrapper->GetIdentityObject(); Map::AddPtr ptr = mMap.lookupForAdd(obj); MOZ_ASSERT(!ptr, "wrapper already in new scope!"); if (ptr) { return ptr->value(); } if (!mMap.add(ptr, obj, wrapper)) { return nullptr; } return wrapper; } void Clear() { mMap.clear(); } uint32_t Count() { return mMap.count(); } Map::Iterator Iter() { return mMap.iter(); } Map::ModIterator ModIter() { return mMap.modIter(); } size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; private: Map mMap; }; /*************************/ struct IIDHasher { using Key = const nsIID*; using Lookup = Key; // Note this is returning the hash of the bit pattern of the first part of the // nsID, not the hash of the pointer to the nsID. static mozilla::HashNumber hash(Lookup lookup) { uintptr_t v; memcpy(&v, lookup, sizeof(v)); return mozilla::HashGeneric(v); } static bool match(Key key, Lookup lookup) { return key->Equals(*lookup); } }; class IID2NativeInterfaceMap { using Map = mozilla::HashMap; public: IID2NativeInterfaceMap(); XPCNativeInterface* Find(REFNSIID iid) const { Map::Ptr ptr = mMap.lookup(&iid); return ptr ? ptr->value() : nullptr; } bool AddNew(XPCNativeInterface* iface) { MOZ_ASSERT(iface, "bad param"); const nsIID* iid = iface->GetIID(); return mMap.putNew(iid, iface); } void Remove(XPCNativeInterface* iface) { MOZ_ASSERT(iface, "bad param"); mMap.remove(iface->GetIID()); } uint32_t Count() { return mMap.count(); } size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; void Trace(JSTracer* trc); private: Map mMap; }; /*************************/ class ClassInfo2NativeSetMap { using Map = mozilla::HashMap, mozilla::DefaultHasher, mozilla::MallocAllocPolicy>; public: ClassInfo2NativeSetMap(); XPCNativeSet* Find(nsIClassInfo* info) const { auto ptr = mMap.lookup(info); return ptr ? ptr->value().get() : nullptr; } XPCNativeSet* Add(nsIClassInfo* info, XPCNativeSet* set) { MOZ_ASSERT(info, "bad param"); auto ptr = mMap.lookupForAdd(info); if (ptr) { return ptr->value(); } if (!mMap.add(ptr, info, set)) { return nullptr; } return set; } void Remove(nsIClassInfo* info) { MOZ_ASSERT(info, "bad param"); mMap.remove(info); } uint32_t Count() { return mMap.count(); } // 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: Map mMap; }; /*************************/ class ClassInfo2WrappedNativeProtoMap { using Map = mozilla::HashMap, mozilla::MallocAllocPolicy>; public: ClassInfo2WrappedNativeProtoMap(); XPCWrappedNativeProto* Find(nsIClassInfo* info) const { auto ptr = mMap.lookup(info); return ptr ? ptr->value() : nullptr; } XPCWrappedNativeProto* Add(nsIClassInfo* info, XPCWrappedNativeProto* proto) { MOZ_ASSERT(info, "bad param"); auto ptr = mMap.lookupForAdd(info); if (ptr) { return ptr->value(); } if (!mMap.add(ptr, info, proto)) { return nullptr; } return proto; } void Clear() { mMap.clear(); } uint32_t Count() { return mMap.count(); } Map::Iterator Iter() { return mMap.iter(); } Map::ModIterator ModIter() { return mMap.modIter(); } size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; private: Map mMap; }; /*************************/ struct NativeSetHasher { using Key = XPCNativeSet*; using Lookup = const XPCNativeSetKey*; static mozilla::HashNumber hash(Lookup lookup) { return lookup->Hash(); } static bool match(Key key, Lookup lookup); }; class NativeSetMap { using Set = mozilla::HashSet; public: NativeSetMap(); XPCNativeSet* Find(const XPCNativeSetKey* key) const { auto ptr = mSet.lookup(key); return ptr ? *ptr : nullptr; } XPCNativeSet* Add(const XPCNativeSetKey* key, XPCNativeSet* set) { MOZ_ASSERT(key, "bad param"); MOZ_ASSERT(set, "bad param"); auto ptr = mSet.lookupForAdd(key); if (ptr) { return *ptr; } if (!mSet.add(ptr, set)) { return nullptr; } 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; } void Remove(XPCNativeSet* set) { MOZ_ASSERT(set, "bad param"); XPCNativeSetKey key(set); mSet.remove(&key); } uint32_t Count() { return mSet.count(); } Set::Iterator Iter() { return mSet.iter(); } size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; private: Set mSet; }; /***************************************************************************/ class JSObject2JSObjectMap { using Map = JS::GCHashMap, JS::Heap, js::StableCellHasher>, js::SystemAllocPolicy>; public: JSObject2JSObjectMap() = default; inline JSObject* Find(JSObject* key) { MOZ_ASSERT(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) { MOZ_ASSERT(key, "bad param"); Map::AddPtr p = mTable.lookupForAdd(key); if (p) { JSObject* oldValue = p->value(); p->value() = value; return oldValue; } if (!mTable.add(p, key, value)) { return nullptr; } MOZ_ASSERT(xpc::ObjectScope(key)->mWaiverWrapperMap == this); return value; } inline void Remove(JSObject* key) { MOZ_ASSERT(key, "bad param"); mTable.remove(key); } inline uint32_t Count() { return mTable.count(); } void UpdateWeakPointers(JSTracer* trc) { mTable.traceWeak(trc); } private: Map mTable{XPC_WRAPPER_MAP_LENGTH}; }; #endif /* xpcmaps_h___ */