1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
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/. */
6 
7 #include "vm/TypedArrayObject-inl.h"
8 #include "vm/TypedArrayObject.h"
9 
10 #include "mozilla/Alignment.h"
11 #include "mozilla/FloatingPoint.h"
12 #include "mozilla/PodOperations.h"
13 #include "mozilla/TextUtils.h"
14 
15 #include <string>
16 #include <string.h>
17 #if !defined(XP_WIN) && !defined(__wasi__)
18 #  include <sys/mman.h>
19 #endif
20 
21 #include "jsapi.h"
22 #include "jsnum.h"
23 #include "jstypes.h"
24 
25 #include "builtin/Array.h"
26 #include "builtin/DataViewObject.h"
27 #include "builtin/TypedArrayConstants.h"
28 #include "gc/Barrier.h"
29 #include "gc/Marking.h"
30 #include "gc/MaybeRooted.h"
31 #include "jit/InlinableNatives.h"
32 #include "js/Conversions.h"
33 #include "js/experimental/TypedData.h"  // JS_GetArrayBufferViewType, JS_GetTypedArray{Length,ByteOffset,ByteLength}, JS_IsTypedArrayObject
34 #include "js/friend/ErrorMessages.h"    // js::GetErrorMessage, JSMSG_*
35 #include "js/PropertySpec.h"
36 #include "js/ScalarType.h"  // js::Scalar::Type
37 #include "js/UniquePtr.h"
38 #include "js/Wrapper.h"
39 #include "util/DifferentialTesting.h"
40 #include "util/Text.h"
41 #include "util/Windows.h"
42 #include "vm/ArrayBufferObject.h"
43 #include "vm/FunctionFlags.h"  // js::FunctionFlags
44 #include "vm/GlobalObject.h"
45 #include "vm/Interpreter.h"
46 #include "vm/JSContext.h"
47 #include "vm/JSObject.h"
48 #include "vm/PIC.h"
49 #include "vm/SelfHosting.h"
50 #include "vm/SharedMem.h"
51 #include "vm/Uint8Clamped.h"
52 #include "vm/WrapperObject.h"
53 
54 #include "gc/Nursery-inl.h"
55 #include "gc/StoreBuffer-inl.h"
56 #include "vm/ArrayBufferObject-inl.h"
57 #include "vm/JSAtom-inl.h"
58 #include "vm/NativeObject-inl.h"
59 #include "vm/Shape-inl.h"
60 
61 using namespace js;
62 
63 using JS::CanonicalizeNaN;
64 using JS::ToInt32;
65 using JS::ToUint32;
66 using mozilla::IsAsciiDigit;
67 
68 /*
69  * TypedArrayObject
70  *
71  * The non-templated base class for the specific typed implementations.
72  * This class holds all the member variables that are used by
73  * the subclasses.
74  */
75 
convertForSideEffect(JSContext * cx,HandleValue v) const76 bool TypedArrayObject::convertForSideEffect(JSContext* cx,
77                                             HandleValue v) const {
78   switch (type()) {
79     case Scalar::BigInt64:
80     case Scalar::BigUint64: {
81       return ToBigInt(cx, v) != nullptr;
82     }
83     case Scalar::Int8:
84     case Scalar::Uint8:
85     case Scalar::Int16:
86     case Scalar::Uint16:
87     case Scalar::Int32:
88     case Scalar::Uint32:
89     case Scalar::Float32:
90     case Scalar::Float64:
91     case Scalar::Uint8Clamped: {
92       double ignore;
93       return ToNumber(cx, v, &ignore);
94     }
95     case Scalar::MaxTypedArrayViewType:
96     case Scalar::Int64:
97     case Scalar::Simd128:
98       MOZ_CRASH("Unsupported TypedArray type");
99   }
100   MOZ_ASSERT_UNREACHABLE("Invalid scalar type");
101   return false;
102 }
103 
104 /* static */
is(HandleValue v)105 bool TypedArrayObject::is(HandleValue v) {
106   return v.isObject() && v.toObject().is<TypedArrayObject>();
107 }
108 
109 /* static */
ensureHasBuffer(JSContext * cx,Handle<TypedArrayObject * > tarray)110 bool TypedArrayObject::ensureHasBuffer(JSContext* cx,
111                                        Handle<TypedArrayObject*> tarray) {
112   if (tarray->hasBuffer()) {
113     return true;
114   }
115 
116   size_t byteLength = tarray->byteLength();
117 
118   AutoRealm ar(cx, tarray);
119   Rooted<ArrayBufferObject*> buffer(
120       cx, ArrayBufferObject::createZeroed(cx, tarray->byteLength()));
121   if (!buffer) {
122     return false;
123   }
124 
125   // Attaching the first view to an array buffer is infallible.
126   MOZ_ALWAYS_TRUE(buffer->addView(cx, tarray));
127 
128   // tarray is not shared, because if it were it would have a buffer.
129   memcpy(buffer->dataPointer(), tarray->dataPointerUnshared(), byteLength);
130 
131   // If the object is in the nursery, the buffer will be freed by the next
132   // nursery GC. Free the data slot pointer if the object has no inline data.
133   size_t nbytes = RoundUp(byteLength, sizeof(Value));
134   Nursery& nursery = cx->nursery();
135   if (tarray->isTenured() && !tarray->hasInlineElements() &&
136       !nursery.isInside(tarray->elements())) {
137     js_free(tarray->elements());
138     RemoveCellMemory(tarray, nbytes, MemoryUse::TypedArrayElements);
139   }
140 
141   tarray->setPrivate(buffer->dataPointer());
142 
143   tarray->setFixedSlot(TypedArrayObject::BUFFER_SLOT, ObjectValue(*buffer));
144 
145   return true;
146 }
147 
148 #ifdef DEBUG
assertZeroLengthArrayData() const149 void TypedArrayObject::assertZeroLengthArrayData() const {
150   if (length() == 0 && !hasBuffer()) {
151     uint8_t* end = fixedData(TypedArrayObject::FIXED_DATA_START);
152     MOZ_ASSERT(end[0] == ZeroLengthArrayData);
153   }
154 }
155 #endif
156 
finalize(JSFreeOp * fop,JSObject * obj)157 void TypedArrayObject::finalize(JSFreeOp* fop, JSObject* obj) {
158   MOZ_ASSERT(!IsInsideNursery(obj));
159   TypedArrayObject* curObj = &obj->as<TypedArrayObject>();
160 
161   // Template objects or discarded objects (which didn't have enough room
162   // for inner elements) don't have anything to free.
163   if (!curObj->elementsRaw()) {
164     return;
165   }
166 
167   curObj->assertZeroLengthArrayData();
168 
169   // Typed arrays with a buffer object do not need to be free'd
170   if (curObj->hasBuffer()) {
171     return;
172   }
173 
174   // Free the data slot pointer if it does not point into the old JSObject.
175   if (!curObj->hasInlineElements()) {
176     size_t nbytes = RoundUp(curObj->byteLength(), sizeof(Value));
177     fop->free_(obj, curObj->elements(), nbytes, MemoryUse::TypedArrayElements);
178   }
179 }
180 
181 /* static */
objectMoved(JSObject * obj,JSObject * old)182 size_t TypedArrayObject::objectMoved(JSObject* obj, JSObject* old) {
183   TypedArrayObject* newObj = &obj->as<TypedArrayObject>();
184   const TypedArrayObject* oldObj = &old->as<TypedArrayObject>();
185   MOZ_ASSERT(newObj->elementsRaw() == oldObj->elementsRaw());
186   MOZ_ASSERT(obj->isTenured());
187 
188   // Typed arrays with a buffer object do not need an update.
189   if (oldObj->hasBuffer()) {
190     return 0;
191   }
192 
193   if (!IsInsideNursery(old)) {
194     // Update the data slot pointer if it points to the old JSObject.
195     if (oldObj->hasInlineElements()) {
196       newObj->setInlineElements();
197     }
198 
199     return 0;
200   }
201 
202   void* buf = oldObj->elements();
203 
204   // Discarded objects (which didn't have enough room for inner elements) don't
205   // have any data to move.
206   if (!buf) {
207     return 0;
208   }
209 
210   Nursery& nursery = obj->runtimeFromMainThread()->gc.nursery();
211   if (!nursery.isInside(buf)) {
212     nursery.removeMallocedBufferDuringMinorGC(buf);
213     size_t nbytes = RoundUp(newObj->byteLength(), sizeof(Value));
214     AddCellMemory(newObj, nbytes, MemoryUse::TypedArrayElements);
215     return 0;
216   }
217 
218   // Determine if we can use inline data for the target array. If this is
219   // possible, the nursery will have picked an allocation size that is large
220   // enough.
221   size_t nbytes = oldObj->byteLength();
222   MOZ_ASSERT(nbytes <= Nursery::MaxNurseryBufferSize);
223 
224   constexpr size_t headerSize = dataOffset() + sizeof(HeapSlot);
225 
226   // See AllocKindForLazyBuffer.
227   gc::AllocKind newAllocKind = obj->asTenured().getAllocKind();
228   MOZ_ASSERT_IF(nbytes == 0,
229                 headerSize + sizeof(uint8_t) <= GetGCKindBytes(newAllocKind));
230 
231   if (headerSize + nbytes <= GetGCKindBytes(newAllocKind)) {
232     MOZ_ASSERT(oldObj->hasInlineElements());
233 #ifdef DEBUG
234     if (nbytes == 0) {
235       uint8_t* output = newObj->fixedData(TypedArrayObject::FIXED_DATA_START);
236       output[0] = ZeroLengthArrayData;
237     }
238 #endif
239     newObj->setInlineElements();
240   } else {
241     MOZ_ASSERT(!oldObj->hasInlineElements());
242 
243     AutoEnterOOMUnsafeRegion oomUnsafe;
244     nbytes = RoundUp(nbytes, sizeof(Value));
245     void* data = newObj->zone()->pod_arena_malloc<uint8_t>(
246         js::ArrayBufferContentsArena, nbytes);
247     if (!data) {
248       oomUnsafe.crash(
249           "Failed to allocate typed array elements while tenuring.");
250     }
251     MOZ_ASSERT(!nursery.isInside(data));
252     InitObjectPrivate(newObj, data, nbytes, MemoryUse::TypedArrayElements);
253   }
254 
255   mozilla::PodCopy(newObj->elements(), oldObj->elements(), nbytes);
256 
257   // Set a forwarding pointer for the element buffers in case they were
258   // preserved on the stack by Ion.
259   nursery.setForwardingPointerWhileTenuring(
260       oldObj->elements(), newObj->elements(),
261       /* direct = */ nbytes >= sizeof(uintptr_t));
262 
263   return newObj->hasInlineElements() ? 0 : nbytes;
264 }
265 
hasInlineElements() const266 bool TypedArrayObject::hasInlineElements() const {
267   return elements() == this->fixedData(TypedArrayObject::FIXED_DATA_START) &&
268          byteLength() <= TypedArrayObject::INLINE_BUFFER_LIMIT;
269 }
270 
setInlineElements()271 void TypedArrayObject::setInlineElements() {
272   char* dataSlot = reinterpret_cast<char*>(this) + dataOffset();
273   *reinterpret_cast<void**>(dataSlot) =
274       this->fixedData(TypedArrayObject::FIXED_DATA_START);
275 }
276 
277 /* Helper clamped uint8_t type */
278 
ClampDoubleToUint8(const double x)279 uint32_t js::ClampDoubleToUint8(const double x) {
280   // Not < so that NaN coerces to 0
281   if (!(x >= 0)) {
282     return 0;
283   }
284 
285   if (x > 255) {
286     return 255;
287   }
288 
289   double toTruncate = x + 0.5;
290   uint8_t y = uint8_t(toTruncate);
291 
292   /*
293    * now val is rounded to nearest, ties rounded up.  We want
294    * rounded to nearest ties to even, so check whether we had a
295    * tie.
296    */
297   if (y == toTruncate) {
298     /*
299      * It was a tie (since adding 0.5 gave us the exact integer
300      * we want).  Since we rounded up, we either already have an
301      * even number or we have an odd number but the number we
302      * want is one less.  So just unconditionally masking out the
303      * ones bit should do the trick to get us the value we
304      * want.
305      */
306     return y & ~1;
307   }
308 
309   return y;
310 }
311 
312 namespace {
313 
314 enum class SpeciesConstructorOverride { None, ArrayBuffer };
315 
316 template <typename NativeType>
317 class TypedArrayObjectTemplate : public TypedArrayObject {
318   friend class TypedArrayObject;
319 
320  public:
ArrayTypeID()321   static constexpr Scalar::Type ArrayTypeID() {
322     return TypeIDOfType<NativeType>::id;
323   }
protoKey()324   static constexpr JSProtoKey protoKey() {
325     return TypeIDOfType<NativeType>::protoKey;
326   }
327 
ArrayTypeIsUnsigned()328   static constexpr bool ArrayTypeIsUnsigned() {
329     return TypeIsUnsigned<NativeType>();
330   }
ArrayTypeIsFloatingPoint()331   static constexpr bool ArrayTypeIsFloatingPoint() {
332     return TypeIsFloatingPoint<NativeType>();
333   }
334 
335   static constexpr size_t BYTES_PER_ELEMENT = sizeof(NativeType);
336 
createPrototype(JSContext * cx,JSProtoKey key)337   static JSObject* createPrototype(JSContext* cx, JSProtoKey key) {
338     Handle<GlobalObject*> global = cx->global();
339     RootedObject typedArrayProto(
340         cx, GlobalObject::getOrCreateTypedArrayPrototype(cx, global));
341     if (!typedArrayProto) {
342       return nullptr;
343     }
344 
345     const JSClass* clasp = TypedArrayObject::protoClassForType(ArrayTypeID());
346     return GlobalObject::createBlankPrototypeInheriting(cx, clasp,
347                                                         typedArrayProto);
348   }
349 
createConstructor(JSContext * cx,JSProtoKey key)350   static JSObject* createConstructor(JSContext* cx, JSProtoKey key) {
351     Handle<GlobalObject*> global = cx->global();
352     RootedFunction ctorProto(
353         cx, GlobalObject::getOrCreateTypedArrayConstructor(cx, global));
354     if (!ctorProto) {
355       return nullptr;
356     }
357 
358     JSFunction* fun = NewFunctionWithProto(
359         cx, class_constructor, 3, FunctionFlags::NATIVE_CTOR, nullptr,
360         ClassName(key, cx), ctorProto, gc::AllocKind::FUNCTION, TenuredObject);
361 
362     if (fun) {
363       fun->setJitInfo(&jit::JitInfo_TypedArrayConstructor);
364     }
365 
366     return fun;
367   }
368 
instanceClass()369   static inline const JSClass* instanceClass() {
370     return TypedArrayObject::classForType(ArrayTypeID());
371   }
372 
is(HandleValue v)373   static bool is(HandleValue v) {
374     return v.isObject() && v.toObject().hasClass(instanceClass());
375   }
376 
377   static bool convertValue(JSContext* cx, HandleValue v, NativeType* result);
378 
newBuiltinClassInstance(JSContext * cx,gc::AllocKind allocKind,NewObjectKind newKind)379   static TypedArrayObject* newBuiltinClassInstance(JSContext* cx,
380                                                    gc::AllocKind allocKind,
381                                                    NewObjectKind newKind) {
382     JSObject* obj =
383         NewBuiltinClassInstance(cx, instanceClass(), allocKind, newKind);
384     return obj ? &obj->as<TypedArrayObject>() : nullptr;
385   }
386 
makeProtoInstance(JSContext * cx,HandleObject proto,gc::AllocKind allocKind)387   static TypedArrayObject* makeProtoInstance(JSContext* cx, HandleObject proto,
388                                              gc::AllocKind allocKind) {
389     MOZ_ASSERT(proto);
390 
391     JSObject* obj =
392         NewObjectWithGivenProto(cx, instanceClass(), proto, allocKind);
393     return obj ? &obj->as<TypedArrayObject>() : nullptr;
394   }
395 
makeInstance(JSContext * cx,Handle<ArrayBufferObjectMaybeShared * > buffer,size_t byteOffset,size_t len,HandleObject proto)396   static TypedArrayObject* makeInstance(
397       JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer,
398       size_t byteOffset, size_t len, HandleObject proto) {
399     MOZ_ASSERT(len <= maxByteLength() / BYTES_PER_ELEMENT);
400 
401     gc::AllocKind allocKind =
402         buffer ? gc::GetGCObjectKind(instanceClass())
403                : AllocKindForLazyBuffer(len * BYTES_PER_ELEMENT);
404 
405     AutoSetNewObjectMetadata metadata(cx);
406     Rooted<TypedArrayObject*> obj(cx);
407     if (proto) {
408       obj = makeProtoInstance(cx, proto, allocKind);
409     } else {
410       obj = newBuiltinClassInstance(cx, allocKind, GenericObject);
411     }
412     if (!obj || !obj->init(cx, buffer, byteOffset, len, BYTES_PER_ELEMENT)) {
413       return nullptr;
414     }
415 
416     return obj;
417   }
418 
makeTemplateObject(JSContext * cx,int32_t len)419   static TypedArrayObject* makeTemplateObject(JSContext* cx, int32_t len) {
420     MOZ_ASSERT(len >= 0);
421     size_t nbytes;
422     MOZ_ALWAYS_TRUE(CalculateAllocSize<NativeType>(len, &nbytes));
423     bool fitsInline = nbytes <= INLINE_BUFFER_LIMIT;
424     gc::AllocKind allocKind = !fitsInline ? gc::GetGCObjectKind(instanceClass())
425                                           : AllocKindForLazyBuffer(nbytes);
426     MOZ_ASSERT(allocKind >= gc::GetGCObjectKind(instanceClass()));
427 
428     AutoSetNewObjectMetadata metadata(cx);
429 
430     Rooted<TypedArrayObject*> tarray(
431         cx, newBuiltinClassInstance(cx, allocKind, TenuredObject));
432     if (!tarray) {
433       return nullptr;
434     }
435 
436     initTypedArraySlots(tarray, len);
437 
438     // Template objects do not need memory for its elements, since there
439     // won't be any elements to store. Therefore, we set the pointer to
440     // nullptr and avoid allocating memory that will never be used.
441     tarray->initPrivate(nullptr);
442 
443     return tarray;
444   }
445 
initTypedArraySlots(TypedArrayObject * tarray,int32_t len)446   static void initTypedArraySlots(TypedArrayObject* tarray, int32_t len) {
447     MOZ_ASSERT(len >= 0);
448     tarray->initFixedSlot(TypedArrayObject::BUFFER_SLOT, NullValue());
449     tarray->initFixedSlot(TypedArrayObject::LENGTH_SLOT, PrivateValue(len));
450     tarray->initFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT,
451                           PrivateValue(size_t(0)));
452 
453     // Verify that the private slot is at the expected place.
454     MOZ_ASSERT(tarray->numFixedSlots() == TypedArrayObject::DATA_SLOT);
455 
456 #ifdef DEBUG
457     if (len == 0) {
458       uint8_t* output = tarray->fixedData(TypedArrayObject::FIXED_DATA_START);
459       output[0] = TypedArrayObject::ZeroLengthArrayData;
460     }
461 #endif
462   }
463 
initTypedArrayData(TypedArrayObject * tarray,void * buf,size_t nbytes,gc::AllocKind allocKind)464   static void initTypedArrayData(TypedArrayObject* tarray, void* buf,
465                                  size_t nbytes, gc::AllocKind allocKind) {
466     if (buf) {
467       InitObjectPrivate(tarray, buf, nbytes, MemoryUse::TypedArrayElements);
468     } else {
469 #ifdef DEBUG
470       constexpr size_t dataOffset = ArrayBufferViewObject::dataOffset();
471       constexpr size_t offset = dataOffset + sizeof(HeapSlot);
472       MOZ_ASSERT(offset + nbytes <= GetGCKindBytes(allocKind));
473 #endif
474 
475       void* data = tarray->fixedData(FIXED_DATA_START);
476       tarray->initPrivate(data);
477       memset(data, 0, nbytes);
478     }
479   }
480 
makeTypedArrayWithTemplate(JSContext * cx,TypedArrayObject * templateObj,int32_t len)481   static TypedArrayObject* makeTypedArrayWithTemplate(
482       JSContext* cx, TypedArrayObject* templateObj, int32_t len) {
483     if (len < 0 || size_t(len) > maxByteLength() / BYTES_PER_ELEMENT) {
484       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
485                                 JSMSG_BAD_ARRAY_LENGTH);
486       return nullptr;
487     }
488 
489     size_t nbytes = size_t(len) * BYTES_PER_ELEMENT;
490     MOZ_ASSERT(nbytes <= maxByteLength());
491 
492     bool fitsInline = nbytes <= INLINE_BUFFER_LIMIT;
493 
494     AutoSetNewObjectMetadata metadata(cx);
495 
496     gc::AllocKind allocKind = !fitsInline ? gc::GetGCObjectKind(instanceClass())
497                                           : AllocKindForLazyBuffer(nbytes);
498     MOZ_ASSERT(templateObj->getClass() == instanceClass());
499 
500     RootedObject proto(cx, templateObj->staticPrototype());
501     TypedArrayObject* obj = makeProtoInstance(cx, proto, allocKind);
502     if (!obj) {
503       return nullptr;
504     }
505 
506     initTypedArraySlots(obj, len);
507 
508     void* buf = nullptr;
509     if (!fitsInline) {
510       MOZ_ASSERT(len > 0);
511 
512       nbytes = RoundUp(nbytes, sizeof(Value));
513       buf = cx->nursery().allocateZeroedBuffer(obj, nbytes,
514                                                js::ArrayBufferContentsArena);
515       if (!buf) {
516         ReportOutOfMemory(cx);
517         return nullptr;
518       }
519     }
520 
521     initTypedArrayData(obj, buf, nbytes, allocKind);
522 
523     return obj;
524   }
525 
makeTypedArrayWithTemplate(JSContext * cx,TypedArrayObject * templateObj,HandleObject array)526   static TypedArrayObject* makeTypedArrayWithTemplate(
527       JSContext* cx, TypedArrayObject* templateObj, HandleObject array) {
528     MOZ_ASSERT(!IsWrapper(array));
529     MOZ_ASSERT(!array->is<ArrayBufferObjectMaybeShared>());
530 
531     return fromArray(cx, array);
532   }
533 
makeTypedArrayWithTemplate(JSContext * cx,TypedArrayObject * templateObj,HandleObject arrayBuffer,HandleValue byteOffsetValue,HandleValue lengthValue)534   static TypedArrayObject* makeTypedArrayWithTemplate(
535       JSContext* cx, TypedArrayObject* templateObj, HandleObject arrayBuffer,
536       HandleValue byteOffsetValue, HandleValue lengthValue) {
537     MOZ_ASSERT(!IsWrapper(arrayBuffer));
538     MOZ_ASSERT(arrayBuffer->is<ArrayBufferObjectMaybeShared>());
539 
540     uint64_t byteOffset, length;
541     if (!byteOffsetAndLength(cx, byteOffsetValue, lengthValue, &byteOffset,
542                              &length)) {
543       return nullptr;
544     }
545 
546     return fromBufferSameCompartment(
547         cx, arrayBuffer.as<ArrayBufferObjectMaybeShared>(), byteOffset, length,
548         nullptr);
549   }
550 
551   // ES2018 draft rev 8340bf9a8427ea81bb0d1459471afbcc91d18add
552   // 22.2.4.1 TypedArray ( )
553   // 22.2.4.2 TypedArray ( length )
554   // 22.2.4.3 TypedArray ( typedArray )
555   // 22.2.4.4 TypedArray ( object )
556   // 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
class_constructor(JSContext * cx,unsigned argc,Value * vp)557   static bool class_constructor(JSContext* cx, unsigned argc, Value* vp) {
558     CallArgs args = CallArgsFromVp(argc, vp);
559 
560     // Step 1 (22.2.4.1) or 2 (22.2.4.2-5).
561     if (!ThrowIfNotConstructing(cx, args, "typed array")) {
562       return false;
563     }
564 
565     JSObject* obj = create(cx, args);
566     if (!obj) {
567       return false;
568     }
569     args.rval().setObject(*obj);
570     return true;
571   }
572 
573  private:
create(JSContext * cx,const CallArgs & args)574   static JSObject* create(JSContext* cx, const CallArgs& args) {
575     MOZ_ASSERT(args.isConstructing());
576 
577     // 22.2.4.1 TypedArray ( )
578     // 22.2.4.2 TypedArray ( length )
579     if (args.length() == 0 || !args[0].isObject()) {
580       // 22.2.4.2, step 3.
581       uint64_t len;
582       if (!ToIndex(cx, args.get(0), JSMSG_BAD_ARRAY_LENGTH, &len)) {
583         return nullptr;
584       }
585 
586       // 22.2.4.1, step 3 and 22.2.4.2, step 5.
587       // 22.2.4.2.1 AllocateTypedArray, step 1.
588       RootedObject proto(cx);
589       if (!GetPrototypeFromBuiltinConstructor(cx, args, protoKey(), &proto)) {
590         return nullptr;
591       }
592 
593       return fromLength(cx, len, proto);
594     }
595 
596     RootedObject dataObj(cx, &args[0].toObject());
597 
598     // 22.2.4.{3,4,5}, step 4.
599     // 22.2.4.2.1 AllocateTypedArray, step 1.
600     RootedObject proto(cx);
601     if (!GetPrototypeFromBuiltinConstructor(cx, args, protoKey(), &proto)) {
602       return nullptr;
603     }
604 
605     // 22.2.4.3 TypedArray ( typedArray )
606     // 22.2.4.4 TypedArray ( object )
607     if (!UncheckedUnwrap(dataObj)->is<ArrayBufferObjectMaybeShared>()) {
608       return fromArray(cx, dataObj, proto);
609     }
610 
611     // 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
612 
613     uint64_t byteOffset, length;
614     if (!byteOffsetAndLength(cx, args.get(1), args.get(2), &byteOffset,
615                              &length)) {
616       return nullptr;
617     }
618 
619     // Steps 9-17.
620     if (dataObj->is<ArrayBufferObjectMaybeShared>()) {
621       HandleArrayBufferObjectMaybeShared buffer =
622           dataObj.as<ArrayBufferObjectMaybeShared>();
623       return fromBufferSameCompartment(cx, buffer, byteOffset, length, proto);
624     }
625     return fromBufferWrapped(cx, dataObj, byteOffset, length, proto);
626   }
627 
628   // ES2018 draft rev 8340bf9a8427ea81bb0d1459471afbcc91d18add
629   // 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
630   // Steps 6-8.
byteOffsetAndLength(JSContext * cx,HandleValue byteOffsetValue,HandleValue lengthValue,uint64_t * byteOffset,uint64_t * length)631   static bool byteOffsetAndLength(JSContext* cx, HandleValue byteOffsetValue,
632                                   HandleValue lengthValue, uint64_t* byteOffset,
633                                   uint64_t* length) {
634     *byteOffset = 0;
635     if (!byteOffsetValue.isUndefined()) {
636       // Step 6.
637       if (!ToIndex(cx, byteOffsetValue, byteOffset)) {
638         return false;
639       }
640 
641       // Step 7.
642       if (*byteOffset % BYTES_PER_ELEMENT != 0) {
643         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
644                                   JSMSG_TYPED_ARRAY_CONSTRUCT_OFFSET_BOUNDS,
645                                   Scalar::name(ArrayTypeID()),
646                                   Scalar::byteSizeString(ArrayTypeID()));
647         return false;
648       }
649     }
650 
651     *length = UINT64_MAX;
652     if (!lengthValue.isUndefined()) {
653       // Step 8.a.
654       if (!ToIndex(cx, lengthValue, length)) {
655         return false;
656       }
657     }
658 
659     return true;
660   }
661 
662   // ES2018 draft rev 8340bf9a8427ea81bb0d1459471afbcc91d18add
663   // 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
664   // Steps 9-12.
computeAndCheckLength(JSContext * cx,HandleArrayBufferObjectMaybeShared bufferMaybeUnwrapped,uint64_t byteOffset,uint64_t lengthIndex,size_t * length)665   static bool computeAndCheckLength(
666       JSContext* cx, HandleArrayBufferObjectMaybeShared bufferMaybeUnwrapped,
667       uint64_t byteOffset, uint64_t lengthIndex, size_t* length) {
668     MOZ_ASSERT(byteOffset % BYTES_PER_ELEMENT == 0);
669     MOZ_ASSERT(byteOffset < uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT));
670     MOZ_ASSERT_IF(lengthIndex != UINT64_MAX,
671                   lengthIndex < uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT));
672 
673     // Step 9.
674     if (bufferMaybeUnwrapped->isDetached()) {
675       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
676                                 JSMSG_TYPED_ARRAY_DETACHED);
677       return false;
678     }
679 
680     // Step 10.
681     size_t bufferByteLength = bufferMaybeUnwrapped->byteLength();
682 
683     size_t len;
684     if (lengthIndex == UINT64_MAX) {
685       // Steps 11.a, 11.c.
686       if (bufferByteLength % BYTES_PER_ELEMENT != 0) {
687         // The given byte array doesn't map exactly to
688         // |BYTES_PER_ELEMENT * N|
689         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
690                                   JSMSG_TYPED_ARRAY_CONSTRUCT_OFFSET_MISALIGNED,
691                                   Scalar::name(ArrayTypeID()),
692                                   Scalar::byteSizeString(ArrayTypeID()));
693         return false;
694       }
695 
696       if (byteOffset > bufferByteLength) {
697         // |byteOffset| is invalid.
698         JS_ReportErrorNumberASCII(
699             cx, GetErrorMessage, nullptr,
700             JSMSG_TYPED_ARRAY_CONSTRUCT_OFFSET_LENGTH_BOUNDS,
701             Scalar::name(ArrayTypeID()));
702         return false;
703       }
704 
705       // Step 11.b.
706       size_t newByteLength = bufferByteLength - size_t(byteOffset);
707       len = newByteLength / BYTES_PER_ELEMENT;
708     } else {
709       // Step 12.a.
710       uint64_t newByteLength = lengthIndex * BYTES_PER_ELEMENT;
711 
712       // Step 12.b.
713       if (byteOffset + newByteLength > bufferByteLength) {
714         // |byteOffset + newByteLength| is too big for the arraybuffer
715         JS_ReportErrorNumberASCII(
716             cx, GetErrorMessage, nullptr,
717             JSMSG_TYPED_ARRAY_CONSTRUCT_ARRAY_LENGTH_BOUNDS,
718             Scalar::name(ArrayTypeID()));
719         return false;
720       }
721 
722       len = size_t(lengthIndex);
723     }
724 
725     if (len > maxByteLength() / BYTES_PER_ELEMENT) {
726       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
727                                 JSMSG_TYPED_ARRAY_CONSTRUCT_TOO_LARGE,
728                                 Scalar::name(ArrayTypeID()));
729       return false;
730     }
731 
732     MOZ_ASSERT(len < SIZE_MAX);
733     *length = len;
734     return true;
735   }
736 
737   // ES2018 draft rev 8340bf9a8427ea81bb0d1459471afbcc91d18add
738   // 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
739   // Steps 9-17.
fromBufferSameCompartment(JSContext * cx,HandleArrayBufferObjectMaybeShared buffer,uint64_t byteOffset,uint64_t lengthIndex,HandleObject proto)740   static TypedArrayObject* fromBufferSameCompartment(
741       JSContext* cx, HandleArrayBufferObjectMaybeShared buffer,
742       uint64_t byteOffset, uint64_t lengthIndex, HandleObject proto) {
743     // Steps 9-12.
744     size_t length = 0;
745     if (!computeAndCheckLength(cx, buffer, byteOffset, lengthIndex, &length)) {
746       return nullptr;
747     }
748 
749     // Steps 13-17.
750     return makeInstance(cx, buffer, byteOffset, length, proto);
751   }
752 
753   // Create a TypedArray object in another compartment.
754   //
755   // ES6 supports creating a TypedArray in global A (using global A's
756   // TypedArray constructor) backed by an ArrayBuffer created in global B.
757   //
758   // Our TypedArrayObject implementation doesn't support a TypedArray in
759   // compartment A backed by an ArrayBuffer in compartment B. So in this
760   // case, we create the TypedArray in B (!) and return a cross-compartment
761   // wrapper.
762   //
763   // Extra twist: the spec says the new TypedArray's [[Prototype]] must be
764   // A's TypedArray.prototype. So even though we're creating the TypedArray
765   // in B, its [[Prototype]] must be (a cross-compartment wrapper for) the
766   // TypedArray.prototype in A.
fromBufferWrapped(JSContext * cx,HandleObject bufobj,uint64_t byteOffset,uint64_t lengthIndex,HandleObject proto)767   static JSObject* fromBufferWrapped(JSContext* cx, HandleObject bufobj,
768                                      uint64_t byteOffset, uint64_t lengthIndex,
769                                      HandleObject proto) {
770     JSObject* unwrapped = CheckedUnwrapStatic(bufobj);
771     if (!unwrapped) {
772       ReportAccessDenied(cx);
773       return nullptr;
774     }
775 
776     if (!unwrapped->is<ArrayBufferObjectMaybeShared>()) {
777       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
778                                 JSMSG_TYPED_ARRAY_BAD_ARGS);
779       return nullptr;
780     }
781 
782     RootedArrayBufferObjectMaybeShared unwrappedBuffer(cx);
783     unwrappedBuffer = &unwrapped->as<ArrayBufferObjectMaybeShared>();
784 
785     size_t length = 0;
786     if (!computeAndCheckLength(cx, unwrappedBuffer, byteOffset, lengthIndex,
787                                &length)) {
788       return nullptr;
789     }
790 
791     // Make sure to get the [[Prototype]] for the created typed array from
792     // this compartment.
793     RootedObject protoRoot(cx, proto);
794     if (!protoRoot) {
795       protoRoot = GlobalObject::getOrCreatePrototype(cx, protoKey());
796       if (!protoRoot) {
797         return nullptr;
798       }
799     }
800 
801     RootedObject typedArray(cx);
802     {
803       JSAutoRealm ar(cx, unwrappedBuffer);
804 
805       RootedObject wrappedProto(cx, protoRoot);
806       if (!cx->compartment()->wrap(cx, &wrappedProto)) {
807         return nullptr;
808       }
809 
810       typedArray =
811           makeInstance(cx, unwrappedBuffer, byteOffset, length, wrappedProto);
812       if (!typedArray) {
813         return nullptr;
814       }
815     }
816 
817     if (!cx->compartment()->wrap(cx, &typedArray)) {
818       return nullptr;
819     }
820 
821     return typedArray;
822   }
823 
824  public:
fromBuffer(JSContext * cx,HandleObject bufobj,size_t byteOffset,int64_t lengthInt)825   static JSObject* fromBuffer(JSContext* cx, HandleObject bufobj,
826                               size_t byteOffset, int64_t lengthInt) {
827     if (byteOffset % BYTES_PER_ELEMENT != 0) {
828       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
829                                 JSMSG_TYPED_ARRAY_CONSTRUCT_OFFSET_BOUNDS,
830                                 Scalar::name(ArrayTypeID()),
831                                 Scalar::byteSizeString(ArrayTypeID()));
832       return nullptr;  // invalid byteOffset
833     }
834 
835     uint64_t lengthIndex = lengthInt >= 0 ? uint64_t(lengthInt) : UINT64_MAX;
836     if (bufobj->is<ArrayBufferObjectMaybeShared>()) {
837       HandleArrayBufferObjectMaybeShared buffer =
838           bufobj.as<ArrayBufferObjectMaybeShared>();
839       return fromBufferSameCompartment(cx, buffer, byteOffset, lengthIndex,
840                                        nullptr);
841     }
842     return fromBufferWrapped(cx, bufobj, byteOffset, lengthIndex, nullptr);
843   }
844 
maybeCreateArrayBuffer(JSContext * cx,uint64_t count,HandleObject nonDefaultProto,MutableHandle<ArrayBufferObject * > buffer)845   static bool maybeCreateArrayBuffer(JSContext* cx, uint64_t count,
846                                      HandleObject nonDefaultProto,
847                                      MutableHandle<ArrayBufferObject*> buffer) {
848     if (count > maxByteLength() / BYTES_PER_ELEMENT) {
849       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
850                                 JSMSG_BAD_ARRAY_LENGTH);
851       return false;
852     }
853     size_t byteLength = count * BYTES_PER_ELEMENT;
854 
855     MOZ_ASSERT(byteLength <= maxByteLength());
856     static_assert(INLINE_BUFFER_LIMIT % BYTES_PER_ELEMENT == 0,
857                   "ArrayBuffer inline storage shouldn't waste any space");
858 
859     if (!nonDefaultProto && byteLength <= INLINE_BUFFER_LIMIT) {
860       // The array's data can be inline, and the buffer created lazily.
861       return true;
862     }
863 
864     ArrayBufferObject* buf =
865         ArrayBufferObject::createZeroed(cx, byteLength, nonDefaultProto);
866     if (!buf) {
867       return false;
868     }
869 
870     buffer.set(buf);
871     return true;
872   }
873 
874   // 22.2.4.1 TypedArray ( )
875   // 22.2.4.2 TypedArray ( length )
fromLength(JSContext * cx,uint64_t nelements,HandleObject proto=nullptr)876   static JSObject* fromLength(JSContext* cx, uint64_t nelements,
877                               HandleObject proto = nullptr) {
878     // 22.2.4.1, step 1 and 22.2.4.2, steps 1-3 (performed in caller).
879     // 22.2.4.1, step 2 and 22.2.4.2, step 4 (implicit).
880     // 22.2.4.1, step 3 and 22.2.4.2, step 5 (call AllocateTypedArray).
881 
882     Rooted<ArrayBufferObject*> buffer(cx);
883     if (!maybeCreateArrayBuffer(cx, nelements, nullptr, &buffer)) {
884       return nullptr;
885     }
886 
887     return makeInstance(cx, buffer, 0, nelements, proto);
888   }
889 
890   static bool AllocateArrayBuffer(JSContext* cx, HandleObject ctor,
891                                   size_t count,
892                                   MutableHandle<ArrayBufferObject*> buffer);
893 
894   static TypedArrayObject* fromArray(JSContext* cx, HandleObject other,
895                                      HandleObject proto = nullptr);
896 
897   static TypedArrayObject* fromTypedArray(JSContext* cx, HandleObject other,
898                                           bool isWrapped, HandleObject proto);
899 
900   static TypedArrayObject* fromObject(JSContext* cx, HandleObject other,
901                                       HandleObject proto);
902 
getIndex(TypedArrayObject * tarray,size_t index)903   static const NativeType getIndex(TypedArrayObject* tarray, size_t index) {
904     MOZ_ASSERT(index < tarray->length());
905     return jit::AtomicOperations::loadSafeWhenRacy(
906         tarray->dataPointerEither().cast<NativeType*>() + index);
907   }
908 
setIndex(TypedArrayObject & tarray,size_t index,NativeType val)909   static void setIndex(TypedArrayObject& tarray, size_t index, NativeType val) {
910     MOZ_ASSERT(index < tarray.length());
911     jit::AtomicOperations::storeSafeWhenRacy(
912         tarray.dataPointerEither().cast<NativeType*>() + index, val);
913   }
914 
915   static bool getElement(JSContext* cx, TypedArrayObject* tarray, size_t index,
916                          MutableHandleValue val);
917   static bool getElementPure(TypedArrayObject* tarray, size_t index, Value* vp);
918 
919   static bool setElement(JSContext* cx, Handle<TypedArrayObject*> obj,
920                          uint64_t index, HandleValue v, ObjectOpResult& result);
921 };
922 
923 template <typename NativeType>
convertValue(JSContext * cx,HandleValue v,NativeType * result)924 bool TypedArrayObjectTemplate<NativeType>::convertValue(JSContext* cx,
925                                                         HandleValue v,
926                                                         NativeType* result) {
927   double d;
928   if (!ToNumber(cx, v, &d)) {
929     return false;
930   }
931 
932   if (js::SupportDifferentialTesting()) {
933     // See the comment in ElementSpecific::doubleToNative.
934     d = JS::CanonicalizeNaN(d);
935   }
936 
937   // Assign based on characteristics of the destination type
938   if constexpr (ArrayTypeIsFloatingPoint()) {
939     *result = NativeType(d);
940   } else if constexpr (ArrayTypeIsUnsigned()) {
941     static_assert(sizeof(NativeType) <= 4);
942     uint32_t n = ToUint32(d);
943     *result = NativeType(n);
944   } else if constexpr (ArrayTypeID() == Scalar::Uint8Clamped) {
945     // The uint8_clamped type has a special rounding converter
946     // for doubles.
947     *result = NativeType(d);
948   } else {
949     static_assert(sizeof(NativeType) <= 4);
950     int32_t n = ToInt32(d);
951     *result = NativeType(n);
952   }
953   return true;
954 }
955 
956 template <>
convertValue(JSContext * cx,HandleValue v,int64_t * result)957 bool TypedArrayObjectTemplate<int64_t>::convertValue(JSContext* cx,
958                                                      HandleValue v,
959                                                      int64_t* result) {
960   JS_TRY_VAR_OR_RETURN_FALSE(cx, *result, ToBigInt64(cx, v));
961   return true;
962 }
963 
964 template <>
convertValue(JSContext * cx,HandleValue v,uint64_t * result)965 bool TypedArrayObjectTemplate<uint64_t>::convertValue(JSContext* cx,
966                                                       HandleValue v,
967                                                       uint64_t* result) {
968   JS_TRY_VAR_OR_RETURN_FALSE(cx, *result, ToBigUint64(cx, v));
969   return true;
970 }
971 
972 // https://tc39.github.io/proposal-bigint/#sec-integerindexedelementset
973 // 9.4.5.11 IntegerIndexedElementSet ( O, index, value )
974 template <typename NativeType>
setElement(JSContext * cx,Handle<TypedArrayObject * > obj,uint64_t index,HandleValue v,ObjectOpResult & result)975 /* static */ bool TypedArrayObjectTemplate<NativeType>::setElement(
976     JSContext* cx, Handle<TypedArrayObject*> obj, uint64_t index, HandleValue v,
977     ObjectOpResult& result) {
978   MOZ_ASSERT(!obj->hasDetachedBuffer());
979   MOZ_ASSERT(index < obj->length());
980 
981   // Step 1 is enforced by the caller.
982 
983   // Steps 2-3.
984   NativeType nativeValue;
985   if (!convertValue(cx, v, &nativeValue)) {
986     return false;
987   }
988 
989   // Step 4.
990   if (index < obj->length()) {
991     MOZ_ASSERT(!obj->hasDetachedBuffer(),
992                "detaching an array buffer sets the length to zero");
993     TypedArrayObjectTemplate<NativeType>::setIndex(*obj, index, nativeValue);
994   }
995 
996   // Step 5.
997   return result.succeed();
998 }
999 
1000 #define CREATE_TYPE_FOR_TYPED_ARRAY(T, N) \
1001   typedef TypedArrayObjectTemplate<T> N##Array;
1002 JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPE_FOR_TYPED_ARRAY)
1003 #undef CREATE_TYPE_FOR_TYPED_ARRAY
1004 
1005 } /* anonymous namespace */
1006 
NewTypedArrayWithTemplateAndLength(JSContext * cx,HandleObject templateObj,int32_t len)1007 TypedArrayObject* js::NewTypedArrayWithTemplateAndLength(
1008     JSContext* cx, HandleObject templateObj, int32_t len) {
1009   MOZ_ASSERT(templateObj->is<TypedArrayObject>());
1010   TypedArrayObject* tobj = &templateObj->as<TypedArrayObject>();
1011 
1012   switch (tobj->type()) {
1013 #define CREATE_TYPED_ARRAY(T, N)                                             \
1014   case Scalar::N:                                                            \
1015     return TypedArrayObjectTemplate<T>::makeTypedArrayWithTemplate(cx, tobj, \
1016                                                                    len);
1017     JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPED_ARRAY)
1018 #undef CREATE_TYPED_ARRAY
1019     default:
1020       MOZ_CRASH("Unsupported TypedArray type");
1021   }
1022 }
1023 
NewTypedArrayWithTemplateAndArray(JSContext * cx,HandleObject templateObj,HandleObject array)1024 TypedArrayObject* js::NewTypedArrayWithTemplateAndArray(
1025     JSContext* cx, HandleObject templateObj, HandleObject array) {
1026   MOZ_ASSERT(templateObj->is<TypedArrayObject>());
1027   TypedArrayObject* tobj = &templateObj->as<TypedArrayObject>();
1028 
1029   switch (tobj->type()) {
1030 #define CREATE_TYPED_ARRAY(T, N)                                             \
1031   case Scalar::N:                                                            \
1032     return TypedArrayObjectTemplate<T>::makeTypedArrayWithTemplate(cx, tobj, \
1033                                                                    array);
1034     JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPED_ARRAY)
1035 #undef CREATE_TYPED_ARRAY
1036     default:
1037       MOZ_CRASH("Unsupported TypedArray type");
1038   }
1039 }
1040 
NewTypedArrayWithTemplateAndBuffer(JSContext * cx,HandleObject templateObj,HandleObject arrayBuffer,HandleValue byteOffset,HandleValue length)1041 TypedArrayObject* js::NewTypedArrayWithTemplateAndBuffer(
1042     JSContext* cx, HandleObject templateObj, HandleObject arrayBuffer,
1043     HandleValue byteOffset, HandleValue length) {
1044   MOZ_ASSERT(templateObj->is<TypedArrayObject>());
1045   TypedArrayObject* tobj = &templateObj->as<TypedArrayObject>();
1046 
1047   switch (tobj->type()) {
1048 #define CREATE_TYPED_ARRAY(T, N)                                    \
1049   case Scalar::N:                                                   \
1050     return TypedArrayObjectTemplate<T>::makeTypedArrayWithTemplate( \
1051         cx, tobj, arrayBuffer, byteOffset, length);
1052     JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPED_ARRAY)
1053 #undef CREATE_TYPED_ARRAY
1054     default:
1055       MOZ_CRASH("Unsupported TypedArray type");
1056   }
1057 }
1058 
1059 // ES2018 draft rev 2aea8f3e617b49df06414eb062ab44fad87661d3
1060 // 24.1.1.1 AllocateArrayBuffer ( constructor, byteLength )
1061 // byteLength = count * BYTES_PER_ELEMENT
1062 template <typename T>
AllocateArrayBuffer(JSContext * cx,HandleObject ctor,size_t count,MutableHandle<ArrayBufferObject * > buffer)1063 /* static */ bool TypedArrayObjectTemplate<T>::AllocateArrayBuffer(
1064     JSContext* cx, HandleObject ctor, size_t count,
1065     MutableHandle<ArrayBufferObject*> buffer) {
1066   // 24.1.1.1 step 1 (partially).
1067   RootedObject proto(cx);
1068 
1069   JSObject* arrayBufferCtor =
1070       GlobalObject::getOrCreateArrayBufferConstructor(cx, cx->global());
1071   if (!arrayBufferCtor) {
1072     return false;
1073   }
1074 
1075   // As an optimization, skip the "prototype" lookup for %ArrayBuffer%.
1076   if (ctor != arrayBufferCtor) {
1077     // 9.1.13 OrdinaryCreateFromConstructor, steps 1-2.
1078     if (!GetPrototypeFromConstructor(cx, ctor, JSProto_ArrayBuffer, &proto)) {
1079       return false;
1080     }
1081   }
1082 
1083   // 24.1.1.1 steps 1 (remaining part), 2-6.
1084   if (!maybeCreateArrayBuffer(cx, count, proto, buffer)) {
1085     return false;
1086   }
1087 
1088   return true;
1089 }
1090 
IsArrayBufferSpecies(JSContext * cx,JSFunction * species)1091 static bool IsArrayBufferSpecies(JSContext* cx, JSFunction* species) {
1092   return IsSelfHostedFunctionWithName(species, cx->names().ArrayBufferSpecies);
1093 }
1094 
GetBufferSpeciesConstructor(JSContext * cx,Handle<TypedArrayObject * > typedArray,bool isWrapped,SpeciesConstructorOverride override)1095 static JSObject* GetBufferSpeciesConstructor(
1096     JSContext* cx, Handle<TypedArrayObject*> typedArray, bool isWrapped,
1097     SpeciesConstructorOverride override) {
1098   RootedObject defaultCtor(
1099       cx, GlobalObject::getOrCreateArrayBufferConstructor(cx, cx->global()));
1100   if (!defaultCtor) {
1101     return nullptr;
1102   }
1103 
1104   // Use the current global's ArrayBuffer if the override is set.
1105   if (override == SpeciesConstructorOverride::ArrayBuffer) {
1106     return defaultCtor;
1107   }
1108 
1109   RootedObject obj(cx, typedArray->bufferEither());
1110   if (!obj) {
1111     MOZ_ASSERT(!isWrapped);
1112 
1113     // The buffer was never exposed to content code, so if
1114     // 1. %ArrayBufferPrototype%.constructor == %ArrayBuffer%, and
1115     // 2. %ArrayBuffer%[@@species] == ArrayBufferSpecies
1116     // we don't have to reify the buffer object and can simply return the
1117     // default arrray buffer constructor.
1118 
1119     JSObject* proto =
1120         GlobalObject::getOrCreateArrayBufferPrototype(cx, cx->global());
1121     if (!proto) {
1122       return nullptr;
1123     }
1124 
1125     Value ctor;
1126     bool found;
1127     if (GetOwnPropertyPure(cx, proto, NameToId(cx->names().constructor), &ctor,
1128                            &found) &&
1129         ctor.isObject() && &ctor.toObject() == defaultCtor) {
1130       jsid speciesId = SYMBOL_TO_JSID(cx->wellKnownSymbols().species);
1131       JSFunction* getter;
1132       if (GetOwnGetterPure(cx, defaultCtor, speciesId, &getter) && getter &&
1133           IsArrayBufferSpecies(cx, getter)) {
1134         return defaultCtor;
1135       }
1136     }
1137 
1138     if (!TypedArrayObject::ensureHasBuffer(cx, typedArray)) {
1139       return nullptr;
1140     }
1141 
1142     obj.set(typedArray->bufferEither());
1143   } else {
1144     if (isWrapped && !cx->compartment()->wrap(cx, &obj)) {
1145       return nullptr;
1146     }
1147   }
1148 
1149   return SpeciesConstructor(cx, obj, defaultCtor, IsArrayBufferSpecies);
1150 }
1151 
1152 template <typename T>
fromArray(JSContext * cx,HandleObject other,HandleObject proto)1153 /* static */ TypedArrayObject* TypedArrayObjectTemplate<T>::fromArray(
1154     JSContext* cx, HandleObject other, HandleObject proto /* = nullptr */) {
1155   // Allow nullptr proto for FriendAPI methods, which don't care about
1156   // subclassing.
1157   if (other->is<TypedArrayObject>()) {
1158     return fromTypedArray(cx, other, /* wrapped= */ false, proto);
1159   }
1160 
1161   if (other->is<WrapperObject>() &&
1162       UncheckedUnwrap(other)->is<TypedArrayObject>()) {
1163     return fromTypedArray(cx, other, /* wrapped= */ true, proto);
1164   }
1165 
1166   return fromObject(cx, other, proto);
1167 }
1168 
1169 // ES2018 draft rev 272beb67bc5cd9fd18a220665198384108208ee1
1170 // 22.2.4.3 TypedArray ( typedArray )
1171 template <typename T>
fromTypedArray(JSContext * cx,HandleObject other,bool isWrapped,HandleObject proto)1172 /* static */ TypedArrayObject* TypedArrayObjectTemplate<T>::fromTypedArray(
1173     JSContext* cx, HandleObject other, bool isWrapped, HandleObject proto) {
1174   // Step 1.
1175   MOZ_ASSERT_IF(!isWrapped, other->is<TypedArrayObject>());
1176   MOZ_ASSERT_IF(isWrapped, other->is<WrapperObject>() &&
1177                                UncheckedUnwrap(other)->is<TypedArrayObject>());
1178 
1179   // Step 2 (Already performed in caller).
1180 
1181   // Steps 3-4 (Allocation deferred until later).
1182 
1183   // Step 5.
1184   Rooted<TypedArrayObject*> srcArray(cx);
1185   if (!isWrapped) {
1186     srcArray = &other->as<TypedArrayObject>();
1187   } else {
1188     srcArray = other->maybeUnwrapAs<TypedArrayObject>();
1189     if (!srcArray) {
1190       ReportAccessDenied(cx);
1191       return nullptr;
1192     }
1193   }
1194 
1195   // To keep things simpler, we always reify the array buffer for cross-realm or
1196   // wrapped typed arrays. Note: isWrapped does not imply cross-realm, because
1197   // of same-compartment wrappers.
1198   if (cx->realm() != srcArray->realm() || isWrapped) {
1199     if (!TypedArrayObject::ensureHasBuffer(cx, srcArray)) {
1200       return nullptr;
1201     }
1202   }
1203 
1204   // Step 6 (skipped).
1205 
1206   // Step 7.
1207   if (srcArray->hasDetachedBuffer()) {
1208     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1209                               JSMSG_TYPED_ARRAY_DETACHED);
1210     return nullptr;
1211   }
1212 
1213   // Step 8 (skipped).
1214 
1215   // Step 9.
1216   size_t elementLength = srcArray->length();
1217 
1218   // Steps 10-15 (skipped).
1219 
1220   // Steps 16-17.
1221   bool isShared = srcArray->isSharedMemory();
1222   SpeciesConstructorOverride override =
1223       isShared ? SpeciesConstructorOverride::ArrayBuffer
1224                : SpeciesConstructorOverride::None;
1225 
1226   RootedObject bufferCtor(
1227       cx, GetBufferSpeciesConstructor(cx, srcArray, isWrapped, override));
1228   if (!bufferCtor) {
1229     return nullptr;
1230   }
1231 
1232   // Steps 18-19.
1233   Rooted<ArrayBufferObject*> buffer(cx);
1234 
1235   // Step 19.a or 18.a, 24.1.1.4 CloneArrayBuffer(...) steps 1-3.
1236   if (!AllocateArrayBuffer(cx, bufferCtor, elementLength, &buffer)) {
1237     return nullptr;
1238   }
1239 
1240   // Step 19.b or 24.1.1.4 step 4.
1241   if (srcArray->hasDetachedBuffer()) {
1242     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1243                               JSMSG_TYPED_ARRAY_DETACHED);
1244     return nullptr;
1245   }
1246 
1247   // BigInt proposal 7.24, step 19.c.
1248   if (Scalar::isBigIntType(ArrayTypeID()) !=
1249       Scalar::isBigIntType(srcArray->type())) {
1250     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1251                               JSMSG_TYPED_ARRAY_NOT_COMPATIBLE,
1252                               srcArray->getClass()->name,
1253                               TypedArrayObject::classes[ArrayTypeID()].name);
1254     return nullptr;
1255   }
1256 
1257   // Steps 3-4 (remaining part), 20-23.
1258   Rooted<TypedArrayObject*> obj(
1259       cx, makeInstance(cx, buffer, 0, elementLength, proto));
1260   if (!obj) {
1261     return nullptr;
1262   }
1263 
1264   // Steps 19.c-f or 24.1.1.4 steps 5-7.
1265   MOZ_ASSERT(!obj->isSharedMemory());
1266   if (isShared) {
1267     if (!ElementSpecific<T, SharedOps>::setFromTypedArray(obj, srcArray, 0)) {
1268       return nullptr;
1269     }
1270   } else {
1271     if (!ElementSpecific<T, UnsharedOps>::setFromTypedArray(obj, srcArray, 0)) {
1272       return nullptr;
1273     }
1274   }
1275 
1276   // Step 24.
1277   return obj;
1278 }
1279 
IsOptimizableInit(JSContext * cx,HandleObject iterable,bool * optimized)1280 static MOZ_ALWAYS_INLINE bool IsOptimizableInit(JSContext* cx,
1281                                                 HandleObject iterable,
1282                                                 bool* optimized) {
1283   MOZ_ASSERT(!*optimized);
1284 
1285   if (!IsPackedArray(iterable)) {
1286     return true;
1287   }
1288 
1289   ForOfPIC::Chain* stubChain = ForOfPIC::getOrCreate(cx);
1290   if (!stubChain) {
1291     return false;
1292   }
1293 
1294   return stubChain->tryOptimizeArray(cx, iterable.as<ArrayObject>(), optimized);
1295 }
1296 
1297 // ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e
1298 // 22.2.4.4 TypedArray ( object )
1299 template <typename T>
fromObject(JSContext * cx,HandleObject other,HandleObject proto)1300 /* static */ TypedArrayObject* TypedArrayObjectTemplate<T>::fromObject(
1301     JSContext* cx, HandleObject other, HandleObject proto) {
1302   // Steps 1-2 (Already performed in caller).
1303 
1304   // Steps 3-4 (Allocation deferred until later).
1305 
1306   bool optimized = false;
1307   if (!IsOptimizableInit(cx, other, &optimized)) {
1308     return nullptr;
1309   }
1310 
1311   // Fast path when iterable is a packed array using the default iterator.
1312   if (optimized) {
1313     // Step 6.a (We don't need to call IterableToList for the fast path).
1314     HandleArrayObject array = other.as<ArrayObject>();
1315 
1316     // Step 6.b.
1317     size_t len = array->getDenseInitializedLength();
1318 
1319     // Step 6.c.
1320     Rooted<ArrayBufferObject*> buffer(cx);
1321     if (!maybeCreateArrayBuffer(cx, len, nullptr, &buffer)) {
1322       return nullptr;
1323     }
1324 
1325     Rooted<TypedArrayObject*> obj(cx, makeInstance(cx, buffer, 0, len, proto));
1326     if (!obj) {
1327       return nullptr;
1328     }
1329 
1330     // Steps 6.d-e.
1331     MOZ_ASSERT(!obj->isSharedMemory());
1332     if (!ElementSpecific<T, UnsharedOps>::initFromIterablePackedArray(cx, obj,
1333                                                                       array)) {
1334       return nullptr;
1335     }
1336 
1337     // Step 6.f (The assertion isn't applicable for the fast path).
1338 
1339     // Step 6.g.
1340     return obj;
1341   }
1342 
1343   // Step 5.
1344   RootedValue callee(cx);
1345   RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator));
1346   if (!GetProperty(cx, other, other, iteratorId, &callee)) {
1347     return nullptr;
1348   }
1349 
1350   // Steps 6-8.
1351   RootedObject arrayLike(cx);
1352   if (!callee.isNullOrUndefined()) {
1353     // Throw if other[Symbol.iterator] isn't callable.
1354     if (!callee.isObject() || !callee.toObject().isCallable()) {
1355       RootedValue otherVal(cx, ObjectValue(*other));
1356       UniqueChars bytes =
1357           DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, otherVal, nullptr);
1358       if (!bytes) {
1359         return nullptr;
1360       }
1361       JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE,
1362                                bytes.get());
1363       return nullptr;
1364     }
1365 
1366     FixedInvokeArgs<2> args2(cx);
1367     args2[0].setObject(*other);
1368     args2[1].set(callee);
1369 
1370     // Step 6.a.
1371     RootedValue rval(cx);
1372     if (!CallSelfHostedFunction(cx, cx->names().IterableToList,
1373                                 UndefinedHandleValue, args2, &rval)) {
1374       return nullptr;
1375     }
1376 
1377     // Steps 6.b-g (Implemented in steps 9-13 below).
1378     arrayLike = &rval.toObject();
1379   } else {
1380     // Step 7 is an assertion: object is not an Iterator. Testing this is
1381     // literally the very last thing we did, so we don't assert here.
1382 
1383     // Step 8.
1384     arrayLike = other;
1385   }
1386 
1387   // Step 9.
1388   uint64_t len;
1389   if (!GetLengthProperty(cx, arrayLike, &len)) {
1390     return nullptr;
1391   }
1392 
1393   // Step 10.
1394   Rooted<ArrayBufferObject*> buffer(cx);
1395   if (!maybeCreateArrayBuffer(cx, len, nullptr, &buffer)) {
1396     return nullptr;
1397   }
1398 
1399   MOZ_ASSERT(len <= maxByteLength() / BYTES_PER_ELEMENT);
1400 
1401   Rooted<TypedArrayObject*> obj(cx, makeInstance(cx, buffer, 0, len, proto));
1402   if (!obj) {
1403     return nullptr;
1404   }
1405 
1406   // Steps 11-12.
1407   MOZ_ASSERT(!obj->isSharedMemory());
1408   if (!ElementSpecific<T, UnsharedOps>::setFromNonTypedArray(cx, obj, arrayLike,
1409                                                              len)) {
1410     return nullptr;
1411   }
1412 
1413   // Step 13.
1414   return obj;
1415 }
1416 
TypedArrayConstructor(JSContext * cx,unsigned argc,Value * vp)1417 bool TypedArrayConstructor(JSContext* cx, unsigned argc, Value* vp) {
1418   CallArgs args = CallArgsFromVp(argc, vp);
1419   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1420                             JSMSG_TYPED_ARRAY_CALL_OR_CONSTRUCT,
1421                             args.isConstructing() ? "construct" : "call");
1422   return false;
1423 }
1424 
1425 template <typename T>
GetTemplateObjectForNative(JSContext * cx,const JS::HandleValueArray args,MutableHandleObject res)1426 static bool GetTemplateObjectForNative(JSContext* cx,
1427                                        const JS::HandleValueArray args,
1428                                        MutableHandleObject res) {
1429   if (args.length() == 0) {
1430     return true;
1431   }
1432 
1433   HandleValue arg = args[0];
1434   if (arg.isInt32()) {
1435     uint32_t len = 0;
1436     if (arg.toInt32() >= 0) {
1437       len = arg.toInt32();
1438     }
1439 
1440     size_t nbytes;
1441     if (!js::CalculateAllocSize<T>(len, &nbytes) ||
1442         nbytes > TypedArrayObject::maxByteLength()) {
1443       return true;
1444     }
1445 
1446     res.set(TypedArrayObjectTemplate<T>::makeTemplateObject(cx, len));
1447     return !!res;
1448   }
1449 
1450   // We don't support wrappers, because of the complicated interaction between
1451   // wrapped ArrayBuffers and TypedArrays, see |fromBufferWrapped()|.
1452   if (arg.isObject() && !IsWrapper(&arg.toObject())) {
1453     // We don't use the template's length in the object case, so we can create
1454     // the template typed array with an initial length of zero.
1455     uint32_t len = 0;
1456     res.set(TypedArrayObjectTemplate<T>::makeTemplateObject(cx, len));
1457     return !!res;
1458   }
1459 
1460   return true;
1461 }
1462 
GetTemplateObjectForNative(JSContext * cx,Native native,const JS::HandleValueArray args,MutableHandleObject res)1463 /* static */ bool TypedArrayObject::GetTemplateObjectForNative(
1464     JSContext* cx, Native native, const JS::HandleValueArray args,
1465     MutableHandleObject res) {
1466   MOZ_ASSERT(!res);
1467 #define CHECK_TYPED_ARRAY_CONSTRUCTOR(T, N)                        \
1468   if (native == &TypedArrayObjectTemplate<T>::class_constructor) { \
1469     return ::GetTemplateObjectForNative<T>(cx, args, res);         \
1470   }
1471   JS_FOR_EACH_TYPED_ARRAY(CHECK_TYPED_ARRAY_CONSTRUCTOR)
1472 #undef CHECK_TYPED_ARRAY_CONSTRUCTOR
1473   return true;
1474 }
1475 
LengthGetterImpl(JSContext * cx,const CallArgs & args)1476 static bool LengthGetterImpl(JSContext* cx, const CallArgs& args) {
1477   auto* tarr = &args.thisv().toObject().as<TypedArrayObject>();
1478   args.rval().set(tarr->lengthValue());
1479   return true;
1480 }
1481 
TypedArray_lengthGetter(JSContext * cx,unsigned argc,Value * vp)1482 static bool TypedArray_lengthGetter(JSContext* cx, unsigned argc, Value* vp) {
1483   CallArgs args = CallArgsFromVp(argc, vp);
1484   return CallNonGenericMethod<TypedArrayObject::is, LengthGetterImpl>(cx, args);
1485 }
1486 
ByteOffsetGetterImpl(JSContext * cx,const CallArgs & args)1487 static bool ByteOffsetGetterImpl(JSContext* cx, const CallArgs& args) {
1488   auto* tarr = &args.thisv().toObject().as<TypedArrayObject>();
1489   args.rval().set(tarr->byteOffsetValue());
1490   return true;
1491 }
1492 
TypedArray_byteOffsetGetter(JSContext * cx,unsigned argc,Value * vp)1493 static bool TypedArray_byteOffsetGetter(JSContext* cx, unsigned argc,
1494                                         Value* vp) {
1495   CallArgs args = CallArgsFromVp(argc, vp);
1496   return CallNonGenericMethod<TypedArrayObject::is, ByteOffsetGetterImpl>(cx,
1497                                                                           args);
1498 }
1499 
ByteLengthGetterImpl(JSContext * cx,const CallArgs & args)1500 static bool ByteLengthGetterImpl(JSContext* cx, const CallArgs& args) {
1501   auto* tarr = &args.thisv().toObject().as<TypedArrayObject>();
1502   args.rval().set(tarr->byteLengthValue());
1503   return true;
1504 }
1505 
TypedArray_byteLengthGetter(JSContext * cx,unsigned argc,Value * vp)1506 static bool TypedArray_byteLengthGetter(JSContext* cx, unsigned argc,
1507                                         Value* vp) {
1508   CallArgs args = CallArgsFromVp(argc, vp);
1509   return CallNonGenericMethod<TypedArrayObject::is, ByteLengthGetterImpl>(cx,
1510                                                                           args);
1511 }
1512 
BufferGetterImpl(JSContext * cx,const CallArgs & args)1513 static bool BufferGetterImpl(JSContext* cx, const CallArgs& args) {
1514   MOZ_ASSERT(TypedArrayObject::is(args.thisv()));
1515   Rooted<TypedArrayObject*> tarray(
1516       cx, &args.thisv().toObject().as<TypedArrayObject>());
1517   if (!TypedArrayObject::ensureHasBuffer(cx, tarray)) {
1518     return false;
1519   }
1520   args.rval().set(tarray->bufferValue());
1521   return true;
1522 }
1523 
1524 /*static*/
TypedArray_bufferGetter(JSContext * cx,unsigned argc,Value * vp)1525 bool js::TypedArray_bufferGetter(JSContext* cx, unsigned argc, Value* vp) {
1526   CallArgs args = CallArgsFromVp(argc, vp);
1527   return CallNonGenericMethod<TypedArrayObject::is, BufferGetterImpl>(cx, args);
1528 }
1529 
1530 // ES2019 draft rev fc9ecdcd74294d0ca3146d4b274e2a8e79565dc3
1531 // 22.2.3.32 get %TypedArray%.prototype [ @@toStringTag ]
TypedArray_toStringTagGetter(JSContext * cx,unsigned argc,Value * vp)1532 static bool TypedArray_toStringTagGetter(JSContext* cx, unsigned argc,
1533                                          Value* vp) {
1534   CallArgs args = CallArgsFromVp(argc, vp);
1535 
1536   // Steps 1-2.
1537   if (!args.thisv().isObject()) {
1538     args.rval().setUndefined();
1539     return true;
1540   }
1541 
1542   JSObject* obj = CheckedUnwrapStatic(&args.thisv().toObject());
1543   if (!obj) {
1544     ReportAccessDenied(cx);
1545     return false;
1546   }
1547 
1548   // Step 3.
1549   if (!obj->is<TypedArrayObject>()) {
1550     args.rval().setUndefined();
1551     return true;
1552   }
1553 
1554   // Steps 4-6.
1555   JSProtoKey protoKey = StandardProtoKeyOrNull(obj);
1556   MOZ_ASSERT(protoKey);
1557 
1558   args.rval().setString(ClassName(protoKey, cx));
1559   return true;
1560 }
1561 
1562 /* static */ const JSPropertySpec TypedArrayObject::protoAccessors[] = {
1563     JS_PSG("length", TypedArray_lengthGetter, 0),
1564     JS_PSG("buffer", TypedArray_bufferGetter, 0),
1565     JS_PSG("byteLength", TypedArray_byteLengthGetter, 0),
1566     JS_PSG("byteOffset", TypedArray_byteOffsetGetter, 0),
1567     JS_SYM_GET(toStringTag, TypedArray_toStringTagGetter, 0),
1568     JS_PS_END};
1569 
1570 template <typename T>
SetFromTypedArray(Handle<TypedArrayObject * > target,Handle<TypedArrayObject * > source,size_t offset)1571 static inline bool SetFromTypedArray(Handle<TypedArrayObject*> target,
1572                                      Handle<TypedArrayObject*> source,
1573                                      size_t offset) {
1574   // WARNING: |source| may be an unwrapped typed array from a different
1575   // compartment. Proceed with caution!
1576 
1577   if (target->isSharedMemory() || source->isSharedMemory()) {
1578     return ElementSpecific<T, SharedOps>::setFromTypedArray(target, source,
1579                                                             offset);
1580   }
1581   return ElementSpecific<T, UnsharedOps>::setFromTypedArray(target, source,
1582                                                             offset);
1583 }
1584 
1585 template <typename T>
SetFromNonTypedArray(JSContext * cx,Handle<TypedArrayObject * > target,HandleObject source,size_t len,size_t offset)1586 static inline bool SetFromNonTypedArray(JSContext* cx,
1587                                         Handle<TypedArrayObject*> target,
1588                                         HandleObject source, size_t len,
1589                                         size_t offset) {
1590   MOZ_ASSERT(!source->is<TypedArrayObject>(), "use SetFromTypedArray");
1591 
1592   if (target->isSharedMemory()) {
1593     return ElementSpecific<T, SharedOps>::setFromNonTypedArray(
1594         cx, target, source, len, offset);
1595   }
1596   return ElementSpecific<T, UnsharedOps>::setFromNonTypedArray(
1597       cx, target, source, len, offset);
1598 }
1599 
1600 // ES2017 draft rev c57ef95c45a371f9c9485bb1c3881dbdc04524a2
1601 // 22.2.3.23 %TypedArray%.prototype.set ( overloaded [ , offset ] )
1602 // 22.2.3.23.1 %TypedArray%.prototype.set ( array [ , offset ] )
1603 // 22.2.3.23.2 %TypedArray%.prototype.set( typedArray [ , offset ] )
1604 /* static */
set_impl(JSContext * cx,const CallArgs & args)1605 bool TypedArrayObject::set_impl(JSContext* cx, const CallArgs& args) {
1606   MOZ_ASSERT(TypedArrayObject::is(args.thisv()));
1607 
1608   // Steps 1-5 (Validation performed as part of CallNonGenericMethod).
1609   Rooted<TypedArrayObject*> target(
1610       cx, &args.thisv().toObject().as<TypedArrayObject>());
1611 
1612   // Steps 6-7.
1613   double targetOffset = 0;
1614   if (args.length() > 1) {
1615     // Step 6.
1616     if (!ToInteger(cx, args[1], &targetOffset)) {
1617       return false;
1618     }
1619 
1620     // Step 7.
1621     if (targetOffset < 0) {
1622       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
1623       return false;
1624     }
1625   }
1626 
1627   // Steps 8-9.
1628   if (target->hasDetachedBuffer()) {
1629     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1630                               JSMSG_TYPED_ARRAY_DETACHED);
1631     return false;
1632   }
1633 
1634   // 22.2.3.23.1, step 15. (22.2.3.23.2 only applies if args[0] is a typed
1635   // array, so it doesn't make a difference there to apply ToObject here.)
1636   RootedObject src(cx, ToObject(cx, args.get(0)));
1637   if (!src) {
1638     return false;
1639   }
1640 
1641   Rooted<TypedArrayObject*> srcTypedArray(cx);
1642   {
1643     JSObject* obj = CheckedUnwrapStatic(src);
1644     if (!obj) {
1645       ReportAccessDenied(cx);
1646       return false;
1647     }
1648 
1649     if (obj->is<TypedArrayObject>()) {
1650       srcTypedArray = &obj->as<TypedArrayObject>();
1651     }
1652   }
1653 
1654   if (srcTypedArray) {
1655     // Remaining steps of 22.2.3.23.2.
1656 
1657     // WARNING: |srcTypedArray| may be an unwrapped typed array from a
1658     // different compartment. Proceed with caution!
1659 
1660     // Steps 11-12.
1661     if (srcTypedArray->hasDetachedBuffer()) {
1662       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1663                                 JSMSG_TYPED_ARRAY_DETACHED);
1664       return false;
1665     }
1666 
1667     // Step 10 (Reordered).
1668     size_t targetLength = target->length();
1669 
1670     // Step 22 (Split into two checks to provide better error messages).
1671     if (targetOffset > targetLength) {
1672       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
1673       return false;
1674     }
1675 
1676     // Step 22 (Cont'd).
1677     size_t offset = size_t(targetOffset);
1678     if (srcTypedArray->length() > targetLength - offset) {
1679       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1680                                 JSMSG_SOURCE_ARRAY_TOO_LONG);
1681       return false;
1682     }
1683 
1684     if (Scalar::isBigIntType(target->type()) !=
1685         Scalar::isBigIntType(srcTypedArray->type())) {
1686       JS_ReportErrorNumberASCII(
1687           cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_NOT_COMPATIBLE,
1688           srcTypedArray->getClass()->name, target->getClass()->name);
1689       return false;
1690     }
1691 
1692     // Steps 13-21, 23-28.
1693     switch (target->type()) {
1694 #define SET_FROM_TYPED_ARRAY(T, N)                                          \
1695   case Scalar::N:                                                           \
1696     if (!SetFromTypedArray<T>(target, srcTypedArray, offset)) return false; \
1697     break;
1698       JS_FOR_EACH_TYPED_ARRAY(SET_FROM_TYPED_ARRAY)
1699 #undef SET_FROM_TYPED_ARRAY
1700       default:
1701         MOZ_CRASH("Unsupported TypedArray type");
1702     }
1703   } else {
1704     // Remaining steps of 22.2.3.23.1.
1705 
1706     // Step 10.
1707     // We can't reorder this step because side-effects in step 16 can
1708     // detach the underlying array buffer from the typed array.
1709     size_t targetLength = target->length();
1710 
1711     // Step 16.
1712     uint64_t srcLength;
1713     if (!GetLengthProperty(cx, src, &srcLength)) {
1714       return false;
1715     }
1716 
1717     // Step 17 (Split into two checks to provide better error messages).
1718     if (targetOffset > targetLength) {
1719       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
1720       return false;
1721     }
1722 
1723     // Step 17 (Cont'd).
1724     size_t offset = size_t(targetOffset);
1725     if (srcLength > targetLength - offset) {
1726       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1727                                 JSMSG_SOURCE_ARRAY_TOO_LONG);
1728       return false;
1729     }
1730 
1731     MOZ_ASSERT(srcLength <= targetLength);
1732 
1733     // Steps 11-14, 18-21.
1734     if (srcLength > 0) {
1735       // GetLengthProperty in step 16 can lead to the execution of user
1736       // code which may detach the buffer. Handle this case here to
1737       // ensure SetFromNonTypedArray is never called with a detached
1738       // buffer. We still need to execute steps 21.a-b for their
1739       // possible side-effects.
1740       if (target->hasDetachedBuffer()) {
1741         // Steps 21.a-b.
1742         RootedValue v(cx);
1743         if (!GetElement(cx, src, src, 0, &v)) {
1744           return false;
1745         }
1746 
1747         if (!target->convertForSideEffect(cx, v)) {
1748           return false;
1749         }
1750 
1751         // Step 21.c.
1752         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1753                                   JSMSG_TYPED_ARRAY_DETACHED);
1754         return false;
1755       }
1756 
1757       switch (target->type()) {
1758 #define SET_FROM_NON_TYPED_ARRAY(T, N)                                \
1759   case Scalar::N:                                                     \
1760     if (!SetFromNonTypedArray<T>(cx, target, src, srcLength, offset)) \
1761       return false;                                                   \
1762     break;
1763         JS_FOR_EACH_TYPED_ARRAY(SET_FROM_NON_TYPED_ARRAY)
1764 #undef SET_FROM_NON_TYPED_ARRAY
1765         default:
1766           MOZ_CRASH("Unsupported TypedArray type");
1767       }
1768 
1769       // Step 21.c.
1770       // SetFromNonTypedArray doesn't throw when the array buffer gets
1771       // detached.
1772       if (target->hasDetachedBuffer()) {
1773         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1774                                   JSMSG_TYPED_ARRAY_DETACHED);
1775         return false;
1776       }
1777     }
1778   }
1779 
1780   // Step 29/22.
1781   args.rval().setUndefined();
1782   return true;
1783 }
1784 
1785 /* static */
set(JSContext * cx,unsigned argc,Value * vp)1786 bool TypedArrayObject::set(JSContext* cx, unsigned argc, Value* vp) {
1787   CallArgs args = CallArgsFromVp(argc, vp);
1788   return CallNonGenericMethod<TypedArrayObject::is, TypedArrayObject::set_impl>(
1789       cx, args);
1790 }
1791 
1792 // ES2020 draft rev dc1e21c454bd316810be1c0e7af0131a2d7f38e9
1793 // 22.2.3.5 %TypedArray%.prototype.copyWithin ( target, start [ , end ] )
1794 /* static */
copyWithin_impl(JSContext * cx,const CallArgs & args)1795 bool TypedArrayObject::copyWithin_impl(JSContext* cx, const CallArgs& args) {
1796   MOZ_ASSERT(TypedArrayObject::is(args.thisv()));
1797 
1798   // Steps 1-2.
1799   Rooted<TypedArrayObject*> tarray(
1800       cx, &args.thisv().toObject().as<TypedArrayObject>());
1801   if (tarray->hasDetachedBuffer()) {
1802     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1803                               JSMSG_TYPED_ARRAY_DETACHED);
1804     return false;
1805   }
1806 
1807   // Step 3.
1808   size_t len = tarray->length();
1809 
1810   // Step 4.
1811   double relativeTarget;
1812   if (!ToInteger(cx, args.get(0), &relativeTarget)) {
1813     return false;
1814   }
1815 
1816   // Step 5.
1817   uint64_t to;
1818   if (relativeTarget < 0) {
1819     to = std::max(len + relativeTarget, 0.0);
1820   } else {
1821     to = std::min(relativeTarget, double(len));
1822   }
1823 
1824   // Step 6.
1825   double relativeStart;
1826   if (!ToInteger(cx, args.get(1), &relativeStart)) {
1827     return false;
1828   }
1829 
1830   // Step 7.
1831   uint64_t from;
1832   if (relativeStart < 0) {
1833     from = std::max(len + relativeStart, 0.0);
1834   } else {
1835     from = std::min(relativeStart, double(len));
1836   }
1837 
1838   // Step 8.
1839   double relativeEnd;
1840   if (!args.hasDefined(2)) {
1841     relativeEnd = len;
1842   } else {
1843     if (!ToInteger(cx, args[2], &relativeEnd)) {
1844       return false;
1845     }
1846   }
1847 
1848   // Step 9.
1849   uint64_t final_;
1850   if (relativeEnd < 0) {
1851     final_ = std::max(len + relativeEnd, 0.0);
1852   } else {
1853     final_ = std::min(relativeEnd, double(len));
1854   }
1855 
1856   // Step 10.
1857   MOZ_ASSERT(to <= len);
1858   uint64_t count;
1859   if (from <= final_) {
1860     count = std::min(final_ - from, len - to);
1861   } else {
1862     count = 0;
1863   }
1864 
1865   // Step 11.
1866   //
1867   // Note that getting or setting a typed array element must throw if the
1868   // underlying buffer is detached, so the code below checks for detachment.
1869   // This happens *only* if a get/set occurs, i.e. when |count > 0|.
1870   //
1871   // Also note that this copies elements effectively by memmove, *not* in
1872   // step 11's specified order.  This is unobservable, even when the underlying
1873   // buffer is a SharedArrayBuffer instance, because the access is unordered and
1874   // therefore is allowed to have data races.
1875 
1876   if (count == 0) {
1877     args.rval().setObject(*tarray);
1878     return true;
1879   }
1880 
1881   if (tarray->hasDetachedBuffer()) {
1882     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1883                               JSMSG_TYPED_ARRAY_DETACHED);
1884     return false;
1885   }
1886 
1887   // Don't multiply by |tarray->bytesPerElement()| in case the compiler can't
1888   // strength-reduce multiplication by 1/2/4/8 into the equivalent shift.
1889   const size_t ElementShift = TypedArrayShift(tarray->type());
1890 
1891   MOZ_ASSERT((SIZE_MAX >> ElementShift) > to);
1892   size_t byteDest = to << ElementShift;
1893 
1894   MOZ_ASSERT((SIZE_MAX >> ElementShift) > from);
1895   size_t byteSrc = from << ElementShift;
1896 
1897   MOZ_ASSERT((SIZE_MAX >> ElementShift) >= count);
1898   size_t byteSize = count << ElementShift;
1899 
1900 #ifdef DEBUG
1901   {
1902     size_t viewByteLength = tarray->byteLength();
1903     MOZ_ASSERT(byteSize <= viewByteLength);
1904     MOZ_ASSERT(byteDest < viewByteLength);
1905     MOZ_ASSERT(byteSrc < viewByteLength);
1906     MOZ_ASSERT(byteDest <= viewByteLength - byteSize);
1907     MOZ_ASSERT(byteSrc <= viewByteLength - byteSize);
1908   }
1909 #endif
1910 
1911   SharedMem<uint8_t*> data = tarray->dataPointerEither().cast<uint8_t*>();
1912   if (tarray->isSharedMemory()) {
1913     jit::AtomicOperations::memmoveSafeWhenRacy(data + byteDest, data + byteSrc,
1914                                                byteSize);
1915   } else {
1916     memmove(data.unwrapUnshared() + byteDest, data.unwrapUnshared() + byteSrc,
1917             byteSize);
1918   }
1919 
1920   args.rval().setObject(*tarray);
1921   return true;
1922 }
1923 
1924 /* static */
copyWithin(JSContext * cx,unsigned argc,Value * vp)1925 bool TypedArrayObject::copyWithin(JSContext* cx, unsigned argc, Value* vp) {
1926   CallArgs args = CallArgsFromVp(argc, vp);
1927   return CallNonGenericMethod<TypedArrayObject::is,
1928                               TypedArrayObject::copyWithin_impl>(cx, args);
1929 }
1930 
1931 /* static */ const JSFunctionSpec TypedArrayObject::protoFunctions[] = {
1932     JS_SELF_HOSTED_FN("subarray", "TypedArraySubarray", 2, 0),
1933     JS_FN("set", TypedArrayObject::set, 1, 0),
1934     JS_FN("copyWithin", TypedArrayObject::copyWithin, 2, 0),
1935     JS_SELF_HOSTED_FN("every", "TypedArrayEvery", 1, 0),
1936     JS_SELF_HOSTED_FN("fill", "TypedArrayFill", 3, 0),
1937     JS_SELF_HOSTED_FN("filter", "TypedArrayFilter", 1, 0),
1938     JS_SELF_HOSTED_FN("find", "TypedArrayFind", 1, 0),
1939     JS_SELF_HOSTED_FN("findIndex", "TypedArrayFindIndex", 1, 0),
1940     JS_SELF_HOSTED_FN("forEach", "TypedArrayForEach", 1, 0),
1941     JS_SELF_HOSTED_FN("indexOf", "TypedArrayIndexOf", 2, 0),
1942     JS_SELF_HOSTED_FN("join", "TypedArrayJoin", 1, 0),
1943     JS_SELF_HOSTED_FN("lastIndexOf", "TypedArrayLastIndexOf", 1, 0),
1944     JS_SELF_HOSTED_FN("map", "TypedArrayMap", 1, 0),
1945     JS_SELF_HOSTED_FN("reduce", "TypedArrayReduce", 1, 0),
1946     JS_SELF_HOSTED_FN("reduceRight", "TypedArrayReduceRight", 1, 0),
1947     JS_SELF_HOSTED_FN("reverse", "TypedArrayReverse", 0, 0),
1948     JS_SELF_HOSTED_FN("slice", "TypedArraySlice", 2, 0),
1949     JS_SELF_HOSTED_FN("some", "TypedArraySome", 1, 0),
1950     JS_SELF_HOSTED_FN("sort", "TypedArraySort", 1, 0),
1951     JS_SELF_HOSTED_FN("entries", "TypedArrayEntries", 0, 0),
1952     JS_SELF_HOSTED_FN("keys", "TypedArrayKeys", 0, 0),
1953     JS_SELF_HOSTED_FN("values", "$TypedArrayValues", 0, 0),
1954     JS_SELF_HOSTED_SYM_FN(iterator, "$TypedArrayValues", 0, 0),
1955     JS_SELF_HOSTED_FN("includes", "TypedArrayIncludes", 2, 0),
1956     JS_SELF_HOSTED_FN("toString", "ArrayToString", 0, 0),
1957     JS_SELF_HOSTED_FN("toLocaleString", "TypedArrayToLocaleString", 2, 0),
1958     JS_SELF_HOSTED_FN("at", "TypedArrayAt", 1, 0),
1959     JS_FS_END};
1960 
1961 /* static */ const JSFunctionSpec TypedArrayObject::staticFunctions[] = {
1962     JS_SELF_HOSTED_FN("from", "TypedArrayStaticFrom", 3, 0),
1963     JS_SELF_HOSTED_FN("of", "TypedArrayStaticOf", 0, 0), JS_FS_END};
1964 
1965 /* static */ const JSPropertySpec TypedArrayObject::staticProperties[] = {
1966     JS_SELF_HOSTED_SYM_GET(species, "$TypedArraySpecies", 0), JS_PS_END};
1967 
CreateSharedTypedArrayPrototype(JSContext * cx,JSProtoKey key)1968 static JSObject* CreateSharedTypedArrayPrototype(JSContext* cx,
1969                                                  JSProtoKey key) {
1970   return GlobalObject::createBlankPrototype(
1971       cx, cx->global(), &TypedArrayObject::sharedTypedArrayPrototypeClass);
1972 }
1973 
1974 static const ClassSpec TypedArrayObjectSharedTypedArrayPrototypeClassSpec = {
1975     GenericCreateConstructor<TypedArrayConstructor, 0, gc::AllocKind::FUNCTION>,
1976     CreateSharedTypedArrayPrototype,
1977     TypedArrayObject::staticFunctions,
1978     TypedArrayObject::staticProperties,
1979     TypedArrayObject::protoFunctions,
1980     TypedArrayObject::protoAccessors,
1981     nullptr,
1982     ClassSpec::DontDefineConstructor};
1983 
1984 /* static */ const JSClass TypedArrayObject::sharedTypedArrayPrototypeClass = {
1985     "TypedArrayPrototype", JSCLASS_HAS_CACHED_PROTO(JSProto_TypedArray),
1986     JS_NULL_CLASS_OPS, &TypedArrayObjectSharedTypedArrayPrototypeClassSpec};
1987 
1988 namespace {
1989 
1990 // This default implementation is only valid for integer types less
1991 // than 32-bits in size.
1992 template <typename NativeType>
getElementPure(TypedArrayObject * tarray,size_t index,Value * vp)1993 bool TypedArrayObjectTemplate<NativeType>::getElementPure(
1994     TypedArrayObject* tarray, size_t index, Value* vp) {
1995   static_assert(sizeof(NativeType) < 4,
1996                 "this method must only handle NativeType values that are "
1997                 "always exact int32_t values");
1998 
1999   *vp = Int32Value(getIndex(tarray, index));
2000   return true;
2001 }
2002 
2003 // We need to specialize for floats and other integer types.
2004 template <>
getElementPure(TypedArrayObject * tarray,size_t index,Value * vp)2005 bool TypedArrayObjectTemplate<int32_t>::getElementPure(TypedArrayObject* tarray,
2006                                                        size_t index,
2007                                                        Value* vp) {
2008   *vp = Int32Value(getIndex(tarray, index));
2009   return true;
2010 }
2011 
2012 template <>
getElementPure(TypedArrayObject * tarray,size_t index,Value * vp)2013 bool TypedArrayObjectTemplate<uint32_t>::getElementPure(
2014     TypedArrayObject* tarray, size_t index, Value* vp) {
2015   uint32_t val = getIndex(tarray, index);
2016   *vp = NumberValue(val);
2017   return true;
2018 }
2019 
2020 template <>
getElementPure(TypedArrayObject * tarray,size_t index,Value * vp)2021 bool TypedArrayObjectTemplate<float>::getElementPure(TypedArrayObject* tarray,
2022                                                      size_t index, Value* vp) {
2023   float val = getIndex(tarray, index);
2024   double dval = val;
2025 
2026   /*
2027    * Doubles in typed arrays could be typed-punned arrays of integers. This
2028    * could allow user code to break the engine-wide invariant that only
2029    * canonical nans are stored into jsvals, which means user code could
2030    * confuse the engine into interpreting a double-typed jsval as an
2031    * object-typed jsval.
2032    *
2033    * This could be removed for platforms/compilers known to convert a 32-bit
2034    * non-canonical nan to a 64-bit canonical nan.
2035    */
2036   *vp = JS::CanonicalizedDoubleValue(dval);
2037   return true;
2038 }
2039 
2040 template <>
getElementPure(TypedArrayObject * tarray,size_t index,Value * vp)2041 bool TypedArrayObjectTemplate<double>::getElementPure(TypedArrayObject* tarray,
2042                                                       size_t index, Value* vp) {
2043   double val = getIndex(tarray, index);
2044 
2045   /*
2046    * Doubles in typed arrays could be typed-punned arrays of integers. This
2047    * could allow user code to break the engine-wide invariant that only
2048    * canonical nans are stored into jsvals, which means user code could
2049    * confuse the engine into interpreting a double-typed jsval as an
2050    * object-typed jsval.
2051    */
2052   *vp = JS::CanonicalizedDoubleValue(val);
2053   return true;
2054 }
2055 
2056 template <>
getElementPure(TypedArrayObject * tarray,size_t index,Value * vp)2057 bool TypedArrayObjectTemplate<int64_t>::getElementPure(TypedArrayObject* tarray,
2058                                                        size_t index,
2059                                                        Value* vp) {
2060   return false;
2061 }
2062 
2063 template <>
getElementPure(TypedArrayObject * tarray,size_t index,Value * vp)2064 bool TypedArrayObjectTemplate<uint64_t>::getElementPure(
2065     TypedArrayObject* tarray, size_t index, Value* vp) {
2066   return false;
2067 }
2068 } /* anonymous namespace */
2069 
2070 namespace {
2071 
2072 template <typename NativeType>
getElement(JSContext * cx,TypedArrayObject * tarray,size_t index,MutableHandleValue val)2073 bool TypedArrayObjectTemplate<NativeType>::getElement(JSContext* cx,
2074                                                       TypedArrayObject* tarray,
2075                                                       size_t index,
2076                                                       MutableHandleValue val) {
2077   MOZ_ALWAYS_TRUE(getElementPure(tarray, index, val.address()));
2078   return true;
2079 }
2080 
2081 template <>
getElement(JSContext * cx,TypedArrayObject * tarray,size_t index,MutableHandleValue val)2082 bool TypedArrayObjectTemplate<int64_t>::getElement(JSContext* cx,
2083                                                    TypedArrayObject* tarray,
2084                                                    size_t index,
2085                                                    MutableHandleValue val) {
2086   int64_t n = getIndex(tarray, index);
2087   BigInt* res = BigInt::createFromInt64(cx, n);
2088   if (!res) {
2089     return false;
2090   }
2091   val.setBigInt(res);
2092   return true;
2093 }
2094 
2095 template <>
getElement(JSContext * cx,TypedArrayObject * tarray,size_t index,MutableHandleValue val)2096 bool TypedArrayObjectTemplate<uint64_t>::getElement(JSContext* cx,
2097                                                     TypedArrayObject* tarray,
2098                                                     size_t index,
2099                                                     MutableHandleValue val) {
2100   uint64_t n = getIndex(tarray, index);
2101   BigInt* res = BigInt::createFromUint64(cx, n);
2102   if (!res) {
2103     return false;
2104   }
2105   val.setBigInt(res);
2106   return true;
2107 }
2108 } /* anonymous namespace */
2109 
2110 namespace js {
2111 
2112 template <>
getElement(JSContext * cx,size_t index,MutableHandleValue val)2113 bool TypedArrayObject::getElement<CanGC>(JSContext* cx, size_t index,
2114                                          MutableHandleValue val) {
2115   switch (type()) {
2116 #define GET_ELEMENT(T, N) \
2117   case Scalar::N:         \
2118     return N##Array::getElement(cx, this, index, val);
2119     JS_FOR_EACH_TYPED_ARRAY(GET_ELEMENT)
2120 #undef GET_ELEMENT
2121     case Scalar::MaxTypedArrayViewType:
2122     case Scalar::Int64:
2123     case Scalar::Simd128:
2124       break;
2125   }
2126 
2127   MOZ_CRASH("Unknown TypedArray type");
2128 }
2129 
2130 template <>
getElement(JSContext * cx,size_t index,typename MaybeRooted<Value,NoGC>::MutableHandleType vp)2131 bool TypedArrayObject::getElement<NoGC>(
2132     JSContext* cx, size_t index,
2133     typename MaybeRooted<Value, NoGC>::MutableHandleType vp) {
2134   return getElementPure(index, vp.address());
2135 }
2136 
2137 }  // namespace js
2138 
getElementPure(size_t index,Value * vp)2139 bool TypedArrayObject::getElementPure(size_t index, Value* vp) {
2140   switch (type()) {
2141 #define GET_ELEMENT_PURE(T, N) \
2142   case Scalar::N:              \
2143     return N##Array::getElementPure(this, index, vp);
2144     JS_FOR_EACH_TYPED_ARRAY(GET_ELEMENT_PURE)
2145 #undef GET_ELEMENT
2146     case Scalar::MaxTypedArrayViewType:
2147     case Scalar::Int64:
2148     case Scalar::Simd128:
2149       break;
2150   }
2151 
2152   MOZ_CRASH("Unknown TypedArray type");
2153 }
2154 
2155 /* static */
getElements(JSContext * cx,Handle<TypedArrayObject * > tarray,Value * vp)2156 bool TypedArrayObject::getElements(JSContext* cx,
2157                                    Handle<TypedArrayObject*> tarray,
2158                                    Value* vp) {
2159   size_t length = tarray->length();
2160   MOZ_ASSERT_IF(length > 0, !tarray->hasDetachedBuffer());
2161 
2162   switch (tarray->type()) {
2163 #define GET_ELEMENTS(T, N)                                                     \
2164   case Scalar::N:                                                              \
2165     for (size_t i = 0; i < length; ++i, ++vp) {                                \
2166       if (!N##Array::getElement(cx, tarray, i,                                 \
2167                                 MutableHandleValue::fromMarkedLocation(vp))) { \
2168         return false;                                                          \
2169       }                                                                        \
2170     }                                                                          \
2171     return true;
2172     JS_FOR_EACH_TYPED_ARRAY(GET_ELEMENTS)
2173 #undef GET_ELEMENTS
2174     case Scalar::MaxTypedArrayViewType:
2175     case Scalar::Int64:
2176     case Scalar::Simd128:
2177       break;
2178   }
2179 
2180   MOZ_CRASH("Unknown TypedArray type");
2181 }
2182 
2183 /***
2184  *** JS impl
2185  ***/
2186 
2187 /*
2188  * TypedArrayObject boilerplate
2189  */
2190 
2191 static const JSClassOps TypedArrayClassOps = {
2192     nullptr,                       // addProperty
2193     nullptr,                       // delProperty
2194     nullptr,                       // enumerate
2195     nullptr,                       // newEnumerate
2196     nullptr,                       // resolve
2197     nullptr,                       // mayResolve
2198     TypedArrayObject::finalize,    // finalize
2199     nullptr,                       // call
2200     nullptr,                       // hasInstance
2201     nullptr,                       // construct
2202     ArrayBufferViewObject::trace,  // trace
2203 };
2204 
2205 static const ClassExtension TypedArrayClassExtension = {
2206     TypedArrayObject::objectMoved,  // objectMovedOp
2207 };
2208 
2209 static const JSPropertySpec
2210     static_prototype_properties[Scalar::MaxTypedArrayViewType][2] = {
2211 #define IMPL_TYPED_ARRAY_PROPERTIES(NativeType, Name)               \
2212   {JS_INT32_PS("BYTES_PER_ELEMENT", Name##Array::BYTES_PER_ELEMENT, \
2213                JSPROP_READONLY | JSPROP_PERMANENT),                 \
2214    JS_PS_END},
2215 
2216         JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_PROPERTIES)
2217 #undef IMPL_TYPED_ARRAY_PROPERTIES
2218 };
2219 
2220 static const ClassSpec
2221     TypedArrayObjectClassSpecs[Scalar::MaxTypedArrayViewType] = {
2222 #define IMPL_TYPED_ARRAY_CLASS_SPEC(NativeType, Name) \
2223   {Name##Array::createConstructor,                    \
2224    Name##Array::createPrototype,                      \
2225    nullptr,                                           \
2226    static_prototype_properties[Scalar::Type::Name],   \
2227    nullptr,                                           \
2228    static_prototype_properties[Scalar::Type::Name],   \
2229    nullptr,                                           \
2230    JSProto_TypedArray},
2231 
2232         JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_CLASS_SPEC)
2233 #undef IMPL_TYPED_ARRAY_CLASS_SPEC
2234 };
2235 
2236 const JSClass TypedArrayObject::classes[Scalar::MaxTypedArrayViewType] = {
2237 #define IMPL_TYPED_ARRAY_CLASS(NativeType, Name)                               \
2238   {#Name "Array",                                                              \
2239    JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) |              \
2240        JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_##Name##Array) | \
2241        JSCLASS_DELAY_METADATA_BUILDER | JSCLASS_SKIP_NURSERY_FINALIZE |        \
2242        JSCLASS_BACKGROUND_FINALIZE,                                            \
2243    &TypedArrayClassOps, &TypedArrayObjectClassSpecs[Scalar::Type::Name],       \
2244    &TypedArrayClassExtension},
2245 
2246     JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_CLASS)
2247 #undef IMPL_TYPED_ARRAY_CLASS
2248 };
2249 
2250 // The various typed array prototypes are supposed to 1) be normal objects,
2251 // 2) stringify to "[object <name of constructor>]", and 3) (Gecko-specific)
2252 // be xrayable.  The first and second requirements mandate (in the absence of
2253 // @@toStringTag) a custom class.  The third requirement mandates that each
2254 // prototype's class have the relevant typed array's cached JSProtoKey in them.
2255 // Thus we need one class with cached prototype per kind of typed array, with a
2256 // delegated ClassSpec.
2257 //
2258 // Actually ({}).toString.call(Uint8Array.prototype) should throw, because
2259 // Uint8Array.prototype lacks the the typed array internal slots.  (Same as
2260 // with %TypedArray%.prototype.)  It's not clear this is desirable (see
2261 // above), but it's what we've always done, so keep doing it till we
2262 // implement @@toStringTag or ES6 changes.
2263 const JSClass TypedArrayObject::protoClasses[Scalar::MaxTypedArrayViewType] = {
2264 #define IMPL_TYPED_ARRAY_PROTO_CLASS(NativeType, Name)                       \
2265   {#Name "Array.prototype", JSCLASS_HAS_CACHED_PROTO(JSProto_##Name##Array), \
2266    JS_NULL_CLASS_OPS, &TypedArrayObjectClassSpecs[Scalar::Type::Name]},
2267 
2268     JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_PROTO_CLASS)
2269 #undef IMPL_TYPED_ARRAY_PROTO_CLASS
2270 };
2271 
2272 /* static */
isOriginalLengthGetter(Native native)2273 bool TypedArrayObject::isOriginalLengthGetter(Native native) {
2274   return native == TypedArray_lengthGetter;
2275 }
2276 
2277 /* static */
isOriginalByteOffsetGetter(Native native)2278 bool TypedArrayObject::isOriginalByteOffsetGetter(Native native) {
2279   return native == TypedArray_byteOffsetGetter;
2280 }
2281 
2282 /* static */
isOriginalByteLengthGetter(Native native)2283 bool TypedArrayObject::isOriginalByteLengthGetter(Native native) {
2284   return native == TypedArray_byteLengthGetter;
2285 }
2286 
IsTypedArrayConstructor(const JSObject * obj)2287 bool js::IsTypedArrayConstructor(const JSObject* obj) {
2288 #define CHECK_TYPED_ARRAY_CONSTRUCTOR(T, N)                 \
2289   if (IsNativeFunction(obj, N##Array::class_constructor)) { \
2290     return true;                                            \
2291   }
2292   JS_FOR_EACH_TYPED_ARRAY(CHECK_TYPED_ARRAY_CONSTRUCTOR)
2293 #undef CHECK_TYPED_ARRAY_CONSTRUCTOR
2294   return false;
2295 }
2296 
IsTypedArrayConstructor(HandleValue v,Scalar::Type type)2297 bool js::IsTypedArrayConstructor(HandleValue v, Scalar::Type type) {
2298   return IsNativeFunction(v, TypedArrayConstructorNative(type));
2299 }
2300 
TypedArrayConstructorNative(Scalar::Type type)2301 JSNative js::TypedArrayConstructorNative(Scalar::Type type) {
2302 #define TYPED_ARRAY_CONSTRUCTOR_NATIVE(T, N) \
2303   if (type == Scalar::N) {                   \
2304     return N##Array::class_constructor;      \
2305   }
2306   JS_FOR_EACH_TYPED_ARRAY(TYPED_ARRAY_CONSTRUCTOR_NATIVE)
2307 #undef TYPED_ARRAY_CONSTRUCTOR_NATIVE
2308 
2309   MOZ_CRASH("unexpected typed array type");
2310 }
2311 
IsBufferSource(JSObject * object,SharedMem<uint8_t * > * dataPointer,size_t * byteLength)2312 bool js::IsBufferSource(JSObject* object, SharedMem<uint8_t*>* dataPointer,
2313                         size_t* byteLength) {
2314   if (object->is<TypedArrayObject>()) {
2315     TypedArrayObject& view = object->as<TypedArrayObject>();
2316     *dataPointer = view.dataPointerEither().cast<uint8_t*>();
2317     *byteLength = view.byteLength();
2318     return true;
2319   }
2320 
2321   if (object->is<DataViewObject>()) {
2322     DataViewObject& view = object->as<DataViewObject>();
2323     *dataPointer = view.dataPointerEither().cast<uint8_t*>();
2324     *byteLength = view.byteLength();
2325     return true;
2326   }
2327 
2328   if (object->is<ArrayBufferObject>()) {
2329     ArrayBufferObject& buffer = object->as<ArrayBufferObject>();
2330     *dataPointer = buffer.dataPointerShared();
2331     *byteLength = buffer.byteLength();
2332     return true;
2333   }
2334 
2335   if (object->is<SharedArrayBufferObject>()) {
2336     SharedArrayBufferObject& buffer = object->as<SharedArrayBufferObject>();
2337     *dataPointer = buffer.dataPointerShared();
2338     *byteLength = buffer.byteLength();
2339     return true;
2340   }
2341 
2342   return false;
2343 }
2344 
2345 template <typename CharT>
2346 struct CompareStringInfinityOrNaN;
2347 
2348 template <>
2349 struct CompareStringInfinityOrNaN<Latin1Char> {
2350   using CharTraitT = char;
2351   static const char Infinity[];
2352   static const char NaN[];
2353 };
2354 
2355 template <>
2356 struct CompareStringInfinityOrNaN<char16_t> {
2357   using CharTraitT = char16_t;
2358   static const char16_t Infinity[];
2359   static const char16_t NaN[];
2360 };
2361 
2362 const char CompareStringInfinityOrNaN<Latin1Char>::Infinity[] = "Infinity";
2363 const char CompareStringInfinityOrNaN<Latin1Char>::NaN[] = "NaN";
2364 const char16_t CompareStringInfinityOrNaN<char16_t>::Infinity[] = u"Infinity";
2365 const char16_t CompareStringInfinityOrNaN<char16_t>::NaN[] = u"NaN";
2366 
2367 template <typename CharT>
StringIsInfinity(mozilla::Range<const CharT> s)2368 static inline bool StringIsInfinity(mozilla::Range<const CharT> s) {
2369   using CharTraitT = typename CompareStringInfinityOrNaN<CharT>::CharTraitT;
2370   constexpr auto Infinity = CompareStringInfinityOrNaN<CharT>::Infinity;
2371   // Can be changed to constexpr when compiled with C++17.
2372   size_t length = std::char_traits<CharTraitT>::length(Infinity);
2373 
2374   // While all this looks a bit convoluted to compare a string to "Infinity",
2375   // compilers optimize this to one |cmp| instruction on x64 resp. two for x86,
2376   // when the input is a Latin-1 string, because the string "Infinity" is
2377   // exactly eight characters long, so it can be represented as a single uint64
2378   // value.
2379   return s.length() == length &&
2380          !std::char_traits<CharTraitT>::compare(
2381              reinterpret_cast<const CharTraitT*>(s.begin().get()), Infinity,
2382              length);
2383 }
2384 
2385 template <typename CharT>
StringIsNaN(mozilla::Range<const CharT> s)2386 static inline bool StringIsNaN(mozilla::Range<const CharT> s) {
2387   using CharTraitT = typename CompareStringInfinityOrNaN<CharT>::CharTraitT;
2388   constexpr auto NaN = CompareStringInfinityOrNaN<CharT>::NaN;
2389   // Can be changed to constexpr when compiled with C++17.
2390   size_t length = std::char_traits<CharTraitT>::length(NaN);
2391 
2392   // "NaN" is not as nicely optimizable as "Infinity", but oh well.
2393   return s.length() == length &&
2394          !std::char_traits<CharTraitT>::compare(
2395              reinterpret_cast<const CharTraitT*>(s.begin().get()), NaN, length);
2396 }
2397 
2398 template <typename CharT>
StringToTypedArrayIndexSlow(JSContext * cx,mozilla::Range<const CharT> s,mozilla::Maybe<uint64_t> * indexp)2399 static bool StringToTypedArrayIndexSlow(JSContext* cx,
2400                                         mozilla::Range<const CharT> s,
2401                                         mozilla::Maybe<uint64_t>* indexp) {
2402   const mozilla::RangedPtr<const CharT> start = s.begin();
2403   const mozilla::RangedPtr<const CharT> end = s.end();
2404 
2405   const CharT* actualEnd;
2406   double result;
2407   if (!js_strtod(cx, start.get(), end.get(), &actualEnd, &result)) {
2408     return false;
2409   }
2410 
2411   // The complete string must have been parsed.
2412   if (actualEnd != end.get()) {
2413     MOZ_ASSERT(indexp->isNothing());
2414     return true;
2415   }
2416 
2417   // Now convert it back to a string.
2418   ToCStringBuf cbuf;
2419   const char* cstr = js::NumberToCString(cx, &cbuf, result);
2420   if (!cstr) {
2421     ReportOutOfMemory(cx);
2422     return false;
2423   }
2424 
2425   // Both strings must be equal for a canonical numeric index string.
2426   if (s.length() != strlen(cstr) ||
2427       !EqualChars(start.get(), cstr, s.length())) {
2428     MOZ_ASSERT(indexp->isNothing());
2429     return true;
2430   }
2431 
2432   // Directly perform IsInteger() check and encode negative and non-integer
2433   // indices as OOB.
2434   // See 9.4.5.2 [[HasProperty]], steps 3.b.iii and 3.b.v.
2435   // See 9.4.5.3 [[DefineOwnProperty]], steps 3.b.i and 3.b.iii.
2436   // See 9.4.5.8 IntegerIndexedElementGet, steps 5 and 8.
2437   // See 9.4.5.9 IntegerIndexedElementSet, steps 6 and 9.
2438   if (result < 0 || !IsInteger(result)) {
2439     indexp->emplace(UINT64_MAX);
2440     return true;
2441   }
2442 
2443   // Anything equals-or-larger than 2^53 is definitely OOB, encode it
2444   // accordingly so that the cast to uint64_t is well defined.
2445   if (result >= DOUBLE_INTEGRAL_PRECISION_LIMIT) {
2446     indexp->emplace(UINT64_MAX);
2447     return true;
2448   }
2449 
2450   // The string is an actual canonical numeric index.
2451   indexp->emplace(result);
2452   return true;
2453 }
2454 
2455 template <typename CharT>
StringToTypedArrayIndex(JSContext * cx,mozilla::Range<const CharT> s,mozilla::Maybe<uint64_t> * indexp)2456 bool js::StringToTypedArrayIndex(JSContext* cx, mozilla::Range<const CharT> s,
2457                                  mozilla::Maybe<uint64_t>* indexp) {
2458   mozilla::RangedPtr<const CharT> cp = s.begin();
2459   const mozilla::RangedPtr<const CharT> end = s.end();
2460 
2461   MOZ_ASSERT(cp < end, "caller must check for empty strings");
2462 
2463   bool negative = false;
2464   if (*cp == '-') {
2465     negative = true;
2466     if (++cp == end) {
2467       MOZ_ASSERT(indexp->isNothing());
2468       return true;
2469     }
2470   }
2471 
2472   if (!IsAsciiDigit(*cp)) {
2473     // Check for "NaN", "Infinity", or "-Infinity".
2474     if ((!negative && StringIsNaN<CharT>({cp, end})) ||
2475         StringIsInfinity<CharT>({cp, end})) {
2476       indexp->emplace(UINT64_MAX);
2477     } else {
2478       MOZ_ASSERT(indexp->isNothing());
2479     }
2480     return true;
2481   }
2482 
2483   uint32_t digit = AsciiDigitToNumber(*cp++);
2484 
2485   // Don't allow leading zeros.
2486   if (digit == 0 && cp != end) {
2487     // The string may be of the form "0.xyz". The exponent form isn't possible
2488     // when the string starts with "0".
2489     if (*cp == '.') {
2490       return StringToTypedArrayIndexSlow(cx, s, indexp);
2491     }
2492     MOZ_ASSERT(indexp->isNothing());
2493     return true;
2494   }
2495 
2496   uint64_t index = digit;
2497 
2498   for (; cp < end; cp++) {
2499     if (!IsAsciiDigit(*cp)) {
2500       // Take the slow path when the string has fractional parts or an exponent.
2501       if (*cp == '.' || *cp == 'e') {
2502         return StringToTypedArrayIndexSlow(cx, s, indexp);
2503       }
2504       MOZ_ASSERT(indexp->isNothing());
2505       return true;
2506     }
2507 
2508     digit = AsciiDigitToNumber(*cp);
2509 
2510     static_assert(
2511         uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT) < (UINT64_MAX - 10) / 10,
2512         "2^53 is way below UINT64_MAX, so |10 * index + digit| can't overflow");
2513 
2514     index = 10 * index + digit;
2515 
2516     // Also take the slow path when the string is larger-or-equals 2^53.
2517     if (index >= uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT)) {
2518       return StringToTypedArrayIndexSlow(cx, s, indexp);
2519     }
2520   }
2521 
2522   if (negative) {
2523     indexp->emplace(UINT64_MAX);
2524   } else {
2525     indexp->emplace(index);
2526   }
2527   return true;
2528 }
2529 
2530 template bool js::StringToTypedArrayIndex(JSContext* cx,
2531                                           mozilla::Range<const char16_t> s,
2532                                           mozilla::Maybe<uint64_t>* indexOut);
2533 
2534 template bool js::StringToTypedArrayIndex(JSContext* cx,
2535                                           mozilla::Range<const Latin1Char> s,
2536                                           mozilla::Maybe<uint64_t>* indexOut);
2537 
SetTypedArrayElement(JSContext * cx,Handle<TypedArrayObject * > obj,uint64_t index,HandleValue v,ObjectOpResult & result)2538 bool js::SetTypedArrayElement(JSContext* cx, Handle<TypedArrayObject*> obj,
2539                               uint64_t index, HandleValue v,
2540                               ObjectOpResult& result) {
2541   TypedArrayObject* tobj = &obj->as<TypedArrayObject>();
2542 
2543   switch (tobj->type()) {
2544 #define SET_TYPED_ARRAY_ELEMENT(T, N) \
2545   case Scalar::N:                     \
2546     return TypedArrayObjectTemplate<T>::setElement(cx, obj, index, v, result);
2547     JS_FOR_EACH_TYPED_ARRAY(SET_TYPED_ARRAY_ELEMENT)
2548 #undef SET_TYPED_ARRAY_ELEMENT
2549     case Scalar::MaxTypedArrayViewType:
2550     case Scalar::Int64:
2551     case Scalar::Simd128:
2552       break;
2553   }
2554 
2555   MOZ_CRASH("Unsupported TypedArray type");
2556 }
2557 
2558 // ES2021 draft rev b3f9b5089bcc3ddd8486379015cd11eb1427a5eb
2559 // 9.4.5.3 [[DefineOwnProperty]], step 3.b.
DefineTypedArrayElement(JSContext * cx,Handle<TypedArrayObject * > obj,uint64_t index,Handle<PropertyDescriptor> desc,ObjectOpResult & result)2560 bool js::DefineTypedArrayElement(JSContext* cx, Handle<TypedArrayObject*> obj,
2561                                  uint64_t index,
2562                                  Handle<PropertyDescriptor> desc,
2563                                  ObjectOpResult& result) {
2564   // These are all substeps of 3.b.
2565 
2566   // Step i.
2567   if (index >= obj->length()) {
2568     if (obj->hasDetachedBuffer()) {
2569       return result.fail(JSMSG_TYPED_ARRAY_DETACHED);
2570     }
2571     return result.fail(JSMSG_DEFINE_BAD_INDEX);
2572   }
2573 
2574   // Step ii.
2575   if (desc.isAccessorDescriptor()) {
2576     return result.fail(JSMSG_CANT_REDEFINE_PROP);
2577   }
2578 
2579   // Step iii.
2580   if (desc.hasConfigurable() && !desc.configurable()) {
2581     return result.fail(JSMSG_CANT_REDEFINE_PROP);
2582   }
2583 
2584   // Step iv.
2585   if (desc.hasEnumerable() && !desc.enumerable()) {
2586     return result.fail(JSMSG_CANT_REDEFINE_PROP);
2587   }
2588 
2589   // Step v.
2590   if (desc.hasWritable() && !desc.writable()) {
2591     return result.fail(JSMSG_CANT_REDEFINE_PROP);
2592   }
2593 
2594   // Step vi.
2595   if (desc.hasValue()) {
2596     switch (obj->type()) {
2597 #define DEFINE_TYPED_ARRAY_ELEMENT(T, N)                           \
2598   case Scalar::N:                                                  \
2599     return TypedArrayObjectTemplate<T>::setElement(cx, obj, index, \
2600                                                    desc.value(), result);
2601       JS_FOR_EACH_TYPED_ARRAY(DEFINE_TYPED_ARRAY_ELEMENT)
2602 #undef DEFINE_TYPED_ARRAY_ELEMENT
2603       case Scalar::MaxTypedArrayViewType:
2604       case Scalar::Int64:
2605       case Scalar::Simd128:
2606         break;
2607     }
2608 
2609     MOZ_CRASH("Unsupported TypedArray type");
2610   }
2611 
2612   // Step vii.
2613   return result.succeed();
2614 }
2615 
2616 /* JS Friend API */
2617 
2618 template <typename NativeType>
2619 struct ExternalTypeOf {
2620   using Type = NativeType;
2621 };
2622 
2623 template <>
2624 struct ExternalTypeOf<uint8_clamped> {
2625   using Type = uint8_t;
2626 };
2627 
2628 #define IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(NativeType, Name)                \
2629   JS_PUBLIC_API JSObject* JS_New##Name##Array(JSContext* cx,                 \
2630                                               size_t nelements) {            \
2631     return TypedArrayObjectTemplate<NativeType>::fromLength(cx, nelements);  \
2632   }                                                                          \
2633                                                                              \
2634   JS_PUBLIC_API JSObject* JS_New##Name##ArrayFromArray(JSContext* cx,        \
2635                                                        HandleObject other) { \
2636     return TypedArrayObjectTemplate<NativeType>::fromArray(cx, other);       \
2637   }                                                                          \
2638                                                                              \
2639   JS_PUBLIC_API JSObject* JS_New##Name##ArrayWithBuffer(                     \
2640       JSContext* cx, HandleObject arrayBuffer, size_t byteOffset,            \
2641       int64_t length) {                                                      \
2642     return TypedArrayObjectTemplate<NativeType>::fromBuffer(                 \
2643         cx, arrayBuffer, byteOffset, length);                                \
2644   }                                                                          \
2645                                                                              \
2646   JS_PUBLIC_API JSObject* js::Unwrap##Name##Array(JSObject* obj) {           \
2647     obj = obj->maybeUnwrapIf<TypedArrayObject>();                            \
2648     if (!obj) {                                                              \
2649       return nullptr;                                                        \
2650     }                                                                        \
2651     const JSClass* clasp = obj->getClass();                                  \
2652     if (clasp != TypedArrayObjectTemplate<NativeType>::instanceClass()) {    \
2653       return nullptr;                                                        \
2654     }                                                                        \
2655     return obj;                                                              \
2656   }                                                                          \
2657                                                                              \
2658   JS_PUBLIC_API bool JS_Is##Name##Array(JSObject* obj) {                     \
2659     return js::Unwrap##Name##Array(obj) != nullptr;                          \
2660   }                                                                          \
2661                                                                              \
2662   const JSClass* const js::detail::Name##ArrayClassPtr =                     \
2663       &js::TypedArrayObject::classes                                         \
2664           [TypedArrayObjectTemplate<NativeType>::ArrayTypeID()];             \
2665                                                                              \
2666   JS_PUBLIC_API JSObject* JS_GetObjectAs##Name##Array(                       \
2667       JSObject* obj, size_t* length, bool* isShared,                         \
2668       ExternalTypeOf<NativeType>::Type** data) {                             \
2669     obj = js::Unwrap##Name##Array(obj);                                      \
2670     if (!obj) {                                                              \
2671       return nullptr;                                                        \
2672     }                                                                        \
2673     TypedArrayObject* tarr = &obj->as<TypedArrayObject>();                   \
2674     *length = tarr->length();                                                \
2675     *isShared = tarr->isSharedMemory();                                      \
2676     *data = static_cast<ExternalTypeOf<NativeType>::Type*>(                  \
2677         tarr->dataPointerEither().unwrap(                                    \
2678             /*safe - caller sees isShared flag*/));                          \
2679     return obj;                                                              \
2680   }                                                                          \
2681                                                                              \
2682   JS_PUBLIC_API ExternalTypeOf<NativeType>::Type* JS_Get##Name##ArrayData(   \
2683       JSObject* obj, bool* isSharedMemory, const JS::AutoRequireNoGC&) {     \
2684     TypedArrayObject* tarr = obj->maybeUnwrapAs<TypedArrayObject>();         \
2685     if (!tarr) {                                                             \
2686       return nullptr;                                                        \
2687     }                                                                        \
2688     MOZ_ASSERT(tarr->type() == TypeIDOfType<NativeType>::id);                \
2689     *isSharedMemory = tarr->isSharedMemory();                                \
2690     return static_cast<ExternalTypeOf<NativeType>::Type*>(                   \
2691         tarr->dataPointerEither().unwrap(/*safe - caller sees isShared*/));  \
2692   }
2693 
JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS)2694 JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS)
2695 #undef IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS
2696 
2697 JS_PUBLIC_API bool JS_IsTypedArrayObject(JSObject* obj) {
2698   return obj->canUnwrapAs<TypedArrayObject>();
2699 }
2700 
JS_GetTypedArrayLength(JSObject * obj)2701 JS_PUBLIC_API size_t JS_GetTypedArrayLength(JSObject* obj) {
2702   TypedArrayObject* tarr = obj->maybeUnwrapAs<TypedArrayObject>();
2703   if (!tarr) {
2704     return 0;
2705   }
2706   return tarr->length();
2707 }
2708 
JS_GetTypedArrayByteOffset(JSObject * obj)2709 JS_PUBLIC_API size_t JS_GetTypedArrayByteOffset(JSObject* obj) {
2710   TypedArrayObject* tarr = obj->maybeUnwrapAs<TypedArrayObject>();
2711   if (!tarr) {
2712     return 0;
2713   }
2714   return tarr->byteOffset();
2715 }
2716 
JS_GetTypedArrayByteLength(JSObject * obj)2717 JS_PUBLIC_API size_t JS_GetTypedArrayByteLength(JSObject* obj) {
2718   TypedArrayObject* tarr = obj->maybeUnwrapAs<TypedArrayObject>();
2719   if (!tarr) {
2720     return 0;
2721   }
2722   return tarr->byteLength();
2723 }
2724 
JS_GetTypedArraySharedness(JSObject * obj)2725 JS_PUBLIC_API bool JS_GetTypedArraySharedness(JSObject* obj) {
2726   TypedArrayObject* tarr = obj->maybeUnwrapAs<TypedArrayObject>();
2727   if (!tarr) {
2728     return false;
2729   }
2730   return tarr->isSharedMemory();
2731 }
2732 
JS_GetArrayBufferViewType(JSObject * obj)2733 JS_PUBLIC_API js::Scalar::Type JS_GetArrayBufferViewType(JSObject* obj) {
2734   ArrayBufferViewObject* view = obj->maybeUnwrapAs<ArrayBufferViewObject>();
2735   if (!view) {
2736     return Scalar::MaxTypedArrayViewType;
2737   }
2738 
2739   if (view->is<TypedArrayObject>()) {
2740     return view->as<TypedArrayObject>().type();
2741   }
2742   if (view->is<DataViewObject>()) {
2743     return Scalar::MaxTypedArrayViewType;
2744   }
2745   MOZ_CRASH("invalid ArrayBufferView type");
2746 }
2747 
JS_MaxMovableTypedArraySize()2748 JS_PUBLIC_API size_t JS_MaxMovableTypedArraySize() {
2749   return TypedArrayObject::INLINE_BUFFER_LIMIT;
2750 }
2751