1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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 uintptr_t v;
25 memcpy(&v, key, sizeof(v));
26 return HashGeneric(v);
27 }
28
MatchIIDPtrKey(const PLDHashEntryHdr * entry,const void * key)29 static bool MatchIIDPtrKey(const PLDHashEntryHdr* entry, const void* key) {
30 return ((const nsID*)key)
31 ->Equals(*((const nsID*)((PLDHashEntryStub*)entry)->key));
32 }
33
HashNativeKey(const void * data)34 static PLDHashNumber HashNativeKey(const void* data) {
35 return static_cast<const XPCNativeSetKey*>(data)->Hash();
36 }
37
38 /***************************************************************************/
39 // implement JSObject2WrappedJSMap...
40
UpdateWeakPointersAfterGC()41 void JSObject2WrappedJSMap::UpdateWeakPointersAfterGC() {
42 // Check all wrappers and update their JSObject pointer if it has been
43 // moved. Release any wrappers whose weakly held JSObject has died.
44
45 nsTArray<RefPtr<nsXPCWrappedJS>> dying;
46 for (auto iter = mTable.modIter(); !iter.done(); iter.next()) {
47 nsXPCWrappedJS* wrapper = iter.get().value();
48 MOZ_ASSERT(wrapper, "found a null JS wrapper!");
49
50 // Walk the wrapper chain and update all JSObjects.
51 while (wrapper) {
52 if (wrapper->IsSubjectToFinalization()) {
53 wrapper->UpdateObjectPointerAfterGC();
54 if (!wrapper->GetJSObjectPreserveColor()) {
55 dying.AppendElement(dont_AddRef(wrapper));
56 }
57 }
58 wrapper = wrapper->GetNextWrapper();
59 }
60
61 // Remove or update the JSObject key in the table if necessary.
62 JSObject* obj = iter.get().key().unbarrieredGet();
63 JS_UpdateWeakPointerAfterGCUnbarriered(&obj);
64 if (!obj) {
65 iter.remove();
66 } else {
67 iter.get().mutableKey() = obj;
68 }
69 }
70 }
71
ShutdownMarker()72 void JSObject2WrappedJSMap::ShutdownMarker() {
73 for (auto iter = mTable.iter(); !iter.done(); iter.next()) {
74 nsXPCWrappedJS* wrapper = iter.get().value();
75 MOZ_ASSERT(wrapper, "found a null JS wrapper!");
76 MOZ_ASSERT(wrapper->IsValid(), "found an invalid JS wrapper!");
77 wrapper->SystemIsBeingShutDown();
78 }
79 }
80
SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const81 size_t JSObject2WrappedJSMap::SizeOfIncludingThis(
82 mozilla::MallocSizeOf mallocSizeOf) const {
83 size_t n = mallocSizeOf(this);
84 n += mTable.shallowSizeOfExcludingThis(mallocSizeOf);
85 return n;
86 }
87
SizeOfWrappedJS(mozilla::MallocSizeOf mallocSizeOf) const88 size_t JSObject2WrappedJSMap::SizeOfWrappedJS(
89 mozilla::MallocSizeOf mallocSizeOf) const {
90 size_t n = 0;
91 for (auto iter = mTable.iter(); !iter.done(); iter.next()) {
92 n += iter.get().value()->SizeOfIncludingThis(mallocSizeOf);
93 }
94 return n;
95 }
96
97 /***************************************************************************/
98 // implement Native2WrappedNativeMap...
99
Native2WrappedNativeMap()100 Native2WrappedNativeMap::Native2WrappedNativeMap()
101 : mTable(PLDHashTable::StubOps(), sizeof(Entry), XPC_NATIVE_MAP_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 IID2NativeInterfaceMap...
116
117 const struct PLDHashTableOps IID2NativeInterfaceMap::Entry::sOps = {
118 HashIIDPtrKey, MatchIIDPtrKey, PLDHashTable::MoveEntryStub,
119 PLDHashTable::ClearEntryStub};
120
IID2NativeInterfaceMap()121 IID2NativeInterfaceMap::IID2NativeInterfaceMap()
122 : mTable(&Entry::sOps, sizeof(Entry), XPC_NATIVE_INTERFACE_MAP_LENGTH) {}
123
SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const124 size_t IID2NativeInterfaceMap::SizeOfIncludingThis(
125 mozilla::MallocSizeOf mallocSizeOf) const {
126 size_t n = mallocSizeOf(this);
127 n += mTable.ShallowSizeOfExcludingThis(mallocSizeOf);
128 for (auto iter = mTable.ConstIter(); !iter.Done(); iter.Next()) {
129 auto entry = static_cast<IID2NativeInterfaceMap::Entry*>(iter.Get());
130 n += entry->value->SizeOfIncludingThis(mallocSizeOf);
131 }
132 return n;
133 }
134
135 /***************************************************************************/
136 // implement ClassInfo2NativeSetMap...
137
138 // static
Match(const PLDHashEntryHdr * aEntry,const void * aKey)139 bool ClassInfo2NativeSetMap::Entry::Match(const PLDHashEntryHdr* aEntry,
140 const void* aKey) {
141 return static_cast<const Entry*>(aEntry)->key == aKey;
142 }
143
144 // static
Clear(PLDHashTable * aTable,PLDHashEntryHdr * aEntry)145 void ClassInfo2NativeSetMap::Entry::Clear(PLDHashTable* aTable,
146 PLDHashEntryHdr* aEntry) {
147 auto entry = static_cast<Entry*>(aEntry);
148 NS_RELEASE(entry->value);
149
150 entry->key = nullptr;
151 entry->value = nullptr;
152 }
153
154 const PLDHashTableOps ClassInfo2NativeSetMap::Entry::sOps = {
155 PLDHashTable::HashVoidPtrKeyStub, Match, PLDHashTable::MoveEntryStub, Clear,
156 nullptr};
157
ClassInfo2NativeSetMap()158 ClassInfo2NativeSetMap::ClassInfo2NativeSetMap()
159 : mTable(&ClassInfo2NativeSetMap::Entry::sOps, sizeof(Entry),
160 XPC_NATIVE_SET_MAP_LENGTH) {}
161
ShallowSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)162 size_t ClassInfo2NativeSetMap::ShallowSizeOfIncludingThis(
163 mozilla::MallocSizeOf mallocSizeOf) {
164 size_t n = mallocSizeOf(this);
165 n += mTable.ShallowSizeOfExcludingThis(mallocSizeOf);
166 return n;
167 }
168
169 /***************************************************************************/
170 // implement ClassInfo2WrappedNativeProtoMap...
171
ClassInfo2WrappedNativeProtoMap()172 ClassInfo2WrappedNativeProtoMap::ClassInfo2WrappedNativeProtoMap()
173 : mTable(PLDHashTable::StubOps(), sizeof(Entry),
174 XPC_NATIVE_PROTO_MAP_LENGTH) {}
175
SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const176 size_t ClassInfo2WrappedNativeProtoMap::SizeOfIncludingThis(
177 mozilla::MallocSizeOf mallocSizeOf) const {
178 size_t n = mallocSizeOf(this);
179 n += mTable.ShallowSizeOfExcludingThis(mallocSizeOf);
180 for (auto iter = mTable.ConstIter(); !iter.Done(); iter.Next()) {
181 auto entry =
182 static_cast<ClassInfo2WrappedNativeProtoMap::Entry*>(iter.Get());
183 n += mallocSizeOf(entry->value);
184 }
185 return n;
186 }
187
188 /***************************************************************************/
189 // implement NativeSetMap...
190
Match(const PLDHashEntryHdr * entry,const void * key)191 bool NativeSetMap::Entry::Match(const PLDHashEntryHdr* entry, const void* key) {
192 auto Key = static_cast<const XPCNativeSetKey*>(key);
193 XPCNativeSet* SetInTable = ((Entry*)entry)->key_value;
194 XPCNativeSet* Set = Key->GetBaseSet();
195 XPCNativeInterface* Addition = Key->GetAddition();
196
197 if (!Set) {
198 // This is a special case to deal with the invariant that says:
199 // "All sets have exactly one nsISupports interface and it comes first."
200 // See XPCNativeSet::NewInstance for details.
201 //
202 // Though we might have a key that represents only one interface, we
203 // know that if that one interface were contructed into a set then
204 // it would end up really being a set with two interfaces (except for
205 // the case where the one interface happened to be nsISupports).
206
207 return (SetInTable->GetInterfaceCount() == 1 &&
208 SetInTable->GetInterfaceAt(0) == Addition) ||
209 (SetInTable->GetInterfaceCount() == 2 &&
210 SetInTable->GetInterfaceAt(1) == Addition);
211 }
212
213 if (!Addition && Set == SetInTable) {
214 return true;
215 }
216
217 uint16_t count = Set->GetInterfaceCount();
218 if (count + (Addition ? 1 : 0) != SetInTable->GetInterfaceCount()) {
219 return false;
220 }
221
222 XPCNativeInterface** CurrentInTable = SetInTable->GetInterfaceArray();
223 XPCNativeInterface** Current = Set->GetInterfaceArray();
224 for (uint16_t i = 0; i < count; i++) {
225 if (*(Current++) != *(CurrentInTable++)) {
226 return false;
227 }
228 }
229 return !Addition || Addition == *(CurrentInTable++);
230 }
231
232 const struct PLDHashTableOps NativeSetMap::Entry::sOps = {
233 HashNativeKey, Match, PLDHashTable::MoveEntryStub,
234 PLDHashTable::ClearEntryStub};
235
NativeSetMap()236 NativeSetMap::NativeSetMap()
237 : mTable(&Entry::sOps, sizeof(Entry), XPC_NATIVE_SET_MAP_LENGTH) {}
238
SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const239 size_t NativeSetMap::SizeOfIncludingThis(
240 mozilla::MallocSizeOf mallocSizeOf) const {
241 size_t n = mallocSizeOf(this);
242 n += mTable.ShallowSizeOfExcludingThis(mallocSizeOf);
243 for (auto iter = mTable.ConstIter(); !iter.Done(); iter.Next()) {
244 auto entry = static_cast<NativeSetMap::Entry*>(iter.Get());
245 n += entry->key_value->SizeOfIncludingThis(mallocSizeOf);
246 }
247 return n;
248 }
249
250 /***************************************************************************/
251 // implement XPCWrappedNativeProtoMap...
252
XPCWrappedNativeProtoMap()253 XPCWrappedNativeProtoMap::XPCWrappedNativeProtoMap()
254 : mTable(PLDHashTable::StubOps(), sizeof(PLDHashEntryStub),
255 XPC_DYING_NATIVE_PROTO_MAP_LENGTH) {}
256
257 /***************************************************************************/
258