Backed out 4 changesets (bug 973238) for build bustage on a CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Fri, 16 May 2014 13:52:49 -0700
changeset 183634 a1a599888834c784e2db167d9d5aec852862aefe
parent 183633 505f38b0649b153adb70834fb1d1808bfc91d227
child 183635 7dabcbd22f0943f0d40705d644bc2c1577ec7429
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)
bugs973238
milestone32.0a1
backs outcc81a0f56362553c66ac50ccb1d41ae7e8352662
472dffac7f217819e028cd3886b09c48d88a7ff7
e40e2dbf7d9e0860ca3e6975cc2722a311aa3484
2ca44eb13ecf1014d45de955afc93bb71b4ac359
Backed out 4 changesets (bug 973238) for build bustage on a CLOSED TREE Backed out changeset cc81a0f56362 (bug 973238) Backed out changeset 472dffac7f21 (bug 973238) Backed out changeset e40e2dbf7d9e (bug 973238) Backed out changeset 2ca44eb13ecf (bug 973238)
js/src/builtin/TypedObject.cpp
js/src/builtin/TypedObject.h
js/src/builtin/TypedObject.js
js/src/builtin/TypedObjectConstants.h
js/src/gc/Marking.cpp
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/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/vm/TypedArrayObject.h
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -817,17 +817,18 @@ js::IsTypedObjectArray(JSObject &obj)
 }
 
 /*********************************
  * StructType class
  */
 
 const Class StructTypeDescr::class_ = {
     "StructType",
-    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS),
+    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) |
+    JSCLASS_HAS_PRIVATE, // used to store FieldList
     JS_PropertyStub,
     JS_DeletePropertyStub,
     JS_PropertyStub,
     JS_StrictPropertyStub,
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     nullptr, /* finalize */
@@ -1478,16 +1479,27 @@ 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;
+        }
+    }
 
     return static_cast<TypedObject*>(&*obj);
 }
 
 void
 TypedObject::attach(ArrayBufferObject &buffer, int32_t offset)
 {
     JS_ASSERT(offset >= 0);
@@ -1621,16 +1633,19 @@ 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,22 +650,18 @@ 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 typedProto().typeDescr();
+        return getReservedSlot(JS_TYPEDOBJ_SLOT_TYPE_DESCR).toObject().as<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,39 +23,32 @@
     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
@@ -194,17 +187,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, TypedObjectTypeDescr(fromValue))) {
+    if (!descr.variable && DescrsEquiv(descr, TYPEDOBJ_TYPE_DESCR(fromValue))) {
       if (!TypedObjectIsAttached(fromValue))
         ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
 
       var size = DESCR_SIZE(descr);
       Memcpy(typedObj, offset, fromValue, 0, size);
       return;
     }
   }
@@ -388,17 +381,17 @@ function Reify(sourceDescr,
 
   return TypedObjectGet(sourceDescr, sourceTypedObj, sourceOffset);
 }
 
 function FillTypedArrayWithValue(destArray, fromValue) {
   assert(IsObject(handle) && ObjectIsTypedObject(destArray),
          "FillTypedArrayWithValue: not typed handle");
 
-  var descr = TypedObjectTypeDescr(destArray);
+  var descr = TYPEDOBJ_TYPE_DESCR(destArray);
   var length = DESCR_SIZED_ARRAY_LENGTH(descr);
   if (length === 0)
     return;
 
   // Use convert and copy to to produce the first element:
   var elemDescr = DESCR_ARRAY_ELEMENT_TYPE(descr);
   TypedObjectSet(elemDescr, destArray, 0, fromValue);
 
@@ -442,17 +435,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 = TypedObjectTypeDescr(this);
+  var oldArrayType = TYPEDOBJ_TYPE_DESCR(this);
   var oldArrayReprKind = DESCR_KIND(oldArrayType);
   var oldElementType = oldArrayType;
   var oldElementCount = 1;
   switch (oldArrayReprKind) {
   case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
     oldElementCount *= this.length;
     oldElementType = oldElementType.elementType;
     break;
@@ -509,17 +502,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 = TypedObjectTypeDescr(this);
+  var descr = TYPEDOBJ_TYPE_DESCR(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+")";
 }
 
@@ -587,17 +580,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 TypedObjectTypeDescr(obj);
+    return TYPEDOBJ_TYPE_DESCR(obj);
 
   // Note: Do not create bindings for `Any`, `String`, etc in
   // Utilities.js, but rather access them through
   // `GetTypedObjectModule()`. The reason is that bindings
   // you create in Utilities.js are part of the self-hosted global,
   // vs the user-accessible global, and hence should not escape to
   // user script.
   var T = GetTypedObjectModule();
@@ -681,17 +674,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 = TypedObjectTypeDescr(this);
+  var thisType = TYPEDOBJ_TYPE_DESCR(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);
@@ -701,49 +694,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 = TypedObjectTypeDescr(this);
+  var thisType = TYPEDOBJ_TYPE_DESCR(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 = TypedObjectTypeDescr(this);
+  var thisType = TYPEDOBJ_TYPE_DESCR(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 = TypedObjectTypeDescr(this);
+  var thisType = TYPEDOBJ_TYPE_DESCR(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);
@@ -751,17 +744,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 = TypedObjectTypeDescr(this);
+  var thisType = TYPEDOBJ_TYPE_DESCR(this);
   if (!TypeDescrIsArrayType(thisType))
     return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   if (typeof func !== "function")
     return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   return FilterTypedSeqImpl(this, func);
 }
@@ -1134,17 +1127,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(TypedObjectTypeDescr(typedObj),
+    typedObj = NewDerivedTypedObject(TYPEDOBJ_TYPE_DESCR(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_TYPEDARR_SLOT_TYPE             5 // For typed arrays only, type code
+#define JS_TYPEDOBJ_SLOT_TYPE_DESCR       5 // For typed objects, type descr
 
 #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/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1254,16 +1254,18 @@ ScanTypeObject(GCMarker *gcmarker, types
         PushMarkStack(gcmarker, type->proto().toObject());
 
     if (type->singleton() && !type->lazy())
         PushMarkStack(gcmarker, type->singleton());
 
     if (type->hasNewScript()) {
         PushMarkStack(gcmarker, type->newScript()->fun);
         PushMarkStack(gcmarker, type->newScript()->templateObject);
+    } else if (type->hasTypedObject()) {
+        PushMarkStack(gcmarker, type->typedObject()->descrHeapPtr());
     }
 
     if (type->interpretedFunction)
         PushMarkStack(gcmarker, type->interpretedFunction);
 }
 
 static void
 gc::MarkChildren(JSTracer *trc, types::TypeObject *type)
@@ -1279,16 +1281,18 @@ gc::MarkChildren(JSTracer *trc, types::T
         MarkObject(trc, &type->protoRaw(), "type_proto");
 
     if (type->singleton() && !type->lazy())
         MarkObject(trc, &type->singletonRaw(), "type_singleton");
 
     if (type->hasNewScript()) {
         MarkObject(trc, &type->newScript()->fun, "type_new_function");
         MarkObject(trc, &type->newScript()->templateObject, "type_new_template");
+    } else if (type->hasTypedObject()) {
+        MarkObject(trc, &type->typedObject()->descrHeapPtr(), "type_heap_ptr");
     }
 
     if (type->interpretedFunction)
         MarkObject(trc, &type->interpretedFunction, "type_function");
 }
 
 static void
 gc::MarkChildren(JSTracer *trc, jit::JitCode *code)
--- a/js/src/jit-test/tests/TypedObject/prototypes.js
+++ b/js/src/jit-test/tests/TypedObject/prototypes.js
@@ -1,33 +1,22 @@
 // API Surface Test: check that mutating prototypes
-// of type objects has no effect, and that mutating
-// the prototypes of typed objects is an error.
+// of type descriptors has no effect.
 
 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,33 +4302,16 @@ 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,17 +166,16 @@ 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
@@ -10245,56 +10245,44 @@ IonBuilder::typeSetToTypeDescrSet(types:
 
     // And only known objects.
     if (types->unknownObject())
         return true;
 
     TypeDescrSetBuilder set;
     for (uint32_t i = 0; i < types->getObjectCount(); i++) {
         types::TypeObject *type = types->getTypeObject(i);
-        if (!type)
-            return true;
-
-        if (!IsTypedObjectClass(type->clasp()))
+        if (!type || type->unknownProperties())
             return true;
 
-        TaggedProto proto = type->proto();
-
-        // typed objects have immutable prototypes, and they are
-        // always instances of TypedProto
-        JS_ASSERT(proto.isObject() && proto.toObject()->is<TypedProto>());
-
-        TypedProto &typedProto = proto.toObject()->as<TypedProto>();
-        if (!set.insert(&typedProto.typeDescr()))
+        if (!type->hasTypedObject())
+            return true;
+
+        TypeDescr &descr = type->typedObject()->descr();
+        if (!set.insert(&descr))
             return false;
     }
 
     return set.build(*this, out);
 }
 
 MDefinition *
 IonBuilder::loadTypedObjectType(MDefinition *typedObj)
 {
     // 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 *proto = MTypedObjectProto::New(alloc(), typedObj);
-    current->add(proto);
-
-    MInstruction *load = MLoadFixedSlot::New(alloc(), proto, JS_TYPROTO_SLOT_DESCR);
+    MInstruction *load = MLoadFixedSlot::New(alloc(), typedObj,
+                                             JS_TYPEDOBJ_SLOT_TYPE_DESCR);
     current->add(load);
-
-    MInstruction *unbox = MUnbox::New(alloc(), load, MIRType_Object, MUnbox::Infallible);
-    current->add(unbox);
-
-    return unbox;
+    return load;
 }
 
 // Given a typed object `typedObj` and an offset `offset` into that
 // object's data, returns another typed object and adusted offset
 // where the data can be found. Often, these returned values are the
 // same as the inputs, but in cases where intermediate derived type
 // objects have been created, the return values will remove
 // intermediate layers (often rendering those derived type objects
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -3689,35 +3689,16 @@ 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,17 +245,16 @@
     _(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,26 +2357,16 @@ 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,17 +177,16 @@ 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,49 +1612,16 @@ 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,17 +131,16 @@ 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,17 +219,16 @@ 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,19 +1150,10 @@ 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,14 +685,12 @@ 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/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -3035,39 +3035,16 @@ TypeObject::markUnknown(ExclusiveContext
         if (prop) {
             prop->types.addType(cx, Type::UnknownType());
             prop->types.setNonDataProperty(cx);
         }
     }
 }
 
 void
-TypeObject::maybeClearNewScriptAddendumOnOOM()
-{
-    if (!isMarked())
-        return;
-
-    if (!addendum || addendum->kind != TypeObjectAddendum::NewScript)
-        return;
-
-    for (unsigned i = 0; i < getPropertyCount(); i++) {
-        Property *prop = getProperty(i);
-        if (!prop)
-            continue;
-        if (prop->types.definiteProperty())
-            prop->types.setNonDataPropertyIgnoringConstraints();
-    }
-
-    // This method is called during GC sweeping, so there is no write barrier
-    // that needs to be triggered.
-    js_free(addendum);
-    addendum.unsafeSet(nullptr);
-}
-
-void
 TypeObject::clearAddendum(ExclusiveContext *cx)
 {
     JS_ASSERT(!(flags() & OBJECT_FLAG_ADDENDUM_CLEARED));
 
     addFlags(OBJECT_FLAG_ADDENDUM_CLEARED);
 
     /*
      * It is possible for the object to not have a new script or other
@@ -3081,16 +3058,20 @@ TypeObject::clearAddendum(ExclusiveConte
      */
     if (!addendum)
         return;
 
     switch (addendum->kind) {
       case TypeObjectAddendum::NewScript:
         clearNewScriptAddendum(cx);
         break;
+
+      case TypeObjectAddendum::TypedObject:
+        clearTypedObjectAddendum(cx);
+        break;
     }
 
     /* We nullptr out addendum *before* freeing it so the write barrier works. */
     TypeObjectAddendum *savedAddendum = addendum;
     addendum = nullptr;
     js_free(savedAddendum);
 
     markStateChange(cx);
@@ -3196,16 +3177,44 @@ TypeObject::clearNewScriptAddendum(Exclu
         }
     } else {
         // Threads with an ExclusiveContext are not allowed to run scripts.
         JS_ASSERT(!cx->perThreadData->activation());
     }
 }
 
 void
+TypeObject::maybeClearNewScriptAddendumOnOOM()
+{
+    if (!isMarked())
+        return;
+
+    if (!addendum || addendum->kind != TypeObjectAddendum::NewScript)
+        return;
+
+    for (unsigned i = 0; i < getPropertyCount(); i++) {
+        Property *prop = getProperty(i);
+        if (!prop)
+            continue;
+        if (prop->types.definiteProperty())
+            prop->types.setNonDataPropertyIgnoringConstraints();
+    }
+
+    // This method is called during GC sweeping, so there is no write barrier
+    // that needs to be triggered.
+    js_free(addendum);
+    addendum.unsafeSet(nullptr);
+}
+
+void
+TypeObject::clearTypedObjectAddendum(ExclusiveContext *cx)
+{
+}
+
+void
 TypeObject::print()
 {
     TaggedProto tagged(proto());
     fprintf(stderr, "%s : %s",
             TypeObjectString(this),
             tagged.isObject() ? TypeString(Type::ObjectType(tagged.toObject()))
                               : (tagged.isLazy() ? "(lazy)" : "(null)"));
 
@@ -4546,22 +4555,67 @@ TypeScript::printTypes(JSContext *cx, Ha
             fprintf(stderr, "\n");
         }
     }
 
     fprintf(stderr, "\n");
 }
 #endif /* DEBUG */
 
+/////////////////////////////////////////////////////////////////////
+// Binary data
+/////////////////////////////////////////////////////////////////////
+
 void
 TypeObject::setAddendum(TypeObjectAddendum *addendum)
 {
     this->addendum = addendum;
 }
 
+bool
+TypeObject::addTypedObjectAddendum(JSContext *cx, Handle<TypeDescr*> descr)
+{
+    // Type descriptors are always pre-tenured. This is both because
+    // we expect them to live a long time and so that they can be
+    // safely accessed during ion compilation.
+    JS_ASSERT(!IsInsideNursery(cx->runtime(), descr));
+    JS_ASSERT(descr);
+
+    if (flags() & OBJECT_FLAG_ADDENDUM_CLEARED)
+        return true;
+
+    JS_ASSERT(!unknownProperties());
+
+    if (addendum) {
+        JS_ASSERT(hasTypedObject());
+        JS_ASSERT(&typedObject()->descr() == descr);
+        return true;
+    }
+
+    TypeTypedObject *typedObject = js_new<TypeTypedObject>(descr);
+    if (!typedObject)
+        return false;
+    addendum = typedObject;
+    return true;
+}
+
+/////////////////////////////////////////////////////////////////////
+// Type object addenda constructor
+/////////////////////////////////////////////////////////////////////
+
 TypeObjectAddendum::TypeObjectAddendum(Kind kind)
   : kind(kind)
 {}
 
 TypeNewScript::TypeNewScript()
   : TypeObjectAddendum(NewScript)
 {}
 
+TypeTypedObject::TypeTypedObject(Handle<TypeDescr*> descr)
+  : TypeObjectAddendum(TypedObject),
+    descr_(descr)
+{
+}
+
+TypeDescr &
+js::types::TypeTypedObject::descr() {
+    return descr_->as<TypeDescr>();
+}
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -807,36 +807,47 @@ struct Property
       : id(o.id.get()), types(o.types)
     {}
 
     static uint32_t keyBits(jsid id) { return uint32_t(JSID_BITS(id)); }
     static jsid getKey(Property *p) { return p->id; }
 };
 
 struct TypeNewScript;
+struct TypeTypedObject;
 
 struct TypeObjectAddendum
 {
     enum Kind {
-        NewScript
+        NewScript,
+        TypedObject
     };
 
     TypeObjectAddendum(Kind kind);
 
     const Kind kind;
 
     bool isNewScript() {
         return kind == NewScript;
     }
 
     TypeNewScript *asNewScript() {
         JS_ASSERT(isNewScript());
         return (TypeNewScript*) this;
     }
 
+    bool isTypedObject() {
+        return kind == TypedObject;
+    }
+
+    TypeTypedObject *asTypedObject() {
+        JS_ASSERT(isTypedObject());
+        return (TypeTypedObject*) this;
+    }
+
     static inline void writeBarrierPre(TypeObjectAddendum *type);
 
     static void writeBarrierPost(TypeObjectAddendum *newScript, void *addr) {}
 };
 
 /*
  * Information attached to a TypeObject if it is always constructed using 'new'
  * on a particular script. This is used to manage state related to the definite
@@ -883,16 +894,31 @@ struct TypeNewScript : public TypeObject
           : kind(kind), offset(offset)
         {}
     };
     Initializer *initializerList;
 
     static inline void writeBarrierPre(TypeNewScript *newScript);
 };
 
+struct TypeTypedObject : public TypeObjectAddendum
+{
+  private:
+    HeapPtrObject descr_;
+
+  public:
+    TypeTypedObject(Handle<TypeDescr*> descr);
+
+    HeapPtrObject &descrHeapPtr() {
+        return descr_;
+    }
+
+    TypeDescr &descr();
+};
+
 /*
  * Lazy type objects overview.
  *
  * Type objects which represent at most one JS object are constructed lazily.
  * These include types for native functions, standard classes, scripted
  * functions defined at the top level of global/eval scripts, and in some
  * other cases. Typical web workloads often create many windows (and many
  * copies of standard natives) and many scripts, with comparatively few
@@ -996,18 +1022,35 @@ struct TypeObject : gc::BarrieredCell<Ty
     bool hasNewScript() const {
         return addendum && addendum->isNewScript();
     }
 
     TypeNewScript *newScript() {
         return addendum->asNewScript();
     }
 
+    bool hasTypedObject() {
+        return addendum && addendum->isTypedObject();
+    }
+
+    TypeTypedObject *typedObject() {
+        return addendum->asTypedObject();
+    }
+
     void setAddendum(TypeObjectAddendum *addendum);
 
+    /*
+     * Tag the type object for a binary data type descriptor, instance,
+     * or handle with the type representation of the data it points at.
+     * If this type object is already tagged with a binary data addendum,
+     * this addendum must already be associated with the same TypeRepresentation,
+     * and the method has no effect.
+     */
+    bool addTypedObjectAddendum(JSContext *cx, Handle<TypeDescr*> descr);
+
   private:
     /*
      * Properties of this object. This may contain JSID_VOID, representing the
      * types of all integer indexes of the object, and/or JSID_EMPTY, holding
      * constraints listening to changes to the object's state.
      *
      * The type sets in the properties of a type object describe the possible
      * values that can be read out of that property in actual JS objects.
@@ -1109,19 +1152,20 @@ struct TypeObject : gc::BarrieredCell<Ty
     void addPrototype(JSContext *cx, TypeObject *proto);
     void addPropertyType(ExclusiveContext *cx, jsid id, Type type);
     void addPropertyType(ExclusiveContext *cx, jsid id, const Value &value);
     void markPropertyNonData(ExclusiveContext *cx, jsid id);
     void markPropertyNonWritable(ExclusiveContext *cx, jsid id);
     void markStateChange(ExclusiveContext *cx);
     void setFlags(ExclusiveContext *cx, TypeObjectFlags flags);
     void markUnknown(ExclusiveContext *cx);
-    void maybeClearNewScriptAddendumOnOOM();
     void clearAddendum(ExclusiveContext *cx);
     void clearNewScriptAddendum(ExclusiveContext *cx);
+    void clearTypedObjectAddendum(ExclusiveContext *cx);
+    void maybeClearNewScriptAddendumOnOOM();
     bool isPropertyNonData(jsid id);
     bool isPropertyNonWritable(jsid id);
 
     void print();
 
     inline void clearProperties();
     inline void sweep(FreeOp *fop, bool *oom);
 
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -1219,16 +1219,19 @@ TypeObjectAddendum::writeBarrierPre(Type
 {
 #ifdef JSGC_INCREMENTAL
     if (!type)
         return;
 
     switch (type->kind) {
       case NewScript:
         return TypeNewScript::writeBarrierPre(type->asNewScript());
+
+      case TypedObject:
+        return TypeTypedObject::writeBarrierPre(type->asTypedObject());
     }
 #endif
 }
 
 inline void
 TypeNewScript::writeBarrierPre(TypeNewScript *newScript)
 {
 #ifdef JSGC_INCREMENTAL
--- 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_TYPEDARR_SLOT_TYPE;
+    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 const size_t FIXED_DATA_START = DATA_SLOT + 1;