Bug 922115 - Unsized array support in Typed Objects r=sfink
authorNicholas D. Matsakis <nmatsakis@mozilla.com>
Mon, 30 Sep 2013 10:19:09 -0400
changeset 159317 d58ca9a622c04d83f16077d8cfc5ab69e8b868bd
parent 159316 957d85b31ff325d765c2b5bc09d85c201072362e
child 159318 c996211971a85633a6d2a60c58921566e765e90f
push id37258
push usernmatsakis@mozilla.com
push dateSat, 07 Dec 2013 01:44:00 +0000
treeherdermozilla-inbound@c996211971a8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs922115
milestone28.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 922115 - Unsized array support in Typed Objects r=sfink
js/src/builtin/TypeRepresentation.cpp
js/src/builtin/TypeRepresentation.h
js/src/builtin/TypedObject.cpp
js/src/builtin/TypedObject.h
js/src/builtin/TypedObject.js
js/src/builtin/TypedObjectConstants.h
js/src/jit-test/tests/TypedObject/fuzz10.js
js/src/jit-test/tests/TypedObject/fuzz11.js
js/src/jit-test/tests/TypedObject/fuzz4.js
js/src/jit-test/tests/TypedObject/fuzz8.js
js/src/jit-test/tests/TypedObject/fuzz9.js
js/src/jit-test/tests/TypedObject/jit-complex.js
js/src/jit-test/tests/TypedObject/jit-prefix.js
js/src/jit-test/tests/TypedObject/jit-read-many.js
js/src/jit-test/tests/TypedObject/jit-read-u16-from-mdim-array.js
js/src/jit-test/tests/TypedObject/jit-read-u16-from-struct-array-in-struct.js
js/src/jit-test/tests/TypedObject/jit-read-u16-from-struct-array.js
js/src/jit-test/tests/TypedObject/jit-read-u16-from-u16-array-in-struct.js
js/src/jit-test/tests/TypedObject/jit-read-u16-from-u16-array.js
js/src/jit-test/tests/TypedObject/jit-read-u32-from-mdim-array.js
js/src/jit-test/tests/TypedObject/jit-read-u32-from-struct-array-in-struct.js
js/src/jit-test/tests/TypedObject/jit-read-u32-from-struct-array.js
js/src/jit-test/tests/TypedObject/jit-read-u32-from-u32-array-in-struct.js
js/src/jit-test/tests/TypedObject/jit-read-u32-from-u32-array.js
js/src/jit-test/tests/TypedObject/jit-read-unsized.js
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/TypeRepresentationSet.cpp
js/src/jit/TypeRepresentationSet.h
js/src/js.msg
js/src/tests/ecma_6/TypedObject/arrayequiv.js
js/src/tests/ecma_6/TypedObject/arrayofstructs.js
js/src/tests/ecma_6/TypedObject/arraytype.js
js/src/tests/ecma_6/TypedObject/arrayzerolen.js
js/src/tests/ecma_6/TypedObject/handle.js
js/src/tests/ecma_6/TypedObject/handle_get_set.js
js/src/tests/ecma_6/TypedObject/handle_move.js
js/src/tests/ecma_6/TypedObject/handle_unattached.js
js/src/tests/ecma_6/TypedObject/handle_unsized.js
js/src/tests/ecma_6/TypedObject/memory.js
js/src/tests/ecma_6/TypedObject/objecttype.js
js/src/tests/ecma_6/TypedObject/redimension.js
js/src/tests/ecma_6/TypedObject/referencetypecoercions.js
js/src/tests/ecma_6/TypedObject/referencetypetrace.js
js/src/tests/ecma_6/TypedObject/simd/float32x4handle.js
js/src/tests/ecma_6/TypedObject/simd/float32x4reify.js
js/src/tests/ecma_6/TypedObject/simd/float32x4setter.js
js/src/tests/ecma_6/TypedObject/simd/int32x4handle.js
js/src/tests/ecma_6/TypedObject/simd/int32x4reify.js
js/src/tests/ecma_6/TypedObject/simd/int32x4setter.js
js/src/tests/ecma_6/TypedObject/size_and_alignment.js
js/src/tests/ecma_6/TypedObject/unsizedarrays.js
js/src/tests/ecma_6/TypedObject/unsizedarraysanddim.js
js/src/tests/ecma_6/TypedObject/unsizedarraysembedded.js
js/src/vm/CommonPropertyNames.h
--- a/js/src/builtin/TypeRepresentation.cpp
+++ b/js/src/builtin/TypeRepresentation.cpp
@@ -63,18 +63,23 @@ TypeRepresentationHasher::match(TypeRepr
         return matchReferences(key1->asReference(), key2->asReference());
 
       case TypeRepresentation::X4:
         return matchX4s(key1->asX4(), key2->asX4());
 
       case TypeRepresentation::Struct:
         return matchStructs(key1->asStruct(), key2->asStruct());
 
-      case TypeRepresentation::Array:
-        return matchArrays(key1->asArray(), key2->asArray());
+      case TypeRepresentation::SizedArray:
+        return matchSizedArrays(key1->asSizedArray(),
+                                key2->asSizedArray());
+
+      case TypeRepresentation::UnsizedArray:
+        return matchUnsizedArrays(key1->asUnsizedArray(),
+                                  key2->asUnsizedArray());
     }
 
     MOZ_ASSUME_UNREACHABLE("Invalid kind");
 }
 
 bool
 TypeRepresentationHasher::matchScalars(ScalarTypeRepresentation *key1,
                                        ScalarTypeRepresentation *key2)
@@ -110,41 +115,52 @@ TypeRepresentationHasher::matchStructs(S
         if (key1->field(i).typeRepr != key2->field(i).typeRepr)
             return false;
     }
 
     return true;
 }
 
 bool
-TypeRepresentationHasher::matchArrays(ArrayTypeRepresentation *key1,
-                                      ArrayTypeRepresentation *key2)
+TypeRepresentationHasher::matchSizedArrays(SizedArrayTypeRepresentation *key1,
+                                           SizedArrayTypeRepresentation *key2)
 {
     // We assume that these pointers have been canonicalized:
     return key1->element() == key2->element() &&
            key1->length() == key2->length();
 }
 
+bool
+TypeRepresentationHasher::matchUnsizedArrays(UnsizedArrayTypeRepresentation *key1,
+                                             UnsizedArrayTypeRepresentation *key2)
+{
+    // We assume that these pointers have been canonicalized:
+    return key1->element() == key2->element();
+}
+
 HashNumber
 TypeRepresentationHasher::hash(TypeRepresentation *key) {
     switch (key->kind()) {
       case TypeRepresentation::Scalar:
         return hashScalar(key->asScalar());
 
       case TypeRepresentation::Reference:
         return hashReference(key->asReference());
 
       case TypeRepresentation::X4:
         return hashX4(key->asX4());
 
       case TypeRepresentation::Struct:
         return hashStruct(key->asStruct());
 
-      case TypeRepresentation::Array:
-        return hashArray(key->asArray());
+      case TypeRepresentation::UnsizedArray:
+        return hashUnsizedArray(key->asUnsizedArray());
+
+      case TypeRepresentation::SizedArray:
+        return hashSizedArray(key->asSizedArray());
     }
 
     MOZ_ASSUME_UNREACHABLE("Invalid kind");
 }
 
 HashNumber
 TypeRepresentationHasher::hashScalar(ScalarTypeRepresentation *key)
 {
@@ -170,102 +186,118 @@ TypeRepresentationHasher::hashStruct(Str
     for (HashNumber i = 0; i < key->fieldCount(); i++) {
         hash = AddToHash(hash, JSID_BITS(key->field(i).id.get()));
         hash = AddToHash(hash, key->field(i).typeRepr);
     }
     return hash;
 }
 
 HashNumber
-TypeRepresentationHasher::hashArray(ArrayTypeRepresentation *key)
+TypeRepresentationHasher::hashSizedArray(SizedArrayTypeRepresentation *key)
 {
     return HashGeneric(key->kind(), key->element(), key->length());
 }
 
+HashNumber
+TypeRepresentationHasher::hashUnsizedArray(UnsizedArrayTypeRepresentation *key)
+{
+    return HashGeneric(key->kind(), key->element());
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // Constructors
 
-TypeRepresentation::TypeRepresentation(Kind kind, size_t size,
-                                       size_t align, bool opaque)
-  : size_(size),
-    alignment_(align),
-    kind_(kind),
+TypeRepresentation::TypeRepresentation(Kind kind, bool opaque)
+  : kind_(kind),
     opaque_(opaque)
 {}
 
+SizedTypeRepresentation::SizedTypeRepresentation(Kind kind, bool opaque,
+                                                 size_t size, size_t align)
+  : TypeRepresentation(kind, opaque),
+    size_(size),
+    alignment_(align)
+{}
+
 static size_t ScalarSizes[] = {
 #define SCALAR_SIZE(_kind, _type, _name)                        \
     sizeof(_type),
     JS_FOR_EACH_SCALAR_TYPE_REPR(SCALAR_SIZE) 0
 #undef SCALAR_SIZE
 };
 
 ScalarTypeRepresentation::ScalarTypeRepresentation(Type type)
-  : TypeRepresentation(Scalar, ScalarSizes[type], ScalarSizes[type], false),
+  : SizedTypeRepresentation(Scalar, false, ScalarSizes[type], ScalarSizes[type]),
     type_(type)
 {
 }
 
 static size_t X4Sizes[] = {
 #define X4_SIZE(_kind, _type, _name)                        \
     sizeof(_type) * 4,
     JS_FOR_EACH_X4_TYPE_REPR(X4_SIZE) 0
 #undef X4_SIZE
 };
 
 X4TypeRepresentation::X4TypeRepresentation(Type type)
-  : TypeRepresentation(X4, X4Sizes[type], X4Sizes[type], false),
+  : SizedTypeRepresentation(X4, false, X4Sizes[type], X4Sizes[type]),
     type_(type)
 {
 }
 
 ReferenceTypeRepresentation::ReferenceTypeRepresentation(Type type)
-  : TypeRepresentation(Reference, 0, 1, true),
+  : SizedTypeRepresentation(Reference, true, 0, 1),
     type_(type)
 {
     switch (type) {
       case TYPE_ANY:
         size_ = sizeof(js::HeapValue);
         alignment_ = MOZ_ALIGNOF(js::HeapValue);
         break;
 
       case TYPE_OBJECT:
       case TYPE_STRING:
         size_ = sizeof(js::HeapPtrObject);
         alignment_ = MOZ_ALIGNOF(js::HeapPtrObject);
         break;
     }
 }
 
-ArrayTypeRepresentation::ArrayTypeRepresentation(TypeRepresentation *element,
-                                                 size_t length)
-  : TypeRepresentation(Array, element->size() * length,
-                       element->alignment(), element->opaque()),
+SizedArrayTypeRepresentation::SizedArrayTypeRepresentation(SizedTypeRepresentation *element,
+                                                           size_t length)
+  : SizedTypeRepresentation(SizedArray, element->opaque(),
+                            element->size() * length, element->alignment()),
     element_(element),
     length_(length)
 {
 }
 
+UnsizedArrayTypeRepresentation::UnsizedArrayTypeRepresentation(SizedTypeRepresentation *element)
+  : TypeRepresentation(UnsizedArray, element->opaque()),
+    element_(element)
+{
+}
+
 static inline size_t alignTo(size_t address, size_t align) {
     JS_ASSERT(IsPowerOfTwo(align));
     return (address + align - 1) & -align;
 }
 
 StructField::StructField(size_t index,
                          jsid &id,
-                         TypeRepresentation *typeRepr,
+                         SizedTypeRepresentation *typeRepr,
                          size_t offset)
   : index(index),
     id(id),
     typeRepr(typeRepr),
     offset(offset)
 {}
 
 StructTypeRepresentation::StructTypeRepresentation()
-  : TypeRepresentation(Struct, 0, 1, false),
+  : SizedTypeRepresentation(Struct, false, 0, 1),
     fieldCount_(0) // see ::init() below!
 {
     // note: size_, alignment_, and opaque_ are computed in ::init() below
 }
 
 bool
 StructTypeRepresentation::init(JSContext *cx,
                                AutoIdVector &ids,
@@ -280,17 +312,18 @@ StructTypeRepresentation::init(JSContext
     // consistency across build environments.
     uint32_t totalSize = 0;
 
     // These will be adjusted in the loop below:
     alignment_ = 1;
     opaque_ = false;
 
     for (size_t i = 0; i < ids.length(); i++) {
-        TypeRepresentation *fieldTypeRepr = fromOwnerObject(*typeReprOwners[i]);
+        SizedTypeRepresentation *fieldTypeRepr =
+            fromOwnerObject(*typeReprOwners[i])->asSized();
 
         if (fieldTypeRepr->opaque())
             opaque_ = true;
 
         uint32_t alignedSize = alignTo(totalSize, fieldTypeRepr->alignment());
         if (alignedSize < totalSize) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                                  JSMSG_TYPEDOBJECT_TOO_BIG);
@@ -351,23 +384,31 @@ TypeRepresentation::addToTableOrFree(JSC
         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()));
+
+    if (isSized()) {
+        ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_SIZE,
+                                      Int32Value(asSized()->size()));
+        ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_ALIGNMENT,
+                                      Int32Value(asSized()->alignment()));
+    }
 
     switch (kind()) {
-      case Array:
+      case UnsizedArray:
+        break;
+
+      case SizedArray:
         ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_LENGTH,
-                                      Int32Value(asArray()->length()));
+                                      Int32Value(asSizedArray()->length()));
         break;
 
       case Scalar:
         ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_TYPE,
                                       Int32Value(asScalar()->type()));
         break;
 
       case Reference:
@@ -447,44 +488,68 @@ ReferenceTypeRepresentation::Create(JSCo
         return nullptr;
     new(ptr) ReferenceTypeRepresentation(type);
 
     return ptr->addToTableOrFree(cx, p);
 }
 
 /*static*/
 JSObject *
-ArrayTypeRepresentation::Create(JSContext *cx,
-                                TypeRepresentation *element,
-                                size_t length)
+SizedArrayTypeRepresentation::Create(JSContext *cx,
+                                     SizedTypeRepresentation *element,
+                                     size_t length)
 {
     JSCompartment *comp = cx->compartment();
 
     // Overly conservative, since we are using `size_t` to represent
     // size, but `SafeMul` operators on `int32_t` types. Still, it
     // should be good enough for now.
     int32_t temp;
     if (!SafeMul(element->size(), length, &temp)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                              JSMSG_TYPEDOBJECT_TOO_BIG);
         return nullptr;
     }
 
-    ArrayTypeRepresentation sample(element, length);
+    SizedArrayTypeRepresentation sample(element, length);
     TypeRepresentationHash::AddPtr p = comp->typeReprs.lookupForAdd(&sample);
     if (p)
         return (*p)->ownerObject();
 
     // Note: cannot use cx->new_ because constructor is private.
-    ArrayTypeRepresentation *ptr =
-        (ArrayTypeRepresentation *) cx->malloc_(
-            sizeof(ArrayTypeRepresentation));
+    SizedArrayTypeRepresentation *ptr =
+        (SizedArrayTypeRepresentation *) cx->malloc_(
+            sizeof(SizedArrayTypeRepresentation));
     if (!ptr)
         return nullptr;
-    new(ptr) ArrayTypeRepresentation(element, length);
+    new(ptr) SizedArrayTypeRepresentation(element, length);
+
+    return ptr->addToTableOrFree(cx, p);
+}
+
+
+/*static*/
+JSObject *
+UnsizedArrayTypeRepresentation::Create(JSContext *cx,
+                                       SizedTypeRepresentation *element)
+{
+    JSCompartment *comp = cx->compartment();
+
+    UnsizedArrayTypeRepresentation sample(element);
+    TypeRepresentationHash::AddPtr p = comp->typeReprs.lookupForAdd(&sample);
+    if (p)
+        return (*p)->ownerObject();
+
+    // Note: cannot use cx->new_ because constructor is private.
+    UnsizedArrayTypeRepresentation *ptr =
+        (UnsizedArrayTypeRepresentation *) cx->malloc_(
+            sizeof(UnsizedArrayTypeRepresentation));
+    if (!ptr)
+        return nullptr;
+    new(ptr) UnsizedArrayTypeRepresentation(element);
 
     return ptr->addToTableOrFree(cx, p);
 }
 
 /*static*/
 JSObject *
 StructTypeRepresentation::Create(JSContext *cx,
                                  AutoIdVector &ids,
@@ -539,33 +604,44 @@ TypeRepresentation::traceFields(JSTracer
       case Reference:
       case X4:
         break;
 
       case Struct:
         asStruct()->traceStructFields(trace);
         break;
 
-      case Array:
-        asArray()->traceArrayFields(trace);
+      case SizedArray:
+        asSizedArray()->traceSizedArrayFields(trace);
+        break;
+
+      case UnsizedArray:
+        asUnsizedArray()->traceUnsizedArrayFields(trace);
         break;
     }
 }
 
 void
 StructTypeRepresentation::traceStructFields(JSTracer *trace)
 {
     for (size_t i = 0; i < fieldCount(); i++) {
         gc::MarkId(trace, &fields()[i].id, "typerepr_field_id");
         fields()[i].typeRepr->mark(trace);
     }
 }
 
 void
-ArrayTypeRepresentation::traceArrayFields(JSTracer *trace)
+SizedArrayTypeRepresentation::traceSizedArrayFields(JSTracer *trace)
+{
+    this->mark(trace);
+    element_->mark(trace);
+}
+
+void
+UnsizedArrayTypeRepresentation::traceUnsizedArrayFields(JSTracer *trace)
 {
     this->mark(trace);
     element_->mark(trace);
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // Finalization
 
@@ -589,18 +665,21 @@ TypeRepresentation::appendString(JSConte
         return asScalar()->appendStringScalar(cx, contents);
 
       case Reference:
         return asReference()->appendStringReference(cx, contents);
 
       case X4:
         return asX4()->appendStringX4(cx, contents);
 
-      case Array:
-        return asArray()->appendStringArray(cx, contents);
+      case SizedArray:
+        return asSizedArray()->appendStringSizedArray(cx, contents);
+
+      case UnsizedArray:
+        return asUnsizedArray()->appendStringUnsizedArray(cx, contents);
 
       case Struct:
         return asStruct()->appendStringStruct(cx, contents);
     }
 
     MOZ_ASSUME_UNREACHABLE("Invalid kind");
     return false;
 }
@@ -657,31 +736,52 @@ X4TypeRepresentation::appendStringX4(JSC
         return contents.append("float32x4");
       case TYPE_INT32:
         return contents.append("int32x4");
     }
     MOZ_ASSUME_UNREACHABLE("Invalid type");
 }
 
 bool
-ArrayTypeRepresentation::appendStringArray(JSContext *cx, StringBuffer &contents)
+SizedArrayTypeRepresentation::appendStringSizedArray(JSContext *cx, StringBuffer &contents)
 {
-    if (!contents.append("ArrayType("))
+    SizedTypeRepresentation *elementType = element();
+    while (elementType->isSizedArray())
+        elementType = elementType->asSizedArray()->element();
+    if (!elementType->appendString(cx, contents))
         return false;
 
+    contents.append(".array(");
+    SizedArrayTypeRepresentation *arrayType = this;
+    while (arrayType != NULL) {
+        if (!NumberValueToStringBuffer(cx, NumberValue(length()), contents))
+            return false;
+
+        if (arrayType->element()->isSizedArray()) {
+            if (!contents.append(","))
+                return false;
+            arrayType = arrayType->element()->asSizedArray();
+        } else {
+            break;
+        }
+    }
+
+    if (!contents.append(")"))
+        return false;
+
+    return true;
+}
+
+bool
+UnsizedArrayTypeRepresentation::appendStringUnsizedArray(JSContext *cx, StringBuffer &contents)
+{
     if (!element()->appendString(cx, contents))
         return false;
 
-    if (!contents.append(", "))
-        return false;
-
-    if (!NumberValueToStringBuffer(cx, NumberValue(length()), contents))
-        return false;
-
-    if (!contents.append(")"))
+    if (!contents.append(".array()"))
         return false;
 
     return true;
 }
 
 bool
 StructTypeRepresentation::appendStringStruct(JSContext *cx, StringBuffer &contents)
 {
@@ -714,41 +814,48 @@ StructTypeRepresentation::appendStringSt
     return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // Walking memory
 
 template<typename V>
 static void
-visitReferences(TypeRepresentation *repr, uint8_t *mem, V& visitor)
+visitReferences(SizedTypeRepresentation *repr,
+                uint8_t *mem,
+                V& visitor)
 {
     if (repr->transparent())
         return;
 
     switch (repr->kind()) {
       case TypeRepresentation::Scalar:
       case TypeRepresentation::X4:
         return;
 
       case TypeRepresentation::Reference:
         visitor.visitReference(repr->asReference(), mem);
         return;
 
-      case TypeRepresentation::Array:
+      case TypeRepresentation::SizedArray:
       {
-        ArrayTypeRepresentation *arrayRepr = repr->asArray();
-        TypeRepresentation *elementRepr = arrayRepr->element();
+        SizedArrayTypeRepresentation *arrayRepr = repr->asSizedArray();
+        SizedTypeRepresentation *elementRepr = arrayRepr->element();
         for (size_t i = 0; i < arrayRepr->length(); i++) {
             visitReferences(elementRepr, mem, visitor);
             mem += elementRepr->size();
         }
         return;
       }
 
+      case TypeRepresentation::UnsizedArray:
+      {
+        MOZ_ASSUME_UNREACHABLE("Only Sized Type representations");
+      }
+
       case TypeRepresentation::Struct:
       {
         StructTypeRepresentation *structRepr = repr->asStruct();
         for (size_t i = 0; i < structRepr->fieldCount(); i++) {
             const StructField &f = structRepr->field(i);
             visitReferences(f.typeRepr, mem + f.offset, visitor);
         }
         return;
@@ -801,22 +908,33 @@ js::MemoryInitVisitor::visitReference(Re
         return;
       }
     }
 
     MOZ_ASSUME_UNREACHABLE("Invalid kind");
 }
 
 void
-TypeRepresentation::initInstance(const JSRuntime *rt, uint8_t *mem)
+SizedTypeRepresentation::initInstance(const JSRuntime *rt,
+                                      uint8_t *mem,
+                                      size_t length)
 {
     MemoryInitVisitor visitor(rt);
+
+    // Initialize the 0th instance
     memset(mem, 0, size());
     if (opaque())
         visitReferences(this, mem, visitor);
+
+    // Stamp out N copies of later instances
+    uint8_t *target = mem;
+    for (size_t i = 1; i < length; i++) {
+        target += size();
+        memcpy(target, mem, size());
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // Tracing instances
 
 namespace js {
 class MemoryTracingVisitor {
     JSTracer *trace_;
@@ -860,20 +978,26 @@ js::MemoryTracingVisitor::visitReference
         return;
       }
     }
 
     MOZ_ASSUME_UNREACHABLE("Invalid kind");
 }
 
 void
-TypeRepresentation::traceInstance(JSTracer *trace, uint8_t *mem)
+SizedTypeRepresentation::traceInstance(JSTracer *trace,
+                                       uint8_t *mem,
+                                       size_t length)
 {
     MemoryTracingVisitor visitor(trace);
-    visitReferences(this, mem, visitor);
+
+    for (size_t i = 0; i < length; i++) {
+        visitReferences(this, mem, visitor);
+        mem += size();
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // Misc
 
 const StructField *
 StructTypeRepresentation::fieldNamed(jsid id) const
 {
--- a/js/src/builtin/TypeRepresentation.h
+++ b/js/src/builtin/TypeRepresentation.h
@@ -61,158 +61,176 @@
 
 #include "builtin/TypedObjectConstants.h"
 #include "gc/Barrier.h"
 #include "js/HashTable.h"
 
 namespace js {
 
 class TypeRepresentation;
+class SizedTypeRepresentation;
 class ScalarTypeRepresentation;
 class ReferenceTypeRepresentation;
 class X4TypeRepresentation;
-class ArrayTypeRepresentation;
+class SizedArrayTypeRepresentation;
+class UnsizedArrayTypeRepresentation;
 class StructTypeRepresentation;
 
 struct Class;
 class StringBuffer;
 
 struct TypeRepresentationHasher
 {
     typedef TypeRepresentation *Lookup;
     static HashNumber hash(TypeRepresentation *key);
     static bool match(TypeRepresentation *key1, TypeRepresentation *key2);
 
   private:
     static HashNumber hashScalar(ScalarTypeRepresentation *key);
     static HashNumber hashReference(ReferenceTypeRepresentation *key);
     static HashNumber hashX4(X4TypeRepresentation *key);
     static HashNumber hashStruct(StructTypeRepresentation *key);
-    static HashNumber hashArray(ArrayTypeRepresentation *key);
+    static HashNumber hashUnsizedArray(UnsizedArrayTypeRepresentation *key);
+    static HashNumber hashSizedArray(SizedArrayTypeRepresentation *key);
 
     static bool matchScalars(ScalarTypeRepresentation *key1,
                              ScalarTypeRepresentation *key2);
     static bool matchReferences(ReferenceTypeRepresentation *key1,
                                 ReferenceTypeRepresentation *key2);
     static bool matchX4s(X4TypeRepresentation *key1,
                          X4TypeRepresentation *key2);
     static bool matchStructs(StructTypeRepresentation *key1,
                              StructTypeRepresentation *key2);
-    static bool matchArrays(ArrayTypeRepresentation *key1,
-                            ArrayTypeRepresentation *key2);
+    static bool matchSizedArrays(SizedArrayTypeRepresentation *key1,
+                                 SizedArrayTypeRepresentation *key2);
+    static bool matchUnsizedArrays(UnsizedArrayTypeRepresentation *key1,
+                                   UnsizedArrayTypeRepresentation *key2);
 };
 
 typedef js::HashSet<TypeRepresentation *,
                     TypeRepresentationHasher,
                     RuntimeAllocPolicy> TypeRepresentationHash;
 
 class TypeRepresentationHelper;
 
 class TypeRepresentation {
   public:
     enum Kind {
         Scalar = JS_TYPEREPR_SCALAR_KIND,
         Reference = JS_TYPEREPR_REFERENCE_KIND,
         X4 = JS_TYPEREPR_X4_KIND,
         Struct = JS_TYPEREPR_STRUCT_KIND,
-        Array = JS_TYPEREPR_ARRAY_KIND
+        SizedArray = JS_TYPEREPR_SIZED_ARRAY_KIND,
+        UnsizedArray = JS_TYPEREPR_UNSIZED_ARRAY_KIND,
     };
 
   protected:
-    TypeRepresentation(Kind kind, size_t size, size_t align, bool opaque);
+    TypeRepresentation(Kind kind, bool opaque);
 
     // in order to call addToTableOrFree()
     friend class TypeRepresentationHelper;
 
-    size_t size_;
-    size_t alignment_;
     Kind kind_;
     bool opaque_;
 
     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);
 
     HeapPtrObject ownerObject_;
     void traceFields(JSTracer *tracer);
 
   public:
-    size_t size() const { return size_; }
-    size_t alignment() const { return alignment_; }
     Kind kind() const { return kind_; }
     bool opaque() const { return opaque_; }
     bool transparent() const { return !opaque_; }
     JSObject *ownerObject() const { return ownerObject_.get(); }
 
     // Appends a stringified form of this type representation onto
     // buffer, for use in error messages and the like.
     bool appendString(JSContext *cx, StringBuffer &buffer);
 
-    // Initializes memory that contains an instance of this type
-    // with appropriate default values (typically 0).
-    void initInstance(const JSRuntime *rt, uint8_t *mem);
-
-    // Traces memory that contains an instance of this type.
-    void traceInstance(JSTracer *trace, uint8_t *mem);
-
     static bool isOwnerObject(JSObject &obj);
     static TypeRepresentation *fromOwnerObject(JSObject &obj);
 
+    static bool isSized(Kind kind) {
+        return kind > JS_TYPEREPR_MAX_UNSIZED_KIND;
+    }
+
+    bool isSized() const {
+        return isSized(kind());
+    }
+
+    inline SizedTypeRepresentation *asSized();
+
     bool isScalar() const {
         return kind() == Scalar;
     }
 
-    ScalarTypeRepresentation *asScalar() {
-        JS_ASSERT(isScalar());
-        return (ScalarTypeRepresentation*) this;
-    }
+    inline ScalarTypeRepresentation *asScalar();
 
     bool isReference() const {
         return kind() == Reference;
     }
 
-    ReferenceTypeRepresentation *asReference() {
-        JS_ASSERT(isReference());
-        return (ReferenceTypeRepresentation*) this;
-    }
+    inline ReferenceTypeRepresentation *asReference();
 
     bool isX4() const {
         return kind() == X4;
     }
 
-    X4TypeRepresentation *asX4() {
-        JS_ASSERT(isX4());
-        return (X4TypeRepresentation*) this;
+    inline X4TypeRepresentation *asX4();
+
+    bool isSizedArray() const {
+        return kind() == SizedArray;
     }
 
-    bool isArray() const {
-        return kind() == Array;
+    inline SizedArrayTypeRepresentation *asSizedArray();
+
+    bool isUnsizedArray() const {
+        return kind() == UnsizedArray;
     }
 
-    ArrayTypeRepresentation *asArray() {
-        JS_ASSERT(isArray());
-        return (ArrayTypeRepresentation*) this;
+    inline UnsizedArrayTypeRepresentation *asUnsizedArray();
+
+    bool isAnyArray() const {
+        return isSizedArray() || isUnsizedArray();
     }
 
     bool isStruct() const {
         return kind() == Struct;
     }
 
-    StructTypeRepresentation *asStruct() {
-        JS_ASSERT(isStruct());
-        return (StructTypeRepresentation*) this;
-    }
+    inline StructTypeRepresentation *asStruct();
 
     void mark(JSTracer *tracer);
 };
 
-class ScalarTypeRepresentation : public TypeRepresentation {
+class SizedTypeRepresentation : public TypeRepresentation {
+  protected:
+    SizedTypeRepresentation(Kind kind, bool opaque, size_t size, size_t align);
+
+    size_t size_;
+    size_t alignment_;
+
+  public:
+    size_t size() const { return size_; }
+    size_t alignment() const { return alignment_; }
+
+    // Initializes memory that contains `count` instances of this type
+    void initInstance(const JSRuntime *rt, uint8_t *mem, size_t count);
+
+    // Traces memory that contains `count` instances of this type.
+    void traceInstance(JSTracer *trace, uint8_t *mem, size_t count);
+};
+
+class ScalarTypeRepresentation : public SizedTypeRepresentation {
   public:
     // Must match order of JS_FOR_EACH_SCALAR_TYPE_REPR below
     enum Type {
         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,
@@ -268,17 +286,17 @@ class ScalarTypeRepresentation : public 
     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_)                                    \
     JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(macro_)                           \
     macro_(ScalarTypeRepresentation::TYPE_UINT8_CLAMPED, uint8_t, uint8Clamped)
 
-class ReferenceTypeRepresentation : public TypeRepresentation {
+class ReferenceTypeRepresentation : public SizedTypeRepresentation {
   public:
     // Must match order of JS_FOR_EACH_REFERENCE_TYPE_REPR below
     enum Type {
         TYPE_ANY = JS_REFERENCETYPEREPR_ANY,
         TYPE_OBJECT = JS_REFERENCETYPEREPR_OBJECT,
         TYPE_STRING = JS_REFERENCETYPEREPR_STRING,
     };
     static const int32_t TYPE_MAX = TYPE_STRING + 1;
@@ -307,17 +325,17 @@ class ReferenceTypeRepresentation : publ
     static JSObject *Create(JSContext *cx, Type type);
 };
 
 #define JS_FOR_EACH_REFERENCE_TYPE_REPR(macro_)                             \
     macro_(ReferenceTypeRepresentation::TYPE_ANY,    HeapValue, Any)        \
     macro_(ReferenceTypeRepresentation::TYPE_OBJECT, HeapPtrObject, Object) \
     macro_(ReferenceTypeRepresentation::TYPE_STRING, HeapPtrString, string)
 
-class X4TypeRepresentation : public TypeRepresentation {
+class X4TypeRepresentation : public SizedTypeRepresentation {
   public:
     enum Type {
         TYPE_INT32 = JS_X4TYPEREPR_INT32,
         TYPE_FLOAT32 = JS_X4TYPEREPR_FLOAT32,
     };
 
   private:
     // so TypeRepresentation can call appendStringScalar() etc
@@ -341,60 +359,84 @@ class X4TypeRepresentation : public Type
     static JSObject *Create(JSContext *cx, Type type);
 };
 
 // Must be in same order as the enum ScalarTypeRepresentation::Type:
 #define JS_FOR_EACH_X4_TYPE_REPR(macro_)                                      \
     macro_(X4TypeRepresentation::TYPE_INT32, int32_t, int32)                  \
     macro_(X4TypeRepresentation::TYPE_FLOAT32, float, float32)
 
-class ArrayTypeRepresentation : public TypeRepresentation {
+class UnsizedArrayTypeRepresentation : public TypeRepresentation {
   private:
     // so TypeRepresentation can call appendStringArray() etc
     friend class TypeRepresentation;
 
-    TypeRepresentation *element_;
-    size_t length_;
+    SizedTypeRepresentation *element_;
 
-    ArrayTypeRepresentation(TypeRepresentation *element,
-                            size_t length);
+    UnsizedArrayTypeRepresentation(SizedTypeRepresentation *element);
 
     // See TypeRepresentation::traceFields()
-    void traceArrayFields(JSTracer *trace);
+    void traceUnsizedArrayFields(JSTracer *trace);
 
     // See TypeRepresentation::appendString()
-    bool appendStringArray(JSContext *cx, StringBuffer &buffer);
+    bool appendStringUnsizedArray(JSContext *cx, StringBuffer &buffer);
 
   public:
-    TypeRepresentation *element() {
+    SizedTypeRepresentation *element() {
+        return element_;
+    }
+
+    static JSObject *Create(JSContext *cx,
+                            SizedTypeRepresentation *elementTypeRepr);
+};
+
+class SizedArrayTypeRepresentation : public SizedTypeRepresentation {
+  private:
+    // so TypeRepresentation can call appendStringSizedArray() etc
+    friend class TypeRepresentation;
+
+    SizedTypeRepresentation *element_;
+    size_t length_;
+
+    SizedArrayTypeRepresentation(SizedTypeRepresentation *element,
+                                 size_t length);
+
+    // See TypeRepresentation::traceFields()
+    void traceSizedArrayFields(JSTracer *trace);
+
+    // See TypeRepresentation::appendString()
+    bool appendStringSizedArray(JSContext *cx, StringBuffer &buffer);
+
+  public:
+    SizedTypeRepresentation *element() {
         return element_;
     }
 
     size_t length() {
         return length_;
     }
 
     static JSObject *Create(JSContext *cx,
-                            TypeRepresentation *elementTypeRepr,
+                            SizedTypeRepresentation *elementTypeRepr,
                             size_t length);
 };
 
 struct StructField {
     size_t index;
     HeapId id;
-    TypeRepresentation *typeRepr;
+    SizedTypeRepresentation *typeRepr;
     size_t offset;
 
     explicit StructField(size_t index,
                          jsid &id,
-                         TypeRepresentation *typeRepr,
+                         SizedTypeRepresentation *typeRepr,
                          size_t offset);
 };
 
-class StructTypeRepresentation : public TypeRepresentation {
+class StructTypeRepresentation : public SizedTypeRepresentation {
   private:
     // so TypeRepresentation can call appendStringStruct() etc
     friend class TypeRepresentation;
 
     size_t fieldCount_;
 
     // StructTypeRepresentations are allocated with extra space to
     // store the contents of the fields array.
@@ -423,16 +465,65 @@ class StructTypeRepresentation : public 
 
     const StructField &field(size_t i) const {
         JS_ASSERT(i < fieldCount());
         return fields()[i];
     }
 
     const StructField *fieldNamed(jsid id) const;
 
+    // Creates a struct type containing fields with names from `ids`
+    // and types from `typeReprOwners`. The latter should be the owner
+    // objects of a set of sized type representations.
     static JSObject *Create(JSContext *cx,
                             AutoIdVector &ids,
                             AutoObjectVector &typeReprOwners);
 };
 
+// Definitions of the casting methods. These are pulled out of the
+// main class definition because both the super- and subtypes must be
+// defined for C++ to permit the static_cast.
+
+SizedTypeRepresentation *
+TypeRepresentation::asSized() {
+    JS_ASSERT(isSized());
+    return static_cast<SizedTypeRepresentation*>(this);
+}
+
+ScalarTypeRepresentation *
+TypeRepresentation::asScalar() {
+    JS_ASSERT(isScalar());
+    return static_cast<ScalarTypeRepresentation*>(this);
+}
+
+ReferenceTypeRepresentation *
+TypeRepresentation::asReference() {
+    JS_ASSERT(isReference());
+    return static_cast<ReferenceTypeRepresentation*>(this);
+}
+
+X4TypeRepresentation *
+TypeRepresentation::asX4() {
+    JS_ASSERT(isX4());
+    return static_cast<X4TypeRepresentation*>(this);
+}
+
+SizedArrayTypeRepresentation *
+TypeRepresentation::asSizedArray() {
+    JS_ASSERT(isSizedArray());
+    return static_cast<SizedArrayTypeRepresentation*>(this);
+}
+
+UnsizedArrayTypeRepresentation *
+TypeRepresentation::asUnsizedArray() {
+    JS_ASSERT(isUnsizedArray());
+    return static_cast<UnsizedArrayTypeRepresentation*>(this);
+}
+
+StructTypeRepresentation *
+TypeRepresentation::asStruct() {
+    JS_ASSERT(isStruct());
+    return static_cast<StructTypeRepresentation*>(this);
+}
+
 } // namespace js
 
 #endif
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -48,68 +48,64 @@ static const JSFunctionSpec TypedObjectM
 
 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
-ReportCannotConvertTo(JSContext *cx, HandleObject fromObject, const char *toType)
-{
-    RootedValue fromValue(cx, ObjectValue(*fromObject));
-    ReportCannotConvertTo(cx, fromValue, toType);
-}
-
-static void
-ReportCannotConvertTo(JSContext *cx, HandleValue fromValue, HandleString toType)
-{
-    const char *fnName = JS_EncodeString(cx, toType);
-    if (!fnName)
-        return;
-    ReportCannotConvertTo(cx, fromValue, fnName);
-    JS_free(cx, (void *) fnName);
-}
-
-static void
-ReportCannotConvertTo(JSContext *cx, HandleValue fromValue, TypeRepresentation *toType)
-{
-    StringBuffer contents(cx);
-    if (!toType->appendString(cx, contents))
-        return;
-
-    RootedString result(cx, contents.finishString());
-    if (!result)
-        return;
-
-    ReportCannotConvertTo(cx, fromValue, result);
-}
-
-static int32_t
-Clamp(int32_t value, int32_t min, int32_t max)
-{
-    JS_ASSERT(min < max);
-    if (value < min)
-        return min;
-    if (value > max)
-        return max;
-    return value;
-}
-
 static inline JSObject *
 ToObjectIfObject(HandleValue value)
 {
     if (!value.isObject())
         return nullptr;
 
     return &value.toObject();
 }
 
 static inline bool
+IsTypedDatum(JSObject &obj)
+{
+    return obj.is<TypedObject>() || obj.is<TypedHandle>();
+}
+
+static inline uint8_t *
+TypedMem(JSObject &val)
+{
+    JS_ASSERT(IsTypedDatum(val));
+    return (uint8_t*) val.getPrivate();
+}
+
+static inline bool IsTypeObject(JSObject &type);
+
+/*
+ * Given a user-visible type descriptor object, returns the
+ * owner object for the TypeRepresentation* that we use internally.
+ */
+static JSObject *
+typeRepresentationOwnerObj(JSObject &typeObj)
+{
+    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(JSObject &typeObj)
+{
+    return TypeRepresentation::fromOwnerObject(*typeRepresentationOwnerObj(typeObj));
+}
+
+static inline bool
 IsScalarTypeObject(JSObject &type)
 {
     return type.hasClass(&ScalarType::class_);
 }
 
 static inline bool
 IsReferenceTypeObject(JSObject &type)
 {
@@ -151,49 +147,31 @@ IsTypeObject(JSObject &type)
 {
     return IsScalarTypeObject(type) ||
            IsReferenceTypeObject(type) ||
            IsX4TypeObject(type) ||
            IsComplexTypeObject(type);
 }
 
 static inline bool
-IsTypedDatum(JSObject &obj)
+IsTypeObjectOfKind(JSObject &type, TypeRepresentation::Kind kind)
 {
-    return obj.is<TypedObject>() || obj.is<TypedHandle>();
-}
-
-static inline uint8_t *
-TypedMem(JSObject &val)
-{
-    JS_ASSERT(IsTypedDatum(val));
-    return (uint8_t*) val.getPrivate();
+    if (!IsTypeObject(type))
+        return false;
+
+    return typeRepresentation(type)->kind() == kind;
 }
 
-/*
- * Given a user-visible type descriptor object, returns the
- * owner object for the TypeRepresentation* that we use internally.
- */
-static JSObject *
-typeRepresentationOwnerObj(JSObject &typeObj)
+static inline bool
+IsSizedTypeObject(JSObject &type)
 {
-    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(JSObject &typeObj)
-{
-    return TypeRepresentation::fromOwnerObject(*typeRepresentationOwnerObj(typeObj));
+    if (!IsTypeObject(type))
+        return false;
+
+    return typeRepresentation(type)->isSized();
 }
 
 static inline JSObject *
 GetType(JSObject &datum)
 {
     JS_ASSERT(IsTypedDatum(datum));
     return &datum.getReservedSlot(JS_DATUM_SLOT_TYPE_OBJ).toObject();
 }
@@ -318,34 +296,83 @@ Reify(JSContext *cx,
 
     to.set(args.rval());
     return true;
 }
 
 static inline size_t
 TypeSize(JSObject &type)
 {
-    return typeRepresentation(type)->size();
+    return typeRepresentation(type)->asSized()->size();
+}
+
+static inline size_t
+DatumLength(JSObject &val)
+{
+    JS_ASSERT(IsTypedDatum(val));
+    JS_ASSERT(typeRepresentation(*GetType(val))->isAnyArray());
+    return val.getReservedSlot(JS_DATUM_SLOT_LENGTH).toInt32();
 }
 
 static inline size_t
 DatumSize(JSObject &val)
 {
-    return TypeSize(*GetType(val));
+    JSObject *typeObj = GetType(val);
+    TypeRepresentation *typeRepr = typeRepresentation(*typeObj);
+    switch (typeRepr->kind()) {
+      case TypeRepresentation::Scalar:
+      case TypeRepresentation::X4:
+      case TypeRepresentation::Reference:
+      case TypeRepresentation::Struct:
+      case TypeRepresentation::SizedArray:
+        return typeRepr->asSized()->size();
+
+      case TypeRepresentation::UnsizedArray:
+        return typeRepr->asUnsizedArray()->element()->size() * DatumLength(val);
+    }
+    MOZ_ASSUME_UNREACHABLE("unhandled typerepresentation kind");
 }
 
 static inline bool
 IsTypedDatumOfKind(JSObject &obj, TypeRepresentation::Kind kind)
 {
     if (!IsTypedDatum(obj))
         return false;
     TypeRepresentation *repr = typeRepresentation(*GetType(obj));
     return repr->kind() == kind;
 }
 
+static inline bool
+IsArrayTypedDatum(JSObject &obj)
+{
+    if (!IsTypedDatum(obj))
+        return false;
+    TypeRepresentation *repr = typeRepresentation(*GetType(obj));
+    return repr->isAnyArray();
+}
+
+// Extracts the `prototype` property from `obj`, throwing if it is
+// missing or not an object.
+static JSObject *
+GetPrototype(JSContext *cx, HandleObject obj)
+{
+    RootedValue prototypeVal(cx);
+    if (!JSObject::getProperty(cx, obj, obj, cx->names().prototype,
+                               &prototypeVal))
+    {
+        return nullptr;
+    }
+    if (!prototypeVal.isObject()) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
+                             JSMSG_INVALID_PROTOTYPE);
+        return nullptr;
+    }
+    return &prototypeVal.toObject();
+}
+
 /***************************************************************************
  * Scalar type objects
  *
  * Scalar type objects like `uint8`, `uint16`, are all instances of
  * the ScalarType class. Like all type objects, they have a reserved
  * slot pointing to a TypeRepresentation object, which is used to
  * distinguish which scalar type object this actually is.
  */
@@ -366,16 +393,17 @@ const Class js::ScalarType::class_ = {
     nullptr,
     nullptr,
     nullptr
 };
 
 const JSFunctionSpec js::ScalarType::typeObjectMethods[] = {
     JS_FN("toSource", TypeObjectToSource, 0, 0),
     {"handle", {nullptr, nullptr}, 2, 0, "HandleCreate"},
+    {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
     {"equivalent", {nullptr, nullptr}, 1, 0, "TypeObjectEquivalent"},
     JS_FS_END
 };
 
 template <typename T>
 static T ConvertScalar(double d)
 {
     if (TypeIsFloatingPoint<T>()) {
@@ -450,16 +478,17 @@ const Class js::ReferenceType::class_ = 
     nullptr,
     nullptr,
     nullptr
 };
 
 const JSFunctionSpec js::ReferenceType::typeObjectMethods[] = {
     JS_FN("toSource", TypeObjectToSource, 0, 0),
     {"handle", {nullptr, nullptr}, 2, 0, "HandleCreate"},
+    {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
     {"equivalent", {nullptr, nullptr}, 1, 0, "TypeObjectEquivalent"},
     JS_FS_END
 };
 
 bool
 js::ReferenceType::call(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -537,41 +566,25 @@ js::ReferenceType::call(JSContext *cx, u
  * (And, again, same with respect to s and S.)
  *
  * This function creates the A.prototype/S.prototype object.  It takes
  * as an argument either the TypedObject.ArrayType or the
  * TypedObject.StructType constructor function, then returns an empty
  * object with the .prototype.prototype object as its [[Prototype]].
  */
 static JSObject *
-CreateComplexTypeInstancePrototype(JSContext *cx,
-                                   HandleObject typeObjectCtor)
+CreatePrototypeObjectForComplexTypeInstance(JSContext *cx,
+                                            HandleObject ctorPrototype)
 {
-    RootedValue ctorPrototypeVal(cx);
-    if (!JSObject::getProperty(cx, typeObjectCtor, typeObjectCtor,
-                               cx->names().prototype,
-                               &ctorPrototypeVal))
-    {
+    RootedObject ctorPrototypePrototype(cx, GetPrototype(cx, ctorPrototype));
+    if (!ctorPrototypePrototype)
         return nullptr;
-    }
-    JS_ASSERT(ctorPrototypeVal.isObject()); // immutable binding
-    RootedObject ctorPrototypeObj(cx, &ctorPrototypeVal.toObject());
-
-    RootedValue ctorPrototypePrototypeVal(cx);
-    if (!JSObject::getProperty(cx, ctorPrototypeObj, ctorPrototypeObj,
-                               cx->names().prototype,
-                               &ctorPrototypePrototypeVal))
-    {
-        return nullptr;
-    }
-
-    JS_ASSERT(ctorPrototypePrototypeVal.isObject()); // immutable binding
-    RootedObject proto(cx, &ctorPrototypePrototypeVal.toObject());
-    return NewObjectWithGivenProto(cx, &JSObject::class_, proto,
-                                   &typeObjectCtor->global());
+
+    return NewObjectWithGivenProto(cx, &JSObject::class_,
+                                   &*ctorPrototypePrototype, nullptr);
 }
 
 const Class ArrayType::class_ = {
     "ArrayType",
     JSCLASS_HAS_RESERVED_SLOTS(JS_TYPEOBJ_ARRAY_SLOTS),
     JS_PropertyStub,
     JS_DeletePropertyStub,
     JS_PropertyStub,
@@ -588,384 +601,254 @@ const Class ArrayType::class_ = {
 };
 
 const JSPropertySpec ArrayType::typeObjectProperties[] = {
     JS_PS_END
 };
 
 const JSFunctionSpec ArrayType::typeObjectMethods[] = {
     {"handle", {nullptr, nullptr}, 2, 0, "HandleCreate"},
-    JS_FN("repeat", ArrayType::repeat, 1, 0),
+    {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
+    JS_FN("dimension", ArrayType::dimension, 1, 0),
     JS_FN("toSource", TypeObjectToSource, 0, 0),
     {"equivalent", {nullptr, nullptr}, 1, 0, "TypeObjectEquivalent"},
     JS_FS_END
 };
 
 const JSPropertySpec ArrayType::typedObjectProperties[] = {
     JS_PS_END
 };
 
 const JSFunctionSpec ArrayType::typedObjectMethods[] = {
-    JS_FN("subarray", ArrayType::subarray, 2, 0),
     {"forEach", {nullptr, nullptr}, 1, 0, "ArrayForEach"},
     {"redimension", {nullptr, nullptr}, 1, 0, "TypedArrayRedimension"},
     JS_FS_END
 };
 
 static JSObject *
-ArrayElementType(HandleObject array)
-{
-    JS_ASSERT(IsArrayTypeObject(*array));
-    return &array->getReservedSlot(JS_TYPEOBJ_SLOT_ARRAY_ELEM_TYPE).toObject();
-}
-
-static bool
-FillTypedArrayWithValue(JSContext *cx, HandleObject array, HandleValue val)
-{
-    JS_ASSERT(IsTypedDatumOfKind(*array, TypeRepresentation::Array));
-
-    RootedFunction func(
-        cx, SelfHostedFunction(cx, cx->names().FillTypedArrayWithValue));
-    if (!func)
-        return false;
-
-    InvokeArgs args(cx);
-    if (!args.init(2))
-        return false;
-
-    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 || !IsArrayTypeObject(*thisObj)) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
-                             JSMSG_INCOMPATIBLE_PROTO,
-                             "ArrayType", "repeat",
-                             InformalValueTypeName(args.thisv()));
-        return false;
-    }
-
-    RootedObject binaryArray(cx, TypedObject::createZeroed(cx, thisObj));
-    if (!binaryArray)
-        return false;
-
-    RootedValue val(cx, args[0]);
-    if (!FillTypedArrayWithValue(cx, binaryArray, val))
-        return false;
-
-    args.rval().setObject(*binaryArray);
-    return true;
-}
-
-/**
- * The subarray function first creates an ArrayType instance
- * which will act as the elementType for the subarray.
- *
- * var MA = new ArrayType(elementType, 10);
- * var mb = MA.repeat(val);
- *
- * mb.subarray(begin, end=mb.length) => (Only for +ve)
- *     var internalSA = new ArrayType(elementType, end-begin);
- *     var ret = new internalSA()
- *     for (var i = begin; i < end; i++)
- *         ret[i-begin] = ret[i]
- *     return ret
- *
- * The range specified by the begin and end values is clamped to the valid
- * index range for the current array. If the computed length of the new
- * TypedArray would be negative, it is clamped to zero.
- * see: http://www.khronos.org/registry/typedarray/specs/latest/#7
- *
- */
-/* static */ bool
-ArrayType::subarray(JSContext *cx, unsigned int argc, Value *vp)
+ArrayElementType(JSObject &array)
 {
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    if (args.length() < 1) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage,
-                             nullptr, JSMSG_MORE_ARGS_NEEDED,
-                             "subarray()", "0", "s");
-        return false;
-    }
-
-    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 (!IsTypedDatumOfKind(*thisObj, TypeRepresentation::Array)) {
-        ReportCannotConvertTo(cx, thisObj, "binary array");
-        return false;
-    }
-
-    RootedObject type(cx, GetType(*thisObj));
-    ArrayTypeRepresentation *typeRepr = typeRepresentation(*type)->asArray();
-    size_t length = typeRepr->length();
-
-    int32_t begin = args[0].toInt32();
-    int32_t end = length;
-
-    if (args.length() >= 2) {
-        if (!args[1].isInt32()) {
-            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
-                    JSMSG_TYPEDOBJECT_SUBARRAY_INTEGER_ARG, "2");
-            return false;
-        }
-
-        end = args[1].toInt32();
-    }
-
-    if (begin < 0)
-        begin = length + begin;
-    if (end < 0)
-        end = length + end;
-
-    begin = Clamp(begin, 0, length);
-    end = Clamp(end, 0, length);
-
-    int32_t sublength = end - begin; // end exclusive
-    sublength = Clamp(sublength, 0, length);
-
-    RootedObject globalObj(cx, cx->compartment()->maybeGlobal());
-    JS_ASSERT(globalObj);
-    Rooted<GlobalObject*> global(cx, &globalObj->as<GlobalObject>());
-    RootedObject arrayTypeGlobal(cx, global->getArrayType(cx));
-
-    RootedObject elementType(cx, ArrayElementType(type));
-    RootedObject subArrayType(cx, ArrayType::create(cx, arrayTypeGlobal,
-                                                    elementType, sublength));
-    if (!subArrayType)
-        return false;
-
-    int32_t elementSize = typeRepr->element()->size();
-    size_t offset = elementSize * begin;
-
-    RootedObject subarray(
-        cx, TypedDatum::createDerived(cx, subArrayType, thisObj, offset));
-    if (!subarray)
-        return false;
-
-    args.rval().setObject(*subarray);
-    return true;
-}
-
-static bool
-ArrayFillSubarray(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,
-                             "fill()", "0", "s");
-        return false;
-    }
-
-    RootedObject thisObj(cx, ToObjectIfObject(args.thisv()));
-    if (!thisObj || !IsTypedDatumOfKind(*thisObj, TypeRepresentation::Array)) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage,
-                             nullptr, JSMSG_INCOMPATIBLE_PROTO,
-                             "ArrayType", "fill",
-                             InformalValueTypeName(args.thisv()));
-        return false;
-    }
-
-    Value funArrayTypeVal = GetFunctionNativeReserved(&args.callee(), 0);
-    JS_ASSERT(funArrayTypeVal.isObject());
-
-    RootedObject type(cx, GetType(*thisObj));
-    TypeRepresentation *typeRepr = typeRepresentation(*type);
-    RootedObject funArrayType(cx, &funArrayTypeVal.toObject());
-    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 FillTypedArrayWithValue(cx, thisObj, val);
+    JS_ASSERT(IsArrayTypeObject(array));
+    return &array.getReservedSlot(JS_TYPEOBJ_SLOT_ARRAY_ELEM_TYPE).toObject();
 }
 
 static bool
 InitializeCommonTypeDescriptorProperties(JSContext *cx,
                                          HandleObject obj,
                                          HandleObject typeReprOwnerObj)
 {
     TypeRepresentation *typeRepr =
         TypeRepresentation::fromOwnerObject(*typeReprOwnerObj);
 
-    if (typeRepr->transparent()) {
+    if (typeRepr->transparent() && typeRepr->isSized()) {
+        SizedTypeRepresentation *sizedTypeRepr = typeRepr->asSized();
+
         // byteLength
-        RootedValue typeByteLength(cx, NumberValue(typeRepr->size()));
+        RootedValue typeByteLength(cx, NumberValue(sizedTypeRepr->size()));
         if (!JSObject::defineProperty(cx, obj, cx->names().byteLength,
                                       typeByteLength,
                                       nullptr, nullptr,
                                       JSPROP_READONLY | JSPROP_PERMANENT))
         {
             return false;
         }
 
         // byteAlignment
-        RootedValue typeByteAlignment(cx, NumberValue(typeRepr->alignment()));
+        RootedValue typeByteAlignment(
+            cx, NumberValue(sizedTypeRepr->alignment()));
         if (!JSObject::defineProperty(cx, obj, cx->names().byteAlignment,
                                       typeByteAlignment,
                                       nullptr, nullptr,
                                       JSPROP_READONLY | JSPROP_PERMANENT))
         {
             return false;
         }
+    } else {
+        RootedValue undef(cx, UndefinedValue());
+
+        // byteLength
+        if (!JSObject::defineProperty(cx, obj, cx->names().byteLength,
+                                      undef,
+                                      nullptr, nullptr,
+                                      JSPROP_READONLY | JSPROP_PERMANENT))
+        {
+            return false;
+        }
+
+        // byteAlignment
+        if (!JSObject::defineProperty(cx, obj, cx->names().byteAlignment,
+                                      undef,
+                                      nullptr, nullptr,
+                                      JSPROP_READONLY | JSPROP_PERMANENT))
+        {
+            return false;
+        }
     }
 
-    // variable -- always false since we do not yet support variable-size types
-    RootedValue variable(cx, JSVAL_FALSE);
+    // variable -- true for unsized arrays
+    RootedValue variable(cx, BooleanValue(!typeRepr->isSized()));
     if (!JSObject::defineProperty(cx, obj, cx->names().variable,
                                   variable,
                                   nullptr, nullptr,
                                   JSPROP_READONLY | JSPROP_PERMANENT))
     {
         return false;
     }
 
     return true;
 }
 
 JSObject *
 ArrayType::create(JSContext *cx,
-                  HandleObject metaTypeObject,
-                  HandleObject elementType,
-                  size_t length)
+                  HandleObject arrayTypePrototype,
+                  HandleObject arrayTypeReprObj,
+                  HandleObject elementType)
 {
-    JS_ASSERT(elementType);
+    JS_ASSERT(TypeRepresentation::isOwnerObject(*arrayTypeReprObj));
     JS_ASSERT(IsTypeObject(*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,
-                               &prototypeVal))
-    {
-        return nullptr;
-    }
-    JS_ASSERT(prototypeVal.isObject()); // immutable binding
-
     RootedObject obj(
-        cx, NewObjectWithGivenProto(cx, &ArrayType::class_,
-                                    &prototypeVal.toObject(), cx->global()));
+        cx, NewObjectWithClassProto(cx, &ArrayType::class_,
+                                    arrayTypePrototype, nullptr));
     if (!obj)
         return nullptr;
-    obj->initReservedSlot(JS_TYPEOBJ_SLOT_TYPE_REPR, ObjectValue(*typeReprObj));
+    obj->initReservedSlot(JS_TYPEOBJ_SLOT_TYPE_REPR,
+                          ObjectValue(*arrayTypeReprObj));
 
     RootedValue elementTypeVal(cx, ObjectValue(*elementType));
     if (!JSObject::defineProperty(cx, obj, cx->names().elementType,
                                   elementTypeVal, nullptr, nullptr,
                                   JSPROP_READONLY | JSPROP_PERMANENT))
         return nullptr;
 
     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))
+    if (!InitializeCommonTypeDescriptorProperties(cx, obj, arrayTypeReprObj))
         return nullptr;
 
     RootedObject prototypeObj(
-        cx, CreateComplexTypeInstancePrototype(cx, metaTypeObject));
+        cx, CreatePrototypeObjectForComplexTypeInstance(cx, arrayTypePrototype));
     if (!prototypeObj)
         return nullptr;
 
     if (!LinkConstructorAndPrototype(cx, obj, prototypeObj))
         return nullptr;
 
-    JSFunction *fillFun = DefineFunctionWithReserved(cx, prototypeObj, "fill", ArrayFillSubarray, 1, 0);
-    if (!fillFun)
-        return nullptr;
-
-    // This is important
-    // so that A.prototype.fill.call(b, val)
-    // where b.type != A raises an error
-    SetFunctionNativeReserved(fillFun, 0, ObjectValue(*obj));
-
     return obj;
 }
 
 bool
 ArrayType::construct(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (!args.isConstructing()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                              JSMSG_NOT_FUNCTION, "ArrayType");
         return false;
     }
 
-    if (args.length() != 2 ||
-        !args[0].isObject() ||
-        !args[1].isNumber() ||
-        args[1].toNumber() < 0)
+    RootedObject arrayTypeGlobal(cx, &args.callee());
+
+    // Expect one argument which is a sized type object
+    if (args.length() < 1) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
+                             "ArrayType", "0", "");
+        return false;
+    }
+
+    if (!args[0].isObject() || !IsSizedTypeObject(args[0].toObject())) {
+        ReportCannotConvertTo(cx, args[0], "ArrayType element specifier");
+        return false;
+    }
+
+    RootedObject elementType(cx, &args[0].toObject());
+    SizedTypeRepresentation *elementTypeRepr =
+        typeRepresentation(*elementType)->asSized();
+
+    // construct the type repr
+    RootedObject arrayTypeReprObj(
+        cx, UnsizedArrayTypeRepresentation::Create(cx, elementTypeRepr));
+    if (!arrayTypeReprObj)
+        return false;
+
+    // Extract ArrayType.prototype
+    RootedObject arrayTypePrototype(cx, GetPrototype(cx, arrayTypeGlobal));
+    if (!arrayTypePrototype)
+        return nullptr;
+
+    // Create the instance of ArrayType
+    RootedObject obj(
+        cx, create(cx, arrayTypePrototype, arrayTypeReprObj, elementType));
+    if (!obj)
+        return false;
+
+    // Add `length` property, which is undefined for an unsized array.
+    RootedValue lengthVal(cx, UndefinedValue());
+    if (!JSObject::defineProperty(cx, obj, cx->names().length,
+                                  lengthVal, nullptr, nullptr,
+                                  JSPROP_READONLY | JSPROP_PERMANENT))
+        return nullptr;
+
+    args.rval().setObject(*obj);
+    return true;
+}
+
+/*static*/ bool
+ArrayType::dimension(JSContext *cx, unsigned int argc, jsval *vp)
+{
+    // Expect that the `this` pointer is an unsized array type
+    // and the first argument is an integer size.
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (args.length() != 1 ||
+        !args.thisv().isObject() ||
+        !IsTypeObjectOfKind(args.thisv().toObject(), TypeRepresentation::UnsizedArray) ||
+        !args[0].isInt32() ||
+        args[0].toInt32() < 0)
     {
         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 (!IsTypeObject(*elementType)) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
-                             JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
+    // Extract arguments.
+    RootedObject unsizedTypeObj(cx, &args.thisv().toObject());
+    UnsizedArrayTypeRepresentation *unsizedTypeRepr =
+        typeRepresentation(*unsizedTypeObj)->asUnsizedArray();
+    int32_t length = args[0].toInt32();
+
+    // Create sized type representation.
+    RootedObject sizedTypeReprObj(
+        cx, SizedArrayTypeRepresentation::Create(
+            cx, unsizedTypeRepr->element(), length));
+    if (!sizedTypeReprObj)
         return false;
-    }
-
-    if (!args[1].isInt32()) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
-                             JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
-        return false;
-    }
-
-    int32_t length = args[1].toInt32();
-    if (length < 0) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
-                             JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
-        return false;
-    }
-
-    RootedObject obj(cx, create(cx, arrayTypeGlobal, elementType, length));
+
+    // Create the sized type object.
+    RootedObject elementType(cx, ArrayElementType(*unsizedTypeObj));
+    RootedObject obj(
+        cx, create(cx, unsizedTypeObj, sizedTypeReprObj, elementType));
     if (!obj)
         return false;
+
+    // Add `length` property.
+    RootedValue lengthVal(cx, Int32Value(length));
+    if (!JSObject::defineProperty(cx, obj, cx->names().length,
+                                  lengthVal, nullptr, nullptr,
+                                  JSPROP_READONLY | JSPROP_PERMANENT))
+        return nullptr;
+
+    // Add `unsized` property, which is a link from the sized
+    // array to the unsized array.
+    RootedValue unsizedTypeObjValue(cx, ObjectValue(*unsizedTypeObj));
+    if (!JSObject::defineProperty(cx, obj, cx->names().unsized,
+                                  unsizedTypeObjValue, nullptr, nullptr,
+                                  JSPROP_READONLY | JSPROP_PERMANENT))
+        return nullptr;
+
     args.rval().setObject(*obj);
     return true;
 }
 
 /*********************************
  * StructType class
  */
 
@@ -989,16 +872,17 @@ const Class StructType::class_ = {
 };
 
 const JSPropertySpec StructType::typeObjectProperties[] = {
     JS_PS_END
 };
 
 const JSFunctionSpec StructType::typeObjectMethods[] = {
     {"handle", {nullptr, nullptr}, 2, 0, "HandleCreate"},
+    {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
     JS_FN("toSource", TypeObjectToSource, 0, 0),
     {"equivalent", {nullptr, nullptr}, 1, 0, "TypeObjectEquivalent"},
     JS_FS_END
 };
 
 const JSPropertySpec StructType::typedObjectProperties[] = {
     JS_PS_END
 };
@@ -1033,17 +917,17 @@ 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 || !IsTypeObject(*fieldType)) {
+        if (!fieldType || !IsSizedTypeObject(*fieldType)) {
             ReportCannotConvertTo(cx, fieldTypeVal, "StructType field specifier");
             return false;
         }
 
         if (!fieldTypeObjs.append(fieldTypeVal)) {
             js_ReportOutOfMemory(cx);
             return false;
         }
@@ -1129,45 +1013,43 @@ StructType::layout(JSContext *cx, Handle
                                   fieldTypesValue, nullptr, nullptr,
                                   JSPROP_READONLY | JSPROP_PERMANENT))
         return false;
 
     return true;
 }
 
 JSObject *
-StructType::create(JSContext *cx, HandleObject metaTypeObject,
+StructType::create(JSContext *cx,
+                   HandleObject metaTypeObject,
                    HandleObject fields)
 {
-    RootedValue prototypeVal(cx);
-    if (!JSObject::getProperty(cx, metaTypeObject, metaTypeObject,
-                               cx->names().prototype,
-                               &prototypeVal))
+    RootedObject structTypePrototype(cx, GetPrototype(cx, metaTypeObject));
+    if (!structTypePrototype)
         return nullptr;
-    JS_ASSERT(prototypeVal.isObject()); // immutable binding
 
     RootedObject obj(
-        cx, NewObjectWithGivenProto(cx, &StructType::class_,
-                                    &prototypeVal.toObject(), cx->global()));
+        cx, NewObjectWithClassProto(cx, &StructType::class_,
+                                    structTypePrototype, nullptr));
     if (!obj)
         return nullptr;
 
     if (!StructType::layout(cx, obj, fields))
         return nullptr;
 
     RootedObject fieldsProto(cx);
     if (!JSObject::getProto(cx, fields, &fieldsProto))
         return nullptr;
 
     RootedObject typeReprObj(cx, typeRepresentationOwnerObj(*obj));
     if (!InitializeCommonTypeDescriptorProperties(cx, obj, typeReprObj))
         return nullptr;
 
     RootedObject prototypeObj(
-        cx, CreateComplexTypeInstancePrototype(cx, metaTypeObject));
+        cx, CreatePrototypeObjectForComplexTypeInstance(cx, structTypePrototype));
     if (!prototypeObj)
         return nullptr;
 
     if (!LinkConstructorAndPrototype(cx, obj, prototypeObj))
         return nullptr;
 
     return obj;
 }
@@ -1305,40 +1187,58 @@ const Class X4Type::class_ = {
     nullptr
 };
 
 // These classes just exist to group together various properties and so on.
 namespace js {
 class Int32x4Defn {
   public:
     static const X4TypeRepresentation::Type type = X4TypeRepresentation::TYPE_INT32;
+    static const JSFunctionSpec TypeDescriptorMethods[];
     static const JSPropertySpec TypedObjectProperties[];
     static const JSFunctionSpec TypedObjectMethods[];
 };
 class Float32x4Defn {
   public:
     static const X4TypeRepresentation::Type type = X4TypeRepresentation::TYPE_FLOAT32;
+    static const JSFunctionSpec TypeDescriptorMethods[];
     static const JSPropertySpec TypedObjectProperties[];
     static const JSFunctionSpec TypedObjectMethods[];
 };
 } // namespace js
 
+const JSFunctionSpec js::Int32x4Defn::TypeDescriptorMethods[] = {
+    JS_FN("toSource", TypeObjectToSource, 0, 0),
+    {"handle", {nullptr, nullptr}, 2, 0, "HandleCreate"},
+    {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
+    {"equivalent", {nullptr, nullptr}, 1, 0, "TypeObjectEquivalent"},
+    JS_FS_END
+};
+
 const JSPropertySpec js::Int32x4Defn::TypedObjectProperties[] = {
     JS_SELF_HOSTED_GET("x", "Int32x4Lane0", JSPROP_PERMANENT),
     JS_SELF_HOSTED_GET("y", "Int32x4Lane1", JSPROP_PERMANENT),
     JS_SELF_HOSTED_GET("z", "Int32x4Lane2", JSPROP_PERMANENT),
     JS_SELF_HOSTED_GET("w", "Int32x4Lane3", JSPROP_PERMANENT),
     JS_PS_END
 };
 
 const JSFunctionSpec js::Int32x4Defn::TypedObjectMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "X4ToSource", 0, 0),
     JS_FS_END
 };
 
+const JSFunctionSpec js::Float32x4Defn::TypeDescriptorMethods[] = {
+    JS_FN("toSource", TypeObjectToSource, 0, 0),
+    {"handle", {nullptr, nullptr}, 2, 0, "HandleCreate"},
+    {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
+    {"equivalent", {nullptr, nullptr}, 1, 0, "TypeObjectEquivalent"},
+    JS_FS_END
+};
+
 const JSPropertySpec js::Float32x4Defn::TypedObjectProperties[] = {
     JS_SELF_HOSTED_GET("x", "Float32x4Lane0", JSPROP_PERMANENT),
     JS_SELF_HOSTED_GET("y", "Float32x4Lane1", JSPROP_PERMANENT),
     JS_SELF_HOSTED_GET("z", "Float32x4Lane2", JSPROP_PERMANENT),
     JS_SELF_HOSTED_GET("w", "Float32x4Lane3", JSPROP_PERMANENT),
     JS_PS_END
 };
 
@@ -1384,16 +1284,19 @@ CreateX4Class(JSContext *cx, Handle<Glob
     }
 
     // Link type constructor to the type representation
 
     x4->initReservedSlot(JS_TYPEOBJ_SLOT_TYPE_REPR, ObjectValue(*typeReprObj));
 
     // Link constructor to prototype and install properties
 
+    if (!JS_DefineFunctions(cx, x4, T::TypeDescriptorMethods))
+        return nullptr;
+
     if (!LinkConstructorAndPrototype(cx, x4, proto) ||
         !DefinePropertiesAndBrand(cx, proto, T::TypedObjectProperties,
                                   T::TypedObjectMethods))
     {
         return nullptr;
     }
 
     return x4;
@@ -1413,17 +1316,17 @@ X4Type::call(JSContext *cx, unsigned arg
 
     double values[LANES];
     for (uint32_t i = 0; i < LANES; i++) {
         if (!ToNumber(cx, args[i], &values[i]))
             return false;
     }
 
     RootedObject typeObj(cx, &args.callee());
-    RootedObject result(cx, TypedObject::createZeroed(cx, typeObj));
+    RootedObject result(cx, TypedObject::createZeroed(cx, typeObj, 0));
     if (!result)
         return false;
 
     X4TypeRepresentation *typeRepr = typeRepresentation(*typeObj)->asX4();
     switch (typeRepr->type()) {
 #define STORE_LANES(_constant, _type, _name)                                  \
       case _constant:                                                         \
       {                                                                       \
@@ -1480,19 +1383,18 @@ DefineMetaTypeObject(JSContext *cx,
                                   protoProtoValue,
                                   nullptr, nullptr,
                                   JSPROP_READONLY | JSPROP_PERMANENT))
         return nullptr;
 
     // Create ctor itself
 
     const int constructorLength = 2;
-    RootedFunction ctor(cx,
-                        global->createConstructor(cx, T::construct,
-                                                  className, constructorLength));
+    RootedFunction ctor(cx);
+    ctor = global->createConstructor(cx, T::construct, className, constructorLength);
     if (!ctor ||
         !LinkConstructorAndPrototype(cx, ctor, proto) ||
         !DefinePropertiesAndBrand(cx, proto,
                                   T::typeObjectProperties,
                                   T::typeObjectMethods) ||
         !DefinePropertiesAndBrand(cx, protoProto,
                                   T::typedObjectProperties,
                                   T::typedObjectMethods))
@@ -1654,32 +1556,35 @@ js_InitTypedObjectDummy(JSContext *cx, H
  *
  * Datums represent either typed objects or handles. See comment in
  * TypedObject.h.
  */
 
 template<class T>
 /*static*/ T *
 TypedDatum::createUnattached(JSContext *cx,
-                             HandleObject type)
+                             HandleObject type,
+                             int32_t length)
 {
     JS_STATIC_ASSERT(T::IsTypedDatumClass);
     JS_ASSERT(IsTypeObject(*type));
 
-    RootedObject obj(cx, createUnattachedWithClass(cx, &T::class_, type));
+    RootedObject obj(
+        cx, createUnattachedWithClass(cx, &T::class_, type, length));
     if (!obj)
         return nullptr;
 
     return &obj->as<T>();
 }
 
 /*static*/ TypedDatum *
 TypedDatum::createUnattachedWithClass(JSContext *cx,
                                       const Class *clasp,
-                                      HandleObject type)
+                                      HandleObject type,
+                                      int32_t length)
 {
     JS_ASSERT(IsTypeObject(*type));
     JS_ASSERT(clasp == &TypedObject::class_ || clasp == &TypedHandle::class_);
     JS_ASSERT(JSCLASS_RESERVED_SLOTS(clasp) == JS_DATUM_SLOTS);
     JS_ASSERT(clasp->hasPrivate());
 
     RootedObject proto(cx);
     if (IsSimpleTypeObject(*type)) {
@@ -1698,16 +1603,17 @@ TypedDatum::createUnattachedWithClass(JS
     RootedObject obj(
         cx, NewObjectWithClassProto(cx, clasp, &*proto, nullptr));
     if (!obj)
         return nullptr;
 
     obj->setPrivate(nullptr);
     obj->initReservedSlot(JS_DATUM_SLOT_TYPE_OBJ, ObjectValue(*type));
     obj->initReservedSlot(JS_DATUM_SLOT_OWNER, NullValue());
+    obj->initReservedSlot(JS_DATUM_SLOT_LENGTH, Int32Value(length));
 
     // Tag the type object for this instance with the type
     // representation, if that has not been done already.
     if (cx->typeInferenceEnabled() && !IsSimpleTypeObject(*type)) {
         // FIXME Bug 929651           ^~~~~~~~~~~~~~~~~~~~~~~~~~~
         RootedTypeObject typeObj(cx, obj->getType(cx));
         if (typeObj) {
             TypeRepresentation *typeRepr = typeRepresentation(*type);
@@ -1737,26 +1643,52 @@ TypedDatum::attach(JSObject &datum, uint
 
     // find the owner, which is often but not always `datum`
     JSObject &owner = datum.getReservedSlot(JS_DATUM_SLOT_OWNER).toObject();
 
     setPrivate(mem);
     setReservedSlot(JS_DATUM_SLOT_OWNER, ObjectValue(owner));
 }
 
+// Returns a suitable JS_DATUM_SLOT_LENGTH value for an instance of
+// the type `type`. `type` must not be an unsized array.
+static int32_t
+DatumLengthFromType(JSObject &type)
+{
+    TypeRepresentation *typeRepr = typeRepresentation(type);
+    switch (typeRepr->kind()) {
+      case TypeRepresentation::Scalar:
+      case TypeRepresentation::Reference:
+      case TypeRepresentation::Struct:
+      case TypeRepresentation::X4:
+        return 0;
+
+      case TypeRepresentation::SizedArray:
+        return typeRepr->asSizedArray()->length();
+
+      case TypeRepresentation::UnsizedArray:
+        MOZ_ASSUME_UNREACHABLE("DatumLengthFromType() invoked on unsized type");
+    }
+    MOZ_ASSUME_UNREACHABLE("Invalid kind");
+}
+
 /*static*/ TypedDatum *
 TypedDatum::createDerived(JSContext *cx, HandleObject type,
                           HandleObject datum, size_t offset)
 {
+    JS_ASSERT(IsTypeObject(*type));
     JS_ASSERT(IsTypedDatum(*datum));
     JS_ASSERT(offset <= DatumSize(*datum));
     JS_ASSERT(offset + TypeSize(*type) <= DatumSize(*datum));
 
+    int32_t length = DatumLengthFromType(*type);
+
     const js::Class *clasp = datum->getClass();
-    Rooted<TypedDatum*> obj(cx, createUnattachedWithClass(cx, clasp, type));
+    Rooted<TypedDatum*> obj(
+        cx, createUnattachedWithClass(cx, clasp, type, length));
     if (!obj)
         return nullptr;
 
     obj->attach(*datum, offset);
     return obj;
 }
 
 static bool
@@ -1791,20 +1723,34 @@ ReportDatumTypeError(JSContext *cx,
 /*static*/ void
 TypedDatum::obj_trace(JSTracer *trace, JSObject *object)
 {
     JS_ASSERT(IsTypedDatum(*object));
 
     for (size_t i = 0; i < JS_DATUM_SLOTS; i++)
         gc::MarkSlot(trace, &object->getReservedSlotRef(i), "TypedObjectSlot");
 
-    uint8_t *mem = TypedMem(*object);
     TypeRepresentation *repr = typeRepresentation(*GetType(*object));
-    if (repr->opaque())
-        repr->traceInstance(trace, mem);
+    if (repr->opaque()) {
+        uint8_t *mem = TypedMem(*object);
+        switch (repr->kind()) {
+          case TypeRepresentation::Scalar:
+          case TypeRepresentation::Reference:
+          case TypeRepresentation::Struct:
+          case TypeRepresentation::SizedArray:
+          case TypeRepresentation::X4:
+            repr->asSized()->traceInstance(trace, mem, 1);
+            break;
+
+          case TypeRepresentation::UnsizedArray:
+            repr->asUnsizedArray()->element()->traceInstance(
+                trace, mem, DatumLength(*object));
+            break;
+        }
+    }
 }
 
 /*static*/ void
 TypedDatum::obj_finalize(js::FreeOp *op, JSObject *obj)
 {
     // if the obj owns the memory, indicating by the owner slot being
     // set to itself, then we must free it when finalized.
 
@@ -1829,17 +1775,18 @@ TypedDatum::obj_lookupGeneric(JSContext 
     TypeRepresentation *typeRepr = typeRepresentation(*type);
 
     switch (typeRepr->kind()) {
       case TypeRepresentation::Scalar:
       case TypeRepresentation::Reference:
       case TypeRepresentation::X4:
         break;
 
-      case TypeRepresentation::Array:
+      case TypeRepresentation::SizedArray:
+      case TypeRepresentation::UnsizedArray:
       {
         uint32_t index;
         if (js_IdIsIndex(id, &index))
             return obj_lookupElement(cx, obj, index, objp, propp);
 
         if (JSID_IS_ATOM(id, cx->names().length)) {
             MarkNonNativePropertyFound(propp);
             objp.set(obj);
@@ -2008,19 +1955,27 @@ TypedDatum::obj_getGeneric(JSContext *cx
     switch (typeRepr->kind()) {
       case TypeRepresentation::Scalar:
       case TypeRepresentation::Reference:
         break;
 
       case TypeRepresentation::X4:
         break;
 
-      case TypeRepresentation::Array:
+      case TypeRepresentation::SizedArray:
+      case TypeRepresentation::UnsizedArray:
         if (JSID_IS_ATOM(id, cx->names().length)) {
-            vp.setInt32(typeRepr->asArray()->length());
+            if (!TypedMem(*obj)) { // unattached
+                JS_ReportErrorNumber(
+                    cx, js_GetErrorMessage,
+                    nullptr, JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
+                return false;
+            }
+
+            vp.setInt32(DatumLength(*obj));
             return true;
         }
         break;
 
       case TypeRepresentation::Struct: {
         StructTypeRepresentation *structTypeRepr = typeRepr->asStruct();
         const StructField *field = structTypeRepr->fieldNamed(id);
         if (!field)
@@ -2069,28 +2024,33 @@ TypedDatum::obj_getElementIfPresent(JSCo
 
     switch (typeRepr->kind()) {
       case TypeRepresentation::Scalar:
       case TypeRepresentation::Reference:
       case TypeRepresentation::X4:
       case TypeRepresentation::Struct:
         break;
 
-      case TypeRepresentation::Array: {
+      case TypeRepresentation::SizedArray:
+      case TypeRepresentation::UnsizedArray:
+      {
+        JS_ASSERT(IsArrayTypedDatum(*obj));
+
         *present = true;
-        ArrayTypeRepresentation *arrayTypeRepr = typeRepr->asArray();
-
-        if (index >= arrayTypeRepr->length()) {
+
+        if (index >= DatumLength(*obj)) {
             vp.setUndefined();
             return true;
         }
 
-        RootedObject elementType(cx, ArrayElementType(type));
-        size_t offset = arrayTypeRepr->element()->size() * index;
-        return Reify(cx, arrayTypeRepr->element(), elementType, obj, offset, vp);
+        RootedObject elementType(cx, ArrayElementType(*GetType(*obj)));
+        SizedTypeRepresentation *elementTypeRepr =
+            typeRepresentation(*elementType)->asSized();
+        size_t offset = elementTypeRepr->size() * index;
+        return Reify(cx, elementTypeRepr, elementType, obj, offset, vp);
       }
     }
 
     RootedObject proto(cx, obj->getProto());
     if (!proto) {
         *present = false;
         vp.setUndefined();
         return true;
@@ -2122,17 +2082,18 @@ TypedDatum::obj_setGeneric(JSContext *cx
     switch (typeRepr->kind()) {
       case ScalarTypeRepresentation::Scalar:
       case TypeRepresentation::Reference:
         break;
 
       case ScalarTypeRepresentation::X4:
         break;
 
-      case ScalarTypeRepresentation::Array:
+      case ScalarTypeRepresentation::SizedArray:
+      case ScalarTypeRepresentation::UnsizedArray:
         if (JSID_IS_ATOM(id, cx->names().length)) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage,
                                  nullptr, JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
             return false;
         }
         break;
 
       case ScalarTypeRepresentation::Struct: {
@@ -2157,39 +2118,41 @@ TypedDatum::obj_setProperty(JSContext *c
                              bool strict)
 {
     RootedId id(cx, NameToId(name));
     return obj_setGeneric(cx, obj, id, vp, strict);
 }
 
 bool
 TypedDatum::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);
 
     switch (typeRepr->kind()) {
       case TypeRepresentation::Scalar:
       case TypeRepresentation::Reference:
       case TypeRepresentation::X4:
       case TypeRepresentation::Struct:
         break;
 
-      case TypeRepresentation::Array: {
-        ArrayTypeRepresentation *arrayTypeRepr = typeRepr->asArray();
-
-        if (index >= arrayTypeRepr->length()) {
+      case TypeRepresentation::SizedArray:
+      case TypeRepresentation::UnsizedArray:
+      {
+        if (index >= DatumLength(*obj)) {
             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;
+        RootedObject elementType(cx, ArrayElementType(*GetType(*obj)));
+        SizedTypeRepresentation *elementTypeRepr =
+            typeRepresentation(*elementType)->asSized();
+        size_t offset = elementTypeRepr->size() * index;
         return ConvertAndCopyTo(cx, elementType, obj, offset, vp);
       }
     }
 
     return ReportDatumTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, obj);
 }
 
 bool
@@ -2212,17 +2175,18 @@ TypedDatum::obj_getGenericAttributes(JSC
     switch (typeRepr->kind()) {
       case TypeRepresentation::Scalar:
       case TypeRepresentation::Reference:
         break;
 
       case TypeRepresentation::X4:
         break;
 
-      case TypeRepresentation::Array:
+      case TypeRepresentation::SizedArray:
+      case TypeRepresentation::UnsizedArray:
         if (js_IdIsIndex(id, &index)) {
             *attrsp = JSPROP_ENUMERATE | JSPROP_PERMANENT;
             return true;
         }
         if (JSID_IS_ATOM(id, cx->names().length)) {
             *attrsp = JSPROP_READONLY | JSPROP_PERMANENT;
             return true;
         }
@@ -2253,17 +2217,18 @@ IsOwnId(JSContext *cx, HandleObject obj,
     TypeRepresentation *typeRepr = typeRepresentation(*type);
 
     switch (typeRepr->kind()) {
       case TypeRepresentation::Scalar:
       case TypeRepresentation::Reference:
       case TypeRepresentation::X4:
         return false;
 
-      case TypeRepresentation::Array:
+      case TypeRepresentation::SizedArray:
+      case TypeRepresentation::UnsizedArray:
         return js_IdIsIndex(id, &index) || JSID_IS_ATOM(id, cx->names().length);
 
       case TypeRepresentation::Struct:
         return typeRepr->asStruct()->fieldNamed(id) != nullptr;
     }
 
     return false;
 }
@@ -2357,32 +2322,33 @@ TypedDatum::obj_enumerate(JSContext *cx,
 
           case JSENUMERATE_NEXT:
           case JSENUMERATE_DESTROY:
             statep.setNull();
             break;
         }
         break;
 
-      case TypeRepresentation::Array:
+      case TypeRepresentation::SizedArray:
+      case TypeRepresentation::UnsizedArray:
         switch (enum_op) {
           case JSENUMERATE_INIT_ALL:
           case JSENUMERATE_INIT:
             statep.setInt32(0);
-            idp.set(INT_TO_JSID(typeRepr->asArray()->length()));
+            idp.set(INT_TO_JSID(DatumLength(*obj)));
             break;
 
           case JSENUMERATE_NEXT:
             index = static_cast<int32_t>(statep.toInt32());
 
-            if (index < typeRepr->asArray()->length()) {
+            if (index < DatumLength(*obj)) {
                 idp.set(INT_TO_JSID(index));
                 statep.setInt32(index + 1);
             } else {
-                JS_ASSERT(index == typeRepr->asArray()->length());
+                JS_ASSERT(index == DatumLength(*obj));
                 statep.setNull();
             }
 
             break;
 
           case JSENUMERATE_DESTROY:
             statep.setNull();
             break;
@@ -2417,17 +2383,17 @@ TypedDatum::obj_enumerate(JSContext *cx,
     }
 
     return true;
 }
 
 /* static */ size_t
 TypedDatum::dataOffset()
 {
-    return JSObject::getPrivateDataOffset(JS_DATUM_SLOTS + 1);
+    return JSObject::getPrivateDataOffset(JS_DATUM_SLOTS);
 }
 
 /******************************************************************************
  * Typed Objects
  */
 
 const Class TypedObject::class_ = {
     "TypedObject",
@@ -2473,47 +2439,122 @@ const Class TypedObject::class_ = {
         TypedDatum::obj_deleteElement,
         TypedDatum::obj_deleteSpecial,
         nullptr, nullptr, // watch/unwatch
         TypedDatum::obj_enumerate,
         nullptr, /* thisObject */
     }
 };
 
-/*static*/ JSObject *
-TypedObject::createZeroed(JSContext *cx, HandleObject type)
+/*static*/ TypedObject *
+TypedObject::createZeroed(JSContext *cx,
+                          HandleObject typeObj,
+                          int32_t length)
 {
-    Rooted<TypedObject*> obj(cx, createUnattached<TypedObject>(cx, type));
+    // Create unattached wrapper object.
+    Rooted<TypedObject*> obj(cx);
+    obj = createUnattached<TypedObject>(cx, typeObj, length);
     if (!obj)
         return nullptr;
 
-    TypeRepresentation *typeRepr = typeRepresentation(*type);
-    size_t memsize = typeRepr->size();
-    uint8_t *memory = (uint8_t*) cx->malloc_(memsize);
-    if (!memory)
-        return nullptr;
-    typeRepr->initInstance(cx->runtime(), memory);
-    obj->attach(memory);
-    return obj;
+    // Allocate and initialize the memory for this instance.
+    // Also initialize the JS_DATUM_SLOT_LENGTH slot.
+    TypeRepresentation *typeRepr = typeRepresentation(*typeObj);
+    switch (typeRepr->kind()) {
+      case TypeRepresentation::Scalar:
+      case TypeRepresentation::Reference:
+      case TypeRepresentation::Struct:
+      case TypeRepresentation::X4:
+      {
+        uint8_t *memory = (uint8_t*) cx->malloc_(typeRepr->asSized()->size());
+        if (!memory)
+            return nullptr;
+        typeRepr->asSized()->initInstance(cx->runtime(), memory, 1);
+        obj->attach(memory);
+        return obj;
+      }
+
+      case TypeRepresentation::SizedArray:
+      {
+        uint8_t *memory = (uint8_t*) cx->malloc_(typeRepr->asSizedArray()->size());
+        if (!memory)
+            return nullptr;
+        typeRepr->asSizedArray()->initInstance(cx->runtime(), memory, 1);
+        obj->attach(memory);
+        return obj;
+      }
+
+      case TypeRepresentation::UnsizedArray:
+      {
+        SizedTypeRepresentation *elementTypeRepr =
+            typeRepr->asUnsizedArray()->element();
+
+        int32_t totalSize;
+        if (!SafeMul(elementTypeRepr->size(), length, &totalSize)) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
+                                 JSMSG_TYPEDOBJECT_TOO_BIG);
+            return nullptr;
+        }
+
+        uint8_t *memory = (uint8_t*) JS_malloc(cx, totalSize);
+        if (!memory)
+            return nullptr;
+
+        elementTypeRepr->initInstance(cx->runtime(), memory, length);
+        obj->attach(memory);
+        return obj;
+      }
+    }
+
+    MOZ_ASSUME_UNREACHABLE("Bad TypeRepresentation Kind");
 }
 
 /*static*/ bool
 TypedObject::construct(JSContext *cx, unsigned int argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedObject callee(cx, &args.callee());
     JS_ASSERT(IsTypeObject(*callee));
-
-    RootedObject obj(cx, createZeroed(cx, callee));
+    TypeRepresentation *typeRepr = typeRepresentation(*callee);
+
+    // Determine the length based on the target type.
+    uint32_t nextArg = 0;
+    int32_t length = 0;
+    switch (typeRepr->kind()) {
+      case TypeRepresentation::Scalar:
+      case TypeRepresentation::Reference:
+      case TypeRepresentation::Struct:
+      case TypeRepresentation::X4:
+        length = 0;
+        break;
+
+      case TypeRepresentation::SizedArray:
+        length = typeRepr->asSizedArray()->length();
+        break;
+
+      case TypeRepresentation::UnsizedArray:
+        // First argument is a length.
+        if (nextArg >= argc || !args[nextArg].isInt32()) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage,
+                                 nullptr, JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS,
+                                 "1", "array length");
+            return false;
+        }
+        length = args[nextArg++].toInt32();
+        break;
+    }
+
+    // Create zeroed wrapper object.
+    Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length));
     if (!obj)
-        return false;
-
-    if (argc == 1) {
-        RootedValue initial(cx, args[0]);
+        return nullptr;
+
+    if (nextArg < argc) {
+        RootedValue initial(cx, args[nextArg++]);
         if (!ConvertAndCopyTo(cx, obj, initial))
             return false;
     }
 
     args.rval().setObject(*obj);
     return true;
 }
 
@@ -2582,21 +2623,22 @@ const JSFunctionSpec TypedHandle::handle
  * Intrinsics
  */
 
 bool
 js::NewTypedHandle(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JS_ASSERT(argc == 1);
-    JS_ASSERT(args[0].isObject() && IsTypeObject(args[0].toObject()));
+    JS_ASSERT(args[0].isObject() && IsSizedTypeObject(args[0].toObject()));
 
     RootedObject typeObj(cx, &args[0].toObject());
-    Rooted<TypedHandle*> obj(
-        cx, TypedDatum::createUnattached<TypedHandle>(cx, typeObj));
+    int32_t length = DatumLengthFromType(*typeObj);
+    Rooted<TypedHandle*> obj(cx);
+    obj = TypedDatum::createUnattached<TypedHandle>(cx, typeObj, length);
     if (!obj)
         return false;
     args.rval().setObject(*obj);
     return true;
 }
 
 bool
 js::NewDerivedTypedDatum(JSContext *cx, unsigned argc, Value *vp)
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -143,16 +143,28 @@ class X4Type : public JSObject
 };
 
 /*
  * Type descriptor created by `new ArrayType(...)`
  */
 class ArrayType : public JSObject
 {
   private:
+    // Helper for creating a new ArrayType object, either sized or unsized.
+    // - `arrayTypePrototype` - prototype for the new object to be created,
+    //                          either ArrayType.prototype or
+    //                          unsizedArrayType.__proto__ depending on
+    //                          whether this is a sized or unsized array
+    // - `arrayTypeReprObj` - a type representation object for the array
+    // - `elementType` - type object for the elements in the array
+    static JSObject *create(JSContext *cx,
+                            HandleObject arrayTypePrototype,
+                            HandleObject arrayTypeReprObj,
+                            HandleObject elementType);
+
   public:
     static const Class class_;
 
     // Properties and methods to be installed on ArrayType.prototype,
     // and hence inherited by all array type objects:
     static const JSPropertySpec typeObjectProperties[];
     static const JSFunctionSpec typeObjectMethods[];
 
@@ -160,20 +172,19 @@ class ArrayType : public JSObject
     // and hence inherited by all array *typed* objects:
     static const JSPropertySpec typedObjectProperties[];
     static const JSFunctionSpec typedObjectMethods[];
 
     // This is the function that gets called when the user
     // does `new ArrayType(elem)`. It produces an array type object.
     static bool construct(JSContext *cx, unsigned argc, Value *vp);
 
-    static JSObject *create(JSContext *cx, HandleObject arrayTypeGlobal,
-                            HandleObject elementType, size_t length);
-    static bool repeat(JSContext *cx, unsigned argc, Value *vp);
-    static bool subarray(JSContext *cx, unsigned argc, Value *vp);
+    // This is the sized method on unsized array type objects.  It
+    // produces a sized variant.
+    static bool dimension(JSContext *cx, unsigned int argc, jsval *vp);
 
     static JSObject *elementType(JSContext *cx, HandleObject obj);
 };
 
 /*
  * Type descriptor created by `new StructType(...)`
  */
 class StructType : public JSObject
@@ -301,50 +312,56 @@ class TypedDatum : public JSObject
     // object or this object may alias data owned by someone else.)
     // This function returns the offset in bytes within the object
     // where the `void*` pointer can be found. It is intended for use
     // by the JIT.
     static size_t dataOffset();
 
     static TypedDatum *createUnattachedWithClass(JSContext *cx,
                                                  const Class *clasp,
-                                                 HandleObject type);
+                                                 HandleObject type,
+                                                 int32_t length);
 
     // Creates an unattached typed object or handle (depending on the
     // type parameter T). Note that it is only legal for unattached
     // handles to escape to the end user; for non-handles, the caller
     // should always invoke one of the `attach()` methods below.
     //
     // Arguments:
     // - type: type object for resulting object
     template<class T>
-    static T *createUnattached(JSContext *cx, HandleObject type);
+    static T *createUnattached(JSContext *cx, HandleObject type, int32_t length);
 
     // Creates a datum that aliases the memory pointed at by `owner`
     // at the given offset. The datum will be a handle iff type is a
     // handle and a typed object otherwise.
     static TypedDatum *createDerived(JSContext *cx,
                                      HandleObject type,
                                      HandleObject typedContents,
                                      size_t offset);
 
+
     // If `this` is the owner of the memory, use this.
     void attach(uint8_t *mem);
 
     // Otherwise, use this to attach to memory referenced by another datum.
     void attach(JSObject &datum, uint32_t offset);
 };
 
 class TypedObject : public TypedDatum
 {
   public:
     static const Class class_;
 
-    // creates zeroed memory of size of type
-    static JSObject *createZeroed(JSContext *cx, HandleObject type);
+    // Creates a new typed object whose memory is freshly allocated
+    // and initialized with zeroes (or, in the case of references, an
+    // appropriate default value).
+    static TypedObject *createZeroed(JSContext *cx,
+                                     HandleObject typeObj,
+                                     int32_t length);
 
     // user-accessible constructor (`new TypeDescriptor(...)`)
     static bool construct(JSContext *cx, unsigned argc, Value *vp);
 };
 
 class TypedHandle : public TypedDatum
 {
   public:
@@ -355,16 +372,23 @@ class TypedHandle : public TypedDatum
 /*
  * Usage: NewTypedHandle(typeObj)
  *
  * Constructs a new, unattached instance of `Handle`.
  */
 bool NewTypedHandle(JSContext *cx, unsigned argc, Value *vp);
 
 /*
+ * Usage: NewTypedHandle(typeObj)
+ *
+ * Constructs a new, unattached instance of `Handle`.
+ */
+bool NewTypedHandle(JSContext *cx, unsigned argc, Value *vp);
+
+/*
  * Usage: NewDerivedTypedDatum(typeObj, owner, offset)
  *
  * Constructs a new, unattached instance of `Handle`.
  */
 bool NewDerivedTypedDatum(JSContext *cx, unsigned argc, Value *vp);
 
 /*
  * Usage: AttachHandle(handle, newOwner, newOffset)
--- a/js/src/builtin/TypedObject.js
+++ b/js/src/builtin/TypedObject.js
@@ -9,16 +9,18 @@
     UnsafeGetReservedSlot(obj, JS_TYPEOBJ_SLOT_TYPE_REPR)
 
 // Typed object slots
 
 #define DATUM_TYPE_OBJ(obj) \
     UnsafeGetReservedSlot(obj, JS_DATUM_SLOT_TYPE_OBJ)
 #define DATUM_OWNER(obj) \
     UnsafeGetReservedSlot(obj, JS_DATUM_SLOT_OWNER)
+#define DATUM_LENGTH(obj) \
+    TO_INT32(UnsafeGetReservedSlot(obj, JS_DATUM_SLOT_LENGTH))
 
 // 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) \
@@ -90,38 +92,51 @@ TypedObjectPointer.prototype.reset = fun
   this.offset = inPtr.offset;
   return this;
 };
 
 TypedObjectPointer.prototype.kind = function() {
   return REPR_KIND(this.typeRepr);
 }
 
+TypedObjectPointer.prototype.length = function() {
+  switch (this.kind()) {
+  case JS_TYPEREPR_SIZED_ARRAY_KIND:
+    return REPR_LENGTH(this.typeRepr);
+
+  case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
+    return DATUM_LENGTH(this.datum);
+  }
+  assert(false, "length() invoked on non-array-type");
+  return 0;
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // 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:
   case JS_TYPEREPR_REFERENCE_KIND:
   case JS_TYPEREPR_X4_KIND:
     break;
 
-  case JS_TYPEREPR_ARRAY_KIND:
+  case JS_TYPEREPR_SIZED_ARRAY_KIND:
+  case JS_TYPEREPR_UNSIZED_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 >= 0 && index < REPR_LENGTH(this.typeRepr))
+    if (index === propName && index >= 0 && index < this.length())
       return this.moveToElem(index);
     break;
 
   case JS_TYPEREPR_STRUCT_KIND:
     if (HAS_PROPERTY(this.typeObj.fieldTypes, propName))
       return this.moveToField(propName);
     break;
   }
@@ -129,21 +144,22 @@ TypedObjectPointer.prototype.moveTo = fu
   ThrowError(JSMSG_TYPEDOBJECT_NO_SUCH_PROP, propName);
   return undefined;
 };
 
 // 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,
+  assert(this.kind() == JS_TYPEREPR_SIZED_ARRAY_KIND ||
+         this.kind() == JS_TYPEREPR_UNSIZED_ARRAY_KIND,
          "moveToElem invoked on non-array");
   assert(TO_INT32(index) === index,
          "moveToElem invoked with non-integer index");
-  assert(index >= 0 && index < REPR_LENGTH(this.typeRepr),
+  assert(index >= 0 && index < this.length(),
          "moveToElem invoked with out-of-bounds index: " + index);
 
   var elementTypeObj = this.typeObj.elementType;
   var elementTypeRepr = TYPE_TYPE_REPR(elementTypeObj);
   this.typeRepr = elementTypeRepr;
   this.typeObj = elementTypeObj;
   var elementSize = REPR_SIZE(elementTypeRepr);
 
@@ -195,19 +211,24 @@ TypedObjectPointer.prototype.get = funct
     return this.getScalar();
 
   case JS_TYPEREPR_REFERENCE_KIND:
     return this.getReference();
 
   case JS_TYPEREPR_X4_KIND:
     return this.getX4();
 
-  case JS_TYPEREPR_ARRAY_KIND:
+  case JS_TYPEREPR_SIZED_ARRAY_KIND:
+    return NewDerivedTypedDatum(this.typeObj, this.datum, this.offset);
+
   case JS_TYPEREPR_STRUCT_KIND:
     return NewDerivedTypedDatum(this.typeObj, this.datum, this.offset);
+
+  case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
+    assert(false, "Unhandled repr kind: " + REPR_KIND(this.typeRepr));
   }
 
   assert(false, "Unhandled kind: " + REPR_KIND(this.typeRepr));
   return undefined;
 }
 
 TypedObjectPointer.prototype.getScalar = function() {
   var type = REPR_TYPE(this.typeRepr);
@@ -293,18 +314,18 @@ TypedObjectPointer.prototype.getX4 = fun
 TypedObjectPointer.prototype.set = function(fromValue) {
   assert(ObjectIsAttached(this.datum), "set() called with unattached datum");
 
   var typeRepr = this.typeRepr;
 
   // Fast path: `fromValue` is a typed object with same type
   // representation as the destination. In that case, we can just do a
   // memcpy.
-  if (IsObject(fromValue) && HaveSameClass(fromValue, this.datum)) {
-    if (DATUM_TYPE_REPR(fromValue) === typeRepr) {
+  if (IsObject(fromValue) && ObjectIsTypedDatum(fromValue)) {
+    if (!typeRepr.variable && DATUM_TYPE_REPR(fromValue) === typeRepr) {
       if (!ObjectIsAttached(fromValue))
         ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
 
       var size = REPR_SIZE(typeRepr);
       Memcpy(this.datum, this.offset, fromValue, 0, size);
       return;
     }
   }
@@ -317,22 +338,23 @@ TypedObjectPointer.prototype.set = funct
   case JS_TYPEREPR_REFERENCE_KIND:
     this.setReference(fromValue);
     return;
 
   case JS_TYPEREPR_X4_KIND:
     this.setX4(fromValue);
     return;
 
-  case JS_TYPEREPR_ARRAY_KIND:
+  case JS_TYPEREPR_SIZED_ARRAY_KIND:
+  case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
     if (!IsObject(fromValue))
       break;
 
     // Check that "array-like" fromValue has an appropriate length.
-    var length = REPR_LENGTH(typeRepr);
+    var length = this.length();
     if (fromValue.length !== length)
       break;
 
     // Adapt each element.
     if (length > 0) {
       var tempPtr = this.copy().moveToElem(0);
       var size = REPR_SIZE(tempPtr.typeRepr);
       for (var i = 0; i < length; i++) {
@@ -536,28 +558,41 @@ function TypedArrayRedimension(newArrayT
     ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "this", "typed array");
 
   if (!IsObject(newArrayType) || !ObjectIsTypeObject(newArrayType))
     ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, 1, "type object");
 
   // Peel away the outermost array layers from the type of `this` to find
   // the core element type. In the process, count the number of elements.
   var oldArrayType = DATUM_TYPE_OBJ(this);
+  var oldArrayReprKind = REPR_KIND(TYPE_TYPE_REPR(oldArrayType));
   var oldElementType = oldArrayType;
   var oldElementCount = 1;
-  while (REPR_KIND(TYPE_TYPE_REPR(oldElementType)) == JS_TYPEREPR_ARRAY_KIND) {
+  switch (oldArrayReprKind) {
+  case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
+    oldElementCount *= this.length;
+    oldElementType = oldElementType.elementType;
+    break;
+
+  case JS_TYPEREPR_SIZED_ARRAY_KIND:
+    break;
+
+  default:
+    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "this", "typed array");
+  }
+  while (REPR_KIND(TYPE_TYPE_REPR(oldElementType)) === JS_TYPEREPR_SIZED_ARRAY_KIND) {
     oldElementCount *= oldElementType.length;
     oldElementType = oldElementType.elementType;
   }
 
   // Peel away the outermost array layers from `newArrayType`. In the
   // process, count the number of elements.
   var newElementType = newArrayType;
   var newElementCount = 1;
-  while (REPR_KIND(TYPE_TYPE_REPR(newElementType)) == JS_TYPEREPR_ARRAY_KIND) {
+  while (REPR_KIND(TYPE_TYPE_REPR(newElementType)) == JS_TYPEREPR_SIZED_ARRAY_KIND) {
     newElementCount *= newElementType.length;
     newElementType = newElementType.elementType;
   }
 
   // Check that the total number of elements does not change.
   if (oldElementCount !== newElementCount) {
     ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, 1,
                "New number of elements does not match old number of elements");
@@ -587,16 +622,28 @@ function TypedArrayRedimension(newArrayT
 // This is the `handle([obj, [...path]])` method on type objects.
 // User exposed!
 //
 // FIXME bug 929656 -- label algorithms with steps from the spec
 function HandleCreate(obj, ...path) {
   if (!IsObject(this) || !ObjectIsTypeObject(this))
     ThrowError(JSMSG_INCOMPATIBLE_PROTO, "Type", "handle", "value");
 
+  switch (REPR_KIND(TYPE_TYPE_REPR(this))) {
+  case JS_TYPEREPR_SCALAR_KIND:
+  case JS_TYPEREPR_REFERENCE_KIND:
+  case JS_TYPEREPR_X4_KIND:
+  case JS_TYPEREPR_SIZED_ARRAY_KIND:
+  case JS_TYPEREPR_STRUCT_KIND:
+    break;
+
+  case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
+    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_TO_UNSIZED);
+  }
+
   var handle = NewTypedHandle(this);
 
   if (obj !== undefined)
     HandleMoveInternal(handle, obj, path)
 
   return handle;
 }
 
@@ -724,16 +771,33 @@ function X4ToSource() {
 
 ///////////////////////////////////////////////////////////////////////////
 // Miscellaneous
 
 // This is the `objectType()` function defined in the spec.
 // It returns the type of its argument.
 //
 // Warning: user exposed!
+function ArrayShorthand(...dims) {
+  if (!IsObject(this) || !ObjectIsTypeObject(this))
+    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS,
+               "this", "typed object");
+
+  var T = StandardTypeObjectDescriptors();
+
+  if (dims.length == 0)
+    return new T.ArrayType(this);
+
+  var accum = this;
+  for (var i = dims.length - 1; i >= 0; i--)
+    accum = new T.ArrayType(accum).dimension(dims[i]);
+  return accum;
+}
+
+// Warning: user exposed!
 function TypeOfTypedDatum(obj) {
   if (IsObject(obj) && ObjectIsTypedDatum(obj))
     return DATUM_TYPE_OBJ(obj);
 
   // Note: Do not create bindings for `Any`, `String`, etc in
   // Utilities.js, but rather access them through
   // `StandardTypeObjectDescriptors()`. The reason is that bindings
   // you create in Utilities.js are part of the self-hosted global,
--- a/js/src/builtin/TypedObjectConstants.h
+++ b/js/src/builtin/TypedObjectConstants.h
@@ -24,55 +24,57 @@
 #define JS_TYPEOBJ_SCALAR_SLOTS            1  // Maximum number
 
 // Slots on references
 #define JS_TYPEOBJ_REFERENCE_SLOTS         1  // Maximum number
 
 // Slots on x4s
 #define JS_TYPEOBJ_X4_SLOTS                1  // Maximum number
 
-// Slots on arrays
+// Slots on array type objects
 #define JS_TYPEOBJ_SLOT_ARRAY_ELEM_TYPE    1
-#define JS_TYPEOBJ_ARRAY_SLOTS             2  // Maximum number
+#define JS_TYPEOBJ_ARRAY_SLOTS             3  // 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:
+// Slots on *all* type representations:
 #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:
+// Slots on sized arrays:
 #define JS_TYPEREPR_SLOT_LENGTH    3 // Length of the array
 
 // Slots on scalars, references, and X4s:
 #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, which 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_REFERENCE_KIND 1
-#define JS_TYPEREPR_X4_KIND        2
-#define JS_TYPEREPR_STRUCT_KIND    3
-#define JS_TYPEREPR_ARRAY_KIND     4
+#define JS_TYPEREPR_UNSIZED_ARRAY_KIND  0
+#define JS_TYPEREPR_MAX_UNSIZED_KIND    0    // Unsized kinds go above here
+#define JS_TYPEREPR_SCALAR_KIND         1
+#define JS_TYPEREPR_REFERENCE_KIND      2
+#define JS_TYPEREPR_STRUCT_KIND         3
+#define JS_TYPEREPR_SIZED_ARRAY_KIND    4
+#define JS_TYPEREPR_X4_KIND             5
 
 // These constants are for use exclusively in JS code. In C++ code,
 // prefer ScalarTypeRepresentation::TYPE_INT8 etc, which 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
@@ -98,11 +100,17 @@
 #define JS_X4TYPEREPR_INT32         0
 #define JS_X4TYPEREPR_FLOAT32       1
 
 ///////////////////////////////////////////////////////////////////////////
 // Slots for typed objects (actually, any TypedContents objects)
 
 #define JS_DATUM_SLOT_TYPE_OBJ 0  // Type object for a given typed object
 #define JS_DATUM_SLOT_OWNER    1  // Owner of data (if null, this is owner)
-#define JS_DATUM_SLOTS         2  // Number of slots for typed objs
+#define JS_DATUM_SLOT_LENGTH   2  // Length of array (see (*) below)
+#define JS_DATUM_SLOTS         3  // Number of slots for typed objs
+
+// (*) The JS_DATUM_SLOT_LENGTH slot stores the length for datums of
+// sized and unsized array type. The slot contains 0 for non-arrays.
+// The slot also contains 0 for *unattached* datums, no matter what
+// type they have.
 
 #endif
--- a/js/src/jit-test/tests/TypedObject/fuzz10.js
+++ b/js/src/jit-test/tests/TypedObject/fuzz10.js
@@ -1,8 +1,8 @@
 // |jit-test| error:Error
 
 if (!this.hasOwnProperty("TypedObject"))
   throw new Error("type too large");
 
-var AA = new TypedObject.ArrayType(new ArrayType(TypedObject.uint8, 2147483647), 5);
+var AA = TypedObject.uint8.array(2147483647).array(5);
 var aa = new AA();
 var aa0 = aa[0];
--- a/js/src/jit-test/tests/TypedObject/fuzz11.js
+++ b/js/src/jit-test/tests/TypedObject/fuzz11.js
@@ -1,13 +1,13 @@
 // |jit-test| error:Error
 
 if (!this.hasOwnProperty("TypedObject"))
   throw new Error("type too large");
 
-var A = new TypedObject.ArrayType(TypedObject.uint8, 2147483647);
+var A = TypedObject.uint8.array(2147483647);
 var S = new TypedObject.StructType({a: A,
                                     b: A,
                                     c: A,
                                     d: A,
                                     e: A});
 var aa = new S();
 var aa0 = aa.a;
--- a/js/src/jit-test/tests/TypedObject/fuzz4.js
+++ b/js/src/jit-test/tests/TypedObject/fuzz4.js
@@ -1,10 +1,10 @@
 // |jit-test| error:Error;
 
 if (!this.hasOwnProperty("TypedObject"))
   throw new Error();
 
-var A = new TypedObject.ArrayType(TypedObject.uint8, 10);
+var A = TypedObject.uint8.array(10);
 var a = new A();
 a.forEach(function(val, i) {
   assertEq(arguments[5], a);
 });
--- a/js/src/jit-test/tests/TypedObject/fuzz8.js
+++ b/js/src/jit-test/tests/TypedObject/fuzz8.js
@@ -1,6 +1,6 @@
 // |jit-test| error:Error
 
 if (!this.hasOwnProperty("TypedObject"))
   throw new Error();
 
-new TypedObject.ArrayType(TypedObject.uint8, .0000000009);
+TypedObject.uint8.array(.0000000009);
--- a/js/src/jit-test/tests/TypedObject/fuzz9.js
+++ b/js/src/jit-test/tests/TypedObject/fuzz9.js
@@ -1,10 +1,10 @@
 // |jit-test| error: TypeError
 
 if (!this.hasOwnProperty("TypedObject"))
   throw new TypeError();
 
-var Vec3 = new TypedObject.ArrayType(TypedObject.float32, 3);
-var Sprite = new TypedObject.ArrayType(Vec3, 3);
+var Vec3 = TypedObject.float32.array(3);
+var Sprite = Vec3.array(3);
 var mario = new Sprite();
 mario[/\u00ee[]/] = new Vec3([1, 0, 0]);
 
--- a/js/src/jit-test/tests/TypedObject/jit-complex.js
+++ b/js/src/jit-test/tests/TypedObject/jit-complex.js
@@ -1,30 +1,33 @@
 // Test that we can optimize stuff like line.target.x without
 // creating an intermediate object.
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
+setJitCompilerOption("ion.usecount.trigger", 30);
+
 var PointType = new TypedObject.StructType({x: TypedObject.float64,
                                             y: TypedObject.float64});
 var LineType = new TypedObject.StructType({source: PointType,
                                            target: PointType});
 
-function manhattenDistance(line) {
+function manhattanDistance(line) {
   return (Math.abs(line.target.x - line.source.x) +
           Math.abs(line.target.y - line.source.y));
 }
 
 function foo() {
-  var N = 30000;
+  var N = 100;
   var points = [];
   var obj;
   var s;
 
   var fromAToB = new LineType({source: {x: 22, y: 44},
                                target: {x: 66, y: 88}});
 
-  for (var i = 0; i < N; i++)
-    assertEq(manhattenDistance(fromAToB), 88);
+  for (var i = 0; i < N; i++) {
+    assertEq(manhattanDistance(fromAToB), 88);
+  }
 }
 
 foo();
--- a/js/src/jit-test/tests/TypedObject/jit-prefix.js
+++ b/js/src/jit-test/tests/TypedObject/jit-prefix.js
@@ -1,11 +1,13 @@
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
+setJitCompilerOption("ion.usecount.trigger", 30);
+
 var PointType2 =
   new TypedObject.StructType({
     x: TypedObject.float64,
     y: TypedObject.float64});
 
 var PointType3 =
   new TypedObject.StructType({
     x: TypedObject.float64,
@@ -17,17 +19,17 @@ function xPlusY(p) {
 }
 
 function xPlusYTweak(p) {
   p.x = 22;
   return xPlusY(p);
 }
 
 function foo() {
-  var N = 30000;
+  var N = 100;
   var points = [];
   var obj;
   var s;
 
   for (var i = 0; i < N; i++) {
     if ((i % 2) == 0 || true)
       obj = new PointType2({x: i, y: i+1});
     else
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObject/jit-read-many.js
@@ -0,0 +1,62 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+if (!this.hasOwnProperty("TypedObject"))
+  quit();
+
+// Test a single function (`bar`) being used with arrays that are all
+// of known length, but not the same length.
+
+setJitCompilerOption("ion.usecount.trigger", 30);
+
+var N0 = 50;
+var N1 = 100;
+var N2 = 150;
+var T = TypedObject;
+var Array0 = T.uint32.array(N0);
+var Array1 = T.uint32.array(N1);
+var Array2 = T.uint32.array(N2);
+
+function bar(array, i, v) {
+  assertEq(array[i], v);
+}
+
+function foo() {
+  var array0 = new Array0();
+  var array1 = new Array1();
+  var array2 = new Array2();
+
+  for (var i = 0; i < N0; i++)
+    array0[i] = i + 0;
+
+  for (var i = 0; i < N1; i++)
+    array1[i] = i + 1;
+
+  for (var i = 0; i < N2; i++)
+    array2[i] = i + 2;
+
+  // get it primed up..
+  for (var i = 0; i < N0; i++) {
+    bar(array0, i, i);
+    bar(array1, i, i + 1);
+    bar(array2, i, i + 2);
+  }
+
+  // ...do some OOB accesses...
+  for (var i = N0; i < N1; i++) {
+    bar(array0, i, undefined);
+    bar(array1, i, i + 1);
+    bar(array2, i, i + 2);
+  }
+
+  // ...and some more.
+  for (var i = N1; i < N2; i++) {
+    bar(array0, i, undefined);
+    bar(array1, i, undefined);
+    bar(array2, i, i + 2);
+  }
+}
+
+foo();
--- a/js/src/jit-test/tests/TypedObject/jit-read-u16-from-mdim-array.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u16-from-mdim-array.js
@@ -1,18 +1,19 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
-var PointType = new TypedObject.ArrayType(TypedObject.uint16, 3);
-var VecPointType = new TypedObject.ArrayType(PointType, 3);
+var T = TypedObject;
+var PointType = T.uint16.array(3);
+var VecPointType = PointType.array(3);
 
 function foo() {
   for (var i = 0; i < 10000; i += 9) {
     var vec = new VecPointType([
       [i,   i+1, i+2],
       [i+3, i+4, i+5],
       [i+6, i+7, i+8]]);
     var sum = vec[0][0] + vec[0][1] + vec[0][2];
--- a/js/src/jit-test/tests/TypedObject/jit-read-u16-from-struct-array-in-struct.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u16-from-struct-array-in-struct.js
@@ -5,17 +5,17 @@
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
 var PointType = new TypedObject.StructType({x: TypedObject.uint16,
                                             y: TypedObject.uint16,
                                             z: TypedObject.uint16});
 
-var VecPointType = new TypedObject.ArrayType(PointType, 3);
+var VecPointType = PointType.array(3);
 
 var PairVecType = new TypedObject.StructType({fst: VecPointType,
                                               snd: VecPointType});
 
 function foo() {
   for (var i = 0; i < 5000; i += 9) {
     var p = new PairVecType({fst: [{x:    i, y: i+1, z:i+2},
                                    {x:  i+3, y: i+4, z:i+5},
--- a/js/src/jit-test/tests/TypedObject/jit-read-u16-from-struct-array.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u16-from-struct-array.js
@@ -5,17 +5,17 @@
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
 var PointType = new TypedObject.StructType({x: TypedObject.uint16,
                                             y: TypedObject.uint16,
                                             z: TypedObject.uint16});
 
-var VecPointType = new TypedObject.ArrayType(PointType, 3);
+var VecPointType = PointType.array(3);
 
 function foo() {
   for (var i = 0; i < 10000; i += 9) {
     var vec = new VecPointType([
       {x: i,   y:i+1, z:i+2},
       {x: i+3, y:i+4, z:i+5},
       {x: i+6, y:i+7, z:i+8}]);
     var sum = vec[0].x + vec[0].y + vec[0].z;
--- a/js/src/jit-test/tests/TypedObject/jit-read-u16-from-u16-array-in-struct.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u16-from-u16-array-in-struct.js
@@ -1,17 +1,17 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
-var Vec3u16Type = new TypedObject.ArrayType(TypedObject.uint16, 3);
+var Vec3u16Type = TypedObject.uint16.array(3);
 var PairVec3u16Type = new TypedObject.StructType({fst: Vec3u16Type,
                                                   snd: Vec3u16Type});
 
 function foo_u16() {
   for (var i = 0; i < 15000; i += 6) {
     var p = new PairVec3u16Type({fst: [i, i+1, i+2],
                                  snd: [i+3,i+4,i+5]});
     var sum = p.fst[0] + p.fst[1] + p.fst[2];
--- a/js/src/jit-test/tests/TypedObject/jit-read-u16-from-u16-array.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u16-from-u16-array.js
@@ -1,17 +1,17 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
-var Vec3u16Type = new TypedObject.ArrayType(TypedObject.uint16, 3);
+var Vec3u16Type = TypedObject.uint16.array(3);
 
 function foo_u16() {
   for (var i = 0; i < 30000; i += 3) {
     var vec = new Vec3u16Type([i, i+1, i+2]);
     var sum = vec[0] + vec[1] + vec[2];
     assertEq(sum, 3*i + 3);
   }
 }
--- a/js/src/jit-test/tests/TypedObject/jit-read-u32-from-mdim-array.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u32-from-mdim-array.js
@@ -1,18 +1,18 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
-var PointType = new TypedObject.ArrayType(TypedObject.uint32, 3);
-var VecPointType = new TypedObject.ArrayType(PointType, 3);
+var PointType = TypedObject.uint32.array(3);
+var VecPointType = PointType.array(3);
 
 function foo() {
   for (var i = 0; i < 10000; i += 9) {
     var vec = new VecPointType([
       [i,   i+1, i+2],
       [i+3, i+4, i+5],
       [i+6, i+7, i+8]]);
     var sum = vec[0][0] + vec[0][1] + vec[0][2];
--- a/js/src/jit-test/tests/TypedObject/jit-read-u32-from-struct-array-in-struct.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u32-from-struct-array-in-struct.js
@@ -5,17 +5,17 @@
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
 var PointType = new TypedObject.StructType({x: TypedObject.uint32,
                                             y: TypedObject.uint32,
                                             z: TypedObject.uint32});
 
-var VecPointType = new TypedObject.ArrayType(PointType, 3);
+var VecPointType = PointType.array(3);
 
 var PairVecType = new TypedObject.StructType({fst: VecPointType,
                                               snd: VecPointType});
 
 function foo() {
   for (var i = 0; i < 5000; i += 9) {
     var p = new PairVecType({fst: [{x:    i, y: i+1, z:i+2},
                                    {x:  i+3, y: i+4, z:i+5},
--- a/js/src/jit-test/tests/TypedObject/jit-read-u32-from-struct-array.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u32-from-struct-array.js
@@ -5,17 +5,17 @@
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
 var PointType = new TypedObject.StructType({x: TypedObject.uint32,
                                             y: TypedObject.uint32,
                                             z: TypedObject.uint32});
 
-var VecPointType = new TypedObject.ArrayType(PointType, 3);
+var VecPointType = PointType.array(3);
 
 function foo() {
   for (var i = 0; i < 10000; i += 9) {
     var vec = new VecPointType([
       {x: i,   y:i+1, z:i+2},
       {x: i+3, y:i+4, z:i+5},
       {x: i+6, y:i+7, z:i+8}]);
     var sum = vec[0].x + vec[0].y + vec[0].z;
--- a/js/src/jit-test/tests/TypedObject/jit-read-u32-from-u32-array-in-struct.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u32-from-u32-array-in-struct.js
@@ -1,17 +1,17 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
-var Vec3u32Type = new TypedObject.ArrayType(TypedObject.uint32, 3);
+var Vec3u32Type = TypedObject.uint32.array(3);
 var PairVec3u32Type = new TypedObject.StructType({fst: Vec3u32Type,
                                                   snd: Vec3u32Type});
 
 function foo_u32() {
   for (var i = 0; i < 15000; i += 6) {
     var p = new PairVec3u32Type({fst: [i, i+1, i+2],
                                  snd: [i+3,i+4,i+5]});
     var sum = p.fst[0] + p.fst[1] + p.fst[2];
--- a/js/src/jit-test/tests/TypedObject/jit-read-u32-from-u32-array.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u32-from-u32-array.js
@@ -1,17 +1,17 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
-var Vec3u32Type = new TypedObject.ArrayType(TypedObject.uint32, 3);
+var Vec3u32Type = TypedObject.uint32.array(3);
 
 function foo_u32() {
   for (var i = 0; i < 30000; i += 3) {
     var vec = new Vec3u32Type([i, i+1, i+2]);
     var sum = vec[0] + vec[1] + vec[2];
     assertEq(sum, 3*i + 3);
   }
 }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObject/jit-read-unsized.js
@@ -0,0 +1,47 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+if (!this.hasOwnProperty("TypedObject"))
+  quit();
+
+setJitCompilerOption("ion.usecount.trigger", 30);
+
+var N = 100;
+var T = TypedObject;
+var Point = new T.StructType({x: T.uint32, y: T.uint32, z: T.uint32});
+var PointArray = Point.array();
+
+function bar(array, i, x, y, z) {
+  assertEq(array[i].x, x);
+  assertEq(array[i].y, y);
+  assertEq(array[i].z, z);
+}
+
+function foo() {
+  var array = new PointArray(N);
+  for (var i = 0; i < N; i++) {
+    array[i].x = i + 0;
+    array[i].y = i + 1;
+    array[i].z = i + 2;
+  }
+
+  // get it primed up..
+  for (var i = 0; i < N; i++)
+    bar(array, i, i, i + 1, i + 2);
+
+  // ...do some OOB accesses...
+  for (var i = 0; i < N; i++) {
+    try {
+      bar(array, N, undefined, undefined, undefined);
+      assertEq(false, true);
+    } catch(e) { }
+  }
+
+  // ...test again.
+  for (var i = 0; i < N; i++)
+    bar(array, i, i, i + 1, i + 2);
+}
+
+foo();
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6590,95 +6590,124 @@ IonBuilder::getElemTryTypedObject(bool *
         return false;
 
     if (!objTypeReprs.allOfArrayKind())
         return true;
 
     TypeRepresentationSet elemTypeReprs;
     if (!objTypeReprs.arrayElementType(*this, &elemTypeReprs))
         return false;
+    if (elemTypeReprs.empty())
+        return true;
+
+    JS_ASSERT(TypeRepresentation::isSized(elemTypeReprs.kind()));
 
     size_t elemSize;
     if (!elemTypeReprs.allHaveSameSize(&elemSize))
         return true;
 
     switch (elemTypeReprs.kind()) {
       case TypeRepresentation::X4:
         // FIXME (bug 894104): load into a MIRType_float32x4 etc
         return true;
 
       case TypeRepresentation::Struct:
-      case TypeRepresentation::Array:
+      case TypeRepresentation::SizedArray:
         return getElemTryComplexElemOfTypedObject(emitted,
                                                   obj,
                                                   index,
                                                   objTypeReprs,
                                                   elemTypeReprs,
                                                   elemSize);
       case TypeRepresentation::Scalar:
         return getElemTryScalarElemOfTypedObject(emitted,
                                                  obj,
                                                  index,
                                                  objTypeReprs,
                                                  elemTypeReprs,
                                                  elemSize);
 
       case TypeRepresentation::Reference:
         return true;
+
+      case TypeRepresentation::UnsizedArray:
+        MOZ_ASSUME_UNREACHABLE("Unsized arrays cannot be element types");
     }
 
     MOZ_ASSUME_UNREACHABLE("Bad kind");
 }
 
 static MIRType
 MIRTypeForTypedArrayRead(ScalarTypeRepresentation::Type arrayType,
                          bool observedDouble);
 
 bool
+IonBuilder::checkTypedObjectIndexInBounds(size_t elemSize,
+                                          MDefinition *obj,
+                                          MDefinition *index,
+                                          MDefinition **indexAsByteOffset,
+                                          TypeRepresentationSet objTypeReprs)
+{
+    // Ensure index is an integer.
+    MInstruction *idInt32 = MToInt32::New(alloc(), index);
+    current->add(idInt32);
+
+    // If we know the length statically from the type, just embed it.
+    // Otherwise, load it from the appropriate reserved slot on the
+    // typed object.  We know it's an int32, so we can convert from
+    // Value to int32 using truncation.
+    size_t lenOfAll;
+    MDefinition *length;
+    if (objTypeReprs.hasKnownArrayLength(&lenOfAll)) {
+        length = constantInt(lenOfAll);
+    } else {
+        MInstruction *lengthValue = MLoadFixedSlot::New(alloc(), obj, JS_DATUM_SLOT_LENGTH);
+        current->add(lengthValue);
+
+        MInstruction *length32 = MTruncateToInt32::New(alloc(), lengthValue);
+        current->add(length32);
+
+        length = length32;
+    }
+
+    index = addBoundsCheck(idInt32, length);
+
+    // Since we passed the bounds check, it is impossible for the
+    // result of multiplication to overflow; so enable imul path.
+    MMul *mul = MMul::New(alloc(), index, constantInt(elemSize),
+                          MIRType_Int32, MMul::Integer);
+    current->add(mul);
+
+    *indexAsByteOffset = mul;
+    return true;
+}
+
+bool
 IonBuilder::getElemTryScalarElemOfTypedObject(bool *emitted,
                                               MDefinition *obj,
                                               MDefinition *index,
                                               TypeRepresentationSet objTypeReprs,
                                               TypeRepresentationSet elemTypeReprs,
                                               size_t elemSize)
 {
-    JS_ASSERT(objTypeReprs.allOfArrayKind());
+    JS_ASSERT(objTypeReprs.kind() == TypeRepresentation::SizedArray);
 
     // Must always be loading the same scalar type
     if (!elemTypeReprs.singleton())
         return true;
     ScalarTypeRepresentation *elemTypeRepr = elemTypeReprs.getTypeRepresentation()->asScalar();
-
-    // Get the length.
-    size_t lenOfAll = objTypeReprs.arrayLength();
-    if (lenOfAll >= size_t(INT_MAX)) // int32 max is bound
-        return true;
-    MInstruction *length = MConstant::New(alloc(), Int32Value(int32_t(lenOfAll)));
-
-    *emitted = true;
-    current->add(length);
-
-    // Ensure index is an integer.
-    MInstruction *idInt32 = MToInt32::New(alloc(), index);
-    current->add(idInt32);
-    index = idInt32;
-
-    // Typed-object accesses usually in bounds (bail out otherwise).
-    index = addBoundsCheck(index, length);
-
-    // Since we passed the bounds check, it is impossible for the
-    // result of multiplication to overflow; so enable imul path.
-    const int32_t alignment = elemTypeRepr->alignment();
-    MMul *indexAsByteOffset = MMul::New(alloc(), index, constantInt(alignment),
-                                        MIRType_Int32, MMul::Integer);
-    current->add(indexAsByteOffset);
+    JS_ASSERT(elemSize == elemTypeRepr->alignment());
+
+    MDefinition *indexAsByteOffset;
+    if (!checkTypedObjectIndexInBounds(elemSize, obj, index, &indexAsByteOffset, objTypeReprs))
+        return false;
 
     // Find location within the owner object.
     MDefinition *elements, *scaledOffset;
-    loadTypedObjectElements(obj, indexAsByteOffset, alignment, &elements, &scaledOffset);
+    loadTypedObjectElements(obj, indexAsByteOffset, elemSize, &elements, &scaledOffset);
 
     // Load the element.
     MLoadTypedArrayElement *load = MLoadTypedArrayElement::New(alloc(), elements, scaledOffset, elemTypeRepr->type());
     current->add(load);
     current->push(load);
 
     // If we are reading in-bounds elements, we can use knowledge about
     // the array type to determine the result type, even if the opcode has
@@ -6687,75 +6716,53 @@ IonBuilder::getElemTryScalarElemOfTypedO
     types::TemporaryTypeSet *resultTypes = bytecodeTypes(pc);
     bool allowDouble = resultTypes->hasType(types::Type::DoubleType());
     MIRType knownType = MIRTypeForTypedArrayRead(elemTypeRepr->type(), allowDouble);
     // Note: we can ignore the type barrier here, we know the type must
     // be valid and unbarriered.
     load->setResultType(knownType);
     load->setResultTypeSet(resultTypes);
 
+    *emitted = true;
     return true;
 }
 
 bool
 IonBuilder::getElemTryComplexElemOfTypedObject(bool *emitted,
                                                MDefinition *obj,
                                                MDefinition *index,
                                                TypeRepresentationSet objTypeReprs,
                                                TypeRepresentationSet elemTypeReprs,
                                                size_t elemSize)
 {
     JS_ASSERT(objTypeReprs.allOfArrayKind());
 
     MDefinition *type = loadTypedObjectType(obj);
     MDefinition *elemType = typeObjectForElementFromArrayStructType(type);
 
-    // Get the length.
-    size_t lenOfAll = objTypeReprs.arrayLength();
-    if (lenOfAll >= size_t(INT_MAX)) // int32 max is bound
-        return true;
-    MInstruction *length = MConstant::New(alloc(), Int32Value(int32_t(lenOfAll)));
-
-    *emitted = true;
-    current->add(length);
-
-    // Ensure index is an integer.
-    MInstruction *idInt32 = MToInt32::New(alloc(), index);
-    current->add(idInt32);
-    index = idInt32;
-
-    // Typed-object accesses usually in bounds (bail out otherwise).
-    index = addBoundsCheck(index, length);
-
-    // Convert array index to element data offset.
-    MConstant *alignment = MConstant::New(alloc(), Int32Value(elemSize));
-    current->add(alignment);
-
-    // Since we passed the bounds check, it is impossible for the
-    // result of multiplication to overflow; so enable imul path.
-    MMul *indexAsByteOffset = MMul::New(alloc(), index, alignment, MIRType_Int32,
-                                        MMul::Integer);
-    current->add(indexAsByteOffset);
+    MDefinition *indexAsByteOffset;
+    if (!checkTypedObjectIndexInBounds(elemSize, obj, index, &indexAsByteOffset, objTypeReprs))
+        return false;
 
     // Find location within the owner object.
     MDefinition *owner, *ownerOffset;
     loadTypedObjectData(obj, indexAsByteOffset, &owner, &ownerOffset);
 
     // Create the derived type object.
     MInstruction *derived = MNewDerivedTypedObject::New(alloc(),
                                                         elemTypeReprs,
                                                         elemType,
                                                         owner,
                                                         ownerOffset);
 
     types::TemporaryTypeSet *resultTypes = bytecodeTypes(pc);
     derived->setResultTypeSet(resultTypes);
     current->add(derived);
     current->push(derived);
-
+    *emitted = true;
     return true;
 }
 
 bool
 IonBuilder::getElemTryDense(bool *emitted, MDefinition *obj, MDefinition *index)
 {
     JS_ASSERT(*emitted == false);
 
@@ -8291,28 +8298,31 @@ IonBuilder::getPropTryTypedObject(bool *
       case TypeRepresentation::Reference:
         return true;
 
       case TypeRepresentation::X4:
         // FIXME (bug 894104): load into a MIRType_float32x4 etc
         return true;
 
       case TypeRepresentation::Struct:
-      case TypeRepresentation::Array:
+      case TypeRepresentation::SizedArray:
         return getPropTryComplexPropOfTypedObject(emitted,
                                                   fieldOffset,
                                                   fieldTypeReprs,
                                                   fieldIndex,
                                                   resultTypes);
 
       case TypeRepresentation::Scalar:
         return getPropTryScalarPropOfTypedObject(emitted,
                                                  fieldOffset,
                                                  fieldTypeReprs,
                                                  resultTypes);
+
+      case TypeRepresentation::UnsizedArray:
+        MOZ_ASSUME_UNREACHABLE("Field of unsized array type");
     }
 
     MOZ_ASSUME_UNREACHABLE("Bad kind");
 }
 
 bool
 IonBuilder::getPropTryScalarPropOfTypedObject(bool *emitted,
                                               int32_t fieldOffset,
@@ -8858,17 +8868,18 @@ IonBuilder::setPropTryTypedObject(bool *
 
     switch (fieldTypeReprs.kind()) {
       case TypeRepresentation::X4:
         // FIXME (bug 894104): store into a MIRType_float32x4 etc
         return true;
 
       case TypeRepresentation::Reference:
       case TypeRepresentation::Struct:
-      case TypeRepresentation::Array:
+      case TypeRepresentation::SizedArray:
+      case TypeRepresentation::UnsizedArray:
         // For now, only optimize storing scalars.
         return true;
 
       case TypeRepresentation::Scalar:
         return setPropTryScalarPropOfTypedObject(emitted, obj, fieldOffset,
                                                  value, fieldTypeReprs);
     }
 
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -448,16 +448,21 @@ class IonBuilder : public MIRGenerator
                                  MDefinition **ownerScaledOffset);
     MDefinition *typeObjectForElementFromArrayStructType(MDefinition *typedObj);
     MDefinition *typeObjectForFieldFromStructType(MDefinition *type,
                                                   size_t fieldIndex);
     bool storeScalarTypedObjectValue(MDefinition *typedObj,
                                      MDefinition *offset,
                                      ScalarTypeRepresentation *typeRepr,
                                      MDefinition *value);
+    bool checkTypedObjectIndexInBounds(size_t elemSize,
+                                       MDefinition *obj,
+                                       MDefinition *index,
+                                       MDefinition **indexAsByteOffset,
+                                       TypeRepresentationSet objTypeReprs);
 
     // jsop_setelem() helpers.
     bool setElemTryTyped(bool *emitted, MDefinition *object,
                          MDefinition *index, MDefinition *value);
     bool setElemTryTypedStatic(bool *emitted, MDefinition *object,
                                MDefinition *index, MDefinition *value);
     bool setElemTryDense(bool *emitted, MDefinition *object,
                          MDefinition *index, MDefinition *value);
--- a/js/src/jit/TypeRepresentationSet.cpp
+++ b/js/src/jit/TypeRepresentationSet.cpp
@@ -179,17 +179,29 @@ TypeRepresentationSet::getTypeRepresenta
 }
 
 bool
 TypeRepresentationSet::allOfArrayKind()
 {
     if (empty())
         return false;
 
-    return kind() == TypeRepresentation::Array;
+    switch (kind()) {
+      case TypeRepresentation::SizedArray:
+      case TypeRepresentation::UnsizedArray:
+        return true;
+
+      case TypeRepresentation::X4:
+      case TypeRepresentation::Reference:
+      case TypeRepresentation::Scalar:
+      case TypeRepresentation::Struct:
+        return false;
+    }
+
+    MOZ_ASSUME_UNREACHABLE("Invalid kind() in TypeRepresentationSet");
 }
 
 bool
 TypeRepresentationSet::allOfKind(TypeRepresentation::Kind aKind)
 {
     if (empty())
         return false;
 
@@ -197,55 +209,78 @@ TypeRepresentationSet::allOfKind(TypeRep
 }
 
 bool
 TypeRepresentationSet::allHaveSameSize(size_t *out)
 {
     if (empty())
         return false;
 
-    size_t size = get(0)->size();
+    JS_ASSERT(TypeRepresentation::isSized(kind()));
+
+    size_t size = get(0)->asSized()->size();
     for (size_t i = 1; i < length(); i++) {
-        if (get(i)->size() != size)
+        if (get(i)->asSized()->size() != size)
             return false;
     }
 
     *out = size;
     return true;
 }
 
 TypeRepresentation::Kind
 TypeRepresentationSet::kind()
 {
     JS_ASSERT(!empty());
     return get(0)->kind();
 }
 
-size_t
-TypeRepresentationSet::arrayLength()
+bool
+TypeRepresentationSet::hasKnownArrayLength(size_t *l)
 {
-    JS_ASSERT(kind() == TypeRepresentation::Array);
-    const size_t result = get(0)->asArray()->length();
-    for (size_t i = 1; i < length(); i++) {
-        if (get(i)->asArray()->length() != result)
-            return SIZE_MAX;
+    switch (kind()) {
+      case TypeRepresentation::UnsizedArray:
+        return false;
+
+      case TypeRepresentation::SizedArray:
+      {
+        const size_t result = get(0)->asSizedArray()->length();
+        for (size_t i = 1; i < length(); i++) {
+            if (get(i)->asSizedArray()->length() != result)
+                return false;
+        }
+        *l = result;
+        return true;
+      }
+
+      default:
+        MOZ_ASSUME_UNREACHABLE("Invalid array size for call to arrayLength()");
     }
-    return result;
 }
 
 bool
 TypeRepresentationSet::arrayElementType(IonBuilder &builder,
                                         TypeRepresentationSet *out)
 {
-    JS_ASSERT(kind() == TypeRepresentation::Array);
-
     TypeRepresentationSetBuilder elementTypes;
     for (size_t i = 0; i < length(); i++) {
-        if (!elementTypes.insert(get(i)->asArray()->element()))
-            return false;
+        switch (kind()) {
+          case TypeRepresentation::UnsizedArray:
+            if (!elementTypes.insert(get(i)->asUnsizedArray()->element()))
+                return false;
+            break;
+
+          case TypeRepresentation::SizedArray:
+            if (!elementTypes.insert(get(i)->asSizedArray()->element()))
+                return false;
+            break;
+
+          default:
+            MOZ_ASSUME_UNREACHABLE("Invalid kind for arrayElementType()");
+        }
     }
     return elementTypes.build(builder, out);
 }
 
 bool
 TypeRepresentationSet::fieldNamed(IonBuilder &builder,
                                   jsid id,
                                   size_t *offset,
--- a/js/src/jit/TypeRepresentationSet.h
+++ b/js/src/jit/TypeRepresentationSet.h
@@ -108,23 +108,24 @@ class TypeRepresentationSet {
     TypeRepresentation::Kind kind();
 
     //////////////////////////////////////////////////////////////////////
     // The following operations are only valid on a singleton set:
 
     TypeRepresentation *getTypeRepresentation();
 
     //////////////////////////////////////////////////////////////////////
-    // Array operations
+    // SizedArray operations
     //
-    // Only valid when `kind() == TypeRepresentation::Array`
+    // Only valid when `kind() == TypeRepresentation::SizedArray`
 
-    // Returns the length of the arrays in this set, or SIZE_MAX
-    // if they are not all the same.
-    size_t arrayLength();
+    // Determines whether all arrays in this set have the same,
+    // statically known, array length and return that length
+    // (via `*length`) if so. Otherwise returns false.
+    bool hasKnownArrayLength(size_t *length);
 
     // Returns a `TypeRepresentationSet` representing the element
     // types of the various array types in this set. The returned set
     // may be the empty set.
     bool arrayElementType(IonBuilder &builder, TypeRepresentationSet *out);
 
     //////////////////////////////////////////////////////////////////////
     // Struct operations
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -426,8 +426,10 @@ MSG_DEF(JSMSG_RC_AFTER_IMPORT_SPEC_LIST,
 MSG_DEF(JSMSG_FROM_AFTER_IMPORT_SPEC_SET, 372, 0, JSEXN_SYNTAXERR, "missing keyword 'from' after import specifier set")
 MSG_DEF(JSMSG_DECLARATION_AFTER_IMPORT, 373, 0, JSEXN_SYNTAXERR, "missing declaration after 'import' keyword")
 MSG_DEF(JSMSG_MODULE_SPEC_AFTER_FROM,   374, 0, JSEXN_SYNTAXERR, "missing module specifier after 'from' keyword")
 MSG_DEF(JSMSG_MODULES_NOT_IMPLEMENTED,  375, 0, JSEXN_SYNTAXERR, "modules are not implemented yet")
 MSG_DEF(JSMSG_EXPORT_DECL_AT_TOP_LEVEL, 376, 0, JSEXN_SYNTAXERR, "export declarations may only appear at top level")
 MSG_DEF(JSMSG_RC_AFTER_EXPORT_SPEC_LIST, 377, 0, JSEXN_SYNTAXERR, "missing '}' after export specifier list")
 MSG_DEF(JSMSG_NO_EXPORT_NAME,           378, 0, JSEXN_SYNTAXERR, "missing export name")
 MSG_DEF(JSMSG_DECLARATION_AFTER_EXPORT, 379, 0, JSEXN_SYNTAXERR, "missing declaration after 'export' keyword")
+MSG_DEF(JSMSG_INVALID_PROTOTYPE,        380, 0, JSEXN_TYPEERR, "prototype field is not an object")
+MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_TO_UNSIZED, 381, 0, JSEXN_TYPEERR, "cannot create a handle to an unsized type")
--- a/js/src/tests/ecma_6/TypedObject/arrayequiv.js
+++ b/js/src/tests/ecma_6/TypedObject/arrayequiv.js
@@ -24,36 +24,36 @@ function assertNotEquivalent(t1, t2) {
   assertEq(false, t2.equivalent(t1));
 }
 
 function runTests() {
   print(BUGNUMBER + ": " + summary);
 
   // Create a line:
   var PixelType1 = new StructType({x: uint8, y: uint8});
-  var PixelsType1 = new ArrayType(PixelType1, 22);
+  var PixelsType1 = PixelType1.array(22);
 
   // Sanity checks about type equivalence:
   assertEquivalent(PixelType1, PixelType1);
   assertEquivalent(PixelsType1, PixelsType1);
   assertNotEquivalent(PixelType1, PixelsType1);
 
   // Define the same two types again. Equivalent.
   var PixelType2 = new StructType({x: uint8, y: uint8});
-  var PixelsType2 = new ArrayType(PixelType2, 22);
+  var PixelsType2 = PixelType2.array(22);
   assertEquivalent(PixelType1, PixelType2);
   assertEquivalent(PixelsType1, PixelsType2);
 
   // Define the pixel type with field order reversed. Not equivalent.
   var PixelType3 = new StructType({y: uint8, x: uint8});
-  var PixelsType3 = new ArrayType(PixelType3, 22);
+  var PixelsType3 = PixelType3.array(22);
   assertNotEquivalent(PixelType1, PixelType3);
   assertNotEquivalent(PixelsType1, PixelsType3);
 
   // Define the pixels type with different number of elements. Not equivalent.
-  var PixelsType3 = new ArrayType(PixelType1, 23);
+  var PixelsType3 = PixelType1.array(23);
   assertNotEquivalent(PixelsType1, PixelsType3);
 
   reportCompare(true, true);
   print("Tests complete");
 }
 
 runTests();
--- a/js/src/tests/ecma_6/TypedObject/arrayofstructs.js
+++ b/js/src/tests/ecma_6/TypedObject/arrayofstructs.js
@@ -2,19 +2,19 @@
 var BUGNUMBER = 578700;
 var summary = 'TypedObjects StructType prototype chains';
 
 var ArrayType = TypedObject.ArrayType;
 var StructType = TypedObject.StructType;
 var float32 = TypedObject.float32;
 
 function runTests() {
-  var Point = new ArrayType(float32, 3);
+  var Point = new ArrayType(float32).dimension(3);
   var Line = new StructType({from: Point, to: Point});
-  var Lines = new ArrayType(Line, 3);
+  var Lines = new ArrayType(Line).dimension(3);
 
   var lines = new Lines([
     {from: [1, 2, 3], to: [4, 5, 6]},
     {from: [7, 8, 9], to: [10, 11, 12]},
     {from: [13, 14, 15], to: [16, 17, 18]}
   ]);
 
   assertEq(lines[1].to[1], 11);
--- a/js/src/tests/ecma_6/TypedObject/arraytype.js
+++ b/js/src/tests/ecma_6/TypedObject/arraytype.js
@@ -17,35 +17,30 @@ var ArrayType = TypedObject.ArrayType;
 var uint8 = TypedObject.uint8;
 var float32 = TypedObject.float32;
 var uint32 = TypedObject.uint32;
 
 function runTests() {
     print(BUGNUMBER + ": " + summary);
 
     assertEq(typeof ArrayType.prototype.prototype.forEach == "function", true);
-    assertEq(typeof ArrayType.prototype.prototype.subarray == "function", true);
 
     assertThrows(function() ArrayType(uint8, 10));
     assertThrows(function() new ArrayType());
     assertThrows(function() new ArrayType(""));
     assertThrows(function() new ArrayType(5));
-    assertThrows(function() new ArrayType(uint8, -1));
-    var A = new ArrayType(uint8, 10);
-    assertEq(A.__proto__, ArrayType.prototype);
+    assertThrows(function() new ArrayType(uint8).dimension(-1));
+    var A = new ArrayType(uint8).dimension(10);
+    assertEq(A.__proto__.__proto__, ArrayType.prototype);
     assertEq(A.length, 10);
     assertEq(A.elementType, uint8);
     assertEq(A.byteLength, 10);
-    assertEq(A.toSource(), "ArrayType(uint8, 10)");
+    assertEq(A.toSource(), "uint8.array(10)");
 
-    assertEq(A.prototype.__proto__, ArrayType.prototype.prototype);
-    assertEq(typeof A.prototype.fill, "function");
-
-    var X = { __proto__: A };
-    assertThrows(function() X.repeat(42));
+    assertEq(A.prototype.__proto__.__proto__, ArrayType.prototype.prototype);
 
     var a = new A();
     assertEq(a.__proto__, A.prototype);
     assertEq(a.length, 10);
 
     assertThrows(function() a.length = 2);
 
     for (var i = 0; i < a.length; i++)
@@ -74,18 +69,18 @@ function runTests() {
     for (var i = 0; i < b.length; i++)
         assertEq(b[i], i%2);
 
     assertThrows(function() new A(5));
     assertThrows(function() new A(/fail/));
     // Length different
     assertThrows(function() new A([0, 1, 0, 1, 0, 1, 0, 1, 0]));
 
-    var Vec3 = new ArrayType(float32, 3);
-    var Sprite = new ArrayType(Vec3, 3); // say for position, velocity, and direction
+    var Vec3 = new ArrayType(float32).dimension(3);
+    var Sprite = new ArrayType(Vec3).dimension(3); // say for position, velocity, and direction
     assertEq(Sprite.elementType, Vec3);
     assertEq(Sprite.elementType.elementType, float32);
 
 
     var mario = new Sprite();
     // setting using binary data
     mario[0] = new Vec3([1, 0, 0]);
     // setting using JS array conversion
@@ -96,77 +91,20 @@ function runTests() {
     assertEq(mario[0][1], 0);
     assertEq(mario[0][2], 0);
 
     assertThrows(function() mario[1] = 5);
     mario[1][1] = {};
     assertEq(Number.isNaN(mario[1][1]), true);
 
     // ok this is just for kicks
-    var AllSprites = new ArrayType(Sprite, 65536);
+    var AllSprites = new ArrayType(Sprite).dimension(65536);
     var as = new AllSprites();
     assertEq(as.length, 65536);
 
-    // test methods
-    var c = new A();
-    c.fill(3);
-    for (var i = 0; i < c.length; i++)
-        assertEq(c[i], 3);
-
-    assertThrows(function() Vec3.prototype.fill.call(c, 2));
-
-    var d = A.repeat(10);
-    for (var i = 0; i < d.length; i++)
-        assertEq(d[i], 10);
-
-    assertThrows(function() ArrayType.prototype.repeat.call(d, 2));
-
-    var MA = new ArrayType(uint32, 5);
-    var ma = new MA([1, 2, 3, 4, 5]);
-
-    var mb = ma.subarray(2);
-    assertEq(mb.length, 3);
-    assertEq(mb[0], 3);
-    assertEq(mb[1], 4);
-    assertEq(mb[2], 5);
-
-    assertThrows(function() ma.subarray());
-    assertThrows(function() ma.subarray(2.14));
-    assertThrows(function() ma.subarray({}));
-    assertThrows(function() ma.subarray(2, []));
-
-    var range = ma.subarray(0, 3);
-    assertEq(range.length, 3);
-    assertEq(range[0], 1);
-    assertEq(range[1], 2);
-    assertEq(range[2], 3);
-
-    assertEq(ma.subarray(ma.length).length, 0);
-    assertEq(ma.subarray(ma.length, ma.length-1).length, 0);
-
-    var rangeNeg = ma.subarray(-2);
-    assertEq(rangeNeg.length, 2);
-    assertEq(rangeNeg[0], 4);
-    assertEq(rangeNeg[1], 5);
-
-    var rangeNeg = ma.subarray(-5, -3);
-    assertEq(rangeNeg.length, 2);
-    assertEq(rangeNeg[0], 1);
-    assertEq(rangeNeg[1], 2);
-
-    assertEq(ma.subarray(-2, -3).length, 0);
-    assertEq(ma.subarray(-6).length, ma.length);
-
-    var modifyOriginal = ma.subarray(2);
-    modifyOriginal[0] = 42;
-    assertEq(ma[2], 42);
-
-    ma[4] = 97;
-    assertEq(modifyOriginal[2], 97);
-
     var indexPropDesc = Object.getOwnPropertyDescriptor(as, '0');
     assertEq(typeof indexPropDesc == "undefined", false);
     assertEq(indexPropDesc.configurable, false);
     assertEq(indexPropDesc.enumerable, true);
     assertEq(indexPropDesc.writable, true);
 
     var lengthPropDesc = Object.getOwnPropertyDescriptor(as, 'length');
     assertEq(typeof lengthPropDesc == "undefined", false);
@@ -178,17 +116,17 @@ function runTests() {
     for (var nm in as) {
       assertEq(+nm, counter++);
     }
     assertEq(counter, as.length);
 
     assertThrows(function() Object.defineProperty(o, "foo", { value: "bar" }));
 
     // check if a reference acts the way it should
-    var AA = new ArrayType(new ArrayType(uint8, 5), 5);
+    var AA = uint8.array(5, 5);
     var aa = new AA();
     var aa0 = aa[0];
     aa[0] = [0,1,2,3,4];
     for (var i = 0; i < aa0.length; i++)
         assertEq(aa0[i], i);
 
     if (typeof reportCompare === "function")
         reportCompare(true, true);
--- a/js/src/tests/ecma_6/TypedObject/arrayzerolen.js
+++ b/js/src/tests/ecma_6/TypedObject/arrayzerolen.js
@@ -2,16 +2,16 @@
 var BUGNUMBER = 926401;
 var summary = 'TypedObjects ArrayType implementation';
 
 // Test creation of zero-length array
 
 function runTest() {
   var T = TypedObject;
   var Color = new T.StructType({'r': T.uint8, 'g': T.uint8, 'b': T.uint8});
-  var Rainbow = new T.ArrayType(Color, 0);
+  var Rainbow = Color.array(0);
   var theOneISawWasJustBlack = new Rainbow([]);
   if (typeof reportCompare === "function")
     reportCompare(true, true);
   print("Tests complete");
 }
 
 runTest();
--- a/js/src/tests/ecma_6/TypedObject/handle.js
+++ b/js/src/tests/ecma_6/TypedObject/handle.js
@@ -6,19 +6,19 @@
  */
 
 var BUGNUMBER = 898342;
 var summary = 'Handles';
 
 var T = TypedObject;
 
 function runTests() {
-  var Point = new T.ArrayType(T.float32, 3);
+  var Point = T.float32.array(3);
   var Line = new T.StructType({from: Point, to: Point});
-  var Lines = new T.ArrayType(Line, 3);
+  var Lines = Line.array(3);
 
   var lines = new Lines([
     {from: [1, 2, 3], to: [4, 5, 6]},
     {from: [7, 8, 9], to: [10, 11, 12]},
     {from: [13, 14, 15], to: [16, 17, 18]}
   ]);
 
   var handle = Lines.handle(lines);
--- a/js/src/tests/ecma_6/TypedObject/handle_get_set.js
+++ b/js/src/tests/ecma_6/TypedObject/handle_get_set.js
@@ -5,21 +5,21 @@
  * http://creativecommons.org/licenses/publicdomain/
  */
 
 var BUGNUMBER = 898342;
 var summary = 'Handle Move';
 
 var T = TypedObject;
 
-var Point = new T.ArrayType(T.float32, 3);
+var Point = T.float32.array(3);
 var Line = new T.StructType({from: Point, to: Point});
-var Lines = new T.ArrayType(Line, 3);
+var Lines = Line.array(3);
 
-var Objects = new T.ArrayType(T.Object, 3);
+var Objects = T.Object.array(3);
 
 function runTests() {
   function testHandleGetSetWithScalarType() {
     var lines = new Lines([
       {from: [1, 2, 3], to: [4, 5, 6]},
       {from: [7, 8, 9], to: [10, 11, 12]},
       {from: [13, 14, 15], to: [16, 17, 18]}
     ]);
--- a/js/src/tests/ecma_6/TypedObject/handle_move.js
+++ b/js/src/tests/ecma_6/TypedObject/handle_move.js
@@ -5,19 +5,19 @@
  * http://creativecommons.org/licenses/publicdomain/
  */
 
 var BUGNUMBER = 898342;
 var summary = 'Handle Move';
 
 var T = TypedObject;
 
-var Point = new T.ArrayType(T.float32, 3);
+var Point = T.float32.array(3);
 var Line = new T.StructType({from: Point, to: Point});
-var Lines = new T.ArrayType(Line, 3);
+var Lines = Line.array(3);
 
 function runTests() {
   function testHandleToPoint() {
     var lines = new Lines([
       {from: [1, 2, 3], to: [4, 5, 6]},
       {from: [7, 8, 9], to: [10, 11, 12]},
       {from: [13, 14, 15], to: [16, 17, 18]}
     ]);
@@ -85,17 +85,17 @@ function runTests() {
     // Spot check the results
     assertEq(lines[0].from[0], 101);
     assertEq(lines[1].to[1], 111);
     assertEq(lines[2].to[2], 118);
   }
   testHandleToFloat();
 
   function testHandleToEquivalentType() {
-    var Point2 = new T.ArrayType(T.float32, 3);
+    var Point2 = T.float32.array(3);
 
     assertEq(Point.equivalent(Point2), true);
 
     var lines = new Lines([
       {from: [1, 2, 3], to: [4, 5, 6]},
       {from: [7, 8, 9], to: [10, 11, 12]},
       {from: [13, 14, 15], to: [16, 17, 18]}
     ]);
--- a/js/src/tests/ecma_6/TypedObject/handle_unattached.js
+++ b/js/src/tests/ecma_6/TypedObject/handle_unattached.js
@@ -7,22 +7,26 @@
 
 var BUGNUMBER = 898342;
 var summary = 'Unattached handles';
 
 var T = TypedObject;
 
 function runTests() {
   var Line = new T.StructType({from: T.uint8, to: T.uint8});
-  var Lines = new T.ArrayType(Line, 3);
+  var Lines = Line.array(3);
 
   // Create unattached handle to array, struct:
   var handle = Lines.handle();
   var handle0 = Line.handle();
 
+  // Accessing length throws:
+  assertThrowsInstanceOf(function() handle.length, TypeError,
+                         "handle.length did not yield error");
+
   // Accessing properties throws:
   assertThrowsInstanceOf(function() handle[0], TypeError,
                          "Unattached handle did not yield error");
   assertThrowsInstanceOf(function() handle0.from, TypeError,
                          "Unattached handle did not yield error");
 
   // Handle.get() throws:
   assertThrowsInstanceOf(function() T.Handle.get(handle), TypeError,
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedObject/handle_unsized.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var BUGNUMBER = 922115;
+var summary = 'check we cannot create handles to unsized arrays';
+
+var T = TypedObject;
+
+function runTests() {
+  var Line = new T.StructType({from: T.uint8, to: T.uint8});
+  var Lines = Line.array();
+  assertThrowsInstanceOf(function() Lines.handle(), TypeError,
+                         "was able to create handle to unsized array");
+
+  reportCompare(true, true);
+  print("Tests complete");
+}
+
+runTests();
+
+
--- a/js/src/tests/ecma_6/TypedObject/memory.js
+++ b/js/src/tests/ecma_6/TypedObject/memory.js
@@ -18,49 +18,56 @@ var int16 = TypedObject.int16;
 var int32 = TypedObject.int32;
 var float32 = TypedObject.float32;
 var float64 = TypedObject.float64;
 
 
 function runTests() {
     print(BUGNUMBER + ": " + summary);
 
-    var AA = new ArrayType(new ArrayType(uint8, 5), 5);
+    var AA = uint8.array(5, 5);
     var aa = new AA();
     var aa0 = aa[0];
     aa[0] = [0,1,2,3,4];
 
     aa = null;
 
     gc();
     spin();
 
     for (var i = 0; i < aa0.length; i++)
         assertEq(aa0[i], i);
 
-    var AAA = new ArrayType(AA, 5);
+    var AAA = AA.array(5);
     var aaa = new AAA();
     var a0 = aaa[0][0];
 
     for (var i = 0; i < a0.length; i++)
         assertEq(a0[i], 0);
 
     aaa[0] = [[0,1,2,3,4], [0,1,2,3,4], [0,1,2,3,4], [0,1,2,3,4], [0,1,2,3,4]];
 
     aaa = null;
 
     gc();
     spin();
     for (var i = 0; i < a0.length; i++)
         assertEq(a0[i], i);
 
     var Color = new StructType({'r': uint8, 'g': uint8, 'b': uint8});
-    var Rainbow = new ArrayType(Color, 7);
+    var Rainbow = Color.array(7);
 
-    var theOneISawWasJustBlack = Rainbow.repeat({'r': 0, 'g': 0, 'b': 0});
+    var theOneISawWasJustBlack = new Rainbow([
+      {'r': 0, 'g': 0, 'b': 0},
+      {'r': 0, 'g': 0, 'b': 0},
+      {'r': 0, 'g': 0, 'b': 0},
+      {'r': 0, 'g': 0, 'b': 0},
+      {'r': 0, 'g': 0, 'b': 0},
+      {'r': 0, 'g': 0, 'b': 0},
+      {'r': 0, 'g': 0, 'b': 0}]);
 
     var middleBand = theOneISawWasJustBlack[3];
     theOneISawWasJustBlack = null;
     gc();
     spin();
     assertEq(middleBand['r'] == 0 && middleBand['g'] == 0 && middleBand['b'] == 0, true);
     middleBand.r = 255;
     middleBand.g = 207;
--- a/js/src/tests/ecma_6/TypedObject/objecttype.js
+++ b/js/src/tests/ecma_6/TypedObject/objecttype.js
@@ -5,19 +5,19 @@ var summary = 'objecttype';
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
 var T = TypedObject;
 
 function runTests() {
-  var Point = new T.ArrayType(T.float32, 3);
+  var Point = T.float32.array(3);
   var Line = new T.StructType({from: Point, to: Point});
-  var Lines = new T.ArrayType(Line, 3);
+  var Lines = Line.array(3);
 
   var lines = new Lines([
     {from: [1, 2, 3], to: [4, 5, 6]},
     {from: [7, 8, 9], to: [10, 11, 12]},
     {from: [13, 14, 15], to: [16, 17, 18]}
   ]);
 
   assertEq(T.objectType(lines), Lines);
--- a/js/src/tests/ecma_6/TypedObject/redimension.js
+++ b/js/src/tests/ecma_6/TypedObject/redimension.js
@@ -8,54 +8,50 @@ var summary = 'redimension method';
  */
 
 var T = TypedObject;
 
 function runTests() {
   var counter;
 
   // create an array of 40 bytes with some initial data
-  var Bytes40 = new T.ArrayType(T.uint8, 40);
+  var Bytes40 = T.uint8.array(40);
   var bytes40 = new Bytes40();
   for (var i = 0, counter = 0; i < 40; i++)
     bytes40[i] = counter++;
 
   // redimension to an array of 10x4 bytes, check data is unchanged
-  var Bytes10times4 = new T.ArrayType(new T.ArrayType(T.uint8, 4), 10);
+  var Bytes10times4 = T.uint8.array(10, 4);
   var bytes10times4 = bytes40.redimension(Bytes10times4);
   counter = 0;
   for (var i = 0; i < 10; i++)
     for (var j = 0; j < 4; j++)
       assertEq(counter++, bytes10times4[i][j]);
 
   // redimension to an array of 2x5x2x2, check data is unchanged
-  var Bytes2times5times2times2 =
-    new T.ArrayType(
-      new T.ArrayType(
-        new T.ArrayType(
-          new T.ArrayType(T.uint8, 2), 2), 5), 2);
+  var Bytes2times5times2times2 = T.uint8.array(2, 5, 2, 2);
   var bytes2times5times2times2 = bytes10times4.redimension(Bytes2times5times2times2);
   counter = 0;
   for (var i = 0; i < 2; i++)
     for (var j = 0; j < 5; j++)
       for (var k = 0; k < 2; k++)
         for (var l = 0; l < 2; l++)
           assertEq(counter++, bytes2times5times2times2[i][j][k][l]);
 
   // test what happens if number of elements does not match
   assertThrowsInstanceOf(() => {
-    var Bytes10times5 = new T.ArrayType(new T.ArrayType(T.uint8, 5), 10);
+    var Bytes10times5 = T.uint8.array(10, 5);
     bytes40.redimension(Bytes10times5);
-  }, TypeError, "New number of elements does not match old number of elements");
+  }, TypeError);
 
   // test what happens if inner type does not match, even if size is the same
   assertThrowsInstanceOf(() => {
-    var Words40 = new T.ArrayType(T.uint8Clamped, 40);
+    var Words40 = T.uint8Clamped.array(40);
     bytes40.redimension(Words40);
-  }, TypeError, "New element type is not equivalent to old element type");
+  }, TypeError);
 
   reportCompare(true, true);
   print("Tests complete");
 }
 
 runTests();
 
 
--- a/js/src/tests/ecma_6/TypedObject/referencetypecoercions.js
+++ b/js/src/tests/ecma_6/TypedObject/referencetypecoercions.js
@@ -22,17 +22,17 @@ function TestValues(type, values) {
   }
 
   for (var i = 0; i < values.length; i++) {
     var struct = new Struct();
     struct.f = values[i].input;
     compare(struct.f, values[i]);
   }
 
-  var Array = new ArrayType(type, 1);
+  var Array = new ArrayType(type).dimension(1);
   for (var i = 0; i < values.length; i++) {
     var array = new Array();
     array[0] = values[i].input;
     compare(array[0], values[i]);
   }
 
   function compare(v, spec) {
     if (spec.source)
--- a/js/src/tests/ecma_6/TypedObject/referencetypetrace.js
+++ b/js/src/tests/ecma_6/TypedObject/referencetypetrace.js
@@ -34,27 +34,36 @@ function TestStructFields(RefType) {
   var s1 = new S1({f: rabbit});
   assertCanReach(s1, rabbit);
   s1.f = null;
   assertCannotReach(s1, rabbit);
 }
 
 function TestArrayElements(RefType) {
   var rabbit = {};
-  var S1 = new ArrayType(RefType, 1);
+  var S1 = new ArrayType(RefType).dimension(1);
   var s1 = new S1([rabbit]);
   assertCanReach(s1, rabbit);
   s1[0] = null;
   assertCannotReach(s1, rabbit);
 }
 
+function TestUnsizedArrayElements(RefType) {
+  var rabbit = {};
+  var S1 = new ArrayType(RefType);
+  var s1 = new S1(1, [rabbit]);
+  assertCanReach(s1, rabbit);
+  s1[0] = null;
+  assertCannotReach(s1, rabbit);
+}
+
 function TestStructInArray(RefType) {
   var rabbit = {};
   var S2 = new StructType({f: RefType, g: RefType});
-  var S1 = new ArrayType(S2, 1);
+  var S1 = new ArrayType(S2).dimension(1);
   var s1 = new S1([{f: rabbit, g: {}}]);
   assertCanReach(s1, rabbit);
   s1[0].f = null;
   assertCannotReach(s1, rabbit);
 }
 
 function TestStringInStruct() {
   // Rather subtle hair-pullingly maddening testing phenomena: If you
@@ -77,16 +86,19 @@ function runTests()
   printStatus(summary);
 
   TestStructFields(Object);
   TestStructFields(Any);
 
   TestArrayElements(Object);
   TestArrayElements(Any);
 
+  TestUnsizedArrayElements(Object);
+  TestUnsizedArrayElements(Any);
+
   TestStructInArray(Object);
   TestStructInArray(Any);
 
   TestStringInStruct();
 
   reportCompare(true, true, "TypedObjects trace tests");
 }
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4handle.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4handle.js
@@ -10,17 +10,17 @@ var summary = 'float32x4 handles';
 var ArrayType = TypedObject.ArrayType;
 var float32x4 = TypedObject.float32x4;
 var float32 = TypedObject.float32;
 var Handle = TypedObject.Handle;
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  var Array = new ArrayType(float32x4, 3);
+  var Array = float32x4.array(3);
   var array = new Array([float32x4(1, 2, 3, 4),
                          float32x4(5, 6, 7, 8),
                          float32x4(9, 10, 11, 12)]);
 
   // Test that trying to create handle into the interior of a
   // float32x4 fails.
 
   assertThrowsInstanceOf(function() {
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4reify.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4reify.js
@@ -8,17 +8,17 @@ var summary = 'float32x4 reify';
  */
 
 var ArrayType = TypedObject.ArrayType;
 var float32x4 = TypedObject.float32x4;
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  var Array = new ArrayType(float32x4, 3);
+  var Array = float32x4.array(3);
   var array = new Array([float32x4(1, 2, 3, 4),
                          float32x4(5, 6, 7, 8),
                          float32x4(9, 10, 11, 12)]);
 
   // Test that reading array[1] produces a *copy* of float32x4, not an
   // alias into the array.
 
   var f = array[1];
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4setter.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4setter.js
@@ -8,17 +8,17 @@ var summary = 'float32x4 setting';
  */
 
 var ArrayType = TypedObject.ArrayType;
 var float32x4 = TypedObject.float32x4;
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  var Array = new ArrayType(float32x4, 3);
+  var Array = float32x4.array(3);
   var array = new Array([float32x4(1, 2, 3, 4),
                          float32x4(5, 6, 7, 8),
                          float32x4(9, 10, 11, 12)]);
   assertEq(array[1].w, 8);
 
   // Test that we are allowed to write float32x4 values into array,
   // but not other things.
 
--- a/js/src/tests/ecma_6/TypedObject/simd/int32x4handle.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4handle.js
@@ -10,17 +10,17 @@ var summary = 'int32x4 handles';
 var ArrayType = TypedObject.ArrayType;
 var int32x4 = TypedObject.int32x4;
 var int32 = TypedObject.int32;
 var Handle = TypedObject.Handle;
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  var Array = new ArrayType(int32x4, 3);
+  var Array = int32x4.array(3);
   var array = new Array([int32x4(1, 2, 3, 4),
                          int32x4(5, 6, 7, 8),
                          int32x4(9, 10, 11, 12)]);
 
   // Test that trying to create handle into the interior of a
   // int32x4 fails.
 
   assertThrowsInstanceOf(function() {
--- a/js/src/tests/ecma_6/TypedObject/simd/int32x4reify.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4reify.js
@@ -8,17 +8,17 @@ var summary = 'int32x4 reify';
  */
 
 var ArrayType = TypedObject.ArrayType;
 var int32x4 = TypedObject.int32x4;
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  var Array = new ArrayType(int32x4, 3);
+  var Array = int32x4.array(3);
   var array = new Array([int32x4(1, 2, 3, 4),
                          int32x4(5, 6, 7, 8),
                          int32x4(9, 10, 11, 12)]);
 
   // Test that reading array[1] produces a *copy* of int32x4, not an
   // alias into the array.
 
   var f = array[1];
--- a/js/src/tests/ecma_6/TypedObject/simd/int32x4setter.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4setter.js
@@ -8,17 +8,17 @@ var summary = 'int32x4 setting';
  */
 
 var ArrayType = TypedObject.ArrayType;
 var int32x4 = TypedObject.int32x4;
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  var Array = new ArrayType(int32x4, 3);
+  var Array = int32x4.array(3);
   var array = new Array([int32x4(1, 2, 3, 4),
                          int32x4(5, 6, 7, 8),
                          int32x4(9, 10, 11, 12)]);
   assertEq(array[1].w, 8);
 
   // Test that we are allowed to write int32x4 values into array,
   // but not other things.
 
--- a/js/src/tests/ecma_6/TypedObject/size_and_alignment.js
+++ b/js/src/tests/ecma_6/TypedObject/size_and_alignment.js
@@ -34,19 +34,19 @@ function runTests() {
 
     {type: float32, size: 4, alignment: 4},
     {type: float64, size: 8, alignment: 8},
 
     {type: new StructType({a: uint8, b: uint16, c: uint8}), size: 6, alignment: 2},
 
     {type: new StructType({a: uint8, b: uint8, c: uint16}), size: 4, alignment: 2},
 
-    {type: new ArrayType(uint8, 32), size: 32, alignment: 1},
-    {type: new ArrayType(uint16, 16), size: 32, alignment: 2},
-    {type: new ArrayType(uint32, 8), size: 32, alignment: 4},
+    {type: new ArrayType(uint8).dimension(32), size: 32, alignment: 1},
+    {type: new ArrayType(uint16).dimension(16), size: 32, alignment: 2},
+    {type: new ArrayType(uint32).dimension(8), size: 32, alignment: 4},
   ];
 
   for (var i = 0; i < typesAndAlignments.length; i++) {
     var test = typesAndAlignments[i];
     print("Type:", test.type.toSource(),
           "Size:", test.type.byteLength,
           "Alignment:", test.type.byteAlignment);
     assertEq(test.type.byteLength, test.size);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedObject/unsizedarrays.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
+var BUGNUMBER = 922115;
+var summary = 'TypedObjects ArrayType implementation';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var { ArrayType, StructType, uint8, float32, uint32 } = TypedObject;
+var ObjectType = TypedObject.Object;
+
+function runTests() {
+  print(BUGNUMBER + ": " + summary);
+
+  (function SimpleArrayOfTwoObjects() {
+    var Objects = new ArrayType(ObjectType);
+    var objects2 = new Objects(2, [{f: "Hello"},
+                                   {f: "World"}]);
+    assertEq(objects2[0].f, "Hello");
+    assertEq(objects2[1].f, "World");
+    assertEq(objects2.length, 2);
+  })();
+
+  (function EmbedUnsizedArraysBad() {
+    var Objects = new ArrayType(ObjectType);
+    assertThrows(() => new ArrayType(Objects));
+    assertThrows(() => new StructType({f: Objects}));
+  })();
+
+  (function MultipleSizes() {
+    var Uints = new ArrayType(uint32);
+    var Point = new StructType({values: new ArrayType(uint32).dimension(3)});
+
+    var uints = new Uints(3, [0, 1, 2]);
+    var point = new Point({values: uints});
+
+    assertEq(uints.length, point.values.length);
+    for (var i = 0; i < uints.length; i++) {
+      assertEq(uints[i], i);
+      assertEq(uints[i], point.values[i]);
+    }
+  })();
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+  print("Tests complete");
+}
+
+runTests();
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedObject/unsizedarraysanddim.js
@@ -0,0 +1,47 @@
+// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
+var BUGNUMBER = 922115;
+var summary = 'TypedObjects ArrayType implementation';
+
+var ArrayType = TypedObject.ArrayType;
+var StructType = TypedObject.StructType;
+var uint8 = TypedObject.uint8;
+var float32 = TypedObject.float32;
+var uint32 = TypedObject.uint32;
+var ObjectType = TypedObject.Object;
+
+function runTests() {
+  (function DimensionLinkedToUndimension() {
+    var UintsA = uint32.array();
+    var FiveUintsA = UintsA.dimension(5);
+    var FiveUintsB = uint32.array(5);
+
+    assertEq(true, FiveUintsA.equivalent(FiveUintsB));
+    assertEq(true, FiveUintsA.unsized === UintsA);
+    assertEq(true, FiveUintsB.unsized !== UintsA);
+  })();
+
+  (function PrototypeHierarchy() {
+    var Uint8s = uint8.array();
+    assertEq(Uint8s.prototype.__proto__, ArrayType.prototype.prototype);
+    Uint8s.prototype.sum = function() {
+      var r = 0;
+      for (var i = 0; i < this.length; i++)
+        r = uint8(r + this[i]);
+      return r;
+    };
+
+    var FiveUint8s = Uint8s.dimension(5);
+    assertEq(FiveUint8s.__proto__, Uint8s);
+
+    var fiveUint8s = new FiveUint8s([128, 128, 128, 128, 128]);
+    assertEq(128, fiveUint8s.sum());
+  })();
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+
+  print("Tests complete");
+}
+
+runTests();
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedObject/unsizedarraysembedded.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
+var BUGNUMBER = 922115;
+var summary = 'cannot embed unsized array in a struct';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var T = TypedObject;
+
+function runTests() {
+  print(BUGNUMBER + ": " + summary);
+
+  var Uints = T.uint32.array();
+  assertThrowsInstanceOf(() => { new T.StructType({f: Uints}) }, TypeError);
+  assertThrowsInstanceOf(() => { Uints.array() }, TypeError);
+
+  reportCompare(true, true);
+  print("Tests complete");
+}
+
+runTests();
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -162,16 +162,17 @@
     macro(toUTCString, toUTCString, "toUTCString") \
     macro(true, true_, "true") \
     macro(unescape, unescape, "unescape") \
     macro(uneval, uneval, "uneval") \
     macro(uint8, uint8, "uint8") \
     macro(uint8Clamped, uint8Clamped, "uint8Clamped") \
     macro(uint16, uint16, "uint16") \
     macro(uint32, uint32, "uint32") \
+    macro(unsized, unsized, "unsized") \
     macro(unwatch, unwatch, "unwatch") \
     macro(url, url, "url") \
     macro(usage, usage, "usage") \
     macro(useGrouping, useGrouping, "useGrouping") \
     macro(useAsm, useAsm, "use asm") \
     macro(useStrict, useStrict, "use strict") \
     macro(value, value, "value") \
     macro(valueOf, valueOf, "valueOf") \