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/. */
7 #include "vm/TypedArrayObject-inl.h"
8 #include "vm/TypedArrayObject.h"
10 #include "mozilla/FloatingPoint.h"
11 #include "mozilla/IntegerTypeTraits.h"
12 #include "mozilla/Likely.h"
13 #include "mozilla/PodOperations.h"
14 #include "mozilla/TextUtils.h"
21 #include <string_view>
22 #if !defined(XP_WIN) && !defined(__wasi__)
23 # include <sys/mman.h>
25 #include <type_traits>
30 #include "builtin/Array.h"
31 #include "builtin/DataViewObject.h"
32 #include "gc/Barrier.h"
33 #include "gc/MaybeRooted.h"
34 #include "jit/InlinableNatives.h"
35 #include "js/Conversions.h"
36 #include "js/experimental/TypedData.h" // JS_GetArrayBufferViewType, JS_GetTypedArray{Length,ByteOffset,ByteLength}, JS_IsTypedArrayObject
37 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
38 #include "js/PropertySpec.h"
39 #include "js/ScalarType.h" // JS::Scalar::Type
40 #include "js/UniquePtr.h"
41 #include "js/Wrapper.h"
42 #include "util/DifferentialTesting.h"
43 #include "util/StringBuffer.h"
44 #include "util/Text.h"
45 #include "util/WindowsWrapper.h"
46 #include "vm/ArrayBufferObject.h"
47 #include "vm/FunctionFlags.h" // js::FunctionFlags
48 #include "vm/GlobalObject.h"
49 #include "vm/JSContext.h"
50 #include "vm/JSObject.h"
52 #include "vm/SelfHosting.h"
53 #include "vm/SharedMem.h"
54 #include "vm/Uint8Clamped.h"
55 #include "vm/WrapperObject.h"
57 #include "gc/Nursery-inl.h"
58 #include "vm/ArrayBufferObject-inl.h"
59 #include "vm/Compartment-inl.h"
60 #include "vm/GeckoProfiler-inl.h"
61 #include "vm/NativeObject-inl.h"
65 using JS::CanonicalizeNaN
;
68 using mozilla::IsAsciiDigit
;
73 * The non-templated base class for the specific typed implementations.
74 * This class holds all the member variables that are used by
78 bool TypedArrayObject::convertValue(JSContext
* cx
, HandleValue v
,
79 MutableHandleValue result
) const {
81 case Scalar::BigInt64
:
82 case Scalar::BigUint64
: {
83 BigInt
* bi
= ToBigInt(cx
, v
);
98 case Scalar::Uint8Clamped
: {
100 if (!ToNumber(cx
, v
, &num
)) {
103 result
.setNumber(num
);
106 case Scalar::MaxTypedArrayViewType
:
108 case Scalar::Simd128
:
109 MOZ_CRASH("Unsupported TypedArray type");
111 MOZ_ASSERT_UNREACHABLE("Invalid scalar type");
115 static bool IsTypedArrayObject(HandleValue v
) {
116 return v
.isObject() && v
.toObject().is
<TypedArrayObject
>();
119 static bool IsUint8ArrayObject(HandleValue v
) {
120 return IsTypedArrayObject(v
) &&
121 v
.toObject().as
<TypedArrayObject
>().type() == Scalar::Uint8
;
125 bool TypedArrayObject::ensureHasBuffer(JSContext
* cx
,
126 Handle
<TypedArrayObject
*> typedArray
) {
127 if (typedArray
->hasBuffer()) {
131 MOZ_ASSERT(typedArray
->is
<FixedLengthTypedArrayObject
>(),
132 "Resizable TypedArrays always use an ArrayBuffer");
134 Rooted
<FixedLengthTypedArrayObject
*> tarray(
135 cx
, &typedArray
->as
<FixedLengthTypedArrayObject
>());
137 size_t byteLength
= tarray
->byteLength();
139 AutoRealm
ar(cx
, tarray
);
140 Rooted
<ArrayBufferObject
*> buffer(
141 cx
, ArrayBufferObject::createZeroed(cx
, tarray
->byteLength()));
146 buffer
->pinLength(tarray
->isLengthPinned());
148 // Attaching the first view to an array buffer is infallible.
149 MOZ_ALWAYS_TRUE(buffer
->addView(cx
, tarray
));
151 // tarray is not shared, because if it were it would have a buffer.
152 memcpy(buffer
->dataPointer(), tarray
->dataPointerUnshared(), byteLength
);
154 // If the object is in the nursery, the buffer will be freed by the next
155 // nursery GC. Free the data slot pointer if the object has no inline data.
156 size_t nbytes
= RoundUp(byteLength
, sizeof(Value
));
157 Nursery
& nursery
= cx
->nursery();
158 if (tarray
->isTenured() && !tarray
->hasInlineElements() &&
159 !nursery
.isInside(tarray
->elements())) {
160 js_free(tarray
->elements());
161 RemoveCellMemory(tarray
, nbytes
, MemoryUse::TypedArrayElements
);
164 tarray
->setFixedSlot(TypedArrayObject::DATA_SLOT
,
165 PrivateValue(buffer
->dataPointer()));
166 tarray
->setFixedSlot(TypedArrayObject::BUFFER_SLOT
, ObjectValue(*buffer
));
172 void FixedLengthTypedArrayObject::assertZeroLengthArrayData() const {
173 if (length() == 0 && !hasBuffer()) {
174 uint8_t* end
= fixedData(FixedLengthTypedArrayObject::FIXED_DATA_START
);
175 MOZ_ASSERT(end
[0] == ZeroLengthArrayData
);
180 void FixedLengthTypedArrayObject::finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
181 MOZ_ASSERT(!IsInsideNursery(obj
));
182 auto* curObj
= &obj
->as
<FixedLengthTypedArrayObject
>();
184 // Template objects or discarded objects (which didn't have enough room
185 // for inner elements) don't have anything to free.
186 if (!curObj
->elementsRaw()) {
190 curObj
->assertZeroLengthArrayData();
192 // Typed arrays with a buffer object do not need to be free'd
193 if (curObj
->hasBuffer()) {
197 // Free the data slot pointer if it does not point into the old JSObject.
198 if (!curObj
->hasInlineElements()) {
199 size_t nbytes
= RoundUp(curObj
->byteLength(), sizeof(Value
));
200 gcx
->free_(obj
, curObj
->elements(), nbytes
, MemoryUse::TypedArrayElements
);
205 size_t FixedLengthTypedArrayObject::objectMoved(JSObject
* obj
, JSObject
* old
) {
206 auto* newObj
= &obj
->as
<FixedLengthTypedArrayObject
>();
207 const auto* oldObj
= &old
->as
<FixedLengthTypedArrayObject
>();
208 MOZ_ASSERT(newObj
->elementsRaw() == oldObj
->elementsRaw());
210 // Typed arrays with a buffer object do not need an update.
211 if (oldObj
->hasBuffer()) {
215 if (!IsInsideNursery(old
)) {
216 // Update the data slot pointer if it points to the old JSObject.
217 if (oldObj
->hasInlineElements()) {
218 newObj
->setInlineElements();
224 void* buf
= oldObj
->elements();
226 // Discarded objects (which didn't have enough room for inner elements) don't
227 // have any data to move.
232 Nursery
& nursery
= obj
->runtimeFromMainThread()->gc
.nursery();
234 // Determine if we can use inline data for the target array. If this is
235 // possible, the nursery will have picked an allocation size that is large
237 size_t nbytes
= oldObj
->byteLength();
238 bool canUseDirectForward
= nbytes
>= sizeof(uintptr_t);
240 constexpr size_t headerSize
= dataOffset() + sizeof(HeapSlot
);
242 gc::AllocKind allocKind
= oldObj
->allocKindForTenure();
243 MOZ_ASSERT_IF(obj
->isTenured(), obj
->asTenured().getAllocKind() == allocKind
);
244 MOZ_ASSERT_IF(nbytes
== 0,
245 headerSize
+ sizeof(uint8_t) <= GetGCKindBytes(allocKind
));
247 if (nursery
.isInside(buf
) &&
248 headerSize
+ nbytes
<= GetGCKindBytes(allocKind
)) {
249 MOZ_ASSERT(oldObj
->hasInlineElements());
253 newObj
->fixedData(FixedLengthTypedArrayObject::FIXED_DATA_START
);
254 output
[0] = ZeroLengthArrayData
;
257 newObj
->setInlineElements();
258 mozilla::PodCopy(newObj
->elements(), oldObj
->elements(), nbytes
);
260 // Set a forwarding pointer for the element buffers in case they were
261 // preserved on the stack by Ion.
262 nursery
.setForwardingPointerWhileTenuring(
263 oldObj
->elements(), newObj
->elements(), canUseDirectForward
);
268 // Non-inline allocations are rounded up.
269 nbytes
= RoundUp(nbytes
, sizeof(Value
));
271 Nursery::WasBufferMoved result
= nursery
.maybeMoveBufferOnPromotion(
272 &buf
, newObj
, nbytes
, MemoryUse::TypedArrayElements
,
273 ArrayBufferContentsArena
);
274 if (result
== Nursery::BufferMoved
) {
275 newObj
->setReservedSlot(DATA_SLOT
, PrivateValue(buf
));
277 // Set a forwarding pointer for the element buffers in case they were
278 // preserved on the stack by Ion.
279 nursery
.setForwardingPointerWhileTenuring(
280 oldObj
->elements(), newObj
->elements(), canUseDirectForward
);
288 bool FixedLengthTypedArrayObject::hasInlineElements() const {
290 this->fixedData(FixedLengthTypedArrayObject::FIXED_DATA_START
) &&
291 byteLength() <= FixedLengthTypedArrayObject::INLINE_BUFFER_LIMIT
;
294 void FixedLengthTypedArrayObject::setInlineElements() {
295 char* dataSlot
= reinterpret_cast<char*>(this) + dataOffset();
296 *reinterpret_cast<void**>(dataSlot
) =
297 this->fixedData(FixedLengthTypedArrayObject::FIXED_DATA_START
);
300 /* Helper clamped uint8_t type */
302 uint32_t js::ClampDoubleToUint8(const double x
) {
303 // Not < so that NaN coerces to 0
312 double toTruncate
= x
+ 0.5;
313 uint8_t y
= uint8_t(toTruncate
);
316 * now val is rounded to nearest, ties rounded up. We want
317 * rounded to nearest ties to even, so check whether we had a
320 if (y
== toTruncate
) {
322 * It was a tie (since adding 0.5 gave us the exact integer
323 * we want). Since we rounded up, we either already have an
324 * even number or we have an odd number but the number we
325 * want is one less. So just unconditionally masking out the
326 * ones bit should do the trick to get us the value we
335 static void ReportOutOfBounds(JSContext
* cx
, TypedArrayObject
* typedArray
) {
336 if (typedArray
->hasDetachedBuffer()) {
337 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
338 JSMSG_TYPED_ARRAY_DETACHED
);
340 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
341 JSMSG_TYPED_ARRAY_RESIZED_BOUNDS
);
347 template <class TypedArrayType
>
348 static TypedArrayType
* NewTypedArrayObject(JSContext
* cx
, const JSClass
* clasp
,
350 gc::AllocKind allocKind
,
354 MOZ_ASSERT(CanChangeToBackgroundAllocKind(allocKind
, clasp
));
355 allocKind
= ForegroundToBackgroundAllocKind(allocKind
);
357 static_assert(std::is_same_v
<TypedArrayType
, FixedLengthTypedArrayObject
> ||
358 std::is_same_v
<TypedArrayType
, ResizableTypedArrayObject
>);
360 // Fixed length typed arrays can store data inline so we only use fixed slots
361 // to cover the reserved slots, ignoring the AllocKind.
362 MOZ_ASSERT(ClassCanHaveFixedData(clasp
));
363 constexpr size_t nfixed
= TypedArrayType::RESERVED_SLOTS
;
364 static_assert(nfixed
<= NativeObject::MAX_FIXED_SLOTS
);
365 static_assert(!std::is_same_v
<TypedArrayType
, FixedLengthTypedArrayObject
> ||
366 nfixed
== FixedLengthTypedArrayObject::FIXED_DATA_START
);
368 Rooted
<SharedShape
*> shape(
370 SharedShape::getInitialShape(cx
, clasp
, cx
->realm(), AsTaggedProto(proto
),
371 nfixed
, ObjectFlags()));
376 return NativeObject::create
<TypedArrayType
>(cx
, allocKind
, heap
, shape
);
379 template <typename NativeType
>
380 class FixedLengthTypedArrayObjectTemplate
;
382 template <typename NativeType
>
383 class ResizableTypedArrayObjectTemplate
;
385 template <typename NativeType
>
386 class TypedArrayObjectTemplate
{
387 friend class js::TypedArrayObject
;
389 using FixedLengthTypedArray
= FixedLengthTypedArrayObjectTemplate
<NativeType
>;
390 using ResizableTypedArray
= ResizableTypedArrayObjectTemplate
<NativeType
>;
391 using AutoLength
= ArrayBufferViewObject::AutoLength
;
393 static constexpr auto ByteLengthLimit
= TypedArrayObject::ByteLengthLimit
;
394 static constexpr auto INLINE_BUFFER_LIMIT
=
395 FixedLengthTypedArrayObject::INLINE_BUFFER_LIMIT
;
398 static constexpr Scalar::Type
ArrayTypeID() {
399 return TypeIDOfType
<NativeType
>::id
;
401 static constexpr JSProtoKey
protoKey() {
402 return TypeIDOfType
<NativeType
>::protoKey
;
405 static constexpr bool ArrayTypeIsUnsigned() {
406 return TypeIsUnsigned
<NativeType
>();
408 static constexpr bool ArrayTypeIsFloatingPoint() {
409 return TypeIsFloatingPoint
<NativeType
>();
412 static constexpr size_t BYTES_PER_ELEMENT
= sizeof(NativeType
);
414 static JSObject
* createPrototype(JSContext
* cx
, JSProtoKey key
) {
415 Handle
<GlobalObject
*> global
= cx
->global();
416 RootedObject
typedArrayProto(
417 cx
, GlobalObject::getOrCreateTypedArrayPrototype(cx
, global
));
418 if (!typedArrayProto
) {
422 const JSClass
* clasp
= TypedArrayObject::protoClassForType(ArrayTypeID());
423 return GlobalObject::createBlankPrototypeInheriting(cx
, clasp
,
427 static JSObject
* createConstructor(JSContext
* cx
, JSProtoKey key
) {
428 Handle
<GlobalObject
*> global
= cx
->global();
429 RootedFunction
ctorProto(
430 cx
, GlobalObject::getOrCreateTypedArrayConstructor(cx
, global
));
435 JSFunction
* fun
= NewFunctionWithProto(
436 cx
, class_constructor
, 3, FunctionFlags::NATIVE_CTOR
, nullptr,
437 ClassName(key
, cx
), ctorProto
, gc::AllocKind::FUNCTION
, TenuredObject
);
440 fun
->setJitInfo(&jit::JitInfo_TypedArrayConstructor
);
446 static bool convertValue(JSContext
* cx
, HandleValue v
, NativeType
* result
);
448 static TypedArrayObject
* makeTypedArrayWithTemplate(
449 JSContext
* cx
, TypedArrayObject
* templateObj
, HandleObject array
) {
450 MOZ_ASSERT(!IsWrapper(array
));
451 MOZ_ASSERT(!array
->is
<ArrayBufferObjectMaybeShared
>());
453 return fromArray(cx
, array
);
456 static TypedArrayObject
* makeTypedArrayWithTemplate(
457 JSContext
* cx
, TypedArrayObject
* templateObj
, HandleObject arrayBuffer
,
458 HandleValue byteOffsetValue
, HandleValue lengthValue
) {
459 MOZ_ASSERT(!IsWrapper(arrayBuffer
));
460 MOZ_ASSERT(arrayBuffer
->is
<ArrayBufferObjectMaybeShared
>());
462 uint64_t byteOffset
, length
;
463 if (!byteOffsetAndLength(cx
, byteOffsetValue
, lengthValue
, &byteOffset
,
468 return fromBufferSameCompartment(
469 cx
, arrayBuffer
.as
<ArrayBufferObjectMaybeShared
>(), byteOffset
, length
,
473 // ES2023 draft rev cf86f1cdc28e809170733d74ea64fd0f3dd79f78
474 // 23.2.5.1 TypedArray ( ...args )
475 static bool class_constructor(JSContext
* cx
, unsigned argc
, Value
* vp
) {
476 AutoJSConstructorProfilerEntry
pseudoFrame(cx
, "[TypedArray]");
477 CallArgs args
= CallArgsFromVp(argc
, vp
);
480 if (!ThrowIfNotConstructing(cx
, args
, "typed array")) {
485 JSObject
* obj
= create(cx
, args
);
489 args
.rval().setObject(*obj
);
494 static JSObject
* create(JSContext
* cx
, const CallArgs
& args
) {
495 MOZ_ASSERT(args
.isConstructing());
498 if (args
.length() == 0 || !args
[0].isObject()) {
501 if (!ToIndex(cx
, args
.get(0), JSMSG_BAD_ARRAY_LENGTH
, &len
)) {
505 // Steps 5.a and 6.c.iii.
506 RootedObject
proto(cx
);
507 if (!GetPrototypeFromBuiltinConstructor(cx
, args
, protoKey(), &proto
)) {
511 return fromLength(cx
, len
, proto
);
514 RootedObject
dataObj(cx
, &args
[0].toObject());
517 // 23.2.5.1.1 AllocateTypedArray, step 1.
518 RootedObject
proto(cx
);
519 if (!GetPrototypeFromBuiltinConstructor(cx
, args
, protoKey(), &proto
)) {
523 // Steps 6.b.ii and 6.b.iv.
524 if (!UncheckedUnwrap(dataObj
)->is
<ArrayBufferObjectMaybeShared
>()) {
525 return fromArray(cx
, dataObj
, proto
);
528 // Steps 6.b.iii.1-2.
529 // 23.2.5.1.3 InitializeTypedArrayFromArrayBuffer, steps 2 and 4.
530 uint64_t byteOffset
, length
;
531 if (!byteOffsetAndLength(cx
, args
.get(1), args
.get(2), &byteOffset
,
537 if (dataObj
->is
<ArrayBufferObjectMaybeShared
>()) {
538 auto buffer
= dataObj
.as
<ArrayBufferObjectMaybeShared
>();
539 return fromBufferSameCompartment(cx
, buffer
, byteOffset
, length
, proto
);
541 return fromBufferWrapped(cx
, dataObj
, byteOffset
, length
, proto
);
544 // ES2023 draft rev cf86f1cdc28e809170733d74ea64fd0f3dd79f78
545 // 23.2.5.1.3 InitializeTypedArrayFromArrayBuffer ( O, buffer, byteOffset,
546 // length ) Steps 2 and 4.
547 static bool byteOffsetAndLength(JSContext
* cx
, HandleValue byteOffsetValue
,
548 HandleValue lengthValue
, uint64_t* byteOffset
,
552 if (!byteOffsetValue
.isUndefined()) {
553 if (!ToIndex(cx
, byteOffsetValue
, byteOffset
)) {
558 if (*byteOffset
% BYTES_PER_ELEMENT
!= 0) {
559 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
560 JSMSG_TYPED_ARRAY_CONSTRUCT_OFFSET_BOUNDS
,
561 Scalar::name(ArrayTypeID()),
562 Scalar::byteSizeString(ArrayTypeID()));
568 *length
= UINT64_MAX
;
569 if (!lengthValue
.isUndefined()) {
570 if (!ToIndex(cx
, lengthValue
, length
)) {
578 // ES2023 draft rev cf86f1cdc28e809170733d74ea64fd0f3dd79f78
579 // 23.2.5.1.3 InitializeTypedArrayFromArrayBuffer ( O, buffer, byteOffset,
580 // length ) Steps 5-8.
581 static bool computeAndCheckLength(
582 JSContext
* cx
, Handle
<ArrayBufferObjectMaybeShared
*> bufferMaybeUnwrapped
,
583 uint64_t byteOffset
, uint64_t lengthIndex
, size_t* length
,
584 AutoLength
* autoLength
) {
585 MOZ_ASSERT(byteOffset
% BYTES_PER_ELEMENT
== 0);
586 MOZ_ASSERT(byteOffset
< uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT
));
587 MOZ_ASSERT_IF(lengthIndex
!= UINT64_MAX
,
588 lengthIndex
< uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT
));
591 if (bufferMaybeUnwrapped
->isDetached()) {
592 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
593 JSMSG_TYPED_ARRAY_DETACHED
);
598 size_t bufferByteLength
= bufferMaybeUnwrapped
->byteLength();
599 MOZ_ASSERT(bufferByteLength
<= ByteLengthLimit
);
602 if (lengthIndex
== UINT64_MAX
) {
603 // Check if |byteOffset| valid.
604 if (byteOffset
> bufferByteLength
) {
605 JS_ReportErrorNumberASCII(
606 cx
, GetErrorMessage
, nullptr,
607 JSMSG_TYPED_ARRAY_CONSTRUCT_OFFSET_LENGTH_BOUNDS
,
608 Scalar::name(ArrayTypeID()));
612 // Resizable buffers without an explicit length are auto-length.
613 if (bufferMaybeUnwrapped
->isResizable()) {
615 *autoLength
= AutoLength::Yes
;
619 // Steps 7.a and 7.c.
620 if (bufferByteLength
% BYTES_PER_ELEMENT
!= 0) {
621 // The given byte array doesn't map exactly to
622 // |BYTES_PER_ELEMENT * N|
623 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
624 JSMSG_TYPED_ARRAY_CONSTRUCT_OFFSET_MISALIGNED
,
625 Scalar::name(ArrayTypeID()),
626 Scalar::byteSizeString(ArrayTypeID()));
631 size_t newByteLength
= bufferByteLength
- size_t(byteOffset
);
632 len
= newByteLength
/ BYTES_PER_ELEMENT
;
635 uint64_t newByteLength
= lengthIndex
* BYTES_PER_ELEMENT
;
638 if (byteOffset
+ newByteLength
> bufferByteLength
) {
639 // |byteOffset + newByteLength| is too big for the arraybuffer
640 JS_ReportErrorNumberASCII(
641 cx
, GetErrorMessage
, nullptr,
642 JSMSG_TYPED_ARRAY_CONSTRUCT_ARRAY_LENGTH_BOUNDS
,
643 Scalar::name(ArrayTypeID()));
647 len
= size_t(lengthIndex
);
650 MOZ_ASSERT(len
<= ByteLengthLimit
/ BYTES_PER_ELEMENT
);
652 *autoLength
= AutoLength::No
;
656 // ES2023 draft rev cf86f1cdc28e809170733d74ea64fd0f3dd79f78
657 // 23.2.5.1.3 InitializeTypedArrayFromArrayBuffer ( O, buffer, byteOffset,
658 // length ) Steps 5-13.
659 static TypedArrayObject
* fromBufferSameCompartment(
660 JSContext
* cx
, Handle
<ArrayBufferObjectMaybeShared
*> buffer
,
661 uint64_t byteOffset
, uint64_t lengthIndex
, HandleObject proto
) {
664 auto autoLength
= AutoLength::No
;
665 if (!computeAndCheckLength(cx
, buffer
, byteOffset
, lengthIndex
, &length
,
670 if (!buffer
->isResizable()) {
672 return FixedLengthTypedArray::makeInstance(cx
, buffer
, byteOffset
, length
,
676 return ResizableTypedArray::makeInstance(cx
, buffer
, byteOffset
, length
,
680 // Create a TypedArray object in another compartment.
682 // ES6 supports creating a TypedArray in global A (using global A's
683 // TypedArray constructor) backed by an ArrayBuffer created in global B.
685 // Our TypedArrayObject implementation doesn't support a TypedArray in
686 // compartment A backed by an ArrayBuffer in compartment B. So in this
687 // case, we create the TypedArray in B (!) and return a cross-compartment
690 // Extra twist: the spec says the new TypedArray's [[Prototype]] must be
691 // A's TypedArray.prototype. So even though we're creating the TypedArray
692 // in B, its [[Prototype]] must be (a cross-compartment wrapper for) the
693 // TypedArray.prototype in A.
694 static JSObject
* fromBufferWrapped(JSContext
* cx
, HandleObject bufobj
,
695 uint64_t byteOffset
, uint64_t lengthIndex
,
696 HandleObject proto
) {
697 JSObject
* unwrapped
= CheckedUnwrapStatic(bufobj
);
699 ReportAccessDenied(cx
);
703 if (!unwrapped
->is
<ArrayBufferObjectMaybeShared
>()) {
704 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
705 JSMSG_TYPED_ARRAY_BAD_ARGS
);
709 Rooted
<ArrayBufferObjectMaybeShared
*> unwrappedBuffer(cx
);
710 unwrappedBuffer
= &unwrapped
->as
<ArrayBufferObjectMaybeShared
>();
713 auto autoLength
= AutoLength::No
;
714 if (!computeAndCheckLength(cx
, unwrappedBuffer
, byteOffset
, lengthIndex
,
715 &length
, &autoLength
)) {
719 // Make sure to get the [[Prototype]] for the created typed array from
721 RootedObject
protoRoot(cx
, proto
);
723 protoRoot
= GlobalObject::getOrCreatePrototype(cx
, protoKey());
729 RootedObject
typedArray(cx
);
731 JSAutoRealm
ar(cx
, unwrappedBuffer
);
733 RootedObject
wrappedProto(cx
, protoRoot
);
734 if (!cx
->compartment()->wrap(cx
, &wrappedProto
)) {
738 if (!unwrappedBuffer
->isResizable()) {
739 typedArray
= FixedLengthTypedArray::makeInstance(
740 cx
, unwrappedBuffer
, byteOffset
, length
, wrappedProto
);
742 typedArray
= ResizableTypedArray::makeInstance(
743 cx
, unwrappedBuffer
, byteOffset
, length
, autoLength
, wrappedProto
);
750 if (!cx
->compartment()->wrap(cx
, &typedArray
)) {
758 static JSObject
* fromBuffer(JSContext
* cx
, HandleObject bufobj
,
759 size_t byteOffset
, int64_t lengthInt
) {
760 if (byteOffset
% BYTES_PER_ELEMENT
!= 0) {
761 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
762 JSMSG_TYPED_ARRAY_CONSTRUCT_OFFSET_BOUNDS
,
763 Scalar::name(ArrayTypeID()),
764 Scalar::byteSizeString(ArrayTypeID()));
765 return nullptr; // invalid byteOffset
768 uint64_t lengthIndex
= lengthInt
>= 0 ? uint64_t(lengthInt
) : UINT64_MAX
;
769 if (bufobj
->is
<ArrayBufferObjectMaybeShared
>()) {
770 auto buffer
= bufobj
.as
<ArrayBufferObjectMaybeShared
>();
771 return fromBufferSameCompartment(cx
, buffer
, byteOffset
, lengthIndex
,
774 return fromBufferWrapped(cx
, bufobj
, byteOffset
, lengthIndex
, nullptr);
777 static bool maybeCreateArrayBuffer(JSContext
* cx
, uint64_t count
,
778 MutableHandle
<ArrayBufferObject
*> buffer
) {
779 if (count
> ByteLengthLimit
/ BYTES_PER_ELEMENT
) {
780 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
781 JSMSG_BAD_ARRAY_LENGTH
);
784 size_t byteLength
= count
* BYTES_PER_ELEMENT
;
786 MOZ_ASSERT(byteLength
<= ByteLengthLimit
);
787 static_assert(INLINE_BUFFER_LIMIT
% BYTES_PER_ELEMENT
== 0,
788 "ArrayBuffer inline storage shouldn't waste any space");
790 if (byteLength
<= INLINE_BUFFER_LIMIT
) {
791 // The array's data can be inline, and the buffer created lazily.
795 ArrayBufferObject
* buf
= ArrayBufferObject::createZeroed(cx
, byteLength
);
804 // ES2023 draft rev cf86f1cdc28e809170733d74ea64fd0f3dd79f78
805 // 23.2.5.1.1 AllocateTypedArray ( constructorName, newTarget, defaultProto [
807 static TypedArrayObject
* fromLength(JSContext
* cx
, uint64_t nelements
,
808 HandleObject proto
= nullptr,
809 gc::Heap heap
= gc::Heap::Default
) {
810 Rooted
<ArrayBufferObject
*> buffer(cx
);
811 if (!maybeCreateArrayBuffer(cx
, nelements
, &buffer
)) {
815 return FixedLengthTypedArray::makeInstance(cx
, buffer
, 0, nelements
, proto
,
819 static TypedArrayObject
* fromArray(JSContext
* cx
, HandleObject other
,
820 HandleObject proto
= nullptr);
822 static TypedArrayObject
* fromTypedArray(JSContext
* cx
, HandleObject other
,
823 bool isWrapped
, HandleObject proto
);
825 static TypedArrayObject
* fromObject(JSContext
* cx
, HandleObject other
,
828 static const NativeType
getIndex(TypedArrayObject
* tarray
, size_t index
) {
829 MOZ_ASSERT(index
< tarray
->length().valueOr(0));
830 return jit::AtomicOperations::loadSafeWhenRacy(
831 tarray
->dataPointerEither().cast
<NativeType
*>() + index
);
834 static void setIndex(TypedArrayObject
& tarray
, size_t index
, NativeType val
) {
835 MOZ_ASSERT(index
< tarray
.length().valueOr(0));
836 jit::AtomicOperations::storeSafeWhenRacy(
837 tarray
.dataPointerEither().cast
<NativeType
*>() + index
, val
);
840 static bool getElement(JSContext
* cx
, TypedArrayObject
* tarray
, size_t index
,
841 MutableHandleValue val
);
842 static bool getElementPure(TypedArrayObject
* tarray
, size_t index
, Value
* vp
);
844 static bool setElement(JSContext
* cx
, Handle
<TypedArrayObject
*> obj
,
845 uint64_t index
, HandleValue v
, ObjectOpResult
& result
);
848 template <typename NativeType
>
849 class FixedLengthTypedArrayObjectTemplate
850 : public FixedLengthTypedArrayObject
,
851 public TypedArrayObjectTemplate
<NativeType
> {
852 friend class js::TypedArrayObject
;
854 using TypedArrayTemplate
= TypedArrayObjectTemplate
<NativeType
>;
857 using TypedArrayTemplate::ArrayTypeID
;
858 using TypedArrayTemplate::BYTES_PER_ELEMENT
;
859 using TypedArrayTemplate::protoKey
;
861 static inline const JSClass
* instanceClass() {
862 static_assert(ArrayTypeID() <
863 std::size(TypedArrayObject::fixedLengthClasses
));
864 return &TypedArrayObject::fixedLengthClasses
[ArrayTypeID()];
867 static FixedLengthTypedArrayObject
* newBuiltinClassInstance(
868 JSContext
* cx
, gc::AllocKind allocKind
, gc::Heap heap
) {
869 RootedObject
proto(cx
, GlobalObject::getOrCreatePrototype(cx
, protoKey()));
873 return NewTypedArrayObject
<FixedLengthTypedArrayObject
>(
874 cx
, instanceClass(), proto
, allocKind
, heap
);
877 static FixedLengthTypedArrayObject
* makeProtoInstance(
878 JSContext
* cx
, HandleObject proto
, gc::AllocKind allocKind
) {
880 return NewTypedArrayObject
<FixedLengthTypedArrayObject
>(
881 cx
, instanceClass(), proto
, allocKind
, gc::Heap::Default
);
884 static FixedLengthTypedArrayObject
* makeInstance(
885 JSContext
* cx
, Handle
<ArrayBufferObjectMaybeShared
*> buffer
,
886 size_t byteOffset
, size_t len
, HandleObject proto
,
887 gc::Heap heap
= gc::Heap::Default
) {
888 MOZ_ASSERT(len
<= ByteLengthLimit
/ BYTES_PER_ELEMENT
);
890 gc::AllocKind allocKind
=
891 buffer
? gc::GetGCObjectKind(instanceClass())
892 : AllocKindForLazyBuffer(len
* BYTES_PER_ELEMENT
);
894 AutoSetNewObjectMetadata
metadata(cx
);
895 FixedLengthTypedArrayObject
* obj
;
897 obj
= makeProtoInstance(cx
, proto
, allocKind
);
899 obj
= newBuiltinClassInstance(cx
, allocKind
, heap
);
901 if (!obj
|| !obj
->init(cx
, buffer
, byteOffset
, len
, BYTES_PER_ELEMENT
)) {
908 static FixedLengthTypedArrayObject
* makeTemplateObject(JSContext
* cx
,
910 MOZ_ASSERT(len
>= 0);
912 MOZ_ALWAYS_TRUE(CalculateAllocSize
<NativeType
>(len
, &nbytes
));
913 bool fitsInline
= nbytes
<= INLINE_BUFFER_LIMIT
;
914 gc::AllocKind allocKind
= !fitsInline
? gc::GetGCObjectKind(instanceClass())
915 : AllocKindForLazyBuffer(nbytes
);
916 MOZ_ASSERT(allocKind
>= gc::GetGCObjectKind(instanceClass()));
918 AutoSetNewObjectMetadata
metadata(cx
);
920 auto* tarray
= newBuiltinClassInstance(cx
, allocKind
, gc::Heap::Tenured
);
925 initTypedArraySlots(tarray
, len
);
927 // Template objects don't need memory for their elements, since there
928 // won't be any elements to store.
929 MOZ_ASSERT(tarray
->getReservedSlot(DATA_SLOT
).isUndefined());
934 static void initTypedArraySlots(FixedLengthTypedArrayObject
* tarray
,
936 MOZ_ASSERT(len
>= 0);
937 tarray
->initFixedSlot(TypedArrayObject::BUFFER_SLOT
, JS::FalseValue());
938 tarray
->initFixedSlot(TypedArrayObject::LENGTH_SLOT
, PrivateValue(len
));
939 tarray
->initFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT
,
940 PrivateValue(size_t(0)));
945 tarray
->fixedData(FixedLengthTypedArrayObject::FIXED_DATA_START
);
946 output
[0] = TypedArrayObject::ZeroLengthArrayData
;
951 static void initTypedArrayData(FixedLengthTypedArrayObject
* tarray
, void* buf
,
952 size_t nbytes
, gc::AllocKind allocKind
) {
954 InitReservedSlot(tarray
, TypedArrayObject::DATA_SLOT
, buf
, nbytes
,
955 MemoryUse::TypedArrayElements
);
958 constexpr size_t dataOffset
= ArrayBufferViewObject::dataOffset();
959 constexpr size_t offset
= dataOffset
+ sizeof(HeapSlot
);
960 MOZ_ASSERT(offset
+ nbytes
<= GetGCKindBytes(allocKind
));
963 void* data
= tarray
->fixedData(FIXED_DATA_START
);
964 tarray
->initReservedSlot(DATA_SLOT
, PrivateValue(data
));
965 memset(data
, 0, nbytes
);
969 static FixedLengthTypedArrayObject
* makeTypedArrayWithTemplate(
970 JSContext
* cx
, TypedArrayObject
* templateObj
, int32_t len
) {
971 if (len
< 0 || size_t(len
) > ByteLengthLimit
/ BYTES_PER_ELEMENT
) {
972 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
973 JSMSG_BAD_ARRAY_LENGTH
);
977 size_t nbytes
= size_t(len
) * BYTES_PER_ELEMENT
;
978 MOZ_ASSERT(nbytes
<= ByteLengthLimit
);
980 bool fitsInline
= nbytes
<= INLINE_BUFFER_LIMIT
;
982 AutoSetNewObjectMetadata
metadata(cx
);
984 gc::AllocKind allocKind
= !fitsInline
? gc::GetGCObjectKind(instanceClass())
985 : AllocKindForLazyBuffer(nbytes
);
986 MOZ_ASSERT(templateObj
->getClass() == instanceClass());
988 RootedObject
proto(cx
, templateObj
->staticPrototype());
989 auto* obj
= makeProtoInstance(cx
, proto
, allocKind
);
994 initTypedArraySlots(obj
, len
);
1000 nbytes
= RoundUp(nbytes
, sizeof(Value
));
1001 buf
= cx
->nursery().allocateZeroedBuffer(obj
, nbytes
,
1002 js::ArrayBufferContentsArena
);
1004 ReportOutOfMemory(cx
);
1009 initTypedArrayData(obj
, buf
, nbytes
, allocKind
);
1015 template <typename NativeType
>
1016 class ResizableTypedArrayObjectTemplate
1017 : public ResizableTypedArrayObject
,
1018 public TypedArrayObjectTemplate
<NativeType
> {
1019 friend class js::TypedArrayObject
;
1021 using TypedArrayTemplate
= TypedArrayObjectTemplate
<NativeType
>;
1024 using TypedArrayTemplate::ArrayTypeID
;
1025 using TypedArrayTemplate::BYTES_PER_ELEMENT
;
1026 using TypedArrayTemplate::protoKey
;
1028 static inline const JSClass
* instanceClass() {
1029 static_assert(ArrayTypeID() <
1030 std::size(TypedArrayObject::resizableClasses
));
1031 return &TypedArrayObject::resizableClasses
[ArrayTypeID()];
1034 static ResizableTypedArrayObject
* newBuiltinClassInstance(
1035 JSContext
* cx
, gc::AllocKind allocKind
, gc::Heap heap
) {
1036 RootedObject
proto(cx
, GlobalObject::getOrCreatePrototype(cx
, protoKey()));
1040 return NewTypedArrayObject
<ResizableTypedArrayObject
>(
1041 cx
, instanceClass(), proto
, allocKind
, heap
);
1044 static ResizableTypedArrayObject
* makeProtoInstance(JSContext
* cx
,
1046 gc::AllocKind allocKind
) {
1048 return NewTypedArrayObject
<ResizableTypedArrayObject
>(
1049 cx
, instanceClass(), proto
, allocKind
, gc::Heap::Default
);
1052 static ResizableTypedArrayObject
* makeInstance(
1053 JSContext
* cx
, Handle
<ArrayBufferObjectMaybeShared
*> buffer
,
1054 size_t byteOffset
, size_t len
, AutoLength autoLength
,
1055 HandleObject proto
) {
1057 MOZ_ASSERT(buffer
->isResizable());
1058 MOZ_ASSERT(!buffer
->isDetached());
1059 MOZ_ASSERT(autoLength
== AutoLength::No
|| len
== 0,
1060 "length is zero for 'auto' length views");
1061 MOZ_ASSERT(len
<= ByteLengthLimit
/ BYTES_PER_ELEMENT
);
1063 gc::AllocKind allocKind
= gc::GetGCObjectKind(instanceClass());
1065 AutoSetNewObjectMetadata
metadata(cx
);
1066 ResizableTypedArrayObject
* obj
;
1068 obj
= makeProtoInstance(cx
, proto
, allocKind
);
1070 obj
= newBuiltinClassInstance(cx
, allocKind
, gc::Heap::Default
);
1072 if (!obj
|| !obj
->initResizable(cx
, buffer
, byteOffset
, len
,
1073 BYTES_PER_ELEMENT
, autoLength
)) {
1080 static ResizableTypedArrayObject
* makeTemplateObject(JSContext
* cx
) {
1081 gc::AllocKind allocKind
= gc::GetGCObjectKind(instanceClass());
1083 AutoSetNewObjectMetadata
metadata(cx
);
1085 auto* tarray
= newBuiltinClassInstance(cx
, allocKind
, gc::Heap::Tenured
);
1090 tarray
->initFixedSlot(TypedArrayObject::BUFFER_SLOT
, JS::FalseValue());
1091 tarray
->initFixedSlot(TypedArrayObject::LENGTH_SLOT
,
1092 PrivateValue(size_t(0)));
1093 tarray
->initFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT
,
1094 PrivateValue(size_t(0)));
1095 tarray
->initFixedSlot(AUTO_LENGTH_SLOT
, BooleanValue(false));
1096 tarray
->initFixedSlot(ResizableTypedArrayObject::INITIAL_LENGTH_SLOT
,
1097 PrivateValue(size_t(0)));
1098 tarray
->initFixedSlot(ResizableTypedArrayObject::INITIAL_BYTE_OFFSET_SLOT
,
1099 PrivateValue(size_t(0)));
1101 // Template objects don't need memory for their elements, since there
1102 // won't be any elements to store.
1103 MOZ_ASSERT(tarray
->getReservedSlot(DATA_SLOT
).isUndefined());
1109 template <typename NativeType
>
1110 bool TypedArrayObjectTemplate
<NativeType
>::convertValue(JSContext
* cx
,
1112 NativeType
* result
) {
1114 if (!ToNumber(cx
, v
, &d
)) {
1118 if (js::SupportDifferentialTesting()) {
1119 // See the comment in ElementSpecific::doubleToNative.
1120 d
= JS::CanonicalizeNaN(d
);
1123 // Assign based on characteristics of the destination type
1124 if constexpr (ArrayTypeIsFloatingPoint()) {
1125 *result
= NativeType(d
);
1126 } else if constexpr (ArrayTypeIsUnsigned()) {
1127 static_assert(sizeof(NativeType
) <= 4);
1128 uint32_t n
= ToUint32(d
);
1129 *result
= NativeType(n
);
1130 } else if constexpr (ArrayTypeID() == Scalar::Uint8Clamped
) {
1131 // The uint8_clamped type has a special rounding converter
1133 *result
= NativeType(d
);
1135 static_assert(sizeof(NativeType
) <= 4);
1136 int32_t n
= ToInt32(d
);
1137 *result
= NativeType(n
);
1143 bool TypedArrayObjectTemplate
<int64_t>::convertValue(JSContext
* cx
,
1146 JS_TRY_VAR_OR_RETURN_FALSE(cx
, *result
, ToBigInt64(cx
, v
));
1151 bool TypedArrayObjectTemplate
<uint64_t>::convertValue(JSContext
* cx
,
1154 JS_TRY_VAR_OR_RETURN_FALSE(cx
, *result
, ToBigUint64(cx
, v
));
1158 // https://tc39.github.io/proposal-bigint/#sec-integerindexedelementset
1159 // 9.4.5.11 IntegerIndexedElementSet ( O, index, value )
1160 template <typename NativeType
>
1161 /* static */ bool TypedArrayObjectTemplate
<NativeType
>::setElement(
1162 JSContext
* cx
, Handle
<TypedArrayObject
*> obj
, uint64_t index
, HandleValue v
,
1163 ObjectOpResult
& result
) {
1164 MOZ_ASSERT(!obj
->hasDetachedBuffer());
1165 MOZ_ASSERT(index
< obj
->length().valueOr(0));
1167 // Step 1 is enforced by the caller.
1170 NativeType nativeValue
;
1171 if (!convertValue(cx
, v
, &nativeValue
)) {
1176 if (index
< obj
->length().valueOr(0)) {
1177 MOZ_ASSERT(!obj
->hasDetachedBuffer(),
1178 "detaching an array buffer sets the length to zero");
1179 TypedArrayObjectTemplate
<NativeType
>::setIndex(*obj
, index
, nativeValue
);
1183 return result
.succeed();
1186 } /* anonymous namespace */
1188 TypedArrayObject
* js::NewTypedArrayWithTemplateAndLength(
1189 JSContext
* cx
, HandleObject templateObj
, int32_t len
) {
1190 MOZ_ASSERT(templateObj
->is
<TypedArrayObject
>());
1191 TypedArrayObject
* tobj
= &templateObj
->as
<TypedArrayObject
>();
1193 switch (tobj
->type()) {
1194 #define CREATE_TYPED_ARRAY(_, T, N) \
1196 return FixedLengthTypedArrayObjectTemplate<T>::makeTypedArrayWithTemplate( \
1198 JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPED_ARRAY
)
1199 #undef CREATE_TYPED_ARRAY
1201 MOZ_CRASH("Unsupported TypedArray type");
1205 TypedArrayObject
* js::NewTypedArrayWithTemplateAndArray(
1206 JSContext
* cx
, HandleObject templateObj
, HandleObject array
) {
1207 MOZ_ASSERT(templateObj
->is
<TypedArrayObject
>());
1208 TypedArrayObject
* tobj
= &templateObj
->as
<TypedArrayObject
>();
1210 switch (tobj
->type()) {
1211 #define CREATE_TYPED_ARRAY(_, T, N) \
1213 return TypedArrayObjectTemplate<T>::makeTypedArrayWithTemplate(cx, tobj, \
1215 JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPED_ARRAY
)
1216 #undef CREATE_TYPED_ARRAY
1218 MOZ_CRASH("Unsupported TypedArray type");
1222 TypedArrayObject
* js::NewTypedArrayWithTemplateAndBuffer(
1223 JSContext
* cx
, HandleObject templateObj
, HandleObject arrayBuffer
,
1224 HandleValue byteOffset
, HandleValue length
) {
1225 MOZ_ASSERT(templateObj
->is
<TypedArrayObject
>());
1226 TypedArrayObject
* tobj
= &templateObj
->as
<TypedArrayObject
>();
1228 switch (tobj
->type()) {
1229 #define CREATE_TYPED_ARRAY(_, T, N) \
1231 return TypedArrayObjectTemplate<T>::makeTypedArrayWithTemplate( \
1232 cx, tobj, arrayBuffer, byteOffset, length);
1233 JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPED_ARRAY
)
1234 #undef CREATE_TYPED_ARRAY
1236 MOZ_CRASH("Unsupported TypedArray type");
1240 TypedArrayObject
* js::NewUint8ArrayWithLength(JSContext
* cx
, int32_t len
,
1242 return TypedArrayObjectTemplate
<uint8_t>::fromLength(cx
, len
, nullptr, heap
);
1245 template <typename T
>
1246 /* static */ TypedArrayObject
* TypedArrayObjectTemplate
<T
>::fromArray(
1247 JSContext
* cx
, HandleObject other
, HandleObject proto
/* = nullptr */) {
1248 // Allow nullptr proto for FriendAPI methods, which don't care about
1250 if (other
->is
<TypedArrayObject
>()) {
1251 return fromTypedArray(cx
, other
, /* wrapped= */ false, proto
);
1254 if (other
->is
<WrapperObject
>() &&
1255 UncheckedUnwrap(other
)->is
<TypedArrayObject
>()) {
1256 return fromTypedArray(cx
, other
, /* wrapped= */ true, proto
);
1259 return fromObject(cx
, other
, proto
);
1262 // ES2023 draft rev cf86f1cdc28e809170733d74ea64fd0f3dd79f78
1263 // 23.2.5.1 TypedArray ( ...args )
1264 // 23.2.5.1.2 InitializeTypedArrayFromTypedArray ( O, srcArray )
1265 template <typename T
>
1266 /* static */ TypedArrayObject
* TypedArrayObjectTemplate
<T
>::fromTypedArray(
1267 JSContext
* cx
, HandleObject other
, bool isWrapped
, HandleObject proto
) {
1268 MOZ_ASSERT_IF(!isWrapped
, other
->is
<TypedArrayObject
>());
1269 MOZ_ASSERT_IF(isWrapped
, other
->is
<WrapperObject
>() &&
1270 UncheckedUnwrap(other
)->is
<TypedArrayObject
>());
1272 Rooted
<TypedArrayObject
*> srcArray(cx
);
1274 srcArray
= &other
->as
<TypedArrayObject
>();
1276 srcArray
= other
->maybeUnwrapAs
<TypedArrayObject
>();
1278 ReportAccessDenied(cx
);
1283 // InitializeTypedArrayFromTypedArray, step 1. (Skipped)
1285 // InitializeTypedArrayFromTypedArray, step 2.
1286 auto srcLength
= srcArray
->length();
1288 ReportOutOfBounds(cx
, srcArray
);
1292 // InitializeTypedArrayFromTypedArray, steps 3-7. (Skipped)
1294 // InitializeTypedArrayFromTypedArray, step 8.
1295 size_t elementLength
= *srcLength
;
1297 // InitializeTypedArrayFromTypedArray, step 9. (Skipped)
1299 // InitializeTypedArrayFromTypedArray, step 10.a. (Partial)
1300 // InitializeTypedArrayFromTypedArray, step 11.a.
1301 Rooted
<ArrayBufferObject
*> buffer(cx
);
1302 if (!maybeCreateArrayBuffer(cx
, elementLength
, &buffer
)) {
1306 // InitializeTypedArrayFromTypedArray, step 11.b.
1307 if (Scalar::isBigIntType(ArrayTypeID()) !=
1308 Scalar::isBigIntType(srcArray
->type())) {
1309 JS_ReportErrorNumberASCII(
1310 cx
, GetErrorMessage
, nullptr, JSMSG_TYPED_ARRAY_NOT_COMPATIBLE
,
1311 srcArray
->getClass()->name
,
1312 TypedArrayObject::fixedLengthClasses
[ArrayTypeID()].name
);
1317 // InitializeTypedArrayFromTypedArray, steps 12-15.
1318 Rooted
<TypedArrayObject
*> obj(cx
, FixedLengthTypedArray::makeInstance(
1319 cx
, buffer
, 0, elementLength
, proto
));
1324 MOZ_RELEASE_ASSERT(!srcArray
->hasDetachedBuffer());
1326 // InitializeTypedArrayFromTypedArray, steps 10.a. (Remaining parts)
1327 // InitializeTypedArrayFromTypedArray, steps 11.c-f.
1328 MOZ_ASSERT(!obj
->isSharedMemory());
1329 if (srcArray
->isSharedMemory()) {
1330 if (!ElementSpecific
<T
, SharedOps
>::setFromTypedArray(
1331 obj
, elementLength
, srcArray
, elementLength
, 0)) {
1332 MOZ_ASSERT_UNREACHABLE(
1333 "setFromTypedArray can only fail for overlapping buffers");
1337 if (!ElementSpecific
<T
, UnsharedOps
>::setFromTypedArray(
1338 obj
, elementLength
, srcArray
, elementLength
, 0)) {
1339 MOZ_ASSERT_UNREACHABLE(
1340 "setFromTypedArray can only fail for overlapping buffers");
1349 static MOZ_ALWAYS_INLINE
bool IsOptimizableInit(JSContext
* cx
,
1350 HandleObject iterable
,
1352 MOZ_ASSERT(!*optimized
);
1354 if (!IsPackedArray(iterable
)) {
1358 ForOfPIC::Chain
* stubChain
= ForOfPIC::getOrCreate(cx
);
1363 return stubChain
->tryOptimizeArray(cx
, iterable
.as
<ArrayObject
>(), optimized
);
1366 // ES2023 draft rev cf86f1cdc28e809170733d74ea64fd0f3dd79f78
1367 // 23.2.5.1 TypedArray ( ...args )
1368 // 23.2.5.1.4 InitializeTypedArrayFromList ( O, values )
1369 // 23.2.5.1.5 InitializeTypedArrayFromArrayLike ( O, arrayLike )
1370 template <typename T
>
1371 /* static */ TypedArrayObject
* TypedArrayObjectTemplate
<T
>::fromObject(
1372 JSContext
* cx
, HandleObject other
, HandleObject proto
) {
1373 // Steps 1-4 and 6.a (Already performed in caller).
1375 // Steps 6.b.i (Allocation deferred until later).
1377 // Steps 6.b.ii-iii. (Not applicable)
1381 bool optimized
= false;
1382 if (!IsOptimizableInit(cx
, other
, &optimized
)) {
1386 // Fast path when iterable is a packed array using the default iterator.
1388 // Steps 6.b.iv.2-3. (We don't need to call IterableToList for the fast
1390 Handle
<ArrayObject
*> array
= other
.as
<ArrayObject
>();
1392 // InitializeTypedArrayFromList, step 1.
1393 size_t len
= array
->getDenseInitializedLength();
1395 // InitializeTypedArrayFromList, step 2.
1396 Rooted
<ArrayBufferObject
*> buffer(cx
);
1397 if (!maybeCreateArrayBuffer(cx
, len
, &buffer
)) {
1402 Rooted
<FixedLengthTypedArrayObject
*> obj(
1403 cx
, FixedLengthTypedArray::makeInstance(cx
, buffer
, 0, len
, proto
));
1408 // InitializeTypedArrayFromList, steps 3-4.
1409 MOZ_ASSERT(!obj
->isSharedMemory());
1410 if (!ElementSpecific
<T
, UnsharedOps
>::initFromIterablePackedArray(cx
, obj
,
1415 // InitializeTypedArrayFromList, step 5. (The assertion isn't applicable for
1422 // Step 6.b.iv.1 (Assertion; implicit in our implementation).
1425 RootedValue
callee(cx
);
1426 RootedId
iteratorId(cx
, PropertyKey::Symbol(cx
->wellKnownSymbols().iterator
));
1427 if (!GetProperty(cx
, other
, other
, iteratorId
, &callee
)) {
1431 // Steps 6.b.iv.3-4.
1432 RootedObject
arrayLike(cx
);
1433 if (!callee
.isNullOrUndefined()) {
1434 // Throw if other[Symbol.iterator] isn't callable.
1435 if (!callee
.isObject() || !callee
.toObject().isCallable()) {
1436 RootedValue
otherVal(cx
, ObjectValue(*other
));
1438 DecompileValueGenerator(cx
, JSDVG_SEARCH_STACK
, otherVal
, nullptr);
1442 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr, JSMSG_NOT_ITERABLE
,
1447 FixedInvokeArgs
<2> args2(cx
);
1448 args2
[0].setObject(*other
);
1449 args2
[1].set(callee
);
1452 RootedValue
rval(cx
);
1453 if (!CallSelfHostedFunction(cx
, cx
->names().IterableToList
,
1454 UndefinedHandleValue
, args2
, &rval
)) {
1458 // Step 6.b.iv.3.b (Implemented below).
1459 arrayLike
= &rval
.toObject();
1461 // Step 4.a is an assertion: object is not an Iterator. Testing this is
1462 // literally the very last thing we did, so we don't assert here.
1464 // Step 4.b (Implemented below).
1468 // We implement InitializeTypedArrayFromList in terms of
1469 // InitializeTypedArrayFromArrayLike.
1471 // InitializeTypedArrayFromArrayLike, step 1.
1473 if (!GetLengthProperty(cx
, arrayLike
, &len
)) {
1477 // InitializeTypedArrayFromArrayLike, step 2.
1478 Rooted
<ArrayBufferObject
*> buffer(cx
);
1479 if (!maybeCreateArrayBuffer(cx
, len
, &buffer
)) {
1483 MOZ_ASSERT(len
<= ByteLengthLimit
/ BYTES_PER_ELEMENT
);
1486 Rooted
<TypedArrayObject
*> obj(
1487 cx
, FixedLengthTypedArray::makeInstance(cx
, buffer
, 0, len
, proto
));
1492 // InitializeTypedArrayFromArrayLike, steps 3-4.
1493 MOZ_ASSERT(!obj
->isSharedMemory());
1494 if (!ElementSpecific
<T
, UnsharedOps
>::setFromNonTypedArray(cx
, obj
, arrayLike
,
1503 static bool TypedArrayConstructor(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1504 CallArgs args
= CallArgsFromVp(argc
, vp
);
1505 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1506 JSMSG_TYPED_ARRAY_CALL_OR_CONSTRUCT
,
1507 args
.isConstructing() ? "construct" : "call");
1511 template <typename T
>
1512 static bool GetTemplateObjectForNative(JSContext
* cx
,
1513 const JS::HandleValueArray args
,
1514 MutableHandleObject res
) {
1515 if (args
.length() == 0) {
1519 HandleValue arg
= args
[0];
1520 if (arg
.isInt32()) {
1522 if (arg
.toInt32() >= 0) {
1523 len
= arg
.toInt32();
1527 if (!js::CalculateAllocSize
<T
>(len
, &nbytes
) ||
1528 nbytes
> TypedArrayObject::ByteLengthLimit
) {
1533 FixedLengthTypedArrayObjectTemplate
<T
>::makeTemplateObject(cx
, len
));
1537 if (!arg
.isObject()) {
1540 auto* obj
= &arg
.toObject();
1542 // We don't support wrappers, because of the complicated interaction between
1543 // wrapped ArrayBuffers and TypedArrays, see |fromBufferWrapped()|.
1544 if (IsWrapper(obj
)) {
1548 // We don't use the template's length in the object case, so we can create
1549 // the template typed array with an initial length of zero.
1552 if (!obj
->is
<ArrayBufferObjectMaybeShared
>() ||
1553 !obj
->as
<ArrayBufferObjectMaybeShared
>().isResizable()) {
1555 FixedLengthTypedArrayObjectTemplate
<T
>::makeTemplateObject(cx
, len
));
1557 res
.set(ResizableTypedArrayObjectTemplate
<T
>::makeTemplateObject(cx
));
1562 /* static */ bool TypedArrayObject::GetTemplateObjectForNative(
1563 JSContext
* cx
, Native native
, const JS::HandleValueArray args
,
1564 MutableHandleObject res
) {
1566 #define CHECK_TYPED_ARRAY_CONSTRUCTOR(_, T, N) \
1567 if (native == &TypedArrayObjectTemplate<T>::class_constructor) { \
1568 return ::GetTemplateObjectForNative<T>(cx, args, res); \
1570 JS_FOR_EACH_TYPED_ARRAY(CHECK_TYPED_ARRAY_CONSTRUCTOR
)
1571 #undef CHECK_TYPED_ARRAY_CONSTRUCTOR
1575 static bool LengthGetterImpl(JSContext
* cx
, const CallArgs
& args
) {
1576 auto* tarr
= &args
.thisv().toObject().as
<TypedArrayObject
>();
1577 args
.rval().setNumber(tarr
->length().valueOr(0));
1581 static bool TypedArray_lengthGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1582 CallArgs args
= CallArgsFromVp(argc
, vp
);
1583 return CallNonGenericMethod
<IsTypedArrayObject
, LengthGetterImpl
>(cx
, args
);
1586 static bool ByteOffsetGetterImpl(JSContext
* cx
, const CallArgs
& args
) {
1587 auto* tarr
= &args
.thisv().toObject().as
<TypedArrayObject
>();
1588 args
.rval().setNumber(tarr
->byteOffset().valueOr(0));
1592 static bool TypedArray_byteOffsetGetter(JSContext
* cx
, unsigned argc
,
1594 CallArgs args
= CallArgsFromVp(argc
, vp
);
1595 return CallNonGenericMethod
<IsTypedArrayObject
, ByteOffsetGetterImpl
>(cx
,
1599 static bool ByteLengthGetterImpl(JSContext
* cx
, const CallArgs
& args
) {
1600 auto* tarr
= &args
.thisv().toObject().as
<TypedArrayObject
>();
1601 args
.rval().setNumber(tarr
->byteLength().valueOr(0));
1605 static bool TypedArray_byteLengthGetter(JSContext
* cx
, unsigned argc
,
1607 CallArgs args
= CallArgsFromVp(argc
, vp
);
1608 return CallNonGenericMethod
<IsTypedArrayObject
, ByteLengthGetterImpl
>(cx
,
1612 static bool BufferGetterImpl(JSContext
* cx
, const CallArgs
& args
) {
1613 MOZ_ASSERT(IsTypedArrayObject(args
.thisv()));
1614 Rooted
<TypedArrayObject
*> tarray(
1615 cx
, &args
.thisv().toObject().as
<TypedArrayObject
>());
1616 if (!TypedArrayObject::ensureHasBuffer(cx
, tarray
)) {
1619 args
.rval().set(tarray
->bufferValue());
1623 static bool TypedArray_bufferGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1624 CallArgs args
= CallArgsFromVp(argc
, vp
);
1625 return CallNonGenericMethod
<IsTypedArrayObject
, BufferGetterImpl
>(cx
, args
);
1628 // ES2019 draft rev fc9ecdcd74294d0ca3146d4b274e2a8e79565dc3
1629 // 22.2.3.32 get %TypedArray%.prototype [ @@toStringTag ]
1630 static bool TypedArray_toStringTagGetter(JSContext
* cx
, unsigned argc
,
1632 CallArgs args
= CallArgsFromVp(argc
, vp
);
1635 if (!args
.thisv().isObject()) {
1636 args
.rval().setUndefined();
1640 JSObject
* obj
= CheckedUnwrapStatic(&args
.thisv().toObject());
1642 ReportAccessDenied(cx
);
1647 if (!obj
->is
<TypedArrayObject
>()) {
1648 args
.rval().setUndefined();
1653 JSProtoKey protoKey
= StandardProtoKeyOrNull(obj
);
1654 MOZ_ASSERT(protoKey
);
1656 args
.rval().setString(ClassName(protoKey
, cx
));
1660 /* static */ const JSPropertySpec
TypedArrayObject::protoAccessors
[] = {
1661 JS_PSG("length", TypedArray_lengthGetter
, 0),
1662 JS_PSG("buffer", TypedArray_bufferGetter
, 0),
1663 JS_PSG("byteLength", TypedArray_byteLengthGetter
, 0),
1664 JS_PSG("byteOffset", TypedArray_byteOffsetGetter
, 0),
1665 JS_SYM_GET(toStringTag
, TypedArray_toStringTagGetter
, 0),
1668 template <typename T
>
1669 static inline bool SetFromTypedArray(Handle
<TypedArrayObject
*> target
,
1670 size_t targetLength
,
1671 Handle
<TypedArrayObject
*> source
,
1672 size_t sourceLength
, size_t offset
) {
1673 // WARNING: |source| may be an unwrapped typed array from a different
1674 // compartment. Proceed with caution!
1676 if (target
->isSharedMemory() || source
->isSharedMemory()) {
1677 return ElementSpecific
<T
, SharedOps
>::setFromTypedArray(
1678 target
, targetLength
, source
, sourceLength
, offset
);
1680 return ElementSpecific
<T
, UnsharedOps
>::setFromTypedArray(
1681 target
, targetLength
, source
, sourceLength
, offset
);
1684 template <typename T
>
1685 static inline bool SetFromNonTypedArray(JSContext
* cx
,
1686 Handle
<TypedArrayObject
*> target
,
1687 HandleObject source
, size_t len
,
1689 MOZ_ASSERT(!source
->is
<TypedArrayObject
>(), "use SetFromTypedArray");
1691 if (target
->isSharedMemory()) {
1692 return ElementSpecific
<T
, SharedOps
>::setFromNonTypedArray(
1693 cx
, target
, source
, len
, offset
);
1695 return ElementSpecific
<T
, UnsharedOps
>::setFromNonTypedArray(
1696 cx
, target
, source
, len
, offset
);
1699 // ES2023 draft rev 22cc56ab08fcab92a865978c0aa5c6f1d8ce250f
1700 // 23.2.3.24.1 SetTypedArrayFromTypedArray ( target, targetOffset, source )
1701 static bool SetTypedArrayFromTypedArray(JSContext
* cx
,
1702 Handle
<TypedArrayObject
*> target
,
1703 double targetOffset
,
1704 size_t targetLength
,
1705 Handle
<TypedArrayObject
*> source
) {
1706 // WARNING: |source| may be an unwrapped typed array from a different
1707 // compartment. Proceed with caution!
1709 MOZ_ASSERT(targetOffset
>= 0);
1711 // Steps 1-3. (Performed in caller.)
1712 MOZ_ASSERT(!target
->hasDetachedBuffer());
1715 auto sourceLength
= source
->length();
1716 if (!sourceLength
) {
1717 ReportOutOfBounds(cx
, source
);
1721 // Steps 13-14 (Split into two checks to provide better error messages).
1722 if (targetOffset
> targetLength
) {
1723 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_BAD_INDEX
);
1727 // Step 14 (Cont'd).
1728 size_t offset
= size_t(targetOffset
);
1729 if (*sourceLength
> targetLength
- offset
) {
1730 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1731 JSMSG_SOURCE_ARRAY_TOO_LONG
);
1736 if (Scalar::isBigIntType(target
->type()) !=
1737 Scalar::isBigIntType(source
->type())) {
1738 JS_ReportErrorNumberASCII(
1739 cx
, GetErrorMessage
, nullptr, JSMSG_TYPED_ARRAY_NOT_COMPATIBLE
,
1740 source
->getClass()->name
, target
->getClass()->name
);
1744 // Steps 6-12, 16-24.
1745 switch (target
->type()) {
1746 #define SET_FROM_TYPED_ARRAY(_, T, N) \
1748 if (!SetFromTypedArray<T>(target, targetLength, source, *sourceLength, \
1750 ReportOutOfMemory(cx); \
1754 JS_FOR_EACH_TYPED_ARRAY(SET_FROM_TYPED_ARRAY
)
1755 #undef SET_FROM_TYPED_ARRAY
1757 MOZ_CRASH("Unsupported TypedArray type");
1763 // ES2023 draft rev 22cc56ab08fcab92a865978c0aa5c6f1d8ce250f
1764 // 23.2.3.24.1 SetTypedArrayFromArrayLike ( target, targetOffset, source )
1765 static bool SetTypedArrayFromArrayLike(JSContext
* cx
,
1766 Handle
<TypedArrayObject
*> target
,
1767 double targetOffset
, size_t targetLength
,
1769 MOZ_ASSERT(targetOffset
>= 0);
1771 // Steps 1-2. (Performed in caller.)
1772 MOZ_ASSERT(target
->length().isSome());
1774 // Steps 3-4. (Performed in caller.)
1778 if (!GetLengthProperty(cx
, src
, &srcLength
)) {
1782 // Steps 6-7 (Split into two checks to provide better error messages).
1783 if (targetOffset
> targetLength
) {
1784 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_BAD_INDEX
);
1789 size_t offset
= size_t(targetOffset
);
1790 if (srcLength
> targetLength
- offset
) {
1791 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1792 JSMSG_SOURCE_ARRAY_TOO_LONG
);
1796 MOZ_ASSERT(srcLength
<= targetLength
);
1799 if (srcLength
> 0) {
1800 switch (target
->type()) {
1801 #define SET_FROM_NON_TYPED_ARRAY(_, T, N) \
1803 if (!SetFromNonTypedArray<T>(cx, target, src, srcLength, offset)) \
1806 JS_FOR_EACH_TYPED_ARRAY(SET_FROM_NON_TYPED_ARRAY
)
1807 #undef SET_FROM_NON_TYPED_ARRAY
1809 MOZ_CRASH("Unsupported TypedArray type");
1817 // ES2023 draft rev 22cc56ab08fcab92a865978c0aa5c6f1d8ce250f
1818 // 23.2.3.24 %TypedArray%.prototype.set ( source [ , offset ] )
1819 // 23.2.3.24.1 SetTypedArrayFromTypedArray ( target, targetOffset, source )
1820 // 23.2.3.24.2 SetTypedArrayFromArrayLike ( target, targetOffset, source )
1822 bool TypedArrayObject::set_impl(JSContext
* cx
, const CallArgs
& args
) {
1823 MOZ_ASSERT(IsTypedArrayObject(args
.thisv()));
1825 // Steps 1-3 (Validation performed as part of CallNonGenericMethod).
1826 Rooted
<TypedArrayObject
*> target(
1827 cx
, &args
.thisv().toObject().as
<TypedArrayObject
>());
1830 double targetOffset
= 0;
1831 if (args
.length() > 1) {
1833 if (!ToInteger(cx
, args
[1], &targetOffset
)) {
1838 if (targetOffset
< 0) {
1839 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_BAD_INDEX
);
1844 // 23.2.3.24.1, steps 1-2.
1845 // 23.2.3.24.2, steps 1-2.
1846 auto targetLength
= target
->length();
1847 if (!targetLength
) {
1848 ReportOutOfBounds(cx
, target
);
1852 // 23.2.3.24.2, step 4. (23.2.3.24.1 only applies if args[0] is a typed
1853 // array, so it doesn't make a difference there to apply ToObject here.)
1854 RootedObject
src(cx
, ToObject(cx
, args
.get(0)));
1859 Rooted
<TypedArrayObject
*> srcTypedArray(cx
);
1861 JSObject
* obj
= CheckedUnwrapStatic(src
);
1863 ReportAccessDenied(cx
);
1867 if (obj
->is
<TypedArrayObject
>()) {
1868 srcTypedArray
= &obj
->as
<TypedArrayObject
>();
1873 if (srcTypedArray
) {
1874 if (!SetTypedArrayFromTypedArray(cx
, target
, targetOffset
, *targetLength
,
1879 if (!SetTypedArrayFromArrayLike(cx
, target
, targetOffset
, *targetLength
,
1886 args
.rval().setUndefined();
1891 bool TypedArrayObject::set(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1892 CallArgs args
= CallArgsFromVp(argc
, vp
);
1893 return CallNonGenericMethod
<IsTypedArrayObject
, TypedArrayObject::set_impl
>(
1897 // ES2020 draft rev dc1e21c454bd316810be1c0e7af0131a2d7f38e9
1898 // 22.2.3.5 %TypedArray%.prototype.copyWithin ( target, start [ , end ] )
1900 bool TypedArrayObject::copyWithin_impl(JSContext
* cx
, const CallArgs
& args
) {
1901 MOZ_ASSERT(IsTypedArrayObject(args
.thisv()));
1904 Rooted
<TypedArrayObject
*> tarray(
1905 cx
, &args
.thisv().toObject().as
<TypedArrayObject
>());
1907 auto arrayLength
= tarray
->length();
1909 ReportOutOfBounds(cx
, tarray
);
1914 size_t len
= *arrayLength
;
1917 double relativeTarget
;
1918 if (!ToInteger(cx
, args
.get(0), &relativeTarget
)) {
1924 if (relativeTarget
< 0) {
1925 to
= std::max(len
+ relativeTarget
, 0.0);
1927 to
= std::min(relativeTarget
, double(len
));
1931 double relativeStart
;
1932 if (!ToInteger(cx
, args
.get(1), &relativeStart
)) {
1938 if (relativeStart
< 0) {
1939 from
= std::max(len
+ relativeStart
, 0.0);
1941 from
= std::min(relativeStart
, double(len
));
1946 if (!args
.hasDefined(2)) {
1949 if (!ToInteger(cx
, args
[2], &relativeEnd
)) {
1956 if (relativeEnd
< 0) {
1957 final_
= std::max(len
+ relativeEnd
, 0.0);
1959 final_
= std::min(relativeEnd
, double(len
));
1963 MOZ_ASSERT(to
<= len
);
1965 if (from
<= final_
) {
1966 count
= std::min(final_
- from
, len
- to
);
1973 // Note that this copies elements effectively by memmove, *not* in
1974 // step 11's specified order. This is unobservable, even when the underlying
1975 // buffer is a SharedArrayBuffer instance, because the access is unordered and
1976 // therefore is allowed to have data races.
1979 args
.rval().setObject(*tarray
);
1983 // Reacquire the length because side-effects may have detached or resized the
1985 arrayLength
= tarray
->length();
1987 ReportOutOfBounds(cx
, tarray
);
1991 // Recompute the bounds if the current length is smaller.
1992 if (*arrayLength
< len
) {
1993 MOZ_ASSERT(to
+ count
<= len
);
1994 MOZ_ASSERT(from
+ count
<= len
);
1998 // Don't copy any bytes if either index is no longer in-bounds.
1999 if (to
>= len
|| from
>= len
) {
2000 args
.rval().setObject(*tarray
);
2004 // Restrict |count| to not copy any bytes after the end of the array.
2005 count
= std::min(count
, std::min(len
- to
, len
- from
));
2006 MOZ_ASSERT(count
> 0);
2009 // Don't multiply by |tarray->bytesPerElement()| in case the compiler can't
2010 // strength-reduce multiplication by 1/2/4/8 into the equivalent shift.
2011 const size_t ElementShift
= TypedArrayShift(tarray
->type());
2013 MOZ_ASSERT((SIZE_MAX
>> ElementShift
) > to
);
2014 size_t byteDest
= to
<< ElementShift
;
2016 MOZ_ASSERT((SIZE_MAX
>> ElementShift
) > from
);
2017 size_t byteSrc
= from
<< ElementShift
;
2019 MOZ_ASSERT((SIZE_MAX
>> ElementShift
) >= count
);
2020 size_t byteSize
= count
<< ElementShift
;
2024 size_t viewByteLength
= len
<< ElementShift
;
2025 MOZ_ASSERT(byteSize
<= viewByteLength
);
2026 MOZ_ASSERT(byteDest
< viewByteLength
);
2027 MOZ_ASSERT(byteSrc
< viewByteLength
);
2028 MOZ_ASSERT(byteDest
<= viewByteLength
- byteSize
);
2029 MOZ_ASSERT(byteSrc
<= viewByteLength
- byteSize
);
2033 SharedMem
<uint8_t*> data
= tarray
->dataPointerEither().cast
<uint8_t*>();
2034 if (tarray
->isSharedMemory()) {
2035 jit::AtomicOperations::memmoveSafeWhenRacy(data
+ byteDest
, data
+ byteSrc
,
2038 memmove(data
.unwrapUnshared() + byteDest
, data
.unwrapUnshared() + byteSrc
,
2042 args
.rval().setObject(*tarray
);
2047 bool TypedArrayObject::copyWithin(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2048 AutoJSMethodProfilerEntry
pseudoFrame(cx
, "[TypedArray].prototype",
2050 CallArgs args
= CallArgsFromVp(argc
, vp
);
2051 return CallNonGenericMethod
<IsTypedArrayObject
,
2052 TypedArrayObject::copyWithin_impl
>(cx
, args
);
2055 // Byte vector with large enough inline storage to allow constructing small
2056 // typed arrays without extra heap allocations.
2058 js::Vector
<uint8_t, FixedLengthTypedArrayObject::INLINE_BUFFER_LIMIT
>;
2060 static UniqueChars
QuoteString(JSContext
* cx
, char16_t ch
) {
2061 Sprinter
sprinter(cx
);
2062 if (!sprinter
.init()) {
2067 js::EscapePrinter
ep(sprinter
, esc
);
2070 return sprinter
.release();
2074 * FromHex ( string [ , maxLength ] )
2076 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-fromhex
2078 static bool FromHex(JSContext
* cx
, Handle
<JSString
*> string
, size_t maxLength
,
2079 ByteVector
& bytes
, size_t* readLength
) {
2080 // Step 1. (Not applicable in our implementation.)
2083 size_t length
= string
->length();
2086 if (length
% 2 != 0) {
2087 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
2088 JSMSG_TYPED_ARRAY_BAD_HEX_STRING_LENGTH
);
2092 JSLinearString
* linear
= string
->ensureLinear(cx
);
2097 // Step 4. (Not applicable in our implementation.)
2098 MOZ_ASSERT(bytes
.empty());
2104 while (index
< length
&& bytes
.length() < maxLength
) {
2106 char16_t c0
= linear
->latin1OrTwoByteChar(index
);
2107 char16_t c1
= linear
->latin1OrTwoByteChar(index
+ 1);
2110 if (MOZ_UNLIKELY(!mozilla::IsAsciiHexDigit(c0
) ||
2111 !mozilla::IsAsciiHexDigit(c1
))) {
2112 char16_t ch
= !mozilla::IsAsciiHexDigit(c0
) ? c0
: c1
;
2113 if (auto str
= QuoteString(cx
, ch
)) {
2114 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
2115 JSMSG_TYPED_ARRAY_BAD_HEX_DIGIT
, str
.get());
2124 uint8_t byte
= (mozilla::AsciiAlphanumericToNumber(c0
) << 4) +
2125 mozilla::AsciiAlphanumericToNumber(c1
);
2128 if (!bytes
.append(byte
)) {
2134 *readLength
= index
;
2139 * Uint8Array.fromHex ( string )
2141 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.fromhex
2143 static bool uint8array_fromHex(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2144 CallArgs args
= CallArgsFromVp(argc
, vp
);
2147 if (!args
.get(0).isString()) {
2148 return ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_SEARCH_STACK
,
2149 args
.get(0), nullptr, "not a string");
2151 Rooted
<JSString
*> string(cx
, args
[0].toString());
2154 constexpr size_t maxLength
= std::numeric_limits
<size_t>::max();
2155 ByteVector
bytes(cx
);
2156 size_t unusedReadLength
;
2157 if (!FromHex(cx
, string
, maxLength
, bytes
, &unusedReadLength
)) {
2162 size_t resultLength
= bytes
.length();
2166 TypedArrayObjectTemplate
<uint8_t>::fromLength(cx
, resultLength
);
2172 auto target
= SharedMem
<uint8_t*>::unshared(tarray
->dataPointerUnshared());
2173 auto source
= SharedMem
<uint8_t*>::unshared(bytes
.begin());
2174 UnsharedOps::podCopy(target
, source
, resultLength
);
2177 args
.rval().setObject(*tarray
);
2182 * Uint8Array.prototype.setFromHex ( string )
2184 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.prototype.setfromhex
2186 static bool uint8array_setFromHex(JSContext
* cx
, const CallArgs
& args
) {
2187 Rooted
<TypedArrayObject
*> tarray(
2188 cx
, &args
.thisv().toObject().as
<TypedArrayObject
>());
2191 if (!args
.get(0).isString()) {
2192 return ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_SEARCH_STACK
,
2193 args
.get(0), nullptr, "not a string");
2195 Rooted
<JSString
*> string(cx
, args
[0].toString());
2198 auto length
= tarray
->length();
2200 ReportOutOfBounds(cx
, tarray
);
2205 size_t maxLength
= *length
;
2208 ByteVector
bytes(cx
);
2210 if (!FromHex(cx
, string
, maxLength
, bytes
, &readLength
)) {
2215 size_t written
= bytes
.length();
2219 // The underlying buffer has neither been detached nor shrunk. (It may have
2220 // been grown when it's a growable shared buffer and a concurrent thread
2221 // resized the buffer.)
2222 MOZ_ASSERT(!tarray
->hasDetachedBuffer());
2223 MOZ_ASSERT(tarray
->length().valueOr(0) >= *length
);
2226 MOZ_ASSERT(written
<= *length
);
2228 // Step 13. (Inlined SetUint8ArrayBytes)
2229 auto target
= tarray
->dataPointerEither().cast
<uint8_t*>();
2230 auto source
= SharedMem
<uint8_t*>::unshared(bytes
.begin());
2231 if (tarray
->isSharedMemory()) {
2232 SharedOps::podCopy(target
, source
, written
);
2234 UnsharedOps::podCopy(target
, source
, written
);
2238 Rooted
<PlainObject
*> result(cx
, NewPlainObject(cx
));
2244 Rooted
<Value
> readValue(cx
, NumberValue(readLength
));
2245 if (!DefineDataProperty(cx
, result
, cx
->names().read
, readValue
)) {
2250 Rooted
<Value
> writtenValue(cx
, NumberValue(written
));
2251 if (!DefineDataProperty(cx
, result
, cx
->names().written
, writtenValue
)) {
2256 args
.rval().setObject(*result
);
2261 * Uint8Array.prototype.setFromHex ( string )
2263 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.prototype.setfromhex
2265 static bool uint8array_setFromHex(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2266 CallArgs args
= CallArgsFromVp(argc
, vp
);
2269 return CallNonGenericMethod
<IsUint8ArrayObject
, uint8array_setFromHex
>(cx
,
2274 * Uint8Array.prototype.toHex ( )
2276 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.prototype.tohex
2278 static bool uint8array_toHex(JSContext
* cx
, const CallArgs
& args
) {
2279 Rooted
<TypedArrayObject
*> tarray(
2280 cx
, &args
.thisv().toObject().as
<TypedArrayObject
>());
2282 // Step 3. (Partial)
2283 auto length
= tarray
->length();
2285 ReportOutOfBounds(cx
, tarray
);
2289 // |length| is limited by |ByteLengthLimit|, which ensures that multiplying it
2290 // by two won't overflow.
2291 static_assert(TypedArrayObject::ByteLengthLimit
<=
2292 std::numeric_limits
<size_t>::max() / 2);
2293 MOZ_ASSERT(*length
<= TypedArrayObject::ByteLengthLimit
);
2295 // Compute the output string length. Each byte is encoded as two characters,
2296 // so the output length is exactly twice as large as |length|.
2297 size_t outLength
= *length
* 2;
2298 if (outLength
> JSString::MAX_LENGTH
) {
2299 ReportAllocationOverflow(cx
);
2304 JSStringBuilder
sb(cx
);
2305 if (!sb
.reserve(outLength
)) {
2309 // NB: Lower case hex digits.
2310 static constexpr char HexDigits
[] = "0123456789abcdef";
2311 static_assert(std::char_traits
<char>::length(HexDigits
) == 16);
2315 // Our implementation directly converts the bytes to their string
2316 // representation instead of first collecting them into an intermediate list.
2317 auto data
= tarray
->dataPointerEither().cast
<uint8_t*>();
2318 for (size_t index
= 0; index
< *length
; index
++) {
2319 auto byte
= jit::AtomicOperations::loadSafeWhenRacy(data
+ index
);
2321 sb
.infallibleAppend(HexDigits
[byte
>> 4]);
2322 sb
.infallibleAppend(HexDigits
[byte
& 0xf]);
2325 MOZ_ASSERT(sb
.length() == outLength
, "all characters were written");
2328 auto* str
= sb
.finishString();
2333 args
.rval().setString(str
);
2338 * Uint8Array.prototype.toHex ( )
2340 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.prototype.tohex
2342 static bool uint8array_toHex(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2343 CallArgs args
= CallArgsFromVp(argc
, vp
);
2346 return CallNonGenericMethod
<IsUint8ArrayObject
, uint8array_toHex
>(cx
, args
);
2349 /* static */ const JSFunctionSpec
TypedArrayObject::protoFunctions
[] = {
2350 JS_SELF_HOSTED_FN("subarray", "TypedArraySubarray", 2, 0),
2351 JS_FN("set", TypedArrayObject::set
, 1, 0),
2352 JS_FN("copyWithin", TypedArrayObject::copyWithin
, 2, 0),
2353 JS_SELF_HOSTED_FN("every", "TypedArrayEvery", 1, 0),
2354 JS_SELF_HOSTED_FN("fill", "TypedArrayFill", 3, 0),
2355 JS_SELF_HOSTED_FN("filter", "TypedArrayFilter", 1, 0),
2356 JS_SELF_HOSTED_FN("find", "TypedArrayFind", 1, 0),
2357 JS_SELF_HOSTED_FN("findIndex", "TypedArrayFindIndex", 1, 0),
2358 JS_SELF_HOSTED_FN("findLast", "TypedArrayFindLast", 1, 0),
2359 JS_SELF_HOSTED_FN("findLastIndex", "TypedArrayFindLastIndex", 1, 0),
2360 JS_SELF_HOSTED_FN("forEach", "TypedArrayForEach", 1, 0),
2361 JS_SELF_HOSTED_FN("indexOf", "TypedArrayIndexOf", 2, 0),
2362 JS_SELF_HOSTED_FN("join", "TypedArrayJoin", 1, 0),
2363 JS_SELF_HOSTED_FN("lastIndexOf", "TypedArrayLastIndexOf", 1, 0),
2364 JS_SELF_HOSTED_FN("map", "TypedArrayMap", 1, 0),
2365 JS_SELF_HOSTED_FN("reduce", "TypedArrayReduce", 1, 0),
2366 JS_SELF_HOSTED_FN("reduceRight", "TypedArrayReduceRight", 1, 0),
2367 JS_SELF_HOSTED_FN("reverse", "TypedArrayReverse", 0, 0),
2368 JS_SELF_HOSTED_FN("slice", "TypedArraySlice", 2, 0),
2369 JS_SELF_HOSTED_FN("some", "TypedArraySome", 1, 0),
2370 JS_SELF_HOSTED_FN("sort", "TypedArraySort", 1, 0),
2371 JS_SELF_HOSTED_FN("entries", "TypedArrayEntries", 0, 0),
2372 JS_SELF_HOSTED_FN("keys", "TypedArrayKeys", 0, 0),
2373 JS_SELF_HOSTED_FN("values", "$TypedArrayValues", 0, 0),
2374 JS_SELF_HOSTED_SYM_FN(iterator
, "$TypedArrayValues", 0, 0),
2375 JS_SELF_HOSTED_FN("includes", "TypedArrayIncludes", 2, 0),
2376 JS_SELF_HOSTED_FN("toString", "ArrayToString", 0, 0),
2377 JS_SELF_HOSTED_FN("toLocaleString", "TypedArrayToLocaleString", 2, 0),
2378 JS_SELF_HOSTED_FN("at", "TypedArrayAt", 1, 0),
2379 JS_SELF_HOSTED_FN("toReversed", "TypedArrayToReversed", 0, 0),
2380 JS_SELF_HOSTED_FN("toSorted", "TypedArrayToSorted", 1, 0),
2381 JS_SELF_HOSTED_FN("with", "TypedArrayWith", 2, 0),
2385 /* static */ const JSFunctionSpec
TypedArrayObject::staticFunctions
[] = {
2386 JS_SELF_HOSTED_FN("from", "TypedArrayStaticFrom", 3, 0),
2387 JS_SELF_HOSTED_FN("of", "TypedArrayStaticOf", 0, 0),
2391 /* static */ const JSPropertySpec
TypedArrayObject::staticProperties
[] = {
2392 JS_SELF_HOSTED_SYM_GET(species
, "$TypedArraySpecies", 0),
2396 static JSObject
* CreateSharedTypedArrayPrototype(JSContext
* cx
,
2398 return GlobalObject::createBlankPrototype(
2399 cx
, cx
->global(), &TypedArrayObject::sharedTypedArrayPrototypeClass
);
2402 static const ClassSpec TypedArrayObjectSharedTypedArrayPrototypeClassSpec
= {
2403 GenericCreateConstructor
<TypedArrayConstructor
, 0, gc::AllocKind::FUNCTION
>,
2404 CreateSharedTypedArrayPrototype
,
2405 TypedArrayObject::staticFunctions
,
2406 TypedArrayObject::staticProperties
,
2407 TypedArrayObject::protoFunctions
,
2408 TypedArrayObject::protoAccessors
,
2410 ClassSpec::DontDefineConstructor
,
2413 /* static */ const JSClass
TypedArrayObject::sharedTypedArrayPrototypeClass
= {
2414 "TypedArrayPrototype",
2415 JSCLASS_HAS_CACHED_PROTO(JSProto_TypedArray
),
2417 &TypedArrayObjectSharedTypedArrayPrototypeClassSpec
,
2422 // This default implementation is only valid for integer types less
2423 // than 32-bits in size.
2424 template <typename NativeType
>
2425 bool TypedArrayObjectTemplate
<NativeType
>::getElementPure(
2426 TypedArrayObject
* tarray
, size_t index
, Value
* vp
) {
2427 static_assert(sizeof(NativeType
) < 4,
2428 "this method must only handle NativeType values that are "
2429 "always exact int32_t values");
2431 *vp
= Int32Value(getIndex(tarray
, index
));
2435 // We need to specialize for floats and other integer types.
2437 bool TypedArrayObjectTemplate
<int32_t>::getElementPure(TypedArrayObject
* tarray
,
2440 *vp
= Int32Value(getIndex(tarray
, index
));
2445 bool TypedArrayObjectTemplate
<uint32_t>::getElementPure(
2446 TypedArrayObject
* tarray
, size_t index
, Value
* vp
) {
2447 uint32_t val
= getIndex(tarray
, index
);
2448 *vp
= NumberValue(val
);
2453 bool TypedArrayObjectTemplate
<float>::getElementPure(TypedArrayObject
* tarray
,
2454 size_t index
, Value
* vp
) {
2455 float val
= getIndex(tarray
, index
);
2459 * Doubles in typed arrays could be typed-punned arrays of integers. This
2460 * could allow user code to break the engine-wide invariant that only
2461 * canonical nans are stored into jsvals, which means user code could
2462 * confuse the engine into interpreting a double-typed jsval as an
2463 * object-typed jsval.
2465 * This could be removed for platforms/compilers known to convert a 32-bit
2466 * non-canonical nan to a 64-bit canonical nan.
2468 *vp
= JS::CanonicalizedDoubleValue(dval
);
2473 bool TypedArrayObjectTemplate
<double>::getElementPure(TypedArrayObject
* tarray
,
2474 size_t index
, Value
* vp
) {
2475 double val
= getIndex(tarray
, index
);
2478 * Doubles in typed arrays could be typed-punned arrays of integers. This
2479 * could allow user code to break the engine-wide invariant that only
2480 * canonical nans are stored into jsvals, which means user code could
2481 * confuse the engine into interpreting a double-typed jsval as an
2482 * object-typed jsval.
2484 *vp
= JS::CanonicalizedDoubleValue(val
);
2489 bool TypedArrayObjectTemplate
<int64_t>::getElementPure(TypedArrayObject
* tarray
,
2496 bool TypedArrayObjectTemplate
<uint64_t>::getElementPure(
2497 TypedArrayObject
* tarray
, size_t index
, Value
* vp
) {
2500 } /* anonymous namespace */
2504 template <typename NativeType
>
2505 bool TypedArrayObjectTemplate
<NativeType
>::getElement(JSContext
* cx
,
2506 TypedArrayObject
* tarray
,
2508 MutableHandleValue val
) {
2509 MOZ_ALWAYS_TRUE(getElementPure(tarray
, index
, val
.address()));
2514 bool TypedArrayObjectTemplate
<int64_t>::getElement(JSContext
* cx
,
2515 TypedArrayObject
* tarray
,
2517 MutableHandleValue val
) {
2518 int64_t n
= getIndex(tarray
, index
);
2519 BigInt
* res
= BigInt::createFromInt64(cx
, n
);
2528 bool TypedArrayObjectTemplate
<uint64_t>::getElement(JSContext
* cx
,
2529 TypedArrayObject
* tarray
,
2531 MutableHandleValue val
) {
2532 uint64_t n
= getIndex(tarray
, index
);
2533 BigInt
* res
= BigInt::createFromUint64(cx
, n
);
2540 } /* anonymous namespace */
2545 bool TypedArrayObject::getElement
<CanGC
>(JSContext
* cx
, size_t index
,
2546 MutableHandleValue val
) {
2548 #define GET_ELEMENT(_, T, N) \
2550 return TypedArrayObjectTemplate<T>::getElement(cx, this, index, val);
2551 JS_FOR_EACH_TYPED_ARRAY(GET_ELEMENT
)
2553 case Scalar::MaxTypedArrayViewType
:
2555 case Scalar::Simd128
:
2559 MOZ_CRASH("Unknown TypedArray type");
2563 bool TypedArrayObject::getElement
<NoGC
>(
2564 JSContext
* cx
, size_t index
,
2565 typename MaybeRooted
<Value
, NoGC
>::MutableHandleType vp
) {
2566 return getElementPure(index
, vp
.address());
2571 bool TypedArrayObject::getElementPure(size_t index
, Value
* vp
) {
2573 #define GET_ELEMENT_PURE(_, T, N) \
2575 return TypedArrayObjectTemplate<T>::getElementPure(this, index, vp);
2576 JS_FOR_EACH_TYPED_ARRAY(GET_ELEMENT_PURE
)
2578 case Scalar::MaxTypedArrayViewType
:
2580 case Scalar::Simd128
:
2584 MOZ_CRASH("Unknown TypedArray type");
2588 bool TypedArrayObject::getElements(JSContext
* cx
,
2589 Handle
<TypedArrayObject
*> tarray
,
2591 size_t length
= tarray
->length().valueOr(0);
2592 MOZ_ASSERT_IF(length
> 0, !tarray
->hasDetachedBuffer());
2594 switch (tarray
->type()) {
2595 #define GET_ELEMENTS(_, T, N) \
2597 for (size_t i = 0; i < length; ++i, ++vp) { \
2598 if (!TypedArrayObjectTemplate<T>::getElement( \
2599 cx, tarray, i, MutableHandleValue::fromMarkedLocation(vp))) { \
2604 JS_FOR_EACH_TYPED_ARRAY(GET_ELEMENTS
)
2606 case Scalar::MaxTypedArrayViewType
:
2608 case Scalar::Simd128
:
2612 MOZ_CRASH("Unknown TypedArray type");
2620 * TypedArrayObject boilerplate
2623 static const JSClassOps TypedArrayClassOps
= {
2624 nullptr, // addProperty
2625 nullptr, // delProperty
2626 nullptr, // enumerate
2627 nullptr, // newEnumerate
2629 nullptr, // mayResolve
2630 FixedLengthTypedArrayObject::finalize
, // finalize
2632 nullptr, // construct
2633 ArrayBufferViewObject::trace
, // trace
2636 static const JSClassOps ResizableTypedArrayClassOps
= {
2637 nullptr, // addProperty
2638 nullptr, // delProperty
2639 nullptr, // enumerate
2640 nullptr, // newEnumerate
2642 nullptr, // mayResolve
2643 nullptr, // finalize
2645 nullptr, // construct
2646 ArrayBufferViewObject::trace
, // trace
2649 static const ClassExtension TypedArrayClassExtension
= {
2650 FixedLengthTypedArrayObject::objectMoved
, // objectMovedOp
2653 static const JSPropertySpec
2654 static_prototype_properties
[Scalar::MaxTypedArrayViewType
][2] = {
2655 #define IMPL_TYPED_ARRAY_PROPERTIES(ExternalType, NativeType, Name) \
2657 JS_INT32_PS("BYTES_PER_ELEMENT", \
2658 TypedArrayObjectTemplate<NativeType>::BYTES_PER_ELEMENT, \
2659 JSPROP_READONLY | JSPROP_PERMANENT), \
2663 JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_PROPERTIES
)
2664 #undef IMPL_TYPED_ARRAY_PROPERTIES
2667 static const JSFunctionSpec uint8array_static_methods
[] = {
2668 JS_FN("fromHex", uint8array_fromHex
, 1, 0),
2672 static const JSFunctionSpec uint8array_methods
[] = {
2673 JS_FN("setFromHex", uint8array_setFromHex
, 1, 0),
2674 JS_FN("toHex", uint8array_toHex
, 0, 0),
2678 static constexpr const JSFunctionSpec
* TypedArrayStaticMethods(
2679 Scalar::Type type
) {
2680 if (type
== Scalar::Uint8
) {
2681 return uint8array_static_methods
;
2686 static constexpr const JSFunctionSpec
* TypedArrayMethods(Scalar::Type type
) {
2687 if (type
== Scalar::Uint8
) {
2688 return uint8array_methods
;
2693 static const ClassSpec
2694 TypedArrayObjectClassSpecs
[Scalar::MaxTypedArrayViewType
] = {
2695 #define IMPL_TYPED_ARRAY_CLASS_SPEC(ExternalType, NativeType, Name) \
2697 TypedArrayObjectTemplate<NativeType>::createConstructor, \
2698 TypedArrayObjectTemplate<NativeType>::createPrototype, \
2699 TypedArrayStaticMethods(Scalar::Type::Name), \
2700 static_prototype_properties[Scalar::Type::Name], \
2701 TypedArrayMethods(Scalar::Type::Name), \
2702 static_prototype_properties[Scalar::Type::Name], \
2704 JSProto_TypedArray, \
2707 JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_CLASS_SPEC
)
2708 #undef IMPL_TYPED_ARRAY_CLASS_SPEC
2711 // Class definitions for fixed length and resizable typed arrays. Stored into a
2712 // 2-dimensional array to ensure the classes are in contiguous memory.
2713 const JSClass
TypedArrayObject::anyClasses
[2][Scalar::MaxTypedArrayViewType
] = {
2714 // Class definitions for fixed length typed arrays.
2716 #define IMPL_TYPED_ARRAY_CLASS(ExternalType, NativeType, Name) \
2719 JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) | \
2720 JSCLASS_HAS_CACHED_PROTO(JSProto_##Name##Array) | \
2721 JSCLASS_DELAY_METADATA_BUILDER | JSCLASS_SKIP_NURSERY_FINALIZE | \
2722 JSCLASS_BACKGROUND_FINALIZE, \
2723 &TypedArrayClassOps, \
2724 &TypedArrayObjectClassSpecs[Scalar::Type::Name], \
2725 &TypedArrayClassExtension, \
2728 JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_CLASS
)
2729 #undef IMPL_TYPED_ARRAY_CLASS
2732 // Class definitions for resizable typed arrays.
2734 #define IMPL_TYPED_ARRAY_CLASS(ExternalType, NativeType, Name) \
2737 JSCLASS_HAS_RESERVED_SLOTS(ResizableTypedArrayObject::RESERVED_SLOTS) | \
2738 JSCLASS_HAS_CACHED_PROTO(JSProto_##Name##Array) | \
2739 JSCLASS_DELAY_METADATA_BUILDER, \
2740 &ResizableTypedArrayClassOps, \
2741 &TypedArrayObjectClassSpecs[Scalar::Type::Name], \
2742 JS_NULL_CLASS_EXT, \
2745 JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_CLASS
)
2746 #undef IMPL_TYPED_ARRAY_CLASS
2751 &TypedArrayObject::fixedLengthClasses
)[Scalar::MaxTypedArrayViewType
] =
2752 TypedArrayObject::anyClasses
[0];
2755 &TypedArrayObject::resizableClasses
)[Scalar::MaxTypedArrayViewType
] =
2756 TypedArrayObject::anyClasses
[1];
2758 // The various typed array prototypes are supposed to 1) be normal objects,
2759 // 2) stringify to "[object <name of constructor>]", and 3) (Gecko-specific)
2760 // be xrayable. The first and second requirements mandate (in the absence of
2761 // @@toStringTag) a custom class. The third requirement mandates that each
2762 // prototype's class have the relevant typed array's cached JSProtoKey in them.
2763 // Thus we need one class with cached prototype per kind of typed array, with a
2764 // delegated ClassSpec.
2766 // Actually ({}).toString.call(Uint8Array.prototype) should throw, because
2767 // Uint8Array.prototype lacks the the typed array internal slots. (Same as
2768 // with %TypedArray%.prototype.) It's not clear this is desirable (see
2769 // above), but it's what we've always done, so keep doing it till we
2770 // implement @@toStringTag or ES6 changes.
2771 const JSClass
TypedArrayObject::protoClasses
[Scalar::MaxTypedArrayViewType
] = {
2772 #define IMPL_TYPED_ARRAY_PROTO_CLASS(ExternalType, NativeType, Name) \
2774 #Name "Array.prototype", \
2775 JSCLASS_HAS_CACHED_PROTO(JSProto_##Name##Array), \
2776 JS_NULL_CLASS_OPS, \
2777 &TypedArrayObjectClassSpecs[Scalar::Type::Name], \
2780 JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_PROTO_CLASS
)
2781 #undef IMPL_TYPED_ARRAY_PROTO_CLASS
2785 bool TypedArrayObject::isOriginalLengthGetter(Native native
) {
2786 return native
== TypedArray_lengthGetter
;
2790 bool TypedArrayObject::isOriginalByteOffsetGetter(Native native
) {
2791 return native
== TypedArray_byteOffsetGetter
;
2795 bool TypedArrayObject::isOriginalByteLengthGetter(Native native
) {
2796 return native
== TypedArray_byteLengthGetter
;
2799 bool js::IsTypedArrayConstructor(const JSObject
* obj
) {
2800 #define CHECK_TYPED_ARRAY_CONSTRUCTOR(_, T, N) \
2801 if (IsNativeFunction(obj, TypedArrayObjectTemplate<T>::class_constructor)) { \
2804 JS_FOR_EACH_TYPED_ARRAY(CHECK_TYPED_ARRAY_CONSTRUCTOR
)
2805 #undef CHECK_TYPED_ARRAY_CONSTRUCTOR
2809 bool js::IsTypedArrayConstructor(HandleValue v
, Scalar::Type type
) {
2810 return IsNativeFunction(v
, TypedArrayConstructorNative(type
));
2813 JSNative
js::TypedArrayConstructorNative(Scalar::Type type
) {
2814 #define TYPED_ARRAY_CONSTRUCTOR_NATIVE(_, T, N) \
2815 if (type == Scalar::N) { \
2816 return TypedArrayObjectTemplate<T>::class_constructor; \
2818 JS_FOR_EACH_TYPED_ARRAY(TYPED_ARRAY_CONSTRUCTOR_NATIVE
)
2819 #undef TYPED_ARRAY_CONSTRUCTOR_NATIVE
2821 MOZ_CRASH("unexpected typed array type");
2824 bool js::IsBufferSource(JSObject
* object
, SharedMem
<uint8_t*>* dataPointer
,
2825 size_t* byteLength
) {
2826 if (object
->is
<TypedArrayObject
>()) {
2827 TypedArrayObject
& view
= object
->as
<TypedArrayObject
>();
2828 *dataPointer
= view
.dataPointerEither().cast
<uint8_t*>();
2829 *byteLength
= view
.byteLength().valueOr(0);
2833 if (object
->is
<DataViewObject
>()) {
2834 DataViewObject
& view
= object
->as
<DataViewObject
>();
2835 *dataPointer
= view
.dataPointerEither().cast
<uint8_t*>();
2836 *byteLength
= view
.byteLength().valueOr(0);
2840 if (object
->is
<ArrayBufferObject
>()) {
2841 ArrayBufferObject
& buffer
= object
->as
<ArrayBufferObject
>();
2842 *dataPointer
= buffer
.dataPointerShared();
2843 *byteLength
= buffer
.byteLength();
2847 if (object
->is
<SharedArrayBufferObject
>()) {
2848 SharedArrayBufferObject
& buffer
= object
->as
<SharedArrayBufferObject
>();
2849 *dataPointer
= buffer
.dataPointerShared();
2850 *byteLength
= buffer
.byteLength();
2857 template <typename CharT
>
2858 static inline bool StringIsInfinity(mozilla::Range
<const CharT
> s
) {
2859 static constexpr std::string_view Infinity
= "Infinity";
2861 // Compilers optimize this to one |cmp| instruction on x64 resp. two for x86,
2862 // when the input is a Latin-1 string, because the string "Infinity" is
2863 // exactly eight characters long, so it can be represented as a single uint64
2865 return s
.length() == Infinity
.length() &&
2866 EqualChars(s
.begin().get(), Infinity
.data(), Infinity
.length());
2869 template <typename CharT
>
2870 static inline bool StringIsNaN(mozilla::Range
<const CharT
> s
) {
2871 static constexpr std::string_view NaN
= "NaN";
2873 // "NaN" is not as nicely optimizable as "Infinity", but oh well.
2874 return s
.length() == NaN
.length() &&
2875 EqualChars(s
.begin().get(), NaN
.data(), NaN
.length());
2878 template <typename CharT
>
2879 static mozilla::Maybe
<uint64_t> StringToTypedArrayIndexSlow(
2880 mozilla::Range
<const CharT
> s
) {
2881 const mozilla::RangedPtr
<const CharT
> start
= s
.begin();
2882 const mozilla::RangedPtr
<const CharT
> end
= s
.end();
2884 const CharT
* actualEnd
;
2885 double result
= js_strtod(start
.get(), end
.get(), &actualEnd
);
2887 // The complete string must have been parsed.
2888 if (actualEnd
!= end
.get()) {
2889 return mozilla::Nothing();
2892 // Now convert it back to a string.
2895 const char* cstr
= js::NumberToCString(&cbuf
, result
, &cstrlen
);
2898 // Both strings must be equal for a canonical numeric index string.
2899 if (s
.length() != cstrlen
|| !EqualChars(start
.get(), cstr
, cstrlen
)) {
2900 return mozilla::Nothing();
2903 // Directly perform IsInteger() check and encode negative and non-integer
2905 // See 9.4.5.2 [[HasProperty]], steps 3.b.iii and 3.b.v.
2906 // See 9.4.5.3 [[DefineOwnProperty]], steps 3.b.i and 3.b.iii.
2907 // See 9.4.5.8 IntegerIndexedElementGet, steps 5 and 8.
2908 // See 9.4.5.9 IntegerIndexedElementSet, steps 6 and 9.
2909 if (result
< 0 || !IsInteger(result
)) {
2910 return mozilla::Some(UINT64_MAX
);
2913 // Anything equals-or-larger than 2^53 is definitely OOB, encode it
2914 // accordingly so that the cast to uint64_t is well defined.
2915 if (result
>= DOUBLE_INTEGRAL_PRECISION_LIMIT
) {
2916 return mozilla::Some(UINT64_MAX
);
2919 // The string is an actual canonical numeric index.
2920 return mozilla::Some(result
);
2923 template <typename CharT
>
2924 mozilla::Maybe
<uint64_t> js::StringToTypedArrayIndex(
2925 mozilla::Range
<const CharT
> s
) {
2926 mozilla::RangedPtr
<const CharT
> cp
= s
.begin();
2927 const mozilla::RangedPtr
<const CharT
> end
= s
.end();
2929 MOZ_ASSERT(cp
< end
, "caller must check for empty strings");
2931 bool negative
= false;
2935 return mozilla::Nothing();
2939 if (!IsAsciiDigit(*cp
)) {
2940 // Check for "NaN", "Infinity", or "-Infinity".
2941 if ((!negative
&& StringIsNaN
<CharT
>({cp
, end
})) ||
2942 StringIsInfinity
<CharT
>({cp
, end
})) {
2943 return mozilla::Some(UINT64_MAX
);
2945 return mozilla::Nothing();
2948 uint32_t digit
= AsciiDigitToNumber(*cp
++);
2950 // Don't allow leading zeros.
2951 if (digit
== 0 && cp
!= end
) {
2952 // The string may be of the form "0.xyz". The exponent form isn't possible
2953 // when the string starts with "0".
2955 return StringToTypedArrayIndexSlow(s
);
2957 return mozilla::Nothing();
2960 uint64_t index
= digit
;
2962 for (; cp
< end
; cp
++) {
2963 if (!IsAsciiDigit(*cp
)) {
2964 // Take the slow path when the string has fractional parts or an exponent.
2965 if (*cp
== '.' || *cp
== 'e') {
2966 return StringToTypedArrayIndexSlow(s
);
2968 return mozilla::Nothing();
2971 digit
= AsciiDigitToNumber(*cp
);
2974 uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT
) < (UINT64_MAX
- 10) / 10,
2975 "2^53 is way below UINT64_MAX, so |10 * index + digit| can't overflow");
2977 index
= 10 * index
+ digit
;
2979 // Also take the slow path when the string is larger-or-equals 2^53.
2980 if (index
>= uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT
)) {
2981 return StringToTypedArrayIndexSlow(s
);
2986 return mozilla::Some(UINT64_MAX
);
2988 return mozilla::Some(index
);
2991 template mozilla::Maybe
<uint64_t> js::StringToTypedArrayIndex(
2992 mozilla::Range
<const char16_t
> s
);
2994 template mozilla::Maybe
<uint64_t> js::StringToTypedArrayIndex(
2995 mozilla::Range
<const Latin1Char
> s
);
2997 bool js::SetTypedArrayElement(JSContext
* cx
, Handle
<TypedArrayObject
*> obj
,
2998 uint64_t index
, HandleValue v
,
2999 ObjectOpResult
& result
) {
3000 switch (obj
->type()) {
3001 #define SET_TYPED_ARRAY_ELEMENT(_, T, N) \
3003 return TypedArrayObjectTemplate<T>::setElement(cx, obj, index, v, result);
3004 JS_FOR_EACH_TYPED_ARRAY(SET_TYPED_ARRAY_ELEMENT
)
3005 #undef SET_TYPED_ARRAY_ELEMENT
3006 case Scalar::MaxTypedArrayViewType
:
3008 case Scalar::Simd128
:
3012 MOZ_CRASH("Unsupported TypedArray type");
3015 bool js::SetTypedArrayElementOutOfBounds(JSContext
* cx
,
3016 Handle
<TypedArrayObject
*> obj
,
3017 uint64_t index
, HandleValue v
,
3018 ObjectOpResult
& result
) {
3019 // This method is only called for non-existent properties, which means any
3020 // absent indexed properties must be out of range. Unless the typed array is
3021 // backed by a growable SharedArrayBuffer, in which case another thread may
3022 // have grown the buffer.
3023 MOZ_ASSERT(index
>= obj
->length().valueOr(0) ||
3024 (obj
->isSharedMemory() && obj
->bufferShared()->isGrowable()));
3026 // The following steps refer to 10.4.5.16 TypedArraySetElement.
3029 RootedValue
converted(cx
);
3030 if (!obj
->convertValue(cx
, v
, &converted
)) {
3035 if (index
< obj
->length().valueOr(0)) {
3036 // Side-effects when converting the value may have put the index in-bounds
3037 // when the backing buffer is resizable.
3038 MOZ_ASSERT(obj
->hasResizableBuffer());
3039 return SetTypedArrayElement(cx
, obj
, index
, converted
, result
);
3043 return result
.succeed();
3046 // ES2021 draft rev b3f9b5089bcc3ddd8486379015cd11eb1427a5eb
3047 // 9.4.5.3 [[DefineOwnProperty]], step 3.b.
3048 bool js::DefineTypedArrayElement(JSContext
* cx
, Handle
<TypedArrayObject
*> obj
,
3050 Handle
<PropertyDescriptor
> desc
,
3051 ObjectOpResult
& result
) {
3052 // These are all substeps of 3.b.
3055 if (index
>= obj
->length().valueOr(0)) {
3056 if (obj
->hasDetachedBuffer()) {
3057 return result
.fail(JSMSG_TYPED_ARRAY_DETACHED
);
3059 return result
.fail(JSMSG_DEFINE_BAD_INDEX
);
3063 if (desc
.isAccessorDescriptor()) {
3064 return result
.fail(JSMSG_CANT_REDEFINE_PROP
);
3068 if (desc
.hasConfigurable() && !desc
.configurable()) {
3069 return result
.fail(JSMSG_CANT_REDEFINE_PROP
);
3073 if (desc
.hasEnumerable() && !desc
.enumerable()) {
3074 return result
.fail(JSMSG_CANT_REDEFINE_PROP
);
3078 if (desc
.hasWritable() && !desc
.writable()) {
3079 return result
.fail(JSMSG_CANT_REDEFINE_PROP
);
3083 if (desc
.hasValue()) {
3084 switch (obj
->type()) {
3085 #define DEFINE_TYPED_ARRAY_ELEMENT(_, T, N) \
3087 return TypedArrayObjectTemplate<T>::setElement(cx, obj, index, \
3088 desc.value(), result);
3089 JS_FOR_EACH_TYPED_ARRAY(DEFINE_TYPED_ARRAY_ELEMENT
)
3090 #undef DEFINE_TYPED_ARRAY_ELEMENT
3091 case Scalar::MaxTypedArrayViewType
:
3093 case Scalar::Simd128
:
3097 MOZ_CRASH("Unsupported TypedArray type");
3101 return result
.succeed();
3104 template <typename T
, typename U
>
3105 static constexpr typename
std::enable_if_t
<std::is_unsigned_v
<T
>, U
>
3106 UnsignedSortValue(U val
) {
3110 template <typename T
, typename U
>
3112 typename
std::enable_if_t
<std::is_integral_v
<T
> && std::is_signed_v
<T
>, U
>
3113 UnsignedSortValue(U val
) {
3115 return val
^ static_cast<U
>(std::numeric_limits
<T
>::min());
3118 template <typename T
, typename UnsignedT
>
3120 typename
std::enable_if_t
<std::is_floating_point_v
<T
>, UnsignedT
>
3121 UnsignedSortValue(UnsignedT val
) {
3122 // Flip sign bit for positive numbers; flip all bits for negative numbers,
3123 // except negative NaNs.
3124 using FloatingPoint
= mozilla::FloatingPoint
<T
>;
3125 static_assert(std::is_same_v
<typename
FloatingPoint::Bits
, UnsignedT
>,
3126 "FloatingPoint::Bits matches the unsigned int representation");
3128 // FF80'0000 is negative infinity, (FF80'0000, FFFF'FFFF] are all NaNs with
3129 // the sign-bit set (and the equivalent holds for double values). So any value
3130 // larger than negative infinity is a negative NaN.
3131 constexpr UnsignedT NegativeInfinity
=
3132 FloatingPoint::kSignBit
| FloatingPoint::kExponentBits
;
3133 if (val
> NegativeInfinity
) {
3136 if (val
& FloatingPoint::kSignBit
) {
3139 return val
^ FloatingPoint::kSignBit
;
3142 template <typename T
>
3143 static typename
std::enable_if_t
<std::is_integral_v
<T
> ||
3144 std::is_same_v
<T
, uint8_clamped
>>
3145 TypedArrayStdSort(SharedMem
<void*> data
, size_t length
) {
3146 T
* unwrapped
= data
.cast
<T
*>().unwrapUnshared();
3147 std::sort(unwrapped
, unwrapped
+ length
);
3150 template <typename T
>
3151 static typename
std::enable_if_t
<std::is_floating_point_v
<T
>> TypedArrayStdSort(
3152 SharedMem
<void*> data
, size_t length
) {
3153 // Sort on the unsigned representation for performance reasons.
3155 typename
mozilla::UnsignedStdintTypeForSize
<sizeof(T
)>::Type
;
3156 UnsignedT
* unwrapped
= data
.cast
<UnsignedT
*>().unwrapUnshared();
3157 std::sort(unwrapped
, unwrapped
+ length
, [](UnsignedT x
, UnsignedT y
) {
3158 constexpr auto SortValue
= UnsignedSortValue
<T
, UnsignedT
>;
3159 return SortValue(x
) < SortValue(y
);
3163 template <typename T
, typename Ops
>
3164 static typename
std::enable_if_t
<std::is_same_v
<Ops
, UnsharedOps
>, bool>
3165 TypedArrayStdSort(JSContext
* cx
, TypedArrayObject
* typedArray
, size_t length
) {
3166 TypedArrayStdSort
<T
>(typedArray
->dataPointerEither(), length
);
3170 template <typename T
, typename Ops
>
3171 static typename
std::enable_if_t
<std::is_same_v
<Ops
, SharedOps
>, bool>
3172 TypedArrayStdSort(JSContext
* cx
, TypedArrayObject
* typedArray
, size_t length
) {
3173 // Always create a copy when sorting shared memory backed typed arrays to
3174 // ensure concurrent write accesses doesn't lead to UB when calling std::sort.
3175 auto ptr
= cx
->make_pod_array
<T
>(length
);
3179 SharedMem
<T
*> unshared
= SharedMem
<T
*>::unshared(ptr
.get());
3180 SharedMem
<T
*> data
= typedArray
->dataPointerShared().cast
<T
*>();
3182 Ops::podCopy(unshared
, data
, length
);
3184 TypedArrayStdSort
<T
>(unshared
.template cast
<void*>(), length
);
3186 Ops::podCopy(data
, unshared
, length
);
3191 template <typename T
, typename Ops
>
3192 static bool TypedArrayCountingSort(JSContext
* cx
, TypedArrayObject
* typedArray
,
3194 static_assert(std::is_integral_v
<T
> || std::is_same_v
<T
, uint8_clamped
>,
3195 "Counting sort expects integral array elements");
3197 // Determined by performance testing.
3199 return TypedArrayStdSort
<T
, Ops
>(cx
, typedArray
, length
);
3202 // Map signed values onto the unsigned range when storing in buffer.
3204 typename
mozilla::UnsignedStdintTypeForSize
<sizeof(T
)>::Type
;
3205 constexpr T min
= std::numeric_limits
<T
>::min();
3207 constexpr size_t InlineStorage
= sizeof(T
) == 1 ? 256 : 0;
3208 Vector
<size_t, InlineStorage
> buffer(cx
);
3209 if (!buffer
.resize(size_t(std::numeric_limits
<UnsignedT
>::max()) + 1)) {
3213 SharedMem
<T
*> data
= typedArray
->dataPointerEither().cast
<T
*>();
3215 // Populate the buffer.
3216 for (size_t i
= 0; i
< length
; i
++) {
3217 T val
= Ops::load(data
+ i
);
3218 buffer
[UnsignedT(val
- min
)]++;
3221 // Traverse the buffer in order and write back elements to array.
3222 UnsignedT val
= UnsignedT(-1); // intentional overflow on first increment
3223 for (size_t i
= 0; i
< length
;) {
3224 // Invariant: sum(buffer[val:]) == length-i
3230 for (; j
> 0; j
--) {
3231 Ops::store(data
+ i
++, T(val
+ min
));
3238 template <typename T
, typename U
, typename Ops
>
3239 static void SortByColumn(SharedMem
<U
*> data
, size_t length
, SharedMem
<U
*> aux
,
3241 static_assert(std::is_unsigned_v
<U
>, "SortByColumn sorts on unsigned values");
3242 static_assert(std::is_same_v
<Ops
, UnsharedOps
>,
3243 "SortByColumn only works on unshared data");
3245 // |counts| is used to compute the starting index position for each key.
3246 // Letting counts[0] always be 0, simplifies the transform step below.
3249 // Computing frequency counts for the input [1 2 1] gives:
3250 // 0 1 2 3 ... (keys)
3251 // 0 0 2 1 (frequencies)
3253 // Transforming frequencies to indexes gives:
3254 // 0 1 2 3 ... (keys)
3255 // 0 0 2 3 (indexes)
3257 constexpr size_t R
= 256;
3259 // Initialize all entries to zero.
3260 size_t counts
[R
+ 1] = {};
3262 const auto ByteAtCol
= [col
](U x
) {
3263 U y
= UnsignedSortValue
<T
, U
>(x
);
3264 return static_cast<uint8_t>(y
>> (col
* 8));
3267 // Compute frequency counts.
3268 for (size_t i
= 0; i
< length
; i
++) {
3269 U val
= Ops::load(data
+ i
);
3270 uint8_t b
= ByteAtCol(val
);
3274 // Transform counts to indices.
3275 std::partial_sum(std::begin(counts
), std::end(counts
), std::begin(counts
));
3278 for (size_t i
= 0; i
< length
; i
++) {
3279 U val
= Ops::load(data
+ i
);
3280 uint8_t b
= ByteAtCol(val
);
3281 size_t j
= counts
[b
]++;
3282 MOZ_ASSERT(j
< length
,
3283 "index is in bounds when |data| can't be modified concurrently");
3284 UnsharedOps::store(aux
+ j
, val
);
3288 Ops::podCopy(data
, aux
, length
);
3291 template <typename T
, typename Ops
>
3292 static bool TypedArrayRadixSort(JSContext
* cx
, TypedArrayObject
* typedArray
,
3294 // Determined by performance testing.
3295 constexpr size_t StdSortMinCutoff
= sizeof(T
) == 2 ? 64 : 256;
3297 // Radix sort uses O(n) additional space, limit this space to 64 MB.
3298 constexpr size_t StdSortMaxCutoff
= (64 * 1024 * 1024) / sizeof(T
);
3300 if (length
<= StdSortMinCutoff
|| length
>= StdSortMaxCutoff
) {
3301 return TypedArrayStdSort
<T
, Ops
>(cx
, typedArray
, length
);
3304 if constexpr (sizeof(T
) == 2) {
3305 // Radix sort uses O(n) additional space, so when |n| reaches 2^16, switch
3306 // over to counting sort to limit the additional space needed to 2^16.
3307 constexpr size_t CountingSortMaxCutoff
= 65536;
3309 if (length
>= CountingSortMaxCutoff
) {
3310 return TypedArrayCountingSort
<T
, Ops
>(cx
, typedArray
, length
);
3315 typename
mozilla::UnsignedStdintTypeForSize
<sizeof(T
)>::Type
;
3317 auto ptr
= cx
->make_zeroed_pod_array
<UnsignedT
>(length
);
3321 SharedMem
<UnsignedT
*> aux
= SharedMem
<UnsignedT
*>::unshared(ptr
.get());
3323 SharedMem
<UnsignedT
*> data
=
3324 typedArray
->dataPointerEither().cast
<UnsignedT
*>();
3326 // Always create a copy when sorting shared memory backed typed arrays to
3327 // ensure concurrent write accesses don't lead to computing bad indices.
3328 SharedMem
<UnsignedT
*> unshared
;
3329 SharedMem
<UnsignedT
*> shared
;
3330 UniquePtr
<UnsignedT
[], JS::FreePolicy
> ptrUnshared
;
3331 if constexpr (std::is_same_v
<Ops
, SharedOps
>) {
3332 ptrUnshared
= cx
->make_pod_array
<UnsignedT
>(length
);
3336 unshared
= SharedMem
<UnsignedT
*>::unshared(ptrUnshared
.get());
3339 Ops::podCopy(unshared
, shared
, length
);
3344 for (uint8_t col
= 0; col
< sizeof(UnsignedT
); col
++) {
3345 SortByColumn
<T
, UnsignedT
, UnsharedOps
>(data
, length
, aux
, col
);
3348 if constexpr (std::is_same_v
<Ops
, SharedOps
>) {
3349 Ops::podCopy(shared
, unshared
, length
);
3355 using TypedArraySortFn
= bool (*)(JSContext
*, TypedArrayObject
*, size_t length
);
3357 template <typename T
, typename Ops
>
3358 static constexpr typename
std::enable_if_t
<sizeof(T
) == 1, TypedArraySortFn
>
3360 return TypedArrayCountingSort
<T
, Ops
>;
3363 template <typename T
, typename Ops
>
3364 static constexpr typename
std::enable_if_t
<sizeof(T
) == 2 || sizeof(T
) == 4,
3367 return TypedArrayRadixSort
<T
, Ops
>;
3370 template <typename T
, typename Ops
>
3371 static constexpr typename
std::enable_if_t
<sizeof(T
) == 8, TypedArraySortFn
>
3373 return TypedArrayStdSort
<T
, Ops
>;
3376 bool js::intrinsic_TypedArrayNativeSort(JSContext
* cx
, unsigned argc
,
3378 CallArgs args
= CallArgsFromVp(argc
, vp
);
3379 MOZ_ASSERT(args
.length() == 1);
3381 TypedArrayObject
* typedArray
=
3382 UnwrapAndDowncastValue
<TypedArrayObject
>(cx
, args
[0]);
3387 auto length
= typedArray
->length();
3388 MOZ_RELEASE_ASSERT(length
,
3389 "TypedArray is neither detached nor out-of-bounds");
3391 bool isShared
= typedArray
->isSharedMemory();
3392 switch (typedArray
->type()) {
3393 #define SORT(_, T, N) \
3396 if (!TypedArraySort<T, SharedOps>()(cx, typedArray, *length)) { \
3400 if (!TypedArraySort<T, UnsharedOps>()(cx, typedArray, *length)) { \
3405 JS_FOR_EACH_TYPED_ARRAY(SORT
)
3408 MOZ_CRASH("Unsupported TypedArray type");
3411 args
.rval().set(args
[0]);
3417 #define IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(ExternalType, NativeType, Name) \
3418 JS_PUBLIC_API JSObject* JS_New##Name##Array(JSContext* cx, \
3419 size_t nelements) { \
3420 return TypedArrayObjectTemplate<NativeType>::fromLength(cx, nelements); \
3423 JS_PUBLIC_API JSObject* JS_New##Name##ArrayFromArray(JSContext* cx, \
3424 HandleObject other) { \
3425 return TypedArrayObjectTemplate<NativeType>::fromArray(cx, other); \
3428 JS_PUBLIC_API JSObject* JS_New##Name##ArrayWithBuffer( \
3429 JSContext* cx, HandleObject arrayBuffer, size_t byteOffset, \
3431 return TypedArrayObjectTemplate<NativeType>::fromBuffer( \
3432 cx, arrayBuffer, byteOffset, length); \
3435 JS_PUBLIC_API JSObject* js::Unwrap##Name##Array(JSObject* obj) { \
3436 obj = obj->maybeUnwrapIf<TypedArrayObject>(); \
3440 const JSClass* clasp = obj->getClass(); \
3441 if (clasp != FixedLengthTypedArrayObjectTemplate< \
3442 NativeType>::instanceClass() && \
3444 ResizableTypedArrayObjectTemplate<NativeType>::instanceClass()) { \
3450 JS_PUBLIC_API ExternalType* JS_Get##Name##ArrayLengthAndData( \
3451 JSObject* obj, size_t* length, bool* isSharedMemory, \
3452 const JS::AutoRequireNoGC& nogc) { \
3453 TypedArrayObject* tarr = obj->maybeUnwrapAs<TypedArrayObject>(); \
3457 mozilla::Span<ExternalType> span = \
3458 JS::TypedArray<JS::Scalar::Name>::fromObject(tarr).getData( \
3459 isSharedMemory, nogc); \
3460 *length = span.Length(); \
3461 return span.data(); \
3464 JS_PUBLIC_API ExternalType* JS_Get##Name##ArrayData( \
3465 JSObject* obj, bool* isSharedMemory, const JS::AutoRequireNoGC& nogc) { \
3467 return JS_Get##Name##ArrayLengthAndData(obj, &length, isSharedMemory, \
3470 JS_PUBLIC_API JSObject* JS_GetObjectAs##Name##Array( \
3471 JSObject* obj, size_t* length, bool* isShared, ExternalType** data) { \
3472 obj = js::Unwrap##Name##Array(obj); \
3476 TypedArrayObject* tarr = &obj->as<TypedArrayObject>(); \
3477 *length = tarr->length().valueOr(0); \
3478 *isShared = tarr->isSharedMemory(); \
3479 *data = static_cast<ExternalType*>(tarr->dataPointerEither().unwrap( \
3480 /*safe - caller sees isShared flag*/)); \
3484 JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS
)
3485 #undef IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS
3487 JS_PUBLIC_API
bool JS_IsTypedArrayObject(JSObject
* obj
) {
3488 return obj
->canUnwrapAs
<TypedArrayObject
>();
3491 JS_PUBLIC_API
size_t JS_GetTypedArrayLength(JSObject
* obj
) {
3492 TypedArrayObject
* tarr
= obj
->maybeUnwrapAs
<TypedArrayObject
>();
3496 return tarr
->length().valueOr(0);
3499 JS_PUBLIC_API
size_t JS_GetTypedArrayByteOffset(JSObject
* obj
) {
3500 TypedArrayObject
* tarr
= obj
->maybeUnwrapAs
<TypedArrayObject
>();
3504 return tarr
->byteOffset().valueOr(0);
3507 JS_PUBLIC_API
size_t JS_GetTypedArrayByteLength(JSObject
* obj
) {
3508 TypedArrayObject
* tarr
= obj
->maybeUnwrapAs
<TypedArrayObject
>();
3512 return tarr
->byteLength().valueOr(0);
3515 JS_PUBLIC_API
bool JS_GetTypedArraySharedness(JSObject
* obj
) {
3516 TypedArrayObject
* tarr
= obj
->maybeUnwrapAs
<TypedArrayObject
>();
3520 return tarr
->isSharedMemory();
3523 JS_PUBLIC_API
JS::Scalar::Type
JS_GetArrayBufferViewType(JSObject
* obj
) {
3524 ArrayBufferViewObject
* view
= obj
->maybeUnwrapAs
<ArrayBufferViewObject
>();
3526 return Scalar::MaxTypedArrayViewType
;
3529 if (view
->is
<TypedArrayObject
>()) {
3530 return view
->as
<TypedArrayObject
>().type();
3532 if (view
->is
<DataViewObject
>()) {
3533 return Scalar::MaxTypedArrayViewType
;
3535 MOZ_CRASH("invalid ArrayBufferView type");
3538 JS_PUBLIC_API
size_t JS_MaxMovableTypedArraySize() {
3539 return FixedLengthTypedArrayObject::INLINE_BUFFER_LIMIT
;
3544 const JSClass
* const TypedArray_base::fixedLengthClasses
=
3545 TypedArrayObject::fixedLengthClasses
;
3546 const JSClass
* const TypedArray_base::resizableClasses
=
3547 TypedArrayObject::resizableClasses
;
3549 #define INSTANTIATE(ExternalType, NativeType, Name) \
3550 template class TypedArray<JS::Scalar::Name>;
3551 JS_FOR_EACH_TYPED_ARRAY(INSTANTIATE
)
3554 JS::ArrayBufferOrView
JS::ArrayBufferOrView::unwrap(JSObject
* maybeWrapped
) {
3555 if (!maybeWrapped
) {
3556 return JS::ArrayBufferOrView(nullptr);
3558 auto* ab
= maybeWrapped
->maybeUnwrapIf
<ArrayBufferObjectMaybeShared
>();
3560 return ArrayBufferOrView::fromObject(ab
);
3563 return ArrayBufferView::unwrap(maybeWrapped
);
3566 bool JS::ArrayBufferOrView::isDetached() const {
3568 if (obj
->is
<ArrayBufferObjectMaybeShared
>()) {
3569 return obj
->as
<ArrayBufferObjectMaybeShared
>().isDetached();
3571 return obj
->as
<ArrayBufferViewObject
>().hasDetachedBuffer();
3575 bool JS::ArrayBufferOrView::isResizable() const {
3577 if (obj
->is
<ArrayBufferObjectMaybeShared
>()) {
3578 return obj
->as
<ArrayBufferObjectMaybeShared
>().isResizable();
3580 return obj
->as
<ArrayBufferViewObject
>().hasResizableBuffer();
3584 JS::TypedArray_base
JS::TypedArray_base::fromObject(JSObject
* unwrapped
) {
3585 if (unwrapped
&& unwrapped
->is
<TypedArrayObject
>()) {
3586 return TypedArray_base(unwrapped
);
3588 return TypedArray_base(nullptr);
3591 // Template getData function for TypedArrays, implemented here because
3592 // it requires internal APIs.
3593 template <JS::Scalar::Type EType
>
3594 typename
mozilla::Span
<typename TypedArray
<EType
>::DataType
>
3595 TypedArray
<EType
>::getData(bool* isSharedMemory
, const AutoRequireNoGC
&) {
3596 using ExternalType
= TypedArray
<EType
>::DataType
;
3600 TypedArrayObject
* tarr
= &obj
->as
<TypedArrayObject
>();
3602 *isSharedMemory
= tarr
->isSharedMemory();
3603 return {static_cast<ExternalType
*>(tarr
->dataPointerEither().unwrap(
3604 /*safe - caller sees isShared*/)),
3605 tarr
->length().valueOr(0)};
3608 // Force the method defined above to actually be instantianted in this
3609 // compilation unit and emitted into the object file, since otherwise a binary
3610 // could include the header file and emit an undefined symbol that would not be
3611 // satisfied by the linker. (This happens with opt gtest, at least. In a DEBUG
3612 // build, the header contains a call to this function so it will always be
3614 #define INSTANTIATE_GET_DATA(a, b, Name) \
3615 template mozilla::Span<typename TypedArray<JS::Scalar::Name>::DataType> \
3616 TypedArray<JS::Scalar::Name>::getData(bool* isSharedMemory, \
3617 const AutoRequireNoGC&);
3618 JS_FOR_EACH_TYPED_ARRAY(INSTANTIATE_GET_DATA
)
3619 #undef INSTANTIATE_GET_DATA
3621 } /* namespace JS */