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