Bug 1054882 - Rollup patch. r=till, r=sstangl, r=jorendorff
authorLars T Hansen <lhansen@mozilla.com>
Tue, 16 Sep 2014 18:45:31 +0200
changeset 205505 cf9ed5c3532974b8b55d9b9ec6968d5c9b1e6026
parent 205504 d803279cf506b9b4389905daf26cc0ac873d2b61
child 205506 3a59d92e7cb77181d4dd4d2d4cf8359641eb3186
push id49206
push userlhansen@mozilla.com
push dateTue, 16 Sep 2014 16:47:16 +0000
treeherdermozilla-inbound@cf9ed5c35329 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill, sstangl, jorendorff
bugs1054882
milestone35.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 1054882 - Rollup patch. r=till, r=sstangl, r=jorendorff
dom/tests/mochitest/general/test_interfaces.html
dom/workers/test/test_worker_interfaces.js
js/public/Class.h
js/src/asmjs/AsmJSLink.cpp
js/src/asmjs/AsmJSModule.cpp
js/src/asmjs/AsmJSModule.h
js/src/asmjs/AsmJSValidate.cpp
js/src/gc/Barrier.h
js/src/gc/Marking.cpp
js/src/gc/Marking.h
js/src/jit-test/tests/sharedbuf/asm-link.js
js/src/jit-test/tests/sharedbuf/gc-one-view.js
js/src/jit-test/tests/sharedbuf/gc-two-views.js
js/src/jit-test/tests/sharedbuf/inline-access.js
js/src/jit-test/tests/sharedbuf/is-zeroed.js
js/src/jit/BaselineIC.cpp
js/src/jit/BaselineIC.h
js/src/jit/CodeGenerator.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonCaches.cpp
js/src/jit/IonCaches.h
js/src/jit/MCallOptimize.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/RangeAnalysis.cpp
js/src/js.msg
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jsfriendapi.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsiter.cpp
js/src/jsnum.cpp
js/src/jsnum.h
js/src/jsobj.cpp
js/src/jsobjinlines.h
js/src/jsprototypes.h
js/src/moz.build
js/src/tests/js1_8_5/extensions/sharedtypedarray.js
js/src/tests/js1_8_5/shell.js
js/src/vm/ArrayBufferObject-inl.h
js/src/vm/ArrayBufferObject.cpp
js/src/vm/ArrayBufferObject.h
js/src/vm/ObjectImpl-inl.h
js/src/vm/ObjectImpl.cpp
js/src/vm/SelfHosting.cpp
js/src/vm/Shape-inl.h
js/src/vm/SharedArrayObject.cpp
js/src/vm/SharedArrayObject.h
js/src/vm/SharedTypedArrayObject.cpp
js/src/vm/SharedTypedArrayObject.h
js/src/vm/StructuredClone.cpp
js/src/vm/TypedArrayCommon.h
js/src/vm/TypedArrayObject.cpp
js/src/vm/TypedArrayObject.h
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -70,16 +70,25 @@ var ecmaGlobals =
     "Number",
     "Object",
     "Proxy",
     "RangeError",
     "ReferenceError",
     "RegExp",
     "Set",
     {name: "SharedArrayBuffer", nightly: true},
+    {name: "SharedInt8Array", nightly: true},
+    {name: "SharedUint8Array", nightly: true},
+    {name: "SharedUint8ClampedArray", nightly: true},
+    {name: "SharedInt16Array", nightly: true},
+    {name: "SharedUint16Array", nightly: true},
+    {name: "SharedInt32Array", nightly: true},
+    {name: "SharedUint32Array", nightly: true},
+    {name: "SharedFloat32Array", nightly: true},
+    {name: "SharedFloat64Array", nightly: true},
     {name: "SIMD", nightly: true},
     "StopIteration",
     "String",
     "SyntaxError",
     {name: "TypedObject", nightly: true},
     "TypeError",
     "Uint16Array",
     "Uint32Array",
--- a/dom/workers/test/test_worker_interfaces.js
+++ b/dom/workers/test/test_worker_interfaces.js
@@ -44,16 +44,25 @@ var ecmaGlobals =
     "Number",
     "Object",
     "Proxy",
     "RangeError",
     "ReferenceError",
     "RegExp",
     "Set",
     {name: "SharedArrayBuffer", nightly: true},
+    {name: "SharedInt8Array", nightly: true},
+    {name: "SharedUint8Array", nightly: true},
+    {name: "SharedUint8ClampedArray", nightly: true},
+    {name: "SharedInt16Array", nightly: true},
+    {name: "SharedUint16Array", nightly: true},
+    {name: "SharedInt32Array", nightly: true},
+    {name: "SharedUint32Array", nightly: true},
+    {name: "SharedFloat32Array", nightly: true},
+    {name: "SharedFloat64Array", nightly: true},
     {name: "SIMD", nightly: true},
     "StopIteration",
     "String",
     "SyntaxError",
     {name: "TypedObject", nightly: true},
     "TypeError",
     "Uint16Array",
     "Uint32Array",
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -527,18 +527,18 @@ Valueify(const JSClass *c)
 }
 
 /*
  * Enumeration describing possible values of the [[Class]] internal property
  * value of objects.
  */
 enum ESClassValue {
     ESClass_Object, ESClass_Array, ESClass_Number, ESClass_String,
-    ESClass_Boolean, ESClass_RegExp, ESClass_ArrayBuffer, ESClass_Date,
-    ESClass_Set, ESClass_Map
+    ESClass_Boolean, ESClass_RegExp, ESClass_ArrayBuffer, ESClass_SharedArrayBuffer,
+    ESClass_Date, ESClass_Set, ESClass_Map
 };
 
 /*
  * Return whether the given object has the given [[Class]] internal property
  * value. Beware, this query says nothing about the js::Class of the JSObject
  * so the caller must not assume anything about obj's representation (e.g., obj
  * may be a proxy).
  */
--- a/js/src/asmjs/AsmJSLink.cpp
+++ b/js/src/asmjs/AsmJSLink.cpp
@@ -32,20 +32,24 @@
 #include "asmjs/AsmJSModule.h"
 #include "builtin/SIMD.h"
 #include "frontend/BytecodeCompiler.h"
 #include "jit/Ion.h"
 #include "jit/JitCommon.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
+#include "vm/ArrayBufferObject.h"
+#include "vm/SharedArrayObject.h"
 #include "vm/StringBuffer.h"
 
 #include "jsobjinlines.h"
 
+#include "vm/ArrayBufferObject-inl.h"
+
 using namespace js;
 using namespace js::jit;
 
 using mozilla::IsNaN;
 using mozilla::PodZero;
 
 static bool
 CloneModule(JSContext *cx, MutableHandle<AsmJSModuleObject*> moduleObj)
@@ -217,18 +221,21 @@ static bool
 ValidateArrayView(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal,
                   HandleValue bufferVal)
 {
     RootedPropertyName field(cx, global.viewName());
     RootedValue v(cx);
     if (!GetDataProperty(cx, globalVal, field, &v))
         return false;
 
-    if (!IsTypedArrayConstructor(v, global.viewType()))
+    if (!IsTypedArrayConstructor(v, global.viewType()) &&
+        !IsSharedTypedArrayConstructor(v, global.viewType()))
+    {
         return LinkFail(cx, "bad typed array constructor");
+    }
 
     return true;
 }
 
 static bool
 ValidateMathBuiltinFunction(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal)
 {
     RootedValue v(cx);
@@ -412,17 +419,17 @@ ValidateConstant(JSContext *cx, AsmJSMod
         if (v.toNumber() != global.constantValue())
             return LinkFail(cx, "global constant value mismatch");
     }
 
     return true;
 }
 
 static bool
-LinkModuleToHeap(JSContext *cx, AsmJSModule &module, Handle<ArrayBufferObject*> heap)
+LinkModuleToHeap(JSContext *cx, AsmJSModule &module, Handle<ArrayBufferObjectMaybeShared*> heap)
 {
     uint32_t heapLength = heap->byteLength();
 
     if (IsDeprecatedAsmJSHeapLength(heapLength)) {
         LinkFail(cx, "ArrayBuffer byteLengths smaller than 64KB are deprecated and "
                      "will cause a link-time failure in the future");
 
         // The goal of deprecation is to give apps some time before linking
@@ -457,18 +464,21 @@ LinkModuleToHeap(JSContext *cx, AsmJSMod
     // If we've generated the code with signal handlers in mind (for bounds
     // checks on x64 and for interrupt callback requesting on all platforms),
     // we need to be able to use signals at runtime. In particular, a module
     // can have been created using signals and cached, and executed without
     // signals activated.
     if (module.usesSignalHandlersForInterrupt() && !cx->canUseSignalHandlers())
         return LinkFail(cx, "Code generated with signal handlers but signals are deactivated");
 
-    if (!ArrayBufferObject::prepareForAsmJS(cx, heap, module.usesSignalHandlersForOOB()))
-        return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
+    if (heap->is<ArrayBufferObject>()) {
+        Rooted<ArrayBufferObject *> abheap(cx, &heap->as<ArrayBufferObject>());
+        if (!ArrayBufferObject::prepareForAsmJS(cx, abheap, module.usesSignalHandlersForOOB()))
+            return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
+    }
 
     module.initHeap(heap, cx);
     return true;
 }
 
 static bool
 DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
 {
@@ -481,22 +491,22 @@ DynamicallyLinkModule(JSContext *cx, Cal
     RootedValue importVal(cx);
     if (args.length() > 1)
         importVal = args[1];
 
     RootedValue bufferVal(cx);
     if (args.length() > 2)
         bufferVal = args[2];
 
-    Rooted<ArrayBufferObject*> heap(cx);
+    Rooted<ArrayBufferObjectMaybeShared *> heap(cx);
     if (module.hasArrayView()) {
-        if (!IsTypedArrayBuffer(bufferVal))
+        if (IsArrayBuffer(bufferVal) || IsSharedArrayBuffer(bufferVal))
+            heap = &AsAnyArrayBuffer(bufferVal);
+        else
             return LinkFail(cx, "bad ArrayBuffer argument");
-
-        heap = &AsTypedArrayBuffer(bufferVal);
         if (!LinkModuleToHeap(cx, module, heap))
             return false;
     }
 
     AutoObjectVector ffis(cx);
     if (!ffis.resize(module.numFFIs()))
         return false;
 
@@ -634,17 +644,20 @@ CallAsmJS(JSContext *cx, unsigned argc, 
           }
         }
     }
 
     // An asm.js module is specialized to its heap's base address and length
     // which is normally immutable except for the neuter operation that occurs
     // when an ArrayBuffer is transfered. Throw an internal error if we're
     // about to run with a neutered heap.
-    if (module.maybeHeapBufferObject() && module.maybeHeapBufferObject()->isNeutered()) {
+    if (module.maybeHeapBufferObject() &&
+        module.maybeHeapBufferObject()->is<ArrayBufferObject>() &&
+        module.maybeHeapBufferObject()->as<ArrayBufferObject>().isNeutered())
+    {
         js_ReportOverRecursed(cx);
         return false;
     }
 
     {
         // Push an AsmJSActivation to describe the asm.js frames we're about to
         // push when running this module. Additionally, push a JitActivation so
         // that the optimized asm.js-to-Ion FFI call path (which we want to be
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -39,16 +39,17 @@
 
 #include "frontend/Parser.h"
 #include "jit/IonCode.h"
 #include "js/MemoryMetrics.h"
 
 #include "jsobjinlines.h"
 
 #include "frontend/ParseNode-inl.h"
+#include "vm/ArrayBufferObject-inl.h"
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace jit;
 using namespace frontend;
 using mozilla::BinarySearch;
 using mozilla::PodCopy;
 using mozilla::PodEqual;
@@ -737,17 +738,17 @@ AsmJSModule::staticallyLink(ExclusiveCon
         exitDatum.fun = nullptr;
         exitDatum.ionScript = nullptr;
     }
 
     JS_ASSERT(isStaticallyLinked());
 }
 
 void
-AsmJSModule::initHeap(Handle<ArrayBufferObject*> heap, JSContext *cx)
+AsmJSModule::initHeap(Handle<ArrayBufferObjectMaybeShared *> heap, JSContext *cx)
 {
     JS_ASSERT(IsValidAsmJSHeapLength(heap->byteLength()));
     JS_ASSERT(dynamicallyLinked_);
     JS_ASSERT(!maybeHeap_);
 
     maybeHeap_ = heap;
     heapDatum() = heap->dataPointer();
 
@@ -781,17 +782,18 @@ AsmJSModule::initHeap(Handle<ArrayBuffer
     for (unsigned i = 0; i < heapAccesses_.length(); i++) {
         jit::Assembler::UpdateBoundsCheck(heapLength,
                                           (jit::Instruction*)(heapAccesses_[i].offset() + code_));
     }
 #endif
 }
 
 void
-AsmJSModule::restoreToInitialState(uint8_t *prevCode, ArrayBufferObject *maybePrevBuffer,
+AsmJSModule::restoreToInitialState(uint8_t *prevCode,
+                                   ArrayBufferObjectMaybeShared *maybePrevBuffer,
                                    ExclusiveContext *cx)
 {
 #ifdef DEBUG
     // Put the absolute links back to -1 so PatchDataWithValueCheck assertions
     // in staticallyLink are valid.
     for (size_t imm = 0; imm < AsmJSImm_Limit; imm++) {
         void *callee = AddressOf(AsmJSImmKind(imm), cx);
 
--- a/js/src/asmjs/AsmJSModule.h
+++ b/js/src/asmjs/AsmJSModule.h
@@ -796,17 +796,17 @@ class AsmJSModule
 
     ScriptSource *                        scriptSource_;
     PropertyName *                        globalArgumentName_;
     PropertyName *                        importArgumentName_;
     PropertyName *                        bufferArgumentName_;
     uint8_t *                             code_;
     uint8_t *                             interruptExit_;
     StaticLinkData                        staticLinkData_;
-    HeapPtrArrayBufferObject              maybeHeap_;
+    HeapPtrArrayBufferObjectMaybeShared   maybeHeap_;
     bool                                  dynamicallyLinked_;
     bool                                  loadedFromCache_;
     bool                                  profilingEnabled_;
 
     // This field is accessed concurrently when requesting an interrupt.
     // Access must be synchronized via the runtime's interrupt lock.
     mutable bool                          codeIsProtected_;
 
@@ -1382,19 +1382,20 @@ class AsmJSModule
     // binds the code to a particular heap (via initHeap) and set of global
     // variables. A given asm.js module cannot be dynamically linked more than
     // once so, if JS tries, the module is cloned.
     void setIsDynamicallyLinked() {
         JS_ASSERT(!isDynamicallyLinked());
         dynamicallyLinked_ = true;
         JS_ASSERT(isDynamicallyLinked());
     }
-    void initHeap(Handle<ArrayBufferObject*> heap, JSContext *cx);
+    void initHeap(Handle<ArrayBufferObjectMaybeShared*> heap, JSContext *cx);
     bool clone(JSContext *cx, ScopedJSDeletePtr<AsmJSModule> *moduleOut) const;
-    void restoreToInitialState(uint8_t *prevCode, ArrayBufferObject *maybePrevBuffer,
+    void restoreToInitialState(uint8_t *prevCode,
+                               ArrayBufferObjectMaybeShared *maybePrevBuffer,
                                ExclusiveContext *cx);
 
     /*************************************************************************/
     // Functions that can be called after dynamic linking succeeds:
 
     CodePtr entryTrampoline(const ExportedFunction &func) const {
         JS_ASSERT(isDynamicallyLinked());
         return JS_DATA_TO_FUNC_PTR(CodePtr, code_ + func.pod.codeOffset_);
@@ -1402,17 +1403,17 @@ class AsmJSModule
     uint8_t *interruptExit() const {
         JS_ASSERT(isDynamicallyLinked());
         return interruptExit_;
     }
     uint8_t *maybeHeap() const {
         JS_ASSERT(isDynamicallyLinked());
         return heapDatum();
     }
-    ArrayBufferObject *maybeHeapBufferObject() const {
+    ArrayBufferObjectMaybeShared *maybeHeapBufferObject() const {
         JS_ASSERT(isDynamicallyLinked());
         return maybeHeap_;
     }
     size_t heapLength() const {
         JS_ASSERT(isDynamicallyLinked());
         return maybeHeap_ ? maybeHeap_->byteLength() : 0;
     }
     bool profilingEnabled() const {
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -3510,31 +3510,31 @@ CheckNewArrayView(ModuleCompiler &m, Pro
     PropertyName *bufferName = m.module().bufferArgumentName();
     if (!bufferName)
         return m.fail(bufArg, "cannot create array view without an asm.js heap parameter");
     if (!IsUseOfName(bufArg, bufferName))
         return m.failName(bufArg, "argument to array view constructor must be '%s'", bufferName);
 
     JSAtomState &names = m.cx()->names();
     Scalar::Type type;
-    if (field == names.Int8Array)
+    if (field == names.Int8Array || field == names.SharedInt8Array)
         type = Scalar::Int8;
-    else if (field == names.Uint8Array)
+    else if (field == names.Uint8Array || field == names.SharedUint8Array)
         type = Scalar::Uint8;
-    else if (field == names.Int16Array)
+    else if (field == names.Int16Array || field == names.SharedInt16Array)
         type = Scalar::Int16;
-    else if (field == names.Uint16Array)
+    else if (field == names.Uint16Array || field == names.SharedUint16Array)
         type = Scalar::Uint16;
-    else if (field == names.Int32Array)
+    else if (field == names.Int32Array || field == names.SharedInt32Array)
         type = Scalar::Int32;
-    else if (field == names.Uint32Array)
+    else if (field == names.Uint32Array || field == names.SharedUint32Array)
         type = Scalar::Uint32;
-    else if (field == names.Float32Array)
+    else if (field == names.Float32Array || field == names.SharedFloat32Array)
         type = Scalar::Float32;
-    else if (field == names.Float64Array)
+    else if (field == names.Float64Array || field == names.SharedFloat64Array)
         type = Scalar::Float64;
     else
         return m.fail(ctorExpr, "could not match typed array name");
 
     return m.addArrayView(varName, type, field);
 }
 
 static bool
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -157,19 +157,21 @@ class JSLinearString;
 
 namespace JS {
 class Symbol;
 }
 
 namespace js {
 
 class ArgumentsObject;
+class ArrayBufferObjectMaybeShared;
 class ArrayBufferObject;
 class ArrayBufferViewObject;
 class SharedArrayBufferObject;
+class SharedTypedArrayObject;
 class BaseShape;
 class DebugScopeObject;
 class GlobalObject;
 class LazyScript;
 class NestedScopeObject;
 class Nursery;
 class ObjectImpl;
 class PropertyName;
@@ -200,16 +202,17 @@ CurrentThreadIsIonCompiling();
 bool
 StringIsPermanentAtom(JSString *str);
 
 namespace gc {
 
 template <typename T> struct MapTypeToTraceKind {};
 template <> struct MapTypeToTraceKind<ArgumentsObject>  { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<ArrayBufferObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
+template <> struct MapTypeToTraceKind<ArrayBufferObjectMaybeShared>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<ArrayBufferViewObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<BaseShape>        { static const JSGCTraceKind kind = JSTRACE_BASE_SHAPE; };
 template <> struct MapTypeToTraceKind<DebugScopeObject> { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<GlobalObject>     { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<JS::Symbol>       { static const JSGCTraceKind kind = JSTRACE_SYMBOL; };
 template <> struct MapTypeToTraceKind<JSAtom>           { static const JSGCTraceKind kind = JSTRACE_STRING; };
 template <> struct MapTypeToTraceKind<JSFlatString>     { static const JSGCTraceKind kind = JSTRACE_STRING; };
 template <> struct MapTypeToTraceKind<JSFunction>       { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
@@ -220,16 +223,17 @@ template <> struct MapTypeToTraceKind<JS
 template <> struct MapTypeToTraceKind<LazyScript>       { static const JSGCTraceKind kind = JSTRACE_LAZY_SCRIPT; };
 template <> struct MapTypeToTraceKind<NestedScopeObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<ObjectImpl>       { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<PropertyName>     { static const JSGCTraceKind kind = JSTRACE_STRING; };
 template <> struct MapTypeToTraceKind<SavedFrame>       { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<ScopeObject>      { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<Shape>            { static const JSGCTraceKind kind = JSTRACE_SHAPE; };
 template <> struct MapTypeToTraceKind<SharedArrayBufferObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
+template <> struct MapTypeToTraceKind<SharedTypedArrayObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<UnownedBaseShape> { static const JSGCTraceKind kind = JSTRACE_BASE_SHAPE; };
 template <> struct MapTypeToTraceKind<jit::JitCode>     { static const JSGCTraceKind kind = JSTRACE_JITCODE; };
 template <> struct MapTypeToTraceKind<types::TypeObject>{ static const JSGCTraceKind kind = JSTRACE_TYPE_OBJECT; };
 
 template <typename T>
 void
 MarkUnbarriered(JSTracer *trc, T **thingp, const char *name);
 
@@ -888,16 +892,17 @@ typedef PreBarriered<JSObject*> PreBarri
 typedef PreBarriered<JSScript*> PreBarrieredScript;
 typedef PreBarriered<jit::JitCode*> PreBarrieredJitCode;
 typedef PreBarriered<JSAtom*> PreBarrieredAtom;
 
 typedef RelocatablePtr<JSObject*> RelocatablePtrObject;
 typedef RelocatablePtr<JSScript*> RelocatablePtrScript;
 typedef RelocatablePtr<NestedScopeObject*> RelocatablePtrNestedScopeObject;
 
+typedef HeapPtr<ArrayBufferObjectMaybeShared*> HeapPtrArrayBufferObjectMaybeShared;
 typedef HeapPtr<ArrayBufferObject*> HeapPtrArrayBufferObject;
 typedef HeapPtr<BaseShape*> HeapPtrBaseShape;
 typedef HeapPtr<JSAtom*> HeapPtrAtom;
 typedef HeapPtr<JSFlatString*> HeapPtrFlatString;
 typedef HeapPtr<JSFunction*> HeapPtrFunction;
 typedef HeapPtr<JSLinearString*> HeapPtrLinearString;
 typedef HeapPtr<JSObject*> HeapPtrObject;
 typedef HeapPtr<JSScript*> HeapPtrScript;
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -622,26 +622,28 @@ Update##base##IfRelocated(JSRuntime *rt,
 }
 
 
 DeclMarkerImpl(BaseShape, BaseShape)
 DeclMarkerImpl(BaseShape, UnownedBaseShape)
 DeclMarkerImpl(JitCode, jit::JitCode)
 DeclMarkerImpl(Object, ArgumentsObject)
 DeclMarkerImpl(Object, ArrayBufferObject)
+DeclMarkerImpl(Object, ArrayBufferObjectMaybeShared)
 DeclMarkerImpl(Object, ArrayBufferViewObject)
-DeclMarkerImpl(Object, SharedArrayBufferObject)
 DeclMarkerImpl(Object, DebugScopeObject)
 DeclMarkerImpl(Object, GlobalObject)
 DeclMarkerImpl(Object, JSObject)
 DeclMarkerImpl(Object, JSFunction)
 DeclMarkerImpl(Object, NestedScopeObject)
 DeclMarkerImpl(Object, ObjectImpl)
 DeclMarkerImpl(Object, SavedFrame)
 DeclMarkerImpl(Object, ScopeObject)
+DeclMarkerImpl(Object, SharedArrayBufferObject)
+DeclMarkerImpl(Object, SharedTypedArrayObject)
 DeclMarkerImpl(Script, JSScript)
 DeclMarkerImpl(LazyScript, LazyScript)
 DeclMarkerImpl(Shape, Shape)
 DeclMarkerImpl(String, JSAtom)
 DeclMarkerImpl(String, JSString)
 DeclMarkerImpl(String, JSFlatString)
 DeclMarkerImpl(String, JSLinearString)
 DeclMarkerImpl(String, PropertyName)
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -102,25 +102,27 @@ bool Is##base##AboutToBeFinalized(Barrie
 type *Update##base##IfRelocated(JSRuntime *rt, BarrieredBase<type*> *thingp);                     \
 type *Update##base##IfRelocated(JSRuntime *rt, type **thingp);
 
 DeclMarker(BaseShape, BaseShape)
 DeclMarker(BaseShape, UnownedBaseShape)
 DeclMarker(JitCode, jit::JitCode)
 DeclMarker(Object, ArgumentsObject)
 DeclMarker(Object, ArrayBufferObject)
+DeclMarker(Object, ArrayBufferObjectMaybeShared)
 DeclMarker(Object, ArrayBufferViewObject)
-DeclMarker(Object, SharedArrayBufferObject)
 DeclMarker(Object, DebugScopeObject)
 DeclMarker(Object, GlobalObject)
 DeclMarker(Object, JSObject)
 DeclMarker(Object, JSFunction)
 DeclMarker(Object, NestedScopeObject)
 DeclMarker(Object, SavedFrame)
 DeclMarker(Object, ScopeObject)
+DeclMarker(Object, SharedArrayBufferObject)
+DeclMarker(Object, SharedTypedArrayObject)
 DeclMarker(Script, JSScript)
 DeclMarker(LazyScript, LazyScript)
 DeclMarker(Shape, Shape)
 DeclMarker(String, JSAtom)
 DeclMarker(String, JSString)
 DeclMarker(String, JSFlatString)
 DeclMarker(String, JSLinearString)
 DeclMarker(String, PropertyName)
--- a/js/src/jit-test/tests/sharedbuf/asm-link.js
+++ b/js/src/jit-test/tests/sharedbuf/asm-link.js
@@ -1,10 +1,15 @@
-// Don't assert.
+// Don't assert on linking.
+// Provide superficial functionality.
+
 function $(stdlib, foreign, heap) {
     "use asm";
-    var Float64ArrayView = new stdlib.Float64Array(heap);
-    function f() {}
+    var f64 = new stdlib.SharedFloat64Array(heap);
+    function f() { var v=0.0; v=+f64[0]; return +v; }
     return f
 }
 
-if (typeof SharedArrayBuffer !== "undefined")
-    $(this, {}, new SharedArrayBuffer(4096));
+if (this.SharedArrayBuffer && this.SharedFloat64Array) {
+    var heap = new SharedArrayBuffer(65536);
+    (new SharedFloat64Array(heap))[0] = 3.14159;
+    assertEq($(this, {}, heap)(), 3.14159);
+}
--- a/js/src/jit-test/tests/sharedbuf/gc-one-view.js
+++ b/js/src/jit-test/tests/sharedbuf/gc-one-view.js
@@ -1,10 +1,10 @@
 // Test tracing of a single linked ArrayBufferViewObject.
 
 function f() {
     var x = new SharedArrayBuffer(0x1000);
-    var y = new Int32Array(x);
+    var y = new SharedInt32Array(x);
     gc();
 }
 
-if (typeof SharedArrayBuffer !== "undefined")
+if (this.SharedArrayBuffer && this.SharedInt32Array)
     f();
--- a/js/src/jit-test/tests/sharedbuf/gc-two-views.js
+++ b/js/src/jit-test/tests/sharedbuf/gc-two-views.js
@@ -1,11 +1,11 @@
 // Test tracing of two views of a SharedArrayBuffer. Uses a different path.
 
 function f() {
     var x = new SharedArrayBuffer(0x1000);
-    var y = new Int32Array(x);
-    var z = new Int8Array(x);
+    var y = new SharedInt32Array(x);
+    var z = new SharedInt8Array(x);
     gc();
 }
 
-if (typeof SharedArrayBuffer !== "undefined")
+if (this.SharedArrayBuffer && this.SharedInt32Array && this.SharedInt8Array)
     f();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/sharedbuf/inline-access.js
@@ -0,0 +1,29 @@
+// |jit-test| slow;
+//
+// This is for testing inlining behavior in the jits.
+//
+// For Baseline, run:
+//    $ IONFLAGS=bl-ic .../js --ion-off --baseline-eager inline-access.js
+// Then inspect the output, there should be calls to "GetElem(TypedArray[Int32])",
+// "GetProp(NativeObj/NativeGetter 0x...)", and "SetElem_TypedArray stub"
+// for the read access, length access, and write access respectively, within f.
+//
+// For Ion, run:
+//    $ IONFLAGS=logs .../js --ion-offthread-compile=off inline-access.js
+// Then postprocess with iongraph and verify (by inspecting MIR late in the pipeline)
+// that it contains instructions like "typedarraylength", "loadtypedarrayelement",
+// and "storetypedarrayelement".
+
+if (!this.SharedInt32Array)
+    quit();
+
+function f(ta) {
+    return (ta[2] = ta[0] + ta[1] + ta.length);
+}
+
+var v = new SharedInt32Array(1024);
+var sum = 0;
+var iter = 1000;
+for ( var i=0 ; i < iter ; i++ )
+    sum += f(v);
+assertEq(sum, v.length * iter);
--- a/js/src/jit-test/tests/sharedbuf/is-zeroed.js
+++ b/js/src/jit-test/tests/sharedbuf/is-zeroed.js
@@ -1,12 +1,12 @@
 // Test that the SharedArrayBuffer memory is properly zeroed.
 
 function f() {
     var x = new SharedArrayBuffer(4096);
-    var y = new Int32Array(x);
+    var y = new SharedInt32Array(x);
     assertEq(y[0], 0);
     assertEq(y[1], 0);
     assertEq(y[1023], 0);
 }
 
-if (typeof SharedArrayBuffer !== "undefined")
+if (this.SharedArrayBuffer && this.SharedInt32Array)
     f();
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -19,16 +19,17 @@
 #include "jit/IonLinker.h"
 #include "jit/JitSpewer.h"
 #include "jit/Lowering.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "jit/VMFunctions.h"
 #include "vm/Opcodes.h"
+#include "vm/TypedArrayCommon.h"
 
 #include "jsboolinlines.h"
 #include "jsscriptinlines.h"
 
 #include "jit/IonFrames-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/ScopeObject-inl.h"
 #include "vm/StringObject-inl.h"
@@ -3853,19 +3854,19 @@ static bool TryAttachNativeGetElemStub(J
         stub->addNewStub(newStub);
         return true;
     }
 
     return true;
 }
 
 static bool
-TypedArrayRequiresFloatingPoint(TypedArrayObject *tarr)
-{
-    uint32_t type = tarr->type();
+TypedArrayRequiresFloatingPoint(HandleObject obj)
+{
+    uint32_t type = AnyTypedArrayType(obj);
     return type == Scalar::Uint32 ||
            type == Scalar::Float32 ||
            type == Scalar::Float64;
 }
 
 static bool
 TryAttachGetElemStub(JSContext *cx, JSScript *script, jsbytecode *pc, ICGetElem_Fallback *stub,
                      HandleValue lhs, HandleValue rhs, HandleValue res)
@@ -3926,17 +3927,17 @@ TryAttachGetElemStub(JSContext *cx, JSSc
 
             stub->addNewStub(argsStub);
             return true;
         }
     }
 
     if (obj->isNative()) {
         // Check for NativeObject[int] dense accesses.
-        if (rhs.isInt32() && rhs.toInt32() >= 0 && !obj->is<TypedArrayObject>()) {
+        if (rhs.isInt32() && rhs.toInt32() >= 0 && !IsAnyTypedArray(obj.get())) {
             JitSpew(JitSpew_BaselineIC, "  Generating GetElem(Native[Int32] dense) stub");
             ICGetElem_Dense::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
                                                obj->lastProperty(), isCallElem);
             ICStub *denseStub = compiler.getStub(compiler.getStubSpace(script));
             if (!denseStub)
                 return false;
 
             stub->addNewStub(denseStub);
@@ -3948,34 +3949,35 @@ TryAttachGetElemStub(JSContext *cx, JSSc
             RootedScript rootedScript(cx, script);
             if (!TryAttachNativeGetElemStub(cx, rootedScript, pc, stub, obj, rhs))
                 return false;
             script = rootedScript;
         }
     }
 
     // Check for TypedArray[int] => Number accesses.
-    if (obj->is<TypedArrayObject>() && rhs.isNumber() && res.isNumber() &&
+    if (IsAnyTypedArray(obj.get()) && rhs.isNumber() && res.isNumber() &&
         !TypedArrayGetElemStubExists(stub, obj))
     {
         // Don't attach CALLELEM stubs for accesses on typed array expected to yield numbers.
 #if JS_HAS_NO_SUCH_METHOD
         if (isCallElem)
             return true;
 #endif
 
-        TypedArrayObject *tarr = &obj->as<TypedArrayObject>();
         if (!cx->runtime()->jitSupportsFloatingPoint &&
-            (TypedArrayRequiresFloatingPoint(tarr) || rhs.isDouble()))
+            (TypedArrayRequiresFloatingPoint(obj) || rhs.isDouble()))
         {
             return true;
         }
 
         JitSpew(JitSpew_BaselineIC, "  Generating GetElem(TypedArray[Int32]) stub");
-        ICGetElem_TypedArray::Compiler compiler(cx, tarr->lastProperty(), tarr->type());
+        ICGetElem_TypedArray::Compiler compiler(cx,
+                                                AnyTypedArrayShape(obj.get()),
+                                                AnyTypedArrayType(obj.get()));
         ICStub *typedArrayStub = compiler.getStub(compiler.getStubSpace(script));
         if (!typedArrayStub)
             return false;
 
         stub->addNewStub(typedArrayStub);
         return true;
     }
 
@@ -4607,37 +4609,37 @@ ICGetElem_TypedArray::Compiler::generate
     } else {
         masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
     }
 
     // Unbox key.
     Register key = masm.extractInt32(R1, ExtractTemp1);
 
     // Bounds check.
-    masm.unboxInt32(Address(obj, TypedArrayObject::lengthOffset()), scratchReg);
+    masm.unboxInt32(Address(obj, TypedArrayLayout::lengthOffset()), scratchReg);
     masm.branch32(Assembler::BelowOrEqual, scratchReg, key, &failure);
 
     // Load the elements vector.
-    masm.loadPtr(Address(obj, TypedArrayObject::dataOffset()), scratchReg);
+    masm.loadPtr(Address(obj, TypedArrayLayout::dataOffset()), scratchReg);
 
     // Load the value.
     BaseIndex source(scratchReg, key, ScaleFromElemWidth(Scalar::byteSize(type_)));
     masm.loadFromTypedArray(type_, source, R0, false, scratchReg, &failure);
 
     // Todo: Allow loading doubles from uint32 arrays, but this requires monitoring.
     EmitReturnFromIC(masm);
 
     // Failure case - jump to next stub
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
 }
 
 //
-// GetEelem_Arguments
+// GetElem_Arguments
 //
 bool
 ICGetElem_Arguments::Compiler::generateStubCode(MacroAssembler &masm)
 {
     // Variants of GetElem_Arguments can enter stub frames if entered in CallProp
     // context when noSuchMethod support is on.
 #if JS_HAS_NO_SUCH_METHOD
 #ifdef DEBUG
@@ -5021,17 +5023,17 @@ DoSetElemFallback(JSContext *cx, Baselin
     if (stub->numOptimizedStubs() >= ICSetElem_Fallback::MAX_OPTIMIZED_STUBS) {
         // TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
         // But for now we just bail.
         return true;
     }
 
     // Try to generate new stubs.
     if (obj->isNative() &&
-        !obj->is<TypedArrayObject>() &&
+        !IsAnyTypedArray(obj.get()) &&
         index.isInt32() && index.toInt32() >= 0 &&
         !rhs.isMagic(JS_ELEMENTS_HOLE))
     {
         bool addingCase;
         size_t protoDepth;
 
         if (CanOptimizeDenseSetElem(cx, obj, index.toInt32(), oldShape, oldCapacity, oldInitLength,
                                     &addingCase, &protoDepth))
@@ -5069,37 +5071,40 @@ DoSetElemFallback(JSContext *cx, Baselin
 
                 stub->addNewStub(denseStub);
             }
         }
 
         return true;
     }
 
-    if (obj->is<TypedArrayObject>() && index.isNumber() && rhs.isNumber()) {
-        Rooted<TypedArrayObject*> tarr(cx, &obj->as<TypedArrayObject>());
+    if (IsAnyTypedArray(obj.get()) && index.isNumber() && rhs.isNumber()) {
         if (!cx->runtime()->jitSupportsFloatingPoint &&
-            (TypedArrayRequiresFloatingPoint(tarr) || index.isDouble()))
+            (TypedArrayRequiresFloatingPoint(obj) || index.isDouble()))
         {
             return true;
         }
 
-        uint32_t len = tarr->length();
+        uint32_t len = AnyTypedArrayLength(obj.get());
         double idx = index.toNumber();
         bool expectOutOfBounds = (idx < 0 || idx >= double(len));
 
-        if (!TypedArraySetElemStubExists(stub, tarr, expectOutOfBounds)) {
+        if (!TypedArraySetElemStubExists(stub, obj, expectOutOfBounds)) {
             // Remove any existing TypedArraySetElemStub that doesn't handle out-of-bounds
             if (expectOutOfBounds)
-                RemoveExistingTypedArraySetElemStub(cx, stub, tarr);
+                RemoveExistingTypedArraySetElemStub(cx, stub, obj);
 
             JitSpew(JitSpew_BaselineIC,
                     "  Generating SetElem_TypedArray stub (shape=%p, type=%u, oob=%s)",
-                    tarr->lastProperty(), tarr->type(), expectOutOfBounds ? "yes" : "no");
-            ICSetElem_TypedArray::Compiler compiler(cx, tarr->lastProperty(), tarr->type(),
+                    AnyTypedArrayShape(obj.get()),
+                    AnyTypedArrayType(obj.get()),
+                    expectOutOfBounds ? "yes" : "no");
+            ICSetElem_TypedArray::Compiler compiler(cx,
+                                                    AnyTypedArrayShape(obj.get()),
+                                                    AnyTypedArrayType(obj.get()),
                                                     expectOutOfBounds);
             ICStub *typedArrayStub = compiler.getStub(compiler.getStubSpace(script));
             if (!typedArrayStub)
                 return false;
 
             stub->addNewStub(typedArrayStub);
             return true;
         }
@@ -5511,22 +5516,22 @@ ICSetElem_TypedArray::Compiler::generate
         masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
     }
 
     // Unbox key.
     Register key = masm.extractInt32(R1, ExtractTemp1);
 
     // Bounds check.
     Label oobWrite;
-    masm.unboxInt32(Address(obj, TypedArrayObject::lengthOffset()), scratchReg);
+    masm.unboxInt32(Address(obj, TypedArrayLayout::lengthOffset()), scratchReg);
     masm.branch32(Assembler::BelowOrEqual, scratchReg, key,
                   expectOutOfBounds_ ? &oobWrite : &failure);
 
     // Load the elements vector.
-    masm.loadPtr(Address(obj, TypedArrayObject::dataOffset()), scratchReg);
+    masm.loadPtr(Address(obj, TypedArrayLayout::dataOffset()), scratchReg);
 
     BaseIndex dest(scratchReg, key, ScaleFromElemWidth(Scalar::byteSize(type_)));
     Address value(BaselineStackReg, ICStackValueOffset);
 
     // We need a second scratch register. It's okay to clobber the type tag of
     // R0 or R1, as long as it's restored before jumping to the next stub.
     regs = availableGeneralRegs(0);
     regs.takeUnchecked(obj);
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -13,16 +13,19 @@
 #include "jscompartment.h"
 #include "jsgc.h"
 #include "jsopcode.h"
 
 #include "jit/BaselineJIT.h"
 #include "jit/BaselineRegisters.h"
 
 namespace js {
+
+class TypedArrayLayout;
+
 namespace jit {
 
 //
 // Baseline Inline Caches are polymorphic caches that aggressively
 // share their stub code.
 //
 // Every polymorphic site contains a linked list of stubs which are
 // specific to that site.  These stubs are composed of a |StubData|
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -4352,26 +4352,26 @@ CodeGenerator::visitSetArrayLength(LSetA
     return true;
 }
 
 bool
 CodeGenerator::visitTypedArrayLength(LTypedArrayLength *lir)
 {
     Register obj = ToRegister(lir->object());
     Register out = ToRegister(lir->output());
-    masm.unboxInt32(Address(obj, TypedArrayObject::lengthOffset()), out);
+    masm.unboxInt32(Address(obj, TypedArrayLayout::lengthOffset()), out);
     return true;
 }
 
 bool
 CodeGenerator::visitTypedArrayElements(LTypedArrayElements *lir)
 {
     Register obj = ToRegister(lir->object());
     Register out = ToRegister(lir->output());
-    masm.loadPtr(Address(obj, TypedArrayObject::dataOffset()), out);
+    masm.loadPtr(Address(obj, TypedArrayLayout::dataOffset()), out);
     return true;
 }
 
 bool
 CodeGenerator::visitNeuterCheck(LNeuterCheck *lir)
 {
     Register obj = ToRegister(lir->object());
     Register temp = ToRegister(lir->temp());
@@ -8195,27 +8195,27 @@ bool
 CodeGenerator::visitLoadTypedArrayElementHole(LLoadTypedArrayElementHole *lir)
 {
     Register object = ToRegister(lir->object());
     const ValueOperand out = ToOutValue(lir);
 
     // Load the length.
     Register scratch = out.scratchReg();
     Int32Key key = ToInt32Key(lir->index());
-    masm.unboxInt32(Address(object, TypedArrayObject::lengthOffset()), scratch);
+    masm.unboxInt32(Address(object, TypedArrayLayout::lengthOffset()), scratch);
 
     // Load undefined unless length > key.
     Label inbounds, done;
     masm.branchKey(Assembler::Above, scratch, key, &inbounds);
     masm.moveValue(UndefinedValue(), out);
     masm.jump(&done);
 
     // Load the elements vector.
     masm.bind(&inbounds);
-    masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), scratch);
+    masm.loadPtr(Address(object, TypedArrayLayout::dataOffset()), scratch);
 
     Scalar::Type arrayType = lir->mir()->arrayType();
     int width = Scalar::byteSize(arrayType);
 
     Label fail;
     if (key.isConstant()) {
         Address source(scratch, key.constant() * width);
         masm.loadFromTypedArray(arrayType, source, out, lir->mir()->allowDouble(),
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -7409,54 +7409,54 @@ IonBuilder::getElemTryDense(bool *emitte
 }
 
 bool
 IonBuilder::getElemTryTypedStatic(bool *emitted, MDefinition *obj, MDefinition *index)
 {
     JS_ASSERT(*emitted == false);
 
     Scalar::Type arrayType;
-    if (!ElementAccessIsTypedArray(obj, index, &arrayType))
+    if (!ElementAccessIsAnyTypedArray(obj, index, &arrayType))
         return true;
 
     if (!LIRGenerator::allowStaticTypedArrayAccesses())
         return true;
 
     if (ElementAccessHasExtraIndexedProperty(constraints(), obj))
         return true;
 
     if (!obj->resultTypeSet())
         return true;
 
     JSObject *tarrObj = obj->resultTypeSet()->getSingleton();
     if (!tarrObj)
         return true;
 
-    TypedArrayObject *tarr = &tarrObj->as<TypedArrayObject>();
-
-    types::TypeObjectKey *tarrType = types::TypeObjectKey::get(tarr);
+    types::TypeObjectKey *tarrType = types::TypeObjectKey::get(tarrObj);
     if (tarrType->unknownProperties())
         return true;
 
     // LoadTypedArrayElementStatic currently treats uint32 arrays as int32.
-    Scalar::Type viewType = tarr->type();
+    Scalar::Type viewType = AnyTypedArrayType(tarrObj);
     if (viewType == Scalar::Uint32)
         return true;
 
     MDefinition *ptr = convertShiftToMaskForStaticTypedArray(index, viewType);
     if (!ptr)
         return true;
 
     // Emit LoadTypedArrayElementStatic.
-    tarrType->watchStateChangeForTypedArrayData(constraints());
+
+    if (tarrObj->is<TypedArrayObject>())
+        tarrType->watchStateChangeForTypedArrayData(constraints());
 
     obj->setImplicitlyUsedUnchecked();
     index->setImplicitlyUsedUnchecked();
 
-    MLoadTypedArrayElementStatic *load = MLoadTypedArrayElementStatic::New(alloc(), tarr, ptr);
+    MLoadTypedArrayElementStatic *load = MLoadTypedArrayElementStatic::New(alloc(), tarrObj, ptr);
     current->add(load);
     current->push(load);
 
     // The load is infallible if an undefined result will be coerced to the
     // appropriate numeric type if the read is out of bounds. The truncation
     // analysis picks up some of these cases, but is incomplete with respect
     // to others. For now, sniff the bytecode for simple patterns following
     // the load which guarantee a truncation or numeric conversion.
@@ -7475,17 +7475,17 @@ IonBuilder::getElemTryTypedStatic(bool *
 }
 
 bool
 IonBuilder::getElemTryTypedArray(bool *emitted, MDefinition *obj, MDefinition *index)
 {
     JS_ASSERT(*emitted == false);
 
     Scalar::Type arrayType;
-    if (!ElementAccessIsTypedArray(obj, index, &arrayType))
+    if (!ElementAccessIsAnyTypedArray(obj, index, &arrayType))
         return true;
 
     // Emit typed getelem variant.
     if (!jsop_getelem_typed(obj, index, arrayType))
         return false;
 
     *emitted = true;
     return true;
@@ -7799,35 +7799,36 @@ void
 IonBuilder::addTypedArrayLengthAndData(MDefinition *obj,
                                        BoundsChecking checking,
                                        MDefinition **index,
                                        MInstruction **length, MInstruction **elements)
 {
     MOZ_ASSERT((index != nullptr) == (elements != nullptr));
 
     if (obj->isConstant() && obj->toConstant()->value().isObject()) {
-        TypedArrayObject *tarr = &obj->toConstant()->value().toObject().as<TypedArrayObject>();
-        void *data = tarr->viewData();
+        JSObject *tarr = &obj->toConstant()->value().toObject();
+        void *data = AnyTypedArrayViewData(tarr);
         // Bug 979449 - Optimistically embed the elements and use TI to
         //              invalidate if we move them.
 #ifdef JSGC_GENERATIONAL
         bool isTenured = !tarr->runtimeFromMainThread()->gc.nursery.isInside(data);
 #else
         bool isTenured = true;
 #endif
         if (isTenured && tarr->hasSingletonType()) {
-            // The 'data' pointer can change in rare circumstances
+            // The 'data' pointer of TypedArrayObject can change in rare circumstances
             // (ArrayBufferObject::changeContents).
             types::TypeObjectKey *tarrType = types::TypeObjectKey::get(tarr);
             if (!tarrType->unknownProperties()) {
-                tarrType->watchStateChangeForTypedArrayData(constraints());
+                if (tarr->is<TypedArrayObject>())
+                    tarrType->watchStateChangeForTypedArrayData(constraints());
 
                 obj->setImplicitlyUsedUnchecked();
 
-                int32_t len = AssertedCast<int32_t>(tarr->length());
+                int32_t len = AssertedCast<int32_t>(AnyTypedArrayLength(tarr));
                 *length = MConstant::New(alloc(), Int32Value(len));
                 current->add(*length);
 
                 if (index) {
                     if (checking == DoBoundsCheck)
                         *index = addBoundsCheck(*index, *length);
 
                     *elements = MConstantElements::New(alloc(), data);
@@ -8111,61 +8112,61 @@ IonBuilder::setElemTryScalarElemOfTypedO
 
 bool
 IonBuilder::setElemTryTypedStatic(bool *emitted, MDefinition *object,
                                   MDefinition *index, MDefinition *value)
 {
     JS_ASSERT(*emitted == false);
 
     Scalar::Type arrayType;
-    if (!ElementAccessIsTypedArray(object, index, &arrayType))
+    if (!ElementAccessIsAnyTypedArray(object, index, &arrayType))
         return true;
 
     if (!LIRGenerator::allowStaticTypedArrayAccesses())
         return true;
 
     if (ElementAccessHasExtraIndexedProperty(constraints(), object))
         return true;
 
     if (!object->resultTypeSet())
         return true;
     JSObject *tarrObj = object->resultTypeSet()->getSingleton();
     if (!tarrObj)
         return true;
 
-    TypedArrayObject *tarr = &tarrObj->as<TypedArrayObject>();
-
 #ifdef JSGC_GENERATIONAL
-    if (tarr->runtimeFromMainThread()->gc.nursery.isInside(tarr->viewData()))
+    if (tarrObj->runtimeFromMainThread()->gc.nursery.isInside(AnyTypedArrayViewData(tarrObj)))
         return true;
 #endif
 
-    types::TypeObjectKey *tarrType = types::TypeObjectKey::get(tarr);
+    types::TypeObjectKey *tarrType = types::TypeObjectKey::get(tarrObj);
     if (tarrType->unknownProperties())
         return true;
 
-    Scalar::Type viewType = tarr->type();
+    Scalar::Type viewType = AnyTypedArrayType(tarrObj);
     MDefinition *ptr = convertShiftToMaskForStaticTypedArray(index, viewType);
     if (!ptr)
         return true;
 
     // Emit StoreTypedArrayElementStatic.
-    tarrType->watchStateChangeForTypedArrayData(constraints());
+
+    if (tarrObj->is<TypedArrayObject>())
+        tarrType->watchStateChangeForTypedArrayData(constraints());
 
     object->setImplicitlyUsedUnchecked();
     index->setImplicitlyUsedUnchecked();
 
     // Clamp value to [0, 255] for Uint8ClampedArray.
     MDefinition *toWrite = value;
     if (viewType == Scalar::Uint8Clamped) {
         toWrite = MClampToUint8::New(alloc(), value);
         current->add(toWrite->toInstruction());
     }
 
-    MInstruction *store = MStoreTypedArrayElementStatic::New(alloc(), tarr, ptr, toWrite);
+    MInstruction *store = MStoreTypedArrayElementStatic::New(alloc(), tarrObj, ptr, toWrite);
     current->add(store);
     current->push(value);
 
     if (!resumeAfter(store))
         return false;
 
     *emitted = true;
     return true;
@@ -8173,17 +8174,17 @@ IonBuilder::setElemTryTypedStatic(bool *
 
 bool
 IonBuilder::setElemTryTypedArray(bool *emitted, MDefinition *object,
                                  MDefinition *index, MDefinition *value)
 {
     JS_ASSERT(*emitted == false);
 
     Scalar::Type arrayType;
-    if (!ElementAccessIsTypedArray(object, index, &arrayType))
+    if (!ElementAccessIsAnyTypedArray(object, index, &arrayType))
         return true;
 
     // Emit typed setelem variant.
     if (!jsop_setelem_typed(arrayType, SetElem_Normal, object, index, value))
         return false;
 
     *emitted = true;
     return true;
@@ -8724,17 +8725,17 @@ IonBuilder::objectsHaveCommonPrototype(t
 
             const Class *clasp = type->clasp();
             if (!ClassHasEffectlessLookup(clasp, name) || ClassHasResolveHook(compartment, clasp, name))
                 return false;
 
             // Look for a getter/setter on the class itself which may need
             // to be called. Ignore the getGeneric hook for typed arrays, it
             // only handles integers and forwards names to the prototype.
-            if (isGetter && clasp->ops.getGeneric && !IsTypedArrayClass(clasp))
+            if (isGetter && clasp->ops.getGeneric && !IsAnyTypedArrayClass(clasp))
                 return false;
             if (!isGetter && clasp->ops.setGeneric)
                 return false;
 
             // Test for isOwnProperty() without freezing. If we end up
             // optimizing, freezePropertiesForCommonPropFunc will freeze the
             // property type sets later on.
             types::HeapTypeSetKey property = type->property(NameToId(name));
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -30,16 +30,17 @@
 #include "vm/Shape-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::tl::FloorLog2;
 
 typedef Rooted<TypedArrayObject *> RootedTypedArrayObject;
+typedef Rooted<SharedTypedArrayObject *> RootedSharedTypedArrayObject;
 
 void
 CodeLocationJump::repoint(JitCode *code, MacroAssembler *masm)
 {
     JS_ASSERT(state_ == Relative);
     size_t new_off = (size_t)raw_;
 #ifdef JS_SMALL_BRANCH
     size_t jumpTableEntryOffset = reinterpret_cast<size_t>(jumpTableEntry_);
@@ -1089,43 +1090,44 @@ GenerateArrayLength(JSContext *cx, Macro
 
     /* Failure. */
     masm.bind(&failures);
     attacher.jumpNextStub(masm);
 
     return true;
 }
 
+// In this case, the code for TypedArray and SharedTypedArray is not the same,
+// because the code embeds pointers to the respective class arrays.  Code that
+// caches the stub code must distinguish between the two cases.
 static void
 GenerateTypedArrayLength(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
-                         JSObject *obj, Register object, TypedOrValueRegister output)
+                         const TypedArrayLayout &layout, Register object, TypedOrValueRegister output)
 {
-    JS_ASSERT(obj->is<TypedArrayObject>());
-
     Label failures;
 
     Register tmpReg;
     if (output.hasValue()) {
         tmpReg = output.valueReg().scratchReg();
     } else {
         JS_ASSERT(output.type() == MIRType_Int32);
         tmpReg = output.typedReg().gpr();
     }
     JS_ASSERT(object != tmpReg);
 
     // Implement the negated version of JSObject::isTypedArray predicate.
     masm.loadObjClass(object, tmpReg);
-    masm.branchPtr(Assembler::Below, tmpReg, ImmPtr(&TypedArrayObject::classes[0]),
+    masm.branchPtr(Assembler::Below, tmpReg, ImmPtr(layout.addressOfFirstClass()),
                    &failures);
     masm.branchPtr(Assembler::AboveOrEqual, tmpReg,
-                   ImmPtr(&TypedArrayObject::classes[Scalar::TypeMax]),
+                   ImmPtr(layout.addressOfMaxClass()),
                    &failures);
 
     // Load length.
-    masm.loadTypedOrValue(Address(object, TypedArrayObject::lengthOffset()), output);
+    masm.loadTypedOrValue(Address(object, TypedArrayLayout::lengthOffset()), output);
 
     /* Success. */
     attacher.jumpRejoin(masm);
 
     /* Failure. */
     masm.bind(&failures);
     attacher.jumpNextStub(masm);
 }
@@ -1282,42 +1284,41 @@ GetPropertyIC::tryAttachNative(JSContext
 
 bool
 GetPropertyIC::tryAttachTypedArrayLength(JSContext *cx, HandleScript outerScript, IonScript *ion,
                                          HandleObject obj, HandlePropertyName name, bool *emitted)
 {
     JS_ASSERT(canAttachStub());
     JS_ASSERT(!*emitted);
 
-    if (!obj->is<TypedArrayObject>())
+    if (!IsAnyTypedArray(obj))
         return true;
 
     if (cx->names().length != name)
         return true;
 
-    if (hasTypedArrayLengthStub())
+    if (hasAnyTypedArrayLengthStub(obj))
         return true;
 
     if (output().type() != MIRType_Value && output().type() != MIRType_Int32) {
         // The next execution should cause an invalidation because the type
         // does not fit.
         return true;
     }
 
     if (idempotent())
         return true;
 
     *emitted = true;
 
     MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
-    GenerateTypedArrayLength(cx, masm, attacher, obj, object(), output());
-
-    JS_ASSERT(!hasTypedArrayLengthStub_);
-    hasTypedArrayLengthStub_ = true;
+    GenerateTypedArrayLength(cx, masm, attacher, AnyTypedArrayLayout(obj), object(), output());
+
+    setHasTypedArrayLengthStub(obj);
     return linkAndAttachStub(cx, masm, attacher, ion, "typed array length");
 }
 
 
 static bool
 EmitCallProxyGet(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
                  PropertyName *name, RegisterSet liveRegs, Register object,
                  TypedOrValueRegister output, jsbytecode *pc, void *returnAddr)
@@ -1783,16 +1784,17 @@ GetPropertyIC::update(JSContext *cx, siz
     return true;
 }
 
 void
 GetPropertyIC::reset()
 {
     RepatchIonCache::reset();
     hasTypedArrayLengthStub_ = false;
+    hasSharedTypedArrayLengthStub_ = false;
     hasStrictArgumentsLengthStub_ = false;
     hasNormalArgumentsLengthStub_ = false;
     hasGenericProxyStub_ = false;
 }
 
 bool
 ParallelIonCache::initStubbedShapes(JSContext *cx)
 {
@@ -1832,16 +1834,17 @@ ParallelIonCache::destroy()
     js_delete(stubbedShapes_);
 }
 
 void
 GetPropertyParIC::reset()
 {
     ParallelIonCache::reset();
     hasTypedArrayLengthStub_ = false;
+    hasSharedTypedArrayLengthStub_ = false;
 }
 
 bool
 GetPropertyParIC::attachReadSlot(LockedJSContext &cx, IonScript *ion, HandleObject obj,
                                  HandleObject holder, HandleShape shape)
 {
     // Ready to generate the read slot stub.
     DispatchStubPrepender attacher(*this);
@@ -1862,20 +1865,19 @@ GetPropertyParIC::attachArrayLength(Lock
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel array length");
 }
 
 bool
 GetPropertyParIC::attachTypedArrayLength(LockedJSContext &cx, IonScript *ion, HandleObject obj)
 {
     MacroAssembler masm(cx, ion);
     DispatchStubPrepender attacher(*this);
-    GenerateTypedArrayLength(cx, masm, attacher, obj, object(), output());
-
-    JS_ASSERT(!hasTypedArrayLengthStub_);
-    hasTypedArrayLengthStub_ = true;
+    GenerateTypedArrayLength(cx, masm, attacher, AnyTypedArrayLayout(obj), object(), output());
+
+    setHasTypedArrayLengthStub(obj);
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel typed array length");
 }
 
 bool
 GetPropertyParIC::update(ForkJoinContext *cx, size_t cacheIndex,
                          HandleObject obj, MutableHandleValue vp)
 {
     IonScript *ion = GetTopIonJSScript(cx)->parallelIonScript();
@@ -1922,18 +1924,18 @@ GetPropertyParIC::update(ForkJoinContext
 
                 if (!attachedStub && canCache == GetPropertyIC::CanAttachArrayLength) {
                     if (!cache.attachArrayLength(ncx, ion, obj))
                         return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
                     attachedStub = true;
                 }
             }
 
-            if (!attachedStub && !cache.hasTypedArrayLengthStub() &&
-                obj->is<TypedArrayObject>() && cx->names().length == cache.name() &&
+            if (!attachedStub && !cache.hasAnyTypedArrayLengthStub(obj) &&
+                IsAnyTypedArray(obj) && cx->names().length == cache.name() &&
                 (cache.output().type() == MIRType_Value || cache.output().type() == MIRType_Int32))
             {
                 if (!cache.attachTypedArrayLength(ncx, ion, obj))
                     return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
                 attachedStub = true;
             }
         }
     }
@@ -3184,17 +3186,17 @@ GetElementIC::attachDenseElement(JSConte
     setHasDenseStub();
     return linkAndAttachStub(cx, masm, attacher, ion, "dense array");
 }
 
 /* static */ bool
 GetElementIC::canAttachTypedArrayElement(JSObject *obj, const Value &idval,
                                          TypedOrValueRegister output)
 {
-    if (!obj->is<TypedArrayObject>())
+    if (!IsAnyTypedArray(obj))
         return false;
 
     if (!idval.isInt32() && !idval.isString())
         return false;
 
 
     // Don't emit a stub if the access is out of bounds. We make to make
     // certain that we monitor the type coming out of the typed array when
@@ -3203,43 +3205,43 @@ GetElementIC::canAttachTypedArrayElement
     uint32_t index;
     if (idval.isInt32()) {
         index = idval.toInt32();
     } else {
         index = GetIndexFromString(idval.toString());
         if (index == UINT32_MAX)
             return false;
     }
-    if (index >= obj->as<TypedArrayObject>().length())
+    if (index >= AnyTypedArrayLength(obj))
         return false;
 
     // The output register is not yet specialized as a float register, the only
     // way to accept float typed arrays for now is to return a Value type.
-    uint32_t arrayType = obj->as<TypedArrayObject>().type();
+    uint32_t arrayType = AnyTypedArrayType(obj);
     if (arrayType == Scalar::Float32 || arrayType == Scalar::Float64)
         return output.hasValue();
 
     return output.hasValue() || !output.typedReg().isFloat();
 }
 
 static void
 GenerateGetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
-                             HandleTypedArrayObject tarr, const Value &idval, Register object,
+                             HandleObject tarr, const Value &idval, Register object,
                              ConstantOrRegister index, TypedOrValueRegister output,
                              bool allowDoubleResult)
 {
     JS_ASSERT(GetElementIC::canAttachTypedArrayElement(tarr, idval, output));
 
     Label failures;
 
     // The array type is the object within the table of typed array classes.
-    Scalar::Type arrayType = tarr->type();
+    Scalar::Type arrayType = AnyTypedArrayType(tarr);
 
     // Guard on the shape.
-    Shape *shape = tarr->lastProperty();
+    Shape *shape = AnyTypedArrayShape(tarr);
     masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
 
     // Decide to what type index the stub should be optimized
     Register tmpReg = output.scratchReg().gpr();
     JS_ASSERT(tmpReg != InvalidReg);
     Register indexReg = tmpReg;
     JS_ASSERT(!index.constant());
     if (idval.isString()) {
@@ -3286,26 +3288,26 @@ GenerateGetTypedArrayElement(JSContext *
             masm.unboxInt32(val, indexReg);
         } else {
             JS_ASSERT(!index.reg().typedReg().isFloat());
             indexReg = index.reg().typedReg().gpr();
         }
     }
 
     // Guard on the initialized length.
-    Address length(object, TypedArrayObject::lengthOffset());
+    Address length(object, TypedArrayLayout::lengthOffset());
     masm.branch32(Assembler::BelowOrEqual, length, indexReg, &failures);
 
     // Save the object register on the stack in case of failure.
     Label popAndFail;
     Register elementReg = object;
     masm.push(object);
 
     // Load elements vector.
-    masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), elementReg);
+    masm.loadPtr(Address(object, TypedArrayLayout::dataOffset()), elementReg);
 
     // Load the value. We use an invalid register because the destination
     // register is necessary a non double register.
     int width = Scalar::byteSize(arrayType);
     BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(width));
     if (output.hasValue()) {
         masm.loadFromTypedArray(arrayType, source, output.valueReg(), allowDoubleResult,
                                 elementReg, &popAndFail);
@@ -3321,17 +3323,17 @@ GenerateGetTypedArrayElement(JSContext *
     masm.pop(object);
     masm.bind(&failures);
 
     attacher.jumpNextStub(masm);
 }
 
 bool
 GetElementIC::attachTypedArrayElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
-                                      HandleTypedArrayObject tarr, const Value &idval)
+                                      HandleObject tarr, const Value &idval)
 {
     MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
     GenerateGetTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output(),
                                  allowDoubleResult());
     return linkAndAttachStub(cx, masm, attacher, ion, "typed array");
 }
 
@@ -3493,18 +3495,17 @@ GetElementIC::update(JSContext *cx, size
             attachedStub = true;
         }
         if (!attachedStub && !cache.hasDenseStub() && canAttachDenseElement(obj, idval)) {
             if (!cache.attachDenseElement(cx, outerScript, ion, obj, idval))
                 return false;
             attachedStub = true;
         }
         if (!attachedStub && canAttachTypedArrayElement(obj, idval, cache.output())) {
-            Rooted<TypedArrayObject*> tarr(cx, &obj->as<TypedArrayObject>());
-            if (!cache.attachTypedArrayElement(cx, outerScript, ion, tarr, idval))
+            if (!cache.attachTypedArrayElement(cx, outerScript, ion, obj, idval))
                 return false;
             attachedStub = true;
         }
     }
 
     if (!GetObjectElementOperation(cx, JSOp(*pc), obj, /* wasObject = */true, idval, res))
         return false;
 
@@ -3564,17 +3565,17 @@ IsDenseElementSetInlineable(JSObject *ob
 
     return true;
 }
 
 static bool
 IsTypedArrayElementSetInlineable(JSObject *obj, const Value &idval, const Value &value)
 {
     // Don't bother attaching stubs for assigning strings and objects.
-    return obj->is<TypedArrayObject>() && idval.isInt32() &&
+    return IsAnyTypedArray(obj) && idval.isInt32() &&
            !value.isString() && !value.isObject();
 }
 
 static void
 StoreDenseElement(MacroAssembler &masm, ConstantOrRegister value, Register elements,
                   BaseIndex target)
 {
     // If the ObjectElements::CONVERT_DOUBLE_ELEMENTS flag is set, int32 values
@@ -3738,44 +3739,44 @@ SetElementIC::attachDenseElement(JSConte
     const char *message = guardHoles()            ?
                             "dense array (holes)" :
                             "dense array";
     return linkAndAttachStub(cx, masm, attacher, ion, message);
 }
 
 static bool
 GenerateSetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
-                             HandleTypedArrayObject tarr, Register object,
+                             HandleObject tarr, Register object,
                              ValueOperand indexVal, ConstantOrRegister value,
                              Register tempUnbox, Register temp, FloatRegister tempDouble,
                              FloatRegister tempFloat32)
 {
     Label failures, done, popObjectAndFail;
 
     // Guard on the shape.
-    Shape *shape = tarr->lastProperty();
+    Shape *shape = AnyTypedArrayShape(tarr);
     if (!shape)
         return false;
     masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
 
     // Ensure the index is an int32.
     masm.branchTestInt32(Assembler::NotEqual, indexVal, &failures);
     Register index = masm.extractInt32(indexVal, tempUnbox);
 
     // Guard on the length.
-    Address length(object, TypedArrayObject::lengthOffset());
+    Address length(object, TypedArrayLayout::lengthOffset());
     masm.unboxInt32(length, temp);
     masm.branch32(Assembler::BelowOrEqual, temp, index, &done);
 
     // Load the elements vector.
     Register elements = temp;
-    masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), elements);
+    masm.loadPtr(Address(object, TypedArrayLayout::dataOffset()), elements);
 
     // Set the value.
-    Scalar::Type arrayType = tarr->type();
+    Scalar::Type arrayType = AnyTypedArrayType(tarr);
     int width = Scalar::byteSize(arrayType);
     BaseIndex target(elements, index, ScaleFromElemWidth(width));
 
     if (arrayType == Scalar::Float32) {
         JS_ASSERT_IF(hasUnaliasedDouble(), tempFloat32 != InvalidFloatReg);
         FloatRegister tempFloat = hasUnaliasedDouble() ? tempFloat32 : tempDouble;
         if (!masm.convertConstantOrRegisterToFloat(cx, value, tempFloat, &failures))
             return false;
@@ -3819,17 +3820,17 @@ GenerateSetTypedArrayElement(JSContext *
 
     masm.bind(&failures);
     attacher.jumpNextStub(masm);
     return true;
 }
 
 bool
 SetElementIC::attachTypedArrayElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
-                                      HandleTypedArrayObject tarr)
+                                      HandleObject tarr)
 {
     MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
     if (!GenerateSetTypedArrayElement(cx, masm, attacher, tarr,
                                       object(), index(), value(),
                                       tempToUnboxIndex(), temp(), tempDouble(), tempFloat32()))
     {
         return false;
@@ -3849,18 +3850,17 @@ SetElementIC::update(JSContext *cx, size
     bool attachedStub = false;
     if (cache.canAttachStub()) {
         if (!cache.hasDenseStub() && IsDenseElementSetInlineable(obj, idval)) {
             if (!cache.attachDenseElement(cx, outerScript, ion, obj, idval))
                 return false;
             attachedStub = true;
         }
         if (!attachedStub && IsTypedArrayElementSetInlineable(obj, idval, value)) {
-            RootedTypedArrayObject tarr(cx, &obj->as<TypedArrayObject>());
-            if (!cache.attachTypedArrayElement(cx, outerScript, ion, tarr))
+            if (!cache.attachTypedArrayElement(cx, outerScript, ion, obj))
                 return false;
         }
     }
 
     if (!SetObjectElement(cx, obj, idval, value, cache.strict()))
         return false;
     return true;
 }
@@ -3890,17 +3890,17 @@ SetElementParIC::attachDenseElement(Lock
                             "parallel dense array (holes)" :
                             "parallel dense array";
 
     return linkAndAttachStub(cx, masm, attacher, ion, message);
 }
 
 bool
 SetElementParIC::attachTypedArrayElement(LockedJSContext &cx, IonScript *ion,
-                                         HandleTypedArrayObject tarr)
+                                         HandleObject tarr)
 {
     MacroAssembler masm(cx, ion);
     DispatchStubPrepender attacher(*this);
     if (!GenerateSetTypedArrayElement(cx, masm, attacher, tarr,
                                       object(), index(), value(),
                                       tempToUnboxIndex(), temp(), tempDouble(), tempFloat32()))
     {
         return false;
@@ -3932,18 +3932,17 @@ SetElementParIC::update(ForkJoinContext 
 
             bool attachedStub = false;
             if (IsDenseElementSetInlineable(obj, idval)) {
                 if (!cache.attachDenseElement(ncx, ion, obj, idval))
                     return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
                 attachedStub = true;
             }
             if (!attachedStub && IsTypedArrayElementSetInlineable(obj, idval, value)) {
-                RootedTypedArrayObject tarr(cx, &obj->as<TypedArrayObject>());
-                if (!cache.attachTypedArrayElement(ncx, ion, tarr))
+                if (!cache.attachTypedArrayElement(ncx, ion, obj))
                     return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
             }
         }
     }
 
     return SetElementPar(cx, obj, idval, value, cache.strict());
 }
 
@@ -3975,17 +3974,17 @@ GetElementParIC::attachDenseElement(Lock
     if (!GenerateDenseElement(cx, masm, attacher, obj, idval, object(), index(), output()))
         return false;
 
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel dense element");
 }
 
 bool
 GetElementParIC::attachTypedArrayElement(LockedJSContext &cx, IonScript *ion,
-                                         HandleTypedArrayObject tarr, const Value &idval)
+                                         HandleObject tarr, const Value &idval)
 {
     MacroAssembler masm(cx, ion);
     DispatchStubPrepender attacher(*this);
     GenerateGetTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output(),
                                  allowDoubleResult());
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel typed array");
 }
 
@@ -4043,18 +4042,17 @@ GetElementParIC::update(ForkJoinContext 
             {
                 if (!cache.attachDenseElement(ncx, ion, obj, idval))
                     return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
                 attachedStub = true;
             }
             if (!attachedStub &&
                 GetElementIC::canAttachTypedArrayElement(obj, idval, cache.output()))
             {
-                RootedTypedArrayObject tarr(cx, &obj->as<TypedArrayObject>());
-                if (!cache.attachTypedArrayElement(ncx, ion, tarr, idval))
+                if (!cache.attachTypedArrayElement(ncx, ion, obj, idval))
                     return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
                 attachedStub = true;
             }
         }
     }
 
     return true;
 }
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -9,23 +9,21 @@
 
 #if defined(JS_CODEGEN_ARM)
 # include "jit/arm/Assembler-arm.h"
 #elif defined(JS_CODEGEN_MIPS)
 # include "jit/mips/Assembler-mips.h"
 #endif
 #include "jit/Registers.h"
 #include "jit/shared/Assembler-shared.h"
+#include "vm/TypedArrayCommon.h"
 
 namespace js {
 
 class LockedJSContext;
-class TypedArrayObject;
-
-typedef Handle<TypedArrayObject *> HandleTypedArrayObject;
 
 namespace jit {
 
 #define IONCACHE_KIND_LIST(_)                                   \
     _(GetProperty)                                              \
     _(SetProperty)                                              \
     _(GetElement)                                               \
     _(SetElement)                                               \
@@ -555,16 +553,17 @@ class GetPropertyIC : public RepatchIonC
     TypedOrValueRegister output_;
 
     // Only valid if idempotent
     size_t locationsIndex_;
     size_t numLocations_;
 
     bool monitoredResult_ : 1;
     bool hasTypedArrayLengthStub_ : 1;
+    bool hasSharedTypedArrayLengthStub_ : 1;
     bool hasStrictArgumentsLengthStub_ : 1;
     bool hasNormalArgumentsLengthStub_ : 1;
     bool hasGenericProxyStub_ : 1;
 
   public:
     GetPropertyIC(RegisterSet liveRegs,
                   Register object, PropertyName *name,
                   TypedOrValueRegister output,
@@ -572,16 +571,17 @@ class GetPropertyIC : public RepatchIonC
       : liveRegs_(liveRegs),
         object_(object),
         name_(name),
         output_(output),
         locationsIndex_(0),
         numLocations_(0),
         monitoredResult_(monitoredResult),
         hasTypedArrayLengthStub_(false),
+        hasSharedTypedArrayLengthStub_(false),
         hasStrictArgumentsLengthStub_(false),
         hasNormalArgumentsLengthStub_(false),
         hasGenericProxyStub_(false)
     {
     }
 
     CACHE_HEADER(GetProperty)
 
@@ -594,26 +594,36 @@ class GetPropertyIC : public RepatchIonC
         return name_;
     }
     TypedOrValueRegister output() const {
         return output_;
     }
     bool monitoredResult() const {
         return monitoredResult_;
     }
-    bool hasTypedArrayLengthStub() const {
-        return hasTypedArrayLengthStub_;
+    bool hasAnyTypedArrayLengthStub(HandleObject obj) const {
+        return obj->is<TypedArrayObject>() ? hasTypedArrayLengthStub_ : hasSharedTypedArrayLengthStub_;
     }
     bool hasArgumentsLengthStub(bool strict) const {
         return strict ? hasStrictArgumentsLengthStub_ : hasNormalArgumentsLengthStub_;
     }
     bool hasGenericProxyStub() const {
         return hasGenericProxyStub_;
     }
 
+    void setHasTypedArrayLengthStub(HandleObject obj) {
+        if (obj->is<TypedArrayObject>()) {
+            JS_ASSERT(!hasTypedArrayLengthStub_);
+            hasTypedArrayLengthStub_ = true;
+        } else {
+            JS_ASSERT(!hasSharedTypedArrayLengthStub_);
+            hasSharedTypedArrayLengthStub_ = true;
+        }
+    }
+
     void setLocationInfo(size_t locationsIndex, size_t numLocations) {
         JS_ASSERT(idempotent());
         JS_ASSERT(!numLocations_);
         JS_ASSERT(numLocations);
         locationsIndex_ = locationsIndex;
         numLocations_ = numLocations;
     }
     void getLocationInfo(uint32_t *index, uint32_t *num) const {
@@ -831,17 +841,17 @@ class GetElementIC : public RepatchIonCa
     bool attachGetProp(JSContext *cx, HandleScript outerScript, IonScript *ion,
                        HandleObject obj, const Value &idval, HandlePropertyName name,
                        void *returnAddr);
 
     bool attachDenseElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
                             HandleObject obj, const Value &idval);
 
     bool attachTypedArrayElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
-                                 HandleTypedArrayObject tarr, const Value &idval);
+                                 HandleObject tarr, const Value &idval);
 
     bool attachArgumentsElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
                                 HandleObject obj);
 
     static bool
     update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval,
            MutableHandleValue vp);
 
@@ -929,17 +939,17 @@ class SetElementIC : public RepatchIonCa
         JS_ASSERT(!hasDenseStub());
         hasDenseStub_ = true;
     }
 
     bool attachDenseElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
                             HandleObject obj, const Value &idval);
 
     bool attachTypedArrayElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
-                                 HandleTypedArrayObject tarr);
+                                 HandleObject tarr);
 
     static bool
     update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval,
            HandleValue value);
 };
 
 class BindNameIC : public RepatchIonCache
 {
@@ -1090,23 +1100,25 @@ class ParallelIonCache : public Dispatch
 
 class GetPropertyParIC : public ParallelIonCache
 {
   protected:
     Register object_;
     PropertyName *name_;
     TypedOrValueRegister output_;
     bool hasTypedArrayLengthStub_ : 1;
+    bool hasSharedTypedArrayLengthStub_ : 1;
 
    public:
     GetPropertyParIC(Register object, PropertyName *name, TypedOrValueRegister output)
       : object_(object),
         name_(name),
         output_(output),
-        hasTypedArrayLengthStub_(false)
+        hasTypedArrayLengthStub_(false),
+        hasSharedTypedArrayLengthStub_(false)
     {
     }
 
     CACHE_HEADER(GetPropertyPar)
 
 #ifdef JS_CODEGEN_X86
     // x86 lacks a general purpose scratch register for dispatch caches and
     // must be given one manually.
@@ -1119,18 +1131,28 @@ class GetPropertyParIC : public Parallel
         return object_;
     }
     PropertyName *name() const {
         return name_;
     }
     TypedOrValueRegister output() const {
         return output_;
     }
-    bool hasTypedArrayLengthStub() const {
-        return hasTypedArrayLengthStub_;
+    bool hasAnyTypedArrayLengthStub(HandleObject obj) const {
+        return obj->is<TypedArrayObject>() ? hasTypedArrayLengthStub_ : hasSharedTypedArrayLengthStub_;
+    }
+
+    void setHasTypedArrayLengthStub(HandleObject obj) {
+        if (obj->is<TypedArrayObject>()) {
+            JS_ASSERT(!hasTypedArrayLengthStub_);
+            hasTypedArrayLengthStub_ = true;
+        } else {
+            JS_ASSERT(!hasSharedTypedArrayLengthStub_);
+            hasSharedTypedArrayLengthStub_ = true;
+        }
     }
 
     // CanAttachNativeGetProp Helpers
     typedef LockedJSContext & Context;
     bool canMonitorSingletonUndefinedSlot(HandleObject, HandleShape) const { return true; }
     bool allowGetters() const { return false; }
     bool allowArrayLength(Context, HandleObject) const { return true; }
 
@@ -1193,17 +1215,17 @@ class GetElementParIC : public ParallelI
     bool canMonitorSingletonUndefinedSlot(HandleObject, HandleShape) const { return true; }
     bool allowGetters() const { return false; }
     bool allowArrayLength(Context, HandleObject) const { return false; }
 
     bool attachReadSlot(LockedJSContext &cx, IonScript *ion, HandleObject obj, const Value &idval,
                         HandlePropertyName name, HandleObject holder, HandleShape shape);
     bool attachDenseElement(LockedJSContext &cx, IonScript *ion, HandleObject obj,
                             const Value &idval);
-    bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, HandleTypedArrayObject tarr,
+    bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, HandleObject tarr,
                                  const Value &idval);
 
     static bool update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval,
                        MutableHandleValue vp);
 
 };
 
 class SetPropertyParIC : public ParallelIonCache
@@ -1321,17 +1343,17 @@ class SetElementParIC : public ParallelI
         return strict_;
     }
     bool guardHoles() const {
         return guardHoles_;
     }
 
     bool attachDenseElement(LockedJSContext &cx, IonScript *ion, HandleObject obj,
                             const Value &idval);
-    bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, HandleTypedArrayObject tarr);
+    bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, HandleObject tarr);
 
     static bool update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj,
                        HandleValue idval, HandleValue value);
 };
 
 #undef CACHE_HEADER
 
 // Implement cache casts now that the compiler can see the inheritance.
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -225,22 +225,31 @@ IonBuilder::inlineNativeGetter(CallInfo 
 
     types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet();
     JS_ASSERT(callInfo.argc() == 0);
 
     // Try to optimize typed array lengths. There is one getter for each
     // typed array prototype, and make sure we are accessing the right one
     // for the type of the instance object.
     if (thisTypes) {
-        Scalar::Type type = thisTypes->getTypedArrayType();
+        Scalar::Type type;
+
+        type = thisTypes->getTypedArrayType();
         if (type != Scalar::TypeMax && TypedArrayObject::isOriginalLengthGetter(type, native)) {
             MInstruction *length = addTypedArrayLength(callInfo.thisArg());
             current->push(length);
             return InliningStatus_Inlined;
         }
+
+        type = thisTypes->getSharedTypedArrayType();
+        if (type != Scalar::TypeMax && SharedTypedArrayObject::isOriginalLengthGetter(type, native)) {
+            MInstruction *length = addTypedArrayLength(callInfo.thisArg());
+            current->push(length);
+            return InliningStatus_Inlined;
+        }
     }
 
     return InliningStatus_NotInlined;
 }
 
 types::TemporaryTypeSet *
 IonBuilder::getInlineReturnTypeSet()
 {
@@ -1489,17 +1498,17 @@ IonBuilder::inlineUnsafePutElements(Call
                                                               &obj, nullptr, &elem,
                                                               /* canModify = */ false);
         }
 
         // We can only inline setelem on dense arrays that do not need type
         // barriers and on typed arrays and on typed object arrays.
         Scalar::Type arrayType;
         if ((!isDenseNative || writeNeedsBarrier) &&
-            !ElementAccessIsTypedArray(obj, id, &arrayType) &&
+            !ElementAccessIsAnyTypedArray(obj, id, &arrayType) &&
             !elementAccessIsTypedObjectArrayOfScalarType(obj, id, &arrayType))
         {
             return InliningStatus_NotInlined;
         }
     }
 
     callInfo.setImplicitlyUsedUnchecked();
 
@@ -1518,17 +1527,17 @@ IonBuilder::inlineUnsafePutElements(Call
 
         if (ElementAccessIsDenseNative(obj, id)) {
             if (!inlineUnsafeSetDenseArrayElement(callInfo, base))
                 return InliningStatus_Error;
             continue;
         }
 
         Scalar::Type arrayType;
-        if (ElementAccessIsTypedArray(obj, id, &arrayType)) {
+        if (ElementAccessIsAnyTypedArray(obj, id, &arrayType)) {
             if (!inlineUnsafeSetTypedArrayElement(callInfo, base, arrayType))
                 return InliningStatus_Error;
             continue;
         }
 
         if (elementAccessIsTypedObjectArrayOfScalarType(obj, id, &arrayType)) {
             if (!inlineUnsafeSetTypedObjectArrayElement(callInfo, base, arrayType))
                 return InliningStatus_Error;
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -3349,44 +3349,44 @@ InlinePropertyTable::buildTypeSetForFunc
             types->addType(types::Type::ObjectType(entries_[i]->typeObj), alloc);
     }
     return types;
 }
 
 void *
 MLoadTypedArrayElementStatic::base() const
 {
-    return typedArray_->viewData();
+    return AnyTypedArrayViewData(someTypedArray_);
 }
 
 size_t
 MLoadTypedArrayElementStatic::length() const
 {
-    return typedArray_->byteLength();
+    return AnyTypedArrayByteLength(someTypedArray_);
 }
 
 void *
 MStoreTypedArrayElementStatic::base() const
 {
-    return typedArray_->viewData();
+    return AnyTypedArrayViewData(someTypedArray_);
 }
 
 bool
 MGetElementCache::allowDoubleResult() const
 {
     if (!resultTypeSet())
         return true;
 
     return resultTypeSet()->hasType(types::Type::DoubleType());
 }
 
 size_t
 MStoreTypedArrayElementStatic::length() const
 {
-    return typedArray_->byteLength();
+    return AnyTypedArrayByteLength(someTypedArray_);
 }
 
 bool
 MGetPropertyPolymorphic::mightAlias(const MDefinition *store) const
 {
     // Allow hoisting this instruction if the store does not write to a
     // slot read by this instruction.
 
@@ -3562,34 +3562,37 @@ jit::ElementAccessIsDenseNative(MDefinit
         return false;
 
     types::TemporaryTypeSet *types = obj->resultTypeSet();
     if (!types)
         return false;
 
     // Typed arrays are native classes but do not have dense elements.
     const Class *clasp = types->getKnownClass();
-    return clasp && clasp->isNative() && !IsTypedArrayClass(clasp);
+    return clasp && clasp->isNative() && !IsAnyTypedArrayClass(clasp);
 }
 
 bool
-jit::ElementAccessIsTypedArray(MDefinition *obj, MDefinition *id,
-                               Scalar::Type *arrayType)
+jit::ElementAccessIsAnyTypedArray(MDefinition *obj, MDefinition *id,
+                                  Scalar::Type *arrayType)
 {
     if (obj->mightBeType(MIRType_String))
         return false;
 
     if (id->type() != MIRType_Int32 && id->type() != MIRType_Double)
         return false;
 
     types::TemporaryTypeSet *types = obj->resultTypeSet();
     if (!types)
         return false;
 
     *arrayType = types->getTypedArrayType();
+    if (*arrayType != Scalar::TypeMax)
+        return true;
+    *arrayType = types->getSharedTypedArrayType();
     return *arrayType != Scalar::TypeMax;
 }
 
 bool
 jit::ElementAccessIsPacked(types::CompilerConstraintList *constraints, MDefinition *obj)
 {
     types::TemporaryTypeSet *types = obj->resultTypeSet();
     return types && !types->hasObjectFlags(constraints, types::OBJECT_FLAG_NON_PACKED);
@@ -4028,17 +4031,17 @@ jit::PropertyWriteNeedsTypeBarrier(TempA
     bool success = true;
     for (size_t i = 0; i < types->getObjectCount(); i++) {
         types::TypeObjectKey *object = types->getObject(i);
         if (!object || object->unknownProperties())
             continue;
 
         // TI doesn't track TypedArray objects and should never insert a type
         // barrier for them.
-        if (!name && IsTypedArrayClass(object->clasp()))
+        if (!name && IsAnyTypedArrayClass(object->clasp()))
             continue;
 
         jsid id = name ? NameToId(name) : JSID_VOID;
         types::HeapTypeSetKey property = object->property(id);
         if (!CanWriteProperty(constraints, property, *pvalue)) {
             // Either pobj or pvalue needs to be modified to filter out the
             // types which the value could have but are not in the property,
             // or a VM call is required. A VM call is always required if pobj
@@ -4060,17 +4063,17 @@ jit::PropertyWriteNeedsTypeBarrier(TempA
     if (types->getObjectCount() <= 1)
         return true;
 
     types::TypeObjectKey *excluded = nullptr;
     for (size_t i = 0; i < types->getObjectCount(); i++) {
         types::TypeObjectKey *object = types->getObject(i);
         if (!object || object->unknownProperties())
             continue;
-        if (!name && IsTypedArrayClass(object->clasp()))
+        if (!name && IsAnyTypedArrayClass(object->clasp()))
             continue;
 
         jsid id = name ? NameToId(name) : JSID_VOID;
         types::HeapTypeSetKey property = object->property(id);
         if (CanWriteProperty(constraints, property, *pvalue))
             continue;
 
         if ((property.maybeTypes() && !property.maybeTypes()->empty()) || excluded)
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -18,17 +18,17 @@
 #include "jit/FixedList.h"
 #include "jit/InlineList.h"
 #include "jit/IonAllocPolicy.h"
 #include "jit/IonMacroAssembler.h"
 #include "jit/MOpcodes.h"
 #include "jit/TypedObjectPrediction.h"
 #include "jit/TypePolicy.h"
 #include "vm/ScopeObject.h"
-#include "vm/TypedArrayObject.h"
+#include "vm/TypedArrayCommon.h"
 
 namespace js {
 
 class StringObject;
 
 namespace jit {
 
 class BaselineInspector;
@@ -7945,42 +7945,42 @@ class MLoadTypedArrayElementHole
     ALLOW_CLONE(MLoadTypedArrayElementHole)
 };
 
 // Load a value fallibly or infallibly from a statically known typed array.
 class MLoadTypedArrayElementStatic
   : public MUnaryInstruction,
     public ConvertToInt32Policy<0>
 {
-    MLoadTypedArrayElementStatic(TypedArrayObject *typedArray, MDefinition *ptr)
-      : MUnaryInstruction(ptr), typedArray_(typedArray), fallible_(true)
-    {
-        int type = typedArray_->type();
+    MLoadTypedArrayElementStatic(JSObject *someTypedArray, MDefinition *ptr)
+      : MUnaryInstruction(ptr), someTypedArray_(someTypedArray), fallible_(true)
+    {
+        int type = viewType();
         if (type == Scalar::Float32)
             setResultType(MIRType_Float32);
         else if (type == Scalar::Float64)
             setResultType(MIRType_Double);
         else
             setResultType(MIRType_Int32);
     }
 
-    AlwaysTenured<TypedArrayObject*> typedArray_;
+    AlwaysTenured<JSObject*> someTypedArray_;
     bool fallible_;
 
   public:
     INSTRUCTION_HEADER(LoadTypedArrayElementStatic);
 
-    static MLoadTypedArrayElementStatic *New(TempAllocator &alloc, TypedArrayObject *typedArray,
+    static MLoadTypedArrayElementStatic *New(TempAllocator &alloc, JSObject *someTypedArray,
                                              MDefinition *ptr)
     {
-        return new(alloc) MLoadTypedArrayElementStatic(typedArray, ptr);
+        return new(alloc) MLoadTypedArrayElementStatic(someTypedArray, ptr);
     }
 
     Scalar::Type viewType() const {
-        return typedArray_->type();
+        return AnyTypedArrayType(someTypedArray_);
     }
     void *base() const;
     size_t length() const;
 
     MDefinition *ptr() const { return getOperand(0); }
     AliasSet getAliasSet() const {
         return AliasSet::Load(AliasSet::TypedArrayElement);
     }
@@ -7994,17 +7994,17 @@ class MLoadTypedArrayElementStatic
     }
 
     TypePolicy *typePolicy() {
         return this;
     }
 
     void computeRange(TempAllocator &alloc);
     bool truncate(TruncateKind kind);
-    bool canProduceFloat32() const { return typedArray_->type() == Scalar::Float32; }
+    bool canProduceFloat32() const { return viewType() == Scalar::Float32; }
 };
 
 class MStoreTypedArrayElement
   : public MTernaryInstruction,
     public StoreTypedArrayPolicy
 {
     Scalar::Type arrayType_;
 
@@ -8142,37 +8142,37 @@ class MStoreTypedArrayElementHole
     ALLOW_CLONE(MStoreTypedArrayElementHole)
 };
 
 // Store a value infallibly to a statically known typed array.
 class MStoreTypedArrayElementStatic :
     public MBinaryInstruction
   , public StoreTypedArrayElementStaticPolicy
 {
-    MStoreTypedArrayElementStatic(TypedArrayObject *typedArray, MDefinition *ptr, MDefinition *v)
-      : MBinaryInstruction(ptr, v), typedArray_(typedArray)
+    MStoreTypedArrayElementStatic(JSObject *someTypedArray, MDefinition *ptr, MDefinition *v)
+      : MBinaryInstruction(ptr, v), someTypedArray_(someTypedArray)
     {}
 
-    AlwaysTenured<TypedArrayObject*> typedArray_;
+    AlwaysTenured<JSObject*> someTypedArray_;
 
   public:
     INSTRUCTION_HEADER(StoreTypedArrayElementStatic);
 
-    static MStoreTypedArrayElementStatic *New(TempAllocator &alloc, TypedArrayObject *typedArray,
+    static MStoreTypedArrayElementStatic *New(TempAllocator &alloc, JSObject *someTypedArray,
                                               MDefinition *ptr, MDefinition *v)
     {
-        return new(alloc) MStoreTypedArrayElementStatic(typedArray, ptr, v);
+        return new(alloc) MStoreTypedArrayElementStatic(someTypedArray, ptr, v);
     }
 
     TypePolicy *typePolicy() {
         return this;
     }
 
     Scalar::Type viewType() const {
-        return typedArray_->type();
+        return AnyTypedArrayType(someTypedArray_);
     }
     bool isFloatArray() const {
         return viewType() == Scalar::Float32 ||
                viewType() == Scalar::Float64;
     }
 
     void *base() const;
     size_t length() const;
@@ -8180,17 +8180,17 @@ class MStoreTypedArrayElementStatic :
     MDefinition *ptr() const { return getOperand(0); }
     MDefinition *value() const { return getOperand(1); }
     AliasSet getAliasSet() const {
         return AliasSet::Store(AliasSet::TypedArrayElement);
     }
     TruncateKind operandTruncateKind(size_t index) const;
 
     bool canConsumeFloat32(MUse *use) const {
-        return use == getUseFor(1) && typedArray_->type() == Scalar::Float32;
+        return use == getUseFor(1) && viewType() == Scalar::Float32;
     }
 };
 
 // Compute an "effective address", i.e., a compound computation of the form:
 //   base + index * scale + displacement
 class MEffectiveAddress : public MBinaryInstruction
 {
     MEffectiveAddress(MDefinition *base, MDefinition *index, Scale scale, int32_t displacement)
@@ -11818,18 +11818,18 @@ const MInstruction *MDefinition::toInstr
 MControlInstruction *MDefinition::toControlInstruction() {
     JS_ASSERT(isControlInstruction());
     return (MControlInstruction *)this;
 }
 
 // Helper functions used to decide how to build MIR.
 
 bool ElementAccessIsDenseNative(MDefinition *obj, MDefinition *id);
-bool ElementAccessIsTypedArray(MDefinition *obj, MDefinition *id,
-                               Scalar::Type *arrayType);
+bool ElementAccessIsAnyTypedArray(MDefinition *obj, MDefinition *id,
+                                  Scalar::Type *arrayType);
 bool ElementAccessIsPacked(types::CompilerConstraintList *constraints, MDefinition *obj);
 bool ElementAccessMightBeCopyOnWrite(types::CompilerConstraintList *constraints, MDefinition *obj);
 bool ElementAccessHasExtraIndexedProperty(types::CompilerConstraintList *constraints,
                                           MDefinition *obj);
 MIRType DenseNativeElementType(types::CompilerConstraintList *constraints, MDefinition *obj);
 BarrierKind PropertyReadNeedsTypeBarrier(JSContext *propertycx,
                                          types::CompilerConstraintList *constraints,
                                          types::TypeObjectKey *object, PropertyName *name,
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -10,16 +10,17 @@
 
 #include "jit/Ion.h"
 #include "jit/IonAnalysis.h"
 #include "jit/JitSpewer.h"
 #include "jit/MIR.h"
 #include "jit/MIRGenerator.h"
 #include "jit/MIRGraph.h"
 #include "vm/NumericConversions.h"
+#include "vm/TypedArrayCommon.h"
 
 #include "jsopcodeinlines.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::Abs;
 using mozilla::CountLeadingZeroes32;
@@ -1484,19 +1485,19 @@ MLoadTypedArrayElement::computeRange(Tem
     setRange(GetTypedArrayRange(alloc, arrayType()));
 }
 
 void
 MLoadTypedArrayElementStatic::computeRange(TempAllocator &alloc)
 {
     // We don't currently use MLoadTypedArrayElementStatic for uint32, so we
     // don't have to worry about it returning a value outside our type.
-    JS_ASSERT(typedArray_->type() != Scalar::Uint32);
-
-    setRange(GetTypedArrayRange(alloc, typedArray_->type()));
+    JS_ASSERT(AnyTypedArrayType(someTypedArray_) != Scalar::Uint32);
+
+    setRange(GetTypedArrayRange(alloc, AnyTypedArrayType(someTypedArray_)));
 }
 
 void
 MArrayLength::computeRange(TempAllocator &alloc)
 {
     // Array lengths can go up to UINT32_MAX, but we only create MArrayLength
     // nodes when the value is known to be int32 (see the
     // OBJECT_FLAG_LENGTH_OVERFLOW flag).
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -346,16 +346,17 @@ MSG_DEF(JSMSG_PROXY_GETOWN_OBJORUNDEF, 0
 MSG_DEF(JSMSG_PROXY_REVOKED,           0, JSEXN_TYPEERR, "illegal operation attempted on a revoked proxy")
 
 // Structured cloning
 MSG_DEF(JSMSG_SC_BAD_CLONE_VERSION,    0, JSEXN_ERR, "unsupported structured clone version")
 MSG_DEF(JSMSG_SC_BAD_SERIALIZED_DATA,  1, JSEXN_INTERNALERR, "bad serialized structured data ({0})")
 MSG_DEF(JSMSG_SC_DUP_TRANSFERABLE,     0, JSEXN_TYPEERR, "duplicate transferable for structured clone")
 MSG_DEF(JSMSG_SC_NOT_TRANSFERABLE,     0, JSEXN_TYPEERR, "invalid transferable array for structured clone")
 MSG_DEF(JSMSG_SC_UNSUPPORTED_TYPE,     0, JSEXN_TYPEERR, "unsupported type for structured data")
+MSG_DEF(JSMSG_SC_SHMEM_MUST_TRANSFER,  0, JSEXN_TYPEERR, "SharedArrayBuffer must be explicitly transfered during structured cloning")
 
 // Debugger
 MSG_DEF(JSMSG_ASSIGN_FUNCTION_OR_NULL, 1, JSEXN_TYPEERR, "value assigned to {0} must be a function or null")
 MSG_DEF(JSMSG_DEBUG_BAD_LINE,          0, JSEXN_TYPEERR, "invalid line number")
 MSG_DEF(JSMSG_DEBUG_BAD_OFFSET,        0, JSEXN_TYPEERR, "invalid script offset")
 MSG_DEF(JSMSG_DEBUG_BAD_REFERENT,      2, JSEXN_TYPEERR, "{0} does not refer to {1}")
 MSG_DEF(JSMSG_DEBUG_BAD_RESUMPTION,    0, JSEXN_TYPEERR, "debugger resumption value must be undefined, {throw: val}, {return: val}, or null")
 MSG_DEF(JSMSG_DEBUG_CANT_DEBUG_GLOBAL, 0, JSEXN_ERR, "passing non-debuggable global to addDebuggee")
@@ -416,19 +417,30 @@ MSG_DEF(JSMSG_TYPEDOBJECT_BAD_ARGS,    0
 MSG_DEF(JSMSG_TYPEDOBJECT_BINARYARRAY_BAD_INDEX, 0, JSEXN_RANGEERR, "invalid or out-of-range index")
 MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED, 0, JSEXN_TYPEERR, "handle unattached")
 MSG_DEF(JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD_ARGS, 0, JSEXN_RANGEERR, "invalid field descriptor")
 MSG_DEF(JSMSG_TYPEDOBJECT_TOO_BIG,     0, JSEXN_ERR, "Type is too large to allocate")
 
 // Typed array
 MSG_DEF(JSMSG_BAD_INDEX,               0, JSEXN_RANGEERR, "invalid or out-of-range index")
 MSG_DEF(JSMSG_TYPED_ARRAY_BAD_ARGS,    0, JSEXN_ERR, "invalid arguments")
+MSG_DEF(JSMSG_TYPED_ARRAY_BAD_OBJECT,  0, JSEXN_TYPEERR, "invalid object argument")
 MSG_DEF(JSMSG_TYPED_ARRAY_BAD_INDEX,   0, JSEXN_ERR, "invalid or out-of-range index")
 MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG,1, JSEXN_ERR, "argument {0} must be >= 0")
 
+// Shared array buffer
+MSG_DEF(JSMSG_SHARED_ARRAY_BAD_OBJECT,  0, JSEXN_TYPEERR, "invalid object argument")
+MSG_DEF(JSMSG_SHARED_ARRAY_BAD_LENGTH,  0, JSEXN_RANGEERR, "length argument out of range")
+
+// Shared typed array
+MSG_DEF(JSMSG_SHARED_TYPED_ARRAY_BAD_OBJECT,  0, JSEXN_TYPEERR, "invalid object argument")
+MSG_DEF(JSMSG_SHARED_TYPED_ARRAY_BAD_ARGS,    0, JSEXN_RANGEERR, "bad combination of offset, length, and element size")
+MSG_DEF(JSMSG_SHARED_TYPED_ARRAY_ARG_RANGE,   1, JSEXN_RANGEERR, "argument {0} out of range")
+MSG_DEF(JSMSG_SHARED_TYPED_ARRAY_BAD_LENGTH,  0, JSEXN_TYPEERR, "length argument must not be an object")
+
 // Parallel array
 MSG_DEF(JSMSG_PAR_ARRAY_BAD_ARG,       0, JSEXN_RANGEERR, "invalid parallel method argument")
 MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_BAD_TARGET, 1, JSEXN_ERR, "target for index {0} is not an integer")
 MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_BOUNDS,0, JSEXN_ERR, "index in scatter vector out of bounds")
 MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_CONFLICT, 0, JSEXN_ERR, "no conflict resolution function provided")
 
 // Reflect
 MSG_DEF(JSMSG_BAD_PARSE_NODE,          0, JSEXN_INTERNALERR, "bad parse node")
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -72,21 +72,20 @@
 #include "vm/ErrorObject.h"
 #include "vm/HelperThreads.h"
 #include "vm/Interpreter.h"
 #include "vm/NumericConversions.h"
 #include "vm/RegExpStatics.h"
 #include "vm/Runtime.h"
 #include "vm/SavedStacks.h"
 #include "vm/Shape.h"
-#include "vm/SharedArrayObject.h"
 #include "vm/StopIterationObject.h"
 #include "vm/StringBuffer.h"
 #include "vm/Symbol.h"
-#include "vm/TypedArrayObject.h"
+#include "vm/TypedArrayCommon.h"
 #include "vm/WeakMapObject.h"
 #include "vm/WrapperObject.h"
 #include "vm/Xdr.h"
 
 #include "jsatominlines.h"
 #include "jsfuninlines.h"
 #include "jsinferinlines.h"
 #include "jsscriptinlines.h"
@@ -2735,17 +2734,17 @@ JS_AlreadyHasOwnPropertyById(JSContext *
     if (JSID_IS_INT(id)) {
         uint32_t index = JSID_TO_INT(id);
 
         if (obj->containsDenseElement(index)) {
             *foundp = true;
             return true;
         }
 
-        if (obj->is<TypedArrayObject>() && index < obj->as<TypedArrayObject>().length()) {
+        if (IsAnyTypedArray(obj) && index < AnyTypedArrayLength(obj)) {
             *foundp = true;
             return true;
         }
     }
 
     *foundp = obj->nativeContains(cx, id);
     return true;
 }
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -27,16 +27,17 @@
 #include "ds/Sort.h"
 #include "gc/Heap.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/ForkJoin.h"
 #include "vm/Interpreter.h"
 #include "vm/NumericConversions.h"
 #include "vm/Shape.h"
 #include "vm/StringBuffer.h"
+#include "vm/TypedArrayCommon.h"
 
 #include "jsatominlines.h"
 
 #include "vm/ArgumentsObject-inl.h"
 #include "vm/ArrayObject-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/Runtime-inl.h"
 
@@ -843,17 +844,17 @@ js::ObjectMayHaveExtraIndexedProperties(
          * return true.
          */
         if (!obj->isNative())
             return true;
         if (obj->isIndexed())
             return true;
         if (obj->getDenseInitializedLength() > 0)
             return true;
-        if (obj->is<TypedArrayObject>())
+        if (IsAnyTypedArray(obj))
             return true;
     }
 
     return false;
 }
 
 static bool
 AddLengthProperty(ExclusiveContext *cx, HandleObject obj)
@@ -2068,17 +2069,17 @@ js::array_push(JSContext *cx, unsigned a
 
     /* Steps 2-3. */
     uint32_t length;
     if (!GetLengthProperty(cx, obj, &length))
         return false;
 
     /* Fast path for native objects with dense elements. */
     do {
-        if (!obj->isNative() || obj->is<TypedArrayObject>())
+        if (!obj->isNative() || IsAnyTypedArray(obj.get()))
             break;
 
         if (obj->is<ArrayObject>() && !obj->as<ArrayObject>().lengthIsWritable())
             break;
 
         if (ObjectMayHaveExtraIndexedProperties(obj))
             break;
 
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1472,30 +1472,48 @@ byteSize(Type atype)
  */
 
 extern JS_FRIEND_API(JSObject *)
 JS_NewInt8Array(JSContext *cx, uint32_t nelements);
 extern JS_FRIEND_API(JSObject *)
 JS_NewUint8Array(JSContext *cx, uint32_t nelements);
 extern JS_FRIEND_API(JSObject *)
 JS_NewUint8ClampedArray(JSContext *cx, uint32_t nelements);
-
 extern JS_FRIEND_API(JSObject *)
 JS_NewInt16Array(JSContext *cx, uint32_t nelements);
 extern JS_FRIEND_API(JSObject *)
 JS_NewUint16Array(JSContext *cx, uint32_t nelements);
 extern JS_FRIEND_API(JSObject *)
 JS_NewInt32Array(JSContext *cx, uint32_t nelements);
 extern JS_FRIEND_API(JSObject *)
 JS_NewUint32Array(JSContext *cx, uint32_t nelements);
 extern JS_FRIEND_API(JSObject *)
 JS_NewFloat32Array(JSContext *cx, uint32_t nelements);
 extern JS_FRIEND_API(JSObject *)
 JS_NewFloat64Array(JSContext *cx, uint32_t nelements);
 
+extern JS_FRIEND_API(JSObject *)
+JS_NewSharedInt8Array(JSContext *cx, uint32_t nelements);
+extern JS_FRIEND_API(JSObject *)
+JS_NewSharedUint8Array(JSContext *cx, uint32_t nelements);
+extern JS_FRIEND_API(JSObject *)
+JS_NewSharedUint8ClampedArray(JSContext *cx, uint32_t nelements);
+extern JS_FRIEND_API(JSObject *)
+JS_NewSharedInt16Array(JSContext *cx, uint32_t nelements);
+extern JS_FRIEND_API(JSObject *)
+JS_NewSharedUint16Array(JSContext *cx, uint32_t nelements);
+extern JS_FRIEND_API(JSObject *)
+JS_NewSharedInt32Array(JSContext *cx, uint32_t nelements);
+extern JS_FRIEND_API(JSObject *)
+JS_NewSharedUint32Array(JSContext *cx, uint32_t nelements);
+extern JS_FRIEND_API(JSObject *)
+JS_NewSharedFloat32Array(JSContext *cx, uint32_t nelements);
+extern JS_FRIEND_API(JSObject *)
+JS_NewSharedFloat64Array(JSContext *cx, uint32_t nelements);
+
 /*
  * Create a new typed array and copy in values from the given object. The
  * object is used as if it were an array; that is, the new array (if
  * successfully created) will have length given by array.length, and its
  * elements will be those specified by array[0], array[1], and so on, after
  * conversion to the typed array element type.
  */
 
@@ -1547,32 +1565,69 @@ JS_NewUint32ArrayWithBuffer(JSContext *c
                             uint32_t byteOffset, int32_t length);
 extern JS_FRIEND_API(JSObject *)
 JS_NewFloat32ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
                              uint32_t byteOffset, int32_t length);
 extern JS_FRIEND_API(JSObject *)
 JS_NewFloat64ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
                              uint32_t byteOffset, int32_t length);
 
+// As for the above, passing length==(uint32_t)-1 signifies "up to the
+// end of the buffer".
+
+extern JS_FRIEND_API(JSObject *)
+JS_NewSharedInt8ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
+                                uint32_t byteOffset, uint32_t length);
+extern JS_FRIEND_API(JSObject *)
+JS_NewSharedUint8ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
+                                 uint32_t byteOffset, uint32_t length);
+extern JS_FRIEND_API(JSObject *)
+JS_NewSharedUint8ClampedArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
+                                        uint32_t byteOffset, uint32_t length);
+extern JS_FRIEND_API(JSObject *)
+JS_NewSharedInt16ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
+                                 uint32_t byteOffset, uint32_t length);
+extern JS_FRIEND_API(JSObject *)
+JS_NewSharedUint16ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
+                                  uint32_t byteOffset, uint32_t length);
+extern JS_FRIEND_API(JSObject *)
+JS_NewSharedInt32ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
+                                 uint32_t byteOffset, uint32_t length);
+extern JS_FRIEND_API(JSObject *)
+JS_NewSharedUint32ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
+                                  uint32_t byteOffset, uint32_t length);
+extern JS_FRIEND_API(JSObject *)
+JS_NewSharedFloat32ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
+                                   uint32_t byteOffset, uint32_t length);
+extern JS_FRIEND_API(JSObject *)
+JS_NewSharedFloat64ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
+                                   uint32_t byteOffset, uint32_t length);
+
 /*
  * Create a new ArrayBuffer with the given byte length.
  */
 extern JS_FRIEND_API(JSObject *)
 JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes);
 
 /*
  * Check whether obj supports JS_GetTypedArray* APIs. Note that this may return
  * false if a security wrapper is encountered that denies the unwrapping. If
  * this test or one of the JS_Is*Array tests succeeds, then it is safe to call
  * the various accessor JSAPI calls defined below.
  */
 extern JS_FRIEND_API(bool)
 JS_IsTypedArrayObject(JSObject *obj);
 
 /*
+ * Ditto for JS_GetSharedTypedArray* APIs.
+ */
+extern JS_FRIEND_API(bool)
+JS_IsSharedTypedArrayObject(JSObject *obj);
+
+/*
  * Check whether obj supports JS_GetArrayBufferView* APIs. Note that this may
  * return false if a security wrapper is encountered that denies the
  * unwrapping. If this test or one of the more specific tests succeeds, then it
  * is safe to call the various ArrayBufferView accessor JSAPI calls defined
  * below.
  */
 extern JS_FRIEND_API(bool)
 JS_IsArrayBufferViewObject(JSObject *obj);
@@ -1595,16 +1650,35 @@ extern JS_FRIEND_API(bool)
 JS_IsInt32Array(JSObject *obj);
 extern JS_FRIEND_API(bool)
 JS_IsUint32Array(JSObject *obj);
 extern JS_FRIEND_API(bool)
 JS_IsFloat32Array(JSObject *obj);
 extern JS_FRIEND_API(bool)
 JS_IsFloat64Array(JSObject *obj);
 
+extern JS_FRIEND_API(bool)
+JS_IsSharedInt8Array(JSObject *obj);
+extern JS_FRIEND_API(bool)
+JS_IsSharedUint8Array(JSObject *obj);
+extern JS_FRIEND_API(bool)
+JS_IsSharedUint8ClampedArray(JSObject *obj);
+extern JS_FRIEND_API(bool)
+JS_IsSharedInt16Array(JSObject *obj);
+extern JS_FRIEND_API(bool)
+JS_IsSharedUint16Array(JSObject *obj);
+extern JS_FRIEND_API(bool)
+JS_IsSharedInt32Array(JSObject *obj);
+extern JS_FRIEND_API(bool)
+JS_IsSharedUint32Array(JSObject *obj);
+extern JS_FRIEND_API(bool)
+JS_IsSharedFloat32Array(JSObject *obj);
+extern JS_FRIEND_API(bool)
+JS_IsSharedFloat64Array(JSObject *obj);
+
 /*
  * Test for specific typed array types (ArrayBufferView subtypes) and return
  * the unwrapped object if so, else nullptr.  Never throws.
  */
 
 namespace js {
 
 extern JS_FRIEND_API(JSObject *)
@@ -1627,28 +1701,57 @@ extern JS_FRIEND_API(JSObject *)
 UnwrapFloat64Array(JSObject *obj);
 
 extern JS_FRIEND_API(JSObject *)
 UnwrapArrayBuffer(JSObject *obj);
 
 extern JS_FRIEND_API(JSObject *)
 UnwrapArrayBufferView(JSObject *obj);
 
+extern JS_FRIEND_API(JSObject *)
+UnwrapSharedInt8Array(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapSharedUint8Array(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapSharedUint8ClampedArray(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapSharedInt16Array(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapSharedUint16Array(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapSharedInt32Array(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapSharedUint32Array(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapSharedFloat32Array(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapSharedFloat64Array(JSObject *obj);
+
 namespace detail {
 
 extern JS_FRIEND_DATA(const Class* const) Int8ArrayClassPtr;
 extern JS_FRIEND_DATA(const Class* const) Uint8ArrayClassPtr;
 extern JS_FRIEND_DATA(const Class* const) Uint8ClampedArrayClassPtr;
 extern JS_FRIEND_DATA(const Class* const) Int16ArrayClassPtr;
 extern JS_FRIEND_DATA(const Class* const) Uint16ArrayClassPtr;
 extern JS_FRIEND_DATA(const Class* const) Int32ArrayClassPtr;
 extern JS_FRIEND_DATA(const Class* const) Uint32ArrayClassPtr;
 extern JS_FRIEND_DATA(const Class* const) Float32ArrayClassPtr;
 extern JS_FRIEND_DATA(const Class* const) Float64ArrayClassPtr;
 
+extern JS_FRIEND_DATA(const Class* const) SharedInt8ArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) SharedUint8ArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) SharedUint8ClampedArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) SharedInt16ArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) SharedUint16ArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) SharedInt32ArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) SharedUint32ArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) SharedFloat32ArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) SharedFloat64ArrayClassPtr;
+
 const size_t TypedArrayLengthSlot = 1;
 
 } // namespace detail
 
 /*
  * Test for specific typed array types (ArrayBufferView subtypes) and return
  * the unwrapped object if so, else nullptr.  Never throws.
  */
@@ -1729,16 +1832,19 @@ JS_GetArrayBufferViewType(JSObject *obj)
  * Check whether obj supports the JS_GetArrayBuffer* APIs. Note that this may
  * return false if a security wrapper is encountered that denies the
  * unwrapping. If this test succeeds, then it is safe to call the various
  * accessor JSAPI calls defined below.
  */
 extern JS_FRIEND_API(bool)
 JS_IsArrayBufferObject(JSObject *obj);
 
+extern JS_FRIEND_API(bool)
+JS_IsSharedArrayBufferObject(JSObject *obj);
+
 /*
  * Return the available byte length of an array buffer.
  *
  * |obj| must have passed a JS_IsArrayBufferObject test, or somehow be known
  * that it would pass such a test: it is an ArrayBuffer or a wrapper of an
  * ArrayBuffer, and the unwrapping will succeed.
  */
 extern JS_FRIEND_API(uint32_t)
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2030,16 +2030,26 @@ TemporaryTypeSet::getTypedArrayType()
 {
     const Class *clasp = getKnownClass();
 
     if (clasp && IsTypedArrayClass(clasp))
         return (Scalar::Type) (clasp - &TypedArrayObject::classes[0]);
     return Scalar::TypeMax;
 }
 
+Scalar::Type
+TemporaryTypeSet::getSharedTypedArrayType()
+{
+    const Class *clasp = getKnownClass();
+
+    if (clasp && IsSharedTypedArrayClass(clasp))
+        return (Scalar::Type) (clasp - &SharedTypedArrayObject::classes[0]);
+    return Scalar::TypeMax;
+}
+
 bool
 TemporaryTypeSet::isDOMClass()
 {
     if (unknownObject())
         return false;
 
     unsigned count = getObjectCount();
     for (unsigned i = 0; i < count; i++) {
@@ -2270,18 +2280,22 @@ types::UseNewTypeForInitializer(JSScript
      * Objects created outside loops in global and eval scripts should have
      * singleton types. For now this is only done for plain objects and typed
      * arrays, but not normal arrays.
      */
 
     if (script->functionNonDelazifying() && !script->treatAsRunOnce())
         return GenericObject;
 
-    if (key != JSProto_Object && !(key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray))
+    if (key != JSProto_Object &&
+        !(key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray) &&
+        !(key >= JSProto_SharedInt8Array && key <= JSProto_SharedUint8ClampedArray))
+    {
         return GenericObject;
+    }
 
     /*
      * All loops in the script will have a JSTRY_ITER or JSTRY_LOOP try note
      * indicating their boundary.
      */
 
     if (!script->hasTrynotes())
         return SingletonObject;
@@ -2312,17 +2326,17 @@ types::UseNewTypeForInitializer(JSScript
 
 static inline bool
 ClassCanHaveExtraProperties(const Class *clasp)
 {
     JS_ASSERT(clasp->resolve);
     return clasp->resolve != JS_ResolveStub
         || clasp->ops.lookupGeneric
         || clasp->ops.getGeneric
-        || IsTypedArrayClass(clasp);
+        || IsAnyTypedArrayClass(clasp);
 }
 
 static inline bool
 PrototypeHasIndexedProperty(CompilerConstraintList *constraints, JSObject *obj)
 {
     do {
         TypeObjectKey *type = TypeObjectKey::get(obj);
         if (ClassCanHaveExtraProperties(type->clasp()))
@@ -2352,17 +2366,17 @@ bool
 types::TypeCanHaveExtraIndexedProperties(CompilerConstraintList *constraints,
                                          TemporaryTypeSet *types)
 {
     const Class *clasp = types->getKnownClass();
 
     // Note: typed arrays have indexed properties not accounted for by type
     // information, though these are all in bounds and will be accounted for
     // by JIT paths.
-    if (!clasp || (ClassCanHaveExtraProperties(clasp) && !IsTypedArrayClass(clasp)))
+    if (!clasp || (ClassCanHaveExtraProperties(clasp) && !IsAnyTypedArrayClass(clasp)))
         return true;
 
     if (types->hasObjectFlags(constraints, types::OBJECT_FLAG_SPARSE_INDEXES))
         return true;
 
     JSObject *proto = types->getCommonPrototype();
     if (!proto)
         return true;
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -754,19 +754,22 @@ class TemporaryTypeSet : public TypeSet
     /* Apply func to the members of the set and return an appropriate result.
      * The iteration may end early if the result becomes known early.
      */
     ForAllResult forAllClasses(bool (*func)(const Class *clasp));
 
     /* Get the prototype shared by all objects in this set, or nullptr. */
     JSObject *getCommonPrototype();
 
-    /* Get the typed array type of all objects in this set, or TypedArrayObject::TYPE_MAX. */
+    /* Get the typed array type of all objects in this set, or Scalar::TypeMax. */
     Scalar::Type getTypedArrayType();
 
+    /* Get the shared typed array type of all objects in this set, or Scalar::TypeMax. */
+    Scalar::Type getSharedTypedArrayType();
+
     /* Whether all objects have JSCLASS_IS_DOMJSCLASS set. */
     bool isDOMClass();
 
     /* Whether clasp->isCallable() is true for one or more objects in this set. */
     bool maybeCallable();
 
     /* Whether clasp->emulatesUndefined() is true for one or more objects in this set. */
     bool maybeEmulatesUndefined();
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -13,16 +13,17 @@
 
 #include "mozilla/PodOperations.h"
 
 #include "builtin/SymbolObject.h"
 #include "vm/ArrayObject.h"
 #include "vm/BooleanObject.h"
 #include "vm/NumberObject.h"
 #include "vm/SharedArrayObject.h"
+#include "vm/SharedTypedArrayObject.h"
 #include "vm/StringObject.h"
 #include "vm/TypedArrayObject.h"
 
 #include "jscntxtinlines.h"
 
 #include "jit/ExecutionMode-inl.h"
 
 namespace js {
@@ -311,16 +312,27 @@ GetClassForProtoKey(JSProtoKey key)
       case JSProto_Uint16Array:
       case JSProto_Int32Array:
       case JSProto_Uint32Array:
       case JSProto_Float32Array:
       case JSProto_Float64Array:
       case JSProto_Uint8ClampedArray:
         return &TypedArrayObject::classes[key - JSProto_Int8Array];
 
+      case JSProto_SharedInt8Array:
+      case JSProto_SharedUint8Array:
+      case JSProto_SharedInt16Array:
+      case JSProto_SharedUint16Array:
+      case JSProto_SharedInt32Array:
+      case JSProto_SharedUint32Array:
+      case JSProto_SharedFloat32Array:
+      case JSProto_SharedFloat64Array:
+      case JSProto_SharedUint8ClampedArray:
+        return &SharedTypedArrayObject::classes[key - JSProto_SharedInt8Array];
+
       case JSProto_ArrayBuffer:
         return &ArrayBufferObject::class_;
 
       case JSProto_SharedArrayBuffer:
         return &SharedArrayBufferObject::class_;
 
       case JSProto_DataView:
         return &DataViewObject::class_;
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -25,16 +25,17 @@
 
 #include "ds/Sort.h"
 #include "gc/Marking.h"
 #include "vm/GeneratorObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/Shape.h"
 #include "vm/StopIterationObject.h"
+#include "vm/TypedArrayCommon.h"
 
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/Stack-inl.h"
 #include "vm/String-inl.h"
 
@@ -143,19 +144,19 @@ EnumerateNativeProperties(JSContext *cx,
         for (size_t i = 0; i < initlen; ++i, ++vp) {
             if (!vp->isMagic(JS_ELEMENTS_HOLE)) {
                 /* Dense arrays never get so large that i would not fit into an integer id. */
                 if (!Enumerate(cx, pobj, INT_TO_JSID(i), /* enumerable = */ true, flags, ht, props))
                     return false;
             }
         }
 
-        /* Collect any typed array elements from this object. */
-        if (pobj->is<TypedArrayObject>()) {
-            size_t len = pobj->as<TypedArrayObject>().length();
+        /* Collect any typed array or shared typed array elements from this object. */
+        if (IsAnyTypedArray(pobj)) {
+            size_t len = AnyTypedArrayLength(pobj);
             for (size_t i = 0; i < len; i++) {
                 if (!Enumerate(cx, pobj, INT_TO_JSID(i), /* enumerable = */ true, flags, ht, props))
                     return false;
             }
         }
 
         size_t initialLength = props->length();
 
@@ -706,17 +707,17 @@ js::GetIterator(JSContext *cx, HandleObj
              * allows us to re-use a previous iterator object that is not
              * currently active.
              */
             {
                 JSObject *pobj = obj;
                 do {
                     if (!pobj->isNative() ||
                         !pobj->hasEmptyElements() ||
-                        pobj->is<TypedArrayObject>() ||
+                        IsAnyTypedArray(pobj) ||
                         pobj->hasUncacheableProto() ||
                         pobj->getOps()->enumerate ||
                         pobj->getClass()->enumerate != JS_EnumerateStub ||
                         pobj->nativeContainsPure(cx->names().iteratorIntrinsic))
                     {
                         shapes.clear();
                         goto miss;
                     }
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -1694,16 +1694,52 @@ js::ToUint16Slow(JSContext *cx, const Ha
     unsigned m = JS_BIT(16);
     d = fmod(d, (double) m);
     if (d < 0)
         d += m;
     *out = (uint16_t) d;
     return true;
 }
 
+template<typename T>
+bool
+js::ToLengthClamped(T *cx, HandleValue v, uint32_t *out, bool *overflow)
+{
+    if (v.isInt32()) {
+        int32_t i = v.toInt32();
+        *out = i < 0 ? 0 : i;
+        return true;
+    }
+    double d;
+    if (v.isDouble()) {
+        d = v.toDouble();
+    } else {
+        if (!ToNumber(cx, v, &d)) {
+            *overflow = false;
+            return false;
+        }
+    }
+    d = ToInteger(d);
+    if (d <= 0.0) {
+        *out = 0;
+        return true;
+    }
+    if (d >= (double)0xFFFFFFFEU) {
+        *overflow = true;
+        return false;
+    }
+    *out = (uint32_t)d;
+    return true;
+}
+
+template bool
+js::ToLengthClamped<JSContext>(JSContext*, HandleValue, uint32_t*, bool*);
+template bool
+js::ToLengthClamped<ExclusiveContext>(ExclusiveContext*, HandleValue, uint32_t*, bool*);
+
 template <typename CharT>
 bool
 js_strtod(ThreadSafeContext *cx, const CharT *begin, const CharT *end, const CharT **dEnd,
           double *d)
 {
     const CharT *s = SkipSpace(begin, end);
     size_t length = end - s;
 
--- a/js/src/jsnum.h
+++ b/js/src/jsnum.h
@@ -245,16 +245,25 @@ ToInteger(JSContext *cx, HandleValue v, 
         extern JS_PUBLIC_API(bool) ToNumberSlow(JSContext *cx, Value v, double *dp);
         if (!ToNumberSlow(cx, v, dp))
             return false;
     }
     *dp = ToInteger(*dp);
     return true;
 }
 
+/* ES6 7.1.15 ToLength, but clamped to the [0,2^32-2] range.  If the
+ * return value is false then *overflow will be true iff the value was
+ * not clampable to uint32_t range.
+ *
+ * For JSContext and ExclusiveContext.
+ */
+template<typename T>
+bool ToLengthClamped(T *cx, HandleValue v, uint32_t *out, bool *overflow);
+
 inline bool
 SafeAdd(int32_t one, int32_t two, int32_t *res)
 {
     // Use unsigned for the 32-bit operation since signed overflow gets
     // undefined behavior.
     *res = uint32_t(one) + uint32_t(two);
     int64_t ores = (int64_t)one + (int64_t)two;
     return ores == (int64_t)*res;
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -46,16 +46,17 @@
 #include "jit/BaselineJIT.h"
 #include "js/MemoryMetrics.h"
 #include "js/OldDebugAPI.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/Interpreter.h"
 #include "vm/ProxyObject.h"
 #include "vm/RegExpStaticsObject.h"
 #include "vm/Shape.h"
+#include "vm/TypedArrayCommon.h"
 
 #include "jsatominlines.h"
 #include "jsboolinlines.h"
 #include "jscntxtinlines.h"
 #include "jscompartmentinlines.h"
 
 #include "vm/ArrayObject-inl.h"
 #include "vm/BooleanObject-inl.h"
@@ -1273,17 +1274,17 @@ JSObject::sealOrFreeze(JSContext *cx, Ha
 
     AutoIdVector props(cx);
     if (!GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY | JSITER_SYMBOLS, &props))
         return false;
 
     /* preventExtensions must sparsify dense objects, so we can assign to holes without checks. */
     JS_ASSERT_IF(obj->isNative(), obj->getDenseCapacity() == 0);
 
-    if (obj->isNative() && !obj->inDictionaryMode() && !obj->is<TypedArrayObject>()) {
+    if (obj->isNative() && !obj->inDictionaryMode() && !IsAnyTypedArray(obj)) {
         /*
          * Seal/freeze non-dictionary objects by constructing a new shape
          * hierarchy mirroring the original one, which can be shared if many
          * objects with the same structure are sealed/frozen. If we use the
          * generic path below then any non-empty object will be converted to
          * dictionary mode.
          */
         RootedShape last(cx, EmptyShape::getInitialShape(cx, obj->getClass(),
@@ -1363,24 +1364,24 @@ JSObject::isSealedOrFrozen(JSContext *cx
     bool extensible;
     if (!JSObject::isExtensible(cx, obj, &extensible))
         return false;
     if (extensible) {
         *resultp = false;
         return true;
     }
 
-    if (obj->is<TypedArrayObject>()) {
+    if (IsAnyTypedArray(obj)) {
         if (it == SEAL) {
             // Typed arrays are always sealed.
             *resultp = true;
         } else {
             // Typed arrays cannot be frozen, but an empty typed array is
             // trivially frozen.
-            *resultp = (obj->as<TypedArrayObject>().length() == 0);
+            *resultp = (AnyTypedArrayLength(obj) == 0);
         }
         return true;
     }
 
     AutoIdVector props(cx);
     if (!GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY | JSITER_SYMBOLS, &props))
         return false;
 
@@ -4201,17 +4202,17 @@ DefinePropertyOrElement(typename Executi
                         bool callSetterAfterwards, bool setterIsStrict)
 {
     /* Use dense storage for new indexed properties where possible. */
     if (JSID_IS_INT(id) &&
         getter == JS_PropertyStub &&
         setter == JS_StrictPropertyStub &&
         attrs == JSPROP_ENUMERATE &&
         (!obj->isIndexed() || !obj->nativeContainsPure(id)) &&
-        !obj->is<TypedArrayObject>())
+        !IsAnyTypedArray(obj))
     {
         uint32_t index = JSID_TO_INT(id);
         bool definesPast;
         if (!WouldDefinePastNonwritableLength(cx, obj, index, setterIsStrict, &definesPast))
             return false;
         if (definesPast)
             return true;
 
@@ -4252,17 +4253,17 @@ DefinePropertyOrElement(typename Executi
             if (!WouldDefinePastNonwritableLength(cx, arr, index, setterIsStrict, &definesPast))
                 return false;
             if (definesPast)
                 return true;
         }
     }
 
     // Don't define new indexed properties on typed arrays.
-    if (obj->is<TypedArrayObject>()) {
+    if (IsAnyTypedArray(obj)) {
         uint64_t index;
         if (IsTypedArrayIndex(id, &index))
             return true;
     }
 
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
 
     RootedShape shape(cx, JSObject::putProperty<mode>(cx, obj, id, getter, setter,
@@ -4334,17 +4335,17 @@ js::DefineNativeProperty(ExclusiveContex
         if (!NativeLookupOwnProperty(cx, obj, id, &shape))
             return false;
         if (shape) {
             /*
              * If we are defining a getter whose setter was already defined, or
              * vice versa, finish the job via obj->changeProperty.
              */
             if (IsImplicitDenseOrTypedArrayElement(shape)) {
-                if (obj->is<TypedArrayObject>()) {
+                if (IsAnyTypedArray(obj)) {
                     /* Ignore getter/setter properties added to typed arrays. */
                     return true;
                 }
                 if (!JSObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id)))
                     return false;
                 shape = obj->nativeLookup(cx, id);
             }
             if (shape->isAccessorDescriptor()) {
@@ -4548,20 +4549,20 @@ LookupOwnPropertyInline(ExclusiveContext
         MarkDenseOrTypedArrayElementFound<allowGC>(propp);
         *donep = true;
         return true;
     }
 
     // Check for a typed array element. Integer lookups always finish here
     // so that integer properties on the prototype are ignored even for out
     // of bounds accesses.
-    if (obj->template is<TypedArrayObject>()) {
+    if (IsAnyTypedArray(obj)) {
         uint64_t index;
         if (IsTypedArrayIndex(id, &index)) {
-            if (index < obj->template as<TypedArrayObject>().length()) {
+            if (index < AnyTypedArrayLength(obj)) {
                 objp.set(obj);
                 MarkDenseOrTypedArrayElementFound<allowGC>(propp);
             } else {
                 objp.set(nullptr);
                 propp.set(nullptr);
             }
             *donep = true;
             return true;
@@ -5157,20 +5158,20 @@ LookupPropertyPureInline(JSObject *obj, 
         /* Search for a native dense element, typed array element, or property. */
 
         if (JSID_IS_INT(id) && current->containsDenseElement(JSID_TO_INT(id))) {
             *objp = current;
             MarkDenseOrTypedArrayElementFound<NoGC>(propp);
             return true;
         }
 
-        if (current->is<TypedArrayObject>()) {
+        if (IsAnyTypedArray(current)) {
             uint64_t index;
             if (IsTypedArrayIndex(id, &index)) {
-                if (index < obj->as<TypedArrayObject>().length()) {
+                if (index < AnyTypedArrayLength(obj)) {
                     *objp = current;
                     MarkDenseOrTypedArrayElementFound<NoGC>(propp);
                 } else {
                     *objp = nullptr;
                     *propp = nullptr;
                 }
                 return true;
             }
@@ -5262,18 +5263,18 @@ js::GetPropertyPure(ThreadSafeContext *c
 
     /* Special case 'length' on Array and TypedArray. */
     if (JSID_IS_ATOM(id, cx->names().length)) {
         if (obj->is<ArrayObject>()) {
             vp->setNumber(obj->as<ArrayObject>().length());
             return true;
         }
 
-        if (obj->is<TypedArrayObject>()) {
-            vp->setNumber(obj->as<TypedArrayObject>().length());
+        if (IsAnyTypedArray(obj)) {
+            vp->setNumber(AnyTypedArrayLength(obj));
             return true;
         }
     }
 
     return NativeGetPureInline(obj2, shape, vp);
 }
 
 static bool
@@ -5555,36 +5556,40 @@ baseops::SetPropertyHelper(typename Exec
              */
             shape = nullptr;
         }
     }
 
     if (IsImplicitDenseOrTypedArrayElement(shape)) {
         uint32_t index = JSID_TO_INT(id);
 
-        if (obj->is<TypedArrayObject>()) {
+        if (IsAnyTypedArray(obj)) {
             double d;
             if (mode == ParallelExecution) {
                 // Bail if converting the value might invoke user-defined
                 // conversions.
                 if (vp.isObject())
                     return false;
                 if (!NonObjectToNumber(cxArg, vp, &d))
                     return false;
             } else {
                 if (!ToNumber(cxArg->asJSContext(), vp, &d))
                     return false;
             }
 
             // Silently do nothing for out-of-bounds sets, for consistency with
             // current behavior.  (ES6 currently says to throw for this in
             // strict mode code, so we may eventually need to change.)
-            TypedArrayObject &tarray = obj->as<TypedArrayObject>();
-            if (index < tarray.length())
-                TypedArrayObject::setElement(tarray, index, d);
+            uint32_t len = AnyTypedArrayLength(obj);
+            if (index < len) {
+                if (obj->is<TypedArrayObject>())
+                    TypedArrayObject::setElement(obj->as<TypedArrayObject>(), index, d);
+                else
+                    SharedTypedArrayObject::setElement(obj->as<SharedTypedArrayObject>(), index, d);
+            }
             return true;
         }
 
         bool definesPast;
         if (!WouldDefinePastNonwritableLength(cxArg, obj, index, strict, &definesPast))
             return false;
         if (definesPast) {
             /* Bail out of parallel execution if we are strict to throw. */
@@ -5697,17 +5702,17 @@ baseops::SetAttributes(JSContext *cx, Ha
 {
     RootedObject nobj(cx);
     RootedShape shape(cx);
     if (!baseops::LookupProperty<CanGC>(cx, obj, id, &nobj, &shape))
         return false;
     if (!shape)
         return true;
     if (nobj->isNative() && IsImplicitDenseOrTypedArrayElement(shape)) {
-        if (nobj->is<TypedArrayObject>()) {
+        if (IsAnyTypedArray(nobj.get())) {
             if (*attrsp == (JSPROP_ENUMERATE | JSPROP_PERMANENT))
                 return true;
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_SET_ARRAY_ATTRS);
             return false;
         }
         if (!JSObject::sparsifyDenseElement(cx, nobj, JSID_TO_INT(id)))
             return false;
         shape = obj->nativeLookup(cx, id);
@@ -5736,17 +5741,17 @@ baseops::DeleteGeneric(JSContext *cx, Ha
          * class's delProperty hook, passing succeeded as the result parameter.
          */
         return CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded);
     }
 
     cx->runtime()->gc.poke();
 
     if (IsImplicitDenseOrTypedArrayElement(shape)) {
-        if (obj->is<TypedArrayObject>()) {
+        if (IsAnyTypedArray(obj)) {
             // Don't delete elements from typed arrays.
             *succeeded = false;
             return true;
         }
 
         if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded))
             return false;
         if (!succeeded)
@@ -5797,17 +5802,17 @@ js::WatchGuts(JSContext *cx, JS::HandleO
     }
 
     return wpmap->watch(cx, obj, id, js::WatchHandler, callable);
 }
 
 bool
 baseops::Watch(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable)
 {
-    if (!obj->isNative() || obj->is<TypedArrayObject>()) {
+    if (!obj->isNative() || IsAnyTypedArray(obj)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH,
                              obj->getClass()->name);
         return false;
     }
 
     return WatchGuts(cx, obj, id, callable);
 }
 
@@ -6509,18 +6514,20 @@ JSObject::addSizeOfExcludingThis(mozilla
         // - ( 1.0%, 96.4%): Proxy
 
     } else if (is<ArgumentsObject>()) {
         info->objectsMallocHeapMisc += as<ArgumentsObject>().sizeOfMisc(mallocSizeOf);
     } else if (is<RegExpStaticsObject>()) {
         info->objectsMallocHeapMisc += as<RegExpStaticsObject>().sizeOfData(mallocSizeOf);
     } else if (is<PropertyIteratorObject>()) {
         info->objectsMallocHeapMisc += as<PropertyIteratorObject>().sizeOfMisc(mallocSizeOf);
-    } else if (is<ArrayBufferObject>() || is<SharedArrayBufferObject>()) {
+    } else if (is<ArrayBufferObject>()) {
         ArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, info);
+    } else if (is<SharedArrayBufferObject>()) {
+        SharedArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, info);
     } else if (is<AsmJSModuleObject>()) {
         as<AsmJSModuleObject>().addSizeOfMisc(mallocSizeOf, &info->objectsNonHeapCodeAsmJS,
                                               &info->objectsMallocHeapMisc);
 #ifdef JS_HAS_CTYPES
     } else {
         // This must be the last case.
         info->objectsMallocHeapMisc +=
             js::SizeOfDataIfCDataObject(mallocSizeOf, const_cast<JSObject *>(this));
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -12,16 +12,17 @@
 #include "builtin/MapObject.h"
 #include "builtin/TypedObject.h"
 #include "vm/ArrayObject.h"
 #include "vm/DateObject.h"
 #include "vm/NumberObject.h"
 #include "vm/Probes.h"
 #include "vm/ScopeObject.h"
 #include "vm/StringObject.h"
+#include "vm/TypedArrayCommon.h"
 
 #include "jsatominlines.h"
 #include "jscompartmentinlines.h"
 #include "jsgcinlines.h"
 #include "jsinferinlines.h"
 
 #include "gc/ForkJoinNursery-inl.h"
 #include "vm/ObjectImpl-inl.h"
@@ -337,16 +338,18 @@ JSObject::ensureDenseElementsPreservePac
     return ensureDenseElementsNoPackedCheck(cx, index, extra);
 }
 
 inline js::Value
 JSObject::getDenseOrTypedArrayElement(uint32_t idx)
 {
     if (is<js::TypedArrayObject>())
         return as<js::TypedArrayObject>().getElement(idx);
+    if (is<js::SharedTypedArrayObject>())
+        return as<js::SharedTypedArrayObject>().getElement(idx);
     return getDenseElement(idx);
 }
 
 inline void
 JSObject::initDenseElementsUnbarriered(uint32_t dstStart, const js::Value *src, uint32_t count) {
     /*
      * For use by parallel threads, which since they cannot see nursery
      * things do not require a barrier.
@@ -1118,18 +1121,18 @@ ObjectClassIs(HandleObject obj, ESClassV
 
     switch (classValue) {
       case ESClass_Object: return obj->is<JSObject>();
       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>() || obj->is<SharedArrayBufferObject>();
+      case ESClass_ArrayBuffer: return obj->is<ArrayBufferObject>();
+      case ESClass_SharedArrayBuffer: return obj->is<SharedArrayBufferObject>();
       case ESClass_Date: return obj->is<DateObject>();
       case ESClass_Set: return obj->is<SetObject>();
       case ESClass_Map: return obj->is<MapObject>();
     }
     MOZ_CRASH("bad classValue");
 }
 
 inline bool
--- a/js/src/jsprototypes.h
+++ b/js/src/jsprototypes.h
@@ -30,16 +30,17 @@
  * the latter is called for the rest. Consumers that don't care about this
  * distinction can simply pass the same macro to both, which is exactly what
  * JS_FOR_EACH_PROTOTYPE does.
  */
 
 #define CLASP(name)                 (&name##Class)
 #define OCLASP(name)                (&name##Object::class_)
 #define TYPED_ARRAY_CLASP(type)     (&TypedArrayObject::classes[Scalar::type])
+#define SHARED_TYPED_ARRAY_CLASP(type) (&SharedTypedArrayObject::classes[Scalar::type])
 
 #ifdef ENABLE_PARALLEL_JS
 #define IF_PJS(real,imaginary) real
 #else
 #define IF_PJS(real,imaginary) imaginary
 #endif
 
 #ifdef EXPOSE_INTL_API
@@ -105,12 +106,21 @@
     real(DataView,              35,     js_InitDataViewClass,      OCLASP(DataView)) \
 IF_SYMBOLS(real,imaginary)(Symbol,              36,     js_InitSymbolClass,        &js::SymbolObject::class_) \
 IF_SAB(real,imaginary)(SharedArrayBuffer,       37,     js_InitSharedArrayBufferClass, &js::SharedArrayBufferObject::protoClass) \
 IF_INTL(real,imaginary) (Intl,                  38,     js_InitIntlClass,          CLASP(Intl)) \
 IF_BDATA(real,imaginary)(TypedObject,           39,     js_InitTypedObjectModuleObject,   OCLASP(TypedObjectModule)) \
     imaginary(GeneratorFunction,     40,     js_InitIteratorClasses, dummy) \
 IF_BDATA(real,imaginary)(SIMD,                  41,     js_InitSIMDClass, OCLASP(SIMD)) \
     real(WeakSet,               42,     js_InitWeakSetClass,       OCLASP(WeakSet)) \
+IF_SAB(real,imaginary)(SharedInt8Array,         43,     js_InitViaClassSpec,       SHARED_TYPED_ARRAY_CLASP(Int8)) \
+IF_SAB(real,imaginary)(SharedUint8Array,        44,     js_InitViaClassSpec,       SHARED_TYPED_ARRAY_CLASP(Uint8)) \
+IF_SAB(real,imaginary)(SharedInt16Array,        45,     js_InitViaClassSpec,       SHARED_TYPED_ARRAY_CLASP(Int16)) \
+IF_SAB(real,imaginary)(SharedUint16Array,       46,     js_InitViaClassSpec,       SHARED_TYPED_ARRAY_CLASP(Uint16)) \
+IF_SAB(real,imaginary)(SharedInt32Array,        47,     js_InitViaClassSpec,       SHARED_TYPED_ARRAY_CLASP(Int32)) \
+IF_SAB(real,imaginary)(SharedUint32Array,       48,     js_InitViaClassSpec,       SHARED_TYPED_ARRAY_CLASP(Uint32)) \
+IF_SAB(real,imaginary)(SharedFloat32Array,      49,     js_InitViaClassSpec,       SHARED_TYPED_ARRAY_CLASP(Float32)) \
+IF_SAB(real,imaginary)(SharedFloat64Array,      50,     js_InitViaClassSpec,       SHARED_TYPED_ARRAY_CLASP(Float64)) \
+IF_SAB(real,imaginary)(SharedUint8ClampedArray, 51,     js_InitViaClassSpec,       SHARED_TYPED_ARRAY_CLASP(Uint8Clamped)) \
 
 #define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)
 
 #endif /* jsprototypes_h */
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -263,16 +263,17 @@ UNIFIED_SOURCES += [
     'vm/RegExpObject.cpp',
     'vm/RegExpStatics.cpp',
     'vm/Runtime.cpp',
     'vm/SavedStacks.cpp',
     'vm/ScopeObject.cpp',
     'vm/SelfHosting.cpp',
     'vm/Shape.cpp',
     'vm/SharedArrayObject.cpp',
+    'vm/SharedTypedArrayObject.cpp',
     'vm/SPSProfiler.cpp',
     'vm/Stack.cpp',
     'vm/String.cpp',
     'vm/StringBuffer.cpp',
     'vm/StructuredClone.cpp',
     'vm/Symbol.cpp',
     'vm/ThreadPool.cpp',
     'vm/TypedArrayObject.cpp',
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/extensions/sharedtypedarray.js
@@ -0,0 +1,240 @@
+// |reftest| skip-if(!xulRuntime.shell)
+/* -*- Mode: js2; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Minimal test cases that obey the (current, but undesirable)
+// requirement that the buffer size should be acceptable to asm.js -
+// at least 4K AND a power of 2 OR a multiple of 16MB.  Also, on
+// 64-bit a SharedArrayBuffer is very expensive under these rules - a
+// 4GB area is reserved for it.  So don't go allocating a ton of them.
+//
+// These tests cannot test that sharing works across workers.  There
+// are or will be tests, in dom/workers, that do that.
+//
+// Structured cloning is not tested here (there are no APIs).
+
+var b;
+
+function testSharedArrayBuffer() {
+    b = new SharedArrayBuffer("4096"); // Test string conversion, too
+    assertEq(b instanceof SharedArrayBuffer, true);
+    assertEq(b.byteLength, 4096);
+
+    assertEq(!!SharedArrayBuffer.isView, true);
+
+    b.fnord = "Hi there";
+    assertEq(b.fnord, "Hi there");
+
+    SharedArrayBuffer.prototype.abracadabra = "no wishing for wishes!";
+    assertEq(b.abracadabra, "no wishing for wishes!");
+
+    // can "convert" a buffer (really works as an assertion)
+    assertEq(SharedArrayBuffer(b), b);
+
+    // can't convert any other object
+    assertThrowsInstanceOf(() => SharedArrayBuffer({}), TypeError);
+
+    // asm.js limitation - may go away
+    assertThrowsInstanceOf(() => new SharedArrayBuffer(12), Error);
+
+    // byteLength can be invoked as per normal, indirectly
+    assertEq(Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype,"byteLength").get.call(b), 4096);
+
+    // however byteLength is not generic
+    assertThrowsInstanceOf(() => Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype,"byteLength").get.call({}), TypeError);
+
+}
+
+function testSharedTypedArray() {
+    assertEq(SharedInt8Array.prototype.BYTES_PER_ELEMENT, 1);
+    assertEq(SharedUint8Array.prototype.BYTES_PER_ELEMENT, 1);
+    assertEq(SharedUint8ClampedArray.prototype.BYTES_PER_ELEMENT, 1);
+    assertEq(SharedInt16Array.prototype.BYTES_PER_ELEMENT, 2);
+    assertEq(SharedUint16Array.prototype.BYTES_PER_ELEMENT, 2);
+    assertEq(SharedInt32Array.prototype.BYTES_PER_ELEMENT, 4);
+    assertEq(SharedUint32Array.prototype.BYTES_PER_ELEMENT, 4);
+    assertEq(SharedFloat32Array.prototype.BYTES_PER_ELEMENT, 4);
+    assertEq(SharedFloat32Array.prototype.BYTES_PER_ELEMENT, 4);
+    assertEq(SharedFloat64Array.prototype.BYTES_PER_ELEMENT, 8);
+    assertEq(SharedFloat64Array.prototype.BYTES_PER_ELEMENT, 8);
+
+    assertEq(SharedInt8Array.BYTES_PER_ELEMENT, 1);
+    assertEq(SharedUint8Array.BYTES_PER_ELEMENT, 1);
+    assertEq(SharedUint8ClampedArray.BYTES_PER_ELEMENT, 1);
+    assertEq(SharedInt16Array.BYTES_PER_ELEMENT, 2);
+    assertEq(SharedUint16Array.BYTES_PER_ELEMENT, 2);
+    assertEq(SharedInt32Array.BYTES_PER_ELEMENT, 4);
+    assertEq(SharedUint32Array.BYTES_PER_ELEMENT, 4);
+    assertEq(SharedFloat32Array.BYTES_PER_ELEMENT, 4);
+    assertEq(SharedFloat32Array.BYTES_PER_ELEMENT, 4);
+    assertEq(SharedFloat64Array.BYTES_PER_ELEMENT, 8);
+    assertEq(SharedFloat64Array.BYTES_PER_ELEMENT, 8);
+
+    // Distinct prototypes for distinct types
+    SharedInt8Array.prototype.hello = "hi there";
+    assertEq(SharedUint8Array.prototype.hello, undefined);
+
+    var x1 = new SharedInt8Array(b);
+    var x2 = new SharedInt32Array(b);
+
+    assertEq(SharedArrayBuffer.isView(x1), true);
+    assertEq(SharedArrayBuffer.isView(x2), true);
+    assertEq(SharedArrayBuffer.isView({}), false);
+    assertEq(SharedArrayBuffer.isView(new Int32Array(10)), false);
+
+    assertEq(x1.buffer, b);
+    assertEq(x2.buffer, b);
+
+    assertEq(Object.getOwnPropertyDescriptor(SharedInt8Array.prototype,"buffer").get.call(x1), x1.buffer);
+    assertEq(Object.getOwnPropertyDescriptor(SharedInt32Array.prototype,"buffer").get.call(x2), x2.buffer);
+
+    // Not generic
+    assertThrowsInstanceOf(() => Object.getOwnPropertyDescriptor(SharedInt8Array.prototype,"buffer").get.call({}), TypeError);
+
+    // Not even to other shared typed arrays
+    assertThrowsInstanceOf(() => Object.getOwnPropertyDescriptor(SharedInt8Array.prototype,"buffer").get.call(x2), TypeError);
+
+    assertEq(x1.byteLength, b.byteLength);
+    assertEq(x2.byteLength, b.byteLength);
+
+    assertEq(x1.byteOffset, 0);
+    assertEq(x2.byteOffset, 0);
+
+    assertEq(x1.length, b.byteLength);
+    assertEq(x2.length, b.byteLength / 4);
+
+    // Conversions that should work
+    assertEq(SharedInt8Array(x1), x1);
+    assertEq(SharedInt32Array(x2), x2);
+
+    var x3 = new SharedInt8Array(b, 20);
+    assertEq(x3.length, b.byteLength - 20);
+    assertEq(x3.byteOffset, 20);
+
+    var x3 = new SharedInt32Array(b, 20, 10);
+    assertEq(x3.length, 10);
+    assertEq(x3.byteOffset, 20);
+
+    // Mismatched type
+    assertThrowsInstanceOf(() => SharedInt8Array(x2), TypeError);
+
+    // Unconvertable object
+    assertThrowsInstanceOf(() => SharedInt8Array({}), TypeError);
+
+    // negative start
+    assertThrowsInstanceOf(() => new SharedInt8Array(b, -7), RangeError);
+
+    // not congruent mod element size
+    assertThrowsInstanceOf(() => new SharedInt32Array(b, 3), RangeError);
+
+    // start out of range
+    assertThrowsInstanceOf(() => new SharedInt32Array(b, 4104), RangeError);
+
+    // end out of range
+    assertThrowsInstanceOf(() => new SharedInt32Array(b, 4092, 2), RangeError);
+
+    var b2 = new SharedInt32Array(1024); // Should create a new buffer
+    assertEq(b2.length, 1024);
+    assertEq(b2.byteLength, 4096);
+    assertEq(b2.byteOffset, 0);
+    assertEq(b2.buffer != b, true);
+
+    var b3 = new SharedInt32Array("1024"); // Should create a new buffer
+    assertEq(b3.length, 1024);
+    assertEq(b3.byteLength, 4096);
+    assertEq(b3.byteOffset, 0);
+    assertEq(b3.buffer != b, true);
+
+    // bad length
+    assertThrowsInstanceOf(() => new SharedInt32Array({}), TypeError);
+
+    // Views alias the storage
+    x2[0] = -1;
+    assertEq(x1[0], -1);
+    x1[0] = 1;
+    x1[1] = 1;
+    x1[2] = 1;
+    x1[3] = 1;
+    assertEq(x2[0], 0x01010101);
+
+    assertEq(x2[5], 0);
+    x3[0] = -1;
+    assertEq(x2[5], -1);
+
+    // Out-of-range accesses yield undefined
+    assertEq(x2[1023], 0);
+    assertEq(x2[1024], undefined);
+    assertEq(x2[2047], undefined);
+}
+
+function testSharedTypedArrayMethods() {
+    var v = new SharedInt32Array(b);
+    for ( var i=0 ; i < v.length ; i++ )
+        v[i] = i;
+    
+    // Rudimentary tests - they don't test any boundary conditions
+
+    // subarray
+    var w = v.subarray(10, 20);
+    assertEq(w.length, 10);
+    for ( var i=0 ; i < w.length ; i++ )
+        assertEq(w[i], v[i+10]);
+    for ( var i=0 ; i < w.length ; i++ )
+        w[i] = -w[i];
+    for ( var i=0 ; i < w.length ; i++ )
+        assertEq(w[i], v[i+10]);
+
+    // copyWithin
+    for ( var i=0 ; i < v.length ; i++ )
+        v[i] = i;
+    v.copyWithin(10, 20, 30);
+    for ( var i=0 ; i < 10 ; i++ )
+        assertEq(v[i], i);
+    for ( var i=10 ; i < 20 ; i++ )
+        assertEq(v[i], v[i+10]);
+
+    // set
+    for ( var i=0 ; i < v.length ; i++ )
+        v[i] = i;
+    v.set([-1,-2,-3,-4,-5,-6,-7,-8,-9,-10], 5);
+    for ( var i=5 ; i < 15 ; i++ )
+        assertEq(v[i], -i+4);
+
+    // In the following two cases the two arrays will reference the same buffer,
+    // so there will be an overlapping copy.
+    //
+    // Case 1: Read from lower indices than are written
+    v.set(v.subarray(0, 7), 1);
+    assertEq(v[0], 0);
+    assertEq(v[1], 0);
+    assertEq(v[2], 1);
+    assertEq(v[3], 2);
+    assertEq(v[4], 3);
+    assertEq(v[5], 4);
+    assertEq(v[6], -1);
+    assertEq(v[7], -2);
+    assertEq(v[8], -4);
+    assertEq(v[9], -5);
+
+    // Case 2: Read from higher indices than are written
+    v.set(v.subarray(1, 5), 0);
+    assertEq(v[0], 0);
+    assertEq(v[1], 1);
+    assertEq(v[2], 2);
+    assertEq(v[3], 3);
+    assertEq(v[4], 3);
+    assertEq(v[5], 4);
+    assertEq(v[6], -1);
+    assertEq(v[7], -2);
+    assertEq(v[8], -4);
+    assertEq(v[9], -5);
+}
+
+testSharedArrayBuffer();
+testSharedTypedArray();
+testSharedTypedArrayMethods();
+
+reportCompare(0, 0, 'ok');
--- a/js/src/tests/js1_8_5/shell.js
+++ b/js/src/tests/js1_8_5/shell.js
@@ -1,14 +1,33 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
+// Note, copied from elsewhere
+if (typeof assertThrowsInstanceOf === 'undefined') {
+    var assertThrowsInstanceOf = function assertThrowsInstanceOf(f, ctor, msg) {
+        var fullmsg;
+        try {
+            f();
+        } catch (exc) {
+            if (exc instanceof ctor)
+                return;
+            fullmsg = "Assertion failed: expected exception " + ctor.name + ", got " + exc;
+        }
+        if (fullmsg === undefined)
+            fullmsg = "Assertion failed: expected exception " + ctor.name + ", no exception thrown";
+        if (msg !== undefined)
+            fullmsg += " - " + msg;
+        throw new Error(fullmsg);
+    };
+}
+
 // NOTE: This only turns on 1.8.5 in shell builds.  The browser requires the
 //       futzing in js/src/tests/browser.js (which only turns on 1.8, the most
 //       the browser supports).
 if (typeof version != 'undefined')
 {
   version(185);
 }
 
new file mode 100644
--- /dev/null
+++ b/js/src/vm/ArrayBufferObject-inl.h
@@ -0,0 +1,46 @@
+/* -*- 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_ArrayBufferObject_inl_h
+#define vm_ArrayBufferObject_inl_h
+
+/* Utilities and common inline code for ArrayBufferObject and SharedArrayBufferObject */
+
+#include "vm/ArrayBufferObject.h"
+
+#include "js/Value.h"
+
+#include "vm/SharedArrayObject.h"
+
+namespace js {
+
+inline uint32_t
+AnyArrayBufferByteLength(const ArrayBufferObjectMaybeShared *buf)
+{
+    if (buf->is<ArrayBufferObject>())
+	return buf->as<ArrayBufferObject>().byteLength();
+    return buf->as<SharedArrayBufferObject>().byteLength();
+}
+
+inline uint8_t *
+AnyArrayBufferDataPointer(const ArrayBufferObjectMaybeShared *buf)
+{
+    if (buf->is<ArrayBufferObject>())
+	return buf->as<ArrayBufferObject>().dataPointer();
+    return buf->as<SharedArrayBufferObject>().dataPointer();
+}
+
+inline ArrayBufferObjectMaybeShared &
+AsAnyArrayBuffer(HandleValue val)
+{
+    if (val.toObject().is<ArrayBufferObject>())
+	return val.toObject().as<ArrayBufferObject>();
+    return val.toObject().as<SharedArrayBufferObject>();
+}
+
+} // namespace js
+
+#endif // vm_ArrayBufferObject_inl_h
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -37,17 +37,16 @@
 #include "asmjs/AsmJSValidate.h"
 #include "gc/Barrier.h"
 #include "gc/Marking.h"
 #include "gc/Memory.h"
 #include "js/MemoryMetrics.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/NumericConversions.h"
-#include "vm/SharedArrayObject.h"
 #include "vm/WrapperObject.h"
 
 #include "jsatominlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 #include "vm/Shape-inl.h"
 
@@ -140,48 +139,42 @@ const JSFunctionSpec ArrayBufferObject::
 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>());
+    return v.isObject() && v.toObject().is<ArrayBufferObject>();
 }
 
 bool
 js::IsArrayBuffer(HandleObject obj)
 {
-    return obj->is<ArrayBufferObject>() || obj->is<SharedArrayBufferObject>();
+    return obj->is<ArrayBufferObject>();
 }
 
 bool
 js::IsArrayBuffer(JSObject *obj)
 {
-    return obj->is<ArrayBufferObject>() || obj->is<SharedArrayBufferObject>();
+    return obj->is<ArrayBufferObject>();
 }
 
 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());
@@ -302,19 +295,16 @@ ArrayBufferObject::setViewList(ArrayBuff
         ArrayBufferViewObject::writeBarrierPre(oldHead);
     setViewListNoBarrier(viewsHead);
     PostBarrierTypedArrayObject(this);
 }
 
 bool
 ArrayBufferObject::canNeuter(JSContext *cx)
 {
-    if (isSharedArrayBuffer())
-        return false;
-
     if (isAsmJSArrayBuffer()) {
         if (!ArrayBufferObject::canNeuterAsmJSArrayBuffer(cx, *this))
             return false;
     }
 
     return true;
 }
 
@@ -358,17 +348,16 @@ ArrayBufferObject::neuter(JSContext *cx,
         buffer->setInLiveList(false);
     }
 }
 
 void
 ArrayBufferObject::setNewOwnedData(FreeOp* fop, BufferContents newContents)
 {
     JS_ASSERT(!isAsmJSArrayBuffer());
-    JS_ASSERT(!isSharedArrayBuffer());
 
     if (ownsData()) {
         JS_ASSERT(newContents.data() != dataPointer());
         releaseData(fop);
     }
 
     setDataPointer(newContents, OwnsData);
 }
@@ -400,19 +389,16 @@ ArrayBufferObject::changeContents(JSCont
 }
 
 /* static */ bool
 ArrayBufferObject::prepareForAsmJSNoSignals(JSContext *cx, Handle<ArrayBufferObject*> buffer)
 {
     if (buffer->isAsmJSArrayBuffer())
         return true;
 
-    if (buffer->isSharedArrayBuffer())
-        return true;
-
     if (!ensureNonInline(cx, buffer))
         return false;
 
     buffer->setIsAsmJSArrayBuffer();
     return true;
 }
 
 void
@@ -429,20 +415,16 @@ ArrayBufferObject::prepareForAsmJS(JSCon
 {
     // If we can't use signal handlers, just do it like on other platforms.
     if (!usesSignalHandlers)
         return prepareForAsmJSNoSignals(cx, buffer);
 
     if (buffer->isAsmJSArrayBuffer())
         return true;
 
-    // SharedArrayBuffers are already created with AsmJS support in mind.
-    if (buffer->isSharedArrayBuffer())
-        return true;
-
     // Get the entire reserved region (with all pages inaccessible).
     void *data;
 # ifdef XP_WIN
     data = VirtualAlloc(nullptr, AsmJSMappedSize, MEM_RESERVE, PAGE_NOACCESS);
     if (!data)
         return false;
 # else
     data = MozTaggedAnonymousMmap(nullptr, AsmJSMappedSize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0, "asm-js-reserved");
@@ -523,17 +505,16 @@ ArrayBufferObject::releaseAsmJSArray(Fre
     // See comment above.
     releaseAsmJSArrayNoSignals(fop);
 }
 #endif
 
 bool
 ArrayBufferObject::canNeuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer)
 {
-    JS_ASSERT(!buffer.isSharedArrayBuffer());
     AsmJSActivation *act = cx->mainThread().asmJSActivationStack();
     for (; act; act = act->prevAsmJS()) {
         if (act->module().maybeHeapBufferObject() == &buffer)
             break;
     }
     if (!act)
         return true;
 
@@ -574,18 +555,16 @@ ArrayBufferObject::addView(ArrayBufferVi
     }
 
     setViewList(view);
 }
 
 uint8_t *
 ArrayBufferObject::dataPointer() const
 {
-    if (isSharedArrayBuffer())
-        return (uint8_t *)this->as<SharedArrayBufferObject>().dataPointer();
     return static_cast<uint8_t *>(getSlot(DATA_SLOT).toPrivate());
 }
 
 void
 ArrayBufferObject::releaseData(FreeOp *fop)
 {
     JS_ASSERT(ownsData());
 
@@ -596,17 +575,16 @@ ArrayBufferObject::releaseData(FreeOp *f
         releaseMappedArray();
     else
         fop->free_(dataPointer());
 }
 
 void
 ArrayBufferObject::setDataPointer(BufferContents contents, OwnsState ownsData)
 {
-    MOZ_ASSERT_IF(!is<SharedArrayBufferObject>(), contents.data());
     setSlot(DATA_SLOT, PrivateValue(contents.data()));
     setOwnsData(ownsData);
     setFlags((flags() & ~KIND_MASK) | contents.kind());
 }
 
 size_t
 ArrayBufferObject::byteLength() const
 {
@@ -749,17 +727,16 @@ ArrayBufferObject::createDataViewForThis
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsArrayBuffer, createDataViewForThisImpl>(cx, args);
 }
 
 /* static */ bool
 ArrayBufferObject::ensureNonInline(JSContext *cx, Handle<ArrayBufferObject*> buffer)
 {
     if (!buffer->ownsData()) {
-        MOZ_ASSERT(!buffer->isSharedArrayBuffer());
         BufferContents contents = AllocateArrayBufferContents(cx, buffer->byteLength());
         if (!contents)
             return false;
         memcpy(contents.data(), buffer->dataPointer(), buffer->byteLength());
         buffer->changeContents(cx, contents);
     }
 
     return true;
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -17,42 +17,84 @@ typedef struct JSProperty JSProperty;
 namespace js {
 
 class ArrayBufferViewObject;
 
 // The inheritance hierarchy for the various classes relating to typed arrays
 // is as follows.
 //
 // - JSObject
-//   - ArrayBufferObject
+//   - ArrayBufferObjectMaybeShared
+//     - ArrayBufferObject
 //     - SharedArrayBufferObject
 //   - ArrayBufferViewObject
 //     - DataViewObject
 //     - TypedArrayObject (declared in vm/TypedArrayObject.h)
 //       - TypedArrayObjectTemplate
 //         - Int8ArrayObject
 //         - Uint8ArrayObject
 //         - ...
 //     - TypedObject (declared in builtin/TypedObject.h)
+//   - SharedTypedArrayObject (declared in vm/SharedTypedArrayObject.h)
+//     - SharedTypedArrayObjectTemplate
+//       - SharedInt8ArrayObject
+//       - SharedUint8ArrayObject
+//       - ...
 //
 // Note that |TypedArrayObjectTemplate| is just an implementation
 // detail that makes implementing its various subclasses easier.
+// Note that |TypedArrayObjectTemplate| and |SharedTypedArrayObjectTemplate| are
+// just implementation details that make implementing their various subclasses easier.
+//
+// ArrayBufferObject and SharedArrayBufferObject are unrelated data types:
+// the racy memory of the latter cannot substitute for the non-racy memory of
+// the former; the non-racy memory of the former cannot be used with the atomics;
+// the former can be neutered and the latter not; and they have different
+// method suites.  Hence they have been separated completely.
+//
+// Most APIs will only accept ArrayBufferObject.  ArrayBufferObjectMaybeShared exists
+// as a join point to allow APIs that can take or use either, notably AsmJS.
+//
+// As ArrayBufferObject and SharedArrayBufferObject are separated, so are the
+// TypedArray hierarchies below the two.  However, the TypedArrays have the
+// same layout (see TypedArrayObject.h), so there is little code duplication.
 
 typedef Vector<ArrayBufferObject *, 0, SystemAllocPolicy> ArrayBufferVector;
 
+class ArrayBufferObjectMaybeShared;
+
+uint32_t AnyArrayBufferByteLength(const ArrayBufferObjectMaybeShared *buf);
+uint8_t *AnyArrayBufferDataPointer(const ArrayBufferObjectMaybeShared *buf);
+ArrayBufferObjectMaybeShared &AsAnyArrayBuffer(HandleValue val);
+
+class ArrayBufferObjectMaybeShared : public JSObject
+{
+  public:
+    uint32_t byteLength() {
+        return AnyArrayBufferByteLength(this);
+    }
+
+    uint8_t *dataPointer() {
+        return AnyArrayBufferDataPointer(this);
+    }
+};
+
 /*
  * ArrayBufferObject
  *
  * This class holds the underlying raw buffer that the various
  * ArrayBufferViewObject subclasses (DataViewObject and the TypedArrays)
  * access. It can be created explicitly and passed to an ArrayBufferViewObject
  * subclass, or can be created implicitly by constructing a TypedArrayObject
  * with a size.
+ *
+ * ArrayBufferObject (or really the underlying memory) /is not racy/: the
+ * memory is private to a single worker.
  */
-class ArrayBufferObject : public JSObject
+class ArrayBufferObject : public ArrayBufferObjectMaybeShared
 {
     static bool byteLengthGetterImpl(JSContext *cx, CallArgs args);
     static bool fun_slice_impl(JSContext *cx, CallArgs args);
 
   public:
     static const uint8_t DATA_SLOT = 0;
     static const uint8_t BYTE_LENGTH_SLOT = 1;
     static const uint8_t VIEW_LIST_SLOT = 2;
@@ -62,19 +104,18 @@ class ArrayBufferObject : public JSObjec
 
     static const size_t ARRAY_BUFFER_ALIGNMENT = 8;
 
   public:
 
     enum BufferKind {
         PLAIN_BUFFER        =   0, // malloced or inline data
         ASMJS_BUFFER        = 0x1,
-        SHARED_BUFFER       = 0x2,
-        MAPPED_BUFFER       = 0x4,
-        KIND_MASK           = ASMJS_BUFFER | SHARED_BUFFER | MAPPED_BUFFER
+        MAPPED_BUFFER       = 0x2,
+        KIND_MASK           = ASMJS_BUFFER | MAPPED_BUFFER
     };
 
   protected:
 
     enum ArrayBufferFlags {
         // The flags also store the BufferKind
         BUFFER_KIND_MASK    = BufferKind::KIND_MASK,
 
@@ -218,17 +259,16 @@ class ArrayBufferObject : public JSObjec
      * ArrayBuffer.prototype and neutered ArrayBuffers.
      */
     bool hasData() const {
         return getClass() == &class_;
     }
 
     BufferKind bufferKind() const { return BufferKind(flags() & BUFFER_KIND_MASK); }
     bool isAsmJSArrayBuffer() const { return flags() & ASMJS_BUFFER; }
-    bool isSharedArrayBuffer() const { return flags() & SHARED_BUFFER; }
     bool isMappedArrayBuffer() const { return flags() & MAPPED_BUFFER; }
     bool isNeutered() const { return flags() & NEUTERED_BUFFER; }
 
     static bool prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer,
                                 bool usesSignalHandlers);
     static bool prepareForAsmJSNoSignals(JSContext *cx, Handle<ArrayBufferObject*> buffer);
     static bool canNeuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer);
 
@@ -264,17 +304,16 @@ class ArrayBufferObject : public JSObjec
     }
 
     bool ownsData() const { return flags() & OWNS_DATA; }
     void setOwnsData(OwnsState owns) {
         setFlags(owns ? (flags() | OWNS_DATA) : (flags() & ~OWNS_DATA));
     }
 
     void setIsAsmJSArrayBuffer() { setFlags(flags() | ASMJS_BUFFER); }
-    void setIsSharedArrayBuffer() { setFlags(flags() | SHARED_BUFFER); }
     void setIsMappedArrayBuffer() { setFlags(flags() | MAPPED_BUFFER); }
     void setIsNeutered() { setFlags(flags() | NEUTERED_BUFFER); }
 
     void initialize(size_t byteLength, BufferContents contents, OwnsState ownsState) {
         setByteLength(byteLength);
         setFlags(0);
         setViewListNoBarrier(nullptr);
         setDataPointer(contents, ownsState);
@@ -338,28 +377,27 @@ PostBarrierTypedArrayObject(JSObject *ob
 #endif
 }
 
 inline void
 InitArrayBufferViewDataPointer(ArrayBufferViewObject *obj, ArrayBufferObject *buffer, size_t byteOffset)
 {
     /*
      * 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.
+     * private data rather than a slot to avoid the restriction that
+     * private Values that are pointers must have the low bits clear.
      */
     MOZ_ASSERT(buffer->dataPointer() != nullptr);
     obj->initPrivate(buffer->dataPointer() + byteOffset);
 
     PostBarrierTypedArrayObject(obj);
 }
 
 /*
- * Tests for either ArrayBufferObject or SharedArrayBufferObject.
- * For specific class testing, use e.g., obj->is<ArrayBufferObject>().
+ * Tests for ArrayBufferObject, like obj->is<ArrayBufferObject>().
  */
 bool IsArrayBuffer(HandleValue v);
 bool IsArrayBuffer(HandleObject obj);
 bool IsArrayBuffer(JSObject *obj);
 ArrayBufferObject &AsArrayBuffer(HandleObject obj);
 ArrayBufferObject &AsArrayBuffer(JSObject *obj);
 
 inline void
--- a/js/src/vm/ObjectImpl-inl.h
+++ b/js/src/vm/ObjectImpl-inl.h
@@ -31,19 +31,20 @@ ObjectImpl::isExtensible(ExclusiveContex
     *extensible = obj->nonProxyIsExtensible();
     return true;
 }
 
 inline bool
 ClassCanHaveFixedData(const Class *clasp)
 {
     // Normally, the number of fixed slots given an object is the maximum
-    // permitted for its size class. For array buffers and typed arrays we only
-    // use enough to cover the class reserved slots, so that the remaining
-    // space in the object's allocation is available for the buffer's data.
+    // permitted for its size class. For array buffers and non-shared typed
+    // arrays we only use enough to cover the class reserved slots, so that
+    // the remaining space in the object's allocation is available for the
+    // buffer's data.
     return clasp == &ArrayBufferObject::class_ || IsTypedArrayClass(clasp);
 }
 
 inline void *
 ObjectImpl::fixedData(size_t nslots) const
 {
     JS_ASSERT(ClassCanHaveFixedData(getClass()));
     JS_ASSERT(nslots == numFixedSlots() + (hasPrivate() ? 1 : 0));
--- a/js/src/vm/ObjectImpl.cpp
+++ b/js/src/vm/ObjectImpl.cpp
@@ -4,16 +4,17 @@
  * 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/ObjectImpl-inl.h"
 
 #include "gc/Marking.h"
 #include "js/Value.h"
 #include "vm/Debugger.h"
+#include "vm/TypedArrayCommon.h"
 
 #include "jsobjinlines.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
 
 using JS::GenericNaN;
 
@@ -73,17 +74,17 @@ HeapSlot *const js::emptyObjectElements 
     reinterpret_cast<HeapSlot *>(uintptr_t(&emptyElementsHeader) + sizeof(ObjectElements));
 
 #ifdef DEBUG
 
 bool
 ObjectImpl::canHaveNonEmptyElements()
 {
     JSObject *obj = static_cast<JSObject *>(this);
-    return isNative() && !obj->is<TypedArrayObject>();
+    return isNative() && !IsAnyTypedArray(obj);
 }
 
 #endif // DEBUG
 
 /* static */ bool
 ObjectElements::ConvertElementsToDoubles(JSContext *cx, uintptr_t elementsPtr)
 {
     /*
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -19,16 +19,17 @@
 #include "builtin/SelfHostingDefines.h"
 #include "builtin/TypedObject.h"
 #include "builtin/WeakSetObject.h"
 #include "gc/Marking.h"
 #include "vm/Compression.h"
 #include "vm/ForkJoin.h"
 #include "vm/Interpreter.h"
 #include "vm/String.h"
+#include "vm/TypedArrayCommon.h"
 
 #include "jsfuninlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/BooleanObject-inl.h"
 #include "vm/NumberObject-inl.h"
 #include "vm/StringObject-inl.h"
 
@@ -464,19 +465,19 @@ js::intrinsic_UnsafePutElements(JSContex
 
         JS_ASSERT(args[arri].isObject());
         JS_ASSERT(args[arri].toObject().isNative() || IsTypedObjectArray(args[arri].toObject()));
         JS_ASSERT(args[idxi].isInt32());
 
         RootedObject arrobj(cx, &args[arri].toObject());
         uint32_t idx = args[idxi].toInt32();
 
-        if (arrobj->is<TypedArrayObject>() || arrobj->is<TypedObject>()) {
-            JS_ASSERT(!arrobj->is<TypedArrayObject>() || idx < arrobj->as<TypedArrayObject>().length());
-            JS_ASSERT(!arrobj->is<TypedObject>() || idx < uint32_t(arrobj->as<TypedObject>().length()));
+        if (IsAnyTypedArray(arrobj.get()) || arrobj->is<TypedObject>()) {
+            JS_ASSERT_IF(IsAnyTypedArray(arrobj.get()), idx < AnyTypedArrayLength(arrobj.get()));
+            JS_ASSERT_IF(arrobj->is<TypedObject>(), idx < uint32_t(arrobj->as<TypedObject>().length()));
             RootedValue tmp(cx, args[elemi]);
             // XXX: Always non-strict.
             if (!JSObject::setElement(cx, arrobj, arrobj, idx, &tmp, false))
                 return false;
         } else {
             JS_ASSERT(idx < arrobj->getDenseInitializedLength());
             arrobj->setDenseElementWithType(cx, idx, args[elemi]);
         }
--- a/js/src/vm/Shape-inl.h
+++ b/js/src/vm/Shape-inl.h
@@ -10,17 +10,17 @@
 #include "vm/Shape.h"
 
 #include "mozilla/TypeTraits.h"
 
 #include "jsobj.h"
 
 #include "vm/Interpreter.h"
 #include "vm/ScopeObject.h"
-#include "vm/TypedArrayObject.h"
+#include "vm/TypedArrayCommon.h"
 
 #include "jsatominlines.h"
 #include "jscntxtinlines.h"
 #include "jsgcinlines.h"
 
 namespace js {
 
 inline
@@ -207,17 +207,17 @@ AutoRooterGetterSetter::AutoRooterGetter
 }
 
 static inline uint8_t
 GetShapeAttributes(JSObject *obj, Shape *shape)
 {
     JS_ASSERT(obj->isNative());
 
     if (IsImplicitDenseOrTypedArrayElement(shape)) {
-        if (obj->is<TypedArrayObject>())
+        if (IsAnyTypedArray(obj))
             return JSPROP_ENUMERATE | JSPROP_PERMANENT;
         return JSPROP_ENUMERATE;
     }
 
     return shape->attributes();
 }
 
 #ifdef JSGC_COMPACTING
--- a/js/src/vm/SharedArrayObject.cpp
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -6,37 +6,32 @@
 
 #include "vm/SharedArrayObject.h"
 
 #include "jsprf.h"
 #include "jsobjinlines.h"
 
 #ifdef XP_WIN
 # include "jswin.h"
-#else
+#endif
+#include "jswrapper.h"
+#ifndef XP_WIN
 # include <sys/mman.h>
 #endif
-
 #ifdef MOZ_VALGRIND
 # include <valgrind/memcheck.h>
 #endif
 
 #include "mozilla/Atomics.h"
 
 #include "asmjs/AsmJSValidate.h"
+#include "vm/TypedArrayCommon.h"
 
 using namespace js;
 
-using mozilla::IsNaN;
-using mozilla::PodCopy;
-
-/*
- * 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
@@ -79,17 +74,21 @@ MarkValidRegion(void *addr, size_t len)
 // heap but keep the heap page-aligned, allocate an extra page before the heap.
 static const uint64_t SharedArrayMappedSize = AsmJSMappedSize + AsmJSPageSize;
 static_assert(sizeof(SharedArrayRawBuffer) < AsmJSPageSize, "Page size not big enough");
 #endif
 
 SharedArrayRawBuffer *
 SharedArrayRawBuffer::New(uint32_t length)
 {
-    // Enforced by SharedArrayBufferObject constructor.
+    // The value (uint32_t)-1 is used as a signal in various places,
+    // so guard against it on principle.
+    JS_ASSERT(length != (uint32_t)-1);
+
+    // Enforced by SharedArrayBufferObject::New(cx, length).
     JS_ASSERT(IsValidAsmJSHeapLength(length));
 
 #ifdef JS_CODEGEN_X64
     // Get the entire reserved region (with all pages inaccessible)
     void *p = MapMemory(SharedArrayMappedSize, false);
     if (!p)
         return nullptr;
 
@@ -143,24 +142,25 @@ SharedArrayRawBuffer::dropReference()
         VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(p, SharedArrayMappedSize);
 #       endif
 #else
         UnmapMemory(p, this->length + AsmJSPageSize);
 #endif
     }
 }
 
-/*
- * SharedArrayBufferObject
- */
-bool
-js::IsSharedArrayBuffer(HandleValue v)
-{
-    return v.isObject() && v.toObject().is<SharedArrayBufferObject>();
-}
+const JSFunctionSpec SharedArrayBufferObject::jsfuncs[] = {
+    /* Nothing yet */
+    JS_FS_END
+};
+
+const JSFunctionSpec SharedArrayBufferObject::jsstaticfuncs[] = {
+    JS_FN("isView", SharedArrayBufferObject::fun_isView, 1, 0),
+    JS_FS_END
+};
 
 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;
 }
@@ -168,36 +168,54 @@ SharedArrayBufferObject::byteLengthGette
 bool
 SharedArrayBufferObject::byteLengthGetter(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsSharedArrayBuffer, byteLengthGetterImpl>(cx, args);
 }
 
 bool
+SharedArrayBufferObject::fun_isView(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    args.rval().setBoolean(args.get(0).isObject() &&
+                           JS_IsSharedTypedArrayObject(&args.get(0).toObject()));
+    return true;
+}
+
+bool
 SharedArrayBufferObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
 {
-    int32_t length = 0;
     CallArgs args = CallArgsFromVp(argc, vp);
-    if (args.length() > 0 && !ToInt32(cx, args[0], &length))
-        return false;
 
-    if (length < 0) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
+    if (!args.isConstructing()) {
+        if (args.hasDefined(0) && IsObjectWithClass(args[0], ESClass_SharedArrayBuffer, cx)) {
+            args.rval().set(args[0]);
+            return true;
+        }
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SHARED_ARRAY_BAD_OBJECT);
         return false;
     }
 
-    JSObject *bufobj = New(cx, uint32_t(length));
+    uint32_t length;
+    bool overflow;
+    if (!ToLengthClamped(cx, args[0], &length, &overflow)) {
+        if (overflow)
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SHARED_ARRAY_BAD_LENGTH);
+        return false;
+    }
+
+    JSObject *bufobj = New(cx, length);
     if (!bufobj)
         return false;
     args.rval().setObject(*bufobj);
     return true;
 }
 
-JSObject *
+SharedArrayBufferObject *
 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;
@@ -205,83 +223,70 @@ SharedArrayBufferObject::New(JSContext *
 
     SharedArrayRawBuffer *buffer = SharedArrayRawBuffer::New(length);
     if (!buffer)
         return nullptr;
 
     return New(cx, buffer);
 }
 
-JSObject *
+SharedArrayBufferObject *
 SharedArrayBufferObject::New(JSContext *cx, SharedArrayRawBuffer *buffer)
 {
     Rooted<SharedArrayBufferObject*> obj(cx, NewBuiltinClassInstance<SharedArrayBufferObject>(cx));
     if (!obj)
         return nullptr;
 
     JS_ASSERT(obj->getClass() == &class_);
 
-    obj->initialize(buffer->byteLength(), BufferContents::createUnowned(nullptr), DoesntOwnData);
-
     obj->acceptRawBuffer(buffer);
-    obj->setIsSharedArrayBuffer();
 
     return obj;
 }
 
 void
 SharedArrayBufferObject::acceptRawBuffer(SharedArrayRawBuffer *buffer)
 {
-    setReservedSlot(SharedArrayBufferObject::RAWBUF_SLOT, PrivateValue(buffer));
+    setReservedSlot(RAWBUF_SLOT, PrivateValue(buffer));
 }
 
 void
 SharedArrayBufferObject::dropRawBuffer()
 {
-    setReservedSlot(SharedArrayBufferObject::RAWBUF_SLOT, UndefinedValue());
+    setReservedSlot(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();
+    Value v = getReservedSlot(RAWBUF_SLOT);
+    JS_ASSERT(!v.isUndefined());
+    return reinterpret_cast<SharedArrayRawBuffer *>(v.toPrivate());
 }
 
 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);
+    Value v = buf.getReservedSlot(RAWBUF_SLOT);
     if (!v.isUndefined()) {
         buf.rawBufferObject()->dropReference();
         buf.dropRawBuffer();
     }
 }
 
-/*
- * SharedArrayBufferObject
- */
+/* static */ void
+SharedArrayBufferObject::addSizeOfExcludingThis(JSObject *obj, mozilla::MallocSizeOf mallocSizeOf,
+                                                JS::ClassInfo *info)
+{
+    info->objectsNonHeapElementsMapped += obj->as<SharedArrayBufferObject>().byteLength();
+}
 
 const Class SharedArrayBufferObject::protoClass = {
     "SharedArrayBufferPrototype",
     JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer),
     JS_PropertyStub,         /* addProperty */
     JS_DeletePropertyStub,   /* delProperty */
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
@@ -301,17 +306,17 @@ const Class SharedArrayBufferObject::cla
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     SharedArrayBufferObject::Finalize,
     nullptr,        /* call        */
     nullptr,        /* hasInstance */
     nullptr,        /* construct   */
-    ArrayBufferObject::obj_trace,
+    nullptr,        /* trace */
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT
 };
 
 JSObject *
 js_InitSharedArrayBufferClass(JSContext *cx, HandleObject obj)
 {
     JS_ASSERT(obj->isNative());
@@ -320,29 +325,59 @@ js_InitSharedArrayBufferClass(JSContext 
     if (!proto)
         return nullptr;
 
     RootedFunction ctor(cx, global->createConstructor(cx, SharedArrayBufferObject::class_constructor,
                                                       cx->names().SharedArrayBuffer, 1));
     if (!ctor)
         return nullptr;
 
+    if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_SharedArrayBuffer, ctor, proto))
+        return nullptr;
+
     if (!LinkConstructorAndPrototype(cx, ctor, proto))
         return nullptr;
 
     RootedId byteLengthId(cx, NameToId(cx->names().byteLength));
     unsigned attrs = 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,
+    if (!DefineNativeProperty(cx, proto, byteLengthId, UndefinedHandleValue,
                               JS_DATA_TO_FUNC_PTR(PropertyOp, getter), nullptr, attrs))
-    {
         return nullptr;
-    }
+
+    if (!JS_DefineFunctions(cx, ctor, SharedArrayBufferObject::jsstaticfuncs))
+        return nullptr;
 
-    if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_SharedArrayBuffer, ctor, proto))
+    if (!JS_DefineFunctions(cx, proto, SharedArrayBufferObject::jsfuncs))
         return nullptr;
+
     return proto;
 }
+
+bool
+js::IsSharedArrayBuffer(HandleValue v)
+{
+    return v.isObject() && v.toObject().is<SharedArrayBufferObject>();
+}
+
+bool
+js::IsSharedArrayBuffer(HandleObject o)
+{
+    return o->is<SharedArrayBufferObject>();
+}
+
+SharedArrayBufferObject &
+js::AsSharedArrayBuffer(HandleObject obj)
+{
+    JS_ASSERT(IsSharedArrayBuffer(obj));
+    return obj->as<SharedArrayBufferObject>();
+}
+
+JS_FRIEND_API(bool)
+JS_IsSharedArrayBufferObject(JSObject *obj)
+{
+    obj = CheckedUnwrap(obj);
+    return obj ? obj->is<ArrayBufferObject>() : false;
+}
--- a/js/src/vm/SharedArrayObject.h
+++ b/js/src/vm/SharedArrayObject.h
@@ -25,16 +25,24 @@ namespace js {
  *
  * 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 |
+ *
+ * Observe that if we want to map the data array on a specific address, such
+ * as absolute zero (bug 1056027), then the SharedArrayRawBuffer cannot be
+ * prefixed to the data array, it has to be a separate object, also in
+ * shared memory.  (That would get rid of ~4KB of waste, as well.)  Very little
+ * else would have to change throughout the engine, the SARB would point to
+ * the data array using a constant pointer, instead of computing its
+ * address.
  */
 class SharedArrayRawBuffer
 {
   private:
     mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> refcount;
     uint32_t length;
 
   protected:
@@ -57,50 +65,89 @@ class SharedArrayRawBuffer
 
     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.
+ * When transferred to a WebWorker, the buffer is not neutered on the
+ * parent side, and both child and parent reference the same buffer.
+ *
+ * The underlying memory is memory-mapped and reference counted
+ * (across workers and/or processes).  The SharedArrayBuffer object
+ * has a finalizer that decrements the refcount, the last one to leave
+ * (globally) unmaps the memory.  The sender ups the refcount before
+ * transmitting the memory to another worker.
+ *
+ * SharedArrayBufferObject (or really the underlying memory) /is
+ * racy/: more than one worker can access the memory at the same time.
+ *
+ * A SharedTypedArrayObject (a view) references a SharedArrayBuffer
+ * and keeps it alive.  The SharedArrayBuffer does /not/ reference its
+ * views, nor do the views reference each other in any way.
  */
-class SharedArrayBufferObject : public ArrayBufferObject
+class SharedArrayBufferObject : public ArrayBufferObjectMaybeShared
 {
     static bool byteLengthGetterImpl(JSContext *cx, CallArgs args);
 
   public:
+    // RAWBUF_SLOT holds a pointer (as "private" data) to the
+    // SharedArrayRawBuffer object, which is manually managed storage.
+    static const uint8_t RAWBUF_SLOT = 0;
+
+    static const uint8_t RESERVED_SLOTS = 1;
+
     static const Class class_;
     static const Class protoClass;
+    static const JSFunctionSpec jsfuncs[];
+    static const JSFunctionSpec jsstaticfuncs[];
 
-    // Slot used for storing a pointer to the SharedArrayRawBuffer.
-    static const uint8_t RAWBUF_SLOT = ArrayBufferObject::RESERVED_SLOTS;
+    static bool byteLengthGetter(JSContext *cx, unsigned argc, Value *vp);
 
-    static const uint8_t RESERVED_SLOTS = ArrayBufferObject::RESERVED_SLOTS + 1;
+    static bool fun_isView(JSContext *cx, unsigned argc, Value *vp);
 
     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);
+    static SharedArrayBufferObject *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 SharedArrayBufferObject *New(JSContext *cx, SharedArrayRawBuffer *buffer);
 
     static void Finalize(FreeOp *fop, JSObject *obj);
 
-    void acceptRawBuffer(SharedArrayRawBuffer *buffer);
-    void dropRawBuffer();
+    static void addSizeOfExcludingThis(JSObject *obj, mozilla::MallocSizeOf mallocSizeOf,
+                                       JS::ClassInfo *info);
 
     SharedArrayRawBuffer *rawBufferObject() const;
-    uint8_t *dataPointer() const;
-    uint32_t byteLength() const;
+
+    // Invariant: This method does not cause GC and can be called
+    // without anchoring the object it is called on.
+    void *globalID() const {
+        // The buffer address is good enough as an ID provided the memory is not shared
+        // between processes or, if it is, it is mapped to the same address in every
+        // process.  (At the moment, shared memory cannot be shared between processes.)
+        return dataPointer();
+    }
+
+    uint32_t byteLength() const {
+        return rawBufferObject()->byteLength();
+    }
+
+    uint8_t *dataPointer() const {
+        return rawBufferObject()->dataPointer();
+    }
+
+private:
+    void acceptRawBuffer(SharedArrayRawBuffer *buffer);
+    void dropRawBuffer();
 };
 
-bool
-IsSharedArrayBuffer(HandleValue v);
+bool IsSharedArrayBuffer(HandleValue v);
+bool IsSharedArrayBuffer(HandleObject o);
+
+SharedArrayBufferObject &AsSharedArrayBuffer(HandleObject o);
 
 } // namespace js
 
 #endif // vm_SharedArrayObject_h
new file mode 100644
--- /dev/null
+++ b/js/src/vm/SharedTypedArrayObject.cpp
@@ -0,0 +1,1027 @@
+/* -*- 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/SharedTypedArrayObject.h"
+
+#include "mozilla/Alignment.h"
+#include "mozilla/PodOperations.h"
+
+#include <string.h>
+#ifndef XP_WIN
+# include <sys/mman.h>
+#endif
+
+#include "jsapi.h"
+#include "jsarray.h"
+#include "jscntxt.h"
+#include "jscpucfg.h"
+#include "jsnum.h"
+#include "jsobj.h"
+#include "jstypes.h"
+#include "jsutil.h"
+#ifdef XP_WIN
+# include "jswin.h"
+#endif
+#include "jswrapper.h"
+
+#include "asmjs/AsmJSModule.h"
+#include "asmjs/AsmJSValidate.h"
+#include "gc/Barrier.h"
+#include "gc/Marking.h"
+#include "vm/ArrayBufferObject.h"
+#include "vm/GlobalObject.h"
+#include "vm/Interpreter.h"
+#include "vm/NumericConversions.h"
+#include "vm/SharedArrayObject.h"
+#include "vm/TypedArrayCommon.h"
+#include "vm/WrapperObject.h"
+
+#include "jsatominlines.h"
+#include "jsinferinlines.h"
+#include "jsobjinlines.h"
+
+#include "vm/Shape-inl.h"
+
+using namespace js;
+using namespace js::gc;
+using namespace js::types;
+
+using mozilla::IsNaN;
+using mozilla::NegativeInfinity;
+using mozilla::PodCopy;
+using mozilla::PositiveInfinity;
+using JS::CanonicalizeNaN;
+using JS::GenericNaN;
+
+TypedArrayLayout SharedTypedArrayObject::layout_(true, // shared
+                                                 false, // neuterable
+                                                 &SharedTypedArrayObject::classes[0],
+                                                 &SharedTypedArrayObject::classes[Scalar::TypeMax]);
+
+namespace {
+
+inline void
+InitSharedArrayBufferViewDataPointer(SharedTypedArrayObject *obj, SharedArrayBufferObject *buffer, size_t byteOffset)
+{
+    /*
+     * N.B. The base of the array's data is stored in the object's
+     * private data rather than a slot to avoid the restriction that
+     * private Values that are pointers must have the low bits clear.
+     */
+    MOZ_ASSERT(buffer->dataPointer() != nullptr);
+    obj->initPrivate(buffer->dataPointer() + byteOffset);
+    PostBarrierTypedArrayObject(obj);
+}
+
+// See note in TypedArrayObject.cpp about how we can probably merge
+// the below type with the one in that file, once TypedArrayObject is
+// less dissimilar from SharedTypedArrayObject (ie, when it is closer
+// to ES6).
+
+template<typename NativeType>
+class SharedTypedArrayObjectTemplate : public SharedTypedArrayObject
+{
+    // A value that signifies that we should use the buffer up to the end.
+    static const uint32_t LENGTH_NOT_PROVIDED = (uint32_t)-1;
+
+    // This is the max implementation value of 'length': 2^32-2.
+    // The value 2^32-1 is reserved for LENGTH_NOT_PROVIDED.
+    static const uint32_t MAX_LENGTH = 0xFFFFFFFEU;
+
+    // This is the max value of 'byteOffset': one below the length.
+    static const uint32_t MAX_BYTEOFFSET = MAX_LENGTH - 1;
+
+  public:
+    typedef NativeType ThisType;
+    typedef SharedTypedArrayObjectTemplate<NativeType> ThisTypedArrayObject;
+    static Scalar::Type ArrayTypeID() { return TypeIDOfType<NativeType>(); }
+    static bool ArrayTypeIsUnsigned() { return TypeIsUnsigned<NativeType>(); }
+    static bool ArrayTypeIsFloatingPoint() { return TypeIsFloatingPoint<NativeType>(); }
+
+    static const size_t BYTES_PER_ELEMENT = sizeof(ThisType);
+
+    class SharedTypedArrayObjectAdapter
+    {
+      public:
+        typedef SharedTypedArrayObject TypedArrayObjectType;
+        typedef SharedArrayBufferObject ArrayBufferObjectType;
+        typedef NativeType ElementType;
+        typedef SharedTypedArrayObjectTemplate<NativeType> ThisTypedArrayObjectType;
+
+        static bool ensureHasBuffer(JSContext *cx, Handle<SharedTypedArrayObject *> tarray) {
+            return true;
+        }
+
+        static bool sameBuffer(SharedArrayBufferObject *a, SharedArrayBufferObject *b) {
+            return a->globalID() == b->globalID();
+        }
+    };
+
+    static inline const Class *protoClass()
+    {
+        return &SharedTypedArrayObject::protoClasses[ArrayTypeID()];
+    }
+
+    static JSObject *CreatePrototype(JSContext *cx, JSProtoKey key)
+    {
+        return cx->global()->createBlankPrototype(cx, protoClass());
+    }
+
+    static bool FinishClassInit(JSContext *cx, HandleObject ctor, HandleObject proto);
+
+    static inline const Class *instanceClass()
+    {
+        return &SharedTypedArrayObject::classes[ArrayTypeID()];
+    }
+
+    static bool is(HandleValue v) {
+        return v.isObject() && v.toObject().hasClass(instanceClass());
+    }
+
+    static SharedTypedArrayObject *
+    makeProtoInstance(JSContext *cx, HandleObject proto, AllocKind allocKind)
+    {
+        JS_ASSERT(proto);
+
+        RootedObject obj(cx, NewBuiltinClassInstance(cx, instanceClass(), allocKind));
+        if (!obj)
+            return nullptr;
+
+        types::TypeObject *type = cx->getNewType(obj->getClass(), TaggedProto(proto.get()));
+        if (!type)
+            return nullptr;
+        obj->setType(type);
+
+        return &obj->as<SharedTypedArrayObject>();
+    }
+
+    static SharedTypedArrayObject *
+    makeTypedInstance(JSContext *cx, uint32_t len, AllocKind allocKind)
+    {
+        // Multiplication is safe due to preconditions for makeInstance().
+        if (len * sizeof(NativeType) >= SharedTypedArrayObject::SINGLETON_TYPE_BYTE_LENGTH) {
+            return &NewBuiltinClassInstance(cx, instanceClass(), allocKind,
+                                            SingletonObject)->as<SharedTypedArrayObject>();
+        }
+
+        jsbytecode *pc;
+        RootedScript script(cx, cx->currentScript(&pc));
+        NewObjectKind newKind = script
+                                ? UseNewTypeForInitializer(script, pc, instanceClass())
+                                : GenericObject;
+        RootedObject obj(cx, NewBuiltinClassInstance(cx, instanceClass(), allocKind, newKind));
+        if (!obj)
+            return nullptr;
+
+        if (script) {
+            if (!types::SetInitializerObjectType(cx, script, pc, obj, newKind))
+                return nullptr;
+        }
+
+        return &obj->as<SharedTypedArrayObject>();
+    }
+
+    static JSObject *
+    makeInstance(JSContext *cx, Handle<SharedArrayBufferObject *> buffer, uint32_t byteOffset, uint32_t len,
+                 HandleObject proto)
+    {
+        JS_ASSERT(buffer);
+        JS_ASSERT(len <= MAX_LENGTH / sizeof(NativeType));
+        JS_ASSERT(byteOffset <= MAX_BYTEOFFSET);
+
+        gc::AllocKind allocKind = GetGCObjectKind(instanceClass());
+
+        Rooted<SharedTypedArrayObject*> obj(cx);
+        if (proto)
+            obj = makeProtoInstance(cx, proto, allocKind);
+        else
+            obj = makeTypedInstance(cx, len, allocKind);
+        if (!obj)
+            return nullptr;
+
+        obj->setSlot(TYPE_SLOT, Int32Value(ArrayTypeID()));
+        obj->setSlot(BUFFER_SLOT, ObjectOrNullValue(buffer));
+
+	InitSharedArrayBufferViewDataPointer(obj, buffer, byteOffset);
+
+        obj->setSlot(LENGTH_SLOT, Int32Value(len));
+        obj->setSlot(BYTEOFFSET_SLOT, Int32Value(byteOffset));
+
+#ifdef DEBUG
+        if (buffer) {
+            uint32_t arrayByteLength = obj->byteLength();
+            uint32_t arrayByteOffset = obj->byteOffset();
+            uint32_t bufferByteLength = buffer->byteLength();
+            JS_ASSERT(bufferByteLength - arrayByteOffset >= arrayByteLength);
+            JS_ASSERT(arrayByteOffset <= bufferByteLength);
+        }
+
+        // Verify that the private slot is at the expected place
+        JS_ASSERT(obj->numFixedSlots() == DATA_SLOT);
+#endif
+
+        return obj;
+    }
+
+    static JSObject *
+    makeInstance(JSContext *cx, Handle<SharedArrayBufferObject *> bufobj, uint32_t byteOffset, uint32_t len)
+    {
+        RootedObject nullproto(cx, nullptr);
+        return makeInstance(cx, bufobj, byteOffset, len, nullproto);
+    }
+
+    /*
+     * Shared{Type}Array(object)
+     *
+     * new Shared{Type}Array(length)
+     * new Shared{Type}Array(SharedArrayBuffer, [optional] byteOffset, [optional] length)
+     */
+    static bool
+    class_constructor(JSContext *cx, unsigned argc, Value *vp)
+    {
+        CallArgs args = CallArgsFromVp(argc, vp);
+
+        if (!args.isConstructing()) {
+            if (args.hasDefined(0) &&
+                args[0].isObject() &&
+                args[0].toObject().is<SharedTypedArrayObject>() &&
+                AnyTypedArrayType(&args[0].toObject()) == ArrayTypeID())
+            {
+                args.rval().set(args[0]);
+                return true;
+            }
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SHARED_TYPED_ARRAY_BAD_LENGTH);
+            return false;
+        }
+
+        JSObject *obj = create(cx, args);
+        if (!obj)
+            return false;
+        args.rval().setObject(*obj);
+        return true;
+    }
+
+    static JSObject *
+    create(JSContext *cx, const CallArgs& args)
+    {
+        if (args.length() == 0)
+            return fromLength(cx, 0);
+
+        // Case 1: (length)
+        if (!args[0].isObject()) {
+            // If not an object then it must be a length.
+            uint32_t length;
+            bool overflow;
+            if (!ToLengthClamped(cx, args[0], &length, &overflow)) {
+                if (overflow)
+                    JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
+                return nullptr;
+            }
+            return fromLength(cx, length);
+        }
+
+        // Case 2: (SharedArrayBuffer, [byteOffset, [length]])
+        RootedObject dataObj(cx, &args.get(0).toObject());
+
+        if (!UncheckedUnwrap(dataObj)->is<SharedArrayBufferObject>()) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SHARED_TYPED_ARRAY_BAD_OBJECT);
+            return nullptr;
+	}
+
+        uint32_t byteOffset = 0;
+        uint32_t length = LENGTH_NOT_PROVIDED;
+        if (args.length() > 1) {
+            double numByteOffset;
+            if (!ToInteger(cx, args[1], &numByteOffset))
+                return nullptr;
+
+            if (numByteOffset < 0 || numByteOffset > MAX_BYTEOFFSET) {
+                JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
+                                     JSMSG_SHARED_TYPED_ARRAY_ARG_RANGE, "'byteOffset'");
+                return nullptr;
+            }
+            byteOffset = (uint32_t)numByteOffset;
+
+            if (args.length() > 2) {
+                bool overflow;
+                if (!ToLengthClamped(cx, args[2], &length, &overflow)) {
+                    if (overflow)
+                        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
+                                             JSMSG_SHARED_TYPED_ARRAY_ARG_RANGE, "'length'");
+                    return nullptr;
+                }
+            }
+        }
+
+        return fromBuffer(cx, dataObj, byteOffset, length);
+    }
+
+    static bool
+    createArrayBuffer(JSContext *cx, uint32_t nelements, MutableHandle<SharedArrayBufferObject *> buffer)
+    {
+        if (nelements > MAX_LENGTH / sizeof(NativeType)) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
+                                 JSMSG_NEED_DIET, "size and count");
+            return false;
+        }
+        buffer.set(SharedArrayBufferObject::New(cx, nelements * sizeof(NativeType)));
+        return !!buffer;
+    }
+
+    static bool IsThisClass(HandleValue v) {
+        return v.isObject() && v.toObject().hasClass(instanceClass());
+    }
+
+    template<Value ValueGetter(SharedTypedArrayObject *tarr)>
+    static bool
+    GetterImpl(JSContext *cx, CallArgs args)
+    {
+        JS_ASSERT(IsThisClass(args.thisv()));
+        args.rval().set(ValueGetter(&args.thisv().toObject().as<SharedTypedArrayObject>()));
+        return true;
+    }
+
+    // ValueGetter is a function that takes an unwrapped typed array object and
+    // returns a Value. Given such a function, Getter<> is a native that
+    // retrieves a given Value, probably from a slot on the object.
+    template<Value ValueGetter(SharedTypedArrayObject *tarr)>
+    static bool
+    Getter(JSContext *cx, unsigned argc, Value *vp)
+    {
+        CallArgs args = CallArgsFromVp(argc, vp);
+        return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
+                                    ThisTypedArrayObject::GetterImpl<ValueGetter> >(cx, args);
+    }
+
+    static bool
+    BufferGetterImpl(JSContext *cx, CallArgs args)
+    {
+        JS_ASSERT(IsThisClass(args.thisv()));
+        Rooted<SharedTypedArrayObject *> tarray(cx, &args.thisv().toObject().as<SharedTypedArrayObject>());
+        args.rval().set(bufferValue(tarray));
+        return true;
+    }
+
+    static bool
+    BufferGetter(JSContext *cx, unsigned argc, Value *vp)
+    {
+        CallArgs args = CallArgsFromVp(argc, vp);
+        return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
+                                    ThisTypedArrayObject::BufferGetterImpl>(cx, args);
+    }
+
+    // Define an accessor for a read-only property that invokes a native getter
+    static bool
+    DefineGetter(JSContext *cx, HandleObject proto, PropertyName *name, Native native)
+    {
+        RootedId id(cx, NameToId(name));
+        unsigned attrs = JSPROP_SHARED | JSPROP_GETTER;
+
+        Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal());
+        JSObject *getter = NewFunction(cx, NullPtr(), native, 0,
+                                       JSFunction::NATIVE_FUN, global, NullPtr());
+        if (!getter)
+            return false;
+
+        return DefineNativeProperty(cx, proto, id, UndefinedHandleValue,
+                                    JS_DATA_TO_FUNC_PTR(PropertyOp, getter), nullptr,
+                                    attrs);
+    }
+
+    static const NativeType
+    getIndex(JSObject *obj, uint32_t index)
+    {
+	SharedTypedArrayObject &tarray = obj->as<SharedTypedArrayObject>();
+        MOZ_ASSERT(index < tarray.length());
+        return static_cast<const NativeType*>(tarray.viewData())[index];
+    }
+
+    static void
+    setIndexValue(SharedTypedArrayObject &tarray, uint32_t index, double d)
+    {
+        // If the array is an integer array, we only handle up to
+        // 32-bit ints from this point on.  if we want to handle
+        // 64-bit ints, we'll need some changes.
+
+        // Assign based on characteristics of the destination type
+        if (ArrayTypeIsFloatingPoint()) {
+            setIndex(tarray, index, NativeType(d));
+        } else if (ArrayTypeIsUnsigned()) {
+            JS_ASSERT(sizeof(NativeType) <= 4);
+            uint32_t n = ToUint32(d);
+            setIndex(tarray, index, NativeType(n));
+        } else if (ArrayTypeID() == Scalar::Uint8Clamped) {
+            // The uint8_clamped type has a special rounding converter
+            // for doubles.
+            setIndex(tarray, index, NativeType(d));
+        } else {
+            JS_ASSERT(sizeof(NativeType) <= 4);
+            int32_t n = ToInt32(d);
+            setIndex(tarray, index, NativeType(n));
+        }
+    }
+
+    static void
+    setIndex(SharedTypedArrayObject &tarray, uint32_t index, NativeType val)
+    {
+        MOZ_ASSERT(index < tarray.length());
+        static_cast<NativeType*>(tarray.viewData())[index] = val;
+    }
+
+    static Value getIndexValue(JSObject *tarray, uint32_t index);
+
+    static JSObject *
+    fromBufferWithProto(JSContext *cx, HandleObject bufobj, uint32_t byteOffset, uint32_t length,
+                        HandleObject proto)
+    {
+        if (!ObjectClassIs(bufobj, ESClass_SharedArrayBuffer, cx)) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SHARED_TYPED_ARRAY_BAD_OBJECT);
+            return nullptr; // must be SharedArrayBuffer
+        }
+
+        JS_ASSERT(IsSharedArrayBuffer(bufobj) || bufobj->is<ProxyObject>());
+
+        if (bufobj->is<ProxyObject>()) {
+	    // Complicated, see TypedArrayObject.cpp for code.  For now, punt.
+	    JS_ReportError(cx, "Permission denied to access object");
+	    return nullptr;
+	}
+
+        Rooted<SharedArrayBufferObject *> buffer(cx, &AsSharedArrayBuffer(bufobj));
+
+        if (byteOffset > buffer->byteLength() || byteOffset % sizeof(NativeType) != 0) {
+            // Invalid byteOffset.
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SHARED_TYPED_ARRAY_BAD_ARGS);
+            return nullptr;
+        }
+
+        JS_ASSERT(byteOffset <= buffer->byteLength());
+        uint32_t bytesAvailable = buffer->byteLength() - byteOffset;
+
+        if (length == LENGTH_NOT_PROVIDED) {
+            if (bytesAvailable % sizeof(NativeType) != 0) {
+                JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SHARED_TYPED_ARRAY_BAD_ARGS);
+                return nullptr;
+            }
+            length = bytesAvailable / sizeof(NativeType);
+        }
+
+        if (length > MAX_LENGTH / sizeof(NativeType) || length * sizeof(NativeType) > bytesAvailable) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SHARED_TYPED_ARRAY_BAD_ARGS);
+            return nullptr;
+        }
+
+        return makeInstance(cx, buffer, byteOffset, length, proto);
+    }
+
+    static bool
+    fun_subarray(JSContext *cx, unsigned argc, Value *vp)
+    {
+        CallArgs args = CallArgsFromVp(argc, vp);
+        return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
+                                    TypedArraySubarrayTemplate<SharedTypedArrayObjectAdapter>::fun_subarray_impl>(cx, args);
+    }
+
+    static bool
+    fun_copyWithin(JSContext *cx, unsigned argc, Value *vp)
+    {
+        CallArgs args = CallArgsFromVp(argc, vp);
+        return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
+                                    TypedArrayCopyWithinTemplate<SharedTypedArrayObjectAdapter>::fun_copyWithin_impl>(cx, args);
+    }
+
+    static bool
+    fun_set(JSContext *cx, unsigned argc, Value *vp)
+    {
+        CallArgs args = CallArgsFromVp(argc, vp);
+        return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
+                                    TypedArraySetTemplate<SharedTypedArrayObjectAdapter>::fun_set_impl>(cx, args);
+    }
+
+  public:
+    static JSObject *
+    fromBuffer(JSContext *cx, HandleObject bufobj, uint32_t byteOffset, uint32_t length)
+    {
+        JS_ASSERT(byteOffset <= MAX_BYTEOFFSET);
+        JS_ASSERT(length <= MAX_LENGTH || length == LENGTH_NOT_PROVIDED);
+        RootedObject proto(cx, nullptr);
+        return fromBufferWithProto(cx, bufobj, byteOffset, length, proto);
+    }
+
+    static JSObject *
+    fromLength(JSContext *cx, uint32_t nelements)
+    {
+        JS_ASSERT(nelements <= MAX_LENGTH);
+        Rooted<SharedArrayBufferObject *> buffer(cx);
+        // Invariant established by createArrayBuffer(): nelements * sizeof(NativeType) <= MAX_LENGTH.
+        if (!createArrayBuffer(cx, nelements, &buffer))
+            return nullptr;
+        return makeInstance(cx, buffer, 0, nelements);
+    }
+};
+
+class SharedInt8ArrayObject : public SharedTypedArrayObjectTemplate<int8_t> {
+  public:
+    enum { ACTUAL_TYPE = Scalar::Int8 };
+    static const JSProtoKey key = JSProto_SharedInt8Array;
+    static const JSFunctionSpec jsfuncs[];
+    static const JSPropertySpec jsprops[];
+};
+class SharedUint8ArrayObject : public SharedTypedArrayObjectTemplate<uint8_t> {
+  public:
+    enum { ACTUAL_TYPE = Scalar::Uint8 };
+    static const JSProtoKey key = JSProto_SharedUint8Array;
+    static const JSFunctionSpec jsfuncs[];
+    static const JSPropertySpec jsprops[];
+};
+class SharedInt16ArrayObject : public SharedTypedArrayObjectTemplate<int16_t> {
+  public:
+    enum { ACTUAL_TYPE = Scalar::Int16 };
+    static const JSProtoKey key = JSProto_SharedInt16Array;
+    static const JSFunctionSpec jsfuncs[];
+    static const JSPropertySpec jsprops[];
+};
+class SharedUint16ArrayObject : public SharedTypedArrayObjectTemplate<uint16_t> {
+  public:
+    enum { ACTUAL_TYPE = Scalar::Uint16 };
+    static const JSProtoKey key = JSProto_SharedUint16Array;
+    static const JSFunctionSpec jsfuncs[];
+    static const JSPropertySpec jsprops[];
+};
+class SharedInt32ArrayObject : public SharedTypedArrayObjectTemplate<int32_t> {
+  public:
+    enum { ACTUAL_TYPE = Scalar::Int32 };
+    static const JSProtoKey key = JSProto_SharedInt32Array;
+    static const JSFunctionSpec jsfuncs[];
+    static const JSPropertySpec jsprops[];
+};
+class SharedUint32ArrayObject : public SharedTypedArrayObjectTemplate<uint32_t> {
+  public:
+    enum { ACTUAL_TYPE = Scalar::Uint32 };
+    static const JSProtoKey key = JSProto_SharedUint32Array;
+    static const JSFunctionSpec jsfuncs[];
+    static const JSPropertySpec jsprops[];
+};
+class SharedFloat32ArrayObject : public SharedTypedArrayObjectTemplate<float> {
+  public:
+    enum { ACTUAL_TYPE = Scalar::Float32 };
+    static const JSProtoKey key = JSProto_SharedFloat32Array;
+    static const JSFunctionSpec jsfuncs[];
+    static const JSPropertySpec jsprops[];
+};
+class SharedFloat64ArrayObject : public SharedTypedArrayObjectTemplate<double> {
+  public:
+    enum { ACTUAL_TYPE = Scalar::Float64 };
+    static const JSProtoKey key = JSProto_SharedFloat64Array;
+    static const JSFunctionSpec jsfuncs[];
+    static const JSPropertySpec jsprops[];
+};
+class SharedUint8ClampedArrayObject : public SharedTypedArrayObjectTemplate<uint8_clamped> {
+  public:
+    enum { ACTUAL_TYPE = Scalar::Uint8Clamped };
+    static const JSProtoKey key = JSProto_SharedUint8ClampedArray;
+    static const JSFunctionSpec jsfuncs[];
+    static const JSPropertySpec jsprops[];
+};
+
+} /* anonymous namespace */
+
+/*
+ * SharedTypedArrayObject boilerplate
+ */
+
+#define IMPL_SHARED_TYPED_ARRAY_STATICS(_typedArray)                               \
+const JSFunctionSpec Shared##_typedArray##Object::jsfuncs[] = {                    \
+    JS_FN("subarray", Shared##_typedArray##Object::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \
+    JS_FN("set", Shared##_typedArray##Object::fun_set, 2, JSFUN_GENERIC_NATIVE),   \
+    JS_FN("copyWithin", Shared##_typedArray##Object::fun_copyWithin, 2, JSFUN_GENERIC_NATIVE), \
+    JS_FS_END                                                                      \
+};                                                                                 \
+/* These next 3 functions are brought to you by the buggy GCC we use to build      \
+   B2G ICS. Older GCC versions have a bug in which they fail to compile            \
+   reinterpret_casts of templated functions with the message: "insufficient        \
+   contextual information to determine type". JS_PSG needs to                      \
+   reinterpret_cast<JSPropertyOp>, so this causes problems for us here.            \
+                                                                                   \
+   We could restructure all this code to make this nicer, but since ICS isn't      \
+   going to be around forever (and since this bug is fixed with the newer GCC      \
+   versions we use on JB and KK), the workaround here is designed for ease of      \
+   removal. When you stop seeing ICS Emulator builds on TBPL, remove these 3       \
+   JSNatives and insert the templated callee directly into the JS_PSG below. */    \
+bool Shared##_typedArray##_lengthGetter(JSContext *cx, unsigned argc, Value *vp) {         \
+    return Shared##_typedArray##Object::Getter<Shared##_typedArray##Object::lengthValue>(cx, argc, vp); \
+}                                                                                  \
+bool Shared##_typedArray##_byteLengthGetter(JSContext *cx, unsigned argc, Value *vp) {     \
+    return Shared##_typedArray##Object::Getter<Shared##_typedArray##Object::byteLengthValue>(cx, argc, vp); \
+}                                                                                  \
+bool Shared##_typedArray##_byteOffsetGetter(JSContext *cx, unsigned argc, Value *vp) {     \
+    return Shared##_typedArray##Object::Getter<Shared##_typedArray##Object::byteOffsetValue>(cx, argc, vp); \
+}                                                                                  \
+const JSPropertySpec Shared##_typedArray##Object::jsprops[] = {                            \
+    JS_PSG("length", Shared##_typedArray##_lengthGetter, 0),                               \
+    JS_PSG("buffer", Shared##_typedArray##Object::BufferGetter, 0),                        \
+    JS_PSG("byteLength", Shared##_typedArray##_byteLengthGetter, 0),                       \
+    JS_PSG("byteOffset", Shared##_typedArray##_byteOffsetGetter, 0),                       \
+    JS_PS_END                                                                      \
+};
+
+#define IMPL_SHARED_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Name,NativeType)                             \
+  JS_FRIEND_API(JSObject *) JS_NewShared ## Name ## Array(JSContext *cx, uint32_t nelements)    \
+  {                                                                                             \
+      return SharedTypedArrayObjectTemplate<NativeType>::fromLength(cx, nelements);             \
+  }                                                                                             \
+  JS_FRIEND_API(JSObject *) JS_NewShared ## Name ## ArrayWithBuffer(JSContext *cx,              \
+                               HandleObject arrayBuffer, uint32_t byteOffset, uint32_t length)  \
+  {                                                                                             \
+      return SharedTypedArrayObjectTemplate<NativeType>::fromBuffer(cx, arrayBuffer, byteOffset,\
+                                                                    length);                    \
+  }                                                                                             \
+  JS_FRIEND_API(bool) JS_IsShared ## Name ## Array(JSObject *obj)                               \
+  {                                                                                             \
+      if (!(obj = CheckedUnwrap(obj)))                                                          \
+          return false;                                                                         \
+      const Class *clasp = obj->getClass();                                                     \
+      const Scalar::Type id = SharedTypedArrayObjectTemplate<NativeType>::ArrayTypeID();        \
+      return clasp == &SharedTypedArrayObject::classes[id];                                     \
+  } \
+  JS_FRIEND_API(JSObject *) js::UnwrapShared ## Name ## Array(JSObject *obj)                    \
+  {                                                                                             \
+      obj = CheckedUnwrap(obj);                                                                 \
+      if (!obj)                                                                                 \
+          return nullptr;                                                                       \
+      const Class *clasp = obj->getClass();                                                     \
+      const Scalar::Type id = SharedTypedArrayObjectTemplate<NativeType>::ArrayTypeID();        \
+      if (clasp == &SharedTypedArrayObject::classes[id])                                        \
+          return obj;                                                                           \
+      return nullptr;                                                                           \
+  } \
+  const js::Class* const js::detail::Shared ## Name ## ArrayClassPtr =                          \
+      &js::SharedTypedArrayObject::classes[SharedTypedArrayObjectTemplate<NativeType>::ArrayTypeID()];
+
+IMPL_SHARED_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int8, int8_t)
+IMPL_SHARED_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint8, uint8_t)
+IMPL_SHARED_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint8Clamped, uint8_clamped)
+IMPL_SHARED_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int16, int16_t)
+IMPL_SHARED_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint16, uint16_t)
+IMPL_SHARED_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int32, int32_t)
+IMPL_SHARED_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint32, uint32_t)
+IMPL_SHARED_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float32, float)
+IMPL_SHARED_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float64, double)
+
+#define IMPL_SHARED_TYPED_ARRAY_COMBINED_UNWRAPPERS(Name, ExternalType, InternalType)       \
+  JS_FRIEND_API(JSObject *) JS_GetObjectAsShared ## Name ## Array(JSObject *obj,            \
+                                                                  uint32_t *length,         \
+								  ExternalType **data)      \
+  {                                                                                         \
+      if (!(obj = CheckedUnwrap(obj)))                                                      \
+          return nullptr;                                                                   \
+                                                                                            \
+      const Class *clasp = obj->getClass();                                                 \
+      const Scalar::Type id = SharedTypedArrayObjectTemplate<InternalType>::ArrayTypeID();  \
+      if (clasp != &SharedTypedArrayObject::classes[id])                                    \
+          return nullptr;                                                                   \
+                                                                                            \
+      SharedTypedArrayObject *tarr = &obj->as<SharedTypedArrayObject>();	            \
+      *length = tarr->length();                                                             \
+      *data = static_cast<ExternalType *>(tarr->viewData());                                \
+                                                                                            \
+      return obj;                                                                           \
+  }
+
+IMPL_SHARED_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int8, int8_t, int8_t)
+IMPL_SHARED_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint8, uint8_t, uint8_t)
+IMPL_SHARED_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint8Clamped, uint8_t, uint8_clamped)
+IMPL_SHARED_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int16, int16_t, int16_t)
+IMPL_SHARED_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint16, uint16_t, uint16_t)
+IMPL_SHARED_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int32, int32_t, int32_t)
+IMPL_SHARED_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint32, uint32_t, uint32_t)
+IMPL_SHARED_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float32, float, float)
+IMPL_SHARED_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float64, double, double)
+
+#define SHARED_TYPED_ARRAY_CLASS_SPEC(_typedArray)                             \
+{                                                                              \
+    GenericCreateConstructor<Shared##_typedArray##Object::class_constructor, 3, \
+                             JSFunction::FinalizeKind>,                        \
+    Shared##_typedArray##Object::CreatePrototype,                              \
+    nullptr,                                                                   \
+    Shared##_typedArray##Object::jsfuncs,                                      \
+    Shared##_typedArray##Object::jsprops,                                      \
+    Shared##_typedArray##Object::FinishClassInit                               \
+}
+
+#define IMPL_SHARED_TYPED_ARRAY_PROTO_CLASS(_typedArray)                       \
+{                                                                              \
+    "Shared" #_typedArray "Prototype",                                         \
+    JSCLASS_HAS_RESERVED_SLOTS(SharedTypedArrayObject::RESERVED_SLOTS) |       \
+    JSCLASS_HAS_PRIVATE |                                                      \
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Shared##_typedArray),                     \
+    JS_PropertyStub,         /* addProperty */                                 \
+    JS_DeletePropertyStub,   /* delProperty */                                 \
+    JS_PropertyStub,         /* getProperty */                                 \
+    JS_StrictPropertyStub,   /* setProperty */                                 \
+    JS_EnumerateStub,                                                          \
+    JS_ResolveStub,                                                            \
+    JS_ConvertStub,                                                            \
+    nullptr,                 /* finalize    */                                 \
+    nullptr,                 /* call        */                                 \
+    nullptr,                 /* hasInstance */                                 \
+    nullptr,                 /* construct   */                                 \
+    nullptr,                 /* trace  */                                      \
+    SHARED_TYPED_ARRAY_CLASS_SPEC(_typedArray)                                 \
+}
+
+#define IMPL_SHARED_TYPED_ARRAY_FAST_CLASS(_typedArray)                        \
+{                                                                              \
+    "Shared" #_typedArray,                                                     \
+    JSCLASS_HAS_RESERVED_SLOTS(SharedTypedArrayObject::RESERVED_SLOTS) |       \
+    JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |                        \
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Shared##_typedArray),                     \
+    JS_PropertyStub,         /* addProperty */                                 \
+    JS_DeletePropertyStub,   /* delProperty */                                 \
+    JS_PropertyStub,         /* getProperty */                                 \
+    JS_StrictPropertyStub,   /* setProperty */                                 \
+    JS_EnumerateStub,                                                          \
+    JS_ResolveStub,                                                            \
+    JS_ConvertStub,                                                            \
+    nullptr,                 /* finalize    */                                 \
+    nullptr,                 /* call        */                                 \
+    nullptr,                 /* hasInstance */                                 \
+    nullptr,                 /* construct   */                                 \
+    nullptr,                 /* trace  */	                               \
+    SHARED_TYPED_ARRAY_CLASS_SPEC(_typedArray)                                 \
+}
+
+template<typename NativeType>
+bool
+SharedTypedArrayObjectTemplate<NativeType>::FinishClassInit(JSContext *cx,
+							    HandleObject ctor,
+							    HandleObject proto)
+{
+    RootedValue bytesValue(cx, Int32Value(BYTES_PER_ELEMENT));
+
+    if (!JSObject::defineProperty(cx, ctor,
+                                  cx->names().BYTES_PER_ELEMENT, bytesValue,
+                                  JS_PropertyStub, JS_StrictPropertyStub,
+                                  JSPROP_PERMANENT | JSPROP_READONLY) ||
+        !JSObject::defineProperty(cx, proto,
+                                  cx->names().BYTES_PER_ELEMENT, bytesValue,
+                                  JS_PropertyStub, JS_StrictPropertyStub,
+                                  JSPROP_PERMANENT | JSPROP_READONLY))
+    {
+        return false;
+    }
+
+    return true;
+};
+
+IMPL_SHARED_TYPED_ARRAY_STATICS(Int8Array)
+IMPL_SHARED_TYPED_ARRAY_STATICS(Uint8Array)
+IMPL_SHARED_TYPED_ARRAY_STATICS(Int16Array)
+IMPL_SHARED_TYPED_ARRAY_STATICS(Uint16Array)
+IMPL_SHARED_TYPED_ARRAY_STATICS(Int32Array)
+IMPL_SHARED_TYPED_ARRAY_STATICS(Uint32Array)
+IMPL_SHARED_TYPED_ARRAY_STATICS(Float32Array)
+IMPL_SHARED_TYPED_ARRAY_STATICS(Float64Array)
+IMPL_SHARED_TYPED_ARRAY_STATICS(Uint8ClampedArray)
+
+const Class SharedTypedArrayObject::classes[Scalar::TypeMax] = {
+    IMPL_SHARED_TYPED_ARRAY_FAST_CLASS(Int8Array),
+    IMPL_SHARED_TYPED_ARRAY_FAST_CLASS(Uint8Array),
+    IMPL_SHARED_TYPED_ARRAY_FAST_CLASS(Int16Array),
+    IMPL_SHARED_TYPED_ARRAY_FAST_CLASS(Uint16Array),
+    IMPL_SHARED_TYPED_ARRAY_FAST_CLASS(Int32Array),
+    IMPL_SHARED_TYPED_ARRAY_FAST_CLASS(Uint32Array),
+    IMPL_SHARED_TYPED_ARRAY_FAST_CLASS(Float32Array),
+    IMPL_SHARED_TYPED_ARRAY_FAST_CLASS(Float64Array),
+    IMPL_SHARED_TYPED_ARRAY_FAST_CLASS(Uint8ClampedArray)
+};
+
+const Class SharedTypedArrayObject::protoClasses[Scalar::TypeMax] = {
+    IMPL_SHARED_TYPED_ARRAY_PROTO_CLASS(Int8Array),
+    IMPL_SHARED_TYPED_ARRAY_PROTO_CLASS(Uint8Array),
+    IMPL_SHARED_TYPED_ARRAY_PROTO_CLASS(Int16Array),
+    IMPL_SHARED_TYPED_ARRAY_PROTO_CLASS(Uint16Array),
+    IMPL_SHARED_TYPED_ARRAY_PROTO_CLASS(Int32Array),
+    IMPL_SHARED_TYPED_ARRAY_PROTO_CLASS(Uint32Array),
+    IMPL_SHARED_TYPED_ARRAY_PROTO_CLASS(Float32Array),
+    IMPL_SHARED_TYPED_ARRAY_PROTO_CLASS(Float64Array),
+    IMPL_SHARED_TYPED_ARRAY_PROTO_CLASS(Uint8ClampedArray)
+};
+
+// this default implementation is only valid for integer types
+// less than 32-bits in size.
+template<typename NativeType>
+Value
+SharedTypedArrayObjectTemplate<NativeType>::getIndexValue(JSObject *tarray, uint32_t index)
+{
+    JS_STATIC_ASSERT(sizeof(NativeType) < 4);
+
+    return Int32Value(getIndex(tarray, index));
+}
+
+namespace {
+
+// and we need to specialize for 32-bit integers and floats
+template<>
+Value
+SharedTypedArrayObjectTemplate<int32_t>::getIndexValue(JSObject *tarray, uint32_t index)
+{
+    return Int32Value(getIndex(tarray, index));
+}
+
+template<>
+Value
+SharedTypedArrayObjectTemplate<uint32_t>::getIndexValue(JSObject *tarray, uint32_t index)
+{
+    uint32_t val = getIndex(tarray, index);
+    return NumberValue(val);
+}
+
+template<>
+Value
+SharedTypedArrayObjectTemplate<float>::getIndexValue(JSObject *tarray, uint32_t index)
+{
+    float val = getIndex(tarray, index);
+    double dval = val;
+
+    /*
+     * Doubles in typed arrays could be typed-punned arrays of integers. This
+     * could allow user code to break the engine-wide invariant that only
+     * canonical nans are stored into jsvals, which means user code could
+     * confuse the engine into interpreting a double-typed jsval as an
+     * object-typed jsval.
+     *
+     * This could be removed for platforms/compilers known to convert a 32-bit
+     * non-canonical nan to a 64-bit canonical nan.
+     */
+    return DoubleValue(CanonicalizeNaN(dval));
+}
+
+template<>
+Value
+SharedTypedArrayObjectTemplate<double>::getIndexValue(JSObject *tarray, uint32_t index)
+{
+    double val = getIndex(tarray, index);
+
+    /*
+     * Doubles in typed arrays could be typed-punned arrays of integers. This
+     * could allow user code to break the engine-wide invariant that only
+     * canonical nans are stored into jsvals, which means user code could
+     * confuse the engine into interpreting a double-typed jsval as an
+     * object-typed jsval.
+     */
+    return DoubleValue(CanonicalizeNaN(val));
+}
+
+} /* anonymous namespace */
+
+
+/* static */ bool
+SharedTypedArrayObject::isOriginalLengthGetter(Scalar::Type type, Native native)
+{
+    switch (type) {
+      case Scalar::Int8:
+        return native == SharedInt8Array_lengthGetter;
+      case Scalar::Uint8:
+        return native == SharedUint8Array_lengthGetter;
+      case Scalar::Uint8Clamped:
+        return native == SharedUint8ClampedArray_lengthGetter;
+      case Scalar::Int16:
+        return native == SharedInt16Array_lengthGetter;
+      case Scalar::Uint16:
+        return native == SharedUint16Array_lengthGetter;
+      case Scalar::Int32:
+        return native == SharedInt32Array_lengthGetter;
+      case Scalar::Uint32:
+        return native == SharedUint32Array_lengthGetter;
+      case Scalar::Float32:
+        return native == SharedFloat32Array_lengthGetter;
+      case Scalar::Float64:
+        return native == SharedFloat64Array_lengthGetter;
+      default:
+        MOZ_CRASH("Unknown TypedArray type");
+    }
+}
+
+bool
+js::IsSharedTypedArrayConstructor(HandleValue v, uint32_t type)
+{
+    switch (type) {
+      case Scalar::Int8:
+        return IsNativeFunction(v, SharedInt8ArrayObject::class_constructor);
+      case Scalar::Uint8:
+        return IsNativeFunction(v, SharedUint8ArrayObject::class_constructor);
+      case Scalar::Int16:
+        return IsNativeFunction(v, SharedInt16ArrayObject::class_constructor);
+      case Scalar::Uint16:
+        return IsNativeFunction(v, SharedUint16ArrayObject::class_constructor);
+      case Scalar::Int32:
+        return IsNativeFunction(v, SharedInt32ArrayObject::class_constructor);
+      case Scalar::Uint32:
+        return IsNativeFunction(v, SharedUint32ArrayObject::class_constructor);
+      case Scalar::Float32:
+        return IsNativeFunction(v, SharedFloat32ArrayObject::class_constructor);
+      case Scalar::Float64:
+        return IsNativeFunction(v, SharedFloat64ArrayObject::class_constructor);
+      case Scalar::Uint8Clamped:
+        return IsNativeFunction(v, SharedUint8ClampedArrayObject::class_constructor);
+    }
+    MOZ_CRASH("unexpected shared typed array type");
+}
+
+JS_FRIEND_API(bool)
+JS_IsSharedTypedArrayObject(JSObject *obj)
+{
+    obj = CheckedUnwrap(obj);
+    return obj && obj->is<SharedTypedArrayObject>();
+}
+
+SharedArrayBufferObject *
+SharedTypedArrayObject::buffer() const
+{
+    return &bufferValue(const_cast<SharedTypedArrayObject*>(this)).toObject().as<SharedArrayBufferObject>();
+}
+
+Value
+SharedTypedArrayObject::getElement(uint32_t index)
+{
+    switch (type()) {
+      case Scalar::Int8:
+        return SharedTypedArrayObjectTemplate<int8_t>::getIndexValue(this, index);
+        break;
+      case Scalar::Uint8:
+        return SharedTypedArrayObjectTemplate<uint8_t>::getIndexValue(this, index);
+        break;
+      case Scalar::Uint8Clamped:
+        return SharedTypedArrayObjectTemplate<uint8_clamped>::getIndexValue(this, index);
+        break;
+      case Scalar::Int16:
+        return SharedTypedArrayObjectTemplate<int16_t>::getIndexValue(this, index);
+        break;
+      case Scalar::Uint16:
+        return SharedTypedArrayObjectTemplate<uint16_t>::getIndexValue(this, index);
+        break;
+      case Scalar::Int32:
+        return SharedTypedArrayObjectTemplate<int32_t>::getIndexValue(this, index);
+        break;
+      case Scalar::Uint32:
+        return SharedTypedArrayObjectTemplate<uint32_t>::getIndexValue(this, index);
+        break;
+      case Scalar::Float32:
+        return SharedTypedArrayObjectTemplate<float>::getIndexValue(this, index);
+        break;
+      case Scalar::Float64:
+        return SharedTypedArrayObjectTemplate<double>::getIndexValue(this, index);
+        break;
+      default:
+        MOZ_CRASH("Unknown SharedTypedArray type");
+    }
+}
+
+void
+SharedTypedArrayObject::setElement(SharedTypedArrayObject &obj, uint32_t index, double d)
+{
+    MOZ_ASSERT(index < obj.length());
+
+    switch (obj.type()) {
+      case Scalar::Int8:
+	SharedTypedArrayObjectTemplate<int8_t>::setIndexValue(obj, index, d);
+        break;
+      case Scalar::Uint8:
+        SharedTypedArrayObjectTemplate<uint8_t>::setIndexValue(obj, index, d);
+        break;
+      case Scalar::Uint8Clamped:
+        SharedTypedArrayObjectTemplate<uint8_clamped>::setIndexValue(obj, index, d);
+        break;
+      case Scalar::Int16:
+        SharedTypedArrayObjectTemplate<int16_t>::setIndexValue(obj, index, d);
+        break;
+      case Scalar::Uint16:
+        SharedTypedArrayObjectTemplate<uint16_t>::setIndexValue(obj, index, d);
+        break;
+      case Scalar::Int32:
+        SharedTypedArrayObjectTemplate<int32_t>::setIndexValue(obj, index, d);
+        break;
+      case Scalar::Uint32:
+        SharedTypedArrayObjectTemplate<uint32_t>::setIndexValue(obj, index, d);
+        break;
+      case Scalar::Float32:
+        SharedTypedArrayObjectTemplate<float>::setIndexValue(obj, index, d);
+        break;
+      case Scalar::Float64:
+        SharedTypedArrayObjectTemplate<double>::setIndexValue(obj, index, d);
+        break;
+      default:
+        MOZ_CRASH("Unknown SharedTypedArray type");
+    }
+}
+
+#undef IMPL_SHARED_TYPED_ARRAY_STATICS
+#undef IMPL_SHARED_TYPED_ARRAY_JSAPI_CONSTRUCTORS
+#undef IMPL_SHARED_TYPED_ARRAY_COMBINED_UNWRAPPERS
+#undef SHARED_TYPED_ARRAY_CLASS_SPEC
+#undef IMPL_SHARED_TYPED_ARRAY_PROTO_CLASS
+#undef IMPL_SHARED_TYPED_ARRAY_FAST_CLASS
new file mode 100644
--- /dev/null
+++ b/js/src/vm/SharedTypedArrayObject.h
@@ -0,0 +1,122 @@
+/* -*- 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_SharedTypedArrayObject_h
+#define vm_SharedTypedArrayObject_h
+
+#include "jsobj.h"
+
+#include "builtin/TypedObjectConstants.h"
+#include "gc/Barrier.h"
+#include "js/Class.h"
+#include "vm/ArrayBufferObject.h"
+#include "vm/SharedArrayObject.h"
+#include "vm/TypedArrayObject.h"
+
+typedef struct JSProperty JSProperty;
+
+namespace js {
+
+// Note that the representation of a SharedTypedArrayObject is the
+// same as the representation of a TypedArrayObject, see comments in
+// TypedArrayObject.h.
+
+class SharedTypedArrayObject : public JSObject
+{
+  protected:
+    static const size_t TYPE_SLOT        = TypedArrayLayout::TYPE_SLOT;
+    static const size_t BUFFER_SLOT      = TypedArrayLayout::BUFFER_SLOT;
+    static const size_t BYTEOFFSET_SLOT  = TypedArrayLayout::BYTEOFFSET_SLOT;
+    static const size_t LENGTH_SLOT      = TypedArrayLayout::LENGTH_SLOT;
+    static const size_t RESERVED_SLOTS   = TypedArrayLayout::RESERVED_SLOTS;
+    static const size_t DATA_SLOT        = TypedArrayLayout::DATA_SLOT;
+
+  public:
+    static const Class classes[Scalar::TypeMax];
+    static const Class protoClasses[Scalar::TypeMax];
+
+    static SharedArrayBufferObject *bufferObject(JSContext *cx, Handle<SharedTypedArrayObject *> obj);
+
+    static Value bufferValue(SharedTypedArrayObject *tarr) {
+        return tarr->getFixedSlot(BUFFER_SLOT);
+    }
+    static Value byteOffsetValue(SharedTypedArrayObject *tarr) {
+        return tarr->getFixedSlot(BYTEOFFSET_SLOT);
+    }
+    static Value byteLengthValue(SharedTypedArrayObject *tarr) {
+        int32_t size = Scalar::byteSize(tarr->type());
+        return Int32Value(tarr->getFixedSlot(LENGTH_SLOT).toInt32() * size);
+    }
+    static Value lengthValue(SharedTypedArrayObject *tarr) {
+        return tarr->getFixedSlot(LENGTH_SLOT);
+    }
+
+    static void setElement(SharedTypedArrayObject &obj, uint32_t index, double d);
+
+    static bool isOriginalLengthGetter(Scalar::Type type, Native native);
+
+    SharedArrayBufferObject *buffer() const;
+
+    Scalar::Type type() const {
+        return (Scalar::Type) getFixedSlot(TYPE_SLOT).toInt32();
+    }
+    void *viewData() const {
+        return getPrivate(DATA_SLOT);
+    }
+    uint32_t byteOffset() const {
+        return byteOffsetValue(const_cast<SharedTypedArrayObject*>(this)).toInt32();
+    }
+    uint32_t byteLength() const {
+        return byteLengthValue(const_cast<SharedTypedArrayObject*>(this)).toInt32();
+    }
+    uint32_t length() const {
+        return lengthValue(const_cast<SharedTypedArrayObject*>(this)).toInt32();
+    }
+
+    Value getElement(uint32_t index);
+
+    /*
+     * Byte length above which created typed arrays and data views will have
+     * singleton types regardless of the context in which they are created.
+     */
+    static const uint32_t SINGLETON_TYPE_BYTE_LENGTH = 1024 * 1024 * 10;
+
+  private:
+    static TypedArrayLayout layout_;
+
+  public:
+    static const TypedArrayLayout &layout() {
+        return layout_;
+    }
+};
+
+inline bool
+IsSharedTypedArrayClass(const Class *clasp)
+{
+    return &SharedTypedArrayObject::classes[0] <= clasp &&
+           clasp < &SharedTypedArrayObject::classes[Scalar::TypeMax];
+}
+
+inline bool
+IsSharedTypedArrayProtoClass(const Class *clasp)
+{
+    return &SharedTypedArrayObject::protoClasses[0] <= clasp &&
+           clasp < &SharedTypedArrayObject::protoClasses[Scalar::TypeMax];
+}
+
+bool
+IsSharedTypedArrayConstructor(HandleValue v, uint32_t type);
+
+}  // namespace js
+
+template <>
+inline bool
+JSObject::is<js::SharedTypedArrayObject>() const
+{
+    return js::IsSharedTypedArrayClass(getClass());
+}
+
+#endif // vm_SharedTypedArrayObject_h
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -72,16 +72,17 @@ enum StructuredDataType MOZ_ENUM_TYPE(ui
     SCTAG_STRING_OBJECT,
     SCTAG_NUMBER_OBJECT,
     SCTAG_BACK_REFERENCE_OBJECT,
     SCTAG_DO_NOT_USE_1, // Required for backwards compatibility
     SCTAG_DO_NOT_USE_2, // Required for backwards compatibility
     SCTAG_TYPED_ARRAY_OBJECT,
     SCTAG_MAP_OBJECT,
     SCTAG_SET_OBJECT,
+    SCTAG_SHARED_TYPED_ARRAY_OBJECT,
     SCTAG_END_OF_KEYS,
 
     SCTAG_TYPED_ARRAY_V1_MIN = 0xFFFF0100,
     SCTAG_TYPED_ARRAY_V1_INT8 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Int8,
     SCTAG_TYPED_ARRAY_V1_UINT8 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Uint8,
     SCTAG_TYPED_ARRAY_V1_INT16 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Int16,
     SCTAG_TYPED_ARRAY_V1_UINT16 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Uint16,
     SCTAG_TYPED_ARRAY_V1_INT32 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Int32,
@@ -221,16 +222,17 @@ struct JSStructuredCloneReader {
 
     template <typename CharT>
     JSString *readStringImpl(uint32_t nchars);
     JSString *readString(uint32_t data);
 
     bool checkDouble(double d);
     bool readTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp,
                         bool v1Read = false);
+    bool readSharedTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp);
     bool readArrayBuffer(uint32_t nbytes, MutableHandleValue vp);
     bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp);
     bool startRead(MutableHandleValue vp);
 
     SCInput &in;
 
     // Stack of objects with properties remaining to be read.
     AutoValueVector objs;
@@ -273,16 +275,18 @@ struct JSStructuredCloneWriter {
   private:
     JSContext *context() { return out.context(); }
 
     bool writeTransferMap();
 
     bool writeString(uint32_t tag, JSString *str);
     bool writeArrayBuffer(HandleObject obj);
     bool writeTypedArray(HandleObject obj);
+    bool writeSharedArrayBuffer(HandleObject obj);
+    bool writeSharedTypedArray(HandleObject obj);
     bool startObject(HandleObject obj, bool *backref);
     bool startWrite(HandleValue v);
     bool traverseObject(HandleObject obj);
     bool traverseMap(HandleObject obj);
     bool traverseSet(HandleObject obj);
 
     bool parseTransferable();
     bool reportErrorTransferable();
@@ -876,16 +880,43 @@ JSStructuredCloneWriter::writeArrayBuffe
     ArrayBufferObject &buffer = CheckedUnwrap(obj)->as<ArrayBufferObject>();
     JSAutoCompartment ac(context(), &buffer);
 
     return out.writePair(SCTAG_ARRAY_BUFFER_OBJECT, buffer.byteLength()) &&
            out.writeBytes(buffer.dataPointer(), buffer.byteLength());
 }
 
 bool
+JSStructuredCloneWriter::writeSharedArrayBuffer(HandleObject obj)
+{
+    JS_ReportErrorNumber(context(), js_GetErrorMessage, nullptr, JSMSG_SC_SHMEM_MUST_TRANSFER);
+    return false;
+}
+
+bool
+JSStructuredCloneWriter::writeSharedTypedArray(HandleObject obj)
+{
+    Rooted<SharedTypedArrayObject*> tarr(context(), &CheckedUnwrap(obj)->as<SharedTypedArrayObject>());
+    JSAutoCompartment ac(context(), tarr);
+
+    if (!out.writePair(SCTAG_SHARED_TYPED_ARRAY_OBJECT, tarr->length()))
+        return false;
+    uint64_t type = tarr->type();
+    if (!out.write(type))
+        return false;
+
+    // Write out the SharedArrayBuffer tag and contents.
+    RootedValue val(context(), SharedTypedArrayObject::bufferValue(tarr));
+    if (!startWrite(val))
+        return false;
+
+    return out.write(tarr->byteOffset());
+}
+
+bool
 JSStructuredCloneWriter::startObject(HandleObject obj, bool *backref)
 {
     /* Handle cycles in the object graph. */
     CloneMemory::AddPtr p = memory.lookupForAdd(obj);
     if ((*backref = p))
         return out.writePair(SCTAG_BACK_REFERENCE_OBJECT, p->value());
     if (!memory.add(p, obj, memory.count()))
         return false;
@@ -1021,16 +1052,20 @@ JSStructuredCloneWriter::startWrite(Hand
                    writeString(SCTAG_STRING, re->getSource());
         } else if (ObjectClassIs(obj, ESClass_Date, context())) {
             double d = js_DateGetMsecSinceEpoch(obj);
             return out.writePair(SCTAG_DATE_OBJECT, 0) && out.writeDouble(d);
         } else if (JS_IsTypedArrayObject(obj)) {
             return writeTypedArray(obj);
         } else if (JS_IsArrayBufferObject(obj) && JS_ArrayBufferHasData(obj)) {
             return writeArrayBuffer(obj);
+        } else if (JS_IsSharedTypedArrayObject(obj)) {
+            return writeSharedTypedArray(obj);
+        } else if (JS_IsSharedArrayBufferObject(obj)) {
+            return writeSharedArrayBuffer(obj);
         } else if (ObjectClassIs(obj, ESClass_Object, context())) {
             return traverseObject(obj);
         } else if (ObjectClassIs(obj, ESClass_Array, context())) {
             return traverseObject(obj);
         } else if (ObjectClassIs(obj, ESClass_Boolean, context())) {
             RootedValue unboxed(context());
             if (!Unbox(context(), obj, &unboxed))
                 return false;
@@ -1118,42 +1153,41 @@ JSStructuredCloneWriter::transferOwnersh
         SCInput::getPair(point, &tag, (uint32_t*) &ownership);
         MOZ_ASSERT(tag == SCTAG_TRANSFER_MAP_PENDING_ENTRY);
         MOZ_ASSERT(ownership == JS::SCTAG_TMO_UNFILLED);
 #endif
 
         if (ObjectClassIs(obj, ESClass_ArrayBuffer, context())) {
             // The current setup of the array buffer inheritance hierarchy doesn't
             // lend itself well to generic manipulation via proxies.
-            Rooted<ArrayBufferObject*> arrayBuffer(context(), &CheckedUnwrap(obj)->as<ArrayBufferObject>());
-            if (arrayBuffer->isSharedArrayBuffer()) {
-                SharedArrayRawBuffer *rawbuf = arrayBuffer->as<SharedArrayBufferObject>().rawBufferObject();
-
-                // Avoids a race condition where the parent thread frees the buffer
-                // before the child has accepted the transferable.
-                rawbuf->addReference();
+            Rooted<ArrayBufferObject *> arrayBuffer(context(), &CheckedUnwrap(obj)->as<ArrayBufferObject>());
+            size_t nbytes = arrayBuffer->byteLength();
+            ArrayBufferObject::BufferContents bufContents =
+                ArrayBufferObject::stealContents(context(), arrayBuffer);
+            if (!bufContents)
+                return false; // Destructor will clean up the already-transferred data.
+            content = bufContents.data();
+            tag = SCTAG_TRANSFER_MAP_ARRAY_BUFFER;
+            if (bufContents.kind() & ArrayBufferObject::MAPPED_BUFFER)
+                ownership = JS::SCTAG_TMO_MAPPED_DATA;
+            else
+                ownership = JS::SCTAG_TMO_ALLOC_DATA;
+            extraData = nbytes;
+        } else if (ObjectClassIs(obj, ESClass_SharedArrayBuffer, context())) {
+            Rooted<SharedArrayBufferObject *> sharedArrayBuffer(context(), &CheckedUnwrap(obj)->as<SharedArrayBufferObject>());
+            SharedArrayRawBuffer *rawbuf = sharedArrayBuffer->rawBufferObject();
 
-                tag = SCTAG_TRANSFER_MAP_SHARED_BUFFER;
-                ownership = JS::SCTAG_TMO_SHARED_BUFFER;
-                content = rawbuf;
-                extraData = 0;
-            } else {
-                size_t nbytes = arrayBuffer->byteLength();
-                ArrayBufferObject::BufferContents bufContents =
-                    ArrayBufferObject::stealContents(context(), arrayBuffer);
-                if (!bufContents)
-                    return false; // Destructor will clean up the already-transferred data
-                content = bufContents.data();
-                tag = SCTAG_TRANSFER_MAP_ARRAY_BUFFER;
-                if (bufContents.kind() & ArrayBufferObject::MAPPED_BUFFER)
-                    ownership = JS::SCTAG_TMO_MAPPED_DATA;
-                else
-                    ownership = JS::SCTAG_TMO_ALLOC_DATA;
-                extraData = nbytes;
-            }
+            // Avoids a race condition where the parent thread frees the buffer
+            // before the child has accepted the transferable.
+            rawbuf->addReference();
+
+            tag = SCTAG_TRANSFER_MAP_SHARED_BUFFER;
+            ownership = JS::SCTAG_TMO_SHARED_BUFFER;
+            content = rawbuf;
+            extraData = 0;
         } else {
             if (!callbacks || !callbacks->writeTransfer)
                 return reportErrorTransferable();
             if (!callbacks->writeTransfer(context(), obj, closure, &tag, &ownership, &content, &extraData))
                 return false;
             JS_ASSERT(tag > SCTAG_TRANSFER_MAP_PENDING_ENTRY);
         }
 
@@ -1374,16 +1408,84 @@ JSStructuredCloneReader::readTypedArray(
     vp.setObject(*obj);
 
     allObjs[placeholderIndex].set(vp);
 
     return true;
 }
 
 bool
+JSStructuredCloneReader::readSharedTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp)
+{
+    if (arrayType > Scalar::Uint8Clamped) {
+        JS_ReportErrorNumber(context(), js_GetErrorMessage, nullptr,
+                             JSMSG_SC_BAD_SERIALIZED_DATA, "unhandled typed array element type");
+        return false;
+    }
+
+    // Push a placeholder onto the allObjs list to stand in for the typed array.
+    uint32_t placeholderIndex = allObjs.length();
+    Value dummy = UndefinedValue();
+    if (!allObjs.append(dummy))
+        return false;
+
+    // Read the ArrayBuffer object and its contents (but no properties).
+    RootedValue v(context());
+    uint32_t byteOffset;
+    if (!startRead(&v))
+        return false;
+    uint64_t n;
+    if (!in.read(&n))
+        return false;
+    byteOffset = n;
+    RootedObject buffer(context(), &v.toObject());
+    RootedObject obj(context());
+
+    switch (arrayType) {
+      case Scalar::Int8:
+        obj = JS_NewSharedInt8ArrayWithBuffer(context(), buffer, byteOffset, nelems);
+        break;
+      case Scalar::Uint8:
+        obj = JS_NewSharedUint8ArrayWithBuffer(context(), buffer, byteOffset, nelems);
+        break;
+      case Scalar::Int16:
+        obj = JS_NewSharedInt16ArrayWithBuffer(context(), buffer, byteOffset, nelems);
+        break;
+      case Scalar::Uint16:
+        obj = JS_NewSharedUint16ArrayWithBuffer(context(), buffer, byteOffset, nelems);
+        break;
+      case Scalar::Int32:
+        obj = JS_NewSharedInt32ArrayWithBuffer(context(), buffer, byteOffset, nelems);
+        break;
+      case Scalar::Uint32:
+        obj = JS_NewSharedUint32ArrayWithBuffer(context(), buffer, byteOffset, nelems);
+        break;
+      case Scalar::Float32:
+        obj = JS_NewSharedFloat32ArrayWithBuffer(context(), buffer, byteOffset, nelems);
+        break;
+      case Scalar::Float64:
+        obj = JS_NewSharedFloat64ArrayWithBuffer(context(), buffer, byteOffset, nelems);
+        break;
+      case Scalar::Uint8Clamped:
+        obj = JS_NewSharedUint8ClampedArrayWithBuffer(context(), buffer, byteOffset, nelems);
+        break;
+      default:
+        MOZ_CRASH("Can't happen: arrayType range checked above");
+    }
+
+    if (!obj)
+        return false;
+    vp.setObject(*obj);
+
+    allObjs[placeholderIndex].set(vp);
+
+    return true;
+}
+
+bool
 JSStructuredCloneReader::readArrayBuffer(uint32_t nbytes, MutableHandleValue vp)
 {
     JSObject *obj = ArrayBufferObject::create(context(), nbytes);
     if (!obj)
         return false;
     vp.setObject(*obj);
     ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
     JS_ASSERT(buffer.byteLength() == nbytes);
@@ -1559,22 +1661,31 @@ JSStructuredCloneReader::startRead(Mutab
                              "invalid input");
         return false;
 
       case SCTAG_ARRAY_BUFFER_OBJECT:
         if (!readArrayBuffer(data, vp))
             return false;
         break;
 
-      case SCTAG_TYPED_ARRAY_OBJECT:
-        // readTypedArray adds the array to allObjs
+      case SCTAG_TYPED_ARRAY_OBJECT: {
+        // readTypedArray adds the array to allObjs.
         uint64_t arrayType;
         if (!in.read(&arrayType))
             return false;
         return readTypedArray(arrayType, data, vp);
+      }
+
+      case SCTAG_SHARED_TYPED_ARRAY_OBJECT: {
+        // readSharedTypedArray adds the array to allObjs.
+        uint64_t arrayType;
+        if (!in.read(&arrayType))
+            return false;
+        return readSharedTypedArray(arrayType, data, vp);
+      }
 
       case SCTAG_MAP_OBJECT: {
         JSObject *obj = MapObject::create(context());
         if (!obj || !objs.append(ObjectValue(*obj)))
             return false;
         vp.setObject(*obj);
         break;
       }
new file mode 100644
--- /dev/null
+++ b/js/src/vm/TypedArrayCommon.h
@@ -0,0 +1,657 @@
+/* -*- 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_TypedArrayCommon_h
+#define vm_TypedArrayCommon_h
+
+/* Utilities and common inline code for TypedArray and SharedTypedArray */
+
+#include "mozilla/FloatingPoint.h"
+
+#include "js/Value.h"
+
+#include "vm/SharedTypedArrayObject.h"
+#include "vm/TypedArrayObject.h"
+
+namespace js {
+
+// Definitions below are shared between TypedArrayObject and
+// SharedTypedArrayObject.
+
+// ValueIsLength happens not to be according to ES6, which mandates
+// the use of ToLength, which in turn includes ToNumber, ToInteger,
+// and clamping.  ValueIsLength is used in the current TypedArray code
+// but will disappear when that code is made spec-compliant.
+
+inline bool
+ValueIsLength(const Value &v, uint32_t *len)
+{
+    if (v.isInt32()) {
+        int32_t i = v.toInt32();
+        if (i < 0)
+            return false;
+        *len = i;
+        return true;
+    }
+
+    if (v.isDouble()) {
+        double d = v.toDouble();
+        if (mozilla::IsNaN(d))
+            return false;
+
+        uint32_t length = uint32_t(d);
+        if (d != double(length))
+            return false;
+
+        *len = length;
+        return true;
+    }
+
+    return false;
+}
+
+template<typename NativeType> static inline Scalar::Type TypeIDOfType();
+template<> inline Scalar::Type TypeIDOfType<int8_t>() { return Scalar::Int8; }
+template<> inline Scalar::Type TypeIDOfType<uint8_t>() { return Scalar::Uint8; }
+template<> inline Scalar::Type TypeIDOfType<int16_t>() { return Scalar::Int16; }
+template<> inline Scalar::Type TypeIDOfType<uint16_t>() { return Scalar::Uint16; }
+template<> inline Scalar::Type TypeIDOfType<int32_t>() { return Scalar::Int32; }
+template<> inline Scalar::Type TypeIDOfType<uint32_t>() { return Scalar::Uint32; }
+template<> inline Scalar::Type TypeIDOfType<float>() { return Scalar::Float32; }
+template<> inline Scalar::Type TypeIDOfType<double>() { return Scalar::Float64; }
+template<> inline Scalar::Type TypeIDOfType<uint8_clamped>() { return Scalar::Uint8Clamped; }
+
+inline bool
+IsAnyTypedArray(HandleObject obj)
+{
+    return obj->is<TypedArrayObject>() || obj->is<SharedTypedArrayObject>();
+}
+
+inline bool
+IsAnyTypedArray(const JSObject *obj)
+{
+    return obj->is<TypedArrayObject>() || obj->is<SharedTypedArrayObject>();
+}
+
+inline uint32_t
+AnyTypedArrayLength(HandleObject obj)
+{
+    if (obj->is<TypedArrayObject>())
+	return obj->as<TypedArrayObject>().length();
+    return obj->as<SharedTypedArrayObject>().length();
+}
+
+inline uint32_t
+AnyTypedArrayLength(const JSObject *obj)
+{
+    if (obj->is<TypedArrayObject>())
+	return obj->as<TypedArrayObject>().length();
+    return obj->as<SharedTypedArrayObject>().length();
+}
+
+inline Scalar::Type
+AnyTypedArrayType(HandleObject obj)
+{
+    if (obj->is<TypedArrayObject>())
+	return obj->as<TypedArrayObject>().type();
+    return obj->as<SharedTypedArrayObject>().type();
+}
+
+inline Scalar::Type
+AnyTypedArrayType(const JSObject *obj)
+{
+    if (obj->is<TypedArrayObject>())
+	return obj->as<TypedArrayObject>().type();
+    return obj->as<SharedTypedArrayObject>().type();
+}
+
+inline Shape*
+AnyTypedArrayShape(HandleObject obj)
+{
+    if (obj->is<TypedArrayObject>())
+	return obj->as<TypedArrayObject>().lastProperty();
+    return obj->as<SharedTypedArrayObject>().lastProperty();
+}
+
+inline Shape*
+AnyTypedArrayShape(const JSObject *obj)
+{
+    if (obj->is<TypedArrayObject>())
+	return obj->as<TypedArrayObject>().lastProperty();
+    return obj->as<SharedTypedArrayObject>().lastProperty();
+}
+
+inline const TypedArrayLayout&
+AnyTypedArrayLayout(const JSObject *obj)
+{
+    if (obj->is<TypedArrayObject>())
+	return obj->as<TypedArrayObject>().layout();
+    return obj->as<SharedTypedArrayObject>().layout();
+}
+
+inline void *
+AnyTypedArrayViewData(const JSObject *obj)
+{
+    if (obj->is<TypedArrayObject>())
+	return obj->as<TypedArrayObject>().viewData();
+    return obj->as<SharedTypedArrayObject>().viewData();
+}
+
+inline uint32_t
+AnyTypedArrayByteLength(const JSObject *obj)
+{
+    if (obj->is<TypedArrayObject>())
+	return obj->as<TypedArrayObject>().byteLength();
+    return obj->as<SharedTypedArrayObject>().byteLength();
+}
+
+inline bool
+IsAnyTypedArrayClass(const Class *clasp)
+{
+    return IsTypedArrayClass(clasp) || IsSharedTypedArrayClass(clasp);
+}
+
+// The subarray() method on TypedArray and SharedTypedArray
+
+template<typename Adapter>
+class TypedArraySubarrayTemplate
+{
+    typedef typename Adapter::ElementType NativeType;
+    typedef typename Adapter::TypedArrayObjectType ArrayType;
+    typedef typename Adapter::ArrayBufferObjectType BufferType;
+
+  public:
+    static JSObject *
+    createSubarray(JSContext *cx, HandleObject tarrayArg, uint32_t begin, uint32_t end)
+    {
+        Rooted<ArrayType*> tarray(cx, &tarrayArg->as<ArrayType>());
+
+        if (begin > tarray->length() || end > tarray->length() || begin > end) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
+            return nullptr;
+        }
+
+        if (!Adapter::ensureHasBuffer(cx, tarray))
+            return nullptr;
+
+        Rooted<BufferType *> bufobj(cx, tarray->buffer());
+        JS_ASSERT(bufobj);
+
+        uint32_t length = end - begin;
+
+        JS_ASSERT(begin < UINT32_MAX / sizeof(NativeType));
+        uint32_t arrayByteOffset = tarray->byteOffset();
+        JS_ASSERT(UINT32_MAX - begin * sizeof(NativeType) >= arrayByteOffset);
+        uint32_t byteOffset = arrayByteOffset + begin * sizeof(NativeType);
+
+        return Adapter::ThisTypedArrayObjectType::makeInstance(cx, bufobj, byteOffset, length);
+    }
+
+    static bool
+    fun_subarray_impl(JSContext *cx, CallArgs args)
+    {
+        JS_ASSERT(Adapter::ThisTypedArrayObjectType::IsThisClass(args.thisv()));
+        Rooted<ArrayType *> tarray(cx, &args.thisv().toObject().as<ArrayType>());
+
+        // these are the default values
+        uint32_t length = tarray->length();
+        uint32_t begin = 0, end = length;
+
+        if (args.length() > 0 && !ToClampedIndex(cx, args[0], length, &begin))
+            return false;
+        if (args.length() > 1 && !ToClampedIndex(cx, args[1], length, &end))
+                    return false;
+
+        if (begin > end)
+            begin = end;
+
+        JSObject *nobj = createSubarray(cx, tarray, begin, end);
+        if (!nobj)
+            return false;
+        args.rval().setObject(*nobj);
+        return true;
+    }
+};
+
+// The copyWithin() method on TypedArray and SharedTypedArray
+
+template<typename Adapter>
+class TypedArrayCopyWithinTemplate
+{
+    typedef typename Adapter::ElementType NativeType;
+    typedef typename Adapter::TypedArrayObjectType ArrayType;
+
+  public:
+    /* copyWithin(target, start[, end]) */
+    // ES6 draft rev 26, 22.2.3.5
+    static bool
+    fun_copyWithin_impl(JSContext *cx, CallArgs args)
+    {
+        MOZ_ASSERT(Adapter::ThisTypedArrayObjectType::IsThisClass(args.thisv()));
+
+        // Steps 1-2.
+        Rooted<ArrayType*> obj(cx, &args.thisv().toObject().as<ArrayType>());
+
+        // Steps 3-4.
+        uint32_t len = obj->length();
+
+        // Steps 6-8.
+        uint32_t to;
+        if (!ToClampedIndex(cx, args.get(0), len, &to))
+            return false;
+
+        // Steps 9-11.
+        uint32_t from;
+        if (!ToClampedIndex(cx, args.get(1), len, &from))
+            return false;
+
+        // Steps 12-14.
+        uint32_t final;
+        if (args.get(2).isUndefined()) {
+            final = len;
+        } else {
+            if (!ToClampedIndex(cx, args.get(2), len, &final))
+                return false;
+        }
+
+        // Steps 15-18.
+
+        // If |final - from < 0|, then |count| will be less than 0, so step 18
+        // never loops.  Exit early so |count| can use a non-negative type.
+        // Also exit early if elements are being moved to their pre-existing
+        // location.
+        if (final < from || to == from) {
+            args.rval().setObject(*obj);
+            return true;
+        }
+
+        uint32_t count = Min(final - from, len - to);
+        uint32_t lengthDuringMove = obj->length(); // beware ToClampedIndex
+
+        MOZ_ASSERT(to <= INT32_MAX, "size limited to 2**31");
+        MOZ_ASSERT(from <= INT32_MAX, "size limited to 2**31");
+        MOZ_ASSERT(count <= INT32_MAX, "size limited to 2**31");
+
+        if (from + count > lengthDuringMove || to + count > lengthDuringMove) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
+            return false;
+        }
+
+        const size_t ElementSize = sizeof(NativeType);
+        MOZ_ASSERT(to <= INT32_MAX / ElementSize, "overall byteLength capped at INT32_MAX");
+        MOZ_ASSERT(from <= INT32_MAX / ElementSize, "overall byteLength capped at INT32_MAX");
+        MOZ_ASSERT(count <= INT32_MAX / ElementSize, "overall byteLength capped at INT32_MAX");
+
+        uint32_t byteDest = to * ElementSize;
+        uint32_t byteSrc = from * ElementSize;
+        uint32_t byteSize = count * ElementSize;
+
+#ifdef DEBUG
+        uint32_t viewByteLength = obj->byteLength();
+        JS_ASSERT(byteDest <= viewByteLength);
+        JS_ASSERT(byteSrc <= viewByteLength);
+        JS_ASSERT(byteDest + byteSize <= viewByteLength);
+        JS_ASSERT(byteSrc + byteSize <= viewByteLength);
+
+        // Should not overflow because size is limited to 2^31
+        JS_ASSERT(byteDest + byteSize >= byteDest);
+        JS_ASSERT(byteSrc + byteSize >= byteSrc);
+#endif
+
+        uint8_t *data = static_cast<uint8_t*>(obj->viewData());
+        mozilla::PodMove(&data[byteDest], &data[byteSrc], byteSize);
+
+        // Step 19.
+        args.rval().set(args.thisv());
+        return true;
+    }
+};
+
+// The set() method on TypedArray and SharedTypedArray
+
+template<class Adapter>
+class TypedArraySetTemplate
+{
+    typedef typename Adapter::ElementType NativeType;
+    typedef typename Adapter::TypedArrayObjectType ArrayType;
+
+  public:
+    /* set(array[, offset]) */
+    static bool
+    fun_set_impl(JSContext *cx, CallArgs args)
+    {
+        MOZ_ASSERT(Adapter::ThisTypedArrayObjectType::IsThisClass(args.thisv()));
+
+        Rooted<ArrayType*> tarray(cx, &args.thisv().toObject().as<ArrayType>());
+
+        // first arg must be either a typed array or a JS array
+        if (args.length() == 0 || !args[0].isObject()) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
+            return false;
+        }
+
+        int32_t offset = 0;
+        if (args.length() > 1) {
+            if (!ToInt32(cx, args[1], &offset))
+                return false;
+
+            if (offset < 0 || uint32_t(offset) > tarray->length()) {
+                // the given offset is bogus
+                JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
+                                     JSMSG_TYPED_ARRAY_BAD_INDEX, "2");
+                return false;
+            }
+        }
+
+        if (!args[0].isObject()) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
+            return false;
+        }
+
+        RootedObject arg0(cx, args[0].toObjectOrNull());
+        if (IsAnyTypedArray(arg0.get())) {
+            if (AnyTypedArrayLength(arg0.get()) > tarray->length() - offset) {
+                JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
+                return false;
+            }
+
+            if (!copyFromTypedArray(cx, tarray, arg0, offset))
+                return false;
+        } else {
+            uint32_t len;
+            if (!GetLengthProperty(cx, arg0, &len))
+                return false;
+
+            if (uint32_t(offset) > tarray->length() || len > tarray->length() - offset) {
+                JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
+                return false;
+            }
+
+            if (!copyFromArray(cx, tarray, arg0, len, offset))
+                return false;
+        }
+
+        args.rval().setUndefined();
+        return true;
+    }
+
+    static bool
+    copyFromArray(JSContext *cx, HandleObject thisTypedArrayObj,
+                  HandleObject source, uint32_t len, uint32_t offset = 0)
+    {
+        Rooted<ArrayType*> thisTypedArray(cx, &thisTypedArrayObj->as<ArrayType>());
+        JS_ASSERT(offset <= thisTypedArray->length());
+        JS_ASSERT(len <= thisTypedArray->length() - offset);
+        if (source->is<TypedArrayObject>())
+            return copyFromTypedArray(cx, thisTypedArray, source, offset);
+
+        uint32_t i = 0;
+        if (source->isNative()) {
+            // Attempt fast-path infallible conversion of dense elements up to
+            // the first potentially side-effectful lookup or conversion.
+            uint32_t bound = Min(source->getDenseInitializedLength(), len);
+
+            NativeType *dest = static_cast<NativeType*>(thisTypedArray->viewData()) + offset;
+
+            const Value *srcValues = source->getDenseElements();
+            for (; i < bound; i++) {
+                // Note: holes don't convert infallibly.
+                if (!canConvertInfallibly(srcValues[i]))
+                    break;
+                dest[i] = infallibleValueToNative(srcValues[i]);
+            }
+            if (i == len)
+                return true;
+        }
+
+        // Convert and copy any remaining elements generically.
+        RootedValue v(cx);
+        for (; i < len; i++) {
+            if (!JSObject::getElement(cx, source, source, i, &v))
+                return false;
+
+            NativeType n;
+            if (!valueToNative(cx, v, &n))
+                return false;
+
+            len = Min(len, thisTypedArray->length());
+            if (i >= len)
+                break;
+
+            // Compute every iteration in case getElement acts wacky.
+            void *data = thisTypedArray->viewData();
+            static_cast<NativeType*>(data)[offset + i] = n;
+        }
+
+        return true;
+    }
+
+    static bool
+    copyFromTypedArray(JSContext *cx, JSObject *thisTypedArrayObj, JSObject *tarrayObj,
+                       uint32_t offset)
+    {
+        Rooted<ArrayType *> thisTypedArray(cx, &thisTypedArrayObj->as<ArrayType>());
+        Rooted<ArrayType *> tarray(cx, &tarrayObj->as<ArrayType>());
+        JS_ASSERT(offset <= thisTypedArray->length());
+        JS_ASSERT(tarray->length() <= thisTypedArray->length() - offset);
+
+        // Object equality is not good enough for SharedArrayBufferObject.
+        if (Adapter::sameBuffer(tarray->buffer(), thisTypedArray->buffer()))
+            return copyFromWithOverlap(cx, thisTypedArray, tarray, offset);
+
+        NativeType *dest = static_cast<NativeType*>(thisTypedArray->viewData()) + offset;
+
+        if (tarray->type() == thisTypedArray->type()) {
+            js_memcpy(dest, tarray->viewData(), tarray->byteLength());
+            return true;
+        }
+
+        unsigned srclen = tarray->length();
+        switch (tarray->type()) {
+          case Scalar::Int8: {
+            int8_t *src = static_cast<int8_t*>(tarray->viewData());
+            for (unsigned i = 0; i < srclen; ++i)
+                *dest++ = NativeType(*src++);
+            break;
+          }
+          case Scalar::Uint8:
+          case Scalar::Uint8Clamped: {
+            uint8_t *src = static_cast<uint8_t*>(tarray->viewData());
+            for (unsigned i = 0; i < srclen; ++i)
+                *dest++ = NativeType(*src++);
+            break;
+          }
+          case Scalar::Int16: {
+            int16_t *src = static_cast<int16_t*>(tarray->viewData());
+            for (unsigned i = 0; i < srclen; ++i)
+                *dest++ = NativeType(*src++);
+            break;
+          }
+          case Scalar::Uint16: {
+            uint16_t *src = static_cast<uint16_t*>(tarray->viewData());
+            for (unsigned i = 0; i < srclen; ++i)
+                *dest++ = NativeType(*src++);
+            break;
+          }
+          case Scalar::Int32: {
+            int32_t *src = static_cast<int32_t*>(tarray->viewData());
+            for (unsigned i = 0; i < srclen; ++i)
+                *dest++ = NativeType(*src++);
+            break;
+          }
+          case Scalar::Uint32: {
+            uint32_t *src = static_cast<uint32_t*>(tarray->viewData());
+            for (unsigned i = 0; i < srclen; ++i)
+                *dest++ = NativeType(*src++);
+            break;
+          }
+          case Scalar::Float32: {
+            float *src = static_cast<float*>(tarray->viewData());
+            for (unsigned i = 0; i < srclen; ++i)
+                *dest++ = NativeType(*src++);
+            break;
+          }
+          case Scalar::Float64: {
+            double *src = static_cast<double*>(tarray->viewData());
+            for (unsigned i = 0; i < srclen; ++i)
+                *dest++ = NativeType(*src++);
+            break;
+          }
+          default:
+            MOZ_CRASH("copyFrom with a TypedArrayObject of unknown type");
+        }
+
+        return true;
+    }
+
+    static bool
+    copyFromWithOverlap(JSContext *cx, JSObject *selfObj, JSObject *tarrayObj, uint32_t offset)
+    {
+        ArrayType *self = &selfObj->as<ArrayType>();
+        ArrayType *tarray = &tarrayObj->as<ArrayType>();
+
+        JS_ASSERT(offset <= self->length());
+
+        NativeType *dest = static_cast<NativeType*>(self->viewData()) + offset;
+        uint32_t byteLength = tarray->byteLength();
+
+        if (tarray->type() == self->type()) {
+            memmove(dest, tarray->viewData(), byteLength);
+            return true;
+        }
+
+        // We have to make a copy of the source array here, since
+        // there's overlap, and we have to convert types.
+        uint8_t *srcbuf = self->zone()->template pod_malloc<uint8_t>(byteLength);
+        if (!srcbuf)
+            return false;
+        js_memcpy(srcbuf, tarray->viewData(), byteLength);
+
+        uint32_t len = tarray->length();
+        switch (tarray->type()) {
+          case Scalar::Int8: {
+            int8_t *src = (int8_t*) srcbuf;
+            for (unsigned i = 0; i < len; ++i)
+                *dest++ = NativeType(*src++);
+            break;
+          }
+          case Scalar::Uint8:
+          case Scalar::Uint8Clamped: {
+            uint8_t *src = (uint8_t*) srcbuf;
+            for (unsigned i = 0; i < len; ++i)
+                *dest++ = NativeType(*src++);
+            break;
+          }
+          case Scalar::Int16: {
+            int16_t *src = (int16_t*) srcbuf;
+            for (unsigned i = 0; i < len; ++i)
+                *dest++ = NativeType(*src++);
+            break;
+          }
+          case Scalar::Uint16: {
+            uint16_t *src = (uint16_t*) srcbuf;
+            for (unsigned i = 0; i < len; ++i)
+                *dest++ = NativeType(*src++);
+            break;
+          }
+          case Scalar::Int32: {
+            int32_t *src = (int32_t*) srcbuf;
+            for (unsigned i = 0; i < len; ++i)
+                *dest++ = NativeType(*src++);
+            break;
+          }
+          case Scalar::Uint32: {
+            uint32_t *src = (uint32_t*) srcbuf;
+            for (unsigned i = 0; i < len; ++i)
+                *dest++ = NativeType(*src++);
+            break;
+          }
+          case Scalar::Float32: {
+            float *src = (float*) srcbuf;
+            for (unsigned i = 0; i < len; ++i)
+                *dest++ = NativeType(*src++);
+            break;
+          }
+          case Scalar::Float64: {
+            double *src = (double*) srcbuf;
+            for (unsigned i = 0; i < len; ++i)
+                *dest++ = NativeType(*src++);
+            break;
+          }
+          default:
+            MOZ_CRASH("copyFromWithOverlap with a TypedArrayObject of unknown type");
+        }
+
+        js_free(srcbuf);
+        return true;
+    }
+
+    static bool
+    canConvertInfallibly(const Value &v)
+    {
+        return v.isNumber() || v.isBoolean() || v.isNull() || v.isUndefined();
+    }
+
+    static NativeType
+    infallibleValueToNative(const Value &v)
+    {
+        if (v.isInt32())
+            return NativeType(v.toInt32());
+        if (v.isDouble())
+            return doubleToNative(v.toDouble());
+        if (v.isBoolean())
+            return NativeType(v.toBoolean());
+        if (v.isNull())
+            return NativeType(0);
+
+        MOZ_ASSERT(v.isUndefined());
+        return TypeIsFloatingPoint<NativeType>() ? NativeType(JS::GenericNaN()) : NativeType(0);
+    }
+
+    static bool
+    valueToNative(JSContext *cx, const Value &v, NativeType *result)
+    {
+        MOZ_ASSERT(!v.isMagic());
+
+        if (MOZ_LIKELY(canConvertInfallibly(v))) {
+            *result = infallibleValueToNative(v);
+            return true;
+        }
+
+        double d;
+        MOZ_ASSERT(v.isString() || v.isObject() || v.isSymbol());
+        if (!(v.isString() ? StringToNumber(cx, v.toString(), &d) : ToNumber(cx, v, &d)))
+            return false;
+
+        *result = doubleToNative(d);
+        return true;
+    }
+
+    static NativeType
+    doubleToNative(double d)
+    {
+        if (TypeIsFloatingPoint<NativeType>()) {
+#ifdef JS_MORE_DETERMINISTIC
+            // The JS spec doesn't distinguish among different NaN values, and
+            // it deliberately doesn't specify the bit pattern written to a
+            // typed array when NaN is written into it.  This bit-pattern
+            // inconsistency could confuse deterministic testing, so always
+            // canonicalize NaN values in more-deterministic builds.
+            d = JS::CanonicalizeNaN(d);
+#endif
+            return NativeType(d);
+        }
+        if (MOZ_UNLIKELY(mozilla::IsNaN(d)))
+            return NativeType(0);
+        if (TypeIsUnsigned<NativeType>())
+            return NativeType(ToUint32(d));
+        return NativeType(ToInt32(d));
+    }
+};
+
+} // namespace js
+
+#endif // vm_TypedArrayCommon_h
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -23,92 +23,85 @@
 #include "jsobj.h"
 #include "jstypes.h"
 #include "jsutil.h"
 #ifdef XP_WIN
 # include "jswin.h"
 #endif
 #include "jswrapper.h"
 
+#include "builtin/TypedObjectConstants.h"
 #include "gc/Barrier.h"
 #include "gc/Marking.h"
 #include "vm/ArrayBufferObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/NumericConversions.h"
-#include "vm/SharedArrayObject.h"
+#include "vm/TypedArrayCommon.h"
 #include "vm/WrapperObject.h"
 
 #include "jsatominlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 #include "vm/Shape-inl.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
-using mozilla::DebugOnly;
 using mozilla::IsNaN;
 using mozilla::NegativeInfinity;
 using mozilla::PodCopy;
 using mozilla::PositiveInfinity;
 using JS::CanonicalizeNaN;
 using JS::GenericNaN;
 
-static bool
-ValueIsLength(const Value &v, uint32_t *len)
-{
-    if (v.isInt32()) {
-        int32_t i = v.toInt32();
-        if (i < 0)
-            return false;
-        *len = i;
-        return true;
-    }
-
-    if (v.isDouble()) {
-        double d = v.toDouble();
-        if (IsNaN(d))
-            return false;
-
-        uint32_t length = uint32_t(d);
-        if (d != double(length))
-            return false;
-
-        *len = length;
-        return true;
-    }
-
-    return false;
-}
-
 /*
  * TypedArrayObject
  *
  * The non-templated base class for the specific typed implementations.
  * This class holds all the member variables that are used by
  * the subclasses.
  */
 
+TypedArrayLayout TypedArrayObject::layout_(false, // shared
+                                           true,  // neuterable
+                                           &TypedArrayObject::classes[0],
+                                           &TypedArrayObject::classes[Scalar::TypeMax]);
+
+TypedArrayLayout::TypedArrayLayout(bool isShared, bool isNeuterable, const Class *firstClass,
+                                   const Class *maxClass)
+    : isShared_(isShared)
+    , isNeuterable_(isNeuterable)
+    , firstClass_(firstClass)
+    , maxClass_(maxClass)
+{
+}
+
+/* static */ int
+TypedArrayLayout::lengthOffset()
+{
+    return JSObject::getFixedSlotOffset(LENGTH_SLOT);
+}
+
+/* static */ int
+TypedArrayLayout::dataOffset()
+{
+    return JSObject::getPrivateDataOffset(DATA_SLOT);
+}
+
 void
 TypedArrayObject::neuter(void *newData)
 {
     setSlot(LENGTH_SLOT, Int32Value(0));
     setSlot(BYTEOFFSET_SLOT, Int32Value(0));
     setPrivate(newData);
 }
 
-ArrayBufferObject *
-TypedArrayObject::sharedBuffer() const
-{
-    return &bufferValue(const_cast<TypedArrayObject*>(this)).toObject().as<SharedArrayBufferObject>();
-}
-
 /* static */ bool
 TypedArrayObject::ensureHasBuffer(JSContext *cx, Handle<TypedArrayObject *> tarray)
 {
     if (tarray->buffer())
         return true;
 
     Rooted<ArrayBufferObject *> buffer(cx, ArrayBufferObject::create(cx, tarray->byteLength()));
     if (!buffer)
@@ -118,28 +111,16 @@ TypedArrayObject::ensureHasBuffer(JSCont
 
     memcpy(buffer->dataPointer(), tarray->viewData(), tarray->byteLength());
     InitArrayBufferViewDataPointer(tarray, buffer, 0);
 
     tarray->setSlot(BUFFER_SLOT, ObjectValue(*buffer));
     return true;
 }
 
-/* static */ int
-TypedArrayObject::lengthOffset()
-{
-    return JSObject::getFixedSlotOffset(LENGTH_SLOT);
-}
-
-/* static */ int
-TypedArrayObject::dataOffset()
-{
-    return JSObject::getPrivateDataOffset(DATA_SLOT);
-}
-
 /* static */ void
 TypedArrayObject::ObjectMoved(JSObject *obj, const JSObject *old)
 {
     const TypedArrayObject &src = old->as<TypedArrayObject>();
     if (!src.hasBuffer()) {
         JS_ASSERT(old->getPrivate() == old->fixedData(FIXED_DATA_START));
         obj->setPrivate(obj->fixedData(FIXED_DATA_START));
     }
@@ -175,45 +156,58 @@ js::ClampDoubleToUint8(const double x)
          * want.
          */
         return y & ~1;
     }
 
     return y;
 }
 
-template<typename NativeType> static inline Scalar::Type TypeIDOfType();
-template<> inline Scalar::Type TypeIDOfType<int8_t>() { return Scalar::Int8; }
-template<> inline Scalar::Type TypeIDOfType<uint8_t>() { return Scalar::Uint8; }
-template<> inline Scalar::Type TypeIDOfType<int16_t>() { return Scalar::Int16; }
-template<> inline Scalar::Type TypeIDOfType<uint16_t>() { return Scalar::Uint16; }
-template<> inline Scalar::Type TypeIDOfType<int32_t>() { return Scalar::Int32; }
-template<> inline Scalar::Type TypeIDOfType<uint32_t>() { return Scalar::Uint32; }
-template<> inline Scalar::Type TypeIDOfType<float>() { return Scalar::Float32; }
-template<> inline Scalar::Type TypeIDOfType<double>() { return Scalar::Float64; }
-template<> inline Scalar::Type TypeIDOfType<uint8_clamped>() { return Scalar::Uint8Clamped; }
-
 template<typename ElementType>
 static inline JSObject *
 NewArray(JSContext *cx, uint32_t nelements);
 
 namespace {
 
+// Note, this template can probably be merged in part with the one in
+// SharedTypedArrayObject.cpp once our implementation of
+// TypedArrayObject is closer to ES6: at the moment, our
+// implementation does not process construction arguments in
+// standards-compliant ways, at least, and a larger rewrite around the
+// prototype hierarchy is also coming.
+
 template<typename NativeType>
 class TypedArrayObjectTemplate : public TypedArrayObject
 {
   public:
     typedef NativeType ThisType;
     typedef TypedArrayObjectTemplate<NativeType> ThisTypedArrayObject;
     static Scalar::Type ArrayTypeID() { return TypeIDOfType<NativeType>(); }
     static bool ArrayTypeIsUnsigned() { return TypeIsUnsigned<NativeType>(); }
     static bool ArrayTypeIsFloatingPoint() { return TypeIsFloatingPoint<NativeType>(); }
 
     static const size_t BYTES_PER_ELEMENT = sizeof(ThisType);
 
+    class TypedArrayObjectAdapter
+    {
+      public:
+        typedef TypedArrayObject TypedArrayObjectType;
+        typedef ArrayBufferObject ArrayBufferObjectType;
+        typedef NativeType ElementType;
+        typedef TypedArrayObjectTemplate<NativeType> ThisTypedArrayObjectType;
+
+        static bool ensureHasBuffer(JSContext *cx, Handle<TypedArrayObject *> tarray) {
+            return TypedArrayObject::ensureHasBuffer(cx, tarray);
+        }
+
+        static bool sameBuffer(ArrayBufferObject *a, ArrayBufferObject *b) {
+            return a == b;
+        }
+    };
+
     static inline const Class *protoClass()
     {
         return &TypedArrayObject::protoClasses[ArrayTypeID()];
     }
 
     static JSObject *CreatePrototype(JSContext *cx, JSProtoKey key)
     {
         return cx->global()->createBlankPrototype(cx, protoClass());
@@ -388,24 +382,25 @@ class TypedArrayObjectTemplate : public 
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
             return nullptr;
         }
 
         RootedObject dataObj(cx, &args.get(0).toObject());
 
         /*
          * (typedArray)
+         * (sharedTypedArray)
          * (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
+         * Note that offset and length will be ignored.  Note that a
+         * shared array's values are copied here.
          */
-        if (!UncheckedUnwrap(dataObj)->is<ArrayBufferObject>() &&
-            !UncheckedUnwrap(dataObj)->is<SharedArrayBufferObject>())
+        if (!UncheckedUnwrap(dataObj)->is<ArrayBufferObject>())
         {
             return fromArray(cx, dataObj);
         }
 
         /* (ArrayBuffer, [byteOffset, [length]]) */
         int32_t byteOffset = 0;
         int32_t length = -1;
 
@@ -491,211 +486,38 @@ class TypedArrayObjectTemplate : public 
         if (!getter)
             return false;
 
         return DefineNativeProperty(cx, proto, id, UndefinedHandleValue,
                                     JS_DATA_TO_FUNC_PTR(PropertyOp, getter), nullptr,
                                     attrs);
     }
 
-    /* subarray(start[, end]) */
-    static bool
-    fun_subarray_impl(JSContext *cx, CallArgs args)
-    {
-        JS_ASSERT(IsThisClass(args.thisv()));
-        Rooted<TypedArrayObject*> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>());
-
-        // these are the default values
-        uint32_t length = tarray->length();
-        uint32_t begin = 0, end = length;
-
-        if (args.length() > 0) {
-            if (!ToClampedIndex(cx, args[0], length, &begin))
-                return false;
-
-            if (args.length() > 1) {
-                if (!ToClampedIndex(cx, args[1], length, &end))
-                    return false;
-            }
-        }
-
-        if (begin > end)
-            begin = end;
-
-        JSObject *nobj = createSubarray(cx, tarray, begin, end);
-        if (!nobj)
-            return false;
-        args.rval().setObject(*nobj);
-        return true;
-    }
-
     static bool
     fun_subarray(JSContext *cx, unsigned argc, Value *vp)
     {
         CallArgs args = CallArgsFromVp(argc, vp);
         return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
-                                    ThisTypedArrayObject::fun_subarray_impl>(cx, args);
-    }
-
-    /* copyWithin(target, start[, end]) */
-    // ES6 draft rev 26, 22.2.3.5
-    static bool
-    fun_copyWithin_impl(JSContext *cx, CallArgs args)
-    {
-        MOZ_ASSERT(IsThisClass(args.thisv()));
-
-        // Steps 1-2.
-        Rooted<TypedArrayObject*> obj(cx, &args.thisv().toObject().as<TypedArrayObject>());
-
-        // Steps 3-4.
-        uint32_t len = obj->length();
-
-        // Steps 6-8.
-        uint32_t to;
-        if (!ToClampedIndex(cx, args.get(0), len, &to))
-            return false;
-
-        // Steps 9-11.
-        uint32_t from;
-        if (!ToClampedIndex(cx, args.get(1), len, &from))
-            return false;
-
-        // Steps 12-14.
-        uint32_t final;
-        if (args.get(2).isUndefined()) {
-            final = len;
-        } else {
-            if (!ToClampedIndex(cx, args.get(2), len, &final))
-                return false;
-        }
-
-        // Steps 15-18.
-
-        // If |final - from < 0|, then |count| will be less than 0, so step 18
-        // never loops.  Exit early so |count| can use a non-negative type.
-        // Also exit early if elements are being moved to their pre-existing
-        // location.
-        if (final < from || to == from) {
-            args.rval().setObject(*obj);
-            return true;
-        }
-
-        uint32_t count = Min(final - from, len - to);
-        uint32_t lengthDuringMove = obj->length(); // beware ToClampedIndex
-
-        MOZ_ASSERT(to <= INT32_MAX, "size limited to 2**31");
-        MOZ_ASSERT(from <= INT32_MAX, "size limited to 2**31");
-        MOZ_ASSERT(count <= INT32_MAX, "size limited to 2**31");
-
-        if (from + count > lengthDuringMove || to + count > lengthDuringMove) {
-            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
-            return false;
-        }
-
-        DebugOnly<const size_t> ElementSize = sizeof(NativeType);
-        MOZ_ASSERT(to <= INT32_MAX / ElementSize, "overall byteLength capped at INT32_MAX");
-        MOZ_ASSERT(from <= INT32_MAX / ElementSize, "overall byteLength capped at INT32_MAX");
-        MOZ_ASSERT(count <= INT32_MAX / ElementSize, "overall byteLength capped at INT32_MAX");
-
-        uint32_t byteDest = to * sizeof(NativeType);
-        uint32_t byteSrc = from * sizeof(NativeType);
-        uint32_t byteSize = count * sizeof(NativeType);
-
-#ifdef DEBUG
-        uint32_t viewByteLength = obj->byteLength();
-        JS_ASSERT(byteDest <= viewByteLength);
-        JS_ASSERT(byteSrc <= viewByteLength);
-        JS_ASSERT(byteDest + byteSize <= viewByteLength);
-        JS_ASSERT(byteSrc + byteSize <= viewByteLength);
-
-        // Should not overflow because size is limited to 2^31
-        JS_ASSERT(byteDest + byteSize >= byteDest);
-        JS_ASSERT(byteSrc + byteSize >= byteSrc);
-#endif
-
-        uint8_t *data = static_cast<uint8_t*>(obj->viewData());
-        mozilla::PodMove(&data[byteDest], &data[byteSrc], byteSize);
-
-        // Step 19.
-        args.rval().set(args.thisv());
-        return true;
+                                    TypedArraySubarrayTemplate<TypedArrayObjectAdapter>::fun_subarray_impl>(cx, args);
     }
 
     static bool
     fun_copyWithin(JSContext *cx, unsigned argc, Value *vp)
     {
         CallArgs args = CallArgsFromVp(argc, vp);
         return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
-                                    ThisTypedArrayObject::fun_copyWithin_impl>(cx, args);
-    }
-
-    /* set(array[, offset]) */
-    static bool
-    fun_set_impl(JSContext *cx, CallArgs args)
-    {
-        JS_ASSERT(IsThisClass(args.thisv()));
-        Rooted<TypedArrayObject*> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>());
-
-        // first arg must be either a typed array or a JS array
-        if (args.length() == 0 || !args[0].isObject()) {
-            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
-            return false;
-        }
-
-        int32_t offset = 0;
-        if (args.length() > 1) {
-            if (!ToInt32(cx, args[1], &offset))
-                return false;
-
-            if (offset < 0 || uint32_t(offset) > tarray->length()) {
-                // the given offset is bogus
-                JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
-                                     JSMSG_TYPED_ARRAY_BAD_INDEX, "2");
-                return false;
-            }
-        }
-
-        if (!args[0].isObject()) {
-            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
-            return false;
-        }
-
-        RootedObject arg0(cx, args[0].toObjectOrNull());
-        if (arg0->is<TypedArrayObject>()) {
-            if (arg0->as<TypedArrayObject>().length() > tarray->length() - offset) {
-                JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
-                return false;
-            }
-
-            if (!copyFromTypedArray(cx, tarray, arg0, offset))
-                return false;
-        } else {
-            uint32_t len;
-            if (!GetLengthProperty(cx, arg0, &len))
-                return false;
-
-            if (uint32_t(offset) > tarray->length() || len > tarray->length() - offset) {
-                JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
-                return false;
-            }
-
-            if (!copyFromArray(cx, tarray, arg0, len, offset))
-                return false;
-        }
-
-        args.rval().setUndefined();
-        return true;
+                                    TypedArrayCopyWithinTemplate<TypedArrayObjectAdapter>::fun_copyWithin_impl>(cx, args);
     }
 
     static bool
     fun_set(JSContext *cx, unsigned argc, Value *vp)
     {
         CallArgs args = CallArgsFromVp(argc, vp);
         return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
-                                    ThisTypedArrayObject::fun_set_impl>(cx, args);
+                                    TypedArraySetTemplate<TypedArrayObjectAdapter>::fun_set_impl>(cx, args);
     }
 
   public:
     static JSObject *
     fromBuffer(JSContext *cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt) {
         RootedObject proto(cx, nullptr);
         return fromBufferWithProto(cx, bufobj, byteOffset, lengthInt, proto);
     }
@@ -842,17 +664,17 @@ class TypedArrayObjectTemplate : public 
             return nullptr;
         }
 
         Rooted<ArrayBufferObject *> buffer(cx);
         if (!maybeCreateArrayBuffer(cx, len, &buffer))
             return nullptr;
 
         RootedObject obj(cx, makeInstance(cx, buffer, 0, len));
-        if (!obj || !copyFromArray(cx, obj, other, len))
+        if (!obj || !TypedArraySetTemplate<TypedArrayObjectAdapter>::copyFromArray(cx, obj, other, len))
             return nullptr;
         return obj;
     }
 
     static const NativeType
     getIndex(JSObject *obj, uint32_t index)
     {
         TypedArrayObject &tarray = obj->as<TypedArrayObject>();
@@ -863,314 +685,16 @@ class TypedArrayObjectTemplate : public 
     static void
     setIndex(TypedArrayObject &tarray, uint32_t index, NativeType val)
     {
         MOZ_ASSERT(index < tarray.length());
         static_cast<NativeType*>(tarray.viewData())[index] = val;
     }
 
     static Value getIndexValue(JSObject *tarray, uint32_t index);
-
-    static JSObject *
-    createSubarray(JSContext *cx, HandleObject tarrayArg, uint32_t begin, uint32_t end)
-    {
-        Rooted<TypedArrayObject*> tarray(cx, &tarrayArg->as<TypedArrayObject>());
-
-        if (begin > tarray->length() || end > tarray->length() || begin > end) {
-            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
-            return nullptr;
-        }
-
-        if (!ensureHasBuffer(cx, tarray))
-            return nullptr;
-
-        Rooted<ArrayBufferObject *> bufobj(cx, tarray->buffer());
-        JS_ASSERT(bufobj);
-
-        uint32_t length = end - begin;
-
-        JS_ASSERT(begin < UINT32_MAX / sizeof(NativeType));
-        uint32_t arrayByteOffset = tarray->byteOffset();
-        JS_ASSERT(UINT32_MAX - begin * sizeof(NativeType) >= arrayByteOffset);
-        uint32_t byteOffset = arrayByteOffset + begin * sizeof(NativeType);
-
-        return makeInstance(cx, bufobj, byteOffset, length);
-    }
-
-  protected:
-    static NativeType
-    doubleToNative(double d)
-    {
-        if (TypeIsFloatingPoint<NativeType>()) {
-#ifdef JS_MORE_DETERMINISTIC
-            // The JS spec doesn't distinguish among different NaN values, and
-            // it deliberately doesn't specify the bit pattern written to a
-            // typed array when NaN is written into it.  This bit-pattern
-            // inconsistency could confuse deterministic testing, so always
-            // canonicalize NaN values in more-deterministic builds.
-            d = CanonicalizeNaN(d);
-#endif
-            return NativeType(d);
-        }
-        if (MOZ_UNLIKELY(IsNaN(d)))
-            return NativeType(0);
-        if (TypeIsUnsigned<NativeType>())
-            return NativeType(ToUint32(d));
-        return NativeType(ToInt32(d));
-    }
-
-    static bool
-    canConvertInfallibly(const Value &v)
-    {
-        return v.isNumber() || v.isBoolean() || v.isNull() || v.isUndefined();
-    }
-
-    static NativeType
-    infallibleValueToNative(const Value &v)
-    {
-        if (v.isInt32())
-            return NativeType(v.toInt32());
-        if (v.isDouble())
-            return doubleToNative(v.toDouble());
-        if (v.isBoolean())
-            return NativeType(v.toBoolean());
-        if (v.isNull())
-            return NativeType(0);
-
-        MOZ_ASSERT(v.isUndefined());
-        return ArrayTypeIsFloatingPoint() ? NativeType(GenericNaN()) : NativeType(0);
-    }
-
-    static bool
-    valueToNative(JSContext *cx, const Value &v, NativeType *result)
-    {
-        MOZ_ASSERT(!v.isMagic());
-
-        if (MOZ_LIKELY(canConvertInfallibly(v))) {
-            *result = infallibleValueToNative(v);
-            return true;
-        }
-
-        double d;
-        MOZ_ASSERT(v.isString() || v.isObject() || v.isSymbol());
-        if (!(v.isString() ? StringToNumber(cx, v.toString(), &d) : ToNumber(cx, v, &d)))
-            return false;
-
-        *result = doubleToNative(d);
-        return true;
-    }
-
-    static bool
-    copyFromArray(JSContext *cx, HandleObject thisTypedArrayObj,
-                  HandleObject source, uint32_t len, uint32_t offset = 0)
-    {
-        Rooted<TypedArrayObject*> thisTypedArray(cx, &thisTypedArrayObj->as<TypedArrayObject>());
-        JS_ASSERT(offset <= thisTypedArray->length());
-        JS_ASSERT(len <= thisTypedArray->length() - offset);
-        if (source->is<TypedArrayObject>())
-            return copyFromTypedArray(cx, thisTypedArray, source, offset);
-
-        uint32_t i = 0;
-        if (source->isNative()) {
-            // Attempt fast-path infallible conversion of dense elements up to
-            // the first potentially side-effectful lookup or conversion.
-            uint32_t bound = Min(source->getDenseInitializedLength(), len);
-
-            NativeType *dest = static_cast<NativeType*>(thisTypedArray->viewData()) + offset;
-
-            const Value *srcValues = source->getDenseElements();
-            for (; i < bound; i++) {
-                // Note: holes don't convert infallibly.
-                if (!canConvertInfallibly(srcValues[i]))
-                    break;
-                dest[i] = infallibleValueToNative(srcValues[i]);
-            }
-            if (i == len)
-                return true;
-        }
-
-        // Convert and copy any remaining elements generically.
-        RootedValue v(cx);
-        for (; i < len; i++) {
-            if (!JSObject::getElement(cx, source, source, i, &v))
-                return false;
-
-            NativeType n;
-            if (!valueToNative(cx, v, &n))
-                return false;
-
-            len = Min(len, thisTypedArray->length());
-            if (i >= len)
-                break;
-
-            // Compute every iteration in case getElement acts wacky.
-            void *data = thisTypedArray->viewData();
-            static_cast<NativeType*>(data)[offset + i] = n;
-        }
-
-        return true;
-    }
-
-    static bool
-    copyFromTypedArray(JSContext *cx, JSObject *thisTypedArrayObj, JSObject *tarrayObj,
-                       uint32_t offset)
-    {
-        TypedArrayObject *thisTypedArray = &thisTypedArrayObj->as<TypedArrayObject>();
-        TypedArrayObject *tarray = &tarrayObj->as<TypedArrayObject>();
-        JS_ASSERT(offset <= thisTypedArray->length());
-        JS_ASSERT(tarray->length() <= thisTypedArray->length() - offset);
-        if (tarray->buffer() == thisTypedArray->buffer())
-            return copyFromWithOverlap(cx, thisTypedArray, tarray, offset);
-
-        NativeType *dest = static_cast<NativeType*>(thisTypedArray->viewData()) + offset;
-
-        if (tarray->type() == thisTypedArray->type()) {
-            js_memcpy(dest, tarray->viewData(), tarray->byteLength());
-            return true;
-        }
-
-        unsigned srclen = tarray->length();
-        switch (tarray->type()) {
-          case Scalar::Int8: {
-            int8_t *src = static_cast<int8_t*>(tarray->viewData());
-            for (unsigned i = 0; i < srclen; ++i)
-                *dest++ = NativeType(*src++);
-            break;
-          }
-          case Scalar::Uint8:
-          case Scalar::Uint8Clamped: {
-            uint8_t *src = static_cast<uint8_t*>(tarray->viewData());
-            for (unsigned i = 0; i < srclen; ++i)
-                *dest++ = NativeType(*src++);
-            break;
-          }
-          case Scalar::Int16: {
-            int16_t *src = static_cast<int16_t*>(tarray->viewData());
-            for (unsigned i = 0; i < srclen; ++i)
-                *dest++ = NativeType(*src++);
-            break;
-          }
-          case Scalar::Uint16: {
-            uint16_t *src = static_cast<uint16_t*>(tarray->viewData());
-            for (unsigned i = 0; i < srclen; ++i)
-                *dest++ = NativeType(*src++);
-            break;
-          }
-          case Scalar::Int32: {
-            int32_t *src = static_cast<int32_t*>(tarray->viewData());
-            for (unsigned i = 0; i < srclen; ++i)
-                *dest++ = NativeType(*src++);
-            break;
-          }
-          case Scalar::Uint32: {
-            uint32_t *src = static_cast<uint32_t*>(tarray->viewData());
-            for (unsigned i = 0; i < srclen; ++i)
-                *dest++ = NativeType(*src++);
-            break;
-          }
-          case Scalar::Float32: {
-            float *src = static_cast<float*>(tarray->viewData());
-            for (unsigned i = 0; i < srclen; ++i)
-                *dest++ = NativeType(*src++);
-            break;
-          }
-          case Scalar::Float64: {
-            double *src = static_cast<double*>(tarray->viewData());
-            for (unsigned i = 0; i < srclen; ++i)
-                *dest++ = NativeType(*src++);
-            break;
-          }
-          default:
-            MOZ_CRASH("copyFrom with a TypedArrayObject of unknown type");
-        }
-
-        return true;
-    }
-
-    static bool
-    copyFromWithOverlap(JSContext *cx, JSObject *selfObj, JSObject *tarrayObj, uint32_t offset)
-    {
-        TypedArrayObject *self = &selfObj->as<TypedArrayObject>();
-        TypedArrayObject *tarray = &tarrayObj->as<TypedArrayObject>();
-
-        JS_ASSERT(offset <= self->length());
-
-        NativeType *dest = static_cast<NativeType*>(self->viewData()) + offset;
-        uint32_t byteLength = tarray->byteLength();
-
-        if (tarray->type() == self->type()) {
-            memmove(dest, tarray->viewData(), byteLength);
-            return true;
-        }
-
-        // We have to make a copy of the source array here, since
-        // there's overlap, and we have to convert types.
-        uint8_t *srcbuf = self->zone()->pod_malloc<uint8_t>(byteLength);
-        if (!srcbuf)
-            return false;
-        js_memcpy(srcbuf, tarray->viewData(), byteLength);
-
-        uint32_t len = tarray->length();
-        switch (tarray->type()) {
-          case Scalar::Int8: {
-            int8_t *src = (int8_t*) srcbuf;
-            for (unsigned i = 0; i < len; ++i)
-                *dest++ = NativeType(*src++);
-            break;
-          }
-          case Scalar::Uint8:
-          case Scalar::Uint8Clamped: {
-            uint8_t *src = (uint8_t*) srcbuf;
-            for (unsigned i = 0; i < len; ++i)
-                *dest++ = NativeType(*src++);
-            break;
-          }
-          case Scalar::Int16: {
-            int16_t *src = (int16_t*) srcbuf;
-            for (unsigned i = 0; i < len; ++i)
-                *dest++ = NativeType(*src++);
-            break;
-          }
-          case Scalar::Uint16: {
-            uint16_t *src = (uint16_t*) srcbuf;
-            for (unsigned i = 0; i < len; ++i)
-                *dest++ = NativeType(*src++);
-            break;
-          }
-          case Scalar::Int32: {
-            int32_t *src = (int32_t*) srcbuf;
-            for (unsigned i = 0; i < len; ++i)
-                *dest++ = NativeType(*src++);
-            break;
-          }
-          case Scalar::Uint32: {
-            uint32_t *src = (uint32_t*) srcbuf;
-            for (unsigned i = 0; i < len; ++i)
-                *dest++ = NativeType(*src++);
-            break;
-          }
-          case Scalar::Float32: {
-            float *src = (float*) srcbuf;
-            for (unsigned i = 0; i < len; ++i)
-                *dest++ = NativeType(*src++);
-            break;
-          }
-          case Scalar::Float64: {
-            double *src = (double*) srcbuf;
-            for (unsigned i = 0; i < len; ++i)
-                *dest++ = NativeType(*src++);
-            break;
-          }
-          default:
-            MOZ_CRASH("copyFromWithOverlap with a TypedArrayObject of unknown type");
-        }
-
-        js_free(srcbuf);
-        return true;
-    }
 };
 
 class Int8ArrayObject : public TypedArrayObjectTemplate<int8_t> {
   public:
     enum { ACTUAL_TYPE = Scalar::Int8 };
     static const JSFunctionSpec jsfuncs[];
     static const JSPropertySpec jsprops[];
 };
@@ -2547,33 +2071,16 @@ js::IsTypedArrayConstructor(HandleValue 
       case Scalar::Float64:
         return IsNativeFunction(v, Float64ArrayObject::class_constructor);
       case Scalar::Uint8Clamped:
         return IsNativeFunction(v, Uint8ClampedArrayObject::class_constructor);
     }
     MOZ_CRASH("unexpected typed array type");
 }
 
-bool
-js::IsTypedArrayBuffer(HandleValue v)
-{
-    return v.isObject() &&
-           (v.toObject().is<ArrayBufferObject>() ||
-            v.toObject().is<SharedArrayBufferObject>());
-}
-
-ArrayBufferObject &
-js::AsTypedArrayBuffer(HandleValue v)
-{
-    JS_ASSERT(IsTypedArrayBuffer(v));
-    if (v.toObject().is<ArrayBufferObject>())
-        return v.toObject().as<ArrayBufferObject>();
-    return v.toObject().as<SharedArrayBufferObject>();
-}
-
 template <typename CharT>
 bool
 js::StringIsTypedArrayIndex(const CharT *s, size_t length, uint64_t *indexp)
 {
     const CharT *end = s + length;
 
     if (s == end)
         return false;
--- a/js/src/vm/TypedArrayObject.h
+++ b/js/src/vm/TypedArrayObject.h
@@ -20,24 +20,74 @@ namespace js {
 /*
  * TypedArrayObject
  *
  * The non-templated base class for the specific typed implementations.
  * This class holds all the member variables that are used by
  * the subclasses.
  */
 
+/*
+ * TypedArrayObject and SharedTypedArrayObject are unrelated types in
+ * both C++ and JS, and that is deliberate to avoid one substituting
+ * for the other.  However, they share a fixed representation and have
+ * some variable attributes, all of which are encapsulated in the
+ * TypedArrayLayout class.  The sharing avoids a lot of pointless
+ * duplication in the JITs: one code path can be used, with occasional
+ * decision points based on the attributes.
+ */
+
+class TypedArrayLayout
+{
+    const bool isShared_;
+    const bool isNeuterable_;
+    const Class *firstClass_;
+    const Class *maxClass_;
+
+  public:
+    TypedArrayLayout(bool isShared, bool isNeuterable, const Class *firstClass, const Class *maxClass);
+
+    // Slot containing length of the view in number of typed elements.
+    static const size_t LENGTH_SLOT = JS_BUFVIEW_SLOT_LENGTH;
+
+    // The type value.
+    static const size_t TYPE_SLOT = JS_TYPEDARR_SLOT_TYPE;
+
+    // Underlying (Shared)ArrayBufferObject.
+    static const size_t BUFFER_SLOT = JS_BUFVIEW_SLOT_OWNER;
+
+    // Offset of view within underlying (Shared)ArrayBufferObject.
+    static const size_t BYTEOFFSET_SLOT = JS_BUFVIEW_SLOT_BYTEOFFSET;
+
+    static const size_t RESERVED_SLOTS = JS_TYPEDARR_SLOTS;
+
+    // The raw pointer to the buffer memory, the "private" value.
+    //
+    // This offset is exposed for performance reasons - so that it
+    // need not be looked up on accesses.
+    static const size_t DATA_SLOT = JS_TYPEDARR_SLOT_DATA;
+
+    static int lengthOffset();
+    static int dataOffset();
+
+    bool isSharedMemory() const { return isShared_; }
+    bool isNeuterable() const { return isNeuterable_; }
+    const Class *addressOfFirstClass() const { return firstClass_; }
+    const Class *addressOfMaxClass() const { return maxClass_; }
+};
+
 class TypedArrayObject : public ArrayBufferViewObject
 {
   protected:
     // Typed array properties stored in slots, beyond those shared by all
     // ArrayBufferViews.
-    static const size_t TYPE_SLOT      = JS_TYPEDARR_SLOT_TYPE;
-    static const size_t RESERVED_SLOTS = JS_TYPEDARR_SLOTS;
-    static const size_t DATA_SLOT      = JS_TYPEDARR_SLOT_DATA;
+    static const size_t TYPE_SLOT      = TypedArrayLayout::TYPE_SLOT;
+    static const size_t DATA_SLOT      = TypedArrayLayout::DATA_SLOT;
+
+    static const size_t RESERVED_SLOTS = TypedArrayLayout::RESERVED_SLOTS;
 
     static_assert(js::detail::TypedArrayLengthSlot == LENGTH_SLOT,
                   "bad inlined constant in jsfriendapi.h");
 
   public:
     static const Class classes[Scalar::TypeMax];
     static const Class protoClasses[Scalar::TypeMax];
 
@@ -74,27 +124,24 @@ class TypedArrayObject : public ArrayBuf
     }
     static Value lengthValue(TypedArrayObject *tarr) {
         return tarr->getFixedSlot(LENGTH_SLOT);
     }
 
     static bool
     ensureHasBuffer(JSContext *cx, Handle<TypedArrayObject *> tarray);
 
-    ArrayBufferObject *sharedBuffer() const;
     bool hasBuffer() const {
         return bufferValue(const_cast<TypedArrayObject*>(this)).isObject();
     }
     ArrayBufferObject *buffer() const {
         JSObject *obj = bufferValue(const_cast<TypedArrayObject*>(this)).toObjectOrNull();
         if (!obj)
             return nullptr;
-        if (obj->is<ArrayBufferObject>())
-            return &obj->as<ArrayBufferObject>();
-        return sharedBuffer();
+        return &obj->as<ArrayBufferObject>();
     }
     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 {
@@ -116,20 +163,25 @@ class TypedArrayObject : public ArrayBuf
     }
 
     /*
      * Byte length above which created typed arrays and data views will have
      * singleton types regardless of the context in which they are created.
      */
     static const uint32_t SINGLETON_TYPE_BYTE_LENGTH = 1024 * 1024 * 10;
 
-    static int lengthOffset();
-    static int dataOffset();
+    static bool isOriginalLengthGetter(Scalar::Type type, Native native);
+
+  private:
+    static TypedArrayLayout layout_;
 
-    static bool isOriginalLengthGetter(Scalar::Type type, Native native);
+  public:
+    static const TypedArrayLayout &layout() {
+        return layout_;
+    }
 
     static void ObjectMoved(JSObject *obj, const JSObject *old);
 };
 
 inline bool
 IsTypedArrayClass(const Class *clasp)
 {
     return &TypedArrayObject::classes[0] <= clasp &&
@@ -141,22 +193,16 @@ IsTypedArrayProtoClass(const Class *clas
 {
     return &TypedArrayObject::protoClasses[0] <= clasp &&
            clasp < &TypedArrayObject::protoClasses[Scalar::TypeMax];
 }
 
 bool
 IsTypedArrayConstructor(HandleValue v, uint32_t type);
 
-bool
-IsTypedArrayBuffer(HandleValue v);
-
-ArrayBufferObject &
-AsTypedArrayBuffer(HandleValue v);
-
 // Return value is whether the string is some integer. If the string is an
 // integer which is not representable as a uint64_t, the return value is true
 // and the resulting index is UINT64_MAX.
 template <typename CharT>
 bool
 StringIsTypedArrayIndex(const CharT *s, size_t length, uint64_t *indexp);
 
 inline bool