Marking-inl.h (8048B)
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 #ifndef gc_Marking_inl_h 8 #define gc_Marking_inl_h 9 10 #include "gc/Marking.h" 11 12 #include <type_traits> 13 14 #include "gc/RelocationOverlay.h" 15 #include "gc/Zone.h" 16 #include "js/Id.h" 17 #include "js/Value.h" 18 #include "vm/StringType.h" 19 #include "vm/TaggedProto.h" 20 #include "wasm/WasmAnyRef.h" 21 22 #include "gc/Nursery-inl.h" 23 24 namespace js { 25 namespace gc { 26 27 // An abstraction to re-wrap any kind of typed pointer back to the tagged 28 // pointer it came from with |TaggedPtr<TargetType>::wrap(sourcePtr)|. 29 template <typename T> 30 struct TaggedPtr {}; 31 32 template <> 33 struct TaggedPtr<JS::Value> { 34 static JS::Value wrap(JSObject* obj) { 35 if (!obj) { 36 return JS::NullValue(); 37 } 38 return JS::ObjectValue(*obj); 39 } 40 static JS::Value wrap(JSString* str) { return JS::StringValue(str); } 41 static JS::Value wrap(JS::Symbol* sym) { return JS::SymbolValue(sym); } 42 static JS::Value wrap(JS::BigInt* bi) { return JS::BigIntValue(bi); } 43 template <typename T> 44 static JS::Value wrap(T* priv) { 45 static_assert(std::is_base_of_v<Cell, T>, 46 "Type must be a GC thing derived from js::gc::Cell"); 47 return JS::PrivateGCThingValue(priv); 48 } 49 static JS::Value empty() { return JS::UndefinedValue(); } 50 }; 51 52 template <> 53 struct TaggedPtr<jsid> { 54 static jsid wrap(JSString* str) { return JS::PropertyKey::NonIntAtom(str); } 55 static jsid wrap(JS::Symbol* sym) { return PropertyKey::Symbol(sym); } 56 static jsid empty() { return JS::PropertyKey::Void(); } 57 }; 58 59 template <> 60 struct TaggedPtr<TaggedProto> { 61 static TaggedProto wrap(JSObject* obj) { return TaggedProto(obj); } 62 static TaggedProto empty() { return TaggedProto(); } 63 }; 64 65 template <> 66 struct TaggedPtr<wasm::AnyRef> { 67 static wasm::AnyRef wrap(JSObject* obj) { 68 return wasm::AnyRef::fromJSObjectOrNull(obj); 69 } 70 static wasm::AnyRef wrap(JSString* str) { 71 return wasm::AnyRef::fromJSString(str); 72 } 73 static wasm::AnyRef empty() { return wasm::AnyRef(); } 74 }; 75 76 template <typename T> 77 struct MightBeForwarded { 78 static_assert(std::is_base_of_v<Cell, T>); 79 static_assert(!std::is_same_v<Cell, T> && !std::is_same_v<TenuredCell, T>); 80 81 #define CAN_FORWARD_KIND_OR(_1, _2, Type, _3, _4, _5, canCompact) \ 82 std::is_base_of_v<Type, T> ? canCompact: 83 84 // FOR_EACH_ALLOCKIND doesn't cover every possible type: make sure 85 // to default to `true` for unknown types. 86 static constexpr bool value = FOR_EACH_ALLOCKIND(CAN_FORWARD_KIND_OR) true; 87 #undef CAN_FORWARD_KIND_OR 88 }; 89 90 template <typename T> 91 inline bool IsForwarded(const T* t) { 92 if constexpr (!MightBeForwarded<T>::value) { 93 MOZ_ASSERT(!t->isForwarded()); 94 return false; 95 } 96 97 return t->isForwarded(); 98 } 99 100 template <> 101 inline bool IsForwarded<Cell>(const Cell* t) { 102 return t->isForwarded(); 103 } 104 105 inline bool IsForwarded(const JS::Value& value) { 106 return value.isGCThing() && IsForwarded(value.toGCThing()); 107 } 108 109 template <typename T> 110 inline T* Forwarded(const T* t) { 111 const RelocationOverlay* overlay = RelocationOverlay::fromCell(t); 112 MOZ_ASSERT(overlay->isForwarded()); 113 return reinterpret_cast<T*>(overlay->forwardingAddress()); 114 } 115 116 inline JS::Value Forwarded(const JS::Value& value) { 117 MOZ_ASSERT(IsForwarded(value)); 118 JS::Value result = value; 119 result.changeGCThingPayload(Forwarded(value.toGCThing())); 120 return result; 121 } 122 123 template <typename T> 124 inline T MaybeForwarded(const T& t) { 125 if (!IsForwarded(t)) { 126 return t; 127 } 128 T result = Forwarded(t); 129 MOZ_ASSERT(!IsForwarded(result)); 130 return result; 131 } 132 133 inline const JSClass* MaybeForwardedObjectClass(const JSObject* obj) { 134 Shape* shape = MaybeForwarded(obj->shapeMaybeForwarded()); 135 BaseShape* baseShape = MaybeForwarded(shape->base()); 136 return baseShape->clasp(); 137 } 138 139 template <typename T> 140 inline bool MaybeForwardedObjectIs(const JSObject* obj) { 141 MOZ_ASSERT(!obj->isForwarded()); 142 return MaybeForwardedObjectClass(obj) == &T::class_; 143 } 144 145 template <typename T> 146 inline T& MaybeForwardedObjectAs(JSObject* obj) { 147 MOZ_ASSERT(MaybeForwardedObjectIs<T>(obj)); 148 return *static_cast<T*>(obj); 149 } 150 151 inline RelocationOverlay::RelocationOverlay(Cell* dst) { 152 MOZ_ASSERT(dst->flags() == 0); 153 uintptr_t ptr = uintptr_t(dst); 154 header_.setForwardingAddress(ptr); 155 } 156 157 /* static */ 158 inline RelocationOverlay* RelocationOverlay::forwardCell(Cell* src, Cell* dst) { 159 MOZ_ASSERT(!src->isForwarded()); 160 MOZ_ASSERT(!dst->isForwarded()); 161 return new (src) RelocationOverlay(dst); 162 } 163 164 inline bool IsAboutToBeFinalizedDuringMinorSweep(Cell** cellp) { 165 MOZ_ASSERT(JS::RuntimeHeapIsMinorCollecting()); 166 167 if ((*cellp)->isTenured()) { 168 return false; 169 } 170 171 return !Nursery::getForwardedPointer(cellp); 172 } 173 174 // Special case pre-write barrier for strings used during rope flattening. This 175 // avoids eager marking of ropes which does not immediately mark the cells if we 176 // hit OOM. This does not traverse ropes and is instead called on every node in 177 // a rope during flattening. 178 inline void PreWriteBarrierDuringFlattening(JSString* str) { 179 MOZ_ASSERT(str); 180 MOZ_ASSERT(!JS::RuntimeHeapIsMajorCollecting()); 181 182 if (IsInsideNursery(str)) { 183 return; 184 } 185 186 auto* cell = reinterpret_cast<TenuredCell*>(str); 187 JS::shadow::Zone* zone = cell->shadowZoneFromAnyThread(); 188 if (!zone->needsIncrementalBarrier()) { 189 return; 190 } 191 192 MOZ_ASSERT(!str->isPermanentAndMayBeShared()); 193 MOZ_ASSERT(CurrentThreadCanAccessRuntime(zone->runtimeFromAnyThread())); 194 PerformIncrementalBarrierDuringFlattening(str); 195 } 196 197 #ifdef JSGC_HASH_TABLE_CHECKS 198 199 // Moving GC things whose pointers are used in hash table keys has the potential 200 // to break hash tables in subtle and terrifying ways. For example, a key might 201 // be reported as not present but iterating the table could still return it. 202 // 203 // Check that a table is correct following a moving GC, ensuring that nothing is 204 // present in the table that points into the nursery or that has not been moved, 205 // and that the hash table entries are discoverable. 206 // 207 // |checkEntryAndGetLookup| should check any GC thing pointers in the entry are 208 // valid and return the lookup required to get this entry from the table. 209 210 template <typename Table, typename Range, typename Lookup> 211 void CheckTableEntryAfterMovingGC(const Table& table, const Range& r, 212 const Lookup& lookup) { 213 auto ptr = table.lookup(lookup); 214 MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front()); 215 } 216 217 template <typename Table, typename F> 218 void CheckTableAfterMovingGC(const Table& table, F&& checkEntryAndGetLookup) { 219 for (auto r = table.all(); !r.empty(); r.popFront()) { 220 auto lookup = checkEntryAndGetLookup(r.front()); 221 CheckTableEntryAfterMovingGC(table, r, lookup); 222 } 223 } 224 225 template <typename T> 226 inline bool IsGCThingValidAfterMovingGC(T* t) { 227 if (!t->isTenured()) { 228 return false; 229 } 230 231 TenuredCell* cell = &t->asTenured(); 232 return cell->arena()->allocated() && !cell->isForwarded(); 233 } 234 235 template <typename T> 236 inline void CheckGCThingAfterMovingGC(T* t) { 237 if (t) { 238 MOZ_RELEASE_ASSERT(IsGCThingValidAfterMovingGC(t)); 239 } 240 } 241 242 template <typename T> 243 inline void CheckGCThingAfterMovingGC(T* t, JS::Zone* expectedZone) { 244 if (t) { 245 MOZ_RELEASE_ASSERT(IsGCThingValidAfterMovingGC(t)); 246 JS::Zone* zone = t->zoneFromAnyThread(); 247 MOZ_RELEASE_ASSERT(zone == expectedZone || zone->isAtomsZone()); 248 } 249 } 250 251 template <typename T> 252 inline void CheckGCThingAfterMovingGC(const WeakHeapPtr<T*>& t, 253 JS::Zone* expectedZone) { 254 CheckGCThingAfterMovingGC(t.unbarrieredGet(), expectedZone); 255 } 256 257 inline void CheckProtoAfterMovingGC(const TaggedProto& proto, JS::Zone* zone) { 258 if (proto.isObject()) { 259 CheckGCThingAfterMovingGC(proto.toObject(), zone); 260 } 261 } 262 263 #endif // JSGC_HASH_TABLE_CHECKS 264 265 } /* namespace gc */ 266 } /* namespace js */ 267 268 #endif // gc_Marking_inl_h