Bug 1373579 - Part 3: Require fallible Init method rather than infallible constructor when using fallible allocator. r=billm, a=jcristau FIREFOX_55_0b4_BUILD2 FIREFOX_55_0b4_RELEASE
authorKris Maglione <maglione.k@gmail.com>
Tue, 20 Jun 2017 12:06:13 -0700
changeset 414061 0a6a0148a61c5b2255f8b4272634efc1d1bd0c98
parent 414060 9279e4f3262f6c7ca8d9b8900bd5bcd7ad4c539b
child 414062 0f0d117c2dd3aeacec121d0f325b0b171c21f3a1
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm, jcristau
bugs1373579
milestone55.0
Bug 1373579 - Part 3: Require fallible Init method rather than infallible constructor when using fallible allocator. r=billm, a=jcristau MozReview-Commit-ID: 7ymS0e39mrJ
js/src/builtin/TestingFunctions.cpp
mfbt/BufferList.h
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2371,17 +2371,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
@@ -59,34 +61,40 @@ class BufferList : private AllocPolicy
   // For the convenience of callers, all segments are required to be a multiple
   // of 8 bytes in capacity. Also, every buffer except the last one is required
   // 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
+  // destroyed. If an infallible allocator is used, an initial buffer of size
+  // aInitialSize and capacity aInitialCapacity is allocated automatically. This
+  // data will be contiguous and can be accessed via |Start()|. If a fallible
+  // alloc policy is used, aInitialSize must be 0, and the fallible |Init()|
+  // method may 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,27 @@ 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());
+    MOZ_ASSERT(aInitialCapacity != 0);
+    MOZ_ASSERT(aInitialCapacity % kSegmentAlignment == 0);
+
+    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 +148,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 +268,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);
 
@@ -316,16 +339,17 @@ private:
      mSize(0),
      mStandardCapacity(0)
   {
   }
 
   char* AllocateSegment(size_t aSize, size_t aCapacity)
   {
     MOZ_RELEASE_ASSERT(mOwning);
+    MOZ_ASSERT(aCapacity != 0);
     MOZ_ASSERT(aSize <= aCapacity);
 
     char* data = this->template pod_malloc<char>(aCapacity);
     if (!data) {
       return nullptr;
     }
     if (!mSegments.append(Segment(data, aSize, aCapacity))) {
       this->free_(data);