js/src/vm/TypedArrayObject.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 "vm/TypedArrayObject.h"
     9 #include "mozilla/Alignment.h"
    10 #include "mozilla/FloatingPoint.h"
    11 #include "mozilla/PodOperations.h"
    13 #include <string.h>
    14 #ifndef XP_WIN
    15 # include <sys/mman.h>
    16 #endif
    18 #include "jsapi.h"
    19 #include "jsarray.h"
    20 #include "jscntxt.h"
    21 #include "jscpucfg.h"
    22 #include "jsnum.h"
    23 #include "jsobj.h"
    24 #include "jstypes.h"
    25 #include "jsutil.h"
    26 #ifdef XP_WIN
    27 # include "jswin.h"
    28 #endif
    29 #include "jswrapper.h"
    31 #include "gc/Barrier.h"
    32 #include "gc/Marking.h"
    33 #include "jit/AsmJS.h"
    34 #include "jit/AsmJSModule.h"
    35 #include "vm/ArrayBufferObject.h"
    36 #include "vm/GlobalObject.h"
    37 #include "vm/Interpreter.h"
    38 #include "vm/NumericConversions.h"
    39 #include "vm/SharedArrayObject.h"
    40 #include "vm/WrapperObject.h"
    42 #include "jsatominlines.h"
    43 #include "jsinferinlines.h"
    44 #include "jsobjinlines.h"
    46 #include "vm/Shape-inl.h"
    48 using namespace js;
    49 using namespace js::gc;
    50 using namespace js::types;
    52 using mozilla::IsNaN;
    53 using mozilla::NegativeInfinity;
    54 using mozilla::PodCopy;
    55 using mozilla::PositiveInfinity;
    56 using JS::CanonicalizeNaN;
    57 using JS::GenericNaN;
    59 static bool
    60 ValueIsLength(const Value &v, uint32_t *len)
    61 {
    62     if (v.isInt32()) {
    63         int32_t i = v.toInt32();
    64         if (i < 0)
    65             return false;
    66         *len = i;
    67         return true;
    68     }
    70     if (v.isDouble()) {
    71         double d = v.toDouble();
    72         if (IsNaN(d))
    73             return false;
    75         uint32_t length = uint32_t(d);
    76         if (d != double(length))
    77             return false;
    79         *len = length;
    80         return true;
    81     }
    83     return false;
    84 }
    86 /*
    87  * TypedArrayObject
    88  *
    89  * The non-templated base class for the specific typed implementations.
    90  * This class holds all the member variables that are used by
    91  * the subclasses.
    92  */
    94 void
    95 TypedArrayObject::neuter(void *newData)
    96 {
    97     setSlot(LENGTH_SLOT, Int32Value(0));
    98     setSlot(BYTELENGTH_SLOT, Int32Value(0));
    99     setSlot(BYTEOFFSET_SLOT, Int32Value(0));
   100     setPrivate(newData);
   101 }
   103 ArrayBufferObject *
   104 TypedArrayObject::sharedBuffer() const
   105 {
   106     return &bufferValue(const_cast<TypedArrayObject*>(this)).toObject().as<SharedArrayBufferObject>();
   107 }
   109 /* static */ bool
   110 TypedArrayObject::ensureHasBuffer(JSContext *cx, Handle<TypedArrayObject *> tarray)
   111 {
   112     if (tarray->buffer())
   113         return true;
   115     Rooted<ArrayBufferObject *> buffer(cx, ArrayBufferObject::create(cx, tarray->byteLength()));
   116     if (!buffer)
   117         return false;
   119     buffer->addView(tarray);
   121     memcpy(buffer->dataPointer(), tarray->viewData(), tarray->byteLength());
   122     InitArrayBufferViewDataPointer(tarray, buffer, 0);
   124     tarray->setSlot(BUFFER_SLOT, ObjectValue(*buffer));
   125     return true;
   126 }
   128 /* static */ int
   129 TypedArrayObject::lengthOffset()
   130 {
   131     return JSObject::getFixedSlotOffset(LENGTH_SLOT);
   132 }
   134 /* static */ int
   135 TypedArrayObject::dataOffset()
   136 {
   137     return JSObject::getPrivateDataOffset(DATA_SLOT);
   138 }
   140 /* Helper clamped uint8_t type */
   142 uint32_t JS_FASTCALL
   143 js::ClampDoubleToUint8(const double x)
   144 {
   145     // Not < so that NaN coerces to 0
   146     if (!(x >= 0))
   147         return 0;
   149     if (x > 255)
   150         return 255;
   152     double toTruncate = x + 0.5;
   153     uint8_t y = uint8_t(toTruncate);
   155     /*
   156      * now val is rounded to nearest, ties rounded up.  We want
   157      * rounded to nearest ties to even, so check whether we had a
   158      * tie.
   159      */
   160     if (y == toTruncate) {
   161         /*
   162          * It was a tie (since adding 0.5 gave us the exact integer
   163          * we want).  Since we rounded up, we either already have an
   164          * even number or we have an odd number but the number we
   165          * want is one less.  So just unconditionally masking out the
   166          * ones bit should do the trick to get us the value we
   167          * want.
   168          */
   169         return y & ~1;
   170     }
   172     return y;
   173 }
   175 template<typename NativeType> static inline const int TypeIDOfType();
   176 template<> inline const int TypeIDOfType<int8_t>() { return ScalarTypeDescr::TYPE_INT8; }
   177 template<> inline const int TypeIDOfType<uint8_t>() { return ScalarTypeDescr::TYPE_UINT8; }
   178 template<> inline const int TypeIDOfType<int16_t>() { return ScalarTypeDescr::TYPE_INT16; }
   179 template<> inline const int TypeIDOfType<uint16_t>() { return ScalarTypeDescr::TYPE_UINT16; }
   180 template<> inline const int TypeIDOfType<int32_t>() { return ScalarTypeDescr::TYPE_INT32; }
   181 template<> inline const int TypeIDOfType<uint32_t>() { return ScalarTypeDescr::TYPE_UINT32; }
   182 template<> inline const int TypeIDOfType<float>() { return ScalarTypeDescr::TYPE_FLOAT32; }
   183 template<> inline const int TypeIDOfType<double>() { return ScalarTypeDescr::TYPE_FLOAT64; }
   184 template<> inline const int TypeIDOfType<uint8_clamped>() { return ScalarTypeDescr::TYPE_UINT8_CLAMPED; }
   186 template<typename ElementType>
   187 static inline JSObject *
   188 NewArray(JSContext *cx, uint32_t nelements);
   190 namespace {
   192 template<typename NativeType>
   193 class TypedArrayObjectTemplate : public TypedArrayObject
   194 {
   195   public:
   196     typedef NativeType ThisType;
   197     typedef TypedArrayObjectTemplate<NativeType> ThisTypedArrayObject;
   198     static const int ArrayTypeID() { return TypeIDOfType<NativeType>(); }
   199     static const bool ArrayTypeIsUnsigned() { return TypeIsUnsigned<NativeType>(); }
   200     static const bool ArrayTypeIsFloatingPoint() { return TypeIsFloatingPoint<NativeType>(); }
   202     static const size_t BYTES_PER_ELEMENT = sizeof(ThisType);
   204     static inline const Class *protoClass()
   205     {
   206         return &TypedArrayObject::protoClasses[ArrayTypeID()];
   207     }
   209     static inline const Class *instanceClass()
   210     {
   211         return &TypedArrayObject::classes[ArrayTypeID()];
   212     }
   214     static bool is(HandleValue v) {
   215         return v.isObject() && v.toObject().hasClass(instanceClass());
   216     }
   218     static void
   219     setIndexValue(TypedArrayObject &tarray, uint32_t index, double d)
   220     {
   221         // If the array is an integer array, we only handle up to
   222         // 32-bit ints from this point on.  if we want to handle
   223         // 64-bit ints, we'll need some changes.
   225         // Assign based on characteristics of the destination type
   226         if (ArrayTypeIsFloatingPoint()) {
   227             setIndex(tarray, index, NativeType(d));
   228         } else if (ArrayTypeIsUnsigned()) {
   229             JS_ASSERT(sizeof(NativeType) <= 4);
   230             uint32_t n = ToUint32(d);
   231             setIndex(tarray, index, NativeType(n));
   232         } else if (ArrayTypeID() == ScalarTypeDescr::TYPE_UINT8_CLAMPED) {
   233             // The uint8_clamped type has a special rounding converter
   234             // for doubles.
   235             setIndex(tarray, index, NativeType(d));
   236         } else {
   237             JS_ASSERT(sizeof(NativeType) <= 4);
   238             int32_t n = ToInt32(d);
   239             setIndex(tarray, index, NativeType(n));
   240         }
   241     }
   243     static TypedArrayObject *
   244     makeProtoInstance(JSContext *cx, HandleObject proto, AllocKind allocKind)
   245     {
   246         JS_ASSERT(proto);
   248         RootedObject obj(cx, NewBuiltinClassInstance(cx, instanceClass(), allocKind));
   249         if (!obj)
   250             return nullptr;
   252         types::TypeObject *type = cx->getNewType(obj->getClass(), proto.get());
   253         if (!type)
   254             return nullptr;
   255         obj->setType(type);
   257         return &obj->as<TypedArrayObject>();
   258     }
   260     static TypedArrayObject *
   261     makeTypedInstance(JSContext *cx, uint32_t len, AllocKind allocKind)
   262     {
   263         if (len * sizeof(NativeType) >= TypedArrayObject::SINGLETON_TYPE_BYTE_LENGTH) {
   264             return &NewBuiltinClassInstance(cx, instanceClass(), allocKind,
   265                                             SingletonObject)->as<TypedArrayObject>();
   266         }
   268         jsbytecode *pc;
   269         RootedScript script(cx, cx->currentScript(&pc));
   270         NewObjectKind newKind = script
   271                                 ? UseNewTypeForInitializer(script, pc, instanceClass())
   272                                 : GenericObject;
   273         RootedObject obj(cx, NewBuiltinClassInstance(cx, instanceClass(), allocKind, newKind));
   274         if (!obj)
   275             return nullptr;
   277         if (script) {
   278             if (!types::SetInitializerObjectType(cx, script, pc, obj, newKind))
   279                 return nullptr;
   280         }
   282         return &obj->as<TypedArrayObject>();
   283     }
   285     static JSObject *
   286     makeInstance(JSContext *cx, Handle<ArrayBufferObject *> buffer, uint32_t byteOffset, uint32_t len,
   287                  HandleObject proto)
   288     {
   289         JS_ASSERT_IF(!buffer, byteOffset == 0);
   291         gc::AllocKind allocKind = buffer
   292                                   ? GetGCObjectKind(instanceClass())
   293                                   : AllocKindForLazyBuffer(len * sizeof(NativeType));
   295         Rooted<TypedArrayObject*> obj(cx);
   296         if (proto)
   297             obj = makeProtoInstance(cx, proto, allocKind);
   298         else
   299             obj = makeTypedInstance(cx, len, allocKind);
   300         if (!obj)
   301             return nullptr;
   303         obj->setSlot(TYPE_SLOT, Int32Value(ArrayTypeID()));
   304         obj->setSlot(BUFFER_SLOT, ObjectOrNullValue(buffer));
   306         if (buffer) {
   307             InitArrayBufferViewDataPointer(obj, buffer, byteOffset);
   308         } else {
   309             void *data = obj->fixedData(FIXED_DATA_START);
   310             obj->initPrivate(data);
   311             memset(data, 0, len * sizeof(NativeType));
   312         }
   314         obj->setSlot(LENGTH_SLOT, Int32Value(len));
   315         obj->setSlot(BYTEOFFSET_SLOT, Int32Value(byteOffset));
   316         obj->setSlot(BYTELENGTH_SLOT, Int32Value(len * sizeof(NativeType)));
   317         obj->setSlot(NEXT_VIEW_SLOT, PrivateValue(nullptr));
   319 #ifdef DEBUG
   320         if (buffer) {
   321             uint32_t arrayByteLength = obj->byteLength();
   322             uint32_t arrayByteOffset = obj->byteOffset();
   323             uint32_t bufferByteLength = buffer->byteLength();
   324             JS_ASSERT_IF(!buffer->isNeutered(), buffer->dataPointer() <= obj->viewData());
   325             JS_ASSERT(bufferByteLength - arrayByteOffset >= arrayByteLength);
   326             JS_ASSERT(arrayByteOffset <= bufferByteLength);
   327         }
   329         // Verify that the private slot is at the expected place
   330         JS_ASSERT(obj->numFixedSlots() == DATA_SLOT);
   331 #endif
   333         if (buffer)
   334             buffer->addView(obj);
   336         return obj;
   337     }
   339     static JSObject *
   340     makeInstance(JSContext *cx, Handle<ArrayBufferObject *> bufobj, uint32_t byteOffset, uint32_t len)
   341     {
   342         RootedObject nullproto(cx, nullptr);
   343         return makeInstance(cx, bufobj, byteOffset, len, nullproto);
   344     }
   346     /*
   347      * new [Type]Array(length)
   348      * new [Type]Array(otherTypedArray)
   349      * new [Type]Array(JSArray)
   350      * new [Type]Array(ArrayBuffer, [optional] byteOffset, [optional] length)
   351      */
   352     static bool
   353     class_constructor(JSContext *cx, unsigned argc, Value *vp)
   354     {
   355         /* N.B. this is a constructor for protoClass, not instanceClass! */
   356         CallArgs args = CallArgsFromVp(argc, vp);
   357         JSObject *obj = create(cx, args);
   358         if (!obj)
   359             return false;
   360         args.rval().setObject(*obj);
   361         return true;
   362     }
   364     static JSObject *
   365     create(JSContext *cx, const CallArgs& args)
   366     {
   367         /* () or (number) */
   368         uint32_t len = 0;
   369         if (args.length() == 0 || ValueIsLength(args[0], &len))
   370             return fromLength(cx, len);
   372         /* (not an object) */
   373         if (!args[0].isObject()) {
   374             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
   375             return nullptr;
   376         }
   378         RootedObject dataObj(cx, &args.get(0).toObject());
   380         /*
   381          * (typedArray)
   382          * (type[] array)
   383          *
   384          * Otherwise create a new typed array and copy elements 0..len-1
   385          * properties from the object, treating it as some sort of array.
   386          * Note that offset and length will be ignored
   387          */
   388         if (!UncheckedUnwrap(dataObj)->is<ArrayBufferObject>() &&
   389             !UncheckedUnwrap(dataObj)->is<SharedArrayBufferObject>())
   390         {
   391             return fromArray(cx, dataObj);
   392         }
   394         /* (ArrayBuffer, [byteOffset, [length]]) */
   395         int32_t byteOffset = 0;
   396         int32_t length = -1;
   398         if (args.length() > 1) {
   399             if (!ToInt32(cx, args[1], &byteOffset))
   400                 return nullptr;
   401             if (byteOffset < 0) {
   402                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   403                                      JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "1");
   404                 return nullptr;
   405             }
   407             if (args.length() > 2) {
   408                 if (!ToInt32(cx, args[2], &length))
   409                     return nullptr;
   410                 if (length < 0) {
   411                     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   412                                          JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "2");
   413                     return nullptr;
   414                 }
   415             }
   416         }
   418         Rooted<JSObject*> proto(cx, nullptr);
   419         return fromBuffer(cx, dataObj, byteOffset, length, proto);
   420     }
   422     static bool IsThisClass(HandleValue v) {
   423         return v.isObject() && v.toObject().hasClass(instanceClass());
   424     }
   426     template<Value ValueGetter(TypedArrayObject *tarr)>
   427     static bool
   428     GetterImpl(JSContext *cx, CallArgs args)
   429     {
   430         JS_ASSERT(IsThisClass(args.thisv()));
   431         args.rval().set(ValueGetter(&args.thisv().toObject().as<TypedArrayObject>()));
   432         return true;
   433     }
   435     // ValueGetter is a function that takes an unwrapped typed array object and
   436     // returns a Value. Given such a function, Getter<> is a native that
   437     // retrieves a given Value, probably from a slot on the object.
   438     template<Value ValueGetter(TypedArrayObject *tarr)>
   439     static bool
   440     Getter(JSContext *cx, unsigned argc, Value *vp)
   441     {
   442         CallArgs args = CallArgsFromVp(argc, vp);
   443         return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
   444                                     ThisTypedArrayObject::GetterImpl<ValueGetter> >(cx, args);
   445     }
   447     static bool
   448     BufferGetterImpl(JSContext *cx, CallArgs args)
   449     {
   450         JS_ASSERT(IsThisClass(args.thisv()));
   451         Rooted<TypedArrayObject *> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>());
   452         if (!ensureHasBuffer(cx, tarray))
   453             return false;
   454         args.rval().set(bufferValue(tarray));
   455         return true;
   456     }
   458     // BufferGetter is a function that lazily constructs the array buffer for a
   459     // typed array before fetching it.
   460     static bool
   461     BufferGetter(JSContext *cx, unsigned argc, Value *vp)
   462     {
   463         CallArgs args = CallArgsFromVp(argc, vp);
   464         return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
   465                                     ThisTypedArrayObject::BufferGetterImpl>(cx, args);
   466     }
   468     // Define an accessor for a read-only property that invokes a native getter
   469     static bool
   470     DefineGetter(JSContext *cx, HandleObject proto, PropertyName *name, Native native)
   471     {
   472         RootedId id(cx, NameToId(name));
   473         unsigned attrs = JSPROP_SHARED | JSPROP_GETTER | JSPROP_PERMANENT;
   475         Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal());
   476         JSObject *getter = NewFunction(cx, NullPtr(), native, 0,
   477                                        JSFunction::NATIVE_FUN, global, NullPtr());
   478         if (!getter)
   479             return false;
   481         return DefineNativeProperty(cx, proto, id, UndefinedHandleValue,
   482                                     JS_DATA_TO_FUNC_PTR(PropertyOp, getter), nullptr,
   483                                     attrs);
   484     }
   486     static
   487     bool defineGetters(JSContext *cx, HandleObject proto)
   488     {
   489         if (!DefineGetter(cx, proto, cx->names().length, Getter<lengthValue>))
   490             return false;
   492         if (!DefineGetter(cx, proto, cx->names().buffer, BufferGetter))
   493             return false;
   495         if (!DefineGetter(cx, proto, cx->names().byteLength, Getter<byteLengthValue>))
   496             return false;
   498         if (!DefineGetter(cx, proto, cx->names().byteOffset, Getter<byteOffsetValue>))
   499             return false;
   501         return true;
   502     }
   504     /* subarray(start[, end]) */
   505     static bool
   506     fun_subarray_impl(JSContext *cx, CallArgs args)
   507     {
   508         JS_ASSERT(IsThisClass(args.thisv()));
   509         Rooted<TypedArrayObject*> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>());
   511         // these are the default values
   512         uint32_t length = tarray->length();
   513         uint32_t begin = 0, end = length;
   515         if (args.length() > 0) {
   516             if (!ToClampedIndex(cx, args[0], length, &begin))
   517                 return false;
   519             if (args.length() > 1) {
   520                 if (!ToClampedIndex(cx, args[1], length, &end))
   521                     return false;
   522             }
   523         }
   525         if (begin > end)
   526             begin = end;
   528         JSObject *nobj = createSubarray(cx, tarray, begin, end);
   529         if (!nobj)
   530             return false;
   531         args.rval().setObject(*nobj);
   532         return true;
   533     }
   535     static bool
   536     fun_subarray(JSContext *cx, unsigned argc, Value *vp)
   537     {
   538         CallArgs args = CallArgsFromVp(argc, vp);
   539         return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
   540                                     ThisTypedArrayObject::fun_subarray_impl>(cx, args);
   541     }
   543     /* move(begin, end, dest) */
   544     static bool
   545     fun_move_impl(JSContext *cx, CallArgs args)
   546     {
   547         JS_ASSERT(IsThisClass(args.thisv()));
   548         Rooted<TypedArrayObject*> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>());
   550         if (args.length() < 3) {
   551             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
   552             return false;
   553         }
   555         uint32_t srcBegin;
   556         uint32_t srcEnd;
   557         uint32_t dest;
   559         uint32_t originalLength = tarray->length();
   560         if (!ToClampedIndex(cx, args[0], originalLength, &srcBegin) ||
   561             !ToClampedIndex(cx, args[1], originalLength, &srcEnd) ||
   562             !ToClampedIndex(cx, args[2], originalLength, &dest))
   563         {
   564             return false;
   565         }
   567         if (srcBegin > srcEnd) {
   568             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
   569             return false;
   570         }
   572         uint32_t lengthDuringMove = tarray->length(); // beware ToClampedIndex
   573         uint32_t nelts = srcEnd - srcBegin;
   575         MOZ_ASSERT(dest <= INT32_MAX, "size limited to 2**31");
   576         MOZ_ASSERT(nelts <= INT32_MAX, "size limited to 2**31");
   577         if (dest + nelts > lengthDuringMove || srcEnd > lengthDuringMove) {
   578             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
   579             return false;
   580         }
   582         uint32_t byteDest = dest * sizeof(NativeType);
   583         uint32_t byteSrc = srcBegin * sizeof(NativeType);
   584         uint32_t byteSize = nelts * sizeof(NativeType);
   586 #ifdef DEBUG
   587         uint32_t viewByteLength = tarray->byteLength();
   588         JS_ASSERT(byteDest <= viewByteLength);
   589         JS_ASSERT(byteSrc <= viewByteLength);
   590         JS_ASSERT(byteDest + byteSize <= viewByteLength);
   591         JS_ASSERT(byteSrc + byteSize <= viewByteLength);
   593         // Should not overflow because size is limited to 2^31
   594         JS_ASSERT(byteDest + byteSize >= byteDest);
   595         JS_ASSERT(byteSrc + byteSize >= byteSrc);
   596 #endif
   598         uint8_t *data = static_cast<uint8_t*>(tarray->viewData());
   599         memmove(&data[byteDest], &data[byteSrc], byteSize);
   600         args.rval().setUndefined();
   601         return true;
   602     }
   604     static bool
   605     fun_move(JSContext *cx, unsigned argc, Value *vp)
   606     {
   607         CallArgs args = CallArgsFromVp(argc, vp);
   608         return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
   609                                     ThisTypedArrayObject::fun_move_impl>(cx, args);
   610     }
   612     /* set(array[, offset]) */
   613     static bool
   614     fun_set_impl(JSContext *cx, CallArgs args)
   615     {
   616         JS_ASSERT(IsThisClass(args.thisv()));
   617         Rooted<TypedArrayObject*> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>());
   619         // first arg must be either a typed array or a JS array
   620         if (args.length() == 0 || !args[0].isObject()) {
   621             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
   622             return false;
   623         }
   625         int32_t offset = 0;
   626         if (args.length() > 1) {
   627             if (!ToInt32(cx, args[1], &offset))
   628                 return false;
   630             if (offset < 0 || uint32_t(offset) > tarray->length()) {
   631                 // the given offset is bogus
   632                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   633                                      JSMSG_TYPED_ARRAY_BAD_INDEX, "2");
   634                 return false;
   635             }
   636         }
   638         if (!args[0].isObject()) {
   639             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
   640             return false;
   641         }
   643         RootedObject arg0(cx, args[0].toObjectOrNull());
   644         if (arg0->is<TypedArrayObject>()) {
   645             if (arg0->as<TypedArrayObject>().length() > tarray->length() - offset) {
   646                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
   647                 return false;
   648             }
   650             if (!copyFromTypedArray(cx, tarray, arg0, offset))
   651                 return false;
   652         } else {
   653             uint32_t len;
   654             if (!GetLengthProperty(cx, arg0, &len))
   655                 return false;
   657             if (uint32_t(offset) > tarray->length() || len > tarray->length() - offset) {
   658                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
   659                 return false;
   660             }
   662             if (!copyFromArray(cx, tarray, arg0, len, offset))
   663                 return false;
   664         }
   666         args.rval().setUndefined();
   667         return true;
   668     }
   670     static bool
   671     fun_set(JSContext *cx, unsigned argc, Value *vp)
   672     {
   673         CallArgs args = CallArgsFromVp(argc, vp);
   674         return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
   675                                     ThisTypedArrayObject::fun_set_impl>(cx, args);
   676     }
   678   public:
   679     static JSObject *
   680     fromBuffer(JSContext *cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt,
   681                HandleObject proto)
   682     {
   683         if (!ObjectClassIs(bufobj, ESClass_ArrayBuffer, cx)) {
   684             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
   685             return nullptr; // must be arrayBuffer
   686         }
   688         JS_ASSERT(IsArrayBuffer(bufobj) || bufobj->is<ProxyObject>());
   689         if (bufobj->is<ProxyObject>()) {
   690             /*
   691              * Normally, NonGenericMethodGuard handles the case of transparent
   692              * wrappers. However, we have a peculiar situation: we want to
   693              * construct the new typed array in the compartment of the buffer,
   694              * so that the typed array can point directly at their buffer's
   695              * data without crossing compartment boundaries. So we use the
   696              * machinery underlying NonGenericMethodGuard directly to proxy the
   697              * native call. We will end up with a wrapper in the origin
   698              * compartment for a view in the target compartment referencing the
   699              * ArrayBufferObject in that same compartment.
   700              */
   701             JSObject *wrapped = CheckedUnwrap(bufobj);
   702             if (!wrapped) {
   703                 JS_ReportError(cx, "Permission denied to access object");
   704                 return nullptr;
   705             }
   706             if (IsArrayBuffer(wrapped)) {
   707                 /*
   708                  * And for even more fun, the new view's prototype should be
   709                  * set to the origin compartment's prototype object, not the
   710                  * target's (specifically, the actual view in the target
   711                  * compartment will use as its prototype a wrapper around the
   712                  * origin compartment's view.prototype object).
   713                  *
   714                  * Rather than hack some crazy solution together, implement
   715                  * this all using a private helper function, created when
   716                  * ArrayBufferObject was initialized and cached in the global.
   717                  * This reuses all the existing cross-compartment crazy so we
   718                  * don't have to do anything *uniquely* crazy here.
   719                  */
   721                 Rooted<JSObject*> proto(cx);
   722                 if (!GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &proto))
   723                     return nullptr;
   725                 InvokeArgs args(cx);
   726                 if (!args.init(3))
   727                     return nullptr;
   729                 args.setCallee(cx->compartment()->maybeGlobal()->createArrayFromBuffer<NativeType>());
   730                 args.setThis(ObjectValue(*bufobj));
   731                 args[0].setNumber(byteOffset);
   732                 args[1].setInt32(lengthInt);
   733                 args[2].setObject(*proto);
   735                 if (!Invoke(cx, args))
   736                     return nullptr;
   737                 return &args.rval().toObject();
   738             }
   739         }
   741         if (!IsArrayBuffer(bufobj)) {
   742             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
   743             return nullptr; // must be arrayBuffer
   744         }
   746         Rooted<ArrayBufferObject *> buffer(cx, &AsArrayBuffer(bufobj));
   748         if (byteOffset > buffer->byteLength() || byteOffset % sizeof(NativeType) != 0) {
   749             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
   750             return nullptr; // invalid byteOffset
   751         }
   753         uint32_t len;
   754         if (lengthInt == -1) {
   755             len = (buffer->byteLength() - byteOffset) / sizeof(NativeType);
   756             if (len * sizeof(NativeType) != buffer->byteLength() - byteOffset) {
   757                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   758                                      JSMSG_TYPED_ARRAY_BAD_ARGS);
   759                 return nullptr; // given byte array doesn't map exactly to sizeof(NativeType) * N
   760             }
   761         } else {
   762             len = uint32_t(lengthInt);
   763         }
   765         // Go slowly and check for overflow.
   766         uint32_t arrayByteLength = len * sizeof(NativeType);
   767         if (len >= INT32_MAX / sizeof(NativeType) || byteOffset >= INT32_MAX - arrayByteLength) {
   768             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
   769             return nullptr; // overflow when calculating byteOffset + len * sizeof(NativeType)
   770         }
   772         if (arrayByteLength + byteOffset > buffer->byteLength()) {
   773             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
   774             return nullptr; // byteOffset + len is too big for the arraybuffer
   775         }
   777         return makeInstance(cx, buffer, byteOffset, len, proto);
   778     }
   780     static bool
   781     maybeCreateArrayBuffer(JSContext *cx, uint32_t nelements, MutableHandle<ArrayBufferObject *> buffer)
   782     {
   783         // Make sure that array elements evenly divide into the inline buffer's
   784         // size, for the test below.
   785         JS_STATIC_ASSERT((INLINE_BUFFER_LIMIT / sizeof(NativeType)) * sizeof(NativeType) == INLINE_BUFFER_LIMIT);
   787         if (nelements <= INLINE_BUFFER_LIMIT / sizeof(NativeType)) {
   788             // The array's data can be inline, and the buffer created lazily.
   789             return true;
   790         }
   792         if (nelements >= INT32_MAX / sizeof(NativeType)) {
   793             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   794                                  JSMSG_NEED_DIET, "size and count");
   795             return false;
   796         }
   798         buffer.set(ArrayBufferObject::create(cx, nelements * sizeof(NativeType)));
   799         return !!buffer;
   800     }
   802     static JSObject *
   803     fromLength(JSContext *cx, uint32_t nelements)
   804     {
   805         Rooted<ArrayBufferObject *> buffer(cx);
   806         if (!maybeCreateArrayBuffer(cx, nelements, &buffer))
   807             return nullptr;
   808         return makeInstance(cx, buffer, 0, nelements);
   809     }
   811     static JSObject *
   812     fromArray(JSContext *cx, HandleObject other)
   813     {
   814         uint32_t len;
   815         if (other->is<TypedArrayObject>()) {
   816             len = other->as<TypedArrayObject>().length();
   817         } else if (!GetLengthProperty(cx, other, &len)) {
   818             return nullptr;
   819         }
   821         Rooted<ArrayBufferObject *> buffer(cx);
   822         if (!maybeCreateArrayBuffer(cx, len, &buffer))
   823             return nullptr;
   825         RootedObject obj(cx, makeInstance(cx, buffer, 0, len));
   826         if (!obj || !copyFromArray(cx, obj, other, len))
   827             return nullptr;
   828         return obj;
   829     }
   831     static const NativeType
   832     getIndex(JSObject *obj, uint32_t index)
   833     {
   834         TypedArrayObject &tarray = obj->as<TypedArrayObject>();
   835         MOZ_ASSERT(index < tarray.length());
   836         return static_cast<const NativeType*>(tarray.viewData())[index];
   837     }
   839     static void
   840     setIndex(TypedArrayObject &tarray, uint32_t index, NativeType val)
   841     {
   842         MOZ_ASSERT(index < tarray.length());
   843         static_cast<NativeType*>(tarray.viewData())[index] = val;
   844     }
   846     static Value getIndexValue(JSObject *tarray, uint32_t index);
   848     static JSObject *
   849     createSubarray(JSContext *cx, HandleObject tarrayArg, uint32_t begin, uint32_t end)
   850     {
   851         Rooted<TypedArrayObject*> tarray(cx, &tarrayArg->as<TypedArrayObject>());
   853         if (begin > tarray->length() || end > tarray->length() || begin > end) {
   854             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
   855             return nullptr;
   856         }
   858         if (!ensureHasBuffer(cx, tarray))
   859             return nullptr;
   861         Rooted<ArrayBufferObject *> bufobj(cx, tarray->buffer());
   862         JS_ASSERT(bufobj);
   864         uint32_t length = end - begin;
   866         JS_ASSERT(begin < UINT32_MAX / sizeof(NativeType));
   867         uint32_t arrayByteOffset = tarray->byteOffset();
   868         JS_ASSERT(UINT32_MAX - begin * sizeof(NativeType) >= arrayByteOffset);
   869         uint32_t byteOffset = arrayByteOffset + begin * sizeof(NativeType);
   871         return makeInstance(cx, bufobj, byteOffset, length);
   872     }
   874   protected:
   875     static NativeType
   876     doubleToNative(double d)
   877     {
   878         if (TypeIsFloatingPoint<NativeType>()) {
   879 #ifdef JS_MORE_DETERMINISTIC
   880             // The JS spec doesn't distinguish among different NaN values, and
   881             // it deliberately doesn't specify the bit pattern written to a
   882             // typed array when NaN is written into it.  This bit-pattern
   883             // inconsistency could confuse deterministic testing, so always
   884             // canonicalize NaN values in more-deterministic builds.
   885             d = CanonicalizeNaN(d);
   886 #endif
   887             return NativeType(d);
   888         }
   889         if (MOZ_UNLIKELY(IsNaN(d)))
   890             return NativeType(0);
   891         if (TypeIsUnsigned<NativeType>())
   892             return NativeType(ToUint32(d));
   893         return NativeType(ToInt32(d));
   894     }
   896     static bool
   897     canConvertInfallibly(const Value &v)
   898     {
   899         return v.isNumber() || v.isBoolean() || v.isNull() || v.isUndefined();
   900     }
   902     static NativeType
   903     infallibleValueToNative(const Value &v)
   904     {
   905         if (v.isInt32())
   906             return v.toInt32();
   907         if (v.isDouble())
   908             return doubleToNative(v.toDouble());
   909         if (v.isBoolean())
   910             return v.toBoolean();
   911         if (v.isNull())
   912             return 0;
   914         MOZ_ASSERT(v.isUndefined());
   915         return ArrayTypeIsFloatingPoint() ? NativeType(GenericNaN()) : NativeType(0);
   916     }
   918     static bool
   919     valueToNative(JSContext *cx, const Value &v, NativeType *result)
   920     {
   921         MOZ_ASSERT(!v.isMagic());
   923         if (MOZ_LIKELY(canConvertInfallibly(v))) {
   924             *result = infallibleValueToNative(v);
   925             return true;
   926         }
   928         double d;
   929         MOZ_ASSERT(v.isString() || v.isObject());
   930         if (!(v.isString() ? StringToNumber(cx, v.toString(), &d) : ToNumber(cx, v, &d)))
   931             return false;
   933         *result = doubleToNative(d);
   934         return true;
   935     }
   937     static bool
   938     copyFromArray(JSContext *cx, HandleObject thisTypedArrayObj,
   939                   HandleObject source, uint32_t len, uint32_t offset = 0)
   940     {
   941         Rooted<TypedArrayObject*> thisTypedArray(cx, &thisTypedArrayObj->as<TypedArrayObject>());
   942         JS_ASSERT(offset <= thisTypedArray->length());
   943         JS_ASSERT(len <= thisTypedArray->length() - offset);
   944         if (source->is<TypedArrayObject>())
   945             return copyFromTypedArray(cx, thisTypedArray, source, offset);
   947         uint32_t i = 0;
   948         if (source->isNative()) {
   949             // Attempt fast-path infallible conversion of dense elements up to
   950             // the first potentially side-effectful lookup or conversion.
   951             uint32_t bound = Min(source->getDenseInitializedLength(), len);
   953             NativeType *dest = static_cast<NativeType*>(thisTypedArray->viewData()) + offset;
   955             const Value *srcValues = source->getDenseElements();
   956             for (; i < bound; i++) {
   957                 // Note: holes don't convert infallibly.
   958                 if (!canConvertInfallibly(srcValues[i]))
   959                     break;
   960                 dest[i] = infallibleValueToNative(srcValues[i]);
   961             }
   962             if (i == len)
   963                 return true;
   964         }
   966         // Convert and copy any remaining elements generically.
   967         RootedValue v(cx);
   968         for (; i < len; i++) {
   969             if (!JSObject::getElement(cx, source, source, i, &v))
   970                 return false;
   972             NativeType n;
   973             if (!valueToNative(cx, v, &n))
   974                 return false;
   976             len = Min(len, thisTypedArray->length());
   977             if (i >= len)
   978                 break;
   980             // Compute every iteration in case getElement acts wacky.
   981             void *data = thisTypedArray->viewData();
   982             static_cast<NativeType*>(data)[offset + i] = n;
   983         }
   985         return true;
   986     }
   988     static bool
   989     copyFromTypedArray(JSContext *cx, JSObject *thisTypedArrayObj, JSObject *tarrayObj,
   990                        uint32_t offset)
   991     {
   992         TypedArrayObject *thisTypedArray = &thisTypedArrayObj->as<TypedArrayObject>();
   993         TypedArrayObject *tarray = &tarrayObj->as<TypedArrayObject>();
   994         JS_ASSERT(offset <= thisTypedArray->length());
   995         JS_ASSERT(tarray->length() <= thisTypedArray->length() - offset);
   996         if (tarray->buffer() == thisTypedArray->buffer())
   997             return copyFromWithOverlap(cx, thisTypedArray, tarray, offset);
   999         NativeType *dest = static_cast<NativeType*>(thisTypedArray->viewData()) + offset;
  1001         if (tarray->type() == thisTypedArray->type()) {
  1002             js_memcpy(dest, tarray->viewData(), tarray->byteLength());
  1003             return true;
  1006         unsigned srclen = tarray->length();
  1007         switch (tarray->type()) {
  1008           case ScalarTypeDescr::TYPE_INT8: {
  1009             int8_t *src = static_cast<int8_t*>(tarray->viewData());
  1010             for (unsigned i = 0; i < srclen; ++i)
  1011                 *dest++ = NativeType(*src++);
  1012             break;
  1014           case ScalarTypeDescr::TYPE_UINT8:
  1015           case ScalarTypeDescr::TYPE_UINT8_CLAMPED: {
  1016             uint8_t *src = static_cast<uint8_t*>(tarray->viewData());
  1017             for (unsigned i = 0; i < srclen; ++i)
  1018                 *dest++ = NativeType(*src++);
  1019             break;
  1021           case ScalarTypeDescr::TYPE_INT16: {
  1022             int16_t *src = static_cast<int16_t*>(tarray->viewData());
  1023             for (unsigned i = 0; i < srclen; ++i)
  1024                 *dest++ = NativeType(*src++);
  1025             break;
  1027           case ScalarTypeDescr::TYPE_UINT16: {
  1028             uint16_t *src = static_cast<uint16_t*>(tarray->viewData());
  1029             for (unsigned i = 0; i < srclen; ++i)
  1030                 *dest++ = NativeType(*src++);
  1031             break;
  1033           case ScalarTypeDescr::TYPE_INT32: {
  1034             int32_t *src = static_cast<int32_t*>(tarray->viewData());
  1035             for (unsigned i = 0; i < srclen; ++i)
  1036                 *dest++ = NativeType(*src++);
  1037             break;
  1039           case ScalarTypeDescr::TYPE_UINT32: {
  1040             uint32_t *src = static_cast<uint32_t*>(tarray->viewData());
  1041             for (unsigned i = 0; i < srclen; ++i)
  1042                 *dest++ = NativeType(*src++);
  1043             break;
  1045           case ScalarTypeDescr::TYPE_FLOAT32: {
  1046             float *src = static_cast<float*>(tarray->viewData());
  1047             for (unsigned i = 0; i < srclen; ++i)
  1048                 *dest++ = NativeType(*src++);
  1049             break;
  1051           case ScalarTypeDescr::TYPE_FLOAT64: {
  1052             double *src = static_cast<double*>(tarray->viewData());
  1053             for (unsigned i = 0; i < srclen; ++i)
  1054                 *dest++ = NativeType(*src++);
  1055             break;
  1057           default:
  1058             MOZ_ASSUME_UNREACHABLE("copyFrom with a TypedArrayObject of unknown type");
  1061         return true;
  1064     static bool
  1065     copyFromWithOverlap(JSContext *cx, JSObject *selfObj, JSObject *tarrayObj, uint32_t offset)
  1067         TypedArrayObject *self = &selfObj->as<TypedArrayObject>();
  1068         TypedArrayObject *tarray = &tarrayObj->as<TypedArrayObject>();
  1070         JS_ASSERT(offset <= self->length());
  1072         NativeType *dest = static_cast<NativeType*>(self->viewData()) + offset;
  1073         uint32_t byteLength = tarray->byteLength();
  1075         if (tarray->type() == self->type()) {
  1076             memmove(dest, tarray->viewData(), byteLength);
  1077             return true;
  1080         // We have to make a copy of the source array here, since
  1081         // there's overlap, and we have to convert types.
  1082         void *srcbuf = cx->malloc_(byteLength);
  1083         if (!srcbuf)
  1084             return false;
  1085         js_memcpy(srcbuf, tarray->viewData(), byteLength);
  1087         uint32_t len = tarray->length();
  1088         switch (tarray->type()) {
  1089           case ScalarTypeDescr::TYPE_INT8: {
  1090             int8_t *src = (int8_t*) srcbuf;
  1091             for (unsigned i = 0; i < len; ++i)
  1092                 *dest++ = NativeType(*src++);
  1093             break;
  1095           case ScalarTypeDescr::TYPE_UINT8:
  1096           case ScalarTypeDescr::TYPE_UINT8_CLAMPED: {
  1097             uint8_t *src = (uint8_t*) srcbuf;
  1098             for (unsigned i = 0; i < len; ++i)
  1099                 *dest++ = NativeType(*src++);
  1100             break;
  1102           case ScalarTypeDescr::TYPE_INT16: {
  1103             int16_t *src = (int16_t*) srcbuf;
  1104             for (unsigned i = 0; i < len; ++i)
  1105                 *dest++ = NativeType(*src++);
  1106             break;
  1108           case ScalarTypeDescr::TYPE_UINT16: {
  1109             uint16_t *src = (uint16_t*) srcbuf;
  1110             for (unsigned i = 0; i < len; ++i)
  1111                 *dest++ = NativeType(*src++);
  1112             break;
  1114           case ScalarTypeDescr::TYPE_INT32: {
  1115             int32_t *src = (int32_t*) srcbuf;
  1116             for (unsigned i = 0; i < len; ++i)
  1117                 *dest++ = NativeType(*src++);
  1118             break;
  1120           case ScalarTypeDescr::TYPE_UINT32: {
  1121             uint32_t *src = (uint32_t*) srcbuf;
  1122             for (unsigned i = 0; i < len; ++i)
  1123                 *dest++ = NativeType(*src++);
  1124             break;
  1126           case ScalarTypeDescr::TYPE_FLOAT32: {
  1127             float *src = (float*) srcbuf;
  1128             for (unsigned i = 0; i < len; ++i)
  1129                 *dest++ = NativeType(*src++);
  1130             break;
  1132           case ScalarTypeDescr::TYPE_FLOAT64: {
  1133             double *src = (double*) srcbuf;
  1134             for (unsigned i = 0; i < len; ++i)
  1135                 *dest++ = NativeType(*src++);
  1136             break;
  1138           default:
  1139             MOZ_ASSUME_UNREACHABLE("copyFromWithOverlap with a TypedArrayObject of unknown type");
  1142         js_free(srcbuf);
  1143         return true;
  1145 };
  1147 class Int8ArrayObject : public TypedArrayObjectTemplate<int8_t> {
  1148   public:
  1149     enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_INT8 };
  1150     static const JSProtoKey key = JSProto_Int8Array;
  1151     static const JSFunctionSpec jsfuncs[];
  1152 };
  1153 class Uint8ArrayObject : public TypedArrayObjectTemplate<uint8_t> {
  1154   public:
  1155     enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_UINT8 };
  1156     static const JSProtoKey key = JSProto_Uint8Array;
  1157     static const JSFunctionSpec jsfuncs[];
  1158 };
  1159 class Int16ArrayObject : public TypedArrayObjectTemplate<int16_t> {
  1160   public:
  1161     enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_INT16 };
  1162     static const JSProtoKey key = JSProto_Int16Array;
  1163     static const JSFunctionSpec jsfuncs[];
  1164 };
  1165 class Uint16ArrayObject : public TypedArrayObjectTemplate<uint16_t> {
  1166   public:
  1167     enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_UINT16 };
  1168     static const JSProtoKey key = JSProto_Uint16Array;
  1169     static const JSFunctionSpec jsfuncs[];
  1170 };
  1171 class Int32ArrayObject : public TypedArrayObjectTemplate<int32_t> {
  1172   public:
  1173     enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_INT32 };
  1174     static const JSProtoKey key = JSProto_Int32Array;
  1175     static const JSFunctionSpec jsfuncs[];
  1176 };
  1177 class Uint32ArrayObject : public TypedArrayObjectTemplate<uint32_t> {
  1178   public:
  1179     enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_UINT32 };
  1180     static const JSProtoKey key = JSProto_Uint32Array;
  1181     static const JSFunctionSpec jsfuncs[];
  1182 };
  1183 class Float32ArrayObject : public TypedArrayObjectTemplate<float> {
  1184   public:
  1185     enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_FLOAT32 };
  1186     static const JSProtoKey key = JSProto_Float32Array;
  1187     static const JSFunctionSpec jsfuncs[];
  1188 };
  1189 class Float64ArrayObject : public TypedArrayObjectTemplate<double> {
  1190   public:
  1191     enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_FLOAT64 };
  1192     static const JSProtoKey key = JSProto_Float64Array;
  1193     static const JSFunctionSpec jsfuncs[];
  1194 };
  1195 class Uint8ClampedArrayObject : public TypedArrayObjectTemplate<uint8_clamped> {
  1196   public:
  1197     enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_UINT8_CLAMPED };
  1198     static const JSProtoKey key = JSProto_Uint8ClampedArray;
  1199     static const JSFunctionSpec jsfuncs[];
  1200 };
  1202 } /* anonymous namespace */
  1204 template<typename T>
  1205 bool
  1206 ArrayBufferObject::createTypedArrayFromBufferImpl(JSContext *cx, CallArgs args)
  1208     typedef TypedArrayObjectTemplate<T> ArrayType;
  1209     JS_ASSERT(IsArrayBuffer(args.thisv()));
  1210     JS_ASSERT(args.length() == 3);
  1212     Rooted<JSObject*> buffer(cx, &args.thisv().toObject());
  1213     Rooted<JSObject*> proto(cx, &args[2].toObject());
  1215     Rooted<JSObject*> obj(cx);
  1216     double byteOffset = args[0].toNumber();
  1217     MOZ_ASSERT(0 <= byteOffset);
  1218     MOZ_ASSERT(byteOffset <= UINT32_MAX);
  1219     MOZ_ASSERT(byteOffset == uint32_t(byteOffset));
  1220     obj = ArrayType::fromBuffer(cx, buffer, uint32_t(byteOffset), args[1].toInt32(), proto);
  1221     if (!obj)
  1222         return false;
  1223     args.rval().setObject(*obj);
  1224     return true;
  1227 template<typename T>
  1228 bool
  1229 ArrayBufferObject::createTypedArrayFromBuffer(JSContext *cx, unsigned argc, Value *vp)
  1231     CallArgs args = CallArgsFromVp(argc, vp);
  1232     return CallNonGenericMethod<IsArrayBuffer, createTypedArrayFromBufferImpl<T> >(cx, args);
  1235 // this default implementation is only valid for integer types
  1236 // less than 32-bits in size.
  1237 template<typename NativeType>
  1238 Value
  1239 TypedArrayObjectTemplate<NativeType>::getIndexValue(JSObject *tarray, uint32_t index)
  1241     JS_STATIC_ASSERT(sizeof(NativeType) < 4);
  1243     return Int32Value(getIndex(tarray, index));
  1246 namespace {
  1248 // and we need to specialize for 32-bit integers and floats
  1249 template<>
  1250 Value
  1251 TypedArrayObjectTemplate<int32_t>::getIndexValue(JSObject *tarray, uint32_t index)
  1253     return Int32Value(getIndex(tarray, index));
  1256 template<>
  1257 Value
  1258 TypedArrayObjectTemplate<uint32_t>::getIndexValue(JSObject *tarray, uint32_t index)
  1260     uint32_t val = getIndex(tarray, index);
  1261     return NumberValue(val);
  1264 template<>
  1265 Value
  1266 TypedArrayObjectTemplate<float>::getIndexValue(JSObject *tarray, uint32_t index)
  1268     float val = getIndex(tarray, index);
  1269     double dval = val;
  1271     /*
  1272      * Doubles in typed arrays could be typed-punned arrays of integers. This
  1273      * could allow user code to break the engine-wide invariant that only
  1274      * canonical nans are stored into jsvals, which means user code could
  1275      * confuse the engine into interpreting a double-typed jsval as an
  1276      * object-typed jsval.
  1278      * This could be removed for platforms/compilers known to convert a 32-bit
  1279      * non-canonical nan to a 64-bit canonical nan.
  1280      */
  1281     return DoubleValue(CanonicalizeNaN(dval));
  1284 template<>
  1285 Value
  1286 TypedArrayObjectTemplate<double>::getIndexValue(JSObject *tarray, uint32_t index)
  1288     double val = getIndex(tarray, index);
  1290     /*
  1291      * Doubles in typed arrays could be typed-punned arrays of integers. This
  1292      * could allow user code to break the engine-wide invariant that only
  1293      * canonical nans are stored into jsvals, which means user code could
  1294      * confuse the engine into interpreting a double-typed jsval as an
  1295      * object-typed jsval.
  1296      */
  1297     return DoubleValue(CanonicalizeNaN(val));
  1300 } /* anonymous namespace */
  1302 static NewObjectKind
  1303 DataViewNewObjectKind(JSContext *cx, uint32_t byteLength, JSObject *proto)
  1305     if (!proto && byteLength >= TypedArrayObject::SINGLETON_TYPE_BYTE_LENGTH)
  1306         return SingletonObject;
  1307     jsbytecode *pc;
  1308     JSScript *script = cx->currentScript(&pc);
  1309     if (!script)
  1310         return GenericObject;
  1311     return types::UseNewTypeForInitializer(script, pc, &DataViewObject::class_);
  1314 inline DataViewObject *
  1315 DataViewObject::create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength,
  1316                        Handle<ArrayBufferObject*> arrayBuffer, JSObject *protoArg)
  1318     JS_ASSERT(byteOffset <= INT32_MAX);
  1319     JS_ASSERT(byteLength <= INT32_MAX);
  1321     RootedObject proto(cx, protoArg);
  1322     RootedObject obj(cx);
  1324     // This is overflow-safe: 2 * INT32_MAX is still a valid uint32_t.
  1325     if (byteOffset + byteLength > arrayBuffer->byteLength()) {
  1326         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1");
  1327         return nullptr;
  1331     NewObjectKind newKind = DataViewNewObjectKind(cx, byteLength, proto);
  1332     obj = NewBuiltinClassInstance(cx, &class_, newKind);
  1333     if (!obj)
  1334         return nullptr;
  1336     if (proto) {
  1337         types::TypeObject *type = cx->getNewType(&class_, TaggedProto(proto));
  1338         if (!type)
  1339             return nullptr;
  1340         obj->setType(type);
  1341     } else if (byteLength >= TypedArrayObject::SINGLETON_TYPE_BYTE_LENGTH) {
  1342         JS_ASSERT(obj->hasSingletonType());
  1343     } else {
  1344         jsbytecode *pc;
  1345         RootedScript script(cx, cx->currentScript(&pc));
  1346         if (script) {
  1347             if (!types::SetInitializerObjectType(cx, script, pc, obj, newKind))
  1348                 return nullptr;
  1352     DataViewObject &dvobj = obj->as<DataViewObject>();
  1353     dvobj.setFixedSlot(BYTEOFFSET_SLOT, Int32Value(byteOffset));
  1354     dvobj.setFixedSlot(BYTELENGTH_SLOT, Int32Value(byteLength));
  1355     dvobj.setFixedSlot(BUFFER_SLOT, ObjectValue(*arrayBuffer));
  1356     dvobj.setFixedSlot(NEXT_VIEW_SLOT, PrivateValue(nullptr));
  1357     InitArrayBufferViewDataPointer(&dvobj, arrayBuffer, byteOffset);
  1358     JS_ASSERT(byteOffset + byteLength <= arrayBuffer->byteLength());
  1360     // Verify that the private slot is at the expected place
  1361     JS_ASSERT(dvobj.numFixedSlots() == DATA_SLOT);
  1363     arrayBuffer->addView(&dvobj);
  1365     return &dvobj;
  1368 bool
  1369 DataViewObject::construct(JSContext *cx, JSObject *bufobj, const CallArgs &args, HandleObject proto)
  1371     if (!IsArrayBuffer(bufobj)) {
  1372         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
  1373                              "DataView", "ArrayBuffer", bufobj->getClass()->name);
  1374         return false;
  1377     Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(bufobj));
  1378     uint32_t bufferLength = buffer->byteLength();
  1379     uint32_t byteOffset = 0;
  1380     uint32_t byteLength = bufferLength;
  1382     if (args.length() > 1) {
  1383         if (!ToUint32(cx, args[1], &byteOffset))
  1384             return false;
  1385         if (byteOffset > INT32_MAX) {
  1386             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
  1387                                  JSMSG_ARG_INDEX_OUT_OF_RANGE, "1");
  1388             return false;
  1391         if (args.length() > 2) {
  1392             if (!ToUint32(cx, args[2], &byteLength))
  1393                 return false;
  1394             if (byteLength > INT32_MAX) {
  1395                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
  1396                                      JSMSG_ARG_INDEX_OUT_OF_RANGE, "2");
  1397                 return false;
  1399         } else {
  1400             if (byteOffset > bufferLength) {
  1401                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
  1402                                      JSMSG_ARG_INDEX_OUT_OF_RANGE, "1");
  1403                 return false;
  1406             byteLength = bufferLength - byteOffset;
  1410     /* The sum of these cannot overflow a uint32_t */
  1411     JS_ASSERT(byteOffset <= INT32_MAX);
  1412     JS_ASSERT(byteLength <= INT32_MAX);
  1414     if (byteOffset + byteLength > bufferLength) {
  1415         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1");
  1416         return false;
  1419     JSObject *obj = DataViewObject::create(cx, byteOffset, byteLength, buffer, proto);
  1420     if (!obj)
  1421         return false;
  1422     args.rval().setObject(*obj);
  1423     return true;
  1426 bool
  1427 DataViewObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
  1429     CallArgs args = CallArgsFromVp(argc, vp);
  1431     RootedObject bufobj(cx);
  1432     if (!GetFirstArgumentAsObject(cx, args, "DataView constructor", &bufobj))
  1433         return false;
  1435     if (bufobj->is<WrapperObject>() && IsArrayBuffer(UncheckedUnwrap(bufobj))) {
  1436         Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal());
  1437         Rooted<JSObject*> proto(cx, global->getOrCreateDataViewPrototype(cx));
  1438         if (!proto)
  1439             return false;
  1441         InvokeArgs args2(cx);
  1442         if (!args2.init(args.length() + 1))
  1443             return false;
  1444         args2.setCallee(global->createDataViewForThis());
  1445         args2.setThis(ObjectValue(*bufobj));
  1446         PodCopy(args2.array(), args.array(), args.length());
  1447         args2[args.length()].setObject(*proto);
  1448         if (!Invoke(cx, args2))
  1449             return false;
  1450         args.rval().set(args2.rval());
  1451         return true;
  1454     return construct(cx, bufobj, args, NullPtr());
  1457 template <typename NativeType>
  1458 /* static */ uint8_t *
  1459 DataViewObject::getDataPointer(JSContext *cx, Handle<DataViewObject*> obj, uint32_t offset)
  1461     const size_t TypeSize = sizeof(NativeType);
  1462     if (offset > UINT32_MAX - TypeSize || offset + TypeSize > obj->byteLength()) {
  1463         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1");
  1464         return nullptr;
  1467     return static_cast<uint8_t*>(obj->dataPointer()) + offset;
  1470 static inline bool
  1471 needToSwapBytes(bool littleEndian)
  1473 #if IS_LITTLE_ENDIAN
  1474     return !littleEndian;
  1475 #else
  1476     return littleEndian;
  1477 #endif
  1480 static inline uint8_t
  1481 swapBytes(uint8_t x)
  1483     return x;
  1486 static inline uint16_t
  1487 swapBytes(uint16_t x)
  1489     return ((x & 0xff) << 8) | (x >> 8);
  1492 static inline uint32_t
  1493 swapBytes(uint32_t x)
  1495     return ((x & 0xff) << 24) |
  1496            ((x & 0xff00) << 8) |
  1497            ((x & 0xff0000) >> 8) |
  1498            ((x & 0xff000000) >> 24);
  1501 static inline uint64_t
  1502 swapBytes(uint64_t x)
  1504     uint32_t a = x & UINT32_MAX;
  1505     uint32_t b = x >> 32;
  1506     return (uint64_t(swapBytes(a)) << 32) | swapBytes(b);
  1509 template <typename DataType> struct DataToRepType { typedef DataType result; };
  1510 template <> struct DataToRepType<int8_t>   { typedef uint8_t result; };
  1511 template <> struct DataToRepType<uint8_t>  { typedef uint8_t result; };
  1512 template <> struct DataToRepType<int16_t>  { typedef uint16_t result; };
  1513 template <> struct DataToRepType<uint16_t> { typedef uint16_t result; };
  1514 template <> struct DataToRepType<int32_t>  { typedef uint32_t result; };
  1515 template <> struct DataToRepType<uint32_t> { typedef uint32_t result; };
  1516 template <> struct DataToRepType<float>    { typedef uint32_t result; };
  1517 template <> struct DataToRepType<double>   { typedef uint64_t result; };
  1519 template <typename DataType>
  1520 struct DataViewIO
  1522     typedef typename DataToRepType<DataType>::result ReadWriteType;
  1524     static void fromBuffer(DataType *dest, const uint8_t *unalignedBuffer, bool wantSwap)
  1526         JS_ASSERT((reinterpret_cast<uintptr_t>(dest) & (Min<size_t>(MOZ_ALIGNOF(void*), sizeof(DataType)) - 1)) == 0);
  1527         memcpy((void *) dest, unalignedBuffer, sizeof(ReadWriteType));
  1528         if (wantSwap) {
  1529             ReadWriteType *rwDest = reinterpret_cast<ReadWriteType *>(dest);
  1530             *rwDest = swapBytes(*rwDest);
  1534     static void toBuffer(uint8_t *unalignedBuffer, const DataType *src, bool wantSwap)
  1536         JS_ASSERT((reinterpret_cast<uintptr_t>(src) & (Min<size_t>(MOZ_ALIGNOF(void*), sizeof(DataType)) - 1)) == 0);
  1537         ReadWriteType temp = *reinterpret_cast<const ReadWriteType *>(src);
  1538         if (wantSwap)
  1539             temp = swapBytes(temp);
  1540         memcpy(unalignedBuffer, (void *) &temp, sizeof(ReadWriteType));
  1542 };
  1544 template<typename NativeType>
  1545 /* static */ bool
  1546 DataViewObject::read(JSContext *cx, Handle<DataViewObject*> obj,
  1547                      CallArgs &args, NativeType *val, const char *method)
  1549     if (args.length() < 1) {
  1550         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
  1551                              JSMSG_MORE_ARGS_NEEDED, method, "0", "s");
  1552         return false;
  1555     uint32_t offset;
  1556     if (!ToUint32(cx, args[0], &offset))
  1557         return false;
  1559     bool fromLittleEndian = args.length() >= 2 && ToBoolean(args[1]);
  1561     uint8_t *data = DataViewObject::getDataPointer<NativeType>(cx, obj, offset);
  1562     if (!data)
  1563         return false;
  1565     DataViewIO<NativeType>::fromBuffer(val, data, needToSwapBytes(fromLittleEndian));
  1566     return true;
  1569 template <typename NativeType>
  1570 static inline bool
  1571 WebIDLCast(JSContext *cx, HandleValue value, NativeType *out)
  1573     int32_t temp;
  1574     if (!ToInt32(cx, value, &temp))
  1575         return false;
  1576     // Technically, the behavior of assigning an out of range value to a signed
  1577     // variable is undefined. In practice, compilers seem to do what we want
  1578     // without issuing any warnings.
  1579     *out = static_cast<NativeType>(temp);
  1580     return true;
  1583 template <>
  1584 inline bool
  1585 WebIDLCast<float>(JSContext *cx, HandleValue value, float *out)
  1587     double temp;
  1588     if (!ToNumber(cx, value, &temp))
  1589         return false;
  1590     *out = static_cast<float>(temp);
  1591     return true;
  1594 template <>
  1595 inline bool
  1596 WebIDLCast<double>(JSContext *cx, HandleValue value, double *out)
  1598     return ToNumber(cx, value, out);
  1601 template<typename NativeType>
  1602 /* static */ bool
  1603 DataViewObject::write(JSContext *cx, Handle<DataViewObject*> obj,
  1604                       CallArgs &args, const char *method)
  1606     if (args.length() < 2) {
  1607         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
  1608                              JSMSG_MORE_ARGS_NEEDED, method, "1", "");
  1609         return false;
  1612     uint32_t offset;
  1613     if (!ToUint32(cx, args[0], &offset))
  1614         return false;
  1616     NativeType value;
  1617     if (!WebIDLCast(cx, args[1], &value))
  1618         return false;
  1620     bool toLittleEndian = args.length() >= 3 && ToBoolean(args[2]);
  1622     uint8_t *data = DataViewObject::getDataPointer<NativeType>(cx, obj, offset);
  1623     if (!data)
  1624         return false;
  1626     DataViewIO<NativeType>::toBuffer(data, &value, needToSwapBytes(toLittleEndian));
  1627     return true;
  1630 bool
  1631 DataViewObject::getInt8Impl(JSContext *cx, CallArgs args)
  1633     JS_ASSERT(is(args.thisv()));
  1635     Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
  1637     int8_t val;
  1638     if (!read(cx, thisView, args, &val, "getInt8"))
  1639         return false;
  1640     args.rval().setInt32(val);
  1641     return true;
  1644 bool
  1645 DataViewObject::fun_getInt8(JSContext *cx, unsigned argc, Value *vp)
  1647     CallArgs args = CallArgsFromVp(argc, vp);
  1648     return CallNonGenericMethod<is, getInt8Impl>(cx, args);
  1651 bool
  1652 DataViewObject::getUint8Impl(JSContext *cx, CallArgs args)
  1654     JS_ASSERT(is(args.thisv()));
  1656     Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
  1658     uint8_t val;
  1659     if (!read(cx, thisView, args, &val, "getUint8"))
  1660         return false;
  1661     args.rval().setInt32(val);
  1662     return true;
  1665 bool
  1666 DataViewObject::fun_getUint8(JSContext *cx, unsigned argc, Value *vp)
  1668     CallArgs args = CallArgsFromVp(argc, vp);
  1669     return CallNonGenericMethod<is, getUint8Impl>(cx, args);
  1672 bool
  1673 DataViewObject::getInt16Impl(JSContext *cx, CallArgs args)
  1675     JS_ASSERT(is(args.thisv()));
  1677     Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
  1679     int16_t val;
  1680     if (!read(cx, thisView, args, &val, "getInt16"))
  1681         return false;
  1682     args.rval().setInt32(val);
  1683     return true;
  1686 bool
  1687 DataViewObject::fun_getInt16(JSContext *cx, unsigned argc, Value *vp)
  1689     CallArgs args = CallArgsFromVp(argc, vp);
  1690     return CallNonGenericMethod<is, getInt16Impl>(cx, args);
  1693 bool
  1694 DataViewObject::getUint16Impl(JSContext *cx, CallArgs args)
  1696     JS_ASSERT(is(args.thisv()));
  1698     Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
  1700     uint16_t val;
  1701     if (!read(cx, thisView, args, &val, "getUint16"))
  1702         return false;
  1703     args.rval().setInt32(val);
  1704     return true;
  1707 bool
  1708 DataViewObject::fun_getUint16(JSContext *cx, unsigned argc, Value *vp)
  1710     CallArgs args = CallArgsFromVp(argc, vp);
  1711     return CallNonGenericMethod<is, getUint16Impl>(cx, args);
  1714 bool
  1715 DataViewObject::getInt32Impl(JSContext *cx, CallArgs args)
  1717     JS_ASSERT(is(args.thisv()));
  1719     Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
  1721     int32_t val;
  1722     if (!read(cx, thisView, args, &val, "getInt32"))
  1723         return false;
  1724     args.rval().setInt32(val);
  1725     return true;
  1728 bool
  1729 DataViewObject::fun_getInt32(JSContext *cx, unsigned argc, Value *vp)
  1731     CallArgs args = CallArgsFromVp(argc, vp);
  1732     return CallNonGenericMethod<is, getInt32Impl>(cx, args);
  1735 bool
  1736 DataViewObject::getUint32Impl(JSContext *cx, CallArgs args)
  1738     JS_ASSERT(is(args.thisv()));
  1740     Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
  1742     uint32_t val;
  1743     if (!read(cx, thisView, args, &val, "getUint32"))
  1744         return false;
  1745     args.rval().setNumber(val);
  1746     return true;
  1749 bool
  1750 DataViewObject::fun_getUint32(JSContext *cx, unsigned argc, Value *vp)
  1752     CallArgs args = CallArgsFromVp(argc, vp);
  1753     return CallNonGenericMethod<is, getUint32Impl>(cx, args);
  1756 bool
  1757 DataViewObject::getFloat32Impl(JSContext *cx, CallArgs args)
  1759     JS_ASSERT(is(args.thisv()));
  1761     Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
  1763     float val;
  1764     if (!read(cx, thisView, args, &val, "getFloat32"))
  1765         return false;
  1767     args.rval().setDouble(CanonicalizeNaN(val));
  1768     return true;
  1771 bool
  1772 DataViewObject::fun_getFloat32(JSContext *cx, unsigned argc, Value *vp)
  1774     CallArgs args = CallArgsFromVp(argc, vp);
  1775     return CallNonGenericMethod<is, getFloat32Impl>(cx, args);
  1778 bool
  1779 DataViewObject::getFloat64Impl(JSContext *cx, CallArgs args)
  1781     JS_ASSERT(is(args.thisv()));
  1783     Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
  1785     double val;
  1786     if (!read(cx, thisView, args, &val, "getFloat64"))
  1787         return false;
  1789     args.rval().setDouble(CanonicalizeNaN(val));
  1790     return true;
  1793 bool
  1794 DataViewObject::fun_getFloat64(JSContext *cx, unsigned argc, Value *vp)
  1796     CallArgs args = CallArgsFromVp(argc, vp);
  1797     return CallNonGenericMethod<is, getFloat64Impl>(cx, args);
  1800 bool
  1801 DataViewObject::setInt8Impl(JSContext *cx, CallArgs args)
  1803     JS_ASSERT(is(args.thisv()));
  1805     Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
  1807     if (!write<int8_t>(cx, thisView, args, "setInt8"))
  1808         return false;
  1809     args.rval().setUndefined();
  1810     return true;
  1813 bool
  1814 DataViewObject::fun_setInt8(JSContext *cx, unsigned argc, Value *vp)
  1816     CallArgs args = CallArgsFromVp(argc, vp);
  1817     return CallNonGenericMethod<is, setInt8Impl>(cx, args);
  1820 bool
  1821 DataViewObject::setUint8Impl(JSContext *cx, CallArgs args)
  1823     JS_ASSERT(is(args.thisv()));
  1825     Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
  1827     if (!write<uint8_t>(cx, thisView, args, "setUint8"))
  1828         return false;
  1829     args.rval().setUndefined();
  1830     return true;
  1833 bool
  1834 DataViewObject::fun_setUint8(JSContext *cx, unsigned argc, Value *vp)
  1836     CallArgs args = CallArgsFromVp(argc, vp);
  1837     return CallNonGenericMethod<is, setUint8Impl>(cx, args);
  1840 bool
  1841 DataViewObject::setInt16Impl(JSContext *cx, CallArgs args)
  1843     JS_ASSERT(is(args.thisv()));
  1845     Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
  1847     if (!write<int16_t>(cx, thisView, args, "setInt16"))
  1848         return false;
  1849     args.rval().setUndefined();
  1850     return true;
  1853 bool
  1854 DataViewObject::fun_setInt16(JSContext *cx, unsigned argc, Value *vp)
  1856     CallArgs args = CallArgsFromVp(argc, vp);
  1857     return CallNonGenericMethod<is, setInt16Impl>(cx, args);
  1860 bool
  1861 DataViewObject::setUint16Impl(JSContext *cx, CallArgs args)
  1863     JS_ASSERT(is(args.thisv()));
  1865     Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
  1867     if (!write<uint16_t>(cx, thisView, args, "setUint16"))
  1868         return false;
  1869     args.rval().setUndefined();
  1870     return true;
  1873 bool
  1874 DataViewObject::fun_setUint16(JSContext *cx, unsigned argc, Value *vp)
  1876     CallArgs args = CallArgsFromVp(argc, vp);
  1877     return CallNonGenericMethod<is, setUint16Impl>(cx, args);
  1880 bool
  1881 DataViewObject::setInt32Impl(JSContext *cx, CallArgs args)
  1883     JS_ASSERT(is(args.thisv()));
  1885     Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
  1887     if (!write<int32_t>(cx, thisView, args, "setInt32"))
  1888         return false;
  1889     args.rval().setUndefined();
  1890     return true;
  1893 bool
  1894 DataViewObject::fun_setInt32(JSContext *cx, unsigned argc, Value *vp)
  1896     CallArgs args = CallArgsFromVp(argc, vp);
  1897     return CallNonGenericMethod<is, setInt32Impl>(cx, args);
  1900 bool
  1901 DataViewObject::setUint32Impl(JSContext *cx, CallArgs args)
  1903     JS_ASSERT(is(args.thisv()));
  1905     Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
  1907     if (!write<uint32_t>(cx, thisView, args, "setUint32"))
  1908         return false;
  1909     args.rval().setUndefined();
  1910     return true;
  1913 bool
  1914 DataViewObject::fun_setUint32(JSContext *cx, unsigned argc, Value *vp)
  1916     CallArgs args = CallArgsFromVp(argc, vp);
  1917     return CallNonGenericMethod<is, setUint32Impl>(cx, args);
  1920 bool
  1921 DataViewObject::setFloat32Impl(JSContext *cx, CallArgs args)
  1923     JS_ASSERT(is(args.thisv()));
  1925     Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
  1927     if (!write<float>(cx, thisView, args, "setFloat32"))
  1928         return false;
  1929     args.rval().setUndefined();
  1930     return true;
  1933 bool
  1934 DataViewObject::fun_setFloat32(JSContext *cx, unsigned argc, Value *vp)
  1936     CallArgs args = CallArgsFromVp(argc, vp);
  1937     return CallNonGenericMethod<is, setFloat32Impl>(cx, args);
  1940 bool
  1941 DataViewObject::setFloat64Impl(JSContext *cx, CallArgs args)
  1943     JS_ASSERT(is(args.thisv()));
  1945     Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
  1947     if (!write<double>(cx, thisView, args, "setFloat64"))
  1948         return false;
  1949     args.rval().setUndefined();
  1950     return true;
  1953 bool
  1954 DataViewObject::fun_setFloat64(JSContext *cx, unsigned argc, Value *vp)
  1956     CallArgs args = CallArgsFromVp(argc, vp);
  1957     return CallNonGenericMethod<is, setFloat64Impl>(cx, args);
  1960 Value
  1961 TypedArrayObject::getElement(uint32_t index)
  1963     switch (type()) {
  1964       case ScalarTypeDescr::TYPE_INT8:
  1965         return TypedArrayObjectTemplate<int8_t>::getIndexValue(this, index);
  1966         break;
  1967       case ScalarTypeDescr::TYPE_UINT8:
  1968         return TypedArrayObjectTemplate<uint8_t>::getIndexValue(this, index);
  1969         break;
  1970       case ScalarTypeDescr::TYPE_UINT8_CLAMPED:
  1971         return TypedArrayObjectTemplate<uint8_clamped>::getIndexValue(this, index);
  1972         break;
  1973       case ScalarTypeDescr::TYPE_INT16:
  1974         return TypedArrayObjectTemplate<int16_t>::getIndexValue(this, index);
  1975         break;
  1976       case ScalarTypeDescr::TYPE_UINT16:
  1977         return TypedArrayObjectTemplate<uint16_t>::getIndexValue(this, index);
  1978         break;
  1979       case ScalarTypeDescr::TYPE_INT32:
  1980         return TypedArrayObjectTemplate<int32_t>::getIndexValue(this, index);
  1981         break;
  1982       case ScalarTypeDescr::TYPE_UINT32:
  1983         return TypedArrayObjectTemplate<uint32_t>::getIndexValue(this, index);
  1984         break;
  1985       case ScalarTypeDescr::TYPE_FLOAT32:
  1986         return TypedArrayObjectTemplate<float>::getIndexValue(this, index);
  1987         break;
  1988       case ScalarTypeDescr::TYPE_FLOAT64:
  1989         return TypedArrayObjectTemplate<double>::getIndexValue(this, index);
  1990         break;
  1991       default:
  1992         MOZ_ASSUME_UNREACHABLE("Unknown TypedArray type");
  1993         break;
  1997 void
  1998 TypedArrayObject::setElement(TypedArrayObject &obj, uint32_t index, double d)
  2000     MOZ_ASSERT(index < obj.length());
  2002     switch (obj.type()) {
  2003       case ScalarTypeDescr::TYPE_INT8:
  2004         TypedArrayObjectTemplate<int8_t>::setIndexValue(obj, index, d);
  2005         break;
  2006       case ScalarTypeDescr::TYPE_UINT8:
  2007         TypedArrayObjectTemplate<uint8_t>::setIndexValue(obj, index, d);
  2008         break;
  2009       case ScalarTypeDescr::TYPE_UINT8_CLAMPED:
  2010         TypedArrayObjectTemplate<uint8_clamped>::setIndexValue(obj, index, d);
  2011         break;
  2012       case ScalarTypeDescr::TYPE_INT16:
  2013         TypedArrayObjectTemplate<int16_t>::setIndexValue(obj, index, d);
  2014         break;
  2015       case ScalarTypeDescr::TYPE_UINT16:
  2016         TypedArrayObjectTemplate<uint16_t>::setIndexValue(obj, index, d);
  2017         break;
  2018       case ScalarTypeDescr::TYPE_INT32:
  2019         TypedArrayObjectTemplate<int32_t>::setIndexValue(obj, index, d);
  2020         break;
  2021       case ScalarTypeDescr::TYPE_UINT32:
  2022         TypedArrayObjectTemplate<uint32_t>::setIndexValue(obj, index, d);
  2023         break;
  2024       case ScalarTypeDescr::TYPE_FLOAT32:
  2025         TypedArrayObjectTemplate<float>::setIndexValue(obj, index, d);
  2026         break;
  2027       case ScalarTypeDescr::TYPE_FLOAT64:
  2028         TypedArrayObjectTemplate<double>::setIndexValue(obj, index, d);
  2029         break;
  2030       default:
  2031         MOZ_ASSUME_UNREACHABLE("Unknown TypedArray type");
  2032         break;
  2036 /***
  2037  *** JS impl
  2038  ***/
  2040 /*
  2041  * TypedArrayObject boilerplate
  2042  */
  2044 #ifndef RELEASE_BUILD
  2045 # define IMPL_TYPED_ARRAY_STATICS(_typedArray)                                     \
  2046 const JSFunctionSpec _typedArray##Object::jsfuncs[] = {                            \
  2047     JS_SELF_HOSTED_FN("@@iterator", "ArrayValues", 0, 0),                          \
  2048     JS_FN("subarray", _typedArray##Object::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \
  2049     JS_FN("set", _typedArray##Object::fun_set, 2, JSFUN_GENERIC_NATIVE),           \
  2050     JS_FN("move", _typedArray##Object::fun_move, 3, JSFUN_GENERIC_NATIVE),         \
  2051     JS_FS_END                                                                      \
  2053 #else
  2054 # define IMPL_TYPED_ARRAY_STATICS(_typedArray)                                     \
  2055 const JSFunctionSpec _typedArray##Object::jsfuncs[] = {                            \
  2056     JS_SELF_HOSTED_FN("@@iterator", "ArrayValues", 0, 0),                          \
  2057     JS_FN("subarray", _typedArray##Object::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \
  2058     JS_FN("set", _typedArray##Object::fun_set, 2, JSFUN_GENERIC_NATIVE),           \
  2059     JS_FS_END                                                                      \
  2061 #endif
  2063 #define IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Name,NativeType)                                    \
  2064   JS_FRIEND_API(JSObject *) JS_New ## Name ## Array(JSContext *cx, uint32_t nelements)          \
  2065   {                                                                                             \
  2066       return TypedArrayObjectTemplate<NativeType>::fromLength(cx, nelements);                   \
  2067   }                                                                                             \
  2068   JS_FRIEND_API(JSObject *) JS_New ## Name ## ArrayFromArray(JSContext *cx, HandleObject other) \
  2069   {                                                                                             \
  2070       return TypedArrayObjectTemplate<NativeType>::fromArray(cx, other);                        \
  2071   }                                                                                             \
  2072   JS_FRIEND_API(JSObject *) JS_New ## Name ## ArrayWithBuffer(JSContext *cx,                    \
  2073                                HandleObject arrayBuffer, uint32_t byteOffset, int32_t length)   \
  2074   {                                                                                             \
  2075       return TypedArrayObjectTemplate<NativeType>::fromBuffer(cx, arrayBuffer, byteOffset,      \
  2076                                                               length, js::NullPtr());           \
  2077   }                                                                                             \
  2078   JS_FRIEND_API(bool) JS_Is ## Name ## Array(JSObject *obj)                                     \
  2079   {                                                                                             \
  2080       if (!(obj = CheckedUnwrap(obj)))                                                          \
  2081           return false;                                                                         \
  2082       const Class *clasp = obj->getClass();                                                     \
  2083       return clasp == &TypedArrayObject::classes[TypedArrayObjectTemplate<NativeType>::ArrayTypeID()]; \
  2084   } \
  2085   JS_FRIEND_API(JSObject *) js::Unwrap ## Name ## Array(JSObject *obj)                          \
  2086   {                                                                                             \
  2087       obj = CheckedUnwrap(obj);                                                                 \
  2088       if (!obj)                                                                                 \
  2089           return nullptr;                                                                       \
  2090       const Class *clasp = obj->getClass();                                                     \
  2091       if (clasp == &TypedArrayObject::classes[TypedArrayObjectTemplate<NativeType>::ArrayTypeID()]) \
  2092           return obj;                                                                           \
  2093       return nullptr;                                                                           \
  2094   } \
  2095   const js::Class* const js::detail::Name ## ArrayClassPtr =                                    \
  2096       &js::TypedArrayObject::classes[TypedArrayObjectTemplate<NativeType>::ArrayTypeID()];
  2098 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int8, int8_t)
  2099 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint8, uint8_t)
  2100 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint8Clamped, uint8_clamped)
  2101 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int16, int16_t)
  2102 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint16, uint16_t)
  2103 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int32, int32_t)
  2104 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint32, uint32_t)
  2105 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float32, float)
  2106 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float64, double)
  2108 #define IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Name, ExternalType, InternalType)              \
  2109   JS_FRIEND_API(JSObject *) JS_GetObjectAs ## Name ## Array(JSObject *obj,                  \
  2110                                                             uint32_t *length,               \
  2111                                                             ExternalType **data)            \
  2112   {                                                                                         \
  2113       if (!(obj = CheckedUnwrap(obj)))                                                      \
  2114           return nullptr;                                                                   \
  2116       const Class *clasp = obj->getClass();                                                 \
  2117       if (clasp != &TypedArrayObject::classes[TypedArrayObjectTemplate<InternalType>::ArrayTypeID()]) \
  2118           return nullptr;                                                                   \
  2120       TypedArrayObject *tarr = &obj->as<TypedArrayObject>();                                \
  2121       *length = tarr->length();                                                             \
  2122       *data = static_cast<ExternalType *>(tarr->viewData());                                \
  2124       return obj;                                                                           \
  2127 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int8, int8_t, int8_t)
  2128 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint8, uint8_t, uint8_t)
  2129 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint8Clamped, uint8_t, uint8_clamped)
  2130 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int16, int16_t, int16_t)
  2131 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint16, uint16_t, uint16_t)
  2132 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int32, int32_t, int32_t)
  2133 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint32, uint32_t, uint32_t)
  2134 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float32, float, float)
  2135 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float64, double, double)
  2137 #define IMPL_TYPED_ARRAY_PROTO_CLASS(_typedArray)                              \
  2138 {                                                                              \
  2139     #_typedArray "Prototype",                                                  \
  2140     JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) |             \
  2141     JSCLASS_HAS_PRIVATE |                                                      \
  2142     JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray),                           \
  2143     JS_PropertyStub,         /* addProperty */                                 \
  2144     JS_DeletePropertyStub,   /* delProperty */                                 \
  2145     JS_PropertyStub,         /* getProperty */                                 \
  2146     JS_StrictPropertyStub,   /* setProperty */                                 \
  2147     JS_EnumerateStub,                                                          \
  2148     JS_ResolveStub,                                                            \
  2149     JS_ConvertStub                                                             \
  2152 #define IMPL_TYPED_ARRAY_FAST_CLASS(_typedArray)                               \
  2153 {                                                                              \
  2154     #_typedArray,                                                              \
  2155     JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) |             \
  2156     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |                        \
  2157     JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray),                           \
  2158     JS_PropertyStub,         /* addProperty */                                 \
  2159     JS_DeletePropertyStub,   /* delProperty */                                 \
  2160     JS_PropertyStub,         /* getProperty */                                 \
  2161     JS_StrictPropertyStub,   /* setProperty */                                 \
  2162     JS_EnumerateStub,                                                          \
  2163     JS_ResolveStub,                                                            \
  2164     JS_ConvertStub,                                                            \
  2165     nullptr,                 /* finalize */                                    \
  2166     nullptr,                 /* call        */                                 \
  2167     nullptr,                 /* hasInstance */                                 \
  2168     nullptr,                 /* construct   */                                 \
  2169     ArrayBufferViewObject::trace, /* trace  */                                 \
  2172 template<class ArrayType>
  2173 static inline bool
  2174 InitTypedArrayClass(JSContext *cx)
  2176     Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal());
  2177     if (global->isStandardClassResolved(ArrayType::key))
  2178         return true;
  2180     RootedObject proto(cx, global->createBlankPrototype(cx, ArrayType::protoClass()));
  2181     if (!proto)
  2182         return false;
  2184     RootedFunction ctor(cx);
  2185     ctor = global->createConstructor(cx, ArrayType::class_constructor,
  2186                                      ClassName(ArrayType::key, cx), 3);
  2187     if (!ctor)
  2188         return false;
  2190     if (!LinkConstructorAndPrototype(cx, ctor, proto))
  2191         return false;
  2193     RootedValue bytesValue(cx, Int32Value(ArrayType::BYTES_PER_ELEMENT));
  2195     if (!JSObject::defineProperty(cx, ctor,
  2196                                   cx->names().BYTES_PER_ELEMENT, bytesValue,
  2197                                   JS_PropertyStub, JS_StrictPropertyStub,
  2198                                   JSPROP_PERMANENT | JSPROP_READONLY) ||
  2199         !JSObject::defineProperty(cx, proto,
  2200                                   cx->names().BYTES_PER_ELEMENT, bytesValue,
  2201                                   JS_PropertyStub, JS_StrictPropertyStub,
  2202                                   JSPROP_PERMANENT | JSPROP_READONLY))
  2204         return false;
  2207     if (!ArrayType::defineGetters(cx, proto))
  2208         return false;
  2210     if (!JS_DefineFunctions(cx, proto, ArrayType::jsfuncs))
  2211         return false;
  2213     RootedFunction fun(cx);
  2214     fun =
  2215         NewFunction(cx, NullPtr(),
  2216                     ArrayBufferObject::createTypedArrayFromBuffer<typename ArrayType::ThisType>,
  2217                     0, JSFunction::NATIVE_FUN, global, NullPtr());
  2218     if (!fun)
  2219         return false;
  2221     if (!GlobalObject::initBuiltinConstructor(cx, global, ArrayType::key, ctor, proto))
  2222         return false;
  2224     global->setCreateArrayFromBuffer<typename ArrayType::ThisType>(fun);
  2226     return true;
  2229 IMPL_TYPED_ARRAY_STATICS(Int8Array);
  2230 IMPL_TYPED_ARRAY_STATICS(Uint8Array);
  2231 IMPL_TYPED_ARRAY_STATICS(Int16Array);
  2232 IMPL_TYPED_ARRAY_STATICS(Uint16Array);
  2233 IMPL_TYPED_ARRAY_STATICS(Int32Array);
  2234 IMPL_TYPED_ARRAY_STATICS(Uint32Array);
  2235 IMPL_TYPED_ARRAY_STATICS(Float32Array);
  2236 IMPL_TYPED_ARRAY_STATICS(Float64Array);
  2237 IMPL_TYPED_ARRAY_STATICS(Uint8ClampedArray);
  2239 const Class TypedArrayObject::classes[ScalarTypeDescr::TYPE_MAX] = {
  2240     IMPL_TYPED_ARRAY_FAST_CLASS(Int8Array),
  2241     IMPL_TYPED_ARRAY_FAST_CLASS(Uint8Array),
  2242     IMPL_TYPED_ARRAY_FAST_CLASS(Int16Array),
  2243     IMPL_TYPED_ARRAY_FAST_CLASS(Uint16Array),
  2244     IMPL_TYPED_ARRAY_FAST_CLASS(Int32Array),
  2245     IMPL_TYPED_ARRAY_FAST_CLASS(Uint32Array),
  2246     IMPL_TYPED_ARRAY_FAST_CLASS(Float32Array),
  2247     IMPL_TYPED_ARRAY_FAST_CLASS(Float64Array),
  2248     IMPL_TYPED_ARRAY_FAST_CLASS(Uint8ClampedArray)
  2249 };
  2251 const Class TypedArrayObject::protoClasses[ScalarTypeDescr::TYPE_MAX] = {
  2252     IMPL_TYPED_ARRAY_PROTO_CLASS(Int8Array),
  2253     IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8Array),
  2254     IMPL_TYPED_ARRAY_PROTO_CLASS(Int16Array),
  2255     IMPL_TYPED_ARRAY_PROTO_CLASS(Uint16Array),
  2256     IMPL_TYPED_ARRAY_PROTO_CLASS(Int32Array),
  2257     IMPL_TYPED_ARRAY_PROTO_CLASS(Uint32Array),
  2258     IMPL_TYPED_ARRAY_PROTO_CLASS(Float32Array),
  2259     IMPL_TYPED_ARRAY_PROTO_CLASS(Float64Array),
  2260     IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8ClampedArray)
  2261 };
  2263 #define CHECK(t, a) { if (t == a::IsThisClass) return true; }
  2264 JS_FRIEND_API(bool)
  2265 js::IsTypedArrayThisCheck(JS::IsAcceptableThis test)
  2267     CHECK(test, Int8ArrayObject);
  2268     CHECK(test, Uint8ArrayObject);
  2269     CHECK(test, Int16ArrayObject);
  2270     CHECK(test, Uint16ArrayObject);
  2271     CHECK(test, Int32ArrayObject);
  2272     CHECK(test, Uint32ArrayObject);
  2273     CHECK(test, Float32ArrayObject);
  2274     CHECK(test, Float64ArrayObject);
  2275     CHECK(test, Uint8ClampedArrayObject);
  2276     return false;
  2278 #undef CHECK
  2280 static JSObject *
  2281 InitArrayBufferClass(JSContext *cx)
  2283     Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal());
  2284     if (global->isStandardClassResolved(JSProto_ArrayBuffer))
  2285         return &global->getPrototype(JSProto_ArrayBuffer).toObject();
  2287     RootedObject arrayBufferProto(cx, global->createBlankPrototype(cx, &ArrayBufferObject::protoClass));
  2288     if (!arrayBufferProto)
  2289         return nullptr;
  2291     RootedFunction ctor(cx, global->createConstructor(cx, ArrayBufferObject::class_constructor,
  2292                                                       cx->names().ArrayBuffer, 1));
  2293     if (!ctor)
  2294         return nullptr;
  2296     if (!LinkConstructorAndPrototype(cx, ctor, arrayBufferProto))
  2297         return nullptr;
  2299     RootedId byteLengthId(cx, NameToId(cx->names().byteLength));
  2300     unsigned attrs = JSPROP_SHARED | JSPROP_GETTER | JSPROP_PERMANENT;
  2301     JSObject *getter = NewFunction(cx, NullPtr(), ArrayBufferObject::byteLengthGetter, 0,
  2302                                    JSFunction::NATIVE_FUN, global, NullPtr());
  2303     if (!getter)
  2304         return nullptr;
  2306     if (!DefineNativeProperty(cx, arrayBufferProto, byteLengthId, UndefinedHandleValue,
  2307                               JS_DATA_TO_FUNC_PTR(PropertyOp, getter), nullptr, attrs))
  2308         return nullptr;
  2310     if (!JS_DefineFunctions(cx, ctor, ArrayBufferObject::jsstaticfuncs))
  2311         return nullptr;
  2313     if (!JS_DefineFunctions(cx, arrayBufferProto, ArrayBufferObject::jsfuncs))
  2314         return nullptr;
  2316     if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_ArrayBuffer,
  2317                                               ctor, arrayBufferProto))
  2319         return nullptr;
  2322     return arrayBufferProto;
  2325 const Class DataViewObject::protoClass = {
  2326     "DataViewPrototype",
  2327     JSCLASS_HAS_PRIVATE |
  2328     JSCLASS_HAS_RESERVED_SLOTS(DataViewObject::RESERVED_SLOTS) |
  2329     JSCLASS_HAS_CACHED_PROTO(JSProto_DataView),
  2330     JS_PropertyStub,         /* addProperty */
  2331     JS_DeletePropertyStub,   /* delProperty */
  2332     JS_PropertyStub,         /* getProperty */
  2333     JS_StrictPropertyStub,   /* setProperty */
  2334     JS_EnumerateStub,
  2335     JS_ResolveStub,
  2336     JS_ConvertStub
  2337 };
  2339 const Class DataViewObject::class_ = {
  2340     "DataView",
  2341     JSCLASS_HAS_PRIVATE |
  2342     JSCLASS_IMPLEMENTS_BARRIERS |
  2343     JSCLASS_HAS_RESERVED_SLOTS(DataViewObject::RESERVED_SLOTS) |
  2344     JSCLASS_HAS_CACHED_PROTO(JSProto_DataView),
  2345     JS_PropertyStub,         /* addProperty */
  2346     JS_DeletePropertyStub,   /* delProperty */
  2347     JS_PropertyStub,         /* getProperty */
  2348     JS_StrictPropertyStub,   /* setProperty */
  2349     JS_EnumerateStub,
  2350     JS_ResolveStub,
  2351     JS_ConvertStub,
  2352     nullptr,                 /* finalize */
  2353     nullptr,                 /* call        */
  2354     nullptr,                 /* hasInstance */
  2355     nullptr,                 /* construct   */
  2356     ArrayBufferViewObject::trace, /* trace  */
  2357 };
  2359 const JSFunctionSpec DataViewObject::jsfuncs[] = {
  2360     JS_FN("getInt8",    DataViewObject::fun_getInt8,      1,0),
  2361     JS_FN("getUint8",   DataViewObject::fun_getUint8,     1,0),
  2362     JS_FN("getInt16",   DataViewObject::fun_getInt16,     2,0),
  2363     JS_FN("getUint16",  DataViewObject::fun_getUint16,    2,0),
  2364     JS_FN("getInt32",   DataViewObject::fun_getInt32,     2,0),
  2365     JS_FN("getUint32",  DataViewObject::fun_getUint32,    2,0),
  2366     JS_FN("getFloat32", DataViewObject::fun_getFloat32,   2,0),
  2367     JS_FN("getFloat64", DataViewObject::fun_getFloat64,   2,0),
  2368     JS_FN("setInt8",    DataViewObject::fun_setInt8,      2,0),
  2369     JS_FN("setUint8",   DataViewObject::fun_setUint8,     2,0),
  2370     JS_FN("setInt16",   DataViewObject::fun_setInt16,     3,0),
  2371     JS_FN("setUint16",  DataViewObject::fun_setUint16,    3,0),
  2372     JS_FN("setInt32",   DataViewObject::fun_setInt32,     3,0),
  2373     JS_FN("setUint32",  DataViewObject::fun_setUint32,    3,0),
  2374     JS_FN("setFloat32", DataViewObject::fun_setFloat32,   3,0),
  2375     JS_FN("setFloat64", DataViewObject::fun_setFloat64,   3,0),
  2376     JS_FS_END
  2377 };
  2379 template<Value ValueGetter(DataViewObject *view)>
  2380 bool
  2381 DataViewObject::getterImpl(JSContext *cx, CallArgs args)
  2383     args.rval().set(ValueGetter(&args.thisv().toObject().as<DataViewObject>()));
  2384     return true;
  2387 template<Value ValueGetter(DataViewObject *view)>
  2388 bool
  2389 DataViewObject::getter(JSContext *cx, unsigned argc, Value *vp)
  2391     CallArgs args = CallArgsFromVp(argc, vp);
  2392     return CallNonGenericMethod<is, getterImpl<ValueGetter> >(cx, args);
  2395 template<Value ValueGetter(DataViewObject *view)>
  2396 bool
  2397 DataViewObject::defineGetter(JSContext *cx, PropertyName *name, HandleObject proto)
  2399     RootedId id(cx, NameToId(name));
  2400     unsigned attrs = JSPROP_SHARED | JSPROP_GETTER | JSPROP_PERMANENT;
  2402     Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal());
  2403     JSObject *getter = NewFunction(cx, NullPtr(), DataViewObject::getter<ValueGetter>, 0,
  2404                                    JSFunction::NATIVE_FUN, global, NullPtr());
  2405     if (!getter)
  2406         return false;
  2408     return DefineNativeProperty(cx, proto, id, UndefinedHandleValue,
  2409                                 JS_DATA_TO_FUNC_PTR(PropertyOp, getter), nullptr, attrs);
  2412 /* static */ bool
  2413 DataViewObject::initClass(JSContext *cx)
  2415     Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal());
  2416     if (global->isStandardClassResolved(JSProto_DataView))
  2417         return true;
  2419     RootedObject proto(cx, global->createBlankPrototype(cx, &DataViewObject::protoClass));
  2420     if (!proto)
  2421         return false;
  2423     RootedFunction ctor(cx, global->createConstructor(cx, DataViewObject::class_constructor,
  2424                                                       cx->names().DataView, 3));
  2425     if (!ctor)
  2426         return false;
  2428     if (!LinkConstructorAndPrototype(cx, ctor, proto))
  2429         return false;
  2431     if (!defineGetter<bufferValue>(cx, cx->names().buffer, proto))
  2432         return false;
  2434     if (!defineGetter<byteLengthValue>(cx, cx->names().byteLength, proto))
  2435         return false;
  2437     if (!defineGetter<byteOffsetValue>(cx, cx->names().byteOffset, proto))
  2438         return false;
  2440     if (!JS_DefineFunctions(cx, proto, DataViewObject::jsfuncs))
  2441         return false;
  2443     /*
  2444      * Create a helper function to implement the craziness of
  2445      * |new DataView(new otherWindow.ArrayBuffer())|, and install it in the
  2446      * global for use by the DataViewObject constructor.
  2447      */
  2448     RootedFunction fun(cx, NewFunction(cx, NullPtr(), ArrayBufferObject::createDataViewForThis,
  2449                                        0, JSFunction::NATIVE_FUN, global, NullPtr()));
  2450     if (!fun)
  2451         return false;
  2453     if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_DataView, ctor, proto))
  2454         return false;
  2456     global->setCreateDataViewForThis(fun);
  2458     return true;
  2461 void
  2462 DataViewObject::neuter(void *newData)
  2464     setSlot(BYTELENGTH_SLOT, Int32Value(0));
  2465     setSlot(BYTEOFFSET_SLOT, Int32Value(0));
  2466     setPrivate(newData);
  2469 JSObject *
  2470 js_InitTypedArrayClasses(JSContext *cx, HandleObject obj)
  2472     if (!InitTypedArrayClass<Int8ArrayObject>(cx) ||
  2473         !InitTypedArrayClass<Uint8ArrayObject>(cx) ||
  2474         !InitTypedArrayClass<Int16ArrayObject>(cx) ||
  2475         !InitTypedArrayClass<Uint16ArrayObject>(cx) ||
  2476         !InitTypedArrayClass<Int32ArrayObject>(cx) ||
  2477         !InitTypedArrayClass<Uint32ArrayObject>(cx) ||
  2478         !InitTypedArrayClass<Float32ArrayObject>(cx) ||
  2479         !InitTypedArrayClass<Float64ArrayObject>(cx) ||
  2480         !InitTypedArrayClass<Uint8ClampedArrayObject>(cx) ||
  2481         !DataViewObject::initClass(cx))
  2483         return nullptr;
  2486     return InitArrayBufferClass(cx);
  2489 bool
  2490 js::IsTypedArrayConstructor(HandleValue v, uint32_t type)
  2492     switch (type) {
  2493       case ScalarTypeDescr::TYPE_INT8:
  2494         return IsNativeFunction(v, Int8ArrayObject::class_constructor);
  2495       case ScalarTypeDescr::TYPE_UINT8:
  2496         return IsNativeFunction(v, Uint8ArrayObject::class_constructor);
  2497       case ScalarTypeDescr::TYPE_INT16:
  2498         return IsNativeFunction(v, Int16ArrayObject::class_constructor);
  2499       case ScalarTypeDescr::TYPE_UINT16:
  2500         return IsNativeFunction(v, Uint16ArrayObject::class_constructor);
  2501       case ScalarTypeDescr::TYPE_INT32:
  2502         return IsNativeFunction(v, Int32ArrayObject::class_constructor);
  2503       case ScalarTypeDescr::TYPE_UINT32:
  2504         return IsNativeFunction(v, Uint32ArrayObject::class_constructor);
  2505       case ScalarTypeDescr::TYPE_FLOAT32:
  2506         return IsNativeFunction(v, Float32ArrayObject::class_constructor);
  2507       case ScalarTypeDescr::TYPE_FLOAT64:
  2508         return IsNativeFunction(v, Float64ArrayObject::class_constructor);
  2509       case ScalarTypeDescr::TYPE_UINT8_CLAMPED:
  2510         return IsNativeFunction(v, Uint8ClampedArrayObject::class_constructor);
  2512     MOZ_ASSUME_UNREACHABLE("unexpected typed array type");
  2515 bool
  2516 js::IsTypedArrayBuffer(HandleValue v)
  2518     return v.isObject() &&
  2519            (v.toObject().is<ArrayBufferObject>() ||
  2520             v.toObject().is<SharedArrayBufferObject>());
  2523 ArrayBufferObject &
  2524 js::AsTypedArrayBuffer(HandleValue v)
  2526     JS_ASSERT(IsTypedArrayBuffer(v));
  2527     if (v.toObject().is<ArrayBufferObject>())
  2528         return v.toObject().as<ArrayBufferObject>();
  2529     return v.toObject().as<SharedArrayBufferObject>();
  2532 bool
  2533 js::StringIsTypedArrayIndex(JSLinearString *str, uint64_t *indexp)
  2535     const jschar *s = str->chars();
  2536     const jschar *end = s + str->length();
  2538     if (s == end)
  2539         return false;
  2541     bool negative = false;
  2542     if (*s == '-') {
  2543         negative = true;
  2544         if (++s == end)
  2545             return false;
  2548     if (!JS7_ISDEC(*s))
  2549         return false;
  2551     uint64_t index = 0;
  2552     uint32_t digit = JS7_UNDEC(*s++);
  2554     /* Don't allow leading zeros. */
  2555     if (digit == 0 && s != end)
  2556         return false;
  2558     index = digit;
  2560     for (; s < end; s++) {
  2561         if (!JS7_ISDEC(*s))
  2562             return false;
  2564         digit = JS7_UNDEC(*s);
  2566         /* Watch for overflows. */
  2567         if ((UINT64_MAX - digit) / 10 < index)
  2568             index = UINT64_MAX;
  2569         else
  2570             index = 10 * index + digit;
  2573     if (negative)
  2574         *indexp = UINT64_MAX;
  2575     else
  2576         *indexp = index;
  2577     return true;
  2580 /* JS Friend API */
  2582 JS_FRIEND_API(bool)
  2583 JS_IsTypedArrayObject(JSObject *obj)
  2585     obj = CheckedUnwrap(obj);
  2586     return obj ? obj->is<TypedArrayObject>() : false;
  2589 JS_FRIEND_API(uint32_t)
  2590 JS_GetTypedArrayLength(JSObject *obj)
  2592     obj = CheckedUnwrap(obj);
  2593     if (!obj)
  2594         return 0;
  2595     return obj->as<TypedArrayObject>().length();
  2598 JS_FRIEND_API(uint32_t)
  2599 JS_GetTypedArrayByteOffset(JSObject *obj)
  2601     obj = CheckedUnwrap(obj);
  2602     if (!obj)
  2603         return 0;
  2604     return obj->as<TypedArrayObject>().byteOffset();
  2607 JS_FRIEND_API(uint32_t)
  2608 JS_GetTypedArrayByteLength(JSObject *obj)
  2610     obj = CheckedUnwrap(obj);
  2611     if (!obj)
  2612         return 0;
  2613     return obj->as<TypedArrayObject>().byteLength();
  2616 JS_FRIEND_API(JSArrayBufferViewType)
  2617 JS_GetArrayBufferViewType(JSObject *obj)
  2619     obj = CheckedUnwrap(obj);
  2620     if (!obj)
  2621         return ArrayBufferView::TYPE_MAX;
  2623     if (obj->is<TypedArrayObject>())
  2624         return static_cast<JSArrayBufferViewType>(obj->as<TypedArrayObject>().type());
  2625     else if (obj->is<DataViewObject>())
  2626         return ArrayBufferView::TYPE_DATAVIEW;
  2627     MOZ_ASSUME_UNREACHABLE("invalid ArrayBufferView type");
  2630 JS_FRIEND_API(int8_t *)
  2631 JS_GetInt8ArrayData(JSObject *obj)
  2633     obj = CheckedUnwrap(obj);
  2634     if (!obj)
  2635         return nullptr;
  2636     TypedArrayObject *tarr = &obj->as<TypedArrayObject>();
  2637     JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_INT8);
  2638     return static_cast<int8_t *>(tarr->viewData());
  2641 JS_FRIEND_API(uint8_t *)
  2642 JS_GetUint8ArrayData(JSObject *obj)
  2644     obj = CheckedUnwrap(obj);
  2645     if (!obj)
  2646         return nullptr;
  2647     TypedArrayObject *tarr = &obj->as<TypedArrayObject>();
  2648     JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_UINT8);
  2649     return static_cast<uint8_t *>(tarr->viewData());
  2652 JS_FRIEND_API(uint8_t *)
  2653 JS_GetUint8ClampedArrayData(JSObject *obj)
  2655     obj = CheckedUnwrap(obj);
  2656     if (!obj)
  2657         return nullptr;
  2658     TypedArrayObject *tarr = &obj->as<TypedArrayObject>();
  2659     JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_UINT8_CLAMPED);
  2660     return static_cast<uint8_t *>(tarr->viewData());
  2663 JS_FRIEND_API(int16_t *)
  2664 JS_GetInt16ArrayData(JSObject *obj)
  2666     obj = CheckedUnwrap(obj);
  2667     if (!obj)
  2668         return nullptr;
  2669     TypedArrayObject *tarr = &obj->as<TypedArrayObject>();
  2670     JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_INT16);
  2671     return static_cast<int16_t *>(tarr->viewData());
  2674 JS_FRIEND_API(uint16_t *)
  2675 JS_GetUint16ArrayData(JSObject *obj)
  2677     obj = CheckedUnwrap(obj);
  2678     if (!obj)
  2679         return nullptr;
  2680     TypedArrayObject *tarr = &obj->as<TypedArrayObject>();
  2681     JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_UINT16);
  2682     return static_cast<uint16_t *>(tarr->viewData());
  2685 JS_FRIEND_API(int32_t *)
  2686 JS_GetInt32ArrayData(JSObject *obj)
  2688     obj = CheckedUnwrap(obj);
  2689     if (!obj)
  2690         return nullptr;
  2691     TypedArrayObject *tarr = &obj->as<TypedArrayObject>();
  2692     JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_INT32);
  2693     return static_cast<int32_t *>(tarr->viewData());
  2696 JS_FRIEND_API(uint32_t *)
  2697 JS_GetUint32ArrayData(JSObject *obj)
  2699     obj = CheckedUnwrap(obj);
  2700     if (!obj)
  2701         return nullptr;
  2702     TypedArrayObject *tarr = &obj->as<TypedArrayObject>();
  2703     JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_UINT32);
  2704     return static_cast<uint32_t *>(tarr->viewData());
  2707 JS_FRIEND_API(float *)
  2708 JS_GetFloat32ArrayData(JSObject *obj)
  2710     obj = CheckedUnwrap(obj);
  2711     if (!obj)
  2712         return nullptr;
  2713     TypedArrayObject *tarr = &obj->as<TypedArrayObject>();
  2714     JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_FLOAT32);
  2715     return static_cast<float *>(tarr->viewData());
  2718 JS_FRIEND_API(double *)
  2719 JS_GetFloat64ArrayData(JSObject *obj)
  2721     obj = CheckedUnwrap(obj);
  2722     if (!obj)
  2723         return nullptr;
  2724     TypedArrayObject *tarr = &obj->as<TypedArrayObject>();
  2725     JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_FLOAT64);
  2726     return static_cast<double *>(tarr->viewData());
  2729 JS_FRIEND_API(bool)
  2730 JS_IsDataViewObject(JSObject *obj)
  2732     obj = CheckedUnwrap(obj);
  2733     return obj ? obj->is<DataViewObject>() : false;
  2736 JS_FRIEND_API(uint32_t)
  2737 JS_GetDataViewByteOffset(JSObject *obj)
  2739     obj = CheckedUnwrap(obj);
  2740     if (!obj)
  2741         return 0;
  2742     return obj->as<DataViewObject>().byteOffset();
  2745 JS_FRIEND_API(void *)
  2746 JS_GetDataViewData(JSObject *obj)
  2748     obj = CheckedUnwrap(obj);
  2749     if (!obj)
  2750         return nullptr;
  2751     return obj->as<DataViewObject>().dataPointer();
  2754 JS_FRIEND_API(uint32_t)
  2755 JS_GetDataViewByteLength(JSObject *obj)
  2757     obj = CheckedUnwrap(obj);
  2758     if (!obj)
  2759         return 0;
  2760     return obj->as<DataViewObject>().byteLength();

mercurial