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 #ifndef vm_TypedArrayObject_h
8 #define vm_TypedArrayObject_h
10 #include "mozilla/Maybe.h"
11 #include "mozilla/TextUtils.h"
13 #include "gc/AllocKind.h"
14 #include "gc/MaybeRooted.h"
16 #include "js/experimental/TypedData.h" // js::detail::TypedArrayLengthSlot
17 #include "js/ScalarType.h" // js::Scalar::Type
18 #include "vm/ArrayBufferObject.h"
19 #include "vm/ArrayBufferViewObject.h"
20 #include "vm/JSObject.h"
21 #include "vm/SharedArrayObject.h"
28 * The non-templated base class for the specific typed implementations.
29 * This class holds all the member variables that are used by
33 class TypedArrayObject
: public ArrayBufferViewObject
{
35 static_assert(js::detail::TypedArrayLengthSlot
== LENGTH_SLOT
,
36 "bad inlined constant in TypedData.h");
37 static_assert(js::detail::TypedArrayDataSlot
== DATA_SLOT
,
38 "bad inlined constant in TypedData.h");
40 static bool sameBuffer(Handle
<TypedArrayObject
*> a
,
41 Handle
<TypedArrayObject
*> b
) {
43 if (!a
->hasBuffer() || !b
->hasBuffer()) {
44 return a
.get() == b
.get();
48 if (a
->isSharedMemory() && b
->isSharedMemory()) {
49 return a
->bufferShared()->globalID() == b
->bufferShared()->globalID();
52 return a
->bufferEither() == b
->bufferEither();
55 static const JSClass anyClasses
[2][Scalar::MaxTypedArrayViewType
];
56 static const JSClass (&fixedLengthClasses
)[Scalar::MaxTypedArrayViewType
];
57 static const JSClass (&resizableClasses
)[Scalar::MaxTypedArrayViewType
];
58 static const JSClass protoClasses
[Scalar::MaxTypedArrayViewType
];
59 static const JSClass sharedTypedArrayPrototypeClass
;
61 static const JSClass
* protoClassForType(Scalar::Type type
) {
62 MOZ_ASSERT(type
< Scalar::MaxTypedArrayViewType
);
63 return &protoClasses
[type
];
66 inline Scalar::Type
type() const;
67 inline size_t bytesPerElement() const;
69 static bool ensureHasBuffer(JSContext
* cx
,
70 Handle
<TypedArrayObject
*> typedArray
);
74 * Return the current length, or |Nothing| if the TypedArray is detached or
77 mozilla::Maybe
<size_t> length() const {
78 return ArrayBufferViewObject::length();
82 * Return the current byteLength, or |Nothing| if the TypedArray is detached
85 mozilla::Maybe
<size_t> byteLength() const {
87 [this](size_t value
) { return value
* bytesPerElement(); });
90 // Self-hosted TypedArraySubarray function needs to read [[ByteOffset]], even
91 // when it's currently out-of-bounds.
92 size_t byteOffsetMaybeOutOfBounds() const {
93 // dataPointerOffset() returns the [[ByteOffset]] spec value, except when
94 // the buffer is detached. (bug 1840991)
95 return ArrayBufferViewObject::dataPointerOffset();
98 template <AllowGC allowGC
>
99 bool getElement(JSContext
* cx
, size_t index
,
100 typename MaybeRooted
<Value
, allowGC
>::MutableHandleType val
);
101 bool getElementPure(size_t index
, Value
* vp
);
104 * Copy |length| elements from this typed array to vp. vp must point to rooted
105 * memory. |length| must not exceed the typed array's current length.
107 static bool getElements(JSContext
* cx
, Handle
<TypedArrayObject
*> tarray
,
108 size_t length
, Value
* vp
);
110 static bool GetTemplateObjectForNative(JSContext
* cx
, Native native
,
111 const JS::HandleValueArray args
,
112 MutableHandleObject res
);
114 // Maximum allowed byte length for any typed array.
115 static constexpr size_t ByteLengthLimit
= ArrayBufferObject::ByteLengthLimit
;
117 static bool isOriginalLengthGetter(Native native
);
119 static bool isOriginalByteOffsetGetter(Native native
);
121 static bool isOriginalByteLengthGetter(Native native
);
123 /* Initialization bits */
125 static const JSFunctionSpec protoFunctions
[];
126 static const JSPropertySpec protoAccessors
[];
127 static const JSFunctionSpec staticFunctions
[];
128 static const JSPropertySpec staticProperties
[];
130 /* Accessors and functions */
132 static bool set(JSContext
* cx
, unsigned argc
, Value
* vp
);
133 static bool copyWithin(JSContext
* cx
, unsigned argc
, Value
* vp
);
135 bool convertValue(JSContext
* cx
, HandleValue v
,
136 MutableHandleValue result
) const;
139 static bool set_impl(JSContext
* cx
, const CallArgs
& args
);
140 static bool copyWithin_impl(JSContext
* cx
, const CallArgs
& args
);
143 class FixedLengthTypedArrayObject
: public TypedArrayObject
{
145 static constexpr size_t FIXED_DATA_START
= RESERVED_SLOTS
;
147 // For typed arrays which can store their data inline, the array buffer
148 // object is created lazily.
149 static constexpr uint32_t INLINE_BUFFER_LIMIT
=
150 (NativeObject::MAX_FIXED_SLOTS
- FIXED_DATA_START
) * sizeof(Value
);
152 inline gc::AllocKind
allocKindForTenure() const;
153 static inline gc::AllocKind
AllocKindForLazyBuffer(size_t nbytes
);
155 size_t byteOffset() const {
156 return ArrayBufferViewObject::byteOffsetSlotValue();
159 size_t byteLength() const { return length() * bytesPerElement(); }
161 size_t length() const { return ArrayBufferViewObject::lengthSlotValue(); }
163 bool hasInlineElements() const;
164 void setInlineElements();
165 uint8_t* elementsRaw() const {
166 return maybePtrFromReservedSlot
<uint8_t>(DATA_SLOT
);
168 uint8_t* elements() const {
169 assertZeroLengthArrayData();
170 return elementsRaw();
174 void assertZeroLengthArrayData() const;
176 void assertZeroLengthArrayData() const {};
179 static void finalize(JS::GCContext
* gcx
, JSObject
* obj
);
180 static size_t objectMoved(JSObject
* obj
, JSObject
* old
);
183 class ResizableTypedArrayObject
: public TypedArrayObject
{
185 static const uint8_t RESERVED_SLOTS
= RESIZABLE_RESERVED_SLOTS
;
188 extern TypedArrayObject
* NewTypedArrayWithTemplateAndLength(
189 JSContext
* cx
, HandleObject templateObj
, int32_t len
);
191 extern TypedArrayObject
* NewTypedArrayWithTemplateAndArray(
192 JSContext
* cx
, HandleObject templateObj
, HandleObject array
);
194 extern TypedArrayObject
* NewTypedArrayWithTemplateAndBuffer(
195 JSContext
* cx
, HandleObject templateObj
, HandleObject arrayBuffer
,
196 HandleValue byteOffset
, HandleValue length
);
198 extern TypedArrayObject
* NewUint8ArrayWithLength(
199 JSContext
* cx
, int32_t len
, gc::Heap heap
= gc::Heap::Default
);
201 inline bool IsFixedLengthTypedArrayClass(const JSClass
* clasp
) {
202 return std::begin(TypedArrayObject::fixedLengthClasses
) <= clasp
&&
203 clasp
< std::end(TypedArrayObject::fixedLengthClasses
);
206 inline bool IsResizableTypedArrayClass(const JSClass
* clasp
) {
207 return std::begin(TypedArrayObject::resizableClasses
) <= clasp
&&
208 clasp
< std::end(TypedArrayObject::resizableClasses
);
211 inline bool IsTypedArrayClass(const JSClass
* clasp
) {
212 MOZ_ASSERT(std::end(TypedArrayObject::fixedLengthClasses
) ==
213 std::begin(TypedArrayObject::resizableClasses
),
214 "TypedArray classes are in contiguous memory");
215 return std::begin(TypedArrayObject::fixedLengthClasses
) <= clasp
&&
216 clasp
< std::end(TypedArrayObject::resizableClasses
);
219 inline Scalar::Type
GetTypedArrayClassType(const JSClass
* clasp
) {
220 MOZ_ASSERT(IsTypedArrayClass(clasp
));
221 if (clasp
< std::end(TypedArrayObject::fixedLengthClasses
)) {
222 return static_cast<Scalar::Type
>(clasp
-
223 &TypedArrayObject::fixedLengthClasses
[0]);
225 return static_cast<Scalar::Type
>(clasp
-
226 &TypedArrayObject::resizableClasses
[0]);
229 bool IsTypedArrayConstructor(const JSObject
* obj
);
231 bool IsTypedArrayConstructor(HandleValue v
, Scalar::Type type
);
233 JSNative
TypedArrayConstructorNative(Scalar::Type type
);
235 // In WebIDL terminology, a BufferSource is either an ArrayBuffer or a typed
236 // array view. In either case, extract the dataPointer/byteLength.
237 bool IsBufferSource(JSObject
* object
, SharedMem
<uint8_t*>* dataPointer
,
240 inline Scalar::Type
TypedArrayObject::type() const {
241 return GetTypedArrayClassType(getClass());
244 inline size_t TypedArrayObject::bytesPerElement() const {
245 return Scalar::byteSize(type());
248 // ES2020 draft rev a5375bdad264c8aa264d9c44f57408087761069e
249 // 7.1.16 CanonicalNumericIndexString
251 // Checks whether or not the string is a canonical numeric index string. If the
252 // string is a canonical numeric index which is not representable as a uint64_t,
253 // the returned index is UINT64_MAX.
254 template <typename CharT
>
255 mozilla::Maybe
<uint64_t> StringToTypedArrayIndex(mozilla::Range
<const CharT
> s
);
257 // A string |s| is a TypedArray index (or: canonical numeric index string) iff
258 // |s| is "-0" or |SameValue(ToString(ToNumber(s)), s)| is true. So check for
259 // any characters which can start the string representation of a number,
260 // including "NaN" and "Infinity".
261 template <typename CharT
>
262 inline bool CanStartTypedArrayIndex(CharT ch
) {
263 return mozilla::IsAsciiDigit(ch
) || ch
== '-' || ch
== 'N' || ch
== 'I';
266 [[nodiscard
]] inline mozilla::Maybe
<uint64_t> ToTypedArrayIndex(jsid id
) {
268 int32_t i
= id
.toInt();
270 return mozilla::Some(i
);
273 if (MOZ_UNLIKELY(!id
.isString())) {
274 return mozilla::Nothing();
277 JS::AutoCheckCannotGC nogc
;
278 JSAtom
* atom
= id
.toAtom();
280 if (atom
->empty() || !CanStartTypedArrayIndex(atom
->latin1OrTwoByteChar(0))) {
281 return mozilla::Nothing();
284 if (atom
->hasLatin1Chars()) {
285 mozilla::Range
<const Latin1Char
> chars
= atom
->latin1Range(nogc
);
286 return StringToTypedArrayIndex(chars
);
289 mozilla::Range
<const char16_t
> chars
= atom
->twoByteRange(nogc
);
290 return StringToTypedArrayIndex(chars
);
293 bool SetTypedArrayElement(JSContext
* cx
, Handle
<TypedArrayObject
*> obj
,
294 uint64_t index
, HandleValue v
,
295 ObjectOpResult
& result
);
297 bool SetTypedArrayElementOutOfBounds(JSContext
* cx
,
298 Handle
<TypedArrayObject
*> obj
,
299 uint64_t index
, HandleValue v
,
300 ObjectOpResult
& result
);
303 * Implements [[DefineOwnProperty]] for TypedArrays when the property
304 * key is a TypedArray index.
306 bool DefineTypedArrayElement(JSContext
* cx
, Handle
<TypedArrayObject
*> obj
,
307 uint64_t index
, Handle
<PropertyDescriptor
> desc
,
308 ObjectOpResult
& result
);
310 // Sort a typed array in ascending order. The typed array may be wrapped, but
311 // must not be detached.
312 bool intrinsic_TypedArrayNativeSort(JSContext
* cx
, unsigned argc
, Value
* vp
);
314 static inline constexpr unsigned TypedArrayShift(Scalar::Type viewType
) {
318 case Scalar::Uint8Clamped
:
322 case Scalar::Float16
:
326 case Scalar::Float32
:
328 case Scalar::BigInt64
:
329 case Scalar::BigUint64
:
331 case Scalar::Float64
:
334 MOZ_CRASH("Unexpected array type");
338 static inline constexpr unsigned TypedArrayElemSize(Scalar::Type viewType
) {
339 return 1u << TypedArrayShift(viewType
);
345 inline bool JSObject::is
<js::TypedArrayObject
>() const {
346 return js::IsTypedArrayClass(getClass());
350 inline bool JSObject::is
<js::FixedLengthTypedArrayObject
>() const {
351 return js::IsFixedLengthTypedArrayClass(getClass());
355 inline bool JSObject::is
<js::ResizableTypedArrayObject
>() const {
356 return js::IsResizableTypedArrayClass(getClass());
359 #endif /* vm_TypedArrayObject_h */