Bug 1373579: Part 3 - Require fallible Init method rather than infallible constructor when using fallible allocator. r?billm draft
authorKris Maglione <maglione.k@gmail.com>
Mon, 19 Jun 2017 16:50:37 -0700
changeset 596996 323845c20f255dee6c9f7850e261ce169548824e
parent 596995 8ef61c6d45e66104c1ddcbc15847e3249c0e7bdf
child 634101 47af0821e709231619f4b6413f7a083fc1244092
push id64791
push usermaglione.k@gmail.com
push dateMon, 19 Jun 2017 23:58:29 +0000
reviewersbillm
bugs1373579
milestone56.0a1
Bug 1373579: Part 3 - Require fallible Init method rather than infallible constructor when using fallible allocator. r?billm MozReview-Commit-ID: 7ymS0e39mrJ
js/src/builtin/TestingFunctions.cpp
mfbt/BufferList.h
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2372,17 +2372,19 @@ class CloneBufferObject : public NativeO
         Rooted<CloneBufferObject*> obj(cx, &args.thisv().toObject().as<CloneBufferObject>());
         obj->discard();
 
         char* str = JS_EncodeString(cx, args[0].toString());
         if (!str)
             return false;
         size_t nbytes = JS_GetStringLength(args[0].toString());
         MOZ_ASSERT(nbytes % sizeof(uint64_t) == 0);
-        auto buf = js::MakeUnique<JSStructuredCloneData>(nbytes, nbytes, nbytes);
+        auto buf = js::MakeUnique<JSStructuredCloneData>(0, 0, nbytes);
+        if (!buf->Init(nbytes, nbytes))
+            return false;
         js_memcpy(buf->Start(), str, nbytes);
         JS_free(cx, str);
         obj->setData(buf.release());
 
         args.rval().setUndefined();
         return true;
     }
 
--- a/mfbt/BufferList.h
+++ b/mfbt/BufferList.h
@@ -17,16 +17,18 @@
 #include <string.h>
 
 // BufferList represents a sequence of buffers of data. A BufferList can choose
 // to own its buffers or not. The class handles writing to the buffers,
 // iterating over them, and reading data out. Unlike SegmentedVector, the
 // buffers may be of unequal size. Like SegmentedVector, BufferList is a nice
 // way to avoid large contiguous allocations (which can trigger OOMs).
 
+class InfallibleAllocPolicy;
+
 namespace mozilla {
 
 template<typename AllocPolicy>
 class BufferList : private AllocPolicy
 {
   // Each buffer in a BufferList has a size and a capacity. The first mSize
   // bytes are initialized and the remaining |mCapacity - mSize| bytes are free.
   struct Segment
@@ -61,32 +63,38 @@ class BufferList : private AllocPolicy
   // to be full (i.e., size == capacity). Therefore, a byte at offset N within
   // the BufferList and stored in memory at an address A will satisfy
   // (N % Align == A % Align) if Align == 2, 4, or 8.
   static const size_t kSegmentAlignment = 8;
 
   // Allocate a BufferList. The BufferList will free all its buffers when it is
   // destroyed. An initial buffer of size aInitialSize and capacity
   // aInitialCapacity is allocated automatically. This data will be contiguous
-  // an can be accessed via |Start()|. Subsequent buffers will be allocated with
+  // and can be accessed via |Start()| if, and only if, an infallible alloc
+  // policy is used. If a fallible alloc policy is used, the fallible |Init()|
+  // method must be called instead. Subsequent buffers will be allocated with
   // capacity aStandardCapacity.
   BufferList(size_t aInitialSize,
              size_t aInitialCapacity,
              size_t aStandardCapacity,
              AllocPolicy aAP = AllocPolicy())
    : AllocPolicy(aAP),
      mOwning(true),
      mSegments(aAP),
      mSize(0),
      mStandardCapacity(aStandardCapacity)
   {
     MOZ_ASSERT(aInitialCapacity % kSegmentAlignment == 0);
     MOZ_ASSERT(aStandardCapacity % kSegmentAlignment == 0);
 
     if (aInitialCapacity) {
+      MOZ_ASSERT((aInitialSize == 0 || IsSame<AllocPolicy, InfallibleAllocPolicy>::value),
+                 "BufferList may only be constructed with an initial size when "
+                 "using an infallible alloc policy");
+
       AllocateSegment(aInitialSize, aInitialCapacity);
     }
   }
 
   BufferList(const BufferList& aOther) = delete;
 
   BufferList(BufferList&& aOther)
    : mOwning(aOther.mOwning),
@@ -109,16 +117,24 @@ class BufferList : private AllocPolicy
     mSize = aOther.mSize;
     aOther.mSegments.clear();
     aOther.mSize = 0;
     return *this;
   }
 
   ~BufferList() { Clear(); }
 
+  // Initializes the BufferList with a segment of the given size and capacity.
+  // May only be called once, before any segments have been allocated.
+  bool Init(size_t aInitialSize, size_t aInitialCapacity)
+  {
+    MOZ_ASSERT(mSegments.empty());
+    return AllocateSegment(aInitialSize, aInitialCapacity);
+  }
+
   // Returns the sum of the sizes of all the buffers.
   size_t Size() const { return mSize; }
 
   void Clear()
   {
     if (mOwning) {
       for (Segment& segment : mSegments) {
         this->free_(segment.mData);
@@ -129,17 +145,17 @@ class BufferList : private AllocPolicy
     mSize = 0;
   }
 
   // Iterates over bytes in the segments. You can advance it by as many bytes as
   // you choose.
   class IterImpl
   {
     // Invariants:
-    //   (0) mSegment <= bufferList.mSegments.size()
+    //   (0) mSegment <= bufferList.mSegments.length()
     //   (1) mData <= mDataEnd
     //   (2) If mSegment is not the last segment, mData < mDataEnd
     uintptr_t mSegment;
     char* mData;
     char* mDataEnd;
 
     friend class BufferList;
 
@@ -249,17 +265,21 @@ class BufferList : private AllocPolicy
     bool IsIn(const BufferList& aBuffers) const {
       return mSegment < aBuffers.mSegments.length() &&
              mData >= aBuffers.mSegments[mSegment].mData &&
              mData < aBuffers.mSegments[mSegment].End();
     }
   };
 
   // Special convenience method that returns Iter().Data().
-  char* Start() { return mSegments[0].mData; }
+  char* Start()
+  {
+    MOZ_RELEASE_ASSERT(!mSegments.empty());
+    return mSegments[0].mData;
+  }
   const char* Start() const { return mSegments[0].mData; }
 
   IterImpl Iter() const { return IterImpl(*this); }
 
   // Copies aSize bytes from aData into the BufferList. The storage for these
   // bytes may be split across multiple buffers. Size() is increased by aSize.
   inline bool WriteBytes(const char* aData, size_t aSize);