Bug 898362 - Self-host portions of the typed object logic r=till
authorNicholas D. Matsakis <nmatsakis@mozilla.com>
Tue, 10 Sep 2013 10:35:53 -0400
changeset 151381 fbbdf3bb140c507af0f6a1bfcedd99c7f39dc712
parent 151380 acc16ae0e638e5aed663c63425e507d974ec0417
child 151382 bf072b9171725d2ddc5b535c42a56f007db383c7
push id35146
push usernmatsakis@mozilla.com
push dateFri, 18 Oct 2013 19:52:56 +0000
treeherdermozilla-inbound@fbbdf3bb140c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill
bugs898362
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 898362 - Self-host portions of the typed object logic r=till
js/src/Makefile.in
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/builtin/Utilities.js
js/src/jit/IonBuilder.cpp
js/src/js.msg
js/src/jscntxt.h
js/src/tests/ecma_6/TypedObject/Bug914137.js
js/src/tests/ecma_6/TypedObject/arraytype.js
js/src/tests/ecma_6/TypedObject/numerictypes.js
js/src/tests/ecma_6/TypedObject/scalar_types.js
js/src/vm/CommonPropertyNames.h
js/src/vm/SelfHosting.cpp
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -659,21 +659,22 @@ selfhosting:: selfhosted.out.h
 
 selfhosting_srcs := \
   $(srcdir)/builtin/Utilities.js \
   $(srcdir)/builtin/Array.js \
   $(srcdir)/builtin/Date.js \
   $(srcdir)/builtin/Intl.js \
   $(srcdir)/builtin/IntlData.js \
   $(srcdir)/builtin/Iterator.js \
+  $(srcdir)/builtin/Map.js \
   $(srcdir)/builtin/Number.js \
   $(srcdir)/builtin/ParallelArray.js \
   $(srcdir)/builtin/String.js \
   $(srcdir)/builtin/Set.js \
-  $(srcdir)/builtin/Map.js \
+  $(srcdir)/builtin/TypedObject.js \
   $(NULL)
 
 selfhosted_out_h_deps := \
   $(selfhosting_srcs) \
   $(srcdir)/js.msg \
   $(srcdir)/builtin/embedjs.py \
   $(NULL)
 
--- a/js/src/builtin/TypeRepresentation.cpp
+++ b/js/src/builtin/TypeRepresentation.cpp
@@ -23,17 +23,18 @@ using namespace js;
 using namespace mozilla;
 
 ///////////////////////////////////////////////////////////////////////////
 // Class def'n for the owner object
 
 const Class TypeRepresentation::class_ = {
     "TypeRepresentation",
     JSCLASS_IMPLEMENTS_BARRIERS |
-    JSCLASS_HAS_PRIVATE,
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_RESERVED_SLOTS(JS_TYPEREPR_SLOTS),
     JS_PropertyStub,         /* addProperty */
     JS_DeletePropertyStub,   /* delProperty */
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     obj_finalize,
@@ -280,17 +281,39 @@ TypeRepresentation::addToTableOrFree(JSC
         NewBuiltinClassInstance(cx,
                                 &class_,
                                 gc::GetGCObjectKind(&class_)));
     if (!ownerObject) {
         comp->typeReprs.remove(this);
         js_free(this);
         return nullptr;
     }
+
     ownerObject->setPrivate(this);
+
+    // Assign the various reserved slots:
+    ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_KIND, Int32Value(kind()));
+    ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_SIZE, Int32Value(size()));
+    ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_ALIGNMENT, Int32Value(alignment()));
+
+    switch (kind()) {
+      case Array:
+        ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_LENGTH,
+                                      Int32Value(asArray()->length()));
+        break;
+
+      case Scalar:
+        ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_TYPE,
+                                      Int32Value(asScalar()->type()));
+        break;
+
+      case Struct:
+        break;
+    }
+
     ownerObject_.init(ownerObject);
     return &*ownerObject;
 }
 
 /*static*/
 JSObject *
 ScalarTypeRepresentation::Create(JSContext *cx,
                                  ScalarTypeRepresentation::Type type)
--- a/js/src/builtin/TypeRepresentation.h
+++ b/js/src/builtin/TypeRepresentation.h
@@ -46,16 +46,17 @@
   it describes.
 
  */
 
 #include "jsalloc.h"
 #include "jscntxt.h"
 #include "jspubtd.h"
 
+#include "builtin/TypedObjectConstants.h"
 #include "gc/Barrier.h"
 #include "js/HashTable.h"
 
 namespace js {
 
 class TypeRepresentation;
 class ScalarTypeRepresentation;
 class ArrayTypeRepresentation;
@@ -85,36 +86,36 @@ struct TypeRepresentationHasher
 
 typedef js::HashSet<TypeRepresentation *,
                     TypeRepresentationHasher,
                     RuntimeAllocPolicy> TypeRepresentationHash;
 
 class TypeRepresentation {
   public:
     enum Kind {
-        Scalar,
-        Struct,
-        Array
+        Scalar = JS_TYPEREPR_SCALAR_KIND,
+        Struct = JS_TYPEREPR_STRUCT_KIND,
+        Array = JS_TYPEREPR_ARRAY_KIND
     };
 
   protected:
     TypeRepresentation(Kind kind, size_t size, size_t align);
 
     size_t size_;
     size_t alignment_;
     Kind kind_;
 
     JSObject *addToTableOrFree(JSContext *cx, TypeRepresentationHash::AddPtr &p);
 
   private:
     static const Class class_;
     static void obj_trace(JSTracer *trace, JSObject *object);
     static void obj_finalize(js::FreeOp *fop, JSObject *object);
 
-    js::HeapPtrObject ownerObject_;
+    HeapPtrObject ownerObject_;
     void traceFields(JSTracer *tracer);
 
   public:
     size_t size() const { return size_; }
     size_t alignment() const { return alignment_; }
     Kind kind() const { return kind_; }
     JSObject *ownerObject() const { return ownerObject_.get(); }
 
@@ -154,30 +155,30 @@ class TypeRepresentation {
 
     void mark(JSTracer *tracer);
 };
 
 class ScalarTypeRepresentation : public TypeRepresentation {
   public:
     // Must match order of JS_FOR_EACH_SCALAR_TYPE_REPR below
     enum Type {
-        TYPE_INT8 = 0,
-        TYPE_UINT8,
-        TYPE_INT16,
-        TYPE_UINT16,
-        TYPE_INT32,
-        TYPE_UINT32,
-        TYPE_FLOAT32,
-        TYPE_FLOAT64,
+        TYPE_INT8 = JS_SCALARTYPEREPR_INT8,
+        TYPE_UINT8 = JS_SCALARTYPEREPR_UINT8,
+        TYPE_INT16 = JS_SCALARTYPEREPR_INT16,
+        TYPE_UINT16 = JS_SCALARTYPEREPR_UINT16,
+        TYPE_INT32 = JS_SCALARTYPEREPR_INT32,
+        TYPE_UINT32 = JS_SCALARTYPEREPR_UINT32,
+        TYPE_FLOAT32 = JS_SCALARTYPEREPR_FLOAT32,
+        TYPE_FLOAT64 = JS_SCALARTYPEREPR_FLOAT64,
 
         /*
          * Special type that's a uint8_t, but assignments are clamped to 0 .. 255.
          * Treat the raw data type as a uint8_t.
          */
-        TYPE_UINT8_CLAMPED,
+        TYPE_UINT8_CLAMPED = JS_SCALARTYPEREPR_UINT8_CLAMPED,
     };
     static const int32_t TYPE_MAX = TYPE_UINT8_CLAMPED + 1;
 
   private:
     // so TypeRepresentation can call appendStringScalar() etc
     friend class TypeRepresentation;
 
     Type type_;
@@ -193,26 +194,32 @@ class ScalarTypeRepresentation : public 
     }
 
     bool convertValue(JSContext *cx, HandleValue value, MutableHandleValue vp);
 
     static const char *typeName(Type type);
     static JSObject *Create(JSContext *cx, Type type);
 };
 
-// Must be in same order as the enum:
+// Enumerates the cases of ScalarTypeRepresentation::Type which have
+// unique C representation. In particular, omits Uint8Clamped since it
+// is just a Uint8.
+#define JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(macro_)                     \
+    macro_(ScalarTypeRepresentation::TYPE_INT8,    int8_t,   int8)            \
+    macro_(ScalarTypeRepresentation::TYPE_UINT8,   uint8_t,  uint8)           \
+    macro_(ScalarTypeRepresentation::TYPE_INT16,   int16_t,  int16)           \
+    macro_(ScalarTypeRepresentation::TYPE_UINT16,  uint16_t, uint16)          \
+    macro_(ScalarTypeRepresentation::TYPE_INT32,   int32_t,  int32)           \
+    macro_(ScalarTypeRepresentation::TYPE_UINT32,  uint32_t, uint32)          \
+    macro_(ScalarTypeRepresentation::TYPE_FLOAT32, float,    float32)         \
+    macro_(ScalarTypeRepresentation::TYPE_FLOAT64, double,   float64)
+
+// Must be in same order as the enum ScalarTypeRepresentation::Type:
 #define JS_FOR_EACH_SCALAR_TYPE_REPR(macro_)                                    \
-    macro_(ScalarTypeRepresentation::TYPE_INT8,    int8_t,   int8)              \
-    macro_(ScalarTypeRepresentation::TYPE_UINT8,   uint8_t,  uint8)             \
-    macro_(ScalarTypeRepresentation::TYPE_INT16,   int16_t,  int16)             \
-    macro_(ScalarTypeRepresentation::TYPE_UINT16,  uint16_t, uint16)            \
-    macro_(ScalarTypeRepresentation::TYPE_INT32,   int32_t,  int32)             \
-    macro_(ScalarTypeRepresentation::TYPE_UINT32,  uint32_t, uint32)            \
-    macro_(ScalarTypeRepresentation::TYPE_FLOAT32, float,    float32)           \
-    macro_(ScalarTypeRepresentation::TYPE_FLOAT64, double,   float64)           \
+    JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(macro_)                           \
     macro_(ScalarTypeRepresentation::TYPE_UINT8_CLAMPED, uint8_t, uint8Clamped)
 
 class ArrayTypeRepresentation : public TypeRepresentation {
   private:
     // so TypeRepresentation can call appendStringArray() etc
     friend class TypeRepresentation;
 
     TypeRepresentation *element_;
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -52,24 +52,16 @@ const Class js::TypedObjectClass = {
  * 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);
 
-/*
- * ConvertAndCopyTo() converts `from` to type `type` and stores the result in
- * `mem`, which MUST be pre-allocated to the appropriate size for instances of
- * `type`.
- */
-static bool ConvertAndCopyTo(JSContext *cx, TypeRepresentation *typeRepr,
-                             HandleValue from, uint8_t *mem);
-
 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
@@ -119,173 +111,195 @@ ToObjectIfObject(HandleValue value)
 {
     if (!value.isObject())
         return nullptr;
 
     return &value.toObject();
 }
 
 static inline bool
-IsNumericType(HandleObject type)
+IsNumericTypeObject(JSObject &type)
 {
-    return type &&
-           &NumericTypeClasses[0] <= type->getClass() &&
-           type->getClass() < &NumericTypeClasses[ScalarTypeRepresentation::TYPE_MAX];
+    return &NumericTypeClasses[0] <= type.getClass() &&
+           type.getClass() < &NumericTypeClasses[ScalarTypeRepresentation::TYPE_MAX];
 }
 
 static inline bool
-IsArrayType(HandleObject type)
+IsArrayTypeObject(JSObject &type)
 {
-    return type && type->hasClass(&ArrayType::class_);
+    return type.hasClass(&ArrayType::class_);
 }
 
 static inline bool
-IsStructType(HandleObject type)
+IsStructTypeObject(JSObject &type)
 {
-    return type && type->hasClass(&StructType::class_);
+    return type.hasClass(&StructType::class_);
 }
 
 static inline bool
-IsComplexType(HandleObject type)
+IsComplexTypeObject(JSObject &type)
 {
-    return IsArrayType(type) || IsStructType(type);
+    return IsArrayTypeObject(type) || IsStructTypeObject(type);
 }
 
 static inline bool
-IsBinaryType(HandleObject type)
+IsTypeObject(JSObject &type)
 {
-    return IsNumericType(type) || IsComplexType(type);
+    return IsNumericTypeObject(type) || IsComplexTypeObject(type);
 }
 
 static inline bool
-IsBlock(HandleObject obj)
+IsBlock(JSObject &obj)
 {
-    return obj && obj->hasClass(&BinaryBlock::class_);
+    return obj.hasClass(&BinaryBlock::class_);
 }
 
 static inline uint8_t *
-BlockMem(HandleObject val)
+BlockMem(JSObject &val)
 {
     JS_ASSERT(IsBlock(val));
-    return (uint8_t*) val->getPrivate();
+    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 *
-typeRepresentationOwnerObj(HandleObject typeObj)
+typeRepresentationOwnerObj(JSObject &typeObj)
 {
-    JS_ASSERT(IsBinaryType(typeObj));
-    return &typeObj->getFixedSlot(SLOT_TYPE_REPR).toObject();
+    JS_ASSERT(IsTypeObject(typeObj));
+    return &typeObj.getReservedSlot(JS_TYPEOBJ_SLOT_TYPE_REPR).toObject();
 }
 
 /*
  * 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(HandleObject typeObj)
+typeRepresentation(JSObject &typeObj)
 {
     return TypeRepresentation::fromOwnerObject(typeRepresentationOwnerObj(typeObj));
 }
 
 static inline JSObject *
-GetType(HandleObject block)
+GetType(JSObject &block)
 {
     JS_ASSERT(IsBlock(block));
-    return &block->getFixedSlot(SLOT_DATATYPE).toObject();
+    return &block.getReservedSlot(JS_TYPEDOBJ_SLOT_TYPE_OBJ).toObject();
 }
 
 /*
- * Overwrites the contents of `block` with the converted form of `val`
+ * Overwrites the contents of `block` at offset `offset` with `val`
+ * converted to the type `typeObj`
  */
 static bool
-ConvertAndCopyTo(JSContext *cx, HandleValue val, HandleObject block)
+ConvertAndCopyTo(JSContext *cx,
+                 HandleObject typeObj,
+                 HandleObject block,
+                 int32_t offset,
+                 HandleValue val)
 {
-    uint8_t *memory = BlockMem(block);
-    RootedObject type(cx, GetType(block));
-    TypeRepresentation *typeRepr = typeRepresentation(type);
-    return ConvertAndCopyTo(cx, typeRepr, val, memory);
+    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[3].setInt32(offset);
+    args[4].set(val);
+
+    return Invoke(cx, args);
+}
+
+static bool
+ConvertAndCopyTo(JSContext *cx, HandleObject block, HandleValue val)
+{
+    RootedObject type(cx, GetType(*block));
+    return ConvertAndCopyTo(cx, type, block, 0, val);
 }
 
 static inline size_t
-TypeSize(HandleObject type)
+TypeSize(JSObject &type)
 {
     return typeRepresentation(type)->size();
 }
 
 static inline size_t
-BlockSize(JSContext *cx, HandleObject val)
+BlockSize(JSObject &val)
 {
-    RootedObject type(cx, GetType(val));
-    return TypeSize(type);
+    return TypeSize(*GetType(val));
 }
 
 static inline bool
-IsBlockOfKind(JSContext *cx, HandleObject obj, TypeRepresentation::Kind kind)
+IsBlockOfKind(JSObject &obj, TypeRepresentation::Kind kind)
 {
     if (!IsBlock(obj))
         return false;
-    RootedObject objType(cx, GetType(obj));
-    TypeRepresentation *repr = typeRepresentation(objType);
+    TypeRepresentation *repr = typeRepresentation(*GetType(obj));
     return repr->kind() == kind;
 }
 
 static inline bool
-IsBinaryArray(JSContext *cx, HandleObject obj)
+IsBinaryArray(JSObject &obj)
 {
-    return IsBlockOfKind(cx, obj, TypeRepresentation::Array);
+    return IsBlockOfKind(obj, TypeRepresentation::Array);
 }
 
 static inline bool
-IsBinaryStruct(JSContext *cx, HandleObject obj)
+IsBinaryStruct(JSObject &obj)
 {
-    return IsBlockOfKind(cx, obj, TypeRepresentation::Struct);
+    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 || !IsBinaryType(thisObj)) {
+    if (!thisObj || !IsTypeObject(*thisObj)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                              JSMSG_INCOMPATIBLE_PROTO,
                              "Type", "equivalent",
                              InformalValueTypeName(args.thisv()));
         return false;
     }
 
     if (args.length() < 1) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
                              "Type.equivalent", "1", "s");
         return false;
     }
 
     RootedObject otherObj(cx, ToObjectIfObject(args[0]));
-    if (!otherObj || !IsBinaryType(otherObj)) {
+    if (!otherObj || !IsTypeObject(*otherObj)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPEDOBJECT_NOT_TYPE_OBJECT);
         return false;
     }
 
-    TypeRepresentation *thisRepr = typeRepresentation(thisObj);
-    TypeRepresentation *otherRepr = typeRepresentation(otherObj);
+    TypeRepresentation *thisRepr = typeRepresentation(*thisObj);
+    TypeRepresentation *otherRepr = typeRepresentation(*otherObj);
     args.rval().setBoolean(thisRepr == otherRepr);
     return true;
 }
 
 #define BINARYDATA_NUMERIC_CLASSES(constant_, type_, name_)                   \
 {                                                                             \
     #name_,                                                                   \
-    JSCLASS_HAS_RESERVED_SLOTS(TYPE_RESERVED_SLOTS),                          \
+    JSCLASS_HAS_RESERVED_SLOTS(JS_TYPEOBJ_SCALAR_SLOTS),                      \
     JS_PropertyStub,       /* addProperty */                                  \
     JS_DeletePropertyStub, /* delProperty */                                  \
     JS_PropertyStub,       /* getProperty */                                  \
     JS_StrictPropertyStub, /* setProperty */                                  \
     JS_EnumerateStub,                                                         \
     JS_ResolveStub,                                                           \
     JS_ConvertStub,                                                           \
     nullptr,                                                                  \
@@ -303,120 +317,74 @@ const Class js::NumericTypeClasses[Scala
 #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;
 }
 
-template <ScalarTypeRepresentation::Type type, typename T>
-bool
-NumericType<type, T>::convert(JSContext *cx, HandleValue val, T* converted)
-{
-    if (val.isInt32()) {
-        *converted = T(val.toInt32());
-        return true;
-    }
-
-    double d;
-    if (!ToDoubleForTypedArray(cx, val, &d))
-        return false;
-
-    if (TypeIsFloatingPoint<T>()) {
-        *converted = T(d);
-    } else if (TypeIsUnsigned<T>()) {
-        uint32_t n = ToUint32(d);
-        *converted = T(n);
-    } else {
-        int32_t n = ToInt32(d);
-        *converted = T(n);
-    }
-
-    return true;
-}
-
-template <>
-bool
-NumericType<ScalarTypeRepresentation::TYPE_UINT8_CLAMPED, uint8_t>::convert(
-    JSContext *cx, HandleValue val, uint8_t* converted)
-{
-    double d;
-    if (val.isInt32()) {
-        d = val.toInt32();
-    } else {
-        if (!ToDoubleForTypedArray(cx, val, &d))
-            return false;
-    }
-    *converted = ClampDoubleToUint8(d);
-    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
-ConvertAndCopyScalarTo(JSContext *cx, ScalarTypeRepresentation *typeRepr,
-                       HandleValue from, uint8_t *mem)
-{
-#define CONVERT_CASES(constant_, type_, name_)                                \
-    case constant_: {                                                         \
-        type_ temp;                                                           \
-        if (!NumericType<constant_, type_>::convert(cx, from, &temp))         \
-            return false;                                                     \
-        memcpy(mem, &temp, sizeof(type_));                                    \
-        return true; }
-
-    switch (typeRepr->type()) {
-        JS_FOR_EACH_SCALAR_TYPE_REPR(CONVERT_CASES)
-    }
-#undef CONVERT_CASES
-    return false;
-}
-
-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);
+                cx, BlockMem(*owner) + offset, to);
         JS_FOR_EACH_SCALAR_TYPE_REPR(REIFY_CASES)
     }
 #undef REIFY_CASES
 
     MOZ_ASSUME_UNREACHABLE("Invalid type");
     return false;
 }
 
@@ -426,27 +394,25 @@ NumericType<type, T>::call(JSContext *cx
 {
     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");
         return false;
     }
 
-    RootedValue arg(cx, args[0]);
-    T answer;
-    if (!convert(cx, arg, &answer))
-        return false; // convert() raises TypeError.
+    double number;
+    if (!ToNumber(cx, args[0], &number))
+        return false;
 
-    RootedValue reified(cx);
-    if (!NumericType<type, T>::reify(cx, &answer, &reified)) {
-        return false;
-    }
+    if (type == ScalarTypeRepresentation::TYPE_UINT8_CLAMPED)
+        number = ClampDoubleToUint8(number);
 
-    args.rval().set(reified);
+    T converted = ConvertScalar<T>(number);
+    args.rval().setNumber((double) converted);
     return true;
 }
 
 /*
  * For code like:
  *
  *   var A = new TypedObject.ArrayType(uint8, 10);
  *   var S = new TypedObject.StructType({...});
@@ -564,17 +530,17 @@ CreateMetaTypeObject(JSContext *cx,
         return nullptr;
     }
 
     return ctor;
 }
 
 const Class ArrayType::class_ = {
     "ArrayType",
-    JSCLASS_HAS_RESERVED_SLOTS(ARRAY_TYPE_RESERVED_SLOTS),
+    JSCLASS_HAS_RESERVED_SLOTS(JS_TYPEOBJ_ARRAY_SLOTS),
     JS_PropertyStub,
     JS_DeletePropertyStub,
     JS_PropertyStub,
     JS_StrictPropertyStub,
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     nullptr,
@@ -603,151 +569,89 @@ const JSFunctionSpec ArrayType::typedObj
     JS_FN("subarray", ArrayType::subarray, 2, 0),
     {"forEach", {nullptr, nullptr}, 1, 0, "ArrayForEach"},
     JS_FS_END
 };
 
 static JSObject *
 ArrayElementType(HandleObject array)
 {
-    JS_ASSERT(IsArrayType(array));
-    return &array->getFixedSlot(SLOT_ARRAY_ELEM_TYPE).toObject();
+    JS_ASSERT(IsArrayTypeObject(*array));
+    return &array->getReservedSlot(JS_TYPEOBJ_SLOT_ARRAY_ELEM_TYPE).toObject();
 }
 
 static bool
-ConvertAndCopyArrayTo(JSContext *cx, ArrayTypeRepresentation *typeRepr,
-                      HandleValue from, uint8_t *mem)
+FillTypedArrayWithValue(JSContext *cx, HandleObject array, HandleValue val)
 {
-    if (!from.isObject()) {
-        ReportCannotConvertTo(cx, from, typeRepr);
-        return false;
-    }
+    JS_ASSERT(IsBinaryArray(*array));
 
-    // Check for the fast case, where we can just memcpy:
-    RootedObject fromObject(cx, &from.toObject());
-    if (IsBlock(fromObject)) {
-        RootedObject fromType(cx, GetType(fromObject));
-        TypeRepresentation *fromTypeRepr = typeRepresentation(fromType);
-        if (typeRepr == fromTypeRepr) {
-            memcpy(mem, BlockMem(fromObject), typeRepr->size());
-            return true;
-        }
-    }
-
-    // Otherwise, use a structural comparison:
-    RootedValue fromLenVal(cx);
-    if (!JSObject::getProperty(cx, fromObject, fromObject,
-                               cx->names().length, &fromLenVal))
+    RootedFunction func(
+        cx, SelfHostedFunction(cx, cx->names().FillTypedArrayWithValue));
+    if (!func)
         return false;
 
-    if (!fromLenVal.isInt32()) {
-        ReportCannotConvertTo(cx, from, typeRepr);
-        return false;
-    }
-
-    int32_t fromLenInt32 = fromLenVal.toInt32();
-    if (fromLenInt32 < 0) {
-        ReportCannotConvertTo(cx, from, typeRepr);
-        return false;
-    }
-
-    size_t fromLen = (size_t) fromLenInt32;
-    if (typeRepr->length() != fromLen) {
-        ReportCannotConvertTo(cx, from, typeRepr);
-        return false;
-    }
-
-    TypeRepresentation *elementType = typeRepr->element();
-    uint8_t *p = mem;
-
-    RootedValue fromElem(cx);
-    for (size_t i = 0; i < fromLen; i++) {
-        if (!JSObject::getElement(cx, fromObject, fromObject, i, &fromElem))
-            return false;
-
-        if (!ConvertAndCopyTo(cx, elementType, fromElem, p))
-            return false;
-
-        p += elementType->size();
-    }
-
-    return true;
-}
-
-static bool
-FillBinaryArrayWithValue(JSContext *cx, HandleObject array, HandleValue val)
-{
-    JS_ASSERT(IsBinaryArray(cx, array));
-
-    // set array[0] = [[Convert]](val)
-    RootedObject type(cx, GetType(array));
-    ArrayTypeRepresentation *typeRepr = typeRepresentation(type)->asArray();
-    uint8_t *base = BlockMem(array);
-    if (!ConvertAndCopyTo(cx, typeRepr->element(), val, base))
+    InvokeArgs args(cx);
+    if (!args.init(2))
         return false;
 
-    // copy a[0] into remaining indices.
-    size_t elementSize = typeRepr->element()->size();
-    for (size_t i = 1; i < typeRepr->length(); i++) {
-        uint8_t *dest = base + elementSize * i;
-        memcpy(dest, base, elementSize);
-    }
-
-    return true;
+    args.setCallee(ObjectValue(*func));
+    args[0].setObject(*array);
+    args[1].set(val);
+    return Invoke(cx, args);
 }
 
 bool
 ArrayType::repeat(JSContext *cx, unsigned int argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() < 1) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage,
                              nullptr, JSMSG_MORE_ARGS_NEEDED,
                              "repeat()", "0", "s");
         return false;
     }
 
     RootedObject thisObj(cx, ToObjectIfObject(args.thisv()));
-    if (!thisObj || !IsArrayType(thisObj)) {
+    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));
     if (!binaryArray)
         return false;
 
     RootedValue val(cx, args[0]);
-    if (!FillBinaryArrayWithValue(cx, binaryArray, val))
+    if (!FillTypedArrayWithValue(cx, binaryArray, val))
         return false;
 
     args.rval().setObject(*binaryArray);
     return true;
 }
 
 bool
 ArrayType::toSource(JSContext *cx, unsigned int argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedObject thisObj(cx, ToObjectIfObject(args.thisv()));
-    if (!thisObj || !IsArrayType(thisObj)) {
+    if (!thisObj || !IsArrayTypeObject(*thisObj)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage,
                              nullptr, JSMSG_INCOMPATIBLE_PROTO,
                              "ArrayType", "toSource",
                              InformalValueTypeName(args.thisv()));
         return false;
     }
 
     StringBuffer contents(cx);
-    if (!typeRepresentation(thisObj)->appendString(cx, contents))
+    if (!typeRepresentation(*thisObj)->appendString(cx, contents))
         return false;
 
     RootedString result(cx, contents.finishString());
     if (!result)
         return false;
 
     args.rval().setString(result);
     return true;
@@ -787,23 +691,23 @@ 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(cx, thisObj)) {
+    if (!IsBinaryArray(*thisObj)) {
         ReportCannotConvertTo(cx, thisObj, "binary array");
         return false;
     }
 
-    RootedObject type(cx, GetType(thisObj));
-    ArrayTypeRepresentation *typeRepr = typeRepresentation(type)->asArray();
+    RootedObject type(cx, GetType(*thisObj));
+    ArrayTypeRepresentation *typeRepr = typeRepresentation(*type)->asArray();
     size_t length = typeRepr->length();
 
     int32_t begin = args[0].toInt32();
     int32_t end = length;
 
     if (args.length() >= 2) {
         if (!args[1].isInt32()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
@@ -855,40 +759,40 @@ 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(cx, thisObj)) {
+    if (!thisObj || !IsBinaryArray(*thisObj)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage,
                              nullptr, JSMSG_INCOMPATIBLE_PROTO,
                              "ArrayType", "fill",
                              InformalValueTypeName(args.thisv()));
         return false;
     }
 
     Value funArrayTypeVal = GetFunctionNativeReserved(&args.callee(), 0);
     JS_ASSERT(funArrayTypeVal.isObject());
 
-    RootedObject type(cx, GetType(thisObj));
-    TypeRepresentation *typeRepr = typeRepresentation(type);
+    RootedObject type(cx, GetType(*thisObj));
+    TypeRepresentation *typeRepr = typeRepresentation(*type);
     RootedObject funArrayType(cx, &funArrayTypeVal.toObject());
-    TypeRepresentation *funArrayTypeRepr = typeRepresentation(funArrayType);
+    TypeRepresentation *funArrayTypeRepr = typeRepresentation(*funArrayType);
     if (typeRepr != funArrayTypeRepr) {
         RootedValue thisObjValue(cx, ObjectValue(*thisObj));
         ReportCannotConvertTo(cx, thisObjValue, funArrayTypeRepr);
         return false;
     }
 
     args.rval().setUndefined();
     RootedValue val(cx, args[0]);
-    return FillBinaryArrayWithValue(cx, thisObj, val);
+    return FillTypedArrayWithValue(cx, thisObj, val);
 }
 
 static bool
 InitializeCommonTypeDescriptorProperties(JSContext *cx,
                                          HandleObject obj,
                                          HandleObject typeReprOwnerObj)
 {
     TypeRepresentation *typeRepr =
@@ -936,19 +840,19 @@ InitializeCommonTypeDescriptorProperties
 
 JSObject *
 ArrayType::create(JSContext *cx,
                   HandleObject metaTypeObject,
                   HandleObject elementType,
                   size_t length)
 {
     JS_ASSERT(elementType);
-    JS_ASSERT(IsBinaryType(elementType));
+    JS_ASSERT(IsTypeObject(*elementType));
 
-    TypeRepresentation *elementTypeRepr = typeRepresentation(elementType);
+    TypeRepresentation *elementTypeRepr = typeRepresentation(*elementType);
     RootedObject typeReprObj(
         cx, ArrayTypeRepresentation::Create(cx, elementTypeRepr, length));
     if (!typeReprObj)
         return nullptr;
 
     RootedValue prototypeVal(cx);
     if (!JSObject::getProperty(cx, metaTypeObject, metaTypeObject,
                                cx->names().prototype,
@@ -958,25 +862,27 @@ ArrayType::create(JSContext *cx,
     }
     JS_ASSERT(prototypeVal.isObject()); // immutable binding
 
     RootedObject obj(
         cx, NewObjectWithClassProto(cx, &ArrayType::class_,
                                     &prototypeVal.toObject(), cx->global()));
     if (!obj)
         return nullptr;
-    obj->initFixedSlot(SLOT_TYPE_REPR, ObjectValue(*typeReprObj));
+    obj->initReservedSlot(JS_TYPEOBJ_SLOT_TYPE_REPR,
+                          ObjectValue(*typeReprObj));
 
     RootedValue elementTypeVal(cx, ObjectValue(*elementType));
     if (!JSObject::defineProperty(cx, obj, cx->names().elementType,
                                   elementTypeVal, nullptr, nullptr,
                                   JSPROP_READONLY | JSPROP_PERMANENT))
         return nullptr;
 
-    obj->initFixedSlot(SLOT_ARRAY_ELEM_TYPE, elementTypeVal);
+    obj->initReservedSlot(JS_TYPEOBJ_SLOT_ARRAY_ELEM_TYPE,
+                          elementTypeVal);
 
     RootedValue lengthVal(cx, Int32Value(length));
     if (!JSObject::defineProperty(cx, obj, cx->names().length,
                                   lengthVal, nullptr, nullptr,
                                   JSPROP_READONLY | JSPROP_PERMANENT))
         return nullptr;
 
     if (!InitializeCommonTypeDescriptorProperties(cx, obj, typeReprObj))
@@ -1021,17 +927,17 @@ ArrayType::construct(JSContext *cx, unsi
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                              JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
         return false;
     }
 
     RootedObject arrayTypeGlobal(cx, &args.callee());
     RootedObject elementType(cx, &args[0].toObject());
 
-    if (!IsBinaryType(elementType)) {
+    if (!IsTypeObject(*elementType)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                              JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
         return false;
     }
 
     if (!args[1].isInt32()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                              JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
@@ -1053,17 +959,17 @@ ArrayType::construct(JSContext *cx, unsi
 }
 
 /*********************************
  * Structs
  *********************************/
 
 const Class StructType::class_ = {
     "StructType",
-    JSCLASS_HAS_RESERVED_SLOTS(STRUCT_TYPE_RESERVED_SLOTS) |
+    JSCLASS_HAS_RESERVED_SLOTS(JS_TYPEOBJ_STRUCT_SLOTS) |
     JSCLASS_HAS_PRIVATE, // used to store FieldList
     JS_PropertyStub,
     JS_DeletePropertyStub,
     JS_PropertyStub,
     JS_StrictPropertyStub,
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
@@ -1087,55 +993,16 @@ const JSFunctionSpec StructType::typeObj
 const JSPropertySpec StructType::typedObjectProperties[] = {
     JS_PS_END
 };
 
 const JSFunctionSpec StructType::typedObjectMethods[] = {
     JS_FS_END
 };
 
-static bool
-ConvertAndCopyStructTo(JSContext *cx,
-                       StructTypeRepresentation *typeRepr,
-                       HandleValue from,
-                       uint8_t *mem)
-{
-    if (!from.isObject()) {
-        ReportCannotConvertTo(cx, from, typeRepr);
-        return false;
-    }
-
-    // Check for the fast case, where we can just memcpy:
-    RootedObject fromObject(cx, &from.toObject());
-    if (IsBlock(fromObject)) {
-        RootedObject fromType(cx, GetType(fromObject));
-        TypeRepresentation *fromTypeRepr = typeRepresentation(fromType);
-        if (typeRepr == fromTypeRepr) {
-            memcpy(mem, BlockMem(fromObject), typeRepr->size());
-            return true;
-        }
-    }
-
-    // Otherwise, convert the properties one by one.
-    RootedId fieldId(cx);
-    RootedValue fromProp(cx);
-    for (size_t i = 0; i < typeRepr->fieldCount(); i++) {
-        const StructField &field = typeRepr->field(i);
-
-        fieldId = field.id;
-        if (!JSObject::getGeneric(cx, fromObject, fromObject, fieldId, &fromProp))
-            return false;
-
-        if (!ConvertAndCopyTo(cx, field.typeRepr, fromProp,
-                              mem + field.offset))
-            return false;
-    }
-    return true;
-}
-
 /*
  * NOTE: layout() does not check for duplicates in fields since the arguments
  * to StructType are currently passed as an object literal. Fix this if it
  * changes to taking an array of arrays.
  */
 bool
 StructType::layout(JSContext *cx, HandleObject structType, HandleObject fields)
 {
@@ -1157,50 +1024,51 @@ StructType::layout(JSContext *cx, Handle
         uint32_t index;
         if (js_IdIsIndex(id, &index)) {
             RootedValue idValue(cx, IdToJsval(id));
             ReportCannotConvertTo(cx, idValue, "StructType field name");
             return false;
         }
 
         RootedObject fieldType(cx, ToObjectIfObject(fieldTypeVal));
-        if (!fieldType || !IsBinaryType(fieldType)) {
+        if (!fieldType || !IsTypeObject(*fieldType)) {
             ReportCannotConvertTo(cx, fieldTypeVal, "StructType field specifier");
             return false;
         }
 
         if (!fieldTypeObjs.append(fieldTypeVal)) {
             js_ReportOutOfMemory(cx);
             return false;
         }
 
-        if (!fieldTypeReprObjs.append(typeRepresentationOwnerObj(fieldType))) {
+        if (!fieldTypeReprObjs.append(typeRepresentationOwnerObj(*fieldType))) {
             js_ReportOutOfMemory(cx);
             return false;
         }
     }
 
     // Construct the `TypeRepresentation*`.
     RootedObject typeReprObj(
         cx, StructTypeRepresentation::Create(cx, ids, fieldTypeReprObjs));
     if (!typeReprObj)
         return false;
     StructTypeRepresentation *typeRepr =
         TypeRepresentation::fromOwnerObject(typeReprObj)->asStruct();
-    structType->initReservedSlot(SLOT_TYPE_REPR, ObjectValue(*typeReprObj));
+    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)
         return false;
 
-    structType->initFixedSlot(SLOT_STRUCT_FIELD_TYPES,
-                             ObjectValue(*fieldTypeVec));
+    structType->initReservedSlot(JS_TYPEOBJ_SLOT_STRUCT_FIELD_TYPES,
+                                 ObjectValue(*fieldTypeVec));
 
     // Construct the fieldNames vector
     AutoValueVector fieldNameValues(cx);
     for (unsigned int i = 0; i < ids.length(); i++) {
         RootedValue value(cx, IdToValue(ids[i]));
         if (!fieldNameValues.append(value))
             return false;
     }
@@ -1277,17 +1145,17 @@ StructType::create(JSContext *cx, Handle
 
     if (!StructType::layout(cx, obj, fields))
         return nullptr;
 
     RootedObject fieldsProto(cx);
     if (!JSObject::getProto(cx, fields, &fieldsProto))
         return nullptr;
 
-    RootedObject typeReprObj(cx, typeRepresentationOwnerObj(obj));
+    RootedObject typeReprObj(cx, typeRepresentationOwnerObj(*obj));
     if (!InitializeCommonTypeDescriptorProperties(cx, obj, typeReprObj))
         return nullptr;
 
     RootedObject prototypeObj(
         cx, CreateComplexTypeInstancePrototype(cx, metaTypeObject));
     if (!prototypeObj)
         return nullptr;
 
@@ -1324,57 +1192,35 @@ StructType::construct(JSContext *cx, uns
 }
 
 bool
 StructType::toSource(JSContext *cx, unsigned int argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedObject thisObj(cx, ToObjectIfObject(args.thisv()));
-    if (!thisObj || !IsStructType(thisObj)) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
-                             JSMSG_INCOMPATIBLE_PROTO,
+    if (!thisObj || !IsStructTypeObject(*thisObj)) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
                              "StructType", "toSource",
                              InformalValueTypeName(args.thisv()));
         return false;
     }
 
     StringBuffer contents(cx);
-    if (!typeRepresentation(thisObj)->appendString(cx, contents))
+    if (!typeRepresentation(*thisObj)->appendString(cx, contents))
         return false;
 
     RootedString result(cx, contents.finishString());
     if (!result)
         return false;
 
     args.rval().setString(result);
     return true;
 }
 
-static bool
-ConvertAndCopyTo(JSContext *cx,
-                 TypeRepresentation *typeRepr,
-                 HandleValue from,
-                 uint8_t *mem)
-{
-    switch (typeRepr->kind()) {
-      case TypeRepresentation::Scalar:
-        return ConvertAndCopyScalarTo(cx, typeRepr->asScalar(), from, mem);
-
-      case TypeRepresentation::Array:
-        return ConvertAndCopyArrayTo(cx, typeRepr->asArray(), from, mem);
-
-      case TypeRepresentation::Struct:
-        return ConvertAndCopyStructTo(cx, typeRepr->asStruct(), from, mem);
-    }
-
-    MOZ_ASSUME_UNREACHABLE("Invalid kind");
-    return false;
-}
-
 ///////////////////////////////////////////////////////////////////////////
 
 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)
@@ -1382,17 +1228,17 @@ ReifyComplex(JSContext *cx, HandleObject
     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));
+    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");
@@ -1553,17 +1399,18 @@ DefineNumericClass(JSContext *cx,
                                                     funcProto, global));
     if (!numFun)
         return false;
 
     RootedObject typeReprObj(cx, ScalarTypeRepresentation::Create(cx, type));
     if (!typeReprObj)
         return false;
 
-    numFun->initFixedSlot(SLOT_TYPE_REPR, ObjectValue(*typeReprObj));
+    numFun->initReservedSlot(JS_TYPEOBJ_SLOT_TYPE_REPR,
+                             ObjectValue(*typeReprObj));
 
     if (!InitializeCommonTypeDescriptorProperties(cx, numFun, typeReprObj))
         return false;
 
     if (!JS_DefineFunction(cx, numFun, "toString",
                            NumericTypeToString<type>, 0, 0))
         return false;
 
@@ -1582,17 +1429,17 @@ DefineNumericClass(JSContext *cx,
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // Binary blocks
 
 const Class BinaryBlock::class_ = {
     "BinaryBlock",
     Class::NON_NATIVE |
-    JSCLASS_HAS_RESERVED_SLOTS(BLOCK_RESERVED_SLOTS) |
+    JSCLASS_HAS_RESERVED_SLOTS(JS_TYPEDOBJ_SLOTS) |
     JSCLASS_HAS_PRIVATE |
     JSCLASS_IMPLEMENTS_BARRIERS,
     JS_PropertyStub,
     JS_DeletePropertyStub,
     JS_PropertyStub,
     JS_StrictPropertyStub,
     JS_EnumerateStub,
     JS_ResolveStub,
@@ -1632,20 +1479,20 @@ const Class BinaryBlock::class_ = {
     }
 };
 
 static bool
 ReportBlockTypeError(JSContext *cx,
                      const unsigned errorNumber,
                      HandleObject obj)
 {
-    JS_ASSERT(IsBlock(obj));
+    JS_ASSERT(IsBlock(*obj));
 
-    RootedObject type(cx, GetType(obj));
-    TypeRepresentation *typeRepr = typeRepresentation(type);
+    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;
@@ -1661,124 +1508,124 @@ ReportBlockTypeError(JSContext *cx,
     return false;
 }
 
 /* static */ void
 BinaryBlock::obj_trace(JSTracer *trace, JSObject *object)
 {
     JS_ASSERT(object->hasClass(&class_));
 
-    for (size_t i = 0; i < BLOCK_RESERVED_SLOTS; i++)
+    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->getFixedSlot(SLOT_BLOCKREFOWNER).isNull())
+    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)
 {
-    JS_ASSERT(IsBinaryType(type));
+    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->initFixedSlot(SLOT_DATATYPE, ObjectValue(*type));
-    obj->initFixedSlot(SLOT_BLOCKREFOWNER, owner);
+    obj->initReservedSlot(JS_TYPEDOBJ_SLOT_TYPE_OBJ, ObjectValue(*type));
+    obj->initReservedSlot(JS_TYPEDOBJ_SLOT_OWNER, owner);
 
     // 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);
+            TypeRepresentation *typeRepr = typeRepresentation(*type);
             if (!typeObj->addTypedObjectAddendum(cx, typeRepr))
                 return nullptr;
         }
     }
 
     return obj;
 }
 
 /* static */ JSObject *
 BinaryBlock::createZeroed(JSContext *cx, HandleObject type)
 {
     RootedValue owner(cx, NullValue());
     RootedObject obj(cx, createNull(cx, type, owner));
     if (!obj)
         return nullptr;
 
-    TypeRepresentation *typeRepr = typeRepresentation(type);
+    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(cx, owner));
-    JS_ASSERT(offset + TypeSize(type) <= BlockSize(cx, owner));
+    JS_ASSERT(IsBlock(*owner));
+    JS_ASSERT(offset <= BlockSize(*owner));
+    JS_ASSERT(offset + TypeSize(*type) <= BlockSize(*owner));
 
     RootedValue ownerValue(cx, ObjectValue(*owner));
     RootedObject obj(cx, createNull(cx, type, ownerValue));
     if (!obj)
         return nullptr;
 
-    obj->setPrivate(BlockMem(owner) + offset);
+    obj->setPrivate(BlockMem(*owner) + offset);
     return obj;
 }
 
 /* static */ bool
 BinaryBlock::construct(JSContext *cx, unsigned int argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedObject callee(cx, &args.callee());
-    JS_ASSERT(IsBinaryType(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, initial, obj))
+        if (!ConvertAndCopyTo(cx, obj, initial))
             return false;
     }
 
     args.rval().setObject(*obj);
     return true;
 }
 
 bool
 BinaryBlock::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
                                 MutableHandleObject objp, MutableHandleShape propp)
 {
-    JS_ASSERT(IsBlock(obj));
+    JS_ASSERT(IsBlock(*obj));
 
-    RootedObject type(cx, GetType(obj));
-    TypeRepresentation *typeRepr = typeRepresentation(type);
+    RootedObject type(cx, GetType(*obj));
+    TypeRepresentation *typeRepr = typeRepresentation(*type);
 
     switch (typeRepr->kind()) {
       case TypeRepresentation::Scalar:
         break;
 
       case TypeRepresentation::Array: {
         uint32_t index;
         if (js_IdIsIndex(id, &index))
@@ -1788,20 +1635,20 @@ BinaryBlock::obj_lookupGeneric(JSContext
             MarkNonNativePropertyFound(propp);
             objp.set(obj);
             return true;
         }
         break;
       }
 
       case TypeRepresentation::Struct: {
-        RootedObject type(cx, GetType(obj));
-        JS_ASSERT(IsStructType(type));
+        RootedObject type(cx, GetType(*obj));
+        JS_ASSERT(IsStructTypeObject(*type));
         StructTypeRepresentation *typeRepr =
-            typeRepresentation(type)->asStruct();
+            typeRepresentation(*type)->asStruct();
         const StructField *field = typeRepr->fieldNamed(id);
         if (field) {
             MarkNonNativePropertyFound(propp);
             objp.set(obj);
             return true;
         }
         break;
       }
@@ -1827,17 +1674,17 @@ BinaryBlock::obj_lookupProperty(JSContex
     RootedId id(cx, NameToId(name));
     return obj_lookupGeneric(cx, obj, id, objp, propp);
 }
 
 bool
 BinaryBlock::obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
                                 MutableHandleObject objp, MutableHandleShape propp)
 {
-    JS_ASSERT(IsBlock(obj));
+    JS_ASSERT(IsBlock(*obj));
     MarkNonNativePropertyFound(propp);
     objp.set(obj);
     return true;
 }
 
 static bool
 ReportPropertyError(JSContext *cx,
                     const unsigned errorNumber,
@@ -1898,31 +1745,61 @@ BinaryBlock::obj_defineElement(JSContext
 bool
 BinaryBlock::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,
+                int32_t fieldIndex)
+{
+    JS_ASSERT(IsStructTypeObject(*type));
+
+    // Recover the original type object here (`field` contains
+    // only its canonical form). The difference is observable,
+    // e.g. in a program like:
+    //
+    //     var Point1 = new StructType({x:uint8, y:uint8});
+    //     var Point2 = new StructType({x:uint8, y:uint8});
+    //     var Line1 = new StructType({start:Point1, end: Point1});
+    //     var Line2 = new StructType({start:Point2, end: Point2});
+    //     var line1 = new Line1(...);
+    //     var line2 = new Line2(...);
+    //
+    // In this scenario, line1.start.type() === Point1 and
+    // line2.start.type() === Point2.
+    RootedObject fieldTypes(
+        cx, &type->getReservedSlot(JS_TYPEOBJ_SLOT_STRUCT_FIELD_TYPES).toObject());
+    RootedValue fieldTypeVal(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)
 {
-    JS_ASSERT(IsBlock(obj));
+    JS_ASSERT(IsBlock(*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:
 
-    RootedObject type(cx, GetType(obj));
-    TypeRepresentation *typeRepr = typeRepresentation(type);
+    RootedObject type(cx, GetType(*obj));
+    TypeRepresentation *typeRepr = typeRepresentation(*type);
     switch (typeRepr->kind()) {
       case TypeRepresentation::Scalar:
         break;
 
       case TypeRepresentation::Array:
         if (JSID_IS_ATOM(id, cx->names().length)) {
             vp.setInt32(typeRepr->asArray()->length());
             return true;
@@ -1930,38 +1807,20 @@ BinaryBlock::obj_getGeneric(JSContext *c
         break;
 
       case TypeRepresentation::Struct: {
         StructTypeRepresentation *structTypeRepr = typeRepr->asStruct();
         const StructField *field = structTypeRepr->fieldNamed(id);
         if (!field)
             break;
 
-        // Recover the original type object here (`field` contains
-        // only its canonical form). The difference is observable,
-        // e.g. in a program like:
-        //
-        //     var Point1 = new StructType({x:uint8, y:uint8});
-        //     var Point2 = new StructType({x:uint8, y:uint8});
-        //     var Line1 = new StructType({start:Point1, end: Point1});
-        //     var Line2 = new StructType({start:Point2, end: Point2});
-        //     var line1 = new Line1(...);
-        //     var line2 = new Line2(...);
-        //
-        // In this scenario, line1.start.type() === Point1 and
-        // line2.start.type() === Point2.
-        RootedObject fieldTypes(
-            cx,
-            &type->getFixedSlot(SLOT_STRUCT_FIELD_TYPES).toObject());
-        RootedValue fieldTypeVal(cx);
-        if (!JSObject::getElement(cx, fieldTypes, fieldTypes,
-                                  field->index, &fieldTypeVal))
+        RootedObject fieldType(cx, StructFieldType(cx, type, field->index));
+        if (!fieldType)
             return false;
 
-        RootedObject fieldType(cx, &fieldTypeVal.toObject());
         return Reify(cx, field->typeRepr, fieldType, obj, field->offset, vp);
       }
     }
 
     RootedObject proto(cx, obj->getProto());
     if (!proto) {
         vp.setUndefined();
         return true;
@@ -1986,18 +1845,18 @@ BinaryBlock::obj_getElement(JSContext *c
     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)
 {
-    RootedObject type(cx, GetType(obj));
-    TypeRepresentation *typeRepr = typeRepresentation(type);
+    RootedObject type(cx, GetType(*obj));
+    TypeRepresentation *typeRepr = typeRepresentation(*type);
 
     switch (typeRepr->kind()) {
       case TypeRepresentation::Scalar:
       case TypeRepresentation::Struct:
         break;
 
       case TypeRepresentation::Array: {
         *present = true;
@@ -2030,24 +1889,24 @@ BinaryBlock::obj_getSpecial(JSContext *c
                             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,
-                             MutableHandleValue vp, bool strict)
+                            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);
+    RootedObject type(cx, GetType(*obj));
+    TypeRepresentation *typeRepr = typeRepresentation(*type);
 
     switch (typeRepr->kind()) {
       case ScalarTypeRepresentation::Scalar:
         break;
 
       case ScalarTypeRepresentation::Array:
         if (JSID_IS_ATOM(id, cx->names().length)) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage,
@@ -2056,18 +1915,21 @@ BinaryBlock::obj_setGeneric(JSContext *c
         }
         break;
 
       case ScalarTypeRepresentation::Struct: {
         const StructField *field = typeRepr->asStruct()->fieldNamed(id);
         if (!field)
             break;
 
-        uint8_t *loc = BlockMem(obj) + field->offset;
-        return ConvertAndCopyTo(cx, field->typeRepr, vp, loc);
+        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);
 }
 
 bool
 BinaryBlock::obj_setProperty(JSContext *cx, HandleObject obj,
@@ -2075,38 +1937,38 @@ BinaryBlock::obj_setProperty(JSContext *
                              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,
-                             MutableHandleValue vp, bool strict)
+                            MutableHandleValue vp, bool strict)
 {
-    RootedObject type(cx, GetType(obj));
-    TypeRepresentation *typeRepr = typeRepresentation(type);
+    RootedObject type(cx, GetType(*obj));
+    TypeRepresentation *typeRepr = typeRepresentation(*type);
 
     switch (typeRepr->kind()) {
       case ScalarTypeRepresentation::Scalar:
       case ScalarTypeRepresentation::Struct:
         break;
 
       case ScalarTypeRepresentation::Array: {
         ArrayTypeRepresentation *arrayTypeRepr = typeRepr->asArray();
 
         if (index >= arrayTypeRepr->length()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage,
                                  nullptr, JSMSG_TYPEDOBJECT_BINARYARRAY_BAD_INDEX);
             return false;
         }
 
+        RootedObject elementType(cx, ArrayElementType(type));
         size_t offset = arrayTypeRepr->element()->size() * index;
-        uint8_t *mem = BlockMem(obj) + offset;
-        return ConvertAndCopyTo(cx, arrayTypeRepr->element(), vp, mem);
+        return ConvertAndCopyTo(cx, elementType, obj, offset, vp);
       }
     }
 
     return ReportBlockTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, obj);
 }
 
 bool
 BinaryBlock::obj_setSpecial(JSContext *cx, HandleObject obj,
@@ -2117,18 +1979,18 @@ BinaryBlock::obj_setSpecial(JSContext *c
     return obj_setGeneric(cx, obj, id, vp, strict);
 }
 
 bool
 BinaryBlock::obj_getGenericAttributes(JSContext *cx, HandleObject obj,
                                        HandleId id, unsigned *attrsp)
 {
     uint32_t index;
-    RootedObject type(cx, GetType(obj));
-    TypeRepresentation *typeRepr = typeRepresentation(type);
+    RootedObject type(cx, GetType(*obj));
+    TypeRepresentation *typeRepr = typeRepresentation(*type);
 
     switch (typeRepr->kind()) {
       case TypeRepresentation::Scalar:
         break;
 
       case TypeRepresentation::Array:
         if (js_IdIsIndex(id, &index)) {
             *attrsp = JSPROP_ENUMERATE | JSPROP_PERMANENT;
@@ -2156,18 +2018,18 @@ BinaryBlock::obj_getGenericAttributes(JS
 
     return JSObject::getGenericAttributes(cx, proto, id, attrsp);
 }
 
 static bool
 IsOwnId(JSContext *cx, HandleObject obj, HandleId id)
 {
     uint32_t index;
-    RootedObject type(cx, GetType(obj));
-    TypeRepresentation *typeRepr = typeRepresentation(type);
+    RootedObject type(cx, GetType(*obj));
+    TypeRepresentation *typeRepr = typeRepresentation(*type);
 
     switch (typeRepr->kind()) {
       case TypeRepresentation::Scalar:
         return false;
 
       case TypeRepresentation::Array:
         return js_IdIsIndex(id, &index) || JSID_IS_ATOM(id, cx->names().length);
 
@@ -2177,17 +2039,17 @@ IsOwnId(JSContext *cx, HandleObject obj,
 
     return false;
 }
 
 bool
 BinaryBlock::obj_setGenericAttributes(JSContext *cx, HandleObject obj,
                                        HandleId id, unsigned *attrsp)
 {
-    RootedObject type(cx, GetType(obj));
+    RootedObject type(cx, GetType(*obj));
 
     if (IsOwnId(cx, obj, id))
         return ReportPropertyError(cx, JSMSG_CANT_REDEFINE_PROP, id);
 
     RootedObject proto(cx, obj->getProto());
     if (!proto) {
         *attrsp = 0;
         return true;
@@ -2247,18 +2109,18 @@ BinaryBlock::obj_deleteSpecial(JSContext
 }
 
 bool
 BinaryBlock::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);
+    RootedObject type(cx, GetType(*obj));
+    TypeRepresentation *typeRepr = typeRepresentation(*type);
 
     switch (typeRepr->kind()) {
       case TypeRepresentation::Scalar:
         switch (enum_op) {
           case JSENUMERATE_INIT_ALL:
           case JSENUMERATE_INIT:
             statep.setInt32(0);
             idp.set(INT_TO_JSID(0));
@@ -2325,10 +2187,103 @@ BinaryBlock::obj_enumerate(JSContext *cx
     }
 
     return true;
 }
 
 /* static */ size_t
 BinaryBlock::dataOffset()
 {
-    return JSObject::getPrivateDataOffset(BLOCK_RESERVED_SLOTS + 1);
+    return JSObject::getPrivateDataOffset(JS_TYPEDOBJ_SLOTS + 1);
+}
+
+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;
+}
+
+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[1].isInt32());
+    JS_ASSERT(args[2].isObject() && IsBlock(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();
+    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[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);                 \
+    double d = args[2].toNumber();                                            \
+    *target = ConvertScalar<T>(d);                                            \
+    args.rval().setUndefined();                                               \
+    return true;                                                              \
+}                                                                             \
+                                                                              \
+const JSJitInfo                                                               \
+js::StoreScalar##T::JitInfo =                                                 \
+    JS_JITINFO_NATIVE_PARALLEL(                                               \
+        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[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);                 \
+    args.rval().setNumber((double) *target);                                  \
+    return true;                                                              \
+}                                                                             \
+                                                                              \
+const JSJitInfo                                                               \
+js::LoadScalar##T::JitInfo =                                                  \
+    JS_JITINFO_NATIVE_PARALLEL(                                               \
+        JSParallelNativeThreadSafeWrapper<Func>);
+
+// I was using templates for this stuff instead of macros, but ran
+// into problems with the Unagi compiler.
+JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_STORE_SCALAR_CLASS_IMPL)
+JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_LOAD_SCALAR_CLASS_IMPL)
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -4,68 +4,34 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef builtin_TypedObject_h
 #define builtin_TypedObject_h
 
 #include "jsobj.h"
 
+#include "builtin/TypedObjectConstants.h"
 #include "builtin/TypeRepresentation.h"
 
 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;
 
-// Slots common to all type descriptors:
-enum TypeCommonSlots {
-    // Canonical type representation of this type (see TypeRepresentation.h).
-    SLOT_TYPE_REPR=0,
-    TYPE_RESERVED_SLOTS
-};
-
-// Slots for ArrayType type descriptors:
-enum ArrayTypeCommonSlots {
-    // Type descriptor for the element type.
-    SLOT_ARRAY_ELEM_TYPE = TYPE_RESERVED_SLOTS,
-    ARRAY_TYPE_RESERVED_SLOTS
-};
-
-// Slots for StructType type descriptors:
-enum StructTypeCommonSlots {
-    // JS array containing type descriptors for each field, in order.
-    SLOT_STRUCT_FIELD_TYPES = TYPE_RESERVED_SLOTS,
-    STRUCT_TYPE_RESERVED_SLOTS
-};
-
-// Slots for data blocks:
-enum BlockCommonSlots {
-    // The type descriptor with which this block is associated.
-    SLOT_DATATYPE = 0,
-
-    // If this value is nullptr, then the block instance owns the
-    // uint8_t* in its priate data. Otherwise, this field contains the
-    // owner, and thus keeps the owner alive.
-    SLOT_BLOCKREFOWNER,
-
-    BLOCK_RESERVED_SLOTS
-};
-
 template <ScalarTypeRepresentation::Type type, typename T>
 class NumericType
 {
   private:
     static const Class * typeToClass();
   public:
-    static bool convert(JSContext *cx, HandleValue val, T* converted);
     static bool reify(JSContext *cx, void *mem, MutableHandleValue vp);
     static bool call(JSContext *cx, unsigned argc, Value *vp);
 };
 
 /*
  * These are the classes of the scalar type descriptors, like `uint8`,
  * `uint16` etc. Each of these classes has exactly one instance that
  * is pre-created.
@@ -246,11 +212,59 @@ class BinaryBlock
     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);
 };
 
 
+// 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`.
+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`.
+#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.
+#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
+// into problems with the Unagi compiler.
+JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_STORE_SCALAR_CLASS_DEFN)
+JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_LOAD_SCALAR_CLASS_DEFN)
+
 } // namespace js
 
 #endif /* builtin_TypedObject_h */
+
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/TypedObject.js
@@ -0,0 +1,311 @@
+#include "TypedObjectConstants.h"
+
+///////////////////////////////////////////////////////////////////////////
+// Getters and setters for various slots.
+
+// 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)
+
+// 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) \
+    TO_INT32(UnsafeGetReservedSlot(obj, JS_TYPEREPR_SLOT_ALIGNMENT))
+#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) {
+  // Eventually this will be a slot on typed objects
+  return TYPE_TYPE_REPR(TYPED_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
+//
+// 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) {
+  this.typeRepr = typeRepr;
+  this.typeObj = typeObj;
+  this.owner = owner;
+  this.offset = offset;
+}
+
+MakeConstructible(TypedObjectPointer, {});
+
+#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);
+};
+
+TypedObjectPointer.prototype.reset = function(inPtr) {
+  this.typeRepr = inPtr.typeRepr;
+  this.typeObj = inPtr.typeObj;
+  this.owner = inPtr.owner;
+  this.offset = inPtr.offset;
+  return this;
+};
+
+TypedObjectPointer.prototype.kind = function() {
+  return REPR_KIND(this.typeRepr);
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Moving the pointer
+//
+// The methods in this section adjust `this` in place to point at
+// subelements or subproperties.
+
+// Adjusts `this` in place so that it points at the property
+// `propName`.  Throws if there is no such property. Returns `this`.
+TypedObjectPointer.prototype.moveTo = function(propName) {
+  switch (this.kind()) {
+  case JS_TYPEREPR_SCALAR_KIND:
+    break;
+
+  case JS_TYPEREPR_ARRAY_KIND:
+    // For an array, property must be an element. Note that we use the
+    // length as loaded from the type *representation* as opposed to
+    // the type *object*; this is because some type objects represent
+    // unsized arrays and hence do not have a length.
+    var index = TO_INT32(propName);
+    if (index === propName && index < REPR_LENGTH(this.typeRepr))
+      return this.moveToElem(index);
+    break;
+
+  case JS_TYPEREPR_STRUCT_KIND:
+    if (HAS_PROPERTY(this.typeObj.fieldTypes, propName))
+      return this.moveToField(propName);
+    break;
+  }
+
+  ThrowError(JSMSG_TYPEDOBJECT_NO_SUCH_PROP, propName);
+};
+
+// Adjust `this` in place to point at the element `index`.  `this`
+// must be a array type and `index` must be within bounds. Returns
+// `this`.
+TypedObjectPointer.prototype.moveToElem = function(index) {
+  assert(this.kind() == JS_TYPEREPR_ARRAY_KIND,
+         "moveToElem invoked on non-array");
+  assert(index < REPR_LENGTH(this.typeRepr),
+         "moveToElem invoked with out-of-bounds index");
+
+  var elementTypeObj = this.typeObj.elementType;
+  var elementTypeRepr = TYPE_TYPE_REPR(elementTypeObj);
+  this.typeRepr = elementTypeRepr;
+  this.typeObj = elementTypeObj;
+  var elementSize = REPR_SIZE(elementTypeRepr);
+
+  // Note: we do not allow construction of arrays where the offset
+  // of an element cannot be represented by an int32.
+  this.offset += std_Math_imul(index, elementSize);
+
+  return this;
+};
+
+// Adjust `this` to point at the field `propName`.  `this` must be a
+// struct type and `propName` must be a valid field name. Returns
+// `this`.
+TypedObjectPointer.prototype.moveToField = function(propName) {
+  assert(this.kind() == JS_TYPEREPR_STRUCT_KIND,
+         "moveToField invoked on non-struct");
+  assert(HAS_PROPERTY(this.typeObj.fieldTypes, propName),
+         "moveToField invoked with undefined field");
+
+  var fieldTypeObj = this.typeObj.fieldTypes[propName];
+  var fieldOffset = TO_INT32(this.typeObj.fieldOffsets[propName]);
+  this.typeObj = fieldTypeObj;
+  this.typeRepr = TYPE_TYPE_REPR(fieldTypeObj);
+
+  // 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;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// 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) {
+  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) {
+      var size = REPR_SIZE(typeRepr);
+      Memcpy(this.owner, this.offset, fromValue, 0, size);
+      return;
+    }
+  }
+
+  switch (REPR_KIND(typeRepr)) {
+  case JS_TYPEREPR_SCALAR_KIND:
+    this.setScalar(fromValue);
+    return;
+
+  case JS_TYPEREPR_ARRAY_KIND:
+    if (!IsObject(fromValue))
+      break;
+
+    // Check that "array-like" fromValue has an appropriate length.
+    var length = REPR_LENGTH(typeRepr);
+    if (fromValue.length !== length)
+      break;
+
+    // Adapt each element.
+    var tempPtr = this.copy().moveToElem(0);
+    var size = REPR_SIZE(tempPtr.typeRepr);
+    for (var i = 0; i < length; i++) {
+      tempPtr.set(fromValue[i]);
+      tempPtr.offset += size;
+    }
+    return;
+
+  case JS_TYPEREPR_STRUCT_KIND:
+    if (!IsObject(fromValue))
+      break;
+
+    // Adapt each field.
+    var tempPtr = this.copy();
+    var fieldNames = this.typeObj.fieldNames;
+    for (var i = 0; i < fieldNames.length; i++) {
+      var fieldName = fieldNames[i];
+      tempPtr.reset(this).moveToField(fieldName).set(fromValue[fieldName]);
+    }
+    return;
+  }
+
+  ThrowError(JSMSG_CANT_CONVERT_TO,
+             typeof(fromValue),
+             this.typeRepr.toSource())
+}
+
+// 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,
+                     TO_INT32(fromValue) & 0xFF);
+
+  case JS_SCALARTYPEREPR_UINT8:
+    return Store_uint8(this.owner, this.offset,
+                      TO_UINT32(fromValue) & 0xFF);
+
+  case JS_SCALARTYPEREPR_UINT8_CLAMPED:
+    var v = ClampToUint8(+fromValue);
+    return Store_int8(this.owner, this.offset, v);
+
+  case JS_SCALARTYPEREPR_INT16:
+    return Store_int16(this.owner, this.offset,
+                      TO_INT32(fromValue) & 0xFFFF);
+
+  case JS_SCALARTYPEREPR_UINT16:
+    return Store_uint16(this.owner, this.offset,
+                       TO_UINT32(fromValue) & 0xFFFF);
+
+  case JS_SCALARTYPEREPR_INT32:
+    return Store_int32(this.owner, this.offset,
+                      TO_INT32(fromValue));
+
+  case JS_SCALARTYPEREPR_UINT32:
+    return Store_uint32(this.owner, this.offset,
+                       TO_UINT32(fromValue));
+
+  case JS_SCALARTYPEREPR_FLOAT32:
+    return Store_float32(this.owner, this.offset, +fromValue);
+
+  case JS_SCALARTYPEREPR_FLOAT64:
+    return Store_float64(this.owner, 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,
+                          destOffset,
+                          fromValue)
+{
+  var ptr = new TypedObjectPointer(destTypeRepr, destTypeObj,
+                                   destTypedObj, destOffset);
+  ptr.set(fromValue);
+}
+
+function FillTypedArrayWithValue(destArray, fromValue) {
+  var typeRepr = TYPED_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);
+  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);
+}
+
+
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/TypedObjectConstants.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Specialized .h file to be used by both JS and C++ code.
+
+#ifndef builtin_TypedObjectConstants_h
+#define builtin_TypedObjectConstants_h
+
+///////////////////////////////////////////////////////////////////////////
+// Slots for type objects
+//
+// Some slots apply to all type objects and some are specific to
+// particular kinds of type objects.  Because all type objects, at
+// least for now, have a distinct class, we can assign them distinct
+// numbers of slots depending on their kind.
+
+// Slots on all type objects
+#define JS_TYPEOBJ_SLOT_TYPE_REPR          0  // Associated Type Representation
+
+// Slots on scalars
+#define JS_TYPEOBJ_SCALAR_SLOTS            1  // Maximum number
+
+// Slots on arrays
+#define JS_TYPEOBJ_SLOT_ARRAY_ELEM_TYPE    1
+#define JS_TYPEOBJ_ARRAY_SLOTS             2  // Maximum number
+
+// Slots on structs
+#define JS_TYPEOBJ_SLOT_STRUCT_FIELD_TYPES 1
+#define JS_TYPEOBJ_STRUCT_SLOTS            2  // Maximum number
+
+
+///////////////////////////////////////////////////////////////////////////
+// Slots for type representation objects
+//
+// Some slots apply to all type representations and some are specific
+// to particular kinds of type representations.  Because all type
+// representations share the same class, however, they always have the
+// same number of slots, though not all of them will be initialized or
+// used in the same way.
+
+// Slots on *all* type objects:
+#define JS_TYPEREPR_SLOT_KIND      0 // One of the `kind` constants below
+#define JS_TYPEREPR_SLOT_SIZE      1 // Size in bytes.
+#define JS_TYPEREPR_SLOT_ALIGNMENT 2 // Alignment in bytes.
+
+// Slots on arrays:
+#define JS_TYPEREPR_SLOT_LENGTH    3 // Length of the array
+
+// Slots on scalars:
+#define JS_TYPEREPR_SLOT_TYPE      3 // One of the constants below
+
+// Maximum number of slots for any type representation
+#define JS_TYPEREPR_SLOTS          4
+
+// These constants are for use exclusively in JS code.  In C++ code,
+// prefer TypeRepresentation::Scalar etc, since that allows you to
+// write a switch which will receive a warning if you omit a case.
+#define JS_TYPEREPR_SCALAR_KIND 0
+#define JS_TYPEREPR_STRUCT_KIND 1
+#define JS_TYPEREPR_ARRAY_KIND  2
+
+// These constants are for use exclusively in JS code.  In C++ code,
+// prefer ScalarTypeRepresentation::TYPE_INT8 etc, since that allows
+// you to write a switch which will receive a warning if you omit a
+// case.
+#define JS_SCALARTYPEREPR_INT8          0
+#define JS_SCALARTYPEREPR_UINT8         1
+#define JS_SCALARTYPEREPR_INT16         2
+#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
+
+#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
+
+#endif
--- a/js/src/builtin/Utilities.js
+++ b/js/src/builtin/Utilities.js
@@ -50,16 +50,17 @@ var std_Boolean_toString = Boolean.proto
 var Std_Date = Date;
 var std_Date_now = Date.now;
 var std_Date_valueOf = Date.prototype.valueOf;
 var std_Function_bind = Function.prototype.bind;
 var std_Function_apply = Function.prototype.apply;
 var std_Math_floor = Math.floor;
 var std_Math_max = Math.max;
 var std_Math_min = Math.min;
+var std_Math_imul = Math.imul;
 var std_Number_valueOf = Number.prototype.valueOf;
 var std_Number_POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
 var std_Object_create = Object.create;
 var std_Object_defineProperty = Object.defineProperty;
 var std_Object_getOwnPropertyNames = Object.getOwnPropertyNames;
 var std_Object_hasOwnProperty = Object.prototype.hasOwnProperty;
 var std_RegExp_test = RegExp.prototype.test;
 var Std_String = String;
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -9505,17 +9505,18 @@ 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::SLOT_DATATYPE);
+    MInstruction *load = MLoadFixedSlot::New(typedObj,
+                                             JS_TYPEDOBJ_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
@@ -9593,17 +9594,17 @@ IonBuilder::lookupTypedObjectField(MDefi
 }
 
 MDefinition *
 IonBuilder::typeObjectForFieldFromStructType(MDefinition *typeObj,
                                              size_t fieldIndex)
 {
     // Load list of field type objects.
 
-    MInstruction *fieldTypes = MLoadFixedSlot::New(typeObj, SLOT_STRUCT_FIELD_TYPES);
+    MInstruction *fieldTypes = MLoadFixedSlot::New(typeObj, JS_TYPEOBJ_SLOT_STRUCT_FIELD_TYPES);
     current->add(fieldTypes);
 
     // Index into list with index of field.
 
     MInstruction *fieldTypesElements = MElements::New(fieldTypes);
     current->add(fieldTypesElements);
 
     MConstant *fieldIndexDef = MConstant::New(Int32Value(fieldIndex));
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -410,8 +410,10 @@ MSG_DEF(JSMSG_TYPEDOBJECT_SUBARRAY_INTEG
 MSG_DEF(JSMSG_TYPEDOBJECT_STRUCTTYPE_EMPTY_DESCRIPTOR, 357, 0, JSEXN_ERR, "field descriptor cannot be empty")
 MSG_DEF(JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD_FIELD, 358, 1, JSEXN_ERR, "field {0} is not a valid BinaryData Type descriptor")
 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}")
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -734,16 +734,26 @@ enum DestroyContextMode {
 extern void
 DestroyContext(JSContext *cx, DestroyContextMode mode);
 
 enum ErrorArgumentsType {
     ArgumentsAreUnicode,
     ArgumentsAreASCII
 };
 
+
+/*
+ * Loads and returns a self-hosted function by name. For performance, define
+ * the property name in vm/CommonPropertyNames.h.
+ *
+ * Defined in SelfHosting.cpp.
+ */
+JSFunction *
+SelfHostedFunction(JSContext *cx, HandlePropertyName propName);
+
 } /* namespace js */
 
 #ifdef va_start
 extern bool
 js_ReportErrorVA(JSContext *cx, unsigned flags, const char *format, va_list ap);
 
 extern bool
 js_ReportErrorNumberVA(JSContext *cx, unsigned flags, JSErrorCallback callback,
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedObject/Bug914137.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
+var BUGNUMBER = 914137;
+var summary = 'Fuzz bug';
+
+function test() {
+  var A = new TypedObject.ArrayType(TypedObject.uint8, 2147483647);
+  var a = new A();
+  assertEq(arguments[5], a);
+}
+
+print(BUGNUMBER + ": " + summary);
+assertThrows(test);
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+print("Tests complete");
--- a/js/src/tests/ecma_6/TypedObject/arraytype.js
+++ b/js/src/tests/ecma_6/TypedObject/arraytype.js
@@ -92,17 +92,17 @@ function runTests() {
     mario[1] = [1, 1.414, 3.14];
 
     assertEq(mario[0].length, 3);
     assertEq(mario[0][0], 1);
     assertEq(mario[0][1], 0);
     assertEq(mario[0][2], 0);
 
     assertThrows(function() mario[1] = 5);
-    mario[1][1] = [];
+    mario[1][1] = {};
     assertEq(Number.isNaN(mario[1][1]), true);
 
     // ok this is just for kicks
     var AllSprites = new ArrayType(Sprite, 65536);
     var as = new AllSprites();
     assertEq(as.length, 65536);
 
     // test methods
--- a/js/src/tests/ecma_6/TypedObject/numerictypes.js
+++ b/js/src/tests/ecma_6/TypedObject/numerictypes.js
@@ -108,17 +108,17 @@ function runTests()
         check(function() type(true) === 1);
         check(function() type(false) === 0);
         check(function() type(+Infinity) === Infinity);
         check(function() type(-Infinity) === -Infinity);
         check(function() Number.isNaN(type(NaN)));
         check(function() type.toString() === floatStrings[i]);
         check(function() type(null) == 0);
         check(function() Number.isNaN(type(undefined)));
-        check(function() Number.isNaN(type([])));
+        check(function() type([]) == 0);
         check(function() Number.isNaN(type({})));
         check(function() Number.isNaN(type(/abcd/)));
 
         checkThrows(function() new type());
         checkThrows(function() type());
     }
 
     ///// test ranges and creation
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedObject/scalar_types.js
@@ -0,0 +1,68 @@
+// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
+
+var BUGNUMBER = 578700;
+var summary = 'Byte-sized type conversion';
+
+var T = TypedObject;
+
+function check(results, ctor) {
+  print("ctor = ", ctor.toSource());
+
+  // check applying the ctor directly
+  for (var i = 0; i < results.length; i++)
+    assertEq(results[i][0], ctor(results[i][1]));
+
+  // check writing and reading from a struct
+  var S = new T.StructType({f: ctor});
+  for (var i = 0; i < results.length; i++) {
+    var s = new S({f: results[i][1]});
+    assertEq(results[i][0], s.f);
+  }
+}
+
+function runTests() {
+  print(BUGNUMBER + ": " + summary);
+
+  var int8results = [
+    [22, 22],
+    [-128, 128],
+    [-1, 255],
+    [-128, -128],
+    [127, -129],
+    [0x75, 0x7575],
+    [-123, 0x7585]
+  ];
+  check(int8results, T.int8);
+
+  var uint8results = [
+    [22, 22],
+    [128, 128],
+    [255, 255],
+    [0, 256],
+    [128, -128],
+    [127, -129],
+    [129, 129],
+    [0x75, 0x7575],
+    [0x85, 0x7585]
+  ];
+  check(uint8results, T.uint8);
+
+  var uint8clampedresults = [
+    [22, 22],
+    [128, 128],
+    [255, 255],
+    [0, -128],
+    [0, -129],
+    [129, 129],
+    [255, 0x7575],
+    [255, 0x7585]
+  ];
+  check(uint8clampedresults, T.uint8Clamped);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+
+  print("Tests complete");
+}
+
+runTests();
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -30,16 +30,17 @@
     macro(caseFirst, caseFirst, "caseFirst") \
     macro(Collator, Collator, "Collator") \
     macro(CollatorCompareGet, CollatorCompareGet, "Intl_Collator_compare_get") \
     macro(columnNumber, columnNumber, "columnNumber") \
     macro(compare, compare, "compare") \
     macro(configurable, configurable, "configurable") \
     macro(construct, construct, "construct") \
     macro(constructor, constructor, "constructor") \
+    macro(ConvertAndCopyTo, ConvertAndCopyTo, "ConvertAndCopyTo") \
     macro(currency, currency, "currency") \
     macro(currencyDisplay, currencyDisplay, "currencyDisplay") \
     macro(std_iterator, std_iterator, "@@iterator") \
     macro(DateTimeFormat, DateTimeFormat, "DateTimeFormat") \
     macro(DateTimeFormatFormatGet, DateTimeFormatFormatGet, "Intl_DateTimeFormat_format_get") \
     macro(decodeURI, decodeURI, "decodeURI") \
     macro(decodeURIComponent, decodeURIComponent, "decodeURIComponent") \
     macro(defineProperty, defineProperty, "defineProperty") \
@@ -57,16 +58,17 @@
     macro(enumerate, enumerate, "enumerate") \
     macro(escape, escape, "escape") \
     macro(eval, eval, "eval") \
     macro(false, false_, "false") \
     macro(fieldNames, fieldNames, "fieldNames") \
     macro(fieldOffsets, fieldOffsets, "fieldOffsets") \
     macro(fieldTypes, fieldTypes, "fieldTypes") \
     macro(fileName, fileName, "fileName") \
+    macro(FillTypedArrayWithValue, FillTypedArrayWithValue, "FillTypedArrayWithValue") \
     macro(fix, fix, "fix") \
     macro(float32, float32, "float32") \
     macro(float64, float64, "float64") \
     macro(format, format, "format") \
     macro(get, get, "get") \
     macro(getInternals, getInternals, "getInternals") \
     macro(getOwnPropertyDescriptor, getOwnPropertyDescriptor, "getOwnPropertyDescriptor") \
     macro(getOwnPropertyNames, getOwnPropertyNames, "getOwnPropertyNames") \
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -7,16 +7,17 @@
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsfriendapi.h"
 #include "jsobj.h"
 #include "selfhosted.out.h"
 
 #include "builtin/Intl.h"
 #include "builtin/ParallelArray.h"
+#include "builtin/TypedObject.h"
 #include "gc/Marking.h"
 #include "vm/ForkJoin.h"
 #include "vm/Interpreter.h"
 
 #include "jsfuninlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/BooleanObject-inl.h"
@@ -583,16 +584,33 @@ 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_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)                      \
+    JS_FNINFO("Store_" #_name,                                                \
+              JSNativeThreadSafeWrapper<js::StoreScalar##_type::Func>,        \
+              &js::StoreScalar##_type::JitInfo, 3, 0),                        \
+    JS_FNINFO("Load_" #_name,                                                 \
+              JSNativeThreadSafeWrapper<js::LoadScalar##_type::Func>,         \
+              &js::LoadScalar##_type::JitInfo, 3, 0),
+    JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(LOAD_AND_STORE_FN_DECLS)
+
     // See builtin/Intl.h for descriptions of the intl_* functions.
     JS_FN("intl_availableCalendars", intl_availableCalendars, 1,0),
     JS_FN("intl_availableCollations", intl_availableCollations, 1,0),
     JS_FN("intl_Collator", intl_Collator, 2,0),
     JS_FN("intl_Collator_availableLocales", intl_Collator_availableLocales, 0,0),
     JS_FN("intl_CompareStrings", intl_CompareStrings, 3,0),
     JS_FN("intl_DateTimeFormat", intl_DateTimeFormat, 2,0),
     JS_FN("intl_DateTimeFormat_availableLocales", intl_DateTimeFormat_availableLocales, 0,0),
@@ -906,8 +924,20 @@ JSRuntime::maybeWrappedSelfHostedFunctio
 
     if (!funVal.toObject().as<JSFunction>().isWrappable()) {
         funVal.setUndefined();
         return true;
     }
 
     return cx->compartment()->wrap(cx, funVal);
 }
+
+JSFunction *
+js::SelfHostedFunction(JSContext *cx, HandlePropertyName propName)
+{
+    RootedValue func(cx);
+    if (!cx->global()->getIntrinsicValue(cx, propName, &func))
+        return NULL;
+
+    JS_ASSERT(func.isObject());
+    JS_ASSERT(func.toObject().is<JSFunction>());
+    return &func.toObject().as<JSFunction>();
+}