Bug 1246597 - support DataView on SharedArrayBuffer. r=waldo
authorLars T Hansen <lhansen@mozilla.com>
Wed, 07 Dec 2016 14:21:24 -1000
changeset 370409 9287824003f42411c84cdf4b7b3506371c57a582
parent 370408 3193092d26a96dddb7f112a08d7277c83d9e8f3e
child 370410 195138298619f5ee4c99dd17df0df98737762a25
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswaldo
bugs1246597
milestone53.0a1
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
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);