Bug 933001 - Part 1/5 - Define SharedArrayBufferObject. r=sfink
authorSean Stangl <sstangl@mozilla.com>
Thu, 20 Feb 2014 14:43:03 -0800
changeset 188040 fe2e4e1be13c77e4ff5502d66c27496e0364857e
parent 188039 ac9709133b272c4774dd7a483025f5f5a19c7761
child 188041 d5ebf76f501095c46a1306dcd0d4de8797c2bb5e
push id3503
push userraliiev@mozilla.com
push dateMon, 28 Apr 2014 18:51:11 +0000
treeherdermozilla-beta@c95ac01e332e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs933001
milestone30.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 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 {