Back out b80f97b00d2f (bug 979480), it's in the way of more immediately important work. r=backout
authorJeff Walden <jwalden@mit.edu>
Fri, 14 Mar 2014 16:07:43 -0700
changeset 173722 ad76a457e582
parent 173721 0e768e11d2c6
child 173723 963a4aa83275
push id26415
push userkwierso@gmail.com
push date2014-03-15 05:20 +0000
treeherdermozilla-central@82c90c17fc95 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs979480
milestone30.0a1
backs outb80f97b00d2f
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Back out b80f97b00d2f (bug 979480), it's in the way of more immediately important work. r=backout
content/base/src/nsXMLHttpRequest.cpp
content/base/src/nsXMLHttpRequest.h
content/media/webaudio/AudioBuffer.cpp
js/src/builtin/TypedObject.cpp
js/src/builtin/TypedObjectConstants.h
js/src/gc/Nursery.cpp
js/src/gc/Nursery.h
js/src/jsapi-tests/testArrayBuffer.cpp
js/src/jsapi-tests/testMappedArrayBuffer.cpp
js/src/jsapi.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsgc.cpp
js/src/jsobj.cpp
js/src/jsobjinlines.h
js/src/shell/js.cpp
js/src/vm/ArrayBufferObject.cpp
js/src/vm/ArrayBufferObject.h
js/src/vm/ObjectImpl.cpp
js/src/vm/ObjectImpl.h
js/src/vm/SharedArrayObject.cpp
js/src/vm/SharedArrayObject.h
js/src/vm/StructuredClone.cpp
js/src/vm/TypedArrayObject.cpp
js/src/vm/TypedArrayObject.h
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -3811,46 +3811,45 @@ nsXMLHttpRequestXPCOMifier::GetInterface
   }
 
   return mXHR->GetInterface(aIID, aResult);
 }
 
 namespace mozilla {
 
 ArrayBufferBuilder::ArrayBufferBuilder()
-  : mDataPtr(nullptr),
+  : mRawContents(nullptr),
+    mDataPtr(nullptr),
     mCapacity(0),
     mLength(0)
 {
 }
 
 ArrayBufferBuilder::~ArrayBufferBuilder()
 {
   reset();
 }
 
 void
 ArrayBufferBuilder::reset()
 {
-  if (mDataPtr) {
-    JS_free(nullptr, mDataPtr);
+  if (mRawContents) {
+    JS_free(nullptr, mRawContents);
   }
-  mDataPtr = nullptr;
+  mRawContents = mDataPtr = nullptr;
   mCapacity = mLength = 0;
 }
 
 bool
 ArrayBufferBuilder::setCapacity(uint32_t aNewCap)
 {
-  uint8_t *newdata = (uint8_t *) JS_ReallocateArrayBufferContents(nullptr, aNewCap, mDataPtr, mCapacity);
-  if (!newdata) {
+  if (!JS_ReallocateArrayBufferContents(nullptr, aNewCap, &mRawContents, &mDataPtr)) {
     return false;
   }
 
-  mDataPtr = newdata;
   mCapacity = aNewCap;
   if (mLength > aNewCap) {
     mLength = aNewCap;
   }
 
   return true;
 }
 
@@ -3898,22 +3897,22 @@ ArrayBufferBuilder::getArrayBuffer(JSCon
   // we need to check for mLength == 0, because nothing may have been
   // added
   if (mCapacity > mLength || mLength == 0) {
     if (!setCapacity(mLength)) {
       return nullptr;
     }
   }
 
-  JSObject* obj = JS_NewArrayBufferWithContents(aCx, mLength, mDataPtr);
+  JSObject* obj = JS_NewArrayBufferWithContents(aCx, mRawContents);
   if (!obj) {
     return nullptr;
   }
 
-  mDataPtr = nullptr;
+  mRawContents = mDataPtr = nullptr;
   mLength = mCapacity = 0;
 
   return obj;
 }
 
 /* static */ bool
 ArrayBufferBuilder::areOverlappingRegions(const uint8_t* aStart1,
                                           uint32_t aLength1,
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -57,16 +57,17 @@ namespace mozilla {
 // based reallocation, up to an optional maximum growth given.
 //
 // When all the data has been appended, call getArrayBuffer,
 // passing in the JSContext* for which the ArrayBuffer object
 // is to be created.  This also implicitly resets the builder,
 // or it can be reset explicitly at any point by calling reset().
 class ArrayBufferBuilder
 {
+  void* mRawContents;
   uint8_t* mDataPtr;
   uint32_t mCapacity;
   uint32_t mLength;
 public:
   ArrayBufferBuilder();
   ~ArrayBufferBuilder();
 
   void reset();
--- a/content/media/webaudio/AudioBuffer.cpp
+++ b/content/media/webaudio/AudioBuffer.cpp
@@ -193,21 +193,22 @@ static already_AddRefed<ThreadSharedFloa
 StealJSArrayDataIntoThreadSharedFloatArrayBufferList(JSContext* aJSContext,
                                                      const nsTArray<JSObject*>& aJSArrays)
 {
   nsRefPtr<ThreadSharedFloatArrayBufferList> result =
     new ThreadSharedFloatArrayBufferList(aJSArrays.Length());
   for (uint32_t i = 0; i < aJSArrays.Length(); ++i) {
     JS::Rooted<JSObject*> arrayBuffer(aJSContext,
                                       JS_GetArrayBufferViewBuffer(aJSArrays[i]));
-    uint8_t* stolenData = arrayBuffer
-                          ? (uint8_t*) JS_StealArrayBufferContents(aJSContext, arrayBuffer)
-                          : nullptr;
-    if (stolenData) {
-      result->SetData(i, stolenData, reinterpret_cast<float*>(stolenData));
+    void* dataToFree = nullptr;
+    uint8_t* stolenData = nullptr;
+    if (arrayBuffer &&
+        JS_StealArrayBufferContents(aJSContext, arrayBuffer, &dataToFree,
+                                    &stolenData)) {
+      result->SetData(i, dataToFree, reinterpret_cast<float*>(stolenData));
     } else {
       return nullptr;
     }
   }
   return result.forget();
 }
 
 ThreadSharedFloatArrayBufferList*
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1401,16 +1401,17 @@ TypedObject::createUnattachedWithClass(J
     if (!obj)
         return nullptr;
 
     obj->setPrivate(nullptr);
     obj->initReservedSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(0));
     obj->initReservedSlot(JS_TYPEDOBJ_SLOT_BYTELENGTH, Int32Value(0));
     obj->initReservedSlot(JS_TYPEDOBJ_SLOT_OWNER, NullValue());
     obj->initReservedSlot(JS_TYPEDOBJ_SLOT_NEXT_VIEW, PrivateValue(nullptr));
+    obj->initReservedSlot(JS_TYPEDOBJ_SLOT_NEXT_BUFFER, PrivateValue(UNSET_BUFFER_LINK));
     obj->initReservedSlot(JS_TYPEDOBJ_SLOT_LENGTH, Int32Value(length));
     obj->initReservedSlot(JS_TYPEDOBJ_SLOT_TYPE_DESCR, ObjectValue(*type));
 
     // Tag the type object for this instance with the type
     // representation, if that has not been done already.
     if (cx->typeInferenceEnabled() && !type->is<SimpleTypeDescr>()) {
         // FIXME Bug 929651           ^~~~~~~~~~~~~~~~~~~~~~~~~~~
         RootedTypeObject typeObj(cx, obj->getType(cx));
@@ -1502,17 +1503,17 @@ TypedObject::createZeroed(JSContext *cx,
       case TypeDescr::Scalar:
       case TypeDescr::Reference:
       case TypeDescr::Struct:
       case TypeDescr::X4:
       case TypeDescr::SizedArray:
       {
         size_t totalSize = descr->as<SizedTypeDescr>().size();
         Rooted<ArrayBufferObject*> buffer(cx);
-        buffer = ArrayBufferObject::create(cx, totalSize);
+        buffer = ArrayBufferObject::create(cx, totalSize, false);
         if (!buffer)
             return nullptr;
         typeRepr->asSized()->initInstance(cx->runtime(), buffer->dataPointer(), 1);
         obj->attach(*buffer, 0);
         return obj;
       }
 
       case TypeDescr::UnsizedArray:
@@ -1523,17 +1524,17 @@ TypedObject::createZeroed(JSContext *cx,
         int32_t totalSize;
         if (!SafeMul(elementTypeRepr->size(), length, &totalSize)) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                                  JSMSG_TYPEDOBJECT_TOO_BIG);
             return nullptr;
         }
 
         Rooted<ArrayBufferObject*> buffer(cx);
-        buffer = ArrayBufferObject::create(cx, totalSize);
+        buffer = ArrayBufferObject::create(cx, totalSize, false);
         if (!buffer)
             return nullptr;
 
         if (length)
             elementTypeRepr->initInstance(cx->runtime(), buffer->dataPointer(), length);
         obj->attach(*buffer, 0);
         return obj;
       }
@@ -1717,20 +1718,21 @@ TypedObject::obj_defineProperty(JSContex
     return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs);
 }
 
 bool
 TypedObject::obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v,
                                PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
-    Rooted<jsid> id(cx);
-    if (!IndexToId(cx, index, &id))
+
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
         return false;
-    return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs);
+    return baseops::DefineElement(cx, delegate, index, v, getter, setter, attrs);
 }
 
 bool
 TypedObject::obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
                            HandleId id, MutableHandleValue vp)
 {
     JS_ASSERT(obj->is<TypedObject>());
     Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
--- a/js/src/builtin/TypedObjectConstants.h
+++ b/js/src/builtin/TypedObjectConstants.h
@@ -104,23 +104,24 @@
 
 ///////////////////////////////////////////////////////////////////////////
 // Slots for typed objects
 
 #define JS_TYPEDOBJ_SLOT_BYTEOFFSET       0
 #define JS_TYPEDOBJ_SLOT_BYTELENGTH       1
 #define JS_TYPEDOBJ_SLOT_OWNER            2
 #define JS_TYPEDOBJ_SLOT_NEXT_VIEW        3
-
-#define JS_DATAVIEW_SLOTS              4 // Number of slots for data views
+#define JS_TYPEDOBJ_SLOT_NEXT_BUFFER      4
 
-#define JS_TYPEDOBJ_SLOT_LENGTH           4 // Length of array (see (*) below)
-#define JS_TYPEDOBJ_SLOT_TYPE_DESCR       5 // For typed objects, type descr
+#define JS_DATAVIEW_SLOTS              5 // Number of slots for data views
+
+#define JS_TYPEDOBJ_SLOT_LENGTH           5 // Length of array (see (*) below)
+#define JS_TYPEDOBJ_SLOT_TYPE_DESCR       6 // For typed objects, type descr
 
 #define JS_TYPEDOBJ_SLOT_DATA             7 // private slot, based on alloc kind
-#define JS_TYPEDOBJ_SLOTS                 6 // Number of slots for typed objs
+#define JS_TYPEDOBJ_SLOTS                 7 // Number of slots for typed objs
 
 // (*) The JS_TYPEDOBJ_SLOT_LENGTH slot stores the length for typed objects of
 // sized and unsized array type. The slot contains 0 for non-arrays.
 // The slot also contains 0 for *unattached* typed objects, no matter what
 // type they have.
 
 #endif
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -272,16 +272,34 @@ void
 js::Nursery::notifyInitialSlots(Cell *cell, HeapSlot *slots)
 {
     if (isInside(cell) && !isInside(slots)) {
         /* If this put fails, we will only leak the slots. */
         (void)hugeSlots.put(slots);
     }
 }
 
+void
+js::Nursery::notifyNewElements(gc::Cell *cell, ObjectElements *elements)
+{
+    JS_ASSERT(!isInside(elements));
+    notifyInitialSlots(cell, reinterpret_cast<HeapSlot *>(elements));
+}
+
+void
+js::Nursery::notifyRemovedElements(gc::Cell *cell, ObjectElements *oldElements)
+{
+    JS_ASSERT(cell);
+    JS_ASSERT(oldElements);
+    JS_ASSERT(!isInside(oldElements));
+
+    if (isInside(cell))
+        hugeSlots.remove(reinterpret_cast<HeapSlot *>(oldElements));
+}
+
 namespace js {
 namespace gc {
 
 class MinorCollectionTracer : public JSTracer
 {
   public:
     Nursery *nursery;
     AutoTraceSession session;
@@ -596,16 +614,35 @@ js::Nursery::moveElementsToTenured(JSObj
 
     /* TODO Bug 874151: Prefer to put element data inline if we have space. */
     if (!isInside(srcHeader)) {
         JS_ASSERT(src->elements == dst->elements);
         hugeSlots.remove(reinterpret_cast<HeapSlot*>(srcHeader));
         return 0;
     }
 
+    /* ArrayBuffer stores byte-length, not Value count. */
+    if (src->is<ArrayBufferObject>()) {
+        size_t nbytes;
+        if (src->hasDynamicElements()) {
+            nbytes = sizeof(ObjectElements) + srcHeader->initializedLength;
+            dstHeader = static_cast<ObjectElements *>(zone->malloc_(nbytes));
+            if (!dstHeader)
+                CrashAtUnhandlableOOM("Failed to allocate array buffer elements while tenuring.");
+        } else {
+            dst->setFixedElements();
+            nbytes = GetGCKindSlots(dst->tenuredGetAllocKind()) * sizeof(HeapSlot);
+            dstHeader = dst->getElementsHeader();
+        }
+        js_memcpy(dstHeader, srcHeader, nbytes);
+        setElementsForwardingPointer(srcHeader, dstHeader, nbytes / sizeof(HeapSlot));
+        dst->elements = dstHeader->elements();
+        return src->hasDynamicElements() ? nbytes : 0;
+    }
+
     size_t nslots = ObjectElements::VALUES_PER_HEADER + srcHeader->capacity;
 
     /* Unlike other objects, Arrays can have fixed elements. */
     if (src->is<ArrayObject>() && nslots <= GetGCKindSlots(dstKind)) {
         dst->setFixedElements();
         dstHeader = dst->getElementsHeader();
         js_memcpy(dstHeader, srcHeader, nslots * sizeof(HeapSlot));
         setElementsForwardingPointer(srcHeader, dstHeader, nslots);
@@ -741,17 +778,17 @@ js::Nursery::collect(JSRuntime *rt, JS::
     TIME_START(collectToFP);
     TenureCountCache tenureCounts;
     collectToFixedPoint(&trc, tenureCounts);
     TIME_END(collectToFP);
 
     // Update the array buffer object's view lists.
     TIME_START(sweepArrayBufferViewList);
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
-        if (!c->gcLiveArrayBuffers.empty())
+        if (c->gcLiveArrayBuffers)
             ArrayBufferObject::sweep(c);
     }
     TIME_END(sweepArrayBufferViewList);
 
     // Update any slot or element pointers whose destination has been tenured.
     TIME_START(updateJitActivations);
 #ifdef JS_ION
     js::jit::UpdateJitActivationsForMinorGC(rt, &trc);
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -99,16 +99,22 @@ class Nursery
                                        uint32_t oldCount, uint32_t newCount);
 
     /* Free a slots array. */
     void freeSlots(JSContext *cx, HeapSlot *slots);
 
     /* Add a slots to our tracking list if it is out-of-line. */
     void notifyInitialSlots(gc::Cell *cell, HeapSlot *slots);
 
+    /* Add elements to our tracking list if it is out-of-line. */
+    void notifyNewElements(gc::Cell *cell, ObjectElements *elements);
+
+    /* Remove elements to our tracking list if it is out-of-line. */
+    void notifyRemovedElements(gc::Cell *cell, ObjectElements *oldElements);
+
     typedef Vector<types::TypeObject *, 0, SystemAllocPolicy> TypeObjectList;
 
     /*
      * Do a minor collection, optionally specifying a list to store types which
      * should be pretenured afterwards.
      */
     void collect(JSRuntime *rt, JS::gcreason::Reason reason, TypeObjectList *pretenureTypes);
 
--- a/js/src/jsapi-tests/testArrayBuffer.cpp
+++ b/js/src/jsapi-tests/testArrayBuffer.cpp
@@ -46,17 +46,18 @@ BEGIN_TEST(testArrayBuffer_bug720949_ste
         // Modifying the underlying data should update the value returned through the view
         uint8_t *data = JS_GetStableArrayBufferData(cx, obj);
         CHECK(data != nullptr);
         *reinterpret_cast<uint32_t*>(data) = MAGIC_VALUE_2;
         CHECK(JS_GetElement(cx, view, 0, &v));
         CHECK_SAME(v, INT_TO_JSVAL(MAGIC_VALUE_2));
 
         // Steal the contents
-        void *contents = JS_StealArrayBufferContents(cx, obj);
+        void *contents;
+        CHECK(JS_StealArrayBufferContents(cx, obj, &contents, &data));
         CHECK(contents != nullptr);
         CHECK(data != nullptr);
 
         // Check that the original ArrayBuffer is neutered
         CHECK_EQUAL(JS_GetArrayBufferByteLength(obj), 0);
         CHECK(JS_GetProperty(cx, obj, "byteLength", &v));
         CHECK_SAME(v, INT_TO_JSVAL(0));
         CHECK(JS_GetProperty(cx, view, "byteLength", &v));
@@ -66,17 +67,17 @@ BEGIN_TEST(testArrayBuffer_bug720949_ste
         CHECK(JS_GetProperty(cx, view, "length", &v));
         CHECK_SAME(v, INT_TO_JSVAL(0));
         CHECK_EQUAL(JS_GetArrayBufferByteLength(obj), 0);
         v = JSVAL_VOID;
         JS_GetElement(cx, obj, 0, &v);
         CHECK_SAME(v, JSVAL_VOID);
 
         // Transfer to a new ArrayBuffer
-        JS::RootedObject dst(cx, JS_NewArrayBufferWithContents(cx, size, contents));
+        JS::RootedObject dst(cx, JS_NewArrayBufferWithContents(cx, contents));
         CHECK(JS_IsArrayBufferObject(dst));
         data = JS_GetStableArrayBufferData(cx, obj);
 
         JS::RootedObject dstview(cx, JS_NewInt32ArrayWithBuffer(cx, dst, 0, -1));
         CHECK(dstview != nullptr);
 
         CHECK_EQUAL(JS_GetArrayBufferByteLength(dst), size);
         data = JS_GetStableArrayBufferData(cx, dst);
@@ -99,18 +100,21 @@ BEGIN_TEST(testArrayBuffer_bug720949_vie
     buffer = JS_NewArrayBuffer(cx, 2000);
     buffer = nullptr;
     GC(cx);
 
     // One view.
     {
         buffer = JS_NewArrayBuffer(cx, 2000);
         JS::RootedObject view(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, 0, -1));
-        void *contents = JS_StealArrayBufferContents(cx, buffer);
+        void *contents;
+        uint8_t *data;
+        CHECK(JS_StealArrayBufferContents(cx, buffer, &contents, &data));
         CHECK(contents != nullptr);
+        CHECK(data != nullptr);
         JS_free(nullptr, contents);
         GC(cx);
         CHECK(isNeutered(view));
         CHECK(isNeutered(buffer));
         view = nullptr;
         GC(cx);
         buffer = nullptr;
         GC(cx);
@@ -124,18 +128,21 @@ BEGIN_TEST(testArrayBuffer_bug720949_vie
         JS::RootedObject view2(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, 1, 200));
 
         // Remove, re-add a view
         view2 = nullptr;
         GC(cx);
         view2 = JS_NewUint8ArrayWithBuffer(cx, buffer, 1, 200);
 
         // Neuter
-        void *contents = JS_StealArrayBufferContents(cx, buffer);
+        void *contents;
+        uint8_t *data;
+        CHECK(JS_StealArrayBufferContents(cx, buffer, &contents, &data));
         CHECK(contents != nullptr);
+        CHECK(data != nullptr);
         JS_free(nullptr, contents);
 
         CHECK(isNeutered(view1));
         CHECK(isNeutered(view2));
         CHECK(isNeutered(buffer));
 
         view1 = nullptr;
         GC(cx);
--- a/js/src/jsapi-tests/testMappedArrayBuffer.cpp
+++ b/js/src/jsapi-tests/testMappedArrayBuffer.cpp
@@ -59,21 +59,21 @@ BEGIN_TEST(testMappedArrayBuffer_bug9451
     test_file.remove();
 
     return true;
 }
 
 JSObject *CreateNewObject(const int offset, const int length)
 {
     int fd = open(test_filename, O_RDONLY);
+    void *ptr;
     int new_fd;
-    void *ptr = JS_CreateMappedArrayBufferContents(fd, &new_fd, offset, length);
-    if (!ptr)
+    if (!JS_CreateMappedArrayBufferContents(fd, &new_fd, offset, length, &ptr))
         return nullptr;
-    JSObject *obj = JS_NewArrayBufferWithContents(cx, length, ptr, /* mapped = */ true);
+    JSObject *obj = JS_NewArrayBufferWithContents(cx, ptr);
     close(fd);
 
     return obj;
 }
 
 // Return the fd from object created in the stack.
 int GetNewObjectFD()
 {
@@ -104,19 +104,19 @@ bool TestCreateObject(const int offset, 
     CHECK(VerifyObject(obj, offset, length));
 
     return true;
 }
 
 bool TestReleaseContents()
 {
     int fd = open(test_filename, O_RDONLY);
+    void *ptr;
     int new_fd;
-    void *ptr = JS_CreateMappedArrayBufferContents(fd, &new_fd, 0, 12);
-    if (!ptr)
+    if (!JS_CreateMappedArrayBufferContents(fd, &new_fd, 0, 12, &ptr))
         return false;
     CHECK(fd_is_valid(new_fd));
     JS_ReleaseMappedArrayBufferContents(new_fd, ptr, 12);
     CHECK(!fd_is_valid(new_fd));
     close(fd);
 
     return true;
 }
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3114,58 +3114,67 @@ JS_ClearNonGlobalObject(JSContext *cx, J
 /*
  * Assign 'undefined' to all of the object's non-reserved slots. Note: this is
  * done for all slots, regardless of the associated property descriptor.
  */
 JS_PUBLIC_API(void)
 JS_SetAllNonReservedSlotsToUndefined(JSContext *cx, JSObject *objArg);
 
 /*
- * Create a new array buffer with the given contents. The new array buffer
- * takes ownership: after calling this function, do not free |contents| or use
- * |contents| from another thread. |mapped| indicates whether the contents were
- * created using JS_CreateMappedArrayBufferContents.
+ * Create a new array buffer with the given contents, which must have been
+ * returned by JS_AllocateArrayBufferContents or JS_StealArrayBufferContents.
+ * The new array buffer takes ownership. After calling this function, do not
+ * free |contents| or use |contents| from another thread.
  */
 extern JS_PUBLIC_API(JSObject *)
-JS_NewArrayBufferWithContents(JSContext *cx, size_t nbytes, void *contents, bool mapped = false);
+JS_NewArrayBufferWithContents(JSContext *cx, void *contents);
 
 /*
  * Steal the contents of the given array buffer. The array buffer has its
  * length set to 0 and its contents array cleared. The caller takes ownership
- * of the return value and must free it or transfer ownership via
+ * of |*contents| and must free it or transfer ownership via
  * JS_NewArrayBufferWithContents when done using it.
+ * To free |*contents|, call free().
+ * A pointer to the buffer's data is returned in |*data|. This pointer can
+ * be used until |*contents| is freed or has its ownership transferred.
  */
-extern JS_PUBLIC_API(void *)
-JS_StealArrayBufferContents(JSContext *cx, JS::HandleObject obj);
+extern JS_PUBLIC_API(bool)
+JS_StealArrayBufferContents(JSContext *cx, JS::HandleObject obj, void **contents, uint8_t **data);
 
 /*
  * Allocate memory that may be eventually passed to
  * JS_NewArrayBufferWithContents. |maybecx| is optional; if a non-nullptr cx is
  * given, it will be used for memory accounting and OOM reporting. |nbytes| is
- * the number of payload bytes required.
+ * the number of payload bytes required. The pointer to pass to
+ * JS_NewArrayBufferWithContents is returned in |contents|. The pointer to the
+ * |nbytes| of usable memory is returned in |data|. (*|contents| will contain a
+ * header before |data|.) The only legal operations on *|contents| are to pass
+ * it to either JS_NewArrayBufferWithContents or
+ * JS_ReallocateArrayBufferContents, or free it with js_free or JS_free.
  */
-extern JS_PUBLIC_API(void *)
-JS_AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes);
+extern JS_PUBLIC_API(bool)
+JS_AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void **contents, uint8_t **data);
 
 /*
  * Reallocate memory allocated by JS_AllocateArrayBufferContents, growing or
- * shrinking it as appropriate. If oldContents is null then this behaves like
- * JS_AllocateArrayBufferContents.
+ * shrinking it as appropriate.  The new data pointer will be returned in data.
+ * If *contents is nullptr, behaves like JS_AllocateArrayBufferContents.
  */
-extern JS_PUBLIC_API(void *)
-JS_ReallocateArrayBufferContents(JSContext *cx, uint32_t nbytes, void *oldContents, uint32_t oldNbytes);
+extern JS_PUBLIC_API(bool)
+JS_ReallocateArrayBufferContents(JSContext *cx, uint32_t nbytes, void **contents, uint8_t **data);
 
 /*
  * Create memory mapped array buffer contents.
  * For cloning, the fd will not be closed after mapping, and the caller must
  * take care of closing fd after calling this function.
  * A new duplicated fd used by the mapping is returned in new_fd.
  */
-extern JS_PUBLIC_API(void *)
-JS_CreateMappedArrayBufferContents(int fd, int *new_fd, size_t offset, size_t length);
+extern JS_PUBLIC_API(bool)
+JS_CreateMappedArrayBufferContents(int fd, int *new_fd, size_t offset,
+                                   size_t length, void **contents);
 
 /*
  * Release the allocated resource of mapped array buffer contents before the
  * object is created.
  * If a new object has been created by JS_NewArrayBufferWithContents() with
  * this content, then JS_NeuterArrayBuffer() should be used instead to release
  * the resource used by the object.
  */
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -53,16 +53,17 @@ JSCompartment::JSCompartment(Zone *zone,
     objectMetadataCallback(nullptr),
     lastAnimationTime(0),
     regExps(runtime_),
     typeReprs(runtime_),
     globalWriteBarriered(false),
     propertyTree(thisForCtor()),
     selfHostingScriptSource(nullptr),
     gcIncomingGrayPointers(nullptr),
+    gcLiveArrayBuffers(nullptr),
     gcWeakMapList(nullptr),
     debugModeBits(runtime_->debugMode ? DebugFromC : 0),
     rngState(0),
     watchpointMap(nullptr),
     scriptCountsMap(nullptr),
     debugScriptMap(nullptr),
     debugScopes(nullptr),
     enumerators(nullptr),
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -275,18 +275,18 @@ struct JSCompartment
      * During GC, stores the head of a list of incoming pointers from gray cells.
      *
      * The objects in the list are either cross-compartment wrappers, or
      * debugger wrapper objects.  The list link is either in the second extra
      * slot for the former, or a special slot for the latter.
      */
     JSObject                     *gcIncomingGrayPointers;
 
-    /* During GC, list of live array buffers with >1 view accumulated during tracing. */
-    js::ArrayBufferVector        gcLiveArrayBuffers;
+    /* Linked list of live array buffers with >1 view. */
+    js::ArrayBufferObject        *gcLiveArrayBuffers;
 
     /* Linked list of live weakmaps in this compartment. */
     js::WeakMapBase              *gcWeakMapList;
 
   private:
     enum {
         DebugFromC = 1 << 0,
         DebugFromJS = 1 << 1,
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2996,17 +2996,17 @@ BeginMarkPhase(JSRuntime *rt)
         }
 
         zone->scheduledForDestruction = false;
         zone->maybeAlive = zone->hold;
         zone->setPreservingCode(false);
     }
 
     for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) {
-        JS_ASSERT(c->gcLiveArrayBuffers.empty());
+        JS_ASSERT(!c->gcLiveArrayBuffers);
         c->marked = false;
         if (ShouldPreserveJITCode(c, currentTime))
             c->zone()->setPreservingCode(true);
     }
 
     /*
      * Atoms are not in the cross-compartment map. So if there are any
      * zones that are not being collected, we are not allowed to collect
@@ -4248,17 +4248,17 @@ EndSweepPhase(JSRuntime *rt, JSGCInvocat
                          !zone->allocator.arenas.arenaListsToSweep[i]);
         }
 #endif
     }
 
 #ifdef DEBUG
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
         JS_ASSERT(!c->gcIncomingGrayPointers);
-        JS_ASSERT(c->gcLiveArrayBuffers.empty());
+        JS_ASSERT(!c->gcLiveArrayBuffers);
 
         for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
             if (e.front().key().kind != CrossCompartmentKey::StringWrapper)
                 AssertNotOnGrayList(&e.front().value().get().toObject());
         }
     }
 #endif
 
@@ -4463,17 +4463,17 @@ ResetIncrementalGC(JSRuntime *rt, const 
       default:
         MOZ_ASSUME_UNREACHABLE("Invalid incremental GC state");
     }
 
     rt->gcStats.reset(reason);
 
 #ifdef DEBUG
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
-        JS_ASSERT(c->gcLiveArrayBuffers.empty());
+        JS_ASSERT(!c->gcLiveArrayBuffers);
 
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
         JS_ASSERT(!zone->needsBarrier());
         for (unsigned i = 0; i < FINALIZE_LIMIT; ++i)
             JS_ASSERT(!zone->allocator.arenas.arenaListsToSweep[i]);
     }
 #endif
 }
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -5895,17 +5895,29 @@ js_DumpBacktrace(JSContext *cx)
 void
 JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ObjectsExtraSizes *sizes)
 {
     if (hasDynamicSlots())
         sizes->mallocHeapSlots += mallocSizeOf(slots);
 
     if (hasDynamicElements()) {
         js::ObjectElements *elements = getElementsHeader();
-        sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(elements);
+        if (MOZ_UNLIKELY(elements->isAsmJSArrayBuffer())) {
+#if defined (JS_CPU_X64)
+            // On x64, ArrayBufferObject::prepareForAsmJS switches the
+            // ArrayBufferObject to use mmap'd storage.
+            sizes->nonHeapElementsAsmJS += as<ArrayBufferObject>().byteLength();
+#else
+            sizes->mallocHeapElementsAsmJS += mallocSizeOf(elements);
+#endif
+        } else if (MOZ_UNLIKELY(elements->isMappedArrayBuffer())) {
+            sizes->nonHeapElementsMapped += as<ArrayBufferObject>().byteLength();
+        } else {
+            sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(elements);
+        }
     }
 
     // Other things may be measured in the future if DMD indicates it is worthwhile.
     if (is<JSFunction>() ||
         is<JSObject>() ||
         is<ArrayObject>() ||
         is<CallObject>() ||
         is<RegExpObject>() ||
@@ -5922,18 +5934,16 @@ JSObject::addSizeOfExcludingThis(mozilla
         // - ( 1.0%, 96.4%): Proxy
 
     } else if (is<ArgumentsObject>()) {
         sizes->mallocHeapArgumentsData += as<ArgumentsObject>().sizeOfMisc(mallocSizeOf);
     } else if (is<RegExpStaticsObject>()) {
         sizes->mallocHeapRegExpStatics += as<RegExpStaticsObject>().sizeOfData(mallocSizeOf);
     } else if (is<PropertyIteratorObject>()) {
         sizes->mallocHeapPropertyIteratorData += as<PropertyIteratorObject>().sizeOfMisc(mallocSizeOf);
-    } else if (is<ArrayBufferObject>() || is<SharedArrayBufferObject>()) {
-        ArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, sizes);
 #ifdef JS_ION
     } else if (is<AsmJSModuleObject>()) {
         as<AsmJSModuleObject>().addSizeOfMisc(mallocSizeOf, &sizes->nonHeapCodeAsmJS,
                                               &sizes->mallocHeapAsmJSModuleData);
 #endif
 #ifdef JS_HAS_CTYPES
     } else {
         // This must be the last case.
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -522,17 +522,17 @@ JSObject::create(js::ExclusiveContext *c
     obj->type_.init(type);
     // Note: slots are created and assigned internally by NewGCObject.
     obj->elements = js::emptyObjectElements;
 
     if (clasp->hasPrivate())
         obj->privateRef(shape->numFixedSlots()) = nullptr;
 
     size_t span = shape->slotSpan();
-    if (span)
+    if (span && clasp != &js::ArrayBufferObject::class_)
         obj->initializeSlotRange(0, span);
 
     return obj;
 }
 
 /* static */ inline js::ArrayObject *
 JSObject::createArray(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
                       js::HandleShape shape, js::HandleTypeObject type,
@@ -568,20 +568,24 @@ JSObject::createArray(js::ExclusiveConte
     return &obj->as<js::ArrayObject>();
 }
 
 inline void
 JSObject::finish(js::FreeOp *fop)
 {
     if (hasDynamicSlots())
         fop->free_(slots);
-
     if (hasDynamicElements()) {
         js::ObjectElements *elements = getElementsHeader();
-        fop->free_(elements);
+        if (MOZ_UNLIKELY(elements->isAsmJSArrayBuffer()))
+            js::ArrayBufferObject::releaseAsmJSArrayBuffer(fop, this);
+        else if (MOZ_UNLIKELY(elements->isMappedArrayBuffer()))
+            js::ArrayBufferObject::releaseMappedArrayBuffer(fop, this);
+        else
+            fop->free_(elements);
     }
 }
 
 /* static */ inline bool
 JSObject::hasProperty(JSContext *cx, js::HandleObject obj,
                       js::HandleId id, bool *foundp, unsigned flags)
 {
     JS::RootedObject pobj(cx);
@@ -934,27 +938,16 @@ NewBuiltinClassInstance(ExclusiveContext
 {
     JSObject *obj = NewBuiltinClassInstance(cx, &T::class_, newKind);
     if (!obj)
         return nullptr;
 
     return &obj->as<T>();
 }
 
-template<typename T>
-inline T *
-NewBuiltinClassInstance(ExclusiveContext *cx, gc::AllocKind allocKind, NewObjectKind newKind = GenericObject)
-{
-    JSObject *obj = NewBuiltinClassInstance(cx, &T::class_, allocKind, newKind);
-    if (!obj)
-        return nullptr;
-
-    return &obj->as<T>();
-}
-
 // Used to optimize calls to (new Object())
 bool
 NewObjectScriptedCall(JSContext *cx, MutableHandleObject obj);
 
 /* Make an object with pregenerated shape from a NEWOBJECT bytecode. */
 static inline JSObject *
 CopyInitializerObject(JSContext *cx, HandleObject baseobj, NewObjectKind newKind = GenericObject)
 {
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1020,16 +1020,17 @@ static bool
 CacheEntry_setBytecode(JSContext *cx, HandleObject cache, uint8_t *buffer, uint32_t length)
 {
     JS_ASSERT(CacheEntry_isCacheEntry(cache));
     Rooted<ArrayBufferObject*> arrayBuffer(cx, ArrayBufferObject::create(cx, length, buffer));
 
     if (!arrayBuffer || !ArrayBufferObject::ensureNonInline(cx, arrayBuffer))
         return false;
 
+    memcpy(arrayBuffer->dataPointer(), buffer, length);
     SetReservedSlot(cache, CacheEntry_BYTECODE, OBJECT_TO_JSVAL(arrayBuffer));
     return true;
 }
 
 class AutoSaveFrameChain
 {
     JSContext *cx_;
     bool saved_;
@@ -1278,18 +1279,16 @@ Evaluate(JSContext *cx, unsigned argc, j
             } else if (!mozilla::PodEqual(loadBuffer, saveBuffer.get(), loadLength)) {
                 JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
                                      JSSMSG_CACHE_EQ_CONTENT_FAILED);
             }
         }
 
         if (!CacheEntry_setBytecode(cx, cacheEntry, saveBuffer, saveLength))
             return false;
-
-        saveBuffer.forget();
     }
 
     return JS_WrapValue(cx, args.rval());
 }
 
 static JSString *
 FileAsString(JSContext *cx, const char *pathname)
 {
@@ -6216,17 +6215,12 @@ main(int argc, char **argv, char **envp)
 
     KillWatchdog();
 
 #ifdef JS_THREADSAFE
     for (size_t i = 0; i < workerThreads.length(); i++)
         PR_JoinThread(workerThreads[i]);
 #endif
 
-#ifdef JSGC_GENERATIONAL
-    if (!noggc.empty())
-        noggc.destroy();
-#endif
-
     JS_DestroyRuntime(rt);
     JS_ShutDown();
     return result;
 }
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -28,36 +28,44 @@
 #endif
 #include "jswrapper.h"
 
 #include "gc/Barrier.h"
 #include "gc/Marking.h"
 #include "gc/Memory.h"
 #include "jit/AsmJS.h"
 #include "jit/AsmJSModule.h"
-#include "js/MemoryMetrics.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/NumericConversions.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/WrapperObject.h"
 
 #include "jsatominlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 #include "vm/Shape-inl.h"
 
-using mozilla::DebugOnly;
-
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 /*
+ * Allocate array buffers with the maximum number of fixed slots marked as
+ * reserved, so that the fixed slots may be used for the buffer's contents.
+ * The last fixed slot is kept for the object's private data.
+ */
+static const uint8_t ARRAYBUFFER_RESERVED_SLOTS = JSObject::MAX_FIXED_SLOTS - 1;
+
+// Sentinel value used to initialize ArrayBufferViewObjects' NEXT_BUFFER_SLOTs
+// to show that they have not yet been added to any ArrayBufferObject list.
+js::ArrayBufferObject * const js::UNSET_BUFFER_LINK = reinterpret_cast<js::ArrayBufferObject*>(0x2);
+
+/*
  * Convert |v| to an array index for an array of length |length| per
  * the Typed Array Specification section 7.0, |subarray|. If successful,
  * the output value is in the range [0, length].
  */
 bool
 js::ToClampedIndex(JSContext *cx, HandleValue v, uint32_t length, uint32_t *out)
 {
     int32_t result;
@@ -83,46 +91,71 @@ js::ToClampedIndex(JSContext *cx, Handle
  */
 
 /*
  * ArrayBufferObject (base)
  */
 
 const Class ArrayBufferObject::protoClass = {
     "ArrayBufferPrototype",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_RESERVED_SLOTS(ARRAYBUFFER_RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer),
     JS_PropertyStub,         /* addProperty */
     JS_DeletePropertyStub,   /* delProperty */
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub
 };
 
 const Class ArrayBufferObject::class_ = {
     "ArrayBuffer",
+    JSCLASS_HAS_PRIVATE |
     JSCLASS_IMPLEMENTS_BARRIERS |
-    JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer) |
-    JSCLASS_BACKGROUND_FINALIZE,
+    Class::NON_NATIVE |
+    JSCLASS_HAS_RESERVED_SLOTS(ARRAYBUFFER_RESERVED_SLOTS) |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer),
     JS_PropertyStub,         /* addProperty */
     JS_DeletePropertyStub,   /* delProperty */
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
-    ArrayBufferObject::finalize,
+    nullptr,        /* finalize    */
     nullptr,        /* call        */
     nullptr,        /* hasInstance */
     nullptr,        /* construct   */
     ArrayBufferObject::obj_trace,
     JS_NULL_CLASS_SPEC,
-    JS_NULL_CLASS_EXT
+    JS_NULL_CLASS_EXT,
+    {
+        ArrayBufferObject::obj_lookupGeneric,
+        ArrayBufferObject::obj_lookupProperty,
+        ArrayBufferObject::obj_lookupElement,
+        ArrayBufferObject::obj_defineGeneric,
+        ArrayBufferObject::obj_defineProperty,
+        ArrayBufferObject::obj_defineElement,
+        ArrayBufferObject::obj_getGeneric,
+        ArrayBufferObject::obj_getProperty,
+        ArrayBufferObject::obj_getElement,
+        ArrayBufferObject::obj_setGeneric,
+        ArrayBufferObject::obj_setProperty,
+        ArrayBufferObject::obj_setElement,
+        ArrayBufferObject::obj_getGenericAttributes,
+        ArrayBufferObject::obj_setGenericAttributes,
+        ArrayBufferObject::obj_deleteProperty,
+        ArrayBufferObject::obj_deleteElement,
+        nullptr, nullptr, /* watch/unwatch */
+        nullptr,          /* slice */
+        ArrayBufferObject::obj_enumerate,
+        nullptr,          /* thisObject      */
+    }
 };
 
 const JSFunctionSpec ArrayBufferObject::jsfuncs[] = {
     JS_FN("slice", ArrayBufferObject::fun_slice, 2, JSFUN_GENERIC_NATIVE),
     JS_FS_END
 };
 
 const JSFunctionSpec ArrayBufferObject::jsstaticfuncs[] = {
@@ -259,398 +292,447 @@ ArrayBufferObject::class_constructor(JSC
         return false;
     args.rval().setObject(*bufobj);
     return true;
 }
 
 /*
  * Note that some callers are allowed to pass in a nullptr cx, so we allocate
  * with the cx if available and fall back to the runtime.  If oldptr is given,
- * it's expected to be a previously-allocated contents pointer that we then
- * realloc.
+ * it's expected to be a previously-allocated ObjectElements* pointer that we
+ * then realloc.
  */
-static void *
-AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void *oldptr = nullptr, size_t oldnbytes = 0)
+static ObjectElements *
+AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void *oldptr = nullptr)
 {
-    void *p;
+    uint32_t size = nbytes + sizeof(ObjectElements);
+    ObjectElements *newheader;
 
     // if oldptr is given, then we need to do a realloc
     if (oldptr) {
-        p = maybecx ? maybecx->runtime()->reallocCanGC(oldptr, nbytes) : js_realloc(oldptr, nbytes);
+        ObjectElements *oldheader = static_cast<ObjectElements *>(oldptr);
+        uint32_t oldnbytes = ArrayBufferObject::headerInitializedLength(oldheader);
+
+        void *p = maybecx ? maybecx->runtime()->reallocCanGC(oldptr, size) : js_realloc(oldptr, size);
+        newheader = static_cast<ObjectElements *>(p);
 
         // if we grew the array, we need to set the new bytes to 0
-        if (p && nbytes > oldnbytes)
-            memset(reinterpret_cast<uint8_t *>(p) + oldnbytes, 0, nbytes - oldnbytes);
+        if (newheader && nbytes > oldnbytes)
+            memset(reinterpret_cast<uint8_t*>(newheader->elements()) + oldnbytes, 0, nbytes - oldnbytes);
     } else {
-        p = maybecx ? maybecx->runtime()->callocCanGC(nbytes) : js_calloc(nbytes);
+        void *p = maybecx ? maybecx->runtime()->callocCanGC(size) : js_calloc(size);
+        newheader = static_cast<ObjectElements *>(p);
+    }
+    if (!newheader) {
+        if (maybecx)
+            js_ReportOutOfMemory(maybecx);
+        return nullptr;
     }
 
-    if (!p && maybecx)
-        js_ReportOutOfMemory(maybecx);
-
-    return p;
-}
+    ArrayBufferObject::updateElementsHeader(newheader, nbytes);
 
-ArrayBufferViewObject *
-ArrayBufferObject::viewList() const
-{
-    return reinterpret_cast<ArrayBufferViewObject *>(getSlot(VIEW_LIST_SLOT).toPrivate());
-}
-
-void
-ArrayBufferObject::setViewListNoBarrier(ArrayBufferViewObject *viewsHead)
-{
-    setSlot(VIEW_LIST_SLOT, PrivateValue(viewsHead));
+    return newheader;
 }
 
-void
-ArrayBufferObject::setViewList(ArrayBufferViewObject *viewsHead)
+// The list of views must be stored somewhere in the ArrayBufferObject, but
+// the slots are already being used for the element storage and the private
+// field is used for a delegate object. The ObjectElements header has space
+// for it, but I don't want to mess around with adding unions to it with
+// JS_USE_NEW_OBJECT_REPRESENTATION pending, since it will solve this much
+// more cleanly.
+struct OldObjectRepresentationHack {
+    uint32_t flags;
+    uint32_t initializedLength;
+    EncapsulatedPtr<ArrayBufferViewObject> views;
+};
+
+static ArrayBufferViewObject *
+GetViewList(ArrayBufferObject *obj)
 {
-    if (ArrayBufferViewObject *oldHead = viewList())
-        ArrayBufferViewObject::writeBarrierPre(oldHead);
-    setViewListNoBarrier(viewsHead);
-    PostBarrierTypedArrayObject(this);
+    return reinterpret_cast<OldObjectRepresentationHack*>(obj->getElementsHeader())->views;
 }
 
-bool
-ArrayBufferObject::canNeuter(JSContext *cx)
+static void
+SetViewList(ArrayBufferObject *obj, ArrayBufferViewObject *viewsHead)
 {
-    if (isSharedArrayBuffer())
-        return false;
+    reinterpret_cast<OldObjectRepresentationHack*>(obj->getElementsHeader())->views = viewsHead;
+    PostBarrierTypedArrayObject(obj);
+}
 
-    if (isAsmJSArrayBuffer()) {
-        if (!ArrayBufferObject::canNeuterAsmJSArrayBuffer(cx, *this))
-            return false;
-    }
-
-    return true;
+static void
+InitViewList(ArrayBufferObject *obj, ArrayBufferViewObject *viewsHead)
+{
+    reinterpret_cast<OldObjectRepresentationHack*>(obj->getElementsHeader())->views.init(viewsHead);
+    PostBarrierTypedArrayObject(obj);
 }
 
-/* static */ void
-ArrayBufferObject::neuter(JSContext *cx, Handle<ArrayBufferObject*> buffer)
+static EncapsulatedPtr<ArrayBufferViewObject> &
+GetViewListRef(ArrayBufferObject *obj)
 {
-    JS_ASSERT(buffer->canNeuter(cx));
+    JS_ASSERT(obj->runtimeFromMainThread()->isHeapBusy());
+    return reinterpret_cast<OldObjectRepresentationHack*>(obj->getElementsHeader())->views;
+}
 
-    // Neuter all views on the buffer, clear out the list of views and the
-    // buffer's data.
-
-    for (ArrayBufferViewObject *view = buffer->viewList(); view; view = view->nextView()) {
+/* static */ bool
+ArrayBufferObject::neuterViews(JSContext *cx, Handle<ArrayBufferObject*> buffer)
+{
+    ArrayBufferViewObject *view;
+    size_t numViews = 0;
+    for (view = GetViewList(buffer); view; view = view->nextView()) {
+        numViews++;
         view->neuter(cx);
 
         // Notify compiled jit code that the base pointer has moved.
         MarkObjectStateChange(cx, view);
     }
 
-    if (!buffer->isAsmJSArrayBuffer())
-        buffer->changeContents(cx, nullptr);
-
-    buffer->setByteLength(0);
-    buffer->setViewList(nullptr);
-    buffer->setIsNeutered();
+    // neuterAsmJSArrayBuffer adjusts state specific to the ArrayBuffer data
+    // itself, but it only affects the behavior of views
+    if (buffer->isAsmJSArrayBuffer()) {
+        if (!ArrayBufferObject::neuterAsmJSArrayBuffer(cx, *buffer))
+            return false;
+    }
 
-    // If this is happening during an incremental GC, remove the buffer from
-    // the list of live buffers with multiple views if necessary.
-    if (buffer->inLiveList()) {
-        ArrayBufferVector &gcLiveArrayBuffers = cx->compartment()->gcLiveArrayBuffers;
-        DebugOnly<bool> found = false;
-        for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) {
-            if (buffer == gcLiveArrayBuffers[i]) {
-                found = true;
-                gcLiveArrayBuffers[i] = gcLiveArrayBuffers.back();
-                gcLiveArrayBuffers.popBack();
-                break;
+    // Remove buffer from the list of buffers with > 1 view.
+    if (numViews > 1 && GetViewList(buffer)->bufferLink() != UNSET_BUFFER_LINK) {
+        ArrayBufferObject *prev = buffer->compartment()->gcLiveArrayBuffers;
+        if (prev == buffer) {
+            buffer->compartment()->gcLiveArrayBuffers = GetViewList(prev)->bufferLink();
+        } else {
+            for (ArrayBufferObject *b = GetViewList(prev)->bufferLink();
+                 b;
+                 b = GetViewList(b)->bufferLink())
+            {
+                if (b == buffer) {
+                    GetViewList(prev)->setBufferLink(GetViewList(b)->bufferLink());
+                    break;
+                }
+                prev = b;
             }
         }
-        JS_ASSERT(found);
-        buffer->setInLiveList(false);
     }
+
+    return true;
+}
+
+uint8_t *
+ArrayBufferObject::dataPointer() const {
+    if (isSharedArrayBuffer())
+        return (uint8_t *)this->as<SharedArrayBufferObject>().dataPointer();
+    return (uint8_t *)elements;
 }
 
 void
-ArrayBufferObject::changeContents(JSContext *cx, void *newData)
+ArrayBufferObject::changeContents(JSContext *cx, ObjectElements *newHeader)
 {
     JS_ASSERT(!isAsmJSArrayBuffer());
     JS_ASSERT(!isSharedArrayBuffer());
-    JS_ASSERT_IF(isMappedArrayBuffer(), !newData);
+
+    // Grab out data before invalidating it.
+    uint32_t byteLengthCopy = byteLength();
+    uintptr_t oldDataPointer = uintptr_t(dataPointer());
+    ArrayBufferViewObject *viewListHead = GetViewList(this);
 
     // Update all views.
-    ArrayBufferViewObject *viewListHead = viewList();
+    uintptr_t newDataPointer = uintptr_t(newHeader->elements());
     for (ArrayBufferViewObject *view = viewListHead; view; view = view->nextView()) {
         // Watch out for NULL data pointers in views. This either
         // means that the view is not fully initialized (in which case
         // it'll be initialized later with the correct pointer) or
         // that the view has been neutered. In that case, the buffer
         // is "en route" to being neutered but the isNeuteredBuffer()
         // flag may not yet be set.
-        uint8_t *viewDataPointer = view->dataPointer();
+        uint8_t *viewDataPointer = static_cast<uint8_t*>(view->getPrivate());
         if (viewDataPointer) {
-            JS_ASSERT(newData);
-            viewDataPointer += static_cast<uint8_t *>(newData) - dataPointer();
+            viewDataPointer += newDataPointer - oldDataPointer;
             view->setPrivate(viewDataPointer);
         }
 
         // Notify compiled jit code that the base pointer has moved.
         MarkObjectStateChange(cx, view);
     }
 
-    if (ownsData())
-        releaseData(cx->runtime()->defaultFreeOp());
+    // The list of views in the old header is reachable if the contents are
+    // being transferred, so null it out
+    SetViewList(this, nullptr);
+
+#ifdef JSGC_GENERATIONAL
+    ObjectElements *oldHeader = ObjectElements::fromElements(elements);
+    JS_ASSERT(oldHeader != newHeader);
+    JSRuntime *rt = runtimeFromMainThread();
+    if (hasDynamicElements())
+        rt->gcNursery.notifyRemovedElements(this, oldHeader);
+#endif
+
+    elements = newHeader->elements();
+
+#ifdef JSGC_GENERATIONAL
+    if (hasDynamicElements())
+        rt->gcNursery.notifyNewElements(this, newHeader);
+#endif
+
+    initElementsHeader(newHeader, byteLengthCopy);
+    InitViewList(this, viewListHead);
+}
+
+void
+ArrayBufferObject::neuter(JSContext *cx)
+{
+    JS_ASSERT(!isSharedArrayBuffer());
 
-    setDataPointer(static_cast<uint8_t *>(newData), OwnsData);
+    JS_ASSERT(cx);
+    if (isMappedArrayBuffer()) {
+        releaseMappedArrayBuffer(nullptr, this);
+        setFixedElements();
+    } else if (hasDynamicElements() && !isAsmJSArrayBuffer()) {
+        ObjectElements *oldHeader = getElementsHeader();
+        changeContents(cx, ObjectElements::fromElements(fixedElements()));
+
+        FreeOp fop(cx->runtime(), false);
+        fop.free_(oldHeader);
+    }
+
+    uint32_t byteLen = 0;
+    updateElementsHeader(getElementsHeader(), byteLen);
+
+    getElementsHeader()->setIsNeuteredBuffer();
+}
+
+/* static */ bool
+ArrayBufferObject::ensureNonInline(JSContext *cx, Handle<ArrayBufferObject*> buffer)
+{
+    JS_ASSERT(!buffer->isSharedArrayBuffer());
+    if (buffer->hasDynamicElements())
+        return true;
+
+    ObjectElements *newHeader = AllocateArrayBufferContents(cx, buffer->byteLength());
+    if (!newHeader)
+        return false;
+
+    void *newHeaderDataPointer = reinterpret_cast<void*>(newHeader->elements());
+    memcpy(newHeaderDataPointer, buffer->dataPointer(), buffer->byteLength());
+
+    buffer->changeContents(cx, newHeader);
+    return true;
 }
 
 #if defined(JS_CPU_X64)
 // Refer to comment above AsmJSMappedSize in AsmJS.h.
+JS_STATIC_ASSERT(sizeof(ObjectElements) < AsmJSPageSize);
 JS_STATIC_ASSERT(AsmJSAllocationGranularity == AsmJSPageSize);
 #endif
 
 #if defined(JS_ION) && defined(JS_CPU_X64)
-/* static */ bool
+bool
 ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer)
 {
     if (buffer->isAsmJSArrayBuffer())
         return true;
 
     // SharedArrayBuffers are already created with AsmJS support in mind.
     if (buffer->isSharedArrayBuffer())
         return true;
 
     // Get the entire reserved region (with all pages inaccessible).
-    void *data;
+    void *p;
 # ifdef XP_WIN
-    data = VirtualAlloc(nullptr, AsmJSMappedSize, MEM_RESERVE, PAGE_NOACCESS);
-    if (!data)
+    p = VirtualAlloc(nullptr, AsmJSMappedSize, MEM_RESERVE, PAGE_NOACCESS);
+    if (!p)
         return false;
 # else
-    data = mmap(nullptr, AsmJSMappedSize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
-    if (data == MAP_FAILED)
+    p = mmap(nullptr, AsmJSMappedSize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
+    if (p == MAP_FAILED)
         return false;
 # endif
 
     // Enable access to the valid region.
     JS_ASSERT(buffer->byteLength() % AsmJSAllocationGranularity == 0);
 # ifdef XP_WIN
-    if (!VirtualAlloc(data, buffer->byteLength(), MEM_COMMIT, PAGE_READWRITE)) {
-        VirtualFree(data, 0, MEM_RELEASE);
+    if (!VirtualAlloc(p, AsmJSPageSize + buffer->byteLength(), MEM_COMMIT, PAGE_READWRITE)) {
+        VirtualFree(p, 0, MEM_RELEASE);
         return false;
     }
 # else
-    if (mprotect(data, buffer->byteLength(), PROT_READ | PROT_WRITE)) {
-        munmap(data, AsmJSMappedSize);
+    if (mprotect(p, AsmJSPageSize + buffer->byteLength(), PROT_READ | PROT_WRITE)) {
+        munmap(p, AsmJSMappedSize);
         return false;
     }
 # endif
 
     // Copy over the current contents of the typed array.
+    uint8_t *data = reinterpret_cast<uint8_t*>(p) + AsmJSPageSize;
     memcpy(data, buffer->dataPointer(), buffer->byteLength());
 
     // Swap the new elements into the ArrayBufferObject.
-    buffer->changeContents(cx, data);
-    JS_ASSERT(data == buffer->dataPointer());
+    ObjectElements *newHeader = reinterpret_cast<ObjectElements*>(data - sizeof(ObjectElements));
+    ObjectElements *oldHeader = buffer->hasDynamicElements() ? buffer->getElementsHeader()
+                                                             : nullptr;
+    buffer->changeContents(cx, newHeader);
+    js_free(oldHeader);
 
     // Mark the ArrayBufferObject so (1) we don't do this again, (2) we know not
-    // to js_free the data in the normal way.
-    buffer->setIsAsmJSArrayBuffer();
-
+    // to js_free the header in the normal way.
+    newHeader->setIsAsmJSArrayBuffer();
+    JS_ASSERT(data == buffer->dataPointer());
     return true;
 }
 
 void
-ArrayBufferObject::releaseAsmJSArray(FreeOp *fop)
+ArrayBufferObject::releaseAsmJSArrayBuffer(FreeOp *fop, JSObject *obj)
 {
-    void *data = dataPointer();
+    ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
+    JS_ASSERT(buffer.isAsmJSArrayBuffer());
 
-    JS_ASSERT(uintptr_t(data) % AsmJSPageSize == 0);
+    uint8_t *p = buffer.dataPointer() - AsmJSPageSize ;
+    JS_ASSERT(uintptr_t(p) % AsmJSPageSize == 0);
 # ifdef XP_WIN
-    VirtualFree(data, 0, MEM_RELEASE);
+    VirtualFree(p, 0, MEM_RELEASE);
 # else
-    munmap(data, AsmJSMappedSize);
+    munmap(p, AsmJSMappedSize);
 # endif
 }
 #else  /* defined(JS_ION) && defined(JS_CPU_X64) */
 bool
 ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer)
 {
     if (buffer->isAsmJSArrayBuffer())
         return true;
 
     if (buffer->isSharedArrayBuffer())
         return true;
 
     if (!ensureNonInline(cx, buffer))
         return false;
 
-    buffer->setIsAsmJSArrayBuffer();
+    JS_ASSERT(buffer->hasDynamicElements());
+    buffer->getElementsHeader()->setIsAsmJSArrayBuffer();
     return true;
 }
 
 void
-ArrayBufferObject::releaseAsmJSArray(FreeOp *fop)
+ArrayBufferObject::releaseAsmJSArrayBuffer(FreeOp *fop, JSObject *obj)
 {
-    fop->free_(dataPointer());
+    fop->free_(obj->as<ArrayBufferObject>().getElementsHeader());
 }
 #endif
 
 bool
-ArrayBufferObject::canNeuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer)
+ArrayBufferObject::neuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer)
 {
     JS_ASSERT(!buffer.isSharedArrayBuffer());
 #ifdef JS_ION
     AsmJSActivation *act = cx->mainThread().asmJSActivationStackFromOwnerThread();
     for (; act; act = act->prevAsmJS()) {
         if (act->module().maybeHeapBufferObject() == &buffer)
             break;
     }
     if (!act)
         return true;
 
+    js_ReportOverRecursed(cx);
     return false;
 #else
     return true;
 #endif
 }
 
 void *
 ArrayBufferObject::createMappedArrayBuffer(int fd, int *new_fd, size_t offset, size_t length)
 {
-    void *ptr = AllocateMappedObject(fd, new_fd, offset, length, 8, sizeof(MappingInfoHeader));
+    void *ptr = AllocateMappedObject(fd, new_fd, offset, length, 8,
+                                     sizeof(MappingInfoHeader) + sizeof(ObjectElements));
     if (!ptr)
         return nullptr;
 
-    reinterpret_cast<MappingInfoHeader *>(ptr)->init(*new_fd, offset);
-    return static_cast<uint8_t *>(ptr) + sizeof(MappingInfoHeader);
+    ptr = reinterpret_cast<void *>(uintptr_t(ptr) + sizeof(MappingInfoHeader));
+    ObjectElements *header = reinterpret_cast<ObjectElements *>(ptr);
+    initMappedElementsHeader(header, *new_fd, offset, length);
+
+    return ptr;
 }
 
 void
-ArrayBufferObject::releaseMappedArray()
+ArrayBufferObject::releaseMappedArrayBuffer(FreeOp *fop, JSObject *obj)
 {
-    if (!isMappedArrayBuffer() || isNeutered())
+    ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
+    if(!buffer.isMappedArrayBuffer() || buffer.isNeutered())
         return;
 
-    DeallocateMappedObject(getMappingFD(), getMappingInfoHeader(), byteLength() + sizeof(MappingInfoHeader));
+    ObjectElements *header = buffer.getElementsHeader();
+    if (header)
+        DeallocateMappedObject(buffer.getMappingFD(), header, header->initializedLength);
 }
 
 void
 ArrayBufferObject::addView(ArrayBufferViewObject *view)
 {
+    // This view should never have been associated with a buffer before
+    JS_ASSERT(view->bufferLink() == UNSET_BUFFER_LINK);
+
     // Note that pre-barriers are not needed here because either the list was
     // previously empty, in which case no pointer is being overwritten, or the
     // list was nonempty and will be made weak during this call (and weak
     // pointers cannot violate the snapshot-at-the-beginning invariant.)
 
-    ArrayBufferViewObject *viewsHead = viewList();
+    ArrayBufferViewObject *viewsHead = GetViewList(this);
     if (viewsHead == nullptr) {
         // This ArrayBufferObject will have a single view at this point, so it
         // is a strong pointer (it will be marked during tracing.)
         JS_ASSERT(view->nextView() == nullptr);
     } else {
-        view->setNextView(viewsHead);
+        view->prependToViews(viewsHead);
     }
 
-    setViewList(view);
-}
-
-uint8_t *
-ArrayBufferObject::dataPointer() const
-{
-    if (isSharedArrayBuffer())
-        return (uint8_t *)this->as<SharedArrayBufferObject>().dataPointer();
-    return static_cast<uint8_t *>(getSlot(DATA_SLOT).toPrivate());
-}
-
-void
-ArrayBufferObject::releaseData(FreeOp *fop)
-{
-    JS_ASSERT(ownsData());
-
-    if (isAsmJSArrayBuffer())
-        releaseAsmJSArray(fop);
-    else if (isMappedArrayBuffer())
-        releaseMappedArray();
-    else
-        fop->free_(dataPointer());
-}
-
-void
-ArrayBufferObject::setDataPointer(void *data, OwnsState ownsData)
-{
-    setSlot(DATA_SLOT, PrivateValue(data));
-    setOwnsData(ownsData);
-}
-
-size_t
-ArrayBufferObject::byteLength() const
-{
-    return size_t(getSlot(BYTE_LENGTH_SLOT).toDouble());
-}
-
-void
-ArrayBufferObject::setByteLength(size_t length)
-{
-    setSlot(BYTE_LENGTH_SLOT, DoubleValue(length));
-}
-
-uint32_t
-ArrayBufferObject::flags() const
-{
-    return uint32_t(getSlot(FLAGS_SLOT).toInt32());
-}
-
-void
-ArrayBufferObject::setFlags(uint32_t flags)
-{
-    setSlot(FLAGS_SLOT, Int32Value(flags));
+    SetViewList(this, view);
 }
 
 ArrayBufferObject *
-ArrayBufferObject::create(JSContext *cx, uint32_t nbytes, void *data /* = nullptr */,
-                          NewObjectKind newKind /* = GenericObject */,
-                          bool mapped /* = false */)
+ArrayBufferObject::create(JSContext *cx, uint32_t nbytes, bool clear /* = true */,
+                          NewObjectKind newKind /* = GenericObject */)
 {
-    JS_ASSERT_IF(mapped, data);
-
-    // If we need to allocate data, try to use a larger object size class so
-    // that the array buffer's data can be allocated inline with the object.
-    size_t reservedSlots = JSCLASS_RESERVED_SLOTS(&class_);
-
-    size_t nslots = reservedSlots;
-    if (!data) {
-        size_t usableSlots = JSObject::MAX_FIXED_SLOTS - reservedSlots;
-        if (nbytes <= usableSlots * sizeof(Value)) {
-            int newSlots = (nbytes - 1) / sizeof(Value) + 1;
-            JS_ASSERT(int(nbytes) <= newSlots * int(sizeof(Value)));
-            nslots = reservedSlots + newSlots;
-        } else {
-            data = AllocateArrayBufferContents(cx, nbytes);
-            if (!data)
-                return nullptr;
-        }
-    }
-
-    JS_ASSERT(!(class_.flags & JSCLASS_HAS_PRIVATE));
-    gc::AllocKind allocKind = GetGCObjectKind(nslots);
-
-    Rooted<ArrayBufferObject*> obj(cx, NewBuiltinClassInstance<ArrayBufferObject>(cx, allocKind, newKind));
-    if (!obj) {
-        if (data)
-            js_free(data);
+    Rooted<ArrayBufferObject*> obj(cx, NewBuiltinClassInstance<ArrayBufferObject>(cx, newKind));
+    if (!obj)
         return nullptr;
-    }
+    JS_ASSERT_IF(obj->isTenured(), obj->tenuredGetAllocKind() == gc::FINALIZE_OBJECT16_BACKGROUND);
     JS_ASSERT(obj->getClass() == &class_);
 
-    JS_ASSERT(!gc::IsInsideNursery(cx->runtime(), obj));
+    js::Shape *empty = EmptyShape::getInitialShape(cx, &class_,
+                                                   obj->getProto(), obj->getParent(), obj->getMetadata(),
+                                                   gc::FINALIZE_OBJECT16_BACKGROUND);
+    if (!empty)
+        return nullptr;
+    obj->setLastPropertyInfallible(empty);
+
+    // ArrayBufferObjects delegate added properties to another JSObject, so
+    // their internal layout can use the object's fixed slots for storage.
+    // Set up the object to look like an array with an elements header.
+    JS_ASSERT(!obj->hasDynamicSlots());
+    JS_ASSERT(!obj->hasDynamicElements());
+
+    // The beginning stores an ObjectElements header structure holding the
+    // length. The rest of it is a flat data store for the array buffer.
+    size_t usableSlots = ARRAYBUFFER_RESERVED_SLOTS - ObjectElements::VALUES_PER_HEADER;
 
-    if (data) {
-        obj->initialize(nbytes, data, OwnsData);
-        if (mapped)
-            obj->setIsMappedArrayBuffer();
+    if (nbytes > sizeof(Value) * usableSlots) {
+        ObjectElements *header = AllocateArrayBufferContents(cx, nbytes);
+        if (!header)
+            return nullptr;
+        obj->elements = header->elements();
+
+#ifdef JSGC_GENERATIONAL
+        JSRuntime *rt = obj->runtimeFromMainThread();
+        rt->gcNursery.notifyNewElements(obj, header);
+#endif
+        obj->initElementsHeader(obj->getElementsHeader(), nbytes);
     } else {
-        void *data = &obj->fixedSlots()[reservedSlots];
-        memset(data, 0, nbytes);
-        obj->initialize(nbytes, data, DoesntOwnData);
+        // Elements header must be initialized before dataPointer() is callable.
+        obj->setFixedElements();
+        obj->initElementsHeader(obj->getElementsHeader(), nbytes);
+        if (clear)
+            memset(obj->dataPointer(), 0, nbytes);
     }
 
     return obj;
 }
 
 JSObject *
 ArrayBufferObject::createSlice(JSContext *cx, Handle<ArrayBufferObject*> arrayBuffer,
                                uint32_t begin, uint32_t end)
@@ -658,17 +740,17 @@ ArrayBufferObject::createSlice(JSContext
     JS_ASSERT(begin <= arrayBuffer->byteLength());
     JS_ASSERT(end <= arrayBuffer->byteLength());
     JS_ASSERT(begin <= end);
     uint32_t length = end - begin;
 
     if (!arrayBuffer->hasData())
         return create(cx, 0);
 
-    JSObject *slice = create(cx, length);
+    JSObject *slice = create(cx, length, false);
     if (!slice)
         return nullptr;
     memcpy(slice->as<ArrayBufferObject>().dataPointer(), arrayBuffer->dataPointer() + begin, length);
     return slice;
 }
 
 bool
 ArrayBufferObject::createDataViewForThisImpl(JSContext *cx, CallArgs args)
@@ -697,211 +779,428 @@ ArrayBufferObject::createDataViewForThis
 bool
 ArrayBufferObject::createDataViewForThis(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsArrayBuffer, createDataViewForThisImpl>(cx, args);
 }
 
 /* static */ bool
-ArrayBufferObject::ensureNonInline(JSContext *cx, Handle<ArrayBufferObject*> buffer)
+ArrayBufferObject::stealContents(JSContext *cx, Handle<ArrayBufferObject*> buffer, void **contents,
+                                 uint8_t **data)
 {
-    if (!buffer->ownsData()) {
-        void *data = AllocateArrayBufferContents(cx, buffer->byteLength());
-        if (!data)
+    // If the ArrayBuffer's elements are dynamically allocated and nothing else
+    // prevents us from stealing them, transfer ownership directly.  Otherwise,
+    // the elements are small and allocated inside the ArrayBuffer object's GC
+    // header so we must make a copy.
+    ObjectElements *transferableHeader;
+    bool stolen;
+    if (buffer->hasDynamicElements() && !buffer->isAsmJSArrayBuffer()) {
+        stolen = true;
+        transferableHeader = buffer->getElementsHeader();
+    } else {
+        stolen = false;
+
+        uint32_t byteLen = buffer->byteLength();
+        transferableHeader = AllocateArrayBufferContents(cx, byteLen);
+        if (!transferableHeader)
             return false;
-        memcpy(data, buffer->dataPointer(), buffer->byteLength());
-        buffer->changeContents(cx, data);
+
+        initElementsHeader(transferableHeader, byteLen);
+        void *headerDataPointer = reinterpret_cast<void*>(transferableHeader->elements());
+        memcpy(headerDataPointer, buffer->dataPointer(), byteLen);
     }
+
+    JS_ASSERT(!IsInsideNursery(cx->runtime(), transferableHeader));
+    *contents = transferableHeader;
+    *data = reinterpret_cast<uint8_t *>(transferableHeader + 1);
+
+    // Neuter the views, which may also mprotect(PROT_NONE) the buffer. So do
+    // it after copying out the data.
+    if (!ArrayBufferObject::neuterViews(cx, buffer))
+        return false;
+
+    // If the elements were taken from the neutered buffer, revert it back to
+    // using inline storage so it doesn't attempt to free the stolen elements
+    // when finalized.
+    if (stolen)
+        buffer->changeContents(cx, ObjectElements::fromElements(buffer->fixedElements()));
+
+    buffer->neuter(cx);
     return true;
 }
 
-/* static */ void *
-ArrayBufferObject::stealContents(JSContext *cx, Handle<ArrayBufferObject*> buffer)
+void
+ArrayBufferObject::obj_trace(JSTracer *trc, JSObject *obj)
 {
-    if (!buffer->canNeuter(cx)) {
-        js_ReportOverRecursed(cx);
-        return nullptr;
-    }
-
-    void *data;
-    if (buffer->ownsData()) {
-        data = buffer->dataPointer();
-        buffer->setOwnsData(DoesntOwnData);
-    } else {
-        data = AllocateArrayBufferContents(cx, buffer->byteLength());
-        if (!data)
-            return nullptr;
-        memcpy(data, buffer->dataPointer(), buffer->byteLength());
+    /*
+     * If this object changes, it will get marked via the private data barrier,
+     * so it's safe to leave it Unbarriered.
+     */
+    JSObject *delegate = static_cast<JSObject*>(obj->getPrivate());
+    if (delegate) {
+        JS_SET_TRACING_LOCATION(trc, &obj->privateRef(obj->numFixedSlots()));
+        MarkObjectUnbarriered(trc, &delegate, "arraybuffer.delegate");
+        obj->setPrivateUnbarriered(delegate);
     }
 
-    ArrayBufferObject::neuter(cx, buffer);
-
-    return data;
-}
-
-/* static */ void
-ArrayBufferObject::addSizeOfExcludingThis(JSObject *obj, mozilla::MallocSizeOf mallocSizeOf, JS::ObjectsExtraSizes *sizes)
-{
-    ArrayBufferObject &buffer = AsArrayBuffer(obj);
-
-    if (!buffer.ownsData())
-        return;
-
-    if (MOZ_UNLIKELY(buffer.isAsmJSArrayBuffer())) {
-#if defined (JS_CPU_X64)
-        // On x64, ArrayBufferObject::prepareForAsmJS switches the
-        // ArrayBufferObject to use mmap'd storage.
-        sizes->nonHeapElementsAsmJS += buffer.byteLength();
-#else
-        sizes->mallocHeapElementsAsmJS += mallocSizeOf(buffer.dataPointer());
-#endif
-    } else if (MOZ_UNLIKELY(buffer.isMappedArrayBuffer())) {
-        sizes->nonHeapElementsMapped += buffer.byteLength();
-    } else if (buffer.dataPointer()) {
-        sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(buffer.dataPointer());
-    }
-}
-
-/* static */ void
-ArrayBufferObject::finalize(FreeOp *fop, JSObject *obj)
-{
-    ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
-
-    if (buffer.ownsData())
-        buffer.releaseData(fop);
-}
-
-/* static */ void
-ArrayBufferObject::obj_trace(JSTracer *trc, JSObject *obj)
-{
     if (!IS_GC_MARKING_TRACER(trc) && !trc->runtime->isHeapMinorCollecting())
         return;
 
     // ArrayBufferObjects need to maintain a list of possibly-weak pointers to
     // their views. The straightforward way to update the weak pointers would
     // be in the views' finalizers, but giving views finalizers means they
     // cannot be swept in the background. This results in a very high
     // performance cost.  Instead, ArrayBufferObjects with a single view hold a
     // strong pointer to the view. This can entrain garbage when the single
     // view becomes otherwise unreachable while the buffer is still live, but
     // this is expected to be rare. ArrayBufferObjects with 0-1 views are
     // expected to be by far the most common cases. ArrayBufferObjects with
     // multiple views are collected into a linked list during collection, and
     // then swept to prune out their dead views.
 
     ArrayBufferObject &buffer = AsArrayBuffer(obj);
-    ArrayBufferViewObject *viewsHead = buffer.viewList();
+    ArrayBufferViewObject *viewsHead = UpdateObjectIfRelocated(trc->runtime,
+                                                               &GetViewListRef(&buffer));
     if (!viewsHead)
         return;
 
-    buffer.setViewList(UpdateObjectIfRelocated(trc->runtime, &viewsHead));
-
-    if (viewsHead->nextView() == nullptr) {
+    viewsHead = UpdateObjectIfRelocated(trc->runtime, &GetViewListRef(&buffer));
+    ArrayBufferViewObject *firstView = viewsHead;
+    if (firstView->nextView() == nullptr) {
         // Single view: mark it, but only if we're actually doing a GC pass
         // right now. Otherwise, the tracing pass for barrier verification will
         // fail if we add another view and the pointer becomes weak.
-        MarkObjectUnbarriered(trc, &viewsHead, "arraybuffer.singleview");
-        buffer.setViewListNoBarrier(viewsHead);
+        MarkObject(trc, &GetViewListRef(&buffer), "arraybuffer.singleview");
     } else {
         // Multiple views: do not mark, but append buffer to list.
-        ArrayBufferVector &gcLiveArrayBuffers = buffer.compartment()->gcLiveArrayBuffers;
-
         // obj_trace may be called multiple times before sweep(), so avoid
         // adding this buffer to the list multiple times.
-        if (buffer.inLiveList()) {
+        if (firstView->bufferLink() == UNSET_BUFFER_LINK) {
+            JS_ASSERT(obj->compartment() == firstView->compartment());
+            ArrayBufferObject **bufList = &obj->compartment()->gcLiveArrayBuffers;
+            firstView->setBufferLink(*bufList);
+            *bufList = &AsArrayBuffer(obj);
+        } else {
 #ifdef DEBUG
             bool found = false;
-            for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++)
-                found |= gcLiveArrayBuffers[i] == &buffer;
-            JS_ASSERT(found);
+            for (ArrayBufferObject *p = obj->compartment()->gcLiveArrayBuffers;
+                 p;
+                 p = GetViewList(p)->bufferLink())
+            {
+                if (p == obj)
+                {
+                    JS_ASSERT(!found);
+                    found = true;
+                }
+            }
 #endif
-        } else if (gcLiveArrayBuffers.append(&buffer)) {
-            buffer.setInLiveList(true);
-        } else {
-            CrashAtUnhandlableOOM("OOM while updating live array buffers");
         }
     }
 }
 
 /* static */ void
 ArrayBufferObject::sweep(JSCompartment *compartment)
 {
     JSRuntime *rt = compartment->runtimeFromMainThread();
-    ArrayBufferVector &gcLiveArrayBuffers = compartment->gcLiveArrayBuffers;
-
-    for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) {
-        ArrayBufferObject *buffer = gcLiveArrayBuffers[i];
+    ArrayBufferObject *buffer = compartment->gcLiveArrayBuffers;
+    JS_ASSERT(buffer != UNSET_BUFFER_LINK);
+    compartment->gcLiveArrayBuffers = nullptr;
 
-        JS_ASSERT(buffer->inLiveList());
-        buffer->setInLiveList(false);
+    while (buffer) {
+        ArrayBufferViewObject *viewsHead = UpdateObjectIfRelocated(rt, &GetViewListRef(buffer));
+        JS_ASSERT(viewsHead);
 
-        ArrayBufferViewObject *viewsHead = buffer->viewList();
-        JS_ASSERT(viewsHead);
-        buffer->setViewList(UpdateObjectIfRelocated(rt, &viewsHead));
+        ArrayBufferObject *nextBuffer = viewsHead->bufferLink();
+        JS_ASSERT(nextBuffer != UNSET_BUFFER_LINK);
+        viewsHead->setBufferLink(UNSET_BUFFER_LINK);
 
         // Rebuild the list of views of the ArrayBufferObject, discarding dead
         // views.  If there is only one view, it will have already been marked.
         ArrayBufferViewObject *prevLiveView = nullptr;
         ArrayBufferViewObject *view = viewsHead;
         while (view) {
             JS_ASSERT(buffer->compartment() == view->compartment());
             ArrayBufferViewObject *nextView = view->nextView();
             if (!IsObjectAboutToBeFinalized(&view)) {
                 view->setNextView(prevLiveView);
                 prevLiveView = view;
             }
             view = UpdateObjectIfRelocated(rt, &nextView);
         }
+        SetViewList(buffer, prevLiveView);
 
-        buffer->setViewList(prevLiveView);
+        buffer = nextBuffer;
     }
-
-    gcLiveArrayBuffers.clear();
 }
 
 void
 ArrayBufferObject::resetArrayBufferList(JSCompartment *comp)
 {
-    ArrayBufferVector &gcLiveArrayBuffers = comp->gcLiveArrayBuffers;
-
-    for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) {
-        ArrayBufferObject *buffer = gcLiveArrayBuffers[i];
+    ArrayBufferObject *buffer = comp->gcLiveArrayBuffers;
+    JS_ASSERT(buffer != UNSET_BUFFER_LINK);
+    comp->gcLiveArrayBuffers = nullptr;
 
-        JS_ASSERT(buffer->inLiveList());
-        buffer->setInLiveList(false);
+    while (buffer) {
+        ArrayBufferViewObject *view = GetViewList(buffer);
+        JS_ASSERT(view);
+
+        ArrayBufferObject *nextBuffer = view->bufferLink();
+        JS_ASSERT(nextBuffer != UNSET_BUFFER_LINK);
+
+        view->setBufferLink(UNSET_BUFFER_LINK);
+        buffer = nextBuffer;
     }
-
-    gcLiveArrayBuffers.clear();
 }
 
 /* static */ bool
 ArrayBufferObject::saveArrayBufferList(JSCompartment *comp, ArrayBufferVector &vector)
 {
-    const ArrayBufferVector &gcLiveArrayBuffers = comp->gcLiveArrayBuffers;
+    ArrayBufferObject *buffer = comp->gcLiveArrayBuffers;
+    while (buffer) {
+        JS_ASSERT(buffer != UNSET_BUFFER_LINK);
+        if (!vector.append(buffer))
+            return false;
 
-    for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) {
-        if (!vector.append(gcLiveArrayBuffers[i]))
-            return false;
+        ArrayBufferViewObject *view = GetViewList(buffer);
+        JS_ASSERT(view);
+        buffer = view->bufferLink();
     }
-
     return true;
 }
 
 /* static */ void
 ArrayBufferObject::restoreArrayBufferLists(ArrayBufferVector &vector)
 {
-    for (size_t i = 0; i < vector.length(); i++) {
-        ArrayBufferObject *buffer = vector[i];
+    for (ArrayBufferObject **p = vector.begin(); p != vector.end(); p++) {
+        ArrayBufferObject *buffer = *p;
+        JSCompartment *comp = buffer->compartment();
+        ArrayBufferViewObject *firstView = GetViewList(buffer);
+        JS_ASSERT(firstView);
+        JS_ASSERT(firstView->compartment() == comp);
+        JS_ASSERT(firstView->bufferLink() == UNSET_BUFFER_LINK);
+        firstView->setBufferLink(comp->gcLiveArrayBuffers);
+        comp->gcLiveArrayBuffers = buffer;
+    }
+}
+
+bool
+ArrayBufferObject::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
+                                     MutableHandleObject objp, MutableHandleShape propp)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+
+    bool delegateResult = JSObject::lookupGeneric(cx, delegate, id, objp, propp);
+
+    /* If false, there was an error, so propagate it.
+     * Otherwise, if propp is non-null, the property
+     * was found. Otherwise it was not
+     * found so look in the prototype chain.
+     */
+    if (!delegateResult)
+        return false;
+
+    if (propp) {
+        if (objp == delegate)
+            objp.set(obj);
+        return true;
+    }
+
+    RootedObject proto(cx, obj->getProto());
+    if (!proto) {
+        objp.set(nullptr);
+        propp.set(nullptr);
+        return true;
+    }
+
+    return JSObject::lookupGeneric(cx, proto, id, objp, propp);
+}
+
+bool
+ArrayBufferObject::obj_lookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
+                                      MutableHandleObject objp, MutableHandleShape propp)
+{
+    Rooted<jsid> id(cx, NameToId(name));
+    return obj_lookupGeneric(cx, obj, id, objp, propp);
+}
+
+bool
+ArrayBufferObject::obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
+                                     MutableHandleObject objp, MutableHandleShape propp)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+
+    /*
+     * If false, there was an error, so propagate it.
+     * Otherwise, if propp is non-null, the property
+     * was found. Otherwise it was not
+     * found so look in the prototype chain.
+     */
+    if (!JSObject::lookupElement(cx, delegate, index, objp, propp))
+        return false;
+
+    if (propp) {
+        if (objp == delegate)
+            objp.set(obj);
+        return true;
+    }
+
+    RootedObject proto(cx, obj->getProto());
+    if (proto)
+        return JSObject::lookupElement(cx, proto, index, objp, propp);
+
+    objp.set(nullptr);
+    propp.set(nullptr);
+    return true;
+}
+
+bool
+ArrayBufferObject::obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
+                                     PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
+{
+    AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
+
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+    return baseops::DefineGeneric(cx, delegate, id, v, getter, setter, attrs);
+}
+
+bool
+ArrayBufferObject::obj_defineProperty(JSContext *cx, HandleObject obj,
+                                      HandlePropertyName name, HandleValue v,
+                                      PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
+{
+    Rooted<jsid> id(cx, NameToId(name));
+    return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs);
+}
+
+bool
+ArrayBufferObject::obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v,
+                                     PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
+{
+    AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
 
-        JS_ASSERT(!buffer->inLiveList());
-        buffer->setInLiveList(true);
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+    return baseops::DefineElement(cx, delegate, index, v, getter, setter, attrs);
+}
+
+bool
+ArrayBufferObject::obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
+                                  HandleId id, MutableHandleValue vp)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+    return baseops::GetProperty(cx, delegate, receiver, id, vp);
+}
+
+bool
+ArrayBufferObject::obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
+                                   HandlePropertyName name, MutableHandleValue vp)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+    Rooted<jsid> id(cx, NameToId(name));
+    return baseops::GetProperty(cx, delegate, receiver, id, vp);
+}
+
+bool
+ArrayBufferObject::obj_getElement(JSContext *cx, HandleObject obj,
+                                  HandleObject receiver, uint32_t index, MutableHandleValue vp)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+    return baseops::GetElement(cx, delegate, receiver, index, vp);
+}
+
+bool
+ArrayBufferObject::obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
+                                  MutableHandleValue vp, bool strict)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+
+    return baseops::SetPropertyHelper<SequentialExecution>(cx, delegate, obj, id, 0, vp, strict);
+}
+
+bool
+ArrayBufferObject::obj_setProperty(JSContext *cx, HandleObject obj,
+                                   HandlePropertyName name, MutableHandleValue vp, bool strict)
+{
+    Rooted<jsid> id(cx, NameToId(name));
+    return obj_setGeneric(cx, obj, id, vp, strict);
+}
 
-        buffer->compartment()->gcLiveArrayBuffers.infallibleAppend(buffer);
-    }
+bool
+ArrayBufferObject::obj_setElement(JSContext *cx, HandleObject obj,
+                                  uint32_t index, MutableHandleValue vp, bool strict)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+
+    return baseops::SetElementHelper(cx, delegate, obj, index, 0, vp, strict);
+}
+
+bool
+ArrayBufferObject::obj_getGenericAttributes(JSContext *cx, HandleObject obj,
+                                            HandleId id, unsigned *attrsp)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+    return baseops::GetAttributes(cx, delegate, id, attrsp);
+}
+
+bool
+ArrayBufferObject::obj_setGenericAttributes(JSContext *cx, HandleObject obj,
+                                            HandleId id, unsigned *attrsp)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+    return baseops::SetAttributes(cx, delegate, id, attrsp);
+}
+
+bool
+ArrayBufferObject::obj_deleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
+                                      bool *succeeded)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+    return baseops::DeleteProperty(cx, delegate, name, succeeded);
+}
+
+bool
+ArrayBufferObject::obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
+                                     bool *succeeded)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+    return baseops::DeleteElement(cx, delegate, index, succeeded);
+}
+
+bool
+ArrayBufferObject::obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
+                                 MutableHandleValue statep, MutableHandleId idp)
+{
+    statep.setNull();
+    return true;
 }
 
 /*
  * ArrayBufferViewObject
  */
 
 /*
  * This method is used to trace TypedArrayObjects and DataViewObjects. We need
@@ -914,30 +1213,41 @@ ArrayBufferViewObject::trace(JSTracer *t
 {
     HeapSlot &bufSlot = obj->getReservedSlotRef(BUFFER_SLOT);
     MarkSlot(trc, &bufSlot, "typedarray.buffer");
 
     /* Update obj's data slot if the array buffer moved. Note that during
      * initialization, bufSlot may still be JSVAL_VOID. */
     if (bufSlot.isObject()) {
         ArrayBufferObject &buf = AsArrayBuffer(&bufSlot.toObject());
-        if (buf.isNeutered()) {
+        if (buf.getElementsHeader()->isNeuteredBuffer()) {
             // When a view is neutered, it is set to NULL
             JS_ASSERT(obj->getPrivate() == nullptr);
         } else {
             int32_t offset = obj->getReservedSlot(BYTEOFFSET_SLOT).toInt32();
             obj->initPrivate(buf.dataPointer() + offset);
         }
     }
 
     /* Update NEXT_VIEW_SLOT, if the view moved. */
     IsSlotMarked(&obj->getReservedSlotRef(NEXT_VIEW_SLOT));
 }
 
 void
+ArrayBufferViewObject::prependToViews(ArrayBufferViewObject *viewsHead)
+{
+    setNextView(viewsHead);
+
+    // Move the multiview buffer list link into this view since we're
+    // prepending it to the list.
+    setBufferLink(viewsHead->bufferLink());
+    viewsHead->setBufferLink(UNSET_BUFFER_LINK);
+}
+
+void
 ArrayBufferViewObject::neuter(JSContext *cx)
 {
     if (is<DataViewObject>())
         as<DataViewObject>().neuter();
     else if (is<TypedArrayObject>())
         as<TypedArrayObject>().neuter(cx);
     else
         as<TypedObject>().neuter(cx);
@@ -986,79 +1296,112 @@ JS_FRIEND_API(bool)
 JS_NeuterArrayBuffer(JSContext *cx, HandleObject obj)
 {
     if (!obj->is<ArrayBufferObject>()) {
         JS_ReportError(cx, "ArrayBuffer object required");
         return false;
     }
 
     Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
-
-    if (!buffer->canNeuter(cx)) {
-        js_ReportOverRecursed(cx);
+    if (!ArrayBufferObject::neuterViews(cx, buffer))
         return false;
-    }
-
-    ArrayBufferObject::neuter(cx, buffer);
+    buffer->neuter(cx);
     return true;
 }
 
 JS_FRIEND_API(JSObject *)
 JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes)
 {
     JS_ASSERT(nbytes <= INT32_MAX);
     return ArrayBufferObject::create(cx, nbytes);
 }
 
 JS_PUBLIC_API(JSObject *)
-JS_NewArrayBufferWithContents(JSContext *cx, size_t nbytes, void *contents, bool mapped /* = false */)
+JS_NewArrayBufferWithContents(JSContext *cx, void *contents)
 {
     JS_ASSERT(contents);
-    return ArrayBufferObject::create(cx, nbytes, contents, TenuredObject, mapped);
+
+    // Do not allocate ArrayBuffers with an API-provided pointer in the nursery.
+    // These are likely to be long lived and the nursery does not know how to
+    // free the contents.
+    JSObject *obj = ArrayBufferObject::create(cx, 0, true, TenuredObject);
+    if (!obj)
+        return nullptr;
+    js::ObjectElements *elements = reinterpret_cast<js::ObjectElements *>(contents);
+    obj->setDynamicElements(elements);
+    JS_ASSERT(GetViewList(&obj->as<ArrayBufferObject>()) == nullptr);
+
+#ifdef JSGC_GENERATIONAL
+    cx->runtime()->gcNursery.notifyNewElements(obj, elements);
+#endif
+    return obj;
 }
 
-JS_PUBLIC_API(void *)
-JS_AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes)
+JS_PUBLIC_API(bool)
+JS_AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes,
+                               void **contents, uint8_t **data)
 {
-    return AllocateArrayBufferContents(maybecx, nbytes);
+    js::ObjectElements *header = AllocateArrayBufferContents(maybecx, nbytes);
+    if (!header)
+        return false;
+
+    ArrayBufferObject::updateElementsHeader(header, nbytes);
+
+    *contents = header;
+    *data = reinterpret_cast<uint8_t*>(header->elements());
+    return true;
 }
 
-JS_PUBLIC_API(void *)
-JS_ReallocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void *oldContents, uint32_t oldNbytes)
+JS_PUBLIC_API(bool)
+JS_ReallocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void **contents, uint8_t **data)
 {
-    return AllocateArrayBufferContents(maybecx, nbytes, oldContents, oldNbytes);
+    js::ObjectElements *header = AllocateArrayBufferContents(maybecx, nbytes, *contents);
+    if (!header)
+        return false;
+
+    ArrayBufferObject::initElementsHeader(header, nbytes);
+
+    *contents = header;
+    *data = reinterpret_cast<uint8_t*>(header->elements());
+    return true;
 }
 
 JS_FRIEND_API(bool)
 JS_IsArrayBufferObject(JSObject *obj)
 {
     obj = CheckedUnwrap(obj);
     return obj ? obj->is<ArrayBufferObject>() : false;
 }
 
-JS_PUBLIC_API(void *)
-JS_StealArrayBufferContents(JSContext *cx, HandleObject objArg)
+JS_PUBLIC_API(bool)
+JS_StealArrayBufferContents(JSContext *cx, HandleObject objArg, void **contents, uint8_t **data)
 {
     JSObject *obj = CheckedUnwrap(objArg);
     if (!obj)
-        return nullptr;
+        return false;
 
     if (!obj->is<ArrayBufferObject>()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
-        return nullptr;
+        return false;
     }
 
     Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
-    return ArrayBufferObject::stealContents(cx, buffer);
+    if (!ArrayBufferObject::stealContents(cx, buffer, contents, data))
+        return false;
+
+    return true;
 }
 
-JS_PUBLIC_API(void *)
-JS_CreateMappedArrayBufferContents(int fd, int *new_fd, size_t offset, size_t length)
+JS_PUBLIC_API(bool)
+JS_CreateMappedArrayBufferContents(int fd, int *new_fd, size_t offset,
+                                   size_t length, void **contents)
 {
-    return ArrayBufferObject::createMappedArrayBuffer(fd, new_fd, offset, length);
+    *contents = ArrayBufferObject::createMappedArrayBuffer(fd, new_fd, offset, length);
+
+    return *contents;
 }
 
 JS_PUBLIC_API(void)
 JS_ReleaseMappedArrayBufferContents(int fd, void *contents, size_t length)
 {
     DeallocateMappedObject(fd, contents, length);
 }
 
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -18,21 +18,16 @@ namespace js {
 
 class ArrayBufferViewObject;
 
 // Header for mapped array buffer
 struct MappingInfoHeader
 {
     uint32_t fd;
     uint32_t offset;
-
-    void init(uint32_t fd, uint32_t offset) {
-        this->fd = fd;
-        this->offset = offset;
-    }
 };
 
 // The inheritance hierarchy for the various classes relating to typed arrays
 // is as follows.
 //
 // - JSObject
 //   - ArrayBufferObject
 //     - SharedArrayBufferObject
@@ -60,170 +55,201 @@ typedef Vector<ArrayBufferObject *, 0, S
  * with a size.
  */
 class ArrayBufferObject : public JSObject
 {
     static bool byteLengthGetterImpl(JSContext *cx, CallArgs args);
     static bool fun_slice_impl(JSContext *cx, CallArgs args);
 
   public:
-    static const uint8_t DATA_SLOT = 0;
-    static const uint8_t BYTE_LENGTH_SLOT = 1;
-    static const uint8_t VIEW_LIST_SLOT = 2;
-    static const uint8_t FLAGS_SLOT = 3;
-
-    static const uint8_t RESERVED_SLOTS = 4;
-
     static const Class class_;
 
     static const Class protoClass;
     static const JSFunctionSpec jsfuncs[];
     static const JSFunctionSpec jsstaticfuncs[];
 
     static bool byteLengthGetter(JSContext *cx, unsigned argc, Value *vp);
 
     static bool fun_slice(JSContext *cx, unsigned argc, Value *vp);
 
     static bool fun_isView(JSContext *cx, unsigned argc, Value *vp);
 
     static bool class_constructor(JSContext *cx, unsigned argc, Value *vp);
 
-    static ArrayBufferObject *create(JSContext *cx, uint32_t nbytes, void *contents = nullptr,
-                                     NewObjectKind newKind = GenericObject,
-                                     bool mapped = false);
+    static ArrayBufferObject *create(JSContext *cx, uint32_t nbytes, bool clear = true,
+                                     NewObjectKind newKind = GenericObject);
 
     static JSObject *createSlice(JSContext *cx, Handle<ArrayBufferObject*> arrayBuffer,
                                  uint32_t begin, uint32_t end);
 
     static bool createDataViewForThisImpl(JSContext *cx, CallArgs args);
     static bool createDataViewForThis(JSContext *cx, unsigned argc, Value *vp);
 
     template<typename T>
     static bool createTypedArrayFromBufferImpl(JSContext *cx, CallArgs args);
 
     template<typename T>
     static bool createTypedArrayFromBuffer(JSContext *cx, unsigned argc, Value *vp);
 
     static void obj_trace(JSTracer *trc, JSObject *obj);
 
+    static bool obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
+                                  MutableHandleObject objp, MutableHandleShape propp);
+    static bool obj_lookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
+                                   MutableHandleObject objp, MutableHandleShape propp);
+    static bool obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
+                                  MutableHandleObject objp, MutableHandleShape propp);
+
+    static bool obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
+                                  PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
+    static bool obj_defineProperty(JSContext *cx, HandleObject obj,
+                                   HandlePropertyName name, HandleValue v,
+                                   PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
+    static bool obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v,
+                                  PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
+
+    static bool obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
+                               HandleId id, MutableHandleValue vp);
+    static bool obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
+                                HandlePropertyName name, MutableHandleValue vp);
+    static bool obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver,
+                               uint32_t index, MutableHandleValue vp);
+
+    static bool obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
+                               MutableHandleValue vp, bool strict);
+    static bool obj_setProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
+                                MutableHandleValue vp, bool strict);
+    static bool obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
+                               MutableHandleValue vp, bool strict);
+
+    static bool obj_getGenericAttributes(JSContext *cx, HandleObject obj,
+                                         HandleId id, unsigned *attrsp);
+    static bool obj_setGenericAttributes(JSContext *cx, HandleObject obj,
+                                         HandleId id, unsigned *attrsp);
+
+    static bool obj_deleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
+                                   bool *succeeded);
+    static bool obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
+                                  bool *succeeded);
+
+    static bool obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
+                              MutableHandleValue statep, MutableHandleId idp);
+
     static void sweep(JSCompartment *rt);
 
     static void resetArrayBufferList(JSCompartment *rt);
     static bool saveArrayBufferList(JSCompartment *c, ArrayBufferVector &vector);
     static void restoreArrayBufferLists(ArrayBufferVector &vector);
 
-    static void *stealContents(JSContext *cx, Handle<ArrayBufferObject*> buffer);
+    static bool stealContents(JSContext *cx, Handle<ArrayBufferObject*> buffer, void **contents,
+                              uint8_t **data);
+
+    static void updateElementsHeader(js::ObjectElements *header, uint32_t bytes) {
+        header->initializedLength = bytes;
 
-    static void addSizeOfExcludingThis(JSObject *obj, mozilla::MallocSizeOf mallocSizeOf,
-                                       JS::ObjectsExtraSizes *sizes);
+        // NB: one or both of these fields is clobbered by GetViewList to store
+        // the 'views' link. Set them to 0 to effectively initialize 'views'
+        // to nullptr.
+        header->length = 0;
+        header->capacity = 0;
+    }
 
-    MappingInfoHeader *getMappingInfoHeader() const {
-        JS_ASSERT(isMappedArrayBuffer());
-        return reinterpret_cast<MappingInfoHeader *>(dataPointer() - sizeof(MappingInfoHeader));
+    static void initElementsHeader(js::ObjectElements *header, uint32_t bytes) {
+        header->flags = 0;
+        updateElementsHeader(header, bytes);
     }
 
-    uint32_t getMappingFD() const { return getMappingInfoHeader()->fd; }
-    uint32_t getMappingOffset() const { return getMappingInfoHeader()->offset; }
+    static void initMappedElementsHeader(js::ObjectElements *header, uint32_t fd,
+                                         uint32_t offset, uint32_t bytes) {
+        initElementsHeader(header, bytes);
+        header->setIsMappedArrayBuffer();
+        MappingInfoHeader *mh = getMappingInfoHeader(header);
+        mh->fd = fd;
+        mh->offset = offset;
+    }
+
+    static MappingInfoHeader *getMappingInfoHeader(js::ObjectElements *header) {
+        MOZ_ASSERT(header->isMappedArrayBuffer());
+        return reinterpret_cast<MappingInfoHeader *>(uintptr_t(header) -
+                                                     sizeof(MappingInfoHeader));
+    }
+
+    uint32_t getMappingFD() {
+        MOZ_ASSERT(getElementsHeader()->isMappedArrayBuffer());
+        MappingInfoHeader *mh = getMappingInfoHeader(getElementsHeader());
+        return mh->fd;
+    }
+
+    uint32_t getMappingOffset() const {
+        MOZ_ASSERT(getElementsHeader()->isMappedArrayBuffer());
+        MappingInfoHeader *mh = getMappingInfoHeader(getElementsHeader());
+        return mh->offset;
+    }
+
+    static uint32_t headerInitializedLength(const js::ObjectElements *header) {
+        return header->initializedLength;
+    }
 
     void addView(ArrayBufferViewObject *view);
 
-    void changeContents(JSContext *cx, void *newData);
+    void changeContents(JSContext *cx, ObjectElements *newHeader);
 
     /*
      * Ensure data is not stored inline in the object. Used when handing back a
      * GC-safe pointer.
      */
     static bool ensureNonInline(JSContext *cx, Handle<ArrayBufferObject*> buffer);
 
-    bool canNeuter(JSContext *cx);
-
-    /* Neuter this buffer and all its views. */
-    static void neuter(JSContext *cx, Handle<ArrayBufferObject*> buffer);
+    uint32_t byteLength() const {
+        return getElementsHeader()->initializedLength;
+    }
 
-    uint8_t *dataPointer() const;
-    size_t byteLength() const;
+    /*
+     * Neuter all views of an ArrayBuffer.
+     */
+    static bool neuterViews(JSContext *cx, Handle<ArrayBufferObject*> buffer);
 
-    void releaseData(FreeOp *fop);
+    uint8_t * dataPointer() const;
+
+    /*
+     * Discard the ArrayBuffer contents. For asm.js buffers, at least, should
+     * be called after neuterViews().
+     */
+    void neuter(JSContext *cx);
 
     /*
      * Check if the arrayBuffer contains any data. This will return false for
      * ArrayBuffer.prototype and neutered ArrayBuffers.
      */
     bool hasData() const {
         return getClass() == &class_;
     }
 
-    bool isAsmJSArrayBuffer() const { return flags() & ASMJS_BUFFER; }
-    bool isSharedArrayBuffer() const { return flags() & SHARED_BUFFER; }
-    bool isMappedArrayBuffer() const { return flags() & MAPPED_BUFFER; }
-    bool isNeutered() const { return flags() & NEUTERED_BUFFER; }
-
-    static bool prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer);
-    static bool canNeuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer);
-
-    static void finalize(FreeOp *fop, JSObject *obj);
-
-    static void *createMappedArrayBuffer(int fd, int *new_fd, size_t offset, size_t length);
-
-  protected:
-    enum OwnsState {
-        DoesntOwnData = 0,
-        OwnsData = 1,
-    };
-
-    void setDataPointer(void *data, OwnsState ownsState);
-    void setByteLength(size_t length);
-
-    ArrayBufferViewObject *viewList() const;
-    void setViewList(ArrayBufferViewObject *viewsHead);
-    void setViewListNoBarrier(ArrayBufferViewObject *viewsHead);
-
-    enum ArrayBufferFlags {
-        // In the gcLiveArrayBuffers list.
-        IN_LIVE_LIST       =  0x1,
-
-        // The dataPointer() is owned by this buffer and should be released
-        // when no longer in use. Releasing the pointer may be done by either
-        // freeing or unmapping it, and how to do this is determined by the
-        // buffer's other flags (e.g. ASMJS_BUFFER and MAPPED_BUFFER).
-        OWNS_DATA          =  0x2,
-
-        ASMJS_BUFFER       =  0x4,
-        SHARED_BUFFER      =  0x8,
-        MAPPED_BUFFER      = 0x10,
-        NEUTERED_BUFFER    = 0x20
-    };
-
-    uint32_t flags() const;
-    void setFlags(uint32_t flags);
-
-    bool inLiveList() const { return flags() & IN_LIVE_LIST; }
-    void setInLiveList(bool value) {
-        setFlags(value ? (flags() | IN_LIVE_LIST) : (flags() & ~IN_LIVE_LIST));
+    bool isSharedArrayBuffer() const {
+        return getElementsHeader()->isSharedArrayBuffer();
     }
 
-    bool ownsData() const { return flags() & OWNS_DATA; }
-    void setOwnsData(OwnsState owns) {
-        setFlags(owns ? (flags() | OWNS_DATA) : (flags() & ~OWNS_DATA));
+    bool isAsmJSArrayBuffer() const {
+        return getElementsHeader()->isAsmJSArrayBuffer();
     }
-
-    void setIsAsmJSArrayBuffer() { setFlags(flags() | ASMJS_BUFFER); }
-    void setIsSharedArrayBuffer() { setFlags(flags() | SHARED_BUFFER); }
-    void setIsMappedArrayBuffer() { setFlags(flags() | MAPPED_BUFFER); }
-    void setIsNeutered() { setFlags(flags() | NEUTERED_BUFFER); }
+    bool isNeutered() const {
+        return getElementsHeader()->isNeuteredBuffer();
+    }
+    static bool prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer);
+    static bool neuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer);
+    static void releaseAsmJSArrayBuffer(FreeOp *fop, JSObject *obj);
 
-    void initialize(size_t byteLength, void *data, OwnsState ownsState) {
-        setByteLength(byteLength);
-        setFlags(0);
-        setViewListNoBarrier(nullptr);
-        setDataPointer(data, ownsState);
+    bool isMappedArrayBuffer() const {
+        return getElementsHeader()->isMappedArrayBuffer();
     }
-
-    void releaseAsmJSArray(FreeOp *fop);
-    void releaseMappedArray();
+    void setIsMappedArrayBuffer() {
+        getElementsHeader()->setIsMappedArrayBuffer();
+    }
+    static void *createMappedArrayBuffer(int fd, int *new_fd, size_t offset, size_t length);
+    static void releaseMappedArrayBuffer(FreeOp *fop, JSObject *obj);
 };
 
 /*
  * ArrayBufferViewObject
  *
  * Common definitions shared by all ArrayBufferViews.
  */
 
@@ -237,34 +263,45 @@ class ArrayBufferViewObject : public JSO
     static const size_t BYTELENGTH_SLOT  = JS_TYPEDOBJ_SLOT_BYTELENGTH;
 
     /* Underlying ArrayBufferObject */
     static const size_t BUFFER_SLOT      = JS_TYPEDOBJ_SLOT_OWNER;
 
     /* ArrayBufferObjects point to a linked list of views, chained through this slot */
     static const size_t NEXT_VIEW_SLOT   = JS_TYPEDOBJ_SLOT_NEXT_VIEW;
 
+    /*
+     * When ArrayBufferObjects are traced during GC, they construct a linked
+     * list of ArrayBufferObjects with more than one view, chained through this
+     * slot of the first view of each ArrayBufferObject.
+     */
+    static const size_t NEXT_BUFFER_SLOT = JS_TYPEDOBJ_SLOT_NEXT_BUFFER;
+
   public:
     JSObject *bufferObject() const {
         return &getFixedSlot(BUFFER_SLOT).toObject();
     }
 
+    ArrayBufferObject *bufferLink() {
+        return static_cast<ArrayBufferObject*>(getFixedSlot(NEXT_BUFFER_SLOT).toPrivate());
+    }
+
+    inline void setBufferLink(ArrayBufferObject *buffer);
+
     ArrayBufferViewObject *nextView() const {
         return static_cast<ArrayBufferViewObject*>(getFixedSlot(NEXT_VIEW_SLOT).toPrivate());
     }
 
     inline void setNextView(ArrayBufferViewObject *view);
 
+    void prependToViews(ArrayBufferViewObject *viewsHead);
+
     void neuter(JSContext *cx);
 
     static void trace(JSTracer *trc, JSObject *obj);
-
-    uint8_t *dataPointer() {
-        return static_cast<uint8_t *>(getPrivate());
-    }
 };
 
 bool
 ToClampedIndex(JSContext *cx, HandleValue v, uint32_t length, uint32_t *out);
 
 inline void
 PostBarrierTypedArrayObject(JSObject *obj)
 {
@@ -294,16 +331,23 @@ InitArrayBufferViewDataPointer(ArrayBuff
  */
 bool IsArrayBuffer(HandleValue v);
 bool IsArrayBuffer(HandleObject obj);
 bool IsArrayBuffer(JSObject *obj);
 ArrayBufferObject &AsArrayBuffer(HandleObject obj);
 ArrayBufferObject &AsArrayBuffer(JSObject *obj);
 
 inline void
+ArrayBufferViewObject::setBufferLink(ArrayBufferObject *buffer)
+{
+    setFixedSlot(NEXT_BUFFER_SLOT, PrivateValue(buffer));
+    PostBarrierTypedArrayObject(this);
+}
+
+inline void
 ArrayBufferViewObject::setNextView(ArrayBufferViewObject *view)
 {
     setFixedSlot(NEXT_VIEW_SLOT, PrivateValue(view));
     PostBarrierTypedArrayObject(this);
 }
 
 extern uint32_t JS_FASTCALL
 ClampDoubleToUint8(const double x);
--- a/js/src/vm/ObjectImpl.cpp
+++ b/js/src/vm/ObjectImpl.cpp
@@ -378,16 +378,30 @@ js::ObjectImpl::markChildren(JSTracer *t
         clasp->trace(trc, obj);
 
     if (shape_->isNative()) {
         MarkObjectSlots(trc, obj, 0, obj->slotSpan());
         gc::MarkArraySlots(trc, obj->getDenseInitializedLength(), obj->getDenseElements(), "objectElements");
     }
 }
 
+JSObject *
+js::ArrayBufferDelegate(JSContext *cx, Handle<ObjectImpl*> obj)
+{
+    MOZ_ASSERT(obj->hasClass(&ArrayBufferObject::class_) ||
+               obj->hasClass(&SharedArrayBufferObject::class_));
+
+    if (obj->getPrivate())
+        return static_cast<JSObject *>(obj->getPrivate());
+    JSObject *delegate = NewObjectWithGivenProto(cx, &JSObject::class_,
+                                                 obj->getTaggedProto(), nullptr);
+    obj->setPrivateGCThing(delegate);
+    return delegate;
+}
+
 void
 AutoPropDescRooter::trace(JSTracer *trc)
 {
     gc::MarkValueRoot(trc, &propDesc.pd_, "AutoPropDescRooter pd");
     gc::MarkValueRoot(trc, &propDesc.value_, "AutoPropDescRooter value");
     gc::MarkValueRoot(trc, &propDesc.get_, "AutoPropDescRooter get");
     gc::MarkValueRoot(trc, &propDesc.set_, "AutoPropDescRooter set");
 }
--- a/js/src/vm/ObjectImpl.h
+++ b/js/src/vm/ObjectImpl.h
@@ -163,26 +163,34 @@ ArraySetLength(typename ExecutionModeTra
  * of an object does not necessarily visit indexes in the order they were
  * created.
  */
 class ObjectElements
 {
   public:
     enum Flags {
         CONVERT_DOUBLE_ELEMENTS     = 0x1,
+        ASMJS_ARRAY_BUFFER          = 0x2,
+        NEUTERED_BUFFER             = 0x4,
+        SHARED_ARRAY_BUFFER         = 0x8,
+        MAPPED_ARRAY_BUFFER         = 0x10,
 
         // Present only if these elements correspond to an array with
         // non-writable length; never present for non-arrays.
-        NONWRITABLE_ARRAY_LENGTH    = 0x2
+        NONWRITABLE_ARRAY_LENGTH    = 0x20,
     };
 
   private:
     friend class ::JSObject;
     friend class ObjectImpl;
     friend class ArrayObject;
+    friend class ArrayBufferObject;
+    friend class ArrayBufferViewObject;
+    friend class SharedArrayBufferObject;
+    friend class TypedArrayObject;
     friend class Nursery;
 
     template <ExecutionMode mode>
     friend bool
     ArraySetLength(typename ExecutionModeTraits<mode>::ContextType cx,
                    Handle<ArrayObject*> obj, HandleId id,
                    unsigned attrs, HandleValue value, bool setterIsStrict);
 
@@ -219,16 +227,40 @@ class ObjectElements
         return flags & CONVERT_DOUBLE_ELEMENTS;
     }
     void setShouldConvertDoubleElements() {
         flags |= CONVERT_DOUBLE_ELEMENTS;
     }
     void clearShouldConvertDoubleElements() {
         flags &= ~CONVERT_DOUBLE_ELEMENTS;
     }
+    bool isAsmJSArrayBuffer() const {
+        return flags & ASMJS_ARRAY_BUFFER;
+    }
+    void setIsAsmJSArrayBuffer() {
+        flags |= ASMJS_ARRAY_BUFFER;
+    }
+    bool isNeuteredBuffer() const {
+        return flags & NEUTERED_BUFFER;
+    }
+    void setIsNeuteredBuffer() {
+        flags |= NEUTERED_BUFFER;
+    }
+    bool isSharedArrayBuffer() const {
+        return flags & SHARED_ARRAY_BUFFER;
+    }
+    void setIsSharedArrayBuffer() {
+        flags |= SHARED_ARRAY_BUFFER;
+    }
+    bool isMappedArrayBuffer() const {
+        return flags & MAPPED_ARRAY_BUFFER;
+    }
+    void setIsMappedArrayBuffer() {
+        flags |= MAPPED_ARRAY_BUFFER;
+    }
     bool hasNonwritableArrayLength() const {
         return flags & NONWRITABLE_ARRAY_LENGTH;
     }
     void setNonwritableArrayLength() {
         flags |= NONWRITABLE_ARRAY_LENGTH;
     }
 
   public:
@@ -1009,11 +1041,14 @@ static inline bool
 IsObjectValueInCompartment(js::Value v, JSCompartment *comp)
 {
     if (!v.isObject())
         return true;
     return reinterpret_cast<ObjectImpl*>(&v.toObject())->compartment() == comp;
 }
 #endif
 
+extern JSObject *
+ArrayBufferDelegate(JSContext *cx, Handle<ObjectImpl*> obj);
+
 } /* namespace js */
 
 #endif /* vm_ObjectImpl_h */
--- a/js/src/vm/SharedArrayObject.cpp
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -18,16 +18,18 @@
 #include "mozilla/Atomics.h"
 #include "jit/AsmJS.h"
 
 using namespace js;
 
 using mozilla::IsNaN;
 using mozilla::PodCopy;
 
+#define SHAREDARRAYBUFFER_RESERVED_SLOTS 15
+
 /*
  * SharedArrayRawBuffer
  */
 
 static inline void *
 MapMemory(size_t length, bool commit)
 {
 #ifdef XP_WIN
@@ -172,36 +174,63 @@ SharedArrayBufferObject::New(JSContext *
     if (!IsValidAsmJSHeapLength(length)) {
         ScopedJSFreePtr<char> msg(
             JS_smprintf("SharedArrayBuffer byteLength 0x%x is not a valid length. The next valid "
                         "length is 0x%x", length, RoundUpToNextValidAsmJSHeapLength(length)));
         JS_ReportError(cx, msg.get());
         return nullptr;
     }
 
+    RootedObject obj(cx, NewBuiltinClassInstance(cx, &class_));
+    if (!obj)
+        return nullptr;
+
+    JS_ASSERT(obj->getClass() == &class_);
+
+    Rooted<js::Shape*> empty(cx);
+    empty = EmptyShape::getInitialShape(cx, &class_, obj->getProto(), obj->getParent(),
+                                        obj->getMetadata(), gc::FINALIZE_OBJECT16_BACKGROUND);
+    if (!empty)
+        return nullptr;
+    obj->setLastPropertyInfallible(empty);
+
+    obj->setFixedElements();
+    obj->as<SharedArrayBufferObject>().initElementsHeader(obj->getElementsHeader(), length);
+    obj->getElementsHeader()->setIsSharedArrayBuffer();
+
     SharedArrayRawBuffer *buffer = SharedArrayRawBuffer::New(length);
     if (!buffer)
         return nullptr;
+    obj->as<SharedArrayBufferObject>().acceptRawBuffer(buffer);
 
-    return New(cx, buffer);
+    return obj;
 }
 
 JSObject *
 SharedArrayBufferObject::New(JSContext *cx, SharedArrayRawBuffer *buffer)
 {
-    Rooted<SharedArrayBufferObject*> obj(cx, NewBuiltinClassInstance<SharedArrayBufferObject>(cx));
+    RootedObject obj(cx, NewBuiltinClassInstance(cx, &class_));
     if (!obj)
         return nullptr;
 
     JS_ASSERT(obj->getClass() == &class_);
 
-    obj->initialize(buffer->byteLength(), nullptr, DoesntOwnData);
+    Rooted<js::Shape*> empty(cx);
+    empty = EmptyShape::getInitialShape(cx, &class_, obj->getProto(), obj->getParent(),
+                                        obj->getMetadata(), gc::FINALIZE_OBJECT16_BACKGROUND);
+    if (!empty)
+        return nullptr;
+    obj->setLastPropertyInfallible(empty);
 
-    obj->acceptRawBuffer(buffer);
-    obj->setIsSharedArrayBuffer();
+    obj->setFixedElements();
+    obj->as<SharedArrayBufferObject>().initElementsHeader(obj->getElementsHeader(),
+                                                          buffer->byteLength());
+    obj->getElementsHeader()->setIsSharedArrayBuffer();
+
+    obj->as<SharedArrayBufferObject>().acceptRawBuffer(buffer);
 
     return obj;
 }
 
 void
 SharedArrayBufferObject::acceptRawBuffer(SharedArrayRawBuffer *buffer)
 {
     setReservedSlot(SharedArrayBufferObject::RAWBUF_SLOT, PrivateValue(buffer));
@@ -249,45 +278,71 @@ SharedArrayBufferObject::Finalize(FreeOp
 }
 
 /*
  * SharedArrayBufferObject
  */
 
 const Class SharedArrayBufferObject::protoClass = {
     "SharedArrayBufferPrototype",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_RESERVED_SLOTS(SHAREDARRAYBUFFER_RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer),
     JS_PropertyStub,         /* addProperty */
     JS_DeletePropertyStub,   /* delProperty */
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub
 };
 
 const Class SharedArrayBufferObject::class_ = {
     "SharedArrayBuffer",
+    JSCLASS_HAS_PRIVATE |
     JSCLASS_IMPLEMENTS_BARRIERS |
-    JSCLASS_HAS_RESERVED_SLOTS(SharedArrayBufferObject::RESERVED_SLOTS) |
+    Class::NON_NATIVE |
+    JSCLASS_HAS_RESERVED_SLOTS(SHAREDARRAYBUFFER_RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer),
     JS_PropertyStub,         /* addProperty */
     JS_DeletePropertyStub,   /* delProperty */
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     SharedArrayBufferObject::Finalize,
     nullptr,        /* call        */
     nullptr,        /* hasInstance */
     nullptr,        /* construct   */
     ArrayBufferObject::obj_trace,
     JS_NULL_CLASS_SPEC,
-    JS_NULL_CLASS_EXT
+    JS_NULL_CLASS_EXT,
+    {
+        ArrayBufferObject::obj_lookupGeneric,
+        ArrayBufferObject::obj_lookupProperty,
+        ArrayBufferObject::obj_lookupElement,
+        ArrayBufferObject::obj_defineGeneric,
+        ArrayBufferObject::obj_defineProperty,
+        ArrayBufferObject::obj_defineElement,
+        ArrayBufferObject::obj_getGeneric,
+        ArrayBufferObject::obj_getProperty,
+        ArrayBufferObject::obj_getElement,
+        ArrayBufferObject::obj_setGeneric,
+        ArrayBufferObject::obj_setProperty,
+        ArrayBufferObject::obj_setElement,
+        ArrayBufferObject::obj_getGenericAttributes,
+        ArrayBufferObject::obj_setGenericAttributes,
+        ArrayBufferObject::obj_deleteProperty,
+        ArrayBufferObject::obj_deleteElement,
+        nullptr, nullptr, /* watch/unwatch */
+        nullptr,          /* slice */
+        ArrayBufferObject::obj_enumerate,
+        nullptr,          /* thisObject      */
+    }
 };
 
 JSObject *
 js_InitSharedArrayBufferClass(JSContext *cx, HandleObject obj)
 {
     JS_ASSERT(obj->isNative());
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
     RootedObject proto(cx, global->createBlankPrototype(cx, &SharedArrayBufferObject::protoClass));
--- a/js/src/vm/SharedArrayObject.h
+++ b/js/src/vm/SharedArrayObject.h
@@ -69,19 +69,18 @@ class SharedArrayBufferObject : public A
 {
     static bool byteLengthGetterImpl(JSContext *cx, CallArgs args);
 
   public:
     static const Class class_;
     static const Class protoClass;
 
     // Slot used for storing a pointer to the SharedArrayRawBuffer.
-    static const uint8_t RAWBUF_SLOT = ArrayBufferObject::RESERVED_SLOTS;
-
-    static const uint8_t RESERVED_SLOTS = ArrayBufferObject::RESERVED_SLOTS + 1;
+    // First two slots hold the ObjectElements.
+    static const int32_t RAWBUF_SLOT = 2;
 
     static bool class_constructor(JSContext *cx, unsigned argc, Value *vp);
 
     // Create a SharedArrayBufferObject with a new SharedArrayRawBuffer.
     static JSObject *New(JSContext *cx, uint32_t length);
 
     // Create a SharedArrayBufferObject using an existing SharedArrayRawBuffer.
     static JSObject *New(JSContext *cx, SharedArrayRawBuffer *buffer);
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -980,18 +980,16 @@ JSStructuredCloneWriter::writeTransferMa
             return false;
 
         // Emit a placeholder pointer. We will steal the data and neuter the
         // transferable later, in the case of ArrayBufferObject.
         if (!out.writePair(SCTAG_TRANSFER_MAP_ENTRY, SCTAG_TM_UNFILLED))
             return false;
         if (!out.writePtr(nullptr)) // Pointer to ArrayBuffer contents or to SharedArrayRawBuffer.
             return false;
-        if (!out.write(0)) // |byteLength| for an ArrayBuffer, 0 for SharedArrayBuffer
-            return false;
         if (!out.write(0)) // |userdata|, intended to be passed to callbacks.
             return false;
     }
 
     return true;
 }
 
 bool
@@ -1011,38 +1009,36 @@ JSStructuredCloneWriter::transferOwnersh
 
     for (JS::AutoObjectVector::Range tr = transferableObjects.all(); !tr.empty(); tr.popFront()) {
         RootedObject obj(context(), tr.front());
 
         MOZ_ASSERT(uint32_t(LittleEndian::readUint64(point) >> 32) == SCTAG_TRANSFER_MAP_ENTRY);
         MOZ_ASSERT(uint32_t(LittleEndian::readUint64(point)) == SCTAG_TM_UNFILLED);
 
         if (obj->is<ArrayBufferObject>()) {
-            size_t nbytes = obj->as<ArrayBufferObject>().byteLength();
-            void *contents = JS_StealArrayBufferContents(context(), obj);
-            if (!contents)
+            void *content;
+            uint8_t *data;
+            if (!JS_StealArrayBufferContents(context(), obj, &content, &data))
                 return false; // Destructor will clean up the already-transferred data
 
             uint64_t entryTag = PairToUInt64(SCTAG_TRANSFER_MAP_ENTRY, SCTAG_TM_ALLOC_DATA);
             LittleEndian::writeUint64(point++, entryTag);
-            LittleEndian::writeUint64(point++, reinterpret_cast<uint64_t>(contents));
-            LittleEndian::writeUint64(point++, nbytes);
+            LittleEndian::writeUint64(point++, reinterpret_cast<uint64_t>(content));
             LittleEndian::writeUint64(point++, 0);
         } else {
             SharedArrayRawBuffer *rawbuf = obj->as<SharedArrayBufferObject>().rawBufferObject();
 
             // Avoids a race condition where the parent thread frees the buffer
             // before the child has accepted the transferable.
             rawbuf->addReference();
 
             uint64_t entryTag = PairToUInt64(SCTAG_TRANSFER_MAP_ENTRY, SCTAG_TM_SHARED_BUFFER);
             LittleEndian::writeUint64(point++, entryTag);
             LittleEndian::writeUint64(point++, reinterpret_cast<uint64_t>(rawbuf));
             LittleEndian::writeUint64(point++, 0);
-            LittleEndian::writeUint64(point++, 0);
         }
     }
 
     JS_ASSERT(point <= out.rawBuffer() + out.count());
     JS_ASSERT_IF(point < out.rawBuffer() + out.count(),
                  uint32_t(LittleEndian::readUint64(point) >> 32) != SCTAG_TRANSFER_MAP_ENTRY);
 
     return true;
@@ -1239,23 +1235,23 @@ JSStructuredCloneReader::readArrayBuffer
     JS_ASSERT(buffer.byteLength() == nbytes);
     return in.readArray(buffer.dataPointer(), nbytes);
 }
 
 bool
 JSStructuredCloneReader::readMappedArrayBuffer(Value *vp, uint32_t fd,
                                                uint32_t offset, uint32_t length)
 {
+    void *ptr;
     int new_fd;
-    void *ptr = JS_CreateMappedArrayBufferContents(fd, &new_fd, offset, length);
-    if (!ptr) {
+    if(!JS_CreateMappedArrayBufferContents(fd, &new_fd, offset, length, &ptr)) {
         JS_ReportError(context(), "Failed to create mapped array buffer contents");
         return false;
     }
-    JSObject *obj = JS_NewArrayBufferWithContents(context(), length, ptr, /* mapped = */ true);
+    JSObject *obj = JS_NewArrayBufferWithContents(context(), ptr);
     if (!obj) {
         JS_ReleaseMappedArrayBufferContents(new_fd, ptr, length);
         return false;
     }
     vp->setObject(*obj);
 
     return true;
 }
@@ -1547,28 +1543,24 @@ JSStructuredCloneReader::readTransferMap
             return false;
         JS_ASSERT(tag == SCTAG_TRANSFER_MAP_ENTRY);
         JS_ASSERT(data == SCTAG_TM_ALLOC_DATA || data == SCTAG_TM_SHARED_BUFFER);
 
         void *content;
         if (!in.readPtr(&content))
             return false;
 
-        uint64_t nbytes;
-        if (!in.read(&nbytes))
-            return false;
-
         uint64_t userdata;
         if (!in.read(&userdata))
             return false;
 
         RootedObject obj(context());
 
         if (data == SCTAG_TM_ALLOC_DATA)
-            obj = JS_NewArrayBufferWithContents(context(), nbytes, content);
+            obj = JS_NewArrayBufferWithContents(context(), content);
         else if (data == SCTAG_TM_SHARED_BUFFER)
             obj = SharedArrayBufferObject::New(context(), (SharedArrayRawBuffer *)content);
 
         if (!obj)
             return false;
 
         // Rewind to the SCTAG_TRANSFER_MAP_ENTRY and mark this entry as unowned by
         // the input buffer.
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -344,16 +344,17 @@ class TypedArrayObjectTemplate : public 
 
         Rooted<ArrayBufferObject *> buffer(cx, &AsArrayBuffer(bufobj));
 
         InitArrayBufferViewDataPointer(obj, buffer, byteOffset);
         obj->setSlot(LENGTH_SLOT, Int32Value(len));
         obj->setSlot(BYTEOFFSET_SLOT, Int32Value(byteOffset));
         obj->setSlot(BYTELENGTH_SLOT, Int32Value(len * sizeof(NativeType)));
         obj->setSlot(NEXT_VIEW_SLOT, PrivateValue(nullptr));
+        obj->setSlot(NEXT_BUFFER_SLOT, PrivateValue(UNSET_BUFFER_LINK));
 
         js::Shape *empty = EmptyShape::getInitialShape(cx, fastClass(),
                                                        obj->getProto(), obj->getParent(), obj->getMetadata(),
                                                        gc::FINALIZE_OBJECT8_BACKGROUND);
         if (!empty)
             return nullptr;
         obj->setLastPropertyInfallible(empty);
 
@@ -1332,16 +1333,17 @@ DataViewObject::create(JSContext *cx, ui
         }
     }
 
     DataViewObject &dvobj = obj->as<DataViewObject>();
     dvobj.setFixedSlot(BYTEOFFSET_SLOT, Int32Value(byteOffset));
     dvobj.setFixedSlot(BYTELENGTH_SLOT, Int32Value(byteLength));
     dvobj.setFixedSlot(BUFFER_SLOT, ObjectValue(*arrayBuffer));
     dvobj.setFixedSlot(NEXT_VIEW_SLOT, PrivateValue(nullptr));
+    dvobj.setFixedSlot(NEXT_BUFFER_SLOT, PrivateValue(UNSET_BUFFER_LINK));
     InitArrayBufferViewDataPointer(&dvobj, arrayBuffer, byteOffset);
     JS_ASSERT(byteOffset + byteLength <= arrayBuffer->byteLength());
 
     // Verify that the private slot is at the expected place
     JS_ASSERT(dvobj.numFixedSlots() == DATA_SLOT);
 
     arrayBuffer->addView(&dvobj);
 
--- a/js/src/vm/TypedArrayObject.h
+++ b/js/src/vm/TypedArrayObject.h
@@ -332,17 +332,17 @@ ClampIntForUint8Array(int32_t x)
 {
     if (x < 0)
         return 0;
     if (x > 255)
         return 255;
     return x;
 }
 
-bool ToDoubleForTypedArray(JSContext *cx, JS::HandleValue vp, double *d);
+extern js::ArrayBufferObject * const UNSET_BUFFER_LINK;
 
 } // namespace js
 
 template <>
 inline bool
 JSObject::is<js::TypedArrayObject>() const
 {
     return js::IsTypedArrayClass(getClass());