Bug 1373579: Part 3 - Require fallible Init method rather than infallible constructor when using fallible allocator. r?billm
MozReview-Commit-ID: 7ymS0e39mrJ
--- 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);