GCVector.h (12642B)
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 js_GCVector_h 8 #define js_GCVector_h 9 10 #include "mozilla/Assertions.h" // MOZ_ASSERT 11 #include "mozilla/Attributes.h" // MOZ_STACK_CLASS 12 #include "mozilla/MemoryReporting.h" // MallocSizeOf 13 #include "mozilla/Span.h" 14 #include "mozilla/Vector.h" 15 16 #include <stddef.h> // size_t 17 #include <utility> // forward, move 18 19 #include "js/AllocPolicy.h" 20 #include "js/GCPolicyAPI.h" 21 #include "js/RootingAPI.h" 22 23 class JSTracer; 24 struct JSContext; 25 26 namespace JS { 27 28 // A GCVector is a Vector with an additional trace method that knows how 29 // to visit all of the items stored in the Vector. For vectors that contain GC 30 // things, this is usually more convenient than manually iterating and marking 31 // the contents. 32 // 33 // Most types of GC pointers as keys and values can be traced with no extra 34 // infrastructure. For structs and non-gc-pointer members, ensure that there is 35 // a specialization of GCPolicy<T> with an appropriate trace method available 36 // to handle the custom type. Generic helpers can be found in 37 // js/public/TracingAPI.h. 38 // 39 // Note that although this Vector's trace will deal correctly with moved items, 40 // it does not itself know when to barrier or trace items. To function properly 41 // it must either be used with Rooted, or barriered and traced manually. 42 template <typename T, size_t MinInlineCapacity = 0, 43 typename AllocPolicy = js::TempAllocPolicy> 44 class GCVector { 45 protected: 46 mozilla::Vector<T, MinInlineCapacity, AllocPolicy> vector; 47 48 public: 49 using ElementType = T; 50 static constexpr size_t InlineLength = decltype(vector)::InlineLength; 51 52 explicit GCVector(AllocPolicy alloc) : vector(std::move(alloc)) {} 53 GCVector() : GCVector(AllocPolicy()) {} 54 55 GCVector(GCVector&& vec) : vector(std::move(vec.vector)) {} 56 57 GCVector& operator=(GCVector&& vec) { 58 vector = std::move(vec.vector); 59 return *this; 60 } 61 62 size_t length() const { return vector.length(); } 63 bool empty() const { return vector.empty(); } 64 size_t capacity() const { return vector.capacity(); } 65 66 T* begin() { return vector.begin(); } 67 const T* begin() const { return vector.begin(); } 68 69 T* end() { return vector.end(); } 70 const T* end() const { return vector.end(); } 71 72 T& operator[](size_t i) { return vector[i]; } 73 const T& operator[](size_t i) const { return vector[i]; } 74 75 T& back() { return vector.back(); } 76 const T& back() const { return vector.back(); } 77 78 operator mozilla::Span<T>() { return vector; } 79 operator mozilla::Span<const T>() const { return vector; } 80 81 bool initCapacity(size_t cap) { return vector.initCapacity(cap); } 82 [[nodiscard]] bool reserve(size_t req) { return vector.reserve(req); } 83 void shrinkBy(size_t amount) { return vector.shrinkBy(amount); } 84 void shrinkTo(size_t newLen) { return vector.shrinkTo(newLen); } 85 [[nodiscard]] bool growBy(size_t amount) { return vector.growBy(amount); } 86 [[nodiscard]] bool resize(size_t newLen) { return vector.resize(newLen); } 87 88 void clear() { vector.clear(); } 89 void clearAndFree() { vector.clearAndFree(); } 90 91 template <typename U> 92 bool append(U&& item) { 93 return vector.append(std::forward<U>(item)); 94 } 95 96 void erase(T* it) { vector.erase(it); } 97 void erase(T* begin, T* end) { vector.erase(begin, end); } 98 template <typename Pred> 99 void eraseIf(Pred pred) { 100 vector.eraseIf(pred); 101 } 102 template <typename U> 103 void eraseIfEqual(const U& u) { 104 vector.eraseIfEqual(u); 105 } 106 107 template <typename... Args> 108 [[nodiscard]] bool emplaceBack(Args&&... args) { 109 return vector.emplaceBack(std::forward<Args>(args)...); 110 } 111 112 template <typename... Args> 113 void infallibleEmplaceBack(Args&&... args) { 114 vector.infallibleEmplaceBack(std::forward<Args>(args)...); 115 } 116 117 template <typename U> 118 void infallibleAppend(U&& aU) { 119 return vector.infallibleAppend(std::forward<U>(aU)); 120 } 121 void infallibleAppendN(const T& aT, size_t aN) { 122 return vector.infallibleAppendN(aT, aN); 123 } 124 template <typename U> 125 void infallibleAppend(const U* aBegin, const U* aEnd) { 126 return vector.infallibleAppend(aBegin, aEnd); 127 } 128 template <typename U> 129 void infallibleAppend(const U* aBegin, size_t aLength) { 130 return vector.infallibleAppend(aBegin, aLength); 131 } 132 133 template <typename U> 134 [[nodiscard]] bool appendAll(const U& aU) { 135 return vector.append(aU.begin(), aU.end()); 136 } 137 template <typename T2, size_t MinInlineCapacity2, typename AllocPolicy2> 138 [[nodiscard]] bool appendAll( 139 GCVector<T2, MinInlineCapacity2, AllocPolicy2>&& aU) { 140 return vector.appendAll(std::move(aU.vector)); 141 } 142 143 [[nodiscard]] bool appendN(const T& val, size_t count) { 144 return vector.appendN(val, count); 145 } 146 147 template <typename U> 148 [[nodiscard]] bool append(const U* aBegin, const U* aEnd) { 149 return vector.append(aBegin, aEnd); 150 } 151 template <typename U> 152 [[nodiscard]] bool append(const U* aBegin, size_t aLength) { 153 return vector.append(aBegin, aLength); 154 } 155 156 void popBack() { return vector.popBack(); } 157 T popCopy() { return vector.popCopy(); } 158 159 void swap(GCVector& other) { 160 // Note, this only will work for MinInlineCapacity of zero. 161 // See Bug 1987683. 162 vector.swap(other.vector); 163 } 164 165 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 166 return vector.sizeOfExcludingThis(mallocSizeOf); 167 } 168 169 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 170 return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); 171 } 172 173 void trace(JSTracer* trc) { 174 for (auto& elem : vector) { 175 GCPolicy<T>::trace(trc, &elem, "vector element"); 176 } 177 } 178 179 bool traceWeak(JSTracer* trc) { 180 mutableEraseIf( 181 [trc](T& elem) { return !GCPolicy<T>::traceWeak(trc, &elem); }); 182 return !empty(); 183 } 184 185 // Like eraseIf, but may mutate the contents of the vector. Iterates from 186 // |startIndex| to the last element of the vector. 187 template <typename Pred> 188 void mutableEraseIf(Pred&& pred, size_t startIndex = 0) { 189 MOZ_ASSERT(startIndex <= length()); 190 191 T* src = begin() + startIndex; 192 T* dst = src; 193 while (src != end()) { 194 if (!pred(*src)) { 195 if (src != dst) { 196 *dst = std::move(*src); 197 } 198 dst++; 199 } 200 src++; 201 } 202 203 MOZ_ASSERT(dst <= end()); 204 shrinkBy(end() - dst); 205 } 206 }; 207 208 // AllocPolicy is optional. It has a default value declared in TypeDecls.h 209 template <typename T, typename AllocPolicy> 210 class MOZ_STACK_CLASS StackGCVector : public GCVector<T, 8, AllocPolicy> { 211 public: 212 using Base = GCVector<T, 8, AllocPolicy>; 213 214 void trace(JSTracer* trc) { 215 if constexpr (!GCPolicy<T>::mightBeInNursery()) { 216 // Skip tracing of non-nursery types in minor GC. 217 if (trc->isTenuringTracer()) { 218 #ifdef DEBUG 219 for (auto& elem : this->vector) { 220 MOZ_ASSERT(GCPolicy<T>::isTenured(elem)); 221 } 222 #endif 223 return; 224 } 225 } 226 227 Base::trace(trc); 228 } 229 230 private: 231 // Inherit constructor from GCVector. 232 using Base::Base; 233 }; 234 235 } // namespace JS 236 237 namespace js { 238 239 template <typename Wrapper, typename T, size_t Capacity, typename AllocPolicy> 240 class WrappedPtrOperations<JS::GCVector<T, Capacity, AllocPolicy>, Wrapper> { 241 using Vec = JS::GCVector<T, Capacity, AllocPolicy>; 242 const Vec& vec() const { return static_cast<const Wrapper*>(this)->get(); } 243 244 public: 245 const AllocPolicy& allocPolicy() const { return vec().allocPolicy(); } 246 size_t length() const { return vec().length(); } 247 bool empty() const { return vec().empty(); } 248 size_t capacity() const { return vec().capacity(); } 249 const T* begin() const { return vec().begin(); } 250 const T* end() const { return vec().end(); } 251 const T& back() const { return vec().back(); } 252 253 JS::Handle<T> operator[](size_t aIndex) const { 254 return JS::Handle<T>::fromMarkedLocation(&vec().operator[](aIndex)); 255 } 256 }; 257 258 template <typename Wrapper, typename T, size_t Capacity, typename AllocPolicy> 259 class MutableWrappedPtrOperations<JS::GCVector<T, Capacity, AllocPolicy>, 260 Wrapper> 261 : public WrappedPtrOperations<JS::GCVector<T, Capacity, AllocPolicy>, 262 Wrapper> { 263 using Vec = JS::GCVector<T, Capacity, AllocPolicy>; 264 const Vec& vec() const { return static_cast<const Wrapper*>(this)->get(); } 265 Vec& vec() { return static_cast<Wrapper*>(this)->get(); } 266 267 public: 268 const AllocPolicy& allocPolicy() const { return vec().allocPolicy(); } 269 AllocPolicy& allocPolicy() { return vec().allocPolicy(); } 270 const T* begin() const { return vec().begin(); } 271 T* begin() { return vec().begin(); } 272 const T* end() const { return vec().end(); } 273 T* end() { return vec().end(); } 274 const T& back() const { return vec().back(); } 275 T& back() { return vec().back(); } 276 277 JS::Handle<T> operator[](size_t aIndex) const { 278 return JS::Handle<T>::fromMarkedLocation(&vec().operator[](aIndex)); 279 } 280 JS::MutableHandle<T> operator[](size_t aIndex) { 281 return JS::MutableHandle<T>::fromMarkedLocation(&vec().operator[](aIndex)); 282 } 283 284 [[nodiscard]] bool initCapacity(size_t aRequest) { 285 return vec().initCapacity(aRequest); 286 } 287 [[nodiscard]] bool reserve(size_t aRequest) { 288 return vec().reserve(aRequest); 289 } 290 void shrinkBy(size_t aIncr) { vec().shrinkBy(aIncr); } 291 [[nodiscard]] bool growBy(size_t aIncr) { return vec().growBy(aIncr); } 292 [[nodiscard]] bool resize(size_t aNewLength) { 293 return vec().resize(aNewLength); 294 } 295 void clear() { vec().clear(); } 296 void clearAndFree() { vec().clearAndFree(); } 297 template <typename U> 298 [[nodiscard]] bool append(U&& aU) { 299 return vec().append(std::forward<U>(aU)); 300 } 301 template <typename... Args> 302 [[nodiscard]] bool emplaceBack(Args&&... aArgs) { 303 return vec().emplaceBack(std::forward<Args>(aArgs)...); 304 } 305 template <typename... Args> 306 void infallibleEmplaceBack(Args&&... args) { 307 vec().infallibleEmplaceBack(std::forward<Args>(args)...); 308 } 309 template <typename U> 310 [[nodiscard]] bool appendAll(U&& aU) { 311 return vec().appendAll(aU); 312 } 313 [[nodiscard]] bool appendN(const T& aT, size_t aN) { 314 return vec().appendN(aT, aN); 315 } 316 template <typename U> 317 [[nodiscard]] bool append(const U* aBegin, const U* aEnd) { 318 return vec().append(aBegin, aEnd); 319 } 320 template <typename U> 321 [[nodiscard]] bool append(const U* aBegin, size_t aLength) { 322 return vec().append(aBegin, aLength); 323 } 324 template <typename U> 325 void infallibleAppend(U&& aU) { 326 vec().infallibleAppend(std::forward<U>(aU)); 327 } 328 void infallibleAppendN(const T& aT, size_t aN) { 329 vec().infallibleAppendN(aT, aN); 330 } 331 template <typename U> 332 void infallibleAppend(const U* aBegin, const U* aEnd) { 333 vec().infallibleAppend(aBegin, aEnd); 334 } 335 template <typename U> 336 void infallibleAppend(const U* aBegin, size_t aLength) { 337 vec().infallibleAppend(aBegin, aLength); 338 } 339 void popBack() { vec().popBack(); } 340 T popCopy() { return vec().popCopy(); } 341 void erase(T* aT) { vec().erase(aT); } 342 void erase(T* aBegin, T* aEnd) { vec().erase(aBegin, aEnd); } 343 template <typename Pred> 344 void eraseIf(Pred pred) { 345 vec().eraseIf(pred); 346 } 347 template <typename U> 348 void eraseIfEqual(const U& u) { 349 vec().eraseIfEqual(u); 350 } 351 }; 352 353 template <typename Wrapper, typename T, typename AllocPolicy> 354 class WrappedPtrOperations<JS::StackGCVector<T, AllocPolicy>, Wrapper> 355 : public WrappedPtrOperations< 356 typename JS::StackGCVector<T, AllocPolicy>::Base, Wrapper> {}; 357 358 template <typename Wrapper, typename T, typename AllocPolicy> 359 class MutableWrappedPtrOperations<JS::StackGCVector<T, AllocPolicy>, Wrapper> 360 : public MutableWrappedPtrOperations< 361 typename JS::StackGCVector<T, AllocPolicy>::Base, Wrapper> {}; 362 363 } // namespace js 364 365 namespace JS { 366 367 // An automatically rooted GCVector for stack use. 368 template <typename T> 369 class RootedVector : public Rooted<StackGCVector<T>> { 370 using Vec = StackGCVector<T>; 371 using Base = Rooted<Vec>; 372 373 public: 374 explicit RootedVector(JSContext* cx) : Base(cx, Vec(cx)) {} 375 }; 376 377 // For use in rust code, an analog to RootedVector that doesn't require 378 // instances to be destroyed in LIFO order. 379 template <typename T> 380 class PersistentRootedVector : public PersistentRooted<StackGCVector<T>> { 381 using Vec = StackGCVector<T>; 382 using Base = PersistentRooted<Vec>; 383 384 public: 385 explicit PersistentRootedVector(JSContext* cx) : Base(cx, Vec(cx)) {} 386 }; 387 388 } // namespace JS 389 390 #endif // js_GCVector_h