Bug 973238 Part 4 -- Use prototype rather than reserved slot to uncover descriptor r=jandem
☠☠ backed out by a1a599888834 ☠ ☠
authorNicholas D. Matsakis <nmatsakis@mozilla.com>
Tue, 11 Mar 2014 12:50:32 -0400
changeset 183629 2ca44eb13ecf1014d45de955afc93bb71b4ac359
parent 183628 af5e255b4dee2908eec9f25eefca69fadf34ea31
child 183630 e40e2dbf7d9e0860ca3e6975cc2722a311aa3484
push id6844
push userphilringnalda@gmail.com
push dateSun, 18 May 2014 01:12:08 +0000
treeherderfx-team@41a54c8add09 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs973238
milestone32.0a1
Bug 973238 Part 4 -- Use prototype rather than reserved slot to uncover descriptor r=jandem
js/src/builtin/TypedObject.cpp
js/src/builtin/TypedObject.h
js/src/builtin/TypedObject.js
js/src/builtin/TypedObjectConstants.h
js/src/jit-test/tests/TypedObject/prototypes.js
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/IonBuilder.cpp
js/src/jit/LIR-Common.h
js/src/jit/LOpcodes.h
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/ParallelSafetyAnalysis.cpp
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
js/src/vm/TypedArrayObject.h
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1479,17 +1479,16 @@ TypedObject::createUnattachedWithClass(J
         return nullptr;
 
     obj->setPrivate(nullptr);
     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_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 (!type->is<SimpleTypeDescr>()) { // FIXME Bug 929651
         RootedTypeObject typeObj(cx, obj->getType(cx));
         if (typeObj) {
             if (!typeObj->addTypedObjectAddendum(cx, type))
                 return nullptr;
@@ -1633,19 +1632,16 @@ ReportTypedObjTypeError(JSContext *cx,
 
     JS_free(cx, (void *) typeReprStr);
     return false;
 }
 
 /*static*/ void
 TypedObject::obj_trace(JSTracer *trace, JSObject *object)
 {
-    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>();
     TypeDescr &descr = typedObj.typeDescr();
     if (descr.opaque()) {
         uint8_t *mem = typedObj.typedMem();
         if (!mem)
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -650,18 +650,22 @@ class TypedObject : public ArrayBufferVi
     int32_t offset() const {
         return getReservedSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET).toInt32();
     }
 
     ArrayBufferObject &owner() const {
         return getReservedSlot(JS_TYPEDOBJ_SLOT_OWNER).toObject().as<ArrayBufferObject>();
     }
 
+    TypedProto &typedProto() const {
+        return getProto()->as<TypedProto>();
+    }
+
     TypeDescr &typeDescr() const {
-        return getReservedSlot(JS_TYPEDOBJ_SLOT_TYPE_DESCR).toObject().as<TypeDescr>();
+        return typedProto().typeDescr();
     }
 
     uint8_t *typedMem() const {
         return (uint8_t*) getPrivate();
     }
 
     int32_t byteLength() const {
         return getReservedSlot(JS_TYPEDOBJ_SLOT_BYTELENGTH).toInt32();
--- a/js/src/builtin/TypedObject.js
+++ b/js/src/builtin/TypedObject.js
@@ -23,32 +23,39 @@
     TO_INT32(UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_SIZED_ARRAY_LENGTH))
 #define DESCR_STRUCT_FIELD_NAMES(obj) \
     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 prototype slots
+
+#define TYPROTO_DESCR(obj) \
+    UnsafeGetReservedSlot(obj, JS_TYPROTO_SLOT_DESCR)
+
 // Typed object slots
 
 #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 TypedObjectTypeDescr(typedObj) {
+  return TYPROTO_DESCR(typedObj.__proto__);
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // Getting values
 //
 // 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
@@ -187,17 +194,17 @@ function TypedObjectGetX4(descr, typedOb
 // and works for any type.
 function TypedObjectSet(descr, typedObj, offset, fromValue) {
   assert(TypedObjectIsAttached(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) && ObjectIsTypedObject(fromValue)) {
-    if (!descr.variable && DescrsEquiv(descr, TYPEDOBJ_TYPE_DESCR(fromValue))) {
+    if (!descr.variable && DescrsEquiv(descr, TypedObjectTypeDescr(fromValue))) {
       if (!TypedObjectIsAttached(fromValue))
         ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
 
       var size = DESCR_SIZE(descr);
       Memcpy(typedObj, offset, fromValue, 0, size);
       return;
     }
   }
@@ -381,17 +388,17 @@ function Reify(sourceDescr,
 
   return TypedObjectGet(sourceDescr, sourceTypedObj, sourceOffset);
 }
 
 function FillTypedArrayWithValue(destArray, fromValue) {
   assert(IsObject(handle) && ObjectIsTypedObject(destArray),
          "FillTypedArrayWithValue: not typed handle");
 
-  var descr = TYPEDOBJ_TYPE_DESCR(destArray);
+  var descr = TypedObjectTypeDescr(destArray);
   var length = DESCR_SIZED_ARRAY_LENGTH(descr);
   if (length === 0)
     return;
 
   // Use convert and copy to to produce the first element:
   var elemDescr = DESCR_ARRAY_ELEMENT_TYPE(descr);
   TypedObjectSet(elemDescr, destArray, 0, fromValue);
 
@@ -435,17 +442,17 @@ function TypedArrayRedimension(newArrayT
   if (!IsObject(this) || !ObjectIsTypedObject(this))
     ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   if (!IsObject(newArrayType) || !ObjectIsTypeDescr(newArrayType))
     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 = TYPEDOBJ_TYPE_DESCR(this);
+  var oldArrayType = TypedObjectTypeDescr(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;
@@ -502,17 +509,17 @@ function X4ProtoString(type) {
   assert(false, "Unhandled type constant");
   return undefined;
 }
 
 function X4ToSource() {
   if (!IsObject(this) || !ObjectIsTypedObject(this))
     ThrowError(JSMSG_INCOMPATIBLE_PROTO, "X4", "toSource", typeof this);
 
-  var descr = TYPEDOBJ_TYPE_DESCR(this);
+  var descr = TypedObjectTypeDescr(this);
 
   if (DESCR_KIND(descr) != JS_TYPEREPR_X4_KIND)
     ThrowError(JSMSG_INCOMPATIBLE_PROTO, "X4", "toSource", typeof this);
 
   var type = DESCR_TYPE(descr);
   return X4ProtoString(type)+"("+this.x+", "+this.y+", "+this.z+", "+this.w+")";
 }
 
@@ -580,17 +587,17 @@ function StorageOfTypedObject(obj) {
 }
 
 // This is the `objectType()` function defined in the spec.
 // It returns the type of its argument.
 //
 // Warning: user exposed!
 function TypeOfTypedObject(obj) {
   if (IsObject(obj) && ObjectIsTypedObject(obj))
-    return TYPEDOBJ_TYPE_DESCR(obj);
+    return TypedObjectTypeDescr(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();
@@ -674,17 +681,17 @@ function TypedObjectArrayTypeFrom(a, b, 
       return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   }
 }
 
 // Warning: user exposed!
 function TypedArrayMap(a, b) {
   if (!IsObject(this) || !ObjectIsTypedObject(this))
     return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
-  var thisType = TYPEDOBJ_TYPE_DESCR(this);
+  var thisType = TypedObjectTypeDescr(this);
   if (!TypeDescrIsArrayType(thisType))
     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);
@@ -694,49 +701,49 @@ function TypedArrayMap(a, b) {
 // Warning: user exposed!
 function TypedArrayMapPar(a, b) {
   // Arguments: [depth], func
 
   // Defer to the sequential variant for error cases or
   // when not working with typed objects.
   if (!IsObject(this) || !ObjectIsTypedObject(this))
     return callFunction(TypedArrayMap, this, a, b);
-  var thisType = TYPEDOBJ_TYPE_DESCR(this);
+  var thisType = TypedObjectTypeDescr(this);
   if (!TypeDescrIsArrayType(thisType))
     return callFunction(TypedArrayMap, this, a, b);
 
   if (typeof a === "number" && IsCallable(b))
     return MapTypedParImpl(this, a, thisType, b);
   else if (IsCallable(a))
     return MapTypedParImpl(this, 1, thisType, a);
   return callFunction(TypedArrayMap, this, a, b);
 }
 
 // Warning: user exposed!
 function TypedArrayReduce(a, b) {
   // Arguments: func, [initial]
   if (!IsObject(this) || !ObjectIsTypedObject(this))
     return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
-  var thisType = TYPEDOBJ_TYPE_DESCR(this);
+  var thisType = TypedObjectTypeDescr(this);
   if (!TypeDescrIsArrayType(thisType))
     return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   if (a !== undefined && typeof a !== "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) || !ObjectIsTypedObject(this))
     return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
-  var thisType = TYPEDOBJ_TYPE_DESCR(this);
+  var thisType = TypedObjectTypeDescr(this);
   if (!TypeDescrIsArrayType(thisType))
     return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   if (!IsObject(a) || !ObjectIsTypeDescr(a) || !TypeDescrIsSizedArrayType(a))
     return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   if (d !== undefined && typeof d !== "function")
     return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
@@ -744,17 +751,17 @@ function TypedArrayScatter(a, b, c, d) {
   return ScatterTypedSeqImpl(this, a, b, c, d);
 }
 
 // Warning: user exposed!
 function TypedArrayFilter(func) {
   // Arguments: predicate
   if (!IsObject(this) || !ObjectIsTypedObject(this))
     return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
-  var thisType = TYPEDOBJ_TYPE_DESCR(this);
+  var thisType = TypedObjectTypeDescr(this);
   if (!TypeDescrIsArrayType(thisType))
     return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   if (typeof func !== "function")
     return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   return FilterTypedSeqImpl(this, func);
 }
@@ -1127,17 +1134,17 @@ function RedirectPointer(typedObj, offse
     // Therefore, we reuse a pointer if we are both in parallel mode
     // and we have a transparent output type.  It'd be nice to loosen
     // this condition later by using fancy ion optimizations that
     // assume the value won't escape and copy it if it does. But those
     // don't exist yet. Moreover, checking if the type is transparent
     // is an overapproximation: users can manually declare opaque
     // types that nonetheless only contain scalar data.
 
-    typedObj = NewDerivedTypedObject(TYPEDOBJ_TYPE_DESCR(typedObj),
+    typedObj = NewDerivedTypedObject(TypedObjectTypeDescr(typedObj),
                                      typedObj, 0);
   }
 
   SetTypedObjectOffset(typedObj, offset);
   return typedObj;
 }
 SetScriptHints(RedirectPointer,         { inline: true });
 
--- a/js/src/builtin/TypedObjectConstants.h
+++ b/js/src/builtin/TypedObjectConstants.h
@@ -94,17 +94,17 @@
 #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_DATAVIEW_SLOTS              4 // Number of slots for data views
 
 #define JS_TYPEDOBJ_SLOT_LENGTH           4 // Length of array (see (*) below)
-#define JS_TYPEDOBJ_SLOT_TYPE_DESCR       5 // For typed objects, type descr
+#define JS_TYPEDARR_SLOT_TYPE             5 // For typed arrays only, type code
 
 #define JS_TYPEDOBJ_SLOT_DATA             7 // private slot, based on alloc kind
 #define JS_TYPEDOBJ_SLOTS                 6 // 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* typed objects, no matter what
 // type they have.
--- a/js/src/jit-test/tests/TypedObject/prototypes.js
+++ b/js/src/jit-test/tests/TypedObject/prototypes.js
@@ -1,22 +1,33 @@
 // API Surface Test: check that mutating prototypes
-// of type descriptors has no effect.
+// of type objects has no effect, and that mutating
+// the prototypes of typed objects is an error.
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
+load(libdir + "asserts.js");
+
 var {StructType, uint32, Object, Any, storage, objectType} = TypedObject;
 
 function main() { // once a C programmer, always a C programmer.
   var Uints = new StructType({f: uint32, g: uint32});
   var p = Uints.prototype;
   Uints.prototype = {}; // no effect
   assertEq(p, Uints.prototype);
 
+  var uints = new Uints();
+  assertEq(uints.__proto__, p);
+  assertThrowsInstanceOf(function() uints.__proto__ = {},
+                         TypeError);
+  assertThrowsInstanceOf(function() Object.setPrototypeOf(uints, {}),
+                         TypeError);
+  assertEq(uints.__proto__, p);
+
   var Uintss = Uints.array(2);
   var p = Uintss.prototype;
   Uintss.prototype = {}; // no effect
   assertEq(p, Uintss.prototype);
 
   print("Tests complete");
 }
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -4302,16 +4302,33 @@ CodeGenerator::visitNeuterCheck(LNeuterC
     Imm32 flag(ArrayBufferObject::neuteredFlag());
     if (!bailoutTest32(Assembler::NonZero, temp, flag, lir->snapshot()))
         return false;
 
     return true;
 }
 
 bool
+CodeGenerator::visitTypedObjectProto(LTypedObjectProto *lir)
+{
+    Register obj = ToRegister(lir->object());
+    JS_ASSERT(ToRegister(lir->output()) == ReturnReg);
+
+    // Eventually we ought to inline this helper function for
+    // efficiency, but it's mildly non-trivial since we must reach
+    // into the type object and so on.
+
+    const Register tempReg = ToRegister(lir->temp());
+    masm.setupUnalignedABICall(1, tempReg);
+    masm.passABIArg(obj);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, TypedObjectProto));
+    return true;
+}
+
+bool
 CodeGenerator::visitTypedObjectElements(LTypedObjectElements *lir)
 {
     Register obj = ToRegister(lir->object());
     Register out = ToRegister(lir->output());
     masm.loadPtr(Address(obj, TypedObject::offsetOfDataSlot()), out);
     return true;
 }
 
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -166,16 +166,17 @@ class CodeGenerator : public CodeGenerat
     bool visitLoadArrowThis(LLoadArrowThis *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 visitSetTypedObjectOffset(LSetTypedObjectOffset *lir);
+    bool visitTypedObjectProto(LTypedObjectProto *ins);
     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);
     bool visitBoundsCheckLower(LBoundsCheckLower *lir);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -10269,20 +10269,26 @@ 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_TYPEDOBJ_SLOT_TYPE_DESCR);
+    MInstruction *proto = MTypedObjectProto::New(alloc(), typedObj);
+    current->add(proto);
+
+    MInstruction *load = MLoadFixedSlot::New(alloc(), proto, JS_TYPROTO_SLOT_DESCR);
     current->add(load);
-    return load;
+
+    MInstruction *unbox = MUnbox::New(alloc(), load, MIRType_Object, MUnbox::Infallible);
+    current->add(unbox);
+
+    return unbox;
 }
 
 // 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
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -3689,16 +3689,35 @@ class LTypedArrayElements : public LInst
     LTypedArrayElements(const LAllocation &object) {
         setOperand(0, object);
     }
     const LAllocation *object() {
         return getOperand(0);
     }
 };
 
+// Load a typed object's prototype, which is guaranteed to be a
+// TypedProto object.
+class LTypedObjectProto : public LCallInstructionHelper<1, 1, 1>
+{
+  public:
+    LIR_HEADER(TypedObjectProto)
+
+    LTypedObjectProto(const LAllocation &object, const LDefinition &temp1) {
+        setOperand(0, object);
+        setTemp(0, temp1);
+    }
+    const LAllocation *object() {
+        return getOperand(0);
+    }
+    const LDefinition *temp() {
+        return getTemp(0);
+    }
+};
+
 // Load a typed array's elements vector.
 class LTypedObjectElements : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(TypedObjectElements)
 
     LTypedObjectElements(const LAllocation &object) {
         setOperand(0, object);
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -245,16 +245,17 @@
     _(IteratorStart)                \
     _(IteratorNext)                 \
     _(IteratorMore)                 \
     _(IteratorEnd)                  \
     _(ArrayLength)                  \
     _(SetArrayLength)               \
     _(TypedArrayLength)             \
     _(TypedArrayElements)           \
+    _(TypedObjectProto)             \
     _(TypedObjectElements)          \
     _(SetTypedObjectOffset)         \
     _(StringLength)                 \
     _(ArgumentsLength)              \
     _(GetFrameArgument)             \
     _(SetFrameArgumentT)            \
     _(SetFrameArgumentC)            \
     _(SetFrameArgumentV)            \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2357,16 +2357,26 @@ LIRGenerator::visitTypedArrayLength(MTyp
 bool
 LIRGenerator::visitTypedArrayElements(MTypedArrayElements *ins)
 {
     JS_ASSERT(ins->type() == MIRType_Elements);
     return define(new(alloc()) LTypedArrayElements(useRegisterAtStart(ins->object())), ins);
 }
 
 bool
+LIRGenerator::visitTypedObjectProto(MTypedObjectProto *ins)
+{
+    JS_ASSERT(ins->type() == MIRType_Object);
+    return defineReturn(new(alloc()) LTypedObjectProto(
+                            useFixed(ins->object(), CallTempReg0),
+                            tempFixed(CallTempReg1)),
+                        ins);
+}
+
+bool
 LIRGenerator::visitTypedObjectElements(MTypedObjectElements *ins)
 {
     JS_ASSERT(ins->type() == MIRType_Elements);
     return define(new(alloc()) LTypedObjectElements(useRegisterAtStart(ins->object())), ins);
 }
 
 bool
 LIRGenerator::visitSetTypedObjectOffset(MSetTypedObjectOffset *ins)
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -177,16 +177,17 @@ class LIRGenerator : public LIRGenerator
     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 visitSetTypedObjectOffset(MSetTypedObjectOffset *ins);
+    bool visitTypedObjectProto(MTypedObjectProto *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);
     bool visitStoreElement(MStoreElement *ins);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -1612,16 +1612,49 @@ class MNewPar : public MUnaryInstruction
         return getOperand(0);
     }
 
     JSObject *templateObject() const {
         return templateObject_;
     }
 };
 
+class MTypedObjectProto
+  : public MUnaryInstruction,
+    public SingleObjectPolicy
+{
+  private:
+    MTypedObjectProto(MDefinition *object)
+      : MUnaryInstruction(object)
+    {
+        setResultType(MIRType_Object);
+        setMovable();
+    }
+
+  public:
+    INSTRUCTION_HEADER(TypedObjectProto)
+
+    static MTypedObjectProto *New(TempAllocator &alloc, MDefinition *object) {
+        return new(alloc) MTypedObjectProto(object);
+    }
+
+    TypePolicy *typePolicy() {
+        return this;
+    }
+    MDefinition *object() const {
+        return getOperand(0);
+    }
+    bool congruentTo(const MDefinition *ins) const {
+        return congruentIfOperandsEqual(ins);
+    }
+    AliasSet getAliasSet() const {
+        return AliasSet::Load(AliasSet::ObjectFields);
+    }
+};
+
 // Creates a new derived type object. At runtime, this is just a call
 // to `BinaryBlock::createDerived()`. That is, the MIR itself does not
 // compile to particularly optimized code. However, using a distinct
 // MIR for creating derived type objects allows the compiler to
 // optimize ephemeral typed objects as would be created for a
 // reference like `a.b.c` -- here, the `a.b` will create an ephemeral
 // derived type object that aliases the memory of `a` itself. The
 // specific nature of `a.b` is revealed by using
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -131,16 +131,17 @@ namespace jit {
     _(GuardShapePolymorphic)                                                \
     _(GuardObjectType)                                                      \
     _(GuardObjectIdentity)                                                  \
     _(GuardClass)                                                           \
     _(ArrayLength)                                                          \
     _(SetArrayLength)                                                       \
     _(TypedArrayLength)                                                     \
     _(TypedArrayElements)                                                   \
+    _(TypedObjectProto)                                                     \
     _(TypedObjectElements)                                                  \
     _(SetTypedObjectOffset)                                                 \
     _(InitializedLength)                                                    \
     _(SetInitializedLength)                                                 \
     _(Not)                                                                  \
     _(NeuterCheck)                                                          \
     _(BoundsCheck)                                                          \
     _(BoundsCheckLower)                                                     \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -219,16 +219,17 @@ class ParallelSafetyVisitor : public MIn
     SAFE_OP(GuardObjectType)
     SAFE_OP(GuardObjectIdentity)
     SAFE_OP(GuardClass)
     SAFE_OP(AssertRange)
     SAFE_OP(ArrayLength)
     WRITE_GUARDED_OP(SetArrayLength, elements)
     SAFE_OP(TypedArrayLength)
     SAFE_OP(TypedArrayElements)
+    SAFE_OP(TypedObjectProto)
     SAFE_OP(TypedObjectElements)
     SAFE_OP(SetTypedObjectOffset)
     SAFE_OP(InitializedLength)
     WRITE_GUARDED_OP(SetInitializedLength, elements)
     SAFE_OP(Not)
     SAFE_OP(NeuterCheck)
     SAFE_OP(BoundsCheck)
     SAFE_OP(BoundsCheckLower)
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1150,10 +1150,19 @@ AssertValidValue(JSContext *cx, Value *v
     }
     if (v->isString()) {
         AssertValidStringPtr(cx, v->toString());
         return;
     }
 }
 #endif
 
+// Definition of the MTypedObjectProto MIR.
+JSObject *
+TypedObjectProto(JSObject *obj)
+{
+    JS_ASSERT(obj->is<TypedObject>());
+    TypedObject &typedObj = obj->as<TypedObject>();
+    return &typedObj.typedProto();
+}
+
 } // namespace jit
 } // namespace js
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -685,12 +685,14 @@ bool SetDenseElement(JSContext *cx, Hand
                      bool strict);
 
 #ifdef DEBUG
 void AssertValidObjectPtr(JSContext *cx, JSObject *obj);
 void AssertValidStringPtr(JSContext *cx, JSString *str);
 void AssertValidValue(JSContext *cx, Value *v);
 #endif
 
+JSObject *TypedObjectProto(JSObject *obj);
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_VMFunctions_h */
--- a/js/src/vm/TypedArrayObject.h
+++ b/js/src/vm/TypedArrayObject.h
@@ -27,17 +27,17 @@ namespace js {
  */
 
 class TypedArrayObject : public ArrayBufferViewObject
 {
   protected:
     // Typed array properties stored in slots, beyond those shared by all
     // ArrayBufferViews.
     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 TYPE_SLOT      = JS_TYPEDARR_SLOT_TYPE;
     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 const size_t FIXED_DATA_START = DATA_SLOT + 1;