Bug 695438 - Make typed arrays native objects, allow adding new named properties, r=luke.
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 04 Mar 2014 12:42:08 -0700
changeset 182855 78fa90a29c43031a53904937e63f705f48b73aa2
parent 182854 703bd71926e404e704561866d17bb22e38bf666b
child 182856 722fd1b0e05045c20ab75e47652a1714aa6a06d7
push idunknown
push userunknown
push dateunknown
reviewersluke
bugs695438
milestone30.0a1
Bug 695438 - Make typed arrays native objects, allow adding new named properties, r=luke.
js/src/builtin/Object.cpp
js/src/jit-test/tests/basic/bug829821.js
js/src/jit-test/tests/basic/typed-array-index-out-of-range.js
js/src/jit-test/tests/basic/typed-array-sealed-frozen.js
js/src/jit-test/tests/collections/Array-of-generic-3.js
js/src/jit/BaselineIC.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/MIR.cpp
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jsinfer.cpp
js/src/jsiter.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/vm/ObjectImpl.cpp
js/src/vm/ObjectImpl.h
js/src/vm/OldDebugAPI.cpp
js/src/vm/SelfHosting.cpp
js/src/vm/Shape-inl.h
js/src/vm/Shape.h
js/src/vm/TypedArrayObject.cpp
js/src/vm/TypedArrayObject.h
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -140,17 +140,17 @@ js::ObjectToSource(JSContext *cx, Handle
         RootedShape shape(cx);
         if (!JSObject::lookupGeneric(cx, obj, id, &obj2, &shape))
             return nullptr;
 
         /*  Decide early whether we prefer get/set or old getter/setter syntax. */
         int valcnt = 0;
         if (shape) {
             bool doGet = true;
-            if (obj2->isNative() && !IsImplicitDenseElement(shape)) {
+            if (obj2->isNative() && !IsImplicitDenseOrTypedArrayElement(shape)) {
                 unsigned attrs = shape->attributes();
                 if (attrs & JSPROP_GETTER) {
                     doGet = false;
                     val[valcnt].set(shape->getterValue());
                     gsop[valcnt].set(cx->names().get);
                     valcnt++;
                 }
                 if (attrs & JSPROP_SETTER) {
@@ -449,17 +449,17 @@ obj_lookupGetter(JSContext *cx, unsigned
         return true;
     }
     RootedObject pobj(cx);
     RootedShape shape(cx);
     if (!JSObject::lookupGeneric(cx, obj, id, &pobj, &shape))
         return false;
     args.rval().setUndefined();
     if (shape) {
-        if (pobj->isNative() && !IsImplicitDenseElement(shape)) {
+        if (pobj->isNative() && !IsImplicitDenseOrTypedArrayElement(shape)) {
             if (shape->hasGetterValue())
                 args.rval().set(shape->getterValue());
         }
     }
     return true;
 }
 
 static bool
@@ -485,17 +485,17 @@ obj_lookupSetter(JSContext *cx, unsigned
         return true;
     }
     RootedObject pobj(cx);
     RootedShape shape(cx);
     if (!JSObject::lookupGeneric(cx, obj, id, &pobj, &shape))
         return false;
     args.rval().setUndefined();
     if (shape) {
-        if (pobj->isNative() && !IsImplicitDenseElement(shape)) {
+        if (pobj->isNative() && !IsImplicitDenseOrTypedArrayElement(shape)) {
             if (shape->hasSetterValue())
                 args.rval().set(shape->setterValue());
         }
     }
     return true;
 }
 #endif /* JS_OLD_GETTER_SETTER_METHODS */
 
--- a/js/src/jit-test/tests/basic/bug829821.js
+++ b/js/src/jit-test/tests/basic/bug829821.js
@@ -1,5 +1,5 @@
-
+// |jit-test| error:TypeError
 
 x = [];
 x.unshift(Uint8ClampedArray);
 x.forEach(Array.prototype.push.bind(new Uint8ClampedArray))
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/typed-array-index-out-of-range.js
@@ -0,0 +1,29 @@
+
+// Out of bounds writes on typed arrays are uneffectful for all integers.
+
+var x = new Int32Array(10);
+
+function f() {
+    for (var i = -100; i < 100; i++) {
+	x[i] = i + 1;
+	if (i >= 0 && i < 10)
+	    assertEq(x[i], i + 1);
+	else
+	    assertEq(x[i], undefined);
+    }
+}
+f();
+
+// Integers which don't fit in a double value's mantissa aren't really integers.
+
+var bigint = "" + Math.pow(2, 53);
+x[bigint] = "twelve";
+assertEq(x[bigint], "twelve");
+
+// Infinity and -Infinity maybe are supposed to be integers, but they aren't currently.
+
+x["Infinity"] = "twelve";
+assertEq(x["Infinity"], "twelve");
+
+x["-Infinity"] = "twelve";
+assertEq(x["-Infinity"], "twelve");
--- a/js/src/jit-test/tests/basic/typed-array-sealed-frozen.js
+++ b/js/src/jit-test/tests/basic/typed-array-sealed-frozen.js
@@ -1,13 +1,11 @@
 // Any copyright is dedicated to the Public Domain.
 // http://creativecommons.org/licenses/publicdomain/
 
-// Typed arrays are always sealed.
-
 load(libdir + "asserts.js")
 
 const constructors = [
   Int8Array,
   Uint8Array,
   Uint8ClampedArray,
   Int16Array,
   Uint16Array,
@@ -15,39 +13,73 @@ const constructors = [
   Uint32Array,
   Float32Array,
   Float64Array
 ];
 
 for (constructor of constructors) {
   print("testing non-empty " + constructor.name);
   let a = new constructor(10);
-  assertEq(Object.isExtensible(a), false);
-  assertEq(Object.isSealed(a), true);
+  assertEq(Object.isExtensible(a), true);
+  assertEq(Object.isSealed(a), false);
   assertEq(Object.isFrozen(a), false);
 
   // Should not throw.
   Object.seal(a);
 
   // Should complain that it can't change attributes of indexed typed array properties.
   assertThrowsInstanceOf(() => Object.freeze(a), InternalError);
 }
 
 print();
 
 for (constructor of constructors) {
   print("testing zero-length " + constructor.name);
   let a = new constructor(0);
-  assertEq(Object.isExtensible(a), false);
-  assertEq(Object.isSealed(a), true);
-  assertEq(Object.isFrozen(a), true);
+  assertEq(Object.isExtensible(a), true);
+  assertEq(Object.isSealed(a), false);
+  assertEq(Object.isFrozen(a), false);
 
   // Should not throw.
   Object.seal(a);
   Object.freeze(a);
 }
 
 // isSealed and isFrozen should not try to build an array of all the
 // property names of a typed array, since they're often especially large.
 // This should not throw an allocation error.
 let a = new Uint8Array(1 << 24);
 Object.isSealed(a);
 Object.isFrozen(a);
+
+for (constructor of constructors) {
+  print("testing extensibility " + constructor.name);
+  let a = new constructor(10);
+
+  // New named properties should show up on typed arrays.
+  a.foo = "twelve";
+  assertEq(a.foo, "twelve");
+
+  // New indexed properties should not show up on typed arrays.
+  a[20] = "twelve";
+  assertEq(a[20], undefined);
+
+  // Watch for especially large indexed properties.
+  a[-10 >>> 0] = "twelve";
+  assertEq(a[-10 >>> 0], undefined);
+
+  // Watch for overly large indexed properties.
+  a[Math.pow(2, 53)] = "twelve";
+  assertEq(a[Math.pow(2, 53)], "twelve");
+
+  // Don't define old properties.
+  Object.defineProperty(a, 5, {value: 3});
+  assertEq(a[5], 0);
+
+  // Don't define new properties.
+  Object.defineProperty(a, 20, {value: 3});
+  assertEq(a[20], undefined);
+
+  // Don't delete indexed properties.
+  a[3] = 3;
+  delete a[3];
+  assertEq(a[3], 3);
+}
--- a/js/src/jit-test/tests/collections/Array-of-generic-3.js
+++ b/js/src/jit-test/tests/collections/Array-of-generic-3.js
@@ -1,6 +1,7 @@
+// |jit-test| error:TypeError
 // Array.of can be transplanted to builtin constructors.
 
 load(libdir + "asserts.js");
 
 Uint8Array.of = Array.of;
 assertDeepEq(Uint8Array.of(0x12, 0x34, 0x5678, 0x9abcdef), Uint8Array([0x12, 0x34, 0x78, 0xef]));
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -3299,21 +3299,16 @@ EffectlesslyLookupProperty(JSContext *cx
             return true;
         }
 
         *domProxyHasGeneration = (*shadowsResult == DoesntShadowUnique);
 
         checkObj = GetDOMProxyProto(obj);
         if (!checkObj)
             return true;
-    } else if (obj->is<TypedArrayObject>() && obj->getProto()) {
-        // Typed array objects are non-native, but don't have any named
-        // properties. Just forward the lookup to the prototype, to allow
-        // inlining common getters like byteOffset.
-        checkObj = obj->getProto();
     } else if (!obj->isNative()) {
         return true;
     }
 
     if (checkObj->hasIdempotentProtoChain()) {
         if (!JSObject::lookupProperty(cx, checkObj, name, holder, shape))
             return false;
     } else if (checkObj->isNative()) {
@@ -3323,17 +3318,17 @@ EffectlesslyLookupProperty(JSContext *cx
     }
     return true;
 }
 
 static bool
 IsCacheableProtoChain(JSObject *obj, JSObject *holder, bool isDOMProxy=false)
 {
     JS_ASSERT_IF(isDOMProxy, IsCacheableDOMProxy(obj));
-    JS_ASSERT_IF(!isDOMProxy, obj->isNative() || obj->is<TypedArrayObject>());
+    JS_ASSERT_IF(!isDOMProxy, obj->isNative());
 
     // Don't handle objects which require a prototype guard. This should
     // be uncommon so handling it is likely not worth the complexity.
     if (obj->hasUncacheableProto())
         return false;
 
     JSObject *cur = obj;
     while (cur != holder) {
@@ -3889,17 +3884,17 @@ TryAttachGetElemStub(JSContext *cx, JSSc
 
             stub->addNewStub(argsStub);
             return true;
         }
     }
 
     if (obj->isNative()) {
         // Check for NativeObject[int] dense accesses.
-        if (rhs.isInt32() && rhs.toInt32() >= 0) {
+        if (rhs.isInt32() && rhs.toInt32() >= 0 && !obj->is<TypedArrayObject>()) {
             IonSpew(IonSpew_BaselineIC, "  Generating GetElem(Native[Int32] dense) stub");
             ICGetElem_Dense::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
                                                obj->lastProperty(), isCallElem);
             ICStub *denseStub = compiler.getStub(compiler.getStubSpace(script));
             if (!denseStub)
                 return false;
 
             stub->addNewStub(denseStub);
@@ -3937,20 +3932,20 @@ TryAttachGetElemStub(JSContext *cx, JSSc
         ICStub *typedArrayStub = compiler.getStub(compiler.getStubSpace(script));
         if (!typedArrayStub)
             return false;
 
         stub->addNewStub(typedArrayStub);
         return true;
     }
 
-    // GetElem operations on non-native objects other than typed arrays cannot
-    // be cached by either Baseline or Ion. Indicate this in the cache so that
-    // Ion does not generate a cache for this op.
-    if (!obj->isNative() && !obj->is<TypedArrayObject>())
+    // GetElem operations on non-native objects cannot be cached by either
+    // Baseline or Ion. Indicate this in the cache so that Ion does not
+    // generate a cache for this op.
+    if (!obj->isNative())
         stub->noteNonNativeAccess();
 
     // GetElem operations which could access negative indexes generally can't
     // be optimized without the potential for bailouts, as we can't statically
     // determine that an object has no properties on such indexes.
     if (rhs.isNumber() && rhs.toNumber() < 0)
         stub->noteNegativeIndex();
 
@@ -4985,21 +4980,20 @@ DoSetElemFallback(JSContext *cx, Baselin
     if (stub->numOptimizedStubs() >= ICSetElem_Fallback::MAX_OPTIMIZED_STUBS) {
         // TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
         // But for now we just bail.
         return true;
     }
 
     // Try to generate new stubs.
     if (obj->isNative() &&
+        !obj->is<TypedArrayObject>() &&
         index.isInt32() && index.toInt32() >= 0 &&
         !rhs.isMagic(JS_ELEMENTS_HOLE))
     {
-        JS_ASSERT(!obj->is<TypedArrayObject>());
-
         bool addingCase;
         size_t protoDepth;
 
         if (CanOptimizeDenseSetElem(cx, obj, index.toInt32(), oldShape, oldCapacity, oldInitLength,
                                     &addingCase, &protoDepth))
         {
             RootedShape shape(cx, obj->lastProperty());
             RootedTypeObject type(cx, obj->getType(cx));
@@ -6088,17 +6082,17 @@ TryAttachNativeGetPropStub(JSContext *cx
     RootedShape shape(cx);
     RootedObject holder(cx);
     if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape, &isDOMProxy,
                                     &domProxyShadowsResult, &domProxyHasGeneration))
     {
         return false;
     }
 
-    if (!isDOMProxy && !obj->isNative() && !obj->is<TypedArrayObject>())
+    if (!isDOMProxy && !obj->isNative())
         return true;
 
     bool isCallProp = (JSOp(*pc) == JSOP_CALLPROP);
 
     ICStub *monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
     if (!isDOMProxy && IsCacheableGetPropReadSlot(obj, holder, shape)) {
         bool isFixedSlot;
         uint32_t offset;
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -5917,22 +5917,16 @@ IonBuilder::maybeInsertResume()
     current->add(ins);
 
     return resumeAfter(ins);
 }
 
 static bool
 ClassHasEffectlessLookup(const Class *clasp, PropertyName *name)
 {
-    if (IsTypedArrayClass(clasp)) {
-        // Typed arrays have a lookupGeneric hook, but it only handles
-        // integers, not names.
-        JS_ASSERT(name);
-        return true;
-    }
     return clasp->isNative() && !clasp->ops.lookupGeneric;
 }
 
 static bool
 ClassHasResolveHook(CompileCompartment *comp, const Class *clasp, PropertyName *name)
 {
     // While arrays do not have resolve hooks, the types of their |length|
     // properties are not reflected in type information, so pretend there is a
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -2928,18 +2928,19 @@ jit::ElementAccessIsDenseNative(MDefinit
 
     if (id->type() != MIRType_Int32 && id->type() != MIRType_Double)
         return false;
 
     types::TemporaryTypeSet *types = obj->resultTypeSet();
     if (!types)
         return false;
 
+    // Typed arrays are native classes but do not have dense elements.
     const Class *clasp = types->getKnownClass();
-    return clasp && clasp->isNative();
+    return clasp && clasp->isNative() && !IsTypedArrayClass(clasp);
 }
 
 bool
 jit::ElementAccessIsTypedArray(MDefinition *obj, MDefinition *id,
                                ScalarTypeDescr::Type *arrayType)
 {
     if (obj->mightBeType(MIRType_String))
         return false;
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2710,18 +2710,18 @@ LookupResult(JSContext *cx, HandleObject
             Rooted<PropertyDescriptor> desc(cx);
             if (!Proxy::getPropertyDescriptor(cx, obj2, id, &desc, 0))
                 return false;
             if (!desc.isShared()) {
                 vp.set(desc.value());
                 return true;
             }
         }
-    } else if (IsImplicitDenseElement(shape)) {
-        vp.set(obj2->getDenseElement(JSID_TO_INT(id)));
+    } else if (IsImplicitDenseOrTypedArrayElement(shape)) {
+        vp.set(obj2->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
         return true;
     } else {
         /* Peek at the native property's slot value, without doing a Get. */
         if (shape->hasSlot()) {
             vp.set(obj2->nativeGetSlot(shape->slot()));
             return true;
         }
     }
@@ -2859,19 +2859,31 @@ JS_AlreadyHasOwnPropertyById(JSContext *
         RootedShape prop(cx);
 
         if (!LookupPropertyById(cx, obj, id, 0, &obj2, &prop))
             return false;
         *foundp = (obj == obj2);
         return true;
     }
 
-    if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
-        *foundp = true;
-        return true;
+    // Check for an existing native property on the objct. Be careful not to
+    // call any lookup or resolve hooks.
+
+    if (JSID_IS_INT(id)) {
+        uint32_t index = JSID_TO_INT(id);
+
+        if (obj->containsDenseElement(index)) {
+            *foundp = true;
+            return true;
+        }
+
+        if (obj->is<TypedArrayObject>() && index < obj->as<TypedArrayObject>().length()) {
+            *foundp = true;
+            return true;
+        }
     }
 
     *foundp = obj->nativeContains(cx, id);
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS_AlreadyHasOwnElement(JSContext *cx, HandleObject obj, uint32_t index, bool *foundp)
@@ -3252,19 +3264,19 @@ GetPropertyDescriptorById(JSContext *cx,
         return false;
 
     desc.clear();
     if (!shape || (own && obj != obj2))
         return true;
 
     desc.object().set(obj2);
     if (obj2->isNative()) {
-        if (IsImplicitDenseElement(shape)) {
+        if (IsImplicitDenseOrTypedArrayElement(shape)) {
             desc.setEnumerable();
-            desc.value().set(obj2->getDenseElement(JSID_TO_INT(id)));
+            desc.value().set(obj2->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
         } else {
             desc.setAttributes(shape->attributes());
             desc.setGetter(shape->getter());
             desc.setSetter(shape->setter());
             JS_ASSERT(desc.value().isUndefined());
             if (shape->hasSlot())
                 desc.value().set(obj2->nativeGetSlot(shape->slot()));
         }
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -818,16 +818,18 @@ js::ObjectMayHaveExtraIndexedProperties(
          * return true.
          */
         if (!obj->isNative())
             return true;
         if (obj->isIndexed())
             return true;
         if (obj->getDenseInitializedLength() > 0)
             return true;
+        if (obj->is<TypedArrayObject>())
+            return true;
     }
 
     return false;
 }
 
 const Class ArrayObject::class_ = {
     "Array",
     JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
@@ -2035,17 +2037,17 @@ js::array_push(JSContext *cx, unsigned a
 
     /* Steps 2-3. */
     uint32_t length;
     if (!GetLengthProperty(cx, obj, &length))
         return false;
 
     /* Fast path for native objects with dense elements. */
     do {
-        if (!obj->isNative())
+        if (!obj->isNative() || obj->is<TypedArrayObject>())
             break;
 
         if (obj->is<ArrayObject>() && !obj->as<ArrayObject>().lengthIsWritable())
             break;
 
         if (ObjectMayHaveExtraIndexedProperties(obj))
             break;
 
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2129,17 +2129,20 @@ types::UseNewTypeForInitializer(JSScript
 {
     return UseNewTypeForInitializer(script, pc, JSCLASS_CACHED_PROTO_KEY(clasp));
 }
 
 static inline bool
 ClassCanHaveExtraProperties(const Class *clasp)
 {
     JS_ASSERT(clasp->resolve);
-    return clasp->resolve != JS_ResolveStub || clasp->ops.lookupGeneric || clasp->ops.getGeneric;
+    return clasp->resolve != JS_ResolveStub
+        || clasp->ops.lookupGeneric
+        || clasp->ops.getGeneric
+        || IsTypedArrayClass(clasp);
 }
 
 static inline bool
 PrototypeHasIndexedProperty(CompilerConstraintList *constraints, JSObject *obj)
 {
     do {
         TypeObjectKey *type = TypeObjectKey::get(obj);
         if (ClassCanHaveExtraProperties(type->clasp()))
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -126,23 +126,32 @@ Enumerate(JSContext *cx, HandleObject po
 
     return true;
 }
 
 static bool
 EnumerateNativeProperties(JSContext *cx, HandleObject pobj, unsigned flags, IdSet &ht,
                           AutoIdVector *props)
 {
-    /* Collect any elements from this object. */
+    /* Collect any dense elements from this object. */
     size_t initlen = pobj->getDenseInitializedLength();
     const Value *vp = pobj->getDenseElements();
     for (size_t i = 0; i < initlen; ++i, ++vp) {
         if (!vp->isMagic(JS_ELEMENTS_HOLE)) {
             /* Dense arrays never get so large that i would not fit into an integer id. */
-            if (!Enumerate(cx, pobj, INT_TO_JSID(i), true, flags, ht, props))
+            if (!Enumerate(cx, pobj, INT_TO_JSID(i), /* enumerable = */ true, flags, ht, props))
+                return false;
+        }
+    }
+
+    /* Collect any typed array elements from this object. */
+    if (pobj->is<TypedArrayObject>()) {
+        size_t len = pobj->as<TypedArrayObject>().length();
+        for (size_t i = 0; i < len; i++) {
+            if (!Enumerate(cx, pobj, INT_TO_JSID(i), /* enumerable = */ true, flags, ht, props))
                 return false;
         }
     }
 
     size_t initialLength = props->length();
 
     /* Collect all unique properties from this object's scope. */
     Shape::Range<NoGC> r(pobj->lastProperty());
@@ -618,16 +627,17 @@ js::GetIterator(JSContext *cx, HandleObj
              * allows us to re-use a previous iterator object that is not
              * currently active.
              */
             {
                 JSObject *pobj = obj;
                 do {
                     if (!pobj->isNative() ||
                         !pobj->hasEmptyElements() ||
+                        pobj->is<TypedArrayObject>() ||
                         pobj->hasUncacheableProto() ||
                         pobj->getOps()->enumerate ||
                         pobj->getClass()->enumerate != JS_EnumerateStub ||
                         pobj->nativeContainsPure(cx->names().iteratorIntrinsic))
                     {
                         shapes.clear();
                         goto miss;
                     }
@@ -1048,17 +1058,17 @@ SuppressDeletedPropertyHelper(JSContext 
                         RootedValue idv(cx, StringValue(*idp));
                         if (!ValueToId<CanGC>(cx, idv, &id))
                             return false;
                         if (!JSObject::lookupGeneric(cx, proto, id, &obj2, &prop))
                             return false;
                         if (prop) {
                             unsigned attrs;
                             if (obj2->isNative())
-                                attrs = GetShapeAttributes(prop);
+                                attrs = GetShapeAttributes(obj2, prop);
                             else if (!JSObject::getGenericAttributes(cx, obj2, id, &attrs))
                                 return false;
 
                             if (attrs & JSPROP_ENUMERATE)
                                 continue;
                         }
                     }
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -231,17 +231,17 @@ js::GetOwnPropertyDescriptor(JSContext *
         return false;
     if (!shape) {
         desc.object().set(nullptr);
         return true;
     }
 
     bool doGet = true;
     if (pobj->isNative()) {
-        desc.setAttributes(GetShapeAttributes(shape));
+        desc.setAttributes(GetShapeAttributes(pobj, shape));
         if (desc.hasGetterOrSetterObject()) {
             doGet = false;
             if (desc.hasGetterObject())
                 desc.setGetterObject(shape->getterObject());
             if (desc.hasSetterObject())
                 desc.setSetterObject(shape->setterObject());
         }
     } else {
@@ -594,18 +594,18 @@ DefinePropertyOnObject(JSContext *cx, Ha
          shapeAccessorDescriptor = false,
          shapeWritable = true,
          shapeConfigurable = true,
          shapeEnumerable = true,
          shapeHasDefaultGetter = true,
          shapeHasDefaultSetter = true,
          shapeHasGetterValue = false,
          shapeHasSetterValue = false;
-    uint8_t shapeAttributes = JSPROP_ENUMERATE;
-    if (!IsImplicitDenseElement(shape)) {
+    uint8_t shapeAttributes = GetShapeAttributes(obj, shape);
+    if (!IsImplicitDenseOrTypedArrayElement(shape)) {
         shapeDataDescriptor = shape->isDataDescriptor();
         shapeAccessorDescriptor = shape->isAccessorDescriptor();
         shapeWritable = shape->writable();
         shapeConfigurable = shape->configurable();
         shapeEnumerable = shape->enumerable();
         shapeHasDefaultGetter = shape->hasDefaultGetter();
         shapeHasDefaultSetter = shape->hasDefaultSetter();
         shapeHasGetterValue = shape->hasGetterValue();
@@ -636,18 +636,18 @@ DefinePropertyOnObject(JSContext *cx, Ha
         } else {
             /*
              * Determine the current value of the property once, if the current
              * value might actually need to be used or preserved later.  NB: we
              * guard on whether the current property is a data descriptor to
              * avoid calling a getter; we won't need the value if it's not a
              * data descriptor.
              */
-            if (IsImplicitDenseElement(shape)) {
-                v = obj->getDenseElement(JSID_TO_INT(id));
+            if (IsImplicitDenseOrTypedArrayElement(shape)) {
+                v = obj->getDenseOrTypedArrayElement(JSID_TO_INT(id));
             } else if (shape->isDataDescriptor()) {
                 /*
                  * We must rule out a non-configurable js::PropertyOp-guarded
                  * property becoming a writable unguarded data property, since
                  * such a property can have its value changed to one the getter
                  * and setter preclude.
                  *
                  * A desc lacking writable but with value is a data descriptor
@@ -778,18 +778,18 @@ DefinePropertyOnObject(JSContext *cx, Ha
     if (desc.isGenericDescriptor()) {
         unsigned changed = 0;
         if (desc.hasConfigurable())
             changed |= JSPROP_PERMANENT;
         if (desc.hasEnumerable())
             changed |= JSPROP_ENUMERATE;
 
         attrs = (shapeAttributes & ~changed) | (desc.attributes() & changed);
-        getter = IsImplicitDenseElement(shape) ? JS_PropertyStub : shape->getter();
-        setter = IsImplicitDenseElement(shape) ? JS_StrictPropertyStub : shape->setter();
+        getter = IsImplicitDenseOrTypedArrayElement(shape) ? JS_PropertyStub : shape->getter();
+        setter = IsImplicitDenseOrTypedArrayElement(shape) ? JS_StrictPropertyStub : shape->setter();
     } else if (desc.isDataDescriptor()) {
         unsigned unchanged = 0;
         if (!desc.hasConfigurable())
             unchanged |= JSPROP_PERMANENT;
         if (!desc.hasEnumerable())
             unchanged |= JSPROP_ENUMERATE;
         /* Watch out for accessor -> data transformations here. */
         if (!desc.hasWritable() && shapeDataDescriptor)
@@ -1079,17 +1079,17 @@ JSObject::sealOrFreeze(JSContext *cx, Ha
 
     AutoIdVector props(cx);
     if (!GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props))
         return false;
 
     /* preventExtensions must sparsify dense objects, so we can assign to holes without checks. */
     JS_ASSERT_IF(obj->isNative(), obj->getDenseCapacity() == 0);
 
-    if (obj->isNative() && !obj->inDictionaryMode()) {
+    if (obj->isNative() && !obj->inDictionaryMode() && !obj->is<TypedArrayObject>()) {
         /*
          * Seal/freeze non-dictionary objects by constructing a new shape
          * hierarchy mirroring the original one, which can be shared if many
          * objects with the same structure are sealed/frozen. If we use the
          * generic path below then any non-empty object will be converted to
          * dictionary mode.
          */
         RootedShape last(cx, EmptyShape::getInitialShape(cx, obj->getClass(),
@@ -3061,16 +3061,17 @@ ReallocateElements(ThreadSafeContext *cx
     return static_cast<js::ObjectElements *>(cx->realloc_(oldHeader, oldCount * sizeof(HeapSlot),
                                                           newCount * sizeof(HeapSlot)));
 }
 
 bool
 JSObject::growElements(ThreadSafeContext *cx, uint32_t newcap)
 {
     JS_ASSERT(nonProxyIsExtensible());
+    JS_ASSERT(canHaveNonEmptyElements());
 
     /*
      * When an object with CAPACITY_DOUBLING_MAX or fewer elements needs to
      * grow, double its capacity, to add N elements in amortized O(N) time.
      *
      * Above this limit, grow by 12.5% each time. Speed is still amortized
      * O(N), with a higher constant factor, and we waste less space.
      */
@@ -3127,16 +3128,17 @@ JSObject::growElements(ThreadSafeContext
 
     return true;
 }
 
 void
 JSObject::shrinkElements(ThreadSafeContext *cx, uint32_t newcap)
 {
     JS_ASSERT(cx->isThreadLocal(this));
+    JS_ASSERT(canHaveNonEmptyElements());
 
     uint32_t oldcap = getDenseCapacity();
     JS_ASSERT(newcap <= oldcap);
 
     /* Don't shrink elements below the minimum capacity. */
     if (oldcap <= SLOT_CAPACITY_MIN || !hasDynamicElements())
         return;
 
@@ -3700,17 +3702,18 @@ DefinePropertyOrElement(typename Executi
                         unsigned attrs, unsigned flags, HandleValue value,
                         bool callSetterAfterwards, bool setterIsStrict)
 {
     /* Use dense storage for new indexed properties where possible. */
     if (JSID_IS_INT(id) &&
         getter == JS_PropertyStub &&
         setter == JS_StrictPropertyStub &&
         attrs == JSPROP_ENUMERATE &&
-        (!obj->isIndexed() || !obj->nativeContainsPure(id)))
+        (!obj->isIndexed() || !obj->nativeContainsPure(id)) &&
+        !obj->is<TypedArrayObject>())
     {
         uint32_t index = JSID_TO_INT(id);
         bool definesPast;
         if (!WouldDefinePastNonwritableLength(cx, obj, index, setterIsStrict, &definesPast))
             return false;
         if (definesPast)
             return true;
 
@@ -3750,16 +3753,23 @@ DefinePropertyOrElement(typename Executi
             bool definesPast;
             if (!WouldDefinePastNonwritableLength(cx, arr, index, setterIsStrict, &definesPast))
                 return false;
             if (definesPast)
                 return true;
         }
     }
 
+    // Don't define new indexed properties on typed arrays.
+    if (obj->is<TypedArrayObject>()) {
+        double index;
+        if (IsTypedArrayIndex(id, &index))
+            return true;
+    }
+
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
 
     RootedShape shape(cx, JSObject::putProperty<mode>(cx, obj, id, getter, setter,
                                                       SHAPE_INVALID_SLOT, attrs, flags));
     if (!shape)
         return false;
 
     if (!UpdateShapeTypeAndValue<mode>(cx, obj, shape, value))
@@ -3821,17 +3831,21 @@ js::DefineNativeProperty(ExclusiveContex
     if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
         /*
          * If we are defining a getter whose setter was already defined, or
          * vice versa, finish the job via obj->changeProperty.
          */
         if (!NativeLookupOwnProperty(cx, obj, id, flags, &shape))
             return false;
         if (shape) {
-            if (IsImplicitDenseElement(shape)) {
+            if (IsImplicitDenseOrTypedArrayElement(shape)) {
+                if (obj->is<TypedArrayObject>()) {
+                    /* Ignore getter/setter properties added to typed arrays. */
+                    return true;
+                }
                 if (!JSObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id)))
                     return false;
                 shape = obj->nativeLookup(cx, id);
             }
             if (shape->isAccessorDescriptor()) {
                 shape = JSObject::changeProperty<SequentialExecution>(cx, obj, shape, attrs,
                                                                       JSPROP_GETTER | JSPROP_SETTER,
                                                                       (attrs & JSPROP_GETTER)
@@ -3948,17 +3962,17 @@ CallResolveOp(JSContext *cx, HandleObjec
     } else {
         if (!resolve(cx, obj, id))
             return false;
 
         objp.set(obj);
     }
 
     if (JSID_IS_INT(id) && objp->containsDenseElement(JSID_TO_INT(id))) {
-        MarkDenseElementFound<CanGC>(propp);
+        MarkDenseOrTypedArrayElementFound<CanGC>(propp);
         return true;
     }
 
     Shape *shape;
     if (!objp->nativeEmpty() && (shape = objp->nativeLookup(cx, id)))
         propp.set(shape);
     else
         objp.set(nullptr);
@@ -3974,21 +3988,36 @@ LookupOwnPropertyWithFlagsInline(Exclusi
                                  unsigned flags,
                                  typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
                                  typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp,
                                  bool *donep)
 {
     // Check for a native dense element.
     if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
         objp.set(obj);
-        MarkDenseElementFound<allowGC>(propp);
+        MarkDenseOrTypedArrayElementFound<allowGC>(propp);
         *donep = true;
         return true;
     }
 
+    // Check for a typed array element. Integer lookups always finish here
+    // so that integer properties on the prototype are ignored even for out
+    // of bounds accesses.
+    if (obj->template is<TypedArrayObject>()) {
+        double index;
+        if (IsTypedArrayIndex(id, &index)) {
+            if (index >= 0 && index < obj->template as<TypedArrayObject>().length()) {
+                objp.set(obj);
+                MarkDenseOrTypedArrayElementFound<allowGC>(propp);
+            }
+            *donep = true;
+            return true;
+        }
+    }
+
     // Check for a native property.
     if (Shape *shape = obj->nativeLookup(cx, id)) {
         objp.set(obj);
         propp.set(shape);
         *donep = true;
         return true;
     }
 
@@ -4518,18 +4547,18 @@ GetPropertyHelperInline(JSContext *cx,
         HandleObject receiverHandle = MaybeRooted<JSObject*, allowGC>::toHandle(receiver);
         HandleId idHandle = MaybeRooted<jsid, allowGC>::toHandle(id);
         MutableHandleValue vpHandle = MaybeRooted<Value, allowGC>::toMutableHandle(vp);
         return obj2->template is<ProxyObject>()
                ? Proxy::get(cx, obj2Handle, receiverHandle, idHandle, vpHandle)
                : JSObject::getGeneric(cx, obj2Handle, obj2Handle, idHandle, vpHandle);
     }
 
-    if (IsImplicitDenseElement(shape)) {
-        vp.set(obj2->getDenseElement(JSID_TO_INT(id)));
+    if (IsImplicitDenseOrTypedArrayElement(shape)) {
+        vp.set(obj2->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
         return true;
     }
 
     /* This call site is hot -- use the always-inlined variant of NativeGet(). */
     if (!NativeGetInline<allowGC>(cx, obj, receiver, obj2, shape, vp))
         return false;
 
     return true;
@@ -4552,29 +4581,42 @@ baseops::GetPropertyNoGC(JSContext *cx, 
 static MOZ_ALWAYS_INLINE bool
 LookupPropertyPureInline(JSObject *obj, jsid id, JSObject **objp, Shape **propp)
 {
     if (!obj->isNative())
         return false;
 
     JSObject *current = obj;
     while (true) {
-        /* Search for a native dense element or property. */
-        {
-            if (JSID_IS_INT(id) && current->containsDenseElement(JSID_TO_INT(id))) {
-                *objp = current;
-                MarkDenseElementFound<NoGC>(propp);
+        /* Search for a native dense element, typed array element, or property. */
+
+        if (JSID_IS_INT(id) && current->containsDenseElement(JSID_TO_INT(id))) {
+            *objp = current;
+            MarkDenseOrTypedArrayElementFound<NoGC>(propp);
+            return true;
+        }
+
+        if (current->is<TypedArrayObject>()) {
+            double index;
+            if (IsTypedArrayIndex(id, &index)) {
+                if (index >= 0 && index < obj->as<TypedArrayObject>().length()) {
+                    *objp = current;
+                    MarkDenseOrTypedArrayElementFound<NoGC>(propp);
+                } else {
+                    *objp = nullptr;
+                    *propp = nullptr;
+                }
                 return true;
             }
-
-            if (Shape *shape = current->nativeLookupPure(id)) {
-                *objp = current;
-                *propp = shape;
-                return true;
-            }
+        }
+
+        if (Shape *shape = current->nativeLookupPure(id)) {
+            *objp = current;
+            *propp = shape;
+            return true;
         }
 
         /* Fail if there's a resolve hook. */
         if (current->getClass()->resolve != JS_ResolveStub)
             return false;
 
         JSObject *proto = current->getProto();
 
@@ -4627,38 +4669,16 @@ IdIsLength(ThreadSafeContext *cx, jsid i
  *
  *  - Any object in the lookup chain has a non-stub resolve hook.
  *  - Any object in the lookup chain is non-native.
  *  - The property has a getter.
  */
 bool
 js::GetPropertyPure(ThreadSafeContext *cx, JSObject *obj, jsid id, Value *vp)
 {
-    /* Typed arrays are not native, so we fast-path them here. */
-    if (obj->is<TypedArrayObject>()) {
-        TypedArrayObject *tarr = &obj->as<TypedArrayObject>();
-
-        if (JSID_IS_INT(id)) {
-            uint32_t index = JSID_TO_INT(id);
-            if (index < tarr->length()) {
-                MutableHandleValue vpHandle = MutableHandleValue::fromMarkedLocation(vp);
-                tarr->copyTypedArrayElement(index, vpHandle);
-                return true;
-            }
-            return false;
-        }
-
-        if (IdIsLength(cx, id)) {
-            vp->setNumber(tarr->length());
-            return true;
-        }
-
-        return false;
-    }
-
     /* Deal with native objects. */
     JSObject *obj2;
     Shape *shape;
     if (!LookupPropertyPureInline(obj, id, &obj2, &shape))
         return false;
 
     if (!shape) {
         /* Fail if we have a non-stub class op hooks. */
@@ -4668,25 +4688,32 @@ js::GetPropertyPure(ThreadSafeContext *c
         if (obj->getOps()->getElement)
             return false;
 
         /* Vanilla native object, return undefined. */
         vp->setUndefined();
         return true;
     }
 
-    if (IsImplicitDenseElement(shape)) {
-        *vp = obj2->getDenseElement(JSID_TO_INT(id));
+    if (IsImplicitDenseOrTypedArrayElement(shape)) {
+        *vp = obj2->getDenseOrTypedArrayElement(JSID_TO_INT(id));
         return true;
     }
 
-    /* Special case 'length' on Array. */
-    if (obj->is<ArrayObject>() && IdIsLength(cx, id)) {
-        vp->setNumber(obj->as<ArrayObject>().length());
-        return true;
+    /* Special case 'length' on Array and TypedArray. */
+    if (IdIsLength(cx, id)) {
+        if (obj->is<ArrayObject>()) {
+            vp->setNumber(obj->as<ArrayObject>().length());
+            return true;
+        }
+
+        if (obj->is<TypedArrayObject>()) {
+            vp->setNumber(obj->as<TypedArrayObject>().length());
+            return true;
+        }
     }
 
     return NativeGetPureInline(obj2, shape, vp);
 }
 
 static bool
 MOZ_ALWAYS_INLINE
 GetElementPure(ThreadSafeContext *cx, JSObject *obj, uint32_t index, Value *vp)
@@ -4931,17 +4958,17 @@ baseops::SetPropertyHelper(typename Exec
      * prototypes; or shape is non-null, meaning id was found directly in pobj.
      */
     unsigned attrs = JSPROP_ENUMERATE;
     unsigned flags = 0;
     const Class *clasp = obj->getClass();
     PropertyOp getter = clasp->getProperty;
     StrictPropertyOp setter = clasp->setProperty;
 
-    if (IsImplicitDenseElement(shape)) {
+    if (IsImplicitDenseOrTypedArrayElement(shape)) {
         /* ES5 8.12.4 [[Put]] step 2, for a dense data property on pobj. */
         if (pobj != obj)
             shape = nullptr;
     } else if (shape) {
         /* ES5 8.12.4 [[Put]] step 2. */
         if (shape->isAccessorDescriptor()) {
             if (shape->hasDefaultSetter()) {
                 /* Bail out of parallel execution if we are strict to throw. */
@@ -5008,33 +5035,31 @@ baseops::SetPropertyHelper(typename Exec
             /*
              * Forget we found the proto-property now that we've copied any
              * needed member values.
              */
             shape = nullptr;
         }
     }
 
-    if (IsImplicitDenseElement(shape)) {
+    if (IsImplicitDenseOrTypedArrayElement(shape)) {
         uint32_t index = JSID_TO_INT(id);
         bool definesPast;
         if (!WouldDefinePastNonwritableLength(cxArg, obj, index, strict, &definesPast))
             return false;
         if (definesPast) {
             /* Bail out of parallel execution if we are strict to throw. */
             if (mode == ParallelExecution)
                 return !strict;
             return true;
         }
 
         if (mode == ParallelExecution)
-            obj->setDenseElementIfHasType(index, vp);
-        else
-            obj->setDenseElementWithType(cxArg->asJSContext(), index, vp);
-        return true;
+            return obj->setDenseOrTypedArrayElementIfHasType(cxArg, index, vp);
+        return obj->setDenseOrTypedArrayElementWithType(cxArg->asJSContext(), index, vp);
     }
 
     if (obj->is<ArrayObject>() && id == NameToId(cxArg->names().length)) {
         Rooted<ArrayObject*> arr(cxArg, &obj->as<ArrayObject>());
         return ArraySetLength<mode>(cxArg, arr, id, attrs, vp, strict);
     }
 
     if (!shape) {
@@ -5109,30 +5134,36 @@ baseops::GetAttributes(JSContext *cx, Ha
         return false;
     if (!shape) {
         *attrsp = 0;
         return true;
     }
     if (!nobj->isNative())
         return JSObject::getGenericAttributes(cx, nobj, id, attrsp);
 
-    *attrsp = GetShapeAttributes(shape);
+    *attrsp = GetShapeAttributes(nobj, shape);
     return true;
 }
 
 bool
 baseops::SetAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
 {
     RootedObject nobj(cx);
     RootedShape shape(cx);
     if (!baseops::LookupProperty<CanGC>(cx, obj, id, &nobj, &shape))
         return false;
     if (!shape)
         return true;
-    if (nobj->isNative() && IsImplicitDenseElement(shape)) {
+    if (nobj->isNative() && IsImplicitDenseOrTypedArrayElement(shape)) {
+        if (nobj->is<TypedArrayObject>()) {
+            if (*attrsp == (JSPROP_ENUMERATE | JSPROP_PERMANENT))
+                return true;
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_SET_ARRAY_ATTRS);
+            return false;
+        }
         if (!JSObject::sparsifyDenseElement(cx, nobj, JSID_TO_INT(id)))
             return false;
         shape = obj->nativeLookup(cx, id);
     }
     if (nobj->isNative()) {
         if (!JSObject::changePropertyAttributes(cx, nobj, shape, *attrsp))
             return false;
         if (*attrsp & JSPROP_READONLY)
@@ -5155,17 +5186,23 @@ baseops::DeleteGeneric(JSContext *cx, Ha
          * If no property, or the property comes from a prototype, call the
          * class's delProperty hook, passing succeeded as the result parameter.
          */
         return CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded);
     }
 
     GCPoke(cx->runtime());
 
-    if (IsImplicitDenseElement(shape)) {
+    if (IsImplicitDenseOrTypedArrayElement(shape)) {
+        if (obj->is<TypedArrayObject>()) {
+            // Don't delete elements from typed arrays.
+            *succeeded = false;
+            return true;
+        }
+
         if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded))
             return false;
         if (!succeeded)
             return true;
 
         obj->setDenseElementHole(cx, JSID_TO_INT(id));
         return js_SuppressDeletedProperty(cx, obj, id);
     }
@@ -5232,17 +5269,17 @@ js::WatchGuts(JSContext *cx, JS::HandleO
     }
 
     return wpmap->watch(cx, obj, id, js::WatchHandler, callable);
 }
 
 bool
 baseops::Watch(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable)
 {
-    if (!obj->isNative()) {
+    if (!obj->isNative() || obj->is<TypedArrayObject>()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH,
                              obj->getClass()->name);
         return false;
     }
 
     return WatchGuts(cx, obj, id, callable);
 }
 
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -667,16 +667,22 @@ class JSObject : public js::ObjectImpl
     inline void setDenseElementWithType(js::ExclusiveContext *cx, uint32_t index,
                                         const js::Value &val);
     inline void initDenseElementWithType(js::ExclusiveContext *cx, uint32_t index,
                                          const js::Value &val);
     inline void setDenseElementHole(js::ExclusiveContext *cx, uint32_t index);
     static inline void removeDenseElementForSparseIndex(js::ExclusiveContext *cx,
                                                         js::HandleObject obj, uint32_t index);
 
+    inline js::Value getDenseOrTypedArrayElement(uint32_t idx);
+    inline bool setDenseOrTypedArrayElementIfHasType(js::ThreadSafeContext *cx, uint32_t index,
+                                                     const js::Value &val);
+    inline bool setDenseOrTypedArrayElementWithType(js::ExclusiveContext *cx, uint32_t index,
+                                                    const js::Value &val);
+
     void copyDenseElements(uint32_t dstStart, const js::Value *src, uint32_t count) {
         JS_ASSERT(dstStart + count <= getDenseCapacity());
         JSRuntime *rt = runtimeFromMainThread();
         if (JS::IsIncrementalBarrierNeeded(rt)) {
             JS::Zone *zone = this->zone();
             for (uint32_t i = 0; i < count; ++i)
                 elements[dstStart + i].set(zone, this, js::HeapSlot::Element, dstStart + i, src[i]);
         } else {
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -348,16 +348,43 @@ JSObject::ensureDenseElements(js::Exclus
 inline JSObject::EnsureDenseResult
 JSObject::ensureDenseElementsPreservePackedFlag(js::ThreadSafeContext *cx, uint32_t index,
                                                 uint32_t extra)
 {
     JS_ASSERT(!writeToIndexWouldMarkNotPacked(index));
     return ensureDenseElementsNoPackedCheck(cx, index, extra);
 }
 
+inline js::Value
+JSObject::getDenseOrTypedArrayElement(uint32_t idx)
+{
+    if (is<js::TypedArrayObject>())
+        return as<js::TypedArrayObject>().getElement(idx);
+    return getDenseElement(idx);
+}
+
+inline bool
+JSObject::setDenseOrTypedArrayElementIfHasType(js::ThreadSafeContext *cx, uint32_t index,
+                                               const js::Value &val)
+{
+    if (is<js::TypedArrayObject>())
+        return as<js::TypedArrayObject>().setElement(cx, index, val);
+    return setDenseElementIfHasType(index, val);
+}
+
+inline bool
+JSObject::setDenseOrTypedArrayElementWithType(js::ExclusiveContext *cx, uint32_t index,
+                                              const js::Value &val)
+{
+    if (is<js::TypedArrayObject>())
+        return as<js::TypedArrayObject>().setElement(cx, index, val);
+    setDenseElementWithType(cx, index, val);
+    return true;
+}
+
 /* static */ inline bool
 JSObject::setSingletonType(js::ExclusiveContext *cx, js::HandleObject obj)
 {
     if (!cx->typeInferenceEnabled())
         return true;
 
     JS_ASSERT_IF(cx->isJSContext(),
                  !IsInsideNursery(cx->asJSContext()->runtime(), obj.get()));
--- a/js/src/vm/ObjectImpl.cpp
+++ b/js/src/vm/ObjectImpl.cpp
@@ -148,16 +148,29 @@ PropDesc::wrapInto(JSContext *cx, Handle
 }
 
 static const ObjectElements emptyElementsHeader(0, 0);
 
 /* Objects with no elements share one empty set of elements. */
 HeapSlot *const js::emptyObjectElements =
     reinterpret_cast<HeapSlot *>(uintptr_t(&emptyElementsHeader) + sizeof(ObjectElements));
 
+#ifdef DEBUG
+
+bool
+ObjectImpl::canHaveNonEmptyElements()
+{
+    JSObject *obj = static_cast<JSObject *>(this);
+    if (isNative())
+        return !obj->is<TypedArrayObject>();
+    return obj->is<ArrayBufferObject>() || obj->is<SharedArrayBufferObject>();
+}
+
+#endif // DEBUG
+
 /* static */ bool
 ObjectElements::ConvertElementsToDoubles(JSContext *cx, uintptr_t elementsPtr)
 {
     /*
      * This function is infallible, but has a fallible interface so that it can
      * be called directly from Ion code. Only arrays can have their dense
      * elements converted to doubles, and arrays never have empty elements.
      */
--- a/js/src/vm/ObjectImpl.h
+++ b/js/src/vm/ObjectImpl.h
@@ -679,17 +679,18 @@ class ArrayBufferObject;
  */
 template <ExecutionMode mode>
 extern bool
 ArraySetLength(typename ExecutionModeTraits<mode>::ContextType cx,
                Handle<ArrayObject*> obj, HandleId id,
                unsigned attrs, HandleValue value, bool setterIsStrict);
 
 /*
- * Elements header used for all native objects. The elements component of such
+ * Elements header used for all objects other than non-native objects (except
+ * for ArrayBufferObjects!!!) and typed arrays. The elements component of such
  * objects offers an efficient representation for all or some of the indexed
  * properties of the object, using a flat array of Values rather than a shape
  * hierarchy stored in the object's slots. This structure is immediately
  * followed by an array of elements, with the elements member in an object
  * pointing to the beginning of that array (the end of this structure).
  * See below for usage of this structure.
  *
  * The sets of properties represented by an object's elements and slots
@@ -1452,17 +1453,24 @@ class ObjectImpl : public gc::BarrieredC
 
     inline HeapSlot *fixedElements() const {
         static_assert(2 * sizeof(Value) == sizeof(ObjectElements),
                       "when elements are stored inline, the first two "
                       "slots will hold the ObjectElements header");
         return &fixedSlots()[2];
     }
 
-    void setFixedElements() { this->elements = fixedElements(); }
+#ifdef DEBUG
+    bool canHaveNonEmptyElements();
+#endif
+
+    void setFixedElements() {
+        JS_ASSERT(canHaveNonEmptyElements());
+        this->elements = fixedElements();
+    }
 
     inline bool hasDynamicElements() const {
         /*
          * Note: for objects with zero fixed slots this could potentially give
          * a spurious 'true' result, if the end of this object is exactly
          * aligned with the end of its arena and dynamic slots are allocated
          * immediately afterwards. Such cases cannot occur for dense arrays
          * (which have at least two fixed slots) and can only result in a leak.
--- a/js/src/vm/OldDebugAPI.cpp
+++ b/js/src/vm/OldDebugAPI.cpp
@@ -313,17 +313,17 @@ JS_SetWatchPoint(JSContext *cx, JSObject
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH_PROP);
         return false;
     } else {
         RootedValue val(cx, IdToValue(id));
         if (!ValueToId<CanGC>(cx, val, &propid))
             return false;
     }
 
-    if (!obj->isNative()) {
+    if (!obj->isNative() || obj->is<TypedArrayObject>()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH,
                              obj->getClass()->name);
         return false;
     }
 
     /*
      * Use sparse indexes for watched objects, as dense elements can be written
      * to without checking the watchpoint map.
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -409,32 +409,31 @@ js::intrinsic_UnsafePutElements(JSContex
     }
 
     for (uint32_t base = 0; base < args.length(); base += 3) {
         uint32_t arri = base;
         uint32_t idxi = base+1;
         uint32_t elemi = base+2;
 
         JS_ASSERT(args[arri].isObject());
-        JS_ASSERT(args[arri].toObject().isNative() ||
-                  args[arri].toObject().is<TypedArrayObject>());
+        JS_ASSERT(args[arri].toObject().isNative());
         JS_ASSERT(args[idxi].isInt32());
 
         RootedObject arrobj(cx, &args[arri].toObject());
         uint32_t idx = args[idxi].toInt32();
 
-        if (arrobj->isNative()) {
-            JS_ASSERT(idx < arrobj->getDenseInitializedLength());
-            arrobj->setDenseElementWithType(cx, idx, args[elemi]);
-        } else {
+        if (arrobj->is<TypedArrayObject>()) {
             JS_ASSERT(idx < arrobj->as<TypedArrayObject>().length());
             RootedValue tmp(cx, args[elemi]);
             // XXX: Always non-strict.
             if (!JSObject::setElement(cx, arrobj, arrobj, idx, &tmp, false))
                 return false;
+        } else {
+            JS_ASSERT(idx < arrobj->getDenseInitializedLength());
+            arrobj->setDenseElementWithType(cx, idx, args[elemi]);
         }
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 bool
--- a/js/src/vm/Shape-inl.h
+++ b/js/src/vm/Shape-inl.h
@@ -10,16 +10,17 @@
 #include "vm/Shape.h"
 
 #include "mozilla/TypeTraits.h"
 
 #include "jsobj.h"
 
 #include "vm/Interpreter.h"
 #include "vm/ScopeObject.h"
+#include "vm/TypedArrayObject.h"
 
 #include "jsatominlines.h"
 #include "jscntxtinlines.h"
 #include "jsgcinlines.h"
 
 namespace js {
 
 inline
@@ -199,11 +200,25 @@ AutoRooterGetterSetter::AutoRooterGetter
                                                PropertyOp *pgetter, StrictPropertyOp *psetter
                                                MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
 {
     if (attrs & (JSPROP_GETTER | JSPROP_SETTER))
         inner.construct(cx, attrs, pgetter, psetter);
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 }
 
+static inline uint8_t
+GetShapeAttributes(JSObject *obj, Shape *shape)
+{
+    JS_ASSERT(obj->isNative());
+
+    if (IsImplicitDenseOrTypedArrayElement(shape)) {
+        if (obj->is<TypedArrayObject>())
+            return JSPROP_ENUMERATE | JSPROP_PERMANENT;
+        return JSPROP_ENUMERATE;
+    }
+
+    return shape->attributes();
+}
+
 } /* namespace js */
 
 #endif /* vm_Shape_inl_h */
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -1655,33 +1655,27 @@ template<> struct RootKind<BaseShape *> 
 static inline void
 MarkNonNativePropertyFound(MutableHandleShape propp)
 {
     propp.set(reinterpret_cast<Shape*>(1));
 }
 
 template <AllowGC allowGC>
 static inline void
-MarkDenseElementFound(typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
+MarkDenseOrTypedArrayElementFound(typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
 {
     propp.set(reinterpret_cast<Shape*>(1));
 }
 
 static inline bool
-IsImplicitDenseElement(Shape *prop)
+IsImplicitDenseOrTypedArrayElement(Shape *prop)
 {
     return prop == reinterpret_cast<Shape*>(1);
 }
 
-static inline uint8_t
-GetShapeAttributes(HandleShape shape)
-{
-    return IsImplicitDenseElement(shape) ? JSPROP_ENUMERATE : shape->attributes();
-}
-
 } // namespace js
 
 #ifdef _MSC_VER
 #pragma warning(pop)
 #pragma warning(pop)
 #endif
 
 namespace JS {
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -45,17 +45,19 @@
 
 #include "vm/Shape-inl.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 using mozilla::IsNaN;
+using mozilla::NegativeInfinity;
 using mozilla::PodCopy;
+using mozilla::PositiveInfinity;
 using JS::CanonicalizeNaN;
 using JS::GenericNaN;
 
 static bool
 ValueIsLength(const Value &v, uint32_t *len)
 {
     if (v.isInt32()) {
         int32_t i = v.toInt32();
@@ -112,87 +114,16 @@ TypedArrayObject::neuter(JSContext *cx)
 }
 
 ArrayBufferObject *
 TypedArrayObject::sharedBuffer() const
 {
     return &bufferValue(const_cast<TypedArrayObject*>(this)).toObject().as<SharedArrayBufferObject>();
 }
 
-bool
-TypedArrayObject::obj_lookupGeneric(JSContext *cx, HandleObject tarray, HandleId id,
-                                    MutableHandleObject objp, MutableHandleShape propp)
-{
-    if (tarray->as<TypedArrayObject>().isArrayIndex(id)) {
-        MarkNonNativePropertyFound(propp);
-        objp.set(tarray);
-        return true;
-    }
-
-    RootedObject proto(cx, tarray->getProto());
-    if (!proto) {
-        objp.set(nullptr);
-        propp.set(nullptr);
-        return true;
-    }
-
-    return JSObject::lookupGeneric(cx, proto, id, objp, propp);
-}
-
-bool
-TypedArrayObject::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
-TypedArrayObject::obj_lookupElement(JSContext *cx, HandleObject tarray, uint32_t index,
-                                    MutableHandleObject objp, MutableHandleShape propp)
-{
-    if (index < tarray->as<TypedArrayObject>().length()) {
-        MarkNonNativePropertyFound(propp);
-        objp.set(tarray);
-        return true;
-    }
-
-    RootedObject proto(cx, tarray->getProto());
-    if (proto)
-        return JSObject::lookupElement(cx, proto, index, objp, propp);
-
-    objp.set(nullptr);
-    propp.set(nullptr);
-    return true;
-}
-
-bool
-TypedArrayObject::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
-TypedArrayObject::obj_getGenericAttributes(JSContext *cx, HandleObject obj, HandleId id,
-                                           unsigned *attrsp)
-{
-    *attrsp = JSPROP_PERMANENT | JSPROP_ENUMERATE;
-    return true;
-}
-
-bool
-TypedArrayObject::obj_setGenericAttributes(JSContext *cx, HandleObject obj, HandleId id,
-                                           unsigned *attrsp)
-{
-    JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_SET_ARRAY_ATTRS);
-    return false;
-}
-
 /* static */ int
 TypedArrayObject::lengthOffset()
 {
     return JSObject::getFixedSlotOffset(LENGTH_SLOT);
 }
 
 /* static */ int
 TypedArrayObject::dataOffset()
@@ -230,27 +161,27 @@ js::ClampDoubleToUint8(const double x)
          * want.
          */
         return (y & ~1);
     }
 
     return y;
 }
 
-bool
-js::ToDoubleForTypedArray(JSContext *cx, JS::HandleValue vp, double *d)
+static bool
+ToDoubleForTypedArray(ThreadSafeContext *cx, const Value &vp, double *d)
 {
     if (vp.isDouble()) {
         *d = vp.toDouble();
     } else if (vp.isNull()) {
         *d = 0.0;
     } else if (vp.isPrimitive()) {
         JS_ASSERT(vp.isString() || vp.isUndefined() || vp.isBoolean());
         if (vp.isString()) {
-            if (!ToNumber(cx, vp, d))
+            if (!StringToNumber(cx, vp.toString(), d))
                 return false;
         } else if (vp.isUndefined()) {
             *d = GenericNaN();
         } else {
             *d = double(vp.toBoolean());
         }
     } else {
         // non-primitive assignments become NaN or 0 (for float/int arrays)
@@ -307,93 +238,28 @@ class TypedArrayObjectTemplate : public 
         return &TypedArrayObject::classes[ArrayTypeID()];
     }
 
     static bool is(HandleValue v) {
         return v.isObject() && v.toObject().hasClass(fastClass());
     }
 
     static bool
-    obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandlePropertyName name,
-                    MutableHandleValue vp)
-    {
-        RootedObject proto(cx, obj->getProto());
-        if (!proto) {
-            vp.setUndefined();
-            return true;
-        }
-
-        return JSObject::getProperty(cx, proto, receiver, name, vp);
-    }
-
-    static bool
-    obj_getElement(JSContext *cx, HandleObject tarray, HandleObject receiver, uint32_t index,
-                   MutableHandleValue vp)
-    {
-        if (index < tarray->as<TypedArrayObject>().length()) {
-            copyIndexToValue(tarray, index, vp);
-            return true;
-        }
-
-        vp.setUndefined();
-        return true;
-    }
-
-    static bool
-    obj_getSpecial(JSContext *cx, HandleObject obj, HandleObject receiver, HandleSpecialId sid,
-                   MutableHandleValue vp)
-    {
-        RootedObject proto(cx, obj->getProto());
-        if (!proto) {
-            vp.setUndefined();
-            return true;
-        }
-
-        return JSObject::getSpecial(cx, proto, receiver, sid, vp);
-    }
-
-    static bool
-    obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id,
-                   MutableHandleValue vp)
-    {
-        RootedValue idval(cx, IdToValue(id));
-
-        uint32_t index;
-        if (IsDefinitelyIndex(idval, &index))
-            return obj_getElement(cx, obj, receiver, index, vp);
-
-        Rooted<SpecialId> sid(cx);
-        if (ValueIsSpecial(obj, &idval, &sid, cx))
-            return obj_getSpecial(cx, obj, receiver, sid, vp);
-
-        JSAtom *atom = ToAtom<CanGC>(cx, idval);
-        if (!atom)
-            return false;
-
-        if (atom->isIndex(&index))
-            return obj_getElement(cx, obj, receiver, index, vp);
-
-        Rooted<PropertyName*> name(cx, atom->asPropertyName());
-        return obj_getProperty(cx, obj, receiver, name, vp);
-    }
-
-    static bool
-    setElementTail(JSContext *cx, HandleObject tarray, uint32_t index,
-                   MutableHandleValue vp, bool strict)
+    setIndexValue(ThreadSafeContext *cx, JSObject *tarray, uint32_t index, const Value &value)
     {
         JS_ASSERT(tarray);
         JS_ASSERT(index < tarray->as<TypedArrayObject>().length());
 
-        if (vp.isInt32()) {
-            setIndex(tarray, index, NativeType(vp.toInt32()));
+        if (value.isInt32()) {
+            setIndex(tarray, index, NativeType(value.toInt32()));
             return true;
         }
 
         double d;
-        if (!ToDoubleForTypedArray(cx, vp, &d))
+        if (!ToDoubleForTypedArray(cx, value, &d))
             return false;
 
         // If the array is an integer array, we only handle up to
         // 32-bit ints from this point on.  if we want to handle
         // 64-bit ints, we'll need some changes.
 
         // Assign based on characteristics of the destination type
         if (ArrayTypeIsFloatingPoint()) {
@@ -410,160 +276,16 @@ class TypedArrayObjectTemplate : public 
             JS_ASSERT(sizeof(NativeType) <= 4);
             int32_t n = ToInt32(d);
             setIndex(tarray, index, NativeType(n));
         }
 
         return true;
     }
 
-    static bool
-    obj_setGeneric(JSContext *cx, HandleObject tarray, HandleId id,
-                   MutableHandleValue vp, bool strict)
-    {
-        uint32_t index;
-        // We can't just chain to js_SetPropertyHelper, because we're not a normal object.
-        if (!tarray->as<TypedArrayObject>().isArrayIndex(id, &index)) {
-            // Silent ignore is better than an exception here, because
-            // at some point we may want to support other properties on
-            // these objects.  This is especially true when these arrays
-            // are used to implement HTML Canvas 2D's PixelArray objects,
-            // which used to be plain old arrays.
-            vp.setUndefined();
-            return true;
-        }
-
-        return setElementTail(cx, tarray, index, vp, strict);
-    }
-
-    static bool
-    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);
-    }
-
-    static bool
-    obj_setElement(JSContext *cx, HandleObject tarray, uint32_t index,
-                   MutableHandleValue vp, bool strict)
-    {
-        if (index >= tarray->as<TypedArrayObject>().length()) {
-            // Silent ignore is better than an exception here, because
-            // at some point we may want to support other properties on
-            // these objects.  This is especially true when these arrays
-            // are used to implement HTML Canvas 2D's PixelArray objects,
-            // which used to be plain old arrays.
-            vp.setUndefined();
-            return true;
-        }
-
-        return setElementTail(cx, tarray, index, vp, strict);
-    }
-
-    static bool
-    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);
-    }
-
-    static bool
-    obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
-                      PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
-    {
-        RootedValue tmp(cx, v);
-        return obj_setGeneric(cx, obj, id, &tmp, false);
-    }
-
-    static bool
-    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);
-    }
-
-    static bool
-    obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v,
-                       PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
-    {
-        RootedValue tmp(cx, v);
-        return obj_setElement(cx, obj, index, &tmp, false);
-    }
-
-    static bool
-    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);
-    }
-
-    static bool
-    obj_deleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, bool *succeeded)
-    {
-        *succeeded = true;
-        return true;
-    }
-
-    static bool
-    obj_deleteElement(JSContext *cx, HandleObject tarray, uint32_t index, bool *succeeded)
-    {
-        if (index < tarray->as<TypedArrayObject>().length()) {
-            *succeeded = false;
-            return true;
-        }
-
-        *succeeded = true;
-        return true;
-    }
-
-    static bool
-    obj_deleteSpecial(JSContext *cx, HandleObject tarray, HandleSpecialId sid, bool *succeeded)
-    {
-        *succeeded = true;
-        return true;
-    }
-
-    static bool
-    obj_enumerate(JSContext *cx, HandleObject tarray, JSIterateOp enum_op,
-                  MutableHandleValue statep, MutableHandleId idp)
-    {
-        JS_ASSERT(tarray->is<TypedArrayObject>());
-
-        uint32_t index;
-        switch (enum_op) {
-          case JSENUMERATE_INIT_ALL:
-          case JSENUMERATE_INIT:
-            statep.setInt32(0);
-            idp.set(::INT_TO_JSID(tarray->as<TypedArrayObject>().length()));
-            break;
-
-          case JSENUMERATE_NEXT: {
-            index = static_cast<uint32_t>(statep.toInt32());
-            uint32_t length = tarray->as<TypedArrayObject>().length();
-            if (index < length) {
-                idp.set(::INT_TO_JSID(index));
-                statep.setInt32(index + 1);
-            } else {
-                JS_ASSERT(index == length);
-                statep.setNull();
-            }
-            break;
-          }
-          case JSENUMERATE_DESTROY:
-            statep.setNull();
-            break;
-        }
-
-        return true;
-    }
-
     static TypedArrayObject *
     makeProtoInstance(JSContext *cx, HandleObject proto)
     {
         JS_ASSERT(proto);
 
         RootedObject obj(cx, NewBuiltinClassInstance(cx, fastClass()));
         if (!obj)
             return nullptr;
@@ -624,26 +346,19 @@ class TypedArrayObjectTemplate : public 
 
         InitArrayBufferViewDataPointer(obj, buffer, byteOffset);
         obj->setSlot(LENGTH_SLOT, Int32Value(len));
         obj->setSlot(BYTEOFFSET_SLOT, Int32Value(byteOffset));
         obj->setSlot(BYTELENGTH_SLOT, Int32Value(len * sizeof(NativeType)));
         obj->setSlot(NEXT_VIEW_SLOT, PrivateValue(nullptr));
         obj->setSlot(NEXT_BUFFER_SLOT, PrivateValue(UNSET_BUFFER_LINK));
 
-        // Mark the object as non-extensible. We cannot simply call
-        // obj->preventExtensions() because that has to iterate through all
-        // properties, and on long arrays that is much too slow. We could
-        // initialize the length fields to zero to avoid that, but then it
-        // would just boil down to a slightly slower wrapper around the
-        // following code anyway:
         js::Shape *empty = EmptyShape::getInitialShape(cx, fastClass(),
                                                        obj->getProto(), obj->getParent(), obj->getMetadata(),
-                                                       gc::FINALIZE_OBJECT8_BACKGROUND,
-                                                       BaseShape::NOT_EXTENSIBLE);
+                                                       gc::FINALIZE_OBJECT8_BACKGROUND);
         if (!empty)
             return nullptr;
         obj->setLastPropertyInfallible(empty);
 
 #ifdef DEBUG
         uint32_t bufferByteLength = buffer->byteLength();
         uint32_t arrayByteLength = obj->byteLength();
         uint32_t arrayByteOffset = obj->byteOffset();
@@ -1113,17 +828,17 @@ class TypedArrayObjectTemplate : public 
     }
 
     static void
     setIndex(JSObject *obj, uint32_t index, NativeType val)
     {
         *(static_cast<NativeType*>(obj->as<TypedArrayObject>().viewData()) + index) = val;
     }
 
-    static void copyIndexToValue(JSObject *tarray, uint32_t index, MutableHandleValue vp);
+    static Value getIndexValue(JSObject *tarray, uint32_t index);
 
     static JSObject *
     createSubarray(JSContext *cx, HandleObject tarrayArg, uint32_t begin, uint32_t end)
     {
         Rooted<TypedArrayObject*> tarray(cx, &tarrayArg->as<TypedArrayObject>());
 
         JS_ASSERT(begin <= tarray->length());
         JS_ASSERT(end <= tarray->length());
@@ -1504,82 +1219,76 @@ ArrayBufferObject::createTypedArrayFromB
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsArrayBuffer, createTypedArrayFromBufferImpl<T> >(cx, args);
 }
 
 // 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)
+Value
+TypedArrayObjectTemplate<NativeType>::getIndexValue(JSObject *tarray, uint32_t index)
 {
     JS_STATIC_ASSERT(sizeof(NativeType) < 4);
 
-    vp.setInt32(getIndex(tarray, index));
+    return Int32Value(getIndex(tarray, index));
 }
 
 namespace {
 
 // and we need to specialize for 32-bit integers and floats
 template<>
-void
-TypedArrayObjectTemplate<int32_t>::copyIndexToValue(JSObject *tarray, uint32_t index,
-                                                    MutableHandleValue vp)
+Value
+TypedArrayObjectTemplate<int32_t>::getIndexValue(JSObject *tarray, uint32_t index)
 {
-    int32_t val = getIndex(tarray, index);
-    vp.setInt32(val);
+    return Int32Value(getIndex(tarray, index));
 }
 
 template<>
-void
-TypedArrayObjectTemplate<uint32_t>::copyIndexToValue(JSObject *tarray, uint32_t index,
-                                                     MutableHandleValue vp)
+Value
+TypedArrayObjectTemplate<uint32_t>::getIndexValue(JSObject *tarray, uint32_t index)
 {
     uint32_t val = getIndex(tarray, index);
-    vp.setNumber(val);
+    return NumberValue(val);
 }
 
 template<>
-void
-TypedArrayObjectTemplate<float>::copyIndexToValue(JSObject *tarray, uint32_t index,
-                                                  MutableHandleValue vp)
+Value
+TypedArrayObjectTemplate<float>::getIndexValue(JSObject *tarray, uint32_t index)
 {
     float val = getIndex(tarray, index);
     double dval = val;
 
     /*
      * Doubles in typed arrays could be typed-punned arrays of integers. This
      * could allow user code to break the engine-wide invariant that only
      * canonical nans are stored into jsvals, which means user code could
      * confuse the engine into interpreting a double-typed jsval as an
      * object-typed jsval.
      *
      * This could be removed for platforms/compilers known to convert a 32-bit
      * non-canonical nan to a 64-bit canonical nan.
      */
-    vp.setDouble(CanonicalizeNaN(dval));
+    return DoubleValue(CanonicalizeNaN(dval));
 }
 
 template<>
-void
-TypedArrayObjectTemplate<double>::copyIndexToValue(JSObject *tarray, uint32_t index,
-                                                   MutableHandleValue vp)
+Value
+TypedArrayObjectTemplate<double>::getIndexValue(JSObject *tarray, uint32_t index)
 {
     double val = getIndex(tarray, index);
 
     /*
      * Doubles in typed arrays could be typed-punned arrays of integers. This
      * could allow user code to break the engine-wide invariant that only
      * canonical nans are stored into jsvals, which means user code could
      * confuse the engine into interpreting a double-typed jsval as an
      * object-typed jsval.
      */
-    vp.setDouble(CanonicalizeNaN(val));
+    return DoubleValue(CanonicalizeNaN(val));
 }
 
 } /* anonymous namespace */
 
 static NewObjectKind
 DataViewNewObjectKind(JSContext *cx, uint32_t byteLength, JSObject *proto)
 {
     if (!proto && byteLength >= TypedArrayObject::SINGLETON_TYPE_BYTE_LENGTH)
@@ -2223,48 +1932,87 @@ DataViewObject::setFloat64Impl(JSContext
 
 bool
 DataViewObject::fun_setFloat64(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<is, setFloat64Impl>(cx, args);
 }
 
-void
-TypedArrayObject::copyTypedArrayElement(uint32_t index, MutableHandleValue vp)
+Value
+TypedArrayObject::getElement(uint32_t index)
 {
     JS_ASSERT(index < length());
 
     switch (type()) {
       case ScalarTypeDescr::TYPE_INT8:
-        TypedArrayObjectTemplate<int8_t>::copyIndexToValue(this, index, vp);
+        return TypedArrayObjectTemplate<int8_t>::getIndexValue(this, index);
         break;
       case ScalarTypeDescr::TYPE_UINT8:
-        TypedArrayObjectTemplate<uint8_t>::copyIndexToValue(this, index, vp);
+        return TypedArrayObjectTemplate<uint8_t>::getIndexValue(this, index);
         break;
       case ScalarTypeDescr::TYPE_UINT8_CLAMPED:
-        TypedArrayObjectTemplate<uint8_clamped>::copyIndexToValue(this, index, vp);
+        return TypedArrayObjectTemplate<uint8_clamped>::getIndexValue(this, index);
         break;
       case ScalarTypeDescr::TYPE_INT16:
-        TypedArrayObjectTemplate<int16_t>::copyIndexToValue(this, index, vp);
+        return TypedArrayObjectTemplate<int16_t>::getIndexValue(this, index);
         break;
       case ScalarTypeDescr::TYPE_UINT16:
-        TypedArrayObjectTemplate<uint16_t>::copyIndexToValue(this, index, vp);
+        return TypedArrayObjectTemplate<uint16_t>::getIndexValue(this, index);
         break;
       case ScalarTypeDescr::TYPE_INT32:
-        TypedArrayObjectTemplate<int32_t>::copyIndexToValue(this, index, vp);
+        return TypedArrayObjectTemplate<int32_t>::getIndexValue(this, index);
         break;
       case ScalarTypeDescr::TYPE_UINT32:
-        TypedArrayObjectTemplate<uint32_t>::copyIndexToValue(this, index, vp);
+        return TypedArrayObjectTemplate<uint32_t>::getIndexValue(this, index);
         break;
       case ScalarTypeDescr::TYPE_FLOAT32:
-        TypedArrayObjectTemplate<float>::copyIndexToValue(this, index, vp);
+        return TypedArrayObjectTemplate<float>::getIndexValue(this, index);
         break;
       case ScalarTypeDescr::TYPE_FLOAT64:
-        TypedArrayObjectTemplate<double>::copyIndexToValue(this, index, vp);
+        return TypedArrayObjectTemplate<double>::getIndexValue(this, index);
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("Unknown TypedArray type");
+        break;
+    }
+}
+
+bool
+TypedArrayObject::setElement(ThreadSafeContext *cx, uint32_t index, const Value &value)
+{
+    JS_ASSERT(index < length());
+
+    switch (type()) {
+      case ScalarTypeDescr::TYPE_INT8:
+        return TypedArrayObjectTemplate<int8_t>::setIndexValue(cx, this, index, value);
+        break;
+      case ScalarTypeDescr::TYPE_UINT8:
+        return TypedArrayObjectTemplate<uint8_t>::setIndexValue(cx, this, index, value);
+        break;
+      case ScalarTypeDescr::TYPE_UINT8_CLAMPED:
+        return TypedArrayObjectTemplate<uint8_clamped>::setIndexValue(cx, this, index, value);
+        break;
+      case ScalarTypeDescr::TYPE_INT16:
+        return TypedArrayObjectTemplate<int16_t>::setIndexValue(cx, this, index, value);
+        break;
+      case ScalarTypeDescr::TYPE_UINT16:
+        return TypedArrayObjectTemplate<uint16_t>::setIndexValue(cx, this, index, value);
+        break;
+      case ScalarTypeDescr::TYPE_INT32:
+        return TypedArrayObjectTemplate<int32_t>::setIndexValue(cx, this, index, value);
+        break;
+      case ScalarTypeDescr::TYPE_UINT32:
+        return TypedArrayObjectTemplate<uint32_t>::setIndexValue(cx, this, index, value);
+        break;
+      case ScalarTypeDescr::TYPE_FLOAT32:
+        return TypedArrayObjectTemplate<float>::setIndexValue(cx, this, index, value);
+        break;
+      case ScalarTypeDescr::TYPE_FLOAT64:
+        return TypedArrayObjectTemplate<double>::setIndexValue(cx, this, index, value);
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Unknown TypedArray type");
         break;
     }
 }
 
 /***
@@ -2371,59 +2119,29 @@ IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Flo
     JS_ConvertStub                                                             \
 }
 
 #define IMPL_TYPED_ARRAY_FAST_CLASS(_typedArray)                               \
 {                                                                              \
     #_typedArray,                                                              \
     JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) |             \
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |                        \
-    JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray) |                          \
-    Class::NON_NATIVE,                                                         \
+    JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray),                           \
     JS_PropertyStub,         /* addProperty */                                 \
     JS_DeletePropertyStub,   /* delProperty */                                 \
     JS_PropertyStub,         /* getProperty */                                 \
     JS_StrictPropertyStub,   /* setProperty */                                 \
     JS_EnumerateStub,                                                          \
     JS_ResolveStub,                                                            \
     JS_ConvertStub,                                                            \
     nullptr,                 /* finalize */                                    \
     nullptr,                 /* call        */                                 \
     nullptr,                 /* hasInstance */                                 \
     nullptr,                 /* construct   */                                 \
     ArrayBufferViewObject::trace, /* trace  */                                 \
-    JS_NULL_CLASS_SPEC,                                                        \
-    JS_NULL_CLASS_EXT,                                                         \
-    {                                                                          \
-        _typedArray##Object::obj_lookupGeneric,                                \
-        _typedArray##Object::obj_lookupProperty,                               \
-        _typedArray##Object::obj_lookupElement,                                \
-        _typedArray##Object::obj_lookupSpecial,                                \
-        _typedArray##Object::obj_defineGeneric,                                \
-        _typedArray##Object::obj_defineProperty,                               \
-        _typedArray##Object::obj_defineElement,                                \
-        _typedArray##Object::obj_defineSpecial,                                \
-        _typedArray##Object::obj_getGeneric,                                   \
-        _typedArray##Object::obj_getProperty,                                  \
-        _typedArray##Object::obj_getElement,                                   \
-        _typedArray##Object::obj_getSpecial,                                   \
-        _typedArray##Object::obj_setGeneric,                                   \
-        _typedArray##Object::obj_setProperty,                                  \
-        _typedArray##Object::obj_setElement,                                   \
-        _typedArray##Object::obj_setSpecial,                                   \
-        _typedArray##Object::obj_getGenericAttributes,                         \
-        _typedArray##Object::obj_setGenericAttributes,                         \
-        _typedArray##Object::obj_deleteProperty,                               \
-        _typedArray##Object::obj_deleteElement,                                \
-        _typedArray##Object::obj_deleteSpecial,                                \
-        nullptr, nullptr, /* watch/unwatch */                                  \
-        nullptr,          /* slice */                                          \
-        _typedArray##Object::obj_enumerate,                                    \
-        nullptr,          /* thisObject  */                                    \
-    }                                                                          \
 }
 
 template<class ArrayType>
 static inline JSObject *
 InitTypedArrayClass(JSContext *cx)
 {
     Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal());
     RootedObject proto(cx, global->createBlankPrototype(cx, ArrayType::protoClass()));
@@ -2578,17 +2296,16 @@ const Class DataViewObject::protoClass =
     JS_ResolveStub,
     JS_ConvertStub
 };
 
 const Class DataViewObject::class_ = {
     "DataView",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_IMPLEMENTS_BARRIERS |
-    /* Bug 886622: Consider making this Class NON_NATIVE. */
     JSCLASS_HAS_RESERVED_SLOTS(DataViewObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_DataView),
     JS_PropertyStub,         /* addProperty */
     JS_DeletePropertyStub,   /* delProperty */
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
@@ -2777,16 +2494,68 @@ ArrayBufferObject &
 js::AsTypedArrayBuffer(HandleValue v)
 {
     JS_ASSERT(IsTypedArrayBuffer(v));
     if (v.toObject().is<ArrayBufferObject>())
         return v.toObject().as<ArrayBufferObject>();
     return v.toObject().as<SharedArrayBufferObject>();
 }
 
+bool
+js::StringIsTypedArrayIndex(JSLinearString *str, double *indexp)
+{
+    // Largest double (2^53 - 1) which can be exactly represented in the
+    // mantissa of a double.
+    static const double MAX_INTEGER = 9007199254740991;
+
+    const jschar *s = str->chars();
+    const jschar *end = s + str->length();
+
+    if (s == end)
+        return false;
+
+    bool negative = false;
+    if (*s == '-') {
+        negative = true;
+        if (++s == end)
+            return false;
+    }
+
+    if (!JS7_ISDEC(*s))
+        return false;
+
+    double index = 0;
+    uint32_t digit = JS7_UNDEC(*s++);
+
+    /* Don't allow leading zeros. */
+    if (digit == 0 && s != end)
+        return false;
+
+    index = digit;
+
+    for (; s < end; s++) {
+        if (!JS7_ISDEC(*s))
+            return false;
+
+        digit = JS7_UNDEC(*s);
+
+        /* Watch for mantissa overflows. */
+        if ((MAX_INTEGER - digit) / 10 < index)
+            return false;
+
+        index = 10 * index + digit;
+    }
+
+    if (negative)
+        index = -index;
+
+    *indexp = index;
+    return true;
+}
+
 /* JS Friend API */
 
 JS_FRIEND_API(bool)
 JS_IsTypedArrayObject(JSObject *obj)
 {
     obj = CheckedUnwrap(obj);
     return obj ? obj->is<TypedArrayObject>() : false;
 }
--- a/js/src/vm/TypedArrayObject.h
+++ b/js/src/vm/TypedArrayObject.h
@@ -35,30 +35,16 @@ class TypedArrayObject : public ArrayBuf
     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,
-                                   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_getGenericAttributes(JSContext *cx, HandleObject obj,
-                                         HandleId id, unsigned *attrsp);
-    static bool obj_setGenericAttributes(JSContext *cx, HandleObject obj,
-                                         HandleId id, unsigned *attrsp);
-
     static Value bufferValue(TypedArrayObject *tarr) {
         return tarr->getFixedSlot(BUFFER_SLOT);
     }
     static Value byteOffsetValue(TypedArrayObject *tarr) {
         return tarr->getFixedSlot(BYTEOFFSET_SLOT);
     }
     static Value byteLengthValue(TypedArrayObject *tarr) {
         return tarr->getFixedSlot(BYTELENGTH_SLOT);
@@ -87,17 +73,18 @@ class TypedArrayObject : public ArrayBuf
     uint32_t type() const {
         return getFixedSlot(TYPE_SLOT).toInt32();
     }
     void *viewData() const {
         return static_cast<void*>(getPrivate(DATA_SLOT));
     }
 
     inline bool isArrayIndex(jsid id, uint32_t *ip = nullptr);
-    void copyTypedArrayElement(uint32_t index, MutableHandleValue vp);
+    Value getElement(uint32_t index);
+    bool setElement(ThreadSafeContext *cx, uint32_t index, const Value &value);
 
     void neuter(JSContext *cx);
 
     static uint32_t slotWidth(int atype) {
         switch (atype) {
           case ScalarTypeDescr::TYPE_INT8:
           case ScalarTypeDescr::TYPE_UINT8:
           case ScalarTypeDescr::TYPE_UINT8_CLAMPED:
@@ -148,16 +135,35 @@ bool
 IsTypedArrayConstructor(HandleValue v, uint32_t type);
 
 bool
 IsTypedArrayBuffer(HandleValue v);
 
 ArrayBufferObject &
 AsTypedArrayBuffer(HandleValue v);
 
+bool
+StringIsTypedArrayIndex(JSLinearString *str, double *indexp);
+
+inline bool
+IsTypedArrayIndex(jsid id, double *indexp)
+{
+    if (JSID_IS_INT(id)) {
+        int32_t i = JSID_TO_INT(id);
+        JS_ASSERT(i >= 0);
+        *indexp = (double)i;
+        return true;
+    }
+
+    if (MOZ_UNLIKELY(!JSID_IS_STRING(id)))
+        return false;
+
+    return StringIsTypedArrayIndex(JSID_TO_ATOM(id), indexp);
+}
+
 static inline unsigned
 TypedArrayShift(ArrayBufferView::ViewType viewType)
 {
     switch (viewType) {
       case ArrayBufferView::TYPE_INT8:
       case ArrayBufferView::TYPE_UINT8:
       case ArrayBufferView::TYPE_UINT8_CLAMPED:
         return 0;
@@ -317,18 +323,16 @@ ClampIntForUint8Array(int32_t x)
 {
     if (x < 0)
         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
 {