LCOV - code coverage report
Current view: top level - js/src/ds - LifoAlloc.h (source / functions) Hit Total Coverage
Test: output.info Lines: 238 280 85.0 %
Date: 2017-07-14 16:53:18 Functions: 168 280 60.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : #ifndef ds_LifoAlloc_h
       8             : #define ds_LifoAlloc_h
       9             : 
      10             : #include "mozilla/Attributes.h"
      11             : #include "mozilla/MathAlgorithms.h"
      12             : #include "mozilla/MemoryChecking.h"
      13             : #include "mozilla/MemoryReporting.h"
      14             : #include "mozilla/PodOperations.h"
      15             : #include "mozilla/TemplateLib.h"
      16             : #include "mozilla/TypeTraits.h"
      17             : 
      18             : // This data structure supports stacky LIFO allocation (mark/release and
      19             : // LifoAllocScope). It does not maintain one contiguous segment; instead, it
      20             : // maintains a bunch of linked memory segments. In order to prevent malloc/free
      21             : // thrashing, unused segments are deallocated when garbage collection occurs.
      22             : 
      23             : #include "jsutil.h"
      24             : 
      25             : namespace js {
      26             : 
      27             : namespace detail {
      28             : 
      29             : static const size_t LIFO_ALLOC_ALIGN = 8;
      30             : 
      31             : MOZ_ALWAYS_INLINE
      32             : char*
      33     2465426 : AlignPtr(void* orig)
      34             : {
      35             :     static_assert(mozilla::tl::FloorLog2<LIFO_ALLOC_ALIGN>::value ==
      36             :                   mozilla::tl::CeilingLog2<LIFO_ALLOC_ALIGN>::value,
      37             :                   "LIFO_ALLOC_ALIGN must be a power of two");
      38             : 
      39     2465426 :     char* result = (char*) ((uintptr_t(orig) + (LIFO_ALLOC_ALIGN - 1)) & (~LIFO_ALLOC_ALIGN + 1));
      40     2465426 :     MOZ_ASSERT(uintptr_t(result) % LIFO_ALLOC_ALIGN == 0);
      41     2465426 :     return result;
      42             : }
      43             : 
      44             : // Header for a chunk of memory wrangled by the LifoAlloc.
      45             : class BumpChunk
      46             : {
      47             :     char*       bump;          // start of the available data
      48             :     char*       limit;         // end of the data
      49             :     BumpChunk*  next_;         // the next BumpChunk
      50             :     size_t      bumpSpaceSize;  // size of the data area
      51             : 
      52      583231 :     char* headerBase() { return reinterpret_cast<char*>(this); }
      53     1210353 :     char* bumpBase() const { return limit - bumpSpaceSize; }
      54             : 
      55        4042 :     explicit BumpChunk(size_t bumpSpaceSize)
      56        4042 :       : bump(reinterpret_cast<char*>(this) + sizeof(BumpChunk)),
      57        4042 :         limit(bump + bumpSpaceSize),
      58        8084 :         next_(nullptr), bumpSpaceSize(bumpSpaceSize)
      59             :     {
      60        4042 :         MOZ_ASSERT(bump == AlignPtr(bump));
      61        4042 :     }
      62             : 
      63      593719 :     void setBump(void* ptr) {
      64      593719 :         MOZ_ASSERT(bumpBase() <= ptr);
      65      593719 :         MOZ_ASSERT(ptr <= limit);
      66             : #if defined(DEBUG) || defined(MOZ_HAVE_MEM_CHECKS)
      67      593719 :         char* prevBump = bump;
      68             : #endif
      69      593719 :         bump = static_cast<char*>(ptr);
      70             : #ifdef DEBUG
      71      593719 :         MOZ_ASSERT(contains(prevBump));
      72             : 
      73             :         // Clobber the now-free space.
      74      593712 :         if (prevBump > bump)
      75       19113 :             memset(bump, 0xcd, prevBump - bump);
      76             : #endif
      77             : 
      78             :         // Poison/Unpoison memory that we just free'd/allocated.
      79             : #if defined(MOZ_HAVE_MEM_CHECKS)
      80             :         if (prevBump > bump)
      81             :             MOZ_MAKE_MEM_NOACCESS(bump, prevBump - bump);
      82             :         else if (bump > prevBump)
      83             :             MOZ_MAKE_MEM_UNDEFINED(prevBump, bump - prevBump);
      84             : #endif
      85      593712 :     }
      86             : 
      87             :   public:
      88     1190448 :     BumpChunk* next() const { return next_; }
      89        3142 :     void setNext(BumpChunk* succ) { next_ = succ; }
      90             : 
      91        4014 :     size_t used() const { return bump - bumpBase(); }
      92             : 
      93          21 :     void* start() const { return bumpBase(); }
      94       25664 :     void* end() const { return limit; }
      95             : 
      96           0 :     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) {
      97           0 :         return mallocSizeOf(this);
      98             :     }
      99             : 
     100        6138 :     size_t computedSizeOfIncludingThis() {
     101        6138 :         return limit - headerBase();
     102             :     }
     103             : 
     104        3553 :     void resetBump() {
     105        3553 :         setBump(headerBase() + sizeof(BumpChunk));
     106        3553 :     }
     107             : 
     108       35667 :     void* mark() const { return bump; }
     109             : 
     110       18945 :     void release(void* mark) {
     111       18945 :         MOZ_ASSERT(contains(mark));
     112       18945 :         MOZ_ASSERT(mark <= bump);
     113       18945 :         setBump(mark);
     114       18945 :     }
     115             : 
     116      612662 :     bool contains(void* mark) const {
     117      612662 :         return bumpBase() <= mark && mark <= limit;
     118             :     }
     119             : 
     120             :     bool canAlloc(size_t n);
     121             : 
     122     1294383 :     size_t unused() {
     123     1294383 :         return limit - AlignPtr(bump);
     124             :     }
     125             : 
     126             :     // Try to perform an allocation of size |n|, return null if not possible.
     127             :     MOZ_ALWAYS_INLINE
     128      575691 :     void* tryAlloc(size_t n) {
     129      575691 :         char* aligned = AlignPtr(bump);
     130      575690 :         char* newBump = aligned + n;
     131             : 
     132      575690 :         if (newBump > limit)
     133        4464 :             return nullptr;
     134             : 
     135             :         // Check for overflow.
     136      571226 :         if (MOZ_UNLIKELY(newBump < bump))
     137           0 :             return nullptr;
     138             : 
     139      571226 :         MOZ_ASSERT(canAlloc(n)); // Ensure consistency between "can" and "try".
     140      571224 :         setBump(newBump);
     141      571223 :         return aligned;
     142             :     }
     143             : 
     144             :     static BumpChunk* new_(size_t chunkSize);
     145             :     static void delete_(BumpChunk* chunk);
     146             : };
     147             : 
     148             : } // namespace detail
     149             : 
     150             : // LIFO bump allocator: used for phase-oriented and fast LIFO allocations.
     151             : //
     152             : // Note: |latest| is not necessary "last". We leave BumpChunks latent in the
     153             : // chain after they've been released to avoid thrashing before a GC.
     154             : class LifoAlloc
     155             : {
     156             :     typedef detail::BumpChunk BumpChunk;
     157             : 
     158             :     BumpChunk*  first;
     159             :     BumpChunk*  latest;
     160             :     BumpChunk*  last;
     161             :     size_t      markCount;
     162             :     size_t      defaultChunkSize_;
     163             :     size_t      curSize_;
     164             :     size_t      peakSize_;
     165             : #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     166             :     bool        fallibleScope_;
     167             : #endif
     168             : 
     169             :     void operator=(const LifoAlloc&) = delete;
     170             :     LifoAlloc(const LifoAlloc&) = delete;
     171             : 
     172             :     // Return a BumpChunk that can perform an allocation of at least size |n|
     173             :     // and add it to the chain appropriately.
     174             :     //
     175             :     // Side effect: if retval is non-null, |first| and |latest| are initialized
     176             :     // appropriately.
     177             :     BumpChunk* getOrCreateChunk(size_t n);
     178             : 
     179        3193 :     void reset(size_t defaultChunkSize) {
     180        3193 :         MOZ_ASSERT(mozilla::RoundUpPow2(defaultChunkSize) == defaultChunkSize);
     181        3193 :         first = latest = last = nullptr;
     182        3193 :         defaultChunkSize_ = defaultChunkSize;
     183        3193 :         markCount = 0;
     184        3193 :         curSize_ = 0;
     185        3193 :     }
     186             : 
     187             :     // Append unused chunks to the end of this LifoAlloc.
     188           2 :     void appendUnused(BumpChunk* start, BumpChunk* end) {
     189           2 :         MOZ_ASSERT(start && end);
     190           2 :         if (last)
     191           1 :             last->setNext(start);
     192             :         else
     193           1 :             first = latest = start;
     194           2 :         last = end;
     195           2 :     }
     196             : 
     197             :     // Append used chunks to the end of this LifoAlloc. We act as if all the
     198             :     // chunks in |this| are used, even if they're not, so memory may be wasted.
     199         563 :     void appendUsed(BumpChunk* otherFirst, BumpChunk* otherLatest, BumpChunk* otherLast) {
     200         563 :         MOZ_ASSERT(otherFirst && otherLatest && otherLast);
     201         563 :         if (last)
     202         562 :             last->setNext(otherFirst);
     203             :         else
     204           1 :             first = otherFirst;
     205         563 :         latest = otherLatest;
     206         563 :         last = otherLast;
     207         563 :     }
     208             : 
     209        4607 :     void incrementCurSize(size_t size) {
     210        4607 :         curSize_ += size;
     211        4607 :         if (curSize_ > peakSize_)
     212        4522 :             peakSize_ = curSize_;
     213        4607 :     }
     214        2096 :     void decrementCurSize(size_t size) {
     215        2096 :         MOZ_ASSERT(curSize_ >= size);
     216        2096 :         curSize_ -= size;
     217        2096 :     }
     218             : 
     219             :     MOZ_ALWAYS_INLINE
     220      571229 :     void* allocImpl(size_t n) {
     221             :         void* result;
     222      571229 :         if (latest && (result = latest->tryAlloc(n)))
     223      565296 :             return result;
     224             : 
     225        5929 :         if (!getOrCreateChunk(n))
     226           0 :             return nullptr;
     227             : 
     228             :         // Since we just created a large enough chunk, this can't fail.
     229        5929 :         result = latest->tryAlloc(n);
     230        5929 :         MOZ_ASSERT(result);
     231        5929 :         return result;
     232             :     }
     233             : 
     234             :   public:
     235        2561 :     explicit LifoAlloc(size_t defaultChunkSize)
     236        2561 :       : peakSize_(0)
     237             : #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     238        2561 :       , fallibleScope_(true)
     239             : #endif
     240             :     {
     241        2561 :         reset(defaultChunkSize);
     242        2561 :     }
     243             : 
     244             :     // Steal allocated chunks from |other|.
     245         632 :     void steal(LifoAlloc* other) {
     246         632 :         MOZ_ASSERT(!other->markCount);
     247         632 :         MOZ_ASSERT(!latest);
     248             : 
     249             :         // Copy everything from |other| to |this| except for |peakSize_|, which
     250             :         // requires some care.
     251         632 :         size_t oldPeakSize = peakSize_;
     252         632 :         mozilla::PodAssign(this, other);
     253         632 :         peakSize_ = Max(oldPeakSize, curSize_);
     254             : 
     255         632 :         other->reset(defaultChunkSize_);
     256         632 :     }
     257             : 
     258             :     // Append all chunks from |other|. They are removed from |other|.
     259             :     void transferFrom(LifoAlloc* other);
     260             : 
     261             :     // Append unused chunks from |other|. They are removed from |other|.
     262             :     void transferUnusedFrom(LifoAlloc* other);
     263             : 
     264        1976 :     ~LifoAlloc() { freeAll(); }
     265             : 
     266             :     size_t defaultChunkSize() const { return defaultChunkSize_; }
     267             : 
     268             :     // Frees all held memory.
     269             :     void freeAll();
     270             : 
     271             :     static const unsigned HUGE_ALLOCATION = 50 * 1024 * 1024;
     272        1903 :     void freeAllIfHugeAndUnused() {
     273        1903 :         if (markCount == 0 && curSize_ > HUGE_ALLOCATION)
     274           0 :             freeAll();
     275        1903 :     }
     276             : 
     277             :     MOZ_ALWAYS_INLINE
     278      488852 :     void* alloc(size_t n) {
     279             : #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     280             :         // Only simulate OOMs when we are not using the LifoAlloc as an
     281             :         // infallible allocator.
     282      488852 :         if (fallibleScope_)
     283      480415 :             JS_OOM_POSSIBLY_FAIL();
     284             : #endif
     285      488853 :         return allocImpl(n);
     286             :     }
     287             : 
     288             :     MOZ_ALWAYS_INLINE
     289       82391 :     void* allocInfallible(size_t n) {
     290      164782 :         AutoEnterOOMUnsafeRegion oomUnsafe;
     291       82391 :         if (void* result = allocImpl(n))
     292       82391 :             return result;
     293           0 :         oomUnsafe.crash("LifoAlloc::allocInfallible");
     294             :         return nullptr;
     295             :     }
     296             : 
     297             :     // Ensures that enough space exists to satisfy N bytes worth of
     298             :     // allocation requests, not necessarily contiguous. Note that this does
     299             :     // not guarantee a successful single allocation of N bytes.
     300             :     MOZ_ALWAYS_INLINE
     301      114958 :     MOZ_MUST_USE bool ensureUnusedApproximate(size_t n) {
     302      229915 :         AutoFallibleScope fallibleAllocator(this);
     303      114958 :         size_t total = 0;
     304     1291399 :         for (BumpChunk* chunk = latest; chunk; chunk = chunk->next()) {
     305     1290953 :             total += chunk->unused();
     306     1290963 :             if (total >= n)
     307      114522 :                 return true;
     308             :         }
     309         435 :         BumpChunk* latestBefore = latest;
     310         435 :         if (!getOrCreateChunk(n))
     311           0 :             return false;
     312         435 :         if (latestBefore)
     313         435 :             latest = latestBefore;
     314         435 :         return true;
     315             :     }
     316             : 
     317             :     MOZ_ALWAYS_INLINE
     318        4660 :     void setAsInfallibleByDefault() {
     319             : #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     320        4660 :         fallibleScope_ = false;
     321             : #endif
     322        4660 :     }
     323             : 
     324             :     class MOZ_NON_TEMPORARY_CLASS AutoFallibleScope {
     325             : #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     326             :         LifoAlloc* lifoAlloc_;
     327             :         bool prevFallibleScope_;
     328             :         MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
     329             : 
     330             :       public:
     331      480388 :         explicit AutoFallibleScope(LifoAlloc* lifoAlloc MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
     332      480388 :             MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     333      480388 :             lifoAlloc_ = lifoAlloc;
     334      480388 :             prevFallibleScope_ = lifoAlloc->fallibleScope_;
     335      480388 :             lifoAlloc->fallibleScope_ = true;
     336      480388 :         }
     337             : 
     338      959470 :         ~AutoFallibleScope() {
     339      479735 :             lifoAlloc_->fallibleScope_ = prevFallibleScope_;
     340      479735 :         }
     341             : #else
     342             :       public:
     343             :         explicit AutoFallibleScope(LifoAlloc*) {}
     344             : #endif
     345             :     };
     346             : 
     347             :     template <typename T>
     348        6155 :     T* newArray(size_t count) {
     349             :         static_assert(mozilla::IsPod<T>::value,
     350             :                       "T must be POD so that constructors (and destructors, "
     351             :                       "when the LifoAlloc is freed) need not be called");
     352        6155 :         return newArrayUninitialized<T>(count);
     353             :     }
     354             : 
     355             :     // Create an array with uninitialized elements of type |T|.
     356             :     // The caller is responsible for initialization.
     357             :     template <typename T>
     358        6469 :     T* newArrayUninitialized(size_t count) {
     359             :         size_t bytes;
     360        6469 :         if (MOZ_UNLIKELY(!CalculateAllocSize<T>(count, &bytes)))
     361           0 :             return nullptr;
     362        6469 :         return static_cast<T*>(alloc(bytes));
     363             :     }
     364             : 
     365             :     class Mark {
     366             :         BumpChunk* chunk;
     367             :         void* markInChunk;
     368             :         friend class LifoAlloc;
     369       19607 :         Mark(BumpChunk* chunk, void* markInChunk) : chunk(chunk), markInChunk(markInChunk) {}
     370             :       public:
     371        2065 :         Mark() : chunk(nullptr), markInChunk(nullptr) {}
     372             :     };
     373             : 
     374       19763 :     Mark mark() {
     375       19763 :         markCount++;
     376       19763 :         return latest ? Mark(latest, latest->mark()) : Mark();
     377             :     }
     378             : 
     379       19100 :     void release(Mark mark) {
     380       19100 :         markCount--;
     381       19100 :         if (!mark.chunk) {
     382         155 :             latest = first;
     383         155 :             if (latest)
     384         155 :                 latest->resetBump();
     385             :         } else {
     386       18945 :             latest = mark.chunk;
     387       18945 :             latest->release(mark.markInChunk);
     388             :         }
     389       19100 :     }
     390             : 
     391          20 :     void releaseAll() {
     392          20 :         MOZ_ASSERT(!markCount);
     393          20 :         latest = first;
     394          20 :         if (latest)
     395          20 :             latest->resetBump();
     396          20 :     }
     397             : 
     398             :     // Get the total "used" (occupied bytes) count for the arena chunks.
     399          31 :     size_t used() const {
     400          31 :         size_t accum = 0;
     401          31 :         for (BumpChunk* chunk = first; chunk; chunk = chunk->next()) {
     402          21 :             accum += chunk->used();
     403          21 :             if (chunk == latest)
     404          21 :                 break;
     405             :         }
     406          31 :         return accum;
     407             :     }
     408             : 
     409             :     // Return true if the LifoAlloc does not currently contain any allocations.
     410        4861 :     bool isEmpty() const {
     411        4861 :         return !latest || !latest->used();
     412             :     }
     413             : 
     414             :     // Return the number of bytes remaining to allocate in the current chunk.
     415             :     // e.g. How many bytes we can allocate before needing a new block.
     416        3430 :     size_t availableInCurrentChunk() const {
     417        3430 :         if (!latest)
     418           0 :             return 0;
     419        3430 :         return latest->unused();
     420             :     }
     421             : 
     422             :     // Get the total size of the arena chunks (including unused space).
     423           0 :     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
     424           0 :         size_t n = 0;
     425           0 :         for (BumpChunk* chunk = first; chunk; chunk = chunk->next())
     426           0 :             n += chunk->sizeOfIncludingThis(mallocSizeOf);
     427           0 :         return n;
     428             :     }
     429             : 
     430             :     // Get the total size of the arena chunks (including unused space).
     431          51 :     size_t computedSizeOfExcludingThis() const {
     432          51 :         size_t n = 0;
     433          51 :         for (BumpChunk* chunk = first; chunk; chunk = chunk->next())
     434           0 :             n += chunk->computedSizeOfIncludingThis();
     435          51 :         return n;
     436             :     }
     437             : 
     438             :     // Like sizeOfExcludingThis(), but includes the size of the LifoAlloc itself.
     439           0 :     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
     440           0 :         return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
     441             :     }
     442             : 
     443             :     // Get the peak size of the arena chunks (including unused space and
     444             :     // bookkeeping space).
     445           0 :     size_t peakSizeOfExcludingThis() const { return peakSize_; }
     446             : 
     447             :     // Doesn't perform construction; useful for lazily-initialized POD types.
     448             :     template <typename T>
     449             :     MOZ_ALWAYS_INLINE
     450        3430 :     T* pod_malloc() {
     451        3430 :         return static_cast<T*>(alloc(sizeof(T)));
     452             :     }
     453             : 
     454       49651 :     JS_DECLARE_NEW_METHODS(new_, alloc, MOZ_ALWAYS_INLINE)
     455        9724 :     JS_DECLARE_NEW_METHODS(newInfallible, allocInfallible, MOZ_ALWAYS_INLINE)
     456             : 
     457             : #ifdef DEBUG
     458           0 :     bool contains(void* ptr) const {
     459           0 :         for (BumpChunk* chunk = first; chunk; chunk = chunk->next()) {
     460           0 :             if (chunk->contains(ptr))
     461           0 :                 return true;
     462             :         }
     463           0 :         return false;
     464             :     }
     465             : #endif
     466             : 
     467             :     // A mutable enumeration of the allocated data.
     468             :     class Enum
     469             :     {
     470             :         friend class LifoAlloc;
     471             :         friend class detail::BumpChunk;
     472             : 
     473             :         LifoAlloc* alloc_;  // The LifoAlloc being traversed.
     474             :         BumpChunk* chunk_;  // The current chunk.
     475             :         char* position_;    // The current position (must be within chunk_).
     476             : 
     477             :         // If there is not enough room in the remaining block for |size|,
     478             :         // advance to the next block and update the position.
     479       12832 :         void ensureSpaceAndAlignment(size_t size) {
     480       12832 :             MOZ_ASSERT(!empty());
     481       12832 :             char* aligned = detail::AlignPtr(position_);
     482       12832 :             if (aligned + size > chunk_->end()) {
     483           0 :                 chunk_ = chunk_->next();
     484           0 :                 position_ = static_cast<char*>(chunk_->start());
     485             :             } else {
     486       12832 :                 position_ = aligned;
     487             :             }
     488       12832 :             MOZ_ASSERT(uintptr_t(position_) + size <= uintptr_t(chunk_->end()));
     489       12832 :         }
     490             : 
     491             :       public:
     492          21 :         explicit Enum(LifoAlloc& alloc)
     493          21 :           : alloc_(&alloc),
     494          21 :             chunk_(alloc.first),
     495          42 :             position_(static_cast<char*>(alloc.first ? alloc.first->start() : nullptr))
     496          21 :         {}
     497             : 
     498             :         // Return true if there are no more bytes to enumerate.
     499       16061 :         bool empty() {
     500       16061 :             return !chunk_ || (chunk_ == alloc_->latest && position_ >= chunk_->mark());
     501             :         }
     502             : 
     503             :         // Move the read position forward by the size of one T.
     504             :         template <typename T>
     505        3208 :         void popFront() {
     506        3208 :             popFront(sizeof(T));
     507        3208 :         }
     508             : 
     509             :         // Move the read position forward by |size| bytes.
     510        6416 :         void popFront(size_t size) {
     511        6416 :             ensureSpaceAndAlignment(size);
     512        6416 :             position_ = position_ + size;
     513        6416 :         }
     514             : 
     515             :         // Update the bytes at the current position with a new value.
     516             :         template <typename T>
     517             :         void updateFront(const T& t) {
     518             :             ensureSpaceAndAlignment(sizeof(T));
     519             :             memmove(position_, &t, sizeof(T));
     520             :         }
     521             : 
     522             :         // Return a pointer to the item at the current position. This
     523             :         // returns a pointer to the inline storage, not a copy.
     524             :         template <typename T>
     525        6416 :         T* get(size_t size = sizeof(T)) {
     526        6416 :             ensureSpaceAndAlignment(size);
     527        6416 :             return reinterpret_cast<T*>(position_);
     528             :         }
     529             : 
     530             :         // Return a Mark at the current position of the Enum.
     531             :         Mark mark() {
     532             :             alloc_->markCount++;
     533             :             return Mark(chunk_, position_);
     534             :         }
     535             :     };
     536             : };
     537             : 
     538             : class MOZ_NON_TEMPORARY_CLASS LifoAllocScope
     539             : {
     540             :     LifoAlloc*      lifoAlloc;
     541             :     LifoAlloc::Mark mark;
     542             :     LifoAlloc::AutoFallibleScope fallibleScope;
     543             :     bool            shouldRelease;
     544             :     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
     545             : 
     546             :   public:
     547        5555 :     explicit LifoAllocScope(LifoAlloc* lifoAlloc
     548             :                             MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     549        5555 :       : lifoAlloc(lifoAlloc),
     550             :         mark(lifoAlloc->mark()),
     551             :         fallibleScope(lifoAlloc),
     552        5555 :         shouldRelease(true)
     553             :     {
     554        5555 :         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     555        5555 :     }
     556             : 
     557        9804 :     ~LifoAllocScope() {
     558        4902 :         if (shouldRelease)
     559        4902 :             lifoAlloc->release(mark);
     560        4902 :     }
     561             : 
     562      293372 :     LifoAlloc& alloc() {
     563      293372 :         return *lifoAlloc;
     564             :     }
     565             : 
     566           0 :     void releaseEarly() {
     567           0 :         MOZ_ASSERT(shouldRelease);
     568           0 :         lifoAlloc->release(mark);
     569           0 :         shouldRelease = false;
     570           0 :     }
     571             : };
     572             : 
     573             : enum Fallibility {
     574             :     Fallible,
     575             :     Infallible
     576             : };
     577             : 
     578             : template <Fallibility fb>
     579             : class LifoAllocPolicy
     580             : {
     581             :     LifoAlloc& alloc_;
     582             : 
     583             :   public:
     584        4192 :     MOZ_IMPLICIT LifoAllocPolicy(LifoAlloc& alloc)
     585        4192 :       : alloc_(alloc)
     586        4192 :     {}
     587             :     template <typename T>
     588        3284 :     T* maybe_pod_malloc(size_t numElems) {
     589             :         size_t bytes;
     590        3284 :         if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes)))
     591           0 :             return nullptr;
     592        3284 :         void* p = fb == Fallible ? alloc_.alloc(bytes) : alloc_.allocInfallible(bytes);
     593        3284 :         return static_cast<T*>(p);
     594             :     }
     595             :     template <typename T>
     596           0 :     T* maybe_pod_calloc(size_t numElems) {
     597           0 :         T* p = maybe_pod_malloc<T>(numElems);
     598           0 :         if (MOZ_UNLIKELY(!p))
     599           0 :             return nullptr;
     600           0 :         memset(p, 0, numElems * sizeof(T));
     601           0 :         return p;
     602             :     }
     603             :     template <typename T>
     604         562 :     T* maybe_pod_realloc(T* p, size_t oldSize, size_t newSize) {
     605         562 :         T* n = maybe_pod_malloc<T>(newSize);
     606         562 :         if (MOZ_UNLIKELY(!n))
     607           0 :             return nullptr;
     608         562 :         MOZ_ASSERT(!(oldSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value));
     609         562 :         memcpy(n, p, Min(oldSize * sizeof(T), newSize * sizeof(T)));
     610         562 :         return n;
     611             :     }
     612             :     template <typename T>
     613        2722 :     T* pod_malloc(size_t numElems) {
     614        2722 :         return maybe_pod_malloc<T>(numElems);
     615             :     }
     616             :     template <typename T>
     617           0 :     T* pod_calloc(size_t numElems) {
     618           0 :         return maybe_pod_calloc<T>(numElems);
     619             :     }
     620             :     template <typename T>
     621         562 :     T* pod_realloc(T* p, size_t oldSize, size_t newSize) {
     622         562 :         return maybe_pod_realloc<T>(p, oldSize, newSize);
     623             :     }
     624         873 :     void free_(void* p) {
     625         873 :     }
     626           0 :     void reportAllocOverflow() const {
     627           0 :     }
     628        1951 :     MOZ_MUST_USE bool checkSimulatedOOM() const {
     629        1951 :         return fb == Infallible || !js::oom::ShouldFailWithOOM();
     630             :     }
     631             : };
     632             : 
     633             : } // namespace js
     634             : 
     635             : #endif /* ds_LifoAlloc_h */

Generated by: LCOV version 1.13