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