Bug 1246597 - support DataView on SharedArrayBuffer. r=waldo
authorLars T Hansen <lhansen@mozilla.com>
Wed, 07 Dec 2016 14:21:24 -1000
changeset 325451 9287824003f42411c84cdf4b7b3506371c57a582
parent 325450 3193092d26a96dddb7f112a08d7277c83d9e8f3e
child 325452 195138298619f5ee4c99dd17df0df98737762a25
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewerswaldo
bugs1246597
milestone53.0a1
Bug 1246597 - support DataView on SharedArrayBuffer. r=waldo
js/src/jsfriendapi.h
js/src/tests/js1_8_5/extensions/shareddataview.js
js/src/vm/ArrayBufferObject.cpp
js/src/vm/ArrayBufferObject.h
js/src/vm/SharedMem.h
js/src/vm/TypedArrayObject.cpp
js/src/vm/TypedArrayObject.h
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1973,17 +1973,17 @@ JS_ArrayBufferHasData(JSObject* obj);
  * another thread. Furthermore, the pointer can become invalid on GC (if the
  * data is small and fits inside the array's GC header), so callers must take
  * care not to hold on across anything that could GC.
  *
  * |obj| must have passed a JS_IsArrayBufferObject test, or somehow be known
  * that it would pass such a test: it is an ArrayBuffer or a wrapper of an
  * ArrayBuffer, and the unwrapping will succeed.
  *
- * *isSharedMemory will be set to false, the argument is present to simplify
+ * |*isSharedMemory| will be set to false, the argument is present to simplify
  * its use from code that also interacts with SharedArrayBuffer.
  */
 extern JS_FRIEND_API(uint8_t*)
 JS_GetArrayBufferData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&);
 
 /**
  * Check whether the obj is ArrayBufferObject and memory mapped. Note that this
  * may return false if a security wrapper is encountered that denies the
@@ -2043,17 +2043,17 @@ JS_GetArrayBufferViewByteLength(JSObject
  * another thread. Furthermore, the pointer can become invalid on GC (if the
  * data is small and fits inside the array's GC header), so callers must take
  * care not to hold on across anything that could GC.
  *
  * |obj| must have passed a JS_Is*Array test, or somehow be known that it would
  * pass such a test: it is a typed array or a wrapper of a typed array, and the
  * unwrapping will succeed.
  *
- * *isSharedMemory will be set to true if the typed array maps a
+ * |*isSharedMemory| will be set to true if the typed array maps a
  * SharedArrayBuffer, otherwise to false.
  */
 
 extern JS_FRIEND_API(int8_t*)
 JS_GetInt8ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&);
 extern JS_FRIEND_API(uint8_t*)
 JS_GetUint8ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&);
 extern JS_FRIEND_API(uint8_t*)
@@ -2105,24 +2105,24 @@ JS_IsDetachedArrayBufferObject(JSObject*
 
 /**
  * Check whether obj supports JS_GetDataView* APIs.
  */
 JS_FRIEND_API(bool)
 JS_IsDataViewObject(JSObject* obj);
 
 /**
- * Create a new DataView using the given ArrayBuffer for storage. The given
- * buffer must be an ArrayBuffer (or a cross-compartment wrapper of an
- * ArrayBuffer), and the offset and length must fit within the bounds of the
- * arrayBuffer. Currently, nullptr will be returned and an exception will be
- * thrown if these conditions do not hold, but do not depend on that behavior.
+ * Create a new DataView using the given buffer for storage. The given buffer
+ * must be an ArrayBuffer or SharedArrayBuffer (or a cross-compartment wrapper
+ * of either type), and the offset and length must fit within the bounds of the
+ * buffer. Currently, nullptr will be returned and an exception will be thrown
+ * if these conditions do not hold, but do not depend on that behavior.
  */
 JS_FRIEND_API(JSObject*)
-JS_NewDataView(JSContext* cx, JS::HandleObject arrayBuffer, uint32_t byteOffset, int32_t byteLength);
+JS_NewDataView(JSContext* cx, JS::HandleObject buffer, uint32_t byteOffset, int32_t byteLength);
 
 /**
  * Return the byte offset of a data view into its array buffer. |obj| must be a
  * DataView.
  *
  * |obj| must have passed a JS_IsDataViewObject test, or somehow be known that
  * it would pass such a test: it is a data view or a wrapper of a data view,
  * and the unwrapping will succeed.
@@ -2143,19 +2143,22 @@ JS_GetDataViewByteLength(JSObject* obj);
 
 /**
  * Return a pointer to the beginning of the data referenced by a DataView.
  *
  * |obj| must have passed a JS_IsDataViewObject test, or somehow be known that
  * it would pass such a test: it is a data view or a wrapper of a data view,
  * and the unwrapping will succeed. If cx is nullptr, then DEBUG builds may be
  * unable to assert when unwrapping should be disallowed.
+ *
+ * |*isSharedMemory| will be set to true if the DataView maps a SharedArrayBuffer,
+ * otherwise to false.
  */
 JS_FRIEND_API(void*)
-JS_GetDataViewData(JSObject* obj, const JS::AutoCheckCannotGC&);
+JS_GetDataViewData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&);
 
 namespace js {
 
 /**
  * Add a watchpoint -- in the Object.prototype.watch sense -- to |obj| for the
  * property |id|, using the callable object |callable| as the function to be
  * called for notifications.
  *
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/extensions/shareddataview.js
@@ -0,0 +1,44 @@
+// |reftest| skip-if(!xulRuntime.shell)
+/* -*- Mode: js2; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test DataView on SharedArrayBuffer.
+
+if (!this.SharedArrayBuffer) {
+    reportCompare(true,true);
+    quit(0);
+}
+
+var sab = new SharedArrayBuffer(4096);
+var dv = new DataView(sab);
+
+assertEq(sab, dv.buffer);
+assertEq(dv.byteLength, sab.byteLength);
+assertEq(ArrayBuffer.isView(dv), true);
+
+var dv2 = new DataView(sab, 1075, 2048);
+
+assertEq(sab, dv2.buffer);
+assertEq(dv2.byteLength, 2048);
+assertEq(dv2.byteOffset, 1075);
+assertEq(ArrayBuffer.isView(dv2), true);
+
+// Test that it is the same buffer memory for the two views
+
+dv.setInt8(1075, 37);
+assertEq(dv2.getInt8(0), 37);
+
+// Test that range checking works
+
+assertThrowsInstanceOf(() => dv.setInt32(4098, -1), RangeError);
+assertThrowsInstanceOf(() => dv.setInt32(4094, -1), RangeError);
+assertThrowsInstanceOf(() => dv.setInt32(-1, -1), RangeError);
+
+assertThrowsInstanceOf(() => dv2.setInt32(2080, -1), RangeError);
+assertThrowsInstanceOf(() => dv2.setInt32(2046, -1), RangeError);
+assertThrowsInstanceOf(() => dv2.setInt32(-1, -1), RangeError);
+
+reportCompare(true,true);
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -222,16 +222,48 @@ js::AsArrayBuffer(HandleObject obj)
 
 ArrayBufferObject&
 js::AsArrayBuffer(JSObject* obj)
 {
     MOZ_ASSERT(IsArrayBuffer(obj));
     return obj->as<ArrayBufferObject>();
 }
 
+bool
+js::IsArrayBufferMaybeShared(HandleValue v)
+{
+    return v.isObject() && v.toObject().is<ArrayBufferObjectMaybeShared>();
+}
+
+bool
+js::IsArrayBufferMaybeShared(HandleObject obj)
+{
+    return obj->is<ArrayBufferObjectMaybeShared>();
+}
+
+bool
+js::IsArrayBufferMaybeShared(JSObject* obj)
+{
+    return obj->is<ArrayBufferObjectMaybeShared>();
+}
+
+ArrayBufferObjectMaybeShared&
+js::AsArrayBufferMaybeShared(HandleObject obj)
+{
+    MOZ_ASSERT(IsArrayBufferMaybeShared(obj));
+    return obj->as<ArrayBufferObjectMaybeShared>();
+}
+
+ArrayBufferObjectMaybeShared&
+js::AsArrayBufferMaybeShared(JSObject* obj)
+{
+    MOZ_ASSERT(IsArrayBufferMaybeShared(obj));
+    return obj->as<ArrayBufferObjectMaybeShared>();
+}
+
 MOZ_ALWAYS_INLINE bool
 ArrayBufferObject::byteLengthGetterImpl(JSContext* cx, const CallArgs& args)
 {
     MOZ_ASSERT(IsArrayBuffer(args.thisv()));
     args.rval().setInt32(args.thisv().toObject().as<ArrayBufferObject>().byteLength());
     return true;
 }
 
@@ -1105,46 +1137,47 @@ ArrayBufferObject::createEmpty(JSContext
 
     obj->initEmpty();
     return obj;
 }
 
 bool
 ArrayBufferObject::createDataViewForThisImpl(JSContext* cx, const CallArgs& args)
 {
-    MOZ_ASSERT(IsArrayBuffer(args.thisv()));
+    MOZ_ASSERT(IsArrayBufferMaybeShared(args.thisv()));
 
     /*
      * This method is only called for |DataView(alienBuf, ...)| which calls
      * this as |createDataViewForThis.call(alienBuf, byteOffset, byteLength,
      *                                     DataView.prototype)|,
      * ergo there must be exactly 3 arguments.
      */
     MOZ_ASSERT(args.length() == 3);
 
     uint32_t byteOffset = args[0].toPrivateUint32();
     uint32_t byteLength = args[1].toPrivateUint32();
-    Rooted<ArrayBufferObject*> buffer(cx, &args.thisv().toObject().as<ArrayBufferObject>());
+    Rooted<ArrayBufferObjectMaybeShared*> buffer(cx);
+    buffer = &args.thisv().toObject().as<ArrayBufferObjectMaybeShared>();
 
     /*
      * Pop off the passed-along prototype and delegate to normal DataViewObject
      * construction.
      */
     JSObject* obj = DataViewObject::create(cx, byteOffset, byteLength, buffer, &args[2].toObject());
     if (!obj)
         return false;
     args.rval().setObject(*obj);
     return true;
 }
 
 bool
 ArrayBufferObject::createDataViewForThis(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    return CallNonGenericMethod<IsArrayBuffer, createDataViewForThisImpl>(cx, args);
+    return CallNonGenericMethod<IsArrayBufferMaybeShared, createDataViewForThisImpl>(cx, args);
 }
 
 /* static */ ArrayBufferObject::BufferContents
 ArrayBufferObject::externalizeContents(JSContext* cx, Handle<ArrayBufferObject*> buffer,
                                        bool hasStealableContents)
 {
     MOZ_ASSERT(buffer->isPlain(), "Only support doing this on plain ABOs");
     MOZ_ASSERT(!buffer->isDetached(), "must have contents to externalize");
@@ -1526,31 +1559,35 @@ JSObject::is<js::ArrayBufferObjectMaybeS
 {
     return is<ArrayBufferObject>() || is<SharedArrayBufferObject>();
 }
 
 void
 ArrayBufferViewObject::notifyBufferDetached(JSContext* cx, void* newData)
 {
     if (is<DataViewObject>()) {
+        if (as<DataViewObject>().isSharedMemory())
+            return;
         as<DataViewObject>().notifyBufferDetached(newData);
     } else if (is<TypedArrayObject>()) {
         if (as<TypedArrayObject>().isSharedMemory())
             return;
         as<TypedArrayObject>().notifyBufferDetached(cx, newData);
     } else {
         as<OutlineTypedObject>().notifyBufferDetached(newData);
     }
 }
 
 uint8_t*
 ArrayBufferViewObject::dataPointerUnshared(const JS::AutoRequireNoGC& nogc)
 {
-    if (is<DataViewObject>())
-        return static_cast<uint8_t*>(as<DataViewObject>().dataPointer());
+    if (is<DataViewObject>()) {
+        MOZ_ASSERT(!as<DataViewObject>().isSharedMemory());
+        return static_cast<uint8_t*>(as<DataViewObject>().dataPointerUnshared());
+    }
     if (is<TypedArrayObject>()) {
         MOZ_ASSERT(!as<TypedArrayObject>().isSharedMemory());
         return static_cast<uint8_t*>(as<TypedArrayObject>().viewDataUnshared());
     }
     return as<TypedObject>().typedMem(nogc);
 }
 
 #ifdef DEBUG
@@ -1562,16 +1599,17 @@ ArrayBufferViewObject::isSharedMemory()
     return false;
 }
 #endif
 
 void
 ArrayBufferViewObject::setDataPointerUnshared(uint8_t* data)
 {
     if (is<DataViewObject>()) {
+        MOZ_ASSERT(!as<DataViewObject>().isSharedMemory());
         as<DataViewObject>().setPrivate(data);
     } else if (is<TypedArrayObject>()) {
         MOZ_ASSERT(!as<TypedArrayObject>().isSharedMemory());
         as<TypedArrayObject>().setPrivate(data);
     } else if (is<OutlineTypedObject>()) {
         as<OutlineTypedObject>().setData(data);
     } else {
         MOZ_CRASH();
@@ -1583,17 +1621,17 @@ ArrayBufferViewObject::bufferObject(JSCo
 {
     if (thisObject->is<TypedArrayObject>()) {
         Rooted<TypedArrayObject*> typedArray(cx, &thisObject->as<TypedArrayObject>());
         if (!TypedArrayObject::ensureHasBuffer(cx, typedArray))
             return nullptr;
         return thisObject->as<TypedArrayObject>().bufferEither();
     }
     MOZ_ASSERT(thisObject->is<DataViewObject>());
-    return &thisObject->as<DataViewObject>().arrayBuffer();
+    return &thisObject->as<DataViewObject>().arrayBufferEither();
 }
 
 /* JS Friend API */
 
 JS_FRIEND_API(bool)
 JS_IsArrayBufferViewObject(JSObject* obj)
 {
     obj = CheckedUnwrap(obj);
@@ -1834,22 +1872,23 @@ JS_IsMappedArrayBufferObject(JSObject* o
 
 JS_FRIEND_API(void*)
 JS_GetArrayBufferViewData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
 {
     obj = CheckedUnwrap(obj);
     if (!obj)
         return nullptr;
     if (obj->is<DataViewObject>()) {
-        *isSharedMemory = false;
-        return obj->as<DataViewObject>().dataPointer();
+        DataViewObject& dv = obj->as<DataViewObject>();
+        *isSharedMemory = dv.isSharedMemory();
+        return dv.dataPointerEither().unwrap(/*safe - caller sees isSharedMemory flag*/);
     }
     TypedArrayObject& ta = obj->as<TypedArrayObject>();
     *isSharedMemory = ta.isSharedMemory();
-    return ta.viewDataEither().unwrap(/*safe - caller sees isShared flag*/);
+    return ta.viewDataEither().unwrap(/*safe - caller sees isSharedMemory flag*/);
 }
 
 JS_FRIEND_API(JSObject*)
 JS_GetArrayBufferViewBuffer(JSContext* cx, HandleObject objArg, bool* isSharedMemory)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, objArg);
@@ -1884,32 +1923,36 @@ JS_GetObjectAsArrayBufferView(JSObject* 
     if (!(obj->is<ArrayBufferViewObject>()))
         return nullptr;
 
     js::GetArrayBufferViewLengthAndData(obj, length, isSharedMemory, data);
     return obj;
 }
 
 JS_FRIEND_API(void)
-js::GetArrayBufferViewLengthAndData(JSObject* obj, uint32_t* length, bool* isSharedMemory, uint8_t** data)
+js::GetArrayBufferViewLengthAndData(JSObject* obj, uint32_t* length, bool* isSharedMemory,
+                                    uint8_t** data)
 {
     MOZ_ASSERT(obj->is<ArrayBufferViewObject>());
 
     *length = obj->is<DataViewObject>()
               ? obj->as<DataViewObject>().byteLength()
               : obj->as<TypedArrayObject>().byteLength();
 
     if (obj->is<DataViewObject>()) {
-        *isSharedMemory = false;
-        *data = static_cast<uint8_t*>(obj->as<DataViewObject>().dataPointer());
+        DataViewObject& dv = obj->as<DataViewObject>();
+        *isSharedMemory = dv.isSharedMemory();
+        *data = static_cast<uint8_t*>(
+            dv.dataPointerEither().unwrap(/*safe - caller sees isShared flag*/));
     }
     else {
         TypedArrayObject& ta = obj->as<TypedArrayObject>();
         *isSharedMemory = ta.isSharedMemory();
-        *data = static_cast<uint8_t*>(ta.viewDataEither().unwrap(/*safe - caller sees isShared flag*/));
+        *data = static_cast<uint8_t*>(
+            ta.viewDataEither().unwrap(/*safe - caller sees isShared flag*/));
     }
 }
 
 JS_FRIEND_API(JSObject*)
 JS_GetObjectAsArrayBuffer(JSObject* obj, uint32_t* length, uint8_t** data)
 {
     if (!(obj = CheckedUnwrap(obj)))
         return nullptr;
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -249,22 +249,22 @@ class ArrayBufferObject : public ArrayBu
     static ArrayBufferObject* create(JSContext* cx, uint32_t nbytes,
                                      HandleObject proto = nullptr,
                                      NewObjectKind newKind = GenericObject);
 
     // Create an ArrayBufferObject that is safely finalizable and can later be
     // initialize()d to become a real, content-visible ArrayBufferObject.
     static ArrayBufferObject* createEmpty(JSContext* cx);
 
+    // Also valid when the buffer is a SharedArrayBuffer.
     static bool createDataViewForThisImpl(JSContext* cx, const CallArgs& args);
     static bool createDataViewForThis(JSContext* cx, unsigned argc, Value* vp);
 
     template<typename T>
     static bool createTypedArrayFromBufferImpl(JSContext* cx, const CallArgs& args);
-
     template<typename T>
     static bool createTypedArrayFromBuffer(JSContext* cx, unsigned argc, Value* vp);
 
     static void copyData(Handle<ArrayBufferObject*> toBuffer,
                          Handle<ArrayBufferObject*> fromBuffer,
                          uint32_t fromIndex, uint32_t count);
 
     static void trace(JSTracer* trc, JSObject* obj);
@@ -447,16 +447,25 @@ ToClampedIndex(JSContext* cx, HandleValu
  * Tests for ArrayBufferObject, like obj->is<ArrayBufferObject>().
  */
 bool IsArrayBuffer(HandleValue v);
 bool IsArrayBuffer(HandleObject obj);
 bool IsArrayBuffer(JSObject* obj);
 ArrayBufferObject& AsArrayBuffer(HandleObject obj);
 ArrayBufferObject& AsArrayBuffer(JSObject* obj);
 
+/*
+ * Ditto for ArrayBufferObjectMaybeShared.
+ */
+bool IsArrayBufferMaybeShared(HandleValue v);
+bool IsArrayBufferMaybeShared(HandleObject obj);
+bool IsArrayBufferMaybeShared(JSObject* obj);
+ArrayBufferObjectMaybeShared& AsArrayBufferMaybeShared(HandleObject obj);
+ArrayBufferObjectMaybeShared& AsArrayBufferMaybeShared(JSObject* obj);
+
 extern uint32_t JS_FASTCALL
 ClampDoubleToUint8(const double x);
 
 struct uint8_clamped {
     uint8_t val;
 
     uint8_clamped() { }
     uint8_clamped(const uint8_clamped& other) : val(other.val) { }
--- a/js/src/vm/SharedMem.h
+++ b/js/src/vm/SharedMem.h
@@ -64,16 +64,17 @@ class SharedMem
         ptr_ = that.ptr_;
 #ifdef DEBUG
         sharedness_ = that.sharedness_;
 #endif
         return *this;
     }
 
     // Reinterpret-cast the pointer to type U, preserving sharedness.
+    // Eg, "obj->dataPointerEither().cast<uint8_t*>()" yields a SharedMem<uint8_t*>.
     template<typename U>
     inline SharedMem<U> cast() const {
 #ifdef DEBUG
         MOZ_ASSERT(asValue() % sizeof(mozilla::Conditional<mozilla::IsVoid<typename mozilla::RemovePointer<U>::Type>::value,
                                                            char,
                                                            typename mozilla::RemovePointer<U>::Type>) == 0);
         if (sharedness_ == IsUnshared)
             return SharedMem<U>::unshared(unwrap());
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -34,16 +34,17 @@
 #include "gc/Marking.h"
 #include "jit/InlinableNatives.h"
 #include "js/Conversions.h"
 #include "vm/ArrayBufferObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/PIC.h"
 #include "vm/SelfHosting.h"
+#include "vm/SharedMem.h"
 #include "vm/TypedArrayCommon.h"
 #include "vm/WrapperObject.h"
 
 #include "jsatominlines.h"
 
 #include "gc/Nursery-inl.h"
 #include "gc/StoreBuffer-inl.h"
 #include "vm/ArrayBufferObject-inl.h"
@@ -1647,22 +1648,21 @@ DataViewNewObjectKind(JSContext* cx, uin
     JSScript* script = cx->currentScript(&pc);
     if (script && ObjectGroup::useSingletonForAllocationSite(script, pc, &DataViewObject::class_))
         return SingletonObject;
     return GenericObject;
 }
 
 DataViewObject*
 DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
-                       Handle<ArrayBufferObject*> arrayBuffer, JSObject* protoArg)
+                       Handle<ArrayBufferObjectMaybeShared*> arrayBuffer, JSObject* protoArg)
 {
     MOZ_ASSERT(byteOffset <= INT32_MAX);
     MOZ_ASSERT(byteLength <= INT32_MAX);
     MOZ_ASSERT(byteOffset + byteLength < UINT32_MAX);
-    MOZ_ASSERT(!arrayBuffer || !arrayBuffer->is<SharedArrayBufferObject>());
 
     RootedObject proto(cx, protoArg);
     RootedObject obj(cx);
 
     NewObjectKind newKind = DataViewNewObjectKind(cx, byteLength, proto);
     obj = NewObjectWithClassProto(cx, &class_, proto, newKind);
     if (!obj)
         return nullptr;
@@ -1683,46 +1683,72 @@ DataViewObject::create(JSContext* cx, ui
 
     // Caller should have established these preconditions, and no
     // (non-self-hosted) JS code has had an opportunity to run so nothing can
     // have invalidated them.
     MOZ_ASSERT(byteOffset <= arrayBuffer->byteLength());
     MOZ_ASSERT(byteOffset + byteLength <= arrayBuffer->byteLength());
 
     DataViewObject& dvobj = obj->as<DataViewObject>();
+
+    // The isSharedMemory property is invariant.  Self-hosting code that sets
+    // BUFFER_SLOT or the private slot (if it does) must maintain it by always
+    // setting those to reference shared memory.
+    bool isSharedMemory = IsSharedArrayBuffer(arrayBuffer.get());
+    if (isSharedMemory)
+        dvobj.setIsSharedMemory();
+
     dvobj.setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(byteOffset));
     dvobj.setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(byteLength));
     dvobj.setFixedSlot(TypedArrayObject::BUFFER_SLOT, ObjectValue(*arrayBuffer));
-    dvobj.initPrivate(arrayBuffer->dataPointer() + byteOffset);
+
+    SharedMem<uint8_t*> ptr = arrayBuffer->dataPointerEither();
+    // A pointer to raw shared memory is exposed through the private slot.  This
+    // is safe so long as getPrivate() is not used willy-nilly.  It is wrapped in
+    // other accessors in TypedArrayObject.h.
+    dvobj.initPrivate(ptr.unwrap(/*safe - see above*/) + byteOffset);
 
     // Include a barrier if the data view's data pointer is in the nursery, as
     // is done for typed arrays.
-    if (!IsInsideNursery(obj) && cx->runtime()->gc.nursery.isInside(arrayBuffer->dataPointer()))
-        cx->runtime()->gc.storeBuffer.putWholeCell(obj);
+    if (!IsInsideNursery(obj) && cx->runtime()->gc.nursery.isInside(ptr)) {
+        // Shared buffer data should never be nursery-allocated, so we
+        // need to fail here if isSharedMemory.  However, mmap() can
+        // place a SharedArrayRawBuffer up against the bottom end of a
+        // nursery chunk, and a zero-length buffer will erroneously be
+        // perceived as being inside the nursery; sidestep that.
+        if (isSharedMemory) {
+            MOZ_ASSERT(arrayBuffer->byteLength() == 0 &&
+                       (uintptr_t(ptr.unwrapValue()) & gc::ChunkMask) == 0);
+        } else {
+            cx->runtime()->gc.storeBuffer.putWholeCell(obj);
+        }
+    }
 
     // Verify that the private slot is at the expected place
     MOZ_ASSERT(dvobj.numFixedSlots() == TypedArrayObject::DATA_SLOT);
 
-    if (!arrayBuffer->addView(cx, &dvobj))
-        return nullptr;
+    if (arrayBuffer->is<ArrayBufferObject>()) {
+        if (!arrayBuffer->as<ArrayBufferObject>().addView(cx, &dvobj))
+            return nullptr;
+    }
 
     return &dvobj;
 }
 
 bool
 DataViewObject::getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, const CallArgs& args,
                                            uint32_t* byteOffsetPtr, uint32_t* byteLengthPtr)
 {
-    if (!IsArrayBuffer(bufobj)) {
+    if (!IsArrayBufferMaybeShared(bufobj)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
                                   "DataView", "ArrayBuffer", bufobj->getClass()->name);
         return false;
     }
 
-    Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(bufobj));
+    Rooted<ArrayBufferObjectMaybeShared*> buffer(cx, &AsArrayBufferMaybeShared(bufobj));
     uint32_t byteOffset = 0;
     uint32_t byteLength = buffer->byteLength();
 
     if (args.length() > 1) {
         if (!ToUint32(cx, args[1], &byteOffset))
             return false;
         if (byteOffset > INT32_MAX) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE,
@@ -1785,17 +1811,17 @@ DataViewObject::constructSameCompartment
     if (!getAndCheckConstructorArgs(cx, bufobj, args, &byteOffset, &byteLength))
         return false;
 
     RootedObject proto(cx);
     RootedObject newTarget(cx, &args.newTarget().toObject());
     if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
         return false;
 
-    Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(bufobj));
+    Rooted<ArrayBufferObjectMaybeShared*> buffer(cx, &AsArrayBufferMaybeShared(bufobj));
     JSObject* obj = DataViewObject::create(cx, byteOffset, byteLength, buffer, proto);
     if (!obj)
         return false;
     args.rval().setObject(*obj);
     return true;
 }
 
 // Create a DataView object in another compartment.
@@ -1872,30 +1898,32 @@ DataViewObject::class_constructor(JSCont
         return false;
 
     if (bufobj->is<WrapperObject>())
         return constructWrapped(cx, bufobj, args);
     return constructSameCompartment(cx, bufobj, args);
 }
 
 template <typename NativeType>
-/* static */ uint8_t*
-DataViewObject::getDataPointer(JSContext* cx, Handle<DataViewObject*> obj, double offset)
+/* static */ SharedMem<uint8_t*>
+DataViewObject::getDataPointer(JSContext* cx, Handle<DataViewObject*> obj, double offset,
+                               bool* isSharedMemory)
 {
     MOZ_ASSERT(offset >= 0);
 
     const size_t TypeSize = sizeof(NativeType);
     if (offset > UINT32_MAX - TypeSize || offset + TypeSize > obj->byteLength()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE,
                                   "1");
-        return nullptr;
+        return SharedMem<uint8_t*>::unshared(nullptr);
     }
 
     MOZ_ASSERT(offset < UINT32_MAX);
-    return static_cast<uint8_t*>(obj->dataPointer()) + uint32_t(offset);
+    *isSharedMemory = obj->isSharedMemory();
+    return obj->dataPointerEither().cast<uint8_t*>() + uint32_t(offset);
 }
 
 static inline bool
 needToSwapBytes(bool littleEndian)
 {
 #if MOZ_LITTLE_ENDIAN
     return !littleEndian;
 #else
@@ -1937,38 +1965,56 @@ template <> struct DataToRepType<int8_t>
 template <> struct DataToRepType<uint8_t>  { typedef uint8_t result; };
 template <> struct DataToRepType<int16_t>  { typedef uint16_t result; };
 template <> struct DataToRepType<uint16_t> { typedef uint16_t result; };
 template <> struct DataToRepType<int32_t>  { typedef uint32_t result; };
 template <> struct DataToRepType<uint32_t> { typedef uint32_t result; };
 template <> struct DataToRepType<float>    { typedef uint32_t result; };
 template <> struct DataToRepType<double>   { typedef uint64_t result; };
 
-template <typename DataType>
+static inline void
+Memcpy(uint8_t* dest, uint8_t* src, size_t nbytes)
+{
+    memcpy(dest, src, nbytes);
+}
+
+static inline void
+Memcpy(uint8_t* dest, SharedMem<uint8_t*> src, size_t nbytes)
+{
+    jit::AtomicOperations::memcpySafeWhenRacy(dest, src, nbytes);
+}
+
+static inline void
+Memcpy(SharedMem<uint8_t*> dest, uint8_t* src, size_t nbytes)
+{
+    jit::AtomicOperations::memcpySafeWhenRacy(dest, src, nbytes);
+}
+
+template <typename DataType, typename BufferPtrType>
 struct DataViewIO
 {
     typedef typename DataToRepType<DataType>::result ReadWriteType;
 
-    static void fromBuffer(DataType* dest, const uint8_t* unalignedBuffer, bool wantSwap)
+    static void fromBuffer(DataType* dest, BufferPtrType unalignedBuffer, bool wantSwap)
     {
         MOZ_ASSERT((reinterpret_cast<uintptr_t>(dest) & (Min<size_t>(MOZ_ALIGNOF(void*), sizeof(DataType)) - 1)) == 0);
-        memcpy((void*) dest, unalignedBuffer, sizeof(ReadWriteType));
+        Memcpy((uint8_t*) dest, unalignedBuffer, sizeof(ReadWriteType));
         if (wantSwap) {
             ReadWriteType* rwDest = reinterpret_cast<ReadWriteType*>(dest);
             *rwDest = swapBytes(*rwDest);
         }
     }
 
-    static void toBuffer(uint8_t* unalignedBuffer, const DataType* src, bool wantSwap)
+    static void toBuffer(BufferPtrType unalignedBuffer, const DataType* src, bool wantSwap)
     {
         MOZ_ASSERT((reinterpret_cast<uintptr_t>(src) & (Min<size_t>(MOZ_ALIGNOF(void*), sizeof(DataType)) - 1)) == 0);
         ReadWriteType temp = *reinterpret_cast<const ReadWriteType*>(src);
         if (wantSwap)
             temp = swapBytes(temp);
-        memcpy(unalignedBuffer, (void*) &temp, sizeof(ReadWriteType));
+        Memcpy(unalignedBuffer, (uint8_t*) &temp, sizeof(ReadWriteType));
     }
 };
 
 static bool
 ToIndex(JSContext* cx, HandleValue v, double* index)
 {
     if (v.isUndefined()) {
         *index = 0.0;
@@ -2004,28 +2050,36 @@ DataViewObject::read(JSContext* cx, Hand
     double getIndex;
     if (!ToIndex(cx, args.get(0), &getIndex))
         return false;
 
     // Step 5.
     bool isLittleEndian = args.length() >= 2 && ToBoolean(args[1]);
 
     // Steps 6-7.
-    if (obj->arrayBuffer().isDetached()) {
+    if (obj->arrayBufferEither().isDetached()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
         return false;
     }
 
     // Steps 8-12.
-    uint8_t* data = DataViewObject::getDataPointer<NativeType>(cx, obj, getIndex);
+    bool isSharedMemory;
+    SharedMem<uint8_t*> data = DataViewObject::getDataPointer<NativeType>(cx, obj, getIndex,
+                                                                          &isSharedMemory);
     if (!data)
         return false;
 
     // Step 13.
-    DataViewIO<NativeType>::fromBuffer(val, data, needToSwapBytes(isLittleEndian));
+    if (isSharedMemory) {
+        DataViewIO<NativeType, SharedMem<uint8_t*>>::fromBuffer(val, data,
+                                                                needToSwapBytes(isLittleEndian));
+    } else {
+        DataViewIO<NativeType, uint8_t*>::fromBuffer(val, data.unwrapUnshared(),
+                                                     needToSwapBytes(isLittleEndian));
+    }
     return true;
 }
 
 template <typename NativeType>
 static inline bool
 WebIDLCast(JSContext* cx, HandleValue value, NativeType* out)
 {
     int32_t temp;
@@ -2079,28 +2133,36 @@ DataViewObject::write(JSContext* cx, Han
     if (TypeIsFloatingPoint<NativeType>())
         value = JS::CanonicalizeNaN(value);
 #endif
 
     // Step 6.
     bool isLittleEndian = args.length() >= 3 && ToBoolean(args[2]);
 
     // Steps 7-8.
-    if (obj->arrayBuffer().isDetached()) {
+    if (obj->arrayBufferEither().isDetached()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
         return false;
     }
 
     // Steps 9-13.
-    uint8_t* data = DataViewObject::getDataPointer<NativeType>(cx, obj, getIndex);
+    bool isSharedMemory;
+    SharedMem<uint8_t*> data = DataViewObject::getDataPointer<NativeType>(cx, obj, getIndex,
+                                                                          &isSharedMemory);
     if (!data)
         return false;
 
     // Step 14.
-    DataViewIO<NativeType>::toBuffer(data, &value, needToSwapBytes(isLittleEndian));
+    if (isSharedMemory) {
+        DataViewIO<NativeType, SharedMem<uint8_t*>>::toBuffer(data, &value,
+                                                              needToSwapBytes(isLittleEndian));
+    } else {
+        DataViewIO<NativeType, uint8_t*>::toBuffer(data.unwrapUnshared(), &value,
+                                                   needToSwapBytes(isLittleEndian));
+    }
     return true;
 }
 
 bool
 DataViewObject::getInt8Impl(JSContext* cx, const CallArgs& args)
 {
     MOZ_ASSERT(is(args.thisv()));
 
@@ -3184,44 +3246,46 @@ JS_GetDataViewByteOffset(JSObject* obj)
 {
     obj = CheckedUnwrap(obj);
     if (!obj)
         return 0;
     return obj->as<DataViewObject>().byteOffset();
 }
 
 JS_FRIEND_API(void*)
-JS_GetDataViewData(JSObject* obj, const JS::AutoCheckCannotGC&)
+JS_GetDataViewData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
 {
     obj = CheckedUnwrap(obj);
     if (!obj)
         return nullptr;
-    return obj->as<DataViewObject>().dataPointer();
+    DataViewObject& dv = obj->as<DataViewObject>();
+    *isSharedMemory = dv.isSharedMemory();
+    return dv.dataPointerEither().unwrap(/*safe - caller sees isSharedMemory*/);
 }
 
 JS_FRIEND_API(uint32_t)
 JS_GetDataViewByteLength(JSObject* obj)
 {
     obj = CheckedUnwrap(obj);
     if (!obj)
         return 0;
     return obj->as<DataViewObject>().byteLength();
 }
 
 JS_FRIEND_API(JSObject*)
-JS_NewDataView(JSContext* cx, HandleObject arrayBuffer, uint32_t byteOffset, int32_t byteLength)
+JS_NewDataView(JSContext* cx, HandleObject buffer, uint32_t byteOffset, int32_t byteLength)
 {
     RootedObject constructor(cx);
     JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(&DataViewObject::class_);
     if (!GetBuiltinConstructor(cx, key, &constructor))
         return nullptr;
 
     FixedConstructArgs<3> cargs(cx);
 
-    cargs[0].setObject(*arrayBuffer);
+    cargs[0].setObject(*buffer);
     cargs[1].setNumber(byteOffset);
     cargs[2].setInt32(byteLength);
 
     RootedValue fun(cx, ObjectValue(*constructor));
     RootedObject obj(cx);
     if (!Construct(cx, fun, cargs, fun, &obj))
         return nullptr;
     return obj;
--- a/js/src/vm/TypedArrayObject.h
+++ b/js/src/vm/TypedArrayObject.h
@@ -411,28 +411,33 @@ TypedArrayElemSize(Scalar::Type viewType
 // memory.
 extern void
 SetDisjointTypedElements(TypedArrayObject* target, uint32_t targetOffset,
                          TypedArrayObject* unsafeSrcCrossCompartment);
 
 extern JSObject*
 InitDataViewClass(JSContext* cx, HandleObject obj);
 
+// In the DataViewObject, the private slot contains a raw pointer into
+// the buffer.  The buffer may be shared memory and the raw pointer
+// should not be exposed without sharedness information accompanying
+// it.
+
 class DataViewObject : public NativeObject
 {
   private:
     static const Class protoClass;
 
     static bool is(HandleValue v) {
         return v.isObject() && v.toObject().hasClass(&class_);
     }
 
     template <typename NativeType>
-    static uint8_t*
-    getDataPointer(JSContext* cx, Handle<DataViewObject*> obj, double offset);
+    static SharedMem<uint8_t*>
+    getDataPointer(JSContext* cx, Handle<DataViewObject*> obj, double offset, bool* isSharedMemory);
 
     template<Value ValueGetter(DataViewObject* view)>
     static bool
     getterImpl(JSContext* cx, const CallArgs& args);
 
     template<Value ValueGetter(DataViewObject* view)>
     static bool
     getter(JSContext* cx, unsigned argc, Value* vp);
@@ -444,17 +449,17 @@ class DataViewObject : public NativeObje
     static bool getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, const CallArgs& args,
                                            uint32_t *byteOffset, uint32_t* byteLength);
     static bool constructSameCompartment(JSContext* cx, HandleObject bufobj, const CallArgs& args);
     static bool constructWrapped(JSContext* cx, HandleObject bufobj, const CallArgs& args);
 
     friend bool ArrayBufferObject::createDataViewForThisImpl(JSContext* cx, const CallArgs& args);
     static DataViewObject*
     create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
-           Handle<ArrayBufferObject*> arrayBuffer, JSObject* proto);
+           Handle<ArrayBufferObjectMaybeShared*> arrayBuffer, JSObject* proto);
 
   public:
     static const Class class_;
 
     static Value byteOffsetValue(DataViewObject* view) {
         Value v = view->getFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT);
         MOZ_ASSERT(v.toInt32() >= 0);
         return v;
@@ -473,21 +478,35 @@ class DataViewObject : public NativeObje
     uint32_t byteOffset() const {
         return byteOffsetValue(const_cast<DataViewObject*>(this)).toInt32();
     }
 
     uint32_t byteLength() const {
         return byteLengthValue(const_cast<DataViewObject*>(this)).toInt32();
     }
 
-    ArrayBufferObject& arrayBuffer() const {
-        return bufferValue(const_cast<DataViewObject*>(this)).toObject().as<ArrayBufferObject>();
+    ArrayBufferObjectMaybeShared& arrayBufferEither() const {
+        return bufferValue(
+            const_cast<DataViewObject*>(this)).toObject().as<ArrayBufferObjectMaybeShared>();
     }
 
-    void* dataPointer() const {
+    SharedMem<void*> dataPointerEither() const {
+        void *p = getPrivate();
+        if (isSharedMemory())
+            return SharedMem<void*>::shared(p);
+        return SharedMem<void*>::unshared(p);
+    }
+
+    void* dataPointerUnshared() const {
+        MOZ_ASSERT(!isSharedMemory());
+        return getPrivate();
+    }
+
+    void* dataPointerShared() const {
+        MOZ_ASSERT(isSharedMemory());
         return getPrivate();
     }
 
     static bool class_constructor(JSContext* cx, unsigned argc, Value* vp);
 
     static bool getInt8Impl(JSContext* cx, const CallArgs& args);
     static bool fun_getInt8(JSContext* cx, unsigned argc, Value* vp);