Merge inbound to m-c
authorWes Kocher <wkocher@mozilla.com>
Thu, 20 Feb 2014 19:19:18 -0800
changeset 170167 7010ab83a06ebfa3202088bf0609eb0e01e43dc7
parent 170166 3328dfb0d932f828a081375e8ecfd8405c342f58 (current diff)
parent 170099 2ea5f9a5272ebd6c083fa2935c95dc483e925aa7 (diff)
child 170168 71e4a149726515b4bd238c65aeaa59e268412313
child 170202 ff25ef3f16b0660706708c7dc116cd08573d58f5
child 170229 e6137d480a176dfb2e61e1f902184b9a5911c9c5
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
milestone30.0a1
Merge inbound to m-c
--- a/content/media/encoder/OpusTrackEncoder.cpp
+++ b/content/media/encoder/OpusTrackEncoder.cpp
@@ -34,17 +34,17 @@ static const int MAX_DATA_BYTES = 4096;
 // of PCM audio samples at a fixed rate of 48 kHz."
 static const int kOpusSamplingRate = 48000;
 
 // The duration of an Opus frame, and it must be 2.5, 5, 10, 20, 40 or 60 ms.
 static const int kFrameDurationMs  = 20;
 
 // The supported sampling rate of input signal (Hz),
 // must be one of the following. Will resampled to 48kHz otherwise.
-static const int kOpusSupportedInputSamplingRates[5] =
+static const int kOpusSupportedInputSamplingRates[] =
                    {8000, 12000, 16000, 24000, 48000};
 
 namespace {
 
 // An endian-neutral serialization of integers. Serializing T in little endian
 // format to aOutput, where T is a 16 bits or 32 bits integer.
 template<typename T>
 static void
@@ -65,20 +65,20 @@ SerializeToBuffer(const nsCString& aComm
 }
 
 
 static void
 SerializeOpusIdHeader(uint8_t aChannelCount, uint16_t aPreskip,
                       uint32_t aInputSampleRate, nsTArray<uint8_t>* aOutput)
 {
   // The magic signature, null terminator has to be stripped off from strings.
-  static const uint8_t magic[9] = "OpusHead";
-  memcpy(aOutput->AppendElements(sizeof(magic) - 1), magic, sizeof(magic) - 1);
+  static const uint8_t magic[] = "OpusHead";
+  aOutput->AppendElements(magic, sizeof(magic) - 1);
 
-  // The version, must always be 1 (8 bits, unsigned).
+  // The version must always be 1 (8 bits, unsigned).
   aOutput->AppendElement(1);
 
   // Number of output channels (8 bits, unsigned).
   aOutput->AppendElement(aChannelCount);
 
   // Number of samples (at 48 kHz) to discard from the decoder output when
   // starting playback (16 bits, unsigned, little endian).
   SerializeToBuffer(aPreskip, aOutput);
@@ -96,18 +96,18 @@ SerializeOpusIdHeader(uint8_t aChannelCo
 }
 
 static void
 SerializeOpusCommentHeader(const nsCString& aVendor,
                            const nsTArray<nsCString>& aComments,
                            nsTArray<uint8_t>* aOutput)
 {
   // The magic signature, null terminator has to be stripped off.
-  static const uint8_t magic[9] = "OpusTags";
-  memcpy(aOutput->AppendElements(sizeof(magic) - 1), magic, sizeof(magic) - 1);
+  static const uint8_t magic[] = "OpusTags";
+  aOutput->AppendElements(magic, sizeof(magic) - 1);
 
   // The vendor; Should append in the following order:
   // vendor string length (32 bits, unsigned, little endian)
   // vendor string.
   SerializeToBuffer(aVendor, aOutput);
 
   // Add comments; Should append in the following order:
   // comment list length (32 bits, unsigned, little endian)
@@ -157,17 +157,17 @@ OpusTrackEncoder::Init(int aChannels, in
   // let InterleaveTrackData downmix pcm data.
   mChannels = aChannels > MAX_CHANNELS ? MAX_CHANNELS : aChannels;
 
   // According to www.opus-codec.org, creating an opus encoder requires the
   // sampling rate of source signal be one of 8000, 12000, 16000, 24000, or
   // 48000. If this constraint is not satisfied, we resample the input to 48kHz.
   nsTArray<int> supportedSamplingRates;
   supportedSamplingRates.AppendElements(kOpusSupportedInputSamplingRates,
-                         MOZ_ARRAY_LENGTH(kOpusSupportedInputSamplingRates));
+                         ArrayLength(kOpusSupportedInputSamplingRates));
   if (!supportedSamplingRates.Contains(aSamplingRate)) {
     int error;
     mResampler = speex_resampler_init(mChannels,
                                       aSamplingRate,
                                       kOpusSamplingRate,
                                       SPEEX_RESAMPLER_QUALITY_DEFAULT,
                                       &error);
 
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -34,66 +34,66 @@ extern const JSFunctionSpec Int32x4Metho
 
 ///////////////////////////////////////////////////////////////////////////
 // X4
 
 #define LANE_ACCESSOR(Type32x4, lane) \
     bool Type32x4##Lane##lane(JSContext *cx, unsigned argc, Value *vp) { \
         static const char *laneNames[] = {"lane 0", "lane 1", "lane 2", "lane3"}; \
         CallArgs args = CallArgsFromVp(argc, vp); \
-        if(!args.thisv().isObject() || !args.thisv().toObject().is<TypedDatum>()) {        \
+        if(!args.thisv().isObject() || !args.thisv().toObject().is<TypedObject>()) {        \
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, \
                                  X4TypeDescr::class_.name, laneNames[lane], \
                                  InformalValueTypeName(args.thisv())); \
             return false; \
         } \
-        TypedDatum &datum = args.thisv().toObject().as<TypedDatum>();        \
-        TypeDescr &descr = datum.typeDescr(); \
+        TypedObject &typedObj = args.thisv().toObject().as<TypedObject>(); \
+        TypeDescr &descr = typedObj.typeDescr(); \
         if (descr.kind() != TypeDescr::X4 || \
             descr.as<X4TypeDescr>().type() != Type32x4::type) \
         {  \
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, \
                                  X4TypeDescr::class_.name, laneNames[lane], \
                                  InformalValueTypeName(args.thisv())); \
             return false; \
         } \
-        Type32x4::Elem *data = reinterpret_cast<Type32x4::Elem *>(datum.typedMem()); \
+        Type32x4::Elem *data = reinterpret_cast<Type32x4::Elem *>(typedObj.typedMem()); \
         Type32x4::setReturn(args, data[lane]); \
         return true; \
     }
     LANE_ACCESSOR(Float32x4, 0);
     LANE_ACCESSOR(Float32x4, 1);
     LANE_ACCESSOR(Float32x4, 2);
     LANE_ACCESSOR(Float32x4, 3);
     LANE_ACCESSOR(Int32x4, 0);
     LANE_ACCESSOR(Int32x4, 1);
     LANE_ACCESSOR(Int32x4, 2);
     LANE_ACCESSOR(Int32x4, 3);
 #undef LANE_ACCESSOR
 
 #define SIGN_MASK(Type32x4) \
     bool Type32x4##SignMask(JSContext *cx, unsigned argc, Value *vp) { \
         CallArgs args = CallArgsFromVp(argc, vp); \
-        if(!args.thisv().isObject() || !args.thisv().toObject().is<TypedDatum>()) {        \
+        if(!args.thisv().isObject() || !args.thisv().toObject().is<TypedObject>()) {        \
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, \
                                  X4TypeDescr::class_.name, "signMask", \
                                  InformalValueTypeName(args.thisv())); \
             return false; \
         } \
-        TypedDatum &datum = args.thisv().toObject().as<TypedDatum>();        \
-        TypeDescr &descr = datum.typeDescr(); \
+        TypedObject &typedObj = args.thisv().toObject().as<TypedObject>(); \
+        TypeDescr &descr = typedObj.typeDescr(); \
         if (descr.kind() != TypeDescr::X4 || \
             descr.as<X4TypeDescr>().type() != Type32x4::type) \
         { \
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, \
                                  X4TypeDescr::class_.name, "signMask", \
                                  InformalValueTypeName(args.thisv())); \
             return false; \
         } \
-        Type32x4::Elem *data = reinterpret_cast<Type32x4::Elem *>(datum.typedMem()); \
+        Type32x4::Elem *data = reinterpret_cast<Type32x4::Elem *>(typedObj.typedMem()); \
         int32_t mx = data[0] < 0.0 ? 1 : 0; \
         int32_t my = data[1] < 0.0 ? 1 : 0; \
         int32_t mz = data[2] < 0.0 ? 1 : 0; \
         int32_t mw = data[3] < 0.0 ? 1 : 0; \
         int32_t result = mx | my << 1 | mz << 2 | mw << 3; \
         args.rval().setInt32(result); \
         return true; \
     }
@@ -119,66 +119,66 @@ const Class X4TypeDescr::class_ = {
 };
 
 // These classes just exist to group together various properties and so on.
 namespace js {
 class Int32x4Defn {
   public:
     static const X4TypeDescr::Type type = X4TypeDescr::TYPE_INT32;
     static const JSFunctionSpec TypeDescriptorMethods[];
-    static const JSPropertySpec TypedDatumProperties[];
-    static const JSFunctionSpec TypedDatumMethods[];
+    static const JSPropertySpec TypedObjectProperties[];
+    static const JSFunctionSpec TypedObjectMethods[];
 };
 class Float32x4Defn {
   public:
     static const X4TypeDescr::Type type = X4TypeDescr::TYPE_FLOAT32;
     static const JSFunctionSpec TypeDescriptorMethods[];
-    static const JSPropertySpec TypedDatumProperties[];
-    static const JSFunctionSpec TypedDatumMethods[];
+    static const JSPropertySpec TypedObjectProperties[];
+    static const JSFunctionSpec TypedObjectMethods[];
 };
 } // namespace js
 
 const JSFunctionSpec js::Float32x4Defn::TypeDescriptorMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "DescrToSourceMethod", 0, 0),
     JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
     JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0),
     JS_FS_END
 };
 
-const JSPropertySpec js::Float32x4Defn::TypedDatumProperties[] = {
+const JSPropertySpec js::Float32x4Defn::TypedObjectProperties[] = {
     JS_PSG("x", Float32x4Lane0, JSPROP_PERMANENT),
     JS_PSG("y", Float32x4Lane1, JSPROP_PERMANENT),
     JS_PSG("z", Float32x4Lane2, JSPROP_PERMANENT),
     JS_PSG("w", Float32x4Lane3, JSPROP_PERMANENT),
     JS_PSG("signMask", Float32x4SignMask, JSPROP_PERMANENT),
     JS_PS_END
 };
 
-const JSFunctionSpec js::Float32x4Defn::TypedDatumMethods[] = {
+const JSFunctionSpec js::Float32x4Defn::TypedObjectMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "X4ToSource", 0, 0),
     JS_FS_END
 };
 
 const JSFunctionSpec js::Int32x4Defn::TypeDescriptorMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "DescrToSourceMethod", 0, 0),
     JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
     JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0),
     JS_FS_END,
 };
 
-const JSPropertySpec js::Int32x4Defn::TypedDatumProperties[] = {
+const JSPropertySpec js::Int32x4Defn::TypedObjectProperties[] = {
     JS_PSG("x", Int32x4Lane0, JSPROP_PERMANENT),
     JS_PSG("y", Int32x4Lane1, JSPROP_PERMANENT),
     JS_PSG("z", Int32x4Lane2, JSPROP_PERMANENT),
     JS_PSG("w", Int32x4Lane3, JSPROP_PERMANENT),
     JS_PSG("signMask", Int32x4SignMask, JSPROP_PERMANENT),
     JS_PS_END
 };
 
-const JSFunctionSpec js::Int32x4Defn::TypedDatumMethods[] = {
+const JSFunctionSpec js::Int32x4Defn::TypedObjectMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "X4ToSource", 0, 0),
     JS_FS_END
 };
 
 template<typename T>
 static JSObject *
 CreateX4Class(JSContext *cx, Handle<GlobalObject*> global)
 {
@@ -213,18 +213,18 @@ CreateX4Class(JSContext *cx, Handle<Glob
     x4->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(T::type));
 
     // Link constructor to prototype and install properties.
 
     if (!JS_DefineFunctions(cx, x4, T::TypeDescriptorMethods))
         return nullptr;
 
     if (!LinkConstructorAndPrototype(cx, x4, proto) ||
-        !DefinePropertiesAndBrand(cx, proto, T::TypedDatumProperties,
-                                  T::TypedDatumMethods))
+        !DefinePropertiesAndBrand(cx, proto, T::TypedObjectProperties,
+                                  T::TypedObjectMethods))
     {
         return nullptr;
     }
 
     return x4;
 }
 
 bool
@@ -361,19 +361,19 @@ js_InitSIMDClass(JSContext *cx, HandleOb
     JS_ASSERT(obj->is<GlobalObject>());
     Rooted<GlobalObject *> global(cx, &obj->as<GlobalObject>());
     return SIMDObject::initClass(cx, global);
 }
 
 template<typename V>
 static bool
 ObjectIsVector(JSObject &obj) {
-    if (!obj.is<TypedDatum>())
+    if (!obj.is<TypedObject>())
         return false;
-    TypeDescr &typeRepr = obj.as<TypedDatum>().typeDescr();
+    TypeDescr &typeRepr = obj.as<TypedObject>().typeDescr();
     if (typeRepr.kind() != TypeDescr::X4)
         return false;
     return typeRepr.as<X4TypeDescr>().type() == V::type;
 }
 
 template<typename V>
 JSObject *
 js::Create(JSContext *cx, typename V::Elem *data)
@@ -528,17 +528,17 @@ Func(JSContext *cx, unsigned argc, Value
 
     if (argc == 1) {
         if((!args[0].isObject() || !ObjectIsVector<V>(args[0].toObject()))) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
             return false;
         }
         typename V::Elem *val =
             reinterpret_cast<typename V::Elem *>(
-                args[0].toObject().as<TypedDatum>().typedMem());
+                args[0].toObject().as<TypedObject>().typedMem());
         typename Vret::Elem result[Vret::lanes];
         for (int32_t i = 0; i < Vret::lanes; i++)
             result[i] = Op::apply(val[i], 0);
 
         RootedObject obj(cx, Create<Vret>(cx, result));
         if (!obj)
             return false;
 
@@ -550,20 +550,20 @@ Func(JSContext *cx, unsigned argc, Value
            (!args[1].isObject() || !ObjectIsVector<V>(args[1].toObject())))
         {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
             return false;
         }
 
         typename V::Elem *left =
             reinterpret_cast<typename V::Elem *>(
-                args[0].toObject().as<TypedDatum>().typedMem());
+                args[0].toObject().as<TypedObject>().typedMem());
         typename V::Elem *right =
             reinterpret_cast<typename V::Elem *>(
-                args[1].toObject().as<TypedDatum>().typedMem());
+                args[1].toObject().as<TypedObject>().typedMem());
 
         typename Vret::Elem result[Vret::lanes];
         for (int32_t i = 0; i < Vret::lanes; i++)
             result[i] = Op::apply(left[i], right[i]);
 
         RootedObject obj(cx, Create<Vret>(cx, result));
         if (!obj)
             return false;
@@ -587,17 +587,17 @@ FuncWith(JSContext *cx, unsigned argc, V
         (!args[1].isNumber() && !args[1].isBoolean()))
     {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
         return false;
     }
 
     typename V::Elem *val =
         reinterpret_cast<typename V::Elem *>(
-            args[0].toObject().as<TypedDatum>().typedMem());
+            args[0].toObject().as<TypedObject>().typedMem());
 
     typename Vret::Elem result[Vret::lanes];
     for (int32_t i = 0; i < Vret::lanes; i++) {
         if(args[1].isNumber()) {
             typename Vret::Elem arg1;
             Vret::toType2(cx, args[1], &arg1);
             result[i] = OpWith::apply(i, arg1, val[i]);
         } else if (args[1].isBoolean()) {
@@ -623,17 +623,17 @@ FuncShuffle(JSContext *cx, unsigned argc
             (!args[1].isNumber()))
         {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
             return false;
         }
 
         typename V::Elem *val =
             reinterpret_cast<typename V::Elem *>(
-                args[0].toObject().as<TypedDatum>().typedMem());
+                args[0].toObject().as<TypedObject>().typedMem());
         typename Vret::Elem result[Vret::lanes];
         for (int32_t i = 0; i < Vret::lanes; i++) {
             typename Vret::Elem arg1;
             Vret::toType2(cx, args[1], &arg1);
             result[i] = val[OpShuffle::apply(i * 2, arg1)];
         }
         RootedObject obj(cx, Create<Vret>(cx, result));
         if (!obj)
@@ -646,20 +646,20 @@ FuncShuffle(JSContext *cx, unsigned argc
             (!args[1].isObject() || !ObjectIsVector<V>(args[1].toObject())) ||
             (!args[2].isNumber()))
         {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
             return false;
         }
         typename V::Elem *val1 =
             reinterpret_cast<typename V::Elem *>(
-                args[0].toObject().as<TypedDatum>().typedMem());
+                args[0].toObject().as<TypedObject>().typedMem());
         typename V::Elem *val2 =
             reinterpret_cast<typename V::Elem *>(
-                args[1].toObject().as<TypedDatum>().typedMem());
+                args[1].toObject().as<TypedObject>().typedMem());
         typename Vret::Elem result[Vret::lanes];
         for (int32_t i = 0; i < Vret::lanes; i++) {
             typename Vret::Elem arg2;
             Vret::toType2(cx, args[2], &arg2);
             if(i < Vret::lanes / 2) {
                 result[i] = val1[OpShuffle::apply(i * 2, arg2)];
             } else {
                 result[i] = val2[OpShuffle::apply(i * 2, arg2)];
@@ -686,17 +686,17 @@ FuncConvert(JSContext *cx, unsigned argc
     if ((argc != 1) ||
        (!args[0].isObject() || !ObjectIsVector<V>(args[0].toObject())))
     {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
         return false;
     }
     typename V::Elem *val =
         reinterpret_cast<typename V::Elem *>(
-            args[0].toObject().as<TypedDatum>().typedMem());
+            args[0].toObject().as<TypedObject>().typedMem());
     typename Vret::Elem result[Vret::lanes];
     for (int32_t i = 0; i < Vret::lanes; i++)
         result[i] = static_cast<typename Vret::Elem>(val[i]);
 
     RootedObject obj(cx, Create<Vret>(cx, result));
     if (!obj)
         return false;
 
@@ -713,17 +713,17 @@ FuncConvertBits(JSContext *cx, unsigned 
     if ((argc != 1) ||
        (!args[0].isObject() || !ObjectIsVector<V>(args[0].toObject())))
     {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
         return false;
     }
     typename Vret::Elem *val =
         reinterpret_cast<typename Vret::Elem *>(
-            args[0].toObject().as<TypedDatum>().typedMem());
+            args[0].toObject().as<TypedObject>().typedMem());
 
     RootedObject obj(cx, Create<Vret>(cx, val));
     if (!obj)
         return false;
 
     args.rval().setObject(*obj);
     return true;
 }
@@ -808,21 +808,21 @@ Float32x4Clamp(JSContext *cx, unsigned a
         (!args[0].isObject() || !ObjectIsVector<Float32x4>(args[0].toObject())) ||
         (!args[1].isObject() || !ObjectIsVector<Float32x4>(args[1].toObject())) ||
         (!args[2].isObject() || !ObjectIsVector<Float32x4>(args[2].toObject())))
     {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
         return false;
     }
     float *val = reinterpret_cast<float *>(
-        args[0].toObject().as<TypedDatum>().typedMem());
+        args[0].toObject().as<TypedObject>().typedMem());
     float *lowerLimit = reinterpret_cast<float *>(
-        args[1].toObject().as<TypedDatum>().typedMem());
+        args[1].toObject().as<TypedObject>().typedMem());
     float *upperLimit = reinterpret_cast<float *>(
-        args[2].toObject().as<TypedDatum>().typedMem());
+        args[2].toObject().as<TypedObject>().typedMem());
 
     float result[Float32x4::lanes];
     result[0] = val[0] < lowerLimit[0] ? lowerLimit[0] : val[0];
     result[1] = val[1] < lowerLimit[1] ? lowerLimit[1] : val[1];
     result[2] = val[2] < lowerLimit[2] ? lowerLimit[2] : val[2];
     result[3] = val[3] < lowerLimit[3] ? lowerLimit[3] : val[3];
     result[0] = result[0] > upperLimit[0] ? upperLimit[0] : result[0];
     result[1] = result[1] > upperLimit[1] ? upperLimit[1] : result[1];
@@ -845,21 +845,21 @@ Int32x4Select(JSContext *cx, unsigned ar
         (!args[0].isObject() || !ObjectIsVector<Int32x4>(args[0].toObject())) ||
         (!args[1].isObject() || !ObjectIsVector<Float32x4>(args[1].toObject())) ||
         (!args[2].isObject() || !ObjectIsVector<Float32x4>(args[2].toObject())))
     {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
         return false;
     }
     int32_t *val = reinterpret_cast<int32_t *>(
-        args[0].toObject().as<TypedDatum>().typedMem());
+        args[0].toObject().as<TypedObject>().typedMem());
     int32_t *tv = reinterpret_cast<int32_t *>(
-        args[1].toObject().as<TypedDatum>().typedMem());
+        args[1].toObject().as<TypedObject>().typedMem());
     int32_t *fv = reinterpret_cast<int32_t *>(
-        args[2].toObject().as<TypedDatum>().typedMem());
+        args[2].toObject().as<TypedObject>().typedMem());
     int32_t tr[Int32x4::lanes];
     for (int32_t i = 0; i < Int32x4::lanes; i++)
         tr[i] = And<int32_t, Int32x4>::apply(val[i], tv[i]);
     int32_t fr[Int32x4::lanes];
     for (int32_t i = 0; i < Int32x4::lanes; i++)
         fr[i] = And<int32_t, Int32x4>::apply(Not<int32_t, Int32x4>::apply(val[i], 0), fv[i]);
     int32_t orInt[Int32x4::lanes];
     for (int32_t i = 0; i < Int32x4::lanes; i++)
--- a/js/src/builtin/TypeRepresentation.cpp
+++ b/js/src/builtin/TypeRepresentation.cpp
@@ -200,28 +200,30 @@ HashNumber
 TypeRepresentationHasher::hashUnsizedArray(UnsizedArrayTypeRepresentation *key)
 {
     return HashGeneric(key->kind(), key->element());
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // Constructors
 
-TypeRepresentation::TypeRepresentation(TypeDescr::Kind kind, bool opaque)
+TypeRepresentation::TypeRepresentation(TypeDescr::Kind kind,
+                                       size_t align,
+                                       bool opaque)
   : kind_(kind),
-    opaque_(opaque)
+    opaque_(opaque),
+    alignment_(align)
 {}
 
 SizedTypeRepresentation::SizedTypeRepresentation(SizedTypeDescr::Kind kind,
                                                  bool opaque,
                                                  size_t size,
                                                  size_t align)
-  : TypeRepresentation(kind, opaque),
-    size_(size),
-    alignment_(align)
+  : TypeRepresentation(kind, align, opaque),
+    size_(size)
 {}
 
 ScalarTypeRepresentation::ScalarTypeRepresentation(ScalarTypeDescr::Type type)
   : SizedTypeRepresentation(TypeDescr::Scalar,
                             false,
                             ScalarTypeDescr::size(type),
                             ScalarTypeDescr::alignment(type)),
     type_(type)
@@ -264,17 +266,18 @@ SizedArrayTypeRepresentation::SizedArray
   : SizedTypeRepresentation(TypeDescr::SizedArray, element->opaque(),
                             element->size() * length, element->alignment()),
     element_(element),
     length_(length)
 {
 }
 
 UnsizedArrayTypeRepresentation::UnsizedArrayTypeRepresentation(SizedTypeRepresentation *element)
-  : TypeRepresentation(TypeDescr::UnsizedArray, element->opaque()),
+  : TypeRepresentation(TypeDescr::UnsizedArray, element->alignment(),
+                       element->opaque()),
     element_(element)
 {
 }
 
 static inline size_t alignTo(size_t address, size_t align) {
     JS_ASSERT(IsPowerOfTwo(align));
     return (address + align - 1) & -align;
 }
--- a/js/src/builtin/TypeRepresentation.h
+++ b/js/src/builtin/TypeRepresentation.h
@@ -120,39 +120,41 @@ struct TypeRepresentationHasher
 typedef js::HashSet<TypeRepresentation *,
                     TypeRepresentationHasher,
                     RuntimeAllocPolicy> TypeRepresentationHash;
 
 class TypeRepresentationHelper;
 
 class TypeRepresentation {
   protected:
-    TypeRepresentation(TypeDescr::Kind kind, bool opaque);
+    TypeRepresentation(TypeDescr::Kind kind, size_t alignment, bool opaque);
 
     // in order to call addToTableOrFree()
     friend class TypeRepresentationHelper;
 
     TypeDescr::Kind kind_;
     bool opaque_;
+    size_t alignment_;
 
     JSObject *addToTableOrFree(JSContext *cx, TypeRepresentationHash::AddPtr &p);
 
   private:
     static const Class class_;
     static void obj_trace(JSTracer *trace, JSObject *object);
     static void obj_finalize(js::FreeOp *fop, JSObject *object);
 
     HeapPtrObject ownerObject_;
     void traceFields(JSTracer *tracer);
 
   public:
     TypeDescr::Kind kind() const { return kind_; }
     bool opaque() const { return opaque_; }
     bool transparent() const { return !opaque_; }
     JSObject *ownerObject() const { return ownerObject_.get(); }
+    size_t alignment() const { return alignment_; }
 
     static bool isOwnerObject(JSObject &obj);
     static TypeRepresentation *fromOwnerObject(JSObject &obj);
 
     bool isSized() const {
         return TypeDescr::isSized(kind());
     }
 
@@ -201,21 +203,19 @@ class TypeRepresentation {
     void mark(JSTracer *tracer);
 };
 
 class SizedTypeRepresentation : public TypeRepresentation {
   protected:
     SizedTypeRepresentation(TypeDescr::Kind kind, bool opaque, size_t size, size_t align);
 
     size_t size_;
-    size_t alignment_;
 
   public:
     size_t size() const { return size_; }
-    size_t alignment() const { return alignment_; }
 
     // Initializes memory that contains `count` instances of this type.
     // `count` must be at least 1.
     void initInstance(const JSRuntime *rt, uint8_t *mem, size_t count);
 
     // Traces memory that contains `count` instances of this type.
     void traceInstance(JSTracer *trace, uint8_t *mem, size_t count);
 };
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -38,17 +38,18 @@ const Class js::TypedObjectModuleObject:
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub
 };
 
 static const JSFunctionSpec TypedObjectMethods[] = {
-    JS_SELF_HOSTED_FN("objectType", "TypeOfTypedDatum", 1, 0),
+    JS_SELF_HOSTED_FN("objectType", "TypeOfTypedObject", 1, 0),
+    JS_SELF_HOSTED_FN("storage", "StorageOfTypedObject", 1, 0),
     JS_FS_END
 };
 
 static void
 ReportCannotConvertTo(JSContext *cx, HandleValue fromValue, const char *toType)
 {
     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
                          InformalValueTypeName(fromValue), toType);
@@ -63,94 +64,94 @@ ToObjectIf(HandleValue value)
 
     if (!value.toObject().is<T>())
         return nullptr;
 
     return &value.toObject().as<T>();
 }
 
 /*
- * Overwrites the contents of `datum` at offset `offset` with `val`
+ * Overwrites the contents of `typedObj` at offset `offset` with `val`
  * converted to the type `typeObj`. This is done by delegating to
  * self-hosted code. This is used for assignments and initializations.
  *
  * For example, consider the final assignment in this snippet:
  *
  *    var Point = new StructType({x: float32, y: float32});
  *    var Line = new StructType({from: Point, to: Point});
  *    var line = new Line();
  *    line.to = {x: 22, y: 44};
  *
  * This would result in a call to `ConvertAndCopyTo`
  * where:
  * - typeObj = Point
- * - datum = line
+ * - typedObj = line
  * - offset = sizeof(Point) == 8
  * - val = {x: 22, y: 44}
  * This would result in loading the value of `x`, converting
  * it to a float32, and hen storing it at the appropriate offset,
  * and then doing the same for `y`.
  *
  * Note that the type of `typeObj` may not be the
- * type of `datum` but rather some subcomponent of `datum`.
+ * type of `typedObj` but rather some subcomponent of `typedObj`.
  */
 static bool
 ConvertAndCopyTo(JSContext *cx,
                  HandleTypeDescr typeObj,
-                 HandleTypedDatum datum,
+                 HandleTypedObject typedObj,
                  int32_t offset,
                  HandleValue val)
 {
     RootedFunction func(
         cx, SelfHostedFunction(cx, cx->names().ConvertAndCopyTo));
     if (!func)
         return false;
 
     InvokeArgs args(cx);
     if (!args.init(4))
         return false;
 
     args.setCallee(ObjectValue(*func));
     args[0].setObject(*typeObj);
-    args[1].setObject(*datum);
+    args[1].setObject(*typedObj);
     args[2].setInt32(offset);
     args[3].set(val);
 
     return Invoke(cx, args);
 }
 
 static bool
-ConvertAndCopyTo(JSContext *cx, HandleTypedDatum datum, HandleValue val)
+ConvertAndCopyTo(JSContext *cx, HandleTypedObject typedObj, HandleValue val)
 {
-    Rooted<TypeDescr*> type(cx, &datum->typeDescr());
-    return ConvertAndCopyTo(cx, type, datum, 0, val);
+    Rooted<TypeDescr*> type(cx, &typedObj->typeDescr());
+    return ConvertAndCopyTo(cx, type, typedObj, 0, val);
 }
 
 /*
- * Overwrites the contents of `datum` at offset `offset` with `val`
+ * Overwrites the contents of `typedObj` at offset `offset` with `val`
  * converted to the type `typeObj`
  */
 static bool
 Reify(JSContext *cx,
       HandleTypeDescr type,
-      HandleTypedDatum datum,
+      HandleTypedObject typedObj,
       size_t offset,
       MutableHandleValue to)
 {
     RootedFunction func(cx, SelfHostedFunction(cx, cx->names().Reify));
     if (!func)
         return false;
 
     InvokeArgs args(cx);
     if (!args.init(3))
         return false;
 
     args.setCallee(ObjectValue(*func));
     args[0].setObject(*type);
-    args[1].setObject(*datum);
+    args[1].setObject(*typedObj);
     args[2].setInt32(offset);
 
     if (!Invoke(cx, args))
         return false;
 
     to.set(args.rval());
     return true;
 }
@@ -183,16 +184,21 @@ TypeDescr::typeRepresentation() const {
     return TypeRepresentation::fromOwnerObject(typeRepresentationOwnerObj());
 }
 
 TypeDescr::Kind
 TypeDescr::kind() const {
     return typeRepresentation()->kind();
 }
 
+bool
+TypeDescr::opaque() const {
+    return typeRepresentation()->opaque();
+}
+
 /***************************************************************************
  * Scalar type objects
  *
  * Scalar type objects like `uint8`, `uint16`, are all instances of
  * the ScalarTypeDescr class. Like all type objects, they have a reserved
  * slot pointing to a TypeRepresentation object, which is used to
  * distinguish which scalar type object this actually is.
  */
@@ -434,34 +440,34 @@ const Class UnsizedArrayTypeDescr::class
     JS_PropertyStub,
     JS_StrictPropertyStub,
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     nullptr,
     nullptr,
     nullptr,
-    TypedObject::construct,
+    TypedObject::constructUnsized,
     nullptr
 };
 
 const Class SizedArrayTypeDescr::class_ = {
     "ArrayType",
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS),
     JS_PropertyStub,
     JS_DeletePropertyStub,
     JS_PropertyStub,
     JS_StrictPropertyStub,
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     nullptr,
     nullptr,
     nullptr,
-    TypedObject::construct,
+    TypedObject::constructSized,
     nullptr
 };
 
 const JSPropertySpec ArrayMetaTypeDescr::typeObjectProperties[] = {
     JS_PS_END
 };
 
 const JSFunctionSpec ArrayMetaTypeDescr::typeObjectMethods[] = {
@@ -497,24 +503,25 @@ const JSFunctionSpec ArrayMetaTypeDescr:
 bool
 js::InitializeCommonTypeDescriptorProperties(JSContext *cx,
                                              HandleTypeDescr obj,
                                              HandleObject typeReprOwnerObj)
 {
     TypeRepresentation *typeRepr =
         TypeRepresentation::fromOwnerObject(*typeReprOwnerObj);
 
+    obj->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT,
+                          Int32Value(typeRepr->alignment()));
+
     // Regardless of whether the data is transparent, we always
     // store the internal size/alignment slots.
     if (typeRepr->isSized()) {
         SizedTypeRepresentation *sizedTypeRepr = typeRepr->asSized();
         obj->initReservedSlot(JS_DESCR_SLOT_SIZE,
                               Int32Value(sizedTypeRepr->size()));
-        obj->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT,
-                              Int32Value(sizedTypeRepr->alignment()));
     }
 
     // If data is transparent, also store the public slots.
     if (typeRepr->transparent() && typeRepr->isSized()) {
         SizedTypeRepresentation *sizedTypeRepr = typeRepr->asSized();
 
         // byteLength
         RootedValue typeByteLength(cx, NumberValue(sizedTypeRepr->size()));
@@ -739,17 +746,17 @@ const Class StructTypeDescr::class_ = {
     JS_PropertyStub,
     JS_StrictPropertyStub,
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
-    TypedObject::construct,
+    TypedObject::constructSized,
     nullptr  /* trace */
 };
 
 const JSPropertySpec StructMetaTypeDescr::typeObjectProperties[] = {
     JS_PS_END
 };
 
 const JSFunctionSpec StructMetaTypeDescr::typeObjectMethods[] = {
@@ -1332,46 +1339,40 @@ TypedObjectModuleObject::getSuitableClas
         proto.set(&global->getTypedObjectModule().getSlot(ArrayTypePrototype).toObject());
         break;
     }
 
     return !!proto;
 }
 
 /******************************************************************************
- * Typed datums
- *
- * Datums represent either typed objects or handles. See comment in
- * TypedObject.h.
+ * Typed objects
  */
 
-template<class T>
-/*static*/ T *
-TypedDatum::createUnattached(JSContext *cx,
-                             HandleTypeDescr type,
+/*static*/ TypedObject *
+TypedObject::createUnattached(JSContext *cx,
+                             HandleTypeDescr descr,
                              int32_t length)
 {
-    JS_STATIC_ASSERT(T::IsTypedDatumClass);
-
-    RootedObject obj(cx);
-    obj = createUnattachedWithClass(cx, &T::class_, type, length);
-    if (!obj)
-        return nullptr;
-
-    return &obj->as<T>();
+    if (descr->opaque())
+        return createUnattachedWithClass(cx, &OpaqueTypedObject::class_, descr, length);
+    else
+        return createUnattachedWithClass(cx, &TransparentTypedObject::class_, descr, length);
 }
 
-/*static*/ TypedDatum *
-TypedDatum::createUnattachedWithClass(JSContext *cx,
+
+/*static*/ TypedObject *
+TypedObject::createUnattachedWithClass(JSContext *cx,
                                       const Class *clasp,
                                       HandleTypeDescr type,
                                       int32_t length)
 {
-    JS_ASSERT(clasp == &TypedObject::class_ || clasp == &TypedHandle::class_);
-    JS_ASSERT(JSCLASS_RESERVED_SLOTS(clasp) == JS_DATUM_SLOTS);
+    JS_ASSERT(clasp == &TransparentTypedObject::class_ ||
+              clasp == &OpaqueTypedObject::class_);
+    JS_ASSERT(JSCLASS_RESERVED_SLOTS(clasp) == JS_TYPEDOBJ_SLOTS);
     JS_ASSERT(clasp->hasPrivate());
 
     RootedObject proto(cx);
     if (type->is<SimpleTypeDescr>()) {
         // FIXME Bug 929651 -- What prototype to use?
         proto = type->global().getOrCreateObjectPrototype(cx);
     } else {
         RootedValue protoVal(cx);
@@ -1383,102 +1384,158 @@ TypedDatum::createUnattachedWithClass(JS
         proto = &protoVal.toObject();
     }
 
     RootedObject obj(cx, NewObjectWithClassProto(cx, clasp, &*proto, nullptr));
     if (!obj)
         return nullptr;
 
     obj->setPrivate(nullptr);
-    obj->initReservedSlot(JS_DATUM_SLOT_TYPE_DESCR, ObjectValue(*type));
-    obj->initReservedSlot(JS_DATUM_SLOT_OWNER, NullValue());
-    obj->initReservedSlot(JS_DATUM_SLOT_LENGTH, Int32Value(length));
+    obj->initReservedSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(0));
+    obj->initReservedSlot(JS_TYPEDOBJ_SLOT_BYTELENGTH, Int32Value(0));
+    obj->initReservedSlot(JS_TYPEDOBJ_SLOT_OWNER, NullValue());
+    obj->initReservedSlot(JS_TYPEDOBJ_SLOT_NEXT_VIEW, PrivateValue(nullptr));
+    obj->initReservedSlot(JS_TYPEDOBJ_SLOT_NEXT_BUFFER, PrivateValue(UNSET_BUFFER_LINK));
+    obj->initReservedSlot(JS_TYPEDOBJ_SLOT_LENGTH, Int32Value(length));
+    obj->initReservedSlot(JS_TYPEDOBJ_SLOT_TYPE_DESCR, ObjectValue(*type));
 
     // Tag the type object for this instance with the type
     // representation, if that has not been done already.
     if (cx->typeInferenceEnabled() && !type->is<SimpleTypeDescr>()) {
         // FIXME Bug 929651           ^~~~~~~~~~~~~~~~~~~~~~~~~~~
         RootedTypeObject typeObj(cx, obj->getType(cx));
         if (typeObj) {
             if (!typeObj->addTypedObjectAddendum(cx, type))
                 return nullptr;
         }
     }
 
-    return static_cast<TypedDatum*>(&*obj);
-}
-
-void
-TypedDatum::attach(uint8_t *memory)
-{
-    setPrivate(memory);
-    setReservedSlot(JS_DATUM_SLOT_OWNER, ObjectValue(*this));
+    return static_cast<TypedObject*>(&*obj);
 }
 
 void
-TypedDatum::attach(TypedDatum &datum, uint32_t offset)
+TypedObject::attach(ArrayBufferObject &buffer, int32_t offset)
 {
-    JS_ASSERT(datum.getReservedSlot(JS_DATUM_SLOT_OWNER).isObject());
-    JS_ASSERT(offset + size() <= datum.size());
-
-    // find the location in memory
-    uint8_t *mem = datum.typedMem(offset);
-
-    // find the owner, which is often but not always `datum`
-    TypedDatum &owner = datum.owner();
-
-    setPrivate(mem);
-    setReservedSlot(JS_DATUM_SLOT_OWNER, ObjectValue(owner));
+    JS_ASSERT(offset >= 0);
+    JS_ASSERT(offset + size() <= buffer.byteLength());
+
+    buffer.addView(this);
+    InitArrayBufferViewDataPointer(this, &buffer, offset);
+    setReservedSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(offset));
+    setReservedSlot(JS_TYPEDOBJ_SLOT_OWNER, ObjectValue(buffer));
 }
 
-// Returns a suitable JS_DATUM_SLOT_LENGTH value for an instance of
+void
+TypedObject::attach(TypedObject &typedObj, int32_t offset)
+{
+    attach(typedObj.owner(), typedObj.offset() + offset);
+}
+
+// Returns a suitable JS_TYPEDOBJ_SLOT_LENGTH value for an instance of
 // the type `type`. `type` must not be an unsized array.
 static int32_t
-DatumLengthFromType(TypeDescr &descr)
+TypedObjLengthFromType(TypeDescr &descr)
 {
     TypeRepresentation *typeRepr = descr.typeRepresentation();
     switch (typeRepr->kind()) {
       case TypeDescr::Scalar:
       case TypeDescr::Reference:
       case TypeDescr::Struct:
       case TypeDescr::X4:
         return 0;
 
       case TypeDescr::SizedArray:
         return typeRepr->asSizedArray()->length();
 
       case TypeDescr::UnsizedArray:
-        MOZ_ASSUME_UNREACHABLE("DatumLengthFromType() invoked on unsized type");
+        MOZ_ASSUME_UNREACHABLE("TypedObjLengthFromType() invoked on unsized type");
     }
     MOZ_ASSUME_UNREACHABLE("Invalid kind");
 }
 
-/*static*/ TypedDatum *
-TypedDatum::createDerived(JSContext *cx, HandleSizedTypeDescr type,
-                          HandleTypedDatum datum, size_t offset)
+/*static*/ TypedObject *
+TypedObject::createDerived(JSContext *cx, HandleSizedTypeDescr type,
+                          HandleTypedObject typedObj, size_t offset)
 {
-    JS_ASSERT(offset <= datum->size());
-    JS_ASSERT(offset + type->size() <= datum->size());
-
-    int32_t length = DatumLengthFromType(*type);
-
-    const js::Class *clasp = datum->getClass();
-    Rooted<TypedDatum*> obj(
-        cx, createUnattachedWithClass(cx, clasp, type, length));
+    JS_ASSERT(offset <= typedObj->size());
+    JS_ASSERT(offset + type->size() <= typedObj->size());
+
+    int32_t length = TypedObjLengthFromType(*type);
+
+    const js::Class *clasp = typedObj->getClass();
+    Rooted<TypedObject*> obj(cx);
+    obj = createUnattachedWithClass(cx, clasp, type, length);
+    if (!obj)
+        return nullptr;
+
+    obj->attach(*typedObj, offset);
+    return obj;
+}
+
+/*static*/ TypedObject *
+TypedObject::createZeroed(JSContext *cx,
+                         HandleTypeDescr descr,
+                         int32_t length)
+{
+    // Create unattached wrapper object.
+    Rooted<TypedObject*> obj(cx, createUnattached(cx, descr, length));
     if (!obj)
         return nullptr;
 
-    obj->attach(*datum, offset);
-    return obj;
+    // Allocate and initialize the memory for this instance.
+    // Also initialize the JS_TYPEDOBJ_SLOT_LENGTH slot.
+    TypeRepresentation *typeRepr = descr->typeRepresentation();
+    switch (descr->kind()) {
+      case TypeDescr::Scalar:
+      case TypeDescr::Reference:
+      case TypeDescr::Struct:
+      case TypeDescr::X4:
+      case TypeDescr::SizedArray:
+      {
+        size_t totalSize = descr->as<SizedTypeDescr>().size();
+        Rooted<ArrayBufferObject*> buffer(cx);
+        buffer = ArrayBufferObject::create(cx, totalSize, false);
+        if (!buffer)
+            return nullptr;
+        typeRepr->asSized()->initInstance(cx->runtime(), buffer->dataPointer(), 1);
+        obj->attach(*buffer, 0);
+        return obj;
+      }
+
+      case TypeDescr::UnsizedArray:
+      {
+        SizedTypeRepresentation *elementTypeRepr =
+            typeRepr->asUnsizedArray()->element();
+
+        int32_t totalSize;
+        if (!SafeMul(elementTypeRepr->size(), length, &totalSize)) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
+                                 JSMSG_TYPEDOBJECT_TOO_BIG);
+            return nullptr;
+        }
+
+        Rooted<ArrayBufferObject*> buffer(cx);
+        buffer = ArrayBufferObject::create(cx, totalSize, false);
+        if (!buffer)
+            return nullptr;
+
+        if (length)
+            elementTypeRepr->initInstance(cx->runtime(), buffer->dataPointer(), length);
+        obj->attach(*buffer, 0);
+        return obj;
+      }
+    }
+
+    MOZ_ASSUME_UNREACHABLE("Bad TypeRepresentation Kind");
 }
 
 static bool
-ReportDatumTypeError(JSContext *cx,
+ReportTypedObjTypeError(JSContext *cx,
                      const unsigned errorNumber,
-                     HandleTypedDatum obj)
+                     HandleTypedObject obj)
 {
     // Serialize type string using self-hosted function DescrToSource
     RootedFunction func(
         cx, SelfHostedFunction(cx, cx->names().DescrToSource));
     if (!func)
         return false;
     InvokeArgs args(cx);
     if (!args.init(1))
@@ -1499,70 +1556,54 @@ ReportDatumTypeError(JSContext *cx,
     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                          errorNumber, typeReprStr);
 
     JS_free(cx, (void *) typeReprStr);
     return false;
 }
 
 /*static*/ void
-TypedDatum::obj_trace(JSTracer *trace, JSObject *object)
+TypedObject::obj_trace(JSTracer *trace, JSObject *object)
 {
-    JS_ASSERT(object->is<TypedDatum>());
-    TypedDatum &datum = object->as<TypedDatum>();
-
-    for (size_t i = 0; i < JS_DATUM_SLOTS; i++)
-        gc::MarkSlot(trace, &object->getReservedSlotRef(i), "TypedObjectSlot");
-
-    TypeRepresentation *repr = datum.typeRepresentation();
+    gc::MarkSlot(trace, &object->getReservedSlotRef(JS_TYPEDOBJ_SLOT_TYPE_DESCR),
+                 "TypedObjectTypeDescr");
+
+    ArrayBufferViewObject::trace(trace, object);
+
+    JS_ASSERT(object->is<TypedObject>());
+    TypedObject &typedObj = object->as<TypedObject>();
+    TypeRepresentation *repr = typedObj.typeRepresentation();
     if (repr->opaque()) {
-        uint8_t *mem = datum.typedMem();
+        uint8_t *mem = typedObj.typedMem();
         if (!mem)
             return; // unattached handle or partially constructed
 
         switch (repr->kind()) {
           case TypeDescr::Scalar:
           case TypeDescr::Reference:
           case TypeDescr::Struct:
           case TypeDescr::SizedArray:
           case TypeDescr::X4:
             repr->asSized()->traceInstance(trace, mem, 1);
             break;
 
           case TypeDescr::UnsizedArray:
-            repr->asUnsizedArray()->element()->traceInstance(trace, mem, datum.length());
+            repr->asUnsizedArray()->element()->traceInstance(trace, mem, typedObj.length());
             break;
         }
     }
 }
 
-/*static*/ void
-TypedDatum::obj_finalize(js::FreeOp *op, JSObject *obj)
-{
-    // if the obj owns the memory, indicating by the owner slot being
-    // set to itself, then we must free it when finalized.
-
-    JS_ASSERT(obj->getReservedSlotRef(JS_DATUM_SLOT_OWNER).isObjectOrNull());
-
-    if (obj->getReservedSlot(JS_DATUM_SLOT_OWNER).isNull())
-        return; // Unattached
-
-    if (&obj->getReservedSlot(JS_DATUM_SLOT_OWNER).toObject() == obj) {
-        JS_ASSERT(obj->getPrivate());
-        op->free_(obj->getPrivate());
-    }
-}
-
 bool
-TypedDatum::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
+TypedObject::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
                               MutableHandleObject objp, MutableHandleShape propp)
 {
-    JS_ASSERT(obj->is<TypedDatum>());
-
-    Rooted<TypeDescr*> typeDescr(cx, &obj->as<TypedDatum>().typeDescr());
+    JS_ASSERT(obj->is<TypedObject>());
+
+    Rooted<TypeDescr*> typeDescr(cx, &obj->as<TypedObject>().typeDescr());
     TypeRepresentation *typeRepr = typeDescr->typeRepresentation();
 
     switch (typeRepr->kind()) {
       case TypeDescr::Scalar:
       case TypeDescr::Reference:
       case TypeDescr::X4:
         break;
 
@@ -1600,31 +1641,31 @@ TypedDatum::obj_lookupGeneric(JSContext 
         propp.set(nullptr);
         return true;
     }
 
     return JSObject::lookupGeneric(cx, proto, id, objp, propp);
 }
 
 bool
-TypedDatum::obj_lookupProperty(JSContext *cx,
+TypedObject::obj_lookupProperty(JSContext *cx,
                                 HandleObject obj,
                                 HandlePropertyName name,
                                 MutableHandleObject objp,
                                 MutableHandleShape propp)
 {
     RootedId id(cx, NameToId(name));
     return obj_lookupGeneric(cx, obj, id, objp, propp);
 }
 
 bool
-TypedDatum::obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
+TypedObject::obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
                                 MutableHandleObject objp, MutableHandleShape propp)
 {
-    JS_ASSERT(obj->is<TypedDatum>());
+    JS_ASSERT(obj->is<TypedObject>());
     MarkNonNativePropertyFound(propp);
     objp.set(obj);
     return true;
 }
 
 static bool
 ReportPropertyError(JSContext *cx,
                     const unsigned errorNumber,
@@ -1641,202 +1682,202 @@ ReportPropertyError(JSContext *cx,
     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                          errorNumber, propName);
 
     JS_free(cx, propName);
     return false;
 }
 
 bool
-TypedDatum::obj_lookupSpecial(JSContext *cx, HandleObject obj,
+TypedObject::obj_lookupSpecial(JSContext *cx, HandleObject obj,
                               HandleSpecialId sid, MutableHandleObject objp,
                               MutableHandleShape propp)
 {
     RootedId id(cx, SPECIALID_TO_JSID(sid));
     return obj_lookupGeneric(cx, obj, id, objp, propp);
 }
 
 bool
-TypedDatum::obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
+TypedObject::obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
                               PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
     return ReportPropertyError(cx, JSMSG_UNDEFINED_PROP, id);
 }
 
 bool
-TypedDatum::obj_defineProperty(JSContext *cx, HandleObject obj,
+TypedObject::obj_defineProperty(JSContext *cx, HandleObject obj,
                                HandlePropertyName name, HandleValue v,
                                PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
     Rooted<jsid> id(cx, NameToId(name));
     return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs);
 }
 
 bool
-TypedDatum::obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v,
+TypedObject::obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v,
                                PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
 
     RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
     if (!delegate)
         return false;
     return baseops::DefineElement(cx, delegate, index, v, getter, setter, attrs);
 }
 
 bool
-TypedDatum::obj_defineSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid, HandleValue v,
+TypedObject::obj_defineSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid, HandleValue v,
                               PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
     Rooted<jsid> id(cx, SPECIALID_TO_JSID(sid));
     return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs);
 }
 
 bool
-TypedDatum::obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
+TypedObject::obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
                            HandleId id, MutableHandleValue vp)
 {
-    JS_ASSERT(obj->is<TypedDatum>());
-    Rooted<TypedDatum *> datum(cx, &obj->as<TypedDatum>());
+    JS_ASSERT(obj->is<TypedObject>());
+    Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
 
     // Dispatch elements to obj_getElement:
     uint32_t index;
     if (js_IdIsIndex(id, &index))
         return obj_getElement(cx, obj, receiver, index, vp);
 
     // Handle everything else here:
 
-    TypeRepresentation *typeRepr = datum->typeRepresentation();
+    TypeRepresentation *typeRepr = typedObj->typeRepresentation();
     switch (typeRepr->kind()) {
       case TypeDescr::Scalar:
       case TypeDescr::Reference:
         break;
 
       case TypeDescr::X4:
         break;
 
       case TypeDescr::SizedArray:
       case TypeDescr::UnsizedArray:
         if (JSID_IS_ATOM(id, cx->names().length)) {
-            if (!datum->typedMem()) { // unattached
+            if (!typedObj->typedMem()) { // unattached
                 JS_ReportErrorNumber(
                     cx, js_GetErrorMessage,
                     nullptr, JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
                 return false;
             }
 
-            vp.setInt32(datum->length());
+            vp.setInt32(typedObj->length());
             return true;
         }
         break;
 
       case TypeDescr::Struct: {
-        Rooted<StructTypeDescr*> descr(cx, &datum->typeDescr().as<StructTypeDescr>());
+        Rooted<StructTypeDescr*> descr(cx, &typedObj->typeDescr().as<StructTypeDescr>());
 
         size_t fieldIndex;
         if (!descr->fieldIndex(id, &fieldIndex))
             break;
 
         size_t offset = descr->fieldOffset(fieldIndex);
         Rooted<SizedTypeDescr*> fieldType(cx, &descr->fieldDescr(fieldIndex));
-        return Reify(cx, fieldType, datum, offset, vp);
+        return Reify(cx, fieldType, typedObj, offset, vp);
       }
     }
 
     RootedObject proto(cx, obj->getProto());
     if (!proto) {
         vp.setUndefined();
         return true;
     }
 
     return JSObject::getGeneric(cx, proto, receiver, id, vp);
 }
 
 bool
-TypedDatum::obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
+TypedObject::obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
                               HandlePropertyName name, MutableHandleValue vp)
 {
     RootedId id(cx, NameToId(name));
     return obj_getGeneric(cx, obj, receiver, id, vp);
 }
 
 bool
-TypedDatum::obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver,
+TypedObject::obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver,
                              uint32_t index, MutableHandleValue vp)
 {
-    JS_ASSERT(obj->is<TypedDatum>());
-    Rooted<TypedDatum *> datum(cx, &obj->as<TypedDatum>());
-    Rooted<TypeDescr *> descr(cx, &datum->typeDescr());
+    JS_ASSERT(obj->is<TypedObject>());
+    Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
+    Rooted<TypeDescr *> descr(cx, &typedObj->typeDescr());
 
     switch (descr->kind()) {
       case TypeDescr::Scalar:
       case TypeDescr::Reference:
       case TypeDescr::X4:
       case TypeDescr::Struct:
         break;
 
       case TypeDescr::SizedArray:
-        return obj_getArrayElement<SizedArrayTypeDescr>(cx, datum, descr,
+        return obj_getArrayElement<SizedArrayTypeDescr>(cx, typedObj, descr,
                                                         index, vp);
 
       case TypeDescr::UnsizedArray:
-        return obj_getArrayElement<UnsizedArrayTypeDescr>(cx, datum, descr,
+        return obj_getArrayElement<UnsizedArrayTypeDescr>(cx, typedObj, descr,
                                                           index, vp);
     }
 
     RootedObject proto(cx, obj->getProto());
     if (!proto) {
         vp.setUndefined();
         return true;
     }
 
     return JSObject::getElement(cx, proto, receiver, index, vp);
 }
 
 template<class T>
 /*static*/ bool
-TypedDatum::obj_getArrayElement(JSContext *cx,
-                                Handle<TypedDatum*> datum,
+TypedObject::obj_getArrayElement(JSContext *cx,
+                                Handle<TypedObject*> typedObj,
                                 Handle<TypeDescr*> typeDescr,
                                 uint32_t index,
                                 MutableHandleValue vp)
 {
     JS_ASSERT(typeDescr->is<T>());
 
-    if (index >= datum->length()) {
+    if (index >= typedObj->length()) {
         vp.setUndefined();
         return true;
     }
 
     Rooted<SizedTypeDescr*> elementType(cx, &typeDescr->as<T>().elementType());
     size_t offset = elementType->size() * index;
-    return Reify(cx, elementType, datum, offset, vp);
+    return Reify(cx, elementType, typedObj, offset, vp);
 }
 
 bool
-TypedDatum::obj_getSpecial(JSContext *cx, HandleObject obj,
+TypedObject::obj_getSpecial(JSContext *cx, HandleObject obj,
                             HandleObject receiver, HandleSpecialId sid,
                             MutableHandleValue vp)
 {
     RootedId id(cx, SPECIALID_TO_JSID(sid));
     return obj_getGeneric(cx, obj, receiver, id, vp);
 }
 
 bool
-TypedDatum::obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
+TypedObject::obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
                            MutableHandleValue vp, bool strict)
 {
-    JS_ASSERT(obj->is<TypedDatum>());
-    Rooted<TypedDatum *> datum(cx, &obj->as<TypedDatum>());
+    JS_ASSERT(obj->is<TypedObject>());
+    Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
 
     uint32_t index;
     if (js_IdIsIndex(id, &index))
         return obj_setElement(cx, obj, index, vp, strict);
 
-    TypeRepresentation *typeRepr = datum->typeRepresentation();
+    TypeRepresentation *typeRepr = typedObj->typeRepresentation();
     switch (typeRepr->kind()) {
       case ScalarTypeDescr::Scalar:
       case TypeDescr::Reference:
         break;
 
       case ScalarTypeDescr::X4:
         break;
 
@@ -1845,103 +1886,103 @@ TypedDatum::obj_setGeneric(JSContext *cx
         if (JSID_IS_ATOM(id, cx->names().length)) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage,
                                  nullptr, JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
             return false;
         }
         break;
 
       case ScalarTypeDescr::Struct: {
-        Rooted<StructTypeDescr*> descr(cx, &datum->typeDescr().as<StructTypeDescr>());
+        Rooted<StructTypeDescr*> descr(cx, &typedObj->typeDescr().as<StructTypeDescr>());
 
         size_t fieldIndex;
         if (!descr->fieldIndex(id, &fieldIndex))
             break;
 
         size_t offset = descr->fieldOffset(fieldIndex);
         Rooted<SizedTypeDescr*> fieldType(cx, &descr->fieldDescr(fieldIndex));
-        return ConvertAndCopyTo(cx, fieldType, datum, offset, vp);
+        return ConvertAndCopyTo(cx, fieldType, typedObj, offset, vp);
       }
     }
 
-    return ReportDatumTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, datum);
+    return ReportTypedObjTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, typedObj);
 }
 
 bool
-TypedDatum::obj_setProperty(JSContext *cx, HandleObject obj,
+TypedObject::obj_setProperty(JSContext *cx, HandleObject obj,
                              HandlePropertyName name, MutableHandleValue vp,
                              bool strict)
 {
     RootedId id(cx, NameToId(name));
     return obj_setGeneric(cx, obj, id, vp, strict);
 }
 
 bool
-TypedDatum::obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
+TypedObject::obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
                            MutableHandleValue vp, bool strict)
 {
-    JS_ASSERT(obj->is<TypedDatum>());
-    Rooted<TypedDatum *> datum(cx, &obj->as<TypedDatum>());
-    Rooted<TypeDescr *> descr(cx, &datum->typeDescr());
+    JS_ASSERT(obj->is<TypedObject>());
+    Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
+    Rooted<TypeDescr *> descr(cx, &typedObj->typeDescr());
 
     switch (descr->kind()) {
       case TypeDescr::Scalar:
       case TypeDescr::Reference:
       case TypeDescr::X4:
       case TypeDescr::Struct:
         break;
 
       case TypeDescr::SizedArray:
-        return obj_setArrayElement<SizedArrayTypeDescr>(cx, datum, descr, index, vp);
+        return obj_setArrayElement<SizedArrayTypeDescr>(cx, typedObj, descr, index, vp);
 
       case TypeDescr::UnsizedArray:
-        return obj_setArrayElement<UnsizedArrayTypeDescr>(cx, datum, descr, index, vp);
+        return obj_setArrayElement<UnsizedArrayTypeDescr>(cx, typedObj, descr, index, vp);
     }
 
-    return ReportDatumTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, datum);
+    return ReportTypedObjTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, typedObj);
 }
 
 template<class T>
 /*static*/ bool
-TypedDatum::obj_setArrayElement(JSContext *cx,
-                                Handle<TypedDatum*> datum,
+TypedObject::obj_setArrayElement(JSContext *cx,
+                                Handle<TypedObject*> typedObj,
                                 Handle<TypeDescr*> descr,
                                 uint32_t index,
                                 MutableHandleValue vp)
 {
     JS_ASSERT(descr->is<T>());
 
-    if (index >= datum->length()) {
+    if (index >= typedObj->length()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage,
                              nullptr, JSMSG_TYPEDOBJECT_BINARYARRAY_BAD_INDEX);
         return false;
     }
 
     Rooted<SizedTypeDescr*> elementType(cx);
     elementType = &descr->as<T>().elementType();
     size_t offset = elementType->size() * index;
-    return ConvertAndCopyTo(cx, elementType, datum, offset, vp);
+    return ConvertAndCopyTo(cx, elementType, typedObj, offset, vp);
 }
 
 bool
-TypedDatum::obj_setSpecial(JSContext *cx, HandleObject obj,
+TypedObject::obj_setSpecial(JSContext *cx, HandleObject obj,
                              HandleSpecialId sid, MutableHandleValue vp,
                              bool strict)
 {
     RootedId id(cx, SPECIALID_TO_JSID(sid));
     return obj_setGeneric(cx, obj, id, vp, strict);
 }
 
 bool
-TypedDatum::obj_getGenericAttributes(JSContext *cx, HandleObject obj,
+TypedObject::obj_getGenericAttributes(JSContext *cx, HandleObject obj,
                                      HandleId id, unsigned *attrsp)
 {
     uint32_t index;
-    Rooted<TypedDatum *> datum(cx, &obj->as<TypedDatum>());
-    TypeRepresentation *typeRepr = datum->typeRepresentation();
+    Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
+    TypeRepresentation *typeRepr = typedObj->typeRepresentation();
 
     switch (typeRepr->kind()) {
       case TypeDescr::Scalar:
       case TypeDescr::Reference:
         break;
 
       case TypeDescr::X4:
         break;
@@ -1974,18 +2015,18 @@ TypedDatum::obj_getGenericAttributes(JSC
 
     return JSObject::getGenericAttributes(cx, proto, id, attrsp);
 }
 
 static bool
 IsOwnId(JSContext *cx, HandleObject obj, HandleId id)
 {
     uint32_t index;
-    Rooted<TypedDatum *> datum(cx, &obj->as<TypedDatum>());
-    TypeRepresentation *typeRepr = datum->typeRepresentation();
+    Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
+    TypeRepresentation *typeRepr = typedObj->typeRepresentation();
 
     switch (typeRepr->kind()) {
       case TypeDescr::Scalar:
       case TypeDescr::Reference:
       case TypeDescr::X4:
         return false;
 
       case TypeDescr::SizedArray:
@@ -1995,50 +2036,50 @@ IsOwnId(JSContext *cx, HandleObject obj,
       case TypeDescr::Struct:
         return typeRepr->asStruct()->fieldNamed(id) != nullptr;
     }
 
     return false;
 }
 
 bool
-TypedDatum::obj_setGenericAttributes(JSContext *cx, HandleObject obj,
+TypedObject::obj_setGenericAttributes(JSContext *cx, HandleObject obj,
                                        HandleId id, unsigned *attrsp)
 {
     if (IsOwnId(cx, obj, id))
         return ReportPropertyError(cx, JSMSG_CANT_REDEFINE_PROP, id);
 
     RootedObject proto(cx, obj->getProto());
     if (!proto) {
         *attrsp = 0;
         return true;
     }
 
     return JSObject::setGenericAttributes(cx, proto, id, attrsp);
 }
 
 bool
-TypedDatum::obj_deleteProperty(JSContext *cx, HandleObject obj,
+TypedObject::obj_deleteProperty(JSContext *cx, HandleObject obj,
                                 HandlePropertyName name, bool *succeeded)
 {
     Rooted<jsid> id(cx, NameToId(name));
     if (IsOwnId(cx, obj, id))
         return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
 
     RootedObject proto(cx, obj->getProto());
     if (!proto) {
         *succeeded = false;
         return true;
     }
 
     return JSObject::deleteProperty(cx, proto, name, succeeded);
 }
 
 bool
-TypedDatum::obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
+TypedObject::obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
                                bool *succeeded)
 {
     RootedId id(cx);
     if (!IndexToId(cx, index, &id))
         return false;
 
     if (IsOwnId(cx, obj, id))
         return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
@@ -2048,37 +2089,37 @@ TypedDatum::obj_deleteElement(JSContext 
         *succeeded = false;
         return true;
     }
 
     return JSObject::deleteElement(cx, proto, index, succeeded);
 }
 
 bool
-TypedDatum::obj_deleteSpecial(JSContext *cx, HandleObject obj,
+TypedObject::obj_deleteSpecial(JSContext *cx, HandleObject obj,
                                HandleSpecialId sid, bool *succeeded)
 {
     RootedObject proto(cx, obj->getProto());
     if (!proto) {
         *succeeded = false;
         return true;
     }
 
     return JSObject::deleteSpecial(cx, proto, sid, succeeded);
 }
 
 bool
-TypedDatum::obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
+TypedObject::obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
                            MutableHandleValue statep, MutableHandleId idp)
 {
     uint32_t index;
 
-    JS_ASSERT(obj->is<TypedDatum>());
-    Rooted<TypedDatum *> datum(cx, &obj->as<TypedDatum>());
-    TypeRepresentation *typeRepr = datum->typeRepresentation();
+    JS_ASSERT(obj->is<TypedObject>());
+    Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
+    TypeRepresentation *typeRepr = typedObj->typeRepresentation();
 
     switch (typeRepr->kind()) {
       case TypeDescr::Scalar:
       case TypeDescr::Reference:
       case TypeDescr::X4:
         switch (enum_op) {
           case JSENUMERATE_INIT_ALL:
           case JSENUMERATE_INIT:
@@ -2093,27 +2134,27 @@ TypedDatum::obj_enumerate(JSContext *cx,
         break;
 
       case TypeDescr::SizedArray:
       case TypeDescr::UnsizedArray:
         switch (enum_op) {
           case JSENUMERATE_INIT_ALL:
           case JSENUMERATE_INIT:
             statep.setInt32(0);
-            idp.set(INT_TO_JSID(datum->length()));
+            idp.set(INT_TO_JSID(typedObj->length()));
             break;
 
           case JSENUMERATE_NEXT:
             index = static_cast<int32_t>(statep.toInt32());
 
-            if (index < datum->length()) {
+            if (index < typedObj->length()) {
                 idp.set(INT_TO_JSID(index));
                 statep.setInt32(index + 1);
             } else {
-                JS_ASSERT(index == datum->length());
+                JS_ASSERT(index == typedObj->length());
                 statep.setNull();
             }
 
             break;
 
           case JSENUMERATE_DESTROY:
             statep.setNull();
             break;
@@ -2146,417 +2187,577 @@ TypedDatum::obj_enumerate(JSContext *cx,
         }
         break;
     }
 
     return true;
 }
 
 /* static */ size_t
-TypedDatum::dataOffset()
+TypedObject::dataOffset()
 {
-    return JSObject::getPrivateDataOffset(JS_DATUM_SLOTS);
+    // the offset of 7 is based on the alloc kind
+    return JSObject::getPrivateDataOffset(JS_TYPEDOBJ_SLOT_DATA);
+}
+
+void
+TypedObject::neuter(JSContext *cx)
+{
+    setSlot(JS_TYPEDOBJ_SLOT_LENGTH, Int32Value(0));
+    setSlot(JS_TYPEDOBJ_SLOT_BYTELENGTH, Int32Value(0));
+    setSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(0));
+    setPrivate(nullptr);
 }
 
 /******************************************************************************
  * Typed Objects
  */
 
-const Class TypedObject::class_ = {
+const Class TransparentTypedObject::class_ = {
     "TypedObject",
     Class::NON_NATIVE |
-    JSCLASS_HAS_RESERVED_SLOTS(JS_DATUM_SLOTS) |
+    JSCLASS_HAS_RESERVED_SLOTS(JS_TYPEDOBJ_SLOTS) |
     JSCLASS_HAS_PRIVATE |
     JSCLASS_IMPLEMENTS_BARRIERS,
     JS_PropertyStub,
     JS_DeletePropertyStub,
     JS_PropertyStub,
     JS_StrictPropertyStub,
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
-    TypedDatum::obj_finalize,
+    nullptr,        /* finalize    */
     nullptr,        /* call        */
     nullptr,        /* construct   */
     nullptr,        /* hasInstance */
-    TypedDatum::obj_trace,
+    TypedObject::obj_trace,
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
     {
-        TypedDatum::obj_lookupGeneric,
-        TypedDatum::obj_lookupProperty,
-        TypedDatum::obj_lookupElement,
-        TypedDatum::obj_lookupSpecial,
-        TypedDatum::obj_defineGeneric,
-        TypedDatum::obj_defineProperty,
-        TypedDatum::obj_defineElement,
-        TypedDatum::obj_defineSpecial,
-        TypedDatum::obj_getGeneric,
-        TypedDatum::obj_getProperty,
-        TypedDatum::obj_getElement,
-        TypedDatum::obj_getSpecial,
-        TypedDatum::obj_setGeneric,
-        TypedDatum::obj_setProperty,
-        TypedDatum::obj_setElement,
-        TypedDatum::obj_setSpecial,
-        TypedDatum::obj_getGenericAttributes,
-        TypedDatum::obj_setGenericAttributes,
-        TypedDatum::obj_deleteProperty,
-        TypedDatum::obj_deleteElement,
-        TypedDatum::obj_deleteSpecial,
+        TypedObject::obj_lookupGeneric,
+        TypedObject::obj_lookupProperty,
+        TypedObject::obj_lookupElement,
+        TypedObject::obj_lookupSpecial,
+        TypedObject::obj_defineGeneric,
+        TypedObject::obj_defineProperty,
+        TypedObject::obj_defineElement,
+        TypedObject::obj_defineSpecial,
+        TypedObject::obj_getGeneric,
+        TypedObject::obj_getProperty,
+        TypedObject::obj_getElement,
+        TypedObject::obj_getSpecial,
+        TypedObject::obj_setGeneric,
+        TypedObject::obj_setProperty,
+        TypedObject::obj_setElement,
+        TypedObject::obj_setSpecial,
+        TypedObject::obj_getGenericAttributes,
+        TypedObject::obj_setGenericAttributes,
+        TypedObject::obj_deleteProperty,
+        TypedObject::obj_deleteElement,
+        TypedObject::obj_deleteSpecial,
         nullptr, nullptr, // watch/unwatch
         nullptr,   /* slice */
-        TypedDatum::obj_enumerate,
+        TypedObject::obj_enumerate,
         nullptr, /* thisObject */
     }
 };
 
-/*static*/ TypedObject *
-TypedObject::createZeroed(JSContext *cx,
-                          HandleTypeDescr descr,
-                          int32_t length)
+static int32_t
+LengthForType(TypeDescr &descr)
 {
-    // Create unattached wrapper object.
-    Rooted<TypedObject*> obj(cx);
-    obj = createUnattached<TypedObject>(cx, descr, length);
-    if (!obj)
-        return nullptr;
-
-    // Allocate and initialize the memory for this instance.
-    // Also initialize the JS_DATUM_SLOT_LENGTH slot.
-    TypeRepresentation *typeRepr = descr->typeRepresentation();
-    switch (descr->kind()) {
+    switch (descr.kind()) {
       case TypeDescr::Scalar:
       case TypeDescr::Reference:
       case TypeDescr::Struct:
       case TypeDescr::X4:
-      {
-        uint8_t *memory =
-            (uint8_t*) cx->malloc_(descr->as<SizedTypeDescr>().size());
-        if (!memory)
-            return nullptr;
-        typeRepr->asSized()->initInstance(cx->runtime(), memory, 1);
-        obj->attach(memory);
-        return obj;
-      }
+        return 0;
 
       case TypeDescr::SizedArray:
-      {
-        uint8_t *memory =
-            (uint8_t*) cx->malloc_(descr->as<SizedTypeDescr>().size());
-        if (!memory)
-            return nullptr;
-        typeRepr->asSizedArray()->initInstance(cx->runtime(), memory, 1);
-        obj->attach(memory);
-        return obj;
-      }
+        return descr.as<SizedArrayTypeDescr>().length();
 
       case TypeDescr::UnsizedArray:
-      {
-        SizedTypeRepresentation *elementTypeRepr =
-            typeRepr->asUnsizedArray()->element();
-
-        int32_t totalSize;
-        if (!SafeMul(elementTypeRepr->size(), length, &totalSize)) {
-            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
-                                 JSMSG_TYPEDOBJECT_TOO_BIG);
-            return nullptr;
-        }
-
-        uint8_t *memory = (uint8_t*) JS_malloc(cx, totalSize);
-        if (!memory)
-            return nullptr;
-
-        if (length)
-            elementTypeRepr->initInstance(cx->runtime(), memory, length);
-        obj->attach(memory);
-        return obj;
-      }
+        return 0;
     }
 
-    MOZ_ASSUME_UNREACHABLE("Bad TypeRepresentation Kind");
+    MOZ_ASSUME_UNREACHABLE("Invalid kind");
+}
+
+static bool
+CheckOffset(int32_t offset,
+            int32_t size,
+            int32_t alignment,
+            int32_t bufferLength)
+{
+    JS_ASSERT(size >= 0);
+    JS_ASSERT(alignment >= 0);
+
+    // No negative offsets.
+    if (offset < 0)
+        return false;
+
+    // Offset (plus size) must be fully contained within the buffer.
+    if (offset > bufferLength)
+        return false;
+    if (offset + size < offset)
+        return false;
+    if (offset + size > bufferLength)
+        return false;
+
+    // Offset must be aligned.
+    if ((offset % alignment) != 0)
+        return false;
+
+    return true;
 }
 
 /*static*/ bool
-TypedObject::construct(JSContext *cx, unsigned int argc, Value *vp)
+TypedObject::constructSized(JSContext *cx, unsigned int argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    JS_ASSERT(args.callee().is<SizedTypeDescr>());
+    Rooted<SizedTypeDescr*> callee(cx, &args.callee().as<SizedTypeDescr>());
+
+    // Typed object constructors for sized types are overloaded in
+    // three ways, in order of precedence:
+    //
+    //   new TypeObj()
+    //   new TypeObj(buffer, [offset])
+    //   new TypeObj(data)
+
+    // Zero argument constructor:
+    if (args.length() == 0) {
+        int32_t length = LengthForType(*callee);
+        Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length));
+        if (!obj)
+            return false;
+        args.rval().setObject(*obj);
+        return true;
+    }
+
+    // Buffer constructor.
+    if (args[0].isObject() && args[0].toObject().is<ArrayBufferObject>()) {
+        Rooted<ArrayBufferObject*> buffer(cx);
+        buffer = &args[0].toObject().as<ArrayBufferObject>();
+
+        int32_t offset;
+        if (args.length() >= 2 && !args[1].isUndefined()) {
+            if (!args[1].isInt32()) {
+                JS_ReportErrorNumber(cx, js_GetErrorMessage,
+                                     nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
+                return false;
+            }
+
+            offset = args[1].toInt32();
+        } else {
+            offset = 0;
+        }
+
+        if (args.length() >= 3 && !args[2].isUndefined()) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage,
+                                 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
+            return false;
+        }
+
+        if (!CheckOffset(offset, callee->size(), callee->alignment(),
+                         buffer->byteLength()))
+        {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage,
+                                 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
+            return false;
+        }
+
+        Rooted<TypedObject*> obj(cx);
+        obj = TypedObject::createUnattached(cx, callee, LengthForType(*callee));
+        if (!obj)
+            return false;
+
+        obj->attach(*buffer, offset);
+        args.rval().setObject(*obj);
+        return true;
+    }
+
+    // Data constructor.
+    if (args[0].isObject()) {
+        // Create the typed object.
+        int32_t length = LengthForType(*callee);
+        Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length));
+        if (!obj)
+            return false;
+
+        // Initialize from `arg`.
+        if (!ConvertAndCopyTo(cx, obj, args[0]))
+            return false;
+        args.rval().setObject(*obj);
+        return true;
+    }
+
+    // Something bogus.
+    JS_ReportErrorNumber(cx, js_GetErrorMessage,
+                         nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
+    return false;
+}
+
+/*static*/ bool
+TypedObject::constructUnsized(JSContext *cx, unsigned int argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     JS_ASSERT(args.callee().is<TypeDescr>());
-    Rooted<TypeDescr*> callee(cx, &args.callee().as<TypeDescr>());
-    TypeRepresentation *typeRepr = callee->typeRepresentation();
-
-    // Determine the length based on the target type.
-    uint32_t nextArg = 0;
-    int32_t length = 0;
-    switch (typeRepr->kind()) {
-      case TypeDescr::Scalar:
-      case TypeDescr::Reference:
-      case TypeDescr::Struct:
-      case TypeDescr::X4:
-        length = 0;
-        break;
-
-      case TypeDescr::SizedArray:
-        length = typeRepr->asSizedArray()->length();
-        break;
-
-      case TypeDescr::UnsizedArray:
-        // First argument is a length.
-        if (nextArg >= argc || !args[nextArg].isInt32()) {
+    Rooted<UnsizedArrayTypeDescr*> callee(cx);
+    callee = &args.callee().as<UnsizedArrayTypeDescr>();
+
+    // Typed object constructors for unsized arrays are overloaded in
+    // four ways, in order of precedence:
+    //
+    //   new TypeObj(buffer, [offset, [length]]) // [1]
+    //   new TypeObj(length)                     // [1]
+    //   new TypeObj(data)
+    //   new TypeObj()
+    //
+    // [1] Explicit lengths only available for unsized arrays.
+
+    // Zero argument constructor:
+    if (args.length() == 0) {
+        Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, 0));
+        if (!obj)
+            return false;
+        args.rval().setObject(*obj);
+        return true;
+    }
+
+    // Length constructor.
+    if (args[0].isInt32()) {
+        int32_t length = args[0].toInt32();
+        Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length));
+        if (!obj)
+            return false;
+        args.rval().setObject(*obj);
+        return true;
+    }
+
+    // Buffer constructor.
+    if (args[0].isObject() && args[0].toObject().is<ArrayBufferObject>()) {
+        Rooted<ArrayBufferObject*> buffer(cx);
+        buffer = &args[0].toObject().as<ArrayBufferObject>();
+
+        int32_t offset;
+        if (args.length() >= 2 && !args[1].isUndefined()) {
+            if (!args[1].isInt32()) {
+                JS_ReportErrorNumber(cx, js_GetErrorMessage,
+                                     nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
+                return false;
+            }
+
+            offset = args[1].toInt32();
+        } else {
+            offset = 0;
+        }
+
+        if (!CheckOffset(offset, 0, callee->alignment(), buffer->byteLength())) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage,
-                                 nullptr, JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS,
-                                 "1", "array length");
+                                 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
             return false;
         }
-        length = args[nextArg++].toInt32();
-        break;
+
+        int32_t elemSize = callee->elementType().size();
+        int32_t bytesRemaining = buffer->byteLength() - offset;
+        int32_t maximumLength = bytesRemaining / elemSize;
+
+        int32_t length;
+        if (args.length() >= 3 && !args[2].isUndefined()) {
+            if (!args[2].isInt32()) {
+                JS_ReportErrorNumber(cx, js_GetErrorMessage,
+                                     nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
+                return false;
+            }
+            length = args[2].toInt32();
+
+            if (length < 0 || length > maximumLength) {
+                JS_ReportErrorNumber(cx, js_GetErrorMessage,
+                                     nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
+                return false;
+            }
+        } else {
+            if ((bytesRemaining % elemSize) != 0) {
+                JS_ReportErrorNumber(cx, js_GetErrorMessage,
+                                     nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
+                return false;
+            }
+
+            length = maximumLength;
+        }
+
+        Rooted<TypedObject*> obj(cx);
+        obj = TypedObject::createUnattached(cx, callee, length);
+        if (!obj)
+            return false;
+
+        obj->attach(args[0].toObject().as<ArrayBufferObject>(), offset);
     }
 
-    // Create zeroed wrapper object.
-    Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length));
-    if (!obj)
-        return nullptr;
-
-    if (nextArg < argc) {
-        RootedValue initial(cx, args[nextArg++]);
-        if (!ConvertAndCopyTo(cx, obj, initial))
+    // Data constructor for unsized values
+    if (args[0].isObject()) {
+        // Read length out of the object.
+        RootedObject arg(cx, &args[0].toObject());
+        RootedValue lengthVal(cx);
+        if (!JSObject::getProperty(cx, arg, arg, cx->names().length, &lengthVal))
+            return false;
+        if (!lengthVal.isInt32()) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage,
+                                 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
             return false;
+        }
+        int32_t length = lengthVal.toInt32();
+
+        // Check that length * elementSize does not overflow.
+        int32_t elementSize = callee->elementType().size();
+        int32_t byteLength;
+        if (!SafeMul(elementSize, length, &byteLength)) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage,
+                                 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
+            return false;
+        }
+
+        // Create the unsized array.
+        Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length));
+        if (!obj)
+            return false;
+
+        // Initialize from `arg`
+        if (!ConvertAndCopyTo(cx, obj, args[0]))
+            return false;
+        args.rval().setObject(*obj);
+        return true;
     }
 
-    args.rval().setObject(*obj);
-    return true;
+    // Something bogus.
+    JS_ReportErrorNumber(cx, js_GetErrorMessage,
+                         nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
+    return false;
 }
 
 /******************************************************************************
  * Handles
  */
 
-const Class TypedHandle::class_ = {
+const Class OpaqueTypedObject::class_ = {
     "Handle",
     Class::NON_NATIVE |
-    JSCLASS_HAS_RESERVED_SLOTS(JS_DATUM_SLOTS) |
+    JSCLASS_HAS_RESERVED_SLOTS(JS_TYPEDOBJ_SLOTS) |
     JSCLASS_HAS_PRIVATE |
     JSCLASS_IMPLEMENTS_BARRIERS,
     JS_PropertyStub,
     JS_DeletePropertyStub,
     JS_PropertyStub,
     JS_StrictPropertyStub,
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
-    TypedDatum::obj_finalize,
+    nullptr,        /* finalize    */
     nullptr,        /* call        */
     nullptr,        /* construct   */
     nullptr,        /* hasInstance */
-    TypedDatum::obj_trace,
+    TypedObject::obj_trace,
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
     {
-        TypedDatum::obj_lookupGeneric,
-        TypedDatum::obj_lookupProperty,
-        TypedDatum::obj_lookupElement,
-        TypedDatum::obj_lookupSpecial,
-        TypedDatum::obj_defineGeneric,
-        TypedDatum::obj_defineProperty,
-        TypedDatum::obj_defineElement,
-        TypedDatum::obj_defineSpecial,
-        TypedDatum::obj_getGeneric,
-        TypedDatum::obj_getProperty,
-        TypedDatum::obj_getElement,
-        TypedDatum::obj_getSpecial,
-        TypedDatum::obj_setGeneric,
-        TypedDatum::obj_setProperty,
-        TypedDatum::obj_setElement,
-        TypedDatum::obj_setSpecial,
-        TypedDatum::obj_getGenericAttributes,
-        TypedDatum::obj_setGenericAttributes,
-        TypedDatum::obj_deleteProperty,
-        TypedDatum::obj_deleteElement,
-        TypedDatum::obj_deleteSpecial,
+        TypedObject::obj_lookupGeneric,
+        TypedObject::obj_lookupProperty,
+        TypedObject::obj_lookupElement,
+        TypedObject::obj_lookupSpecial,
+        TypedObject::obj_defineGeneric,
+        TypedObject::obj_defineProperty,
+        TypedObject::obj_defineElement,
+        TypedObject::obj_defineSpecial,
+        TypedObject::obj_getGeneric,
+        TypedObject::obj_getProperty,
+        TypedObject::obj_getElement,
+        TypedObject::obj_getSpecial,
+        TypedObject::obj_setGeneric,
+        TypedObject::obj_setProperty,
+        TypedObject::obj_setElement,
+        TypedObject::obj_setSpecial,
+        TypedObject::obj_getGenericAttributes,
+        TypedObject::obj_setGenericAttributes,
+        TypedObject::obj_deleteProperty,
+        TypedObject::obj_deleteElement,
+        TypedObject::obj_deleteSpecial,
         nullptr, nullptr, // watch/unwatch
         nullptr, // slice
-        TypedDatum::obj_enumerate,
+        TypedObject::obj_enumerate,
         nullptr, /* thisObject */
     }
 };
 
-const JSFunctionSpec TypedHandle::handleStaticMethods[] = {
+const JSFunctionSpec OpaqueTypedObject::handleStaticMethods[] = {
     {"move", {nullptr, nullptr}, 3, 0, "HandleMove"},
     {"get", {nullptr, nullptr}, 1, 0, "HandleGet"},
     {"set", {nullptr, nullptr}, 2, 0, "HandleSet"},
     {"isHandle", {nullptr, nullptr}, 1, 0, "HandleTest"},
     JS_FS_END
 };
 
 /******************************************************************************
  * Intrinsics
  */
 
 bool
-js::NewTypedHandle(JSContext *cx, unsigned argc, Value *vp)
+js::NewOpaqueTypedObject(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    JS_ASSERT(argc == 1);
+    JS_ASSERT(args.length() == 1);
     JS_ASSERT(args[0].isObject() && args[0].toObject().is<SizedTypeDescr>());
 
     Rooted<SizedTypeDescr*> descr(cx, &args[0].toObject().as<SizedTypeDescr>());
-    int32_t length = DatumLengthFromType(*descr);
-    Rooted<TypedHandle*> obj(cx);
-    obj = TypedDatum::createUnattached<TypedHandle>(cx, descr, length);
+    int32_t length = TypedObjLengthFromType(*descr);
+    Rooted<TypedObject*> obj(cx);
+    obj = TypedObject::createUnattachedWithClass(cx, &OpaqueTypedObject::class_, descr, length);
     if (!obj)
         return false;
     args.rval().setObject(*obj);
     return true;
 }
 
 bool
-js::NewDerivedTypedDatum(JSContext *cx, unsigned argc, Value *vp)
+js::NewDerivedTypedObject(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    JS_ASSERT(argc == 3);
+    JS_ASSERT(args.length() == 3);
     JS_ASSERT(args[0].isObject() && args[0].toObject().is<SizedTypeDescr>());
-    JS_ASSERT(args[1].isObject() && args[1].toObject().is<TypedDatum>());
+    JS_ASSERT(args[1].isObject() && args[1].toObject().is<TypedObject>());
     JS_ASSERT(args[2].isInt32());
 
     Rooted<SizedTypeDescr*> descr(cx, &args[0].toObject().as<SizedTypeDescr>());
-    Rooted<TypedDatum*> datum(cx, &args[1].toObject().as<TypedDatum>());
+    Rooted<TypedObject*> typedObj(cx, &args[1].toObject().as<TypedObject>());
     int32_t offset = args[2].toInt32();
 
-    Rooted<TypedDatum*> obj(cx);
-    obj = TypedDatum::createDerived(cx, descr, datum, offset);
+    Rooted<TypedObject*> obj(cx);
+    obj = TypedObject::createDerived(cx, descr, typedObj, offset);
     if (!obj)
         return false;
 
     args.rval().setObject(*obj);
     return true;
 }
 
 bool
-js::AttachHandle(ThreadSafeContext *, unsigned argc, Value *vp)
+js::AttachTypedObject(ThreadSafeContext *, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    JS_ASSERT(argc == 3);
-    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedHandle>());
-    JS_ASSERT(args[1].isObject() && args[1].toObject().is<TypedDatum>());
+    JS_ASSERT(args.length() == 3);
+    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
+    JS_ASSERT(args[1].isObject() && args[1].toObject().is<TypedObject>());
     JS_ASSERT(args[2].isInt32());
 
-    TypedHandle &handle = args[0].toObject().as<TypedHandle>();
-    TypedDatum &target = args[1].toObject().as<TypedDatum>();
+    TypedObject &handle = args[0].toObject().as<TypedObject>();
+    TypedObject &target = args[1].toObject().as<TypedObject>();
+    JS_ASSERT(handle.typedMem() == nullptr); // must not be attached already
     size_t offset = args[2].toInt32();
     handle.attach(target, offset);
     return true;
 }
 
-JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::AttachHandleJitInfo, AttachHandleJitInfo,
-                                      js::AttachHandle);
+JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::AttachTypedObjectJitInfo,
+                                      AttachTypedObjectJitInfo,
+                                      js::AttachTypedObject);
 
 bool
 js::ObjectIsTypeDescr(ThreadSafeContext *, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    JS_ASSERT(argc == 1);
+    JS_ASSERT(args.length() == 1);
     JS_ASSERT(args[0].isObject());
     args.rval().setBoolean(args[0].toObject().is<TypeDescr>());
     return true;
 }
 
 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ObjectIsTypeDescrJitInfo, ObjectIsTypeDescrJitInfo,
                                       js::ObjectIsTypeDescr);
 
 bool
-js::ObjectIsTypeRepresentation(ThreadSafeContext *, unsigned argc, Value *vp)
+js::ObjectIsOpaqueTypedObject(ThreadSafeContext *, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    JS_ASSERT(argc == 1);
+    JS_ASSERT(args.length() == 1);
     JS_ASSERT(args[0].isObject());
-    args.rval().setBoolean(TypeRepresentation::isOwnerObject(args[0].toObject()));
-    return true;
-}
-
-JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ObjectIsTypeRepresentationJitInfo,
-                                      ObjectIsTypeRepresentationJitInfo,
-                                      js::ObjectIsTypeRepresentation);
-
-bool
-js::ObjectIsTypedHandle(ThreadSafeContext *, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    JS_ASSERT(argc == 1);
-    JS_ASSERT(args[0].isObject());
-    args.rval().setBoolean(args[0].toObject().is<TypedHandle>());
+    args.rval().setBoolean(args[0].toObject().is<OpaqueTypedObject>());
     return true;
 }
 
-JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ObjectIsTypedHandleJitInfo, ObjectIsTypedHandleJitInfo,
-                                      js::ObjectIsTypedHandle);
+JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ObjectIsOpaqueTypedObjectJitInfo,
+                                      ObjectIsOpaqueTypedObjectJitInfo,
+                                      js::ObjectIsOpaqueTypedObject);
 
 bool
-js::ObjectIsTypedObject(ThreadSafeContext *, unsigned argc, Value *vp)
+js::ObjectIsTransparentTypedObject(ThreadSafeContext *, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    JS_ASSERT(argc == 1);
+    JS_ASSERT(args.length() == 1);
     JS_ASSERT(args[0].isObject());
-    args.rval().setBoolean(args[0].toObject().is<TypedObject>());
+    args.rval().setBoolean(args[0].toObject().is<TransparentTypedObject>());
     return true;
 }
 
-JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ObjectIsTypedObjectJitInfo, ObjectIsTypedObjectJitInfo,
-                                      js::ObjectIsTypedObject);
+JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ObjectIsTransparentTypedObjectJitInfo,
+                                      ObjectIsTransparentTypedObjectJitInfo,
+                                      js::ObjectIsTransparentTypedObject);
 
 bool
-js::IsAttached(ThreadSafeContext *cx, unsigned argc, Value *vp)
+js::TypedObjectIsAttached(ThreadSafeContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedDatum>());
-    TypedDatum &datum = args[0].toObject().as<TypedDatum>();
-    args.rval().setBoolean(datum.typedMem() != nullptr);
+    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
+    TypedObject &typedObj = args[0].toObject().as<TypedObject>();
+    args.rval().setBoolean(typedObj.typedMem() != nullptr);
     return true;
 }
 
-JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::IsAttachedJitInfo, IsAttachedJitInfo, js::IsAttached);
+JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::TypedObjectIsAttachedJitInfo,
+                                      TypedObjectIsAttachedJitInfo,
+                                      js::TypedObjectIsAttached);
 
 bool
 js::ClampToUint8(ThreadSafeContext *, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    JS_ASSERT(argc == 1);
+    JS_ASSERT(args.length() == 1);
     JS_ASSERT(args[0].isNumber());
     args.rval().setNumber(ClampDoubleToUint8(args[0].toNumber()));
     return true;
 }
 
 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ClampToUint8JitInfo, ClampToUint8JitInfo,
                                       js::ClampToUint8);
 
 bool
 js::Memcpy(ThreadSafeContext *, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JS_ASSERT(args.length() == 5);
-    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedDatum>());
+    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
     JS_ASSERT(args[1].isInt32());
-    JS_ASSERT(args[2].isObject() && args[2].toObject().is<TypedDatum>());
+    JS_ASSERT(args[2].isObject() && args[2].toObject().is<TypedObject>());
     JS_ASSERT(args[3].isInt32());
     JS_ASSERT(args[4].isInt32());
 
-    TypedDatum &targetDatum = args[0].toObject().as<TypedDatum>();
+    TypedObject &targetTypedObj = args[0].toObject().as<TypedObject>();
     int32_t targetOffset = args[1].toInt32();
-    TypedDatum &sourceDatum = args[2].toObject().as<TypedDatum>();
+    TypedObject &sourceTypedObj = args[2].toObject().as<TypedObject>();
     int32_t sourceOffset = args[3].toInt32();
     int32_t size = args[4].toInt32();
 
     JS_ASSERT(targetOffset >= 0);
     JS_ASSERT(sourceOffset >= 0);
     JS_ASSERT(size >= 0);
-    JS_ASSERT((size_t) (size + targetOffset) <= targetDatum.size());
-    JS_ASSERT((size_t) (size + sourceOffset) <= sourceDatum.size());
-
-    uint8_t *target = targetDatum.typedMem(targetOffset);
-    uint8_t *source = sourceDatum.typedMem(sourceOffset);
+    JS_ASSERT((size_t) (size + targetOffset) <= targetTypedObj.size());
+    JS_ASSERT((size_t) (size + sourceOffset) <= sourceTypedObj.size());
+
+    uint8_t *target = targetTypedObj.typedMem(targetOffset);
+    uint8_t *source = sourceTypedObj.typedMem(sourceOffset);
     memcpy(target, source, size);
     args.rval().setUndefined();
     return true;
 }
 
 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::MemcpyJitInfo, MemcpyJitInfo, js::Memcpy);
 
 bool
@@ -2590,104 +2791,107 @@ js::GetInt32x4TypeDescr(JSContext *cx, u
 }
 
 #define JS_STORE_SCALAR_CLASS_IMPL(_constant, T, _name)                         \
 bool                                                                            \
 js::StoreScalar##T::Func(ThreadSafeContext *, unsigned argc, Value *vp)         \
 {                                                                               \
     CallArgs args = CallArgsFromVp(argc, vp);                                   \
     JS_ASSERT(args.length() == 3);                                              \
-    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedDatum>());       \
+    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());      \
     JS_ASSERT(args[1].isInt32());                                               \
     JS_ASSERT(args[2].isNumber());                                              \
                                                                                 \
-    TypedDatum &datum = args[0].toObject().as<TypedDatum>();                    \
+    TypedObject &typedObj = args[0].toObject().as<TypedObject>();               \
     int32_t offset = args[1].toInt32();                                         \
                                                                                 \
     /* Should be guaranteed by the typed objects API: */                        \
     JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                                    \
                                                                                 \
-    T *target = reinterpret_cast<T*>(datum.typedMem(offset));                   \
+    T *target = reinterpret_cast<T*>(typedObj.typedMem(offset));                \
     double d = args[2].toNumber();                                              \
     *target = ConvertScalar<T>(d);                                              \
     args.rval().setUndefined();                                                 \
     return true;                                                                \
 }                                                                               \
                                                                                 \
-JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::StoreScalar##T::JitInfo, StoreScalar##T, \
-                                       js::StoreScalar##T::Func);
+JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::StoreScalar##T::JitInfo,              \
+                                      StoreScalar##T,                           \
+                                      js::StoreScalar##T::Func);
 
 #define JS_STORE_REFERENCE_CLASS_IMPL(_constant, T, _name)                      \
 bool                                                                            \
 js::StoreReference##T::Func(ThreadSafeContext *, unsigned argc, Value *vp)      \
 {                                                                               \
     CallArgs args = CallArgsFromVp(argc, vp);                                   \
     JS_ASSERT(args.length() == 3);                                              \
-    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedDatum>());       \
+    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());      \
     JS_ASSERT(args[1].isInt32());                                               \
                                                                                 \
-    TypedDatum &datum = args[0].toObject().as<TypedDatum>();                    \
+    TypedObject &typedObj = args[0].toObject().as<TypedObject>();               \
     int32_t offset = args[1].toInt32();                                         \
                                                                                 \
     /* Should be guaranteed by the typed objects API: */                        \
     JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                                    \
                                                                                 \
-    T *target = reinterpret_cast<T*>(datum.typedMem(offset));                   \
+    T *target = reinterpret_cast<T*>(typedObj.typedMem(offset));                \
     store(target, args[2]);                                                     \
     args.rval().setUndefined();                                                 \
     return true;                                                                \
 }                                                                               \
                                                                                 \
- JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::StoreReference##T::JitInfo, StoreReference##T, \
-                                       js::StoreReference##T::Func);
-
-#define JS_LOAD_SCALAR_CLASS_IMPL(_constant, T, _name)                          \
-bool                                                                            \
-js::LoadScalar##T::Func(ThreadSafeContext *, unsigned argc, Value *vp)          \
-{                                                                               \
-    CallArgs args = CallArgsFromVp(argc, vp);                                   \
-    JS_ASSERT(args.length() == 2);                                              \
-    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedDatum>());       \
-    JS_ASSERT(args[1].isInt32());                                               \
-                                                                                \
-    TypedDatum &datum = args[0].toObject().as<TypedDatum>();                    \
-    int32_t offset = args[1].toInt32();                                         \
-                                                                                \
-    /* Should be guaranteed by the typed objects API: */                        \
-    JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                                    \
-                                                                                \
-    T *target = reinterpret_cast<T*>(datum.typedMem(offset));                   \
-    args.rval().setNumber((double) *target);                                    \
-    return true;                                                                \
-}                                                                               \
-                                                                                \
-JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::LoadScalar##T::JitInfo, LoadScalar##T, \
+JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::StoreReference##T::JitInfo,           \
+                                      StoreReference##T,                        \
+                                      js::StoreReference##T::Func);
+
+#define JS_LOAD_SCALAR_CLASS_IMPL(_constant, T, _name)                                  \
+bool                                                                                    \
+js::LoadScalar##T::Func(ThreadSafeContext *, unsigned argc, Value *vp)                  \
+{                                                                                       \
+    CallArgs args = CallArgsFromVp(argc, vp);                                           \
+    JS_ASSERT(args.length() == 2);                                                      \
+    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());              \
+    JS_ASSERT(args[1].isInt32());                                                       \
+                                                                                        \
+    TypedObject &typedObj = args[0].toObject().as<TypedObject>();                       \
+    int32_t offset = args[1].toInt32();                                                 \
+                                                                                        \
+    /* Should be guaranteed by the typed objects API: */                                \
+    JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                                            \
+                                                                                        \
+    T *target = reinterpret_cast<T*>(typedObj.typedMem(offset));                        \
+    args.rval().setNumber((double) *target);                                            \
+    return true;                                                                        \
+}                                                                                       \
+                                                                                        \
+JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::LoadScalar##T::JitInfo, LoadScalar##T,        \
                                       js::LoadScalar##T::Func);
 
 #define JS_LOAD_REFERENCE_CLASS_IMPL(_constant, T, _name)                       \
 bool                                                                            \
 js::LoadReference##T::Func(ThreadSafeContext *, unsigned argc, Value *vp)       \
 {                                                                               \
     CallArgs args = CallArgsFromVp(argc, vp);                                   \
     JS_ASSERT(args.length() == 2);                                              \
-    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedDatum>());       \
+    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());      \
     JS_ASSERT(args[1].isInt32());                                               \
                                                                                 \
-    TypedDatum &datum = args[0].toObject().as<TypedDatum>();                    \
+    TypedObject &typedObj = args[0].toObject().as<TypedObject>();               \
     int32_t offset = args[1].toInt32();                                         \
                                                                                 \
     /* Should be guaranteed by the typed objects API: */                        \
     JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                                    \
                                                                                 \
-    T *target = reinterpret_cast<T*>(datum.typedMem(offset));                   \
+    T *target = reinterpret_cast<T*>(typedObj.typedMem(offset));                \
     load(target, args.rval());                                                  \
     return true;                                                                \
 }                                                                               \
                                                                                 \
-JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::LoadReference##T::JitInfo, LoadReference##T, \
+JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::LoadReference##T::JitInfo,            \
+                                      LoadReference##T,                         \
                                       js::LoadReference##T::Func);
 
 // Because the precise syntax for storing values/objects/strings
 // differs, we abstract it away using specialized variants of the
 // private methods `store()` and `load()`.
 
 void
 StoreReferenceHeapValue::store(HeapValue *heap, const Value &v)
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef builtin_TypedObject_h
 #define builtin_TypedObject_h
 
 #include "jsobj.h"
 
 #include "builtin/TypedObjectConstants.h"
+#include "vm/ArrayBufferObject.h"
 
 /*
  * -------------
  * Typed Objects
  * -------------
  *
  * Typed objects are a special kind of JS object where the data is
  * given well-structured form. To use a typed object, users first
@@ -50,33 +51,43 @@
  *
  * Each type object is associated with a *type representation* (see
  * TypeRepresentation.h). Type representations are canonical versions
  * of type objects. We attach them to TI type objects and (eventually)
  * use them for shape guards etc. They are purely internal to the
  * engine and are not exposed to end users (though self-hosted code
  * sometimes accesses them).
  *
- * - Typed datums, objects, and handles:
+ * - Typed objects:
  *
- * A typed object is an instance of a type object. A handle is a
- * relocatable pointer that points into other typed objects. Both of them
- * are basically represented the same way, though they have distinct
- * js::Class entries. They are both subtypes of `TypedDatum`.
+ * A typed object is an instance of a *type object* (note the past
+ * participle). There is one class for *transparent* typed objects and
+ * one for *opaque* typed objects. These classes are equivalent in
+ * basically every way, except that requesting the backing buffer of
+ * an opaque typed object yields null. We use distinct js::Classes to
+ * avoid the need for an extra slot in every typed object.
  *
- * Both typed objects and handles are non-native objects that fully
- * override the property accessors etc. The overridden accessor
- * methods are the same in each and are defined in methods of
- * TypedDatum.
+ * Note that whether a typed object is opaque is not directly
+ * connected to its type. That is, opaque types are *always*
+ * represented by opaque typed objects, but you may have opaque typed
+ * objects for transparent types too. This can occur for two reasons:
+ * (1) a transparent type may be embedded within an opaque type or (2)
+ * users can choose to convert transparent typed objects into opaque
+ * ones to avoid giving access to the buffer itself.
  *
- * Typed datums may be attached or unattached. An unattached typed
- * datum has no memory associated with it; it is basically a null
- * pointer.  This can only happen when a new handle is created, since
- * typed object instances are always associated with memory at the
- * point of creation.
+ * Typed objects (no matter their class) are non-native objects that
+ * fully override the property accessors etc. The overridden accessor
+ * methods are the same in each and are defined in methods of
+ * TypedObject.
+ *
+ * Typed objects may be attached or unattached. An unattached typed
+ * object has no memory associated with it; it is basically a null
+ * pointer. When first created, objects are always attached, but they
+ * can become unattached if their buffer is neutered (note that this
+ * implies that typed objects of opaque types can never be unattached).
  *
  * When a new typed object instance is created, fresh memory is
  * allocated and set as that typed object's private field. The object
  * is then considered the *owner* of that memory: when the object is
  * collected, its finalizer will free the memory. The fact that an
  * object `o` owns its memory is indicated by setting its reserved
  * slot JS_TYPEDOBJ_SLOT_OWNER to `o` (a trivial cycle, in other
  * words).
@@ -139,45 +150,26 @@ class TypeDescr : public JSObject
 
     JSObject &typeRepresentationOwnerObj() const {
         return getReservedSlot(JS_DESCR_SLOT_TYPE_REPR).toObject();
     }
 
     TypeRepresentation *typeRepresentation() const;
 
     TypeDescr::Kind kind() const;
-};
+
+    bool opaque() const;
 
-/*
- * This object exists in order to encapsulate the typed object types
- * somewhat, rather than sticking them all into the global object.
- * Eventually it will go away and become a module.
- */
-class TypedObjectModuleObject : public JSObject {
-  public:
-    enum Slot {
-        ArrayTypePrototype,
-        StructTypePrototype,
-        SlotCount
-    };
-
-    static const Class class_;
-
-    static bool getSuitableClaspAndProto(JSContext *cx,
-                                         TypeDescr::Kind kind,
-                                         const Class **clasp,
-                                         MutableHandleObject proto);
+    size_t alignment() {
+        return getReservedSlot(JS_DESCR_SLOT_ALIGNMENT).toInt32();
+    }
 };
 
 typedef Handle<TypeDescr*> HandleTypeDescr;
 
-bool InitializeCommonTypeDescriptorProperties(JSContext *cx,
-                                              HandleTypeDescr obj,
-                                              HandleObject typeReprOwnerObj);
-
 class SizedTypeDescr : public TypeDescr
 {
   public:
     size_t size() {
         return getReservedSlot(JS_DESCR_SLOT_SIZE).toInt32();
     }
 };
 
@@ -298,16 +290,22 @@ class X4TypeDescr : public SizedTypeDesc
     static bool call(JSContext *cx, unsigned argc, Value *vp);
     static bool is(const Value &v);
 };
 
 #define JS_FOR_EACH_X4_TYPE_REPR(macro_)                             \
     macro_(X4TypeDescr::TYPE_INT32, int32_t, int32)                  \
     macro_(X4TypeDescr::TYPE_FLOAT32, float, float32)
 
+bool IsTypedObjectClass(const Class *clasp); // Defined in TypedArrayObject.h
+
+bool InitializeCommonTypeDescriptorProperties(JSContext *cx,
+                                              HandleTypeDescr obj,
+                                              HandleObject typeReprOwnerObj);
+
 /*
  * Properties and methods of the `ArrayType` meta type object. There
  * is no `class_` field because `ArrayType` is just a native
  * constructor function.
  */
 class ArrayMetaTypeDescr : public JSObject
 {
   private:
@@ -427,41 +425,60 @@ class StructTypeDescr : public SizedType
 
     // Return the offset of the field at index `index`.
     size_t fieldOffset(size_t index);
 };
 
 typedef Handle<StructTypeDescr*> HandleStructTypeDescr;
 
 /*
+ * This object exists in order to encapsulate the typed object types
+ * somewhat, rather than sticking them all into the global object.
+ * Eventually it will go away and become a module.
+ */
+class TypedObjectModuleObject : public JSObject {
+  public:
+    enum Slot {
+        ArrayTypePrototype,
+        StructTypePrototype,
+        SlotCount
+    };
+
+    static const Class class_;
+
+    static bool getSuitableClaspAndProto(JSContext *cx,
+                                         TypeDescr::Kind kind,
+                                         const Class **clasp,
+                                         MutableHandleObject proto);
+};
+
+/*
  * Base type for typed objects and handles. Basically any type whose
  * contents consist of typed memory.
  */
-class TypedDatum : public JSObject
+class TypedObject : public ArrayBufferViewObject
 {
   private:
-    static const bool IsTypedDatumClass = true;
+    static const bool IsTypedObjectClass = true;
 
     template<class T>
     static bool obj_getArrayElement(JSContext *cx,
-                                    Handle<TypedDatum*> datum,
+                                    Handle<TypedObject*> typedObj,
                                     Handle<TypeDescr*> typeDescr,
                                     uint32_t index,
                                     MutableHandleValue vp);
 
     template<class T>
     static bool obj_setArrayElement(JSContext *cx,
-                                    Handle<TypedDatum*> datum,
+                                    Handle<TypedObject*> typedObj,
                                     Handle<TypeDescr*> typeDescr,
                                     uint32_t index,
                                     MutableHandleValue vp);
 
   protected:
-    static void obj_finalize(js::FreeOp *op, JSObject *obj);
-
     static void obj_trace(JSTracer *trace, JSObject *object);
 
     static bool obj_lookupGeneric(JSContext *cx, HandleObject obj,
                                   HandleId id, MutableHandleObject objp,
                                   MutableHandleShape propp);
 
     static bool obj_lookupProperty(JSContext *cx, HandleObject obj,
                                    HandlePropertyName name,
@@ -533,66 +550,87 @@ class TypedDatum : public JSObject
     // Each typed object contains a void* pointer pointing at the
     // binary data that it represents. (That data may be owned by this
     // object or this object may alias data owned by someone else.)
     // This function returns the offset in bytes within the object
     // where the `void*` pointer can be found. It is intended for use
     // by the JIT.
     static size_t dataOffset();
 
-    static TypedDatum *createUnattachedWithClass(JSContext *cx,
+    // Helper for createUnattached()
+    static TypedObject *createUnattachedWithClass(JSContext *cx,
                                                  const Class *clasp,
                                                  HandleTypeDescr type,
                                                  int32_t length);
 
     // Creates an unattached typed object or handle (depending on the
     // type parameter T). Note that it is only legal for unattached
     // handles to escape to the end user; for non-handles, the caller
     // should always invoke one of the `attach()` methods below.
     //
     // Arguments:
     // - type: type object for resulting object
     // - length: 0 unless this is an array, otherwise the length
-    template<class T>
-    static T *createUnattached(JSContext *cx, HandleTypeDescr type,
-                               int32_t length);
+    static TypedObject *createUnattached(JSContext *cx, HandleTypeDescr type,
+                                        int32_t length);
 
-    // Creates a datum that aliases the memory pointed at by `owner`
-    // at the given offset. The datum will be a handle iff type is a
+    // Creates a typedObj that aliases the memory pointed at by `owner`
+    // at the given offset. The typedObj will be a handle iff type is a
     // handle and a typed object otherwise.
-    static TypedDatum *createDerived(JSContext *cx,
+    static TypedObject *createDerived(JSContext *cx,
                                      HandleSizedTypeDescr type,
-                                     Handle<TypedDatum*> typedContents,
+                                     Handle<TypedObject*> typedContents,
                                      size_t offset);
 
+    // Creates a new typed object whose memory is freshly allocated
+    // and initialized with zeroes (or, in the case of references, an
+    // appropriate default value).
+    static TypedObject *createZeroed(JSContext *cx,
+                                    HandleTypeDescr typeObj,
+                                    int32_t length);
 
-    // If `this` is the owner of the memory, use this.
-    void attach(uint8_t *mem);
+    // User-accessible constructor (`new TypeDescriptor(...)`)
+    // used for sized types. Note that the callee here is the *type descriptor*,
+    // not the typedObj.
+    static bool constructSized(JSContext *cx, unsigned argc, Value *vp);
+
+    // As `constructSized`, but for unsized array types.
+    static bool constructUnsized(JSContext *cx, unsigned argc, Value *vp);
 
-    // Otherwise, use this to attach to memory referenced by another datum.
-    void attach(TypedDatum &datum, uint32_t offset);
+    // Use this method when `buffer` is the owner of the memory.
+    void attach(ArrayBufferObject &buffer, int32_t offset);
+
+    // Otherwise, use this to attach to memory referenced by another typedObj.
+    void attach(TypedObject &typedObj, int32_t offset);
 
-    TypedDatum &owner() const {
-        return getReservedSlot(JS_DATUM_SLOT_OWNER).toObject().as<TypedDatum>();
+    // Invoked when array buffer is transferred elsewhere
+    void neuter(JSContext *cx);
+
+    int32_t offset() const {
+        return getReservedSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET).toInt32();
+    }
+
+    ArrayBufferObject &owner() const {
+        return getReservedSlot(JS_TYPEDOBJ_SLOT_OWNER).toObject().as<ArrayBufferObject>();
     }
 
     TypeDescr &typeDescr() const {
-        return getReservedSlot(JS_DATUM_SLOT_TYPE_DESCR).toObject().as<TypeDescr>();
+        return getReservedSlot(JS_TYPEDOBJ_SLOT_TYPE_DESCR).toObject().as<TypeDescr>();
     }
 
     TypeRepresentation *typeRepresentation() const {
         return typeDescr().typeRepresentation();
     }
 
     uint8_t *typedMem() const {
         return (uint8_t*) getPrivate();
     }
 
     size_t length() const {
-        return getReservedSlot(JS_DATUM_SLOT_LENGTH).toInt32();
+        return getReservedSlot(JS_TYPEDOBJ_SLOT_LENGTH).toInt32();
     }
 
     size_t size() const {
         switch (typeDescr().kind()) {
           case TypeDescr::Scalar:
           case TypeDescr::X4:
           case TypeDescr::Reference:
           case TypeDescr::Struct:
@@ -613,113 +651,88 @@ class TypedDatum : public JSObject
         // 0-sized value. (In other words, we maintain the invariant
         // that `offset + size <= size()` -- this is always checked in
         // the caller's side.)
         JS_ASSERT(offset <= size());
         return typedMem() + offset;
     }
 };
 
-typedef Handle<TypedDatum*> HandleTypedDatum;
+typedef Handle<TypedObject*> HandleTypedObject;
 
-class TypedObject : public TypedDatum
+class TransparentTypedObject : public TypedObject
 {
   public:
     static const Class class_;
-
-    // Creates a new typed object whose memory is freshly allocated
-    // and initialized with zeroes (or, in the case of references, an
-    // appropriate default value).
-    static TypedObject *createZeroed(JSContext *cx,
-                                     HandleTypeDescr typeObj,
-                                     int32_t length);
-
-    // user-accessible constructor (`new TypeDescriptor(...)`)
-    static bool construct(JSContext *cx, unsigned argc, Value *vp);
 };
 
-typedef Handle<TypedObject*> HandleTypedObject;
+typedef Handle<TransparentTypedObject*> HandleTransparentTypedObject;
 
-class TypedHandle : public TypedDatum
+class OpaqueTypedObject : public TypedObject
 {
   public:
     static const Class class_;
     static const JSFunctionSpec handleStaticMethods[];
 };
 
 /*
- * Usage: NewTypedHandle(typeObj)
+ * Usage: NewOpaqueTypedObject(typeObj)
  *
  * Constructs a new, unattached instance of `Handle`.
  */
-bool NewTypedHandle(JSContext *cx, unsigned argc, Value *vp);
+bool NewOpaqueTypedObject(JSContext *cx, unsigned argc, Value *vp);
 
 /*
- * Usage: NewTypedHandle(typeObj)
+ * Usage: NewDerivedTypedObject(typeObj, owner, offset)
  *
  * Constructs a new, unattached instance of `Handle`.
  */
-bool NewTypedHandle(JSContext *cx, unsigned argc, Value *vp);
+bool NewDerivedTypedObject(JSContext *cx, unsigned argc, Value *vp);
 
 /*
- * Usage: NewDerivedTypedDatum(typeObj, owner, offset)
+ * Usage: AttachTypedObject(typedObj, newDatum, newOffset)
  *
- * Constructs a new, unattached instance of `Handle`.
- */
-bool NewDerivedTypedDatum(JSContext *cx, unsigned argc, Value *vp);
-
-/*
- * Usage: AttachHandle(handle, newOwner, newOffset)
- *
- * Moves `handle` to point at the memory owned by `newOwner` with
+ * Moves `typedObj` to point at the memory referenced by `newDatum` with
  * the offset `newOffset`.
  */
-bool AttachHandle(ThreadSafeContext *cx, unsigned argc, Value *vp);
-extern const JSJitInfo AttachHandleJitInfo;
+bool AttachTypedObject(ThreadSafeContext *cx, unsigned argc, Value *vp);
+extern const JSJitInfo AttachTypedObjectJitInfo;
 
 /*
  * Usage: ObjectIsTypeDescr(obj)
  *
  * True if `obj` is a type object.
  */
 bool ObjectIsTypeDescr(ThreadSafeContext *cx, unsigned argc, Value *vp);
 extern const JSJitInfo ObjectIsTypeDescrJitInfo;
 
 /*
- * Usage: ObjectIsTypeRepresentation(obj)
- *
- * True if `obj` is a type representation object.
- */
-bool ObjectIsTypeRepresentation(ThreadSafeContext *cx, unsigned argc, Value *vp);
-extern const JSJitInfo ObjectIsTypeRepresentationJitInfo;
-
-/*
- * Usage: ObjectIsTypedHandle(obj)
+ * Usage: ObjectIsOpaqueTypedObject(obj)
  *
  * True if `obj` is a handle.
  */
-bool ObjectIsTypedHandle(ThreadSafeContext *cx, unsigned argc, Value *vp);
-extern const JSJitInfo ObjectIsTypedHandleJitInfo;
+bool ObjectIsOpaqueTypedObject(ThreadSafeContext *cx, unsigned argc, Value *vp);
+extern const JSJitInfo ObjectIsOpaqueTypedObjectJitInfo;
 
 /*
- * Usage: ObjectIsTypedObject(obj)
+ * Usage: ObjectIsTransparentTypedObject(obj)
  *
  * True if `obj` is a typed object.
  */
-bool ObjectIsTypedObject(ThreadSafeContext *cx, unsigned argc, Value *vp);
-extern const JSJitInfo ObjectIsTypedObjectJitInfo;
+bool ObjectIsTransparentTypedObject(ThreadSafeContext *cx, unsigned argc, Value *vp);
+extern const JSJitInfo ObjectIsTransparentTypedObjectJitInfo;
 
 /*
- * Usage: IsAttached(obj)
+ * Usage: TypedObjectIsAttached(obj)
  *
- * Given a TypedDatum `obj`, returns true if `obj` is
+ * Given a TypedObject `obj`, returns true if `obj` is
  * "attached" (i.e., its data pointer is nullptr).
  */
-bool IsAttached(ThreadSafeContext *cx, unsigned argc, Value *vp);
-extern const JSJitInfo IsAttachedJitInfo;
+bool TypedObjectIsAttached(ThreadSafeContext *cx, unsigned argc, Value *vp);
+extern const JSJitInfo TypedObjectIsAttachedJitInfo;
 
 /*
  * Usage: ClampToUint8(v)
  *
  * Same as the C function ClampDoubleToUint8. `v` must be a number.
  */
 bool ClampToUint8(ThreadSafeContext *cx, unsigned argc, Value *vp);
 extern const JSJitInfo ClampToUint8JitInfo;
@@ -747,25 +760,25 @@ extern const JSJitInfo MemcpyJitInfo;
  * to access them; eventually this should be linked into the module
  * system.
  */
 bool GetTypedObjectModule(JSContext *cx, unsigned argc, Value *vp);
 
 /*
  * Usage: GetFloat32x4TypeDescr()
  *
- * Returns the float32x4 type object. SIMD pseudo-module must have 
+ * Returns the float32x4 type object. SIMD pseudo-module must have
  * been initialized for this to be safe.
  */
 bool GetFloat32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp);
 
 /*
  * Usage: GetInt32x4TypeDescr()
  *
- * Returns the int32x4 type object. SIMD pseudo-module must have 
+ * Returns the int32x4 type object. SIMD pseudo-module must have
  * been initialized for this to be safe.
  */
 bool GetInt32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp);
 
 /*
  * Usage: Store_int8(targetDatum, targetOffset, value)
  *        ...
  *        Store_uint8(targetDatum, targetOffset, value)
@@ -846,16 +859,23 @@ class LoadReference##T {                
 
 // I was using templates for this stuff instead of macros, but ran
 // into problems with the Unagi compiler.
 JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_STORE_SCALAR_CLASS_DEFN)
 JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_LOAD_SCALAR_CLASS_DEFN)
 JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_STORE_REFERENCE_CLASS_DEFN)
 JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_LOAD_REFERENCE_CLASS_DEFN)
 
+inline bool
+IsTypedObjectClass(const Class *class_)
+{
+    return class_ == &TransparentTypedObject::class_ ||
+           class_ == &OpaqueTypedObject::class_;
+}
+
 } // namespace js
 
 JSObject *
 js_InitTypedObjectModuleObject(JSContext *cx, JS::HandleObject obj);
 
 template <>
 inline bool
 JSObject::is<js::SimpleTypeDescr>() const
@@ -879,15 +899,15 @@ inline bool
 JSObject::is<js::TypeDescr>() const
 {
     return is<js::SizedTypeDescr>() ||
            is<js::UnsizedArrayTypeDescr>();
 }
 
 template <>
 inline bool
-JSObject::is<js::TypedDatum>() const
+JSObject::is<js::TypedObject>() const
 {
-    return is<js::TypedObject>() || is<js::TypedHandle>();
+    return IsTypedObjectClass(getClass());
 }
 
 #endif /* builtin_TypedObject_h */
 
--- a/js/src/builtin/TypedObject.js
+++ b/js/src/builtin/TypedObject.js
@@ -24,29 +24,33 @@
     UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_STRUCT_FIELD_NAMES)
 #define DESCR_STRUCT_FIELD_TYPES(obj) \
     UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_STRUCT_FIELD_TYPES)
 #define DESCR_STRUCT_FIELD_OFFSETS(obj) \
     UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS)
 
 // Typed object slots
 
-#define DATUM_TYPE_DESCR(obj) \
-    UnsafeGetReservedSlot(obj, JS_DATUM_SLOT_TYPE_DESCR)
-#define DATUM_OWNER(obj) \
-    UnsafeGetReservedSlot(obj, JS_DATUM_SLOT_OWNER)
-#define DATUM_LENGTH(obj) \
-    TO_INT32(UnsafeGetReservedSlot(obj, JS_DATUM_SLOT_LENGTH))
+#define TYPEDOBJ_BYTEOFFSET(obj) \
+    TO_INT32(UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_BYTEOFFSET))
+#define TYPEDOBJ_BYTELENGTH(obj) \
+    TO_INT32(UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_BYTELENGTH))
+#define TYPEDOBJ_TYPE_DESCR(obj) \
+    UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_TYPE_DESCR)
+#define TYPEDOBJ_OWNER(obj) \
+    UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_OWNER)
+#define TYPEDOBJ_LENGTH(obj) \
+    TO_INT32(UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_LENGTH))
 
 #define HAS_PROPERTY(obj, prop) \
     callFunction(std_Object_hasOwnProperty, obj, prop)
 
-function DATUM_TYPE_REPR(obj) {
+function TYPEDOBJ_TYPE_REPR(obj) {
   // Eventually this will be a slot on typed objects
-  return DESCR_TYPE_REPR(DATUM_TYPE_DESCR(obj));
+  return DESCR_TYPE_REPR(TYPEDOBJ_TYPE_DESCR(obj));
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // DescrToSource
 //
 // Converts a type descriptor to a descriptive string
 
 // toSource() for type descriptors.
@@ -130,58 +134,58 @@ function DescrToSource(descr) {
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // TypedObjectPointer
 //
 // TypedObjectPointers are internal structs used to represent a
 // pointer into typed object memory. They pull together:
 // - descr: the type descriptor
-// - datum: the typed object that contains the allocated block of memory
+// - typedObj: the typed object that contains the allocated block of memory
 // - offset: an offset into that typed object
 //
 // They are basically equivalent to a typed object, except that they
 // offer lots of internal unsafe methods and are not native objects.
 // These should never escape into user code; ideally ion would stack
 // allocate them.
 //
 // Most `TypedObjectPointers` methods are written in a "chaining"
 // style, meaning that they return `this`. This is true even though
 // they mutate the receiver in place, because it makes for prettier
 // code.
 
-function TypedObjectPointer(descr, datum, offset) {
+function TypedObjectPointer(descr, typedObj, offset) {
   assert(IsObject(descr) && ObjectIsTypeDescr(descr), "Not descr");
-  assert(IsObject(datum) && ObjectIsTypedDatum(datum), "Not datum");
+  assert(IsObject(typedObj) && ObjectIsTypedObject(typedObj), "Not typedObj");
   assert(TO_INT32(offset) === offset, "offset not int");
 
   this.descr = descr;
-  this.datum = datum;
+  this.typedObj = typedObj;
   this.offset = offset;
 }
 
 MakeConstructible(TypedObjectPointer, {});
 
-TypedObjectPointer.fromTypedDatum = function(typed) {
-  return new TypedObjectPointer(DATUM_TYPE_DESCR(typed), typed, 0);
+TypedObjectPointer.fromTypedObject = function(typed) {
+  return new TypedObjectPointer(TYPEDOBJ_TYPE_DESCR(typed), typed, 0);
 }
 
 #ifdef DEBUG
 TypedObjectPointer.prototype.toString = function() {
   return "Ptr(" + DescrToSource(this.descr) + " @ " + this.offset + ")";
 };
 #endif
 
 TypedObjectPointer.prototype.copy = function() {
-  return new TypedObjectPointer(this.descr, this.datum, this.offset);
+  return new TypedObjectPointer(this.descr, this.typedObj, this.offset);
 };
 
 TypedObjectPointer.prototype.reset = function(inPtr) {
   this.descr = inPtr.descr;
-  this.datum = inPtr.datum;
+  this.typedObj = inPtr.typedObj;
   this.offset = inPtr.offset;
   return this;
 };
 
 TypedObjectPointer.prototype.bump = function(size) {
   assert(TO_INT32(this.offset) === this.offset, "current offset not int");
   assert(TO_INT32(size) === size, "size not int");
   this.offset += size;
@@ -194,17 +198,17 @@ TypedObjectPointer.prototype.kind = func
 // Extract the length. This does a switch on kind, so it's
 // best if we can avoid it.
 TypedObjectPointer.prototype.length = function() {
   switch (this.kind()) {
   case JS_TYPEREPR_SIZED_ARRAY_KIND:
     return DESCR_SIZED_ARRAY_LENGTH(this.descr);
 
   case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
-    return this.datum.length;
+    return this.typedObj.length;
   }
   assert(false, "Invalid kind for length");
   return false;
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // Moving the pointer
 //
@@ -219,33 +223,33 @@ TypedObjectPointer.prototype.moveTo = fu
   case JS_TYPEREPR_REFERENCE_KIND:
   case JS_TYPEREPR_X4_KIND:
     break;
 
   case JS_TYPEREPR_SIZED_ARRAY_KIND:
     return this.moveToArray(propName, DESCR_SIZED_ARRAY_LENGTH(this.descr));
 
   case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
-    return this.moveToArray(propName, this.datum.length);
+    return this.moveToArray(propName, this.typedObj.length);
 
   case JS_TYPEREPR_STRUCT_KIND:
     if (HAS_PROPERTY(this.descr.fieldTypes, propName))
       return this.moveToField(propName);
     break;
   }
 
   ThrowError(JSMSG_TYPEDOBJECT_NO_SUCH_PROP, propName);
   return undefined;
 };
 
 TypedObjectPointer.prototype.moveToArray = function(propName, length) {
   // For an array, property must be an element. Note that we take
   // the length as an argument rather than loading it from the descriptor.
   // This is because this same helper is used for *unsized arrays*, where
-  // the length is drawn from the datum, and *sized arrays*, where the
+  // the length is drawn from the typedObj, and *sized arrays*, where the
   // length is drawn from the type.
   var index = TO_INT32(propName);
   if (index === propName && index >= 0 && index < length)
     return this.moveToElem(index);
 
   ThrowError(JSMSG_TYPEDOBJECT_NO_SUCH_PROP, propName);
   return undefined;
 }
@@ -313,19 +317,19 @@ TypedObjectPointer.prototype.moveToField
 //
 // The methods in this section read from the memory pointed at
 // by `this` and produce JS values. This process is called *reification*
 // in the spec.
 
 // Reifies the value referenced by the pointer, meaning that it
 // returns a new object pointing at the value. If the value is
 // a scalar, it will return a JS number, but otherwise the reified
-// result will be a datum of the same class as the ptr's datum.
+// result will be a typedObj of the same class as the ptr's typedObj.
 TypedObjectPointer.prototype.get = function() {
-  assert(ObjectIsAttached(this.datum), "get() called with unattached datum");
+  assert(TypedObjectIsAttached(this.typedObj), "get() called with unattached typedObj");
 
   switch (this.kind()) {
   case JS_TYPEREPR_SCALAR_KIND:
     return this.getScalar();
 
   case JS_TYPEREPR_REFERENCE_KIND:
     return this.getReference();
 
@@ -342,113 +346,113 @@ TypedObjectPointer.prototype.get = funct
 
   assert(false, "Unhandled kind: " + this.kind());
   return undefined;
 }
 
 TypedObjectPointer.prototype.getDerived = function() {
   assert(!TypeDescrIsSimpleType(this.descr),
          "getDerived() used with simple type");
-  return NewDerivedTypedDatum(this.descr, this.datum, this.offset);
+  return NewDerivedTypedObject(this.descr, this.typedObj, this.offset);
 }
 
 TypedObjectPointer.prototype.getScalar = function() {
   var type = DESCR_TYPE(this.descr);
   switch (type) {
   case JS_SCALARTYPEREPR_INT8:
-    return Load_int8(this.datum, this.offset);
+    return Load_int8(this.typedObj, this.offset);
 
   case JS_SCALARTYPEREPR_UINT8:
   case JS_SCALARTYPEREPR_UINT8_CLAMPED:
-    return Load_uint8(this.datum, this.offset);
+    return Load_uint8(this.typedObj, this.offset);
 
   case JS_SCALARTYPEREPR_INT16:
-    return Load_int16(this.datum, this.offset);
+    return Load_int16(this.typedObj, this.offset);
 
   case JS_SCALARTYPEREPR_UINT16:
-    return Load_uint16(this.datum, this.offset);
+    return Load_uint16(this.typedObj, this.offset);
 
   case JS_SCALARTYPEREPR_INT32:
-    return Load_int32(this.datum, this.offset);
+    return Load_int32(this.typedObj, this.offset);
 
   case JS_SCALARTYPEREPR_UINT32:
-    return Load_uint32(this.datum, this.offset);
+    return Load_uint32(this.typedObj, this.offset);
 
   case JS_SCALARTYPEREPR_FLOAT32:
-    return Load_float32(this.datum, this.offset);
+    return Load_float32(this.typedObj, this.offset);
 
   case JS_SCALARTYPEREPR_FLOAT64:
-    return Load_float64(this.datum, this.offset);
+    return Load_float64(this.typedObj, this.offset);
   }
 
   assert(false, "Unhandled scalar type: " + type);
   return undefined;
 }
 
 TypedObjectPointer.prototype.getReference = function() {
   var type = DESCR_TYPE(this.descr);
   switch (type) {
   case JS_REFERENCETYPEREPR_ANY:
-    return Load_Any(this.datum, this.offset);
+    return Load_Any(this.typedObj, this.offset);
 
   case JS_REFERENCETYPEREPR_OBJECT:
-    return Load_Object(this.datum, this.offset);
+    return Load_Object(this.typedObj, this.offset);
 
   case JS_REFERENCETYPEREPR_STRING:
-    return Load_string(this.datum, this.offset);
+    return Load_string(this.typedObj, this.offset);
   }
 
   assert(false, "Unhandled scalar type: " + type);
   return undefined;
 }
 
 TypedObjectPointer.prototype.getX4 = function() {
   var type = DESCR_TYPE(this.descr);
   switch (type) {
   case JS_X4TYPEREPR_FLOAT32:
-    var x = Load_float32(this.datum, this.offset + 0);
-    var y = Load_float32(this.datum, this.offset + 4);
-    var z = Load_float32(this.datum, this.offset + 8);
-    var w = Load_float32(this.datum, this.offset + 12);
+    var x = Load_float32(this.typedObj, this.offset + 0);
+    var y = Load_float32(this.typedObj, this.offset + 4);
+    var z = Load_float32(this.typedObj, this.offset + 8);
+    var w = Load_float32(this.typedObj, this.offset + 12);
     return GetFloat32x4TypeDescr()(x, y, z, w);
 
   case JS_X4TYPEREPR_INT32:
-    var x = Load_int32(this.datum, this.offset + 0);
-    var y = Load_int32(this.datum, this.offset + 4);
-    var z = Load_int32(this.datum, this.offset + 8);
-    var w = Load_int32(this.datum, this.offset + 12);
+    var x = Load_int32(this.typedObj, this.offset + 0);
+    var y = Load_int32(this.typedObj, this.offset + 4);
+    var z = Load_int32(this.typedObj, this.offset + 8);
+    var w = Load_int32(this.typedObj, this.offset + 12);
     return GetInt32x4TypeDescr()(x, y, z, w);
   }
 
   assert(false, "Unhandled x4 type: " + type);
   return undefined;
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // Setting values
 //
 // The methods in this section modify the data pointed at by `this`.
 
 // Assigns `fromValue` to the memory pointed at by `this`, adapting it
 // to `typeRepr` as needed. This is the most general entry point and
 // works for any type.
 TypedObjectPointer.prototype.set = function(fromValue) {
-  assert(ObjectIsAttached(this.datum), "set() called with unattached datum");
+  assert(TypedObjectIsAttached(this.typedObj), "set() called with unattached typedObj");
 
   // Fast path: `fromValue` is a typed object with same type
   // representation as the destination. In that case, we can just do a
   // memcpy.
-  if (IsObject(fromValue) && ObjectIsTypedDatum(fromValue)) {
+  if (IsObject(fromValue) && ObjectIsTypedObject(fromValue)) {
     var typeRepr = DESCR_TYPE_REPR(this.descr);
-    if (!typeRepr.variable && DATUM_TYPE_REPR(fromValue) === typeRepr) {
-      if (!ObjectIsAttached(fromValue))
+    if (!typeRepr.variable && TYPEDOBJ_TYPE_REPR(fromValue) === typeRepr) {
+      if (!TypedObjectIsAttached(fromValue))
         ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
 
       var size = DESCR_SIZE(this.descr);
-      Memcpy(this.datum, this.offset, fromValue, 0, size);
+      Memcpy(this.typedObj, this.offset, fromValue, 0, size);
       return;
     }
   }
 
   switch (this.kind()) {
   case JS_TYPEREPR_SCALAR_KIND:
     this.setScalar(fromValue);
     return;
@@ -462,17 +466,17 @@ TypedObjectPointer.prototype.set = funct
     return;
 
   case JS_TYPEREPR_SIZED_ARRAY_KIND:
     if (this.setArray(fromValue, DESCR_SIZED_ARRAY_LENGTH(this.descr)))
       return;
     break;
 
   case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
-    if (this.setArray(fromValue, this.datum.length))
+    if (this.setArray(fromValue, this.typedObj.length))
       return;
     break;
 
   case JS_TYPEREPR_STRUCT_KIND:
     if (!IsObject(fromValue))
       break;
 
     // Adapt each field.
@@ -514,66 +518,66 @@ TypedObjectPointer.prototype.setArray = 
 // Sets `fromValue` to `this` assuming that `this` is a scalar type.
 TypedObjectPointer.prototype.setScalar = function(fromValue) {
   assert(this.kind() == JS_TYPEREPR_SCALAR_KIND,
          "setScalar called with non-scalar");
 
   var type = DESCR_TYPE(this.descr);
   switch (type) {
   case JS_SCALARTYPEREPR_INT8:
-    return Store_int8(this.datum, this.offset,
+    return Store_int8(this.typedObj, this.offset,
                      TO_INT32(fromValue) & 0xFF);
 
   case JS_SCALARTYPEREPR_UINT8:
-    return Store_uint8(this.datum, this.offset,
+    return Store_uint8(this.typedObj, this.offset,
                       TO_UINT32(fromValue) & 0xFF);
 
   case JS_SCALARTYPEREPR_UINT8_CLAMPED:
     var v = ClampToUint8(+fromValue);
-    return Store_int8(this.datum, this.offset, v);
+    return Store_int8(this.typedObj, this.offset, v);
 
   case JS_SCALARTYPEREPR_INT16:
-    return Store_int16(this.datum, this.offset,
+    return Store_int16(this.typedObj, this.offset,
                       TO_INT32(fromValue) & 0xFFFF);
 
   case JS_SCALARTYPEREPR_UINT16:
-    return Store_uint16(this.datum, this.offset,
+    return Store_uint16(this.typedObj, this.offset,
                        TO_UINT32(fromValue) & 0xFFFF);
 
   case JS_SCALARTYPEREPR_INT32:
-    return Store_int32(this.datum, this.offset,
+    return Store_int32(this.typedObj, this.offset,
                       TO_INT32(fromValue));
 
   case JS_SCALARTYPEREPR_UINT32:
-    return Store_uint32(this.datum, this.offset,
+    return Store_uint32(this.typedObj, this.offset,
                        TO_UINT32(fromValue));
 
   case JS_SCALARTYPEREPR_FLOAT32:
-    return Store_float32(this.datum, this.offset, +fromValue);
+    return Store_float32(this.typedObj, this.offset, +fromValue);
 
   case JS_SCALARTYPEREPR_FLOAT64:
-    return Store_float64(this.datum, this.offset, +fromValue);
+    return Store_float64(this.typedObj, this.offset, +fromValue);
   }
 
   assert(false, "Unhandled scalar type: " + type);
   return undefined;
 }
 
 TypedObjectPointer.prototype.setReference = function(fromValue) {
   var type = DESCR_TYPE(this.descr);
   switch (type) {
   case JS_REFERENCETYPEREPR_ANY:
-    return Store_Any(this.datum, this.offset, fromValue);
+    return Store_Any(this.typedObj, this.offset, fromValue);
 
   case JS_REFERENCETYPEREPR_OBJECT:
     var value = (fromValue === null ? fromValue : ToObject(fromValue));
-    return Store_Object(this.datum, this.offset, value);
+    return Store_Object(this.typedObj, this.offset, value);
 
   case JS_REFERENCETYPEREPR_STRING:
-    return Store_string(this.datum, this.offset, ToString(fromValue));
+    return Store_string(this.typedObj, this.offset, ToString(fromValue));
   }
 
   assert(false, "Unhandled scalar type: " + type);
   return undefined;
 }
 
 // Sets `fromValue` to `this` assuming that `this` is a scalar type.
 TypedObjectPointer.prototype.setX4 = function(fromValue) {
@@ -588,76 +592,76 @@ TypedObjectPointer.prototype.setX4 = fun
 
 ///////////////////////////////////////////////////////////////////////////
 // C++ Wrappers
 //
 // These helpers are invoked by C++ code or used as method bodies.
 
 // Wrapper for use from C++ code.
 function ConvertAndCopyTo(destDescr,
-                          destDatum,
+                          destTypedObj,
                           destOffset,
                           fromValue)
 {
   assert(IsObject(destDescr) && ObjectIsTypeDescr(destDescr),
          "ConvertAndCopyTo: not type obj");
-  assert(IsObject(destDatum) && ObjectIsTypedDatum(destDatum),
-         "ConvertAndCopyTo: not type datum");
+  assert(IsObject(destTypedObj) && ObjectIsTypedObject(destTypedObj),
+         "ConvertAndCopyTo: not type typedObj");
 
-  if (!ObjectIsAttached(destDatum))
+  if (!TypedObjectIsAttached(destTypedObj))
     ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
 
-  var ptr = new TypedObjectPointer(destDescr, destDatum, destOffset);
+  var ptr = new TypedObjectPointer(destDescr, destTypedObj, destOffset);
   ptr.set(fromValue);
 }
 
 // Wrapper for use from C++ code.
 function Reify(sourceDescr,
-               sourceDatum,
+               sourceTypedObj,
                sourceOffset) {
   assert(IsObject(sourceDescr) && ObjectIsTypeDescr(sourceDescr),
          "Reify: not type obj");
-  assert(IsObject(sourceDatum) && ObjectIsTypedDatum(sourceDatum),
-         "Reify: not type datum");
+  assert(IsObject(sourceTypedObj) && ObjectIsTypedObject(sourceTypedObj),
+         "Reify: not type typedObj");
 
-  if (!ObjectIsAttached(sourceDatum))
+  if (!TypedObjectIsAttached(sourceTypedObj))
     ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
 
-  var ptr = new TypedObjectPointer(sourceDescr, sourceDatum, sourceOffset);
+  var ptr = new TypedObjectPointer(sourceDescr, sourceTypedObj, sourceOffset);
 
   return ptr.get();
 }
 
 function FillTypedArrayWithValue(destArray, fromValue) {
-  assert(IsObject(handle) && ObjectIsTypedDatum(destArray),
+  assert(IsObject(handle) && ObjectIsTypedObject(destArray),
          "FillTypedArrayWithValue: not typed handle");
 
-  var descr = DATUM_TYPE_DESCR(destArray);
+  var descr = TYPEDOBJ_TYPE_DESCR(destArray);
   var length = DESCR_SIZED_ARRAY_LENGTH(descr);
   if (length === 0)
     return;
 
   // Use convert and copy to to produce the first element:
-  var ptr = TypedObjectPointer.fromTypedDatum(destArray);
+  var ptr = TypedObjectPointer.fromTypedObject(destArray);
   ptr.moveToElem(0);
   ptr.set(fromValue);
 
   // Stamp out the remaining copies:
   var elementSize = DESCR_SIZE(ptr.descr);
   var totalSize = length * elementSize;
   for (var offset = elementSize; offset < totalSize; offset += elementSize)
     Memcpy(destArray, offset, destArray, 0, elementSize);
 }
 
 // Warning: user exposed!
 function TypeDescrEquivalent(otherDescr) {
   if (!IsObject(this) || !ObjectIsTypeDescr(this))
-    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "this", "type object");
+    ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   if (!IsObject(otherDescr) || !ObjectIsTypeDescr(otherDescr))
-    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "1", "type object");
+    ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   return DESCR_TYPE_REPR(this) === DESCR_TYPE_REPR(otherDescr);
 }
 
 // TypedArray.redimension(newArrayType)
 //
 // Method that "repackages" the data from this array into a new typed
 // object whose type is `newArrayType`. Once you strip away all the
 // outer array dimensions, the type of `this` array and `newArrayType`
@@ -672,39 +676,39 @@ function TypeDescrEquivalent(otherDescr)
 //     U[2][2][8]
 // Because they all share the same total number (32) of equivalent elements.
 // But it would be illegal to convert `T[32]` to `U[31]` or `U[2][17]`, since
 // the number of elements differs. And it's just plain incompatible to convert
 // if the base element types are not equivalent.
 //
 // Warning: user exposed!
 function TypedArrayRedimension(newArrayType) {
-  if (!IsObject(this) || !ObjectIsTypedDatum(this))
-    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "this", "typed array");
+  if (!IsObject(this) || !ObjectIsTypedObject(this))
+    ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   if (!IsObject(newArrayType) || !ObjectIsTypeDescr(newArrayType))
-    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, 1, "type object");
+    ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   // Peel away the outermost array layers from the type of `this` to find
   // the core element type. In the process, count the number of elements.
-  var oldArrayType = DATUM_TYPE_DESCR(this);
+  var oldArrayType = TYPEDOBJ_TYPE_DESCR(this);
   var oldArrayReprKind = DESCR_KIND(oldArrayType);
   var oldElementType = oldArrayType;
   var oldElementCount = 1;
   switch (oldArrayReprKind) {
   case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
     oldElementCount *= this.length;
     oldElementType = oldElementType.elementType;
     break;
 
   case JS_TYPEREPR_SIZED_ARRAY_KIND:
     break;
 
   default:
-    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "this", "typed array");
+    ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   }
   while (DESCR_KIND(oldElementType) === JS_TYPEREPR_SIZED_ARRAY_KIND) {
     oldElementCount *= oldElementType.length;
     oldElementType = oldElementType.elementType;
   }
 
   // Peel away the outermost array layers from `newArrayType`. In the
   // process, count the number of elements.
@@ -712,32 +716,30 @@ function TypedArrayRedimension(newArrayT
   var newElementCount = 1;
   while (DESCR_KIND(newElementType) == JS_TYPEREPR_SIZED_ARRAY_KIND) {
     newElementCount *= newElementType.length;
     newElementType = newElementType.elementType;
   }
 
   // Check that the total number of elements does not change.
   if (oldElementCount !== newElementCount) {
-    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, 1,
-               "New number of elements does not match old number of elements");
+    ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   }
 
   // Check that the element types are equivalent.
   if (DESCR_TYPE_REPR(oldElementType) !== DESCR_TYPE_REPR(newElementType)) {
-    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, 1,
-               "New element type is not equivalent to old element type");
+    ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   }
 
   // Together, this should imply that the sizes are unchanged.
   assert(DESCR_SIZE(oldArrayType) == DESCR_SIZE(newArrayType),
          "Byte sizes should be equal");
 
   // Rewrap the data from `this` in a new type.
-  return NewDerivedTypedDatum(newArrayType, this, 0);
+  return NewDerivedTypedObject(newArrayType, this, 0);
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // X4
 
 function X4ProtoString(type) {
   switch (type) {
   case JS_X4TYPEREPR_INT32:
@@ -746,54 +748,74 @@ function X4ProtoString(type) {
     return "float32x4";
   }
 
   assert(false, "Unhandled type constant");
   return undefined;
 }
 
 function X4ToSource() {
-  if (!IsObject(this) || !ObjectIsTypedDatum(this))
+  if (!IsObject(this) || !ObjectIsTypedObject(this))
     ThrowError(JSMSG_INCOMPATIBLE_PROTO, "X4", "toSource", typeof this);
 
   if (DESCR_KIND(this) != JS_TYPEREPR_X4_KIND)
     ThrowError(JSMSG_INCOMPATIBLE_PROTO, "X4", "toSource", typeof this);
 
-  var descr = DATUM_TYPE_DESCR(this);
+  var descr = TYPEDOBJ_TYPE_DESCR(this);
   var type = DESCR_TYPE(descr);
   return X4ProtoString(type)+"("+this.x+", "+this.y+", "+this.z+", "+this.w+")";
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // Miscellaneous
 
 // Warning: user exposed!
 function ArrayShorthand(...dims) {
   if (!IsObject(this) || !ObjectIsTypeDescr(this))
-    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS,
-               "this", "typed object");
+    ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   var T = GetTypedObjectModule();
 
   if (dims.length == 0)
     return new T.ArrayType(this);
 
   var accum = this;
   for (var i = dims.length - 1; i >= 0; i--)
     accum = new T.ArrayType(accum).dimension(dims[i]);
   return accum;
 }
 
+// This is the `storage()` function defined in the spec.  When
+// provided with a *transparent* typed object, it returns an object
+// containing buffer, byteOffset, byteLength. When given an opaque
+// typed object, it returns null. Otherwise it throws.
+//
+// Warning: user exposed!
+function StorageOfTypedObject(obj) {
+  if (IsObject(obj)) {
+    if (ObjectIsOpaqueTypedObject(obj))
+      return null;
+
+    if (ObjectIsTransparentTypedObject(obj))
+      return { buffer: TYPEDOBJ_OWNER(obj),
+               byteLength: TYPEDOBJ_BYTELENGTH(obj),
+               byteOffset: TYPEDOBJ_BYTEOFFSET(obj) };
+  }
+
+  ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
+  return null; // pacify silly "always returns a value" lint
+}
+
 // This is the `objectType()` function defined in the spec.
 // It returns the type of its argument.
 //
 // Warning: user exposed!
-function TypeOfTypedDatum(obj) {
-  if (IsObject(obj) && ObjectIsTypedDatum(obj))
-    return DATUM_TYPE_DESCR(obj);
+function TypeOfTypedObject(obj) {
+  if (IsObject(obj) && ObjectIsTypedObject(obj))
+    return TYPEDOBJ_TYPE_DESCR(obj);
 
   // Note: Do not create bindings for `Any`, `String`, etc in
   // Utilities.js, but rather access them through
   // `GetTypedObjectModule()`. The reason is that bindings
   // you create in Utilities.js are part of the self-hosted global,
   // vs the user-accessible global, and hence should not escape to
   // user script.
   var T = GetTypedObjectModule();
@@ -802,163 +824,156 @@ function TypeOfTypedDatum(obj) {
     case "function": return T.Object;
     case "string": return T.String;
     case "number": return T.float64;
     case "undefined": return T.Any;
     default: return T.Any;
   }
 }
 
-function ObjectIsTypedDatum(obj) {
-  assert(IsObject(obj), "ObjectIsTypedDatum invoked with non-object")
-  return ObjectIsTypedObject(obj) || ObjectIsTypedHandle(obj);
-}
-
-function ObjectIsAttached(obj) {
-  assert(IsObject(obj), "ObjectIsAttached invoked with non-object")
-  assert(ObjectIsTypedDatum(obj),
-         "ObjectIsAttached() invoked on invalid obj");
-  return DATUM_OWNER(obj) != null;
+function ObjectIsTypedObject(obj) {
+  assert(IsObject(obj), "ObjectIsTypedObject invoked with non-object")
+  return ObjectIsTransparentTypedObject(obj) || ObjectIsOpaqueTypedObject(obj);
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // TypedObject surface API methods (sequential implementations).
 
 // Warning: user exposed!
 function TypedObjectArrayTypeBuild(a,b,c) {
   // Arguments (this sized) : [depth], func
   // Arguments (this unsized) : length, [depth], func
 
   if (!IsObject(this) || !ObjectIsTypeDescr(this))
-    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "this", "type object");
+    ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   var kind = DESCR_KIND(this);
   switch (kind) {
   case JS_TYPEREPR_SIZED_ARRAY_KIND:
     if (typeof a === "function") // XXX here and elsewhere: these type dispatches are fragile at best.
       return BuildTypedSeqImpl(this, this.length, 1, a);
     else if (typeof a === "number" && typeof b === "function")
       return BuildTypedSeqImpl(this, this.length, a, b);
     else if (typeof a === "number")
-      return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "2", "function");
+      return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
     else
-      return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "1", "function");
+      return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
     var len = a;
     if (typeof b === "function")
       return BuildTypedSeqImpl(this, len, 1, b);
     else if (typeof b === "number" && typeof c === "function")
       return BuildTypedSeqImpl(this, len, b, c);
     else if (typeof b === "number")
-      return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "3", "function");
+      return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
     else
-      return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "2", "function");
+      return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   default:
-    return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "this", "type object");
+    return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   }
 }
 
 // Warning: user exposed!
 function TypedObjectArrayTypeFrom(a, b, c) {
   // Arguments: arrayLike, [depth], func
 
   if (!IsObject(this) || !ObjectIsTypeDescr(this))
-    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "this", "type object");
+    ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
-  var untypedInput = !IsObject(a) || !ObjectIsTypedDatum(a);
+  var untypedInput = !IsObject(a) || !ObjectIsTypedObject(a);
 
   // for untyped input array, the expectation (in terms of error
   // reporting for invalid parameters) is no-depth, despite
   // supporting an explicit depth of 1; while for typed input array,
   // the expectation is explicit depth.
 
   if (untypedInput) {
     var explicitDepth = (b === 1);
     if (explicitDepth && IsCallable(c))
       return MapUntypedSeqImpl(a, this, c);
     else if (IsCallable(b))
       return MapUntypedSeqImpl(a, this, b);
     else
-      return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "2", "function");
+      return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   } else {
     var explicitDepth = (typeof b === "number");
     if (explicitDepth && IsCallable(c))
       return MapTypedSeqImpl(a, b, this, c);
     else if (IsCallable(b))
       return MapTypedSeqImpl(a, 1, this, b);
     else if (explicitDepth)
-      return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "3", "function");
+      return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
     else
-      return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "2", "number");
+      return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   }
 }
 
 // Warning: user exposed!
 function TypedArrayMap(a, b) {
-  if (!IsObject(this) || !ObjectIsTypedDatum(this))
-    return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "this", "typed array");
-  var thisType = DATUM_TYPE_DESCR(this);
+  if (!IsObject(this) || !ObjectIsTypedObject(this))
+    return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
+  var thisType = TYPEDOBJ_TYPE_DESCR(this);
   if (!TypeDescrIsArrayType(thisType))
-    return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "this", "typed array");
+    return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   // Arguments: [depth], func
   if (typeof a === "number" && typeof b === "function")
     return MapTypedSeqImpl(this, a, thisType, b);
   else if (typeof a === "function")
     return MapTypedSeqImpl(this, 1, thisType, a);
   else if (typeof a === "number")
-    return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "3", "function");
+    return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   else
-    return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "2", "function");
+    return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 }
 
 // Warning: user exposed!
 function TypedArrayReduce(a, b) {
   // Arguments: func, [initial]
-  if (!IsObject(this) || !ObjectIsTypedDatum(this))
-    return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "this", "typed array");
-  var thisType = DATUM_TYPE_DESCR(this);
+  if (!IsObject(this) || !ObjectIsTypedObject(this))
+    return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
+  var thisType = TYPEDOBJ_TYPE_DESCR(this);
   if (!TypeDescrIsArrayType(thisType))
-    return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "this", "typed array");
+    return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   if (a !== undefined && typeof a !== "function")
-    return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "1", "function");
+    return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   var outputType = thisType.elementType;
   return ReduceTypedSeqImpl(this, outputType, a, b);
 }
 
 // Warning: user exposed!
 function TypedArrayScatter(a, b, c, d) {
   // Arguments: outputArrayType, indices, defaultValue, conflictFunction
-  if (!IsObject(this) || !ObjectIsTypedDatum(this))
-    return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "this", "typed array");
-  var thisType = DATUM_TYPE_DESCR(this);
+  if (!IsObject(this) || !ObjectIsTypedObject(this))
+    return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
+  var thisType = TYPEDOBJ_TYPE_DESCR(this);
   if (!TypeDescrIsArrayType(thisType))
-    return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "this", "typed array");
+    return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   if (!IsObject(a) || !ObjectIsTypeDescr(a) || !TypeDescrIsSizedArrayType(a))
-    return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "1", "sized array type");
+    return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   if (d !== undefined && typeof d !== "function")
-    return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "4", "function");
+    return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   return ScatterTypedSeqImpl(this, a, b, c, d);
 }
 
 // Warning: user exposed!
 function TypedArrayFilter(func) {
   // Arguments: predicate
-  if (!IsObject(this) || !ObjectIsTypedDatum(this))
-    return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "this", "typed array");
-  var thisType = DATUM_TYPE_DESCR(this);
+  if (!IsObject(this) || !ObjectIsTypedObject(this))
+    return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
+  var thisType = TYPEDOBJ_TYPE_DESCR(this);
   if (!TypeDescrIsArrayType(thisType))
-    return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "this", "typed array");
+    return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   if (typeof func !== "function")
-    return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "1", "function");
+    return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   return FilterTypedSeqImpl(this, func);
 }
 
 // placeholders
 
 // Warning: user exposed!
 function TypedObjectArrayTypeBuildPar(a,b,c) {
@@ -1014,17 +1029,17 @@ function TypeDescrIsArrayType(t) {
   case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
     return true;
   case JS_TYPEREPR_SCALAR_KIND:
   case JS_TYPEREPR_REFERENCE_KIND:
   case JS_TYPEREPR_X4_KIND:
   case JS_TYPEREPR_STRUCT_KIND:
     return false;
   default:
-    return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, 1, "unknown kind of typed object");
+    return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   }
 }
 
 function TypeDescrIsSizedArrayType(t) {
   assert(IsObject(t) && ObjectIsTypeDescr(t), "TypeDescrIsSizedArrayType called on non-type-object");
 
   var kind = DESCR_KIND(t);
   switch (kind) {
@@ -1032,17 +1047,17 @@ function TypeDescrIsSizedArrayType(t) {
     return true;
   case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
   case JS_TYPEREPR_SCALAR_KIND:
   case JS_TYPEREPR_REFERENCE_KIND:
   case JS_TYPEREPR_X4_KIND:
   case JS_TYPEREPR_STRUCT_KIND:
     return false;
   default:
-    return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, 1, "unknown kind of typed object");
+    return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   }
 }
 
 function TypeDescrIsSimpleType(t) {
   assert(IsObject(t) && ObjectIsTypeDescr(t), "TypeDescrIsSimpleType called on non-type-object");
 
   var kind = DESCR_KIND(t);
   switch (kind) {
@@ -1050,27 +1065,27 @@ function TypeDescrIsSimpleType(t) {
   case JS_TYPEREPR_REFERENCE_KIND:
   case JS_TYPEREPR_X4_KIND:
     return true;
   case JS_TYPEREPR_SIZED_ARRAY_KIND:
   case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
   case JS_TYPEREPR_STRUCT_KIND:
     return false;
   default:
-    return ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, 1, "unknown kind of typed object");
+    return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   }
 }
 
 // Bug 956914: make performance-tuned variants tailored to 1, 2, and 3 dimensions.
 function BuildTypedSeqImpl(arrayType, len, depth, func) {
   assert(IsObject(arrayType) && ObjectIsTypeDescr(arrayType), "Build called on non-type-object");
 
   if (depth <= 0 || TO_INT32(depth) !== depth)
     // RangeError("bad depth")
-    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "depth", "positive int");
+    ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   // For example, if we have as input
   //    ArrayType(ArrayType(T, 4), 5)
   // and a depth of 2, we get
   //    grainType = T
   //    iterationSpace = [5, 4]
   var [iterationSpace, grainType, totalLength] =
     ComputeIterationSpace(arrayType, depth, len);
@@ -1205,28 +1220,28 @@ function MapUntypedSeqImpl(inArray, outp
   }
 
   return result;
 }
 
 // Implements |map| and |from| methods for typed |inArray|.
 function MapTypedSeqImpl(inArray, depth, outputType, func) {
   assert(IsObject(outputType) && ObjectIsTypeDescr(outputType), "2. Map/From called on non-type-object outputType");
-  assert(IsObject(inArray) && ObjectIsTypedDatum(inArray), "Map/From called on non-object or untyped input array.");
+  assert(IsObject(inArray) && ObjectIsTypedObject(inArray), "Map/From called on non-object or untyped input array.");
   assert(TypeDescrIsArrayType(outputType), "Map/From called on non array-type outputType");
 
   if (depth <= 0 || TO_INT32(depth) !== depth)
-    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "depth", "positive int");
+    ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   // Compute iteration space for input and output and check for compatibility.
-  var inputType = TypeOfTypedDatum(inArray);
+  var inputType = TypeOfTypedObject(inArray);
   var [inIterationSpace, inGrainType, _] =
     ComputeIterationSpace(inputType, depth, inArray.length);
   if (!IsObject(inGrainType) || !ObjectIsTypeDescr(inGrainType))
-    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, 1, "type object");
+    ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   var [iterationSpace, outGrainType, totalLength] =
     ComputeIterationSpace(outputType, depth, outputType.variable ? inArray.length : outputType.length);
   for (var i = 0; i < depth; i++)
     if (inIterationSpace[i] !== iterationSpace[i])
       // TypeError("Incompatible iteration space in input and output type");
       ThrowError(JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
 
   // Create a zeroed instance with no data
@@ -1293,17 +1308,17 @@ function MapTypedSeqImpl(inArray, depth,
     return DoMapTypedSeqDepth1();
   } else {
     return DoMapTypedSeqDepthN();
   }
 
 }
 
 function ReduceTypedSeqImpl(array, outputType, func, initial) {
-  assert(IsObject(array) && ObjectIsTypedDatum(array), "Reduce called on non-object or untyped input array.");
+  assert(IsObject(array) && ObjectIsTypedObject(array), "Reduce called on non-object or untyped input array.");
   assert(IsObject(outputType) && ObjectIsTypeDescr(outputType), "Reduce called on non-type-object outputType");
 
   var start, value;
 
   if (initial === undefined && array.length < 1)
     // RangeError("reduce requires array of length > 0")
     ThrowError(JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
 
@@ -1334,17 +1349,17 @@ function ReduceTypedSeqImpl(array, outpu
     for (var i = start; i < array.length; i++)
       value = func(value, array[i]);
   }
 
   return value;
 }
 
 function ScatterTypedSeqImpl(array, outputType, indices, defaultValue, conflictFunc) {
-  assert(IsObject(array) && ObjectIsTypedDatum(array), "Scatter called on non-object or untyped input array.");
+  assert(IsObject(array) && ObjectIsTypedObject(array), "Scatter called on non-object or untyped input array.");
   assert(IsObject(outputType) && ObjectIsTypeDescr(outputType), "Scatter called on non-type-object outputType");
   assert(TypeDescrIsSizedArrayType(outputType), "Scatter called on non-sized array type");
   assert(conflictFunc === undefined || typeof conflictFunc === "function", "Scatter called with invalid conflictFunc");
 
   var result = new outputType();
   var bitvec = new Uint8Array(result.length);
   var elemType = outputType.elementType;
   var i, j;
@@ -1364,22 +1379,22 @@ function ScatterTypedSeqImpl(array, outp
     } else {
       result[j] = conflictFunc(result[j], elemType(array[i]));
     }
   }
   return result;
 }
 
 function FilterTypedSeqImpl(array, func) {
-  assert(IsObject(array) && ObjectIsTypedDatum(array), "Filter called on non-object or untyped input array.");
+  assert(IsObject(array) && ObjectIsTypedObject(array), "Filter called on non-object or untyped input array.");
   assert(typeof func === "function", "Filter called with non-function predicate");
 
-  var arrayType = TypeOfTypedDatum(array);
+  var arrayType = TypeOfTypedObject(array);
   if (!TypeDescrIsArrayType(arrayType))
-    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "this", "typed array");
+    ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   var elementType = arrayType.elementType;
   var flags = new Uint8Array(NUM_BYTES(array.length));
   var count = 0;
   var size = DESCR_SIZE(elementType);
   var inPointer = new TypedObjectPointer(elementType, array, 0);
   for (var i = 0; i < array.length; i++) {
     if (func(inPointer.get(), i, array)) {
--- a/js/src/builtin/TypedObjectConstants.h
+++ b/js/src/builtin/TypedObjectConstants.h
@@ -14,18 +14,18 @@
 //
 // Some slots apply to all type objects and some are specific to
 // particular kinds of type objects. For simplicity we use the same
 // number of slots no matter what kind of type descriptor we are
 // working with, even though this is mildly wasteful.
 
 // Slots on all type objects
 #define JS_DESCR_SLOT_TYPE_REPR          0  // Associated Type Representation
-#define JS_DESCR_SLOT_SIZE               1  // Size in bytes, if sized
-#define JS_DESCR_SLOT_ALIGNMENT          2  // Alignment in bytes, if sized
+#define JS_DESCR_SLOT_ALIGNMENT          1  // Alignment in bytes
+#define JS_DESCR_SLOT_SIZE               2  // Size in bytes, if sized, else 0
 
 // Slots on scalars, references, and x4s
 #define JS_DESCR_SLOT_TYPE               3  // Type code
 
 // Slots on all array descriptors
 #define JS_DESCR_SLOT_ARRAY_ELEM_TYPE    3
 
 // Slots on sized array descriptors
@@ -98,21 +98,30 @@
 // These constants are for use exclusively in JS code.  In C++ code,
 // prefer X4TypeRepresentation::TYPE_INT32 etc, since that allows
 // you to write a switch which will receive a warning if you omit a
 // case.
 #define JS_X4TYPEREPR_INT32         0
 #define JS_X4TYPEREPR_FLOAT32       1
 
 ///////////////////////////////////////////////////////////////////////////
-// Slots for typed objects (actually, any TypedContents objects)
+// Slots for typed objects
+
+#define JS_TYPEDOBJ_SLOT_BYTEOFFSET       0
+#define JS_TYPEDOBJ_SLOT_BYTELENGTH       1
+#define JS_TYPEDOBJ_SLOT_OWNER            2
+#define JS_TYPEDOBJ_SLOT_NEXT_VIEW        3
+#define JS_TYPEDOBJ_SLOT_NEXT_BUFFER      4
+
+#define JS_DATAVIEW_SLOTS              5 // Number of slots for data views
 
-#define JS_DATUM_SLOT_TYPE_DESCR 0  // Type descr for a given typed object
-#define JS_DATUM_SLOT_OWNER      1  // Owner of data (if null, this is owner)
-#define JS_DATUM_SLOT_LENGTH     2  // Length of array (see (*) below)
-#define JS_DATUM_SLOTS           3  // Number of slots for typed objs
+#define JS_TYPEDOBJ_SLOT_LENGTH           5 // Length of array (see (*) below)
+#define JS_TYPEDOBJ_SLOT_TYPE_DESCR       6 // For typed objects, type descr
 
-// (*) The JS_DATUM_SLOT_LENGTH slot stores the length for datums of
+#define JS_TYPEDOBJ_SLOT_DATA             7 // private slot, based on alloc kind
+#define JS_TYPEDOBJ_SLOTS                 7 // Number of slots for typed objs
+
+// (*) The JS_TYPEDOBJ_SLOT_LENGTH slot stores the length for typed objects of
 // sized and unsized array type. The slot contains 0 for non-arrays.
-// The slot also contains 0 for *unattached* datums, no matter what
+// The slot also contains 0 for *unattached* typed objects, no matter what
 // type they have.
 
 #endif
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObject/neutertypedobj.js
@@ -0,0 +1,32 @@
+if (!this.hasOwnProperty("TypedObject"))
+  quit();
+
+var {StructType, uint32, storage} = TypedObject;
+var S = new StructType({f: uint32, g: uint32});
+
+function readFromS(s) {
+  return s.f + s.g;
+}
+
+function main() {
+  var s = new S({f: 22, g: 44});
+
+  for (var i = 0; i < 10; i++)
+    assertEq(readFromS(s), 66);
+
+  neuter(storage(s).buffer);
+
+  for (var i = 0; i < 10; i++) {
+    var ok = false;
+
+    try {
+      readFromS(s);
+    } catch (e) {
+      ok = e instanceof TypeError;
+    }
+
+    assertEq(ok, true);
+  }
+}
+
+main();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObject/neutertypedobjsizedarray.js
@@ -0,0 +1,40 @@
+// Test the case where we neuter an instance of a fixed-sized array.
+// This is a bit of a tricky case because we cannot (necessarily) fold
+// the neuter check into the bounds check, as we obtain the bounds
+// directly from the type.
+
+if (!this.hasOwnProperty("TypedObject"))
+  quit();
+
+var {StructType, uint32, storage} = TypedObject;
+var S = new StructType({f: uint32, g: uint32});
+var A = S.array(10);
+
+function readFrom(a) {
+  return a[2].f + a[2].g;
+}
+
+function main() {
+  var a = new A();
+  a[2].f = 22;
+  a[2].g = 44;
+
+  for (var i = 0; i < 10; i++)
+    assertEq(readFrom(a), 66);
+
+  neuter(storage(a).buffer);
+
+  for (var i = 0; i < 10; i++) {
+    var ok = false;
+
+    try {
+      readFrom(a);
+    } catch (e) {
+      ok = e instanceof TypeError;
+    }
+
+    assertEq(ok, true);
+  }
+}
+
+main();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObject/neutertypedobjunsizedarray.js
@@ -0,0 +1,38 @@
+// Test the case where we neuter an instance of a variable-length array.
+// Here we can fold the neuter check into the bounds check.
+
+if (!this.hasOwnProperty("TypedObject"))
+  quit();
+
+var {StructType, uint32, storage} = TypedObject;
+var S = new StructType({f: uint32, g: uint32});
+var A = S.array();
+
+function readFrom(a) {
+  return a[2].f + a[2].g;
+}
+
+function main() {
+  var a = new A(10);
+  a[2].f = 22;
+  a[2].g = 44;
+
+  for (var i = 0; i < 10; i++)
+    assertEq(readFrom(a), 66);
+
+  neuter(storage(a).buffer);
+
+  for (var i = 0; i < 10; i++) {
+    var ok = false;
+
+    try {
+      readFrom(a);
+    } catch (e) {
+      ok = e instanceof TypeError;
+    }
+
+    assertEq(ok, true);
+  }
+}
+
+main();
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -4079,16 +4079,28 @@ CodeGenerator::visitTypedArrayElements(L
 {
     Register obj = ToRegister(lir->object());
     Register out = ToRegister(lir->output());
     masm.loadPtr(Address(obj, TypedArrayObject::dataOffset()), out);
     return true;
 }
 
 bool
+CodeGenerator::visitNeuterCheck(LNeuterCheck *lir)
+{
+    Register obj = ToRegister(lir->object());
+    Register temp = ToRegister(lir->temp());
+    masm.loadPtr(Address(obj, TypedObject::dataOffset()), temp);
+    masm.testPtr(temp, temp);
+    if (!bailoutIf(Assembler::Zero, lir->snapshot()))
+        return false;
+    return true;
+}
+
+bool
 CodeGenerator::visitTypedObjectElements(LTypedObjectElements *lir)
 {
     Register obj = ToRegister(lir->object());
     Register out = ToRegister(lir->output());
     masm.loadPtr(Address(obj, TypedObject::dataOffset()), out);
     return true;
 }
 
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -159,16 +159,17 @@ class CodeGenerator : public CodeGenerat
     bool visitGetArgumentsObjectArg(LGetArgumentsObjectArg *lir);
     bool visitSetArgumentsObjectArg(LSetArgumentsObjectArg *lir);
     bool visitReturnFromCtor(LReturnFromCtor *lir);
     bool visitComputeThis(LComputeThis *lir);
     bool visitArrayLength(LArrayLength *lir);
     bool visitSetArrayLength(LSetArrayLength *lir);
     bool visitTypedArrayLength(LTypedArrayLength *lir);
     bool visitTypedArrayElements(LTypedArrayElements *lir);
+    bool visitNeuterCheck(LNeuterCheck *lir);
     bool visitTypedObjectElements(LTypedObjectElements *lir);
     bool visitStringLength(LStringLength *lir);
     bool visitInitializedLength(LInitializedLength *lir);
     bool visitSetInitializedLength(LSetInitializedLength *lir);
     bool visitNotO(LNotO *ins);
     bool visitNotV(LNotV *ins);
     bool visitBoundsCheck(LBoundsCheck *lir);
     bool visitBoundsCheckRange(LBoundsCheckRange *lir);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6606,39 +6606,49 @@ IonBuilder::getElemTryTypedObject(bool *
 static MIRType
 MIRTypeForTypedArrayRead(ScalarTypeDescr::Type arrayType,
                          bool observedDouble);
 
 bool
 IonBuilder::checkTypedObjectIndexInBounds(size_t elemSize,
                                           MDefinition *obj,
                                           MDefinition *index,
+                                          TypeDescrSet objDescrs,
                                           MDefinition **indexAsByteOffset,
-                                          TypeDescrSet objDescrs)
+                                          bool *canBeNeutered)
 {
     // Ensure index is an integer.
     MInstruction *idInt32 = MToInt32::New(alloc(), index);
     current->add(idInt32);
 
     // If we know the length statically from the type, just embed it.
     // Otherwise, load it from the appropriate reserved slot on the
     // typed object.  We know it's an int32, so we can convert from
     // Value to int32 using truncation.
     size_t lenOfAll;
     MDefinition *length;
     if (objDescrs.hasKnownArrayLength(&lenOfAll)) {
         length = constantInt(lenOfAll);
+
+        // If we are not loading the length from the object itself,
+        // then we still need to check if the object was neutered.
+        *canBeNeutered = true;
     } else {
-        MInstruction *lengthValue = MLoadFixedSlot::New(alloc(), obj, JS_DATUM_SLOT_LENGTH);
+        MInstruction *lengthValue = MLoadFixedSlot::New(alloc(), obj, JS_TYPEDOBJ_SLOT_LENGTH);
         current->add(lengthValue);
 
         MInstruction *length32 = MTruncateToInt32::New(alloc(), lengthValue);
         current->add(length32);
 
         length = length32;
+
+        // If we are loading the length from the object itself,
+        // then we do not need an extra neuter check, because the length
+        // will have been set to 0 when the object was neutered.
+        *canBeNeutered = false;
     }
 
     index = addBoundsCheck(idInt32, length);
 
     // Since we passed the bounds check, it is impossible for the
     // result of multiplication to overflow; so enable imul path.
     MMul *mul = MMul::New(alloc(), index, constantInt(elemSize),
                           MIRType_Int32, MMul::Integer);
@@ -6659,35 +6669,41 @@ IonBuilder::getElemTryScalarElemOfTypedO
     JS_ASSERT(objDescrs.allOfArrayKind());
 
     // Must always be loading the same scalar type
     ScalarTypeDescr::Type elemType;
     if (!elemDescrs.scalarType(&elemType))
         return true;
     JS_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType));
 
+    bool canBeNeutered;
     MDefinition *indexAsByteOffset;
-    if (!checkTypedObjectIndexInBounds(elemSize, obj, index, &indexAsByteOffset, objDescrs))
-        return false;
-
-    return pushScalarLoadFromTypedObject(emitted, obj, indexAsByteOffset, elemType);
+    if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objDescrs,
+                                       &indexAsByteOffset, &canBeNeutered))
+    {
+        return false;
+    }
+
+    return pushScalarLoadFromTypedObject(emitted, obj, indexAsByteOffset, elemType, canBeNeutered);
 }
 
 bool
 IonBuilder::pushScalarLoadFromTypedObject(bool *emitted,
                                           MDefinition *obj,
                                           MDefinition *offset,
-                                          ScalarTypeDescr::Type elemType)
+                                          ScalarTypeDescr::Type elemType,
+                                          bool canBeNeutered)
 {
     size_t size = ScalarTypeDescr::size(elemType);
     JS_ASSERT(size == ScalarTypeDescr::alignment(elemType));
 
     // Find location within the owner object.
     MDefinition *elements, *scaledOffset;
-    loadTypedObjectElements(obj, offset, size, &elements, &scaledOffset);
+    loadTypedObjectElements(obj, offset, size, canBeNeutered,
+                            &elements, &scaledOffset);
 
     // Load the element.
     MLoadTypedArrayElement *load = MLoadTypedArrayElement::New(alloc(), elements, scaledOffset, elemType);
     current->add(load);
     current->push(load);
 
     // If we are reading in-bounds elements, we can use knowledge about
     // the array type to determine the result type, even if the opcode has
@@ -6718,36 +6734,41 @@ IonBuilder::getElemTryComplexElemOfTyped
                                                TypeDescrSet elemDescrs,
                                                size_t elemSize)
 {
     JS_ASSERT(objDescrs.allOfArrayKind());
 
     MDefinition *type = loadTypedObjectType(obj);
     MDefinition *elemTypeObj = typeObjectForElementFromArrayStructType(type);
 
+    bool canBeNeutered;
     MDefinition *indexAsByteOffset;
-    if (!checkTypedObjectIndexInBounds(elemSize, obj, index, &indexAsByteOffset, objDescrs))
-        return false;
+    if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objDescrs,
+                                       &indexAsByteOffset, &canBeNeutered))
+    {
+        return false;
+    }
 
     return pushDerivedTypedObject(emitted, obj, indexAsByteOffset,
-                                  elemDescrs, elemTypeObj);
+                                  elemDescrs, elemTypeObj, canBeNeutered);
 }
 
 bool
 IonBuilder::pushDerivedTypedObject(bool *emitted,
                                    MDefinition *obj,
                                    MDefinition *offset,
                                    TypeDescrSet derivedTypeDescrs,
-                                   MDefinition *derivedTypeObj)
+                                   MDefinition *derivedTypeObj,
+                                   bool canBeNeutered)
 {
     // Find location within the owner object.
     MDefinition *owner, *ownerOffset;
-    loadTypedObjectData(obj, offset, &owner, &ownerOffset);
-
-    // Create the derived datum.
+    loadTypedObjectData(obj, offset, canBeNeutered, &owner, &ownerOffset);
+
+    // Create the derived typed object.
     MInstruction *derivedTypedObj = MNewDerivedTypedObject::New(alloc(),
                                                                 derivedTypeDescrs,
                                                                 derivedTypeObj,
                                                                 owner,
                                                                 ownerOffset);
     current->add(derivedTypedObj);
     current->push(derivedTypedObj);
 
@@ -7396,49 +7417,53 @@ IonBuilder::setElemTryTypedObject(bool *
       case TypeDescr::Reference:
       case TypeDescr::Struct:
       case TypeDescr::SizedArray:
       case TypeDescr::UnsizedArray:
         // For now, only optimize storing scalars.
         return true;
 
       case TypeDescr::Scalar:
-        return setElemTryScalarPropOfTypedObject(emitted,
+        return setElemTryScalarElemOfTypedObject(emitted,
                                                  obj,
                                                  index,
                                                  objTypeDescrs,
                                                  value,
                                                  elemTypeDescrs,
                                                  elemSize);
     }
 
     MOZ_ASSUME_UNREACHABLE("Bad kind");
 }
 
 bool
-IonBuilder::setElemTryScalarPropOfTypedObject(bool *emitted,
+IonBuilder::setElemTryScalarElemOfTypedObject(bool *emitted,
                                               MDefinition *obj,
                                               MDefinition *index,
                                               TypeDescrSet objTypeDescrs,
                                               MDefinition *value,
                                               TypeDescrSet elemTypeDescrs,
                                               size_t elemSize)
 {
     // Must always be loading the same scalar type
     ScalarTypeDescr::Type elemType;
     if (!elemTypeDescrs.scalarType(&elemType))
         return true;
     JS_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType));
 
+    bool canBeNeutered;
     MDefinition *indexAsByteOffset;
-    if (!checkTypedObjectIndexInBounds(elemSize, obj, index, &indexAsByteOffset, objTypeDescrs))
-        return false;
+    if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objTypeDescrs,
+                                       &indexAsByteOffset, &canBeNeutered))
+    {
+        return false;
+    }
 
     // Store the element
-    if (!storeScalarTypedObjectValue(obj, indexAsByteOffset, elemType, value))
+    if (!storeScalarTypedObjectValue(obj, indexAsByteOffset, elemType, canBeNeutered, value))
         return false;
 
     current->push(value);
 
     *emitted = true;
     return true;
 }
 
@@ -8430,17 +8455,17 @@ IonBuilder::getPropTryScalarPropOfTypedO
     if (!fieldDescrs.scalarType(&fieldType))
         return true;
 
     // OK, perform the optimization
 
     MDefinition *typedObj = current->pop();
 
     return pushScalarLoadFromTypedObject(emitted, typedObj, constantInt(fieldOffset),
-                                         fieldType);
+                                         fieldType, true);
 }
 
 bool
 IonBuilder::getPropTryComplexPropOfTypedObject(bool *emitted,
                                                int32_t fieldOffset,
                                                TypeDescrSet fieldDescrs,
                                                size_t fieldIndex,
                                                types::TemporaryTypeSet *resultTypes)
@@ -8454,17 +8479,17 @@ IonBuilder::getPropTryComplexPropOfTyped
 
     MDefinition *typedObj = current->pop();
 
     // Identify the type object for the field.
     MDefinition *type = loadTypedObjectType(typedObj);
     MDefinition *fieldTypeObj = typeObjectForFieldFromStructType(type, fieldIndex);
 
     return pushDerivedTypedObject(emitted, typedObj, constantInt(fieldOffset),
-                                  fieldDescrs, fieldTypeObj);
+                                  fieldDescrs, fieldTypeObj, true);
 }
 
 bool
 IonBuilder::getPropTryDefiniteSlot(bool *emitted, PropertyName *name,
                                    bool barrier, types::TemporaryTypeSet *types)
 {
     JS_ASSERT(*emitted == false);
     types::HeapTypeSetKey property;
@@ -8948,17 +8973,17 @@ IonBuilder::setPropTryScalarPropOfTypedO
 {
     // Must always be loading the same scalar type
     ScalarTypeDescr::Type fieldType;
     if (!fieldDescrs.scalarType(&fieldType))
         return true;
 
     // OK! Perform the optimization.
 
-    if (!storeScalarTypedObjectValue(obj, constantInt(fieldOffset), fieldType, value))
+    if (!storeScalarTypedObjectValue(obj, constantInt(fieldOffset), fieldType, true, value))
         return false;
 
     current->push(value);
 
     *emitted = true;
     return true;
 }
 
@@ -9799,71 +9824,83 @@ IonBuilder::loadTypedObjectType(MDefinit
     // Shortcircuit derived type objects, meaning the intermediate
     // objects created to represent `a.b` in an expression like
     // `a.b.c`. In that case, the type object can be simply pulled
     // from the operands of that instruction.
     if (typedObj->isNewDerivedTypedObject())
         return typedObj->toNewDerivedTypedObject()->type();
 
     MInstruction *load = MLoadFixedSlot::New(alloc(), typedObj,
-                                             JS_DATUM_SLOT_TYPE_DESCR);
+                                             JS_TYPEDOBJ_SLOT_TYPE_DESCR);
     current->add(load);
     return load;
 }
 
 // Given a typed object `typedObj` and an offset `offset` into that
 // object's data, returns another typed object and adusted offset
 // where the data can be found. Often, these returned values are the
 // same as the inputs, but in cases where intermediate derived type
 // objects have been created, the return values will remove
 // intermediate layers (often rendering those derived type objects
 // into dead code).
 void
 IonBuilder::loadTypedObjectData(MDefinition *typedObj,
                                 MDefinition *offset,
+                                bool canBeNeutered,
                                 MDefinition **owner,
                                 MDefinition **ownerOffset)
 {
     JS_ASSERT(typedObj->type() == MIRType_Object);
     JS_ASSERT(offset->type() == MIRType_Int32);
 
     // Shortcircuit derived type objects, meaning the intermediate
     // objects created to represent `a.b` in an expression like
     // `a.b.c`. In that case, the owned and a base offset can be
     // pulled from the operands of the instruction and combined with
     // `offset`.
     if (typedObj->isNewDerivedTypedObject()) {
         MNewDerivedTypedObject *ins = typedObj->toNewDerivedTypedObject();
 
+        // Note: we never need to check for neutering on this path,
+        // because when we create the derived typed object, we check
+        // for neutering there, if needed.
+
         MAdd *offsetAdd = MAdd::NewAsmJS(alloc(), ins->offset(), offset, MIRType_Int32);
         current->add(offsetAdd);
 
         *owner = ins->owner();
         *ownerOffset = offsetAdd;
         return;
     }
 
+    if (canBeNeutered) {
+        MNeuterCheck *chk = MNeuterCheck::New(alloc(), typedObj);
+        current->add(chk);
+        typedObj = chk;
+    }
+
     *owner = typedObj;
     *ownerOffset = offset;
 }
 
 // Takes as input a typed object, an offset into that typed object's
 // memory, and the type repr of the data found at that offset. Returns
 // the elements pointer and a scaled offset. The scaled offset is
 // expressed in units of `unit`; when working with typed array MIR,
 // this is typically the alignment.
 void
 IonBuilder::loadTypedObjectElements(MDefinition *typedObj,
                                     MDefinition *offset,
                                     int32_t unit,
+                                    bool canBeNeutered,
                                     MDefinition **ownerElements,
                                     MDefinition **ownerScaledOffset)
 {
     MDefinition *owner, *ownerOffset;
-    loadTypedObjectData(typedObj, offset, &owner, &ownerOffset);
+    loadTypedObjectData(typedObj, offset, canBeNeutered, &owner, &ownerOffset);
 
     // Load the element data.
     MTypedObjectElements *elements = MTypedObjectElements::New(alloc(), owner);
     current->add(elements);
 
     // Scale to a different unit for compat with typed array MIRs.
     if (unit != 1) {
         MDiv *scaledOffset = MDiv::NewAsmJS(alloc(), ownerOffset, constantInt(unit), MIRType_Int32,
@@ -9955,22 +9992,24 @@ IonBuilder::typeObjectForFieldFromStruct
 
     return unboxFieldType;
 }
 
 bool
 IonBuilder::storeScalarTypedObjectValue(MDefinition *typedObj,
                                         MDefinition *offset,
                                         ScalarTypeDescr::Type type,
+                                        bool canBeNeutered,
                                         MDefinition *value)
 {
     // Find location within the owner object.
     MDefinition *elements, *scaledOffset;
     size_t alignment = ScalarTypeDescr::alignment(type);
-    loadTypedObjectElements(typedObj, offset, alignment, &elements, &scaledOffset);
+    loadTypedObjectElements(typedObj, offset, alignment, canBeNeutered,
+                            &elements, &scaledOffset);
 
     // Clamp value to [0, 255] when type is Uint8Clamped
     MDefinition *toWrite = value;
     if (type == ScalarTypeDescr::TYPE_UINT8_CLAMPED) {
         toWrite = MClampToUint8::New(alloc(), value);
         current->add(toWrite->toInstruction());
     }
 
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -437,59 +437,66 @@ class IonBuilder : public MIRGenerator
     bool lookupTypedObjectField(MDefinition *typedObj,
                                 PropertyName *name,
                                 int32_t *fieldOffset,
                                 TypeDescrSet *fieldTypeReprs,
                                 size_t *fieldIndex);
     MDefinition *loadTypedObjectType(MDefinition *value);
     void loadTypedObjectData(MDefinition *typedObj,
                              MDefinition *offset,
+                             bool canBeNeutered,
                              MDefinition **owner,
                              MDefinition **ownerOffset);
     void loadTypedObjectElements(MDefinition *typedObj,
                                  MDefinition *offset,
                                  int32_t unit,
+                                 bool canBeNeutered,
                                  MDefinition **ownerElements,
                                  MDefinition **ownerScaledOffset);
     MDefinition *typeObjectForElementFromArrayStructType(MDefinition *typedObj);
     MDefinition *typeObjectForFieldFromStructType(MDefinition *type,
                                                   size_t fieldIndex);
     bool storeScalarTypedObjectValue(MDefinition *typedObj,
                                      MDefinition *offset,
                                      ScalarTypeDescr::Type type,
+                                     bool canBeNeutered,
                                      MDefinition *value);
     bool checkTypedObjectIndexInBounds(size_t elemSize,
                                        MDefinition *obj,
                                        MDefinition *index,
+                                       TypeDescrSet objTypeDescrs,
                                        MDefinition **indexAsByteOffset,
-                                       TypeDescrSet objTypeDescrs);
+                                       bool *canBeNeutered);
     bool pushDerivedTypedObject(bool *emitted,
                                 MDefinition *obj,
                                 MDefinition *offset,
                                 TypeDescrSet derivedTypeDescrs,
-                                MDefinition *derivedTypeObj);
+                                MDefinition *derivedTypeObj,
+                                bool canBeNeutered);
     bool pushScalarLoadFromTypedObject(bool *emitted,
                                        MDefinition *obj,
                                        MDefinition *offset,
-                                       ScalarTypeDescr::Type type);
+                                       ScalarTypeDescr::Type type,
+                                       bool canBeNeutered);
+    MDefinition *neuterCheck(MDefinition *obj);
 
     // jsop_setelem() helpers.
     bool setElemTryTypedArray(bool *emitted, MDefinition *object,
                          MDefinition *index, MDefinition *value);
     bool setElemTryTypedObject(bool *emitted, MDefinition *obj,
                                MDefinition *index, MDefinition *value);
     bool setElemTryTypedStatic(bool *emitted, MDefinition *object,
                                MDefinition *index, MDefinition *value);
     bool setElemTryDense(bool *emitted, MDefinition *object,
                          MDefinition *index, MDefinition *value);
     bool setElemTryArguments(bool *emitted, MDefinition *object,
                              MDefinition *index, MDefinition *value);
     bool setElemTryCache(bool *emitted, MDefinition *object,
                          MDefinition *index, MDefinition *value);
-    bool setElemTryScalarPropOfTypedObject(bool *emitted,
+    bool setElemTryScalarElemOfTypedObject(bool *emitted,
                                            MDefinition *obj,
                                            MDefinition *index,
                                            TypeDescrSet objTypeReprs,
                                            MDefinition *value,
                                            TypeDescrSet elemTypeReprs,
                                            size_t elemSize);
 
     // jsop_getelem() helpers.
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -3641,16 +3641,38 @@ class LTypedObjectElements : public LIns
     LIR_HEADER(TypedObjectElements)
 
     LTypedObjectElements(const LAllocation &object) {
         setOperand(0, object);
     }
     const LAllocation *object() {
         return getOperand(0);
     }
+    const MTypedObjectElements *mir() const {
+        return mir_->toTypedObjectElements();
+    }
+};
+
+// Check whether a typed object has a NULL data pointer
+// (i.e., has been neutered).
+class LNeuterCheck : public LInstructionHelper<0, 1, 1>
+{
+  public:
+    LIR_HEADER(NeuterCheck)
+
+    LNeuterCheck(const LAllocation &object, const LDefinition &temp) {
+        setOperand(0, object);
+        setTemp(0, temp);
+    }
+    const LAllocation *object() {
+        return getOperand(0);
+    }
+    const LDefinition *temp() {
+        return getTemp(0);
+    }
 };
 
 // Bailout if index >= length.
 class LBoundsCheck : public LInstructionHelper<0, 2, 0>
 {
   public:
     LIR_HEADER(BoundsCheck)
 
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -177,16 +177,17 @@
     _(TypeBarrierV)                 \
     _(TypeBarrierO)                 \
     _(MonitorTypes)                 \
     _(PostWriteBarrierO)            \
     _(PostWriteBarrierV)            \
     _(PostWriteBarrierAllSlots)     \
     _(InitializedLength)            \
     _(SetInitializedLength)         \
+    _(NeuterCheck)                  \
     _(BoundsCheck)                  \
     _(BoundsCheckRange)             \
     _(BoundsCheckLower)             \
     _(LoadElementV)                 \
     _(LoadElementT)                 \
     _(LoadElementHole)              \
     _(StoreElementV)                \
     _(StoreElementT)                \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2402,16 +2402,26 @@ LIRGenerator::visitNot(MNot *ins)
       }
 
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected MIRType.");
     }
 }
 
 bool
+LIRGenerator::visitNeuterCheck(MNeuterCheck *ins)
+{
+    LNeuterCheck *chk = new(alloc()) LNeuterCheck(useRegister(ins->object()),
+                                                  temp());
+    if (!assignSnapshot(chk, Bailout_BoundsCheck))
+        return false;
+    return redefine(ins, ins->input()) && add(chk, ins);
+}
+
+bool
 LIRGenerator::visitBoundsCheck(MBoundsCheck *ins)
 {
     LInstruction *check;
     if (ins->minimum() || ins->maximum()) {
         check = new(alloc()) LBoundsCheckRange(useRegisterOrConstant(ins->index()),
                                                useAny(ins->length()),
                                                temp());
     } else {
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -168,16 +168,17 @@ class LIRGenerator : public LIRGenerator
     bool visitStoreSlot(MStoreSlot *ins);
     bool visitTypeBarrier(MTypeBarrier *ins);
     bool visitMonitorTypes(MMonitorTypes *ins);
     bool visitPostWriteBarrier(MPostWriteBarrier *ins);
     bool visitArrayLength(MArrayLength *ins);
     bool visitSetArrayLength(MSetArrayLength *ins);
     bool visitTypedArrayLength(MTypedArrayLength *ins);
     bool visitTypedArrayElements(MTypedArrayElements *ins);
+    bool visitNeuterCheck(MNeuterCheck *lir);
     bool visitTypedObjectElements(MTypedObjectElements *ins);
     bool visitInitializedLength(MInitializedLength *ins);
     bool visitSetInitializedLength(MSetInitializedLength *ins);
     bool visitNot(MNot *ins);
     bool visitBoundsCheck(MBoundsCheck *ins);
     bool visitBoundsCheckLower(MBoundsCheckLower *ins);
     bool visitLoadElement(MLoadElement *ins);
     bool visitLoadElementHole(MLoadElementHole *ins);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -770,19 +770,19 @@ IonBuilder::inlineMathPow(CallInfo &call
 
     // Typechecking.
     MIRType baseType = callInfo.getArg(0)->type();
     MIRType powerType = callInfo.getArg(1)->type();
     MIRType outputType = getInlineReturnType();
 
     if (outputType != MIRType_Int32 && outputType != MIRType_Double)
         return InliningStatus_NotInlined;
-    if (baseType != MIRType_Int32 && baseType != MIRType_Double)
+    if (!IsNumberType(baseType))
         return InliningStatus_NotInlined;
-    if (powerType != MIRType_Int32 && powerType != MIRType_Double)
+    if (!IsNumberType(powerType))
         return InliningStatus_NotInlined;
 
     callInfo.setImplicitlyUsedUnchecked();
 
     MDefinition *base = callInfo.getArg(0);
     MDefinition *power = callInfo.getArg(1);
     MDefinition *output = nullptr;
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -5532,16 +5532,51 @@ class MTypedArrayElements
     bool congruentTo(MDefinition *ins) const {
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const {
         return AliasSet::Load(AliasSet::ObjectFields);
     }
 };
 
+// Checks whether a typed object is neutered.
+class MNeuterCheck
+  : public MUnaryInstruction
+{
+  private:
+    MNeuterCheck(MDefinition *object)
+      : MUnaryInstruction(object)
+    {
+        JS_ASSERT(object->type() == MIRType_Object);
+        setResultType(MIRType_Object);
+        setResultTypeSet(object->resultTypeSet());
+        setGuard();
+        setMovable();
+    }
+
+  public:
+    INSTRUCTION_HEADER(NeuterCheck)
+
+    static MNeuterCheck *New(TempAllocator &alloc, MDefinition *object) {
+        return new(alloc) MNeuterCheck(object);
+    }
+
+    MDefinition *object() const {
+        return getOperand(0);
+    }
+
+    bool congruentTo(MDefinition *ins) const {
+        return congruentIfOperandsEqual(ins);
+    }
+
+    AliasSet getAliasSet() const {
+        return AliasSet::Load(AliasSet::ObjectFields);
+    }
+};
+
 // Load a binary data object's "elements", which is just its opaque
 // binary data space. Eventually this should probably be
 // unified with `MTypedArrayElements`.
 class MTypedObjectElements
   : public MUnaryInstruction,
     public SingleObjectPolicy
 {
   private:
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -130,16 +130,17 @@ namespace jit {
     _(ArrayLength)                                                          \
     _(SetArrayLength)                                                       \
     _(TypedArrayLength)                                                     \
     _(TypedArrayElements)                                                   \
     _(TypedObjectElements)                                                  \
     _(InitializedLength)                                                    \
     _(SetInitializedLength)                                                 \
     _(Not)                                                                  \
+    _(NeuterCheck)                                                          \
     _(BoundsCheck)                                                          \
     _(BoundsCheckLower)                                                     \
     _(InArray)                                                              \
     _(LoadElement)                                                          \
     _(LoadElementHole)                                                      \
     _(StoreElement)                                                         \
     _(StoreElementHole)                                                     \
     _(ArrayPopShift)                                                        \
--- a/js/src/jit/ParallelFunctions.cpp
+++ b/js/src/jit/ParallelFunctions.cpp
@@ -77,48 +77,48 @@ jit::ParallelWriteGuard(ForkJoinContext 
     //    In order to be able to distinguish escaped out pointers from
     //    prior iterations and the proper out pointers from the
     //    current iteration, we always track a *target memory region*
     //    (which is a span of bytes within the output buffer) and not
     //    just the output buffer itself.
 
     JS_ASSERT(ForkJoinContext::current() == cx);
 
-    if (object->is<TypedDatum>()) {
-        TypedDatum &datum = object->as<TypedDatum>();
+    if (object->is<TypedObject>()) {
+        TypedObject &typedObj = object->as<TypedObject>();
 
-        // Note: check target region based on `datum`, not the owner.
-        // This is because `datum` may point to some subregion of the
+        // Note: check target region based on `typedObj`, not the owner.
+        // This is because `typedObj` may point to some subregion of the
         // owner and we only care if that *subregion* is within the
         // target region, not the entire owner.
-        if (IsInTargetRegion(cx, &datum))
+        if (IsInTargetRegion(cx, &typedObj))
             return true;
 
         // Also check whether owner is thread-local.
-        TypedDatum &owner = datum.owner();
+        ArrayBufferObject &owner = typedObj.owner();
         return cx->isThreadLocal(&owner);
     }
 
     // For other kinds of writable objects, must be thread-local.
     return cx->isThreadLocal(object);
 }
 
-// Check that |object| (which must be a typed datum) maps
+// Check that |object| (which must be a typed typedObj) maps
 // to memory in the target region.
 //
 // For efficiency, we assume that all handles which the user has
 // access to are either entirely within the target region or entirely
 // without, but not straddling the target region nor encompassing
 // it. This invariant is maintained by the PJS APIs, where the target
 // region and handles are always elements of the same output array.
 bool
-jit::IsInTargetRegion(ForkJoinContext *cx, TypedDatum *datum)
+jit::IsInTargetRegion(ForkJoinContext *cx, TypedObject *typedObj)
 {
-    JS_ASSERT(datum->is<TypedDatum>()); // in case JIT supplies something bogus
-    uint8_t *typedMem = datum->typedMem();
+    JS_ASSERT(typedObj->is<TypedObject>()); // in case JIT supplies something bogus
+    uint8_t *typedMem = typedObj->typedMem();
     return (typedMem >= cx->targetRegionStart &&
             typedMem <  cx->targetRegionEnd);
 }
 
 #ifdef DEBUG
 static void
 printTrace(const char *prefix, struct IonLIRTraceData *cached)
 {
--- a/js/src/jit/ParallelFunctions.h
+++ b/js/src/jit/ParallelFunctions.h
@@ -7,24 +7,24 @@
 #ifndef jit_ParallelFunctions_h
 #define jit_ParallelFunctions_h
 
 #include "gc/Heap.h"
 #include "vm/ForkJoin.h"
 
 namespace js {
 
-class TypedDatum; // subclass of JSObject* defined in builtin/TypedObject.h
+class TypedObject; // subclass of JSObject* defined in builtin/TypedObject.h
 
 namespace jit {
 
 ForkJoinContext *ForkJoinContextPar();
 JSObject *NewGCThingPar(ForkJoinContext *cx, gc::AllocKind allocKind);
 bool ParallelWriteGuard(ForkJoinContext *cx, JSObject *object);
-bool IsInTargetRegion(ForkJoinContext *cx, TypedDatum *object);
+bool IsInTargetRegion(ForkJoinContext *cx, TypedObject *object);
 bool CheckOverRecursedPar(ForkJoinContext *cx);
 bool InterruptCheckPar(ForkJoinContext *cx);
 
 // Extends the given array with `length` new holes.  Returns nullptr on
 // failure or else `array`, which is convenient during code
 // generation.
 JSObject *ExtendArrayPar(ForkJoinContext *cx, JSObject *array, uint32_t length);
 
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -218,16 +218,17 @@ class ParallelSafetyVisitor : public MIn
     SAFE_OP(ArrayLength)
     WRITE_GUARDED_OP(SetArrayLength, elements)
     SAFE_OP(TypedArrayLength)
     SAFE_OP(TypedArrayElements)
     SAFE_OP(TypedObjectElements)
     SAFE_OP(InitializedLength)
     WRITE_GUARDED_OP(SetInitializedLength, elements)
     SAFE_OP(Not)
+    SAFE_OP(NeuterCheck)
     SAFE_OP(BoundsCheck)
     SAFE_OP(BoundsCheckLower)
     SAFE_OP(LoadElement)
     SAFE_OP(LoadElementHole)
     MAYBE_WRITE_GUARDED_OP(StoreElement, elements)
     WRITE_GUARDED_OP(StoreElementHole, elements)
     UNSAFE_OP(ArrayPopShift)
     UNSAFE_OP(ArrayPush)
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -951,23 +951,24 @@ LeaveWith(JSContext *cx, BaselineFrame *
 }
 
 bool
 InitBaselineFrameForOsr(BaselineFrame *frame, StackFrame *interpFrame, uint32_t numStackValues)
 {
     return frame->initForOsr(interpFrame, numStackValues);
 }
 
-JSObject *CreateDerivedTypedObj(JSContext *cx, HandleObject descr,
-                                HandleObject owner, int32_t offset)
+JSObject *
+CreateDerivedTypedObj(JSContext *cx, HandleObject descr,
+                      HandleObject owner, int32_t offset)
 {
     JS_ASSERT(descr->is<SizedTypeDescr>());
-    JS_ASSERT(owner->is<TypedDatum>());
+    JS_ASSERT(owner->is<TypedObject>());
     Rooted<SizedTypeDescr*> descr1(cx, &descr->as<SizedTypeDescr>());
-    Rooted<TypedDatum*> owner1(cx, &owner->as<TypedDatum>());
+    Rooted<TypedObject*> owner1(cx, &owner->as<TypedObject>());
     return TypedObject::createDerived(cx, descr1, owner1, offset);
 }
 
 JSString *
 RegExpReplace(JSContext *cx, HandleString string, HandleObject regexp, HandleString repl)
 {
     JS_ASSERT(string);
     JS_ASSERT(repl);
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -409,17 +409,17 @@ MSG_DEF(JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD
 MSG_DEF(JSMSG_GENERATOR_FINISHED,     356, 0, JSEXN_TYPEERR, "generator has already finished")
 MSG_DEF(JSMSG_TYPEDOBJECT_TOO_BIG, 357, 0, JSEXN_ERR, "Type is too large to allocate")
 MSG_DEF(JSMSG_TYPEDOBJECT_NOT_TYPE_OBJECT, 358, 0, JSEXN_ERR, "Expected a type object")
 MSG_DEF(JSMSG_TOO_MANY_CON_SPREADARGS, 359, 0, JSEXN_RANGEERR, "too many constructor arguments")
 MSG_DEF(JSMSG_TOO_MANY_FUN_SPREADARGS, 360, 0, JSEXN_RANGEERR, "too many function arguments")
 MSG_DEF(JSMSG_DEBUG_NOT_DEBUGGEE,     361, 2, JSEXN_ERR, "{0} is not a debuggee {1}")
 MSG_DEF(JSMSG_TYPEDOBJECT_NOT_TYPED_OBJECT, 362, 0, JSEXN_ERR, "Expected a typed object")
 MSG_DEF(JSMSG_TYPEDOBJECT_NO_SUCH_PROP, 363, 1, JSEXN_TYPEERR, "No such property: {0}")
-MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, 364, 2, JSEXN_TYPEERR, "argument {0} invalid: expected {1}")
+MSG_DEF(JSMSG_TYPEDOBJECT_BAD_ARGS, 364, 0, JSEXN_TYPEERR, "invalid arguments")
 MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED, 365, 0, JSEXN_TYPEERR, "handle unattached")
 MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_BAD_TYPE, 366, 0, JSEXN_TYPEERR, "handle moved to destination of incorrect type")
 
 MSG_DEF(JSMSG_IMPORT_DECL_AT_TOP_LEVEL, 367, 0, JSEXN_SYNTAXERR, "import declarations may only appear at top level")
 MSG_DEF(JSMSG_NO_IMPORT_NAME,         368, 0, JSEXN_SYNTAXERR, "missing import name")
 MSG_DEF(JSMSG_AS_AFTER_RESERVED_WORD, 369, 1, JSEXN_SYNTAXERR, "missing keyword 'as' after reserved word '{0}'")
 MSG_DEF(JSMSG_NO_BINDING_NAME,	      370, 0, JSEXN_SYNTAXERR, "missing binding name")
 MSG_DEF(JSMSG_RC_AFTER_IMPORT_SPEC_LIST, 371, 0, JSEXN_SYNTAXERR, "missing '}' after module specifier list")
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -912,16 +912,27 @@ NewBuiltinClassInstance(ExclusiveContext
 
 inline JSObject *
 NewBuiltinClassInstance(ExclusiveContext *cx, const Class *clasp, NewObjectKind newKind = GenericObject)
 {
     gc::AllocKind allocKind = gc::GetGCObjectKind(clasp);
     return NewBuiltinClassInstance(cx, clasp, allocKind, newKind);
 }
 
+template<typename T>
+inline T *
+NewBuiltinClassInstance(ExclusiveContext *cx, NewObjectKind newKind = GenericObject)
+{
+    JSObject *obj = NewBuiltinClassInstance(cx, &T::class_, newKind);
+    if (!obj)
+        return nullptr;
+
+    return &obj->as<T>();
+}
+
 // Used to optimize calls to (new Object())
 bool
 NewObjectScriptedCall(JSContext *cx, MutableHandleObject obj);
 
 /* Make an object with pregenerated shape from a NEWOBJECT bytecode. */
 static inline JSObject *
 CopyInitializerObject(JSContext *cx, HandleObject baseobj, NewObjectKind newKind = GenericObject)
 {
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -150,16 +150,17 @@ UNIFIED_SOURCES += [
     'jsstr.cpp',
     'jswatchpoint.cpp',
     'jsweakmap.cpp',
     'jsworkers.cpp',
     'jswrapper.cpp',
     'perf/jsperf.cpp',
     'prmjtime.cpp',
     'vm/ArgumentsObject.cpp',
+    'vm/ArrayBufferObject.cpp',
     'vm/CallNonGenericMethod.cpp',
     'vm/CharacterEncoding.cpp',
     'vm/Compression.cpp',
     'vm/DateTime.cpp',
     'vm/Debugger.cpp',
     'vm/ErrorObject.cpp',
     'vm/ForkJoin.cpp',
     'vm/GlobalObject.cpp',
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedObject/atopbuffer.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
+var BUGNUMBER = 898356;
+
+var {StructType, uint32, Object, Any, storage, objectType} = TypedObject;
+
+function main() { // once a C programmer, always a C programmer.
+  print(BUGNUMBER + ": " + summary);
+
+  var Uints = new StructType({f: uint32, g: uint32});
+
+  var anArray = new Uint32Array(2);
+  anArray[0] = 22;
+  anArray[1] = 44;
+
+  var uints = new Uints(anArray.buffer);
+  assertEq(storage(uints).buffer, anArray.buffer);
+  assertEq(uints.f, 22);
+  assertEq(uints.g, 44);
+
+  uints.f++;
+  assertEq(anArray[0], 23);
+
+  reportCompare(true, true);
+  print("Tests complete");
+}
+
+main();
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedObject/atopbufferwithoffset.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
+var BUGNUMBER = 898356;
+
+var {StructType, uint32, Object, Any, storage, objectType} = TypedObject;
+
+function main() { // once a C programmer, always a C programmer.
+  print(BUGNUMBER + ": " + summary);
+
+  var Uints = new StructType({f: uint32, g: uint32});
+
+  var anArray = new Uint32Array(4);
+  anArray[1] = 22;
+  anArray[2] = 44;
+
+  var uints = new Uints(anArray.buffer, 4);
+  assertEq(storage(uints).buffer, anArray.buffer);
+  assertEq(uints.f, 22);
+  assertEq(uints.g, 44);
+  uints.f++;
+  assertEq(anArray[1], 23);
+
+  // No misaligned byte offsets or offsets that would stretch past the end:
+  assertThrows(() => new Uints(anArray.buffer, -4)); // negative
+  assertThrows(() => new Uints(anArray.buffer, -3)); // negative
+  assertThrows(() => new Uints(anArray.buffer, -2)); // negative
+  assertThrows(() => new Uints(anArray.buffer, -1)); // negative
+  new Uints(anArray.buffer, 0);                      // ok
+  assertThrows(() => new Uints(anArray.buffer, 1));  // misaligned
+  assertThrows(() => new Uints(anArray.buffer, 2));  // misaligned
+  assertThrows(() => new Uints(anArray.buffer, 3));  // misaligned
+  new Uints(anArray.buffer, 4);                      // ok
+  assertThrows(() => new Uints(anArray.buffer, 5));  // misaligned
+  assertThrows(() => new Uints(anArray.buffer, 6));  // misaligned
+  assertThrows(() => new Uints(anArray.buffer, 7));  // misaligned
+  new Uints(anArray.buffer, 8);                      // ok
+  assertThrows(() => new Uints(anArray.buffer, 9));  // misaligned
+  assertThrows(() => new Uints(anArray.buffer, 10)); // misaligned
+  assertThrows(() => new Uints(anArray.buffer, 11)); // misaligned
+  assertThrows(() => new Uints(anArray.buffer, 12)); // would extend past end
+  assertThrows(() => new Uints(anArray.buffer, 13)); // misaligned
+  assertThrows(() => new Uints(anArray.buffer, 14)); // misaligned
+  assertThrows(() => new Uints(anArray.buffer, 15)); // misaligned
+  assertThrows(() => new Uints(anArray.buffer, 16)); // would extend past end
+  assertThrows(() => new Uints(anArray.buffer, 17)); // misaligned
+  assertThrows(() => new Uints(anArray.buffer, 4294967292)); // overflows int
+
+  reportCompare(true, true);
+  print("Tests complete");
+}
+
+main();
--- a/js/src/tests/ecma_6/TypedObject/referencetypetrace.js
+++ b/js/src/tests/ecma_6/TypedObject/referencetypetrace.js
@@ -44,17 +44,17 @@ function TestArrayElements(RefType) {
   assertCanReach(s1, rabbit);
   s1[0] = null;
   assertCannotReach(s1, rabbit);
 }
 
 function TestUnsizedArrayElements(RefType) {
   var rabbit = {};
   var S1 = new ArrayType(RefType);
-  var s1 = new S1(1, [rabbit]);
+  var s1 = new S1([rabbit]);
   assertCanReach(s1, rabbit);
   s1[0] = null;
   assertCannotReach(s1, rabbit);
 }
 
 function TestStructInArray(RefType) {
   var rabbit = {};
   var S2 = new StructType({f: RefType, g: RefType});
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedObject/storageopaque.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
+var BUGNUMBER = 898356;
+
+var {StructType, uint32, Object, Any, storage, objectType} = TypedObject;
+
+function main() { // once a C programmer, always a C programmer.
+  print(BUGNUMBER + ": " + summary);
+
+  var Uints = new StructType({f: uint32, g: uint32});
+  var uints = new Uints({f: 0, g: 1});
+  assertEq(storage(uints) != null, true);
+
+  var Objects = new StructType({f: Object, g: Object});
+  var objects = new Objects({f: 0, g: 1});
+  assertEq(storage(objects), null);
+
+  var Anys = new StructType({f: Any, g: Any});
+  var anys = new Anys({f: 0, g: 1});
+  assertEq(storage(anys), null);
+
+  // Note: test that `mixed.g`, when derived from an opaque buffer,
+  // remains opaque.
+  var Mixed = new StructType({f: Object, g: Uints});
+  var mixed = new Mixed({f: 0, g: {f: 22, g: 44}});
+  assertEq(storage(mixed), null);
+  assertEq(objectType(mixed.g), Uints);
+  assertEq(storage(mixed.g), null);
+
+  reportCompare(true, true);
+  print("Tests complete");
+}
+
+main();
--- a/js/src/tests/ecma_6/TypedObject/unsizedarrays.js
+++ b/js/src/tests/ecma_6/TypedObject/unsizedarrays.js
@@ -11,18 +11,18 @@ var { ArrayType, StructType, uint8, floa
 var ObjectType = TypedObject.Object;
 
 function runTests() {
   print(BUGNUMBER + ": " + summary);
 
   (function SimpleArrayOfTwoObjects() {
     print("SimpleArrayOfTwoObjects");
     var Objects = new ArrayType(ObjectType);
-    var objects2 = new Objects(2, [{f: "Hello"},
-                                   {f: "World"}]);
+    var objects2 = new Objects([{f: "Hello"},
+                                {f: "World"}]);
     assertEq(objects2[0].f, "Hello");
     assertEq(objects2[1].f, "World");
     assertEq(objects2.length, 2);
   })();
 
   (function EmbedUnsizedArraysBad() {
     print("EmbedUnsizedArraysBad");
     var Objects = new ArrayType(ObjectType);
@@ -30,17 +30,17 @@ function runTests() {
     assertThrows(() => new StructType({f: Objects}));
   })();
 
   (function MultipleSizes() {
     print("MultipleSizes");
     var Uints = new ArrayType(uint32);
     var Point = new StructType({values: new ArrayType(uint32).dimension(3)});
 
-    var uints = new Uints(3, [0, 1, 2]);
+    var uints = new Uints([0, 1, 2]);
     var point = new Point({values: uints});
 
     assertEq(uints.length, point.values.length);
     for (var i = 0; i < uints.length; i++) {
       assertEq(uints[i], i);
       assertEq(uints[i], point.values[i]);
     }
   })();
new file mode 100644
--- /dev/null
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -0,0 +1,1427 @@
+/* -*- 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/ArrayBufferObject.h"
+
+#include "mozilla/Alignment.h"
+#include "mozilla/FloatingPoint.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 "gc/Barrier.h"
+#include "gc/Marking.h"
+#include "jit/AsmJS.h"
+#include "jit/AsmJSModule.h"
+#include "vm/GlobalObject.h"
+#include "vm/Interpreter.h"
+#include "vm/NumericConversions.h"
+#include "vm/WrapperObject.h"
+
+#include "jsatominlines.h"
+#include "jsinferinlines.h"
+#include "jsobjinlines.h"
+
+#include "vm/Shape-inl.h"
+
+#if JS_USE_NEW_OBJECT_REPRESENTATION
+// See the comment above OldObjectRepresentationHack.
+#  error "TypedArray support for new object representation unimplemented."
+#endif
+
+using namespace js;
+using namespace js::gc;
+using namespace js::types;
+
+/*
+ * Allocate array buffers with the maximum number of fixed slots marked as
+ * reserved, so that the fixed slots may be used for the buffer's contents.
+ * The last fixed slot is kept for the object's private data.
+ */
+static const uint8_t ARRAYBUFFER_RESERVED_SLOTS = JSObject::MAX_FIXED_SLOTS - 1;
+
+// Sentinel value used to initialize ArrayBufferViewObjects' NEXT_BUFFER_SLOTs
+// to show that they have not yet been added to any ArrayBufferObject list.
+js::ArrayBufferObject * const js::UNSET_BUFFER_LINK = reinterpret_cast<js::ArrayBufferObject*>(0x2);
+
+/*
+ * Convert |v| to an array index for an array of length |length| per
+ * the Typed Array Specification section 7.0, |subarray|. If successful,
+ * the output value is in the range [0, length].
+ */
+bool
+js::ToClampedIndex(JSContext *cx, HandleValue v, uint32_t length, uint32_t *out)
+{
+    int32_t result;
+    if (!ToInt32(cx, v, &result))
+        return false;
+    if (result < 0) {
+        result += length;
+        if (result < 0)
+            result = 0;
+    } else if (uint32_t(result) > length) {
+        result = length;
+    }
+    *out = uint32_t(result);
+    return true;
+}
+
+/*
+ * ArrayBufferObject
+ *
+ * This class holds the underlying raw buffer that the TypedArrayObject classes
+ * access.  It can be created explicitly and passed to a TypedArrayObject, or
+ * can be created implicitly by constructing a TypedArrayObject with a size.
+ */
+
+/*
+ * ArrayBufferObject (base)
+ */
+
+const Class ArrayBufferObject::protoClass = {
+    "ArrayBufferPrototype",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_RESERVED_SLOTS(ARRAYBUFFER_RESERVED_SLOTS) |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer),
+    JS_PropertyStub,         /* addProperty */
+    JS_DeletePropertyStub,   /* delProperty */
+    JS_PropertyStub,         /* getProperty */
+    JS_StrictPropertyStub,   /* setProperty */
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub
+};
+
+const Class ArrayBufferObject::class_ = {
+    "ArrayBuffer",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_IMPLEMENTS_BARRIERS |
+    Class::NON_NATIVE |
+    JSCLASS_HAS_RESERVED_SLOTS(ARRAYBUFFER_RESERVED_SLOTS) |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer),
+    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   */
+    ArrayBufferObject::obj_trace,
+    JS_NULL_CLASS_SPEC,
+    JS_NULL_CLASS_EXT,
+    {
+        ArrayBufferObject::obj_lookupGeneric,
+        ArrayBufferObject::obj_lookupProperty,
+        ArrayBufferObject::obj_lookupElement,
+        ArrayBufferObject::obj_lookupSpecial,
+        ArrayBufferObject::obj_defineGeneric,
+        ArrayBufferObject::obj_defineProperty,
+        ArrayBufferObject::obj_defineElement,
+        ArrayBufferObject::obj_defineSpecial,
+        ArrayBufferObject::obj_getGeneric,
+        ArrayBufferObject::obj_getProperty,
+        ArrayBufferObject::obj_getElement,
+        ArrayBufferObject::obj_getSpecial,
+        ArrayBufferObject::obj_setGeneric,
+        ArrayBufferObject::obj_setProperty,
+        ArrayBufferObject::obj_setElement,
+        ArrayBufferObject::obj_setSpecial,
+        ArrayBufferObject::obj_getGenericAttributes,
+        ArrayBufferObject::obj_setGenericAttributes,
+        ArrayBufferObject::obj_deleteProperty,
+        ArrayBufferObject::obj_deleteElement,
+        ArrayBufferObject::obj_deleteSpecial,
+        nullptr, nullptr, /* watch/unwatch */
+        nullptr,          /* slice */
+        ArrayBufferObject::obj_enumerate,
+        nullptr,          /* thisObject      */
+    }
+};
+
+const JSFunctionSpec ArrayBufferObject::jsfuncs[] = {
+    JS_FN("slice", ArrayBufferObject::fun_slice, 2, JSFUN_GENERIC_NATIVE),
+    JS_FS_END
+};
+
+const JSFunctionSpec ArrayBufferObject::jsstaticfuncs[] = {
+    JS_FN("isView", ArrayBufferObject::fun_isView, 1, 0),
+    JS_FS_END
+};
+
+MOZ_ALWAYS_INLINE bool
+ArrayBufferObject::byteLengthGetterImpl(JSContext *cx, CallArgs args)
+{
+    JS_ASSERT(IsArrayBuffer(args.thisv()));
+    args.rval().setInt32(args.thisv().toObject().as<ArrayBufferObject>().byteLength());
+    return true;
+}
+
+bool
+ArrayBufferObject::byteLengthGetter(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return CallNonGenericMethod<IsArrayBuffer, byteLengthGetterImpl>(cx, args);
+}
+
+bool
+ArrayBufferObject::fun_slice_impl(JSContext *cx, CallArgs args)
+{
+    JS_ASSERT(IsArrayBuffer(args.thisv()));
+
+    Rooted<ArrayBufferObject*> thisObj(cx, &args.thisv().toObject().as<ArrayBufferObject>());
+
+    // these are the default values
+    uint32_t length = thisObj->byteLength();
+    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 = createSlice(cx, thisObj, begin, end);
+    if (!nobj)
+        return false;
+    args.rval().setObject(*nobj);
+    return true;
+}
+
+bool
+ArrayBufferObject::fun_slice(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return CallNonGenericMethod<IsArrayBuffer, fun_slice_impl>(cx, args);
+}
+
+/*
+ * ArrayBuffer.isView(obj); ES6 (Dec 2013 draft) 24.1.3.1
+ */
+bool
+ArrayBufferObject::fun_isView(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    args.rval().setBoolean(args.get(0).isObject() &&
+                           JS_IsArrayBufferViewObject(&args.get(0).toObject()));
+    return true;
+}
+
+/*
+ * new ArrayBuffer(byteLength)
+ */
+bool
+ArrayBufferObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
+{
+    int32_t nbytes = 0;
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (argc > 0 && !ToInt32(cx, args[0], &nbytes))
+        return false;
+
+    if (nbytes < 0) {
+        /*
+         * We're just not going to support arrays that are bigger than what will fit
+         * as an integer value; if someone actually ever complains (validly), then we
+         * can fix.
+         */
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
+        return false;
+    }
+
+    JSObject *bufobj = create(cx, uint32_t(nbytes));
+    if (!bufobj)
+        return false;
+    args.rval().setObject(*bufobj);
+    return true;
+}
+
+/*
+ * Note that some callers are allowed to pass in a nullptr cx, so we allocate
+ * with the cx if available and fall back to the runtime.  If oldptr is given,
+ * it's expected to be a previously-allocated ObjectElements* pointer that we
+ * then realloc.
+ */
+static ObjectElements *
+AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void *oldptr = nullptr)
+{
+    uint32_t size = nbytes + sizeof(ObjectElements);
+    ObjectElements *newheader;
+
+    // if oldptr is given, then we need to do a realloc
+    if (oldptr) {
+        ObjectElements *oldheader = static_cast<ObjectElements *>(oldptr);
+        uint32_t oldnbytes = ArrayBufferObject::headerInitializedLength(oldheader);
+
+        void *p = maybecx ? maybecx->runtime()->reallocCanGC(oldptr, size) : js_realloc(oldptr, size);
+        newheader = static_cast<ObjectElements *>(p);
+
+        // if we grew the array, we need to set the new bytes to 0
+        if (newheader && nbytes > oldnbytes)
+            memset(reinterpret_cast<uint8_t*>(newheader->elements()) + oldnbytes, 0, nbytes - oldnbytes);
+    } else {
+        void *p = maybecx ? maybecx->runtime()->callocCanGC(size) : js_calloc(size);
+        newheader = static_cast<ObjectElements *>(p);
+    }
+    if (!newheader) {
+        if (maybecx)
+            js_ReportOutOfMemory(maybecx);
+        return nullptr;
+    }
+
+    ArrayBufferObject::updateElementsHeader(newheader, nbytes);
+
+    return newheader;
+}
+
+// The list of views must be stored somewhere in the ArrayBufferObject, but
+// the slots are already being used for the element storage and the private
+// field is used for a delegate object. The ObjectElements header has space
+// for it, but I don't want to mess around with adding unions to it with
+// JS_USE_NEW_OBJECT_REPRESENTATION pending, since it will solve this much
+// more cleanly.
+struct OldObjectRepresentationHack {
+    uint32_t flags;
+    uint32_t initializedLength;
+    EncapsulatedPtr<ArrayBufferViewObject> views;
+};
+
+static ArrayBufferViewObject *
+GetViewList(ArrayBufferObject *obj)
+{
+    return reinterpret_cast<OldObjectRepresentationHack*>(obj->getElementsHeader())->views;
+}
+
+static void
+SetViewList(ArrayBufferObject *obj, ArrayBufferViewObject *viewsHead)
+{
+    reinterpret_cast<OldObjectRepresentationHack*>(obj->getElementsHeader())->views = viewsHead;
+    PostBarrierTypedArrayObject(obj);
+}
+
+static void
+InitViewList(ArrayBufferObject *obj, ArrayBufferViewObject *viewsHead)
+{
+    reinterpret_cast<OldObjectRepresentationHack*>(obj->getElementsHeader())->views.init(viewsHead);
+    PostBarrierTypedArrayObject(obj);
+}
+
+static EncapsulatedPtr<ArrayBufferViewObject> &
+GetViewListRef(ArrayBufferObject *obj)
+{
+    JS_ASSERT(obj->runtimeFromMainThread()->isHeapBusy());
+    return reinterpret_cast<OldObjectRepresentationHack*>(obj->getElementsHeader())->views;
+}
+
+/* static */ bool
+ArrayBufferObject::neuterViews(JSContext *cx, Handle<ArrayBufferObject*> buffer)
+{
+    ArrayBufferViewObject *view;
+    size_t numViews = 0;
+    for (view = GetViewList(buffer); view; view = view->nextView()) {
+        numViews++;
+        view->neuter(cx);
+
+        // Notify compiled jit code that the base pointer has moved.
+        MarkObjectStateChange(cx, view);
+    }
+
+    // neuterAsmJSArrayBuffer adjusts state specific to the ArrayBuffer data
+    // itself, but it only affects the behavior of views
+    if (buffer->isAsmJSArrayBuffer()) {
+        if (!ArrayBufferObject::neuterAsmJSArrayBuffer(cx, *buffer))
+            return false;
+    }
+
+    // Remove buffer from the list of buffers with > 1 view.
+    if (numViews > 1 && GetViewList(buffer)->bufferLink() != UNSET_BUFFER_LINK) {
+        ArrayBufferObject *prev = buffer->compartment()->gcLiveArrayBuffers;
+        if (prev == buffer) {
+            buffer->compartment()->gcLiveArrayBuffers = GetViewList(prev)->bufferLink();
+        } else {
+            for (ArrayBufferObject *b = GetViewList(prev)->bufferLink();
+                 b;
+                 b = GetViewList(b)->bufferLink())
+            {
+                if (b == buffer) {
+                    GetViewList(prev)->setBufferLink(GetViewList(b)->bufferLink());
+                    break;
+                }
+                prev = b;
+            }
+        }
+    }
+
+    return true;
+}
+
+void
+ArrayBufferObject::changeContents(JSContext *cx, ObjectElements *newHeader)
+{
+    JS_ASSERT(!isAsmJSArrayBuffer());
+
+    // Grab out data before invalidating it.
+    uint32_t byteLengthCopy = byteLength();
+    uintptr_t oldDataPointer = uintptr_t(dataPointer());
+    ArrayBufferViewObject *viewListHead = GetViewList(this);
+
+    // Update all views.
+    uintptr_t newDataPointer = uintptr_t(newHeader->elements());
+    for (ArrayBufferViewObject *view = viewListHead; view; view = view->nextView()) {
+        uintptr_t newDataPtr = uintptr_t(view->getPrivate()) - oldDataPointer + newDataPointer;
+        view->setPrivate(reinterpret_cast<uint8_t*>(newDataPtr));
+
+        // Notify compiled jit code that the base pointer has moved.
+        MarkObjectStateChange(cx, view);
+    }
+
+    // The list of views in the old header is reachable if the contents are
+    // being transferred, so null it out
+    SetViewList(this, nullptr);
+
+#ifdef JSGC_GENERATIONAL
+    ObjectElements *oldHeader = ObjectElements::fromElements(elements);
+    JS_ASSERT(oldHeader != newHeader);
+    JSRuntime *rt = runtimeFromMainThread();
+    if (hasDynamicElements())
+        rt->gcNursery.notifyRemovedElements(this, oldHeader);
+#endif
+
+    elements = newHeader->elements();
+
+#ifdef JSGC_GENERATIONAL
+    if (hasDynamicElements())
+        rt->gcNursery.notifyNewElements(this, newHeader);
+#endif
+
+    initElementsHeader(newHeader, byteLengthCopy);
+    InitViewList(this, viewListHead);
+}
+
+void
+ArrayBufferObject::neuter(JSContext *cx)
+{
+    JS_ASSERT(cx);
+    if (hasDynamicElements() && !isAsmJSArrayBuffer()) {
+        ObjectElements *oldHeader = getElementsHeader();
+        changeContents(cx, ObjectElements::fromElements(fixedElements()));
+
+        FreeOp fop(cx->runtime(), false);
+        fop.free_(oldHeader);
+    }
+
+    uint32_t byteLen = 0;
+    updateElementsHeader(getElementsHeader(), byteLen);
+
+    getElementsHeader()->setIsNeuteredBuffer();
+}
+
+/* static */ bool
+ArrayBufferObject::ensureNonInline(JSContext *cx, Handle<ArrayBufferObject*> buffer)
+{
+    if (buffer->hasDynamicElements())
+        return true;
+
+    ObjectElements *newHeader = AllocateArrayBufferContents(cx, buffer->byteLength());
+    if (!newHeader)
+        return false;
+
+    void *newHeaderDataPointer = reinterpret_cast<void*>(newHeader->elements());
+    memcpy(newHeaderDataPointer, buffer->dataPointer(), buffer->byteLength());
+
+    buffer->changeContents(cx, newHeader);
+    return true;
+}
+
+#if defined(JS_ION) && defined(JS_CPU_X64)
+// To avoid dynamically checking bounds on each load/store, asm.js code relies
+// on the SIGSEGV handler in AsmJSSignalHandlers.cpp. However, this only works
+// if we can guarantee that *any* out-of-bounds access generates a fault. This
+// isn't generally true since an out-of-bounds access could land on other
+// Mozilla data. To overcome this on x64, we reserve an entire 4GB space,
+// making only the range [0, byteLength) accessible, and use a 32-bit unsigned
+// index into this space. (x86 and ARM require different tricks.)
+//
+// One complication is that we need to put an ObjectElements struct immediately
+// before the data array (as required by the general JSObject data structure).
+// Thus, we must stick a page before the elements to hold ObjectElements.
+//
+//   |<------------------------------ 4GB + 1 pages --------------------->|
+//           |<--- sizeof --->|<------------------- 4GB ----------------->|
+//
+//   | waste | ObjectElements | data array | inaccessible reserved memory |
+//                            ^            ^                              ^
+//                            |            \                             /
+//                      obj->elements       required to be page boundaries
+//
+JS_STATIC_ASSERT(sizeof(ObjectElements) < AsmJSPageSize);
+JS_STATIC_ASSERT(AsmJSAllocationGranularity == AsmJSPageSize);
+static const size_t AsmJSMappedSize = AsmJSPageSize + AsmJSBufferProtectedSize;
+
+bool
+ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer)
+{
+    if (buffer->isAsmJSArrayBuffer())
+        return true;
+
+    // Get the entire reserved region (with all pages inaccessible).
+    void *p;
+# ifdef XP_WIN
+    p = VirtualAlloc(nullptr, AsmJSMappedSize, MEM_RESERVE, PAGE_NOACCESS);
+    if (!p)
+        return false;
+# else
+    p = mmap(nullptr, AsmJSMappedSize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
+    if (p == MAP_FAILED)
+        return false;
+# endif
+
+    // Enable access to the valid region.
+    JS_ASSERT(buffer->byteLength() % AsmJSAllocationGranularity == 0);
+# ifdef XP_WIN
+    if (!VirtualAlloc(p, AsmJSPageSize + buffer->byteLength(), MEM_COMMIT, PAGE_READWRITE)) {
+        VirtualFree(p, 0, MEM_RELEASE);
+        return false;
+    }
+# else
+    if (mprotect(p, AsmJSPageSize + buffer->byteLength(), PROT_READ | PROT_WRITE)) {
+        munmap(p, AsmJSMappedSize);
+        return false;
+    }
+# endif
+
+    // Copy over the current contents of the typed array.
+    uint8_t *data = reinterpret_cast<uint8_t*>(p) + AsmJSPageSize;
+    memcpy(data, buffer->dataPointer(), buffer->byteLength());
+
+    // Swap the new elements into the ArrayBufferObject.
+    ObjectElements *newHeader = reinterpret_cast<ObjectElements*>(data - sizeof(ObjectElements));
+    ObjectElements *oldHeader = buffer->hasDynamicElements() ? buffer->getElementsHeader()
+                                                             : nullptr;
+    buffer->changeContents(cx, newHeader);
+    js_free(oldHeader);
+
+    // Mark the ArrayBufferObject so (1) we don't do this again, (2) we know not
+    // to js_free the header in the normal way.
+    newHeader->setIsAsmJSArrayBuffer();
+    JS_ASSERT(data == buffer->dataPointer());
+    return true;
+}
+
+void
+ArrayBufferObject::releaseAsmJSArrayBuffer(FreeOp *fop, JSObject *obj)
+{
+    ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
+    JS_ASSERT(buffer.isAsmJSArrayBuffer());
+
+    uint8_t *p = buffer.dataPointer() - AsmJSPageSize ;
+    JS_ASSERT(uintptr_t(p) % AsmJSPageSize == 0);
+# ifdef XP_WIN
+    VirtualFree(p, 0, MEM_RELEASE);
+# else
+    munmap(p, AsmJSMappedSize);
+# endif
+}
+#else  /* defined(JS_ION) && defined(JS_CPU_X64) */
+bool
+ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer)
+{
+    if (buffer->isAsmJSArrayBuffer())
+        return true;
+
+    if (!ensureNonInline(cx, buffer))
+        return false;
+
+    JS_ASSERT(buffer->hasDynamicElements());
+    buffer->getElementsHeader()->setIsAsmJSArrayBuffer();
+    return true;
+}
+
+void
+ArrayBufferObject::releaseAsmJSArrayBuffer(FreeOp *fop, JSObject *obj)
+{
+    fop->free_(obj->as<ArrayBufferObject>().getElementsHeader());
+}
+#endif
+
+bool
+ArrayBufferObject::neuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer)
+{
+#ifdef JS_ION
+    AsmJSActivation *act = cx->mainThread().asmJSActivationStackFromOwnerThread();
+    for (; act; act = act->prev()) {
+        if (act->module().maybeHeapBufferObject() == &buffer)
+            break;
+    }
+    if (!act)
+        return true;
+
+    js_ReportOverRecursed(cx);
+    return false;
+#else
+    return true;
+#endif
+}
+
+void
+ArrayBufferObject::addView(ArrayBufferViewObject *view)
+{
+    // This view should never have been associated with a buffer before
+    JS_ASSERT(view->bufferLink() == UNSET_BUFFER_LINK);
+
+    // Note that pre-barriers are not needed here because either the list was
+    // previously empty, in which case no pointer is being overwritten, or the
+    // list was nonempty and will be made weak during this call (and weak
+    // pointers cannot violate the snapshot-at-the-beginning invariant.)
+
+    ArrayBufferViewObject *viewsHead = GetViewList(this);
+    if (viewsHead == nullptr) {
+        // This ArrayBufferObject will have a single view at this point, so it
+        // is a strong pointer (it will be marked during tracing.)
+        JS_ASSERT(view->nextView() == nullptr);
+    } else {
+        view->prependToViews(viewsHead);
+    }
+
+    SetViewList(this, view);
+}
+
+ArrayBufferObject *
+ArrayBufferObject::create(JSContext *cx, uint32_t nbytes, bool clear /* = true */)
+{
+    Rooted<ArrayBufferObject*> obj(cx, NewBuiltinClassInstance<ArrayBufferObject>(cx));
+    if (!obj)
+        return nullptr;
+    JS_ASSERT_IF(obj->isTenured(), obj->tenuredGetAllocKind() == gc::FINALIZE_OBJECT16_BACKGROUND);
+    JS_ASSERT(obj->getClass() == &class_);
+
+    js::Shape *empty = EmptyShape::getInitialShape(cx, &class_,
+                                                   obj->getProto(), obj->getParent(), obj->getMetadata(),
+                                                   gc::FINALIZE_OBJECT16_BACKGROUND);
+    if (!empty)
+        return nullptr;
+    obj->setLastPropertyInfallible(empty);
+
+    // ArrayBufferObjects delegate added properties to another JSObject, so
+    // their internal layout can use the object's fixed slots for storage.
+    // Set up the object to look like an array with an elements header.
+    JS_ASSERT(!obj->hasDynamicSlots());
+    JS_ASSERT(!obj->hasDynamicElements());
+
+    // The beginning stores an ObjectElements header structure holding the
+    // length. The rest of it is a flat data store for the array buffer.
+    size_t usableSlots = ARRAYBUFFER_RESERVED_SLOTS - ObjectElements::VALUES_PER_HEADER;
+
+    if (nbytes > sizeof(Value) * usableSlots) {
+        ObjectElements *header = AllocateArrayBufferContents(cx, nbytes);
+        if (!header)
+            return nullptr;
+        obj->elements = header->elements();
+
+#ifdef JSGC_GENERATIONAL
+        JSRuntime *rt = obj->runtimeFromMainThread();
+        rt->gcNursery.notifyNewElements(obj, header);
+#endif
+    } else {
+        obj->setFixedElements();
+        if (clear)
+            memset(obj->dataPointer(), 0, nbytes);
+    }
+
+    obj->initElementsHeader(obj->getElementsHeader(), nbytes);
+
+    return obj;
+}
+
+JSObject *
+ArrayBufferObject::createSlice(JSContext *cx, Handle<ArrayBufferObject*> arrayBuffer,
+                               uint32_t begin, uint32_t end)
+{
+    JS_ASSERT(begin <= arrayBuffer->byteLength());
+    JS_ASSERT(end <= arrayBuffer->byteLength());
+    JS_ASSERT(begin <= end);
+    uint32_t length = end - begin;
+
+    if (!arrayBuffer->hasData())
+        return create(cx, 0);
+
+    JSObject *slice = create(cx, length, false);
+    if (!slice)
+        return nullptr;
+    memcpy(slice->as<ArrayBufferObject>().dataPointer(), arrayBuffer->dataPointer() + begin, length);
+    return slice;
+}
+
+bool
+ArrayBufferObject::createDataViewForThisImpl(JSContext *cx, CallArgs args)
+{
+    JS_ASSERT(IsArrayBuffer(args.thisv()));
+
+    /*
+     * This method is only called for |DataView(alienBuf, ...)| which calls
+     * this as |createDataViewForThis.call(alienBuf, ..., DataView.prototype)|,
+     * ergo there must be at least two arguments.
+     */
+    JS_ASSERT(args.length() >= 2);
+
+    Rooted<JSObject*> proto(cx, &args[args.length() - 1].toObject());
+
+    Rooted<JSObject*> buffer(cx, &args.thisv().toObject());
+
+    /*
+     * Pop off the passed-along prototype and delegate to normal DataViewObject
+     * construction.
+     */
+    CallArgs frobbedArgs = CallArgsFromVp(args.length() - 1, args.base());
+    return DataViewObject::construct(cx, buffer, frobbedArgs, proto);
+}
+
+bool
+ArrayBufferObject::createDataViewForThis(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return CallNonGenericMethod<IsArrayBuffer, createDataViewForThisImpl>(cx, args);
+}
+
+/* static */ bool
+ArrayBufferObject::stealContents(JSContext *cx, Handle<ArrayBufferObject*> buffer, void **contents,
+                                 uint8_t **data)
+{
+    // If the ArrayBuffer's elements are dynamically allocated and nothing else
+    // prevents us from stealing them, transfer ownership directly.  Otherwise,
+    // the elements are small and allocated inside the ArrayBuffer object's GC
+    // header so we must make a copy.
+    ObjectElements *transferableHeader;
+    bool stolen;
+    if (buffer->hasDynamicElements() && !buffer->isAsmJSArrayBuffer()) {
+        stolen = true;
+        transferableHeader = buffer->getElementsHeader();
+    } else {
+        stolen = false;
+
+        uint32_t byteLen = buffer->byteLength();
+        transferableHeader = AllocateArrayBufferContents(cx, byteLen);
+        if (!transferableHeader)
+            return false;
+
+        initElementsHeader(transferableHeader, byteLen);
+        void *headerDataPointer = reinterpret_cast<void*>(transferableHeader->elements());
+        memcpy(headerDataPointer, buffer->dataPointer(), byteLen);
+    }
+
+    JS_ASSERT(!IsInsideNursery(cx->runtime(), transferableHeader));
+    *contents = transferableHeader;
+    *data = reinterpret_cast<uint8_t *>(transferableHeader + 1);
+
+    // Neuter the views, which may also mprotect(PROT_NONE) the buffer. So do
+    // it after copying out the data.
+    if (!ArrayBufferObject::neuterViews(cx, buffer))
+        return false;
+
+    // If the elements were taken from the neutered buffer, revert it back to
+    // using inline storage so it doesn't attempt to free the stolen elements
+    // when finalized.
+    if (stolen)
+        buffer->changeContents(cx, ObjectElements::fromElements(buffer->fixedElements()));
+
+    buffer->neuter(cx);
+    return true;
+}
+
+void
+ArrayBufferObject::obj_trace(JSTracer *trc, JSObject *obj)
+{
+    /*
+     * If this object changes, it will get marked via the private data barrier,
+     * so it's safe to leave it Unbarriered.
+     */
+    JSObject *delegate = static_cast<JSObject*>(obj->getPrivate());
+    if (delegate) {
+        JS_SET_TRACING_LOCATION(trc, &obj->privateRef(obj->numFixedSlots()));
+        MarkObjectUnbarriered(trc, &delegate, "arraybuffer.delegate");
+        obj->setPrivateUnbarriered(delegate);
+    }
+
+    // ArrayBufferObjects need to maintain a list of possibly-weak pointers to
+    // their views. The straightforward way to update the weak pointers would
+    // be in the views' finalizers, but giving views finalizers means they
+    // cannot be swept in the background. This results in a very high
+    // performance cost.  Instead, ArrayBufferObjects with a single view hold a
+    // strong pointer to the view. This can entrain garbage when the single
+    // view becomes otherwise unreachable while the buffer is still live, but
+    // this is expected to be rare. ArrayBufferObjects with 0-1 views are
+    // expected to be by far the most common cases. ArrayBufferObjects with
+    // multiple views are collected into a linked list during collection, and
+    // then swept to prune out their dead views.
+
+    ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
+    ArrayBufferViewObject *viewsHead = GetViewList(&buffer);
+    if (!viewsHead)
+        return;
+
+    // During minor collections, mark weak pointers on the buffer strongly.
+    if (trc->runtime->isHeapMinorCollecting()) {
+        MarkObject(trc, &GetViewListRef(&buffer), "arraybuffer.viewlist");
+        ArrayBufferViewObject *prior = GetViewList(&buffer);
+        for (ArrayBufferViewObject *view = prior->nextView();
+             view;
+             prior = view, view = view->nextView())
+        {
+            MarkObjectUnbarriered(trc, &view, "arraybuffer.views");
+            prior->setNextView(view);
+        }
+        return;
+    }
+
+    ArrayBufferViewObject *firstView = viewsHead;
+    if (firstView->nextView() == nullptr) {
+        // Single view: mark it, but only if we're actually doing a GC pass
+        // right now. Otherwise, the tracing pass for barrier verification will
+        // fail if we add another view and the pointer becomes weak.
+        if (IS_GC_MARKING_TRACER(trc))
+            MarkObject(trc, &GetViewListRef(&buffer), "arraybuffer.singleview");
+    } else {
+        // Multiple views: do not mark, but append buffer to list.
+        if (IS_GC_MARKING_TRACER(trc)) {
+            // obj_trace may be called multiple times before sweep(), so avoid
+            // adding this buffer to the list multiple times.
+            if (firstView->bufferLink() == UNSET_BUFFER_LINK) {
+                JS_ASSERT(obj->compartment() == firstView->compartment());
+                ArrayBufferObject **bufList = &obj->compartment()->gcLiveArrayBuffers;
+                firstView->setBufferLink(*bufList);
+                *bufList = &obj->as<ArrayBufferObject>();
+            } else {
+#ifdef DEBUG
+                bool found = false;
+                for (ArrayBufferObject *p = obj->compartment()->gcLiveArrayBuffers;
+                     p;
+                     p = GetViewList(p)->bufferLink())
+                {
+                    if (p == obj)
+                    {
+                        JS_ASSERT(!found);
+                        found = true;
+                    }
+                }
+#endif
+            }
+        }
+    }
+}
+
+void
+ArrayBufferObject::sweep(JSCompartment *compartment)
+{
+    ArrayBufferObject *buffer = compartment->gcLiveArrayBuffers;
+    JS_ASSERT(buffer != UNSET_BUFFER_LINK);
+    compartment->gcLiveArrayBuffers = nullptr;
+
+    while (buffer) {
+        ArrayBufferViewObject *viewsHead = GetViewList(buffer);
+        JS_ASSERT(viewsHead);
+
+        ArrayBufferObject *nextBuffer = viewsHead->bufferLink();
+        JS_ASSERT(nextBuffer != UNSET_BUFFER_LINK);
+        viewsHead->setBufferLink(UNSET_BUFFER_LINK);
+
+        // Rebuild the list of views of the ArrayBufferObject, discarding dead
+        // views.  If there is only one view, it will have already been marked.
+        ArrayBufferViewObject *prevLiveView = nullptr;
+        ArrayBufferViewObject *view = viewsHead;
+        while (view) {
+            JS_ASSERT(buffer->compartment() == view->compartment());
+            ArrayBufferViewObject *nextView = view->nextView();
+            if (!IsObjectAboutToBeFinalized(&view)) {
+                view->setNextView(prevLiveView);
+                prevLiveView = view;
+            }
+            view = nextView;
+        }
+        SetViewList(buffer, prevLiveView);
+
+        buffer = nextBuffer;
+    }
+}
+
+void
+ArrayBufferObject::resetArrayBufferList(JSCompartment *comp)
+{
+    ArrayBufferObject *buffer = comp->gcLiveArrayBuffers;
+    JS_ASSERT(buffer != UNSET_BUFFER_LINK);
+    comp->gcLiveArrayBuffers = nullptr;
+
+    while (buffer) {
+        ArrayBufferViewObject *view = GetViewList(buffer);
+        JS_ASSERT(view);
+
+        ArrayBufferObject *nextBuffer = view->bufferLink();
+        JS_ASSERT(nextBuffer != UNSET_BUFFER_LINK);
+
+        view->setBufferLink(UNSET_BUFFER_LINK);
+        buffer = nextBuffer;
+    }
+}
+
+/* static */ bool
+ArrayBufferObject::saveArrayBufferList(JSCompartment *comp, ArrayBufferVector &vector)
+{
+    ArrayBufferObject *buffer = comp->gcLiveArrayBuffers;
+    while (buffer) {
+        JS_ASSERT(buffer != UNSET_BUFFER_LINK);
+        if (!vector.append(buffer))
+            return false;
+
+        ArrayBufferViewObject *view = GetViewList(buffer);
+        JS_ASSERT(view);
+        buffer = view->bufferLink();
+    }
+    return true;
+}
+
+/* static */ void
+ArrayBufferObject::restoreArrayBufferLists(ArrayBufferVector &vector)
+{
+    for (ArrayBufferObject **p = vector.begin(); p != vector.end(); p++) {
+        ArrayBufferObject *buffer = *p;
+        JSCompartment *comp = buffer->compartment();
+        ArrayBufferViewObject *firstView = GetViewList(buffer);
+        JS_ASSERT(firstView);
+        JS_ASSERT(firstView->compartment() == comp);
+        JS_ASSERT(firstView->bufferLink() == UNSET_BUFFER_LINK);
+        firstView->setBufferLink(comp->gcLiveArrayBuffers);
+        comp->gcLiveArrayBuffers = buffer;
+    }
+}
+
+bool
+ArrayBufferObject::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
+                                     MutableHandleObject objp, MutableHandleShape propp)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+
+    bool delegateResult = JSObject::lookupGeneric(cx, delegate, id, objp, propp);
+
+    /* If false, there was an error, so propagate it.
+     * Otherwise, if propp is non-null, the property
+     * was found. Otherwise it was not
+     * found so look in the prototype chain.
+     */
+    if (!delegateResult)
+        return false;
+
+    if (propp) {
+        if (objp == delegate)
+            objp.set(obj);
+        return true;
+    }
+
+    RootedObject proto(cx, obj->getProto());
+    if (!proto) {
+        objp.set(nullptr);
+        propp.set(nullptr);
+        return true;
+    }
+
+    return JSObject::lookupGeneric(cx, proto, id, objp, propp);
+}
+
+bool
+ArrayBufferObject::obj_lookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
+                                      MutableHandleObject objp, MutableHandleShape propp)
+{
+    Rooted<jsid> id(cx, NameToId(name));
+    return obj_lookupGeneric(cx, obj, id, objp, propp);
+}
+
+bool
+ArrayBufferObject::obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
+                                     MutableHandleObject objp, MutableHandleShape propp)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+
+    /*
+     * If false, there was an error, so propagate it.
+     * Otherwise, if propp is non-null, the property
+     * was found. Otherwise it was not
+     * found so look in the prototype chain.
+     */
+    if (!JSObject::lookupElement(cx, delegate, index, objp, propp))
+        return false;
+
+    if (propp) {
+        if (objp == delegate)
+            objp.set(obj);
+        return true;
+    }
+
+    RootedObject proto(cx, obj->getProto());
+    if (proto)
+        return JSObject::lookupElement(cx, proto, index, objp, propp);
+
+    objp.set(nullptr);
+    propp.set(nullptr);
+    return true;
+}
+
+bool
+ArrayBufferObject::obj_lookupSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
+                                     MutableHandleObject objp, MutableHandleShape propp)
+{
+    Rooted<jsid> id(cx, SPECIALID_TO_JSID(sid));
+    return obj_lookupGeneric(cx, obj, id, objp, propp);
+}
+
+bool
+ArrayBufferObject::obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
+                                     PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
+{
+    AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
+
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+    return baseops::DefineGeneric(cx, delegate, id, v, getter, setter, attrs);
+}
+
+bool
+ArrayBufferObject::obj_defineProperty(JSContext *cx, HandleObject obj,
+                                      HandlePropertyName name, HandleValue v,
+                                      PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
+{
+    Rooted<jsid> id(cx, NameToId(name));
+    return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs);
+}
+
+bool
+ArrayBufferObject::obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v,
+                                     PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
+{
+    AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
+
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+    return baseops::DefineElement(cx, delegate, index, v, getter, setter, attrs);
+}
+
+bool
+ArrayBufferObject::obj_defineSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid, HandleValue v,
+                                     PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
+{
+    Rooted<jsid> id(cx, SPECIALID_TO_JSID(sid));
+    return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs);
+}
+
+bool
+ArrayBufferObject::obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
+                                  HandleId id, MutableHandleValue vp)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+    return baseops::GetProperty(cx, delegate, receiver, id, vp);
+}
+
+bool
+ArrayBufferObject::obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
+                                   HandlePropertyName name, MutableHandleValue vp)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+    Rooted<jsid> id(cx, NameToId(name));
+    return baseops::GetProperty(cx, delegate, receiver, id, vp);
+}
+
+bool
+ArrayBufferObject::obj_getElement(JSContext *cx, HandleObject obj,
+                                  HandleObject receiver, uint32_t index, MutableHandleValue vp)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+    return baseops::GetElement(cx, delegate, receiver, index, vp);
+}
+
+bool
+ArrayBufferObject::obj_getSpecial(JSContext *cx, HandleObject obj,
+                                  HandleObject receiver, HandleSpecialId sid,
+                                  MutableHandleValue vp)
+{
+    Rooted<jsid> id(cx, SPECIALID_TO_JSID(sid));
+    return obj_getGeneric(cx, obj, receiver, id, vp);
+}
+
+bool
+ArrayBufferObject::obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
+                                  MutableHandleValue vp, bool strict)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+
+    return baseops::SetPropertyHelper<SequentialExecution>(cx, delegate, obj, id, 0, vp, strict);
+}
+
+bool
+ArrayBufferObject::obj_setProperty(JSContext *cx, HandleObject obj,
+                                   HandlePropertyName name, MutableHandleValue vp, bool strict)
+{
+    Rooted<jsid> id(cx, NameToId(name));
+    return obj_setGeneric(cx, obj, id, vp, strict);
+}
+
+bool
+ArrayBufferObject::obj_setElement(JSContext *cx, HandleObject obj,
+                                  uint32_t index, MutableHandleValue vp, bool strict)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+
+    return baseops::SetElementHelper(cx, delegate, obj, index, 0, vp, strict);
+}
+
+bool
+ArrayBufferObject::obj_setSpecial(JSContext *cx, HandleObject obj,
+                                  HandleSpecialId sid, MutableHandleValue vp, bool strict)
+{
+    Rooted<jsid> id(cx, SPECIALID_TO_JSID(sid));
+    return obj_setGeneric(cx, obj, id, vp, strict);
+}
+
+bool
+ArrayBufferObject::obj_getGenericAttributes(JSContext *cx, HandleObject obj,
+                                            HandleId id, unsigned *attrsp)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+    return baseops::GetAttributes(cx, delegate, id, attrsp);
+}
+
+bool
+ArrayBufferObject::obj_setGenericAttributes(JSContext *cx, HandleObject obj,
+                                            HandleId id, unsigned *attrsp)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+    return baseops::SetAttributes(cx, delegate, id, attrsp);
+}
+
+bool
+ArrayBufferObject::obj_deleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
+                                      bool *succeeded)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+    return baseops::DeleteProperty(cx, delegate, name, succeeded);
+}
+
+bool
+ArrayBufferObject::obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
+                                     bool *succeeded)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+    return baseops::DeleteElement(cx, delegate, index, succeeded);
+}
+
+bool
+ArrayBufferObject::obj_deleteSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
+                                     bool *succeeded)
+{
+    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
+    if (!delegate)
+        return false;
+    return baseops::DeleteSpecial(cx, delegate, sid, succeeded);
+}
+
+bool
+ArrayBufferObject::obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
+                                 MutableHandleValue statep, MutableHandleId idp)
+{
+    statep.setNull();
+    return true;
+}
+
+/*
+ * ArrayBufferViewObject
+ */
+
+/*
+ * This method is used to trace TypedArrayObjects and DataViewObjects. We need
+ * a custom tracer because some of an ArrayBufferViewObject's reserved slots
+ * are weak references, and some need to be updated specially during moving
+ * GCs.
+ */
+/* static */ void
+ArrayBufferViewObject::trace(JSTracer *trc, JSObject *obj)
+{
+    HeapSlot &bufSlot = obj->getReservedSlotRef(BUFFER_SLOT);
+    MarkSlot(trc, &bufSlot, "typedarray.buffer");
+
+    /* Update obj's data slot if the array buffer moved. Note that during
+     * initialization, bufSlot may still be JSVAL_VOID. */
+    if (bufSlot.isObject()) {
+        ArrayBufferObject &buf = bufSlot.toObject().as<ArrayBufferObject>();
+        int32_t offset = obj->getReservedSlot(BYTEOFFSET_SLOT).toInt32();
+        obj->initPrivate(buf.dataPointer() + offset);
+    }
+
+    /* Update NEXT_VIEW_SLOT, if the view moved. */
+    IsSlotMarked(&obj->getReservedSlotRef(NEXT_VIEW_SLOT));
+}
+
+void
+ArrayBufferViewObject::prependToViews(ArrayBufferViewObject *viewsHead)
+{
+    setNextView(viewsHead);
+
+    // Move the multiview buffer list link into this view since we're
+    // prepending it to the list.
+    setBufferLink(viewsHead->bufferLink());
+    viewsHead->setBufferLink(UNSET_BUFFER_LINK);
+}
+
+void
+ArrayBufferViewObject::neuter(JSContext *cx)
+{
+    if (is<DataViewObject>())
+        as<DataViewObject>().neuter();
+    else if (is<TypedArrayObject>())
+        as<TypedArrayObject>().neuter(cx);
+    else
+        as<TypedObject>().neuter(cx);
+}
+
+/* JS Friend API */
+
+JS_FRIEND_API(bool)
+JS_IsArrayBufferViewObject(JSObject *obj)
+{
+    obj = CheckedUnwrap(obj);
+    return obj ? obj->is<ArrayBufferViewObject>() : false;
+}
+
+JS_FRIEND_API(uint32_t)
+JS_GetArrayBufferByteLength(JSObject *obj)
+{
+    obj = CheckedUnwrap(obj);
+    return obj ? obj->as<ArrayBufferObject>().byteLength() : 0;
+}
+
+JS_FRIEND_API(uint8_t *)
+JS_GetArrayBufferData(JSObject *obj)
+{
+    obj = CheckedUnwrap(obj);
+    if (!obj)
+        return nullptr;
+    return obj->as<ArrayBufferObject>().dataPointer();
+}
+
+JS_FRIEND_API(uint8_t *)
+JS_GetStableArrayBufferData(JSContext *cx, JSObject *obj)
+{
+    obj = CheckedUnwrap(obj);
+    if (!obj)
+        return nullptr;
+
+    Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
+    if (!ArrayBufferObject::ensureNonInline(cx, buffer))
+        return nullptr;
+
+    return buffer->dataPointer();
+}
+
+JS_FRIEND_API(bool)
+JS_NeuterArrayBuffer(JSContext *cx, HandleObject obj)
+{
+    if (!obj->is<ArrayBufferObject>()) {
+        JS_ReportError(cx, "ArrayBuffer object required");
+        return false;
+    }
+
+    Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
+    if (!ArrayBufferObject::neuterViews(cx, buffer))
+        return false;
+    buffer->neuter(cx);
+    return true;
+}
+
+JS_FRIEND_API(JSObject *)
+JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes)
+{
+    JS_ASSERT(nbytes <= INT32_MAX);
+    return ArrayBufferObject::create(cx, nbytes);
+}
+
+JS_PUBLIC_API(JSObject *)
+JS_NewArrayBufferWithContents(JSContext *cx, void *contents)
+{
+    JS_ASSERT(contents);
+    JSObject *obj = ArrayBufferObject::create(cx, 0);
+    if (!obj)
+        return nullptr;
+    js::ObjectElements *elements = reinterpret_cast<js::ObjectElements *>(contents);
+    obj->setDynamicElements(elements);
+    JS_ASSERT(GetViewList(&obj->as<ArrayBufferObject>()) == nullptr);
+
+#ifdef JSGC_GENERATIONAL
+    cx->runtime()->gcNursery.notifyNewElements(obj, elements);
+#endif
+    return obj;
+}
+
+JS_PUBLIC_API(bool)
+JS_AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes,
+                               void **contents, uint8_t **data)
+{
+    js::ObjectElements *header = AllocateArrayBufferContents(maybecx, nbytes);
+    if (!header)
+        return false;
+
+    ArrayBufferObject::updateElementsHeader(header, nbytes);
+
+    *contents = header;
+    *data = reinterpret_cast<uint8_t*>(header->elements());
+    return true;
+}
+
+JS_PUBLIC_API(bool)
+JS_ReallocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void **contents, uint8_t **data)
+{
+    js::ObjectElements *header = AllocateArrayBufferContents(maybecx, nbytes, *contents);
+    if (!header)
+        return false;
+
+    ArrayBufferObject::initElementsHeader(header, nbytes);
+
+    *contents = header;
+    *data = reinterpret_cast<uint8_t*>(header->elements());
+    return true;
+}
+
+JS_FRIEND_API(bool)
+JS_IsArrayBufferObject(JSObject *obj)
+{
+    obj = CheckedUnwrap(obj);
+    return obj ? obj->is<ArrayBufferObject>() : false;
+}
+
+JS_PUBLIC_API(bool)
+JS_StealArrayBufferContents(JSContext *cx, HandleObject objArg, void **contents, uint8_t **data)
+{
+    JSObject *obj = CheckedUnwrap(objArg);
+    if (!obj)
+        return false;
+
+    if (!obj->is<ArrayBufferObject>()) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
+        return false;
+    }
+
+    Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
+    if (!ArrayBufferObject::stealContents(cx, buffer, contents, data))
+        return false;
+
+    return true;
+}
+
+JS_FRIEND_API(void *)
+JS_GetArrayBufferViewData(JSObject *obj)
+{
+    obj = CheckedUnwrap(obj);
+    if (!obj)
+        return nullptr;
+    return obj->is<DataViewObject>() ? obj->as<DataViewObject>().dataPointer()
+                                     : obj->as<TypedArrayObject>().viewData();
+}
+
+JS_FRIEND_API(JSObject *)
+JS_GetArrayBufferViewBuffer(JSObject *obj)
+{
+    obj = CheckedUnwrap(obj);
+    if (!obj)
+        return nullptr;
+    return obj->as<ArrayBufferViewObject>().bufferObject();
+}
+
+JS_FRIEND_API(uint32_t)
+JS_GetArrayBufferViewByteLength(JSObject *obj)
+{
+    obj = CheckedUnwrap(obj);
+    if (!obj)
+        return 0;
+    return obj->is<DataViewObject>()
+           ? obj->as<DataViewObject>().byteLength()
+           : obj->as<TypedArrayObject>().byteLength();
+}
+
+JS_FRIEND_API(JSObject *)
+JS_GetObjectAsArrayBufferView(JSObject *obj, uint32_t *length, uint8_t **data)
+{
+    if (!(obj = CheckedUnwrap(obj)))
+        return nullptr;
+    if (!(obj->is<ArrayBufferViewObject>()))
+        return nullptr;
+
+    *length = obj->is<DataViewObject>()
+              ? obj->as<DataViewObject>().byteLength()
+              : obj->as<TypedArrayObject>().byteLength();
+
+    *data = static_cast<uint8_t*>(obj->is<DataViewObject>()
+                                  ? obj->as<DataViewObject>().dataPointer()
+                                  : obj->as<TypedArrayObject>().viewData());
+    return obj;
+}
+
+JS_FRIEND_API(JSObject *)
+JS_GetObjectAsArrayBuffer(JSObject *obj, uint32_t *length, uint8_t **data)
+{
+    if (!(obj = CheckedUnwrap(obj)))
+        return nullptr;
+    if (!obj->is<ArrayBufferObject>())
+        return nullptr;
+
+    *length = obj->as<ArrayBufferObject>().byteLength();
+    *data = obj->as<ArrayBufferObject>().dataPointer();
+
+    return obj;
+}
+
new file mode 100644
--- /dev/null
+++ b/js/src/vm/ArrayBufferObject.h
@@ -0,0 +1,317 @@
+/* -*- 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_h
+#define vm_ArrayBufferObject_h
+
+#include "jsobj.h"
+
+#include "builtin/TypedObjectConstants.h"
+#include "vm/Runtime.h"
+
+typedef struct JSProperty JSProperty;
+
+namespace js {
+
+class ArrayBufferViewObject;
+
+// The inheritance hierarchy for the various classes relating to typed arrays
+// is as follows.
+//
+// - JSObject
+//   - ArrayBufferObject
+//   - ArrayBufferViewObject
+//     - DataViewObject
+//     - TypedArrayObject (declared in vm/TypedArrayObject.h)
+//       - TypedArrayObjectTemplate
+//         - Int8ArrayObject
+//         - Uint8ArrayObject
+//         - ...
+//     - TypedObject (declared in builtin/TypedObject.h)
+//
+// Note that |TypedArrayObjectTemplate| is just an implementation
+// detail that makes implementing its various subclasses easier.
+
+typedef Vector<ArrayBufferObject *, 0, SystemAllocPolicy> ArrayBufferVector;
+
+/*
+ * 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.
+ */
+class ArrayBufferObject : public JSObject
+{
+    static bool byteLengthGetterImpl(JSContext *cx, CallArgs args);
+    static bool fun_slice_impl(JSContext *cx, CallArgs args);
+
+  public:
+    static const Class class_;
+
+    static const Class protoClass;
+    static const JSFunctionSpec jsfuncs[];
+    static const JSFunctionSpec jsstaticfuncs[];
+
+    static bool byteLengthGetter(JSContext *cx, unsigned argc, Value *vp);
+
+    static bool fun_slice(JSContext *cx, unsigned argc, Value *vp);
+
+    static bool fun_isView(JSContext *cx, unsigned argc, Value *vp);
+
+    static bool class_constructor(JSContext *cx, unsigned argc, Value *vp);
+
+    static ArrayBufferObject *create(JSContext *cx, uint32_t nbytes, bool clear = true);
+
+    static JSObject *createSlice(JSContext *cx, Handle<ArrayBufferObject*> arrayBuffer,
+                                 uint32_t begin, uint32_t end);
+
+    static bool createDataViewForThisImpl(JSContext *cx, CallArgs args);
+    static bool createDataViewForThis(JSContext *cx, unsigned argc, Value *vp);
+
+    template<typename T>
+    static bool createTypedArrayFromBufferImpl(JSContext *cx, CallArgs args);
+
+    template<typename T>
+    static bool createTypedArrayFromBuffer(JSContext *cx, unsigned argc, Value *vp);
+
+    static void obj_trace(JSTracer *trc, JSObject *obj);
+
+    static bool obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
+                                  MutableHandleObject objp, MutableHandleShape propp);
+    static bool obj_lookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
+                                   MutableHandleObject objp, MutableHandleShape propp);
+    static bool obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
+                                  MutableHandleObject objp, MutableHandleShape propp);
+    static bool obj_lookupSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
+                                  MutableHandleObject objp, MutableHandleShape propp);
+
+    static bool obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
+                                  PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
+    static bool obj_defineProperty(JSContext *cx, HandleObject obj,
+                                   HandlePropertyName name, HandleValue v,
+                                   PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
+    static bool obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v,
+                                  PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
+    static bool obj_defineSpecial(JSContext *cx, HandleObject obj,
+                                  HandleSpecialId sid, HandleValue v,
+                                  PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
+
+    static bool obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
+                               HandleId id, MutableHandleValue vp);
+
+    static bool obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
+                                HandlePropertyName name, MutableHandleValue vp);
+
+    static bool obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver,
+                               uint32_t index, MutableHandleValue vp);
+
+    static bool obj_getSpecial(JSContext *cx, HandleObject obj, HandleObject receiver,
+                               HandleSpecialId sid, MutableHandleValue vp);
+
+    static bool obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
+                               MutableHandleValue vp, bool strict);
+    static bool obj_setProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
+                                MutableHandleValue vp, bool strict);
+    static bool obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
+                               MutableHandleValue vp, bool strict);
+    static bool obj_setSpecial(JSContext *cx, HandleObject obj,
+                               HandleSpecialId sid, MutableHandleValue vp, bool strict);
+
+    static bool obj_getGenericAttributes(JSContext *cx, HandleObject obj,
+                                         HandleId id, unsigned *attrsp);
+    static bool obj_setGenericAttributes(JSContext *cx, HandleObject obj,
+                                         HandleId id, unsigned *attrsp);
+
+    static bool obj_deleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
+                                   bool *succeeded);
+    static bool obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
+                                  bool *succeeded);
+    static bool obj_deleteSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
+                                  bool *succeeded);
+
+    static bool obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
+                              MutableHandleValue statep, MutableHandleId idp);
+
+    static void sweep(JSCompartment *rt);
+
+    static void resetArrayBufferList(JSCompartment *rt);
+    static bool saveArrayBufferList(JSCompartment *c, ArrayBufferVector &vector);
+    static void restoreArrayBufferLists(ArrayBufferVector &vector);
+
+    static bool stealContents(JSContext *cx, Handle<ArrayBufferObject*> buffer, void **contents,
+                              uint8_t **data);
+
+    static void updateElementsHeader(js::ObjectElements *header, uint32_t bytes) {
+        header->initializedLength = bytes;
+
+        // NB: one or both of these fields is clobbered by GetViewList to store
+        // the 'views' link. Set them to 0 to effectively initialize 'views'
+        // to nullptr.
+        header->length = 0;
+        header->capacity = 0;
+    }
+
+    static void initElementsHeader(js::ObjectElements *header, uint32_t bytes) {
+        header->flags = 0;
+        updateElementsHeader(header, bytes);
+    }
+
+    static uint32_t headerInitializedLength(const js::ObjectElements *header) {
+        return header->initializedLength;
+    }
+
+    void addView(ArrayBufferViewObject *view);
+
+    void changeContents(JSContext *cx, ObjectElements *newHeader);
+
+    /*
+     * Ensure data is not stored inline in the object. Used when handing back a
+     * GC-safe pointer.
+     */
+    static bool ensureNonInline(JSContext *cx, Handle<ArrayBufferObject*> buffer);
+
+    uint32_t byteLength() const {
+        return getElementsHeader()->initializedLength;
+    }
+
+    /*
+     * Neuter all views of an ArrayBuffer.
+     */
+    static bool neuterViews(JSContext *cx, Handle<ArrayBufferObject*> buffer);
+
+    inline uint8_t * dataPointer() const {
+        return (uint8_t *) elements;
+    }
+
+    /*
+     * Discard the ArrayBuffer contents. For asm.js buffers, at least, should
+     * be called after neuterViews().
+     */
+    void neuter(JSContext *cx);
+
+    /*
+     * Check if the arrayBuffer contains any data. This will return false for
+     * ArrayBuffer.prototype and neutered ArrayBuffers.
+     */
+    bool hasData() const {
+        return getClass() == &class_;
+    }
+
+    bool isAsmJSArrayBuffer() const {
+        return getElementsHeader()->isAsmJSArrayBuffer();
+    }
+    bool isNeutered() const {
+        return getElementsHeader()->isNeuteredBuffer();
+    }
+    static bool prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer);
+    static bool neuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer);
+    static void releaseAsmJSArrayBuffer(FreeOp *fop, JSObject *obj);
+};
+
+/*
+ * ArrayBufferViewObject
+ *
+ * Common definitions shared by all ArrayBufferViews.
+ */
+
+class ArrayBufferViewObject : public JSObject
+{
+  protected:
+    /* Offset of view in underlying ArrayBufferObject */
+    static const size_t BYTEOFFSET_SLOT  = JS_TYPEDOBJ_SLOT_BYTEOFFSET;
+
+    /* Byte length of view */
+    static const size_t BYTELENGTH_SLOT  = JS_TYPEDOBJ_SLOT_BYTELENGTH;
+
+    /* Underlying ArrayBufferObject */
+    static const size_t BUFFER_SLOT      = JS_TYPEDOBJ_SLOT_OWNER;
+
+    /* ArrayBufferObjects point to a linked list of views, chained through this slot */
+    static const size_t NEXT_VIEW_SLOT   = JS_TYPEDOBJ_SLOT_NEXT_VIEW;
+
+    /*
+     * When ArrayBufferObjects are traced during GC, they construct a linked
+     * list of ArrayBufferObjects with more than one view, chained through this
+     * slot of the first view of each ArrayBufferObject.
+     */
+    static const size_t NEXT_BUFFER_SLOT = JS_TYPEDOBJ_SLOT_NEXT_BUFFER;
+
+  public:
+    JSObject *bufferObject() const {
+        return &getFixedSlot(BUFFER_SLOT).toObject();
+    }
+
+    ArrayBufferObject *bufferLink() {
+        return static_cast<ArrayBufferObject*>(getFixedSlot(NEXT_BUFFER_SLOT).toPrivate());
+    }
+
+    inline void setBufferLink(ArrayBufferObject *buffer);
+
+    ArrayBufferViewObject *nextView() const {
+        return static_cast<ArrayBufferViewObject*>(getFixedSlot(NEXT_VIEW_SLOT).toPrivate());
+    }
+
+    inline void setNextView(ArrayBufferViewObject *view);
+
+    void prependToViews(ArrayBufferViewObject *viewsHead);
+
+    void neuter(JSContext *cx);
+
+    static void trace(JSTracer *trc, JSObject *obj);
+};
+
+bool
+ToClampedIndex(JSContext *cx, HandleValue v, uint32_t length, uint32_t *out);
+
+inline void
+PostBarrierTypedArrayObject(JSObject *obj)
+{
+#ifdef JSGC_GENERATIONAL
+    JS_ASSERT(obj);
+    JSRuntime *rt = obj->runtimeFromMainThread();
+    if (!rt->isHeapBusy() && !IsInsideNursery(rt, obj))
+        rt->gcStoreBuffer.putWholeCell(obj);
+#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.
+     */
+    obj->initPrivate(buffer->dataPointer() + byteOffset);
+    PostBarrierTypedArrayObject(obj);
+}
+
+MOZ_ALWAYS_INLINE bool
+IsArrayBuffer(HandleValue v)
+{
+    return v.isObject() && v.toObject().is<ArrayBufferObject>();
+}
+
+inline void
+ArrayBufferViewObject::setBufferLink(ArrayBufferObject *buffer)
+{
+    setFixedSlot(NEXT_BUFFER_SLOT, PrivateValue(buffer));
+    PostBarrierTypedArrayObject(this);
+}
+
+inline void
+ArrayBufferViewObject::setNextView(ArrayBufferViewObject *view)
+{
+    setFixedSlot(NEXT_VIEW_SLOT, PrivateValue(view));
+    PostBarrierTypedArrayObject(this);
+}
+
+} // namespace js
+
+#endif // vm_ArrayBufferObject_h
--- a/js/src/vm/ForkJoin.cpp
+++ b/js/src/vm/ForkJoin.cpp
@@ -2166,21 +2166,21 @@ intrinsic_SetForkJoinTargetRegionPar(For
     // the middle of another. This is because the guarding code
     // assumes that handles, which never straddle across elements,
     // will either be contained entirely within the target region or
     // be contained entirely without of the region, and not straddling
     // the region nor encompassing it.
 
     CallArgs args = CallArgsFromVp(argc, vp);
     JS_ASSERT(argc == 3);
-    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedDatum>());
+    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
     JS_ASSERT(args[1].isInt32());
     JS_ASSERT(args[2].isInt32());
 
-    uint8_t *mem = args[0].toObject().as<TypedDatum>().typedMem();
+    uint8_t *mem = args[0].toObject().as<TypedObject>().typedMem();
     int32_t start = args[1].toInt32();
     int32_t end = args[2].toInt32();
 
     cx->targetRegionStart = mem + start;
     cx->targetRegionEnd = mem + end;
     return true;
 }
 
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -693,40 +693,37 @@ static const JSFunctionSpec intrinsic_fu
     JS_FNINFO("ForkJoinGetSlice",
               intrinsic_ForkJoinGetSlice,
               &intrinsic_ForkJoinGetSlice_jitInfo, 1, 0),
     JS_FNINFO("InParallelSection",
               intrinsic_InParallelSection,
               &intrinsic_InParallelSection_jitInfo, 0, 0),
 
     // See builtin/TypedObject.h for descriptors of the typedobj functions.
-    JS_FN("NewTypedHandle",
-          js::NewTypedHandle,
+    JS_FN("NewOpaqueTypedObject",
+          js::NewOpaqueTypedObject,
           1, 0),
-    JS_FN("NewDerivedTypedDatum",
-          js::NewDerivedTypedDatum,
+    JS_FN("NewDerivedTypedObject",
+          js::NewDerivedTypedObject,
           3, 0),
-    JS_FNINFO("AttachHandle",
-              JSNativeThreadSafeWrapper<js::AttachHandle>,
-              &js::AttachHandleJitInfo, 5, 0),
+    JS_FNINFO("AttachTypedObject",
+              JSNativeThreadSafeWrapper<js::AttachTypedObject>,
+              &js::AttachTypedObjectJitInfo, 5, 0),
     JS_FNINFO("ObjectIsTypeDescr",
               JSNativeThreadSafeWrapper<js::ObjectIsTypeDescr>,
               &js::ObjectIsTypeDescrJitInfo, 5, 0),
-    JS_FNINFO("ObjectIsTypeRepresentation",
-              JSNativeThreadSafeWrapper<js::ObjectIsTypeRepresentation>,
-              &js::ObjectIsTypeRepresentationJitInfo, 5, 0),
-    JS_FNINFO("ObjectIsTypedObject",
-              JSNativeThreadSafeWrapper<js::ObjectIsTypedObject>,
-              &js::ObjectIsTypedObjectJitInfo, 5, 0),
-    JS_FNINFO("ObjectIsTypedHandle",
-              JSNativeThreadSafeWrapper<js::ObjectIsTypedHandle>,
-              &js::ObjectIsTypedHandleJitInfo, 5, 0),
-    JS_FN("NewHandle",
-          js::NewTypedHandle,
-          1, 0),
+    JS_FNINFO("ObjectIsTransparentTypedObject",
+              JSNativeThreadSafeWrapper<js::ObjectIsTransparentTypedObject>,
+              &js::ObjectIsTransparentTypedObjectJitInfo, 5, 0),
+    JS_FNINFO("TypedObjectIsAttached",
+              JSNativeThreadSafeWrapper<js::TypedObjectIsAttached>,
+              &js::TypedObjectIsAttachedJitInfo, 1, 0),
+    JS_FNINFO("ObjectIsOpaqueTypedObject",
+              JSNativeThreadSafeWrapper<js::ObjectIsOpaqueTypedObject>,
+              &js::ObjectIsOpaqueTypedObjectJitInfo, 5, 0),
     JS_FNINFO("ClampToUint8",
               JSNativeThreadSafeWrapper<js::ClampToUint8>,
               &js::ClampToUint8JitInfo, 1, 0),
     JS_FNINFO("Memcpy",
               JSNativeThreadSafeWrapper<js::Memcpy>,
               &js::MemcpyJitInfo, 5, 0),
     JS_FN("GetTypedObjectModule", js::GetTypedObjectModule, 0, 0),
     JS_FN("GetFloat32x4TypeDescr", js::GetFloat32x4TypeDescr, 0, 0),
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -38,41 +38,25 @@
 #include "vm/WrapperObject.h"
 
 #include "jsatominlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 #include "vm/Shape-inl.h"
 
-#if JS_USE_NEW_OBJECT_REPRESENTATION
-// See the comment above OldObjectRepresentationHack.
-#  error "TypedArray support for new object representation unimplemented."
-#endif
-
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 using mozilla::IsNaN;
 using mozilla::PodCopy;
 using JS::CanonicalizeNaN;
 using JS::GenericNaN;
 
-/*
- * Allocate array buffers with the maximum number of fixed slots marked as
- * reserved, so that the fixed slots may be used for the buffer's contents.
- * The last fixed slot is kept for the object's private data.
- */
-static const uint8_t ARRAYBUFFER_RESERVED_SLOTS = JSObject::MAX_FIXED_SLOTS - 1;
-
-// Sentinel value used to initialize ArrayBufferViewObjects' NEXT_BUFFER_SLOTs
-// to show that they have not yet been added to any ArrayBufferObject list.
-js::ArrayBufferObject * const UNSET_BUFFER_LINK = reinterpret_cast<js::ArrayBufferObject*>(0x2);
-
 static bool
 ValueIsLength(const Value &v, uint32_t *len)
 {
     if (v.isInt32()) {
         int32_t i = v.toInt32();
         if (i < 0)
             return false;
         *len = i;
@@ -91,1092 +75,16 @@ ValueIsLength(const Value &v, uint32_t *
         *len = length;
         return true;
     }
 
     return false;
 }
 
 /*
- * Convert |v| to an array index for an array of length |length| per
- * the Typed Array Specification section 7.0, |subarray|. If successful,
- * the output value is in the range [0, length].
- */
-static bool
-ToClampedIndex(JSContext *cx, HandleValue v, uint32_t length, uint32_t *out)
-{
-    int32_t result;
-    if (!ToInt32(cx, v, &result))
-        return false;
-    if (result < 0) {
-        result += length;
-        if (result < 0)
-            result = 0;
-    } else if (uint32_t(result) > length) {
-        result = length;
-    }
-    *out = uint32_t(result);
-    return true;
-}
-
-/*
- * ArrayBufferObject
- *
- * This class holds the underlying raw buffer that the TypedArrayObject classes
- * access.  It can be created explicitly and passed to a TypedArrayObject, or
- * can be created implicitly by constructing a TypedArrayObject with a size.
- */
-
-MOZ_ALWAYS_INLINE bool
-IsArrayBuffer(HandleValue v)
-{
-    return v.isObject() && v.toObject().hasClass(&ArrayBufferObject::class_);
-}
-
-MOZ_ALWAYS_INLINE bool
-ArrayBufferObject::byteLengthGetterImpl(JSContext *cx, CallArgs args)
-{
-    JS_ASSERT(IsArrayBuffer(args.thisv()));
-    args.rval().setInt32(args.thisv().toObject().as<ArrayBufferObject>().byteLength());
-    return true;
-}
-
-bool
-ArrayBufferObject::byteLengthGetter(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    return CallNonGenericMethod<IsArrayBuffer, byteLengthGetterImpl>(cx, args);
-}
-
-bool
-ArrayBufferObject::fun_slice_impl(JSContext *cx, CallArgs args)
-{
-    JS_ASSERT(IsArrayBuffer(args.thisv()));
-
-    Rooted<ArrayBufferObject*> thisObj(cx, &args.thisv().toObject().as<ArrayBufferObject>());
-
-    // these are the default values
-    uint32_t length = thisObj->byteLength();
-    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 = createSlice(cx, thisObj, begin, end);
-    if (!nobj)
-        return false;
-    args.rval().setObject(*nobj);
-    return true;
-}
-
-bool
-ArrayBufferObject::fun_slice(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    return CallNonGenericMethod<IsArrayBuffer, fun_slice_impl>(cx, args);
-}
-
-/*
- * ArrayBuffer.isView(obj); ES6 (Dec 2013 draft) 24.1.3.1
- */
-bool
-ArrayBufferObject::fun_isView(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    args.rval().setBoolean(args.get(0).isObject() &&
-                           JS_IsArrayBufferViewObject(&args.get(0).toObject()));
-    return true;
-}
-
-/*
- * new ArrayBuffer(byteLength)
- */
-bool
-ArrayBufferObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
-{
-    int32_t nbytes = 0;
-    CallArgs args = CallArgsFromVp(argc, vp);
-    if (argc > 0 && !ToInt32(cx, args[0], &nbytes))
-        return false;
-
-    if (nbytes < 0) {
-        /*
-         * We're just not going to support arrays that are bigger than what will fit
-         * as an integer value; if someone actually ever complains (validly), then we
-         * can fix.
-         */
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
-        return false;
-    }
-
-    JSObject *bufobj = create(cx, uint32_t(nbytes));
-    if (!bufobj)
-        return false;
-    args.rval().setObject(*bufobj);
-    return true;
-}
-
-/*
- * Note that some callers are allowed to pass in a nullptr cx, so we allocate
- * with the cx if available and fall back to the runtime.  If oldptr is given,
- * it's expected to be a previously-allocated ObjectElements* pointer that we
- * then realloc.
- */
-static ObjectElements *
-AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void *oldptr = nullptr)
-{
-    uint32_t size = nbytes + sizeof(ObjectElements);
-    ObjectElements *newheader;
-
-    // if oldptr is given, then we need to do a realloc
-    if (oldptr) {
-        ObjectElements *oldheader = static_cast<ObjectElements *>(oldptr);
-        uint32_t oldnbytes = ArrayBufferObject::headerInitializedLength(oldheader);
-
-        void *p = maybecx ? maybecx->runtime()->reallocCanGC(oldptr, size) : js_realloc(oldptr, size);
-        newheader = static_cast<ObjectElements *>(p);
-
-        // if we grew the array, we need to set the new bytes to 0
-        if (newheader && nbytes > oldnbytes)
-            memset(reinterpret_cast<uint8_t*>(newheader->elements()) + oldnbytes, 0, nbytes - oldnbytes);
-    } else {
-        void *p = maybecx ? maybecx->runtime()->callocCanGC(size) : js_calloc(size);
-        newheader = static_cast<ObjectElements *>(p);
-    }
-    if (!newheader) {
-        if (maybecx)
-            js_ReportOutOfMemory(maybecx);
-        return nullptr;
-    }
-
-    ArrayBufferObject::updateElementsHeader(newheader, nbytes);
-
-    return newheader;
-}
-
-static inline void
-PostBarrierTypedArrayObject(JSObject *obj)
-{
-#ifdef JSGC_GENERATIONAL
-    JS_ASSERT(obj);
-    JSRuntime *rt = obj->runtimeFromMainThread();
-    if (!rt->isHeapBusy() && !IsInsideNursery(rt, obj))
-        rt->gcStoreBuffer.putWholeCell(obj);
-#endif
-}
-
-// The list of views must be stored somewhere in the ArrayBufferObject, but
-// the slots are already being used for the element storage and the private
-// field is used for a delegate object. The ObjectElements header has space
-// for it, but I don't want to mess around with adding unions to it with
-// JS_USE_NEW_OBJECT_REPRESENTATION pending, since it will solve this much
-// more cleanly.
-struct OldObjectRepresentationHack {
-    uint32_t flags;
-    uint32_t initializedLength;
-    EncapsulatedPtr<ArrayBufferViewObject> views;
-};
-
-static ArrayBufferViewObject *
-GetViewList(ArrayBufferObject *obj)
-{
-    return reinterpret_cast<OldObjectRepresentationHack*>(obj->getElementsHeader())->views;
-}
-
-static void
-SetViewList(ArrayBufferObject *obj, ArrayBufferViewObject *viewsHead)
-{
-    reinterpret_cast<OldObjectRepresentationHack*>(obj->getElementsHeader())->views = viewsHead;
-    PostBarrierTypedArrayObject(obj);
-}
-
-static void
-InitViewList(ArrayBufferObject *obj, ArrayBufferViewObject *viewsHead)
-{
-    reinterpret_cast<OldObjectRepresentationHack*>(obj->getElementsHeader())->views.init(viewsHead);
-    PostBarrierTypedArrayObject(obj);
-}
-
-static EncapsulatedPtr<ArrayBufferViewObject> &
-GetViewListRef(ArrayBufferObject *obj)
-{
-    JS_ASSERT(obj->runtimeFromMainThread()->isHeapBusy());
-    return reinterpret_cast<OldObjectRepresentationHack*>(obj->getElementsHeader())->views;
-}
-
-/* static */ bool
-ArrayBufferObject::neuterViews(JSContext *cx, Handle<ArrayBufferObject*> buffer)
-{
-    ArrayBufferViewObject *view;
-    size_t numViews = 0;
-    for (view = GetViewList(buffer); view; view = view->nextView()) {
-        numViews++;
-        view->neuter(cx);
-
-        // Notify compiled jit code that the base pointer has moved.
-        MarkObjectStateChange(cx, view);
-    }
-
-    // neuterAsmJSArrayBuffer adjusts state specific to the ArrayBuffer data
-    // itself, but it only affects the behavior of views
-    if (buffer->isAsmJSArrayBuffer()) {
-        if (!ArrayBufferObject::neuterAsmJSArrayBuffer(cx, *buffer))
-            return false;
-    }
-
-    // Remove buffer from the list of buffers with > 1 view.
-    if (numViews > 1 && GetViewList(buffer)->bufferLink() != UNSET_BUFFER_LINK) {
-        ArrayBufferObject *prev = buffer->compartment()->gcLiveArrayBuffers;
-        if (prev == buffer) {
-            buffer->compartment()->gcLiveArrayBuffers = GetViewList(prev)->bufferLink();
-        } else {
-            for (ArrayBufferObject *b = GetViewList(prev)->bufferLink();
-                 b;
-                 b = GetViewList(b)->bufferLink())
-            {
-                if (b == buffer) {
-                    GetViewList(prev)->setBufferLink(GetViewList(b)->bufferLink());
-                    break;
-                }
-                prev = b;
-            }
-        }
-    }
-
-    return true;
-}
-
-void
-ArrayBufferObject::changeContents(JSContext *cx, ObjectElements *newHeader)
-{
-    JS_ASSERT(!isAsmJSArrayBuffer());
-
-    // Grab out data before invalidating it.
-    uint32_t byteLengthCopy = byteLength();
-    uintptr_t oldDataPointer = uintptr_t(dataPointer());
-    ArrayBufferViewObject *viewListHead = GetViewList(this);
-
-    // Update all views.
-    uintptr_t newDataPointer = uintptr_t(newHeader->elements());
-    for (ArrayBufferViewObject *view = viewListHead; view; view = view->nextView()) {
-        uintptr_t newDataPtr = uintptr_t(view->getPrivate()) - oldDataPointer + newDataPointer;
-        view->setPrivate(reinterpret_cast<uint8_t*>(newDataPtr));
-
-        // Notify compiled jit code that the base pointer has moved.
-        MarkObjectStateChange(cx, view);
-    }
-
-    // The list of views in the old header is reachable if the contents are
-    // being transferred, so null it out
-    SetViewList(this, nullptr);
-
-#ifdef JSGC_GENERATIONAL
-    ObjectElements *oldHeader = ObjectElements::fromElements(elements);
-    JS_ASSERT(oldHeader != newHeader);
-    JSRuntime *rt = runtimeFromMainThread();
-    if (hasDynamicElements())
-        rt->gcNursery.notifyRemovedElements(this, oldHeader);
-#endif
-
-    elements = newHeader->elements();
-
-#ifdef JSGC_GENERATIONAL
-    if (hasDynamicElements())
-        rt->gcNursery.notifyNewElements(this, newHeader);
-#endif
-
-    initElementsHeader(newHeader, byteLengthCopy);
-    InitViewList(this, viewListHead);
-}
-
-void
-ArrayBufferObject::neuter(JSContext *cx)
-{
-    JS_ASSERT(cx);
-    if (hasDynamicElements() && !isAsmJSArrayBuffer()) {
-        ObjectElements *oldHeader = getElementsHeader();
-        changeContents(cx, ObjectElements::fromElements(fixedElements()));
-
-        FreeOp fop(cx->runtime(), false);
-        fop.free_(oldHeader);
-    }
-
-    uint32_t byteLen = 0;
-    updateElementsHeader(getElementsHeader(), byteLen);
-
-    getElementsHeader()->setIsNeuteredBuffer();
-}
-
-/* static */ bool
-ArrayBufferObject::ensureNonInline(JSContext *cx, Handle<ArrayBufferObject*> buffer)
-{
-    if (buffer->hasDynamicElements())
-        return true;
-
-    ObjectElements *newHeader = AllocateArrayBufferContents(cx, buffer->byteLength());
-    if (!newHeader)
-        return false;
-
-    void *newHeaderDataPointer = reinterpret_cast<void*>(newHeader->elements());
-    memcpy(newHeaderDataPointer, buffer->dataPointer(), buffer->byteLength());
-
-    buffer->changeContents(cx, newHeader);
-    return true;
-}
-
-#if defined(JS_ION) && defined(JS_CPU_X64)
-// To avoid dynamically checking bounds on each load/store, asm.js code relies
-// on the SIGSEGV handler in AsmJSSignalHandlers.cpp. However, this only works
-// if we can guarantee that *any* out-of-bounds access generates a fault. This
-// isn't generally true since an out-of-bounds access could land on other
-// Mozilla data. To overcome this on x64, we reserve an entire 4GB space,
-// making only the range [0, byteLength) accessible, and use a 32-bit unsigned
-// index into this space. (x86 and ARM require different tricks.)
-//
-// One complication is that we need to put an ObjectElements struct immediately
-// before the data array (as required by the general JSObject data structure).
-// Thus, we must stick a page before the elements to hold ObjectElements.
-//
-//   |<------------------------------ 4GB + 1 pages --------------------->|
-//           |<--- sizeof --->|<------------------- 4GB ----------------->|
-//
-//   | waste | ObjectElements | data array | inaccessible reserved memory |
-//                            ^            ^                              ^
-//                            |            \                             /
-//                      obj->elements       required to be page boundaries
-//
-JS_STATIC_ASSERT(sizeof(ObjectElements) < AsmJSPageSize);
-JS_STATIC_ASSERT(AsmJSAllocationGranularity == AsmJSPageSize);
-static const size_t AsmJSMappedSize = AsmJSPageSize + AsmJSBufferProtectedSize;
-
-bool
-ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer)
-{
-    if (buffer->isAsmJSArrayBuffer())
-        return true;
-
-    // Get the entire reserved region (with all pages inaccessible).
-    void *p;
-# ifdef XP_WIN
-    p = VirtualAlloc(nullptr, AsmJSMappedSize, MEM_RESERVE, PAGE_NOACCESS);
-    if (!p)
-        return false;
-# else
-    p = mmap(nullptr, AsmJSMappedSize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
-    if (p == MAP_FAILED)
-        return false;
-# endif
-
-    // Enable access to the valid region.
-    JS_ASSERT(buffer->byteLength() % AsmJSAllocationGranularity == 0);
-# ifdef XP_WIN
-    if (!VirtualAlloc(p, AsmJSPageSize + buffer->byteLength(), MEM_COMMIT, PAGE_READWRITE)) {
-        VirtualFree(p, 0, MEM_RELEASE);
-        return false;
-    }
-# else
-    if (mprotect(p, AsmJSPageSize + buffer->byteLength(), PROT_READ | PROT_WRITE)) {
-        munmap(p, AsmJSMappedSize);
-        return false;
-    }
-# endif
-
-    // Copy over the current contents of the typed array.
-    uint8_t *data = reinterpret_cast<uint8_t*>(p) + AsmJSPageSize;
-    memcpy(data, buffer->dataPointer(), buffer->byteLength());
-
-    // Swap the new elements into the ArrayBufferObject.
-    ObjectElements *newHeader = reinterpret_cast<ObjectElements*>(data - sizeof(ObjectElements));
-    ObjectElements *oldHeader = buffer->hasDynamicElements() ? buffer->getElementsHeader()
-                                                             : nullptr;
-    buffer->changeContents(cx, newHeader);
-    js_free(oldHeader);
-
-    // Mark the ArrayBufferObject so (1) we don't do this again, (2) we know not
-    // to js_free the header in the normal way.
-    newHeader->setIsAsmJSArrayBuffer();
-    JS_ASSERT(data == buffer->dataPointer());
-    return true;
-}
-
-void
-ArrayBufferObject::releaseAsmJSArrayBuffer(FreeOp *fop, JSObject *obj)
-{
-    ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
-    JS_ASSERT(buffer.isAsmJSArrayBuffer());
-
-    uint8_t *p = buffer.dataPointer() - AsmJSPageSize ;
-    JS_ASSERT(uintptr_t(p) % AsmJSPageSize == 0);
-# ifdef XP_WIN
-    VirtualFree(p, 0, MEM_RELEASE);
-# else
-    munmap(p, AsmJSMappedSize);
-# endif
-}
-#else  /* defined(JS_ION) && defined(JS_CPU_X64) */
-bool
-ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer)
-{
-    if (buffer->isAsmJSArrayBuffer())
-        return true;
-
-    if (!ensureNonInline(cx, buffer))
-        return false;
-
-    JS_ASSERT(buffer->hasDynamicElements());
-    buffer->getElementsHeader()->setIsAsmJSArrayBuffer();
-    return true;
-}
-
-void
-ArrayBufferObject::releaseAsmJSArrayBuffer(FreeOp *fop, JSObject *obj)
-{
-    fop->free_(obj->as<ArrayBufferObject>().getElementsHeader());
-}
-#endif
-
-bool
-ArrayBufferObject::neuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer)
-{
-#ifdef JS_ION
-    AsmJSActivation *act = cx->mainThread().asmJSActivationStackFromOwnerThread();
-    for (; act; act = act->prev()) {
-        if (act->module().maybeHeapBufferObject() == &buffer)
-            break;
-    }
-    if (!act)
-        return true;
-
-    js_ReportOverRecursed(cx);
-    return false;
-#else
-    return true;
-#endif
-}
-
-void
-ArrayBufferObject::addView(ArrayBufferViewObject *view)
-{
-    // This view should never have been associated with a buffer before
-    JS_ASSERT(view->bufferLink() == UNSET_BUFFER_LINK);
-
-    // Note that pre-barriers are not needed here because either the list was
-    // previously empty, in which case no pointer is being overwritten, or the
-    // list was nonempty and will be made weak during this call (and weak
-    // pointers cannot violate the snapshot-at-the-beginning invariant.)
-
-    ArrayBufferViewObject *viewsHead = GetViewList(this);
-    if (viewsHead == nullptr) {
-        // This ArrayBufferObject will have a single view at this point, so it
-        // is a strong pointer (it will be marked during tracing.)
-        JS_ASSERT(view->nextView() == nullptr);
-    } else {
-        view->prependToViews(viewsHead);
-    }
-
-    SetViewList(this, view);
-}
-
-ArrayBufferObject *
-ArrayBufferObject::create(JSContext *cx, uint32_t nbytes, bool clear /* = true */)
-{
-    RootedObject obj(cx, NewBuiltinClassInstance(cx, &class_));
-    if (!obj)
-        return nullptr;
-    JS_ASSERT_IF(obj->isTenured(), obj->tenuredGetAllocKind() == gc::FINALIZE_OBJECT16_BACKGROUND);
-    JS_ASSERT(obj->getClass() == &class_);
-
-    js::Shape *empty = EmptyShape::getInitialShape(cx, &class_,
-                                                   obj->getProto(), obj->getParent(), obj->getMetadata(),
-                                                   gc::FINALIZE_OBJECT16_BACKGROUND);
-    if (!empty)
-        return nullptr;
-    obj->setLastPropertyInfallible(empty);
-
-    // ArrayBufferObjects delegate added properties to another JSObject, so
-    // their internal layout can use the object's fixed slots for storage.
-    // Set up the object to look like an array with an elements header.
-    JS_ASSERT(!obj->hasDynamicSlots());
-    JS_ASSERT(!obj->hasDynamicElements());
-
-    // The beginning stores an ObjectElements header structure holding the
-    // length. The rest of it is a flat data store for the array buffer.
-    size_t usableSlots = ARRAYBUFFER_RESERVED_SLOTS - ObjectElements::VALUES_PER_HEADER;
-
-    Handle<ArrayBufferObject*> buffer = obj.as<ArrayBufferObject>();
-
-    if (nbytes > sizeof(Value) * usableSlots) {
-        ObjectElements *header = AllocateArrayBufferContents(cx, nbytes);
-        if (!header)
-            return nullptr;
-        buffer->elements = header->elements();
-
-#ifdef JSGC_GENERATIONAL
-        JSRuntime *rt = buffer->runtimeFromMainThread();
-        rt->gcNursery.notifyNewElements(buffer, header);
-#endif
-    } else {
-        buffer->setFixedElements();
-        if (clear)
-            memset(buffer->dataPointer(), 0, nbytes);
-    }
-
-    buffer->initElementsHeader(buffer->getElementsHeader(), nbytes);
-
-    return buffer;
-}
-
-JSObject *
-ArrayBufferObject::createSlice(JSContext *cx, Handle<ArrayBufferObject*> arrayBuffer,
-                               uint32_t begin, uint32_t end)
-{
-    JS_ASSERT(begin <= arrayBuffer->byteLength());
-    JS_ASSERT(end <= arrayBuffer->byteLength());
-    JS_ASSERT(begin <= end);
-    uint32_t length = end - begin;
-
-    if (!arrayBuffer->hasData())
-        return create(cx, 0);
-
-    JSObject *slice = create(cx, length, false);
-    if (!slice)
-        return nullptr;
-    memcpy(slice->as<ArrayBufferObject>().dataPointer(), arrayBuffer->dataPointer() + begin, length);
-    return slice;
-}
-
-bool
-ArrayBufferObject::createDataViewForThisImpl(JSContext *cx, CallArgs args)
-{
-    JS_ASSERT(IsArrayBuffer(args.thisv()));
-
-    /*
-     * This method is only called for |DataView(alienBuf, ...)| which calls
-     * this as |createDataViewForThis.call(alienBuf, ..., DataView.prototype)|,
-     * ergo there must be at least two arguments.
-     */
-    JS_ASSERT(args.length() >= 2);
-
-    Rooted<JSObject*> proto(cx, &args[args.length() - 1].toObject());
-
-    Rooted<JSObject*> buffer(cx, &args.thisv().toObject());
-
-    /*
-     * Pop off the passed-along prototype and delegate to normal DataViewObject
-     * construction.
-     */
-    CallArgs frobbedArgs = CallArgsFromVp(args.length() - 1, args.base());
-    return DataViewObject::construct(cx, buffer, frobbedArgs, proto);
-}
-
-bool
-ArrayBufferObject::createDataViewForThis(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    return CallNonGenericMethod<IsArrayBuffer, createDataViewForThisImpl>(cx, args);
-}
-
-/* static */ bool
-ArrayBufferObject::stealContents(JSContext *cx, Handle<ArrayBufferObject*> buffer, void **contents,
-                                 uint8_t **data)
-{
-    // If the ArrayBuffer's elements are dynamically allocated and nothing else
-    // prevents us from stealing them, transfer ownership directly.  Otherwise,
-    // the elements are small and allocated inside the ArrayBuffer object's GC
-    // header so we must make a copy.
-    ObjectElements *transferableHeader;
-    bool stolen;
-    if (buffer->hasDynamicElements() && !buffer->isAsmJSArrayBuffer()) {
-        stolen = true;
-        transferableHeader = buffer->getElementsHeader();
-    } else {
-        stolen = false;
-
-        uint32_t byteLen = buffer->byteLength();
-        transferableHeader = AllocateArrayBufferContents(cx, byteLen);
-        if (!transferableHeader)
-            return false;
-
-        initElementsHeader(transferableHeader, byteLen);
-        void *headerDataPointer = reinterpret_cast<void*>(transferableHeader->elements());
-        memcpy(headerDataPointer, buffer->dataPointer(), byteLen);
-    }
-
-    JS_ASSERT(!IsInsideNursery(cx->runtime(), transferableHeader));
-    *contents = transferableHeader;
-    *data = reinterpret_cast<uint8_t *>(transferableHeader + 1);
-
-    // Neuter the views, which may also mprotect(PROT_NONE) the buffer. So do
-    // it after copying out the data.
-    if (!ArrayBufferObject::neuterViews(cx, buffer))
-        return false;
-
-    // If the elements were taken from the neutered buffer, revert it back to
-    // using inline storage so it doesn't attempt to free the stolen elements
-    // when finalized.
-    if (stolen)
-        buffer->changeContents(cx, ObjectElements::fromElements(buffer->fixedElements()));
-
-    buffer->neuter(cx);
-    return true;
-}
-
-void
-ArrayBufferObject::obj_trace(JSTracer *trc, JSObject *obj)
-{
-    /*
-     * If this object changes, it will get marked via the private data barrier,
-     * so it's safe to leave it Unbarriered.
-     */
-    JSObject *delegate = static_cast<JSObject*>(obj->getPrivate());
-    if (delegate) {
-        JS_SET_TRACING_LOCATION(trc, &obj->privateRef(obj->numFixedSlots()));
-        MarkObjectUnbarriered(trc, &delegate, "arraybuffer.delegate");
-        obj->setPrivateUnbarriered(delegate);
-    }
-
-    // ArrayBufferObjects need to maintain a list of possibly-weak pointers to
-    // their views. The straightforward way to update the weak pointers would
-    // be in the views' finalizers, but giving views finalizers means they
-    // cannot be swept in the background. This results in a very high
-    // performance cost.  Instead, ArrayBufferObjects with a single view hold a
-    // strong pointer to the view. This can entrain garbage when the single
-    // view becomes otherwise unreachable while the buffer is still live, but
-    // this is expected to be rare. ArrayBufferObjects with 0-1 views are
-    // expected to be by far the most common cases. ArrayBufferObjects with
-    // multiple views are collected into a linked list during collection, and
-    // then swept to prune out their dead views.
-
-    ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
-    ArrayBufferViewObject *viewsHead = GetViewList(&buffer);
-    if (!viewsHead)
-        return;
-
-    // During minor collections, mark weak pointers on the buffer strongly.
-    if (trc->runtime->isHeapMinorCollecting()) {
-        MarkObject(trc, &GetViewListRef(&buffer), "arraybuffer.viewlist");
-        ArrayBufferViewObject *prior = GetViewList(&buffer);
-        for (ArrayBufferViewObject *view = prior->nextView();
-             view;
-             prior = view, view = view->nextView())
-        {
-            MarkObjectUnbarriered(trc, &view, "arraybuffer.views");
-            prior->setNextView(view);
-        }
-        return;
-    }
-
-    ArrayBufferViewObject *firstView = viewsHead;
-    if (firstView->nextView() == nullptr) {
-        // Single view: mark it, but only if we're actually doing a GC pass
-        // right now. Otherwise, the tracing pass for barrier verification will
-        // fail if we add another view and the pointer becomes weak.
-        if (IS_GC_MARKING_TRACER(trc))
-            MarkObject(trc, &GetViewListRef(&buffer), "arraybuffer.singleview");
-    } else {
-        // Multiple views: do not mark, but append buffer to list.
-        if (IS_GC_MARKING_TRACER(trc)) {
-            // obj_trace may be called multiple times before sweep(), so avoid
-            // adding this buffer to the list multiple times.
-            if (firstView->bufferLink() == UNSET_BUFFER_LINK) {
-                JS_ASSERT(obj->compartment() == firstView->compartment());
-                ArrayBufferObject **bufList = &obj->compartment()->gcLiveArrayBuffers;
-                firstView->setBufferLink(*bufList);
-                *bufList = &obj->as<ArrayBufferObject>();
-            } else {
-#ifdef DEBUG
-                bool found = false;
-                for (ArrayBufferObject *p = obj->compartment()->gcLiveArrayBuffers;
-                     p;
-                     p = GetViewList(p)->bufferLink())
-                {
-                    if (p == obj)
-                    {
-                        JS_ASSERT(!found);
-                        found = true;
-                    }
-                }
-#endif
-            }
-        }
-    }
-}
-
-void
-ArrayBufferObject::sweep(JSCompartment *compartment)
-{
-    ArrayBufferObject *buffer = compartment->gcLiveArrayBuffers;
-    JS_ASSERT(buffer != UNSET_BUFFER_LINK);
-    compartment->gcLiveArrayBuffers = nullptr;
-
-    while (buffer) {
-        ArrayBufferViewObject *viewsHead = GetViewList(buffer);
-        JS_ASSERT(viewsHead);
-
-        ArrayBufferObject *nextBuffer = viewsHead->bufferLink();
-        JS_ASSERT(nextBuffer != UNSET_BUFFER_LINK);
-        viewsHead->setBufferLink(UNSET_BUFFER_LINK);
-
-        // Rebuild the list of views of the ArrayBufferObject, discarding dead
-        // views.  If there is only one view, it will have already been marked.
-        ArrayBufferViewObject *prevLiveView = nullptr;
-        ArrayBufferViewObject *view = viewsHead;
-        while (view) {
-            JS_ASSERT(buffer->compartment() == view->compartment());
-            ArrayBufferViewObject *nextView = view->nextView();
-            if (!IsObjectAboutToBeFinalized(&view)) {
-                view->setNextView(prevLiveView);
-                prevLiveView = view;
-            }
-            view = nextView;
-        }
-        SetViewList(buffer, prevLiveView);
-
-        buffer = nextBuffer;
-    }
-}
-
-void
-ArrayBufferObject::resetArrayBufferList(JSCompartment *comp)
-{
-    ArrayBufferObject *buffer = comp->gcLiveArrayBuffers;
-    JS_ASSERT(buffer != UNSET_BUFFER_LINK);
-    comp->gcLiveArrayBuffers = nullptr;
-
-    while (buffer) {
-        ArrayBufferViewObject *view = GetViewList(buffer);
-        JS_ASSERT(view);
-
-        ArrayBufferObject *nextBuffer = view->bufferLink();
-        JS_ASSERT(nextBuffer != UNSET_BUFFER_LINK);
-
-        view->setBufferLink(UNSET_BUFFER_LINK);
-        buffer = nextBuffer;
-    }
-}
-
-/* static */ bool
-ArrayBufferObject::saveArrayBufferList(JSCompartment *comp, ArrayBufferVector &vector)
-{
-    ArrayBufferObject *buffer = comp->gcLiveArrayBuffers;
-    while (buffer) {
-        JS_ASSERT(buffer != UNSET_BUFFER_LINK);
-        if (!vector.append(buffer))
-            return false;
-
-        ArrayBufferViewObject *view = GetViewList(buffer);
-        JS_ASSERT(view);
-        buffer = view->bufferLink();
-    }
-    return true;
-}
-
-/* static */ void
-ArrayBufferObject::restoreArrayBufferLists(ArrayBufferVector &vector)
-{
-    for (ArrayBufferObject **p = vector.begin(); p != vector.end(); p++) {
-        ArrayBufferObject *buffer = *p;
-        JSCompartment *comp = buffer->compartment();
-        ArrayBufferViewObject *firstView = GetViewList(buffer);
-        JS_ASSERT(firstView);
-        JS_ASSERT(firstView->compartment() == comp);
-        JS_ASSERT(firstView->bufferLink() == UNSET_BUFFER_LINK);
-        firstView->setBufferLink(comp->gcLiveArrayBuffers);
-        comp->gcLiveArrayBuffers = buffer;
-    }
-}
-
-bool
-ArrayBufferObject::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
-                                     MutableHandleObject objp, MutableHandleShape propp)
-{
-    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
-    if (!delegate)
-        return false;
-
-    bool delegateResult = JSObject::lookupGeneric(cx, delegate, id, objp, propp);
-
-    /* If false, there was an error, so propagate it.
-     * Otherwise, if propp is non-null, the property
-     * was found. Otherwise it was not
-     * found so look in the prototype chain.
-     */
-    if (!delegateResult)
-        return false;
-
-    if (propp) {
-        if (objp == delegate)
-            objp.set(obj);
-        return true;
-    }
-
-    RootedObject proto(cx, obj->getProto());
-    if (!proto) {
-        objp.set(nullptr);
-        propp.set(nullptr);
-        return true;
-    }
-
-    return JSObject::lookupGeneric(cx, proto, id, objp, propp);
-}
-
-bool
-ArrayBufferObject::obj_lookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
-                                      MutableHandleObject objp, MutableHandleShape propp)
-{
-    Rooted<jsid> id(cx, NameToId(name));
-    return obj_lookupGeneric(cx, obj, id, objp, propp);
-}
-
-bool
-ArrayBufferObject::obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
-                                     MutableHandleObject objp, MutableHandleShape propp)
-{
-    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
-    if (!delegate)
-        return false;
-
-    /*
-     * If false, there was an error, so propagate it.
-     * Otherwise, if propp is non-null, the property
-     * was found. Otherwise it was not
-     * found so look in the prototype chain.
-     */
-    if (!JSObject::lookupElement(cx, delegate, index, objp, propp))
-        return false;
-
-    if (propp) {
-        if (objp == delegate)
-            objp.set(obj);
-        return true;
-    }
-
-    RootedObject proto(cx, obj->getProto());
-    if (proto)
-        return JSObject::lookupElement(cx, proto, index, objp, propp);
-
-    objp.set(nullptr);
-    propp.set(nullptr);
-    return true;
-}
-
-bool
-ArrayBufferObject::obj_lookupSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
-                                     MutableHandleObject objp, MutableHandleShape propp)
-{
-    Rooted<jsid> id(cx, SPECIALID_TO_JSID(sid));
-    return obj_lookupGeneric(cx, obj, id, objp, propp);
-}
-
-bool
-ArrayBufferObject::obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
-                                     PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
-{
-    AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
-
-    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
-    if (!delegate)
-        return false;
-    return baseops::DefineGeneric(cx, delegate, id, v, getter, setter, attrs);
-}
-
-bool
-ArrayBufferObject::obj_defineProperty(JSContext *cx, HandleObject obj,
-                                      HandlePropertyName name, HandleValue v,
-                                      PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
-{
-    Rooted<jsid> id(cx, NameToId(name));
-    return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs);
-}
-
-bool
-ArrayBufferObject::obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v,
-                                     PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
-{
-    AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
-
-    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
-    if (!delegate)
-        return false;
-    return baseops::DefineElement(cx, delegate, index, v, getter, setter, attrs);
-}
-
-bool
-ArrayBufferObject::obj_defineSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid, HandleValue v,
-                                     PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
-{
-    Rooted<jsid> id(cx, SPECIALID_TO_JSID(sid));
-    return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs);
-}
-
-bool
-ArrayBufferObject::obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
-                                  HandleId id, MutableHandleValue vp)
-{
-    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
-    if (!delegate)
-        return false;
-    return baseops::GetProperty(cx, delegate, receiver, id, vp);
-}
-
-bool
-ArrayBufferObject::obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
-                                   HandlePropertyName name, MutableHandleValue vp)
-{
-    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
-    if (!delegate)
-        return false;
-    Rooted<jsid> id(cx, NameToId(name));
-    return baseops::GetProperty(cx, delegate, receiver, id, vp);
-}
-
-bool
-ArrayBufferObject::obj_getElement(JSContext *cx, HandleObject obj,
-                                  HandleObject receiver, uint32_t index, MutableHandleValue vp)
-{
-    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
-    if (!delegate)
-        return false;
-    return baseops::GetElement(cx, delegate, receiver, index, vp);
-}
-
-bool
-ArrayBufferObject::obj_getSpecial(JSContext *cx, HandleObject obj,
-                                  HandleObject receiver, HandleSpecialId sid,
-                                  MutableHandleValue vp)
-{
-    Rooted<jsid> id(cx, SPECIALID_TO_JSID(sid));
-    return obj_getGeneric(cx, obj, receiver, id, vp);
-}
-
-bool
-ArrayBufferObject::obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
-                                  MutableHandleValue vp, bool strict)
-{
-    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
-    if (!delegate)
-        return false;
-
-    return baseops::SetPropertyHelper<SequentialExecution>(cx, delegate, obj, id, 0, vp, strict);
-}
-
-bool
-ArrayBufferObject::obj_setProperty(JSContext *cx, HandleObject obj,
-                                   HandlePropertyName name, MutableHandleValue vp, bool strict)
-{
-    Rooted<jsid> id(cx, NameToId(name));
-    return obj_setGeneric(cx, obj, id, vp, strict);
-}
-
-bool
-ArrayBufferObject::obj_setElement(JSContext *cx, HandleObject obj,
-                                  uint32_t index, MutableHandleValue vp, bool strict)
-{
-    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
-    if (!delegate)
-        return false;
-
-    return baseops::SetElementHelper(cx, delegate, obj, index, 0, vp, strict);
-}
-
-bool
-ArrayBufferObject::obj_setSpecial(JSContext *cx, HandleObject obj,
-                                  HandleSpecialId sid, MutableHandleValue vp, bool strict)
-{
-    Rooted<jsid> id(cx, SPECIALID_TO_JSID(sid));
-    return obj_setGeneric(cx, obj, id, vp, strict);
-}
-
-bool
-ArrayBufferObject::obj_getGenericAttributes(JSContext *cx, HandleObject obj,
-                                            HandleId id, unsigned *attrsp)
-{
-    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
-    if (!delegate)
-        return false;
-    return baseops::GetAttributes(cx, delegate, id, attrsp);
-}
-
-bool
-ArrayBufferObject::obj_setGenericAttributes(JSContext *cx, HandleObject obj,
-                                            HandleId id, unsigned *attrsp)
-{
-    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
-    if (!delegate)
-        return false;
-    return baseops::SetAttributes(cx, delegate, id, attrsp);
-}
-
-bool
-ArrayBufferObject::obj_deleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
-                                      bool *succeeded)
-{
-    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
-    if (!delegate)
-        return false;
-    return baseops::DeleteProperty(cx, delegate, name, succeeded);
-}
-
-bool
-ArrayBufferObject::obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
-                                     bool *succeeded)
-{
-    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
-    if (!delegate)
-        return false;
-    return baseops::DeleteElement(cx, delegate, index, succeeded);
-}
-
-bool
-ArrayBufferObject::obj_deleteSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
-                                     bool *succeeded)
-{
-    RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
-    if (!delegate)
-        return false;
-    return baseops::DeleteSpecial(cx, delegate, sid, succeeded);
-}
-
-bool
-ArrayBufferObject::obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
-                                 MutableHandleValue statep, MutableHandleId idp)
-{
-    statep.setNull();
-    return true;
-}
-
-/*
- * ArrayBufferViewObject
- */
-
-inline void
-ArrayBufferViewObject::setBufferLink(ArrayBufferObject *buffer)
-{
-    setFixedSlot(NEXT_BUFFER_SLOT, PrivateValue(buffer));
-    PostBarrierTypedArrayObject(this);
-}
-
-inline void
-ArrayBufferViewObject::setNextView(ArrayBufferViewObject *view)
-{
-    setFixedSlot(NEXT_VIEW_SLOT, PrivateValue(view));
-    PostBarrierTypedArrayObject(this);
-}
-
-/*
  * TypedArrayObject
  *
  * The non-templated base class for the specific typed implementations.
  * This class holds all the member variables that are used by
  * the subclasses.
  */
 
 inline bool
@@ -1347,67 +255,31 @@ js::ToDoubleForTypedArray(JSContext *cx,
     // to a float array and then read back as integer. To work around this, we
     // always canonicalize NaN values in more-deterministic builds.
     *d = CanonicalizeNaN(*d);
 #endif
 
     return true;
 }
 
-/*
- * This method is used to trace TypedArrayObjects and DataViewObjects. We need
- * a custom tracer because some of an ArrayBufferViewObject's reserved slots
- * are weak references, and some need to be updated specially during moving
- * GCs.
- */
-/* static */ void
-ArrayBufferViewObject::trace(JSTracer *trc, JSObject *obj)
-{
-    HeapSlot &bufSlot = obj->getReservedSlotRef(BUFFER_SLOT);
-    MarkSlot(trc, &bufSlot, "typedarray.buffer");
-
-    /* Update obj's data slot if the array buffer moved. Note that during
-     * initialization, bufSlot may still be JSVAL_VOID. */
-    if (bufSlot.isObject()) {
-        ArrayBufferObject &buf = bufSlot.toObject().as<ArrayBufferObject>();
-        int32_t offset = obj->getReservedSlot(BYTEOFFSET_SLOT).toInt32();
-        obj->initPrivate(buf.dataPointer() + offset);
-    }
-
-    /* Update NEXT_VIEW_SLOT, if the view moved. */
-    IsSlotMarked(&obj->getReservedSlotRef(NEXT_VIEW_SLOT));
-}
-
 template<typename NativeType> static inline const int TypeIDOfType();
 template<> inline const int TypeIDOfType<int8_t>() { return ScalarTypeDescr::TYPE_INT8; }
 template<> inline const int TypeIDOfType<uint8_t>() { return ScalarTypeDescr::TYPE_UINT8; }
 template<> inline const int TypeIDOfType<int16_t>() { return ScalarTypeDescr::TYPE_INT16; }
 template<> inline const int TypeIDOfType<uint16_t>() { return ScalarTypeDescr::TYPE_UINT16; }
 template<> inline const int TypeIDOfType<int32_t>() { return ScalarTypeDescr::TYPE_INT32; }
 template<> inline const int TypeIDOfType<uint32_t>() { return ScalarTypeDescr::TYPE_UINT32; }
 template<> inline const int TypeIDOfType<float>() { return ScalarTypeDescr::TYPE_FLOAT32; }
 template<> inline const int TypeIDOfType<double>() { return ScalarTypeDescr::TYPE_FLOAT64; }
 template<> inline const int TypeIDOfType<uint8_clamped>() { return ScalarTypeDescr::TYPE_UINT8_CLAMPED; }
 
 template<typename ElementType>
 static inline JSObject *
 NewArray(JSContext *cx, uint32_t nelements);
 
-static inline void
-InitArrayBufferViewDataPointer(JSObject *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.
-     */
-    obj->initPrivate(buffer->dataPointer() + byteOffset);
-    PostBarrierTypedArrayObject(obj);
-}
-
 namespace {
 
 template<typename NativeType>
 class TypedArrayObjectTemplate : public TypedArrayObject
 {
   public:
     typedef NativeType ThisType;
     typedef TypedArrayObjectTemplate<NativeType> ThisTypedArrayObject;
@@ -2618,36 +1490,16 @@ ArrayBufferObject::createTypedArrayFromB
 template<typename T>
 bool
 ArrayBufferObject::createTypedArrayFromBuffer(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsArrayBuffer, createTypedArrayFromBufferImpl<T> >(cx, args);
 }
 
-void
-ArrayBufferViewObject::prependToViews(ArrayBufferViewObject *viewsHead)
-{
-    setNextView(viewsHead);
-
-    // Move the multiview buffer list link into this view since we're
-    // prepending it to the list.
-    setBufferLink(viewsHead->bufferLink());
-    viewsHead->setBufferLink(UNSET_BUFFER_LINK);
-}
-
-void
-ArrayBufferViewObject::neuter(JSContext *cx)
-{
-    if (is<DataViewObject>())
-        as<DataViewObject>().neuter();
-    else
-        as<TypedArrayObject>().neuter(cx);
-}
-
 // this default implementation is only valid for integer types
 // less than 32-bits in size.
 template<typename NativeType>
 void
 TypedArrayObjectTemplate<NativeType>::copyIndexToValue(JSObject *tarray, uint32_t index,
                                                        MutableHandleValue vp)
 {
     JS_STATIC_ASSERT(sizeof(NativeType) < 4);
@@ -2762,17 +1614,17 @@ DataViewObject::create(JSContext *cx, ui
     }
 
     DataViewObject &dvobj = obj->as<DataViewObject>();
     dvobj.setFixedSlot(BYTEOFFSET_SLOT, Int32Value(byteOffset));
     dvobj.setFixedSlot(BYTELENGTH_SLOT, Int32Value(byteLength));
     dvobj.setFixedSlot(BUFFER_SLOT, ObjectValue(*arrayBuffer));
     dvobj.setFixedSlot(NEXT_VIEW_SLOT, PrivateValue(nullptr));
     dvobj.setFixedSlot(NEXT_BUFFER_SLOT, PrivateValue(UNSET_BUFFER_LINK));
-    InitArrayBufferViewDataPointer(obj, arrayBuffer, byteOffset);
+    InitArrayBufferViewDataPointer(&dvobj, arrayBuffer, byteOffset);
     JS_ASSERT(byteOffset + byteLength <= arrayBuffer->byteLength());
 
     // Verify that the private slot is at the expected place
     JS_ASSERT(dvobj.numFixedSlots() == DATA_SLOT);
 
     arrayBuffer->as<ArrayBufferObject>().addView(&dvobj);
 
     return &dvobj;
@@ -3404,94 +2256,16 @@ TypedArrayObject::copyTypedArrayElement(
     }
 }
 
 /***
  *** JS impl
  ***/
 
 /*
- * ArrayBufferObject (base)
- */
-
-const Class ArrayBufferObject::protoClass = {
-    "ArrayBufferPrototype",
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_RESERVED_SLOTS(ARRAYBUFFER_RESERVED_SLOTS) |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer),
-    JS_PropertyStub,         /* addProperty */
-    JS_DeletePropertyStub,   /* delProperty */
-    JS_PropertyStub,         /* getProperty */
-    JS_StrictPropertyStub,   /* setProperty */
-    JS_EnumerateStub,
-    JS_ResolveStub,
-    JS_ConvertStub
-};
-
-const Class ArrayBufferObject::class_ = {
-    "ArrayBuffer",
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_IMPLEMENTS_BARRIERS |
-    Class::NON_NATIVE |
-    JSCLASS_HAS_RESERVED_SLOTS(ARRAYBUFFER_RESERVED_SLOTS) |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer),
-    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   */
-    ArrayBufferObject::obj_trace,
-    JS_NULL_CLASS_SPEC,
-    JS_NULL_CLASS_EXT,
-    {
-        ArrayBufferObject::obj_lookupGeneric,
-        ArrayBufferObject::obj_lookupProperty,
-        ArrayBufferObject::obj_lookupElement,
-        ArrayBufferObject::obj_lookupSpecial,
-        ArrayBufferObject::obj_defineGeneric,
-        ArrayBufferObject::obj_defineProperty,
-        ArrayBufferObject::obj_defineElement,
-        ArrayBufferObject::obj_defineSpecial,
-        ArrayBufferObject::obj_getGeneric,
-        ArrayBufferObject::obj_getProperty,
-        ArrayBufferObject::obj_getElement,
-        ArrayBufferObject::obj_getSpecial,
-        ArrayBufferObject::obj_setGeneric,
-        ArrayBufferObject::obj_setProperty,
-        ArrayBufferObject::obj_setElement,
-        ArrayBufferObject::obj_setSpecial,
-        ArrayBufferObject::obj_getGenericAttributes,
-        ArrayBufferObject::obj_setGenericAttributes,
-        ArrayBufferObject::obj_deleteProperty,
-        ArrayBufferObject::obj_deleteElement,
-        ArrayBufferObject::obj_deleteSpecial,
-        nullptr, nullptr, /* watch/unwatch */
-        nullptr,          /* slice */
-        ArrayBufferObject::obj_enumerate,
-        nullptr,          /* thisObject      */
-    }
-};
-
-const JSFunctionSpec ArrayBufferObject::jsfuncs[] = {
-    JS_FN("slice", ArrayBufferObject::fun_slice, 2, JSFUN_GENERIC_NATIVE),
-    JS_FS_END
-};
-
-const JSFunctionSpec ArrayBufferObject::jsstaticfuncs[] = {
-    JS_FN("isView", ArrayBufferObject::fun_isView, 1, 0),
-    JS_FS_END
-};
-
-/*
  * TypedArrayObject boilerplate
  */
 
 #ifndef RELEASE_BUILD
 # define IMPL_TYPED_ARRAY_STATICS(_typedArray)                                     \
 const JSFunctionSpec _typedArray##Object::jsfuncs[] = {                            \
     JS_SELF_HOSTED_FN("@@iterator", "ArrayValues", 0, 0),                          \
     JS_FN("subarray", _typedArray##Object::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \
@@ -3984,153 +2758,22 @@ bool
 js::IsTypedArrayBuffer(HandleValue v)
 {
     return v.isObject() && v.toObject().is<ArrayBufferObject>();
 }
 
 /* JS Friend API */
 
 JS_FRIEND_API(bool)
-JS_IsArrayBufferObject(JSObject *obj)
-{
-    obj = CheckedUnwrap(obj);
-    return obj ? obj->is<ArrayBufferObject>() : false;
-}
-
-JS_FRIEND_API(bool)
 JS_IsTypedArrayObject(JSObject *obj)
 {
     obj = CheckedUnwrap(obj);
     return obj ? obj->is<TypedArrayObject>() : false;
 }
 
-JS_FRIEND_API(bool)
-JS_IsArrayBufferViewObject(JSObject *obj)
-{
-    obj = CheckedUnwrap(obj);
-    return obj ? obj->is<ArrayBufferViewObject>() : false;
-}
-
-JS_FRIEND_API(uint32_t)
-JS_GetArrayBufferByteLength(JSObject *obj)
-{
-    obj = CheckedUnwrap(obj);
-    return obj ? obj->as<ArrayBufferObject>().byteLength() : 0;
-}
-
-JS_FRIEND_API(uint8_t *)
-JS_GetArrayBufferData(JSObject *obj)
-{
-    obj = CheckedUnwrap(obj);
-    if (!obj)
-        return nullptr;
-    return obj->as<ArrayBufferObject>().dataPointer();
-}
-
-JS_FRIEND_API(uint8_t *)
-JS_GetStableArrayBufferData(JSContext *cx, JSObject *obj)
-{
-    obj = CheckedUnwrap(obj);
-    if (!obj)
-        return nullptr;
-
-    Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
-    if (!ArrayBufferObject::ensureNonInline(cx, buffer))
-        return nullptr;
-
-    return buffer->dataPointer();
-}
-
-JS_FRIEND_API(bool)
-JS_NeuterArrayBuffer(JSContext *cx, HandleObject obj)
-{
-    if (!obj->is<ArrayBufferObject>()) {
-        JS_ReportError(cx, "ArrayBuffer object required");
-        return false;
-    }
-
-    Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
-    if (!ArrayBufferObject::neuterViews(cx, buffer))
-        return false;
-    buffer->neuter(cx);
-    return true;
-}
-
-JS_FRIEND_API(JSObject *)
-JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes)
-{
-    JS_ASSERT(nbytes <= INT32_MAX);
-    return ArrayBufferObject::create(cx, nbytes);
-}
-
-JS_PUBLIC_API(JSObject *)
-JS_NewArrayBufferWithContents(JSContext *cx, void *contents)
-{
-    JS_ASSERT(contents);
-    JSObject *obj = ArrayBufferObject::create(cx, 0);
-    if (!obj)
-        return nullptr;
-    js::ObjectElements *elements = reinterpret_cast<js::ObjectElements *>(contents);
-    obj->setDynamicElements(elements);
-    JS_ASSERT(GetViewList(&obj->as<ArrayBufferObject>()) == nullptr);
-
-#ifdef JSGC_GENERATIONAL
-    cx->runtime()->gcNursery.notifyNewElements(obj, elements);
-#endif
-    return obj;
-}
-
-JS_PUBLIC_API(bool)
-JS_AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes,
-                               void **contents, uint8_t **data)
-{
-    js::ObjectElements *header = AllocateArrayBufferContents(maybecx, nbytes);
-    if (!header)
-        return false;
-
-    ArrayBufferObject::updateElementsHeader(header, nbytes);
-
-    *contents = header;
-    *data = reinterpret_cast<uint8_t*>(header->elements());
-    return true;
-}
-
-JS_PUBLIC_API(bool)
-JS_ReallocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void **contents, uint8_t **data)
-{
-    js::ObjectElements *header = AllocateArrayBufferContents(maybecx, nbytes, *contents);
-    if (!header)
-        return false;
-
-    ArrayBufferObject::initElementsHeader(header, nbytes);
-
-    *contents = header;
-    *data = reinterpret_cast<uint8_t*>(header->elements());
-    return true;
-}
-
-JS_PUBLIC_API(bool)
-JS_StealArrayBufferContents(JSContext *cx, HandleObject objArg, void **contents, uint8_t **data)
-{
-    JSObject *obj = CheckedUnwrap(objArg);
-    if (!obj)
-        return false;
-
-    if (!obj->is<ArrayBufferObject>()) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
-        return false;
-    }
-
-    Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
-    if (!ArrayBufferObject::stealContents(cx, buffer, contents, data))
-        return false;
-
-    return true;
-}
-
 JS_FRIEND_API(uint32_t)
 JS_GetTypedArrayLength(JSObject *obj)
 {
     obj = CheckedUnwrap(obj);
     if (!obj)
         return 0;
     return obj->as<TypedArrayObject>().length();
 }
@@ -4294,70 +2937,8 @@ JS_GetDataViewData(JSObject *obj)
 JS_FRIEND_API(uint32_t)
 JS_GetDataViewByteLength(JSObject *obj)
 {
     obj = CheckedUnwrap(obj);
     if (!obj)
         return 0;
     return obj->as<DataViewObject>().byteLength();
 }
-
-JS_FRIEND_API(void *)
-JS_GetArrayBufferViewData(JSObject *obj)
-{
-    obj = CheckedUnwrap(obj);
-    if (!obj)
-        return nullptr;
-    return obj->is<DataViewObject>() ? obj->as<DataViewObject>().dataPointer()
-                                     : obj->as<TypedArrayObject>().viewData();
-}
-
-JS_FRIEND_API(JSObject *)
-JS_GetArrayBufferViewBuffer(JSObject *obj)
-{
-    obj = CheckedUnwrap(obj);
-    if (!obj)
-        return nullptr;
-    return obj->as<ArrayBufferViewObject>().bufferObject();
-}
-
-JS_FRIEND_API(uint32_t)
-JS_GetArrayBufferViewByteLength(JSObject *obj)
-{
-    obj = CheckedUnwrap(obj);
-    if (!obj)
-        return 0;
-    return obj->is<DataViewObject>()
-           ? obj->as<DataViewObject>().byteLength()
-           : obj->as<TypedArrayObject>().byteLength();
-}
-
-JS_FRIEND_API(JSObject *)
-JS_GetObjectAsArrayBufferView(JSObject *obj, uint32_t *length, uint8_t **data)
-{
-    if (!(obj = CheckedUnwrap(obj)))
-        return nullptr;
-    if (!(obj->is<ArrayBufferViewObject>()))
-        return nullptr;
-
-    *length = obj->is<DataViewObject>()
-              ? obj->as<DataViewObject>().byteLength()
-              : obj->as<TypedArrayObject>().byteLength();
-
-    *data = static_cast<uint8_t*>(obj->is<DataViewObject>()
-                                  ? obj->as<DataViewObject>().dataPointer()
-                                  : obj->as<TypedArrayObject>().viewData());
-    return obj;
-}
-
-JS_FRIEND_API(JSObject *)
-JS_GetObjectAsArrayBuffer(JSObject *obj, uint32_t *length, uint8_t **data)
-{
-    if (!(obj = CheckedUnwrap(obj)))
-        return nullptr;
-    if (!obj->is<ArrayBufferObject>())
-        return nullptr;
-
-    *length = obj->as<ArrayBufferObject>().byteLength();
-    *data = obj->as<ArrayBufferObject>().dataPointer();
-
-    return obj;
-}
--- a/js/src/vm/TypedArrayObject.h
+++ b/js/src/vm/TypedArrayObject.h
@@ -7,289 +7,39 @@
 #ifndef vm_TypedArrayObject_h
 #define vm_TypedArrayObject_h
 
 #include "jsobj.h"
 
 #include "builtin/TypedObject.h"
 #include "gc/Barrier.h"
 #include "js/Class.h"
+#include "vm/ArrayBufferObject.h"
 
 typedef struct JSProperty JSProperty;
 
 namespace js {
 
-typedef Vector<ArrayBufferObject *, 0, SystemAllocPolicy> ArrayBufferVector;
-
-// The inheritance hierarchy for the various classes relating to typed arrays
-// is as follows.
-//
-// - JSObject
-//   - ArrayBufferObject
-//   - ArrayBufferViewObject
-//     - DataViewObject
-//     - TypedArrayObject
-//       - TypedArrayObjectTemplate
-//         - Int8ArrayObject
-//         - Uint8ArrayObject
-//         - ...
-//
-// Note that |TypedArrayObjectTemplate| is just an implementation detail that
-// makes implementing its various subclasses easier.
-
-class ArrayBufferViewObject;
-
-/*
- * 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.
- */
-class ArrayBufferObject : public JSObject
-{
-    static bool byteLengthGetterImpl(JSContext *cx, CallArgs args);
-    static bool fun_slice_impl(JSContext *cx, CallArgs args);
-
-  public:
-    static const Class class_;
-
-    static const Class protoClass;
-    static const JSFunctionSpec jsfuncs[];
-    static const JSFunctionSpec jsstaticfuncs[];
-
-    static bool byteLengthGetter(JSContext *cx, unsigned argc, Value *vp);
-
-    static bool fun_slice(JSContext *cx, unsigned argc, Value *vp);
-
-    static bool fun_isView(JSContext *cx, unsigned argc, Value *vp);
-
-    static bool class_constructor(JSContext *cx, unsigned argc, Value *vp);
-
-    static ArrayBufferObject *create(JSContext *cx, uint32_t nbytes, bool clear = true);
-
-    static JSObject *createSlice(JSContext *cx, Handle<ArrayBufferObject*> arrayBuffer,
-                                 uint32_t begin, uint32_t end);
-
-    static bool createDataViewForThisImpl(JSContext *cx, CallArgs args);
-    static bool createDataViewForThis(JSContext *cx, unsigned argc, Value *vp);
-
-    template<typename T>
-    static bool createTypedArrayFromBufferImpl(JSContext *cx, CallArgs args);
-
-    template<typename T>
-    static bool createTypedArrayFromBuffer(JSContext *cx, unsigned argc, Value *vp);
-
-    static void obj_trace(JSTracer *trc, JSObject *obj);
-
-    static bool obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
-                                  MutableHandleObject objp, MutableHandleShape propp);
-    static bool obj_lookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
-                                   MutableHandleObject objp, MutableHandleShape propp);
-    static bool obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
-                                  MutableHandleObject objp, MutableHandleShape propp);
-    static bool obj_lookupSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
-                                  MutableHandleObject objp, MutableHandleShape propp);
-
-    static bool obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
-                                  PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
-    static bool obj_defineProperty(JSContext *cx, HandleObject obj,
-                                   HandlePropertyName name, HandleValue v,
-                                   PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
-    static bool obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v,
-                                  PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
-    static bool obj_defineSpecial(JSContext *cx, HandleObject obj,
-                                  HandleSpecialId sid, HandleValue v,
-                                  PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
-
-    static bool obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
-                               HandleId id, MutableHandleValue vp);
-
-    static bool obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
-                                HandlePropertyName name, MutableHandleValue vp);
-
-    static bool obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver,
-                               uint32_t index, MutableHandleValue vp);
-
-    static bool obj_getSpecial(JSContext *cx, HandleObject obj, HandleObject receiver,
-                               HandleSpecialId sid, MutableHandleValue vp);
-
-    static bool obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
-                               MutableHandleValue vp, bool strict);
-    static bool obj_setProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
-                                MutableHandleValue vp, bool strict);
-    static bool obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
-                               MutableHandleValue vp, bool strict);
-    static bool obj_setSpecial(JSContext *cx, HandleObject obj,
-                               HandleSpecialId sid, MutableHandleValue vp, bool strict);
-
-    static bool obj_getGenericAttributes(JSContext *cx, HandleObject obj,
-                                         HandleId id, unsigned *attrsp);
-    static bool obj_setGenericAttributes(JSContext *cx, HandleObject obj,
-                                         HandleId id, unsigned *attrsp);
-
-    static bool obj_deleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
-                                   bool *succeeded);
-    static bool obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
-                                  bool *succeeded);
-    static bool obj_deleteSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
-                                  bool *succeeded);
-
-    static bool obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
-                              MutableHandleValue statep, MutableHandleId idp);
-
-    static void sweep(JSCompartment *rt);
-
-    static void resetArrayBufferList(JSCompartment *rt);
-    static bool saveArrayBufferList(JSCompartment *c, ArrayBufferVector &vector);
-    static void restoreArrayBufferLists(ArrayBufferVector &vector);
-
-    static bool stealContents(JSContext *cx, Handle<ArrayBufferObject*> buffer, void **contents,
-                              uint8_t **data);
-
-    static void updateElementsHeader(js::ObjectElements *header, uint32_t bytes) {
-        header->initializedLength = bytes;
-
-        // NB: one or both of these fields is clobbered by GetViewList to store
-        // the 'views' link. Set them to 0 to effectively initialize 'views'
-        // to nullptr.
-        header->length = 0;
-        header->capacity = 0;
-    }
-
-    static void initElementsHeader(js::ObjectElements *header, uint32_t bytes) {
-        header->flags = 0;
-        updateElementsHeader(header, bytes);
-    }
-
-    static uint32_t headerInitializedLength(const js::ObjectElements *header) {
-        return header->initializedLength;
-    }
-
-    void addView(ArrayBufferViewObject *view);
-
-    void changeContents(JSContext *cx, ObjectElements *newHeader);
-
-    /*
-     * Ensure data is not stored inline in the object. Used when handing back a
-     * GC-safe pointer.
-     */
-    static bool ensureNonInline(JSContext *cx, Handle<ArrayBufferObject*> buffer);
-
-    uint32_t byteLength() const {
-        return getElementsHeader()->initializedLength;
-    }
-
-    /*
-     * Neuter all views of an ArrayBuffer.
-     */
-    static bool neuterViews(JSContext *cx, Handle<ArrayBufferObject*> buffer);
-
-    inline uint8_t * dataPointer() const {
-        return (uint8_t *) elements;
-    }
-
-    /*
-     * Discard the ArrayBuffer contents. For asm.js buffers, at least, should
-     * be called after neuterViews().
-     */
-    void neuter(JSContext *cx);
-
-    /*
-     * Check if the arrayBuffer contains any data. This will return false for
-     * ArrayBuffer.prototype and neutered ArrayBuffers.
-     */
-    bool hasData() const {
-        return getClass() == &class_;
-    }
-
-    bool isAsmJSArrayBuffer() const {
-        return getElementsHeader()->isAsmJSArrayBuffer();
-    }
-    bool isNeutered() const {
-        return getElementsHeader()->isNeuteredBuffer();
-    }
-    static bool prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer);
-    static bool neuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer);
-    static void releaseAsmJSArrayBuffer(FreeOp *fop, JSObject *obj);
-};
-
-/*
- * ArrayBufferViewObject
- *
- * Common definitions shared by all ArrayBufferViews.
- */
-
-class ArrayBufferViewObject : public JSObject
-{
-  protected:
-    /* Offset of view in underlying ArrayBufferObject */
-    static const size_t BYTEOFFSET_SLOT  = 0;
-
-    /* Byte length of view */
-    static const size_t BYTELENGTH_SLOT  = 1;
-
-    /* Underlying ArrayBufferObject */
-    static const size_t BUFFER_SLOT      = 2;
-
-    /* ArrayBufferObjects point to a linked list of views, chained through this slot */
-    static const size_t NEXT_VIEW_SLOT   = 3;
-
-    /*
-     * When ArrayBufferObjects are traced during GC, they construct a linked
-     * list of ArrayBufferObjects with more than one view, chained through this
-     * slot of the first view of each ArrayBufferObject.
-     */
-    static const size_t NEXT_BUFFER_SLOT = 4;
-
-    static const size_t NUM_SLOTS        = 5;
-
-  public:
-    JSObject *bufferObject() const {
-        return &getFixedSlot(BUFFER_SLOT).toObject();
-    }
-
-    ArrayBufferObject *bufferLink() {
-        return static_cast<ArrayBufferObject*>(getFixedSlot(NEXT_BUFFER_SLOT).toPrivate());
-    }
-
-    inline void setBufferLink(ArrayBufferObject *buffer);
-
-    ArrayBufferViewObject *nextView() const {
-        return static_cast<ArrayBufferViewObject*>(getFixedSlot(NEXT_VIEW_SLOT).toPrivate());
-    }
-
-    inline void setNextView(ArrayBufferViewObject *view);
-
-    void prependToViews(ArrayBufferViewObject *viewsHead);
-
-    void neuter(JSContext *cx);
-
-    static void trace(JSTracer *trc, JSObject *obj);
-};
-
 /*
  * TypedArrayObject
  *
  * The non-templated base class for the specific typed implementations.
  * This class holds all the member variables that are used by
  * the subclasses.
  */
 
 class TypedArrayObject : public ArrayBufferViewObject
 {
   protected:
     // Typed array properties stored in slots, beyond those shared by all
     // ArrayBufferViews.
-    static const size_t LENGTH_SLOT    = ArrayBufferViewObject::NUM_SLOTS;
-    static const size_t TYPE_SLOT      = ArrayBufferViewObject::NUM_SLOTS + 1;
-    static const size_t RESERVED_SLOTS = ArrayBufferViewObject::NUM_SLOTS + 2;
-    static const size_t DATA_SLOT      = 7; // private slot, based on alloc kind
+    static const size_t LENGTH_SLOT    = JS_TYPEDOBJ_SLOT_LENGTH;
+    static const size_t TYPE_SLOT      = JS_TYPEDOBJ_SLOT_TYPE_DESCR;
+    static const size_t RESERVED_SLOTS = JS_TYPEDOBJ_SLOTS;
+    static const size_t DATA_SLOT      = JS_TYPEDOBJ_SLOT_DATA;
 
   public:
     static const Class classes[ScalarTypeDescr::TYPE_MAX];
     static const Class protoClasses[ScalarTypeDescr::TYPE_MAX];
 
     static bool obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
                                   MutableHandleObject objp, MutableHandleShape propp);
     static bool obj_lookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
@@ -415,18 +165,18 @@ TypedArrayShift(ArrayBufferView::ViewTyp
         return 3;
       default:;
     }
     MOZ_ASSUME_UNREACHABLE("Unexpected array type");
 }
 
 class DataViewObject : public ArrayBufferViewObject
 {
-    static const size_t RESERVED_SLOTS = ArrayBufferViewObject::NUM_SLOTS;
-    static const size_t DATA_SLOT      = 7; // private slot, based on alloc kind
+    static const size_t RESERVED_SLOTS = JS_DATAVIEW_SLOTS;
+    static const size_t DATA_SLOT      = JS_TYPEDOBJ_SLOT_DATA;
 
   private:
     static const Class protoClass;
 
     static bool is(HandleValue v) {
         return v.isObject() && v.toObject().hasClass(&class_);
     }
 
@@ -562,25 +312,28 @@ ClampIntForUint8Array(int32_t x)
         return 0;
     if (x > 255)
         return 255;
     return x;
 }
 
 bool ToDoubleForTypedArray(JSContext *cx, JS::HandleValue vp, double *d);
 
+extern js::ArrayBufferObject * const UNSET_BUFFER_LINK;
+
 } // namespace js
 
 template <>
 inline bool
 JSObject::is<js::TypedArrayObject>() const
 {
     return js::IsTypedArrayClass(getClass());
 }
 
 template <>
 inline bool
 JSObject::is<js::ArrayBufferViewObject>() const
 {
-    return is<js::DataViewObject>() || is<js::TypedArrayObject>();
+    return is<js::DataViewObject>() || is<js::TypedArrayObject>() ||
+           IsTypedObjectClass(getClass());
 }
 
 #endif /* vm_TypedArrayObject_h */
--- a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
+++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
@@ -18,20 +18,20 @@ SandboxBroker::SandboxBroker()
   if (!sBrokerService) {
     sBrokerService = sandbox::SandboxFactory::GetBrokerServices();
     if (sBrokerService) {
       sandbox::ResultCode result = sBrokerService->Init();
       if (result != sandbox::SBOX_ALL_OK) {
         sBrokerService = nullptr;
       }
     }
+  }
 
-    // We'll start to increase the restrictions over time.
-    mPolicy = sBrokerService->CreatePolicy();
-  }
+  // We'll start to increase the restrictions over time.
+  mPolicy = sBrokerService->CreatePolicy();
 }
 
 bool
 SandboxBroker::AllowPipe(const wchar_t *aPath)
 {
   return mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_NAMED_PIPES,
                           sandbox::TargetPolicy::NAMEDPIPES_ALLOW_ANY, aPath);
 }
@@ -50,16 +50,19 @@ SandboxBroker::LaunchApp(const wchar_t *
   // Medium integrity, unrestricted, in the same window station, within the
   // same desktop, and has no job object.
   // We'll start to increase the restrictions over time.
   mPolicy->SetJobLevel(sandbox::JOB_NONE, 0);
   mPolicy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
                          sandbox::USER_RESTRICTED_SAME_ACCESS);
   mPolicy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_UNTRUSTED);
 
+  // Set an alternate Desktop within a new window station
+  mPolicy->SetAlternateDesktop(false);
+
   // Ceate the sandboxed process
   PROCESS_INFORMATION targetInfo;
   sandbox::ResultCode result;
   result = sBrokerService->SpawnTarget(aPath, aArguments, mPolicy, &targetInfo);
 
   // The sandboxed process is started in a suspended state, resumeit now that
   // we'eve set things up.
   ResumeThread(targetInfo.hThread);
--- a/xpcom/reflect/xptcall/src/md/unix/xptc_gcc_x86_unix.h
+++ b/xpcom/reflect/xptcall/src/md/unix/xptc_gcc_x86_unix.h
@@ -10,10 +10,8 @@
 //
 
 #ifdef MOZ_NEED_LEADING_UNDERSCORE
 #define SYMBOL_UNDERSCORE "_"
 #else
 #define SYMBOL_UNDERSCORE
 #endif
 
-
-#define ATTRIBUTE_USED __attribute__ ((__used__))
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_alpha_openbsd.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_alpha_openbsd.cpp
@@ -6,17 +6,17 @@
 /* Implement shared vtbl methods. */
 
 #include "xptcprivate.h"
 #include "xptiprivate.h"
 
 /* Prototype specifies unmangled function name and disables unused warning */
 static nsresult
 PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint64_t* args)
-__asm__("PrepareAndDispatch") __attribute__((used));
+__asm__("PrepareAndDispatch") ATTRIBUTE_USED;
 
 static nsresult
 PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint64_t* args)
 {
     const uint8_t PARAM_BUFFER_COUNT = 16;
     const uint8_t NUM_ARG_REGS = 6-1;        // -1 for "this" pointer
 
     nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_amd64_openbsd.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_amd64_openbsd.cpp
@@ -23,17 +23,17 @@ const uint32_t FPR_COUNT            = 8;
 //
 // - 'args[]' contains the arguments passed on stack
 // - 'gpregs[]' contains the arguments passed in integer registers
 // - 'fpregs[]' contains the arguments passed in floating point registers
 // 
 // The parameters are mapped into an array of type 'nsXPTCMiniVariant'
 // and then the method gets called.
 
-extern "C" nsresult
+extern "C" nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase * self, uint32_t methodIndex,
                    uint64_t * args, uint64_t * gpregs, double *fpregs)
 {
     nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
     nsXPTCMiniVariant* dispatchParams = nullptr;
     const nsXPTMethodInfo* info;
     uint32_t paramCount;
     uint32_t i;
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_arm.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_arm.cpp
@@ -7,31 +7,19 @@
 
 #include "xptcprivate.h"
 #include "xptiprivate.h"
 
 #if !defined(__arm__) && !(defined(LINUX) || defined(ANDROID))
 #error "This code is for Linux ARM only. Please check if it works for you, too.\nDepends strongly on gcc behaviour."
 #endif
 
-#ifdef __GNUC__
-/* This tells gcc3.4+ not to optimize away symbols.
- * @see http://gcc.gnu.org/gcc-3.4/changes.html
- */
-#define DONT_DROP_OR_WARN __attribute__((used))
-#else
-/* This tells older gccs not to warn about unused vairables.
- * @see http://docs.freebsd.org/info/gcc/gcc.info.Variable_Attributes.html
- */
-#define DONT_DROP_OR_WARN __attribute__((unused))
-#endif
-
 /* Specify explicitly a symbol for this function, don't try to guess the c++ mangled symbol.  */
 static nsresult PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args) asm("_PrepareAndDispatch")
-DONT_DROP_OR_WARN;
+ATTRIBUTE_USED;
 
 #ifdef __ARM_EABI__
 #define DOUBLEWORD_ALIGN(p) ((uint32_t *)((((uint32_t)(p)) + 7) & 0xfffffff8))
 #else
 #define DOUBLEWORD_ALIGN(p) (p)
 #endif
 
 // Apple's iOS toolchain is lame and does not support .cfi directives.
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_arm_netbsd.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_arm_netbsd.cpp
@@ -2,17 +2,17 @@
 /* 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/. */
 
 /* Implement shared vtbl methods. */
 
 #include "xptcprivate.h"
 
-nsresult
+nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args)
 {
 #define PARAM_BUFFER_COUNT     16
 
     nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
     nsXPTCMiniVariant* dispatchParams = nullptr;
     nsIInterfaceInfo* iface_info = nullptr;
     const nsXPTMethodInfo* info;
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_arm_openbsd.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_arm_openbsd.cpp
@@ -19,17 +19,17 @@
  */
 #define DONT_DROP_OR_WARN __attribute__((unused))
 #endif
 
 /* Specify explicitly a symbol for this function, don't try to guess the c++ mangled symbol.  */
 static nsresult PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args) asm("_PrepareAndDispatch")
 DONT_DROP_OR_WARN;
 
-static nsresult
+static nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args)
 {
 #define PARAM_BUFFER_COUNT     16
 
     nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
     nsXPTCMiniVariant* dispatchParams = nullptr;
     const nsXPTMethodInfo* info;
     uint8_t paramCount;
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_ipf32.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_ipf32.cpp
@@ -11,17 +11,17 @@
 
 #include <stddef.h>
 #include <stdlib.h>
 
 // "This code is for IA64 only"
 
 /* Implement shared vtbl methods. */
 
-extern "C" nsresult
+extern "C" nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex,
   uint64_t* intargs, uint64_t* floatargs, uint64_t* restargs)
 {
 
 #define PARAM_BUFFER_COUNT     16
 
   nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
   nsXPTCMiniVariant* dispatchParams = nullptr;
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_ipf64.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_ipf64.cpp
@@ -12,17 +12,17 @@
 #include <stddef.h>
 #include <stdlib.h>
 #include <stdint.h>
 
 // "This code is for IA64 only"
 
 /* Implement shared vtbl methods. */
 
-extern "C" nsresult
+extern "C" nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex,
   uint64_t* intargs, uint64_t* floatargs, uint64_t* restargs)
 {
 
 #define PARAM_BUFFER_COUNT     16
 
   nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
   nsXPTCMiniVariant* dispatchParams = nullptr;
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_linux_alpha.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_linux_alpha.cpp
@@ -6,17 +6,17 @@
 /* Implement shared vtbl methods. */
 
 #include "xptcprivate.h"
 #include "xptiprivate.h"
 
 /* Prototype specifies unmangled function name and disables unused warning */
 static nsresult
 PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint64_t* args)
-__asm__("PrepareAndDispatch") __attribute__((used));
+__asm__("PrepareAndDispatch") ATTRIBUTE_USED;
 
 static nsresult
 PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint64_t* args)
 {
     const uint8_t PARAM_BUFFER_COUNT = 16;
     const uint8_t NUM_ARG_REGS = 6-1;        // -1 for "this" pointer
 
     nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_linux_m68k.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_linux_m68k.cpp
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Implement shared vtbl methods. */
 
 #include "xptcprivate.h"
 #include "xptiprivate.h"
 
 extern "C" {
-    nsresult
+    nsresult ATTRIBUTE_USED
     PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args)
     {
 #define PARAM_BUFFER_COUNT     16
 
         nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
         nsXPTCMiniVariant* dispatchParams = nullptr;
         const nsXPTMethodInfo* info;
         uint8_t paramCount;
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_linux_s390.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_linux_s390.cpp
@@ -4,17 +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/. */
 
 /* Implement shared vtbl methods. */
 
 #include "xptcprivate.h"
 #include "xptiprivate.h"
 
-static nsresult
+static nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, 
                    uint32_t* a_gpr, uint64_t *a_fpr, uint32_t *a_ov)
 {
 #define PARAM_BUFFER_COUNT     16
 
     nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
     nsXPTCMiniVariant* dispatchParams = nullptr;
     const nsXPTMethodInfo* info;
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_linux_s390x.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_linux_s390x.cpp
@@ -4,17 +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/. */
 
 /* Implement shared vtbl methods. */
 
 #include "xptcprivate.h"
 #include "xptiprivate.h"
 
-static nsresult
+static nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, 
                    uint64_t* a_gpr, uint64_t *a_fpr, uint64_t *a_ov)
 {
 #define PARAM_BUFFER_COUNT     16
 
     nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
     nsXPTCMiniVariant* dispatchParams = nullptr;
     const nsXPTMethodInfo* info;
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_mips.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_mips.cpp
@@ -10,17 +10,17 @@
 
 #include <stdint.h>
 
 /*
  * This is for MIPS O32 ABI
  * Args contains a0-3 and then the stack.
  * Because a0 is 'this', we want to skip it
  */
-extern "C" nsresult
+extern "C" nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args)
 {
     args++; // always skip over a0
 
 #define PARAM_BUFFER_COUNT		16
 
     nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
     nsXPTCMiniVariant* dispatchParams = nullptr;
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_mips64.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_mips64.cpp
@@ -15,17 +15,17 @@
  *
  * When we're called, the "gp" registers are stored in gprData and
  * the "fp" registers are stored in fprData.  There are 8 regs
  * available which correspond to the first 7 parameters of the
  * function and the "this" pointer.  If there are additional parms,
  * they are stored on the stack at address "args".
  *
  */
-extern "C" nsresult
+extern "C" nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint64_t* args,
                    uint64_t *gprData, double *fprData)
 {
 #define PARAM_BUFFER_COUNT        16
 #define PARAM_GPR_COUNT            7
 #define PARAM_FPR_COUNT            7
 
     nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_netbsd_m68k.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_netbsd_m68k.cpp
@@ -7,17 +7,17 @@
 
 #include "xptcprivate.h"
 
 #if !defined(__NetBSD__) || !defined(__m68k__)
 #error This code is for NetBSD/m68k only
 #endif
 
 extern "C" {
-    static nsresult
+    static nsresult ATTRIBUTE_USED
     PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args)
     {
 #define PARAM_BUFFER_COUNT     16
 
         nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
         nsXPTCMiniVariant* dispatchParams = nullptr;
         nsIInterfaceInfo* iface_info = nullptr;
         const nsXPTMethodInfo* info;
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_pa32.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_pa32.cpp
@@ -9,17 +9,17 @@
 
 #include "xptcprivate.h"
 #include "xptiprivate.h" 
 
 #if _HPUX
 #error "This code is for HP-PA RISC 32 bit mode only"
 #endif
 
-extern "C" nsresult
+extern "C" nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex,
   uint32_t* args, uint32_t* floatargs)
 {
 
   typedef struct {
     uint32_t hi;
     uint32_t lo;
   } DU;
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_ppc64_linux.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_ppc64_linux.cpp
@@ -27,17 +27,17 @@
 //
 // - 'args[]' contains the arguments passed on stack
 // - 'gprData[]' contains the arguments passed in integer registers
 // - 'fprData[]' contains the arguments passed in floating point registers
 // 
 // The parameters are mapped into an array of type 'nsXPTCMiniVariant'
 // and then the method gets called.
 #include <stdio.h>
-extern "C" nsresult
+extern "C" nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase* self,
                    uint64_t methodIndex,
                    uint64_t* args,
                    uint64_t *gprData,
                    double *fprData)
 {
     nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
     nsXPTCMiniVariant* dispatchParams = nullptr;
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_ppc_aix.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_ppc_aix.cpp
@@ -11,17 +11,17 @@
 #if defined(AIX)
 
 /*
         For PPC (AIX & MAC), the first 8 integral and the first 13 f.p. parameters 
         arrive in a separate chunk of data that has been loaded from the registers. 
         The args pointer has been set to the start of the parameters BEYOND the ones
         arriving in registers
 */
-extern "C" nsresult
+extern "C" nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args, uint32_t *gprData, double *fprData)
 {
     typedef struct {
         uint32_t hi;
         uint32_t lo;      // have to move 64 bit entities as 32 bit halves since
     } DU;               // stack slots are not guaranteed 16 byte aligned
 
 #define PARAM_BUFFER_COUNT     16
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_ppc_aix64.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_ppc_aix64.cpp
@@ -10,17 +10,17 @@
 #if defined(AIX)
 
 /*
         For PPC (AIX & MAC), the first 8 integral and the first 13 f.p. parameters 
         arrive in a separate chunk of data that has been loaded from the registers. 
         The args pointer has been set to the start of the parameters BEYOND the ones
         arriving in registers
 */
-extern "C" nsresult
+extern "C" nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase* self, uint64_t methodIndex, uint64_t* args, uint64_t *gprData, double *fprData)
 {
 
 #define PARAM_BUFFER_COUNT     16
 #define PARAM_GPR_COUNT         7  
 
     nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
     nsXPTCMiniVariant* dispatchParams = nullptr;
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_ppc_linux.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_ppc_linux.cpp
@@ -26,17 +26,17 @@
 //
 // - 'args[]' contains the arguments passed on stack
 // - 'gprData[]' contains the arguments passed in integer registers
 // - 'fprData[]' contains the arguments passed in floating point registers
 // 
 // The parameters are mapped into an array of type 'nsXPTCMiniVariant'
 // and then the method gets called.
 
-extern "C" nsresult
+extern "C" nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase* self,
                    uint32_t methodIndex,
                    uint32_t* args,
                    uint32_t *gprData,
                    double *fprData)
 {
     nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
     nsXPTCMiniVariant* dispatchParams = nullptr;
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_ppc_netbsd.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_ppc_netbsd.cpp
@@ -22,17 +22,17 @@
 //
 // - 'args[]' contains the arguments passed on stack
 // - 'gprData[]' contains the arguments passed in integer registers
 // - 'fprData[]' contains the arguments passed in floating point registers
 // 
 // The parameters are mapped into an array of type 'nsXPTCMiniVariant'
 // and then the method gets called.
 
-extern "C" nsresult
+extern "C" nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase* self,
                    uint32_t methodIndex,
                    uint32_t* args,
                    uint32_t *gprData,
                    double *fprData)
 {
     nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
     nsXPTCMiniVariant* dispatchParams = nullptr;
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_ppc_openbsd.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_ppc_openbsd.cpp
@@ -23,17 +23,17 @@
 //
 // - 'args[]' contains the arguments passed on stack
 // - 'gprData[]' contains the arguments passed in integer registers
 // - 'fprData[]' contains the arguments passed in floating point registers
 // 
 // The parameters are mapped into an array of type 'nsXPTCMiniVariant'
 // and then the method gets called.
 
-extern "C" nsresult
+extern "C" nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase* self,
                    uint32_t methodIndex,
                    uint32_t* args,
                    uint32_t *gprData,
                    double *fprData)
 {
     nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
     nsXPTCMiniVariant* dispatchParams = nullptr;
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_ppc_rhapsody.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_ppc_rhapsody.cpp
@@ -30,17 +30,17 @@
  * stack without any special padding.
  *
  * See also xptcstubs_asm_ppc_darwin.s.m4:_SharedStub.
  *
  * ABI reference:
  * http://developer.apple.com/documentation/DeveloperTools/Conceptual/
  *  MachORuntime/PowerPCConventions/chapter_3_section_1.html */
 
-extern "C" nsresult
+extern "C" nsresult ATTRIBUTE_USED
 PrepareAndDispatch(
   nsXPTCStubBase *self,
   uint32_t        methodIndex,
   uint32_t       *argsStack,
   uint32_t       *argsGPR,
   double         *argsFPR) {
 #define PARAM_BUFFER_COUNT 16
 #define PARAM_FPR_COUNT    13
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_sparc64_openbsd.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_sparc64_openbsd.cpp
@@ -6,17 +6,17 @@
 
 /* Implement shared vtbl methods. */
 
 #include "xptcprivate.h"
 #include "xptiprivate.h"
 
 #if defined(sparc) || defined(__sparc__)
 
-extern "C" nsresult
+extern "C" nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase* self, uint64_t methodIndex, uint64_t* args)
 {
 
 #define PARAM_BUFFER_COUNT     16
 
     nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
     nsXPTCMiniVariant* dispatchParams = nullptr;
     const nsXPTMethodInfo* info;
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_sparc_netbsd.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_sparc_netbsd.cpp
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Implement shared vtbl methods. */
 
 #include "xptcprivate.h"
 
 #if defined(sparc) || defined(__sparc__)
 
-extern "C" nsresult
+extern "C" nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args)
 {
 
     typedef struct {
         uint32_t hi;
         uint32_t lo;
     } DU;               // have to move 64 bit entities as 32 bit halves since
                         // stack slots are not guaranteed 16 byte aligned
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_sparc_openbsd.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_sparc_openbsd.cpp
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Implement shared vtbl methods. */
 
 #include "xptcprivate.h"
 
 #if defined(sparc) || defined(__sparc__)
 
-extern "C" nsresult
+extern "C" nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args)
 {
 
     typedef struct {
         uint32_t hi;
         uint32_t lo;
     } DU;               // have to move 64 bit entities as 32 bit halves since
                         // stack slots are not guaranteed 16 byte aligned
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_sparc_solaris.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_sparc_solaris.cpp
@@ -5,17 +5,17 @@
 
 /* Implement shared vtbl methods. */
 
 #include "xptcprivate.h"
 #include "xptiprivate.h"
 
 #if defined(sparc) || defined(__sparc__)
 
-extern "C" nsresult
+extern "C" nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args)
 {
 
     typedef struct {
         uint32_t hi;
         uint32_t lo;
     } DU;               // have to move 64 bit entities as 32 bit halves since
                         // stack slots are not guaranteed 16 byte aligned
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_sparcv9_solaris.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_sparcv9_solaris.cpp
@@ -6,17 +6,17 @@
 
 /* Implement shared vtbl methods. */
 
 #include "xptcprivate.h"
 #include "xptiprivate.h"
 
 #if defined(sparc) || defined(__sparc__)
 
-extern "C" nsresult
+extern "C" nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase* self, uint64_t methodIndex, uint64_t* args)
 {
 
 #define PARAM_BUFFER_COUNT     16
 
     nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
     nsXPTCMiniVariant* dispatchParams = nullptr;
     const nsXPTMethodInfo* info;
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_x86_64_darwin.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_x86_64_darwin.cpp
@@ -25,17 +25,17 @@ const uint32_t FPR_COUNT            = 8;
 //
 // - 'args[]' contains the arguments passed on stack
 // - 'gpregs[]' contains the arguments passed in integer registers
 // - 'fpregs[]' contains the arguments passed in floating point registers
 // 
 // The parameters are mapped into an array of type 'nsXPTCMiniVariant'
 // and then the method gets called.
 
-extern "C" nsresult
+extern "C" nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase * self, uint32_t methodIndex,
                    uint64_t * args, uint64_t * gpregs, double *fpregs)
 {
     nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
     nsXPTCMiniVariant* dispatchParams = nullptr;
     const nsXPTMethodInfo* info;
     uint32_t paramCount;
     uint32_t i;
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_x86_64_linux.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_x86_64_linux.cpp
@@ -25,17 +25,17 @@ const uint32_t FPR_COUNT            = 8;
 //
 // - 'args[]' contains the arguments passed on stack
 // - 'gpregs[]' contains the arguments passed in integer registers
 // - 'fpregs[]' contains the arguments passed in floating point registers
 // 
 // The parameters are mapped into an array of type 'nsXPTCMiniVariant'
 // and then the method gets called.
 
-extern "C" nsresult
+extern "C" nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase * self, uint32_t methodIndex,
                    uint64_t * args, uint64_t * gpregs, double *fpregs)
 {
     nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
     nsXPTCMiniVariant* dispatchParams = nullptr;
     const nsXPTMethodInfo* info;
     uint32_t paramCount;
     uint32_t i;
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_x86_64_solaris.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_x86_64_solaris.cpp
@@ -25,17 +25,17 @@ const uint32_t FPR_COUNT            = 8;
 //
 // - 'args[]' contains the arguments passed on stack
 // - 'gpregs[]' contains the arguments passed in integer registers
 // - 'fpregs[]' contains the arguments passed in floating point registers
 // 
 // The parameters are mapped into an array of type 'nsXPTCMiniVariant'
 // and then the method gets called.
 
-extern "C" nsresult
+extern "C" nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase * self, uint32_t methodIndex,
                    uint64_t * args, uint64_t * gpregs, double *fpregs)
 {
     nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
     nsXPTCMiniVariant* dispatchParams = nullptr;
     const nsXPTMethodInfo* info;
     uint32_t paramCount;
     uint32_t i;
--- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_x86_solaris.cpp
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_x86_solaris.cpp
@@ -4,17 +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/. */
 
 /* Implement shared vtbl methods. */
 
 #include "xptcprivate.h"
 #include "xptiprivate.h"
 
-nsresult
+nsresult ATTRIBUTE_USED
 PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args)
 {
 #define PARAM_BUFFER_COUNT     16
 
     nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
     nsXPTCMiniVariant* dispatchParams = nullptr;
     const nsXPTMethodInfo* info;
     uint8_t paramCount;
--- a/xpcom/reflect/xptcall/src/md/win32/xptcstubs_x86_64_gnu.cpp
+++ b/xpcom/reflect/xptcall/src/md/win32/xptcstubs_x86_64_gnu.cpp
@@ -11,17 +11,17 @@
  * This is for Windows 64 bit (x86_64) using GCC syntax
  * Code was copied from the MSVC version.
  */
 
 #if !defined(_AMD64_) || !defined(__GNUC__)
 #  error xptcstubs_x86_64_gnu.cpp being used unexpectedly
 #endif
 
-extern "C" nsresult
+extern "C" nsresult __attribute__((__used__))
 PrepareAndDispatch(nsXPTCStubBase * self, uint32_t methodIndex,
                    uint64_t * args, uint64_t * gprData, double *fprData)
 {
 #define PARAM_BUFFER_COUNT  16
 //
 // "this" pointer is first parameter, so parameter count is 3.
 //
 #define PARAM_GPR_COUNT   3
--- a/xpcom/reflect/xptcall/src/xptcprivate.h
+++ b/xpcom/reflect/xptcall/src/xptcprivate.h
@@ -53,9 +53,15 @@ public:
     xptiInterfaceEntry*    mEntry;
 
     ~nsXPTCStubBase() { }
 };
 
 #undef STUB_ENTRY
 #undef SENTINEL_ENTRY
 
+#if defined(__clang__) || defined(__GNUC__)
+#define ATTRIBUTE_USED __attribute__ ((__used__))
+#else
+#define ATTRIBUTE_USED
+#endif
+
 #endif /* xptcprivate_h___ */