Bug 578700 - ArrayType and BinaryArray implementation. r=nmatsakis
authorNikhil Marathe <nsm.nikhil@gmail.com>
Thu, 25 Jul 2013 17:59:14 -0700
changeset 140052 8253f5b39cbd23c09ee7485489d5daee520555a9
parent 140051 2aa4435cd79882c98217991bad72c65633269430
child 140053 43d1eada77d636ac906a4d9e9c4788896252496e
push id31583
push usernsm.nikhil@gmail.com
push dateFri, 26 Jul 2013 01:00:52 +0000
treeherdermozilla-inbound@13b28328f010 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnmatsakis
bugs578700
milestone25.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 578700 - ArrayType and BinaryArray implementation. r=nmatsakis
js/src/builtin/BinaryData.cpp
js/src/builtin/BinaryData.h
js/src/js.msg
js/src/jsapi.cpp
js/src/tests/ecma_6/BinaryData/arraytype.js
js/src/tests/ecma_6/BinaryData/memory.js
js/src/vm/CommonPropertyNames.h
--- a/js/src/builtin/BinaryData.cpp
+++ b/js/src/builtin/BinaryData.cpp
@@ -4,26 +4,47 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "builtin/BinaryData.h"
 
 #include "mozilla/FloatingPoint.h"
 
 #include "jscompartment.h"
+#include "jsfun.h"
 #include "jsobj.h"
 
+#include "vm/TypedArrayObject.h"
+#include "vm/String.h"
+#include "vm/StringBuffer.h"
+#include "vm/GlobalObject.h"
+
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 
-#include "vm/GlobalObject.h"
-#include "vm/TypedArrayObject.h"
-
 using namespace js;
 
+/*
+ * Reify() takes a complex binary data object `owner` and an offset and tries to
+ * convert the value of type `type` at that offset to a JS Value stored in
+ * `vp`.
+ *
+ * NOTE: `type` is NOT the type of `owner`, but the type of `owner.elementType` in
+ * case of BinaryArray or `owner[field].type` in case of BinaryStruct.
+ */
+static bool Reify(JSContext *cx, HandleObject type, HandleObject owner,
+                  size_t offset, MutableHandleValue vp);
+
+/*
+ * ConvertAndCopyTo() converts `from` to type `type` and stores the result in
+ * `mem`, which MUST be pre-allocated to the appropriate size for instances of
+ * `type`.
+ */
+static bool ConvertAndCopyTo(JSContext *cx, HandleObject type,
+                             HandleValue from, uint8_t *mem);
 JSBool TypeThrowError(JSContext *cx, unsigned argc, Value *vp)
 {
     return ReportIsNotFunction(cx, *vp);
 }
 
 JSBool DataThrowError(JSContext *cx, unsigned argc, Value *vp)
 {
     return ReportIsNotFunction(cx, *vp);
@@ -41,23 +62,130 @@ ReportTypeError(JSContext *cx, Value fro
 static void
 ReportTypeError(JSContext *cx, Value fromValue, JSString *toType)
 {
     const char *fnName = JS_EncodeString(cx, toType);
     ReportTypeError(cx, fromValue, fnName);
     JS_free(cx, (void *) fnName);
 }
 
+// The false return value allows callers to just return as soon as this is
+// called.
+// So yes this call is with side effects.
+static bool
+ReportTypeError(JSContext *cx, Value fromValue, HandleObject exemplar)
+{
+    RootedValue v(cx, ObjectValue(*exemplar));
+    ReportTypeError(cx, fromValue, ToString<CanGC>(cx, v));
+    return false;
+}
+
 static inline bool
-IsNumericType(JSObject *type)
+IsNumericType(HandleObject type)
 {
     return type && &NumericTypeClasses[NUMERICTYPE_UINT8] <= type->getClass() &&
                    type->getClass() <= &NumericTypeClasses[NUMERICTYPE_FLOAT64];
 }
 
+static inline bool
+IsArrayType(HandleObject type)
+{
+    return type && type->hasClass(&ArrayType::class_);
+}
+
+static inline bool
+IsStructType(HandleObject type)
+{
+    return type && type->hasClass(&StructTypeClass);
+}
+
+static inline bool
+IsComplexType(HandleObject type)
+{
+    return IsArrayType(type) || IsStructType(type);
+}
+
+static inline bool
+IsBinaryType(HandleObject type)
+{
+    return IsNumericType(type) || IsComplexType(type);
+}
+
+static inline bool
+IsBinaryArray(HandleObject type)
+{
+    return type && type->hasClass(&BinaryArray::class_);
+}
+
+static inline bool
+IsBlock(HandleObject type)
+{
+    return type->hasClass(&BinaryArray::class_);
+    /* TODO || type->hasClass(&BinaryStruct::class_); */
+}
+
+static inline JSObject *
+GetType(HandleObject block)
+{
+    JS_ASSERT(IsBlock(block));
+    return block->getFixedSlot(SLOT_DATATYPE).toObjectOrNull();
+}
+
+static size_t
+GetMemSize(JSContext *cx, HandleObject type)
+{
+    if (IsComplexType(type))
+        return type->getFixedSlot(SLOT_MEMSIZE).toInt32();
+
+    RootedObject typeObj(cx, type);
+    RootedValue val(cx);
+    JS_ASSERT(IsNumericType(type));
+    JSObject::getProperty(cx, typeObj, typeObj, cx->names().bytes, &val);
+    return val.toInt32();
+}
+
+static size_t
+GetAlign(JSContext *cx, HandleObject type)
+{
+    if (IsComplexType(type))
+        return type->getFixedSlot(SLOT_ALIGN).toInt32();
+
+    RootedObject typeObj(cx, type);
+    RootedValue val(cx);
+    JS_ASSERT(&NumericTypeClasses[NUMERICTYPE_UINT8] <= type->getClass() &&
+              type->getClass() <= &NumericTypeClasses[NUMERICTYPE_FLOAT64]);
+    JSObject::getProperty(cx, typeObj, typeObj, cx->names().bytes, &val);
+    return val.toInt32();
+}
+
+static bool
+IsSameBinaryDataType(JSContext *cx, HandleObject type1, HandleObject type2)
+{
+    JS_ASSERT(IsBinaryType(type1));
+    JS_ASSERT(IsBinaryType(type2));
+
+    if (IsNumericType(type1)) {
+        return type1->hasClass(type2->getClass());
+    } else if (IsArrayType(type1) && IsArrayType(type2)) {
+        if (ArrayType::length(cx, type1) != ArrayType::length(cx, type2))
+            return false;
+
+        RootedObject elementType1(cx, ArrayType::elementType(cx, type1));
+        RootedObject elementType2(cx, ArrayType::elementType(cx, type2));
+        return IsSameBinaryDataType(cx, elementType1, elementType2);
+    } else if (IsStructType(type1) && IsStructType(type2)) {
+        // Struct types compare on structural equality.
+        // FIXME(nsm): Implement.
+        JS_ASSERT(0);
+        return false;
+    }
+
+    return false;
+}
+
 template <typename Domain, typename Input>
 bool
 InRange(Input x)
 {
     return std::numeric_limits<Domain>::min() <= x &&
            x <= std::numeric_limits<Domain>::max();
 }
 
@@ -160,54 +288,893 @@ NumericType<T>::call(JSContext *cx, unsi
         return false;
     }
 
     RootedValue arg(cx, args[0]);
     T answer;
     if (!convert(cx, arg, &answer))
         return false; // convert() raises TypeError.
 
-    // TODO Once reify is implemented (in a later patch) this will be replaced
-    // by a call to reify.
-    args.rval().set(NumberValue(answer));
+    RootedValue reified(cx);
+    if (!NumericType<T>::reify(cx, &answer, &reified)) {
+        return false;
+    }
+
+    args.rval().set(reified);
     return true;
 }
 
 template<unsigned int N>
 JSBool
 NumericTypeToString(JSContext *cx, unsigned int argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JS_ASSERT(NUMERICTYPE_UINT8 <= N && N <= NUMERICTYPE_FLOAT64);
     JSString *s = JS_NewStringCopyZ(cx, NumericTypeClasses[N].name);
     args.rval().set(StringValue(s));
     return true;
 }
 
+/*
+ * When creating:
+ *   var A = new ArrayType(uint8, 10)
+ * or
+ *   var S = new StructType({...})
+ *
+ * A.prototype.__proto__ === ArrayType.prototype.prototype (and similar for
+ * StructType).
+ *
+ * This function takes a reference to either ArrayType or StructType and
+ * returns a JSObject which can be set as A.prototype.
+ */
+JSObject *
+SetupAndGetPrototypeObjectForComplexTypeInstance(JSContext *cx,
+                                                 HandleObject complexTypeGlobal)
+{
+    RootedObject global(cx, cx->compartment()->maybeGlobal());
+    RootedValue complexTypePrototypeVal(cx);
+    RootedValue complexTypePrototypePrototypeVal(cx);
+
+    if (!JSObject::getProperty(cx, complexTypeGlobal, complexTypeGlobal,
+                               cx->names().classPrototype, &complexTypePrototypeVal))
+        return NULL;
+
+    RootedObject complexTypePrototypeObj(cx,
+        complexTypePrototypeVal.toObjectOrNull());
+
+    if (!JSObject::getProperty(cx, complexTypePrototypeObj,
+                               complexTypePrototypeObj,
+                               cx->names().classPrototype,
+                               &complexTypePrototypePrototypeVal))
+        return NULL;
+
+    RootedObject prototypeObj(cx,
+        NewObjectWithGivenProto(cx, &JSObject::class_, NULL, global));
+
+    if (!JS_SetPrototype(cx, prototypeObj,
+                         complexTypePrototypePrototypeVal.toObjectOrNull()))
+        return NULL;
+
+    return prototypeObj;
+}
+
+Class ArrayType::class_ = {
+    "ArrayType",
+    JSCLASS_HAS_RESERVED_SLOTS(TYPE_RESERVED_SLOTS) |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayType),
+    JS_PropertyStub,
+    JS_DeletePropertyStub,
+    JS_PropertyStub,
+    JS_StrictPropertyStub,
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    BinaryArray::construct,
+    NULL
+};
+
+Class BinaryArray::class_ = {
+    "BinaryArray",
+    Class::NON_NATIVE |
+    JSCLASS_HAS_RESERVED_SLOTS(BLOCK_RESERVED_SLOTS) |
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayType),
+    JS_PropertyStub,
+    JS_DeletePropertyStub,
+    JS_PropertyStub,
+    JS_StrictPropertyStub,
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub,
+    BinaryArray::finalize,
+    NULL,           /* checkAccess */
+    NULL,           /* call        */
+    NULL,           /* construct   */
+    NULL,           /* hasInstance */
+    BinaryArray::obj_trace,
+    JS_NULL_CLASS_EXT,
+    {
+        BinaryArray::obj_lookupGeneric,
+        BinaryArray::obj_lookupProperty,
+        BinaryArray::obj_lookupElement,
+        BinaryArray::obj_lookupSpecial,
+        NULL,
+        NULL,
+        NULL,
+        NULL,
+        BinaryArray::obj_getGeneric,
+        BinaryArray::obj_getProperty,
+        BinaryArray::obj_getElement,
+        BinaryArray::obj_getElementIfPresent,
+        BinaryArray::obj_getSpecial,
+        BinaryArray::obj_setGeneric,
+        BinaryArray::obj_setProperty,
+        BinaryArray::obj_setElement,
+        BinaryArray::obj_setSpecial,
+        BinaryArray::obj_getGenericAttributes,
+        BinaryArray::obj_getPropertyAttributes,
+        BinaryArray::obj_getElementAttributes,
+        BinaryArray::obj_getSpecialAttributes,
+        NULL,
+        NULL,
+        NULL,
+        NULL,
+        NULL,
+        NULL,
+        NULL,
+        BinaryArray::obj_enumerate,
+        NULL,
+    }
+};
+
+inline uint32_t
+ArrayType::length(JSContext *cx, HandleObject obj)
+{
+    JS_ASSERT(obj && IsArrayType(obj));
+    RootedValue vp(cx, UndefinedValue());
+    if (!JSObject::getProperty(cx, obj, obj, cx->names().length, &vp))
+        return -1;
+    JS_ASSERT(vp.isInt32());
+    JS_ASSERT(vp.toInt32() >= 0);
+    return (uint32_t) vp.toInt32();
+}
+
+inline JSObject *
+ArrayType::elementType(JSContext *cx, HandleObject array)
+{
+    JS_ASSERT(IsArrayType(array));
+    RootedObject arr(cx, array);
+    RootedValue elementTypeVal(cx);
+    if (!JSObject::getProperty(cx, arr, arr,
+                               cx->names().elementType, &elementTypeVal))
+
+    JS_ASSERT(elementTypeVal.isObject());
+    return elementTypeVal.toObjectOrNull();
+}
+
+bool
+ArrayType::convertAndCopyTo(JSContext *cx, HandleObject exemplar,
+                            HandleValue from, uint8_t *mem)
+{
+    if (!from.isObject()) {
+        return ReportTypeError(cx, from, exemplar);
+    }
+
+    RootedObject val(cx, from.toObjectOrNull());
+    if (IsBlock(val)) {
+        RootedObject type(cx, GetType(val));
+        if (IsSameBinaryDataType(cx, exemplar, type)) {
+            uint8_t *priv = (uint8_t*) val->getPrivate();
+            memcpy(mem, priv, GetMemSize(cx, exemplar));
+            return true;
+        }
+        return ReportTypeError(cx, from, exemplar);
+    }
+
+    RootedObject valRooted(cx, val);
+    RootedValue fromLenVal(cx);
+    if (!JSObject::getProperty(cx, valRooted, valRooted,
+                               cx->names().length, &fromLenVal) ||
+        !fromLenVal.isInt32())
+    {
+        return ReportTypeError(cx, from, exemplar);
+    }
+
+    uint32_t fromLen = fromLenVal.toInt32();
+
+    if (ArrayType::length(cx, exemplar) != fromLen) {
+        return ReportTypeError(cx, from, exemplar);
+    }
+
+    RootedObject elementType(cx, ArrayType::elementType(cx, exemplar));
+
+    uint32_t offsetMult = GetMemSize(cx, elementType);
+
+    for (uint32_t i = 0; i < fromLen; i++) {
+        RootedValue fromElem(cx);
+        if (!JSObject::getElement(cx, valRooted, valRooted, i, &fromElem)) {
+            return ReportTypeError(cx, from, exemplar);
+        }
+
+        if (!ConvertAndCopyTo(cx, elementType, fromElem,
+                              (uint8_t *) mem + (offsetMult * i))) {
+            return false; // TypeError raised by ConvertAndCopyTo.
+        }
+    }
+
+    return true;
+}
+
+inline bool
+ArrayType::reify(JSContext *cx, HandleObject type,
+                 HandleObject owner, size_t offset, MutableHandleValue to)
+{
+    JSObject *obj = BinaryArray::create(cx, type, owner, offset);
+    if (!obj)
+        return false;
+    to.setObject(*obj);
+    return true;
+}
+
+JSObject *
+ArrayType::create(JSContext *cx, HandleObject arrayTypeGlobal,
+                  HandleObject elementType, uint32_t length)
+{
+    JS_ASSERT(elementType);
+    JS_ASSERT(IsBinaryType(elementType));
+
+    RootedObject obj(cx, NewBuiltinClassInstance(cx, &ArrayType::class_));
+    if (!obj)
+        return NULL;
+
+    RootedValue elementTypeVal(cx, ObjectValue(*elementType));
+    if (!JSObject::defineProperty(cx, obj, cx->names().elementType,
+                                  elementTypeVal, NULL, NULL,
+                                  JSPROP_READONLY | JSPROP_PERMANENT))
+        return NULL;
+
+    RootedValue lengthVal(cx, Int32Value(length));
+    if (!JSObject::defineProperty(cx, obj, cx->names().length,
+                                  lengthVal, NULL, NULL,
+                                  JSPROP_READONLY | JSPROP_PERMANENT))
+        return NULL;
+
+    RootedValue elementTypeBytes(cx);
+    if (!JSObject::getProperty(cx, elementType, elementType, cx->names().bytes, &elementTypeBytes))
+        return NULL;
+
+    JS_ASSERT(elementTypeBytes.isInt32());
+
+    /* since this is the JS visible size and maybe not
+     * the actual size in terms of memory layout, it is
+     * always elementType.bytes * length */
+    RootedValue typeBytes(cx, NumberValue(elementTypeBytes.toInt32() * length));
+    if (!JSObject::defineProperty(cx, obj, cx->names().bytes,
+                                  typeBytes,
+                                  NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT))
+        return NULL;
+
+    obj->setFixedSlot(SLOT_MEMSIZE,
+                      Int32Value(::GetMemSize(cx, elementType) * length));
+
+    obj->setFixedSlot(SLOT_ALIGN, Int32Value(::GetAlign(cx, elementType)));
+
+    RootedObject prototypeObj(cx,
+        SetupAndGetPrototypeObjectForComplexTypeInstance(cx, arrayTypeGlobal));
+
+    if (!prototypeObj)
+        return NULL;
+
+    if (!LinkConstructorAndPrototype(cx, obj, prototypeObj))
+        return NULL;
+
+    if (!JS_DefineFunction(cx, prototypeObj, "fill", BinaryArray::fill, 1, 0))
+        return NULL;
+
+    RootedId id(cx, NON_INTEGER_ATOM_TO_JSID(cx->names().length));
+    unsigned flags = JSPROP_SHARED | JSPROP_GETTER | JSPROP_PERMANENT;
+
+    RootedObject global(cx, cx->compartment()->maybeGlobal());
+    JSObject *getter =
+        NewFunction(cx, NullPtr(), BinaryArray::lengthGetter,
+                    0, JSFunction::NATIVE_FUN, global, NullPtr());
+    if (!getter)
+        return NULL;
+
+    RootedValue value(cx);
+    if (!DefineNativeProperty(cx, prototypeObj, id, value,
+                                JS_DATA_TO_FUNC_PTR(PropertyOp, getter), NULL,
+                                flags, 0, 0))
+        return NULL;
+    return obj;
+}
+
 JSBool
-createArrayType(JSContext *cx, unsigned argc, Value *vp)
+ArrayType::construct(JSContext *cx, unsigned argc, Value *vp)
 {
-    return false;
+    if (!JS_IsConstructing(cx, vp)) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
+                             JSMSG_NOT_FUNCTION, "ArrayType");
+        return false;
+    }
+
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (argc != 2 ||
+        !args[0].isObject() ||
+        !args[1].isNumber() ||
+        args[1].toNumber() < 0)
+    {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
+                             JSMSG_BINARYDATA_ARRAYTYPE_BAD_ARGS);
+        return false;
+    }
+
+    RootedObject arrayTypeGlobal(cx, &args.callee());
+    RootedObject elementType(cx, args[0].toObjectOrNull());
+
+    if (!IsBinaryType(elementType)) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
+                             JSMSG_BINARYDATA_ARRAYTYPE_BAD_ARGS);
+        return false;
+    }
+
+    JSObject *obj = create(cx, arrayTypeGlobal, elementType, args[1].toInt32());
+    if (!obj)
+        return false;
+    args.rval().setObject(*obj);
+    return true;
 }
 
 JSBool
 createStructType(JSContext *cx, unsigned argc, Value *vp)
 {
     return false;
 }
 
 JSBool
 DataInstanceUpdate(JSContext *cx, unsigned argc, Value *vp)
 {
     return false;
 }
 
 JSBool
-ArrayTypeObject::repeat(JSContext *cx, unsigned int argc, Value *vp)
+ArrayType::repeat(JSContext *cx, unsigned int argc, Value *vp)
+{
+    return false;
+}
+
+JSBool
+ArrayType::toString(JSContext *cx, unsigned int argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    RootedObject thisObj(cx, args.thisv().toObjectOrNull());
+    JS_ASSERT(thisObj);
+    if (!IsArrayType(thisObj)) {
+        RootedObject obj(cx, args.thisv().toObjectOrNull());
+        JS_ReportErrorNumber(cx, js_GetErrorMessage,
+                             NULL, JSMSG_INCOMPATIBLE_PROTO, "ArrayType", "toString", JS_GetClass(obj)->name);
+        return false;
+    }
+
+    StringBuffer contents(cx);
+    contents.append("ArrayType(");
+
+    RootedValue elementTypeVal(cx, ObjectValue(*elementType(cx, thisObj)));
+    JSString *str = ToString<CanGC>(cx, elementTypeVal);
+
+    contents.append(str);
+    contents.append(", ");
+
+    Value len = NumberValue(length(cx, thisObj));
+    contents.append(JS_ValueToString(cx, len));
+    contents.append(")");
+    args.rval().setString(contents.finishString());
+    return true;
+}
+
+JSObject *
+BinaryArray::createEmpty(JSContext *cx, HandleObject type)
+{
+    JS_ASSERT(IsArrayType(type));
+    RootedObject typeRooted(cx, type);
+
+    RootedValue protoVal(cx);
+    if (!JSObject::getProperty(cx, typeRooted, typeRooted,
+                               cx->names().classPrototype, &protoVal))
+        return NULL;
+
+    RootedObject obj(cx,
+        NewObjectWithClassProto(cx, &BinaryArray::class_,
+                                protoVal.toObjectOrNull(), NULL));
+    obj->setFixedSlot(SLOT_DATATYPE, ObjectValue(*type));
+    obj->setFixedSlot(SLOT_BLOCKREFOWNER, NullValue());
+    return obj;
+}
+
+JSObject *
+BinaryArray::create(JSContext *cx, HandleObject type)
+{
+    JSObject *obj = createEmpty(cx, type);
+    if (!obj)
+        return NULL;
+
+    int32_t memsize = GetMemSize(cx, type);
+    void *memory = JS_malloc(cx, memsize);
+    if (!memory)
+        return NULL;
+    memset(memory, 0, memsize);
+    obj->setPrivate(memory);
+    return obj;
+}
+
+JSObject *
+BinaryArray::create(JSContext *cx, HandleObject type, HandleValue initial)
+{
+    JSObject *obj = create(cx, type);
+    if (!obj)
+        return NULL;
+
+    uint8_t *memory = (uint8_t*) obj->getPrivate();
+    if (!ConvertAndCopyTo(cx, type, initial, memory))
+        return NULL;
+
+    return obj;
+}
+
+JSObject *
+BinaryArray::create(JSContext *cx, HandleObject type,
+                    HandleObject owner, size_t offset)
+{
+    JS_ASSERT(IsBlock(owner));
+    JSObject *obj = createEmpty(cx, type);
+    if (!obj)
+        return NULL;
+
+    obj->setPrivate(((uint8_t *) owner->getPrivate()) + offset);
+    obj->setFixedSlot(SLOT_BLOCKREFOWNER, ObjectValue(*owner));
+    return obj;
+}
+
+JSBool
+BinaryArray::construct(JSContext *cx, unsigned int argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    RootedObject callee(cx, &args.callee());
+
+    if (!IsArrayType(callee)) {
+        ReportTypeError(cx, args.calleev(), "is not an ArrayType");
+        return false;
+    }
+
+    JSObject *obj = NULL;
+    if (argc == 1) {
+        RootedValue v(cx, args[0]);
+        obj = create(cx, callee, v);
+    } else {
+        obj = create(cx, callee);
+    }
+
+    if (obj)
+        args.rval().setObject(*obj);
+
+    return obj != NULL;
+}
+
+void
+BinaryArray::finalize(js::FreeOp *op, JSObject *obj)
+{
+    if (obj->getFixedSlot(SLOT_BLOCKREFOWNER).isNull())
+        op->free_(obj->getPrivate());
+}
+
+void
+BinaryArray::obj_trace(JSTracer *tracer, JSObject *obj)
+{
+    Value val = obj->getFixedSlot(SLOT_BLOCKREFOWNER);
+    if (val.isObject()) {
+        HeapPtrObject owner(val.toObjectOrNull());
+        MarkObject(tracer, &owner, "binaryarray.blockRefOwner");
+    }
+}
+
+JSBool
+BinaryArray::lengthGetter(JSContext *cx, unsigned int argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    RootedObject thisObj(cx, args.thisv().toObjectOrNull());
+    JS_ASSERT(IsBinaryArray(thisObj));
+
+    RootedObject type(cx, GetType(thisObj));
+    vp->setInt32(ArrayType::length(cx, type));
+    return true;
+}
+
+JSBool
+BinaryArray::forEach(JSContext *cx, unsigned int argc, Value *vp)
+{
+    JS_ASSERT(0);
+    return false;
+}
+
+JSBool
+BinaryArray::subarray(JSContext *cx, unsigned int argc, Value *vp)
+{
+    JS_ASSERT(0);
+    return false;
+}
+
+JSBool
+BinaryArray::fill(JSContext *cx, unsigned int argc, Value *vp)
+{
+    JS_ASSERT(0);
+    return false;
+}
+
+JSBool
+BinaryArray::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
+                                MutableHandleObject objp, MutableHandleShape propp)
+{
+    JS_ASSERT(IsBinaryArray(obj));
+    RootedObject type(cx, GetType(obj));
+
+    uint32_t index;
+    if (js_IdIsIndex(id, &index) &&
+        index < ArrayType::length(cx, type)) {
+        MarkNonNativePropertyFound(propp);
+        objp.set(obj);
+        return true;
+    }
+
+    if (JSID_IS_ATOM(id, cx->names().length)) {
+        MarkNonNativePropertyFound(propp);
+        objp.set(obj);
+        return true;
+    }
+
+    RootedObject proto(cx, obj->getProto());
+    if (!proto) {
+        objp.set(NULL);
+        propp.set(NULL);
+        return true;
+    }
+
+    return JSObject::lookupGeneric(cx, proto, id, objp, propp);
+}
+
+JSBool
+BinaryArray::obj_lookupProperty(JSContext *cx,
+                                HandleObject obj,
+                                HandlePropertyName name,
+                                MutableHandleObject objp,
+                                MutableHandleShape propp)
+{
+    RootedId id(cx, NameToId(name));
+    return obj_lookupGeneric(cx, obj, id, objp, propp);
+}
+
+JSBool
+BinaryArray::obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
+                                MutableHandleObject objp, MutableHandleShape propp)
+{
+    JS_ASSERT(IsBinaryArray(obj));
+    RootedObject type(cx, GetType(obj));
+
+    if (index < ArrayType::length(cx, type)) {
+        MarkNonNativePropertyFound(propp);
+        objp.set(obj);
+        return true;
+    }
+
+    RootedObject proto(cx, obj->getProto());
+    if (proto)
+        return JSObject::lookupElement(cx, proto, index, objp, propp);
+
+    objp.set(NULL);
+    propp.set(NULL);
+    return true;
+}
+
+JSBool
+BinaryArray::obj_lookupSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
+                                MutableHandleObject objp, MutableHandleShape propp)
+{
+    RootedId id(cx, SPECIALID_TO_JSID(sid));
+    return obj_lookupGeneric(cx, obj, id, objp, propp);
+}
+
+JSBool
+BinaryArray::obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
+                             HandleId id, MutableHandleValue vp)
+{
+    uint32_t index;
+    if (js_IdIsIndex(id, &index)) {
+        return obj_getElement(cx, obj, receiver, index, vp);
+    }
+
+    RootedValue idValue(cx, IdToValue(id));
+    Rooted<PropertyName*> name(cx, ToAtom<CanGC>(cx, idValue)->asPropertyName());
+    return obj_getProperty(cx, obj, receiver, name, vp);
+}
+
+JSBool
+BinaryArray::obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
+                              HandlePropertyName name, MutableHandleValue vp)
 {
+    RootedObject proto(cx, obj->getProto());
+    if (!proto) {
+        vp.setUndefined();
+        return true;
+    }
+
+    return JSObject::getProperty(cx, proto, receiver, name, vp);
+}
+
+JSBool
+BinaryArray::obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver,
+                             uint32_t index, MutableHandleValue vp)
+{
+    RootedObject type(cx, GetType(obj));
+
+    if (index < ArrayType::length(cx, type)) {
+        RootedObject elementType(cx, ArrayType::elementType(cx, type));
+        size_t offset = GetMemSize(cx, elementType) * index;
+        return Reify(cx, elementType, obj, offset, vp);
+    }
+
+    RootedObject proto(cx, obj->getProto());
+    if (!proto) {
+        vp.setUndefined();
+        return true;
+    }
+
+    return JSObject::getElement(cx, proto, receiver, index, vp);
+}
+
+JSBool
+BinaryArray::obj_getElementIfPresent(JSContext *cx, HandleObject obj,
+                                     HandleObject receiver, uint32_t index,
+                                     MutableHandleValue vp, bool *present)
+{
+    RootedObject type(cx, GetType(obj));
+
+    if (index < ArrayType::length(cx, type)) {
+        *present = true;
+        return obj_getElement(cx, obj, receiver, index, vp);
+    }
+
+    *present = false;
+    vp.setUndefined();
+    return true;
+}
+
+JSBool
+BinaryArray::obj_getSpecial(JSContext *cx, HandleObject obj, HandleObject receiver,
+                             HandleSpecialId sid, MutableHandleValue vp)
+{
+    RootedId id(cx, SPECIALID_TO_JSID(sid));
+    return obj_getGeneric(cx, obj, receiver, id, vp);
+}
+
+JSBool
+BinaryArray::obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
+                             MutableHandleValue vp, JSBool strict)
+{
+	uint32_t index;
+	if (js_IdIsIndex(id, &index)) {
+	    return obj_setElement(cx, obj, index, vp, strict);
+    }
+
+    if (JSID_IS_ATOM(id, cx->names().length)) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage,
+                             NULL, JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
+        return false;
+    }
+
+	return true;
+}
+
+JSBool
+BinaryArray::obj_setProperty(JSContext *cx, HandleObject obj,
+                             HandlePropertyName name, MutableHandleValue vp,
+                             JSBool strict)
+{
+    RootedId id(cx, NameToId(name));
+    return obj_setGeneric(cx, obj, id, vp, strict);
+}
+
+JSBool
+BinaryArray::obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
+                             MutableHandleValue vp, JSBool strict)
+{
+    RootedObject type(cx, GetType(obj));
+    if (index >= ArrayType::length(cx, type)) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage,
+                             NULL, JSMSG_BINARYDATA_BINARYARRAY_BAD_INDEX);
+        return false;
+    }
+
+    RootedValue elementTypeVal(cx);
+    if (!JSObject::getProperty(cx, type, type, cx->names().elementType,
+                               &elementTypeVal))
+        return false;
+
+    RootedObject elementType(cx, elementTypeVal.toObjectOrNull());
+    uint32_t offset = GetMemSize(cx, elementType) * index;
+
+    bool result =
+        ConvertAndCopyTo(cx, elementType, vp,
+                         ((uint8_t*) obj->getPrivate()) + offset );
+
+    if (!result) {
+        return false;
+    }
+
+    return true;
+}
+
+JSBool
+BinaryArray::obj_setSpecial(JSContext *cx, HandleObject obj,
+                             HandleSpecialId sid, MutableHandleValue vp,
+                             JSBool strict)
+{
+    RootedId id(cx, SPECIALID_TO_JSID(sid));
+    return obj_setGeneric(cx, obj, id, vp, strict);
+}
+
+JSBool
+BinaryArray::obj_getGenericAttributes(JSContext *cx, HandleObject obj,
+                                       HandleId id, unsigned *attrsp)
+{
+    uint32_t index;
+    RootedObject type(cx, GetType(obj));
+
+    if (js_IdIsIndex(id, &index) &&
+        index < ArrayType::length(cx, type)) {
+        *attrsp = JSPROP_ENUMERATE | JSPROP_PERMANENT; // should we report JSPROP_INDEX?
+        return true;
+    }
+
+    if (JSID_IS_ATOM(id, cx->names().length)) {
+        *attrsp = JSPROP_READONLY | JSPROP_PERMANENT;
+        return true;
+    }
+
+	return false;
+}
+
+JSBool
+BinaryArray::obj_getPropertyAttributes(JSContext *cx, HandleObject obj,
+                                        HandlePropertyName name, unsigned *attrsp)
+{
+    RootedId id(cx, NameToId(name));
+    return obj_getGenericAttributes(cx, obj, id, attrsp);
+}
+
+JSBool
+BinaryArray::obj_getElementAttributes(JSContext *cx, HandleObject obj,
+                                       uint32_t index, unsigned *attrsp)
+{
+    RootedId id(cx, INT_TO_JSID(index));
+    return obj_getGenericAttributes(cx, obj, id, attrsp);
+}
+
+JSBool
+BinaryArray::obj_getSpecialAttributes(JSContext *cx, HandleObject obj,
+                                       HandleSpecialId sid, unsigned *attrsp)
+{
+    RootedId id(cx, SPECIALID_TO_JSID(sid));
+    return obj_getGenericAttributes(cx, obj, id, attrsp);
+}
+
+JSBool
+BinaryArray::obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
+                            MutableHandleValue statep, MutableHandleId idp)
+{
+    JS_ASSERT(IsBinaryArray(obj));
+
+    RootedObject type(cx, GetType(obj));
+
+    uint32_t index;
+    switch (enum_op) {
+        case JSENUMERATE_INIT_ALL:
+        case JSENUMERATE_INIT:
+            statep.setInt32(0);
+            idp.set(INT_TO_JSID(ArrayType::length(cx, type)));
+            break;
+
+        case JSENUMERATE_NEXT:
+            index = static_cast<uint32_t>(statep.toInt32());
+
+            if (index < ArrayType::length(cx, type)) {
+                idp.set(INT_TO_JSID(index));
+                statep.setInt32(index + 1);
+            } else {
+                JS_ASSERT(index == ArrayType::length(cx, type));
+                statep.setNull();
+            }
+
+            break;
+
+        case JSENUMERATE_DESTROY:
+            statep.setNull();
+            break;
+    }
+
+	return true;
+}
+
+static bool
+Reify(JSContext *cx, HandleObject type,
+      HandleObject owner, size_t offset, MutableHandleValue to)
+{
+    if (IsArrayType(type)) {
+        return ArrayType::reify(cx, type, owner, offset, to);
+    }
+    if (type->hasClass(&StructTypeClass))
+        JS_ASSERT(0);
+    JS_ASSERT(&NumericTypeClasses[NUMERICTYPE_UINT8] <= type->getClass() &&
+              type->getClass() <= &NumericTypeClasses[NUMERICTYPE_FLOAT64]);
+
+#define REIFY_CASES(constant_, type_)\
+        case constant_:\
+            return NumericType<type_##_t>::reify(cx,\
+                    ((uint8_t *) owner->getPrivate()) + offset, to);
+
+    switch(type->getFixedSlot(SLOT_DATATYPE).toInt32()) {
+        BINARYDATA_FOR_EACH_NUMERIC_TYPES(REIFY_CASES);
+        default:
+            abort();
+    }
+#undef REIFY_CASES
+    return false;
+}
+
+static bool
+ConvertAndCopyTo(JSContext *cx, HandleObject type, HandleValue from, uint8_t *mem)
+{
+    if (IsComplexType(type)) {
+        uint8_t *block = NULL;
+        if (IsArrayType(type)) {
+            if (!ArrayType::convertAndCopyTo(cx, type, from, mem))
+                return false;
+        } else if (type->hasClass(&StructTypeClass)) {
+            JS_ASSERT(0);
+        } else {
+            JS_ASSERT(0);
+        }
+
+        return true;
+    }
+
+    JS_ASSERT(&NumericTypeClasses[NUMERICTYPE_UINT8] <= type->getClass() &&
+              type->getClass() <= &NumericTypeClasses[NUMERICTYPE_FLOAT64]);
+
+#define CONVERT_CASES(constant_, type_)\
+        case constant_:\
+                       {\
+            type_##_t temp;\
+            bool ok = NumericType<type_##_t>::convert(cx, from, &temp);\
+            if (!ok)\
+                return false;\
+            memcpy(mem, &temp, sizeof(type_##_t));\
+            return true; }
+
+    switch(type->getFixedSlot(0).toInt32()) {
+        BINARYDATA_FOR_EACH_NUMERIC_TYPES(CONVERT_CASES);
+        default:
+            abort();
+    }
+#undef CONVERT_CASES
     return false;
 }
 
 bool
 GlobalObject::initDataObject(JSContext *cx, Handle<GlobalObject *> global)
 {
     RootedObject DataProto(cx);
     DataProto = NewObjectWithGivenProto(cx, &DataClass,
@@ -259,19 +1226,21 @@ GlobalObject::initTypeObject(JSContext *
                                        TypeCtor, TypeProto))
         return false;
 
     global->setReservedSlot(JSProto_Type, ObjectValue(*TypeCtor));
     return true;
 }
 
 static JSObject *
-SetupComplexHeirarchy(JSContext *cx, Handle<GlobalObject *> global,
-                      HandleObject complexObject)
+SetupComplexHeirarchy(JSContext *cx, HandleObject obj, JSProtoKey protoKey,
+                      HandleObject complexObject, MutableHandleObject proto,
+                      MutableHandleObject protoProto)
 {
+    Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
     // get the 'Type' constructor
     RootedObject TypeObject(cx, global->getOrCreateTypeObject(cx));
     if (!TypeObject)
         return NULL;
 
     // Set complexObject.__proto__ = Type
     if (!JS_SetPrototype(cx, complexObject, TypeObject))
         return NULL;
@@ -284,70 +1253,101 @@ SetupComplexHeirarchy(JSContext *cx, Han
     if (!JSObject::getProperty(cx, DataObject, DataObject,
                                cx->names().classPrototype, &DataProtoVal))
         return NULL;
 
     RootedObject DataProto(cx, DataProtoVal.toObjectOrNull());
     if (!DataProto)
         return NULL;
 
-    // Set complexObject.prototype.__proto__ = Data
-    RootedObject prototypeObj(cx, JS_NewObject(cx, NULL, NULL, global));
+    RootedObject prototypeObj(cx,
+        NewObjectWithGivenProto(cx, &JSObject::class_, NULL, global));
+    if (!prototypeObj)
+        return NULL;
     if (!LinkConstructorAndPrototype(cx, complexObject, prototypeObj))
         return NULL;
+    if (!DefineConstructorAndPrototype(cx, global, protoKey,
+                                       complexObject, prototypeObj))
+        return NULL;
 
+    // Set complexObject.prototype.__proto__ = Data
     if (!JS_SetPrototype(cx, prototypeObj, DataObject))
         return NULL;
 
+    proto.set(prototypeObj);
+
     // Set complexObject.prototype.prototype.__proto__ = Data.prototype
     RootedObject prototypePrototypeObj(cx, JS_NewObject(cx, NULL, NULL,
                                        global));
 
     if (!LinkConstructorAndPrototype(cx, prototypeObj,
                                      prototypePrototypeObj))
         return NULL;
 
     if (!JS_SetPrototype(cx, prototypePrototypeObj, DataProto))
         return NULL;
 
+    protoProto.set(prototypePrototypeObj);
+
     return complexObject;
 }
 
 static JSObject *
-InitComplexClasses(JSContext *cx, Handle<GlobalObject *> global)
+InitArrayType(JSContext *cx, HandleObject obj)
 {
-    RootedFunction ArrayTypeFun(cx,
-            JS_DefineFunction(cx, global, "ArrayType", createArrayType, 1, 0));
+    JS_ASSERT(obj->isNative());
+    Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
+    RootedFunction ctor(cx,
+        global->createConstructor(cx, ArrayType::construct,
+                                  cx->names().ArrayType, 2));
 
-    if (!ArrayTypeFun)
+    if (!ctor)
         return NULL;
 
-    if (!SetupComplexHeirarchy(cx, global, ArrayTypeFun))
+    RootedObject proto(cx);
+    RootedObject protoProto(cx);
+    if (!SetupComplexHeirarchy(cx, obj, JSProto_ArrayType,
+                               ctor, &proto, &protoProto))
+        return NULL;
+
+    if (!JS_DefineFunction(cx, proto, "repeat", ArrayType::repeat, 1, 0))
         return NULL;
 
-    // ArrayType.prototype.repeat
-    RootedValue ArrayTypePrototypeVal(cx);
-    if (!JSObject::getProperty(cx, ArrayTypeFun, ArrayTypeFun,
-                               cx->names().classPrototype, &ArrayTypePrototypeVal))
+    if (!JS_DefineFunction(cx, proto, "toString", ArrayType::toString, 0, 0))
+        return NULL;
+
+    if (!JS_DefineFunction(cx, protoProto, "forEach", BinaryArray::forEach, 1, 0))
+        return NULL;
+
+    if (!JS_DefineFunction(cx, protoProto, "subarray",
+                           BinaryArray::subarray, 1, 0))
         return NULL;
 
-    if (!JS_DefineFunction(cx, ArrayTypePrototypeVal.toObjectOrNull(), "repeat",
-                           ArrayTypeObject::repeat, 1, 0))
-        return NULL;
+    return proto;
+}
 
+static JSObject *
+InitStructType(JSContext *cx, HandleObject obj)
+{
+    JS_ASSERT(obj->isNative());
+    Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
     RootedFunction StructTypeFun(cx,
-        JS_DefineFunction(cx, global, "StructType", createStructType, 1, 0));
+        global->createConstructor(cx, createStructType,
+                                  cx->names().StructType, 1));
 
     if (!StructTypeFun)
         return NULL;
 
-    if (!SetupComplexHeirarchy(cx, global, StructTypeFun))
+    RootedObject proto(cx);
+    RootedObject protoProto(cx);
+    if (!SetupComplexHeirarchy(cx, obj, JSProto_StructType,
+                               StructTypeFun, &proto, &protoProto))
         return NULL;
 
-    return global;
+    return proto;
 }
 
 JSObject *
 js_InitBinaryDataClasses(JSContext *cx, HandleObject obj)
 {
     JS_ASSERT(obj->is<GlobalObject>());
     Rooted<GlobalObject *> global(cx, &obj->as<GlobalObject>());
 
@@ -371,12 +1371,16 @@ js_InitBinaryDataClasses(JSContext *cx, 
 \
         if (!JS_DefineFunction(cx, numFun, "toString",\
                                NumericTypeToString<constant_>, 0, 0))\
             return NULL;\
     } while(0);
     BINARYDATA_FOR_EACH_NUMERIC_TYPES(BINARYDATA_NUMERIC_DEFINE)
 #undef BINARYDATA_NUMERIC_DEFINE
 
-    if (!InitComplexClasses(cx, global))
+    if (!InitArrayType(cx, obj))
         return NULL;
+
+    if (!InitStructType(cx, obj))
+        return NULL;
+
     return global;
 }
--- a/js/src/builtin/BinaryData.h
+++ b/js/src/builtin/BinaryData.h
@@ -8,20 +8,16 @@
 #define builtin_BinaryData_h
 
 #include "jsapi.h"
 #include "jsobj.h"
 #include "jsfriendapi.h"
 #include "gc/Heap.h"
 
 namespace js {
-class Block : public gc::Cell
-{
-};
-
 typedef float float32_t;
 typedef double float64_t;
 
 enum {
     NUMERICTYPE_UINT8 = 0,
     NUMERICTYPE_UINT16,
     NUMERICTYPE_UINT32,
     NUMERICTYPE_UINT64,
@@ -37,16 +33,17 @@ enum {
 enum TypeCommonSlots {
     SLOT_MEMSIZE = 0,
     SLOT_ALIGN,
     TYPE_RESERVED_SLOTS
 };
 
 enum BlockCommonSlots {
     SLOT_DATATYPE = 0,
+    SLOT_BLOCKREFOWNER,
     BLOCK_RESERVED_SLOTS
 };
 
 static Class DataClass = {
     "Data",
     JSCLASS_HAS_CACHED_PROTO(JSProto_Data),
     JS_PropertyStub,
     JS_DeletePropertyStub,
@@ -135,40 +132,160 @@ bool NumericType<float64_t>::reify(JSCon
     NULL,\
     NULL\
 },
 
 static Class NumericTypeClasses[NUMERICTYPES] = {
     BINARYDATA_FOR_EACH_NUMERIC_TYPES(BINARYDATA_NUMERIC_CLASSES)
 };
 
-static Class ArrayTypeClass = {
-    "ArrayType",
-    JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayType),
-    JS_PropertyStub,
-    JS_DeletePropertyStub,
-    JS_PropertyStub,
-    JS_StrictPropertyStub,
-    JS_EnumerateStub,
-    JS_ResolveStub,
-    JS_ConvertStub
+/* This represents the 'A' and it's [[Prototype]] chain
+ * in:
+ *   A = new ArrayType(Type, N);
+ *   a = new A();
+ */
+class ArrayType : public JSObject
+{
+    private:
+        static JSObject *create(JSContext *cx, HandleObject arrayTypeGlobal,
+                                HandleObject elementType, uint32_t length);
+    public:
+        static Class class_;
+
+        static JSBool construct(JSContext *cx, unsigned int argc, jsval *vp);
+        static JSBool repeat(JSContext *cx, unsigned int argc, jsval *vp);
+
+        static JSBool toString(JSContext *cx, unsigned int argc, jsval *vp);
+
+        static uint32_t length(JSContext *cx, HandleObject obj);
+        static JSObject *elementType(JSContext *cx, HandleObject obj);
+        static bool convertAndCopyTo(JSContext *cx, HandleObject exemplar,
+                            HandleValue from, uint8_t *mem);
+        static bool reify(JSContext *cx, HandleObject type, HandleObject owner,
+                          size_t offset, MutableHandleValue to);
+};
+
+/* This represents the 'a' and it's [[Prototype]] chain */
+class BinaryArray
+{
+    private:
+        static JSObject *createEmpty(JSContext *cx, HandleObject type);
+
+        // creates initialized memory of size of type
+        static JSObject *create(JSContext *cx, HandleObject type);
+        // attempts to [[Convert]]
+        static JSObject *create(JSContext *cx, HandleObject type,
+                                HandleValue initial);
+
+    public:
+        static Class class_;
+
+        // uses passed block as memory
+        static JSObject *create(JSContext *cx, HandleObject type,
+                                HandleObject owner, size_t offset);
+        static JSBool construct(JSContext *cx, unsigned int argc, jsval *vp);
+
+        static void finalize(FreeOp *op, JSObject *obj);
+        static void obj_trace(JSTracer *tracer, JSObject *obj);
+
+        static JSBool forEach(JSContext *cx, unsigned int argc, jsval *vp);
+        static JSBool subarray(JSContext *cx, unsigned int argc, jsval *vp);
+        static JSBool fill(JSContext *cx, unsigned int argc, jsval *vp);
+
+        static JSBool obj_lookupGeneric(JSContext *cx, HandleObject obj,
+                                        HandleId id, MutableHandleObject objp,
+                                        MutableHandleShape propp);
+
+        static JSBool obj_lookupProperty(JSContext *cx, HandleObject obj,
+                                         HandlePropertyName name,
+                                         MutableHandleObject objp,
+                                         MutableHandleShape propp);
+
+        static JSBool obj_lookupElement(JSContext *cx, HandleObject obj,
+                                        uint32_t index, MutableHandleObject objp,
+                                        MutableHandleShape propp);
+
+        static JSBool obj_lookupSpecial(JSContext *cx, HandleObject obj,
+                                        HandleSpecialId sid,
+                                        MutableHandleObject objp,
+                                        MutableHandleShape propp);
+
+        static JSBool obj_getGeneric(JSContext *cx, HandleObject obj,
+                                     HandleObject receiver,
+                                     HandleId id,
+                                     MutableHandleValue vp);
+
+        static JSBool obj_getProperty(JSContext *cx, HandleObject obj,
+                                      HandleObject receiver,
+                                      HandlePropertyName name,
+                                      MutableHandleValue vp);
+
+        static JSBool obj_getElement(JSContext *cx, HandleObject obj,
+                                     HandleObject receiver,
+                                     uint32_t index,
+                                     MutableHandleValue vp);
+
+        static JSBool obj_getElementIfPresent(JSContext *cx, HandleObject obj,
+                                              HandleObject receiver,
+                                              uint32_t index,
+                                              MutableHandleValue vp,
+                                              bool *present);
+
+        static JSBool obj_getSpecial(JSContext *cx, HandleObject obj,
+                                     HandleObject receiver,
+                                     HandleSpecialId sid,
+                                     MutableHandleValue vp);
+
+        static JSBool obj_setGeneric(JSContext *cx, HandleObject obj,
+                                     HandleId id, MutableHandleValue vp,
+                                     JSBool strict);
+
+        static JSBool obj_setProperty(JSContext *cx, HandleObject obj,
+                                      HandlePropertyName name,
+                                      MutableHandleValue vp,
+                                      JSBool strict);
+
+        static JSBool obj_setElement(JSContext *cx, HandleObject obj,
+                                     uint32_t index, MutableHandleValue vp,
+                                     JSBool strict);
+
+        static JSBool obj_setSpecial(JSContext *cx, HandleObject obj,
+                                     HandleSpecialId sid,
+                                     MutableHandleValue vp,
+                                     JSBool strict);
+
+        static JSBool obj_getGenericAttributes(JSContext *cx, HandleObject obj,
+                                               HandleId id, unsigned *attrsp);
+
+        static JSBool obj_getPropertyAttributes(JSContext *cx, HandleObject obj,
+                                                HandlePropertyName name,
+                                                unsigned *attrsp);
+
+        static JSBool obj_getElementAttributes(JSContext *cx, HandleObject obj,
+                                               uint32_t index, unsigned *attrsp);
+
+        static JSBool obj_getSpecialAttributes(JSContext *cx, HandleObject obj,
+                                               HandleSpecialId sid,
+                                               unsigned *attrsp);
+
+        static JSBool obj_enumerate(JSContext *cx, HandleObject obj,
+                                    JSIterateOp enum_op,
+                                    MutableHandleValue statep,
+                                    MutableHandleId idp);
+
+        static JSBool lengthGetter(JSContext *cx, unsigned int argc, jsval *vp);
+
 };
 
 static Class StructTypeClass = {
     "StructType",
     JSCLASS_HAS_CACHED_PROTO(JSProto_StructType),
     JS_PropertyStub,
     JS_DeletePropertyStub,
     JS_PropertyStub,
     JS_StrictPropertyStub,
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub
 };
-
-class ArrayTypeObject : public JSObject
-{
-    public:
-        static JSBool repeat(JSContext *cx, unsigned int argc, jsval *vp);
-};
 }
 
 #endif /* builtin_BinaryData_h */
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -397,8 +397,10 @@ MSG_DEF(JSMSG_USE_ASM_LINK_FAIL,      34
 MSG_DEF(JSMSG_USE_ASM_TYPE_OK,        344, 1, JSEXN_ERR,     "successfully compiled asm.js code ({0})")
 MSG_DEF(JSMSG_BAD_ARROW_ARGS,         345, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)")
 MSG_DEF(JSMSG_YIELD_IN_ARROW,         346, 0, JSEXN_SYNTAXERR, "arrow function may not contain yield")
 MSG_DEF(JSMSG_WRONG_VALUE,            347, 2, JSEXN_ERR, "expected {0} but found {1}")
 MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_BAD_TARGET, 348, 1, JSEXN_ERR, "target for index {0} is not an integer")
 MSG_DEF(JSMSG_SELFHOSTED_UNBOUND_NAME,349, 0, JSEXN_TYPEERR, "self-hosted code may not contain unbound name lookups")
 MSG_DEF(JSMSG_DEPRECATED_SOURCE_MAP,  350, 0, JSEXN_SYNTAXERR, "Using //@ to indicate source map URL pragmas is deprecated. Use //# instead")
 MSG_DEF(JSMSG_BAD_DESTRUCT_ASSIGN,    351, 1, JSEXN_SYNTAXERR, "can't assign to {0} using destructuring assignment")
+MSG_DEF(JSMSG_BINARYDATA_ARRAYTYPE_BAD_ARGS, 352, 0, JSEXN_ERR, "Invalid arguments")
+MSG_DEF(JSMSG_BINARYDATA_BINARYARRAY_BAD_INDEX, 353, 0, JSEXN_RANGEERR, "invalid or out-of-range index")
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1913,17 +1913,17 @@ static const JSStdName standard_class_na
     /* Binary Data */
 #ifdef ENABLE_BINARYDATA
     {js_InitBinaryDataClasses,          EAGER_ATOM_AND_CLASP(Type)},
     {js_InitBinaryDataClasses,          EAGER_ATOM_AND_CLASP(Data)},
 #define BINARYDATA_NUMERIC_NAMES(constant_, type_)\
     {js_InitBinaryDataClasses,          EAGER_CLASS_ATOM(type_),      &NumericTypeClasses[constant_]},
     BINARYDATA_FOR_EACH_NUMERIC_TYPES(BINARYDATA_NUMERIC_NAMES)
 #undef BINARYDATA_NUMERIC_NAMES
-    {js_InitBinaryDataClasses,          EAGER_ATOM_AND_CLASP(ArrayType)},
+    {js_InitBinaryDataClasses,          EAGER_CLASS_ATOM(ArrayType),  &js::ArrayType::class_},
     {js_InitBinaryDataClasses,          EAGER_ATOM_AND_CLASP(StructType)},
 #endif
     {NULL,                      0, NULL}
 };
 
 static const JSStdName object_prototype_names[] = {
     /* Object.prototype properties (global delegates to Object.prototype). */
     {js_InitObjectClass,        EAGER_ATOM(proto), &JSObject::class_},
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/BinaryData/arraytype.js
@@ -0,0 +1,128 @@
+// |reftest| skip-if(!this.hasOwnProperty("Type"))
+var BUGNUMBER = 578700;
+var summary = 'BinaryData ArrayType implementation';
+
+function assertThrows(f) {
+    var ok = false;
+    try {
+        f();
+    } catch (exc) {
+        ok = true;
+    }
+    if (!ok)
+        throw new TypeError("Assertion failed: " + f + " did not throw as expected");
+}
+
+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);
+    assertEq(A.length, 10);
+    assertEq(A.elementType, uint8);
+    assertEq(A.bytes, 10);
+    assertEq(A.toString(), "ArrayType(uint8, 10)");
+
+    assertEq(A.prototype.__proto__, ArrayType.prototype.prototype);
+    assertEq(typeof A.prototype.fill, "function");
+
+    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++)
+        a[i] = i*2;
+
+    for (var i = 0; i < a.length; i++)
+        assertEq(a[i], i*2);
+
+
+    // Range.
+    assertThrows(function() a[i] = 5);
+
+    assertEq(a[a.length], undefined);
+
+    // constructor takes initial value
+    var b = new A(a);
+    for (var i = 0; i < a.length; i++)
+        assertEq(a[i], i*2);
+
+    var b = new A([0, 1, 0, 1, 0, 1, 0, 1, 0, 1]);
+    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
+    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
+    mario[1] = [1, 1.414, 3.14];
+
+    assertEq(mario[0].length, 3);
+    assertEq(mario[0][0], 1);
+    assertEq(mario[0][1], 0);
+    assertEq(mario[0][2], 0);
+
+    assertThrows(function() mario[1] = 5);
+    mario[1][1] = [];
+    assertEq(Number.isNaN(mario[1][1]), true);
+
+
+    // ok this is just for kicks
+    var AllSprites = new ArrayType(Sprite, 65536);
+    var as = new AllSprites();
+    assertEq(as.length, 65536);
+
+
+    as.foo = "bar";
+
+    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);
+    assertEq(lengthPropDesc.configurable, false);
+    assertEq(lengthPropDesc.enumerable, false);
+    assertEq(lengthPropDesc.writable, false);
+
+    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 = 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);
+    print("Tests complete");
+}
+
+runTests();
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/BinaryData/memory.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty("Type"))
+var BUGNUMBER = 578700;
+var summary = 'BinaryData memory check';
+
+function spin() {
+    for (var i = 0; i < 10000; i++)
+        ;
+}
+
+function runTests() {
+    print(BUGNUMBER + ": " + summary);
+
+    var AA = new ArrayType(new ArrayType(uint8, 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 = 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);
+
+    if (typeof reportCompare === "function")
+        reportCompare(true, true);
+    print("Tests complete");
+}
+
+runTests();
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -42,16 +42,17 @@
     macro(decodeURI, decodeURI, "decodeURI") \
     macro(decodeURIComponent, decodeURIComponent, "decodeURIComponent") \
     macro(defineProperty, defineProperty, "defineProperty") \
     macro(defineGetter, defineGetter, "__defineGetter__") \
     macro(defineSetter, defineSetter, "__defineSetter__") \
     macro(delete, delete_, "delete") \
     macro(deleteProperty, deleteProperty, "deleteProperty") \
     macro(each, each, "each") \
+    macro(elementType, elementType, "elementType") \
     macro(empty, empty, "") \
     macro(encodeURI, encodeURI, "encodeURI") \
     macro(encodeURIComponent, encodeURIComponent, "encodeURIComponent") \
     macro(enumerable, enumerable, "enumerable") \
     macro(enumerate, enumerate, "enumerate") \
     macro(escape, escape, "escape") \
     macro(eval, eval, "eval") \
     macro(false, false_, "false") \