js/src/builtin/TypedObject.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/builtin/TypedObject.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,3282 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99:
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "builtin/TypedObject.h"
    1.11 +
    1.12 +#include "mozilla/CheckedInt.h"
    1.13 +
    1.14 +#include "jscompartment.h"
    1.15 +#include "jsfun.h"
    1.16 +#include "jsobj.h"
    1.17 +#include "jsutil.h"
    1.18 +
    1.19 +#include "gc/Marking.h"
    1.20 +#include "js/Vector.h"
    1.21 +#include "vm/GlobalObject.h"
    1.22 +#include "vm/ObjectImpl.h"
    1.23 +#include "vm/String.h"
    1.24 +#include "vm/StringBuffer.h"
    1.25 +#include "vm/TypedArrayObject.h"
    1.26 +
    1.27 +#include "jsatominlines.h"
    1.28 +#include "jsobjinlines.h"
    1.29 +
    1.30 +#include "vm/Shape-inl.h"
    1.31 +
    1.32 +using mozilla::CheckedInt32;
    1.33 +using mozilla::DebugOnly;
    1.34 +
    1.35 +using namespace js;
    1.36 +
    1.37 +const Class js::TypedObjectModuleObject::class_ = {
    1.38 +    "TypedObject",
    1.39 +    JSCLASS_HAS_RESERVED_SLOTS(SlotCount) |
    1.40 +    JSCLASS_HAS_CACHED_PROTO(JSProto_TypedObject),
    1.41 +    JS_PropertyStub,         /* addProperty */
    1.42 +    JS_DeletePropertyStub,   /* delProperty */
    1.43 +    JS_PropertyStub,         /* getProperty */
    1.44 +    JS_StrictPropertyStub,   /* setProperty */
    1.45 +    JS_EnumerateStub,
    1.46 +    JS_ResolveStub,
    1.47 +    JS_ConvertStub
    1.48 +};
    1.49 +
    1.50 +static const JSFunctionSpec TypedObjectMethods[] = {
    1.51 +    JS_SELF_HOSTED_FN("objectType", "TypeOfTypedObject", 1, 0),
    1.52 +    JS_SELF_HOSTED_FN("storage", "StorageOfTypedObject", 1, 0),
    1.53 +    JS_FS_END
    1.54 +};
    1.55 +
    1.56 +static void
    1.57 +ReportCannotConvertTo(JSContext *cx, HandleValue fromValue, const char *toType)
    1.58 +{
    1.59 +    JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
    1.60 +                         InformalValueTypeName(fromValue), toType);
    1.61 +}
    1.62 +
    1.63 +template<class T>
    1.64 +static inline T*
    1.65 +ToObjectIf(HandleValue value)
    1.66 +{
    1.67 +    if (!value.isObject())
    1.68 +        return nullptr;
    1.69 +
    1.70 +    if (!value.toObject().is<T>())
    1.71 +        return nullptr;
    1.72 +
    1.73 +    return &value.toObject().as<T>();
    1.74 +}
    1.75 +
    1.76 +static inline CheckedInt32 roundUpToAlignment(CheckedInt32 address, int32_t align)
    1.77 +{
    1.78 +    JS_ASSERT(IsPowerOfTwo(align));
    1.79 +
    1.80 +    // Note: Be careful to order operators such that we first make the
    1.81 +    // value smaller and then larger, so that we don't get false
    1.82 +    // overflow errors due to (e.g.) adding `align` and then
    1.83 +    // subtracting `1` afterwards when merely adding `align-1` would
    1.84 +    // not have overflowed. Note that due to the nature of two's
    1.85 +    // complement representation, if `address` is already aligned,
    1.86 +    // then adding `align-1` cannot itself cause an overflow.
    1.87 +
    1.88 +    return ((address + (align - 1)) / align) * align;
    1.89 +}
    1.90 +
    1.91 +/*
    1.92 + * Overwrites the contents of `typedObj` at offset `offset` with `val`
    1.93 + * converted to the type `typeObj`. This is done by delegating to
    1.94 + * self-hosted code. This is used for assignments and initializations.
    1.95 + *
    1.96 + * For example, consider the final assignment in this snippet:
    1.97 + *
    1.98 + *    var Point = new StructType({x: float32, y: float32});
    1.99 + *    var Line = new StructType({from: Point, to: Point});
   1.100 + *    var line = new Line();
   1.101 + *    line.to = {x: 22, y: 44};
   1.102 + *
   1.103 + * This would result in a call to `ConvertAndCopyTo`
   1.104 + * where:
   1.105 + * - typeObj = Point
   1.106 + * - typedObj = line
   1.107 + * - offset = sizeof(Point) == 8
   1.108 + * - val = {x: 22, y: 44}
   1.109 + * This would result in loading the value of `x`, converting
   1.110 + * it to a float32, and hen storing it at the appropriate offset,
   1.111 + * and then doing the same for `y`.
   1.112 + *
   1.113 + * Note that the type of `typeObj` may not be the
   1.114 + * type of `typedObj` but rather some subcomponent of `typedObj`.
   1.115 + */
   1.116 +static bool
   1.117 +ConvertAndCopyTo(JSContext *cx,
   1.118 +                 HandleTypeDescr typeObj,
   1.119 +                 HandleTypedObject typedObj,
   1.120 +                 int32_t offset,
   1.121 +                 HandleValue val)
   1.122 +{
   1.123 +    RootedFunction func(
   1.124 +        cx, SelfHostedFunction(cx, cx->names().ConvertAndCopyTo));
   1.125 +    if (!func)
   1.126 +        return false;
   1.127 +
   1.128 +    InvokeArgs args(cx);
   1.129 +    if (!args.init(4))
   1.130 +        return false;
   1.131 +
   1.132 +    args.setCallee(ObjectValue(*func));
   1.133 +    args[0].setObject(*typeObj);
   1.134 +    args[1].setObject(*typedObj);
   1.135 +    args[2].setInt32(offset);
   1.136 +    args[3].set(val);
   1.137 +
   1.138 +    return Invoke(cx, args);
   1.139 +}
   1.140 +
   1.141 +static bool
   1.142 +ConvertAndCopyTo(JSContext *cx, HandleTypedObject typedObj, HandleValue val)
   1.143 +{
   1.144 +    Rooted<TypeDescr*> type(cx, &typedObj->typeDescr());
   1.145 +    return ConvertAndCopyTo(cx, type, typedObj, 0, val);
   1.146 +}
   1.147 +
   1.148 +/*
   1.149 + * Overwrites the contents of `typedObj` at offset `offset` with `val`
   1.150 + * converted to the type `typeObj`
   1.151 + */
   1.152 +static bool
   1.153 +Reify(JSContext *cx,
   1.154 +      HandleTypeDescr type,
   1.155 +      HandleTypedObject typedObj,
   1.156 +      size_t offset,
   1.157 +      MutableHandleValue to)
   1.158 +{
   1.159 +    RootedFunction func(cx, SelfHostedFunction(cx, cx->names().Reify));
   1.160 +    if (!func)
   1.161 +        return false;
   1.162 +
   1.163 +    InvokeArgs args(cx);
   1.164 +    if (!args.init(3))
   1.165 +        return false;
   1.166 +
   1.167 +    args.setCallee(ObjectValue(*func));
   1.168 +    args[0].setObject(*type);
   1.169 +    args[1].setObject(*typedObj);
   1.170 +    args[2].setInt32(offset);
   1.171 +
   1.172 +    if (!Invoke(cx, args))
   1.173 +        return false;
   1.174 +
   1.175 +    to.set(args.rval());
   1.176 +    return true;
   1.177 +}
   1.178 +
   1.179 +// Extracts the `prototype` property from `obj`, throwing if it is
   1.180 +// missing or not an object.
   1.181 +static JSObject *
   1.182 +GetPrototype(JSContext *cx, HandleObject obj)
   1.183 +{
   1.184 +    RootedValue prototypeVal(cx);
   1.185 +    if (!JSObject::getProperty(cx, obj, obj, cx->names().prototype,
   1.186 +                               &prototypeVal))
   1.187 +    {
   1.188 +        return nullptr;
   1.189 +    }
   1.190 +    if (!prototypeVal.isObject()) {
   1.191 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   1.192 +                             JSMSG_INVALID_PROTOTYPE);
   1.193 +        return nullptr;
   1.194 +    }
   1.195 +    return &prototypeVal.toObject();
   1.196 +}
   1.197 +
   1.198 +/***************************************************************************
   1.199 + * Typed Prototypes
   1.200 + *
   1.201 + * Every type descriptor has an associated prototype. Instances of
   1.202 + * that type descriptor use this as their prototype. Per the spec,
   1.203 + * typed object prototypes cannot be mutated.
   1.204 + */
   1.205 +
   1.206 +const Class js::TypedProto::class_ = {
   1.207 +    "TypedProto",
   1.208 +    JSCLASS_HAS_RESERVED_SLOTS(JS_TYPROTO_SLOTS),
   1.209 +    JS_PropertyStub,       /* addProperty */
   1.210 +    JS_DeletePropertyStub, /* delProperty */
   1.211 +    JS_PropertyStub,       /* getProperty */
   1.212 +    JS_StrictPropertyStub, /* setProperty */
   1.213 +    JS_EnumerateStub,
   1.214 +    JS_ResolveStub,
   1.215 +    JS_ConvertStub,
   1.216 +    nullptr,
   1.217 +    nullptr,
   1.218 +    nullptr,
   1.219 +    nullptr,
   1.220 +    nullptr
   1.221 +};
   1.222 +
   1.223 +/***************************************************************************
   1.224 + * Scalar type objects
   1.225 + *
   1.226 + * Scalar type objects like `uint8`, `uint16`, are all instances of
   1.227 + * the ScalarTypeDescr class. Like all type objects, they have a reserved
   1.228 + * slot pointing to a TypeRepresentation object, which is used to
   1.229 + * distinguish which scalar type object this actually is.
   1.230 + */
   1.231 +
   1.232 +const Class js::ScalarTypeDescr::class_ = {
   1.233 +    "Scalar",
   1.234 +    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS),
   1.235 +    JS_PropertyStub,       /* addProperty */
   1.236 +    JS_DeletePropertyStub, /* delProperty */
   1.237 +    JS_PropertyStub,       /* getProperty */
   1.238 +    JS_StrictPropertyStub, /* setProperty */
   1.239 +    JS_EnumerateStub,
   1.240 +    JS_ResolveStub,
   1.241 +    JS_ConvertStub,
   1.242 +    nullptr,
   1.243 +    ScalarTypeDescr::call,
   1.244 +    nullptr,
   1.245 +    nullptr,
   1.246 +    nullptr
   1.247 +};
   1.248 +
   1.249 +const JSFunctionSpec js::ScalarTypeDescr::typeObjectMethods[] = {
   1.250 +    JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
   1.251 +    {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
   1.252 +    {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
   1.253 +    JS_FS_END
   1.254 +};
   1.255 +
   1.256 +static int32_t ScalarSizes[] = {
   1.257 +#define SCALAR_SIZE(_kind, _type, _name)                        \
   1.258 +    sizeof(_type),
   1.259 +    JS_FOR_EACH_SCALAR_TYPE_REPR(SCALAR_SIZE) 0
   1.260 +#undef SCALAR_SIZE
   1.261 +};
   1.262 +
   1.263 +int32_t
   1.264 +ScalarTypeDescr::size(Type t)
   1.265 +{
   1.266 +    return ScalarSizes[t];
   1.267 +}
   1.268 +
   1.269 +int32_t
   1.270 +ScalarTypeDescr::alignment(Type t)
   1.271 +{
   1.272 +    return ScalarSizes[t];
   1.273 +}
   1.274 +
   1.275 +/*static*/ const char *
   1.276 +ScalarTypeDescr::typeName(Type type)
   1.277 +{
   1.278 +    switch (type) {
   1.279 +#define NUMERIC_TYPE_TO_STRING(constant_, type_, name_) \
   1.280 +        case constant_: return #name_;
   1.281 +        JS_FOR_EACH_SCALAR_TYPE_REPR(NUMERIC_TYPE_TO_STRING)
   1.282 +    }
   1.283 +    MOZ_ASSUME_UNREACHABLE("Invalid type");
   1.284 +}
   1.285 +
   1.286 +bool
   1.287 +ScalarTypeDescr::call(JSContext *cx, unsigned argc, Value *vp)
   1.288 +{
   1.289 +    CallArgs args = CallArgsFromVp(argc, vp);
   1.290 +    if (args.length() < 1) {
   1.291 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
   1.292 +                             args.callee().getClass()->name, "0", "s");
   1.293 +        return false;
   1.294 +    }
   1.295 +
   1.296 +    Rooted<ScalarTypeDescr *> descr(cx, &args.callee().as<ScalarTypeDescr>());
   1.297 +    ScalarTypeDescr::Type type = descr->type();
   1.298 +
   1.299 +    double number;
   1.300 +    if (!ToNumber(cx, args[0], &number))
   1.301 +        return false;
   1.302 +
   1.303 +    if (type == ScalarTypeDescr::TYPE_UINT8_CLAMPED)
   1.304 +        number = ClampDoubleToUint8(number);
   1.305 +
   1.306 +    switch (type) {
   1.307 +#define SCALARTYPE_CALL(constant_, type_, name_)                             \
   1.308 +      case constant_: {                                                       \
   1.309 +          type_ converted = ConvertScalar<type_>(number);                     \
   1.310 +          args.rval().setNumber((double) converted);                          \
   1.311 +          return true;                                                        \
   1.312 +      }
   1.313 +
   1.314 +        JS_FOR_EACH_SCALAR_TYPE_REPR(SCALARTYPE_CALL)
   1.315 +#undef SCALARTYPE_CALL
   1.316 +
   1.317 +    }
   1.318 +    return true;
   1.319 +}
   1.320 +
   1.321 +/***************************************************************************
   1.322 + * Reference type objects
   1.323 + *
   1.324 + * Reference type objects like `Any` or `Object` basically work the
   1.325 + * same way that the scalar type objects do. There is one class with
   1.326 + * many instances, and each instance has a reserved slot with a
   1.327 + * TypeRepresentation object, which is used to distinguish which
   1.328 + * reference type object this actually is.
   1.329 + */
   1.330 +
   1.331 +const Class js::ReferenceTypeDescr::class_ = {
   1.332 +    "Reference",
   1.333 +    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS),
   1.334 +    JS_PropertyStub,       /* addProperty */
   1.335 +    JS_DeletePropertyStub, /* delProperty */
   1.336 +    JS_PropertyStub,       /* getProperty */
   1.337 +    JS_StrictPropertyStub, /* setProperty */
   1.338 +    JS_EnumerateStub,
   1.339 +    JS_ResolveStub,
   1.340 +    JS_ConvertStub,
   1.341 +    nullptr,
   1.342 +    ReferenceTypeDescr::call,
   1.343 +    nullptr,
   1.344 +    nullptr,
   1.345 +    nullptr
   1.346 +};
   1.347 +
   1.348 +const JSFunctionSpec js::ReferenceTypeDescr::typeObjectMethods[] = {
   1.349 +    JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
   1.350 +    {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
   1.351 +    {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
   1.352 +    JS_FS_END
   1.353 +};
   1.354 +
   1.355 +static int32_t ReferenceSizes[] = {
   1.356 +#define REFERENCE_SIZE(_kind, _type, _name)                        \
   1.357 +    sizeof(_type),
   1.358 +    JS_FOR_EACH_REFERENCE_TYPE_REPR(REFERENCE_SIZE) 0
   1.359 +#undef REFERENCE_SIZE
   1.360 +};
   1.361 +
   1.362 +int32_t
   1.363 +ReferenceTypeDescr::size(Type t)
   1.364 +{
   1.365 +    return ReferenceSizes[t];
   1.366 +}
   1.367 +
   1.368 +int32_t
   1.369 +ReferenceTypeDescr::alignment(Type t)
   1.370 +{
   1.371 +    return ReferenceSizes[t];
   1.372 +}
   1.373 +
   1.374 +/*static*/ const char *
   1.375 +ReferenceTypeDescr::typeName(Type type)
   1.376 +{
   1.377 +    switch (type) {
   1.378 +#define NUMERIC_TYPE_TO_STRING(constant_, type_, name_) \
   1.379 +        case constant_: return #name_;
   1.380 +        JS_FOR_EACH_REFERENCE_TYPE_REPR(NUMERIC_TYPE_TO_STRING)
   1.381 +    }
   1.382 +    MOZ_ASSUME_UNREACHABLE("Invalid type");
   1.383 +}
   1.384 +
   1.385 +bool
   1.386 +js::ReferenceTypeDescr::call(JSContext *cx, unsigned argc, Value *vp)
   1.387 +{
   1.388 +    CallArgs args = CallArgsFromVp(argc, vp);
   1.389 +
   1.390 +    JS_ASSERT(args.callee().is<ReferenceTypeDescr>());
   1.391 +    Rooted<ReferenceTypeDescr *> descr(cx, &args.callee().as<ReferenceTypeDescr>());
   1.392 +
   1.393 +    if (args.length() < 1) {
   1.394 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   1.395 +                             JSMSG_MORE_ARGS_NEEDED,
   1.396 +                             descr->typeName(), "0", "s");
   1.397 +        return false;
   1.398 +    }
   1.399 +
   1.400 +    switch (descr->type()) {
   1.401 +      case ReferenceTypeDescr::TYPE_ANY:
   1.402 +        args.rval().set(args[0]);
   1.403 +        return true;
   1.404 +
   1.405 +      case ReferenceTypeDescr::TYPE_OBJECT:
   1.406 +      {
   1.407 +        RootedObject obj(cx, ToObject(cx, args[0]));
   1.408 +        if (!obj)
   1.409 +            return false;
   1.410 +        args.rval().setObject(*obj);
   1.411 +        return true;
   1.412 +      }
   1.413 +
   1.414 +      case ReferenceTypeDescr::TYPE_STRING:
   1.415 +      {
   1.416 +        RootedString obj(cx, ToString<CanGC>(cx, args[0]));
   1.417 +        if (!obj)
   1.418 +            return false;
   1.419 +        args.rval().setString(&*obj);
   1.420 +        return true;
   1.421 +      }
   1.422 +    }
   1.423 +
   1.424 +    MOZ_ASSUME_UNREACHABLE("Unhandled Reference type");
   1.425 +}
   1.426 +
   1.427 +/***************************************************************************
   1.428 + * X4 type objects
   1.429 + *
   1.430 + * Note: these are partially defined in SIMD.cpp
   1.431 + */
   1.432 +
   1.433 +static int32_t X4Sizes[] = {
   1.434 +#define X4_SIZE(_kind, _type, _name)                        \
   1.435 +    sizeof(_type) * 4,
   1.436 +    JS_FOR_EACH_X4_TYPE_REPR(X4_SIZE) 0
   1.437 +#undef X4_SIZE
   1.438 +};
   1.439 +
   1.440 +int32_t
   1.441 +X4TypeDescr::size(Type t)
   1.442 +{
   1.443 +    return X4Sizes[t];
   1.444 +}
   1.445 +
   1.446 +int32_t
   1.447 +X4TypeDescr::alignment(Type t)
   1.448 +{
   1.449 +    return X4Sizes[t];
   1.450 +}
   1.451 +
   1.452 +/***************************************************************************
   1.453 + * ArrayMetaTypeDescr class
   1.454 + */
   1.455 +
   1.456 +/*
   1.457 + * For code like:
   1.458 + *
   1.459 + *   var A = new TypedObject.ArrayType(uint8, 10);
   1.460 + *   var S = new TypedObject.StructType({...});
   1.461 + *
   1.462 + * As usual, the [[Prototype]] of A is
   1.463 + * TypedObject.ArrayType.prototype.  This permits adding methods to
   1.464 + * all ArrayType types, by setting
   1.465 + * TypedObject.ArrayType.prototype.methodName = function() { ... }.
   1.466 + * The same holds for S with respect to TypedObject.StructType.
   1.467 + *
   1.468 + * We may also want to add methods to *instances* of an ArrayType:
   1.469 + *
   1.470 + *   var a = new A();
   1.471 + *   var s = new S();
   1.472 + *
   1.473 + * As usual, the [[Prototype]] of a is A.prototype.  What's
   1.474 + * A.prototype?  It's an empty object, and you can set
   1.475 + * A.prototype.methodName = function() { ... } to add a method to all
   1.476 + * A instances.  (And the same with respect to s and S.)
   1.477 + *
   1.478 + * But what if you want to add a method to all ArrayType instances,
   1.479 + * not just all A instances?  (Or to all StructType instances.)  The
   1.480 + * [[Prototype]] of the A.prototype empty object is
   1.481 + * TypedObject.ArrayType.prototype.prototype (two .prototype levels!).
   1.482 + * So just set TypedObject.ArrayType.prototype.prototype.methodName =
   1.483 + * function() { ... } to add a method to all ArrayType instances.
   1.484 + * (And, again, same with respect to s and S.)
   1.485 + *
   1.486 + * This function creates the A.prototype/S.prototype object.  It takes
   1.487 + * as an argument either the TypedObject.ArrayType or the
   1.488 + * TypedObject.StructType constructor function, then returns an empty
   1.489 + * object with the .prototype.prototype object as its [[Prototype]].
   1.490 + */
   1.491 +static TypedProto *
   1.492 +CreatePrototypeObjectForComplexTypeInstance(JSContext *cx,
   1.493 +                                            Handle<TypeDescr*> descr,
   1.494 +                                            HandleObject ctorPrototype)
   1.495 +{
   1.496 +    RootedObject ctorPrototypePrototype(cx, GetPrototype(cx, ctorPrototype));
   1.497 +    if (!ctorPrototypePrototype)
   1.498 +        return nullptr;
   1.499 +
   1.500 +    Rooted<TypedProto*> result(cx);
   1.501 +    result = NewObjectWithProto<TypedProto>(cx,
   1.502 +                                            &*ctorPrototypePrototype,
   1.503 +                                            nullptr,
   1.504 +                                            TenuredObject);
   1.505 +    if (!result)
   1.506 +        return nullptr;
   1.507 +
   1.508 +    result->initTypeDescrSlot(*descr);
   1.509 +    return result;
   1.510 +}
   1.511 +
   1.512 +const Class UnsizedArrayTypeDescr::class_ = {
   1.513 +    "ArrayType",
   1.514 +    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS),
   1.515 +    JS_PropertyStub,
   1.516 +    JS_DeletePropertyStub,
   1.517 +    JS_PropertyStub,
   1.518 +    JS_StrictPropertyStub,
   1.519 +    JS_EnumerateStub,
   1.520 +    JS_ResolveStub,
   1.521 +    JS_ConvertStub,
   1.522 +    nullptr,
   1.523 +    nullptr,
   1.524 +    nullptr,
   1.525 +    TypedObject::constructUnsized,
   1.526 +    nullptr
   1.527 +};
   1.528 +
   1.529 +const Class SizedArrayTypeDescr::class_ = {
   1.530 +    "ArrayType",
   1.531 +    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS),
   1.532 +    JS_PropertyStub,
   1.533 +    JS_DeletePropertyStub,
   1.534 +    JS_PropertyStub,
   1.535 +    JS_StrictPropertyStub,
   1.536 +    JS_EnumerateStub,
   1.537 +    JS_ResolveStub,
   1.538 +    JS_ConvertStub,
   1.539 +    nullptr,
   1.540 +    nullptr,
   1.541 +    nullptr,
   1.542 +    TypedObject::constructSized,
   1.543 +    nullptr
   1.544 +};
   1.545 +
   1.546 +const JSPropertySpec ArrayMetaTypeDescr::typeObjectProperties[] = {
   1.547 +    JS_PS_END
   1.548 +};
   1.549 +
   1.550 +const JSFunctionSpec ArrayMetaTypeDescr::typeObjectMethods[] = {
   1.551 +    {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
   1.552 +    JS_FN("dimension", UnsizedArrayTypeDescr::dimension, 1, 0),
   1.553 +    JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
   1.554 +    {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
   1.555 +    JS_SELF_HOSTED_FN("build",    "TypedObjectArrayTypeBuild", 3, 0),
   1.556 +    JS_SELF_HOSTED_FN("buildPar", "TypedObjectArrayTypeBuildPar", 3, 0),
   1.557 +    JS_SELF_HOSTED_FN("from",     "TypedObjectArrayTypeFrom", 3, 0),
   1.558 +    JS_SELF_HOSTED_FN("fromPar",  "TypedObjectArrayTypeFromPar", 3, 0),
   1.559 +    JS_FS_END
   1.560 +};
   1.561 +
   1.562 +const JSPropertySpec ArrayMetaTypeDescr::typedObjectProperties[] = {
   1.563 +    JS_PS_END
   1.564 +};
   1.565 +
   1.566 +const JSFunctionSpec ArrayMetaTypeDescr::typedObjectMethods[] = {
   1.567 +    {"forEach", {nullptr, nullptr}, 1, 0, "ArrayForEach"},
   1.568 +    {"redimension", {nullptr, nullptr}, 1, 0, "TypedArrayRedimension"},
   1.569 +    JS_SELF_HOSTED_FN("map",        "TypedArrayMap",        2, 0),
   1.570 +    JS_SELF_HOSTED_FN("mapPar",     "TypedArrayMapPar",     2, 0),
   1.571 +    JS_SELF_HOSTED_FN("reduce",     "TypedArrayReduce",     2, 0),
   1.572 +    JS_SELF_HOSTED_FN("reducePar",  "TypedArrayReducePar",  2, 0),
   1.573 +    JS_SELF_HOSTED_FN("scatter",    "TypedArrayScatter",    4, 0),
   1.574 +    JS_SELF_HOSTED_FN("scatterPar", "TypedArrayScatterPar", 4, 0),
   1.575 +    JS_SELF_HOSTED_FN("filter",     "TypedArrayFilter",     1, 0),
   1.576 +    JS_SELF_HOSTED_FN("filterPar",  "TypedArrayFilterPar",  1, 0),
   1.577 +    JS_FS_END
   1.578 +};
   1.579 +
   1.580 +bool
   1.581 +js::CreateUserSizeAndAlignmentProperties(JSContext *cx, HandleTypeDescr descr)
   1.582 +{
   1.583 +    // If data is transparent, also store the public slots.
   1.584 +    if (descr->transparent() && descr->is<SizedTypeDescr>()) {
   1.585 +        Rooted<SizedTypeDescr*> sizedDescr(cx, &descr->as<SizedTypeDescr>());
   1.586 +
   1.587 +        // byteLength
   1.588 +        RootedValue typeByteLength(cx, Int32Value(sizedDescr->size()));
   1.589 +        if (!JSObject::defineProperty(cx, descr, cx->names().byteLength,
   1.590 +                                      typeByteLength,
   1.591 +                                      nullptr, nullptr,
   1.592 +                                      JSPROP_READONLY | JSPROP_PERMANENT))
   1.593 +        {
   1.594 +            return false;
   1.595 +        }
   1.596 +
   1.597 +        // byteAlignment
   1.598 +        RootedValue typeByteAlignment(cx, Int32Value(sizedDescr->alignment()));
   1.599 +        if (!JSObject::defineProperty(cx, descr, cx->names().byteAlignment,
   1.600 +                                      typeByteAlignment,
   1.601 +                                      nullptr, nullptr,
   1.602 +                                      JSPROP_READONLY | JSPROP_PERMANENT))
   1.603 +        {
   1.604 +            return false;
   1.605 +        }
   1.606 +    } else {
   1.607 +        // byteLength
   1.608 +        if (!JSObject::defineProperty(cx, descr, cx->names().byteLength,
   1.609 +                                      UndefinedHandleValue,
   1.610 +                                      nullptr, nullptr,
   1.611 +                                      JSPROP_READONLY | JSPROP_PERMANENT))
   1.612 +        {
   1.613 +            return false;
   1.614 +        }
   1.615 +
   1.616 +        // byteAlignment
   1.617 +        if (!JSObject::defineProperty(cx, descr, cx->names().byteAlignment,
   1.618 +                                      UndefinedHandleValue,
   1.619 +                                      nullptr, nullptr,
   1.620 +                                      JSPROP_READONLY | JSPROP_PERMANENT))
   1.621 +        {
   1.622 +            return false;
   1.623 +        }
   1.624 +    }
   1.625 +
   1.626 +    // variable -- true for unsized arrays
   1.627 +    RootedValue variable(cx, BooleanValue(!descr->is<SizedTypeDescr>()));
   1.628 +    if (!JSObject::defineProperty(cx, descr, cx->names().variable,
   1.629 +                                  variable,
   1.630 +                                  nullptr, nullptr,
   1.631 +                                  JSPROP_READONLY | JSPROP_PERMANENT))
   1.632 +    {
   1.633 +        return false;
   1.634 +    }
   1.635 +
   1.636 +    return true;
   1.637 +}
   1.638 +
   1.639 +template<class T>
   1.640 +T *
   1.641 +ArrayMetaTypeDescr::create(JSContext *cx,
   1.642 +                           HandleObject arrayTypePrototype,
   1.643 +                           HandleSizedTypeDescr elementType,
   1.644 +                           HandleAtom stringRepr,
   1.645 +                           int32_t size)
   1.646 +{
   1.647 +    Rooted<T*> obj(cx);
   1.648 +    obj = NewObjectWithProto<T>(cx, arrayTypePrototype, nullptr, TenuredObject);
   1.649 +    if (!obj)
   1.650 +        return nullptr;
   1.651 +
   1.652 +    obj->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(T::Kind));
   1.653 +    obj->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
   1.654 +    obj->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(elementType->alignment()));
   1.655 +    obj->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(size));
   1.656 +    obj->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(elementType->opaque()));
   1.657 +    obj->initReservedSlot(JS_DESCR_SLOT_ARRAY_ELEM_TYPE, ObjectValue(*elementType));
   1.658 +
   1.659 +    RootedValue elementTypeVal(cx, ObjectValue(*elementType));
   1.660 +    if (!JSObject::defineProperty(cx, obj, cx->names().elementType,
   1.661 +                                  elementTypeVal, nullptr, nullptr,
   1.662 +                                  JSPROP_READONLY | JSPROP_PERMANENT))
   1.663 +        return nullptr;
   1.664 +
   1.665 +    if (!CreateUserSizeAndAlignmentProperties(cx, obj))
   1.666 +        return nullptr;
   1.667 +
   1.668 +    Rooted<TypedProto*> prototypeObj(cx);
   1.669 +    prototypeObj = CreatePrototypeObjectForComplexTypeInstance(cx, obj, arrayTypePrototype);
   1.670 +    if (!prototypeObj)
   1.671 +        return nullptr;
   1.672 +
   1.673 +    obj->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*prototypeObj));
   1.674 +
   1.675 +    if (!LinkConstructorAndPrototype(cx, obj, prototypeObj))
   1.676 +        return nullptr;
   1.677 +
   1.678 +    return obj;
   1.679 +}
   1.680 +
   1.681 +bool
   1.682 +ArrayMetaTypeDescr::construct(JSContext *cx, unsigned argc, Value *vp)
   1.683 +{
   1.684 +    CallArgs args = CallArgsFromVp(argc, vp);
   1.685 +
   1.686 +    if (!args.isConstructing()) {
   1.687 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   1.688 +                             JSMSG_NOT_FUNCTION, "ArrayType");
   1.689 +        return false;
   1.690 +    }
   1.691 +
   1.692 +    RootedObject arrayTypeGlobal(cx, &args.callee());
   1.693 +
   1.694 +    // Expect one argument which is a sized type object
   1.695 +    if (args.length() < 1) {
   1.696 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
   1.697 +                             "ArrayType", "0", "");
   1.698 +        return false;
   1.699 +    }
   1.700 +
   1.701 +    if (!args[0].isObject() || !args[0].toObject().is<SizedTypeDescr>()) {
   1.702 +        ReportCannotConvertTo(cx, args[0], "ArrayType element specifier");
   1.703 +        return false;
   1.704 +    }
   1.705 +
   1.706 +    Rooted<SizedTypeDescr*> elementType(cx);
   1.707 +    elementType = &args[0].toObject().as<SizedTypeDescr>();
   1.708 +
   1.709 +    // Construct a canonical string `new ArrayType(<elementType>)`:
   1.710 +    StringBuffer contents(cx);
   1.711 +    contents.append("new ArrayType(");
   1.712 +    contents.append(&elementType->stringRepr());
   1.713 +    contents.append(")");
   1.714 +    RootedAtom stringRepr(cx, contents.finishAtom());
   1.715 +    if (!stringRepr)
   1.716 +        return nullptr;
   1.717 +
   1.718 +    // Extract ArrayType.prototype
   1.719 +    RootedObject arrayTypePrototype(cx, GetPrototype(cx, arrayTypeGlobal));
   1.720 +    if (!arrayTypePrototype)
   1.721 +        return nullptr;
   1.722 +
   1.723 +    // Create the instance of ArrayType
   1.724 +    Rooted<UnsizedArrayTypeDescr *> obj(cx);
   1.725 +    obj = create<UnsizedArrayTypeDescr>(cx, arrayTypePrototype, elementType,
   1.726 +                                        stringRepr, 0);
   1.727 +    if (!obj)
   1.728 +        return false;
   1.729 +
   1.730 +    // Add `length` property, which is undefined for an unsized array.
   1.731 +    if (!JSObject::defineProperty(cx, obj, cx->names().length,
   1.732 +                                  UndefinedHandleValue, nullptr, nullptr,
   1.733 +                                  JSPROP_READONLY | JSPROP_PERMANENT))
   1.734 +        return nullptr;
   1.735 +
   1.736 +    args.rval().setObject(*obj);
   1.737 +    return true;
   1.738 +}
   1.739 +
   1.740 +/*static*/ bool
   1.741 +UnsizedArrayTypeDescr::dimension(JSContext *cx, unsigned int argc, jsval *vp)
   1.742 +{
   1.743 +    // Expect that the `this` pointer is an unsized array type
   1.744 +    // and the first argument is an integer size.
   1.745 +    CallArgs args = CallArgsFromVp(argc, vp);
   1.746 +    if (args.length() != 1 ||
   1.747 +        !args.thisv().isObject() ||
   1.748 +        !args.thisv().toObject().is<UnsizedArrayTypeDescr>() ||
   1.749 +        !args[0].isInt32() ||
   1.750 +        args[0].toInt32() < 0)
   1.751 +    {
   1.752 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   1.753 +                             JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
   1.754 +        return false;
   1.755 +    }
   1.756 +
   1.757 +    // Extract arguments.
   1.758 +    Rooted<UnsizedArrayTypeDescr*> unsizedTypeDescr(cx);
   1.759 +    unsizedTypeDescr = &args.thisv().toObject().as<UnsizedArrayTypeDescr>();
   1.760 +    int32_t length = args[0].toInt32();
   1.761 +    Rooted<SizedTypeDescr*> elementType(cx, &unsizedTypeDescr->elementType());
   1.762 +
   1.763 +    // Compute the size.
   1.764 +    CheckedInt32 size = CheckedInt32(elementType->size()) * length;
   1.765 +    if (!size.isValid()) {
   1.766 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   1.767 +                             JSMSG_TYPEDOBJECT_TOO_BIG);
   1.768 +        return nullptr;
   1.769 +    }
   1.770 +
   1.771 +    // Construct a canonical string `new ArrayType(<elementType>).dimension(N)`:
   1.772 +    StringBuffer contents(cx);
   1.773 +    contents.append("new ArrayType(");
   1.774 +    contents.append(&elementType->stringRepr());
   1.775 +    contents.append(").dimension(");
   1.776 +    if (!NumberValueToStringBuffer(cx, NumberValue(length), contents))
   1.777 +        return false;
   1.778 +    contents.append(")");
   1.779 +    RootedAtom stringRepr(cx, contents.finishAtom());
   1.780 +    if (!stringRepr)
   1.781 +        return nullptr;
   1.782 +
   1.783 +    // Create the sized type object.
   1.784 +    Rooted<SizedArrayTypeDescr*> obj(cx);
   1.785 +    obj = ArrayMetaTypeDescr::create<SizedArrayTypeDescr>(cx, unsizedTypeDescr,
   1.786 +                                                          elementType,
   1.787 +                                                          stringRepr, size.value());
   1.788 +    if (!obj)
   1.789 +        return false;
   1.790 +
   1.791 +    obj->initReservedSlot(JS_DESCR_SLOT_SIZED_ARRAY_LENGTH,
   1.792 +                          Int32Value(length));
   1.793 +
   1.794 +    // Add `length` property.
   1.795 +    RootedValue lengthVal(cx, Int32Value(length));
   1.796 +    if (!JSObject::defineProperty(cx, obj, cx->names().length,
   1.797 +                                  lengthVal, nullptr, nullptr,
   1.798 +                                  JSPROP_READONLY | JSPROP_PERMANENT))
   1.799 +        return nullptr;
   1.800 +
   1.801 +    // Add `unsized` property, which is a link from the sized
   1.802 +    // array to the unsized array.
   1.803 +    RootedValue unsizedTypeDescrValue(cx, ObjectValue(*unsizedTypeDescr));
   1.804 +    if (!JSObject::defineProperty(cx, obj, cx->names().unsized,
   1.805 +                                  unsizedTypeDescrValue, nullptr, nullptr,
   1.806 +                                  JSPROP_READONLY | JSPROP_PERMANENT))
   1.807 +        return nullptr;
   1.808 +
   1.809 +    args.rval().setObject(*obj);
   1.810 +    return true;
   1.811 +}
   1.812 +
   1.813 +bool
   1.814 +js::IsTypedObjectArray(JSObject &obj)
   1.815 +{
   1.816 +    if (!obj.is<TypedObject>())
   1.817 +        return false;
   1.818 +    TypeDescr& d = obj.as<TypedObject>().typeDescr();
   1.819 +    return d.is<SizedArrayTypeDescr>() || d.is<UnsizedArrayTypeDescr>();
   1.820 +}
   1.821 +
   1.822 +/*********************************
   1.823 + * StructType class
   1.824 + */
   1.825 +
   1.826 +const Class StructTypeDescr::class_ = {
   1.827 +    "StructType",
   1.828 +    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) |
   1.829 +    JSCLASS_HAS_PRIVATE, // used to store FieldList
   1.830 +    JS_PropertyStub,
   1.831 +    JS_DeletePropertyStub,
   1.832 +    JS_PropertyStub,
   1.833 +    JS_StrictPropertyStub,
   1.834 +    JS_EnumerateStub,
   1.835 +    JS_ResolveStub,
   1.836 +    JS_ConvertStub,
   1.837 +    nullptr, /* finalize */
   1.838 +    nullptr, /* call */
   1.839 +    nullptr, /* hasInstance */
   1.840 +    TypedObject::constructSized,
   1.841 +    nullptr  /* trace */
   1.842 +};
   1.843 +
   1.844 +const JSPropertySpec StructMetaTypeDescr::typeObjectProperties[] = {
   1.845 +    JS_PS_END
   1.846 +};
   1.847 +
   1.848 +const JSFunctionSpec StructMetaTypeDescr::typeObjectMethods[] = {
   1.849 +    {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
   1.850 +    JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
   1.851 +    {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
   1.852 +    JS_FS_END
   1.853 +};
   1.854 +
   1.855 +const JSPropertySpec StructMetaTypeDescr::typedObjectProperties[] = {
   1.856 +    JS_PS_END
   1.857 +};
   1.858 +
   1.859 +const JSFunctionSpec StructMetaTypeDescr::typedObjectMethods[] = {
   1.860 +    JS_FS_END
   1.861 +};
   1.862 +
   1.863 +JSObject *
   1.864 +StructMetaTypeDescr::create(JSContext *cx,
   1.865 +                            HandleObject metaTypeDescr,
   1.866 +                            HandleObject fields)
   1.867 +{
   1.868 +    // Obtain names of fields, which are the own properties of `fields`
   1.869 +    AutoIdVector ids(cx);
   1.870 +    if (!GetPropertyNames(cx, fields, JSITER_OWNONLY, &ids))
   1.871 +        return nullptr;
   1.872 +
   1.873 +    // Iterate through each field. Collect values for the various
   1.874 +    // vectors below and also track total size and alignment. Be wary
   1.875 +    // of overflow!
   1.876 +    StringBuffer stringBuffer(cx);     // Canonical string repr
   1.877 +    AutoValueVector fieldNames(cx);    // Name of each field.
   1.878 +    AutoValueVector fieldTypeObjs(cx); // Type descriptor of each field.
   1.879 +    AutoValueVector fieldOffsets(cx);  // Offset of each field field.
   1.880 +    RootedObject userFieldOffsets(cx); // User-exposed {f:offset} object
   1.881 +    RootedObject userFieldTypes(cx);   // User-exposed {f:descr} object.
   1.882 +    CheckedInt32 sizeSoFar(0);         // Size of struct thus far.
   1.883 +    int32_t alignment = 1;             // Alignment of struct.
   1.884 +    bool opaque = false;               // Opacity of struct.
   1.885 +
   1.886 +    userFieldOffsets = NewObjectWithProto<JSObject>(cx, nullptr, nullptr, TenuredObject);
   1.887 +    if (!userFieldOffsets)
   1.888 +        return nullptr;
   1.889 +
   1.890 +    userFieldTypes = NewObjectWithProto<JSObject>(cx, nullptr, nullptr, TenuredObject);
   1.891 +    if (!userFieldTypes)
   1.892 +        return nullptr;
   1.893 +
   1.894 +    if (!stringBuffer.append("new StructType({")) {
   1.895 +        js_ReportOutOfMemory(cx);
   1.896 +        return nullptr;
   1.897 +    }
   1.898 +
   1.899 +    RootedValue fieldTypeVal(cx);
   1.900 +    RootedId id(cx);
   1.901 +    Rooted<SizedTypeDescr*> fieldType(cx);
   1.902 +    for (unsigned int i = 0; i < ids.length(); i++) {
   1.903 +        id = ids[i];
   1.904 +
   1.905 +        // Check that all the property names are non-numeric strings.
   1.906 +        uint32_t unused;
   1.907 +        if (!JSID_IS_ATOM(id) || JSID_TO_ATOM(id)->isIndex(&unused)) {
   1.908 +            RootedValue idValue(cx, IdToValue(id));
   1.909 +            ReportCannotConvertTo(cx, idValue, "StructType field name");
   1.910 +            return nullptr;
   1.911 +        }
   1.912 +
   1.913 +        // Load the value for the current field from the `fields` object.
   1.914 +        // The value should be a type descriptor.
   1.915 +        if (!JSObject::getGeneric(cx, fields, fields, id, &fieldTypeVal))
   1.916 +            return nullptr;
   1.917 +        fieldType = ToObjectIf<SizedTypeDescr>(fieldTypeVal);
   1.918 +        if (!fieldType) {
   1.919 +            ReportCannotConvertTo(cx, fieldTypeVal, "StructType field specifier");
   1.920 +            return nullptr;
   1.921 +        }
   1.922 +
   1.923 +        // Collect field name and type object
   1.924 +        RootedValue fieldName(cx, IdToValue(id));
   1.925 +        if (!fieldNames.append(fieldName)) {
   1.926 +            js_ReportOutOfMemory(cx);
   1.927 +            return nullptr;
   1.928 +        }
   1.929 +        if (!fieldTypeObjs.append(ObjectValue(*fieldType))) {
   1.930 +            js_ReportOutOfMemory(cx);
   1.931 +            return nullptr;
   1.932 +        }
   1.933 +
   1.934 +        // userFieldTypes[id] = typeObj
   1.935 +        if (!JSObject::defineGeneric(cx, userFieldTypes, id,
   1.936 +                                     fieldTypeObjs.handleAt(i), nullptr, nullptr,
   1.937 +                                     JSPROP_READONLY | JSPROP_PERMANENT))
   1.938 +            return nullptr;
   1.939 +
   1.940 +        // Append "f:Type" to the string repr
   1.941 +        if (i > 0 && !stringBuffer.append(", ")) {
   1.942 +            js_ReportOutOfMemory(cx);
   1.943 +            return nullptr;
   1.944 +        }
   1.945 +        if (!stringBuffer.append(JSID_TO_ATOM(id))) {
   1.946 +            js_ReportOutOfMemory(cx);
   1.947 +            return nullptr;
   1.948 +        }
   1.949 +        if (!stringBuffer.append(": ")) {
   1.950 +            js_ReportOutOfMemory(cx);
   1.951 +            return nullptr;
   1.952 +        }
   1.953 +        if (!stringBuffer.append(&fieldType->stringRepr())) {
   1.954 +            js_ReportOutOfMemory(cx);
   1.955 +            return nullptr;
   1.956 +        }
   1.957 +
   1.958 +        // Offset of this field is the current total size adjusted for
   1.959 +        // the field's alignment.
   1.960 +        CheckedInt32 offset = roundUpToAlignment(sizeSoFar, fieldType->alignment());
   1.961 +        if (!offset.isValid()) {
   1.962 +            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   1.963 +                                 JSMSG_TYPEDOBJECT_TOO_BIG);
   1.964 +            return nullptr;
   1.965 +        }
   1.966 +        if (!fieldOffsets.append(Int32Value(offset.value()))) {
   1.967 +            js_ReportOutOfMemory(cx);
   1.968 +            return nullptr;
   1.969 +        }
   1.970 +
   1.971 +        // userFieldOffsets[id] = offset
   1.972 +        RootedValue offsetValue(cx, Int32Value(offset.value()));
   1.973 +        if (!JSObject::defineGeneric(cx, userFieldOffsets, id,
   1.974 +                                     offsetValue, nullptr, nullptr,
   1.975 +                                     JSPROP_READONLY | JSPROP_PERMANENT))
   1.976 +            return nullptr;
   1.977 +
   1.978 +        // Add space for this field to the total struct size.
   1.979 +        sizeSoFar = offset + fieldType->size();
   1.980 +        if (!sizeSoFar.isValid()) {
   1.981 +            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   1.982 +                                 JSMSG_TYPEDOBJECT_TOO_BIG);
   1.983 +            return nullptr;
   1.984 +        }
   1.985 +
   1.986 +        // Struct is opaque if any field is opaque
   1.987 +        if (fieldType->opaque())
   1.988 +            opaque = true;
   1.989 +
   1.990 +        // Alignment of the struct is the max of the alignment of its fields.
   1.991 +        alignment = js::Max(alignment, fieldType->alignment());
   1.992 +    }
   1.993 +
   1.994 +    // Complete string representation.
   1.995 +    if (!stringBuffer.append("})")) {
   1.996 +        js_ReportOutOfMemory(cx);
   1.997 +        return nullptr;
   1.998 +    }
   1.999 +    RootedAtom stringRepr(cx, stringBuffer.finishAtom());
  1.1000 +    if (!stringRepr)
  1.1001 +        return nullptr;
  1.1002 +
  1.1003 +    // Adjust the total size to be a multiple of the final alignment.
  1.1004 +    CheckedInt32 totalSize = roundUpToAlignment(sizeSoFar, alignment);
  1.1005 +    if (!totalSize.isValid()) {
  1.1006 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
  1.1007 +                             JSMSG_TYPEDOBJECT_TOO_BIG);
  1.1008 +        return nullptr;
  1.1009 +    }
  1.1010 +
  1.1011 +    // Now create the resulting type descriptor.
  1.1012 +    RootedObject structTypePrototype(cx, GetPrototype(cx, metaTypeDescr));
  1.1013 +    if (!structTypePrototype)
  1.1014 +        return nullptr;
  1.1015 +
  1.1016 +    Rooted<StructTypeDescr*> descr(cx);
  1.1017 +    descr = NewObjectWithProto<StructTypeDescr>(cx, structTypePrototype, nullptr,
  1.1018 +                                                TenuredObject);
  1.1019 +    if (!descr)
  1.1020 +        return nullptr;
  1.1021 +
  1.1022 +    descr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(TypeDescr::Struct));
  1.1023 +    descr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
  1.1024 +    descr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(alignment));
  1.1025 +    descr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(totalSize.value()));
  1.1026 +    descr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(opaque));
  1.1027 +
  1.1028 +    // Construct for internal use an array with the name for each field.
  1.1029 +    {
  1.1030 +        RootedObject fieldNamesVec(cx);
  1.1031 +        fieldNamesVec = NewDenseCopiedArray(cx, fieldNames.length(),
  1.1032 +                                            fieldNames.begin(), nullptr,
  1.1033 +                                            TenuredObject);
  1.1034 +        if (!fieldNamesVec)
  1.1035 +            return nullptr;
  1.1036 +        descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES,
  1.1037 +                                     ObjectValue(*fieldNamesVec));
  1.1038 +    }
  1.1039 +
  1.1040 +    // Construct for internal use an array with the type object for each field.
  1.1041 +    {
  1.1042 +        RootedObject fieldTypeVec(cx);
  1.1043 +        fieldTypeVec = NewDenseCopiedArray(cx, fieldTypeObjs.length(),
  1.1044 +                                           fieldTypeObjs.begin(), nullptr,
  1.1045 +                                           TenuredObject);
  1.1046 +        if (!fieldTypeVec)
  1.1047 +            return nullptr;
  1.1048 +        descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_TYPES,
  1.1049 +                                     ObjectValue(*fieldTypeVec));
  1.1050 +    }
  1.1051 +
  1.1052 +    // Construct for internal use an array with the offset for each field.
  1.1053 +    {
  1.1054 +        RootedObject fieldOffsetsVec(cx);
  1.1055 +        fieldOffsetsVec = NewDenseCopiedArray(cx, fieldOffsets.length(),
  1.1056 +                                              fieldOffsets.begin(), nullptr,
  1.1057 +                                              TenuredObject);
  1.1058 +        if (!fieldOffsetsVec)
  1.1059 +            return nullptr;
  1.1060 +        descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS,
  1.1061 +                                     ObjectValue(*fieldOffsetsVec));
  1.1062 +    }
  1.1063 +
  1.1064 +    // Create data properties fieldOffsets and fieldTypes
  1.1065 +    if (!JSObject::freeze(cx, userFieldOffsets))
  1.1066 +        return nullptr;
  1.1067 +    if (!JSObject::freeze(cx, userFieldTypes))
  1.1068 +        return nullptr;
  1.1069 +    RootedValue userFieldOffsetsValue(cx, ObjectValue(*userFieldOffsets));
  1.1070 +    if (!JSObject::defineProperty(cx, descr, cx->names().fieldOffsets,
  1.1071 +                                  userFieldOffsetsValue, nullptr, nullptr,
  1.1072 +                                  JSPROP_READONLY | JSPROP_PERMANENT))
  1.1073 +    {
  1.1074 +        return nullptr;
  1.1075 +    }
  1.1076 +    RootedValue userFieldTypesValue(cx, ObjectValue(*userFieldTypes));
  1.1077 +    if (!JSObject::defineProperty(cx, descr, cx->names().fieldTypes,
  1.1078 +                                  userFieldTypesValue, nullptr, nullptr,
  1.1079 +                                  JSPROP_READONLY | JSPROP_PERMANENT))
  1.1080 +    {
  1.1081 +        return nullptr;
  1.1082 +    }
  1.1083 +
  1.1084 +    if (!CreateUserSizeAndAlignmentProperties(cx, descr))
  1.1085 +        return nullptr;
  1.1086 +
  1.1087 +    Rooted<TypedProto*> prototypeObj(cx);
  1.1088 +    prototypeObj = CreatePrototypeObjectForComplexTypeInstance(cx, descr, structTypePrototype);
  1.1089 +    if (!prototypeObj)
  1.1090 +        return nullptr;
  1.1091 +
  1.1092 +    descr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*prototypeObj));
  1.1093 +
  1.1094 +    if (!LinkConstructorAndPrototype(cx, descr, prototypeObj))
  1.1095 +        return nullptr;
  1.1096 +
  1.1097 +    return descr;
  1.1098 +}
  1.1099 +
  1.1100 +bool
  1.1101 +StructMetaTypeDescr::construct(JSContext *cx, unsigned int argc, Value *vp)
  1.1102 +{
  1.1103 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.1104 +
  1.1105 +    if (!args.isConstructing()) {
  1.1106 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
  1.1107 +                             JSMSG_NOT_FUNCTION, "StructType");
  1.1108 +        return false;
  1.1109 +    }
  1.1110 +
  1.1111 +    if (args.length() >= 1 && args[0].isObject()) {
  1.1112 +        RootedObject metaTypeDescr(cx, &args.callee());
  1.1113 +        RootedObject fields(cx, &args[0].toObject());
  1.1114 +        RootedObject obj(cx, create(cx, metaTypeDescr, fields));
  1.1115 +        if (!obj)
  1.1116 +            return false;
  1.1117 +        args.rval().setObject(*obj);
  1.1118 +        return true;
  1.1119 +    }
  1.1120 +
  1.1121 +    JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
  1.1122 +                         JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD_ARGS);
  1.1123 +    return false;
  1.1124 +}
  1.1125 +
  1.1126 +size_t
  1.1127 +StructTypeDescr::fieldCount()
  1.1128 +{
  1.1129 +    return getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).toObject().getDenseInitializedLength();
  1.1130 +}
  1.1131 +
  1.1132 +bool
  1.1133 +StructTypeDescr::fieldIndex(jsid id, size_t *out)
  1.1134 +{
  1.1135 +    JSObject &fieldNames = getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).toObject();
  1.1136 +    size_t l = fieldNames.getDenseInitializedLength();
  1.1137 +    for (size_t i = 0; i < l; i++) {
  1.1138 +        JSAtom &a = fieldNames.getDenseElement(i).toString()->asAtom();
  1.1139 +        if (JSID_IS_ATOM(id, &a)) {
  1.1140 +            *out = i;
  1.1141 +            return true;
  1.1142 +        }
  1.1143 +    }
  1.1144 +    return false;
  1.1145 +}
  1.1146 +
  1.1147 +JSAtom &
  1.1148 +StructTypeDescr::fieldName(size_t index)
  1.1149 +{
  1.1150 +    JSObject &fieldNames = getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).toObject();
  1.1151 +    return fieldNames.getDenseElement(index).toString()->asAtom();
  1.1152 +}
  1.1153 +
  1.1154 +int32_t
  1.1155 +StructTypeDescr::fieldOffset(size_t index)
  1.1156 +{
  1.1157 +    JSObject &fieldOffsets =
  1.1158 +        getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS).toObject();
  1.1159 +    JS_ASSERT(index < fieldOffsets.getDenseInitializedLength());
  1.1160 +    return fieldOffsets.getDenseElement(index).toInt32();
  1.1161 +}
  1.1162 +
  1.1163 +SizedTypeDescr&
  1.1164 +StructTypeDescr::fieldDescr(size_t index)
  1.1165 +{
  1.1166 +    JSObject &fieldDescrs =
  1.1167 +        getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_TYPES).toObject();
  1.1168 +    JS_ASSERT(index < fieldDescrs.getDenseInitializedLength());
  1.1169 +    return fieldDescrs.getDenseElement(index).toObject().as<SizedTypeDescr>();
  1.1170 +}
  1.1171 +
  1.1172 +/******************************************************************************
  1.1173 + * Creating the TypedObject "module"
  1.1174 + *
  1.1175 + * We create one global, `TypedObject`, which contains the following
  1.1176 + * members:
  1.1177 + *
  1.1178 + * 1. uint8, uint16, etc
  1.1179 + * 2. ArrayType
  1.1180 + * 3. StructType
  1.1181 + *
  1.1182 + * Each of these is a function and hence their prototype is
  1.1183 + * `Function.__proto__` (in terms of the JS Engine, they are not
  1.1184 + * JSFunctions but rather instances of their own respective JSClasses
  1.1185 + * which override the call and construct operations).
  1.1186 + *
  1.1187 + * Each type object also has its own `prototype` field. Therefore,
  1.1188 + * using `StructType` as an example, the basic setup is:
  1.1189 + *
  1.1190 + *   StructType --__proto__--> Function.__proto__
  1.1191 + *        |
  1.1192 + *    prototype -- prototype --> { }
  1.1193 + *        |
  1.1194 + *        v
  1.1195 + *       { } -----__proto__--> Function.__proto__
  1.1196 + *
  1.1197 + * When a new type object (e.g., an instance of StructType) is created,
  1.1198 + * it will look as follows:
  1.1199 + *
  1.1200 + *   MyStruct -__proto__-> StructType.prototype -__proto__-> Function.__proto__
  1.1201 + *        |                          |
  1.1202 + *        |                     prototype
  1.1203 + *        |                          |
  1.1204 + *        |                          v
  1.1205 + *    prototype -----__proto__----> { }
  1.1206 + *        |
  1.1207 + *        v
  1.1208 + *       { } --__proto__-> Object.prototype
  1.1209 + *
  1.1210 + * Finally, when an instance of `MyStruct` is created, its
  1.1211 + * structure is as follows:
  1.1212 + *
  1.1213 + *    object -__proto__->
  1.1214 + *      MyStruct.prototype -__proto__->
  1.1215 + *        StructType.prototype.prototype -__proto__->
  1.1216 + *          Object.prototype
  1.1217 + */
  1.1218 +
  1.1219 +// Here `T` is either `ScalarTypeDescr` or `ReferenceTypeDescr`
  1.1220 +template<typename T>
  1.1221 +static bool
  1.1222 +DefineSimpleTypeDescr(JSContext *cx,
  1.1223 +                      Handle<GlobalObject *> global,
  1.1224 +                      HandleObject module,
  1.1225 +                      typename T::Type type,
  1.1226 +                      HandlePropertyName className)
  1.1227 +{
  1.1228 +    RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
  1.1229 +    if (!objProto)
  1.1230 +        return false;
  1.1231 +
  1.1232 +    RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx));
  1.1233 +    if (!funcProto)
  1.1234 +        return false;
  1.1235 +
  1.1236 +    Rooted<T*> descr(cx);
  1.1237 +    descr = NewObjectWithProto<T>(cx, funcProto, global, TenuredObject);
  1.1238 +    if (!descr)
  1.1239 +        return false;
  1.1240 +
  1.1241 +    descr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(T::Kind));
  1.1242 +    descr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(className));
  1.1243 +    descr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(T::alignment(type)));
  1.1244 +    descr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(T::size(type)));
  1.1245 +    descr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(T::Opaque));
  1.1246 +    descr->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(type));
  1.1247 +
  1.1248 +    if (!CreateUserSizeAndAlignmentProperties(cx, descr))
  1.1249 +        return false;
  1.1250 +
  1.1251 +    if (!JS_DefineFunctions(cx, descr, T::typeObjectMethods))
  1.1252 +        return false;
  1.1253 +
  1.1254 +    // Create the typed prototype for the scalar type. This winds up
  1.1255 +    // not being user accessible, but we still create one for consistency.
  1.1256 +    Rooted<TypedProto*> proto(cx);
  1.1257 +    proto = NewObjectWithProto<TypedProto>(cx, objProto, nullptr, TenuredObject);
  1.1258 +    if (!proto)
  1.1259 +        return nullptr;
  1.1260 +    proto->initTypeDescrSlot(*descr);
  1.1261 +    descr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*proto));
  1.1262 +
  1.1263 +    RootedValue descrValue(cx, ObjectValue(*descr));
  1.1264 +    if (!JSObject::defineProperty(cx, module, className,
  1.1265 +                                  descrValue, nullptr, nullptr, 0))
  1.1266 +    {
  1.1267 +        return false;
  1.1268 +    }
  1.1269 +
  1.1270 +    return true;
  1.1271 +}
  1.1272 +
  1.1273 +///////////////////////////////////////////////////////////////////////////
  1.1274 +
  1.1275 +template<typename T>
  1.1276 +static JSObject *
  1.1277 +DefineMetaTypeDescr(JSContext *cx,
  1.1278 +                    Handle<GlobalObject*> global,
  1.1279 +                    HandleObject module,
  1.1280 +                    TypedObjectModuleObject::Slot protoSlot)
  1.1281 +{
  1.1282 +    RootedAtom className(cx, Atomize(cx, T::class_.name,
  1.1283 +                                     strlen(T::class_.name)));
  1.1284 +    if (!className)
  1.1285 +        return nullptr;
  1.1286 +
  1.1287 +    RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx));
  1.1288 +    if (!funcProto)
  1.1289 +        return nullptr;
  1.1290 +
  1.1291 +    // Create ctor.prototype, which inherits from Function.__proto__
  1.1292 +
  1.1293 +    RootedObject proto(cx, NewObjectWithProto<JSObject>(cx, funcProto, global,
  1.1294 +                                                        SingletonObject));
  1.1295 +    if (!proto)
  1.1296 +        return nullptr;
  1.1297 +
  1.1298 +    // Create ctor.prototype.prototype, which inherits from Object.__proto__
  1.1299 +
  1.1300 +    RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
  1.1301 +    if (!objProto)
  1.1302 +        return nullptr;
  1.1303 +    RootedObject protoProto(cx);
  1.1304 +    protoProto = NewObjectWithProto<JSObject>(cx, objProto,
  1.1305 +                                              global, SingletonObject);
  1.1306 +    if (!protoProto)
  1.1307 +        return nullptr;
  1.1308 +
  1.1309 +    RootedValue protoProtoValue(cx, ObjectValue(*protoProto));
  1.1310 +    if (!JSObject::defineProperty(cx, proto, cx->names().prototype,
  1.1311 +                                  protoProtoValue,
  1.1312 +                                  nullptr, nullptr,
  1.1313 +                                  JSPROP_READONLY | JSPROP_PERMANENT))
  1.1314 +        return nullptr;
  1.1315 +
  1.1316 +    // Create ctor itself
  1.1317 +
  1.1318 +    const int constructorLength = 2;
  1.1319 +    RootedFunction ctor(cx);
  1.1320 +    ctor = global->createConstructor(cx, T::construct, className, constructorLength);
  1.1321 +    if (!ctor ||
  1.1322 +        !LinkConstructorAndPrototype(cx, ctor, proto) ||
  1.1323 +        !DefinePropertiesAndBrand(cx, proto,
  1.1324 +                                  T::typeObjectProperties,
  1.1325 +                                  T::typeObjectMethods) ||
  1.1326 +        !DefinePropertiesAndBrand(cx, protoProto,
  1.1327 +                                  T::typedObjectProperties,
  1.1328 +                                  T::typedObjectMethods))
  1.1329 +    {
  1.1330 +        return nullptr;
  1.1331 +    }
  1.1332 +
  1.1333 +    module->initReservedSlot(protoSlot, ObjectValue(*proto));
  1.1334 +
  1.1335 +    return ctor;
  1.1336 +}
  1.1337 +
  1.1338 +/*  The initialization strategy for TypedObjects is mildly unusual
  1.1339 + * compared to other classes. Because all of the types are members
  1.1340 + * of a single global, `TypedObject`, we basically make the
  1.1341 + * initializer for the `TypedObject` class populate the
  1.1342 + * `TypedObject` global (which is referred to as "module" herein).
  1.1343 + */
  1.1344 +bool
  1.1345 +GlobalObject::initTypedObjectModule(JSContext *cx, Handle<GlobalObject*> global)
  1.1346 +{
  1.1347 +    RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
  1.1348 +    if (!objProto)
  1.1349 +        return false;
  1.1350 +
  1.1351 +    Rooted<TypedObjectModuleObject*> module(cx);
  1.1352 +    module = NewObjectWithProto<TypedObjectModuleObject>(cx, objProto, global);
  1.1353 +    if (!module)
  1.1354 +        return false;
  1.1355 +
  1.1356 +    if (!JS_DefineFunctions(cx, module, TypedObjectMethods))
  1.1357 +        return false;
  1.1358 +
  1.1359 +    // uint8, uint16, any, etc
  1.1360 +
  1.1361 +#define BINARYDATA_SCALAR_DEFINE(constant_, type_, name_)                       \
  1.1362 +    if (!DefineSimpleTypeDescr<ScalarTypeDescr>(cx, global, module, constant_,      \
  1.1363 +                                            cx->names().name_))                 \
  1.1364 +        return nullptr;
  1.1365 +    JS_FOR_EACH_SCALAR_TYPE_REPR(BINARYDATA_SCALAR_DEFINE)
  1.1366 +#undef BINARYDATA_SCALAR_DEFINE
  1.1367 +
  1.1368 +#define BINARYDATA_REFERENCE_DEFINE(constant_, type_, name_)                    \
  1.1369 +    if (!DefineSimpleTypeDescr<ReferenceTypeDescr>(cx, global, module, constant_,   \
  1.1370 +                                               cx->names().name_))              \
  1.1371 +        return nullptr;
  1.1372 +    JS_FOR_EACH_REFERENCE_TYPE_REPR(BINARYDATA_REFERENCE_DEFINE)
  1.1373 +#undef BINARYDATA_REFERENCE_DEFINE
  1.1374 +
  1.1375 +    // ArrayType.
  1.1376 +
  1.1377 +    RootedObject arrayType(cx);
  1.1378 +    arrayType = DefineMetaTypeDescr<ArrayMetaTypeDescr>(
  1.1379 +        cx, global, module, TypedObjectModuleObject::ArrayTypePrototype);
  1.1380 +    if (!arrayType)
  1.1381 +        return nullptr;
  1.1382 +
  1.1383 +    RootedValue arrayTypeValue(cx, ObjectValue(*arrayType));
  1.1384 +    if (!JSObject::defineProperty(cx, module, cx->names().ArrayType,
  1.1385 +                                  arrayTypeValue,
  1.1386 +                                  nullptr, nullptr,
  1.1387 +                                  JSPROP_READONLY | JSPROP_PERMANENT))
  1.1388 +        return nullptr;
  1.1389 +
  1.1390 +    // StructType.
  1.1391 +
  1.1392 +    RootedObject structType(cx);
  1.1393 +    structType = DefineMetaTypeDescr<StructMetaTypeDescr>(
  1.1394 +        cx, global, module, TypedObjectModuleObject::StructTypePrototype);
  1.1395 +    if (!structType)
  1.1396 +        return nullptr;
  1.1397 +
  1.1398 +    RootedValue structTypeValue(cx, ObjectValue(*structType));
  1.1399 +    if (!JSObject::defineProperty(cx, module, cx->names().StructType,
  1.1400 +                                  structTypeValue,
  1.1401 +                                  nullptr, nullptr,
  1.1402 +                                  JSPROP_READONLY | JSPROP_PERMANENT))
  1.1403 +        return nullptr;
  1.1404 +
  1.1405 +    // Everything is setup, install module on the global object:
  1.1406 +    RootedValue moduleValue(cx, ObjectValue(*module));
  1.1407 +    global->setConstructor(JSProto_TypedObject, moduleValue);
  1.1408 +    if (!JSObject::defineProperty(cx, global, cx->names().TypedObject,
  1.1409 +                                  moduleValue,
  1.1410 +                                  nullptr, nullptr,
  1.1411 +                                  0))
  1.1412 +    {
  1.1413 +        return nullptr;
  1.1414 +    }
  1.1415 +
  1.1416 +    return module;
  1.1417 +}
  1.1418 +
  1.1419 +JSObject *
  1.1420 +js_InitTypedObjectModuleObject(JSContext *cx, HandleObject obj)
  1.1421 +{
  1.1422 +    JS_ASSERT(obj->is<GlobalObject>());
  1.1423 +    Rooted<GlobalObject *> global(cx, &obj->as<GlobalObject>());
  1.1424 +    return global->getOrCreateTypedObjectModule(cx);
  1.1425 +}
  1.1426 +
  1.1427 +JSObject *
  1.1428 +js_InitTypedObjectDummy(JSContext *cx, HandleObject obj)
  1.1429 +{
  1.1430 +    /*
  1.1431 +     * This function is entered into the jsprototypes.h table
  1.1432 +     * as the initializer for `TypedObject`. It should not
  1.1433 +     * be executed via the `standard_class_atoms` mechanism.
  1.1434 +     */
  1.1435 +
  1.1436 +    MOZ_ASSUME_UNREACHABLE("shouldn't be initializing TypedObject via the JSProtoKey initializer mechanism");
  1.1437 +}
  1.1438 +
  1.1439 +/******************************************************************************
  1.1440 + * Typed objects
  1.1441 + */
  1.1442 +
  1.1443 +/*static*/ TypedObject *
  1.1444 +TypedObject::createUnattached(JSContext *cx,
  1.1445 +                             HandleTypeDescr descr,
  1.1446 +                             int32_t length)
  1.1447 +{
  1.1448 +    if (descr->opaque())
  1.1449 +        return createUnattachedWithClass(cx, &OpaqueTypedObject::class_, descr, length);
  1.1450 +    else
  1.1451 +        return createUnattachedWithClass(cx, &TransparentTypedObject::class_, descr, length);
  1.1452 +}
  1.1453 +
  1.1454 +
  1.1455 +/*static*/ TypedObject *
  1.1456 +TypedObject::createUnattachedWithClass(JSContext *cx,
  1.1457 +                                      const Class *clasp,
  1.1458 +                                      HandleTypeDescr type,
  1.1459 +                                      int32_t length)
  1.1460 +{
  1.1461 +    JS_ASSERT(clasp == &TransparentTypedObject::class_ ||
  1.1462 +              clasp == &OpaqueTypedObject::class_);
  1.1463 +    JS_ASSERT(JSCLASS_RESERVED_SLOTS(clasp) == JS_TYPEDOBJ_SLOTS);
  1.1464 +    JS_ASSERT(clasp->hasPrivate());
  1.1465 +
  1.1466 +    RootedObject proto(cx);
  1.1467 +    if (type->is<SimpleTypeDescr>()) {
  1.1468 +        // FIXME Bug 929651 -- What prototype to use?
  1.1469 +        proto = type->global().getOrCreateObjectPrototype(cx);
  1.1470 +    } else {
  1.1471 +        RootedValue protoVal(cx);
  1.1472 +        if (!JSObject::getProperty(cx, type, type,
  1.1473 +                                   cx->names().prototype, &protoVal))
  1.1474 +        {
  1.1475 +            return nullptr;
  1.1476 +        }
  1.1477 +        proto = &protoVal.toObject();
  1.1478 +    }
  1.1479 +
  1.1480 +    RootedObject obj(cx, NewObjectWithClassProto(cx, clasp, &*proto, nullptr));
  1.1481 +    if (!obj)
  1.1482 +        return nullptr;
  1.1483 +
  1.1484 +    obj->setPrivate(nullptr);
  1.1485 +    obj->initReservedSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(0));
  1.1486 +    obj->initReservedSlot(JS_TYPEDOBJ_SLOT_BYTELENGTH, Int32Value(0));
  1.1487 +    obj->initReservedSlot(JS_TYPEDOBJ_SLOT_OWNER, NullValue());
  1.1488 +    obj->initReservedSlot(JS_TYPEDOBJ_SLOT_NEXT_VIEW, PrivateValue(nullptr));
  1.1489 +    obj->initReservedSlot(JS_TYPEDOBJ_SLOT_LENGTH, Int32Value(length));
  1.1490 +    obj->initReservedSlot(JS_TYPEDOBJ_SLOT_TYPE_DESCR, ObjectValue(*type));
  1.1491 +
  1.1492 +    // Tag the type object for this instance with the type
  1.1493 +    // representation, if that has not been done already.
  1.1494 +    if (!type->is<SimpleTypeDescr>()) { // FIXME Bug 929651
  1.1495 +        RootedTypeObject typeObj(cx, obj->getType(cx));
  1.1496 +        if (typeObj) {
  1.1497 +            if (!typeObj->addTypedObjectAddendum(cx, type))
  1.1498 +                return nullptr;
  1.1499 +        }
  1.1500 +    }
  1.1501 +
  1.1502 +    return static_cast<TypedObject*>(&*obj);
  1.1503 +}
  1.1504 +
  1.1505 +void
  1.1506 +TypedObject::attach(ArrayBufferObject &buffer, int32_t offset)
  1.1507 +{
  1.1508 +    JS_ASSERT(offset >= 0);
  1.1509 +    JS_ASSERT((size_t) (offset + size()) <= buffer.byteLength());
  1.1510 +
  1.1511 +    buffer.addView(this);
  1.1512 +    InitArrayBufferViewDataPointer(this, &buffer, offset);
  1.1513 +    setReservedSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(offset));
  1.1514 +    setReservedSlot(JS_TYPEDOBJ_SLOT_OWNER, ObjectValue(buffer));
  1.1515 +}
  1.1516 +
  1.1517 +void
  1.1518 +TypedObject::attach(TypedObject &typedObj, int32_t offset)
  1.1519 +{
  1.1520 +    JS_ASSERT(!typedObj.owner().isNeutered());
  1.1521 +    JS_ASSERT(typedObj.typedMem() != NULL);
  1.1522 +
  1.1523 +    attach(typedObj.owner(), typedObj.offset() + offset);
  1.1524 +}
  1.1525 +
  1.1526 +// Returns a suitable JS_TYPEDOBJ_SLOT_LENGTH value for an instance of
  1.1527 +// the type `type`. `type` must not be an unsized array.
  1.1528 +static int32_t
  1.1529 +TypedObjLengthFromType(TypeDescr &descr)
  1.1530 +{
  1.1531 +    switch (descr.kind()) {
  1.1532 +      case TypeDescr::Scalar:
  1.1533 +      case TypeDescr::Reference:
  1.1534 +      case TypeDescr::Struct:
  1.1535 +      case TypeDescr::X4:
  1.1536 +        return 0;
  1.1537 +
  1.1538 +      case TypeDescr::SizedArray:
  1.1539 +        return descr.as<SizedArrayTypeDescr>().length();
  1.1540 +
  1.1541 +      case TypeDescr::UnsizedArray:
  1.1542 +        MOZ_ASSUME_UNREACHABLE("TypedObjLengthFromType() invoked on unsized type");
  1.1543 +    }
  1.1544 +    MOZ_ASSUME_UNREACHABLE("Invalid kind");
  1.1545 +}
  1.1546 +
  1.1547 +/*static*/ TypedObject *
  1.1548 +TypedObject::createDerived(JSContext *cx, HandleSizedTypeDescr type,
  1.1549 +                           HandleTypedObject typedObj, int32_t offset)
  1.1550 +{
  1.1551 +    JS_ASSERT(!typedObj->owner().isNeutered());
  1.1552 +    JS_ASSERT(typedObj->typedMem() != NULL);
  1.1553 +    JS_ASSERT(offset <= typedObj->size());
  1.1554 +    JS_ASSERT(offset + type->size() <= typedObj->size());
  1.1555 +
  1.1556 +    int32_t length = TypedObjLengthFromType(*type);
  1.1557 +
  1.1558 +    const js::Class *clasp = typedObj->getClass();
  1.1559 +    Rooted<TypedObject*> obj(cx);
  1.1560 +    obj = createUnattachedWithClass(cx, clasp, type, length);
  1.1561 +    if (!obj)
  1.1562 +        return nullptr;
  1.1563 +
  1.1564 +    obj->attach(*typedObj, offset);
  1.1565 +    return obj;
  1.1566 +}
  1.1567 +
  1.1568 +/*static*/ TypedObject *
  1.1569 +TypedObject::createZeroed(JSContext *cx,
  1.1570 +                          HandleTypeDescr descr,
  1.1571 +                          int32_t length)
  1.1572 +{
  1.1573 +    // Create unattached wrapper object.
  1.1574 +    Rooted<TypedObject*> obj(cx, createUnattached(cx, descr, length));
  1.1575 +    if (!obj)
  1.1576 +        return nullptr;
  1.1577 +
  1.1578 +    // Allocate and initialize the memory for this instance.
  1.1579 +    // Also initialize the JS_TYPEDOBJ_SLOT_LENGTH slot.
  1.1580 +    switch (descr->kind()) {
  1.1581 +      case TypeDescr::Scalar:
  1.1582 +      case TypeDescr::Reference:
  1.1583 +      case TypeDescr::Struct:
  1.1584 +      case TypeDescr::X4:
  1.1585 +      case TypeDescr::SizedArray:
  1.1586 +      {
  1.1587 +        size_t totalSize = descr->as<SizedTypeDescr>().size();
  1.1588 +        Rooted<ArrayBufferObject*> buffer(cx);
  1.1589 +        buffer = ArrayBufferObject::create(cx, totalSize);
  1.1590 +        if (!buffer)
  1.1591 +            return nullptr;
  1.1592 +        descr->as<SizedTypeDescr>().initInstances(cx->runtime(), buffer->dataPointer(), 1);
  1.1593 +        obj->attach(*buffer, 0);
  1.1594 +        return obj;
  1.1595 +      }
  1.1596 +
  1.1597 +      case TypeDescr::UnsizedArray:
  1.1598 +      {
  1.1599 +        Rooted<SizedTypeDescr*> elementTypeRepr(cx);
  1.1600 +        elementTypeRepr = &descr->as<UnsizedArrayTypeDescr>().elementType();
  1.1601 +
  1.1602 +        CheckedInt32 totalSize = CheckedInt32(elementTypeRepr->size()) * length;
  1.1603 +        if (!totalSize.isValid()) {
  1.1604 +            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
  1.1605 +                                 JSMSG_TYPEDOBJECT_TOO_BIG);
  1.1606 +            return nullptr;
  1.1607 +        }
  1.1608 +
  1.1609 +        Rooted<ArrayBufferObject*> buffer(cx);
  1.1610 +        buffer = ArrayBufferObject::create(cx, totalSize.value());
  1.1611 +        if (!buffer)
  1.1612 +            return nullptr;
  1.1613 +
  1.1614 +        if (length)
  1.1615 +            elementTypeRepr->initInstances(cx->runtime(), buffer->dataPointer(), length);
  1.1616 +        obj->attach(*buffer, 0);
  1.1617 +        return obj;
  1.1618 +      }
  1.1619 +    }
  1.1620 +
  1.1621 +    MOZ_ASSUME_UNREACHABLE("Bad TypeRepresentation Kind");
  1.1622 +}
  1.1623 +
  1.1624 +static bool
  1.1625 +ReportTypedObjTypeError(JSContext *cx,
  1.1626 +                        const unsigned errorNumber,
  1.1627 +                        HandleTypedObject obj)
  1.1628 +{
  1.1629 +    // Serialize type string of obj
  1.1630 +    char *typeReprStr = JS_EncodeString(cx, &obj->typeDescr().stringRepr());
  1.1631 +    if (!typeReprStr)
  1.1632 +        return false;
  1.1633 +
  1.1634 +    JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
  1.1635 +                         errorNumber, typeReprStr);
  1.1636 +
  1.1637 +    JS_free(cx, (void *) typeReprStr);
  1.1638 +    return false;
  1.1639 +}
  1.1640 +
  1.1641 +/*static*/ void
  1.1642 +TypedObject::obj_trace(JSTracer *trace, JSObject *object)
  1.1643 +{
  1.1644 +    gc::MarkSlot(trace, &object->getReservedSlotRef(JS_TYPEDOBJ_SLOT_TYPE_DESCR),
  1.1645 +                 "TypedObjectTypeDescr");
  1.1646 +
  1.1647 +    ArrayBufferViewObject::trace(trace, object);
  1.1648 +
  1.1649 +    JS_ASSERT(object->is<TypedObject>());
  1.1650 +    TypedObject &typedObj = object->as<TypedObject>();
  1.1651 +    TypeDescr &descr = typedObj.typeDescr();
  1.1652 +    if (descr.opaque()) {
  1.1653 +        uint8_t *mem = typedObj.typedMem();
  1.1654 +        if (!mem)
  1.1655 +            return; // partially constructed
  1.1656 +
  1.1657 +        if (typedObj.owner().isNeutered())
  1.1658 +            return;
  1.1659 +
  1.1660 +        switch (descr.kind()) {
  1.1661 +          case TypeDescr::Scalar:
  1.1662 +          case TypeDescr::Reference:
  1.1663 +          case TypeDescr::Struct:
  1.1664 +          case TypeDescr::SizedArray:
  1.1665 +          case TypeDescr::X4:
  1.1666 +            descr.as<SizedTypeDescr>().traceInstances(trace, mem, 1);
  1.1667 +            break;
  1.1668 +
  1.1669 +          case TypeDescr::UnsizedArray:
  1.1670 +            descr.as<UnsizedArrayTypeDescr>().elementType().traceInstances(trace, mem, typedObj.length());
  1.1671 +            break;
  1.1672 +        }
  1.1673 +    }
  1.1674 +}
  1.1675 +
  1.1676 +bool
  1.1677 +TypedObject::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
  1.1678 +                              MutableHandleObject objp, MutableHandleShape propp)
  1.1679 +{
  1.1680 +    JS_ASSERT(obj->is<TypedObject>());
  1.1681 +
  1.1682 +    Rooted<TypeDescr*> descr(cx, &obj->as<TypedObject>().typeDescr());
  1.1683 +    switch (descr->kind()) {
  1.1684 +      case TypeDescr::Scalar:
  1.1685 +      case TypeDescr::Reference:
  1.1686 +      case TypeDescr::X4:
  1.1687 +        break;
  1.1688 +
  1.1689 +      case TypeDescr::SizedArray:
  1.1690 +      case TypeDescr::UnsizedArray:
  1.1691 +      {
  1.1692 +        uint32_t index;
  1.1693 +        if (js_IdIsIndex(id, &index))
  1.1694 +            return obj_lookupElement(cx, obj, index, objp, propp);
  1.1695 +
  1.1696 +        if (JSID_IS_ATOM(id, cx->names().length)) {
  1.1697 +            MarkNonNativePropertyFound(propp);
  1.1698 +            objp.set(obj);
  1.1699 +            return true;
  1.1700 +        }
  1.1701 +        break;
  1.1702 +      }
  1.1703 +
  1.1704 +      case TypeDescr::Struct:
  1.1705 +      {
  1.1706 +        StructTypeDescr &structDescr = descr->as<StructTypeDescr>();
  1.1707 +        size_t index;
  1.1708 +        if (structDescr.fieldIndex(id, &index)) {
  1.1709 +            MarkNonNativePropertyFound(propp);
  1.1710 +            objp.set(obj);
  1.1711 +            return true;
  1.1712 +        }
  1.1713 +        break;
  1.1714 +      }
  1.1715 +    }
  1.1716 +
  1.1717 +    RootedObject proto(cx, obj->getProto());
  1.1718 +    if (!proto) {
  1.1719 +        objp.set(nullptr);
  1.1720 +        propp.set(nullptr);
  1.1721 +        return true;
  1.1722 +    }
  1.1723 +
  1.1724 +    return JSObject::lookupGeneric(cx, proto, id, objp, propp);
  1.1725 +}
  1.1726 +
  1.1727 +bool
  1.1728 +TypedObject::obj_lookupProperty(JSContext *cx,
  1.1729 +                                HandleObject obj,
  1.1730 +                                HandlePropertyName name,
  1.1731 +                                MutableHandleObject objp,
  1.1732 +                                MutableHandleShape propp)
  1.1733 +{
  1.1734 +    RootedId id(cx, NameToId(name));
  1.1735 +    return obj_lookupGeneric(cx, obj, id, objp, propp);
  1.1736 +}
  1.1737 +
  1.1738 +bool
  1.1739 +TypedObject::obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
  1.1740 +                                MutableHandleObject objp, MutableHandleShape propp)
  1.1741 +{
  1.1742 +    JS_ASSERT(obj->is<TypedObject>());
  1.1743 +    MarkNonNativePropertyFound(propp);
  1.1744 +    objp.set(obj);
  1.1745 +    return true;
  1.1746 +}
  1.1747 +
  1.1748 +static bool
  1.1749 +ReportPropertyError(JSContext *cx,
  1.1750 +                    const unsigned errorNumber,
  1.1751 +                    HandleId id)
  1.1752 +{
  1.1753 +    RootedString str(cx, IdToString(cx, id));
  1.1754 +    if (!str)
  1.1755 +        return false;
  1.1756 +
  1.1757 +    char *propName = JS_EncodeString(cx, str);
  1.1758 +    if (!propName)
  1.1759 +        return false;
  1.1760 +
  1.1761 +    JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
  1.1762 +                         errorNumber, propName);
  1.1763 +
  1.1764 +    JS_free(cx, propName);
  1.1765 +    return false;
  1.1766 +}
  1.1767 +
  1.1768 +bool
  1.1769 +TypedObject::obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
  1.1770 +                              PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
  1.1771 +{
  1.1772 +    return ReportPropertyError(cx, JSMSG_UNDEFINED_PROP, id);
  1.1773 +}
  1.1774 +
  1.1775 +bool
  1.1776 +TypedObject::obj_defineProperty(JSContext *cx, HandleObject obj,
  1.1777 +                               HandlePropertyName name, HandleValue v,
  1.1778 +                               PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
  1.1779 +{
  1.1780 +    Rooted<jsid> id(cx, NameToId(name));
  1.1781 +    return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs);
  1.1782 +}
  1.1783 +
  1.1784 +bool
  1.1785 +TypedObject::obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v,
  1.1786 +                               PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
  1.1787 +{
  1.1788 +    AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
  1.1789 +    Rooted<jsid> id(cx);
  1.1790 +    if (!IndexToId(cx, index, &id))
  1.1791 +        return false;
  1.1792 +    return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs);
  1.1793 +}
  1.1794 +
  1.1795 +bool
  1.1796 +TypedObject::obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
  1.1797 +                           HandleId id, MutableHandleValue vp)
  1.1798 +{
  1.1799 +    JS_ASSERT(obj->is<TypedObject>());
  1.1800 +    Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
  1.1801 +
  1.1802 +    // Dispatch elements to obj_getElement:
  1.1803 +    uint32_t index;
  1.1804 +    if (js_IdIsIndex(id, &index))
  1.1805 +        return obj_getElement(cx, obj, receiver, index, vp);
  1.1806 +
  1.1807 +    // Handle everything else here:
  1.1808 +
  1.1809 +    switch (typedObj->typeDescr().kind()) {
  1.1810 +      case TypeDescr::Scalar:
  1.1811 +      case TypeDescr::Reference:
  1.1812 +        break;
  1.1813 +
  1.1814 +      case TypeDescr::X4:
  1.1815 +        break;
  1.1816 +
  1.1817 +      case TypeDescr::SizedArray:
  1.1818 +      case TypeDescr::UnsizedArray:
  1.1819 +        if (JSID_IS_ATOM(id, cx->names().length)) {
  1.1820 +            if (typedObj->owner().isNeutered() || !typedObj->typedMem()) { // unattached
  1.1821 +                JS_ReportErrorNumber(
  1.1822 +                    cx, js_GetErrorMessage,
  1.1823 +                    nullptr, JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
  1.1824 +                return false;
  1.1825 +            }
  1.1826 +
  1.1827 +            vp.setInt32(typedObj->length());
  1.1828 +            return true;
  1.1829 +        }
  1.1830 +        break;
  1.1831 +
  1.1832 +      case TypeDescr::Struct: {
  1.1833 +        Rooted<StructTypeDescr*> descr(cx, &typedObj->typeDescr().as<StructTypeDescr>());
  1.1834 +
  1.1835 +        size_t fieldIndex;
  1.1836 +        if (!descr->fieldIndex(id, &fieldIndex))
  1.1837 +            break;
  1.1838 +
  1.1839 +        size_t offset = descr->fieldOffset(fieldIndex);
  1.1840 +        Rooted<SizedTypeDescr*> fieldType(cx, &descr->fieldDescr(fieldIndex));
  1.1841 +        return Reify(cx, fieldType, typedObj, offset, vp);
  1.1842 +      }
  1.1843 +    }
  1.1844 +
  1.1845 +    RootedObject proto(cx, obj->getProto());
  1.1846 +    if (!proto) {
  1.1847 +        vp.setUndefined();
  1.1848 +        return true;
  1.1849 +    }
  1.1850 +
  1.1851 +    return JSObject::getGeneric(cx, proto, receiver, id, vp);
  1.1852 +}
  1.1853 +
  1.1854 +bool
  1.1855 +TypedObject::obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
  1.1856 +                              HandlePropertyName name, MutableHandleValue vp)
  1.1857 +{
  1.1858 +    RootedId id(cx, NameToId(name));
  1.1859 +    return obj_getGeneric(cx, obj, receiver, id, vp);
  1.1860 +}
  1.1861 +
  1.1862 +bool
  1.1863 +TypedObject::obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver,
  1.1864 +                             uint32_t index, MutableHandleValue vp)
  1.1865 +{
  1.1866 +    JS_ASSERT(obj->is<TypedObject>());
  1.1867 +    Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
  1.1868 +    Rooted<TypeDescr *> descr(cx, &typedObj->typeDescr());
  1.1869 +
  1.1870 +    switch (descr->kind()) {
  1.1871 +      case TypeDescr::Scalar:
  1.1872 +      case TypeDescr::Reference:
  1.1873 +      case TypeDescr::X4:
  1.1874 +      case TypeDescr::Struct:
  1.1875 +        break;
  1.1876 +
  1.1877 +      case TypeDescr::SizedArray:
  1.1878 +        return obj_getArrayElement<SizedArrayTypeDescr>(cx, typedObj, descr,
  1.1879 +                                                        index, vp);
  1.1880 +
  1.1881 +      case TypeDescr::UnsizedArray:
  1.1882 +        return obj_getArrayElement<UnsizedArrayTypeDescr>(cx, typedObj, descr,
  1.1883 +                                                          index, vp);
  1.1884 +    }
  1.1885 +
  1.1886 +    RootedObject proto(cx, obj->getProto());
  1.1887 +    if (!proto) {
  1.1888 +        vp.setUndefined();
  1.1889 +        return true;
  1.1890 +    }
  1.1891 +
  1.1892 +    return JSObject::getElement(cx, proto, receiver, index, vp);
  1.1893 +}
  1.1894 +
  1.1895 +template<class T>
  1.1896 +/*static*/ bool
  1.1897 +TypedObject::obj_getArrayElement(JSContext *cx,
  1.1898 +                                Handle<TypedObject*> typedObj,
  1.1899 +                                Handle<TypeDescr*> typeDescr,
  1.1900 +                                uint32_t index,
  1.1901 +                                MutableHandleValue vp)
  1.1902 +{
  1.1903 +    JS_ASSERT(typeDescr->is<T>());
  1.1904 +
  1.1905 +    if (index >= (size_t) typedObj->length()) {
  1.1906 +        vp.setUndefined();
  1.1907 +        return true;
  1.1908 +    }
  1.1909 +
  1.1910 +    Rooted<SizedTypeDescr*> elementType(cx, &typeDescr->as<T>().elementType());
  1.1911 +    size_t offset = elementType->size() * index;
  1.1912 +    return Reify(cx, elementType, typedObj, offset, vp);
  1.1913 +}
  1.1914 +
  1.1915 +bool
  1.1916 +TypedObject::obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
  1.1917 +                           MutableHandleValue vp, bool strict)
  1.1918 +{
  1.1919 +    JS_ASSERT(obj->is<TypedObject>());
  1.1920 +    Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
  1.1921 +
  1.1922 +    uint32_t index;
  1.1923 +    if (js_IdIsIndex(id, &index))
  1.1924 +        return obj_setElement(cx, obj, index, vp, strict);
  1.1925 +
  1.1926 +    switch (typedObj->typeDescr().kind()) {
  1.1927 +      case ScalarTypeDescr::Scalar:
  1.1928 +      case TypeDescr::Reference:
  1.1929 +        break;
  1.1930 +
  1.1931 +      case ScalarTypeDescr::X4:
  1.1932 +        break;
  1.1933 +
  1.1934 +      case ScalarTypeDescr::SizedArray:
  1.1935 +      case ScalarTypeDescr::UnsizedArray:
  1.1936 +        if (JSID_IS_ATOM(id, cx->names().length)) {
  1.1937 +            JS_ReportErrorNumber(cx, js_GetErrorMessage,
  1.1938 +                                 nullptr, JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
  1.1939 +            return false;
  1.1940 +        }
  1.1941 +        break;
  1.1942 +
  1.1943 +      case ScalarTypeDescr::Struct: {
  1.1944 +        Rooted<StructTypeDescr*> descr(cx, &typedObj->typeDescr().as<StructTypeDescr>());
  1.1945 +
  1.1946 +        size_t fieldIndex;
  1.1947 +        if (!descr->fieldIndex(id, &fieldIndex))
  1.1948 +            break;
  1.1949 +
  1.1950 +        size_t offset = descr->fieldOffset(fieldIndex);
  1.1951 +        Rooted<SizedTypeDescr*> fieldType(cx, &descr->fieldDescr(fieldIndex));
  1.1952 +        return ConvertAndCopyTo(cx, fieldType, typedObj, offset, vp);
  1.1953 +      }
  1.1954 +    }
  1.1955 +
  1.1956 +    return ReportTypedObjTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, typedObj);
  1.1957 +}
  1.1958 +
  1.1959 +bool
  1.1960 +TypedObject::obj_setProperty(JSContext *cx, HandleObject obj,
  1.1961 +                             HandlePropertyName name, MutableHandleValue vp,
  1.1962 +                             bool strict)
  1.1963 +{
  1.1964 +    RootedId id(cx, NameToId(name));
  1.1965 +    return obj_setGeneric(cx, obj, id, vp, strict);
  1.1966 +}
  1.1967 +
  1.1968 +bool
  1.1969 +TypedObject::obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
  1.1970 +                           MutableHandleValue vp, bool strict)
  1.1971 +{
  1.1972 +    JS_ASSERT(obj->is<TypedObject>());
  1.1973 +    Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
  1.1974 +    Rooted<TypeDescr *> descr(cx, &typedObj->typeDescr());
  1.1975 +
  1.1976 +    switch (descr->kind()) {
  1.1977 +      case TypeDescr::Scalar:
  1.1978 +      case TypeDescr::Reference:
  1.1979 +      case TypeDescr::X4:
  1.1980 +      case TypeDescr::Struct:
  1.1981 +        break;
  1.1982 +
  1.1983 +      case TypeDescr::SizedArray:
  1.1984 +        return obj_setArrayElement<SizedArrayTypeDescr>(cx, typedObj, descr, index, vp);
  1.1985 +
  1.1986 +      case TypeDescr::UnsizedArray:
  1.1987 +        return obj_setArrayElement<UnsizedArrayTypeDescr>(cx, typedObj, descr, index, vp);
  1.1988 +    }
  1.1989 +
  1.1990 +    return ReportTypedObjTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, typedObj);
  1.1991 +}
  1.1992 +
  1.1993 +template<class T>
  1.1994 +/*static*/ bool
  1.1995 +TypedObject::obj_setArrayElement(JSContext *cx,
  1.1996 +                                Handle<TypedObject*> typedObj,
  1.1997 +                                Handle<TypeDescr*> descr,
  1.1998 +                                uint32_t index,
  1.1999 +                                MutableHandleValue vp)
  1.2000 +{
  1.2001 +    JS_ASSERT(descr->is<T>());
  1.2002 +
  1.2003 +    if (index >= (size_t) typedObj->length()) {
  1.2004 +        JS_ReportErrorNumber(cx, js_GetErrorMessage,
  1.2005 +                             nullptr, JSMSG_TYPEDOBJECT_BINARYARRAY_BAD_INDEX);
  1.2006 +        return false;
  1.2007 +    }
  1.2008 +
  1.2009 +    Rooted<SizedTypeDescr*> elementType(cx);
  1.2010 +    elementType = &descr->as<T>().elementType();
  1.2011 +    size_t offset = elementType->size() * index;
  1.2012 +    return ConvertAndCopyTo(cx, elementType, typedObj, offset, vp);
  1.2013 +}
  1.2014 +
  1.2015 +bool
  1.2016 +TypedObject::obj_getGenericAttributes(JSContext *cx, HandleObject obj,
  1.2017 +                                     HandleId id, unsigned *attrsp)
  1.2018 +{
  1.2019 +    uint32_t index;
  1.2020 +    Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
  1.2021 +    switch (typedObj->typeDescr().kind()) {
  1.2022 +      case TypeDescr::Scalar:
  1.2023 +      case TypeDescr::Reference:
  1.2024 +        break;
  1.2025 +
  1.2026 +      case TypeDescr::X4:
  1.2027 +        break;
  1.2028 +
  1.2029 +      case TypeDescr::SizedArray:
  1.2030 +      case TypeDescr::UnsizedArray:
  1.2031 +        if (js_IdIsIndex(id, &index)) {
  1.2032 +            *attrsp = JSPROP_ENUMERATE | JSPROP_PERMANENT;
  1.2033 +            return true;
  1.2034 +        }
  1.2035 +        if (JSID_IS_ATOM(id, cx->names().length)) {
  1.2036 +            *attrsp = JSPROP_READONLY | JSPROP_PERMANENT;
  1.2037 +            return true;
  1.2038 +        }
  1.2039 +        break;
  1.2040 +
  1.2041 +      case TypeDescr::Struct:
  1.2042 +        size_t index;
  1.2043 +        if (typedObj->typeDescr().as<StructTypeDescr>().fieldIndex(id, &index)) {
  1.2044 +            *attrsp = JSPROP_ENUMERATE | JSPROP_PERMANENT;
  1.2045 +            return true;
  1.2046 +        }
  1.2047 +        break;
  1.2048 +    }
  1.2049 +
  1.2050 +    RootedObject proto(cx, obj->getProto());
  1.2051 +    if (!proto) {
  1.2052 +        *attrsp = 0;
  1.2053 +        return true;
  1.2054 +    }
  1.2055 +
  1.2056 +    return JSObject::getGenericAttributes(cx, proto, id, attrsp);
  1.2057 +}
  1.2058 +
  1.2059 +static bool
  1.2060 +IsOwnId(JSContext *cx, HandleObject obj, HandleId id)
  1.2061 +{
  1.2062 +    uint32_t index;
  1.2063 +    Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
  1.2064 +    switch (typedObj->typeDescr().kind()) {
  1.2065 +      case TypeDescr::Scalar:
  1.2066 +      case TypeDescr::Reference:
  1.2067 +      case TypeDescr::X4:
  1.2068 +        return false;
  1.2069 +
  1.2070 +      case TypeDescr::SizedArray:
  1.2071 +      case TypeDescr::UnsizedArray:
  1.2072 +        return js_IdIsIndex(id, &index) || JSID_IS_ATOM(id, cx->names().length);
  1.2073 +
  1.2074 +      case TypeDescr::Struct:
  1.2075 +        size_t index;
  1.2076 +        if (typedObj->typeDescr().as<StructTypeDescr>().fieldIndex(id, &index))
  1.2077 +            return true;
  1.2078 +    }
  1.2079 +
  1.2080 +    return false;
  1.2081 +}
  1.2082 +
  1.2083 +bool
  1.2084 +TypedObject::obj_setGenericAttributes(JSContext *cx, HandleObject obj,
  1.2085 +                                       HandleId id, unsigned *attrsp)
  1.2086 +{
  1.2087 +    if (IsOwnId(cx, obj, id))
  1.2088 +        return ReportPropertyError(cx, JSMSG_CANT_REDEFINE_PROP, id);
  1.2089 +
  1.2090 +    RootedObject proto(cx, obj->getProto());
  1.2091 +    if (!proto) {
  1.2092 +        *attrsp = 0;
  1.2093 +        return true;
  1.2094 +    }
  1.2095 +
  1.2096 +    return JSObject::setGenericAttributes(cx, proto, id, attrsp);
  1.2097 +}
  1.2098 +
  1.2099 +bool
  1.2100 +TypedObject::obj_deleteProperty(JSContext *cx, HandleObject obj,
  1.2101 +                                HandlePropertyName name, bool *succeeded)
  1.2102 +{
  1.2103 +    Rooted<jsid> id(cx, NameToId(name));
  1.2104 +    if (IsOwnId(cx, obj, id))
  1.2105 +        return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
  1.2106 +
  1.2107 +    RootedObject proto(cx, obj->getProto());
  1.2108 +    if (!proto) {
  1.2109 +        *succeeded = false;
  1.2110 +        return true;
  1.2111 +    }
  1.2112 +
  1.2113 +    return JSObject::deleteProperty(cx, proto, name, succeeded);
  1.2114 +}
  1.2115 +
  1.2116 +bool
  1.2117 +TypedObject::obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
  1.2118 +                               bool *succeeded)
  1.2119 +{
  1.2120 +    RootedId id(cx);
  1.2121 +    if (!IndexToId(cx, index, &id))
  1.2122 +        return false;
  1.2123 +
  1.2124 +    if (IsOwnId(cx, obj, id))
  1.2125 +        return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
  1.2126 +
  1.2127 +    RootedObject proto(cx, obj->getProto());
  1.2128 +    if (!proto) {
  1.2129 +        *succeeded = false;
  1.2130 +        return true;
  1.2131 +    }
  1.2132 +
  1.2133 +    return JSObject::deleteElement(cx, proto, index, succeeded);
  1.2134 +}
  1.2135 +
  1.2136 +bool
  1.2137 +TypedObject::obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
  1.2138 +                           MutableHandleValue statep, MutableHandleId idp)
  1.2139 +{
  1.2140 +    int32_t index;
  1.2141 +
  1.2142 +    JS_ASSERT(obj->is<TypedObject>());
  1.2143 +    Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
  1.2144 +    Rooted<TypeDescr *> descr(cx, &typedObj->typeDescr());
  1.2145 +    switch (descr->kind()) {
  1.2146 +      case TypeDescr::Scalar:
  1.2147 +      case TypeDescr::Reference:
  1.2148 +      case TypeDescr::X4:
  1.2149 +        switch (enum_op) {
  1.2150 +          case JSENUMERATE_INIT_ALL:
  1.2151 +          case JSENUMERATE_INIT:
  1.2152 +            statep.setInt32(0);
  1.2153 +            idp.set(INT_TO_JSID(0));
  1.2154 +
  1.2155 +          case JSENUMERATE_NEXT:
  1.2156 +          case JSENUMERATE_DESTROY:
  1.2157 +            statep.setNull();
  1.2158 +            break;
  1.2159 +        }
  1.2160 +        break;
  1.2161 +
  1.2162 +      case TypeDescr::SizedArray:
  1.2163 +      case TypeDescr::UnsizedArray:
  1.2164 +        switch (enum_op) {
  1.2165 +          case JSENUMERATE_INIT_ALL:
  1.2166 +          case JSENUMERATE_INIT:
  1.2167 +            statep.setInt32(0);
  1.2168 +            idp.set(INT_TO_JSID(typedObj->length()));
  1.2169 +            break;
  1.2170 +
  1.2171 +          case JSENUMERATE_NEXT:
  1.2172 +            index = static_cast<int32_t>(statep.toInt32());
  1.2173 +
  1.2174 +            if (index < typedObj->length()) {
  1.2175 +                idp.set(INT_TO_JSID(index));
  1.2176 +                statep.setInt32(index + 1);
  1.2177 +            } else {
  1.2178 +                JS_ASSERT(index == typedObj->length());
  1.2179 +                statep.setNull();
  1.2180 +            }
  1.2181 +
  1.2182 +            break;
  1.2183 +
  1.2184 +          case JSENUMERATE_DESTROY:
  1.2185 +            statep.setNull();
  1.2186 +            break;
  1.2187 +        }
  1.2188 +        break;
  1.2189 +
  1.2190 +      case TypeDescr::Struct:
  1.2191 +        switch (enum_op) {
  1.2192 +          case JSENUMERATE_INIT_ALL:
  1.2193 +          case JSENUMERATE_INIT:
  1.2194 +            statep.setInt32(0);
  1.2195 +            idp.set(INT_TO_JSID(descr->as<StructTypeDescr>().fieldCount()));
  1.2196 +            break;
  1.2197 +
  1.2198 +          case JSENUMERATE_NEXT:
  1.2199 +            index = static_cast<uint32_t>(statep.toInt32());
  1.2200 +
  1.2201 +            if ((size_t) index < descr->as<StructTypeDescr>().fieldCount()) {
  1.2202 +                idp.set(AtomToId(&descr->as<StructTypeDescr>().fieldName(index)));
  1.2203 +                statep.setInt32(index + 1);
  1.2204 +            } else {
  1.2205 +                statep.setNull();
  1.2206 +            }
  1.2207 +
  1.2208 +            break;
  1.2209 +
  1.2210 +          case JSENUMERATE_DESTROY:
  1.2211 +            statep.setNull();
  1.2212 +            break;
  1.2213 +        }
  1.2214 +        break;
  1.2215 +    }
  1.2216 +
  1.2217 +    return true;
  1.2218 +}
  1.2219 +
  1.2220 +/* static */ size_t
  1.2221 +TypedObject::offsetOfOwnerSlot()
  1.2222 +{
  1.2223 +    return JSObject::getFixedSlotOffset(JS_TYPEDOBJ_SLOT_OWNER);
  1.2224 +}
  1.2225 +
  1.2226 +/* static */ size_t
  1.2227 +TypedObject::offsetOfDataSlot()
  1.2228 +{
  1.2229 +    // the offset of 7 is based on the alloc kind
  1.2230 +    return JSObject::getPrivateDataOffset(JS_TYPEDOBJ_SLOT_DATA);
  1.2231 +}
  1.2232 +
  1.2233 +/* static */ size_t
  1.2234 +TypedObject::offsetOfByteOffsetSlot()
  1.2235 +{
  1.2236 +    return JSObject::getFixedSlotOffset(JS_TYPEDOBJ_SLOT_BYTEOFFSET);
  1.2237 +}
  1.2238 +
  1.2239 +void
  1.2240 +TypedObject::neuter(void *newData)
  1.2241 +{
  1.2242 +    setSlot(JS_TYPEDOBJ_SLOT_LENGTH, Int32Value(0));
  1.2243 +    setSlot(JS_TYPEDOBJ_SLOT_BYTELENGTH, Int32Value(0));
  1.2244 +    setSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(0));
  1.2245 +    setPrivate(newData);
  1.2246 +}
  1.2247 +
  1.2248 +/******************************************************************************
  1.2249 + * Typed Objects
  1.2250 + */
  1.2251 +
  1.2252 +const Class TransparentTypedObject::class_ = {
  1.2253 +    "TypedObject",
  1.2254 +    Class::NON_NATIVE |
  1.2255 +    JSCLASS_HAS_RESERVED_SLOTS(JS_TYPEDOBJ_SLOTS) |
  1.2256 +    JSCLASS_HAS_PRIVATE |
  1.2257 +    JSCLASS_IMPLEMENTS_BARRIERS,
  1.2258 +    JS_PropertyStub,
  1.2259 +    JS_DeletePropertyStub,
  1.2260 +    JS_PropertyStub,
  1.2261 +    JS_StrictPropertyStub,
  1.2262 +    JS_EnumerateStub,
  1.2263 +    JS_ResolveStub,
  1.2264 +    JS_ConvertStub,
  1.2265 +    nullptr,        /* finalize    */
  1.2266 +    nullptr,        /* call        */
  1.2267 +    nullptr,        /* hasInstance */
  1.2268 +    nullptr,        /* construct   */
  1.2269 +    TypedObject::obj_trace,
  1.2270 +    JS_NULL_CLASS_SPEC,
  1.2271 +    JS_NULL_CLASS_EXT,
  1.2272 +    {
  1.2273 +        TypedObject::obj_lookupGeneric,
  1.2274 +        TypedObject::obj_lookupProperty,
  1.2275 +        TypedObject::obj_lookupElement,
  1.2276 +        TypedObject::obj_defineGeneric,
  1.2277 +        TypedObject::obj_defineProperty,
  1.2278 +        TypedObject::obj_defineElement,
  1.2279 +        TypedObject::obj_getGeneric,
  1.2280 +        TypedObject::obj_getProperty,
  1.2281 +        TypedObject::obj_getElement,
  1.2282 +        TypedObject::obj_setGeneric,
  1.2283 +        TypedObject::obj_setProperty,
  1.2284 +        TypedObject::obj_setElement,
  1.2285 +        TypedObject::obj_getGenericAttributes,
  1.2286 +        TypedObject::obj_setGenericAttributes,
  1.2287 +        TypedObject::obj_deleteProperty,
  1.2288 +        TypedObject::obj_deleteElement,
  1.2289 +        nullptr, nullptr, // watch/unwatch
  1.2290 +        nullptr,   /* slice */
  1.2291 +        TypedObject::obj_enumerate,
  1.2292 +        nullptr, /* thisObject */
  1.2293 +    }
  1.2294 +};
  1.2295 +
  1.2296 +static int32_t
  1.2297 +LengthForType(TypeDescr &descr)
  1.2298 +{
  1.2299 +    switch (descr.kind()) {
  1.2300 +      case TypeDescr::Scalar:
  1.2301 +      case TypeDescr::Reference:
  1.2302 +      case TypeDescr::Struct:
  1.2303 +      case TypeDescr::X4:
  1.2304 +        return 0;
  1.2305 +
  1.2306 +      case TypeDescr::SizedArray:
  1.2307 +        return descr.as<SizedArrayTypeDescr>().length();
  1.2308 +
  1.2309 +      case TypeDescr::UnsizedArray:
  1.2310 +        return 0;
  1.2311 +    }
  1.2312 +
  1.2313 +    MOZ_ASSUME_UNREACHABLE("Invalid kind");
  1.2314 +}
  1.2315 +
  1.2316 +static bool
  1.2317 +CheckOffset(int32_t offset,
  1.2318 +            int32_t size,
  1.2319 +            int32_t alignment,
  1.2320 +            int32_t bufferLength)
  1.2321 +{
  1.2322 +    JS_ASSERT(size >= 0);
  1.2323 +    JS_ASSERT(alignment >= 0);
  1.2324 +
  1.2325 +    // No negative offsets.
  1.2326 +    if (offset < 0)
  1.2327 +        return false;
  1.2328 +
  1.2329 +    // Offset (plus size) must be fully contained within the buffer.
  1.2330 +    if (offset > bufferLength)
  1.2331 +        return false;
  1.2332 +    if (offset + size < offset)
  1.2333 +        return false;
  1.2334 +    if (offset + size > bufferLength)
  1.2335 +        return false;
  1.2336 +
  1.2337 +    // Offset must be aligned.
  1.2338 +    if ((offset % alignment) != 0)
  1.2339 +        return false;
  1.2340 +
  1.2341 +    return true;
  1.2342 +}
  1.2343 +
  1.2344 +/*static*/ bool
  1.2345 +TypedObject::constructSized(JSContext *cx, unsigned int argc, Value *vp)
  1.2346 +{
  1.2347 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.2348 +
  1.2349 +    JS_ASSERT(args.callee().is<SizedTypeDescr>());
  1.2350 +    Rooted<SizedTypeDescr*> callee(cx, &args.callee().as<SizedTypeDescr>());
  1.2351 +
  1.2352 +    // Typed object constructors for sized types are overloaded in
  1.2353 +    // three ways, in order of precedence:
  1.2354 +    //
  1.2355 +    //   new TypeObj()
  1.2356 +    //   new TypeObj(buffer, [offset])
  1.2357 +    //   new TypeObj(data)
  1.2358 +
  1.2359 +    // Zero argument constructor:
  1.2360 +    if (args.length() == 0) {
  1.2361 +        int32_t length = LengthForType(*callee);
  1.2362 +        Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length));
  1.2363 +        if (!obj)
  1.2364 +            return false;
  1.2365 +        args.rval().setObject(*obj);
  1.2366 +        return true;
  1.2367 +    }
  1.2368 +
  1.2369 +    // Buffer constructor.
  1.2370 +    if (args[0].isObject() && args[0].toObject().is<ArrayBufferObject>()) {
  1.2371 +        Rooted<ArrayBufferObject*> buffer(cx);
  1.2372 +        buffer = &args[0].toObject().as<ArrayBufferObject>();
  1.2373 +
  1.2374 +        if (callee->opaque() || buffer->isNeutered()) {
  1.2375 +            JS_ReportErrorNumber(cx, js_GetErrorMessage,
  1.2376 +                                 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  1.2377 +            return false;
  1.2378 +        }
  1.2379 +
  1.2380 +        int32_t offset;
  1.2381 +        if (args.length() >= 2 && !args[1].isUndefined()) {
  1.2382 +            if (!args[1].isInt32()) {
  1.2383 +                JS_ReportErrorNumber(cx, js_GetErrorMessage,
  1.2384 +                                     nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  1.2385 +                return false;
  1.2386 +            }
  1.2387 +
  1.2388 +            offset = args[1].toInt32();
  1.2389 +        } else {
  1.2390 +            offset = 0;
  1.2391 +        }
  1.2392 +
  1.2393 +        if (args.length() >= 3 && !args[2].isUndefined()) {
  1.2394 +            JS_ReportErrorNumber(cx, js_GetErrorMessage,
  1.2395 +                                 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  1.2396 +            return false;
  1.2397 +        }
  1.2398 +
  1.2399 +        if (!CheckOffset(offset, callee->size(), callee->alignment(),
  1.2400 +                         buffer->byteLength()))
  1.2401 +        {
  1.2402 +            JS_ReportErrorNumber(cx, js_GetErrorMessage,
  1.2403 +                                 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  1.2404 +            return false;
  1.2405 +        }
  1.2406 +
  1.2407 +        Rooted<TypedObject*> obj(cx);
  1.2408 +        obj = TypedObject::createUnattached(cx, callee, LengthForType(*callee));
  1.2409 +        if (!obj)
  1.2410 +            return false;
  1.2411 +
  1.2412 +        obj->attach(*buffer, offset);
  1.2413 +        args.rval().setObject(*obj);
  1.2414 +        return true;
  1.2415 +    }
  1.2416 +
  1.2417 +    // Data constructor.
  1.2418 +    if (args[0].isObject()) {
  1.2419 +        // Create the typed object.
  1.2420 +        int32_t length = LengthForType(*callee);
  1.2421 +        Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length));
  1.2422 +        if (!obj)
  1.2423 +            return false;
  1.2424 +
  1.2425 +        // Initialize from `arg`.
  1.2426 +        if (!ConvertAndCopyTo(cx, obj, args[0]))
  1.2427 +            return false;
  1.2428 +        args.rval().setObject(*obj);
  1.2429 +        return true;
  1.2430 +    }
  1.2431 +
  1.2432 +    // Something bogus.
  1.2433 +    JS_ReportErrorNumber(cx, js_GetErrorMessage,
  1.2434 +                         nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  1.2435 +    return false;
  1.2436 +}
  1.2437 +
  1.2438 +/*static*/ bool
  1.2439 +TypedObject::constructUnsized(JSContext *cx, unsigned int argc, Value *vp)
  1.2440 +{
  1.2441 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.2442 +
  1.2443 +    JS_ASSERT(args.callee().is<TypeDescr>());
  1.2444 +    Rooted<UnsizedArrayTypeDescr*> callee(cx);
  1.2445 +    callee = &args.callee().as<UnsizedArrayTypeDescr>();
  1.2446 +
  1.2447 +    // Typed object constructors for unsized arrays are overloaded in
  1.2448 +    // four ways, in order of precedence:
  1.2449 +    //
  1.2450 +    //   new TypeObj(buffer, [offset, [length]]) // [1]
  1.2451 +    //   new TypeObj(length)                     // [1]
  1.2452 +    //   new TypeObj(data)
  1.2453 +    //   new TypeObj()
  1.2454 +    //
  1.2455 +    // [1] Explicit lengths only available for unsized arrays.
  1.2456 +
  1.2457 +    // Zero argument constructor:
  1.2458 +    if (args.length() == 0) {
  1.2459 +        Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, 0));
  1.2460 +        if (!obj)
  1.2461 +            return false;
  1.2462 +        args.rval().setObject(*obj);
  1.2463 +        return true;
  1.2464 +    }
  1.2465 +
  1.2466 +    // Length constructor.
  1.2467 +    if (args[0].isInt32()) {
  1.2468 +        int32_t length = args[0].toInt32();
  1.2469 +        if (length < 0) {
  1.2470 +            JS_ReportErrorNumber(cx, js_GetErrorMessage,
  1.2471 +                                 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  1.2472 +            return nullptr;
  1.2473 +        }
  1.2474 +        Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length));
  1.2475 +        if (!obj)
  1.2476 +            return false;
  1.2477 +        args.rval().setObject(*obj);
  1.2478 +        return true;
  1.2479 +    }
  1.2480 +
  1.2481 +    // Buffer constructor.
  1.2482 +    if (args[0].isObject() && args[0].toObject().is<ArrayBufferObject>()) {
  1.2483 +        Rooted<ArrayBufferObject*> buffer(cx);
  1.2484 +        buffer = &args[0].toObject().as<ArrayBufferObject>();
  1.2485 +
  1.2486 +        if (callee->opaque()) {
  1.2487 +            JS_ReportErrorNumber(cx, js_GetErrorMessage,
  1.2488 +                                 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  1.2489 +            return false;
  1.2490 +        }
  1.2491 +
  1.2492 +        int32_t offset;
  1.2493 +        if (args.length() >= 2 && !args[1].isUndefined()) {
  1.2494 +            if (!args[1].isInt32()) {
  1.2495 +                JS_ReportErrorNumber(cx, js_GetErrorMessage,
  1.2496 +                                     nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  1.2497 +                return false;
  1.2498 +            }
  1.2499 +
  1.2500 +            offset = args[1].toInt32();
  1.2501 +        } else {
  1.2502 +            offset = 0;
  1.2503 +        }
  1.2504 +
  1.2505 +        if (!CheckOffset(offset, 0, callee->alignment(), buffer->byteLength())) {
  1.2506 +            JS_ReportErrorNumber(cx, js_GetErrorMessage,
  1.2507 +                                 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  1.2508 +            return false;
  1.2509 +        }
  1.2510 +
  1.2511 +        int32_t elemSize = callee->elementType().size();
  1.2512 +        int32_t bytesRemaining = buffer->byteLength() - offset;
  1.2513 +        int32_t maximumLength = bytesRemaining / elemSize;
  1.2514 +
  1.2515 +        int32_t length;
  1.2516 +        if (args.length() >= 3 && !args[2].isUndefined()) {
  1.2517 +            if (!args[2].isInt32()) {
  1.2518 +                JS_ReportErrorNumber(cx, js_GetErrorMessage,
  1.2519 +                                     nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  1.2520 +                return false;
  1.2521 +            }
  1.2522 +            length = args[2].toInt32();
  1.2523 +
  1.2524 +            if (length < 0 || length > maximumLength) {
  1.2525 +                JS_ReportErrorNumber(cx, js_GetErrorMessage,
  1.2526 +                                     nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  1.2527 +                return false;
  1.2528 +            }
  1.2529 +        } else {
  1.2530 +            if ((bytesRemaining % elemSize) != 0) {
  1.2531 +                JS_ReportErrorNumber(cx, js_GetErrorMessage,
  1.2532 +                                     nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  1.2533 +                return false;
  1.2534 +            }
  1.2535 +
  1.2536 +            length = maximumLength;
  1.2537 +        }
  1.2538 +
  1.2539 +        if (buffer->isNeutered()) {
  1.2540 +            JS_ReportErrorNumber(cx, js_GetErrorMessage,
  1.2541 +                                 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  1.2542 +            return false;
  1.2543 +        }
  1.2544 +
  1.2545 +        Rooted<TypedObject*> obj(cx);
  1.2546 +        obj = TypedObject::createUnattached(cx, callee, length);
  1.2547 +        if (!obj)
  1.2548 +            return false;
  1.2549 +
  1.2550 +        obj->attach(args[0].toObject().as<ArrayBufferObject>(), offset);
  1.2551 +    }
  1.2552 +
  1.2553 +    // Data constructor for unsized values
  1.2554 +    if (args[0].isObject()) {
  1.2555 +        // Read length out of the object.
  1.2556 +        RootedObject arg(cx, &args[0].toObject());
  1.2557 +        RootedValue lengthVal(cx);
  1.2558 +        if (!JSObject::getProperty(cx, arg, arg, cx->names().length, &lengthVal))
  1.2559 +            return false;
  1.2560 +        if (!lengthVal.isInt32()) {
  1.2561 +            JS_ReportErrorNumber(cx, js_GetErrorMessage,
  1.2562 +                                 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  1.2563 +            return false;
  1.2564 +        }
  1.2565 +        int32_t length = lengthVal.toInt32();
  1.2566 +
  1.2567 +        // Check that length * elementSize does not overflow.
  1.2568 +        int32_t elementSize = callee->elementType().size();
  1.2569 +        int32_t byteLength;
  1.2570 +        if (!SafeMul(elementSize, length, &byteLength)) {
  1.2571 +            JS_ReportErrorNumber(cx, js_GetErrorMessage,
  1.2572 +                                 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  1.2573 +            return false;
  1.2574 +        }
  1.2575 +
  1.2576 +        // Create the unsized array.
  1.2577 +        Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length));
  1.2578 +        if (!obj)
  1.2579 +            return false;
  1.2580 +
  1.2581 +        // Initialize from `arg`
  1.2582 +        if (!ConvertAndCopyTo(cx, obj, args[0]))
  1.2583 +            return false;
  1.2584 +        args.rval().setObject(*obj);
  1.2585 +        return true;
  1.2586 +    }
  1.2587 +
  1.2588 +    // Something bogus.
  1.2589 +    JS_ReportErrorNumber(cx, js_GetErrorMessage,
  1.2590 +                         nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  1.2591 +    return false;
  1.2592 +}
  1.2593 +
  1.2594 +/******************************************************************************
  1.2595 + * Handles
  1.2596 + */
  1.2597 +
  1.2598 +const Class OpaqueTypedObject::class_ = {
  1.2599 +    "Handle",
  1.2600 +    Class::NON_NATIVE |
  1.2601 +    JSCLASS_HAS_RESERVED_SLOTS(JS_TYPEDOBJ_SLOTS) |
  1.2602 +    JSCLASS_HAS_PRIVATE |
  1.2603 +    JSCLASS_IMPLEMENTS_BARRIERS,
  1.2604 +    JS_PropertyStub,
  1.2605 +    JS_DeletePropertyStub,
  1.2606 +    JS_PropertyStub,
  1.2607 +    JS_StrictPropertyStub,
  1.2608 +    JS_EnumerateStub,
  1.2609 +    JS_ResolveStub,
  1.2610 +    JS_ConvertStub,
  1.2611 +    nullptr,        /* finalize    */
  1.2612 +    nullptr,        /* call        */
  1.2613 +    nullptr,        /* construct   */
  1.2614 +    nullptr,        /* hasInstance */
  1.2615 +    TypedObject::obj_trace,
  1.2616 +    JS_NULL_CLASS_SPEC,
  1.2617 +    JS_NULL_CLASS_EXT,
  1.2618 +    {
  1.2619 +        TypedObject::obj_lookupGeneric,
  1.2620 +        TypedObject::obj_lookupProperty,
  1.2621 +        TypedObject::obj_lookupElement,
  1.2622 +        TypedObject::obj_defineGeneric,
  1.2623 +        TypedObject::obj_defineProperty,
  1.2624 +        TypedObject::obj_defineElement,
  1.2625 +        TypedObject::obj_getGeneric,
  1.2626 +        TypedObject::obj_getProperty,
  1.2627 +        TypedObject::obj_getElement,
  1.2628 +        TypedObject::obj_setGeneric,
  1.2629 +        TypedObject::obj_setProperty,
  1.2630 +        TypedObject::obj_setElement,
  1.2631 +        TypedObject::obj_getGenericAttributes,
  1.2632 +        TypedObject::obj_setGenericAttributes,
  1.2633 +        TypedObject::obj_deleteProperty,
  1.2634 +        TypedObject::obj_deleteElement,
  1.2635 +        nullptr, nullptr, // watch/unwatch
  1.2636 +        nullptr, // slice
  1.2637 +        TypedObject::obj_enumerate,
  1.2638 +        nullptr, /* thisObject */
  1.2639 +    }
  1.2640 +};
  1.2641 +
  1.2642 +const JSFunctionSpec OpaqueTypedObject::handleStaticMethods[] = {
  1.2643 +    {"move", {nullptr, nullptr}, 3, 0, "HandleMove"},
  1.2644 +    {"get", {nullptr, nullptr}, 1, 0, "HandleGet"},
  1.2645 +    {"set", {nullptr, nullptr}, 2, 0, "HandleSet"},
  1.2646 +    {"isHandle", {nullptr, nullptr}, 1, 0, "HandleTest"},
  1.2647 +    JS_FS_END
  1.2648 +};
  1.2649 +
  1.2650 +/******************************************************************************
  1.2651 + * Intrinsics
  1.2652 + */
  1.2653 +
  1.2654 +bool
  1.2655 +js::NewOpaqueTypedObject(JSContext *cx, unsigned argc, Value *vp)
  1.2656 +{
  1.2657 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.2658 +    JS_ASSERT(args.length() == 1);
  1.2659 +    JS_ASSERT(args[0].isObject() && args[0].toObject().is<SizedTypeDescr>());
  1.2660 +
  1.2661 +    Rooted<SizedTypeDescr*> descr(cx, &args[0].toObject().as<SizedTypeDescr>());
  1.2662 +    int32_t length = TypedObjLengthFromType(*descr);
  1.2663 +    Rooted<TypedObject*> obj(cx);
  1.2664 +    obj = TypedObject::createUnattachedWithClass(cx, &OpaqueTypedObject::class_, descr, length);
  1.2665 +    if (!obj)
  1.2666 +        return false;
  1.2667 +    args.rval().setObject(*obj);
  1.2668 +    return true;
  1.2669 +}
  1.2670 +
  1.2671 +bool
  1.2672 +js::NewDerivedTypedObject(JSContext *cx, unsigned argc, Value *vp)
  1.2673 +{
  1.2674 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.2675 +    JS_ASSERT(args.length() == 3);
  1.2676 +    JS_ASSERT(args[0].isObject() && args[0].toObject().is<SizedTypeDescr>());
  1.2677 +    JS_ASSERT(args[1].isObject() && args[1].toObject().is<TypedObject>());
  1.2678 +    JS_ASSERT(args[2].isInt32());
  1.2679 +
  1.2680 +    Rooted<SizedTypeDescr*> descr(cx, &args[0].toObject().as<SizedTypeDescr>());
  1.2681 +    Rooted<TypedObject*> typedObj(cx, &args[1].toObject().as<TypedObject>());
  1.2682 +    int32_t offset = args[2].toInt32();
  1.2683 +
  1.2684 +    Rooted<TypedObject*> obj(cx);
  1.2685 +    obj = TypedObject::createDerived(cx, descr, typedObj, offset);
  1.2686 +    if (!obj)
  1.2687 +        return false;
  1.2688 +
  1.2689 +    args.rval().setObject(*obj);
  1.2690 +    return true;
  1.2691 +}
  1.2692 +
  1.2693 +bool
  1.2694 +js::AttachTypedObject(ThreadSafeContext *, unsigned argc, Value *vp)
  1.2695 +{
  1.2696 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.2697 +    JS_ASSERT(args.length() == 3);
  1.2698 +    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
  1.2699 +    JS_ASSERT(args[1].isObject() && args[1].toObject().is<TypedObject>());
  1.2700 +    JS_ASSERT(args[2].isInt32());
  1.2701 +
  1.2702 +    TypedObject &handle = args[0].toObject().as<TypedObject>();
  1.2703 +    TypedObject &target = args[1].toObject().as<TypedObject>();
  1.2704 +    JS_ASSERT(handle.typedMem() == nullptr); // must not be attached already
  1.2705 +    size_t offset = args[2].toInt32();
  1.2706 +    handle.attach(target, offset);
  1.2707 +    return true;
  1.2708 +}
  1.2709 +
  1.2710 +JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::AttachTypedObjectJitInfo,
  1.2711 +                                      AttachTypedObjectJitInfo,
  1.2712 +                                      js::AttachTypedObject);
  1.2713 +
  1.2714 +bool
  1.2715 +js::SetTypedObjectOffset(ThreadSafeContext *, unsigned argc, Value *vp)
  1.2716 +{
  1.2717 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.2718 +    JS_ASSERT(args.length() == 2);
  1.2719 +    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
  1.2720 +    JS_ASSERT(args[1].isInt32());
  1.2721 +
  1.2722 +    TypedObject &typedObj = args[0].toObject().as<TypedObject>();
  1.2723 +    int32_t offset = args[1].toInt32();
  1.2724 +
  1.2725 +    JS_ASSERT(!typedObj.owner().isNeutered());
  1.2726 +    JS_ASSERT(typedObj.typedMem() != nullptr); // must be attached already
  1.2727 +
  1.2728 +    typedObj.setPrivate(typedObj.owner().dataPointer() + offset);
  1.2729 +    typedObj.setReservedSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(offset));
  1.2730 +    args.rval().setUndefined();
  1.2731 +    return true;
  1.2732 +}
  1.2733 +
  1.2734 +bool
  1.2735 +js::intrinsic_SetTypedObjectOffset(JSContext *cx, unsigned argc, Value *vp)
  1.2736 +{
  1.2737 +    // Do not use JSNativeThreadSafeWrapper<> so that ion can reference
  1.2738 +    // this function more easily when inlining.
  1.2739 +    return SetTypedObjectOffset(cx, argc, vp);
  1.2740 +}
  1.2741 +
  1.2742 +JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::intrinsic_SetTypedObjectOffsetJitInfo,
  1.2743 +                                      SetTypedObjectJitInfo,
  1.2744 +                                      SetTypedObjectOffset);
  1.2745 +
  1.2746 +bool
  1.2747 +js::ObjectIsTypeDescr(ThreadSafeContext *, unsigned argc, Value *vp)
  1.2748 +{
  1.2749 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.2750 +    JS_ASSERT(args.length() == 1);
  1.2751 +    JS_ASSERT(args[0].isObject());
  1.2752 +    args.rval().setBoolean(args[0].toObject().is<TypeDescr>());
  1.2753 +    return true;
  1.2754 +}
  1.2755 +
  1.2756 +JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ObjectIsTypeDescrJitInfo, ObjectIsTypeDescrJitInfo,
  1.2757 +                                      js::ObjectIsTypeDescr);
  1.2758 +
  1.2759 +bool
  1.2760 +js::ObjectIsTypedObject(ThreadSafeContext *, unsigned argc, Value *vp)
  1.2761 +{
  1.2762 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.2763 +    JS_ASSERT(args.length() == 1);
  1.2764 +    JS_ASSERT(args[0].isObject());
  1.2765 +    args.rval().setBoolean(args[0].toObject().is<TypedObject>());
  1.2766 +    return true;
  1.2767 +}
  1.2768 +
  1.2769 +JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ObjectIsTypedObjectJitInfo,
  1.2770 +                                      ObjectIsTypedObjectJitInfo,
  1.2771 +                                      js::ObjectIsTypedObject);
  1.2772 +
  1.2773 +bool
  1.2774 +js::ObjectIsOpaqueTypedObject(ThreadSafeContext *, unsigned argc, Value *vp)
  1.2775 +{
  1.2776 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.2777 +    JS_ASSERT(args.length() == 1);
  1.2778 +    JS_ASSERT(args[0].isObject());
  1.2779 +    args.rval().setBoolean(args[0].toObject().is<OpaqueTypedObject>());
  1.2780 +    return true;
  1.2781 +}
  1.2782 +
  1.2783 +JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ObjectIsOpaqueTypedObjectJitInfo,
  1.2784 +                                      ObjectIsOpaqueTypedObjectJitInfo,
  1.2785 +                                      js::ObjectIsOpaqueTypedObject);
  1.2786 +
  1.2787 +bool
  1.2788 +js::ObjectIsTransparentTypedObject(ThreadSafeContext *, unsigned argc, Value *vp)
  1.2789 +{
  1.2790 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.2791 +    JS_ASSERT(args.length() == 1);
  1.2792 +    JS_ASSERT(args[0].isObject());
  1.2793 +    args.rval().setBoolean(args[0].toObject().is<TransparentTypedObject>());
  1.2794 +    return true;
  1.2795 +}
  1.2796 +
  1.2797 +JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ObjectIsTransparentTypedObjectJitInfo,
  1.2798 +                                      ObjectIsTransparentTypedObjectJitInfo,
  1.2799 +                                      js::ObjectIsTransparentTypedObject);
  1.2800 +
  1.2801 +bool
  1.2802 +js::TypeDescrIsSimpleType(ThreadSafeContext *, unsigned argc, Value *vp)
  1.2803 +{
  1.2804 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.2805 +    JS_ASSERT(args.length() == 1);
  1.2806 +    JS_ASSERT(args[0].isObject());
  1.2807 +    JS_ASSERT(args[0].toObject().is<js::TypeDescr>());
  1.2808 +    args.rval().setBoolean(args[0].toObject().is<js::SimpleTypeDescr>());
  1.2809 +    return true;
  1.2810 +}
  1.2811 +
  1.2812 +JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::TypeDescrIsSimpleTypeJitInfo,
  1.2813 +                                      TypeDescrIsSimpleTypeJitInfo,
  1.2814 +                                      js::TypeDescrIsSimpleType);
  1.2815 +
  1.2816 +bool
  1.2817 +js::TypeDescrIsArrayType(ThreadSafeContext *, unsigned argc, Value *vp)
  1.2818 +{
  1.2819 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.2820 +    JS_ASSERT(args.length() == 1);
  1.2821 +    JS_ASSERT(args[0].isObject());
  1.2822 +    JS_ASSERT(args[0].toObject().is<js::TypeDescr>());
  1.2823 +    JSObject& obj = args[0].toObject();
  1.2824 +    args.rval().setBoolean(obj.is<js::SizedArrayTypeDescr>() ||
  1.2825 +                           obj.is<js::UnsizedArrayTypeDescr>());
  1.2826 +    return true;
  1.2827 +}
  1.2828 +
  1.2829 +JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::TypeDescrIsArrayTypeJitInfo,
  1.2830 +                                      TypeDescrIsArrayTypeJitInfo,
  1.2831 +                                      js::TypeDescrIsArrayType);
  1.2832 +
  1.2833 +bool
  1.2834 +js::TypeDescrIsSizedArrayType(ThreadSafeContext *, unsigned argc, Value *vp)
  1.2835 +{
  1.2836 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.2837 +    JS_ASSERT(args.length() == 1);
  1.2838 +    JS_ASSERT(args[0].isObject());
  1.2839 +    JS_ASSERT(args[0].toObject().is<js::TypeDescr>());
  1.2840 +    args.rval().setBoolean(args[0].toObject().is<js::SizedArrayTypeDescr>());
  1.2841 +    return true;
  1.2842 +}
  1.2843 +
  1.2844 +JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::TypeDescrIsSizedArrayTypeJitInfo,
  1.2845 +                                      TypeDescrIsSizedArrayTypeJitInfo,
  1.2846 +                                      js::TypeDescrIsSizedArrayType);
  1.2847 +
  1.2848 +bool
  1.2849 +js::TypeDescrIsUnsizedArrayType(ThreadSafeContext *, unsigned argc, Value *vp)
  1.2850 +{
  1.2851 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.2852 +    JS_ASSERT(args.length() == 1);
  1.2853 +    JS_ASSERT(args[0].isObject());
  1.2854 +    JS_ASSERT(args[0].toObject().is<js::TypeDescr>());
  1.2855 +    args.rval().setBoolean(args[0].toObject().is<js::UnsizedArrayTypeDescr>());
  1.2856 +    return true;
  1.2857 +}
  1.2858 +
  1.2859 +JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::TypeDescrIsUnsizedArrayTypeJitInfo,
  1.2860 +                                      TypeDescrIsUnsizedArrayTypeJitInfo,
  1.2861 +                                      js::TypeDescrIsUnsizedArrayType);
  1.2862 +
  1.2863 +bool
  1.2864 +js::TypedObjectIsAttached(ThreadSafeContext *cx, unsigned argc, Value *vp)
  1.2865 +{
  1.2866 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.2867 +    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
  1.2868 +    TypedObject &typedObj = args[0].toObject().as<TypedObject>();
  1.2869 +    args.rval().setBoolean(!typedObj.owner().isNeutered() && typedObj.typedMem() != nullptr);
  1.2870 +    return true;
  1.2871 +}
  1.2872 +
  1.2873 +JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::TypedObjectIsAttachedJitInfo,
  1.2874 +                                      TypedObjectIsAttachedJitInfo,
  1.2875 +                                      js::TypedObjectIsAttached);
  1.2876 +
  1.2877 +bool
  1.2878 +js::ClampToUint8(ThreadSafeContext *, unsigned argc, Value *vp)
  1.2879 +{
  1.2880 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.2881 +    JS_ASSERT(args.length() == 1);
  1.2882 +    JS_ASSERT(args[0].isNumber());
  1.2883 +    args.rval().setNumber(ClampDoubleToUint8(args[0].toNumber()));
  1.2884 +    return true;
  1.2885 +}
  1.2886 +
  1.2887 +JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ClampToUint8JitInfo, ClampToUint8JitInfo,
  1.2888 +                                      js::ClampToUint8);
  1.2889 +
  1.2890 +bool
  1.2891 +js::Memcpy(ThreadSafeContext *, unsigned argc, Value *vp)
  1.2892 +{
  1.2893 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.2894 +    JS_ASSERT(args.length() == 5);
  1.2895 +    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
  1.2896 +    JS_ASSERT(args[1].isInt32());
  1.2897 +    JS_ASSERT(args[2].isObject() && args[2].toObject().is<TypedObject>());
  1.2898 +    JS_ASSERT(args[3].isInt32());
  1.2899 +    JS_ASSERT(args[4].isInt32());
  1.2900 +
  1.2901 +    TypedObject &targetTypedObj = args[0].toObject().as<TypedObject>();
  1.2902 +    int32_t targetOffset = args[1].toInt32();
  1.2903 +    TypedObject &sourceTypedObj = args[2].toObject().as<TypedObject>();
  1.2904 +    int32_t sourceOffset = args[3].toInt32();
  1.2905 +    int32_t size = args[4].toInt32();
  1.2906 +
  1.2907 +    JS_ASSERT(targetOffset >= 0);
  1.2908 +    JS_ASSERT(sourceOffset >= 0);
  1.2909 +    JS_ASSERT(size >= 0);
  1.2910 +    JS_ASSERT(size + targetOffset <= targetTypedObj.size());
  1.2911 +    JS_ASSERT(size + sourceOffset <= sourceTypedObj.size());
  1.2912 +
  1.2913 +    uint8_t *target = targetTypedObj.typedMem(targetOffset);
  1.2914 +    uint8_t *source = sourceTypedObj.typedMem(sourceOffset);
  1.2915 +    memcpy(target, source, size);
  1.2916 +    args.rval().setUndefined();
  1.2917 +    return true;
  1.2918 +}
  1.2919 +
  1.2920 +JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::MemcpyJitInfo, MemcpyJitInfo, js::Memcpy);
  1.2921 +
  1.2922 +bool
  1.2923 +js::GetTypedObjectModule(JSContext *cx, unsigned argc, Value *vp)
  1.2924 +{
  1.2925 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.2926 +    Rooted<GlobalObject*> global(cx, cx->global());
  1.2927 +    JS_ASSERT(global);
  1.2928 +    args.rval().setObject(global->getTypedObjectModule());
  1.2929 +    return true;
  1.2930 +}
  1.2931 +
  1.2932 +bool
  1.2933 +js::GetFloat32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp)
  1.2934 +{
  1.2935 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.2936 +    Rooted<GlobalObject*> global(cx, cx->global());
  1.2937 +    JS_ASSERT(global);
  1.2938 +    args.rval().setObject(global->float32x4TypeDescr());
  1.2939 +    return true;
  1.2940 +}
  1.2941 +
  1.2942 +bool
  1.2943 +js::GetInt32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp)
  1.2944 +{
  1.2945 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.2946 +    Rooted<GlobalObject*> global(cx, cx->global());
  1.2947 +    JS_ASSERT(global);
  1.2948 +    args.rval().setObject(global->int32x4TypeDescr());
  1.2949 +    return true;
  1.2950 +}
  1.2951 +
  1.2952 +#define JS_STORE_SCALAR_CLASS_IMPL(_constant, T, _name)                         \
  1.2953 +bool                                                                            \
  1.2954 +js::StoreScalar##T::Func(ThreadSafeContext *, unsigned argc, Value *vp)         \
  1.2955 +{                                                                               \
  1.2956 +    CallArgs args = CallArgsFromVp(argc, vp);                                   \
  1.2957 +    JS_ASSERT(args.length() == 3);                                              \
  1.2958 +    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());      \
  1.2959 +    JS_ASSERT(args[1].isInt32());                                               \
  1.2960 +    JS_ASSERT(args[2].isNumber());                                              \
  1.2961 +                                                                                \
  1.2962 +    TypedObject &typedObj = args[0].toObject().as<TypedObject>();               \
  1.2963 +    int32_t offset = args[1].toInt32();                                         \
  1.2964 +                                                                                \
  1.2965 +    /* Should be guaranteed by the typed objects API: */                        \
  1.2966 +    JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                                    \
  1.2967 +                                                                                \
  1.2968 +    T *target = reinterpret_cast<T*>(typedObj.typedMem(offset));                \
  1.2969 +    double d = args[2].toNumber();                                              \
  1.2970 +    *target = ConvertScalar<T>(d);                                              \
  1.2971 +    args.rval().setUndefined();                                                 \
  1.2972 +    return true;                                                                \
  1.2973 +}                                                                               \
  1.2974 +                                                                                \
  1.2975 +JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::StoreScalar##T::JitInfo,              \
  1.2976 +                                      StoreScalar##T,                           \
  1.2977 +                                      js::StoreScalar##T::Func);
  1.2978 +
  1.2979 +#define JS_STORE_REFERENCE_CLASS_IMPL(_constant, T, _name)                      \
  1.2980 +bool                                                                            \
  1.2981 +js::StoreReference##T::Func(ThreadSafeContext *, unsigned argc, Value *vp)      \
  1.2982 +{                                                                               \
  1.2983 +    CallArgs args = CallArgsFromVp(argc, vp);                                   \
  1.2984 +    JS_ASSERT(args.length() == 3);                                              \
  1.2985 +    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());      \
  1.2986 +    JS_ASSERT(args[1].isInt32());                                               \
  1.2987 +                                                                                \
  1.2988 +    TypedObject &typedObj = args[0].toObject().as<TypedObject>();               \
  1.2989 +    int32_t offset = args[1].toInt32();                                         \
  1.2990 +                                                                                \
  1.2991 +    /* Should be guaranteed by the typed objects API: */                        \
  1.2992 +    JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                                    \
  1.2993 +                                                                                \
  1.2994 +    T *target = reinterpret_cast<T*>(typedObj.typedMem(offset));                \
  1.2995 +    store(target, args[2]);                                                     \
  1.2996 +    args.rval().setUndefined();                                                 \
  1.2997 +    return true;                                                                \
  1.2998 +}                                                                               \
  1.2999 +                                                                                \
  1.3000 +JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::StoreReference##T::JitInfo,           \
  1.3001 +                                      StoreReference##T,                        \
  1.3002 +                                      js::StoreReference##T::Func);
  1.3003 +
  1.3004 +#define JS_LOAD_SCALAR_CLASS_IMPL(_constant, T, _name)                                  \
  1.3005 +bool                                                                                    \
  1.3006 +js::LoadScalar##T::Func(ThreadSafeContext *, unsigned argc, Value *vp)                  \
  1.3007 +{                                                                                       \
  1.3008 +    CallArgs args = CallArgsFromVp(argc, vp);                                           \
  1.3009 +    JS_ASSERT(args.length() == 2);                                                      \
  1.3010 +    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());              \
  1.3011 +    JS_ASSERT(args[1].isInt32());                                                       \
  1.3012 +                                                                                        \
  1.3013 +    TypedObject &typedObj = args[0].toObject().as<TypedObject>();                       \
  1.3014 +    int32_t offset = args[1].toInt32();                                                 \
  1.3015 +                                                                                        \
  1.3016 +    /* Should be guaranteed by the typed objects API: */                                \
  1.3017 +    JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                                            \
  1.3018 +                                                                                        \
  1.3019 +    T *target = reinterpret_cast<T*>(typedObj.typedMem(offset));                        \
  1.3020 +    args.rval().setNumber((double) *target);                                            \
  1.3021 +    return true;                                                                        \
  1.3022 +}                                                                                       \
  1.3023 +                                                                                        \
  1.3024 +JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::LoadScalar##T::JitInfo, LoadScalar##T,        \
  1.3025 +                                      js::LoadScalar##T::Func);
  1.3026 +
  1.3027 +#define JS_LOAD_REFERENCE_CLASS_IMPL(_constant, T, _name)                       \
  1.3028 +bool                                                                            \
  1.3029 +js::LoadReference##T::Func(ThreadSafeContext *, unsigned argc, Value *vp)       \
  1.3030 +{                                                                               \
  1.3031 +    CallArgs args = CallArgsFromVp(argc, vp);                                   \
  1.3032 +    JS_ASSERT(args.length() == 2);                                              \
  1.3033 +    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());      \
  1.3034 +    JS_ASSERT(args[1].isInt32());                                               \
  1.3035 +                                                                                \
  1.3036 +    TypedObject &typedObj = args[0].toObject().as<TypedObject>();               \
  1.3037 +    int32_t offset = args[1].toInt32();                                         \
  1.3038 +                                                                                \
  1.3039 +    /* Should be guaranteed by the typed objects API: */                        \
  1.3040 +    JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                                    \
  1.3041 +                                                                                \
  1.3042 +    T *target = reinterpret_cast<T*>(typedObj.typedMem(offset));                \
  1.3043 +    load(target, args.rval());                                                  \
  1.3044 +    return true;                                                                \
  1.3045 +}                                                                               \
  1.3046 +                                                                                \
  1.3047 +JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::LoadReference##T::JitInfo,            \
  1.3048 +                                      LoadReference##T,                         \
  1.3049 +                                      js::LoadReference##T::Func);
  1.3050 +
  1.3051 +// Because the precise syntax for storing values/objects/strings
  1.3052 +// differs, we abstract it away using specialized variants of the
  1.3053 +// private methods `store()` and `load()`.
  1.3054 +
  1.3055 +void
  1.3056 +StoreReferenceHeapValue::store(HeapValue *heap, const Value &v)
  1.3057 +{
  1.3058 +    *heap = v;
  1.3059 +}
  1.3060 +
  1.3061 +void
  1.3062 +StoreReferenceHeapPtrObject::store(HeapPtrObject *heap, const Value &v)
  1.3063 +{
  1.3064 +    JS_ASSERT(v.isObjectOrNull()); // or else Store_object is being misused
  1.3065 +    *heap = v.toObjectOrNull();
  1.3066 +}
  1.3067 +
  1.3068 +void
  1.3069 +StoreReferenceHeapPtrString::store(HeapPtrString *heap, const Value &v)
  1.3070 +{
  1.3071 +    JS_ASSERT(v.isString()); // or else Store_string is being misused
  1.3072 +    *heap = v.toString();
  1.3073 +}
  1.3074 +
  1.3075 +void
  1.3076 +LoadReferenceHeapValue::load(HeapValue *heap,
  1.3077 +                             MutableHandleValue v)
  1.3078 +{
  1.3079 +    v.set(*heap);
  1.3080 +}
  1.3081 +
  1.3082 +void
  1.3083 +LoadReferenceHeapPtrObject::load(HeapPtrObject *heap,
  1.3084 +                                 MutableHandleValue v)
  1.3085 +{
  1.3086 +    if (*heap)
  1.3087 +        v.setObject(**heap);
  1.3088 +    else
  1.3089 +        v.setNull();
  1.3090 +}
  1.3091 +
  1.3092 +void
  1.3093 +LoadReferenceHeapPtrString::load(HeapPtrString *heap,
  1.3094 +                                 MutableHandleValue v)
  1.3095 +{
  1.3096 +    v.setString(*heap);
  1.3097 +}
  1.3098 +
  1.3099 +// I was using templates for this stuff instead of macros, but ran
  1.3100 +// into problems with the Unagi compiler.
  1.3101 +JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_STORE_SCALAR_CLASS_IMPL)
  1.3102 +JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_LOAD_SCALAR_CLASS_IMPL)
  1.3103 +JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_STORE_REFERENCE_CLASS_IMPL)
  1.3104 +JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_LOAD_REFERENCE_CLASS_IMPL)
  1.3105 +
  1.3106 +///////////////////////////////////////////////////////////////////////////
  1.3107 +// Walking memory
  1.3108 +
  1.3109 +template<typename V>
  1.3110 +static void
  1.3111 +visitReferences(SizedTypeDescr &descr,
  1.3112 +                uint8_t *mem,
  1.3113 +                V& visitor)
  1.3114 +{
  1.3115 +    if (descr.transparent())
  1.3116 +        return;
  1.3117 +
  1.3118 +    switch (descr.kind()) {
  1.3119 +      case TypeDescr::Scalar:
  1.3120 +      case TypeDescr::X4:
  1.3121 +        return;
  1.3122 +
  1.3123 +      case TypeDescr::Reference:
  1.3124 +        visitor.visitReference(descr.as<ReferenceTypeDescr>(), mem);
  1.3125 +        return;
  1.3126 +
  1.3127 +      case TypeDescr::SizedArray:
  1.3128 +      {
  1.3129 +        SizedArrayTypeDescr &arrayDescr = descr.as<SizedArrayTypeDescr>();
  1.3130 +        SizedTypeDescr &elementDescr = arrayDescr.elementType();
  1.3131 +        for (int32_t i = 0; i < arrayDescr.length(); i++) {
  1.3132 +            visitReferences(elementDescr, mem, visitor);
  1.3133 +            mem += elementDescr.size();
  1.3134 +        }
  1.3135 +        return;
  1.3136 +      }
  1.3137 +
  1.3138 +      case TypeDescr::UnsizedArray:
  1.3139 +      {
  1.3140 +        MOZ_ASSUME_UNREACHABLE("Only Sized Type representations");
  1.3141 +      }
  1.3142 +
  1.3143 +      case TypeDescr::Struct:
  1.3144 +      {
  1.3145 +        StructTypeDescr &structDescr = descr.as<StructTypeDescr>();
  1.3146 +        for (size_t i = 0; i < structDescr.fieldCount(); i++) {
  1.3147 +            SizedTypeDescr &descr = structDescr.fieldDescr(i);
  1.3148 +            size_t offset = structDescr.fieldOffset(i);
  1.3149 +            visitReferences(descr, mem + offset, visitor);
  1.3150 +        }
  1.3151 +        return;
  1.3152 +      }
  1.3153 +    }
  1.3154 +
  1.3155 +    MOZ_ASSUME_UNREACHABLE("Invalid type repr kind");
  1.3156 +}
  1.3157 +
  1.3158 +///////////////////////////////////////////////////////////////////////////
  1.3159 +// Initializing instances
  1.3160 +
  1.3161 +namespace js {
  1.3162 +class MemoryInitVisitor {
  1.3163 +    const JSRuntime *rt_;
  1.3164 +
  1.3165 +  public:
  1.3166 +    MemoryInitVisitor(const JSRuntime *rt)
  1.3167 +      : rt_(rt)
  1.3168 +    {}
  1.3169 +
  1.3170 +    void visitReference(ReferenceTypeDescr &descr, uint8_t *mem);
  1.3171 +};
  1.3172 +} // namespace js
  1.3173 +
  1.3174 +void
  1.3175 +js::MemoryInitVisitor::visitReference(ReferenceTypeDescr &descr, uint8_t *mem)
  1.3176 +{
  1.3177 +    switch (descr.type()) {
  1.3178 +      case ReferenceTypeDescr::TYPE_ANY:
  1.3179 +      {
  1.3180 +        js::HeapValue *heapValue = reinterpret_cast<js::HeapValue *>(mem);
  1.3181 +        heapValue->init(UndefinedValue());
  1.3182 +        return;
  1.3183 +      }
  1.3184 +
  1.3185 +      case ReferenceTypeDescr::TYPE_OBJECT:
  1.3186 +      {
  1.3187 +        js::HeapPtrObject *objectPtr =
  1.3188 +            reinterpret_cast<js::HeapPtrObject *>(mem);
  1.3189 +        objectPtr->init(nullptr);
  1.3190 +        return;
  1.3191 +      }
  1.3192 +
  1.3193 +      case ReferenceTypeDescr::TYPE_STRING:
  1.3194 +      {
  1.3195 +        js::HeapPtrString *stringPtr =
  1.3196 +            reinterpret_cast<js::HeapPtrString *>(mem);
  1.3197 +        stringPtr->init(rt_->emptyString);
  1.3198 +        return;
  1.3199 +      }
  1.3200 +    }
  1.3201 +
  1.3202 +    MOZ_ASSUME_UNREACHABLE("Invalid kind");
  1.3203 +}
  1.3204 +
  1.3205 +void
  1.3206 +SizedTypeDescr::initInstances(const JSRuntime *rt, uint8_t *mem, size_t length)
  1.3207 +{
  1.3208 +    JS_ASSERT(length >= 1);
  1.3209 +
  1.3210 +    MemoryInitVisitor visitor(rt);
  1.3211 +
  1.3212 +    // Initialize the 0th instance
  1.3213 +    memset(mem, 0, size());
  1.3214 +    if (opaque())
  1.3215 +        visitReferences(*this, mem, visitor);
  1.3216 +
  1.3217 +    // Stamp out N copies of later instances
  1.3218 +    uint8_t *target = mem;
  1.3219 +    for (size_t i = 1; i < length; i++) {
  1.3220 +        target += size();
  1.3221 +        memcpy(target, mem, size());
  1.3222 +    }
  1.3223 +}
  1.3224 +
  1.3225 +///////////////////////////////////////////////////////////////////////////
  1.3226 +// Tracing instances
  1.3227 +
  1.3228 +namespace js {
  1.3229 +class MemoryTracingVisitor {
  1.3230 +    JSTracer *trace_;
  1.3231 +
  1.3232 +  public:
  1.3233 +
  1.3234 +    MemoryTracingVisitor(JSTracer *trace)
  1.3235 +      : trace_(trace)
  1.3236 +    {}
  1.3237 +
  1.3238 +    void visitReference(ReferenceTypeDescr &descr, uint8_t *mem);
  1.3239 +};
  1.3240 +} // namespace js
  1.3241 +
  1.3242 +void
  1.3243 +js::MemoryTracingVisitor::visitReference(ReferenceTypeDescr &descr, uint8_t *mem)
  1.3244 +{
  1.3245 +    switch (descr.type()) {
  1.3246 +      case ReferenceTypeDescr::TYPE_ANY:
  1.3247 +      {
  1.3248 +        js::HeapValue *heapValue = reinterpret_cast<js::HeapValue *>(mem);
  1.3249 +        gc::MarkValue(trace_, heapValue, "reference-val");
  1.3250 +        return;
  1.3251 +      }
  1.3252 +
  1.3253 +      case ReferenceTypeDescr::TYPE_OBJECT:
  1.3254 +      {
  1.3255 +        js::HeapPtrObject *objectPtr =
  1.3256 +            reinterpret_cast<js::HeapPtrObject *>(mem);
  1.3257 +        if (*objectPtr)
  1.3258 +            gc::MarkObject(trace_, objectPtr, "reference-obj");
  1.3259 +        return;
  1.3260 +      }
  1.3261 +
  1.3262 +      case ReferenceTypeDescr::TYPE_STRING:
  1.3263 +      {
  1.3264 +        js::HeapPtrString *stringPtr =
  1.3265 +            reinterpret_cast<js::HeapPtrString *>(mem);
  1.3266 +        if (*stringPtr)
  1.3267 +            gc::MarkString(trace_, stringPtr, "reference-str");
  1.3268 +        return;
  1.3269 +      }
  1.3270 +    }
  1.3271 +
  1.3272 +    MOZ_ASSUME_UNREACHABLE("Invalid kind");
  1.3273 +}
  1.3274 +
  1.3275 +void
  1.3276 +SizedTypeDescr::traceInstances(JSTracer *trace, uint8_t *mem, size_t length)
  1.3277 +{
  1.3278 +    MemoryTracingVisitor visitor(trace);
  1.3279 +
  1.3280 +    for (size_t i = 0; i < length; i++) {
  1.3281 +        visitReferences(*this, mem, visitor);
  1.3282 +        mem += size();
  1.3283 +    }
  1.3284 +}
  1.3285 +

mercurial