Bug 933001 - Part 1/5 - Define SharedArrayBufferObject. r=sfink
authorSean Stangl <sstangl@mozilla.com>
Thu, 20 Feb 2014 14:43:03 -0800
changeset 171167 fe2e4e1be13c77e4ff5502d66c27496e0364857e
parent 171166 ac9709133b272c4774dd7a483025f5f5a19c7761
child 171168 d5ebf76f501095c46a1306dcd0d4de8797c2bb5e
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewerssfink
bugs933001
milestone30.0a1
Bug 933001 - Part 1/5 - Define SharedArrayBufferObject. r=sfink
js/src/gc/Marking.cpp
js/src/gc/Marking.h
js/src/jit/AsmJS.h
js/src/jsapi.cpp
js/src/jsgc.h
js/src/jsinferinlines.h
js/src/jsobjinlines.h
js/src/jsprototypes.h
js/src/moz.build
js/src/vm/ArrayBufferObject.cpp
js/src/vm/ArrayBufferObject.h
js/src/vm/GlobalObject.h
js/src/vm/ObjectImpl.cpp
js/src/vm/ObjectImpl.h
js/src/vm/SharedArrayObject.cpp
js/src/vm/SharedArrayObject.h
js/src/vm/TypedArrayObject.cpp
js/src/vm/TypedArrayObject.h
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -427,16 +427,17 @@ Is##base##AboutToBeFinalized(BarrieredPt
 }
 
 DeclMarkerImpl(BaseShape, BaseShape)
 DeclMarkerImpl(BaseShape, UnownedBaseShape)
 DeclMarkerImpl(JitCode, jit::JitCode)
 DeclMarkerImpl(Object, ArgumentsObject)
 DeclMarkerImpl(Object, ArrayBufferObject)
 DeclMarkerImpl(Object, ArrayBufferViewObject)
+DeclMarkerImpl(Object, SharedArrayBufferObject)
 DeclMarkerImpl(Object, DebugScopeObject)
 DeclMarkerImpl(Object, GlobalObject)
 DeclMarkerImpl(Object, JSObject)
 DeclMarkerImpl(Object, JSFunction)
 DeclMarkerImpl(Object, ObjectImpl)
 DeclMarkerImpl(Object, ScopeObject)
 DeclMarkerImpl(Script, JSScript)
 DeclMarkerImpl(LazyScript, LazyScript)
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -12,16 +12,17 @@
 class JSAtom;
 class JSLinearString;
 
 namespace js {
 
 class ArgumentsObject;
 class ArrayBufferObject;
 class ArrayBufferViewObject;
+class SharedArrayBufferObject;
 class BaseShape;
 class DebugScopeObject;
 struct GCMarker;
 class GlobalObject;
 class LazyScript;
 class ScopeObject;
 class Shape;
 class UnownedBaseShape;
@@ -92,16 +93,17 @@ bool Is##base##AboutToBeFinalized(type *
 bool Is##base##AboutToBeFinalized(BarrieredPtr<type> *thingp);                                 \
 
 DeclMarker(BaseShape, BaseShape)
 DeclMarker(BaseShape, UnownedBaseShape)
 DeclMarker(JitCode, jit::JitCode)
 DeclMarker(Object, ArgumentsObject)
 DeclMarker(Object, ArrayBufferObject)
 DeclMarker(Object, ArrayBufferViewObject)
+DeclMarker(Object, SharedArrayBufferObject)
 DeclMarker(Object, DebugScopeObject)
 DeclMarker(Object, GlobalObject)
 DeclMarker(Object, JSObject)
 DeclMarker(Object, JSFunction)
 DeclMarker(Object, ScopeObject)
 DeclMarker(Script, JSScript)
 DeclMarker(LazyScript, LazyScript)
 DeclMarker(Shape, Shape)
--- a/js/src/jit/AsmJS.h
+++ b/js/src/jit/AsmJS.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_AsmJS_h
 #define jit_AsmJS_h
 
 #include <stddef.h>
 
 #include "js/TypeDecls.h"
+#include "vm/ObjectImpl.h"
 
 namespace js {
 
 class ExclusiveContext;
 class AsmJSModule;
 class SPSProfiler;
 namespace frontend {
     template <typename ParseHandler> struct Parser;
@@ -86,17 +87,39 @@ RoundUpToNextValidAsmJSHeapLength(uint32
 extern bool
 IsValidAsmJSHeapLength(uint32_t length);
 
 #ifdef JS_CODEGEN_X64
 // On x64, the internal ArrayBuffer data array is inflated to 4GiB (only the
 // byteLength portion of which is accessible) so that out-of-bounds accesses
 // (made using a uint32 index) are guaranteed to raise a SIGSEGV.
 static const size_t AsmJSBufferProtectedSize = 4 * 1024ULL * 1024ULL * 1024ULL;
-#endif
+
+// To avoid dynamically checking bounds on each load/store, asm.js code relies
+// on the SIGSEGV handler in AsmJSSignalHandlers.cpp. However, this only works
+// if we can guarantee that *any* out-of-bounds access generates a fault. This
+// isn't generally true since an out-of-bounds access could land on other
+// Mozilla data. To overcome this on x64, we reserve an entire 4GB space,
+// making only the range [0, byteLength) accessible, and use a 32-bit unsigned
+// index into this space. (x86 and ARM require different tricks.)
+//
+// One complication is that we need to put an ObjectElements struct immediately
+// before the data array (as required by the general JSObject data structure).
+// Thus, we must stick a page before the elements to hold ObjectElements.
+//
+//   |<------------------------------ 4GB + 1 pages --------------------->|
+//           |<--- sizeof --->|<------------------- 4GB ----------------->|
+//
+//   | waste | ObjectElements | data array | inaccessible reserved memory |
+//                            ^            ^                              ^
+//                            |            \                             /
+//                      obj->elements       required to be page boundaries
+//
+static const size_t AsmJSMappedSize = AsmJSPageSize + AsmJSBufferProtectedSize;
+#endif // JS_CODEGEN_X64
 
 #ifdef JS_ION
 
 // Return whether asm.js optimization is inhibitted by the platform or
 // dynamically disabled:
 extern bool
 IsAsmJSCompilationAvailable(JSContext *cx, unsigned argc, JS::Value *vp);
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -73,16 +73,17 @@
 #include "vm/DateObject.h"
 #include "vm/Debugger.h"
 #include "vm/ErrorObject.h"
 #include "vm/Interpreter.h"
 #include "vm/NumericConversions.h"
 #include "vm/RegExpStatics.h"
 #include "vm/Runtime.h"
 #include "vm/Shape.h"
+#include "vm/SharedArrayObject.h"
 #include "vm/StopIterationObject.h"
 #include "vm/StringBuffer.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/WeakMapObject.h"
 #include "vm/WrapperObject.h"
 #include "vm/Xdr.h"
 #include "yarr/BumpPointerAllocator.h"
 
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -25,16 +25,17 @@ struct JSCompartment;
 class JSFlatString;
 class JSLinearString;
 
 namespace js {
 
 class ArgumentsObject;
 class ArrayBufferObject;
 class ArrayBufferViewObject;
+class SharedArrayBufferObject;
 class BaseShape;
 class DebugScopeObject;
 class GCHelperThread;
 class GlobalObject;
 class LazyScript;
 class Nursery;
 class PropertyName;
 class ScopeObject;
@@ -127,16 +128,17 @@ MapAllocToTraceKind(AllocKind kind)
 
 template <typename T> struct MapTypeToTraceKind {};
 template <> struct MapTypeToTraceKind<ObjectImpl>       { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<JSObject>         { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<JSFunction>       { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<ArgumentsObject>  { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<ArrayBufferObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<ArrayBufferViewObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
+template <> struct MapTypeToTraceKind<SharedArrayBufferObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<DebugScopeObject> { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<GlobalObject>     { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<ScopeObject>      { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<JSScript>         { static const JSGCTraceKind kind = JSTRACE_SCRIPT; };
 template <> struct MapTypeToTraceKind<LazyScript>       { static const JSGCTraceKind kind = JSTRACE_LAZY_SCRIPT; };
 template <> struct MapTypeToTraceKind<Shape>            { static const JSGCTraceKind kind = JSTRACE_SHAPE; };
 template <> struct MapTypeToTraceKind<BaseShape>        { static const JSGCTraceKind kind = JSTRACE_BASE_SHAPE; };
 template <> struct MapTypeToTraceKind<UnownedBaseShape> { static const JSGCTraceKind kind = JSTRACE_BASE_SHAPE; };
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -14,16 +14,17 @@
 #include "mozilla/PodOperations.h"
 
 #include "jsanalyze.h"
 
 #include "jit/ExecutionModeInlines.h"
 #include "vm/ArrayObject.h"
 #include "vm/BooleanObject.h"
 #include "vm/NumberObject.h"
+#include "vm/SharedArrayObject.h"
 #include "vm/StringObject.h"
 #include "vm/TypedArrayObject.h"
 
 #include "jscntxtinlines.h"
 
 namespace js {
 namespace types {
 
@@ -300,16 +301,19 @@ GetClassForProtoKey(JSProtoKey key)
       case JSProto_Float32Array:
       case JSProto_Float64Array:
       case JSProto_Uint8ClampedArray:
         return &TypedArrayObject::classes[key - JSProto_Int8Array];
 
       case JSProto_ArrayBuffer:
         return &ArrayBufferObject::class_;
 
+      case JSProto_SharedArrayBuffer:
+        return &SharedArrayBufferObject::class_;
+
       case JSProto_DataView:
         return &DataViewObject::class_;
 
       default:
         MOZ_ASSUME_UNREACHABLE("Bad proto key");
     }
 }
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -1029,17 +1029,18 @@ ObjectClassIs(HandleObject obj, ESClassV
         return Proxy::objectClassIs(obj, classValue, cx);
 
     switch (classValue) {
       case ESClass_Array: return obj->is<ArrayObject>();
       case ESClass_Number: return obj->is<NumberObject>();
       case ESClass_String: return obj->is<StringObject>();
       case ESClass_Boolean: return obj->is<BooleanObject>();
       case ESClass_RegExp: return obj->is<RegExpObject>();
-      case ESClass_ArrayBuffer: return obj->is<ArrayBufferObject>();
+      case ESClass_ArrayBuffer:
+        return obj->is<ArrayBufferObject>() || obj->is<SharedArrayBufferObject>();
       case ESClass_Date: return obj->is<DateObject>();
     }
     MOZ_ASSUME_UNREACHABLE("bad classValue");
 }
 
 inline bool
 IsObjectWithClass(const Value &v, ESClassValue classValue, JSContext *cx)
 {
--- a/js/src/jsprototypes.h
+++ b/js/src/jsprototypes.h
@@ -86,16 +86,17 @@
     real(Float32Array,          28,     js_InitTypedArrayClasses,  TYPED_ARRAY_CLASP(TYPE_FLOAT32)) \
     real(Float64Array,          29,     js_InitTypedArrayClasses,  TYPED_ARRAY_CLASP(TYPE_FLOAT64)) \
     real(Uint8ClampedArray,     30,     js_InitTypedArrayClasses,  TYPED_ARRAY_CLASP(TYPE_UINT8_CLAMPED)) \
     real(Proxy,                 31,     js_InitProxyClass,         &ProxyObject::uncallableClass_) \
     real(WeakMap,               32,     js_InitWeakMapClass,       OCLASP(WeakMap)) \
     real(Map,                   33,     js_InitMapClass,           OCLASP(Map)) \
     real(Set,                   34,     js_InitSetClass,           OCLASP(Set)) \
     real(DataView,              35,     js_InitTypedArrayClasses,  OCLASP(DataView)) \
-IF_INTL(real,imaginary) (Intl,                  36,     js_InitIntlClass,          CLASP(Intl)) \
-IF_BDATA(real,imaginary)(TypedObject,           37,     js_InitTypedObjectModuleObject,   OCLASP(TypedObjectModule)) \
-    imaginary(GeneratorFunction,     38,     js_InitIteratorClasses, dummy) \
-IF_BDATA(real,imaginary)(SIMD,                  39,     js_InitSIMDClass, OCLASP(SIMD)) \
+    real(SharedArrayBuffer,     36,     js_InitSharedArrayBufferClass, &js::SharedArrayBufferObject::protoClass) \
+IF_INTL(real,imaginary) (Intl,                  37,     js_InitIntlClass,          CLASP(Intl)) \
+IF_BDATA(real,imaginary)(TypedObject,           38,     js_InitTypedObjectModuleObject,   OCLASP(TypedObjectModule)) \
+    imaginary(GeneratorFunction,     39,     js_InitIteratorClasses, dummy) \
+IF_BDATA(real,imaginary)(SIMD,                  40,     js_InitSIMDClass, OCLASP(SIMD)) \
 
 #define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)
 
 #endif /* jsprototypes_h */
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -176,16 +176,17 @@ UNIFIED_SOURCES += [
     'vm/PropertyKey.cpp',
     'vm/ProxyObject.cpp',
     'vm/RegExpObject.cpp',
     'vm/RegExpStatics.cpp',
     'vm/Runtime.cpp',
     'vm/ScopeObject.cpp',
     'vm/SelfHosting.cpp',
     'vm/Shape.cpp',
+    'vm/SharedArrayObject.cpp',
     'vm/SPSProfiler.cpp',
     'vm/Stack.cpp',
     'vm/String.cpp',
     'vm/StringBuffer.cpp',
     'vm/StructuredClone.cpp',
     'vm/ThreadPool.cpp',
     'vm/TypedArrayObject.cpp',
     'vm/Unicode.cpp',
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -30,16 +30,17 @@
 
 #include "gc/Barrier.h"
 #include "gc/Marking.h"
 #include "jit/AsmJS.h"
 #include "jit/AsmJSModule.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"
 
@@ -166,16 +167,54 @@ const JSFunctionSpec ArrayBufferObject::
     JS_FS_END
 };
 
 const JSFunctionSpec ArrayBufferObject::jsstaticfuncs[] = {
     JS_FN("isView", ArrayBufferObject::fun_isView, 1, 0),
     JS_FS_END
 };
 
+bool
+js::IsArrayBuffer(HandleValue v)
+{
+    return v.isObject() &&
+           (v.toObject().is<ArrayBufferObject>() ||
+            v.toObject().is<SharedArrayBufferObject>());
+}
+
+bool
+js::IsArrayBuffer(HandleObject obj)
+{
+    return obj->is<ArrayBufferObject>() || obj->is<SharedArrayBufferObject>();
+}
+
+bool
+js::IsArrayBuffer(JSObject *obj)
+{
+    return obj->is<ArrayBufferObject>() || obj->is<SharedArrayBufferObject>();
+}
+
+ArrayBufferObject &
+js::AsArrayBuffer(HandleObject obj)
+{
+    JS_ASSERT(IsArrayBuffer(obj));
+    if (obj->is<SharedArrayBufferObject>())
+        return obj->as<SharedArrayBufferObject>();
+    return obj->as<ArrayBufferObject>();
+}
+
+ArrayBufferObject &
+js::AsArrayBuffer(JSObject *obj)
+{
+    JS_ASSERT(IsArrayBuffer(obj));
+    if (obj->is<SharedArrayBufferObject>())
+        return obj->as<SharedArrayBufferObject>();
+    return obj->as<ArrayBufferObject>();
+}
+
 MOZ_ALWAYS_INLINE bool
 ArrayBufferObject::byteLengthGetterImpl(JSContext *cx, CallArgs args)
 {
     JS_ASSERT(IsArrayBuffer(args.thisv()));
     args.rval().setInt32(args.thisv().toObject().as<ArrayBufferObject>().byteLength());
     return true;
 }
 
@@ -378,20 +417,28 @@ ArrayBufferObject::neuterViews(JSContext
                 prev = b;
             }
         }
     }
 
     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, ObjectElements *newHeader)
 {
     JS_ASSERT(!isAsmJSArrayBuffer());
+    JS_ASSERT(!isSharedArrayBuffer());
 
     // Grab out data before invalidating it.
     uint32_t byteLengthCopy = byteLength();
     uintptr_t oldDataPointer = uintptr_t(dataPointer());
     ArrayBufferViewObject *viewListHead = GetViewList(this);
 
     // Update all views.
     uintptr_t newDataPointer = uintptr_t(newHeader->elements());
@@ -433,16 +480,18 @@ ArrayBufferObject::changeContents(JSCont
 
     initElementsHeader(newHeader, byteLengthCopy);
     InitViewList(this, viewListHead);
 }
 
 void
 ArrayBufferObject::neuter(JSContext *cx)
 {
+    JS_ASSERT(!isSharedArrayBuffer());
+
     JS_ASSERT(cx);
     if (hasDynamicElements() && !isAsmJSArrayBuffer()) {
         ObjectElements *oldHeader = getElementsHeader();
         changeContents(cx, ObjectElements::fromElements(fixedElements()));
 
         FreeOp fop(cx->runtime(), false);
         fop.free_(oldHeader);
     }
@@ -451,55 +500,38 @@ ArrayBufferObject::neuter(JSContext *cx)
     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_ION) && defined(JS_CPU_X64)
-// To avoid dynamically checking bounds on each load/store, asm.js code relies
-// on the SIGSEGV handler in AsmJSSignalHandlers.cpp. However, this only works
-// if we can guarantee that *any* out-of-bounds access generates a fault. This
-// isn't generally true since an out-of-bounds access could land on other
-// Mozilla data. To overcome this on x64, we reserve an entire 4GB space,
-// making only the range [0, byteLength) accessible, and use a 32-bit unsigned
-// index into this space. (x86 and ARM require different tricks.)
-//
-// One complication is that we need to put an ObjectElements struct immediately
-// before the data array (as required by the general JSObject data structure).
-// Thus, we must stick a page before the elements to hold ObjectElements.
-//
-//   |<------------------------------ 4GB + 1 pages --------------------->|
-//           |<--- sizeof --->|<------------------- 4GB ----------------->|
-//
-//   | waste | ObjectElements | data array | inaccessible reserved memory |
-//                            ^            ^                              ^
-//                            |            \                             /
-//                      obj->elements       required to be page boundaries
-//
+#if defined(JS_CPU_X64)
+// Refer to comment above AsmJSMappedSize in AsmJS.h.
 JS_STATIC_ASSERT(sizeof(ObjectElements) < AsmJSPageSize);
 JS_STATIC_ASSERT(AsmJSAllocationGranularity == AsmJSPageSize);
-static const size_t AsmJSMappedSize = AsmJSPageSize + AsmJSBufferProtectedSize;
+#endif
 
+#if defined(JS_ION) && defined(JS_CPU_X64)
 bool
 ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer)
 {
     if (buffer->isAsmJSArrayBuffer())
         return true;
 
     // Get the entire reserved region (with all pages inaccessible).
     void *p;
@@ -654,24 +686,25 @@ ArrayBufferObject::create(JSContext *cx,
         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 {
+        // Elements header must be initialized before dataPointer() is callable.
         obj->setFixedElements();
+        obj->initElementsHeader(obj->getElementsHeader(), nbytes);
         if (clear)
             memset(obj->dataPointer(), 0, nbytes);
     }
 
-    obj->initElementsHeader(obj->getElementsHeader(), nbytes);
-
     return obj;
 }
 
 JSObject *
 ArrayBufferObject::createSlice(JSContext *cx, Handle<ArrayBufferObject*> arrayBuffer,
                                uint32_t begin, uint32_t end)
 {
     JS_ASSERT(begin <= arrayBuffer->byteLength());
@@ -786,17 +819,17 @@ ArrayBufferObject::obj_trace(JSTracer *t
     // 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 = obj->as<ArrayBufferObject>();
+    ArrayBufferObject &buffer = AsArrayBuffer(obj);
     ArrayBufferViewObject *viewsHead = GetViewList(&buffer);
     if (!viewsHead)
         return;
 
     // During minor collections, mark weak pointers on the buffer strongly.
     if (trc->runtime->isHeapMinorCollecting()) {
         MarkObject(trc, &GetViewListRef(&buffer), "arraybuffer.viewlist");
         ArrayBufferViewObject *prior = GetViewList(&buffer);
@@ -821,17 +854,17 @@ ArrayBufferObject::obj_trace(JSTracer *t
         // Multiple views: do not mark, but append buffer to list.
         if (IS_GC_MARKING_TRACER(trc)) {
             // obj_trace may be called multiple times before sweep(), so avoid
             // adding this buffer to the list multiple times.
             if (firstView->bufferLink() == UNSET_BUFFER_LINK) {
                 JS_ASSERT(obj->compartment() == firstView->compartment());
                 ArrayBufferObject **bufList = &obj->compartment()->gcLiveArrayBuffers;
                 firstView->setBufferLink(*bufList);
-                *bufList = &obj->as<ArrayBufferObject>();
+                *bufList = &AsArrayBuffer(obj);
             } else {
 #ifdef DEBUG
                 bool found = false;
                 for (ArrayBufferObject *p = obj->compartment()->gcLiveArrayBuffers;
                      p;
                      p = GetViewList(p)->bufferLink())
                 {
                     if (p == obj)
@@ -1203,17 +1236,17 @@ ArrayBufferObject::obj_enumerate(JSConte
 ArrayBufferViewObject::trace(JSTracer *trc, JSObject *obj)
 {
     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 = bufSlot.toObject().as<ArrayBufferObject>();
+        ArrayBufferObject &buf = AsArrayBuffer(&bufSlot.toObject());
         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);
         }
     }
@@ -1252,36 +1285,36 @@ JS_IsArrayBufferViewObject(JSObject *obj
     obj = CheckedUnwrap(obj);
     return obj ? obj->is<ArrayBufferViewObject>() : false;
 }
 
 JS_FRIEND_API(uint32_t)
 JS_GetArrayBufferByteLength(JSObject *obj)
 {
     obj = CheckedUnwrap(obj);
-    return obj ? obj->as<ArrayBufferObject>().byteLength() : 0;
+    return obj ? AsArrayBuffer(obj).byteLength() : 0;
 }
 
 JS_FRIEND_API(uint8_t *)
 JS_GetArrayBufferData(JSObject *obj)
 {
     obj = CheckedUnwrap(obj);
     if (!obj)
         return nullptr;
-    return obj->as<ArrayBufferObject>().dataPointer();
+    return AsArrayBuffer(obj).dataPointer();
 }
 
 JS_FRIEND_API(uint8_t *)
 JS_GetStableArrayBufferData(JSContext *cx, JSObject *obj)
 {
     obj = CheckedUnwrap(obj);
     if (!obj)
         return nullptr;
 
-    Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
+    Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(obj));
     if (!ArrayBufferObject::ensureNonInline(cx, buffer))
         return nullptr;
 
     return buffer->dataPointer();
 }
 
 JS_FRIEND_API(bool)
 JS_NeuterArrayBuffer(JSContext *cx, HandleObject obj)
@@ -1425,17 +1458,17 @@ JS_GetObjectAsArrayBufferView(JSObject *
     return obj;
 }
 
 JS_FRIEND_API(JSObject *)
 JS_GetObjectAsArrayBuffer(JSObject *obj, uint32_t *length, uint8_t **data)
 {
     if (!(obj = CheckedUnwrap(obj)))
         return nullptr;
-    if (!obj->is<ArrayBufferObject>())
+    if (!IsArrayBuffer(obj))
         return nullptr;
 
-    *length = obj->as<ArrayBufferObject>().byteLength();
-    *data = obj->as<ArrayBufferObject>().dataPointer();
+    *length = AsArrayBuffer(obj).byteLength();
+    *data = AsArrayBuffer(obj).dataPointer();
 
     return obj;
 }
 
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -18,16 +18,17 @@ namespace js {
 
 class ArrayBufferViewObject;
 
 // The inheritance hierarchy for the various classes relating to typed arrays
 // is as follows.
 //
 // - JSObject
 //   - ArrayBufferObject
+//     - SharedArrayBufferObject
 //   - ArrayBufferViewObject
 //     - DataViewObject
 //     - TypedArrayObject (declared in vm/TypedArrayObject.h)
 //       - TypedArrayObjectTemplate
 //         - Int8ArrayObject
 //         - Uint8ArrayObject
 //         - ...
 //     - TypedObject (declared in builtin/TypedObject.h)
@@ -180,34 +181,36 @@ class ArrayBufferObject : public JSObjec
         return getElementsHeader()->initializedLength;
     }
 
     /*
      * Neuter all views of an ArrayBuffer.
      */
     static bool neuterViews(JSContext *cx, Handle<ArrayBufferObject*> buffer);
 
-    inline uint8_t * dataPointer() const {
-        return (uint8_t *) elements;
-    }
+    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 isSharedArrayBuffer() const {
+        return getElementsHeader()->isSharedArrayBuffer();
+    }
+
     bool isAsmJSArrayBuffer() const {
         return getElementsHeader()->isAsmJSArrayBuffer();
     }
     bool isNeutered() const {
         return getElementsHeader()->isNeuteredBuffer();
     }
     static bool prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer);
     static bool neuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer);
@@ -287,21 +290,25 @@ InitArrayBufferViewDataPointer(ArrayBuff
      * N.B. The base of the array's data is stored in the object's
      * private data rather than a slot to avoid alignment restrictions
      * on private Values.
      */
     obj->initPrivate(buffer->dataPointer() + byteOffset);
     PostBarrierTypedArrayObject(obj);
 }
 
-MOZ_ALWAYS_INLINE bool
-IsArrayBuffer(HandleValue v)
-{
-    return v.isObject() && v.toObject().is<ArrayBufferObject>();
-}
+/*
+ * Tests for either ArrayBufferObject or SharedArrayBufferObject.
+ * For specific class testing, use e.g., obj->is<ArrayBufferObject>().
+ */
+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);
 }
 
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -21,16 +21,19 @@ extern JSObject *
 js_InitObjectClass(JSContext *cx, js::HandleObject obj);
 
 extern JSObject *
 js_InitFunctionClass(JSContext *cx, js::HandleObject obj);
 
 extern JSObject *
 js_InitTypedArrayClasses(JSContext *cx, js::HandleObject obj);
 
+extern JSObject *
+js_InitSharedArrayBufferClass(JSContext *cx, js::HandleObject obj);
+
 namespace js {
 
 class Debugger;
 class TypedObjectModuleObject;
 
 /*
  * Global object slots are reserved as follows:
  *
@@ -257,16 +260,19 @@ class GlobalObject : public JSObject
         return classIsInitialized(JSProto_String);
     }
     bool regexpClassInitialized() const {
         return classIsInitialized(JSProto_RegExp);
     }
     bool arrayBufferClassInitialized() const {
         return classIsInitialized(JSProto_ArrayBuffer);
     }
+    bool sharedArrayBufferClassInitialized() const {
+        return classIsInitialized(JSProto_SharedArrayBuffer);
+    }
     bool errorClassesInitialized() const {
         return classIsInitialized(JSProto_Error);
     }
     bool dataViewClassInitialized() const {
         return classIsInitialized(JSProto_DataView);
     }
     bool typedArrayClassesInitialized() const {
         // This alias exists only for clarity: in reality all the typed array
@@ -383,16 +389,22 @@ class GlobalObject : public JSObject
     }
 
     static JSObject *getOrCreateArrayBufferPrototype(JSContext *cx, Handle<GlobalObject*> global) {
         if (!ensureConstructor(cx, global, JSProto_ArrayBuffer))
             return nullptr;
         return &global->getPrototype(JSProto_ArrayBuffer).toObject();
     }
 
+    JSObject *getOrCreateSharedArrayBufferPrototype(JSContext *cx, Handle<GlobalObject*> global) {
+        if (!ensureConstructor(cx, global, JSProto_SharedArrayBuffer))
+            return nullptr;
+        return &global->getPrototype(JSProto_SharedArrayBuffer).toObject();
+    }
+
     static JSObject *getOrCreateCustomErrorPrototype(JSContext *cx,
                                                      Handle<GlobalObject*> global,
                                                      JSExnType exnType)
     {
         JSProtoKey key = GetExceptionProtoKey(exnType);
         if (!ensureConstructor(cx, global, key))
             return nullptr;
         return &global->getPrototype(key).toObject();
--- a/js/src/vm/ObjectImpl.cpp
+++ b/js/src/vm/ObjectImpl.cpp
@@ -528,17 +528,19 @@ DenseElementsHeader::defineElement(JSCon
     obj->elements[index].set(obj->asObjectPtr(), HeapSlot::Element, index, desc.value());
     *succeeded = true;
     return true;
 }
 
 JSObject *
 js::ArrayBufferDelegate(JSContext *cx, Handle<ObjectImpl*> obj)
 {
-    MOZ_ASSERT(obj->hasClass(&ArrayBufferObject::class_));
+    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;
 }
 
--- a/js/src/vm/ObjectImpl.h
+++ b/js/src/vm/ObjectImpl.h
@@ -754,28 +754,30 @@ ArraySetLength(typename ExecutionModeTra
  */
 class ObjectElements
 {
   public:
     enum Flags {
         CONVERT_DOUBLE_ELEMENTS     = 0x1,
         ASMJS_ARRAY_BUFFER          = 0x2,
         NEUTERED_BUFFER             = 0x4,
+        SHARED_ARRAY_BUFFER         = 0x8,
 
         // Present only if these elements correspond to an array with
         // non-writable length; never present for non-arrays.
-        NONWRITABLE_ARRAY_LENGTH    = 0x8
+        NONWRITABLE_ARRAY_LENGTH    = 0x10
     };
 
   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);
@@ -825,16 +827,22 @@ class ObjectElements
         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 hasNonwritableArrayLength() const {
         return flags & NONWRITABLE_ARRAY_LENGTH;
     }
     void setNonwritableArrayLength() {
         flags |= NONWRITABLE_ARRAY_LENGTH;
     }
 
   public:
new file mode 100644
--- /dev/null
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -0,0 +1,387 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "vm/SharedArrayObject.h"
+
+#include "jsprf.h"
+#include "jsobjinlines.h"
+
+#ifdef XP_WIN
+# include "jswin.h"
+#else
+# include <sys/mman.h>
+#endif
+
+#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
+    int prot = (commit ? MEM_COMMIT : MEM_RESERVE);
+    int flags = (commit ? PAGE_READWRITE : PAGE_NOACCESS);
+    return VirtualAlloc(nullptr, length, prot, flags);
+#else
+    int prot = (commit ? (PROT_READ | PROT_WRITE) : PROT_NONE);
+    void *p = mmap(nullptr, length, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
+    if (p == MAP_FAILED)
+        return nullptr;
+    return p;
+#endif
+}
+
+static inline void
+UnmapMemory(void *addr, size_t len)
+{
+#ifdef XP_WIN
+    VirtualFree(addr, 0, MEM_RELEASE);
+#else
+    munmap(addr, len);
+#endif
+}
+
+static inline bool
+MarkValidRegion(void *addr, size_t len)
+{
+#ifdef XP_WIN
+    if (!VirtualAlloc(addr, len, MEM_COMMIT, PAGE_READWRITE))
+        return false;
+    return true;
+#else
+    if (mprotect(addr, len, PROT_READ | PROT_WRITE))
+        return false;
+    return true;
+#endif
+}
+
+SharedArrayRawBuffer *
+SharedArrayRawBuffer::New(uint32_t length)
+{
+    // Enforced by SharedArrayBufferObject constructor.
+    JS_ASSERT(IsValidAsmJSHeapLength(length));
+
+#ifdef JS_CPU_X64
+    // Get the entire reserved region (with all pages inaccessible)
+    void *p = MapMemory(AsmJSMappedSize, false);
+    if (!p)
+        return nullptr;
+
+    if (!MarkValidRegion(p, AsmJSPageSize + length)) {
+        UnmapMemory(p, AsmJSMappedSize);
+        return nullptr;
+    }
+
+    uint8_t *buffer = reinterpret_cast<uint8_t*>(p) + AsmJSPageSize;
+    uint8_t *base = buffer - sizeof(SharedArrayRawBuffer);
+    return new (base) SharedArrayRawBuffer(buffer, length);
+#else
+    uint32_t allocSize = sizeof(SharedArrayRawBuffer) + length;
+    if (allocSize <= length)
+        return nullptr;
+
+    void *base = MapMemory(allocSize, true);
+    if (!base)
+        return nullptr;
+
+    uint8_t *buffer = reinterpret_cast<uint8_t*>(base) + sizeof(SharedArrayRawBuffer);
+    return new (base) SharedArrayRawBuffer(buffer, length);
+#endif
+}
+
+void
+SharedArrayRawBuffer::addReference()
+{
+    JS_ASSERT(this->refcount > 0);
+    ++this->refcount; // Atomic.
+}
+
+void
+SharedArrayRawBuffer::dropReference()
+{
+    // Drop the reference to the buffer.
+    uint32_t refcount = --this->refcount; // Atomic.
+
+    // If this was the final reference, release the buffer.
+    if (refcount == 0) {
+#ifdef JS_CPU_X64
+        uint8_t *p = this->dataPointer() - AsmJSPageSize;
+        JS_ASSERT(uintptr_t(p) % AsmJSPageSize == 0);
+        UnmapMemory(p, AsmJSMappedSize);
+#else
+        uint8_t *p = (uint8_t *)this;
+        UnmapMemory(p, this->length);
+#endif
+    }
+}
+
+/*
+ * SharedArrayBufferObject
+ */
+bool
+js::IsSharedArrayBuffer(HandleValue v)
+{
+    return v.isObject() && v.toObject().is<SharedArrayBufferObject>();
+}
+
+MOZ_ALWAYS_INLINE bool
+SharedArrayBufferObject::byteLengthGetterImpl(JSContext *cx, CallArgs args)
+{
+    JS_ASSERT(IsSharedArrayBuffer(args.thisv()));
+    args.rval().setInt32(args.thisv().toObject().as<SharedArrayBufferObject>().byteLength());
+    return true;
+}
+
+bool
+SharedArrayBufferObject::byteLengthGetter(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return CallNonGenericMethod<IsSharedArrayBuffer, byteLengthGetterImpl>(cx, args);
+}
+
+bool
+SharedArrayBufferObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
+{
+    int32_t length = 0;
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (argc > 0 && !ToInt32(cx, args[0], &length))
+        return false;
+
+    if (length < 0) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
+        return false;
+    }
+
+    JSObject *bufobj = New(cx, uint32_t(length));
+    if (!bufobj)
+        return false;
+    args.rval().setObject(*bufobj);
+    return true;
+}
+
+JSObject *
+SharedArrayBufferObject::New(JSContext *cx, uint32_t length)
+{
+    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 obj;
+}
+
+JSObject *
+SharedArrayBufferObject::New(JSContext *cx, SharedArrayRawBuffer *buffer)
+{
+    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(),
+                                                          buffer->byteLength());
+    obj->getElementsHeader()->setIsSharedArrayBuffer();
+
+    obj->as<SharedArrayBufferObject>().acceptRawBuffer(buffer);
+
+    return obj;
+}
+
+void
+SharedArrayBufferObject::acceptRawBuffer(SharedArrayRawBuffer *buffer)
+{
+    setReservedSlot(SharedArrayBufferObject::RAWBUF_SLOT, PrivateValue(buffer));
+}
+
+void
+SharedArrayBufferObject::dropRawBuffer()
+{
+    setReservedSlot(SharedArrayBufferObject::RAWBUF_SLOT, UndefinedValue());
+}
+
+SharedArrayRawBuffer *
+SharedArrayBufferObject::rawBufferObject() const
+{
+    // RAWBUF_SLOT must be populated via acceptRawBuffer(),
+    // and the raw buffer must not have been dropped.
+    Value v = getReservedSlot(SharedArrayBufferObject::RAWBUF_SLOT);
+    return (SharedArrayRawBuffer *)v.toPrivate();
+}
+
+uint8_t *
+SharedArrayBufferObject::dataPointer() const
+{
+    return rawBufferObject()->dataPointer();
+}
+
+uint32_t
+SharedArrayBufferObject::byteLength() const
+{
+    return rawBufferObject()->byteLength();
+}
+
+void
+SharedArrayBufferObject::Finalize(FreeOp *fop, JSObject *obj)
+{
+    SharedArrayBufferObject &buf = obj->as<SharedArrayBufferObject>();
+
+    // Detect the case of failure during SharedArrayBufferObject creation,
+    // which causes a SharedArrayRawBuffer to never be attached.
+    Value v = buf.getReservedSlot(SharedArrayBufferObject::RAWBUF_SLOT);
+    if (!v.isUndefined()) {
+        buf.rawBufferObject()->dropReference();
+        buf.dropRawBuffer();
+    }
+}
+
+/*
+ * 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 |
+    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,
+    {
+        ArrayBufferObject::obj_lookupGeneric,
+        ArrayBufferObject::obj_lookupProperty,
+        ArrayBufferObject::obj_lookupElement,
+        ArrayBufferObject::obj_lookupSpecial,
+        ArrayBufferObject::obj_defineGeneric,
+        ArrayBufferObject::obj_defineProperty,
+        ArrayBufferObject::obj_defineElement,
+        ArrayBufferObject::obj_defineSpecial,
+        ArrayBufferObject::obj_getGeneric,
+        ArrayBufferObject::obj_getProperty,
+        ArrayBufferObject::obj_getElement,
+        ArrayBufferObject::obj_getSpecial,
+        ArrayBufferObject::obj_setGeneric,
+        ArrayBufferObject::obj_setProperty,
+        ArrayBufferObject::obj_setElement,
+        ArrayBufferObject::obj_setSpecial,
+        ArrayBufferObject::obj_getGenericAttributes,
+        ArrayBufferObject::obj_setGenericAttributes,
+        ArrayBufferObject::obj_deleteProperty,
+        ArrayBufferObject::obj_deleteElement,
+        ArrayBufferObject::obj_deleteSpecial,
+        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));
+    if (!proto)
+        return nullptr;
+
+    RootedFunction ctor(cx, global->createConstructor(cx, SharedArrayBufferObject::class_constructor,
+                                                      cx->names().SharedArrayBuffer, 1));
+    if (!ctor)
+        return nullptr;
+
+    if (!LinkConstructorAndPrototype(cx, ctor, proto))
+        return nullptr;
+
+    RootedId byteLengthId(cx, NameToId(cx->names().byteLength));
+    unsigned flags = JSPROP_SHARED | JSPROP_GETTER | JSPROP_PERMANENT;
+    JSObject *getter = NewFunction(cx, NullPtr(), SharedArrayBufferObject::byteLengthGetter, 0,
+                                   JSFunction::NATIVE_FUN, global, NullPtr());
+    if (!getter)
+        return nullptr;
+
+    RootedValue value(cx, UndefinedValue());
+    if (!DefineNativeProperty(cx, proto, byteLengthId, value,
+                              JS_DATA_TO_FUNC_PTR(PropertyOp, getter), nullptr, flags, 0, 0))
+    {
+        return nullptr;
+    }
+
+    if (!DefineConstructorAndPrototype(cx, global, JSProto_SharedArrayBuffer, ctor, proto))
+        return nullptr;
+    return proto;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/vm/SharedArrayObject.h
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef vm_SharedArrayObject_h
+#define vm_SharedArrayObject_h
+
+#include "mozilla/Atomics.h"
+
+#include "jsapi.h"
+#include "jsobj.h"
+#include "jstypes.h"
+
+#include "gc/Barrier.h"
+#include "vm/ArrayBufferObject.h"
+
+typedef struct JSProperty JSProperty;
+
+namespace js {
+
+/*
+ * SharedArrayRawBuffer
+ *
+ * A bookkeeping object always stored immediately before the raw buffer.
+ * The buffer itself is mmap()'d and refcounted.
+ * SharedArrayBufferObjects and AsmJS code may hold references.
+ *
+ *           |<------ sizeof ------>|<- length ->|
+ *
+ *   | waste | SharedArrayRawBuffer | data array | waste |
+ */
+class SharedArrayRawBuffer
+{
+  private:
+    mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> refcount;
+    uint32_t length;
+
+  protected:
+    SharedArrayRawBuffer(uint8_t *buffer, uint32_t length)
+      : refcount(1), length(length)
+    {
+        JS_ASSERT(buffer == dataPointer());
+    }
+
+  public:
+    static SharedArrayRawBuffer *New(uint32_t length);
+
+    inline uint8_t *dataPointer() const {
+        return ((uint8_t *)this) + sizeof(SharedArrayRawBuffer);
+    }
+
+    inline uint32_t byteLength() const {
+        return length;
+    }
+
+    void addReference();
+    void dropReference();
+};
+
+/*
+ * SharedArrayBufferObject
+ *
+ * When transferred to a WebWorker, the buffer is not neutered on the parent side,
+ * and both child and parent reference the same buffer.
+ */
+class SharedArrayBufferObject : public ArrayBufferObject
+{
+    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.
+    // 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);
+
+    static bool byteLengthGetter(JSContext *cx, unsigned argc, Value *vp);
+
+    static void Finalize(FreeOp *fop, JSObject *obj);
+
+    void acceptRawBuffer(SharedArrayRawBuffer *buffer);
+    void dropRawBuffer();
+
+    SharedArrayRawBuffer *rawBufferObject() const;
+    uint8_t *dataPointer() const;
+    uint32_t byteLength() const;
+};
+
+bool
+IsSharedArrayBuffer(HandleValue v);
+
+} // namespace js
+
+#endif // vm_SharedArrayObject_h
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -27,19 +27,21 @@
 # include "jswin.h"
 #endif
 #include "jswrapper.h"
 
 #include "gc/Barrier.h"
 #include "gc/Marking.h"
 #include "jit/AsmJS.h"
 #include "jit/AsmJSModule.h"
+#include "vm/ArrayBufferObject.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"
 
@@ -104,16 +106,22 @@ void
 TypedArrayObject::neuter(JSContext *cx)
 {
     setSlot(LENGTH_SLOT, Int32Value(0));
     setSlot(BYTELENGTH_SLOT, Int32Value(0));
     setSlot(BYTEOFFSET_SLOT, Int32Value(0));
     setPrivate(nullptr);
 }
 
+ArrayBufferObject *
+TypedArrayObject::sharedBuffer() const
+{
+    return &bufferValue(const_cast<TypedArrayObject*>(this)).toObject().as<SharedArrayBufferObject>();
+}
+
 bool
 TypedArrayObject::obj_lookupGeneric(JSContext *cx, HandleObject tarray, HandleId id,
                                     MutableHandleObject objp, MutableHandleShape propp)
 {
     if (tarray->as<TypedArrayObject>().isArrayIndex(id)) {
         MarkNonNativePropertyFound(propp);
         objp.set(tarray);
         return true;
@@ -607,17 +615,17 @@ class TypedArrayObjectTemplate : public 
         if (!obj)
             return nullptr;
         JS_ASSERT_IF(obj->isTenured(),
                      obj->tenuredGetAllocKind() == gc::FINALIZE_OBJECT8_BACKGROUND);
 
         obj->setSlot(TYPE_SLOT, Int32Value(ArrayTypeID()));
         obj->setSlot(BUFFER_SLOT, ObjectValue(*bufobj));
 
-        Rooted<ArrayBufferObject *> buffer(cx, &bufobj->as<ArrayBufferObject>());
+        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));
 
@@ -696,18 +704,21 @@ class TypedArrayObjectTemplate : public 
         /*
          * (typedArray)
          * (type[] array)
          *
          * Otherwise create a new typed array and copy elements 0..len-1
          * properties from the object, treating it as some sort of array.
          * Note that offset and length will be ignored
          */
-        if (!UncheckedUnwrap(dataObj)->is<ArrayBufferObject>())
+        if (!UncheckedUnwrap(dataObj)->is<ArrayBufferObject>() &&
+            !UncheckedUnwrap(dataObj)->is<SharedArrayBufferObject>())
+        {
             return fromArray(cx, dataObj);
+        }
 
         /* (ArrayBuffer, [byteOffset, [length]]) */
         int32_t byteOffset = 0;
         int32_t length = -1;
 
         if (args.length() > 1) {
             if (!ToInt32(cx, args[1], &byteOffset))
                 return nullptr;
@@ -969,17 +980,17 @@ class TypedArrayObjectTemplate : public 
     fromBuffer(JSContext *cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt,
                HandleObject proto)
     {
         if (!ObjectClassIs(bufobj, ESClass_ArrayBuffer, cx)) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
             return nullptr; // must be arrayBuffer
         }
 
-        JS_ASSERT(bufobj->is<ArrayBufferObject>() || bufobj->is<ProxyObject>());
+        JS_ASSERT(IsArrayBuffer(bufobj) || bufobj->is<ProxyObject>());
         if (bufobj->is<ProxyObject>()) {
             /*
              * Normally, NonGenericMethodGuard handles the case of transparent
              * wrappers. However, we have a peculiar situation: we want to
              * construct the new typed array in the compartment of the buffer,
              * so that the typed array can point directly at their buffer's
              * data without crossing compartment boundaries. So we use the
              * machinery underlying NonGenericMethodGuard directly to proxy the
@@ -987,17 +998,17 @@ class TypedArrayObjectTemplate : public 
              * compartment for a view in the target compartment referencing the
              * ArrayBufferObject in that same compartment.
              */
             JSObject *wrapped = CheckedUnwrap(bufobj);
             if (!wrapped) {
                 JS_ReportError(cx, "Permission denied to access object");
                 return nullptr;
             }
-            if (wrapped->is<ArrayBufferObject>()) {
+            if (IsArrayBuffer(wrapped)) {
                 /*
                  * And for even more fun, the new view's prototype should be
                  * set to the origin compartment's prototype object, not the
                  * target's (specifically, the actual view in the target
                  * compartment will use as its prototype a wrapper around the
                  * origin compartment's view.prototype object).
                  *
                  * Rather than hack some crazy solution together, implement
@@ -1022,22 +1033,22 @@ class TypedArrayObjectTemplate : public 
                 args[2].setObject(*proto);
 
                 if (!Invoke(cx, args))
                     return nullptr;
                 return &args.rval().toObject();
             }
         }
 
-        if (!bufobj->is<ArrayBufferObject>()) {
+        if (!IsArrayBuffer(bufobj)) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
             return nullptr; // must be arrayBuffer
         }
 
-        ArrayBufferObject &buffer = bufobj->as<ArrayBufferObject>();
+        ArrayBufferObject &buffer = AsArrayBuffer(bufobj);
 
         if (byteOffset > buffer.byteLength() || byteOffset % sizeof(NativeType) != 0) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
             return nullptr; // invalid byteOffset
         }
 
         uint32_t len;
         if (lengthInt == -1) {
@@ -1620,31 +1631,31 @@ DataViewObject::create(JSContext *cx, ui
     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->as<ArrayBufferObject>().addView(&dvobj);
+    arrayBuffer->addView(&dvobj);
 
     return &dvobj;
 }
 
 bool
 DataViewObject::construct(JSContext *cx, JSObject *bufobj, const CallArgs &args, HandleObject proto)
 {
-    if (!bufobj->is<ArrayBufferObject>()) {
+    if (!IsArrayBuffer(bufobj)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
                              "DataView", "ArrayBuffer", bufobj->getClass()->name);
         return false;
     }
 
-    Rooted<ArrayBufferObject*> buffer(cx, &bufobj->as<ArrayBufferObject>());
+    Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(bufobj));
     uint32_t bufferLength = buffer->byteLength();
     uint32_t byteOffset = 0;
     uint32_t byteLength = bufferLength;
 
     if (args.length() > 1) {
         if (!ToUint32(cx, args[1], &byteOffset))
             return false;
         if (byteOffset > INT32_MAX) {
@@ -1692,17 +1703,17 @@ bool
 DataViewObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedObject bufobj(cx);
     if (!GetFirstArgumentAsObject(cx, args, "DataView constructor", &bufobj))
         return false;
 
-    if (bufobj->is<WrapperObject>() && UncheckedUnwrap(bufobj)->is<ArrayBufferObject>()) {
+    if (bufobj->is<WrapperObject>() && IsArrayBuffer(UncheckedUnwrap(bufobj))) {
         Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal());
         Rooted<JSObject*> proto(cx, global->getOrCreateDataViewPrototype(cx));
         if (!proto)
             return false;
 
         InvokeArgs args2(cx);
         if (!args2.init(args.length() + 1))
             return false;
--- a/js/src/vm/TypedArrayObject.h
+++ b/js/src/vm/TypedArrayObject.h
@@ -62,18 +62,22 @@ class TypedArrayObject : public ArrayBuf
     }
     static Value byteLengthValue(TypedArrayObject *tarr) {
         return tarr->getFixedSlot(BYTELENGTH_SLOT);
     }
     static Value lengthValue(TypedArrayObject *tarr) {
         return tarr->getFixedSlot(LENGTH_SLOT);
     }
 
+    ArrayBufferObject *sharedBuffer() const;
     ArrayBufferObject *buffer() const {
-        return &bufferValue(const_cast<TypedArrayObject*>(this)).toObject().as<ArrayBufferObject>();
+        JSObject &obj = bufferValue(const_cast<TypedArrayObject*>(this)).toObject();
+        if (obj.is<ArrayBufferObject>())
+            return &obj.as<ArrayBufferObject>();
+        return sharedBuffer();
     }
     uint32_t byteOffset() const {
         return byteOffsetValue(const_cast<TypedArrayObject*>(this)).toInt32();
     }
     uint32_t byteLength() const {
         return byteLengthValue(const_cast<TypedArrayObject*>(this)).toInt32();
     }
     uint32_t length() const {