Bug 898342 - Implement movable handles for Typed Objects r=till
authorNicholas D. Matsakis <nmatsakis@mozilla.com>
Wed, 11 Sep 2013 16:24:23 -0400
changeset 151879 35a83682c173c50c54901ccb5781448d2f66cc99
parent 151878 eafe6b0acd33631cedf91cfe2f6245ea634bf2f9
child 151880 06880eeb81a0193c82242b7e519ac3747871c626
push id25512
push usercbook@mozilla.com
push dateThu, 24 Oct 2013 05:06:01 +0000
treeherderautoland@19fd3388c372 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill
bugs898342
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 898342 - Implement movable handles for Typed Objects r=till
js/src/builtin/TypeRepresentation.cpp
js/src/builtin/TypeRepresentation.h
js/src/builtin/TypedObject.cpp
js/src/builtin/TypedObject.h
js/src/builtin/TypedObject.js
js/src/builtin/TypedObjectConstants.h
js/src/jit/CodeGenerator.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/VMFunctions.cpp
js/src/js.msg
js/src/tests/ecma_6/TypedObject/handle.js
js/src/tests/ecma_6/TypedObject/handle_get_set.js
js/src/tests/ecma_6/TypedObject/handle_move.js
js/src/tests/ecma_6/TypedObject/handle_unattached.js
js/src/vm/CommonPropertyNames.h
js/src/vm/SelfHosting.cpp
--- a/js/src/builtin/TypeRepresentation.cpp
+++ b/js/src/builtin/TypeRepresentation.cpp
@@ -218,17 +218,17 @@ StructTypeRepresentation::init(JSContext
 
     // We compute alignment into the field `align_` directly in the
     // loop below, but not `size_` because we have to very careful
     // about overflow. For now, we always use a uint32_t for
     // consistency across build environments.
     uint32_t totalSize = 0;
 
     for (size_t i = 0; i < ids.length(); i++) {
-        TypeRepresentation *fieldTypeRepr = fromOwnerObject(typeReprOwners[i]);
+        TypeRepresentation *fieldTypeRepr = fromOwnerObject(*typeReprOwners[i]);
 
         uint32_t alignedSize = alignTo(totalSize, fieldTypeRepr->alignment());
         if (alignedSize < totalSize) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                                  JSMSG_TYPEDOBJECT_TOO_BIG);
             return false;
         }
 
@@ -407,17 +407,17 @@ TypeRepresentation::mark(JSTracer *trace
     // contents. This is the typical scheme for marking objects.  See
     // gc/Marking.cpp for more details.
     gc::MarkObject(trace, &ownerObject_, "typeRepresentation_ownerObject");
 }
 
 /*static*/ void
 TypeRepresentation::obj_trace(JSTracer *trace, JSObject *object)
 {
-    fromOwnerObject(object)->traceFields(trace);
+    fromOwnerObject(*object)->traceFields(trace);
 }
 
 void
 TypeRepresentation::traceFields(JSTracer *trace)
 {
     mark(trace); // don't forget to mark the self-reference here!
 
     switch (kind()) {
@@ -452,17 +452,17 @@ ArrayTypeRepresentation::traceArrayField
 
 ///////////////////////////////////////////////////////////////////////////
 // Finalization
 
 /*static*/ void
 TypeRepresentation::obj_finalize(js::FreeOp *fop, JSObject *object)
 {
     JSCompartment *comp = object->compartment();
-    TypeRepresentation *typeRepr = fromOwnerObject(object);
+    TypeRepresentation *typeRepr = fromOwnerObject(*object);
     comp->typeReprs.remove(typeRepr);
     js_free(typeRepr);
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // To string
 
 bool
@@ -568,21 +568,21 @@ StructTypeRepresentation::fieldNamed(jsi
     for (size_t i = 0; i < fieldCount(); i++) {
         if (field(i).id.get() == id)
             return &field(i);
     }
     return nullptr;
 }
 
 /*static*/ bool
-TypeRepresentation::isTypeRepresentationOwnerObject(JSObject *obj)
+TypeRepresentation::isOwnerObject(JSObject &obj)
 {
-    return obj->getClass() == &class_;
+    return obj.getClass() == &class_;
 }
 
 /*static*/ TypeRepresentation *
-TypeRepresentation::fromOwnerObject(JSObject *obj)
+TypeRepresentation::fromOwnerObject(JSObject &obj)
 {
-    JS_ASSERT(obj->getClass() == &class_);
-    return (TypeRepresentation*) obj->getPrivate();
+    JS_ASSERT(obj.getClass() == &class_);
+    return (TypeRepresentation*) obj.getPrivate();
 }
 
 
--- a/js/src/builtin/TypeRepresentation.h
+++ b/js/src/builtin/TypeRepresentation.h
@@ -118,18 +118,18 @@ class TypeRepresentation {
     size_t alignment() const { return alignment_; }
     Kind kind() const { return kind_; }
     JSObject *ownerObject() const { return ownerObject_.get(); }
 
     // Appends a stringified form of this type representation onto
     // buffer, for use in error messages and the like.
     bool appendString(JSContext *cx, StringBuffer &buffer);
 
-    static bool isTypeRepresentationOwnerObject(JSObject *obj);
-    static TypeRepresentation *fromOwnerObject(JSObject *obj);
+    static bool isOwnerObject(JSObject &obj);
+    static TypeRepresentation *fromOwnerObject(JSObject &obj);
 
     bool isScalar() const {
         return kind() == Scalar;
     }
 
     ScalarTypeRepresentation *asScalar() {
         JS_ASSERT(isScalar());
         return (ScalarTypeRepresentation*) this;
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -36,32 +36,16 @@ const Class js::TypedObjectClass = {
     JS_DeletePropertyStub,   /* delProperty */
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub
 };
 
-/*
- * Reify() converts a binary value into a JS Object.
- *
- * The type of the value to be converted is described by `typeRepr`,
- * and `type` is an associated type descriptor object with that
- * same representation (note that there may be many descriptors
- * that all share the same `typeRepr`).
- *
- * The value is located at the offset `offset` in the binary block
- * associated with `owner`, which must be a BinaryBlock object.
- *
- * Upon success, the result is stored in `vp`.
- */
-static bool Reify(JSContext *cx, TypeRepresentation *typeRepr, HandleObject type,
-                  HandleObject owner, size_t offset, MutableHandleValue vp);
-
 static void
 ReportCannotConvertTo(JSContext *cx, HandleValue fromValue, const char *toType)
 {
     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
                          InformalValueTypeName(fromValue), toType);
 }
 
 static void
@@ -142,25 +126,25 @@ IsComplexTypeObject(JSObject &type)
 
 static inline bool
 IsTypeObject(JSObject &type)
 {
     return IsNumericTypeObject(type) || IsComplexTypeObject(type);
 }
 
 static inline bool
-IsBlock(JSObject &obj)
+IsTypedDatum(JSObject &obj)
 {
-    return obj.hasClass(&BinaryBlock::class_);
+    return obj.is<TypedObject>() || obj.is<TypedHandle>();
 }
 
 static inline uint8_t *
-BlockMem(JSObject &val)
+TypedMem(JSObject &val)
 {
-    JS_ASSERT(IsBlock(val));
+    JS_ASSERT(IsTypedDatum(val));
     return (uint8_t*) val.getPrivate();
 }
 
 /*
  * Given a user-visible type descriptor object, returns the
  * owner object for the TypeRepresentation* that we use internally.
  */
 static JSObject *
@@ -174,96 +158,144 @@ typeRepresentationOwnerObj(JSObject &typ
  * Given a user-visible type descriptor object, returns the
  * TypeRepresentation* that we use internally.
  *
  * Note: this pointer is valid only so long as `typeObj` remains rooted.
  */
 static TypeRepresentation *
 typeRepresentation(JSObject &typeObj)
 {
-    return TypeRepresentation::fromOwnerObject(typeRepresentationOwnerObj(typeObj));
+    return TypeRepresentation::fromOwnerObject(*typeRepresentationOwnerObj(typeObj));
 }
 
 static inline JSObject *
-GetType(JSObject &block)
+GetType(JSObject &datum)
 {
-    JS_ASSERT(IsBlock(block));
-    return &block.getReservedSlot(JS_TYPEDOBJ_SLOT_TYPE_OBJ).toObject();
+    JS_ASSERT(IsTypedDatum(datum));
+    return &datum.getReservedSlot(JS_DATUM_SLOT_TYPE_OBJ).toObject();
 }
 
 /*
- * Overwrites the contents of `block` at offset `offset` with `val`
- * converted to the type `typeObj`
+ * Overwrites the contents of `datum` at offset `offset` with `val`
+ * converted to the type `typeObj`. This is done by delegating to
+ * self-hosted code. This is used for assignments and initializations.
+ *
+ * For example, consider the final assignment in this snippet:
+ *
+ *    var Point = new StructType({x: float32, y: float32});
+ *    var Line = new StructType({from: Point, to: Point});
+ *    var line = new Line();
+ *    line.to = {x: 22, y: 44};
+ *
+ * This would result in a call to `ConvertAndCopyTo`
+ * where:
+ * - typeObj = Point
+ * - datum = line
+ * - offset = sizeof(Point) == 8
+ * - val = {x: 22, y: 44}
+ * This would result in loading the value of `x`, converting
+ * it to a float32, and hen storing it at the appropriate offset,
+ * and then doing the same for `y`.
+ *
+ * Note that the type of `typeObj` may not be the
+ * type of `datum` but rather some subcomponent of `datum`.
  */
 static bool
 ConvertAndCopyTo(JSContext *cx,
                  HandleObject typeObj,
-                 HandleObject block,
+                 HandleObject datum,
                  int32_t offset,
                  HandleValue val)
 {
+    JS_ASSERT(IsTypeObject(*typeObj));
+    JS_ASSERT(IsTypedDatum(*datum));
+
     RootedFunction func(
         cx, SelfHostedFunction(cx, cx->names().ConvertAndCopyTo));
     if (!func)
         return false;
 
     InvokeArgs args(cx);
     if (!args.init(5))
         return false;
 
     args.setCallee(ObjectValue(*func));
     args[0].setObject(*typeRepresentationOwnerObj(*typeObj));
     args[1].setObject(*typeObj);
-    args[2].setObject(*block);
+    args[2].setObject(*datum);
     args[3].setInt32(offset);
     args[4].set(val);
 
     return Invoke(cx, args);
 }
 
 static bool
-ConvertAndCopyTo(JSContext *cx, HandleObject block, HandleValue val)
+ConvertAndCopyTo(JSContext *cx, HandleObject datum, HandleValue val)
+{
+    RootedObject type(cx, GetType(*datum));
+    return ConvertAndCopyTo(cx, type, datum, 0, val);
+}
+
+/*
+ * Overwrites the contents of `datum` at offset `offset` with `val`
+ * converted to the type `typeObj`
+ */
+static bool
+Reify(JSContext *cx,
+      TypeRepresentation *typeRepr,
+      HandleObject type,
+      HandleObject datum,
+      size_t offset,
+      MutableHandleValue to)
 {
-    RootedObject type(cx, GetType(*block));
-    return ConvertAndCopyTo(cx, type, block, 0, val);
+    JS_ASSERT(IsTypeObject(*type));
+    JS_ASSERT(IsTypedDatum(*datum));
+
+    RootedFunction func(cx, SelfHostedFunction(cx, cx->names().Reify));
+    if (!func)
+        return false;
+
+    InvokeArgs args(cx);
+    if (!args.init(4))
+        return false;
+
+    args.setCallee(ObjectValue(*func));
+    args[0].setObject(*typeRepr->ownerObject());
+    args[1].setObject(*type);
+    args[2].setObject(*datum);
+    args[3].setInt32(offset);
+
+    if (!Invoke(cx, args))
+        return false;
+
+    to.set(args.rval());
+    return true;
 }
 
 static inline size_t
 TypeSize(JSObject &type)
 {
     return typeRepresentation(type)->size();
 }
 
 static inline size_t
-BlockSize(JSObject &val)
+DatumSize(JSObject &val)
 {
     return TypeSize(*GetType(val));
 }
 
 static inline bool
-IsBlockOfKind(JSObject &obj, TypeRepresentation::Kind kind)
+IsTypedDatumOfKind(JSObject &obj, TypeRepresentation::Kind kind)
 {
-    if (!IsBlock(obj))
+    if (!IsTypedDatum(obj))
         return false;
     TypeRepresentation *repr = typeRepresentation(*GetType(obj));
     return repr->kind() == kind;
 }
 
-static inline bool
-IsBinaryArray(JSObject &obj)
-{
-    return IsBlockOfKind(obj, TypeRepresentation::Array);
-}
-
-static inline bool
-IsBinaryStruct(JSObject &obj)
-{
-    return IsBlockOfKind(obj, TypeRepresentation::Struct);
-}
-
 static bool
 TypeEquivalent(JSContext *cx, unsigned int argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedObject thisObj(cx, ToObjectIfObject(args.thisv()));
     if (!thisObj || !IsTypeObject(*thisObj)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
@@ -305,94 +337,52 @@ TypeEquivalent(JSContext *cx, unsigned i
     nullptr,                                                                  \
     nullptr,                                                                  \
     NumericType<constant_, type_>::call,                                      \
     nullptr,                                                                  \
     nullptr,                                                                  \
     nullptr                                                                   \
 },
 
+static const JSFunctionSpec NumericTypeObjectMethods[] = {
+    {"handle", {NULL, NULL}, 2, 0, "HandleCreate"},
+    JS_FS_END
+};
+
 const Class js::NumericTypeClasses[ScalarTypeRepresentation::TYPE_MAX] = {
     JS_FOR_EACH_SCALAR_TYPE_REPR(BINARYDATA_NUMERIC_CLASSES)
 };
 
-#define BINARYDATA_TYPE_TO_CLASS(constant_, type_, name_)                     \
-    template <>                                                               \
-    const Class *                                                             \
-    NumericType<constant_, type_>::typeToClass()                              \
-    {                                                                         \
-        return &NumericTypeClasses[constant_];                                \
-    }
-
 template <typename T>
 static T ConvertScalar(double d)
 {
     if (TypeIsFloatingPoint<T>()) {
         return T(d);
     } else if (TypeIsUnsigned<T>()) {
         uint32_t n = ToUint32(d);
         return T(n);
     } else {
         int32_t n = ToInt32(d);
         return T(n);
     }
 }
 
-
-/**
- * This namespace declaration is required because of a weird 'specialization in
- * different namespace' error that happens in gcc, only on type specialized
- * template functions.
- */
-namespace js {
-JS_FOR_EACH_SCALAR_TYPE_REPR(BINARYDATA_TYPE_TO_CLASS);
-
-template <ScalarTypeRepresentation::Type type, typename T>
-JS_ALWAYS_INLINE
-bool NumericType<type, T>::reify(JSContext *cx, void *mem, MutableHandleValue vp)
-{
-    vp.setNumber((double)*((T*)mem) );
-    return true;
-}
-
-} // namespace js
-
 template<ScalarTypeRepresentation::Type N>
 static bool
 NumericTypeToString(JSContext *cx, unsigned int argc, Value *vp)
 {
     static_assert(N < ScalarTypeRepresentation::TYPE_MAX, "bad numeric type");
     CallArgs args = CallArgsFromVp(argc, vp);
     JSString *s = JS_NewStringCopyZ(cx, ScalarTypeRepresentation::typeName(N));
     if (!s)
         return false;
     args.rval().setString(s);
     return true;
 }
 
-static bool
-ReifyScalar(JSContext *cx, ScalarTypeRepresentation *typeRepr, HandleObject type,
-            HandleObject owner, size_t offset, MutableHandleValue to)
-{
-    JS_ASSERT(&NumericTypeClasses[0] <= type->getClass() &&
-              type->getClass() <= &NumericTypeClasses[ScalarTypeRepresentation::TYPE_MAX]);
-
-    switch (typeRepr->type()) {
-#define REIFY_CASES(constant_, type_, name_)                                  \
-        case constant_:                                                       \
-          return NumericType<constant_, type_>::reify(                        \
-                cx, BlockMem(*owner) + offset, to);
-        JS_FOR_EACH_SCALAR_TYPE_REPR(REIFY_CASES)
-    }
-#undef REIFY_CASES
-
-    MOZ_ASSUME_UNREACHABLE("Invalid type");
-    return false;
-}
-
 template <ScalarTypeRepresentation::Type type, typename T>
 bool
 NumericType<type, T>::call(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() < 1) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
                              args.callee().getClass()->name, "0", "s");
@@ -542,25 +532,26 @@ const Class ArrayType::class_ = {
     JS_StrictPropertyStub,
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
-    BinaryBlock::construct,
+    TypedObject::construct,
     nullptr
 };
 
 const JSPropertySpec ArrayType::typeObjectProperties[] = {
     JS_PS_END
 };
 
 const JSFunctionSpec ArrayType::typeObjectMethods[] = {
+    {"handle", {NULL, NULL}, 2, 0, "HandleCreate"},
     JS_FN("repeat", ArrayType::repeat, 1, 0),
     JS_FN("toSource", ArrayType::toSource, 0, 0),
     JS_FS_END
 };
 
 const JSPropertySpec ArrayType::typedObjectProperties[] = {
     JS_PS_END
 };
@@ -576,17 +567,17 @@ ArrayElementType(HandleObject array)
 {
     JS_ASSERT(IsArrayTypeObject(*array));
     return &array->getReservedSlot(JS_TYPEOBJ_SLOT_ARRAY_ELEM_TYPE).toObject();
 }
 
 static bool
 FillTypedArrayWithValue(JSContext *cx, HandleObject array, HandleValue val)
 {
-    JS_ASSERT(IsBinaryArray(*array));
+    JS_ASSERT(IsTypedDatumOfKind(*array, TypeRepresentation::Array));
 
     RootedFunction func(
         cx, SelfHostedFunction(cx, cx->names().FillTypedArrayWithValue));
     if (!func)
         return false;
 
     InvokeArgs args(cx);
     if (!args.init(2))
@@ -614,17 +605,17 @@ ArrayType::repeat(JSContext *cx, unsigne
     if (!thisObj || !IsArrayTypeObject(*thisObj)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                              JSMSG_INCOMPATIBLE_PROTO,
                              "ArrayType", "repeat",
                              InformalValueTypeName(args.thisv()));
         return false;
     }
 
-    RootedObject binaryArray(cx, BinaryBlock::createZeroed(cx, thisObj));
+    RootedObject binaryArray(cx, TypedObject::createZeroed(cx, thisObj));
     if (!binaryArray)
         return false;
 
     RootedValue val(cx, args[0]);
     if (!FillTypedArrayWithValue(cx, binaryArray, val))
         return false;
 
     args.rval().setObject(*binaryArray);
@@ -691,17 +682,17 @@ ArrayType::subarray(JSContext *cx, unsig
 
     if (!args[0].isInt32()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                              JSMSG_TYPEDOBJECT_SUBARRAY_INTEGER_ARG, "1");
         return false;
     }
 
     RootedObject thisObj(cx, &args.thisv().toObject());
-    if (!IsBinaryArray(*thisObj)) {
+    if (!IsTypedDatumOfKind(*thisObj, TypeRepresentation::Array)) {
         ReportCannotConvertTo(cx, thisObj, "binary array");
         return false;
     }
 
     RootedObject type(cx, GetType(*thisObj));
     ArrayTypeRepresentation *typeRepr = typeRepresentation(*type)->asArray();
     size_t length = typeRepr->length();
 
@@ -738,17 +729,18 @@ ArrayType::subarray(JSContext *cx, unsig
     RootedObject subArrayType(cx, ArrayType::create(cx, arrayTypeGlobal,
                                                     elementType, sublength));
     if (!subArrayType)
         return false;
 
     int32_t elementSize = typeRepr->element()->size();
     size_t offset = elementSize * begin;
 
-    RootedObject subarray(cx, BinaryBlock::createDerived(cx, subArrayType, thisObj, offset));
+    RootedObject subarray(
+        cx, TypedDatum::createDerived(cx, subArrayType, thisObj, offset));
     if (!subarray)
         return false;
 
     args.rval().setObject(*subarray);
     return true;
 }
 
 static bool
@@ -759,17 +751,17 @@ ArrayFillSubarray(JSContext *cx, unsigne
     if (args.length() < 1) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage,
                              nullptr, JSMSG_MORE_ARGS_NEEDED,
                              "fill()", "0", "s");
         return false;
     }
 
     RootedObject thisObj(cx, ToObjectIfObject(args.thisv()));
-    if (!thisObj || !IsBinaryArray(*thisObj)) {
+    if (!thisObj || !IsTypedDatumOfKind(*thisObj, TypeRepresentation::Array)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage,
                              nullptr, JSMSG_INCOMPATIBLE_PROTO,
                              "ArrayType", "fill",
                              InformalValueTypeName(args.thisv()));
         return false;
     }
 
     Value funArrayTypeVal = GetFunctionNativeReserved(&args.callee(), 0);
@@ -791,17 +783,17 @@ ArrayFillSubarray(JSContext *cx, unsigne
 }
 
 static bool
 InitializeCommonTypeDescriptorProperties(JSContext *cx,
                                          HandleObject obj,
                                          HandleObject typeReprOwnerObj)
 {
     TypeRepresentation *typeRepr =
-        TypeRepresentation::fromOwnerObject(typeReprOwnerObj);
+        TypeRepresentation::fromOwnerObject(*typeReprOwnerObj);
 
     // equivalent()
     if (!JS_DefineFunction(cx, obj, "equivalent",
                            TypeEquivalent, 1, 0))
     {
         return false;
     }
 
@@ -972,25 +964,26 @@ const Class StructType::class_ = {
     JS_StrictPropertyStub,
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     nullptr, /* finalize */
     nullptr, /* checkAccess */
     nullptr, /* call */
     nullptr, /* hasInstance */
-    BinaryBlock::construct,
+    TypedObject::construct,
     nullptr  /* trace */
 };
 
 const JSPropertySpec StructType::typeObjectProperties[] = {
     JS_PS_END
 };
 
 const JSFunctionSpec StructType::typeObjectMethods[] = {
+    {"handle", {NULL, NULL}, 2, 0, "HandleCreate"},
     JS_FN("toSource", StructType::toSource, 0, 0),
     JS_FS_END
 };
 
 const JSPropertySpec StructType::typedObjectProperties[] = {
     JS_PS_END
 };
 
@@ -1046,17 +1039,17 @@ StructType::layout(JSContext *cx, Handle
     }
 
     // Construct the `TypeRepresentation*`.
     RootedObject typeReprObj(
         cx, StructTypeRepresentation::Create(cx, ids, fieldTypeReprObjs));
     if (!typeReprObj)
         return false;
     StructTypeRepresentation *typeRepr =
-        TypeRepresentation::fromOwnerObject(typeReprObj)->asStruct();
+        TypeRepresentation::fromOwnerObject(*typeReprObj)->asStruct();
     structType->initReservedSlot(JS_TYPEOBJ_SLOT_TYPE_REPR,
                                  ObjectValue(*typeReprObj));
 
     // Construct for internal use an array with the type object for each field.
     RootedObject fieldTypeVec(
         cx, NewDenseCopiedArray(cx, fieldTypeObjs.length(),
                                 fieldTypeObjs.begin()));
     if (!fieldTypeVec)
@@ -1083,21 +1076,19 @@ StructType::layout(JSContext *cx, Handle
                                   JSPROP_READONLY | JSPROP_PERMANENT))
         return false;
 
     // Construct the fieldNames, fieldOffsets and fieldTypes objects:
     // fieldNames : [ string ]
     // fieldOffsets : { string: integer, ... }
     // fieldTypes : { string: Type, ... }
     RootedObject fieldOffsets(
-        cx,
-        NewObjectWithClassProto(cx, &JSObject::class_, nullptr, nullptr));
+        cx, NewObjectWithClassProto(cx, &JSObject::class_, NULL, NULL));
     RootedObject fieldTypes(
-        cx,
-        NewObjectWithClassProto(cx, &JSObject::class_, nullptr, nullptr));
+        cx, NewObjectWithClassProto(cx, &JSObject::class_, NULL, NULL));
     for (size_t i = 0; i < typeRepr->fieldCount(); i++) {
         const StructField &field = typeRepr->field(i);
         RootedId fieldId(cx, field.id);
 
         // fieldOffsets[id] = offset
         RootedValue offset(cx, NumberValue(field.offset));
         if (!JSObject::defineGeneric(cx, fieldOffsets, fieldId,
                                      offset, nullptr, nullptr,
@@ -1213,42 +1204,16 @@ StructType::toSource(JSContext *cx, unsi
         return false;
 
     args.rval().setString(result);
     return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////
 
-static bool
-ReifyComplex(JSContext *cx, HandleObject type, HandleObject owner,
-             size_t offset, MutableHandleValue to)
-{
-    RootedObject obj(cx, BinaryBlock::createDerived(cx, type, owner, offset));
-    if (!obj)
-        return false;
-    to.setObject(*obj);
-    return true;
-}
-
-static bool
-Reify(JSContext *cx, TypeRepresentation *typeRepr, HandleObject type,
-      HandleObject owner, size_t offset, MutableHandleValue to)
-{
-    JS_ASSERT(typeRepr == typeRepresentation(*type));
-    switch (typeRepr->kind()) {
-      case TypeRepresentation::Scalar:
-        return ReifyScalar(cx, typeRepr->asScalar(), type, owner, offset, to);
-      case TypeRepresentation::Struct:
-      case TypeRepresentation::Array:
-        return ReifyComplex(cx, type, owner, offset, to);
-    }
-    MOZ_ASSUME_UNREACHABLE("Invalid typeRepr kind");
-}
-
 ///////////////////////////////////////////////////////////////////////////
 // Creating the TypedObject "module"
 //
 // We create one global, `TypedObject`, which contains the following
 // members:
 //
 // 1. uint8, uint16, etc
 // 2. ArrayType
@@ -1364,16 +1329,34 @@ js_InitTypedObjectClass(JSContext *cx, H
                                   moduleValue,
                                   nullptr, nullptr,
                                   0))
         return nullptr;
     global->setReservedSlot(JSProto_TypedObject, moduleValue);
     global->setArrayType(arrayType);
     global->markStandardClassInitializedNoProto(&TypedObjectClass);
 
+    //  Handle
+
+    RootedObject handle(cx, NewBuiltinClassInstance(cx, &JSObject::class_));
+    if (!module)
+        return NULL;
+
+    if (!JS_DefineFunctions(cx, handle, TypedHandle::handleStaticMethods))
+        return NULL;
+
+    RootedValue handleValue(cx, ObjectValue(*handle));
+    if (!JSObject::defineProperty(cx, module, cx->names().Handle,
+                                  handleValue,
+                                  NULL, NULL,
+                                  JSPROP_READONLY | JSPROP_PERMANENT))
+    {
+        return NULL;
+    }
+
     return module;
 }
 
 JSObject *
 js_InitTypedObjectDummy(JSContext *cx, HandleObject obj)
 {
     /*
      * This function is entered into the jsprototypes.h table
@@ -1405,16 +1388,19 @@ DefineNumericClass(JSContext *cx,
         return false;
 
     numFun->initReservedSlot(JS_TYPEOBJ_SLOT_TYPE_REPR,
                              ObjectValue(*typeReprObj));
 
     if (!InitializeCommonTypeDescriptorProperties(cx, numFun, typeReprObj))
         return false;
 
+    if (!JS_DefineFunctions(cx, numFun, NumericTypeObjectMethods))
+        return false;
+
     if (!JS_DefineFunction(cx, numFun, "toString",
                            NumericTypeToString<type>, 0, 0))
         return false;
 
     if (!JS_DefineFunction(cx, numFun, "toSource",
                            NumericTypeToString<type>, 0, 0))
     {
         return false;
@@ -1424,205 +1410,181 @@ DefineNumericClass(JSContext *cx,
     if (!JSObject::defineProperty(cx, module, className,
                                   numFunValue, nullptr, nullptr, 0))
         return false;
 
     return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////
-// Binary blocks
+// Typed datums
+
+template<class T>
+/*static*/ T *
+TypedDatum::createUnattached(JSContext *cx,
+                             HandleObject type)
+{
+    JS_STATIC_ASSERT(T::IsTypedDatumClass);
+    JS_ASSERT(IsTypeObject(*type));
+
+    RootedObject obj(cx, createUnattachedWithClass(cx, &T::class_, type));
+    if (!obj)
+        return NULL;
+
+    return &obj->as<T>();
+}
+
+/*static*/ TypedDatum *
+TypedDatum::createUnattachedWithClass(JSContext *cx,
+                                      const Class *clasp,
+                                      HandleObject type)
+{
+    JS_ASSERT(IsTypeObject(*type));
+    JS_ASSERT(clasp == &TypedObject::class_ || clasp == &TypedHandle::class_);
+    JS_ASSERT(JSCLASS_RESERVED_SLOTS(clasp) == JS_DATUM_SLOTS);
+    JS_ASSERT(clasp->hasPrivate());
+
+    RootedObject proto(cx);
+    if (IsNumericTypeObject(*type)) {
+        // FIXME Bug 929651 -- What prototype to use?
+        proto = type->global().getOrCreateObjectPrototype(cx);
+    } else {
+        RootedValue protoVal(cx);
+        if (!JSObject::getProperty(cx, type, type,
+                                   cx->names().prototype, &protoVal))
+        {
+            return nullptr;
+        }
+        proto = &protoVal.toObject();
+    }
+
+    RootedObject obj(
+        cx, NewObjectWithClassProto(cx, clasp, &*proto, NULL));
+    if (!obj)
+        return NULL;
+
+    obj->setPrivate(nullptr);
+    obj->initReservedSlot(JS_DATUM_SLOT_TYPE_OBJ, ObjectValue(*type));
+    obj->initReservedSlot(JS_DATUM_SLOT_OWNER, NullValue());
 
-const Class BinaryBlock::class_ = {
-    "BinaryBlock",
-    Class::NON_NATIVE |
-    JSCLASS_HAS_RESERVED_SLOTS(JS_TYPEDOBJ_SLOTS) |
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_IMPLEMENTS_BARRIERS,
-    JS_PropertyStub,
-    JS_DeletePropertyStub,
-    JS_PropertyStub,
-    JS_StrictPropertyStub,
-    JS_EnumerateStub,
-    JS_ResolveStub,
-    JS_ConvertStub,
-    BinaryBlock::obj_finalize,
-    nullptr,        /* checkAccess */
-    nullptr,        /* call        */
-    nullptr,        /* construct   */
-    nullptr,        /* hasInstance */
-    BinaryBlock::obj_trace,
-    JS_NULL_CLASS_EXT,
-    {
-        BinaryBlock::obj_lookupGeneric,
-        BinaryBlock::obj_lookupProperty,
-        BinaryBlock::obj_lookupElement,
-        BinaryBlock::obj_lookupSpecial,
-        BinaryBlock::obj_defineGeneric,
-        BinaryBlock::obj_defineProperty,
-        BinaryBlock::obj_defineElement,
-        BinaryBlock::obj_defineSpecial,
-        BinaryBlock::obj_getGeneric,
-        BinaryBlock::obj_getProperty,
-        BinaryBlock::obj_getElement,
-        BinaryBlock::obj_getElementIfPresent,
-        BinaryBlock::obj_getSpecial,
-        BinaryBlock::obj_setGeneric,
-        BinaryBlock::obj_setProperty,
-        BinaryBlock::obj_setElement,
-        BinaryBlock::obj_setSpecial,
-        BinaryBlock::obj_getGenericAttributes,
-        BinaryBlock::obj_setGenericAttributes,
-        BinaryBlock::obj_deleteProperty,
-        BinaryBlock::obj_deleteElement,
-        BinaryBlock::obj_deleteSpecial,
-        BinaryBlock::obj_enumerate,
-        nullptr, /* thisObject */
+    // Tag the type object for this instance with the type
+    // representation, if that has not been done already.
+    if (cx->typeInferenceEnabled() && !IsNumericTypeObject(*type)) {
+        // FIXME Bug 929651           ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+        RootedTypeObject typeObj(cx, obj->getType(cx));
+        if (typeObj) {
+            TypeRepresentation *typeRepr = typeRepresentation(*type);
+            if (!typeObj->addTypedObjectAddendum(cx, typeRepr))
+                return nullptr;
+        }
     }
-};
+
+    return static_cast<TypedDatum*>(&*obj);
+}
+
+/* static */ void
+TypedDatum::attach(void *memory)
+{
+    setPrivate(memory);
+    setReservedSlot(JS_DATUM_SLOT_OWNER, ObjectValue(*this));
+}
+
+/*static*/ void
+TypedDatum::attach(JSObject &datum, uint32_t offset)
+{
+    JS_ASSERT(IsTypedDatum(datum));
+    JS_ASSERT(datum.getReservedSlot(JS_DATUM_SLOT_OWNER).isObject());
+
+    // find the location in memory
+    uint8_t *mem = TypedMem(datum) + offset;
+
+    // find the owner, which is often but not always `datum`
+    JSObject &owner = datum.getReservedSlot(JS_DATUM_SLOT_OWNER).toObject();
+
+    setPrivate(mem);
+    setReservedSlot(JS_DATUM_SLOT_OWNER, ObjectValue(owner));
+}
+
+/*static*/ TypedDatum *
+TypedDatum::createDerived(JSContext *cx, HandleObject type,
+                          HandleObject datum, size_t offset)
+{
+    JS_ASSERT(IsTypedDatum(*datum));
+    JS_ASSERT(offset <= DatumSize(*datum));
+    JS_ASSERT(offset + TypeSize(*type) <= DatumSize(*datum));
+
+    const js::Class *clasp = datum->getClass();
+    Rooted<TypedDatum*> obj(cx, createUnattachedWithClass(cx, clasp, type));
+    if (!obj)
+        return nullptr;
+
+    obj->attach(*datum, offset);
+    return obj;
+}
 
 static bool
-ReportBlockTypeError(JSContext *cx,
+ReportDatumTypeError(JSContext *cx,
                      const unsigned errorNumber,
                      HandleObject obj)
 {
-    JS_ASSERT(IsBlock(*obj));
+    JS_ASSERT(IsTypedDatum(*obj));
 
     RootedObject type(cx, GetType(*obj));
     TypeRepresentation *typeRepr = typeRepresentation(*type);
 
     StringBuffer contents(cx);
     if (!typeRepr->appendString(cx, contents))
         return false;
 
     RootedString result(cx, contents.finishString());
     if (!result)
         return false;
 
     char *typeReprStr = JS_EncodeString(cx, result.get());
     if (!typeReprStr)
         return false;
 
-    JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
+    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                          errorNumber, typeReprStr);
 
     JS_free(cx, (void *) typeReprStr);
     return false;
 }
 
-/* static */ void
-BinaryBlock::obj_trace(JSTracer *trace, JSObject *object)
-{
-    JS_ASSERT(object->hasClass(&class_));
-
-    for (size_t i = 0; i < JS_TYPEDOBJ_SLOTS; i++)
-        gc::MarkSlot(trace, &object->getReservedSlotRef(i), "BinaryBlockSlot");
-}
-
-/* static */ void
-BinaryBlock::obj_finalize(js::FreeOp *op, JSObject *obj)
-{
-    if (!obj->getReservedSlot(JS_TYPEDOBJ_SLOT_OWNER).isNull())
-        return;
-
-    if (void *mem = obj->getPrivate())
-        op->free_(mem);
-}
-
-/* static */ JSObject *
-BinaryBlock::createNull(JSContext *cx, HandleObject type, HandleValue owner)
+/*static*/ void
+TypedDatum::obj_trace(JSTracer *trace, JSObject *object)
 {
-    JS_ASSERT(IsTypeObject(*type));
-
-    RootedValue protoVal(cx);
-    if (!JSObject::getProperty(cx, type, type,
-                               cx->names().prototype, &protoVal))
-        return nullptr;
-
-    RootedObject obj(cx,
-        NewObjectWithClassProto(cx, &class_, &protoVal.toObject(), nullptr));
-    obj->initReservedSlot(JS_TYPEDOBJ_SLOT_TYPE_OBJ, ObjectValue(*type));
-    obj->initReservedSlot(JS_TYPEDOBJ_SLOT_OWNER, owner);
+    JS_ASSERT(IsTypedDatum(*object));
 
-    // Tag the type object for this instance with the type
-    // representation, if that has not been done already.
-    if (cx->typeInferenceEnabled()) {
-        RootedTypeObject typeObj(cx, obj->getType(cx));
-        if (typeObj) {
-            TypeRepresentation *typeRepr = typeRepresentation(*type);
-            if (!typeObj->addTypedObjectAddendum(cx, typeRepr))
-                return nullptr;
-        }
-    }
-
-    return obj;
+    for (size_t i = 0; i < JS_DATUM_SLOTS; i++)
+        gc::MarkSlot(trace, &object->getReservedSlotRef(i), "TypedObjectSlot");
 }
 
-/* static */ JSObject *
-BinaryBlock::createZeroed(JSContext *cx, HandleObject type)
+/*static*/ void
+TypedDatum::obj_finalize(js::FreeOp *op, JSObject *obj)
 {
-    RootedValue owner(cx, NullValue());
-    RootedObject obj(cx, createNull(cx, type, owner));
-    if (!obj)
-        return nullptr;
+    // if the obj owns the memory, indicating by the owner slot being
+    // set to itself, then we must free it when finalized.
 
-    TypeRepresentation *typeRepr = typeRepresentation(*type);
-    size_t memsize = typeRepr->size();
-    void *memory = JS_malloc(cx, memsize);
-    if (!memory)
-        return nullptr;
-    memset(memory, 0, memsize);
-    obj->setPrivate(memory);
-    return obj;
-}
-
-/* static */ JSObject *
-BinaryBlock::createDerived(JSContext *cx, HandleObject type,
-                           HandleObject owner, size_t offset)
-{
-    JS_ASSERT(IsBlock(*owner));
-    JS_ASSERT(offset <= BlockSize(*owner));
-    JS_ASSERT(offset + TypeSize(*type) <= BlockSize(*owner));
+    JS_ASSERT(obj->getReservedSlotRef(JS_DATUM_SLOT_OWNER).isObjectOrNull());
 
-    RootedValue ownerValue(cx, ObjectValue(*owner));
-    RootedObject obj(cx, createNull(cx, type, ownerValue));
-    if (!obj)
-        return nullptr;
-
-    obj->setPrivate(BlockMem(*owner) + offset);
-    return obj;
-}
-
-/* static */ bool
-BinaryBlock::construct(JSContext *cx, unsigned int argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
+    if (obj->getReservedSlot(JS_DATUM_SLOT_OWNER).isNull())
+        return; // Unattached
 
-    RootedObject callee(cx, &args.callee());
-    JS_ASSERT(IsTypeObject(*callee));
-
-    RootedObject obj(cx, createZeroed(cx, callee));
-    if (!obj)
-        return false;
-
-    if (argc == 1) {
-        RootedValue initial(cx, args[0]);
-        if (!ConvertAndCopyTo(cx, obj, initial))
-            return false;
+    if (&obj->getReservedSlot(JS_DATUM_SLOT_OWNER).toObject() == obj) {
+        JS_ASSERT(obj->getPrivate());
+        op->free_(obj->getPrivate());
     }
-
-    args.rval().setObject(*obj);
-    return true;
 }
 
 bool
-BinaryBlock::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
+TypedDatum::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
                                 MutableHandleObject objp, MutableHandleShape propp)
 {
-    JS_ASSERT(IsBlock(*obj));
+    JS_ASSERT(IsTypedDatum(*obj));
 
     RootedObject type(cx, GetType(*obj));
     TypeRepresentation *typeRepr = typeRepresentation(*type);
 
     switch (typeRepr->kind()) {
       case TypeRepresentation::Scalar:
         break;
 
@@ -1660,31 +1622,31 @@ BinaryBlock::obj_lookupGeneric(JSContext
         propp.set(nullptr);
         return true;
     }
 
     return JSObject::lookupGeneric(cx, proto, id, objp, propp);
 }
 
 bool
-BinaryBlock::obj_lookupProperty(JSContext *cx,
+TypedDatum::obj_lookupProperty(JSContext *cx,
                                 HandleObject obj,
                                 HandlePropertyName name,
                                 MutableHandleObject objp,
                                 MutableHandleShape propp)
 {
     RootedId id(cx, NameToId(name));
     return obj_lookupGeneric(cx, obj, id, objp, propp);
 }
 
 bool
-BinaryBlock::obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
+TypedDatum::obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
                                 MutableHandleObject objp, MutableHandleShape propp)
 {
-    JS_ASSERT(IsBlock(*obj));
+    JS_ASSERT(IsTypedDatum(*obj));
     MarkNonNativePropertyFound(propp);
     objp.set(obj);
     return true;
 }
 
 static bool
 ReportPropertyError(JSContext *cx,
                     const unsigned errorNumber,
@@ -1701,55 +1663,55 @@ ReportPropertyError(JSContext *cx,
     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                          errorNumber, propName);
 
     JS_free(cx, propName);
     return false;
 }
 
 bool
-BinaryBlock::obj_lookupSpecial(JSContext *cx, HandleObject obj,
-                               HandleSpecialId sid, MutableHandleObject objp,
-                               MutableHandleShape propp)
+TypedDatum::obj_lookupSpecial(JSContext *cx, HandleObject obj,
+                              HandleSpecialId sid, MutableHandleObject objp,
+                              MutableHandleShape propp)
 {
     RootedId id(cx, SPECIALID_TO_JSID(sid));
     return obj_lookupGeneric(cx, obj, id, objp, propp);
 }
 
 bool
-BinaryBlock::obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
-                               PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
+TypedDatum::obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
+                              PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
     return ReportPropertyError(cx, JSMSG_UNDEFINED_PROP, id);
 }
 
 bool
-BinaryBlock::obj_defineProperty(JSContext *cx, HandleObject obj,
-                                HandlePropertyName name, HandleValue v,
-                                PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
+TypedDatum::obj_defineProperty(JSContext *cx, HandleObject obj,
+                               HandlePropertyName name, HandleValue v,
+                               PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
     Rooted<jsid> id(cx, NameToId(name));
     return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs);
 }
 
 bool
-BinaryBlock::obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v,
+TypedDatum::obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v,
                                PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
 
     RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
     if (!delegate)
         return false;
     return baseops::DefineElement(cx, delegate, index, v, getter, setter, attrs);
 }
 
 bool
-BinaryBlock::obj_defineSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid, HandleValue v,
-                               PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
+TypedDatum::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 JSObject *
 StructFieldType(JSContext *cx,
                 HandleObject type,
@@ -1776,20 +1738,20 @@ StructFieldType(JSContext *cx,
     if (!JSObject::getElement(cx, fieldTypes, fieldTypes,
                               fieldIndex, &fieldTypeVal))
         return nullptr;
 
     return &fieldTypeVal.toObject();
 }
 
 bool
-BinaryBlock::obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
-                             HandleId id, MutableHandleValue vp)
+TypedDatum::obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
+                           HandleId id, MutableHandleValue vp)
 {
-    JS_ASSERT(IsBlock(*obj));
+    JS_ASSERT(IsTypedDatum(*obj));
 
     // Dispatch elements to obj_getElement:
     uint32_t index;
     if (js_IdIsIndex(id, &index))
         return obj_getElement(cx, obj, receiver, index, vp);
 
     // Handle everything else here:
 
@@ -1825,35 +1787,35 @@ BinaryBlock::obj_getGeneric(JSContext *c
         vp.setUndefined();
         return true;
     }
 
     return JSObject::getGeneric(cx, proto, receiver, id, vp);
 }
 
 bool
-BinaryBlock::obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
+TypedDatum::obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
                               HandlePropertyName name, MutableHandleValue vp)
 {
     RootedId id(cx, NameToId(name));
     return obj_getGeneric(cx, obj, receiver, id, vp);
 }
 
 bool
-BinaryBlock::obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver,
+TypedDatum::obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver,
                              uint32_t index, MutableHandleValue vp)
 {
     bool present;
     return obj_getElementIfPresent(cx, obj, receiver, index, vp, &present);
 }
 
 bool
-BinaryBlock::obj_getElementIfPresent(JSContext *cx, HandleObject obj,
-                                     HandleObject receiver, uint32_t index,
-                                     MutableHandleValue vp, bool *present)
+TypedDatum::obj_getElementIfPresent(JSContext *cx, HandleObject obj,
+                                    HandleObject receiver, uint32_t index,
+                                    MutableHandleValue vp, bool *present)
 {
     RootedObject type(cx, GetType(*obj));
     TypeRepresentation *typeRepr = typeRepresentation(*type);
 
     switch (typeRepr->kind()) {
       case TypeRepresentation::Scalar:
       case TypeRepresentation::Struct:
         break;
@@ -1879,26 +1841,26 @@ BinaryBlock::obj_getElementIfPresent(JSC
         vp.setUndefined();
         return true;
     }
 
     return JSObject::getElementIfPresent(cx, proto, receiver, index, vp, present);
 }
 
 bool
-BinaryBlock::obj_getSpecial(JSContext *cx, HandleObject obj,
+TypedDatum::obj_getSpecial(JSContext *cx, HandleObject obj,
                             HandleObject receiver, HandleSpecialId sid,
                             MutableHandleValue vp)
 {
     RootedId id(cx, SPECIALID_TO_JSID(sid));
     return obj_getGeneric(cx, obj, receiver, id, vp);
 }
 
 bool
-BinaryBlock::obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
+TypedDatum::obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
                             MutableHandleValue vp, bool strict)
 {
     uint32_t index;
     if (js_IdIsIndex(id, &index))
         return obj_setElement(cx, obj, index, vp, strict);
 
     RootedObject type(cx, GetType(*obj));
     TypeRepresentation *typeRepr = typeRepresentation(*type);
@@ -1923,30 +1885,30 @@ BinaryBlock::obj_setGeneric(JSContext *c
         RootedObject fieldType(cx, StructFieldType(cx, type, field->index));
         if (!fieldType)
             return false;
 
         return ConvertAndCopyTo(cx, fieldType, obj, field->offset, vp);
       }
     }
 
-    return ReportBlockTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, obj);
+    return ReportDatumTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, obj);
 }
 
 bool
-BinaryBlock::obj_setProperty(JSContext *cx, HandleObject obj,
+TypedDatum::obj_setProperty(JSContext *cx, HandleObject obj,
                              HandlePropertyName name, MutableHandleValue vp,
                              bool strict)
 {
     RootedId id(cx, NameToId(name));
     return obj_setGeneric(cx, obj, id, vp, strict);
 }
 
 bool
-BinaryBlock::obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
+TypedDatum::obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
                             MutableHandleValue vp, bool strict)
 {
     RootedObject type(cx, GetType(*obj));
     TypeRepresentation *typeRepr = typeRepresentation(*type);
 
     switch (typeRepr->kind()) {
       case ScalarTypeRepresentation::Scalar:
       case ScalarTypeRepresentation::Struct:
@@ -1962,30 +1924,30 @@ BinaryBlock::obj_setElement(JSContext *c
         }
 
         RootedObject elementType(cx, ArrayElementType(type));
         size_t offset = arrayTypeRepr->element()->size() * index;
         return ConvertAndCopyTo(cx, elementType, obj, offset, vp);
       }
     }
 
-    return ReportBlockTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, obj);
+    return ReportDatumTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, obj);
 }
 
 bool
-BinaryBlock::obj_setSpecial(JSContext *cx, HandleObject obj,
+TypedDatum::obj_setSpecial(JSContext *cx, HandleObject obj,
                              HandleSpecialId sid, MutableHandleValue vp,
                              bool strict)
 {
     RootedId id(cx, SPECIALID_TO_JSID(sid));
     return obj_setGeneric(cx, obj, id, vp, strict);
 }
 
 bool
-BinaryBlock::obj_getGenericAttributes(JSContext *cx, HandleObject obj,
+TypedDatum::obj_getGenericAttributes(JSContext *cx, HandleObject obj,
                                        HandleId id, unsigned *attrsp)
 {
     uint32_t index;
     RootedObject type(cx, GetType(*obj));
     TypeRepresentation *typeRepr = typeRepresentation(*type);
 
     switch (typeRepr->kind()) {
       case TypeRepresentation::Scalar:
@@ -2036,17 +1998,17 @@ IsOwnId(JSContext *cx, HandleObject obj,
       case TypeRepresentation::Struct:
         return typeRepr->asStruct()->fieldNamed(id) != nullptr;
     }
 
     return false;
 }
 
 bool
-BinaryBlock::obj_setGenericAttributes(JSContext *cx, HandleObject obj,
+TypedDatum::obj_setGenericAttributes(JSContext *cx, HandleObject obj,
                                        HandleId id, unsigned *attrsp)
 {
     RootedObject type(cx, GetType(*obj));
 
     if (IsOwnId(cx, obj, id))
         return ReportPropertyError(cx, JSMSG_CANT_REDEFINE_PROP, id);
 
     RootedObject proto(cx, obj->getProto());
@@ -2054,34 +2016,34 @@ BinaryBlock::obj_setGenericAttributes(JS
         *attrsp = 0;
         return true;
     }
 
     return JSObject::setGenericAttributes(cx, proto, id, attrsp);
 }
 
 bool
-BinaryBlock::obj_deleteProperty(JSContext *cx, HandleObject obj,
+TypedDatum::obj_deleteProperty(JSContext *cx, HandleObject obj,
                                 HandlePropertyName name, bool *succeeded)
 {
     Rooted<jsid> id(cx, NameToId(name));
     if (IsOwnId(cx, obj, id))
         return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
 
     RootedObject proto(cx, obj->getProto());
     if (!proto) {
         *succeeded = false;
         return true;
     }
 
     return JSObject::deleteProperty(cx, proto, name, succeeded);
 }
 
 bool
-BinaryBlock::obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
+TypedDatum::obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
                                bool *succeeded)
 {
     RootedId id(cx);
     if (!IndexToId(cx, index, &id))
         return false;
 
     if (IsOwnId(cx, obj, id))
         return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
@@ -2091,30 +2053,30 @@ BinaryBlock::obj_deleteElement(JSContext
         *succeeded = false;
         return true;
     }
 
     return JSObject::deleteElement(cx, proto, index, succeeded);
 }
 
 bool
-BinaryBlock::obj_deleteSpecial(JSContext *cx, HandleObject obj,
+TypedDatum::obj_deleteSpecial(JSContext *cx, HandleObject obj,
                                HandleSpecialId sid, bool *succeeded)
 {
     RootedObject proto(cx, obj->getProto());
     if (!proto) {
         *succeeded = false;
         return true;
     }
 
     return JSObject::deleteSpecial(cx, proto, sid, succeeded);
 }
 
 bool
-BinaryBlock::obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
+TypedDatum::obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
                            MutableHandleValue statep, MutableHandleId idp)
 {
     uint32_t index;
 
     RootedObject type(cx, GetType(*obj));
     TypeRepresentation *typeRepr = typeRepresentation(*type);
 
     switch (typeRepr->kind()) {
@@ -2185,22 +2147,299 @@ BinaryBlock::obj_enumerate(JSContext *cx
         }
         break;
     }
 
     return true;
 }
 
 /* static */ size_t
-BinaryBlock::dataOffset()
+TypedDatum::dataOffset()
+{
+    return JSObject::getPrivateDataOffset(JS_DATUM_SLOTS + 1);
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Typed Objects
+
+const Class TypedObject::class_ = {
+    "TypedObject",
+    Class::NON_NATIVE |
+    JSCLASS_HAS_RESERVED_SLOTS(JS_DATUM_SLOTS) |
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_IMPLEMENTS_BARRIERS,
+    JS_PropertyStub,
+    JS_DeletePropertyStub,
+    JS_PropertyStub,
+    JS_StrictPropertyStub,
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub,
+    TypedDatum::obj_finalize,
+    NULL,           /* checkAccess */
+    NULL,           /* call        */
+    NULL,           /* construct   */
+    NULL,           /* hasInstance */
+    TypedDatum::obj_trace,
+    JS_NULL_CLASS_EXT,
+    {
+        TypedDatum::obj_lookupGeneric,
+        TypedDatum::obj_lookupProperty,
+        TypedDatum::obj_lookupElement,
+        TypedDatum::obj_lookupSpecial,
+        TypedDatum::obj_defineGeneric,
+        TypedDatum::obj_defineProperty,
+        TypedDatum::obj_defineElement,
+        TypedDatum::obj_defineSpecial,
+        TypedDatum::obj_getGeneric,
+        TypedDatum::obj_getProperty,
+        TypedDatum::obj_getElement,
+        TypedDatum::obj_getElementIfPresent,
+        TypedDatum::obj_getSpecial,
+        TypedDatum::obj_setGeneric,
+        TypedDatum::obj_setProperty,
+        TypedDatum::obj_setElement,
+        TypedDatum::obj_setSpecial,
+        TypedDatum::obj_getGenericAttributes,
+        TypedDatum::obj_setGenericAttributes,
+        TypedDatum::obj_deleteProperty,
+        TypedDatum::obj_deleteElement,
+        TypedDatum::obj_deleteSpecial,
+        TypedDatum::obj_enumerate,
+        NULL, /* thisObject */
+    }
+};
+
+/*static*/ JSObject *
+TypedObject::createZeroed(JSContext *cx, HandleObject type)
+{
+    Rooted<TypedObject*> obj(cx, createUnattached<TypedObject>(cx, type));
+    if (!obj)
+        return NULL;
+
+    TypeRepresentation *typeRepr = typeRepresentation(*type);
+    size_t memsize = typeRepr->size();
+    void *memory = JS_malloc(cx, memsize);
+    if (!memory)
+        return NULL;
+    memset(memory, 0, memsize);
+    obj->attach(memory);
+    return obj;
+}
+
+/*static*/ bool
+TypedObject::construct(JSContext *cx, unsigned int argc, Value *vp)
 {
-    return JSObject::getPrivateDataOffset(JS_TYPEDOBJ_SLOTS + 1);
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    RootedObject callee(cx, &args.callee());
+    JS_ASSERT(IsTypeObject(*callee));
+
+    RootedObject obj(cx, createZeroed(cx, callee));
+    if (!obj)
+        return false;
+
+    if (argc == 1) {
+        RootedValue initial(cx, args[0]);
+        if (!ConvertAndCopyTo(cx, obj, initial))
+            return false;
+    }
+
+    args.rval().setObject(*obj);
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Handles
+
+const Class TypedHandle::class_ = {
+    "Handle",
+    Class::NON_NATIVE |
+    JSCLASS_HAS_RESERVED_SLOTS(JS_DATUM_SLOTS) |
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_IMPLEMENTS_BARRIERS,
+    JS_PropertyStub,
+    JS_DeletePropertyStub,
+    JS_PropertyStub,
+    JS_StrictPropertyStub,
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub,
+    TypedDatum::obj_finalize,
+    NULL,           /* checkAccess */
+    NULL,           /* call        */
+    NULL,           /* construct   */
+    NULL,           /* hasInstance */
+    TypedDatum::obj_trace,
+    JS_NULL_CLASS_EXT,
+    {
+        TypedDatum::obj_lookupGeneric,
+        TypedDatum::obj_lookupProperty,
+        TypedDatum::obj_lookupElement,
+        TypedDatum::obj_lookupSpecial,
+        TypedDatum::obj_defineGeneric,
+        TypedDatum::obj_defineProperty,
+        TypedDatum::obj_defineElement,
+        TypedDatum::obj_defineSpecial,
+        TypedDatum::obj_getGeneric,
+        TypedDatum::obj_getProperty,
+        TypedDatum::obj_getElement,
+        TypedDatum::obj_getElementIfPresent,
+        TypedDatum::obj_getSpecial,
+        TypedDatum::obj_setGeneric,
+        TypedDatum::obj_setProperty,
+        TypedDatum::obj_setElement,
+        TypedDatum::obj_setSpecial,
+        TypedDatum::obj_getGenericAttributes,
+        TypedDatum::obj_setGenericAttributes,
+        TypedDatum::obj_deleteProperty,
+        TypedDatum::obj_deleteElement,
+        TypedDatum::obj_deleteSpecial,
+        TypedDatum::obj_enumerate,
+        NULL, /* thisObject */
+    }
+};
+
+const JSFunctionSpec TypedHandle::handleStaticMethods[] = {
+    {"move", {NULL, NULL}, 3, 0, "HandleMove"},
+    {"get", {NULL, NULL}, 1, 0, "HandleGet"},
+    {"set", {NULL, NULL}, 2, 0, "HandleSet"},
+    {"isHandle", {NULL, NULL}, 1, 0, "HandleTest"},
+    JS_FS_END
+};
+
+///////////////////////////////////////////////////////////////////////////
+// Intrinsics
+
+bool
+js::NewTypedHandle(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    JS_ASSERT(argc == 1);
+    JS_ASSERT(args[0].isObject() && IsTypeObject(args[0].toObject()));
+
+    RootedObject typeObj(cx, &args[0].toObject());
+    Rooted<TypedHandle*> obj(
+        cx, TypedDatum::createUnattached<TypedHandle>(cx, typeObj));
+    if (!obj)
+        return false;
+    args.rval().setObject(*obj);
+    return true;
 }
 
 bool
+js::NewDerivedTypedDatum(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    JS_ASSERT(argc == 3);
+    JS_ASSERT(args[0].isObject() && IsTypeObject(args[0].toObject()));
+    JS_ASSERT(args[1].isObject() && IsTypedDatum(args[1].toObject()));
+    JS_ASSERT(args[2].isInt32());
+
+    RootedObject typeObj(cx, &args[0].toObject());
+    RootedObject datum(cx, &args[1].toObject());
+    int32_t offset = args[2].toInt32();
+
+    Rooted<TypedDatum*> obj(
+        cx, TypedDatum::createDerived(cx, typeObj, datum, offset));
+    if (!obj)
+        return false;
+
+    args.rval().setObject(*obj);
+    return true;
+}
+
+bool
+js::AttachHandle(ThreadSafeContext *, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    JS_ASSERT(argc == 3);
+    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedHandle>());
+    JS_ASSERT(args[1].isObject() && IsTypedDatum(args[1].toObject()));
+    JS_ASSERT(args[2].isInt32());
+
+    TypedHandle &handle = args[0].toObject().as<TypedHandle>();
+    handle.attach(args[1].toObject(), args[2].toInt32());
+    return true;
+}
+
+const JSJitInfo js::AttachHandleJitInfo =
+    JS_JITINFO_NATIVE_PARALLEL(
+        JSParallelNativeThreadSafeWrapper<js::AttachHandle>);
+
+bool
+js::ObjectIsTypeObject(ThreadSafeContext *, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    JS_ASSERT(argc == 1);
+    JS_ASSERT(args[0].isObject());
+    args.rval().setBoolean(IsTypeObject(args[0].toObject()));
+    return true;
+}
+
+const JSJitInfo js::ObjectIsTypeObjectJitInfo =
+    JS_JITINFO_NATIVE_PARALLEL(
+        JSParallelNativeThreadSafeWrapper<js::ObjectIsTypeObject>);
+
+bool
+js::ObjectIsTypeRepresentation(ThreadSafeContext *, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    JS_ASSERT(argc == 1);
+    JS_ASSERT(args[0].isObject());
+    args.rval().setBoolean(TypeRepresentation::isOwnerObject(args[0].toObject()));
+    return true;
+}
+
+const JSJitInfo js::ObjectIsTypeRepresentationJitInfo =
+    JS_JITINFO_NATIVE_PARALLEL(
+        JSParallelNativeThreadSafeWrapper<js::ObjectIsTypeRepresentation>);
+
+bool
+js::ObjectIsTypedHandle(ThreadSafeContext *, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    JS_ASSERT(argc == 1);
+    JS_ASSERT(args[0].isObject());
+    args.rval().setBoolean(args[0].toObject().is<TypedHandle>());
+    return true;
+}
+
+const JSJitInfo js::ObjectIsTypedHandleJitInfo =
+    JS_JITINFO_NATIVE_PARALLEL(
+        JSParallelNativeThreadSafeWrapper<js::ObjectIsTypedHandle>);
+
+bool
+js::ObjectIsTypedObject(ThreadSafeContext *, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    JS_ASSERT(argc == 1);
+    JS_ASSERT(args[0].isObject());
+    args.rval().setBoolean(args[0].toObject().is<TypedObject>());
+    return true;
+}
+
+const JSJitInfo js::ObjectIsTypedObjectJitInfo =
+    JS_JITINFO_NATIVE_PARALLEL(
+        JSParallelNativeThreadSafeWrapper<js::ObjectIsTypedObject>);
+
+bool
+js::IsAttached(ThreadSafeContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    JS_ASSERT(args[0].isObject() && IsTypedDatum(args[0].toObject()));
+    args.rval().setBoolean(TypedMem(args[0].toObject()) != NULL);
+    return true;
+}
+
+const JSJitInfo js::IsAttachedJitInfo =
+    JS_JITINFO_NATIVE_PARALLEL(
+        JSParallelNativeThreadSafeWrapper<js::IsAttached>);
+
+bool
 js::ClampToUint8(ThreadSafeContext *, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JS_ASSERT(argc == 1);
     JS_ASSERT(args[0].isNumber());
     args.rval().setNumber(ClampDoubleToUint8(args[0].toNumber()));
     return true;
 }
@@ -2209,50 +2448,50 @@ const JSJitInfo js::ClampToUint8JitInfo 
     JS_JITINFO_NATIVE_PARALLEL(
         JSParallelNativeThreadSafeWrapper<js::ClampToUint8>);
 
 bool
 js::Memcpy(ThreadSafeContext *, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JS_ASSERT(args.length() == 5);
-    JS_ASSERT(args[0].isObject() && IsBlock(args[0].toObject()));
+    JS_ASSERT(args[0].isObject() && IsTypedDatum(args[0].toObject()));
     JS_ASSERT(args[1].isInt32());
-    JS_ASSERT(args[2].isObject() && IsBlock(args[2].toObject()));
+    JS_ASSERT(args[2].isObject() && IsTypedDatum(args[2].toObject()));
     JS_ASSERT(args[3].isInt32());
     JS_ASSERT(args[4].isInt32());
 
-    uint8_t *target = BlockMem(args[0].toObject()) + args[1].toInt32();
-    uint8_t *source = BlockMem(args[2].toObject()) + args[3].toInt32();
+    uint8_t *target = TypedMem(args[0].toObject()) + args[1].toInt32();
+    uint8_t *source = TypedMem(args[2].toObject()) + args[3].toInt32();
     int32_t size = args[4].toInt32();
     memcpy(target, source, size);
     args.rval().setUndefined();
     return true;
 }
 
 const JSJitInfo js::MemcpyJitInfo =
     JS_JITINFO_NATIVE_PARALLEL(
         JSParallelNativeThreadSafeWrapper<js::Memcpy>);
 
 #define JS_STORE_SCALAR_CLASS_IMPL(_constant, T, _name)                       \
 bool                                                                          \
 js::StoreScalar##T::Func(ThreadSafeContext *, unsigned argc, Value *vp)       \
 {                                                                             \
     CallArgs args = CallArgsFromVp(argc, vp);                                 \
     JS_ASSERT(args.length() == 3);                                            \
-    JS_ASSERT(args[0].isObject() && IsBlock(args[0].toObject()));             \
+    JS_ASSERT(args[0].isObject() && IsTypedDatum(args[0].toObject()));        \
     JS_ASSERT(args[1].isInt32());                                             \
     JS_ASSERT(args[2].isNumber());                                            \
                                                                               \
     int32_t offset = args[1].toInt32();                                       \
                                                                               \
     /* Should be guaranteed by the typed objects API: */                      \
     JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                                  \
                                                                               \
-    T *target = (T*) (BlockMem(args[0].toObject()) + offset);                 \
+    T *target = (T*) (TypedMem(args[0].toObject()) + offset);                 \
     double d = args[2].toNumber();                                            \
     *target = ConvertScalar<T>(d);                                            \
     args.rval().setUndefined();                                               \
     return true;                                                              \
 }                                                                             \
                                                                               \
 const JSJitInfo                                                               \
 js::StoreScalar##T::JitInfo =                                                 \
@@ -2260,25 +2499,25 @@ js::StoreScalar##T::JitInfo =           
         JSParallelNativeThreadSafeWrapper<Func>);
 
 #define JS_LOAD_SCALAR_CLASS_IMPL(_constant, T, _name)                        \
 bool                                                                          \
 js::LoadScalar##T::Func(ThreadSafeContext *, unsigned argc, Value *vp)        \
 {                                                                             \
     CallArgs args = CallArgsFromVp(argc, vp);                                 \
     JS_ASSERT(args.length() == 2);                                            \
-    JS_ASSERT(args[0].isObject() && IsBlock(args[0].toObject()));             \
+    JS_ASSERT(args[0].isObject() && IsTypedDatum(args[0].toObject()));        \
     JS_ASSERT(args[1].isInt32());                                             \
                                                                               \
     int32_t offset = args[1].toInt32();                                       \
                                                                               \
     /* Should be guaranteed by the typed objects API: */                      \
     JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                                  \
                                                                               \
-    T *target = (T*) (BlockMem(args[0].toObject()) + offset);                 \
+    T *target = (T*) (TypedMem(args[0].toObject()) + offset);                 \
     args.rval().setNumber((double) *target);                                  \
     return true;                                                              \
 }                                                                             \
                                                                               \
 const JSJitInfo                                                               \
 js::LoadScalar##T::JitInfo =                                                  \
     JS_JITINFO_NATIVE_PARALLEL(                                               \
         JSParallelNativeThreadSafeWrapper<Func>);
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -7,16 +7,98 @@
 #ifndef builtin_TypedObject_h
 #define builtin_TypedObject_h
 
 #include "jsobj.h"
 
 #include "builtin/TypedObjectConstants.h"
 #include "builtin/TypeRepresentation.h"
 
+/*
+ * -------------
+ * Typed Objects
+ * -------------
+ *
+ * Typed objects are a special kind of JS object where the data is
+ * given well-structured form. To use a typed object, users first
+ * create *type objects* (no relation to the type objects used in TI)
+ * that define the type layout. For example, a statement like:
+ *
+ *    var PointType = new StructType({x: uint8, y: uint8});
+ *
+ * would create a type object PointType that is a struct with
+ * two fields, each of uint8 type.
+ *
+ * This comment typically assumes familiary with the API.  For more
+ * info on the API itself, see the Harmony wiki page at
+ * http://wiki.ecmascript.org/doku.php?id=harmony:typed_objects or the
+ * ES6 spec (not finalized at the time of this writing).
+ *
+ * - Initialization:
+ *
+ * Currently, all "globals" related to typed objects are packaged
+ * within a single "module" object `TypedObject`. This module has its
+ * own js::Class and when that class is initialized, we also create
+ * and define all other values (in `js_InitTypedObjectClass()`).
+ *
+ * - Type objects, meta type objects, and type representations:
+ *
+ * There are a number of pre-defined type objects, one for each
+ * scalar type (`uint8` etc). Each of these has its own class_,
+ * defined in `DefineNumericClass()`.
+ *
+ * There are also meta type objects (`ArrayType`, `StructType`).
+ * These constructors are not themselves type objects but rather the
+ * means for the *user* to construct new typed objects.
+ *
+ * Each type object is associated with a *type representation* (see
+ * TypeRepresentation.h). Type representations are canonical versions
+ * of type objects. We attach them to TI type objects and (eventually)
+ * use them for shape guards etc. They are purely internal to the
+ * engine and are not exposed to end users (though self-hosted code
+ * sometimes accesses them).
+ *
+ * - Typed datums, objects, and handles:
+ *
+ * A typed object is an instance of a type object. A handle is a
+ * relocatable pointer that points into other typed objects. Both of them
+ * are basically represented the same way, though they have distinct
+ * js::Class entries. They are both subtypes of `TypedDatum`.
+ *
+ * Both typed objects and handles are non-native objects that fully
+ * override the property accessors etc. The overridden accessor
+ * methods are the same in each and are defined in methods of
+ * TypedDatum.
+ *
+ * Typed datums may be attached or unattached. An unattached typed
+ * datum has no memory associated with it; it is basically a null
+ * pointer.  This can only happen when a new handle is created, since
+ * typed object instances are always associated with memory at the
+ * point of creation.
+ *
+ * When a new typed object instance is created, fresh memory is
+ * allocated and set as that typed object's private field. The object
+ * is then considered the *owner* of that memory: when the object is
+ * collected, its finalizer will free the memory. The fact that an
+ * object `o` owns its memory is indicated by setting its reserved
+ * slot JS_TYPEDOBJ_SLOT_OWNER to `o` (a trivial cycle, in other
+ * words).
+ *
+ * Later, *derived* typed objects can be created, typically via an
+ * access like `o.f` where `f` is some complex (non-scalar) type, but
+ * also explicitly via Handle objects. In those cases, the memory
+ * pointer of the derived object is set to alias the owner's memory
+ * pointer, and the owner slot for the derived object is set to the
+ * owner object, thus ensuring that the owner is not collected while
+ * the derived object is alive. We always maintain the invariant that
+ * JS_TYPEDOBJ_SLOT_OWNER is the true owner of the memory, meaning
+ * that there is a shallow tree. This prevents an access pattern like
+ * `a.b.c.d` from keeping all the intermediate objects alive.
+ */
+
 namespace js {
 
 /*
  * This object exists in order to encapsulate the typed object types
  * somewhat, rather than sticking them all into the global object.
  * Eventually it will go away and become a module.
  */
 extern const Class TypedObjectClass;
@@ -74,17 +156,18 @@ class ArrayType : public JSObject
 /*
  * Type descriptor created by `new StructType(...)`
  */
 class StructType : public JSObject
 {
   private:
     static JSObject *create(JSContext *cx, HandleObject structTypeGlobal,
                             HandleObject fields);
-    /**
+
+    /*
      * Sets up structType slots based on calculated memory size
      * and alignment and stores fieldmap as well.
      */
     static bool layout(JSContext *cx, HandleObject structType,
                        HandleObject fields);
 
   public:
     static const Class class_;
@@ -105,158 +188,262 @@ class StructType : public JSObject
 
     static bool toSource(JSContext *cx, unsigned argc, Value *vp);
 
     static bool convertAndCopyTo(JSContext *cx,
                                  StructTypeRepresentation *typeRepr,
                                  HandleValue from, uint8_t *mem);
 };
 
-/* Binary data objects and handles */
-class BinaryBlock
+/*
+ * Base type for typed objects and handles. Basically any type whose
+ * contents consist of typed memory.
+ */
+class TypedDatum : public JSObject
 {
   private:
-    // Creates a binary data object of the given type and class, but with
-    // a nullptr memory pointer. Caller must use setPrivate() to set the
-    // memory pointer properly.
-    static JSObject *createNull(JSContext *cx, HandleObject type,
-                                HandleValue owner);
+    static const bool IsTypedDatumClass = true;
 
+  protected:
     static void obj_finalize(js::FreeOp *op, JSObject *obj);
 
     static void obj_trace(JSTracer *trace, JSObject *object);
 
     static bool obj_lookupGeneric(JSContext *cx, HandleObject obj,
-                                    HandleId id, MutableHandleObject objp,
-                                    MutableHandleShape propp);
+                                  HandleId id, MutableHandleObject objp,
+                                  MutableHandleShape propp);
 
     static bool obj_lookupProperty(JSContext *cx, HandleObject obj,
-                                     HandlePropertyName name,
-                                     MutableHandleObject objp,
-                                     MutableHandleShape propp);
+                                   HandlePropertyName name,
+                                   MutableHandleObject objp,
+                                   MutableHandleShape propp);
 
     static bool obj_lookupElement(JSContext *cx, HandleObject obj,
-                                    uint32_t index, MutableHandleObject objp,
-                                    MutableHandleShape propp);
+                                  uint32_t index, MutableHandleObject objp,
+                                  MutableHandleShape propp);
 
     static bool obj_lookupSpecial(JSContext *cx, HandleObject obj,
-                                    HandleSpecialId sid,
-                                    MutableHandleObject objp,
-                                    MutableHandleShape propp);
+                                  HandleSpecialId sid,
+                                  MutableHandleObject objp,
+                                  MutableHandleShape propp);
 
     static bool obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
-                                    PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
+                                  PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
 
     static bool obj_defineProperty(JSContext *cx, HandleObject obj,
-                                     HandlePropertyName name, HandleValue v,
-                                     PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
+                                   HandlePropertyName name, HandleValue v,
+                                   PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
 
     static bool obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v,
-                                    PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
+                                  PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
 
     static bool obj_defineSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid, HandleValue v,
-                                    PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
+                                  PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
 
     static bool obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
-                                 HandleId id, MutableHandleValue vp);
+                               HandleId id, MutableHandleValue vp);
 
     static bool obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
-                                  HandlePropertyName name, MutableHandleValue vp);
+                                HandlePropertyName name, MutableHandleValue vp);
 
     static bool obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver,
-                                 uint32_t index, MutableHandleValue vp);
+                               uint32_t index, MutableHandleValue vp);
 
     static bool obj_getSpecial(JSContext *cx, HandleObject obj, HandleObject receiver,
-                                 HandleSpecialId sid, MutableHandleValue vp);
+                               HandleSpecialId sid, MutableHandleValue vp);
 
     static bool obj_getElementIfPresent(JSContext *cx, HandleObject obj,
-                                          HandleObject receiver, uint32_t index,
-                                          MutableHandleValue vp, bool *present);
+                                        HandleObject receiver, uint32_t index,
+                                        MutableHandleValue vp, bool *present);
     static bool obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
-                                 MutableHandleValue vp, bool strict);
+                               MutableHandleValue vp, bool strict);
     static bool obj_setProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
-                                  MutableHandleValue vp, bool strict);
+                                MutableHandleValue vp, bool strict);
     static bool obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
-                                 MutableHandleValue vp, bool strict);
+                               MutableHandleValue vp, bool strict);
     static bool obj_setSpecial(JSContext *cx, HandleObject obj,
-                                 HandleSpecialId sid, MutableHandleValue vp, bool strict);
+                               HandleSpecialId sid, MutableHandleValue vp, bool strict);
 
     static bool obj_getGenericAttributes(JSContext *cx, HandleObject obj,
-                                           HandleId id, unsigned *attrsp);
+                                         HandleId id, unsigned *attrsp);
     static bool obj_setGenericAttributes(JSContext *cx, HandleObject obj,
-                                           HandleId id, unsigned *attrsp);
+                                         HandleId id, unsigned *attrsp);
 
     static bool obj_deleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
-                                     bool *succeeded);
+                                   bool *succeeded);
     static bool obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
-                                    bool *succeeded);
+                                  bool *succeeded);
     static bool obj_deleteSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
-                                    bool *succeeded);
+                                  bool *succeeded);
 
     static bool obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
-                                MutableHandleValue statep, MutableHandleId idp);
+                              MutableHandleValue statep, MutableHandleId idp);
 
   public:
-    static const Class class_;
-
     // Returns the offset in bytes within the object where the `void*`
     // pointer can be found.
     static size_t dataOffset();
 
-    static bool isBlock(HandleObject val);
-    static uint8_t *mem(HandleObject val);
+    static TypedDatum *createUnattachedWithClass(JSContext *cx,
+                                                 const Class *clasp,
+                                                 HandleObject type);
+
+    // Creates an unattached typed object or handle (depending on the
+    // type parameter T). Note that it is only legal for unattached
+    // handles to escape to the end user; for non-handles, the caller
+    // should always invoke one of the `attach()` methods below.
+    //
+    // Arguments:
+    // - type: type object for resulting object
+    template<class T>
+    static T *createUnattached(JSContext *cx, HandleObject type);
+
+    // Creates a datum that aliases the memory pointed at by `owner`
+    // at the given offset. The datum will be a handle iff type is a
+    // handle and a typed object otherwise.
+    static TypedDatum *createDerived(JSContext *cx,
+                                     HandleObject type,
+                                     HandleObject typedContents,
+                                     size_t offset);
+
+    // If `this` is the owner of the memory, use this.
+    void attach(void *mem);
+
+    // Otherwise, use this to attach to memory referenced by another datum.
+    void attach(JSObject &datum, uint32_t offset);
+};
+
+class TypedObject : public TypedDatum
+{
+  public:
+    static const Class class_;
 
     // creates zeroed memory of size of type
     static JSObject *createZeroed(JSContext *cx, HandleObject type);
 
-    // creates a block that aliases the memory owned by `owner` at the
-    // given offset
-    static JSObject *createDerived(JSContext *cx, HandleObject type,
-                                   HandleObject owner, size_t offset);
-
     // user-accessible constructor (`new TypeDescriptor(...)`)
     static bool construct(JSContext *cx, unsigned argc, Value *vp);
 };
 
+class TypedHandle : public TypedDatum
+{
+  public:
+    static const Class class_;
+    static const JSFunctionSpec handleStaticMethods[];
+};
 
-// Usage: ClampToUint8(v)
-//
-// Same as the C function ClampDoubleToUint8. `v` must be a number.
+/*
+ * Usage: NewTypedHandle(typeObj)
+ *
+ * Constructs a new, unattached instance of `Handle`.
+ */
+bool NewTypedHandle(JSContext *cx, unsigned argc, Value *vp);
+
+/*
+ * Usage: NewDerivedTypedDatum(typeObj, owner, offset)
+ *
+ * Constructs a new, unattached instance of `Handle`.
+ */
+bool NewDerivedTypedDatum(JSContext *cx, unsigned argc, Value *vp);
+
+/*
+ * Usage: AttachHandle(handle, newOwner, newOffset)
+ *
+ * Moves `handle` to point at the memory owned by `newOwner` with
+ * the offset `newOffset`.
+ */
+bool AttachHandle(ThreadSafeContext *cx, unsigned argc, Value *vp);
+extern const JSJitInfo AttachHandleJitInfo;
+
+/*
+ * Usage: ObjectIsTypeObject(obj)
+ *
+ * True if `obj` is a type object.
+ */
+bool ObjectIsTypeObject(ThreadSafeContext *cx, unsigned argc, Value *vp);
+extern const JSJitInfo ObjectIsTypeObjectJitInfo;
+
+/*
+ * Usage: ObjectIsTypeRepresentation(obj)
+ *
+ * True if `obj` is a type representation object.
+ */
+bool ObjectIsTypeRepresentation(ThreadSafeContext *cx, unsigned argc, Value *vp);
+extern const JSJitInfo ObjectIsTypeRepresentationJitInfo;
+
+/*
+ * Usage: ObjectIsTypedHandle(obj)
+ *
+ * True if `obj` is a handle.
+ */
+bool ObjectIsTypedHandle(ThreadSafeContext *cx, unsigned argc, Value *vp);
+extern const JSJitInfo ObjectIsTypedHandleJitInfo;
+
+/*
+ * Usage: ObjectIsTypedObject(obj)
+ *
+ * True if `obj` is a typed object.
+ */
+bool ObjectIsTypedObject(ThreadSafeContext *cx, unsigned argc, Value *vp);
+extern const JSJitInfo ObjectIsTypedObjectJitInfo;
+
+/*
+ * Usage: IsAttached(obj)
+ *
+ * Given a TypedDatum `obj`, returns true if `obj` is
+ * "attached" (i.e., its data pointer is NULL).
+ */
+bool IsAttached(ThreadSafeContext *cx, unsigned argc, Value *vp);
+extern const JSJitInfo IsAttachedJitInfo;
+
+/*
+ * Usage: ClampToUint8(v)
+ *
+ * Same as the C function ClampDoubleToUint8. `v` must be a number.
+ */
 bool ClampToUint8(ThreadSafeContext *cx, unsigned argc, Value *vp);
 extern const JSJitInfo ClampToUint8JitInfo;
 
-// Usage: Memcpy(targetTypedObj, targetOffset,
-//               sourceTypedObj, sourceOffset,
-//               size)
-//
-// Intrinsic function. Copies size bytes from the data for
-// `sourceTypedObj` at `sourceOffset` into the data for
-// `targetTypedObj` at `targetOffset`.
+/*
+ * Usage: Memcpy(targetDatum, targetOffset,
+ *               sourceDatum, sourceOffset,
+ *               size)
+ *
+ * Intrinsic function. Copies size bytes from the data for
+ * `sourceDatum` at `sourceOffset` into the data for
+ * `targetDatum` at `targetOffset`.
+ *
+ * Both `sourceDatum` and `targetDatum` must be attached.
+ */
 bool Memcpy(ThreadSafeContext *cx, unsigned argc, Value *vp);
-
 extern const JSJitInfo MemcpyJitInfo;
 
-// Usage: StoreScalar(targetTypedObj, targetOffset, value)
-//
-// Intrinsic function. Stores value (which must be an int32 or uint32)
-// by `scalarTypeRepr` (which must be a type repr obj) and stores the
-// value at the memory for `targetTypedObj` at offset `targetOffset`.
+/*
+ * Usage: StoreScalar(targetDatum, targetOffset, value)
+ *
+ * Intrinsic function. Stores value (which must be an int32 or uint32)
+ * by `scalarTypeRepr` (which must be a type repr obj) and stores the
+ * value at the memory for `targetDatum` at offset `targetOffset`.
+ * `targetDatum` must be attached.
+ */
 #define JS_STORE_SCALAR_CLASS_DEFN(_constant, T, _name)                       \
 class StoreScalar##T {                                                        \
   public:                                                                     \
     static bool Func(ThreadSafeContext *cx, unsigned argc, Value *vp);        \
     static const JSJitInfo JitInfo;                                           \
 };
 
-// Usage: LoadScalar(targetTypedObj, targetOffset, value)
-//
-// Intrinsic function. Loads value (which must be an int32 or uint32)
-// by `scalarTypeRepr` (which must be a type repr obj) and loads the
-// value at the memory for `targetTypedObj` at offset `targetOffset`.
-// `targetTypedObj` must be attached.
+/*
+ * Usage: LoadScalar(targetDatum, targetOffset, value)
+ *
+ * Intrinsic function. Loads value (which must be an int32 or uint32)
+ * by `scalarTypeRepr` (which must be a type repr obj) and loads the
+ * value at the memory for `targetDatum` at offset `targetOffset`.
+ * `targetDatum` must be attached.
+ */
 #define JS_LOAD_SCALAR_CLASS_DEFN(_constant, T, _name)                        \
 class LoadScalar##T {                                                         \
   public:                                                                     \
     static bool Func(ThreadSafeContext *cx, unsigned argc, Value *vp);        \
     static const JSJitInfo JitInfo;                                           \
 };
 
 // I was using templates for this stuff instead of macros, but ran
--- a/js/src/builtin/TypedObject.js
+++ b/js/src/builtin/TypedObject.js
@@ -5,18 +5,20 @@
 
 // Type object slots
 
 #define TYPE_TYPE_REPR(obj) \
     UnsafeGetReservedSlot(obj, JS_TYPEOBJ_SLOT_TYPE_REPR)
 
 // Typed object slots
 
-#define TYPED_TYPE_OBJ(obj) \
-    UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_TYPE_OBJ)
+#define DATUM_TYPE_OBJ(obj) \
+    UnsafeGetReservedSlot(obj, JS_DATUM_SLOT_TYPE_OBJ)
+#define DATUM_OWNER(obj) \
+    UnsafeGetReservedSlot(obj, JS_DATUM_SLOT_OWNER)
 
 // Type repr slots
 
 #define REPR_KIND(obj)   \
     TO_INT32(UnsafeGetReservedSlot(obj, JS_TYPEREPR_SLOT_KIND))
 #define REPR_SIZE(obj)   \
     TO_INT32(UnsafeGetReservedSlot(obj, JS_TYPEREPR_SLOT_SIZE))
 #define REPR_ALIGNMENT(obj) \
@@ -24,65 +26,72 @@
 #define REPR_LENGTH(obj)   \
     TO_INT32(UnsafeGetReservedSlot(obj, JS_TYPEREPR_SLOT_LENGTH))
 #define REPR_TYPE(obj)   \
     TO_INT32(UnsafeGetReservedSlot(obj, JS_TYPEREPR_SLOT_TYPE))
 
 #define HAS_PROPERTY(obj, prop) \
     callFunction(std_Object_hasOwnProperty, obj, prop)
 
-function TYPED_TYPE_REPR(obj) {
+function DATUM_TYPE_REPR(obj) {
   // Eventually this will be a slot on typed objects
-  return TYPE_TYPE_REPR(TYPED_TYPE_OBJ(obj));
+  return TYPE_TYPE_REPR(DATUM_TYPE_OBJ(obj));
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // TypedObjectPointer
 //
 // TypedObjectPointers are internal structs used to represent a
 // pointer into typed object memory. They pull together:
 // - typeRepr: the internal type representation
 // - typeObj: the user-visible type object
-// - owner: the owner object that contains the allocated block of memory
-// - offset: an offset into that owner object
+// - datum: the typed object that contains the allocated block of memory
+// - offset: an offset into that typed object
 //
 // They are basically equivalent to a typed object, except that they
 // offer lots of internal unsafe methods and are not native objects.
 // These should never escape into user code; ideally ion would stack
 // allocate them.
 //
 // Most `TypedObjectPointers` methods are written in a "chaining"
 // style, meaning that they return `this`. This is true even though
 // they mutate the receiver in place, because it makes for prettier
 // code.
 
-function TypedObjectPointer(typeRepr, typeObj, owner, offset) {
+function TypedObjectPointer(typeRepr, typeObj, datum, offset) {
   this.typeRepr = typeRepr;
   this.typeObj = typeObj;
-  this.owner = owner;
+  this.datum = datum;
   this.offset = offset;
 }
 
 MakeConstructible(TypedObjectPointer, {});
 
+TypedObjectPointer.fromTypedDatum = function(typed) {
+  return new TypedObjectPointer(DATUM_TYPE_REPR(typed),
+                                DATUM_TYPE_OBJ(typed),
+                                typed,
+                                0);
+}
+
 #ifdef DEBUG
 TypedObjectPointer.prototype.toString = function() {
   return "Ptr(" + this.typeObj.toSource() + " @ " + this.offset + ")";
 };
 #endif
 
 TypedObjectPointer.prototype.copy = function() {
   return new TypedObjectPointer(this.typeRepr, this.typeObj,
-                                this.owner, this.offset);
+                                this.datum, this.offset);
 };
 
 TypedObjectPointer.prototype.reset = function(inPtr) {
   this.typeRepr = inPtr.typeRepr;
   this.typeObj = inPtr.typeObj;
-  this.owner = inPtr.owner;
+  this.datum = inPtr.datum;
   this.offset = inPtr.offset;
   return this;
 };
 
 TypedObjectPointer.prototype.kind = function() {
   return REPR_KIND(this.typeRepr);
 }
 
@@ -157,33 +166,92 @@ TypedObjectPointer.prototype.moveToField
   // Note: we do not allow construction of structs where the
   // offset of a field cannot be represented by an int32.
   this.offset += fieldOffset;
 
   return this;
 }
 
 ///////////////////////////////////////////////////////////////////////////
+// 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
+// returns a new object pointing at the value. If the value is
+// a scalar, it will return a JS number, but otherwise the reified
+// result will be a typed object or handle, depending on the type
+// of the ptr's datum.
+TypedObjectPointer.prototype.get = function() {
+  assert(ObjectIsAttached(this.datum), "get() called with unattached datum");
+
+  if (REPR_KIND(this.typeRepr) == JS_TYPEREPR_SCALAR_KIND)
+    return this.getScalar();
+
+  return NewDerivedTypedDatum(this.typeObj, this.datum, this.offset);
+}
+
+TypedObjectPointer.prototype.getScalar = function() {
+  var type = REPR_TYPE(this.typeRepr);
+  switch (type) {
+  case JS_SCALARTYPEREPR_INT8:
+    return Load_int8(this.datum, this.offset);
+
+  case JS_SCALARTYPEREPR_UINT8:
+  case JS_SCALARTYPEREPR_UINT8_CLAMPED:
+    return Load_uint8(this.datum, this.offset);
+
+  case JS_SCALARTYPEREPR_INT16:
+    return Load_int16(this.datum, this.offset);
+
+  case JS_SCALARTYPEREPR_UINT16:
+    return Load_uint16(this.datum, this.offset);
+
+  case JS_SCALARTYPEREPR_INT32:
+    return Load_int32(this.datum, this.offset);
+
+  case JS_SCALARTYPEREPR_UINT32:
+    return Load_uint32(this.datum, this.offset);
+
+  case JS_SCALARTYPEREPR_FLOAT32:
+    return Load_float32(this.datum, this.offset);
+
+  case JS_SCALARTYPEREPR_FLOAT64:
+    return Load_float64(this.datum, this.offset);
+  }
+
+  assert(false, "Unhandled scalar type: " + type);
+}
+
+///////////////////////////////////////////////////////////////////////////
 // Setting values
 //
 // The methods in this section modify the data pointed at by `this`.
 
 // Assigns `fromValue` to the memory pointed at by `this`, adapting it
 // to `typeRepr` as needed. This is the most general entry point and
 // works for any type.
 TypedObjectPointer.prototype.set = function(fromValue) {
+  assert(ObjectIsAttached(this.datum), "set() called with unattached datum");
+
   var typeRepr = this.typeRepr;
 
   // 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) && HaveSameClass(fromValue, this.owner)) {
-    if (TYPED_TYPE_REPR(fromValue) === typeRepr) {
+  if (IsObject(fromValue) && HaveSameClass(fromValue, this.datum)) {
+    if (DATUM_TYPE_REPR(fromValue) === typeRepr) {
+      if (!ObjectIsAttached(fromValue))
+        ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
+
       var size = REPR_SIZE(typeRepr);
-      Memcpy(this.owner, this.offset, fromValue, 0, size);
+      Memcpy(this.datum, this.offset, fromValue, 0, size);
       return;
     }
   }
 
   switch (REPR_KIND(typeRepr)) {
   case JS_TYPEREPR_SCALAR_KIND:
     this.setScalar(fromValue);
     return;
@@ -228,84 +296,204 @@ TypedObjectPointer.prototype.set = funct
 // Sets `fromValue` to `this` assuming that `this` is a scalar type.
 TypedObjectPointer.prototype.setScalar = function(fromValue) {
   assert(REPR_KIND(this.typeRepr) == JS_TYPEREPR_SCALAR_KIND,
          "setScalar called with non-scalar");
 
   var type = REPR_TYPE(this.typeRepr);
   switch (type) {
   case JS_SCALARTYPEREPR_INT8:
-    return Store_int8(this.owner, this.offset,
+    return Store_int8(this.datum, this.offset,
                      TO_INT32(fromValue) & 0xFF);
 
   case JS_SCALARTYPEREPR_UINT8:
-    return Store_uint8(this.owner, this.offset,
+    return Store_uint8(this.datum, this.offset,
                       TO_UINT32(fromValue) & 0xFF);
 
   case JS_SCALARTYPEREPR_UINT8_CLAMPED:
     var v = ClampToUint8(+fromValue);
-    return Store_int8(this.owner, this.offset, v);
+    return Store_int8(this.datum, this.offset, v);
 
   case JS_SCALARTYPEREPR_INT16:
-    return Store_int16(this.owner, this.offset,
+    return Store_int16(this.datum, this.offset,
                       TO_INT32(fromValue) & 0xFFFF);
 
   case JS_SCALARTYPEREPR_UINT16:
-    return Store_uint16(this.owner, this.offset,
+    return Store_uint16(this.datum, this.offset,
                        TO_UINT32(fromValue) & 0xFFFF);
 
   case JS_SCALARTYPEREPR_INT32:
-    return Store_int32(this.owner, this.offset,
+    return Store_int32(this.datum, this.offset,
                       TO_INT32(fromValue));
 
   case JS_SCALARTYPEREPR_UINT32:
-    return Store_uint32(this.owner, this.offset,
+    return Store_uint32(this.datum, this.offset,
                        TO_UINT32(fromValue));
 
   case JS_SCALARTYPEREPR_FLOAT32:
-    return Store_float32(this.owner, this.offset, +fromValue);
+    return Store_float32(this.datum, this.offset, +fromValue);
 
   case JS_SCALARTYPEREPR_FLOAT64:
-    return Store_float64(this.owner, this.offset, +fromValue);
+    return Store_float64(this.datum, this.offset, +fromValue);
   }
 
   assert(false, "Unhandled scalar type: " + type);
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // C++ Wrappers
 //
 // These helpers are invoked by C++ code or used as method bodies.
 
 // Wrapper for use from C++ code.
 function ConvertAndCopyTo(destTypeRepr,
                           destTypeObj,
-                          destTypedObj,
+                          destDatum,
                           destOffset,
                           fromValue)
 {
+  assert(IsObject(destTypeRepr) && ObjectIsTypeRepresentation(destTypeRepr),
+         "ConvertAndCopyTo: not type repr");
+  assert(IsObject(destTypeObj) && ObjectIsTypeObject(destTypeObj),
+         "ConvertAndCopyTo: not type obj");
+  assert(IsObject(destDatum) && ObjectIsTypedDatum(destDatum),
+         "ConvertAndCopyTo: not type datum");
+
+  if (!ObjectIsAttached(destDatum))
+    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
+
   var ptr = new TypedObjectPointer(destTypeRepr, destTypeObj,
-                                   destTypedObj, destOffset);
+                                   destDatum, destOffset);
   ptr.set(fromValue);
 }
 
+// Wrapper for use from C++ code.
+function Reify(sourceTypeRepr,
+               sourceTypeObj,
+               sourceDatum,
+               sourceOffset) {
+  assert(IsObject(sourceTypeRepr) && ObjectIsTypeRepresentation(sourceTypeRepr),
+         "Reify: not type repr");
+  assert(IsObject(sourceTypeObj) && ObjectIsTypeObject(sourceTypeObj),
+         "Reify: not type obj");
+  assert(IsObject(sourceDatum) && ObjectIsTypedDatum(sourceDatum),
+         "Reify: not type datum");
+
+  if (!ObjectIsAttached(sourceDatum))
+    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
+
+  var ptr = new TypedObjectPointer(sourceTypeRepr, sourceTypeObj,
+                                   sourceDatum, sourceOffset);
+
+  return ptr.get();
+}
+
 function FillTypedArrayWithValue(destArray, fromValue) {
-  var typeRepr = TYPED_TYPE_REPR(destArray);
+  var typeRepr = DATUM_TYPE_REPR(destArray);
   var length = REPR_LENGTH(typeRepr);
   if (length === 0)
     return;
 
   // Use convert and copy to to produce the first element:
-  var ptr = new TypedObjectPointer(typeRepr,
-                                   TYPED_TYPE_OBJ(destArray),
-                                   destArray,
-                                   0);
+  var ptr = TypedObjectPointer.fromTypedDatum(destArray);
   ptr.moveToElem(0);
   ptr.set(fromValue);
 
   // Stamp out the remaining copies:
   var elementSize = REPR_SIZE(ptr.typeRepr);
   var totalSize = length * elementSize;
   for (var offset = elementSize; offset < totalSize; offset += elementSize)
     Memcpy(destArray, offset, destArray, 0, elementSize);
 }
 
+///////////////////////////////////////////////////////////////////////////
+// Handles
+//
+// Note: these methods are directly invokable by users and so must be
+// defensive.
 
+// This is the `handle([obj, [...path]])` method on type objects.
+// User exposed!
+//
+// FIXME bug 929656 -- label algorithms with steps from the spec
+function HandleCreate(obj, ...path) {
+  if (!ObjectIsTypeObject(this))
+    ThrowError(JSMSG_INCOMPATIBLE_PROTO, "Type", "handle", "value");
+
+  var handle = NewTypedHandle(this);
+
+  if (obj !== undefined)
+    HandleMoveInternal(handle, obj, path)
+
+  return handle;
+}
+
+// Handle.move: user exposed!
+// FIXME bug 929656 -- label algorithms with steps from the spec
+function HandleMove(handle, obj, ...path) {
+  if (!ObjectIsTypedHandle(handle))
+    ThrowError(JSMSG_INCOMPATIBLE_PROTO, "Handle", "set", typeof value);
+
+  HandleMoveInternal(handle, obj, path);
+}
+
+function HandleMoveInternal(handle, obj, path) {
+  assert(ObjectIsTypedHandle(handle),
+         "HandleMoveInternal: not typed handle");
+
+  if (!IsObject(obj) || !ObjectIsTypedDatum(obj))
+    ThrowError(JSMSG_INCOMPATIBLE_PROTO);
+
+  var ptr = TypedObjectPointer.fromTypedDatum(obj);
+  for (var i = 0; i < path.length; i++)
+    ptr.moveTo(path[i]);
+
+  // Check that the new destination is equivalent to the handle type.
+  if (ptr.typeRepr !== DATUM_TYPE_REPR(handle))
+    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_TYPE);
+
+  AttachHandle(handle, ptr.datum, ptr.offset)
+}
+
+// Handle.get: user exposed!
+// FIXME bug 929656 -- label algorithms with steps from the spec
+function HandleGet(handle) {
+  if (!IsObject(handle) || !ObjectIsTypedHandle(handle))
+    ThrowError(JSMSG_INCOMPATIBLE_PROTO, "Handle", "set", typeof value);
+
+  if (!ObjectIsAttached(handle))
+    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
+
+  var ptr = TypedObjectPointer.fromTypedDatum(handle);
+  return ptr.get();
+}
+
+// Handle.set: user exposed!
+// FIXME bug 929656 -- label algorithms with steps from the spec
+function HandleSet(handle, value) {
+  if (!IsObject(handle) || !ObjectIsTypedHandle(handle))
+    ThrowError(JSMSG_INCOMPATIBLE_PROTO, "Handle", "set", typeof value);
+
+  if (!ObjectIsAttached(handle))
+    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
+
+  var ptr = TypedObjectPointer.fromTypedDatum(handle);
+  ptr.set(value);
+}
+
+// Handle.isHandle: user exposed!
+// FIXME bug 929656 -- label algorithms with steps from the spec
+function HandleTest(obj) {
+  return IsObject(obj) && ObjectIsTypedHandle(obj);
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Miscellaneous
+
+function ObjectIsTypedDatum(obj) {
+  return ObjectIsTypedObject(obj) || ObjectIsTypedHandle(obj);
+}
+
+function ObjectIsAttached(obj) {
+  assert(ObjectIsTypedDatum(obj),
+         "ObjectIsAttached() invoked on invalid obj");
+  return DATUM_OWNER(obj) != null;
+}
--- a/js/src/builtin/TypedObjectConstants.h
+++ b/js/src/builtin/TypedObjectConstants.h
@@ -72,15 +72,15 @@
 #define JS_SCALARTYPEREPR_UINT16        3
 #define JS_SCALARTYPEREPR_INT32         4
 #define JS_SCALARTYPEREPR_UINT32        5
 #define JS_SCALARTYPEREPR_FLOAT32       6
 #define JS_SCALARTYPEREPR_FLOAT64       7
 #define JS_SCALARTYPEREPR_UINT8_CLAMPED 8
 
 ///////////////////////////////////////////////////////////////////////////
-// Slots for typed objects
+// Slots for typed objects (actually, any TypedContents objects)
 
-#define JS_TYPEDOBJ_SLOT_TYPE_OBJ 0  // Type object for a given typed object
-#define JS_TYPEDOBJ_SLOT_OWNER    1  // Owner of data (if null, this is owner)
-#define JS_TYPEDOBJ_SLOTS         2  // Number of slots for typed objs
+#define JS_DATUM_SLOT_TYPE_OBJ 0  // Type object for a given typed object
+#define JS_DATUM_SLOT_OWNER    1  // Owner of data (if null, this is owner)
+#define JS_DATUM_SLOTS         2  // Number of slots for typed objs
 
 #endif
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3654,17 +3654,17 @@ CodeGenerator::visitTypedArrayElements(L
     return true;
 }
 
 bool
 CodeGenerator::visitTypedObjectElements(LTypedObjectElements *lir)
 {
     Register obj = ToRegister(lir->object());
     Register out = ToRegister(lir->output());
-    masm.loadPtr(Address(obj, BinaryBlock::dataOffset()), out);
+    masm.loadPtr(Address(obj, TypedObject::dataOffset()), out);
     return true;
 }
 
 bool
 CodeGenerator::visitStringLength(LStringLength *lir)
 {
     Register input = ToRegister(lir->string());
     Register output = ToRegister(lir->output());
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -9554,17 +9554,17 @@ 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(typedObj,
-                                             JS_TYPEDOBJ_SLOT_TYPE_OBJ);
+                                             JS_DATUM_SLOT_TYPE_OBJ);
     current->add(load);
     return load;
 }
 
 // Given a typed object `typedObj` and an offset `offset` into that
 // object's data, returns another typed object and adusted offset
 // where the data can be found. Often, these returned values are the
 // same as the inputs, but in cases where intermediate derived type
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -892,14 +892,14 @@ bool
 InitBaselineFrameForOsr(BaselineFrame *frame, StackFrame *interpFrame, uint32_t numStackValues)
 {
     return frame->initForOsr(interpFrame, numStackValues);
 }
 
 JSObject *CreateDerivedTypedObj(JSContext *cx, HandleObject type,
                                 HandleObject owner, int32_t offset)
 {
-    return BinaryBlock::createDerived(cx, type, owner, offset);
+    return TypedObject::createDerived(cx, type, owner, offset);
 }
 
 
 } // namespace jit
 } // namespace js
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -412,8 +412,11 @@ MSG_DEF(JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD
 MSG_DEF(JSMSG_GENERATOR_FINISHED,     359, 0, JSEXN_TYPEERR, "generator has already finished")
 MSG_DEF(JSMSG_TYPEDOBJECT_TOO_BIG, 360, 0, JSEXN_ERR, "Type is too large to allocate")
 MSG_DEF(JSMSG_TYPEDOBJECT_NOT_TYPE_OBJECT, 361, 0, JSEXN_ERR, "Expected a type object")
 MSG_DEF(JSMSG_TOO_MANY_CON_SPREADARGS, 362, 0, JSEXN_RANGEERR, "too many constructor arguments")
 MSG_DEF(JSMSG_TOO_MANY_FUN_SPREADARGS, 363, 0, JSEXN_RANGEERR, "too many function arguments")
 MSG_DEF(JSMSG_DEBUG_NOT_DEBUGGEE,     364, 2, JSEXN_ERR, "{0} is not a debuggee {1}")
 MSG_DEF(JSMSG_TYPEDOBJECT_NOT_TYPED_OBJECT, 365, 0, JSEXN_ERR, "Expected a typed object")
 MSG_DEF(JSMSG_TYPEDOBJECT_NO_SUCH_PROP, 366, 1, JSEXN_TYPEERR, "No such property: {0}")
+MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, 367, 2, JSEXN_TYPEERR, "argument {0} invalid: expected {1}")
+MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED, 368, 0, JSEXN_TYPEERR, "handle unattached")
+MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_BAD_TYPE, 369, 0, JSEXN_TYPEERR, "handle moved to destination of incorrect type")
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedObject/handle.js
@@ -0,0 +1,60 @@
+// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var BUGNUMBER = 898342;
+var summary = 'Handles';
+
+var T = TypedObject;
+
+function runTests() {
+  var Point = new T.ArrayType(T.float32, 3);
+  var Line = new T.StructType({from: Point, to: Point});
+  var Lines = new T.ArrayType(Line, 3);
+
+  var lines = new Lines([
+    {from: [1, 2, 3], to: [4, 5, 6]},
+    {from: [7, 8, 9], to: [10, 11, 12]},
+    {from: [13, 14, 15], to: [16, 17, 18]}
+  ]);
+
+  var handle = Lines.handle(lines);
+  var handle0 = Line.handle(lines, 0);
+  var handle2 = Line.handle(lines, 2);
+
+  // Reads from handles should see the correct data:
+  assertEq(handle[0].from[0], 1);
+  assertEq(handle0.from[0], 1);
+  assertEq(handle2.from[0], 13);
+
+  // Writes to handles should modify the original:
+  handle2.from[0] = 22;
+  assertEq(lines[2].from[0], 22);
+
+  // Reads from handles should see the updated data:
+  assertEq(handle[0].from[0], 1);
+  assertEq(handle0.from[0], 1);
+  assertEq(handle2.from[0], 22);
+
+  // isHandle, when called on nonsense, returns false:
+  assertEq(T.Handle.isHandle(22), false);
+  assertEq(T.Handle.isHandle({}), false);
+
+  // Derived handles should remain handles:
+  assertEq(T.Handle.isHandle(lines), false);
+  assertEq(T.Handle.isHandle(lines[0]), false);
+  assertEq(T.Handle.isHandle(lines[0].from), false);
+  assertEq(T.Handle.isHandle(handle), true);
+  assertEq(T.Handle.isHandle(handle[0]), true);
+  assertEq(T.Handle.isHandle(handle[0].from), true);
+
+  reportCompare(true, true);
+  print("Tests complete");
+}
+
+runTests();
+
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedObject/handle_get_set.js
@@ -0,0 +1,60 @@
+// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var BUGNUMBER = 898342;
+var summary = 'Handle Move';
+
+var T = TypedObject;
+
+var Point = new T.ArrayType(T.float32, 3);
+var Line = new T.StructType({from: Point, to: Point});
+var Lines = new T.ArrayType(Line, 3);
+
+function runTests() {
+  function testHandleGetSetWithScalarType() {
+    var lines = new Lines([
+      {from: [1, 2, 3], to: [4, 5, 6]},
+      {from: [7, 8, 9], to: [10, 11, 12]},
+      {from: [13, 14, 15], to: [16, 17, 18]}
+    ]);
+
+    var handle = T.float32.handle(lines, 0, "to", 1);
+    assertEq(T.Handle.get(handle), 5);
+    T.Handle.set(handle, 22);
+    assertEq(T.Handle.get(handle), 22);
+    assertEq(lines[0].to[1], 22);
+  }
+  testHandleGetSetWithScalarType();
+
+  function testHandleGetSetWithComplexType() {
+    var lines = new Lines([
+      {from: [1, 2, 3], to: [4, 5, 6]},
+      {from: [7, 8, 9], to: [10, 11, 12]},
+      {from: [13, 14, 15], to: [16, 17, 18]}
+    ]);
+
+    var handle = Point.handle(lines, 0, "to");
+
+    T.Handle.set(handle, [22, 23, 24]);
+
+    assertEq(handle[0], 22);
+    assertEq(handle[1], 23);
+    assertEq(handle[2], 24);
+
+    assertEq(T.Handle.get(handle)[0], 22);
+    assertEq(T.Handle.get(handle)[1], 23);
+    assertEq(T.Handle.get(handle)[2], 24);
+  }
+  testHandleGetSetWithComplexType();
+
+  reportCompare(true, true);
+  print("Tests complete");
+}
+
+runTests();
+
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedObject/handle_move.js
@@ -0,0 +1,157 @@
+// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var BUGNUMBER = 898342;
+var summary = 'Handle Move';
+
+var T = TypedObject;
+
+var Point = new T.ArrayType(T.float32, 3);
+var Line = new T.StructType({from: Point, to: Point});
+var Lines = new T.ArrayType(Line, 3);
+
+function runTests() {
+  function testHandleToPoint() {
+    var lines = new Lines([
+      {from: [1, 2, 3], to: [4, 5, 6]},
+      {from: [7, 8, 9], to: [10, 11, 12]},
+      {from: [13, 14, 15], to: [16, 17, 18]}
+    ]);
+
+    function allPoints(lines, func) {
+      var handle = Point.handle();
+      for (var i = 0; i < lines.length; i++) {
+        T.Handle.move(handle, lines, i, "from");
+        func(handle);
+
+        T.Handle.move(handle, lines, i, "to");
+        func(handle);
+      }
+    }
+
+    // Iterate over all ponts and mutate them in place:
+    allPoints(lines, function(p) {
+      p[0] += 100;
+      p[1] += 200;
+      p[2] += 300;
+    });
+
+    // Spot check the results
+    assertEq(lines[0].from[0], 101);
+    assertEq(lines[1].to[1], 211);
+    assertEq(lines[2].to[2], 318);
+  }
+  testHandleToPoint();
+
+  function testHandleToFloat() {
+    var lines = new Lines([
+      {from: [1, 2, 3], to: [4, 5, 6]},
+      {from: [7, 8, 9], to: [10, 11, 12]},
+      {from: [13, 14, 15], to: [16, 17, 18]}
+    ]);
+
+    function allPoints(lines, func) {
+      var handle = T.float32.handle();
+      for (var i = 0; i < lines.length; i++) {
+        T.Handle.move(handle, lines, i, "from", 0);
+        func(handle);
+
+        T.Handle.move(handle, lines, i, "from", 1);
+        func(handle);
+
+        T.Handle.move(handle, lines, i, "from", 2);
+        func(handle);
+
+        T.Handle.move(handle, lines, i, "to", 0);
+        func(handle);
+
+        T.Handle.move(handle, lines, i, "to", 1);
+        func(handle);
+
+        T.Handle.move(handle, lines, i, "to", 2);
+        func(handle);
+      }
+    }
+
+    // Iterate over all ponts and mutate them in place:
+    allPoints(lines, function(p) {
+      T.Handle.set(p, T.Handle.get(p) + 100);
+    });
+
+    // Spot check the results
+    assertEq(lines[0].from[0], 101);
+    assertEq(lines[1].to[1], 111);
+    assertEq(lines[2].to[2], 118);
+  }
+  testHandleToFloat();
+
+  function testHandleToEquivalentType() {
+    var Point2 = new T.ArrayType(T.float32, 3);
+
+    assertEq(Point.equivalent(Point2), true);
+
+    var lines = new Lines([
+      {from: [1, 2, 3], to: [4, 5, 6]},
+      {from: [7, 8, 9], to: [10, 11, 12]},
+      {from: [13, 14, 15], to: [16, 17, 18]}
+    ]);
+
+    var handle = Point2.handle(lines, 0, "to");
+    assertEq(handle[0], 4);
+    assertEq(handle[1], 5);
+    assertEq(handle[2], 6);
+  }
+  testHandleToEquivalentType();
+
+  function testHandleMoveToIllegalType() {
+    var lines = new Lines([
+      {from: [1, 2, 3], to: [4, 5, 6]},
+      {from: [7, 8, 9], to: [10, 11, 12]},
+      {from: [13, 14, 15], to: [16, 17, 18]}
+    ]);
+
+    // Moving a handle to a value of incorrect type should report an error:
+    assertThrowsInstanceOf(function() {
+      Line.handle(lines);
+    }, TypeError, "handle moved to destination of incorrect type");
+    assertThrowsInstanceOf(function() {
+      var h = Line.handle();
+      T.Handle.move(h, lines);
+    }, TypeError, "handle moved to destination of incorrect type");
+    assertThrowsInstanceOf(function() {
+      var h = T.float32.handle();
+      T.Handle.move(h, lines, 0);
+    }, TypeError, "handle moved to destination of incorrect type");
+  }
+  testHandleMoveToIllegalType();
+
+  function testHandleMoveToIllegalProperty() {
+    var lines = new Lines([
+      {from: [1, 2, 3], to: [4, 5, 6]},
+      {from: [7, 8, 9], to: [10, 11, 12]},
+      {from: [13, 14, 15], to: [16, 17, 18]}
+    ]);
+
+    assertThrowsInstanceOf(function() {
+      var h = Point.handle();
+      T.Handle.move(h, lines, 0, "foo");
+    }, TypeError, "No such property: foo");
+
+    assertThrowsInstanceOf(function() {
+      var h = Point.handle();
+      T.Handle.move(h, lines, 22, "to");
+    }, TypeError, "No such property: 22");
+  }
+  testHandleMoveToIllegalProperty();
+
+  reportCompare(true, true);
+  print("Tests complete");
+}
+
+runTests();
+
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedObject/handle_unattached.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var BUGNUMBER = 898342;
+var summary = 'Unattached handles';
+
+var T = TypedObject;
+
+function runTests() {
+  var Line = new T.StructType({from: T.uint8, to: T.uint8});
+  var Lines = new T.ArrayType(Line, 3);
+
+  // Create unattached handle to array, struct:
+  var handle = Lines.handle();
+  var handle0 = Line.handle();
+
+  // Accessing properties throws:
+  assertThrowsInstanceOf(function() handle[0], TypeError,
+                         "Unattached handle did not yield error");
+  assertThrowsInstanceOf(function() handle0.from, TypeError,
+                         "Unattached handle did not yield error");
+
+  // Handle.get() throws:
+  assertThrowsInstanceOf(function() T.Handle.get(handle), TypeError,
+                         "Unattached handle did not yield error");
+  assertThrowsInstanceOf(function() T.Handle.get(handle0), TypeError,
+                         "Unattached handle did not yield error");
+
+  // Handle.set() throws:
+  assertThrowsInstanceOf(function() T.Handle.set(handle, [{},{},{}]), TypeError,
+                         "Unattached handle did not yield error");
+  assertThrowsInstanceOf(function() T.Handle.set(handle0, {}), TypeError,
+                         "Unattached handle did not yield error");
+
+  reportCompare(true, true);
+  print("Tests complete");
+}
+
+runTests();
+
+
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -69,16 +69,17 @@
     macro(float64, float64, "float64") \
     macro(format, format, "format") \
     macro(get, get, "get") \
     macro(getInternals, getInternals, "getInternals") \
     macro(getOwnPropertyDescriptor, getOwnPropertyDescriptor, "getOwnPropertyDescriptor") \
     macro(getOwnPropertyNames, getOwnPropertyNames, "getOwnPropertyNames") \
     macro(getPropertyDescriptor, getPropertyDescriptor, "getPropertyDescriptor") \
     macro(global, global, "global") \
+    macro(Handle, Handle, "Handle") \
     macro(has, has, "has") \
     macro(hasOwn, hasOwn, "hasOwn") \
     macro(hasOwnProperty, hasOwnProperty, "hasOwnProperty") \
     macro(ignoreCase, ignoreCase, "ignoreCase") \
     macro(ignorePunctuation, ignorePunctuation, "ignorePunctuation") \
     macro(index, index, "index") \
     macro(InitializeCollator, InitializeCollator, "InitializeCollator") \
     macro(InitializeDateTimeFormat, InitializeDateTimeFormat, "InitializeDateTimeFormat") \
@@ -127,16 +128,17 @@
     macro(outOfMemory, outOfMemory, "out of memory") \
     macro(parseFloat, parseFloat, "parseFloat") \
     macro(parseInt, parseInt, "parseInt") \
     macro(pattern, pattern, "pattern") \
     macro(preventExtensions, preventExtensions, "preventExtensions") \
     macro(propertyIsEnumerable, propertyIsEnumerable, "propertyIsEnumerable") \
     macro(proto, proto, "__proto__") \
     macro(prototype, prototype, "prototype") \
+    macro(Reify, Reify, "Reify") \
     macro(return, return_, "return") \
     macro(sensitivity, sensitivity, "sensitivity") \
     macro(set, set, "set") \
     macro(shape, shape, "shape") \
     macro(source, source, "source") \
     macro(stack, stack, "stack") \
     macro(sticky, sticky, "sticky") \
     macro(StructType, StructType, "StructType") \
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -623,16 +623,40 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("ForkJoin",                intrinsic_ForkJoin,                2,0),
     JS_FN("ForkJoinSlices",          intrinsic_ForkJoinSlices,          0,0),
     JS_FN("NewParallelArray",        intrinsic_NewParallelArray,        3,0),
     JS_FN("NewDenseArray",           intrinsic_NewDenseArray,           1,0),
     JS_FN("ShouldForceSequential",   intrinsic_ShouldForceSequential,   0,0),
     JS_FN("ParallelTestsShouldPass", intrinsic_ParallelTestsShouldPass, 0,0),
 
     // See builtin/TypedObject.h for descriptors of the typedobj functions.
+    JS_FN("NewTypedHandle",
+          js::NewTypedHandle,
+          1, 0),
+    JS_FN("NewDerivedTypedDatum",
+          js::NewDerivedTypedDatum,
+          3, 0),
+    JS_FNINFO("AttachHandle",
+              JSNativeThreadSafeWrapper<js::AttachHandle>,
+              &js::AttachHandleJitInfo, 5, 0),
+    JS_FNINFO("ObjectIsTypeObject",
+              JSNativeThreadSafeWrapper<js::ObjectIsTypeObject>,
+              &js::ObjectIsTypeObjectJitInfo, 5, 0),
+    JS_FNINFO("ObjectIsTypeRepresentation",
+              JSNativeThreadSafeWrapper<js::ObjectIsTypeRepresentation>,
+              &js::ObjectIsTypeRepresentationJitInfo, 5, 0),
+    JS_FNINFO("ObjectIsTypedObject",
+              JSNativeThreadSafeWrapper<js::ObjectIsTypedObject>,
+              &js::ObjectIsTypedObjectJitInfo, 5, 0),
+    JS_FNINFO("ObjectIsTypedHandle",
+              JSNativeThreadSafeWrapper<js::ObjectIsTypedHandle>,
+              &js::ObjectIsTypedHandleJitInfo, 5, 0),
+    JS_FN("NewHandle",
+          js::NewTypedHandle,
+          1, 0),
     JS_FNINFO("ClampToUint8",
               JSNativeThreadSafeWrapper<js::ClampToUint8>,
               &js::ClampToUint8JitInfo, 1, 0),
     JS_FNINFO("Memcpy",
               JSNativeThreadSafeWrapper<js::Memcpy>,
               &js::MemcpyJitInfo, 5, 0),
 
 #define LOAD_AND_STORE_FN_DECLS(_constant, _type, _name)                      \