js/src/builtin/TypedObject.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "builtin/TypedObject.h"
     9 #include "mozilla/CheckedInt.h"
    11 #include "jscompartment.h"
    12 #include "jsfun.h"
    13 #include "jsobj.h"
    14 #include "jsutil.h"
    16 #include "gc/Marking.h"
    17 #include "js/Vector.h"
    18 #include "vm/GlobalObject.h"
    19 #include "vm/ObjectImpl.h"
    20 #include "vm/String.h"
    21 #include "vm/StringBuffer.h"
    22 #include "vm/TypedArrayObject.h"
    24 #include "jsatominlines.h"
    25 #include "jsobjinlines.h"
    27 #include "vm/Shape-inl.h"
    29 using mozilla::CheckedInt32;
    30 using mozilla::DebugOnly;
    32 using namespace js;
    34 const Class js::TypedObjectModuleObject::class_ = {
    35     "TypedObject",
    36     JSCLASS_HAS_RESERVED_SLOTS(SlotCount) |
    37     JSCLASS_HAS_CACHED_PROTO(JSProto_TypedObject),
    38     JS_PropertyStub,         /* addProperty */
    39     JS_DeletePropertyStub,   /* delProperty */
    40     JS_PropertyStub,         /* getProperty */
    41     JS_StrictPropertyStub,   /* setProperty */
    42     JS_EnumerateStub,
    43     JS_ResolveStub,
    44     JS_ConvertStub
    45 };
    47 static const JSFunctionSpec TypedObjectMethods[] = {
    48     JS_SELF_HOSTED_FN("objectType", "TypeOfTypedObject", 1, 0),
    49     JS_SELF_HOSTED_FN("storage", "StorageOfTypedObject", 1, 0),
    50     JS_FS_END
    51 };
    53 static void
    54 ReportCannotConvertTo(JSContext *cx, HandleValue fromValue, const char *toType)
    55 {
    56     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
    57                          InformalValueTypeName(fromValue), toType);
    58 }
    60 template<class T>
    61 static inline T*
    62 ToObjectIf(HandleValue value)
    63 {
    64     if (!value.isObject())
    65         return nullptr;
    67     if (!value.toObject().is<T>())
    68         return nullptr;
    70     return &value.toObject().as<T>();
    71 }
    73 static inline CheckedInt32 roundUpToAlignment(CheckedInt32 address, int32_t align)
    74 {
    75     JS_ASSERT(IsPowerOfTwo(align));
    77     // Note: Be careful to order operators such that we first make the
    78     // value smaller and then larger, so that we don't get false
    79     // overflow errors due to (e.g.) adding `align` and then
    80     // subtracting `1` afterwards when merely adding `align-1` would
    81     // not have overflowed. Note that due to the nature of two's
    82     // complement representation, if `address` is already aligned,
    83     // then adding `align-1` cannot itself cause an overflow.
    85     return ((address + (align - 1)) / align) * align;
    86 }
    88 /*
    89  * Overwrites the contents of `typedObj` at offset `offset` with `val`
    90  * converted to the type `typeObj`. This is done by delegating to
    91  * self-hosted code. This is used for assignments and initializations.
    92  *
    93  * For example, consider the final assignment in this snippet:
    94  *
    95  *    var Point = new StructType({x: float32, y: float32});
    96  *    var Line = new StructType({from: Point, to: Point});
    97  *    var line = new Line();
    98  *    line.to = {x: 22, y: 44};
    99  *
   100  * This would result in a call to `ConvertAndCopyTo`
   101  * where:
   102  * - typeObj = Point
   103  * - typedObj = line
   104  * - offset = sizeof(Point) == 8
   105  * - val = {x: 22, y: 44}
   106  * This would result in loading the value of `x`, converting
   107  * it to a float32, and hen storing it at the appropriate offset,
   108  * and then doing the same for `y`.
   109  *
   110  * Note that the type of `typeObj` may not be the
   111  * type of `typedObj` but rather some subcomponent of `typedObj`.
   112  */
   113 static bool
   114 ConvertAndCopyTo(JSContext *cx,
   115                  HandleTypeDescr typeObj,
   116                  HandleTypedObject typedObj,
   117                  int32_t offset,
   118                  HandleValue val)
   119 {
   120     RootedFunction func(
   121         cx, SelfHostedFunction(cx, cx->names().ConvertAndCopyTo));
   122     if (!func)
   123         return false;
   125     InvokeArgs args(cx);
   126     if (!args.init(4))
   127         return false;
   129     args.setCallee(ObjectValue(*func));
   130     args[0].setObject(*typeObj);
   131     args[1].setObject(*typedObj);
   132     args[2].setInt32(offset);
   133     args[3].set(val);
   135     return Invoke(cx, args);
   136 }
   138 static bool
   139 ConvertAndCopyTo(JSContext *cx, HandleTypedObject typedObj, HandleValue val)
   140 {
   141     Rooted<TypeDescr*> type(cx, &typedObj->typeDescr());
   142     return ConvertAndCopyTo(cx, type, typedObj, 0, val);
   143 }
   145 /*
   146  * Overwrites the contents of `typedObj` at offset `offset` with `val`
   147  * converted to the type `typeObj`
   148  */
   149 static bool
   150 Reify(JSContext *cx,
   151       HandleTypeDescr type,
   152       HandleTypedObject typedObj,
   153       size_t offset,
   154       MutableHandleValue to)
   155 {
   156     RootedFunction func(cx, SelfHostedFunction(cx, cx->names().Reify));
   157     if (!func)
   158         return false;
   160     InvokeArgs args(cx);
   161     if (!args.init(3))
   162         return false;
   164     args.setCallee(ObjectValue(*func));
   165     args[0].setObject(*type);
   166     args[1].setObject(*typedObj);
   167     args[2].setInt32(offset);
   169     if (!Invoke(cx, args))
   170         return false;
   172     to.set(args.rval());
   173     return true;
   174 }
   176 // Extracts the `prototype` property from `obj`, throwing if it is
   177 // missing or not an object.
   178 static JSObject *
   179 GetPrototype(JSContext *cx, HandleObject obj)
   180 {
   181     RootedValue prototypeVal(cx);
   182     if (!JSObject::getProperty(cx, obj, obj, cx->names().prototype,
   183                                &prototypeVal))
   184     {
   185         return nullptr;
   186     }
   187     if (!prototypeVal.isObject()) {
   188         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   189                              JSMSG_INVALID_PROTOTYPE);
   190         return nullptr;
   191     }
   192     return &prototypeVal.toObject();
   193 }
   195 /***************************************************************************
   196  * Typed Prototypes
   197  *
   198  * Every type descriptor has an associated prototype. Instances of
   199  * that type descriptor use this as their prototype. Per the spec,
   200  * typed object prototypes cannot be mutated.
   201  */
   203 const Class js::TypedProto::class_ = {
   204     "TypedProto",
   205     JSCLASS_HAS_RESERVED_SLOTS(JS_TYPROTO_SLOTS),
   206     JS_PropertyStub,       /* addProperty */
   207     JS_DeletePropertyStub, /* delProperty */
   208     JS_PropertyStub,       /* getProperty */
   209     JS_StrictPropertyStub, /* setProperty */
   210     JS_EnumerateStub,
   211     JS_ResolveStub,
   212     JS_ConvertStub,
   213     nullptr,
   214     nullptr,
   215     nullptr,
   216     nullptr,
   217     nullptr
   218 };
   220 /***************************************************************************
   221  * Scalar type objects
   222  *
   223  * Scalar type objects like `uint8`, `uint16`, are all instances of
   224  * the ScalarTypeDescr class. Like all type objects, they have a reserved
   225  * slot pointing to a TypeRepresentation object, which is used to
   226  * distinguish which scalar type object this actually is.
   227  */
   229 const Class js::ScalarTypeDescr::class_ = {
   230     "Scalar",
   231     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS),
   232     JS_PropertyStub,       /* addProperty */
   233     JS_DeletePropertyStub, /* delProperty */
   234     JS_PropertyStub,       /* getProperty */
   235     JS_StrictPropertyStub, /* setProperty */
   236     JS_EnumerateStub,
   237     JS_ResolveStub,
   238     JS_ConvertStub,
   239     nullptr,
   240     ScalarTypeDescr::call,
   241     nullptr,
   242     nullptr,
   243     nullptr
   244 };
   246 const JSFunctionSpec js::ScalarTypeDescr::typeObjectMethods[] = {
   247     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
   248     {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
   249     {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
   250     JS_FS_END
   251 };
   253 static int32_t ScalarSizes[] = {
   254 #define SCALAR_SIZE(_kind, _type, _name)                        \
   255     sizeof(_type),
   256     JS_FOR_EACH_SCALAR_TYPE_REPR(SCALAR_SIZE) 0
   257 #undef SCALAR_SIZE
   258 };
   260 int32_t
   261 ScalarTypeDescr::size(Type t)
   262 {
   263     return ScalarSizes[t];
   264 }
   266 int32_t
   267 ScalarTypeDescr::alignment(Type t)
   268 {
   269     return ScalarSizes[t];
   270 }
   272 /*static*/ const char *
   273 ScalarTypeDescr::typeName(Type type)
   274 {
   275     switch (type) {
   276 #define NUMERIC_TYPE_TO_STRING(constant_, type_, name_) \
   277         case constant_: return #name_;
   278         JS_FOR_EACH_SCALAR_TYPE_REPR(NUMERIC_TYPE_TO_STRING)
   279     }
   280     MOZ_ASSUME_UNREACHABLE("Invalid type");
   281 }
   283 bool
   284 ScalarTypeDescr::call(JSContext *cx, unsigned argc, Value *vp)
   285 {
   286     CallArgs args = CallArgsFromVp(argc, vp);
   287     if (args.length() < 1) {
   288         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
   289                              args.callee().getClass()->name, "0", "s");
   290         return false;
   291     }
   293     Rooted<ScalarTypeDescr *> descr(cx, &args.callee().as<ScalarTypeDescr>());
   294     ScalarTypeDescr::Type type = descr->type();
   296     double number;
   297     if (!ToNumber(cx, args[0], &number))
   298         return false;
   300     if (type == ScalarTypeDescr::TYPE_UINT8_CLAMPED)
   301         number = ClampDoubleToUint8(number);
   303     switch (type) {
   304 #define SCALARTYPE_CALL(constant_, type_, name_)                             \
   305       case constant_: {                                                       \
   306           type_ converted = ConvertScalar<type_>(number);                     \
   307           args.rval().setNumber((double) converted);                          \
   308           return true;                                                        \
   309       }
   311         JS_FOR_EACH_SCALAR_TYPE_REPR(SCALARTYPE_CALL)
   312 #undef SCALARTYPE_CALL
   314     }
   315     return true;
   316 }
   318 /***************************************************************************
   319  * Reference type objects
   320  *
   321  * Reference type objects like `Any` or `Object` basically work the
   322  * same way that the scalar type objects do. There is one class with
   323  * many instances, and each instance has a reserved slot with a
   324  * TypeRepresentation object, which is used to distinguish which
   325  * reference type object this actually is.
   326  */
   328 const Class js::ReferenceTypeDescr::class_ = {
   329     "Reference",
   330     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS),
   331     JS_PropertyStub,       /* addProperty */
   332     JS_DeletePropertyStub, /* delProperty */
   333     JS_PropertyStub,       /* getProperty */
   334     JS_StrictPropertyStub, /* setProperty */
   335     JS_EnumerateStub,
   336     JS_ResolveStub,
   337     JS_ConvertStub,
   338     nullptr,
   339     ReferenceTypeDescr::call,
   340     nullptr,
   341     nullptr,
   342     nullptr
   343 };
   345 const JSFunctionSpec js::ReferenceTypeDescr::typeObjectMethods[] = {
   346     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
   347     {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
   348     {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
   349     JS_FS_END
   350 };
   352 static int32_t ReferenceSizes[] = {
   353 #define REFERENCE_SIZE(_kind, _type, _name)                        \
   354     sizeof(_type),
   355     JS_FOR_EACH_REFERENCE_TYPE_REPR(REFERENCE_SIZE) 0
   356 #undef REFERENCE_SIZE
   357 };
   359 int32_t
   360 ReferenceTypeDescr::size(Type t)
   361 {
   362     return ReferenceSizes[t];
   363 }
   365 int32_t
   366 ReferenceTypeDescr::alignment(Type t)
   367 {
   368     return ReferenceSizes[t];
   369 }
   371 /*static*/ const char *
   372 ReferenceTypeDescr::typeName(Type type)
   373 {
   374     switch (type) {
   375 #define NUMERIC_TYPE_TO_STRING(constant_, type_, name_) \
   376         case constant_: return #name_;
   377         JS_FOR_EACH_REFERENCE_TYPE_REPR(NUMERIC_TYPE_TO_STRING)
   378     }
   379     MOZ_ASSUME_UNREACHABLE("Invalid type");
   380 }
   382 bool
   383 js::ReferenceTypeDescr::call(JSContext *cx, unsigned argc, Value *vp)
   384 {
   385     CallArgs args = CallArgsFromVp(argc, vp);
   387     JS_ASSERT(args.callee().is<ReferenceTypeDescr>());
   388     Rooted<ReferenceTypeDescr *> descr(cx, &args.callee().as<ReferenceTypeDescr>());
   390     if (args.length() < 1) {
   391         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   392                              JSMSG_MORE_ARGS_NEEDED,
   393                              descr->typeName(), "0", "s");
   394         return false;
   395     }
   397     switch (descr->type()) {
   398       case ReferenceTypeDescr::TYPE_ANY:
   399         args.rval().set(args[0]);
   400         return true;
   402       case ReferenceTypeDescr::TYPE_OBJECT:
   403       {
   404         RootedObject obj(cx, ToObject(cx, args[0]));
   405         if (!obj)
   406             return false;
   407         args.rval().setObject(*obj);
   408         return true;
   409       }
   411       case ReferenceTypeDescr::TYPE_STRING:
   412       {
   413         RootedString obj(cx, ToString<CanGC>(cx, args[0]));
   414         if (!obj)
   415             return false;
   416         args.rval().setString(&*obj);
   417         return true;
   418       }
   419     }
   421     MOZ_ASSUME_UNREACHABLE("Unhandled Reference type");
   422 }
   424 /***************************************************************************
   425  * X4 type objects
   426  *
   427  * Note: these are partially defined in SIMD.cpp
   428  */
   430 static int32_t X4Sizes[] = {
   431 #define X4_SIZE(_kind, _type, _name)                        \
   432     sizeof(_type) * 4,
   433     JS_FOR_EACH_X4_TYPE_REPR(X4_SIZE) 0
   434 #undef X4_SIZE
   435 };
   437 int32_t
   438 X4TypeDescr::size(Type t)
   439 {
   440     return X4Sizes[t];
   441 }
   443 int32_t
   444 X4TypeDescr::alignment(Type t)
   445 {
   446     return X4Sizes[t];
   447 }
   449 /***************************************************************************
   450  * ArrayMetaTypeDescr class
   451  */
   453 /*
   454  * For code like:
   455  *
   456  *   var A = new TypedObject.ArrayType(uint8, 10);
   457  *   var S = new TypedObject.StructType({...});
   458  *
   459  * As usual, the [[Prototype]] of A is
   460  * TypedObject.ArrayType.prototype.  This permits adding methods to
   461  * all ArrayType types, by setting
   462  * TypedObject.ArrayType.prototype.methodName = function() { ... }.
   463  * The same holds for S with respect to TypedObject.StructType.
   464  *
   465  * We may also want to add methods to *instances* of an ArrayType:
   466  *
   467  *   var a = new A();
   468  *   var s = new S();
   469  *
   470  * As usual, the [[Prototype]] of a is A.prototype.  What's
   471  * A.prototype?  It's an empty object, and you can set
   472  * A.prototype.methodName = function() { ... } to add a method to all
   473  * A instances.  (And the same with respect to s and S.)
   474  *
   475  * But what if you want to add a method to all ArrayType instances,
   476  * not just all A instances?  (Or to all StructType instances.)  The
   477  * [[Prototype]] of the A.prototype empty object is
   478  * TypedObject.ArrayType.prototype.prototype (two .prototype levels!).
   479  * So just set TypedObject.ArrayType.prototype.prototype.methodName =
   480  * function() { ... } to add a method to all ArrayType instances.
   481  * (And, again, same with respect to s and S.)
   482  *
   483  * This function creates the A.prototype/S.prototype object.  It takes
   484  * as an argument either the TypedObject.ArrayType or the
   485  * TypedObject.StructType constructor function, then returns an empty
   486  * object with the .prototype.prototype object as its [[Prototype]].
   487  */
   488 static TypedProto *
   489 CreatePrototypeObjectForComplexTypeInstance(JSContext *cx,
   490                                             Handle<TypeDescr*> descr,
   491                                             HandleObject ctorPrototype)
   492 {
   493     RootedObject ctorPrototypePrototype(cx, GetPrototype(cx, ctorPrototype));
   494     if (!ctorPrototypePrototype)
   495         return nullptr;
   497     Rooted<TypedProto*> result(cx);
   498     result = NewObjectWithProto<TypedProto>(cx,
   499                                             &*ctorPrototypePrototype,
   500                                             nullptr,
   501                                             TenuredObject);
   502     if (!result)
   503         return nullptr;
   505     result->initTypeDescrSlot(*descr);
   506     return result;
   507 }
   509 const Class UnsizedArrayTypeDescr::class_ = {
   510     "ArrayType",
   511     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS),
   512     JS_PropertyStub,
   513     JS_DeletePropertyStub,
   514     JS_PropertyStub,
   515     JS_StrictPropertyStub,
   516     JS_EnumerateStub,
   517     JS_ResolveStub,
   518     JS_ConvertStub,
   519     nullptr,
   520     nullptr,
   521     nullptr,
   522     TypedObject::constructUnsized,
   523     nullptr
   524 };
   526 const Class SizedArrayTypeDescr::class_ = {
   527     "ArrayType",
   528     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS),
   529     JS_PropertyStub,
   530     JS_DeletePropertyStub,
   531     JS_PropertyStub,
   532     JS_StrictPropertyStub,
   533     JS_EnumerateStub,
   534     JS_ResolveStub,
   535     JS_ConvertStub,
   536     nullptr,
   537     nullptr,
   538     nullptr,
   539     TypedObject::constructSized,
   540     nullptr
   541 };
   543 const JSPropertySpec ArrayMetaTypeDescr::typeObjectProperties[] = {
   544     JS_PS_END
   545 };
   547 const JSFunctionSpec ArrayMetaTypeDescr::typeObjectMethods[] = {
   548     {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
   549     JS_FN("dimension", UnsizedArrayTypeDescr::dimension, 1, 0),
   550     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
   551     {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
   552     JS_SELF_HOSTED_FN("build",    "TypedObjectArrayTypeBuild", 3, 0),
   553     JS_SELF_HOSTED_FN("buildPar", "TypedObjectArrayTypeBuildPar", 3, 0),
   554     JS_SELF_HOSTED_FN("from",     "TypedObjectArrayTypeFrom", 3, 0),
   555     JS_SELF_HOSTED_FN("fromPar",  "TypedObjectArrayTypeFromPar", 3, 0),
   556     JS_FS_END
   557 };
   559 const JSPropertySpec ArrayMetaTypeDescr::typedObjectProperties[] = {
   560     JS_PS_END
   561 };
   563 const JSFunctionSpec ArrayMetaTypeDescr::typedObjectMethods[] = {
   564     {"forEach", {nullptr, nullptr}, 1, 0, "ArrayForEach"},
   565     {"redimension", {nullptr, nullptr}, 1, 0, "TypedArrayRedimension"},
   566     JS_SELF_HOSTED_FN("map",        "TypedArrayMap",        2, 0),
   567     JS_SELF_HOSTED_FN("mapPar",     "TypedArrayMapPar",     2, 0),
   568     JS_SELF_HOSTED_FN("reduce",     "TypedArrayReduce",     2, 0),
   569     JS_SELF_HOSTED_FN("reducePar",  "TypedArrayReducePar",  2, 0),
   570     JS_SELF_HOSTED_FN("scatter",    "TypedArrayScatter",    4, 0),
   571     JS_SELF_HOSTED_FN("scatterPar", "TypedArrayScatterPar", 4, 0),
   572     JS_SELF_HOSTED_FN("filter",     "TypedArrayFilter",     1, 0),
   573     JS_SELF_HOSTED_FN("filterPar",  "TypedArrayFilterPar",  1, 0),
   574     JS_FS_END
   575 };
   577 bool
   578 js::CreateUserSizeAndAlignmentProperties(JSContext *cx, HandleTypeDescr descr)
   579 {
   580     // If data is transparent, also store the public slots.
   581     if (descr->transparent() && descr->is<SizedTypeDescr>()) {
   582         Rooted<SizedTypeDescr*> sizedDescr(cx, &descr->as<SizedTypeDescr>());
   584         // byteLength
   585         RootedValue typeByteLength(cx, Int32Value(sizedDescr->size()));
   586         if (!JSObject::defineProperty(cx, descr, cx->names().byteLength,
   587                                       typeByteLength,
   588                                       nullptr, nullptr,
   589                                       JSPROP_READONLY | JSPROP_PERMANENT))
   590         {
   591             return false;
   592         }
   594         // byteAlignment
   595         RootedValue typeByteAlignment(cx, Int32Value(sizedDescr->alignment()));
   596         if (!JSObject::defineProperty(cx, descr, cx->names().byteAlignment,
   597                                       typeByteAlignment,
   598                                       nullptr, nullptr,
   599                                       JSPROP_READONLY | JSPROP_PERMANENT))
   600         {
   601             return false;
   602         }
   603     } else {
   604         // byteLength
   605         if (!JSObject::defineProperty(cx, descr, cx->names().byteLength,
   606                                       UndefinedHandleValue,
   607                                       nullptr, nullptr,
   608                                       JSPROP_READONLY | JSPROP_PERMANENT))
   609         {
   610             return false;
   611         }
   613         // byteAlignment
   614         if (!JSObject::defineProperty(cx, descr, cx->names().byteAlignment,
   615                                       UndefinedHandleValue,
   616                                       nullptr, nullptr,
   617                                       JSPROP_READONLY | JSPROP_PERMANENT))
   618         {
   619             return false;
   620         }
   621     }
   623     // variable -- true for unsized arrays
   624     RootedValue variable(cx, BooleanValue(!descr->is<SizedTypeDescr>()));
   625     if (!JSObject::defineProperty(cx, descr, cx->names().variable,
   626                                   variable,
   627                                   nullptr, nullptr,
   628                                   JSPROP_READONLY | JSPROP_PERMANENT))
   629     {
   630         return false;
   631     }
   633     return true;
   634 }
   636 template<class T>
   637 T *
   638 ArrayMetaTypeDescr::create(JSContext *cx,
   639                            HandleObject arrayTypePrototype,
   640                            HandleSizedTypeDescr elementType,
   641                            HandleAtom stringRepr,
   642                            int32_t size)
   643 {
   644     Rooted<T*> obj(cx);
   645     obj = NewObjectWithProto<T>(cx, arrayTypePrototype, nullptr, TenuredObject);
   646     if (!obj)
   647         return nullptr;
   649     obj->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(T::Kind));
   650     obj->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
   651     obj->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(elementType->alignment()));
   652     obj->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(size));
   653     obj->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(elementType->opaque()));
   654     obj->initReservedSlot(JS_DESCR_SLOT_ARRAY_ELEM_TYPE, ObjectValue(*elementType));
   656     RootedValue elementTypeVal(cx, ObjectValue(*elementType));
   657     if (!JSObject::defineProperty(cx, obj, cx->names().elementType,
   658                                   elementTypeVal, nullptr, nullptr,
   659                                   JSPROP_READONLY | JSPROP_PERMANENT))
   660         return nullptr;
   662     if (!CreateUserSizeAndAlignmentProperties(cx, obj))
   663         return nullptr;
   665     Rooted<TypedProto*> prototypeObj(cx);
   666     prototypeObj = CreatePrototypeObjectForComplexTypeInstance(cx, obj, arrayTypePrototype);
   667     if (!prototypeObj)
   668         return nullptr;
   670     obj->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*prototypeObj));
   672     if (!LinkConstructorAndPrototype(cx, obj, prototypeObj))
   673         return nullptr;
   675     return obj;
   676 }
   678 bool
   679 ArrayMetaTypeDescr::construct(JSContext *cx, unsigned argc, Value *vp)
   680 {
   681     CallArgs args = CallArgsFromVp(argc, vp);
   683     if (!args.isConstructing()) {
   684         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   685                              JSMSG_NOT_FUNCTION, "ArrayType");
   686         return false;
   687     }
   689     RootedObject arrayTypeGlobal(cx, &args.callee());
   691     // Expect one argument which is a sized type object
   692     if (args.length() < 1) {
   693         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
   694                              "ArrayType", "0", "");
   695         return false;
   696     }
   698     if (!args[0].isObject() || !args[0].toObject().is<SizedTypeDescr>()) {
   699         ReportCannotConvertTo(cx, args[0], "ArrayType element specifier");
   700         return false;
   701     }
   703     Rooted<SizedTypeDescr*> elementType(cx);
   704     elementType = &args[0].toObject().as<SizedTypeDescr>();
   706     // Construct a canonical string `new ArrayType(<elementType>)`:
   707     StringBuffer contents(cx);
   708     contents.append("new ArrayType(");
   709     contents.append(&elementType->stringRepr());
   710     contents.append(")");
   711     RootedAtom stringRepr(cx, contents.finishAtom());
   712     if (!stringRepr)
   713         return nullptr;
   715     // Extract ArrayType.prototype
   716     RootedObject arrayTypePrototype(cx, GetPrototype(cx, arrayTypeGlobal));
   717     if (!arrayTypePrototype)
   718         return nullptr;
   720     // Create the instance of ArrayType
   721     Rooted<UnsizedArrayTypeDescr *> obj(cx);
   722     obj = create<UnsizedArrayTypeDescr>(cx, arrayTypePrototype, elementType,
   723                                         stringRepr, 0);
   724     if (!obj)
   725         return false;
   727     // Add `length` property, which is undefined for an unsized array.
   728     if (!JSObject::defineProperty(cx, obj, cx->names().length,
   729                                   UndefinedHandleValue, nullptr, nullptr,
   730                                   JSPROP_READONLY | JSPROP_PERMANENT))
   731         return nullptr;
   733     args.rval().setObject(*obj);
   734     return true;
   735 }
   737 /*static*/ bool
   738 UnsizedArrayTypeDescr::dimension(JSContext *cx, unsigned int argc, jsval *vp)
   739 {
   740     // Expect that the `this` pointer is an unsized array type
   741     // and the first argument is an integer size.
   742     CallArgs args = CallArgsFromVp(argc, vp);
   743     if (args.length() != 1 ||
   744         !args.thisv().isObject() ||
   745         !args.thisv().toObject().is<UnsizedArrayTypeDescr>() ||
   746         !args[0].isInt32() ||
   747         args[0].toInt32() < 0)
   748     {
   749         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   750                              JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
   751         return false;
   752     }
   754     // Extract arguments.
   755     Rooted<UnsizedArrayTypeDescr*> unsizedTypeDescr(cx);
   756     unsizedTypeDescr = &args.thisv().toObject().as<UnsizedArrayTypeDescr>();
   757     int32_t length = args[0].toInt32();
   758     Rooted<SizedTypeDescr*> elementType(cx, &unsizedTypeDescr->elementType());
   760     // Compute the size.
   761     CheckedInt32 size = CheckedInt32(elementType->size()) * length;
   762     if (!size.isValid()) {
   763         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   764                              JSMSG_TYPEDOBJECT_TOO_BIG);
   765         return nullptr;
   766     }
   768     // Construct a canonical string `new ArrayType(<elementType>).dimension(N)`:
   769     StringBuffer contents(cx);
   770     contents.append("new ArrayType(");
   771     contents.append(&elementType->stringRepr());
   772     contents.append(").dimension(");
   773     if (!NumberValueToStringBuffer(cx, NumberValue(length), contents))
   774         return false;
   775     contents.append(")");
   776     RootedAtom stringRepr(cx, contents.finishAtom());
   777     if (!stringRepr)
   778         return nullptr;
   780     // Create the sized type object.
   781     Rooted<SizedArrayTypeDescr*> obj(cx);
   782     obj = ArrayMetaTypeDescr::create<SizedArrayTypeDescr>(cx, unsizedTypeDescr,
   783                                                           elementType,
   784                                                           stringRepr, size.value());
   785     if (!obj)
   786         return false;
   788     obj->initReservedSlot(JS_DESCR_SLOT_SIZED_ARRAY_LENGTH,
   789                           Int32Value(length));
   791     // Add `length` property.
   792     RootedValue lengthVal(cx, Int32Value(length));
   793     if (!JSObject::defineProperty(cx, obj, cx->names().length,
   794                                   lengthVal, nullptr, nullptr,
   795                                   JSPROP_READONLY | JSPROP_PERMANENT))
   796         return nullptr;
   798     // Add `unsized` property, which is a link from the sized
   799     // array to the unsized array.
   800     RootedValue unsizedTypeDescrValue(cx, ObjectValue(*unsizedTypeDescr));
   801     if (!JSObject::defineProperty(cx, obj, cx->names().unsized,
   802                                   unsizedTypeDescrValue, nullptr, nullptr,
   803                                   JSPROP_READONLY | JSPROP_PERMANENT))
   804         return nullptr;
   806     args.rval().setObject(*obj);
   807     return true;
   808 }
   810 bool
   811 js::IsTypedObjectArray(JSObject &obj)
   812 {
   813     if (!obj.is<TypedObject>())
   814         return false;
   815     TypeDescr& d = obj.as<TypedObject>().typeDescr();
   816     return d.is<SizedArrayTypeDescr>() || d.is<UnsizedArrayTypeDescr>();
   817 }
   819 /*********************************
   820  * StructType class
   821  */
   823 const Class StructTypeDescr::class_ = {
   824     "StructType",
   825     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) |
   826     JSCLASS_HAS_PRIVATE, // used to store FieldList
   827     JS_PropertyStub,
   828     JS_DeletePropertyStub,
   829     JS_PropertyStub,
   830     JS_StrictPropertyStub,
   831     JS_EnumerateStub,
   832     JS_ResolveStub,
   833     JS_ConvertStub,
   834     nullptr, /* finalize */
   835     nullptr, /* call */
   836     nullptr, /* hasInstance */
   837     TypedObject::constructSized,
   838     nullptr  /* trace */
   839 };
   841 const JSPropertySpec StructMetaTypeDescr::typeObjectProperties[] = {
   842     JS_PS_END
   843 };
   845 const JSFunctionSpec StructMetaTypeDescr::typeObjectMethods[] = {
   846     {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
   847     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
   848     {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
   849     JS_FS_END
   850 };
   852 const JSPropertySpec StructMetaTypeDescr::typedObjectProperties[] = {
   853     JS_PS_END
   854 };
   856 const JSFunctionSpec StructMetaTypeDescr::typedObjectMethods[] = {
   857     JS_FS_END
   858 };
   860 JSObject *
   861 StructMetaTypeDescr::create(JSContext *cx,
   862                             HandleObject metaTypeDescr,
   863                             HandleObject fields)
   864 {
   865     // Obtain names of fields, which are the own properties of `fields`
   866     AutoIdVector ids(cx);
   867     if (!GetPropertyNames(cx, fields, JSITER_OWNONLY, &ids))
   868         return nullptr;
   870     // Iterate through each field. Collect values for the various
   871     // vectors below and also track total size and alignment. Be wary
   872     // of overflow!
   873     StringBuffer stringBuffer(cx);     // Canonical string repr
   874     AutoValueVector fieldNames(cx);    // Name of each field.
   875     AutoValueVector fieldTypeObjs(cx); // Type descriptor of each field.
   876     AutoValueVector fieldOffsets(cx);  // Offset of each field field.
   877     RootedObject userFieldOffsets(cx); // User-exposed {f:offset} object
   878     RootedObject userFieldTypes(cx);   // User-exposed {f:descr} object.
   879     CheckedInt32 sizeSoFar(0);         // Size of struct thus far.
   880     int32_t alignment = 1;             // Alignment of struct.
   881     bool opaque = false;               // Opacity of struct.
   883     userFieldOffsets = NewObjectWithProto<JSObject>(cx, nullptr, nullptr, TenuredObject);
   884     if (!userFieldOffsets)
   885         return nullptr;
   887     userFieldTypes = NewObjectWithProto<JSObject>(cx, nullptr, nullptr, TenuredObject);
   888     if (!userFieldTypes)
   889         return nullptr;
   891     if (!stringBuffer.append("new StructType({")) {
   892         js_ReportOutOfMemory(cx);
   893         return nullptr;
   894     }
   896     RootedValue fieldTypeVal(cx);
   897     RootedId id(cx);
   898     Rooted<SizedTypeDescr*> fieldType(cx);
   899     for (unsigned int i = 0; i < ids.length(); i++) {
   900         id = ids[i];
   902         // Check that all the property names are non-numeric strings.
   903         uint32_t unused;
   904         if (!JSID_IS_ATOM(id) || JSID_TO_ATOM(id)->isIndex(&unused)) {
   905             RootedValue idValue(cx, IdToValue(id));
   906             ReportCannotConvertTo(cx, idValue, "StructType field name");
   907             return nullptr;
   908         }
   910         // Load the value for the current field from the `fields` object.
   911         // The value should be a type descriptor.
   912         if (!JSObject::getGeneric(cx, fields, fields, id, &fieldTypeVal))
   913             return nullptr;
   914         fieldType = ToObjectIf<SizedTypeDescr>(fieldTypeVal);
   915         if (!fieldType) {
   916             ReportCannotConvertTo(cx, fieldTypeVal, "StructType field specifier");
   917             return nullptr;
   918         }
   920         // Collect field name and type object
   921         RootedValue fieldName(cx, IdToValue(id));
   922         if (!fieldNames.append(fieldName)) {
   923             js_ReportOutOfMemory(cx);
   924             return nullptr;
   925         }
   926         if (!fieldTypeObjs.append(ObjectValue(*fieldType))) {
   927             js_ReportOutOfMemory(cx);
   928             return nullptr;
   929         }
   931         // userFieldTypes[id] = typeObj
   932         if (!JSObject::defineGeneric(cx, userFieldTypes, id,
   933                                      fieldTypeObjs.handleAt(i), nullptr, nullptr,
   934                                      JSPROP_READONLY | JSPROP_PERMANENT))
   935             return nullptr;
   937         // Append "f:Type" to the string repr
   938         if (i > 0 && !stringBuffer.append(", ")) {
   939             js_ReportOutOfMemory(cx);
   940             return nullptr;
   941         }
   942         if (!stringBuffer.append(JSID_TO_ATOM(id))) {
   943             js_ReportOutOfMemory(cx);
   944             return nullptr;
   945         }
   946         if (!stringBuffer.append(": ")) {
   947             js_ReportOutOfMemory(cx);
   948             return nullptr;
   949         }
   950         if (!stringBuffer.append(&fieldType->stringRepr())) {
   951             js_ReportOutOfMemory(cx);
   952             return nullptr;
   953         }
   955         // Offset of this field is the current total size adjusted for
   956         // the field's alignment.
   957         CheckedInt32 offset = roundUpToAlignment(sizeSoFar, fieldType->alignment());
   958         if (!offset.isValid()) {
   959             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   960                                  JSMSG_TYPEDOBJECT_TOO_BIG);
   961             return nullptr;
   962         }
   963         if (!fieldOffsets.append(Int32Value(offset.value()))) {
   964             js_ReportOutOfMemory(cx);
   965             return nullptr;
   966         }
   968         // userFieldOffsets[id] = offset
   969         RootedValue offsetValue(cx, Int32Value(offset.value()));
   970         if (!JSObject::defineGeneric(cx, userFieldOffsets, id,
   971                                      offsetValue, nullptr, nullptr,
   972                                      JSPROP_READONLY | JSPROP_PERMANENT))
   973             return nullptr;
   975         // Add space for this field to the total struct size.
   976         sizeSoFar = offset + fieldType->size();
   977         if (!sizeSoFar.isValid()) {
   978             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   979                                  JSMSG_TYPEDOBJECT_TOO_BIG);
   980             return nullptr;
   981         }
   983         // Struct is opaque if any field is opaque
   984         if (fieldType->opaque())
   985             opaque = true;
   987         // Alignment of the struct is the max of the alignment of its fields.
   988         alignment = js::Max(alignment, fieldType->alignment());
   989     }
   991     // Complete string representation.
   992     if (!stringBuffer.append("})")) {
   993         js_ReportOutOfMemory(cx);
   994         return nullptr;
   995     }
   996     RootedAtom stringRepr(cx, stringBuffer.finishAtom());
   997     if (!stringRepr)
   998         return nullptr;
  1000     // Adjust the total size to be a multiple of the final alignment.
  1001     CheckedInt32 totalSize = roundUpToAlignment(sizeSoFar, alignment);
  1002     if (!totalSize.isValid()) {
  1003         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
  1004                              JSMSG_TYPEDOBJECT_TOO_BIG);
  1005         return nullptr;
  1008     // Now create the resulting type descriptor.
  1009     RootedObject structTypePrototype(cx, GetPrototype(cx, metaTypeDescr));
  1010     if (!structTypePrototype)
  1011         return nullptr;
  1013     Rooted<StructTypeDescr*> descr(cx);
  1014     descr = NewObjectWithProto<StructTypeDescr>(cx, structTypePrototype, nullptr,
  1015                                                 TenuredObject);
  1016     if (!descr)
  1017         return nullptr;
  1019     descr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(TypeDescr::Struct));
  1020     descr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
  1021     descr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(alignment));
  1022     descr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(totalSize.value()));
  1023     descr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(opaque));
  1025     // Construct for internal use an array with the name for each field.
  1027         RootedObject fieldNamesVec(cx);
  1028         fieldNamesVec = NewDenseCopiedArray(cx, fieldNames.length(),
  1029                                             fieldNames.begin(), nullptr,
  1030                                             TenuredObject);
  1031         if (!fieldNamesVec)
  1032             return nullptr;
  1033         descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES,
  1034                                      ObjectValue(*fieldNamesVec));
  1037     // Construct for internal use an array with the type object for each field.
  1039         RootedObject fieldTypeVec(cx);
  1040         fieldTypeVec = NewDenseCopiedArray(cx, fieldTypeObjs.length(),
  1041                                            fieldTypeObjs.begin(), nullptr,
  1042                                            TenuredObject);
  1043         if (!fieldTypeVec)
  1044             return nullptr;
  1045         descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_TYPES,
  1046                                      ObjectValue(*fieldTypeVec));
  1049     // Construct for internal use an array with the offset for each field.
  1051         RootedObject fieldOffsetsVec(cx);
  1052         fieldOffsetsVec = NewDenseCopiedArray(cx, fieldOffsets.length(),
  1053                                               fieldOffsets.begin(), nullptr,
  1054                                               TenuredObject);
  1055         if (!fieldOffsetsVec)
  1056             return nullptr;
  1057         descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS,
  1058                                      ObjectValue(*fieldOffsetsVec));
  1061     // Create data properties fieldOffsets and fieldTypes
  1062     if (!JSObject::freeze(cx, userFieldOffsets))
  1063         return nullptr;
  1064     if (!JSObject::freeze(cx, userFieldTypes))
  1065         return nullptr;
  1066     RootedValue userFieldOffsetsValue(cx, ObjectValue(*userFieldOffsets));
  1067     if (!JSObject::defineProperty(cx, descr, cx->names().fieldOffsets,
  1068                                   userFieldOffsetsValue, nullptr, nullptr,
  1069                                   JSPROP_READONLY | JSPROP_PERMANENT))
  1071         return nullptr;
  1073     RootedValue userFieldTypesValue(cx, ObjectValue(*userFieldTypes));
  1074     if (!JSObject::defineProperty(cx, descr, cx->names().fieldTypes,
  1075                                   userFieldTypesValue, nullptr, nullptr,
  1076                                   JSPROP_READONLY | JSPROP_PERMANENT))
  1078         return nullptr;
  1081     if (!CreateUserSizeAndAlignmentProperties(cx, descr))
  1082         return nullptr;
  1084     Rooted<TypedProto*> prototypeObj(cx);
  1085     prototypeObj = CreatePrototypeObjectForComplexTypeInstance(cx, descr, structTypePrototype);
  1086     if (!prototypeObj)
  1087         return nullptr;
  1089     descr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*prototypeObj));
  1091     if (!LinkConstructorAndPrototype(cx, descr, prototypeObj))
  1092         return nullptr;
  1094     return descr;
  1097 bool
  1098 StructMetaTypeDescr::construct(JSContext *cx, unsigned int argc, Value *vp)
  1100     CallArgs args = CallArgsFromVp(argc, vp);
  1102     if (!args.isConstructing()) {
  1103         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
  1104                              JSMSG_NOT_FUNCTION, "StructType");
  1105         return false;
  1108     if (args.length() >= 1 && args[0].isObject()) {
  1109         RootedObject metaTypeDescr(cx, &args.callee());
  1110         RootedObject fields(cx, &args[0].toObject());
  1111         RootedObject obj(cx, create(cx, metaTypeDescr, fields));
  1112         if (!obj)
  1113             return false;
  1114         args.rval().setObject(*obj);
  1115         return true;
  1118     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
  1119                          JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD_ARGS);
  1120     return false;
  1123 size_t
  1124 StructTypeDescr::fieldCount()
  1126     return getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).toObject().getDenseInitializedLength();
  1129 bool
  1130 StructTypeDescr::fieldIndex(jsid id, size_t *out)
  1132     JSObject &fieldNames = getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).toObject();
  1133     size_t l = fieldNames.getDenseInitializedLength();
  1134     for (size_t i = 0; i < l; i++) {
  1135         JSAtom &a = fieldNames.getDenseElement(i).toString()->asAtom();
  1136         if (JSID_IS_ATOM(id, &a)) {
  1137             *out = i;
  1138             return true;
  1141     return false;
  1144 JSAtom &
  1145 StructTypeDescr::fieldName(size_t index)
  1147     JSObject &fieldNames = getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).toObject();
  1148     return fieldNames.getDenseElement(index).toString()->asAtom();
  1151 int32_t
  1152 StructTypeDescr::fieldOffset(size_t index)
  1154     JSObject &fieldOffsets =
  1155         getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS).toObject();
  1156     JS_ASSERT(index < fieldOffsets.getDenseInitializedLength());
  1157     return fieldOffsets.getDenseElement(index).toInt32();
  1160 SizedTypeDescr&
  1161 StructTypeDescr::fieldDescr(size_t index)
  1163     JSObject &fieldDescrs =
  1164         getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_TYPES).toObject();
  1165     JS_ASSERT(index < fieldDescrs.getDenseInitializedLength());
  1166     return fieldDescrs.getDenseElement(index).toObject().as<SizedTypeDescr>();
  1169 /******************************************************************************
  1170  * Creating the TypedObject "module"
  1172  * We create one global, `TypedObject`, which contains the following
  1173  * members:
  1175  * 1. uint8, uint16, etc
  1176  * 2. ArrayType
  1177  * 3. StructType
  1179  * Each of these is a function and hence their prototype is
  1180  * `Function.__proto__` (in terms of the JS Engine, they are not
  1181  * JSFunctions but rather instances of their own respective JSClasses
  1182  * which override the call and construct operations).
  1184  * Each type object also has its own `prototype` field. Therefore,
  1185  * using `StructType` as an example, the basic setup is:
  1187  *   StructType --__proto__--> Function.__proto__
  1188  *        |
  1189  *    prototype -- prototype --> { }
  1190  *        |
  1191  *        v
  1192  *       { } -----__proto__--> Function.__proto__
  1194  * When a new type object (e.g., an instance of StructType) is created,
  1195  * it will look as follows:
  1197  *   MyStruct -__proto__-> StructType.prototype -__proto__-> Function.__proto__
  1198  *        |                          |
  1199  *        |                     prototype
  1200  *        |                          |
  1201  *        |                          v
  1202  *    prototype -----__proto__----> { }
  1203  *        |
  1204  *        v
  1205  *       { } --__proto__-> Object.prototype
  1207  * Finally, when an instance of `MyStruct` is created, its
  1208  * structure is as follows:
  1210  *    object -__proto__->
  1211  *      MyStruct.prototype -__proto__->
  1212  *        StructType.prototype.prototype -__proto__->
  1213  *          Object.prototype
  1214  */
  1216 // Here `T` is either `ScalarTypeDescr` or `ReferenceTypeDescr`
  1217 template<typename T>
  1218 static bool
  1219 DefineSimpleTypeDescr(JSContext *cx,
  1220                       Handle<GlobalObject *> global,
  1221                       HandleObject module,
  1222                       typename T::Type type,
  1223                       HandlePropertyName className)
  1225     RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
  1226     if (!objProto)
  1227         return false;
  1229     RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx));
  1230     if (!funcProto)
  1231         return false;
  1233     Rooted<T*> descr(cx);
  1234     descr = NewObjectWithProto<T>(cx, funcProto, global, TenuredObject);
  1235     if (!descr)
  1236         return false;
  1238     descr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(T::Kind));
  1239     descr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(className));
  1240     descr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(T::alignment(type)));
  1241     descr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(T::size(type)));
  1242     descr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(T::Opaque));
  1243     descr->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(type));
  1245     if (!CreateUserSizeAndAlignmentProperties(cx, descr))
  1246         return false;
  1248     if (!JS_DefineFunctions(cx, descr, T::typeObjectMethods))
  1249         return false;
  1251     // Create the typed prototype for the scalar type. This winds up
  1252     // not being user accessible, but we still create one for consistency.
  1253     Rooted<TypedProto*> proto(cx);
  1254     proto = NewObjectWithProto<TypedProto>(cx, objProto, nullptr, TenuredObject);
  1255     if (!proto)
  1256         return nullptr;
  1257     proto->initTypeDescrSlot(*descr);
  1258     descr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*proto));
  1260     RootedValue descrValue(cx, ObjectValue(*descr));
  1261     if (!JSObject::defineProperty(cx, module, className,
  1262                                   descrValue, nullptr, nullptr, 0))
  1264         return false;
  1267     return true;
  1270 ///////////////////////////////////////////////////////////////////////////
  1272 template<typename T>
  1273 static JSObject *
  1274 DefineMetaTypeDescr(JSContext *cx,
  1275                     Handle<GlobalObject*> global,
  1276                     HandleObject module,
  1277                     TypedObjectModuleObject::Slot protoSlot)
  1279     RootedAtom className(cx, Atomize(cx, T::class_.name,
  1280                                      strlen(T::class_.name)));
  1281     if (!className)
  1282         return nullptr;
  1284     RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx));
  1285     if (!funcProto)
  1286         return nullptr;
  1288     // Create ctor.prototype, which inherits from Function.__proto__
  1290     RootedObject proto(cx, NewObjectWithProto<JSObject>(cx, funcProto, global,
  1291                                                         SingletonObject));
  1292     if (!proto)
  1293         return nullptr;
  1295     // Create ctor.prototype.prototype, which inherits from Object.__proto__
  1297     RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
  1298     if (!objProto)
  1299         return nullptr;
  1300     RootedObject protoProto(cx);
  1301     protoProto = NewObjectWithProto<JSObject>(cx, objProto,
  1302                                               global, SingletonObject);
  1303     if (!protoProto)
  1304         return nullptr;
  1306     RootedValue protoProtoValue(cx, ObjectValue(*protoProto));
  1307     if (!JSObject::defineProperty(cx, proto, cx->names().prototype,
  1308                                   protoProtoValue,
  1309                                   nullptr, nullptr,
  1310                                   JSPROP_READONLY | JSPROP_PERMANENT))
  1311         return nullptr;
  1313     // Create ctor itself
  1315     const int constructorLength = 2;
  1316     RootedFunction ctor(cx);
  1317     ctor = global->createConstructor(cx, T::construct, className, constructorLength);
  1318     if (!ctor ||
  1319         !LinkConstructorAndPrototype(cx, ctor, proto) ||
  1320         !DefinePropertiesAndBrand(cx, proto,
  1321                                   T::typeObjectProperties,
  1322                                   T::typeObjectMethods) ||
  1323         !DefinePropertiesAndBrand(cx, protoProto,
  1324                                   T::typedObjectProperties,
  1325                                   T::typedObjectMethods))
  1327         return nullptr;
  1330     module->initReservedSlot(protoSlot, ObjectValue(*proto));
  1332     return ctor;
  1335 /*  The initialization strategy for TypedObjects is mildly unusual
  1336  * compared to other classes. Because all of the types are members
  1337  * of a single global, `TypedObject`, we basically make the
  1338  * initializer for the `TypedObject` class populate the
  1339  * `TypedObject` global (which is referred to as "module" herein).
  1340  */
  1341 bool
  1342 GlobalObject::initTypedObjectModule(JSContext *cx, Handle<GlobalObject*> global)
  1344     RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
  1345     if (!objProto)
  1346         return false;
  1348     Rooted<TypedObjectModuleObject*> module(cx);
  1349     module = NewObjectWithProto<TypedObjectModuleObject>(cx, objProto, global);
  1350     if (!module)
  1351         return false;
  1353     if (!JS_DefineFunctions(cx, module, TypedObjectMethods))
  1354         return false;
  1356     // uint8, uint16, any, etc
  1358 #define BINARYDATA_SCALAR_DEFINE(constant_, type_, name_)                       \
  1359     if (!DefineSimpleTypeDescr<ScalarTypeDescr>(cx, global, module, constant_,      \
  1360                                             cx->names().name_))                 \
  1361         return nullptr;
  1362     JS_FOR_EACH_SCALAR_TYPE_REPR(BINARYDATA_SCALAR_DEFINE)
  1363 #undef BINARYDATA_SCALAR_DEFINE
  1365 #define BINARYDATA_REFERENCE_DEFINE(constant_, type_, name_)                    \
  1366     if (!DefineSimpleTypeDescr<ReferenceTypeDescr>(cx, global, module, constant_,   \
  1367                                                cx->names().name_))              \
  1368         return nullptr;
  1369     JS_FOR_EACH_REFERENCE_TYPE_REPR(BINARYDATA_REFERENCE_DEFINE)
  1370 #undef BINARYDATA_REFERENCE_DEFINE
  1372     // ArrayType.
  1374     RootedObject arrayType(cx);
  1375     arrayType = DefineMetaTypeDescr<ArrayMetaTypeDescr>(
  1376         cx, global, module, TypedObjectModuleObject::ArrayTypePrototype);
  1377     if (!arrayType)
  1378         return nullptr;
  1380     RootedValue arrayTypeValue(cx, ObjectValue(*arrayType));
  1381     if (!JSObject::defineProperty(cx, module, cx->names().ArrayType,
  1382                                   arrayTypeValue,
  1383                                   nullptr, nullptr,
  1384                                   JSPROP_READONLY | JSPROP_PERMANENT))
  1385         return nullptr;
  1387     // StructType.
  1389     RootedObject structType(cx);
  1390     structType = DefineMetaTypeDescr<StructMetaTypeDescr>(
  1391         cx, global, module, TypedObjectModuleObject::StructTypePrototype);
  1392     if (!structType)
  1393         return nullptr;
  1395     RootedValue structTypeValue(cx, ObjectValue(*structType));
  1396     if (!JSObject::defineProperty(cx, module, cx->names().StructType,
  1397                                   structTypeValue,
  1398                                   nullptr, nullptr,
  1399                                   JSPROP_READONLY | JSPROP_PERMANENT))
  1400         return nullptr;
  1402     // Everything is setup, install module on the global object:
  1403     RootedValue moduleValue(cx, ObjectValue(*module));
  1404     global->setConstructor(JSProto_TypedObject, moduleValue);
  1405     if (!JSObject::defineProperty(cx, global, cx->names().TypedObject,
  1406                                   moduleValue,
  1407                                   nullptr, nullptr,
  1408                                   0))
  1410         return nullptr;
  1413     return module;
  1416 JSObject *
  1417 js_InitTypedObjectModuleObject(JSContext *cx, HandleObject obj)
  1419     JS_ASSERT(obj->is<GlobalObject>());
  1420     Rooted<GlobalObject *> global(cx, &obj->as<GlobalObject>());
  1421     return global->getOrCreateTypedObjectModule(cx);
  1424 JSObject *
  1425 js_InitTypedObjectDummy(JSContext *cx, HandleObject obj)
  1427     /*
  1428      * This function is entered into the jsprototypes.h table
  1429      * as the initializer for `TypedObject`. It should not
  1430      * be executed via the `standard_class_atoms` mechanism.
  1431      */
  1433     MOZ_ASSUME_UNREACHABLE("shouldn't be initializing TypedObject via the JSProtoKey initializer mechanism");
  1436 /******************************************************************************
  1437  * Typed objects
  1438  */
  1440 /*static*/ TypedObject *
  1441 TypedObject::createUnattached(JSContext *cx,
  1442                              HandleTypeDescr descr,
  1443                              int32_t length)
  1445     if (descr->opaque())
  1446         return createUnattachedWithClass(cx, &OpaqueTypedObject::class_, descr, length);
  1447     else
  1448         return createUnattachedWithClass(cx, &TransparentTypedObject::class_, descr, length);
  1452 /*static*/ TypedObject *
  1453 TypedObject::createUnattachedWithClass(JSContext *cx,
  1454                                       const Class *clasp,
  1455                                       HandleTypeDescr type,
  1456                                       int32_t length)
  1458     JS_ASSERT(clasp == &TransparentTypedObject::class_ ||
  1459               clasp == &OpaqueTypedObject::class_);
  1460     JS_ASSERT(JSCLASS_RESERVED_SLOTS(clasp) == JS_TYPEDOBJ_SLOTS);
  1461     JS_ASSERT(clasp->hasPrivate());
  1463     RootedObject proto(cx);
  1464     if (type->is<SimpleTypeDescr>()) {
  1465         // FIXME Bug 929651 -- What prototype to use?
  1466         proto = type->global().getOrCreateObjectPrototype(cx);
  1467     } else {
  1468         RootedValue protoVal(cx);
  1469         if (!JSObject::getProperty(cx, type, type,
  1470                                    cx->names().prototype, &protoVal))
  1472             return nullptr;
  1474         proto = &protoVal.toObject();
  1477     RootedObject obj(cx, NewObjectWithClassProto(cx, clasp, &*proto, nullptr));
  1478     if (!obj)
  1479         return nullptr;
  1481     obj->setPrivate(nullptr);
  1482     obj->initReservedSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(0));
  1483     obj->initReservedSlot(JS_TYPEDOBJ_SLOT_BYTELENGTH, Int32Value(0));
  1484     obj->initReservedSlot(JS_TYPEDOBJ_SLOT_OWNER, NullValue());
  1485     obj->initReservedSlot(JS_TYPEDOBJ_SLOT_NEXT_VIEW, PrivateValue(nullptr));
  1486     obj->initReservedSlot(JS_TYPEDOBJ_SLOT_LENGTH, Int32Value(length));
  1487     obj->initReservedSlot(JS_TYPEDOBJ_SLOT_TYPE_DESCR, ObjectValue(*type));
  1489     // Tag the type object for this instance with the type
  1490     // representation, if that has not been done already.
  1491     if (!type->is<SimpleTypeDescr>()) { // FIXME Bug 929651
  1492         RootedTypeObject typeObj(cx, obj->getType(cx));
  1493         if (typeObj) {
  1494             if (!typeObj->addTypedObjectAddendum(cx, type))
  1495                 return nullptr;
  1499     return static_cast<TypedObject*>(&*obj);
  1502 void
  1503 TypedObject::attach(ArrayBufferObject &buffer, int32_t offset)
  1505     JS_ASSERT(offset >= 0);
  1506     JS_ASSERT((size_t) (offset + size()) <= buffer.byteLength());
  1508     buffer.addView(this);
  1509     InitArrayBufferViewDataPointer(this, &buffer, offset);
  1510     setReservedSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(offset));
  1511     setReservedSlot(JS_TYPEDOBJ_SLOT_OWNER, ObjectValue(buffer));
  1514 void
  1515 TypedObject::attach(TypedObject &typedObj, int32_t offset)
  1517     JS_ASSERT(!typedObj.owner().isNeutered());
  1518     JS_ASSERT(typedObj.typedMem() != NULL);
  1520     attach(typedObj.owner(), typedObj.offset() + offset);
  1523 // Returns a suitable JS_TYPEDOBJ_SLOT_LENGTH value for an instance of
  1524 // the type `type`. `type` must not be an unsized array.
  1525 static int32_t
  1526 TypedObjLengthFromType(TypeDescr &descr)
  1528     switch (descr.kind()) {
  1529       case TypeDescr::Scalar:
  1530       case TypeDescr::Reference:
  1531       case TypeDescr::Struct:
  1532       case TypeDescr::X4:
  1533         return 0;
  1535       case TypeDescr::SizedArray:
  1536         return descr.as<SizedArrayTypeDescr>().length();
  1538       case TypeDescr::UnsizedArray:
  1539         MOZ_ASSUME_UNREACHABLE("TypedObjLengthFromType() invoked on unsized type");
  1541     MOZ_ASSUME_UNREACHABLE("Invalid kind");
  1544 /*static*/ TypedObject *
  1545 TypedObject::createDerived(JSContext *cx, HandleSizedTypeDescr type,
  1546                            HandleTypedObject typedObj, int32_t offset)
  1548     JS_ASSERT(!typedObj->owner().isNeutered());
  1549     JS_ASSERT(typedObj->typedMem() != NULL);
  1550     JS_ASSERT(offset <= typedObj->size());
  1551     JS_ASSERT(offset + type->size() <= typedObj->size());
  1553     int32_t length = TypedObjLengthFromType(*type);
  1555     const js::Class *clasp = typedObj->getClass();
  1556     Rooted<TypedObject*> obj(cx);
  1557     obj = createUnattachedWithClass(cx, clasp, type, length);
  1558     if (!obj)
  1559         return nullptr;
  1561     obj->attach(*typedObj, offset);
  1562     return obj;
  1565 /*static*/ TypedObject *
  1566 TypedObject::createZeroed(JSContext *cx,
  1567                           HandleTypeDescr descr,
  1568                           int32_t length)
  1570     // Create unattached wrapper object.
  1571     Rooted<TypedObject*> obj(cx, createUnattached(cx, descr, length));
  1572     if (!obj)
  1573         return nullptr;
  1575     // Allocate and initialize the memory for this instance.
  1576     // Also initialize the JS_TYPEDOBJ_SLOT_LENGTH slot.
  1577     switch (descr->kind()) {
  1578       case TypeDescr::Scalar:
  1579       case TypeDescr::Reference:
  1580       case TypeDescr::Struct:
  1581       case TypeDescr::X4:
  1582       case TypeDescr::SizedArray:
  1584         size_t totalSize = descr->as<SizedTypeDescr>().size();
  1585         Rooted<ArrayBufferObject*> buffer(cx);
  1586         buffer = ArrayBufferObject::create(cx, totalSize);
  1587         if (!buffer)
  1588             return nullptr;
  1589         descr->as<SizedTypeDescr>().initInstances(cx->runtime(), buffer->dataPointer(), 1);
  1590         obj->attach(*buffer, 0);
  1591         return obj;
  1594       case TypeDescr::UnsizedArray:
  1596         Rooted<SizedTypeDescr*> elementTypeRepr(cx);
  1597         elementTypeRepr = &descr->as<UnsizedArrayTypeDescr>().elementType();
  1599         CheckedInt32 totalSize = CheckedInt32(elementTypeRepr->size()) * length;
  1600         if (!totalSize.isValid()) {
  1601             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
  1602                                  JSMSG_TYPEDOBJECT_TOO_BIG);
  1603             return nullptr;
  1606         Rooted<ArrayBufferObject*> buffer(cx);
  1607         buffer = ArrayBufferObject::create(cx, totalSize.value());
  1608         if (!buffer)
  1609             return nullptr;
  1611         if (length)
  1612             elementTypeRepr->initInstances(cx->runtime(), buffer->dataPointer(), length);
  1613         obj->attach(*buffer, 0);
  1614         return obj;
  1618     MOZ_ASSUME_UNREACHABLE("Bad TypeRepresentation Kind");
  1621 static bool
  1622 ReportTypedObjTypeError(JSContext *cx,
  1623                         const unsigned errorNumber,
  1624                         HandleTypedObject obj)
  1626     // Serialize type string of obj
  1627     char *typeReprStr = JS_EncodeString(cx, &obj->typeDescr().stringRepr());
  1628     if (!typeReprStr)
  1629         return false;
  1631     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
  1632                          errorNumber, typeReprStr);
  1634     JS_free(cx, (void *) typeReprStr);
  1635     return false;
  1638 /*static*/ void
  1639 TypedObject::obj_trace(JSTracer *trace, JSObject *object)
  1641     gc::MarkSlot(trace, &object->getReservedSlotRef(JS_TYPEDOBJ_SLOT_TYPE_DESCR),
  1642                  "TypedObjectTypeDescr");
  1644     ArrayBufferViewObject::trace(trace, object);
  1646     JS_ASSERT(object->is<TypedObject>());
  1647     TypedObject &typedObj = object->as<TypedObject>();
  1648     TypeDescr &descr = typedObj.typeDescr();
  1649     if (descr.opaque()) {
  1650         uint8_t *mem = typedObj.typedMem();
  1651         if (!mem)
  1652             return; // partially constructed
  1654         if (typedObj.owner().isNeutered())
  1655             return;
  1657         switch (descr.kind()) {
  1658           case TypeDescr::Scalar:
  1659           case TypeDescr::Reference:
  1660           case TypeDescr::Struct:
  1661           case TypeDescr::SizedArray:
  1662           case TypeDescr::X4:
  1663             descr.as<SizedTypeDescr>().traceInstances(trace, mem, 1);
  1664             break;
  1666           case TypeDescr::UnsizedArray:
  1667             descr.as<UnsizedArrayTypeDescr>().elementType().traceInstances(trace, mem, typedObj.length());
  1668             break;
  1673 bool
  1674 TypedObject::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
  1675                               MutableHandleObject objp, MutableHandleShape propp)
  1677     JS_ASSERT(obj->is<TypedObject>());
  1679     Rooted<TypeDescr*> descr(cx, &obj->as<TypedObject>().typeDescr());
  1680     switch (descr->kind()) {
  1681       case TypeDescr::Scalar:
  1682       case TypeDescr::Reference:
  1683       case TypeDescr::X4:
  1684         break;
  1686       case TypeDescr::SizedArray:
  1687       case TypeDescr::UnsizedArray:
  1689         uint32_t index;
  1690         if (js_IdIsIndex(id, &index))
  1691             return obj_lookupElement(cx, obj, index, objp, propp);
  1693         if (JSID_IS_ATOM(id, cx->names().length)) {
  1694             MarkNonNativePropertyFound(propp);
  1695             objp.set(obj);
  1696             return true;
  1698         break;
  1701       case TypeDescr::Struct:
  1703         StructTypeDescr &structDescr = descr->as<StructTypeDescr>();
  1704         size_t index;
  1705         if (structDescr.fieldIndex(id, &index)) {
  1706             MarkNonNativePropertyFound(propp);
  1707             objp.set(obj);
  1708             return true;
  1710         break;
  1714     RootedObject proto(cx, obj->getProto());
  1715     if (!proto) {
  1716         objp.set(nullptr);
  1717         propp.set(nullptr);
  1718         return true;
  1721     return JSObject::lookupGeneric(cx, proto, id, objp, propp);
  1724 bool
  1725 TypedObject::obj_lookupProperty(JSContext *cx,
  1726                                 HandleObject obj,
  1727                                 HandlePropertyName name,
  1728                                 MutableHandleObject objp,
  1729                                 MutableHandleShape propp)
  1731     RootedId id(cx, NameToId(name));
  1732     return obj_lookupGeneric(cx, obj, id, objp, propp);
  1735 bool
  1736 TypedObject::obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
  1737                                 MutableHandleObject objp, MutableHandleShape propp)
  1739     JS_ASSERT(obj->is<TypedObject>());
  1740     MarkNonNativePropertyFound(propp);
  1741     objp.set(obj);
  1742     return true;
  1745 static bool
  1746 ReportPropertyError(JSContext *cx,
  1747                     const unsigned errorNumber,
  1748                     HandleId id)
  1750     RootedString str(cx, IdToString(cx, id));
  1751     if (!str)
  1752         return false;
  1754     char *propName = JS_EncodeString(cx, str);
  1755     if (!propName)
  1756         return false;
  1758     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
  1759                          errorNumber, propName);
  1761     JS_free(cx, propName);
  1762     return false;
  1765 bool
  1766 TypedObject::obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
  1767                               PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
  1769     return ReportPropertyError(cx, JSMSG_UNDEFINED_PROP, id);
  1772 bool
  1773 TypedObject::obj_defineProperty(JSContext *cx, HandleObject obj,
  1774                                HandlePropertyName name, HandleValue v,
  1775                                PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
  1777     Rooted<jsid> id(cx, NameToId(name));
  1778     return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs);
  1781 bool
  1782 TypedObject::obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v,
  1783                                PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
  1785     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
  1786     Rooted<jsid> id(cx);
  1787     if (!IndexToId(cx, index, &id))
  1788         return false;
  1789     return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs);
  1792 bool
  1793 TypedObject::obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
  1794                            HandleId id, MutableHandleValue vp)
  1796     JS_ASSERT(obj->is<TypedObject>());
  1797     Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
  1799     // Dispatch elements to obj_getElement:
  1800     uint32_t index;
  1801     if (js_IdIsIndex(id, &index))
  1802         return obj_getElement(cx, obj, receiver, index, vp);
  1804     // Handle everything else here:
  1806     switch (typedObj->typeDescr().kind()) {
  1807       case TypeDescr::Scalar:
  1808       case TypeDescr::Reference:
  1809         break;
  1811       case TypeDescr::X4:
  1812         break;
  1814       case TypeDescr::SizedArray:
  1815       case TypeDescr::UnsizedArray:
  1816         if (JSID_IS_ATOM(id, cx->names().length)) {
  1817             if (typedObj->owner().isNeutered() || !typedObj->typedMem()) { // unattached
  1818                 JS_ReportErrorNumber(
  1819                     cx, js_GetErrorMessage,
  1820                     nullptr, JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
  1821                 return false;
  1824             vp.setInt32(typedObj->length());
  1825             return true;
  1827         break;
  1829       case TypeDescr::Struct: {
  1830         Rooted<StructTypeDescr*> descr(cx, &typedObj->typeDescr().as<StructTypeDescr>());
  1832         size_t fieldIndex;
  1833         if (!descr->fieldIndex(id, &fieldIndex))
  1834             break;
  1836         size_t offset = descr->fieldOffset(fieldIndex);
  1837         Rooted<SizedTypeDescr*> fieldType(cx, &descr->fieldDescr(fieldIndex));
  1838         return Reify(cx, fieldType, typedObj, offset, vp);
  1842     RootedObject proto(cx, obj->getProto());
  1843     if (!proto) {
  1844         vp.setUndefined();
  1845         return true;
  1848     return JSObject::getGeneric(cx, proto, receiver, id, vp);
  1851 bool
  1852 TypedObject::obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
  1853                               HandlePropertyName name, MutableHandleValue vp)
  1855     RootedId id(cx, NameToId(name));
  1856     return obj_getGeneric(cx, obj, receiver, id, vp);
  1859 bool
  1860 TypedObject::obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver,
  1861                              uint32_t index, MutableHandleValue vp)
  1863     JS_ASSERT(obj->is<TypedObject>());
  1864     Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
  1865     Rooted<TypeDescr *> descr(cx, &typedObj->typeDescr());
  1867     switch (descr->kind()) {
  1868       case TypeDescr::Scalar:
  1869       case TypeDescr::Reference:
  1870       case TypeDescr::X4:
  1871       case TypeDescr::Struct:
  1872         break;
  1874       case TypeDescr::SizedArray:
  1875         return obj_getArrayElement<SizedArrayTypeDescr>(cx, typedObj, descr,
  1876                                                         index, vp);
  1878       case TypeDescr::UnsizedArray:
  1879         return obj_getArrayElement<UnsizedArrayTypeDescr>(cx, typedObj, descr,
  1880                                                           index, vp);
  1883     RootedObject proto(cx, obj->getProto());
  1884     if (!proto) {
  1885         vp.setUndefined();
  1886         return true;
  1889     return JSObject::getElement(cx, proto, receiver, index, vp);
  1892 template<class T>
  1893 /*static*/ bool
  1894 TypedObject::obj_getArrayElement(JSContext *cx,
  1895                                 Handle<TypedObject*> typedObj,
  1896                                 Handle<TypeDescr*> typeDescr,
  1897                                 uint32_t index,
  1898                                 MutableHandleValue vp)
  1900     JS_ASSERT(typeDescr->is<T>());
  1902     if (index >= (size_t) typedObj->length()) {
  1903         vp.setUndefined();
  1904         return true;
  1907     Rooted<SizedTypeDescr*> elementType(cx, &typeDescr->as<T>().elementType());
  1908     size_t offset = elementType->size() * index;
  1909     return Reify(cx, elementType, typedObj, offset, vp);
  1912 bool
  1913 TypedObject::obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
  1914                            MutableHandleValue vp, bool strict)
  1916     JS_ASSERT(obj->is<TypedObject>());
  1917     Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
  1919     uint32_t index;
  1920     if (js_IdIsIndex(id, &index))
  1921         return obj_setElement(cx, obj, index, vp, strict);
  1923     switch (typedObj->typeDescr().kind()) {
  1924       case ScalarTypeDescr::Scalar:
  1925       case TypeDescr::Reference:
  1926         break;
  1928       case ScalarTypeDescr::X4:
  1929         break;
  1931       case ScalarTypeDescr::SizedArray:
  1932       case ScalarTypeDescr::UnsizedArray:
  1933         if (JSID_IS_ATOM(id, cx->names().length)) {
  1934             JS_ReportErrorNumber(cx, js_GetErrorMessage,
  1935                                  nullptr, JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
  1936             return false;
  1938         break;
  1940       case ScalarTypeDescr::Struct: {
  1941         Rooted<StructTypeDescr*> descr(cx, &typedObj->typeDescr().as<StructTypeDescr>());
  1943         size_t fieldIndex;
  1944         if (!descr->fieldIndex(id, &fieldIndex))
  1945             break;
  1947         size_t offset = descr->fieldOffset(fieldIndex);
  1948         Rooted<SizedTypeDescr*> fieldType(cx, &descr->fieldDescr(fieldIndex));
  1949         return ConvertAndCopyTo(cx, fieldType, typedObj, offset, vp);
  1953     return ReportTypedObjTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, typedObj);
  1956 bool
  1957 TypedObject::obj_setProperty(JSContext *cx, HandleObject obj,
  1958                              HandlePropertyName name, MutableHandleValue vp,
  1959                              bool strict)
  1961     RootedId id(cx, NameToId(name));
  1962     return obj_setGeneric(cx, obj, id, vp, strict);
  1965 bool
  1966 TypedObject::obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
  1967                            MutableHandleValue vp, bool strict)
  1969     JS_ASSERT(obj->is<TypedObject>());
  1970     Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
  1971     Rooted<TypeDescr *> descr(cx, &typedObj->typeDescr());
  1973     switch (descr->kind()) {
  1974       case TypeDescr::Scalar:
  1975       case TypeDescr::Reference:
  1976       case TypeDescr::X4:
  1977       case TypeDescr::Struct:
  1978         break;
  1980       case TypeDescr::SizedArray:
  1981         return obj_setArrayElement<SizedArrayTypeDescr>(cx, typedObj, descr, index, vp);
  1983       case TypeDescr::UnsizedArray:
  1984         return obj_setArrayElement<UnsizedArrayTypeDescr>(cx, typedObj, descr, index, vp);
  1987     return ReportTypedObjTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, typedObj);
  1990 template<class T>
  1991 /*static*/ bool
  1992 TypedObject::obj_setArrayElement(JSContext *cx,
  1993                                 Handle<TypedObject*> typedObj,
  1994                                 Handle<TypeDescr*> descr,
  1995                                 uint32_t index,
  1996                                 MutableHandleValue vp)
  1998     JS_ASSERT(descr->is<T>());
  2000     if (index >= (size_t) typedObj->length()) {
  2001         JS_ReportErrorNumber(cx, js_GetErrorMessage,
  2002                              nullptr, JSMSG_TYPEDOBJECT_BINARYARRAY_BAD_INDEX);
  2003         return false;
  2006     Rooted<SizedTypeDescr*> elementType(cx);
  2007     elementType = &descr->as<T>().elementType();
  2008     size_t offset = elementType->size() * index;
  2009     return ConvertAndCopyTo(cx, elementType, typedObj, offset, vp);
  2012 bool
  2013 TypedObject::obj_getGenericAttributes(JSContext *cx, HandleObject obj,
  2014                                      HandleId id, unsigned *attrsp)
  2016     uint32_t index;
  2017     Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
  2018     switch (typedObj->typeDescr().kind()) {
  2019       case TypeDescr::Scalar:
  2020       case TypeDescr::Reference:
  2021         break;
  2023       case TypeDescr::X4:
  2024         break;
  2026       case TypeDescr::SizedArray:
  2027       case TypeDescr::UnsizedArray:
  2028         if (js_IdIsIndex(id, &index)) {
  2029             *attrsp = JSPROP_ENUMERATE | JSPROP_PERMANENT;
  2030             return true;
  2032         if (JSID_IS_ATOM(id, cx->names().length)) {
  2033             *attrsp = JSPROP_READONLY | JSPROP_PERMANENT;
  2034             return true;
  2036         break;
  2038       case TypeDescr::Struct:
  2039         size_t index;
  2040         if (typedObj->typeDescr().as<StructTypeDescr>().fieldIndex(id, &index)) {
  2041             *attrsp = JSPROP_ENUMERATE | JSPROP_PERMANENT;
  2042             return true;
  2044         break;
  2047     RootedObject proto(cx, obj->getProto());
  2048     if (!proto) {
  2049         *attrsp = 0;
  2050         return true;
  2053     return JSObject::getGenericAttributes(cx, proto, id, attrsp);
  2056 static bool
  2057 IsOwnId(JSContext *cx, HandleObject obj, HandleId id)
  2059     uint32_t index;
  2060     Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
  2061     switch (typedObj->typeDescr().kind()) {
  2062       case TypeDescr::Scalar:
  2063       case TypeDescr::Reference:
  2064       case TypeDescr::X4:
  2065         return false;
  2067       case TypeDescr::SizedArray:
  2068       case TypeDescr::UnsizedArray:
  2069         return js_IdIsIndex(id, &index) || JSID_IS_ATOM(id, cx->names().length);
  2071       case TypeDescr::Struct:
  2072         size_t index;
  2073         if (typedObj->typeDescr().as<StructTypeDescr>().fieldIndex(id, &index))
  2074             return true;
  2077     return false;
  2080 bool
  2081 TypedObject::obj_setGenericAttributes(JSContext *cx, HandleObject obj,
  2082                                        HandleId id, unsigned *attrsp)
  2084     if (IsOwnId(cx, obj, id))
  2085         return ReportPropertyError(cx, JSMSG_CANT_REDEFINE_PROP, id);
  2087     RootedObject proto(cx, obj->getProto());
  2088     if (!proto) {
  2089         *attrsp = 0;
  2090         return true;
  2093     return JSObject::setGenericAttributes(cx, proto, id, attrsp);
  2096 bool
  2097 TypedObject::obj_deleteProperty(JSContext *cx, HandleObject obj,
  2098                                 HandlePropertyName name, bool *succeeded)
  2100     Rooted<jsid> id(cx, NameToId(name));
  2101     if (IsOwnId(cx, obj, id))
  2102         return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
  2104     RootedObject proto(cx, obj->getProto());
  2105     if (!proto) {
  2106         *succeeded = false;
  2107         return true;
  2110     return JSObject::deleteProperty(cx, proto, name, succeeded);
  2113 bool
  2114 TypedObject::obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
  2115                                bool *succeeded)
  2117     RootedId id(cx);
  2118     if (!IndexToId(cx, index, &id))
  2119         return false;
  2121     if (IsOwnId(cx, obj, id))
  2122         return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
  2124     RootedObject proto(cx, obj->getProto());
  2125     if (!proto) {
  2126         *succeeded = false;
  2127         return true;
  2130     return JSObject::deleteElement(cx, proto, index, succeeded);
  2133 bool
  2134 TypedObject::obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
  2135                            MutableHandleValue statep, MutableHandleId idp)
  2137     int32_t index;
  2139     JS_ASSERT(obj->is<TypedObject>());
  2140     Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
  2141     Rooted<TypeDescr *> descr(cx, &typedObj->typeDescr());
  2142     switch (descr->kind()) {
  2143       case TypeDescr::Scalar:
  2144       case TypeDescr::Reference:
  2145       case TypeDescr::X4:
  2146         switch (enum_op) {
  2147           case JSENUMERATE_INIT_ALL:
  2148           case JSENUMERATE_INIT:
  2149             statep.setInt32(0);
  2150             idp.set(INT_TO_JSID(0));
  2152           case JSENUMERATE_NEXT:
  2153           case JSENUMERATE_DESTROY:
  2154             statep.setNull();
  2155             break;
  2157         break;
  2159       case TypeDescr::SizedArray:
  2160       case TypeDescr::UnsizedArray:
  2161         switch (enum_op) {
  2162           case JSENUMERATE_INIT_ALL:
  2163           case JSENUMERATE_INIT:
  2164             statep.setInt32(0);
  2165             idp.set(INT_TO_JSID(typedObj->length()));
  2166             break;
  2168           case JSENUMERATE_NEXT:
  2169             index = static_cast<int32_t>(statep.toInt32());
  2171             if (index < typedObj->length()) {
  2172                 idp.set(INT_TO_JSID(index));
  2173                 statep.setInt32(index + 1);
  2174             } else {
  2175                 JS_ASSERT(index == typedObj->length());
  2176                 statep.setNull();
  2179             break;
  2181           case JSENUMERATE_DESTROY:
  2182             statep.setNull();
  2183             break;
  2185         break;
  2187       case TypeDescr::Struct:
  2188         switch (enum_op) {
  2189           case JSENUMERATE_INIT_ALL:
  2190           case JSENUMERATE_INIT:
  2191             statep.setInt32(0);
  2192             idp.set(INT_TO_JSID(descr->as<StructTypeDescr>().fieldCount()));
  2193             break;
  2195           case JSENUMERATE_NEXT:
  2196             index = static_cast<uint32_t>(statep.toInt32());
  2198             if ((size_t) index < descr->as<StructTypeDescr>().fieldCount()) {
  2199                 idp.set(AtomToId(&descr->as<StructTypeDescr>().fieldName(index)));
  2200                 statep.setInt32(index + 1);
  2201             } else {
  2202                 statep.setNull();
  2205             break;
  2207           case JSENUMERATE_DESTROY:
  2208             statep.setNull();
  2209             break;
  2211         break;
  2214     return true;
  2217 /* static */ size_t
  2218 TypedObject::offsetOfOwnerSlot()
  2220     return JSObject::getFixedSlotOffset(JS_TYPEDOBJ_SLOT_OWNER);
  2223 /* static */ size_t
  2224 TypedObject::offsetOfDataSlot()
  2226     // the offset of 7 is based on the alloc kind
  2227     return JSObject::getPrivateDataOffset(JS_TYPEDOBJ_SLOT_DATA);
  2230 /* static */ size_t
  2231 TypedObject::offsetOfByteOffsetSlot()
  2233     return JSObject::getFixedSlotOffset(JS_TYPEDOBJ_SLOT_BYTEOFFSET);
  2236 void
  2237 TypedObject::neuter(void *newData)
  2239     setSlot(JS_TYPEDOBJ_SLOT_LENGTH, Int32Value(0));
  2240     setSlot(JS_TYPEDOBJ_SLOT_BYTELENGTH, Int32Value(0));
  2241     setSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(0));
  2242     setPrivate(newData);
  2245 /******************************************************************************
  2246  * Typed Objects
  2247  */
  2249 const Class TransparentTypedObject::class_ = {
  2250     "TypedObject",
  2251     Class::NON_NATIVE |
  2252     JSCLASS_HAS_RESERVED_SLOTS(JS_TYPEDOBJ_SLOTS) |
  2253     JSCLASS_HAS_PRIVATE |
  2254     JSCLASS_IMPLEMENTS_BARRIERS,
  2255     JS_PropertyStub,
  2256     JS_DeletePropertyStub,
  2257     JS_PropertyStub,
  2258     JS_StrictPropertyStub,
  2259     JS_EnumerateStub,
  2260     JS_ResolveStub,
  2261     JS_ConvertStub,
  2262     nullptr,        /* finalize    */
  2263     nullptr,        /* call        */
  2264     nullptr,        /* hasInstance */
  2265     nullptr,        /* construct   */
  2266     TypedObject::obj_trace,
  2267     JS_NULL_CLASS_SPEC,
  2268     JS_NULL_CLASS_EXT,
  2270         TypedObject::obj_lookupGeneric,
  2271         TypedObject::obj_lookupProperty,
  2272         TypedObject::obj_lookupElement,
  2273         TypedObject::obj_defineGeneric,
  2274         TypedObject::obj_defineProperty,
  2275         TypedObject::obj_defineElement,
  2276         TypedObject::obj_getGeneric,
  2277         TypedObject::obj_getProperty,
  2278         TypedObject::obj_getElement,
  2279         TypedObject::obj_setGeneric,
  2280         TypedObject::obj_setProperty,
  2281         TypedObject::obj_setElement,
  2282         TypedObject::obj_getGenericAttributes,
  2283         TypedObject::obj_setGenericAttributes,
  2284         TypedObject::obj_deleteProperty,
  2285         TypedObject::obj_deleteElement,
  2286         nullptr, nullptr, // watch/unwatch
  2287         nullptr,   /* slice */
  2288         TypedObject::obj_enumerate,
  2289         nullptr, /* thisObject */
  2291 };
  2293 static int32_t
  2294 LengthForType(TypeDescr &descr)
  2296     switch (descr.kind()) {
  2297       case TypeDescr::Scalar:
  2298       case TypeDescr::Reference:
  2299       case TypeDescr::Struct:
  2300       case TypeDescr::X4:
  2301         return 0;
  2303       case TypeDescr::SizedArray:
  2304         return descr.as<SizedArrayTypeDescr>().length();
  2306       case TypeDescr::UnsizedArray:
  2307         return 0;
  2310     MOZ_ASSUME_UNREACHABLE("Invalid kind");
  2313 static bool
  2314 CheckOffset(int32_t offset,
  2315             int32_t size,
  2316             int32_t alignment,
  2317             int32_t bufferLength)
  2319     JS_ASSERT(size >= 0);
  2320     JS_ASSERT(alignment >= 0);
  2322     // No negative offsets.
  2323     if (offset < 0)
  2324         return false;
  2326     // Offset (plus size) must be fully contained within the buffer.
  2327     if (offset > bufferLength)
  2328         return false;
  2329     if (offset + size < offset)
  2330         return false;
  2331     if (offset + size > bufferLength)
  2332         return false;
  2334     // Offset must be aligned.
  2335     if ((offset % alignment) != 0)
  2336         return false;
  2338     return true;
  2341 /*static*/ bool
  2342 TypedObject::constructSized(JSContext *cx, unsigned int argc, Value *vp)
  2344     CallArgs args = CallArgsFromVp(argc, vp);
  2346     JS_ASSERT(args.callee().is<SizedTypeDescr>());
  2347     Rooted<SizedTypeDescr*> callee(cx, &args.callee().as<SizedTypeDescr>());
  2349     // Typed object constructors for sized types are overloaded in
  2350     // three ways, in order of precedence:
  2351     //
  2352     //   new TypeObj()
  2353     //   new TypeObj(buffer, [offset])
  2354     //   new TypeObj(data)
  2356     // Zero argument constructor:
  2357     if (args.length() == 0) {
  2358         int32_t length = LengthForType(*callee);
  2359         Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length));
  2360         if (!obj)
  2361             return false;
  2362         args.rval().setObject(*obj);
  2363         return true;
  2366     // Buffer constructor.
  2367     if (args[0].isObject() && args[0].toObject().is<ArrayBufferObject>()) {
  2368         Rooted<ArrayBufferObject*> buffer(cx);
  2369         buffer = &args[0].toObject().as<ArrayBufferObject>();
  2371         if (callee->opaque() || buffer->isNeutered()) {
  2372             JS_ReportErrorNumber(cx, js_GetErrorMessage,
  2373                                  nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  2374             return false;
  2377         int32_t offset;
  2378         if (args.length() >= 2 && !args[1].isUndefined()) {
  2379             if (!args[1].isInt32()) {
  2380                 JS_ReportErrorNumber(cx, js_GetErrorMessage,
  2381                                      nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  2382                 return false;
  2385             offset = args[1].toInt32();
  2386         } else {
  2387             offset = 0;
  2390         if (args.length() >= 3 && !args[2].isUndefined()) {
  2391             JS_ReportErrorNumber(cx, js_GetErrorMessage,
  2392                                  nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  2393             return false;
  2396         if (!CheckOffset(offset, callee->size(), callee->alignment(),
  2397                          buffer->byteLength()))
  2399             JS_ReportErrorNumber(cx, js_GetErrorMessage,
  2400                                  nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  2401             return false;
  2404         Rooted<TypedObject*> obj(cx);
  2405         obj = TypedObject::createUnattached(cx, callee, LengthForType(*callee));
  2406         if (!obj)
  2407             return false;
  2409         obj->attach(*buffer, offset);
  2410         args.rval().setObject(*obj);
  2411         return true;
  2414     // Data constructor.
  2415     if (args[0].isObject()) {
  2416         // Create the typed object.
  2417         int32_t length = LengthForType(*callee);
  2418         Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length));
  2419         if (!obj)
  2420             return false;
  2422         // Initialize from `arg`.
  2423         if (!ConvertAndCopyTo(cx, obj, args[0]))
  2424             return false;
  2425         args.rval().setObject(*obj);
  2426         return true;
  2429     // Something bogus.
  2430     JS_ReportErrorNumber(cx, js_GetErrorMessage,
  2431                          nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  2432     return false;
  2435 /*static*/ bool
  2436 TypedObject::constructUnsized(JSContext *cx, unsigned int argc, Value *vp)
  2438     CallArgs args = CallArgsFromVp(argc, vp);
  2440     JS_ASSERT(args.callee().is<TypeDescr>());
  2441     Rooted<UnsizedArrayTypeDescr*> callee(cx);
  2442     callee = &args.callee().as<UnsizedArrayTypeDescr>();
  2444     // Typed object constructors for unsized arrays are overloaded in
  2445     // four ways, in order of precedence:
  2446     //
  2447     //   new TypeObj(buffer, [offset, [length]]) // [1]
  2448     //   new TypeObj(length)                     // [1]
  2449     //   new TypeObj(data)
  2450     //   new TypeObj()
  2451     //
  2452     // [1] Explicit lengths only available for unsized arrays.
  2454     // Zero argument constructor:
  2455     if (args.length() == 0) {
  2456         Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, 0));
  2457         if (!obj)
  2458             return false;
  2459         args.rval().setObject(*obj);
  2460         return true;
  2463     // Length constructor.
  2464     if (args[0].isInt32()) {
  2465         int32_t length = args[0].toInt32();
  2466         if (length < 0) {
  2467             JS_ReportErrorNumber(cx, js_GetErrorMessage,
  2468                                  nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  2469             return nullptr;
  2471         Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length));
  2472         if (!obj)
  2473             return false;
  2474         args.rval().setObject(*obj);
  2475         return true;
  2478     // Buffer constructor.
  2479     if (args[0].isObject() && args[0].toObject().is<ArrayBufferObject>()) {
  2480         Rooted<ArrayBufferObject*> buffer(cx);
  2481         buffer = &args[0].toObject().as<ArrayBufferObject>();
  2483         if (callee->opaque()) {
  2484             JS_ReportErrorNumber(cx, js_GetErrorMessage,
  2485                                  nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  2486             return false;
  2489         int32_t offset;
  2490         if (args.length() >= 2 && !args[1].isUndefined()) {
  2491             if (!args[1].isInt32()) {
  2492                 JS_ReportErrorNumber(cx, js_GetErrorMessage,
  2493                                      nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  2494                 return false;
  2497             offset = args[1].toInt32();
  2498         } else {
  2499             offset = 0;
  2502         if (!CheckOffset(offset, 0, callee->alignment(), buffer->byteLength())) {
  2503             JS_ReportErrorNumber(cx, js_GetErrorMessage,
  2504                                  nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  2505             return false;
  2508         int32_t elemSize = callee->elementType().size();
  2509         int32_t bytesRemaining = buffer->byteLength() - offset;
  2510         int32_t maximumLength = bytesRemaining / elemSize;
  2512         int32_t length;
  2513         if (args.length() >= 3 && !args[2].isUndefined()) {
  2514             if (!args[2].isInt32()) {
  2515                 JS_ReportErrorNumber(cx, js_GetErrorMessage,
  2516                                      nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  2517                 return false;
  2519             length = args[2].toInt32();
  2521             if (length < 0 || length > maximumLength) {
  2522                 JS_ReportErrorNumber(cx, js_GetErrorMessage,
  2523                                      nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  2524                 return false;
  2526         } else {
  2527             if ((bytesRemaining % elemSize) != 0) {
  2528                 JS_ReportErrorNumber(cx, js_GetErrorMessage,
  2529                                      nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  2530                 return false;
  2533             length = maximumLength;
  2536         if (buffer->isNeutered()) {
  2537             JS_ReportErrorNumber(cx, js_GetErrorMessage,
  2538                                  nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  2539             return false;
  2542         Rooted<TypedObject*> obj(cx);
  2543         obj = TypedObject::createUnattached(cx, callee, length);
  2544         if (!obj)
  2545             return false;
  2547         obj->attach(args[0].toObject().as<ArrayBufferObject>(), offset);
  2550     // Data constructor for unsized values
  2551     if (args[0].isObject()) {
  2552         // Read length out of the object.
  2553         RootedObject arg(cx, &args[0].toObject());
  2554         RootedValue lengthVal(cx);
  2555         if (!JSObject::getProperty(cx, arg, arg, cx->names().length, &lengthVal))
  2556             return false;
  2557         if (!lengthVal.isInt32()) {
  2558             JS_ReportErrorNumber(cx, js_GetErrorMessage,
  2559                                  nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  2560             return false;
  2562         int32_t length = lengthVal.toInt32();
  2564         // Check that length * elementSize does not overflow.
  2565         int32_t elementSize = callee->elementType().size();
  2566         int32_t byteLength;
  2567         if (!SafeMul(elementSize, length, &byteLength)) {
  2568             JS_ReportErrorNumber(cx, js_GetErrorMessage,
  2569                                  nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  2570             return false;
  2573         // Create the unsized array.
  2574         Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length));
  2575         if (!obj)
  2576             return false;
  2578         // Initialize from `arg`
  2579         if (!ConvertAndCopyTo(cx, obj, args[0]))
  2580             return false;
  2581         args.rval().setObject(*obj);
  2582         return true;
  2585     // Something bogus.
  2586     JS_ReportErrorNumber(cx, js_GetErrorMessage,
  2587                          nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
  2588     return false;
  2591 /******************************************************************************
  2592  * Handles
  2593  */
  2595 const Class OpaqueTypedObject::class_ = {
  2596     "Handle",
  2597     Class::NON_NATIVE |
  2598     JSCLASS_HAS_RESERVED_SLOTS(JS_TYPEDOBJ_SLOTS) |
  2599     JSCLASS_HAS_PRIVATE |
  2600     JSCLASS_IMPLEMENTS_BARRIERS,
  2601     JS_PropertyStub,
  2602     JS_DeletePropertyStub,
  2603     JS_PropertyStub,
  2604     JS_StrictPropertyStub,
  2605     JS_EnumerateStub,
  2606     JS_ResolveStub,
  2607     JS_ConvertStub,
  2608     nullptr,        /* finalize    */
  2609     nullptr,        /* call        */
  2610     nullptr,        /* construct   */
  2611     nullptr,        /* hasInstance */
  2612     TypedObject::obj_trace,
  2613     JS_NULL_CLASS_SPEC,
  2614     JS_NULL_CLASS_EXT,
  2616         TypedObject::obj_lookupGeneric,
  2617         TypedObject::obj_lookupProperty,
  2618         TypedObject::obj_lookupElement,
  2619         TypedObject::obj_defineGeneric,
  2620         TypedObject::obj_defineProperty,
  2621         TypedObject::obj_defineElement,
  2622         TypedObject::obj_getGeneric,
  2623         TypedObject::obj_getProperty,
  2624         TypedObject::obj_getElement,
  2625         TypedObject::obj_setGeneric,
  2626         TypedObject::obj_setProperty,
  2627         TypedObject::obj_setElement,
  2628         TypedObject::obj_getGenericAttributes,
  2629         TypedObject::obj_setGenericAttributes,
  2630         TypedObject::obj_deleteProperty,
  2631         TypedObject::obj_deleteElement,
  2632         nullptr, nullptr, // watch/unwatch
  2633         nullptr, // slice
  2634         TypedObject::obj_enumerate,
  2635         nullptr, /* thisObject */
  2637 };
  2639 const JSFunctionSpec OpaqueTypedObject::handleStaticMethods[] = {
  2640     {"move", {nullptr, nullptr}, 3, 0, "HandleMove"},
  2641     {"get", {nullptr, nullptr}, 1, 0, "HandleGet"},
  2642     {"set", {nullptr, nullptr}, 2, 0, "HandleSet"},
  2643     {"isHandle", {nullptr, nullptr}, 1, 0, "HandleTest"},
  2644     JS_FS_END
  2645 };
  2647 /******************************************************************************
  2648  * Intrinsics
  2649  */
  2651 bool
  2652 js::NewOpaqueTypedObject(JSContext *cx, unsigned argc, Value *vp)
  2654     CallArgs args = CallArgsFromVp(argc, vp);
  2655     JS_ASSERT(args.length() == 1);
  2656     JS_ASSERT(args[0].isObject() && args[0].toObject().is<SizedTypeDescr>());
  2658     Rooted<SizedTypeDescr*> descr(cx, &args[0].toObject().as<SizedTypeDescr>());
  2659     int32_t length = TypedObjLengthFromType(*descr);
  2660     Rooted<TypedObject*> obj(cx);
  2661     obj = TypedObject::createUnattachedWithClass(cx, &OpaqueTypedObject::class_, descr, length);
  2662     if (!obj)
  2663         return false;
  2664     args.rval().setObject(*obj);
  2665     return true;
  2668 bool
  2669 js::NewDerivedTypedObject(JSContext *cx, unsigned argc, Value *vp)
  2671     CallArgs args = CallArgsFromVp(argc, vp);
  2672     JS_ASSERT(args.length() == 3);
  2673     JS_ASSERT(args[0].isObject() && args[0].toObject().is<SizedTypeDescr>());
  2674     JS_ASSERT(args[1].isObject() && args[1].toObject().is<TypedObject>());
  2675     JS_ASSERT(args[2].isInt32());
  2677     Rooted<SizedTypeDescr*> descr(cx, &args[0].toObject().as<SizedTypeDescr>());
  2678     Rooted<TypedObject*> typedObj(cx, &args[1].toObject().as<TypedObject>());
  2679     int32_t offset = args[2].toInt32();
  2681     Rooted<TypedObject*> obj(cx);
  2682     obj = TypedObject::createDerived(cx, descr, typedObj, offset);
  2683     if (!obj)
  2684         return false;
  2686     args.rval().setObject(*obj);
  2687     return true;
  2690 bool
  2691 js::AttachTypedObject(ThreadSafeContext *, unsigned argc, Value *vp)
  2693     CallArgs args = CallArgsFromVp(argc, vp);
  2694     JS_ASSERT(args.length() == 3);
  2695     JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
  2696     JS_ASSERT(args[1].isObject() && args[1].toObject().is<TypedObject>());
  2697     JS_ASSERT(args[2].isInt32());
  2699     TypedObject &handle = args[0].toObject().as<TypedObject>();
  2700     TypedObject &target = args[1].toObject().as<TypedObject>();
  2701     JS_ASSERT(handle.typedMem() == nullptr); // must not be attached already
  2702     size_t offset = args[2].toInt32();
  2703     handle.attach(target, offset);
  2704     return true;
  2707 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::AttachTypedObjectJitInfo,
  2708                                       AttachTypedObjectJitInfo,
  2709                                       js::AttachTypedObject);
  2711 bool
  2712 js::SetTypedObjectOffset(ThreadSafeContext *, unsigned argc, Value *vp)
  2714     CallArgs args = CallArgsFromVp(argc, vp);
  2715     JS_ASSERT(args.length() == 2);
  2716     JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
  2717     JS_ASSERT(args[1].isInt32());
  2719     TypedObject &typedObj = args[0].toObject().as<TypedObject>();
  2720     int32_t offset = args[1].toInt32();
  2722     JS_ASSERT(!typedObj.owner().isNeutered());
  2723     JS_ASSERT(typedObj.typedMem() != nullptr); // must be attached already
  2725     typedObj.setPrivate(typedObj.owner().dataPointer() + offset);
  2726     typedObj.setReservedSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(offset));
  2727     args.rval().setUndefined();
  2728     return true;
  2731 bool
  2732 js::intrinsic_SetTypedObjectOffset(JSContext *cx, unsigned argc, Value *vp)
  2734     // Do not use JSNativeThreadSafeWrapper<> so that ion can reference
  2735     // this function more easily when inlining.
  2736     return SetTypedObjectOffset(cx, argc, vp);
  2739 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::intrinsic_SetTypedObjectOffsetJitInfo,
  2740                                       SetTypedObjectJitInfo,
  2741                                       SetTypedObjectOffset);
  2743 bool
  2744 js::ObjectIsTypeDescr(ThreadSafeContext *, unsigned argc, Value *vp)
  2746     CallArgs args = CallArgsFromVp(argc, vp);
  2747     JS_ASSERT(args.length() == 1);
  2748     JS_ASSERT(args[0].isObject());
  2749     args.rval().setBoolean(args[0].toObject().is<TypeDescr>());
  2750     return true;
  2753 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ObjectIsTypeDescrJitInfo, ObjectIsTypeDescrJitInfo,
  2754                                       js::ObjectIsTypeDescr);
  2756 bool
  2757 js::ObjectIsTypedObject(ThreadSafeContext *, unsigned argc, Value *vp)
  2759     CallArgs args = CallArgsFromVp(argc, vp);
  2760     JS_ASSERT(args.length() == 1);
  2761     JS_ASSERT(args[0].isObject());
  2762     args.rval().setBoolean(args[0].toObject().is<TypedObject>());
  2763     return true;
  2766 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ObjectIsTypedObjectJitInfo,
  2767                                       ObjectIsTypedObjectJitInfo,
  2768                                       js::ObjectIsTypedObject);
  2770 bool
  2771 js::ObjectIsOpaqueTypedObject(ThreadSafeContext *, unsigned argc, Value *vp)
  2773     CallArgs args = CallArgsFromVp(argc, vp);
  2774     JS_ASSERT(args.length() == 1);
  2775     JS_ASSERT(args[0].isObject());
  2776     args.rval().setBoolean(args[0].toObject().is<OpaqueTypedObject>());
  2777     return true;
  2780 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ObjectIsOpaqueTypedObjectJitInfo,
  2781                                       ObjectIsOpaqueTypedObjectJitInfo,
  2782                                       js::ObjectIsOpaqueTypedObject);
  2784 bool
  2785 js::ObjectIsTransparentTypedObject(ThreadSafeContext *, unsigned argc, Value *vp)
  2787     CallArgs args = CallArgsFromVp(argc, vp);
  2788     JS_ASSERT(args.length() == 1);
  2789     JS_ASSERT(args[0].isObject());
  2790     args.rval().setBoolean(args[0].toObject().is<TransparentTypedObject>());
  2791     return true;
  2794 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ObjectIsTransparentTypedObjectJitInfo,
  2795                                       ObjectIsTransparentTypedObjectJitInfo,
  2796                                       js::ObjectIsTransparentTypedObject);
  2798 bool
  2799 js::TypeDescrIsSimpleType(ThreadSafeContext *, unsigned argc, Value *vp)
  2801     CallArgs args = CallArgsFromVp(argc, vp);
  2802     JS_ASSERT(args.length() == 1);
  2803     JS_ASSERT(args[0].isObject());
  2804     JS_ASSERT(args[0].toObject().is<js::TypeDescr>());
  2805     args.rval().setBoolean(args[0].toObject().is<js::SimpleTypeDescr>());
  2806     return true;
  2809 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::TypeDescrIsSimpleTypeJitInfo,
  2810                                       TypeDescrIsSimpleTypeJitInfo,
  2811                                       js::TypeDescrIsSimpleType);
  2813 bool
  2814 js::TypeDescrIsArrayType(ThreadSafeContext *, unsigned argc, Value *vp)
  2816     CallArgs args = CallArgsFromVp(argc, vp);
  2817     JS_ASSERT(args.length() == 1);
  2818     JS_ASSERT(args[0].isObject());
  2819     JS_ASSERT(args[0].toObject().is<js::TypeDescr>());
  2820     JSObject& obj = args[0].toObject();
  2821     args.rval().setBoolean(obj.is<js::SizedArrayTypeDescr>() ||
  2822                            obj.is<js::UnsizedArrayTypeDescr>());
  2823     return true;
  2826 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::TypeDescrIsArrayTypeJitInfo,
  2827                                       TypeDescrIsArrayTypeJitInfo,
  2828                                       js::TypeDescrIsArrayType);
  2830 bool
  2831 js::TypeDescrIsSizedArrayType(ThreadSafeContext *, unsigned argc, Value *vp)
  2833     CallArgs args = CallArgsFromVp(argc, vp);
  2834     JS_ASSERT(args.length() == 1);
  2835     JS_ASSERT(args[0].isObject());
  2836     JS_ASSERT(args[0].toObject().is<js::TypeDescr>());
  2837     args.rval().setBoolean(args[0].toObject().is<js::SizedArrayTypeDescr>());
  2838     return true;
  2841 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::TypeDescrIsSizedArrayTypeJitInfo,
  2842                                       TypeDescrIsSizedArrayTypeJitInfo,
  2843                                       js::TypeDescrIsSizedArrayType);
  2845 bool
  2846 js::TypeDescrIsUnsizedArrayType(ThreadSafeContext *, unsigned argc, Value *vp)
  2848     CallArgs args = CallArgsFromVp(argc, vp);
  2849     JS_ASSERT(args.length() == 1);
  2850     JS_ASSERT(args[0].isObject());
  2851     JS_ASSERT(args[0].toObject().is<js::TypeDescr>());
  2852     args.rval().setBoolean(args[0].toObject().is<js::UnsizedArrayTypeDescr>());
  2853     return true;
  2856 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::TypeDescrIsUnsizedArrayTypeJitInfo,
  2857                                       TypeDescrIsUnsizedArrayTypeJitInfo,
  2858                                       js::TypeDescrIsUnsizedArrayType);
  2860 bool
  2861 js::TypedObjectIsAttached(ThreadSafeContext *cx, unsigned argc, Value *vp)
  2863     CallArgs args = CallArgsFromVp(argc, vp);
  2864     JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
  2865     TypedObject &typedObj = args[0].toObject().as<TypedObject>();
  2866     args.rval().setBoolean(!typedObj.owner().isNeutered() && typedObj.typedMem() != nullptr);
  2867     return true;
  2870 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::TypedObjectIsAttachedJitInfo,
  2871                                       TypedObjectIsAttachedJitInfo,
  2872                                       js::TypedObjectIsAttached);
  2874 bool
  2875 js::ClampToUint8(ThreadSafeContext *, unsigned argc, Value *vp)
  2877     CallArgs args = CallArgsFromVp(argc, vp);
  2878     JS_ASSERT(args.length() == 1);
  2879     JS_ASSERT(args[0].isNumber());
  2880     args.rval().setNumber(ClampDoubleToUint8(args[0].toNumber()));
  2881     return true;
  2884 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ClampToUint8JitInfo, ClampToUint8JitInfo,
  2885                                       js::ClampToUint8);
  2887 bool
  2888 js::Memcpy(ThreadSafeContext *, unsigned argc, Value *vp)
  2890     CallArgs args = CallArgsFromVp(argc, vp);
  2891     JS_ASSERT(args.length() == 5);
  2892     JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
  2893     JS_ASSERT(args[1].isInt32());
  2894     JS_ASSERT(args[2].isObject() && args[2].toObject().is<TypedObject>());
  2895     JS_ASSERT(args[3].isInt32());
  2896     JS_ASSERT(args[4].isInt32());
  2898     TypedObject &targetTypedObj = args[0].toObject().as<TypedObject>();
  2899     int32_t targetOffset = args[1].toInt32();
  2900     TypedObject &sourceTypedObj = args[2].toObject().as<TypedObject>();
  2901     int32_t sourceOffset = args[3].toInt32();
  2902     int32_t size = args[4].toInt32();
  2904     JS_ASSERT(targetOffset >= 0);
  2905     JS_ASSERT(sourceOffset >= 0);
  2906     JS_ASSERT(size >= 0);
  2907     JS_ASSERT(size + targetOffset <= targetTypedObj.size());
  2908     JS_ASSERT(size + sourceOffset <= sourceTypedObj.size());
  2910     uint8_t *target = targetTypedObj.typedMem(targetOffset);
  2911     uint8_t *source = sourceTypedObj.typedMem(sourceOffset);
  2912     memcpy(target, source, size);
  2913     args.rval().setUndefined();
  2914     return true;
  2917 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::MemcpyJitInfo, MemcpyJitInfo, js::Memcpy);
  2919 bool
  2920 js::GetTypedObjectModule(JSContext *cx, unsigned argc, Value *vp)
  2922     CallArgs args = CallArgsFromVp(argc, vp);
  2923     Rooted<GlobalObject*> global(cx, cx->global());
  2924     JS_ASSERT(global);
  2925     args.rval().setObject(global->getTypedObjectModule());
  2926     return true;
  2929 bool
  2930 js::GetFloat32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp)
  2932     CallArgs args = CallArgsFromVp(argc, vp);
  2933     Rooted<GlobalObject*> global(cx, cx->global());
  2934     JS_ASSERT(global);
  2935     args.rval().setObject(global->float32x4TypeDescr());
  2936     return true;
  2939 bool
  2940 js::GetInt32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp)
  2942     CallArgs args = CallArgsFromVp(argc, vp);
  2943     Rooted<GlobalObject*> global(cx, cx->global());
  2944     JS_ASSERT(global);
  2945     args.rval().setObject(global->int32x4TypeDescr());
  2946     return true;
  2949 #define JS_STORE_SCALAR_CLASS_IMPL(_constant, T, _name)                         \
  2950 bool                                                                            \
  2951 js::StoreScalar##T::Func(ThreadSafeContext *, unsigned argc, Value *vp)         \
  2952 {                                                                               \
  2953     CallArgs args = CallArgsFromVp(argc, vp);                                   \
  2954     JS_ASSERT(args.length() == 3);                                              \
  2955     JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());      \
  2956     JS_ASSERT(args[1].isInt32());                                               \
  2957     JS_ASSERT(args[2].isNumber());                                              \
  2959     TypedObject &typedObj = args[0].toObject().as<TypedObject>();               \
  2960     int32_t offset = args[1].toInt32();                                         \
  2962     /* Should be guaranteed by the typed objects API: */                        \
  2963     JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                                    \
  2965     T *target = reinterpret_cast<T*>(typedObj.typedMem(offset));                \
  2966     double d = args[2].toNumber();                                              \
  2967     *target = ConvertScalar<T>(d);                                              \
  2968     args.rval().setUndefined();                                                 \
  2969     return true;                                                                \
  2970 }                                                                               \
  2972 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::StoreScalar##T::JitInfo,              \
  2973                                       StoreScalar##T,                           \
  2974                                       js::StoreScalar##T::Func);
  2976 #define JS_STORE_REFERENCE_CLASS_IMPL(_constant, T, _name)                      \
  2977 bool                                                                            \
  2978 js::StoreReference##T::Func(ThreadSafeContext *, unsigned argc, Value *vp)      \
  2979 {                                                                               \
  2980     CallArgs args = CallArgsFromVp(argc, vp);                                   \
  2981     JS_ASSERT(args.length() == 3);                                              \
  2982     JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());      \
  2983     JS_ASSERT(args[1].isInt32());                                               \
  2985     TypedObject &typedObj = args[0].toObject().as<TypedObject>();               \
  2986     int32_t offset = args[1].toInt32();                                         \
  2988     /* Should be guaranteed by the typed objects API: */                        \
  2989     JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                                    \
  2991     T *target = reinterpret_cast<T*>(typedObj.typedMem(offset));                \
  2992     store(target, args[2]);                                                     \
  2993     args.rval().setUndefined();                                                 \
  2994     return true;                                                                \
  2995 }                                                                               \
  2997 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::StoreReference##T::JitInfo,           \
  2998                                       StoreReference##T,                        \
  2999                                       js::StoreReference##T::Func);
  3001 #define JS_LOAD_SCALAR_CLASS_IMPL(_constant, T, _name)                                  \
  3002 bool                                                                                    \
  3003 js::LoadScalar##T::Func(ThreadSafeContext *, unsigned argc, Value *vp)                  \
  3004 {                                                                                       \
  3005     CallArgs args = CallArgsFromVp(argc, vp);                                           \
  3006     JS_ASSERT(args.length() == 2);                                                      \
  3007     JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());              \
  3008     JS_ASSERT(args[1].isInt32());                                                       \
  3010     TypedObject &typedObj = args[0].toObject().as<TypedObject>();                       \
  3011     int32_t offset = args[1].toInt32();                                                 \
  3013     /* Should be guaranteed by the typed objects API: */                                \
  3014     JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                                            \
  3016     T *target = reinterpret_cast<T*>(typedObj.typedMem(offset));                        \
  3017     args.rval().setNumber((double) *target);                                            \
  3018     return true;                                                                        \
  3019 }                                                                                       \
  3021 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::LoadScalar##T::JitInfo, LoadScalar##T,        \
  3022                                       js::LoadScalar##T::Func);
  3024 #define JS_LOAD_REFERENCE_CLASS_IMPL(_constant, T, _name)                       \
  3025 bool                                                                            \
  3026 js::LoadReference##T::Func(ThreadSafeContext *, unsigned argc, Value *vp)       \
  3027 {                                                                               \
  3028     CallArgs args = CallArgsFromVp(argc, vp);                                   \
  3029     JS_ASSERT(args.length() == 2);                                              \
  3030     JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());      \
  3031     JS_ASSERT(args[1].isInt32());                                               \
  3033     TypedObject &typedObj = args[0].toObject().as<TypedObject>();               \
  3034     int32_t offset = args[1].toInt32();                                         \
  3036     /* Should be guaranteed by the typed objects API: */                        \
  3037     JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                                    \
  3039     T *target = reinterpret_cast<T*>(typedObj.typedMem(offset));                \
  3040     load(target, args.rval());                                                  \
  3041     return true;                                                                \
  3042 }                                                                               \
  3044 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::LoadReference##T::JitInfo,            \
  3045                                       LoadReference##T,                         \
  3046                                       js::LoadReference##T::Func);
  3048 // Because the precise syntax for storing values/objects/strings
  3049 // differs, we abstract it away using specialized variants of the
  3050 // private methods `store()` and `load()`.
  3052 void
  3053 StoreReferenceHeapValue::store(HeapValue *heap, const Value &v)
  3055     *heap = v;
  3058 void
  3059 StoreReferenceHeapPtrObject::store(HeapPtrObject *heap, const Value &v)
  3061     JS_ASSERT(v.isObjectOrNull()); // or else Store_object is being misused
  3062     *heap = v.toObjectOrNull();
  3065 void
  3066 StoreReferenceHeapPtrString::store(HeapPtrString *heap, const Value &v)
  3068     JS_ASSERT(v.isString()); // or else Store_string is being misused
  3069     *heap = v.toString();
  3072 void
  3073 LoadReferenceHeapValue::load(HeapValue *heap,
  3074                              MutableHandleValue v)
  3076     v.set(*heap);
  3079 void
  3080 LoadReferenceHeapPtrObject::load(HeapPtrObject *heap,
  3081                                  MutableHandleValue v)
  3083     if (*heap)
  3084         v.setObject(**heap);
  3085     else
  3086         v.setNull();
  3089 void
  3090 LoadReferenceHeapPtrString::load(HeapPtrString *heap,
  3091                                  MutableHandleValue v)
  3093     v.setString(*heap);
  3096 // I was using templates for this stuff instead of macros, but ran
  3097 // into problems with the Unagi compiler.
  3098 JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_STORE_SCALAR_CLASS_IMPL)
  3099 JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_LOAD_SCALAR_CLASS_IMPL)
  3100 JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_STORE_REFERENCE_CLASS_IMPL)
  3101 JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_LOAD_REFERENCE_CLASS_IMPL)
  3103 ///////////////////////////////////////////////////////////////////////////
  3104 // Walking memory
  3106 template<typename V>
  3107 static void
  3108 visitReferences(SizedTypeDescr &descr,
  3109                 uint8_t *mem,
  3110                 V& visitor)
  3112     if (descr.transparent())
  3113         return;
  3115     switch (descr.kind()) {
  3116       case TypeDescr::Scalar:
  3117       case TypeDescr::X4:
  3118         return;
  3120       case TypeDescr::Reference:
  3121         visitor.visitReference(descr.as<ReferenceTypeDescr>(), mem);
  3122         return;
  3124       case TypeDescr::SizedArray:
  3126         SizedArrayTypeDescr &arrayDescr = descr.as<SizedArrayTypeDescr>();
  3127         SizedTypeDescr &elementDescr = arrayDescr.elementType();
  3128         for (int32_t i = 0; i < arrayDescr.length(); i++) {
  3129             visitReferences(elementDescr, mem, visitor);
  3130             mem += elementDescr.size();
  3132         return;
  3135       case TypeDescr::UnsizedArray:
  3137         MOZ_ASSUME_UNREACHABLE("Only Sized Type representations");
  3140       case TypeDescr::Struct:
  3142         StructTypeDescr &structDescr = descr.as<StructTypeDescr>();
  3143         for (size_t i = 0; i < structDescr.fieldCount(); i++) {
  3144             SizedTypeDescr &descr = structDescr.fieldDescr(i);
  3145             size_t offset = structDescr.fieldOffset(i);
  3146             visitReferences(descr, mem + offset, visitor);
  3148         return;
  3152     MOZ_ASSUME_UNREACHABLE("Invalid type repr kind");
  3155 ///////////////////////////////////////////////////////////////////////////
  3156 // Initializing instances
  3158 namespace js {
  3159 class MemoryInitVisitor {
  3160     const JSRuntime *rt_;
  3162   public:
  3163     MemoryInitVisitor(const JSRuntime *rt)
  3164       : rt_(rt)
  3165     {}
  3167     void visitReference(ReferenceTypeDescr &descr, uint8_t *mem);
  3168 };
  3169 } // namespace js
  3171 void
  3172 js::MemoryInitVisitor::visitReference(ReferenceTypeDescr &descr, uint8_t *mem)
  3174     switch (descr.type()) {
  3175       case ReferenceTypeDescr::TYPE_ANY:
  3177         js::HeapValue *heapValue = reinterpret_cast<js::HeapValue *>(mem);
  3178         heapValue->init(UndefinedValue());
  3179         return;
  3182       case ReferenceTypeDescr::TYPE_OBJECT:
  3184         js::HeapPtrObject *objectPtr =
  3185             reinterpret_cast<js::HeapPtrObject *>(mem);
  3186         objectPtr->init(nullptr);
  3187         return;
  3190       case ReferenceTypeDescr::TYPE_STRING:
  3192         js::HeapPtrString *stringPtr =
  3193             reinterpret_cast<js::HeapPtrString *>(mem);
  3194         stringPtr->init(rt_->emptyString);
  3195         return;
  3199     MOZ_ASSUME_UNREACHABLE("Invalid kind");
  3202 void
  3203 SizedTypeDescr::initInstances(const JSRuntime *rt, uint8_t *mem, size_t length)
  3205     JS_ASSERT(length >= 1);
  3207     MemoryInitVisitor visitor(rt);
  3209     // Initialize the 0th instance
  3210     memset(mem, 0, size());
  3211     if (opaque())
  3212         visitReferences(*this, mem, visitor);
  3214     // Stamp out N copies of later instances
  3215     uint8_t *target = mem;
  3216     for (size_t i = 1; i < length; i++) {
  3217         target += size();
  3218         memcpy(target, mem, size());
  3222 ///////////////////////////////////////////////////////////////////////////
  3223 // Tracing instances
  3225 namespace js {
  3226 class MemoryTracingVisitor {
  3227     JSTracer *trace_;
  3229   public:
  3231     MemoryTracingVisitor(JSTracer *trace)
  3232       : trace_(trace)
  3233     {}
  3235     void visitReference(ReferenceTypeDescr &descr, uint8_t *mem);
  3236 };
  3237 } // namespace js
  3239 void
  3240 js::MemoryTracingVisitor::visitReference(ReferenceTypeDescr &descr, uint8_t *mem)
  3242     switch (descr.type()) {
  3243       case ReferenceTypeDescr::TYPE_ANY:
  3245         js::HeapValue *heapValue = reinterpret_cast<js::HeapValue *>(mem);
  3246         gc::MarkValue(trace_, heapValue, "reference-val");
  3247         return;
  3250       case ReferenceTypeDescr::TYPE_OBJECT:
  3252         js::HeapPtrObject *objectPtr =
  3253             reinterpret_cast<js::HeapPtrObject *>(mem);
  3254         if (*objectPtr)
  3255             gc::MarkObject(trace_, objectPtr, "reference-obj");
  3256         return;
  3259       case ReferenceTypeDescr::TYPE_STRING:
  3261         js::HeapPtrString *stringPtr =
  3262             reinterpret_cast<js::HeapPtrString *>(mem);
  3263         if (*stringPtr)
  3264             gc::MarkString(trace_, stringPtr, "reference-str");
  3265         return;
  3269     MOZ_ASSUME_UNREACHABLE("Invalid kind");
  3272 void
  3273 SizedTypeDescr::traceInstances(JSTracer *trace, uint8_t *mem, size_t length)
  3275     MemoryTracingVisitor visitor(trace);
  3277     for (size_t i = 0; i < length; i++) {
  3278         visitReferences(*this, mem, visitor);
  3279         mem += size();

mercurial