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 /* Private maps (hashtables). */
8 
9 #include "mozilla/MathAlgorithms.h"
10 #include "mozilla/MemoryReporting.h"
11 #include "xpcprivate.h"
12 
13 #include "js/HashTable.h"
14 
15 using namespace mozilla;
16 
17 /***************************************************************************/
18 // static shared...
19 
20 // Note this is returning the hash of the bit pattern of the first part of the
21 // nsID, not the hash of the pointer to the nsID.
22 
HashIIDPtrKey(const void * key)23 static PLDHashNumber HashIIDPtrKey(const void* key) {
24   return HashGeneric(*((uintptr_t*)key));
25 }
26 
MatchIIDPtrKey(const PLDHashEntryHdr * entry,const void * key)27 static bool MatchIIDPtrKey(const PLDHashEntryHdr* entry, const void* key) {
28   return ((const nsID*)key)
29       ->Equals(*((const nsID*)((PLDHashEntryStub*)entry)->key));
30 }
31 
HashNativeKey(const void * data)32 static PLDHashNumber HashNativeKey(const void* data) {
33   return static_cast<const XPCNativeSetKey*>(data)->Hash();
34 }
35 
36 /***************************************************************************/
37 // implement JSObject2WrappedJSMap...
38 
UpdateWeakPointersAfterGC()39 void JSObject2WrappedJSMap::UpdateWeakPointersAfterGC() {
40   // Check all wrappers and update their JSObject pointer if it has been
41   // moved. Release any wrappers whose weakly held JSObject has died.
42 
43   nsTArray<RefPtr<nsXPCWrappedJS>> dying;
44   for (Map::Enum e(mTable); !e.empty(); e.popFront()) {
45     nsXPCWrappedJS* wrapper = e.front().value();
46     MOZ_ASSERT(wrapper, "found a null JS wrapper!");
47 
48     // Walk the wrapper chain and update all JSObjects.
49     while (wrapper) {
50       if (wrapper->IsSubjectToFinalization()) {
51         wrapper->UpdateObjectPointerAfterGC();
52         if (!wrapper->GetJSObjectPreserveColor())
53           dying.AppendElement(dont_AddRef(wrapper));
54       }
55       wrapper = wrapper->GetNextWrapper();
56     }
57 
58     // Remove or update the JSObject key in the table if necessary.
59     JSObject* obj = e.front().key().unbarrieredGet();
60     JS_UpdateWeakPointerAfterGCUnbarriered(&obj);
61     if (!obj)
62       e.removeFront();
63     else
64       e.front().mutableKey() = obj;
65   }
66 }
67 
ShutdownMarker()68 void JSObject2WrappedJSMap::ShutdownMarker() {
69   for (Map::Range r = mTable.all(); !r.empty(); r.popFront()) {
70     nsXPCWrappedJS* wrapper = r.front().value();
71     MOZ_ASSERT(wrapper, "found a null JS wrapper!");
72     MOZ_ASSERT(wrapper->IsValid(), "found an invalid JS wrapper!");
73     wrapper->SystemIsBeingShutDown();
74   }
75 }
76 
SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const77 size_t JSObject2WrappedJSMap::SizeOfIncludingThis(
78     mozilla::MallocSizeOf mallocSizeOf) const {
79   size_t n = mallocSizeOf(this);
80   n += mTable.sizeOfExcludingThis(mallocSizeOf);
81   return n;
82 }
83 
SizeOfWrappedJS(mozilla::MallocSizeOf mallocSizeOf) const84 size_t JSObject2WrappedJSMap::SizeOfWrappedJS(
85     mozilla::MallocSizeOf mallocSizeOf) const {
86   size_t n = 0;
87   for (Map::Range r = mTable.all(); !r.empty(); r.popFront())
88     n += r.front().value()->SizeOfIncludingThis(mallocSizeOf);
89   return n;
90 }
91 
92 /***************************************************************************/
93 // implement Native2WrappedNativeMap...
94 
95 // static
newMap(int length)96 Native2WrappedNativeMap* Native2WrappedNativeMap::newMap(int length) {
97   return new Native2WrappedNativeMap(length);
98 }
99 
Native2WrappedNativeMap(int length)100 Native2WrappedNativeMap::Native2WrappedNativeMap(int length)
101     : mTable(PLDHashTable::StubOps(), sizeof(Entry), length) {}
102 
SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const103 size_t Native2WrappedNativeMap::SizeOfIncludingThis(
104     mozilla::MallocSizeOf mallocSizeOf) const {
105   size_t n = mallocSizeOf(this);
106   n += mTable.ShallowSizeOfExcludingThis(mallocSizeOf);
107   for (auto iter = mTable.ConstIter(); !iter.Done(); iter.Next()) {
108     auto entry = static_cast<Native2WrappedNativeMap::Entry*>(iter.Get());
109     n += mallocSizeOf(entry->value);
110   }
111   return n;
112 }
113 
114 /***************************************************************************/
115 // implement IID2WrappedJSClassMap...
116 
117 const struct PLDHashTableOps IID2WrappedJSClassMap::Entry::sOps = {
118     HashIIDPtrKey, MatchIIDPtrKey, PLDHashTable::MoveEntryStub,
119     PLDHashTable::ClearEntryStub};
120 
121 // static
newMap(int length)122 IID2WrappedJSClassMap* IID2WrappedJSClassMap::newMap(int length) {
123   return new IID2WrappedJSClassMap(length);
124 }
125 
IID2WrappedJSClassMap(int length)126 IID2WrappedJSClassMap::IID2WrappedJSClassMap(int length)
127     : mTable(&Entry::sOps, sizeof(Entry), length) {}
128 
129 /***************************************************************************/
130 // implement IID2NativeInterfaceMap...
131 
132 const struct PLDHashTableOps IID2NativeInterfaceMap::Entry::sOps = {
133     HashIIDPtrKey, MatchIIDPtrKey, PLDHashTable::MoveEntryStub,
134     PLDHashTable::ClearEntryStub};
135 
136 // static
newMap(int length)137 IID2NativeInterfaceMap* IID2NativeInterfaceMap::newMap(int length) {
138   return new IID2NativeInterfaceMap(length);
139 }
140 
IID2NativeInterfaceMap(int length)141 IID2NativeInterfaceMap::IID2NativeInterfaceMap(int length)
142     : mTable(&Entry::sOps, sizeof(Entry), length) {}
143 
SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const144 size_t IID2NativeInterfaceMap::SizeOfIncludingThis(
145     mozilla::MallocSizeOf mallocSizeOf) const {
146   size_t n = mallocSizeOf(this);
147   n += mTable.ShallowSizeOfExcludingThis(mallocSizeOf);
148   for (auto iter = mTable.ConstIter(); !iter.Done(); iter.Next()) {
149     auto entry = static_cast<IID2NativeInterfaceMap::Entry*>(iter.Get());
150     n += entry->value->SizeOfIncludingThis(mallocSizeOf);
151   }
152   return n;
153 }
154 
155 /***************************************************************************/
156 // implement ClassInfo2NativeSetMap...
157 
158 // static
Match(const PLDHashEntryHdr * aEntry,const void * aKey)159 bool ClassInfo2NativeSetMap::Entry::Match(const PLDHashEntryHdr* aEntry,
160                                           const void* aKey) {
161   return static_cast<const Entry*>(aEntry)->key == aKey;
162 }
163 
164 // static
Clear(PLDHashTable * aTable,PLDHashEntryHdr * aEntry)165 void ClassInfo2NativeSetMap::Entry::Clear(PLDHashTable* aTable,
166                                           PLDHashEntryHdr* aEntry) {
167   auto entry = static_cast<Entry*>(aEntry);
168   NS_RELEASE(entry->value);
169 
170   entry->key = nullptr;
171   entry->value = nullptr;
172 }
173 
174 const PLDHashTableOps ClassInfo2NativeSetMap::Entry::sOps = {
175     PLDHashTable::HashVoidPtrKeyStub, Match, PLDHashTable::MoveEntryStub, Clear,
176     nullptr};
177 
178 // static
newMap(int length)179 ClassInfo2NativeSetMap* ClassInfo2NativeSetMap::newMap(int length) {
180   return new ClassInfo2NativeSetMap(length);
181 }
182 
ClassInfo2NativeSetMap(int length)183 ClassInfo2NativeSetMap::ClassInfo2NativeSetMap(int length)
184     : mTable(&ClassInfo2NativeSetMap::Entry::sOps, sizeof(Entry), length) {}
185 
ShallowSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)186 size_t ClassInfo2NativeSetMap::ShallowSizeOfIncludingThis(
187     mozilla::MallocSizeOf mallocSizeOf) {
188   size_t n = mallocSizeOf(this);
189   n += mTable.ShallowSizeOfExcludingThis(mallocSizeOf);
190   return n;
191 }
192 
193 /***************************************************************************/
194 // implement ClassInfo2WrappedNativeProtoMap...
195 
196 // static
newMap(int length)197 ClassInfo2WrappedNativeProtoMap* ClassInfo2WrappedNativeProtoMap::newMap(
198     int length) {
199   return new ClassInfo2WrappedNativeProtoMap(length);
200 }
201 
ClassInfo2WrappedNativeProtoMap(int length)202 ClassInfo2WrappedNativeProtoMap::ClassInfo2WrappedNativeProtoMap(int length)
203     : mTable(PLDHashTable::StubOps(), sizeof(Entry), length) {}
204 
SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const205 size_t ClassInfo2WrappedNativeProtoMap::SizeOfIncludingThis(
206     mozilla::MallocSizeOf mallocSizeOf) const {
207   size_t n = mallocSizeOf(this);
208   n += mTable.ShallowSizeOfExcludingThis(mallocSizeOf);
209   for (auto iter = mTable.ConstIter(); !iter.Done(); iter.Next()) {
210     auto entry =
211         static_cast<ClassInfo2WrappedNativeProtoMap::Entry*>(iter.Get());
212     n += mallocSizeOf(entry->value);
213   }
214   return n;
215 }
216 
217 /***************************************************************************/
218 // implement NativeSetMap...
219 
Match(const PLDHashEntryHdr * entry,const void * key)220 bool NativeSetMap::Entry::Match(const PLDHashEntryHdr* entry, const void* key) {
221   auto Key = static_cast<const XPCNativeSetKey*>(key);
222   XPCNativeSet* SetInTable = ((Entry*)entry)->key_value;
223   XPCNativeSet* Set = Key->GetBaseSet();
224   XPCNativeInterface* Addition = Key->GetAddition();
225 
226   if (!Set) {
227     // This is a special case to deal with the invariant that says:
228     // "All sets have exactly one nsISupports interface and it comes first."
229     // See XPCNativeSet::NewInstance for details.
230     //
231     // Though we might have a key that represents only one interface, we
232     // know that if that one interface were contructed into a set then
233     // it would end up really being a set with two interfaces (except for
234     // the case where the one interface happened to be nsISupports).
235 
236     return (SetInTable->GetInterfaceCount() == 1 &&
237             SetInTable->GetInterfaceAt(0) == Addition) ||
238            (SetInTable->GetInterfaceCount() == 2 &&
239             SetInTable->GetInterfaceAt(1) == Addition);
240   }
241 
242   if (!Addition && Set == SetInTable) return true;
243 
244   uint16_t count = Set->GetInterfaceCount();
245   if (count + (Addition ? 1 : 0) != SetInTable->GetInterfaceCount())
246     return false;
247 
248   XPCNativeInterface** CurrentInTable = SetInTable->GetInterfaceArray();
249   XPCNativeInterface** Current = Set->GetInterfaceArray();
250   for (uint16_t i = 0; i < count; i++) {
251     if (*(Current++) != *(CurrentInTable++)) return false;
252   }
253   return !Addition || Addition == *(CurrentInTable++);
254 }
255 
256 const struct PLDHashTableOps NativeSetMap::Entry::sOps = {
257     HashNativeKey, Match, PLDHashTable::MoveEntryStub,
258     PLDHashTable::ClearEntryStub};
259 
260 // static
newMap(int length)261 NativeSetMap* NativeSetMap::newMap(int length) {
262   return new NativeSetMap(length);
263 }
264 
NativeSetMap(int length)265 NativeSetMap::NativeSetMap(int length)
266     : mTable(&Entry::sOps, sizeof(Entry), length) {}
267 
SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const268 size_t NativeSetMap::SizeOfIncludingThis(
269     mozilla::MallocSizeOf mallocSizeOf) const {
270   size_t n = mallocSizeOf(this);
271   n += mTable.ShallowSizeOfExcludingThis(mallocSizeOf);
272   for (auto iter = mTable.ConstIter(); !iter.Done(); iter.Next()) {
273     auto entry = static_cast<NativeSetMap::Entry*>(iter.Get());
274     n += entry->key_value->SizeOfIncludingThis(mallocSizeOf);
275   }
276   return n;
277 }
278 
279 /***************************************************************************/
280 // implement IID2ThisTranslatorMap...
281 
Match(const PLDHashEntryHdr * entry,const void * key)282 bool IID2ThisTranslatorMap::Entry::Match(const PLDHashEntryHdr* entry,
283                                          const void* key) {
284   return ((const nsID*)key)->Equals(((Entry*)entry)->key);
285 }
286 
Clear(PLDHashTable * table,PLDHashEntryHdr * entry)287 void IID2ThisTranslatorMap::Entry::Clear(PLDHashTable* table,
288                                          PLDHashEntryHdr* entry) {
289   static_cast<Entry*>(entry)->value = nullptr;
290   memset(entry, 0, table->EntrySize());
291 }
292 
293 const struct PLDHashTableOps IID2ThisTranslatorMap::Entry::sOps = {
294     HashIIDPtrKey, Match, PLDHashTable::MoveEntryStub, Clear};
295 
296 // static
newMap(int length)297 IID2ThisTranslatorMap* IID2ThisTranslatorMap::newMap(int length) {
298   return new IID2ThisTranslatorMap(length);
299 }
300 
IID2ThisTranslatorMap(int length)301 IID2ThisTranslatorMap::IID2ThisTranslatorMap(int length)
302     : mTable(&Entry::sOps, sizeof(Entry), length) {}
303 
304 /***************************************************************************/
305 // implement XPCWrappedNativeProtoMap...
306 
307 // static
newMap(int length)308 XPCWrappedNativeProtoMap* XPCWrappedNativeProtoMap::newMap(int length) {
309   return new XPCWrappedNativeProtoMap(length);
310 }
311 
XPCWrappedNativeProtoMap(int length)312 XPCWrappedNativeProtoMap::XPCWrappedNativeProtoMap(int length)
313     : mTable(PLDHashTable::StubOps(), sizeof(PLDHashEntryStub), length) {}
314 
315 /***************************************************************************/
316