Coverage Report

Created: 2025-12-12 07:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hermes/include/hermes/VM/JSTypedArray.h
Line
Count
Source
1
/*
2
 * Copyright (c) Meta Platforms, Inc. and affiliates.
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
8
#ifndef HERMES_VM_JSTYPEDARRAY_H
9
#define HERMES_VM_JSTYPEDARRAY_H
10
11
#include "hermes/VM/GCPointer.h"
12
#include "hermes/VM/JSArrayBuffer.h"
13
14
namespace hermes {
15
namespace vm {
16
17
/// JSTypedArrayBase is the object that all JSTypedArrays inherit from, and
18
/// exposes common interface elements to JSTypedArrays.
19
class JSTypedArrayBase : public JSObject {
20
 public:
21
  // The spec is silent about the maximum size of an ArrayBuffer.  If
22
  // this is to be enlarged effectively, other changes (such as the
23
  // use of uint32_t in ObjectVTable's indexed storage members, will
24
  // need to be changed, too.
25
  using size_type = uint32_t;
26
  using Super = JSObject;
27
28
  CallResult<Handle<JSTypedArrayBase>> allocate(
29
      Runtime &runtime,
30
      size_type length = 0);
31
32
  /// Allocate a new instance of a TypedArray matching the runtime type of
33
  /// the \p src TypedArray.
34
  /// The newly allocated TypedArray shares the same underlying ArrayBuffer as
35
  /// \p src.
36
  /// \p beginIndex the first index of \p src to use in the new array.
37
  /// \p endIndex the last index of \p src to use in the new array.
38
  static CallResult<Handle<JSTypedArrayBase>> allocateToSameBuffer(
39
      Runtime &runtime,
40
      Handle<JSTypedArrayBase> src,
41
      size_type beginIndex,
42
      size_type endIndex);
43
44
  static CallResult<Handle<JSTypedArrayBase>> allocateSpecies(
45
      Runtime &runtime,
46
      Handle<JSTypedArrayBase> self,
47
      size_type length);
48
49
  /// ES6 22.2.3.5.1
50
  /// Validates \p thisArg to be a JSTypedArrayBase.
51
  /// If \p checkAttached is true, it will also ensure that the typed array is
52
  /// attached.
53
  static ExecutionStatus validateTypedArray(
54
      Runtime &runtime,
55
      Handle<> thisArg,
56
      bool checkAttached = true);
57
0
  static bool classof(const GCCell *cell) {
58
0
    return kindInRange(
59
0
        cell->getKind(),
60
0
        CellKind::TypedArrayBaseKind_first,
61
0
        CellKind::TypedArrayBaseKind_last);
62
0
  }
63
64
  /// Get the length of this array. This corresponds to the property visible
65
  /// as `.length` in JS.
66
0
  size_type getLength() const {
67
0
    return length_;
68
0
  }
69
70
  /// Get the byte length of this array. This corresponds to the property
71
  /// visible as `.byteLength` in JS.
72
0
  JSArrayBuffer::size_type getByteLength() const {
73
0
    return length_ * getByteWidth();
74
0
  }
75
76
  /// Get the width of the type in number of bytes. This is sizeof(T) for the
77
  /// T used to instantiate a TypedArray.
78
  uint8_t getByteWidth() const;
79
80
  /// \return The underlying array buffer that this TypedArray uses for its
81
  /// storage.
82
  /// \pre This cannot be called on a detached TypedArray.
83
0
  JSArrayBuffer *getBuffer(Runtime &runtime) const {
84
0
    assert(buffer_ && "Must have some JSArrayBuffer");
85
0
    return buffer_.get(runtime);
86
0
  }
87
88
0
  uint8_t *begin(Runtime &runtime) {
89
0
    return buffer_.getNonNull(runtime)->getDataBlock(runtime) + offset_;
90
0
  }
91
0
  uint8_t *end(Runtime &runtime) {
92
0
    return begin(runtime) + getByteLength();
93
0
  }
94
95
  /// \return Whether this JSTypedArrayBase is attached to some buffer.
96
0
  bool attached(Runtime &runtime) const {
97
0
    return buffer_ && buffer_.getNonNull(runtime)->attached();
98
0
  }
99
100
  /// \return The offset from the beginning of the buffer this typed array
101
  /// is looking into.
102
0
  JSArrayBuffer::size_type getByteOffset() const {
103
0
    return offset_;
104
0
  }
105
106
  /// Allocates a buffer using \p runtime with \p length number of
107
  /// elements, each of \p byteWidth size in bytes.
108
  static ExecutionStatus createBuffer(
109
      Runtime &runtime,
110
      Handle<JSTypedArrayBase> selfObj,
111
      uint64_t length);
112
113
  /// Sets the current buffer to a copy of \p src starting from
114
  /// \p byteOffset and going for \p srcSize bytes total.
115
  /// \pre
116
  ///   src must not be a null handle.
117
  ///   byteOffset + srcSize <= src->size()
118
  static ExecutionStatus setToCopyOfBuffer(
119
      Runtime &runtime,
120
      Handle<JSTypedArrayBase> dst,
121
      JSArrayBuffer::size_type dstByteOffset,
122
      Handle<JSArrayBuffer> src,
123
      JSArrayBuffer::size_type srcByteOffset,
124
      JSArrayBuffer::size_type count);
125
126
  /// Sets the current buffer to a copy of \p src.
127
  /// This is a convenient wrapper around `setToCopyOfBuffer` for when a
128
  /// whole TypedArray must be copied into the current one.
129
  /// \pre src must not be a null handle.
130
  static ExecutionStatus setToCopyOfTypedArray(
131
      Runtime &runtime,
132
      Handle<JSTypedArrayBase> dst,
133
      size_type dstIndex,
134
      Handle<JSTypedArrayBase> src,
135
      size_type srcIndex,
136
      size_type count);
137
138
  /// Sets this object to use \p buf as its buffer, starting from \p offset
139
  /// position and goes for \p size indices in the buffer, where each element
140
  /// is aligned on \p byteWidth.  The \p gc parameter is necessary for write
141
  /// barriers.
142
  /// \pre
143
  ///   Both \p offset and \p size must be aligned on byteWidth.
144
  ///   \p offset + size <= the size of \p buf.
145
  ///   Neither \p self nor \p buf can be null.
146
  static void setBuffer(
147
      Runtime &runtime,
148
      JSTypedArrayBase *self,
149
      JSArrayBuffer *buf,
150
      size_type offset,
151
      size_type size,
152
      uint8_t byteWidth);
153
154
 protected:
155
  /// buffer_ is the underlying buffer which holds the data to be viewed.
156
  /// This buffer may be shared with other JSTypedArray instantiations.
157
  GCPointer<JSArrayBuffer> buffer_;
158
  /// length_ is the length of the buffer in terms of the type it is viewed by.
159
  size_type length_;
160
  /// offset_ is the offset to start reading from in the underlying ArrayBuffer.
161
  size_type offset_;
162
163
  explicit JSTypedArrayBase(
164
      Runtime &runtime,
165
      Handle<JSObject> parent,
166
      Handle<HiddenClass> clazz);
167
168
  /// Sets the current buffer's contents to the contents of a buffer from
169
  /// another TypedArray \p src.
170
  /// Copies from \p srcIndex to \p srcIndex + \p count in \p src into
171
  /// \p dstIndex to \p dstIndex + \p count in \p dst.
172
  /// \pre
173
  ///   Both dst and src must be the same runtime type of TypedArray.
174
  ///   For distinct TypedArrays, use `setToCopyOfTypedArray` instead.
175
  static void setToCopyOfBytes(
176
      Runtime &runtime,
177
      Handle<JSTypedArrayBase> dst,
178
      size_type dstIndex,
179
      Handle<JSTypedArrayBase> src,
180
      size_type srcIndex,
181
      size_type count);
182
183
  static std::pair<uint32_t, uint32_t> _getOwnIndexedRangeImpl(
184
      JSObject *selfObj,
185
      Runtime &runtime);
186
187
  static bool
188
  _haveOwnIndexedImpl(JSObject *selfObj, Runtime &runtime, uint32_t index);
189
  static OptValue<PropertyFlags> _getOwnIndexedPropertyFlagsImpl(
190
      JSObject *selfObj,
191
      Runtime &runtime,
192
      uint32_t index);
193
  static bool _deleteOwnIndexedImpl(
194
      Handle<JSObject> selfHandle,
195
      Runtime &runtime,
196
      uint32_t index);
197
  /// Check whether all indexed properties satisfy the requirement specified by
198
  /// \p mode. Either whether they are all non-configurable, or whether they are
199
  /// all both non-configurable and non-writable.
200
  static bool _checkAllOwnIndexedImpl(
201
      JSObject *selfObj,
202
      Runtime &runtime,
203
      ObjectVTable::CheckAllOwnIndexedMode mode);
204
205
  friend void TypedArrayBaseBuildMeta(
206
      const GCCell *cell,
207
      Metadata::Builder &mb);
208
};
209
210
/// JSTypedArray is a collection with array semantics (random access indexing),
211
/// but stores a typed value with a known size, and has a static total size.
212
///
213
/// The templated versions represent the TypedArrays family from JS,
214
/// for example Int8Array is JSTypedArray<int8_t, CellKind::Int8ArrayKind>
215
template <typename T, CellKind C>
216
class JSTypedArray final : public JSTypedArrayBase {
217
 public:
218
  using iterator = T *;
219
220
  static const ObjectVTable vt;
221
222
0
  static constexpr CellKind getCellKind() {
223
0
    return C;
224
0
  }
Unexecuted instantiation: hermes::vm::JSTypedArray<signed char, (hermes::vm::CellKind)35>::getCellKind()
Unexecuted instantiation: hermes::vm::JSTypedArray<short, (hermes::vm::CellKind)36>::getCellKind()
Unexecuted instantiation: hermes::vm::JSTypedArray<int, (hermes::vm::CellKind)37>::getCellKind()
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned char, (hermes::vm::CellKind)38>::getCellKind()
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned char, (hermes::vm::CellKind)39>::getCellKind()
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned short, (hermes::vm::CellKind)40>::getCellKind()
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned int, (hermes::vm::CellKind)41>::getCellKind()
Unexecuted instantiation: hermes::vm::JSTypedArray<float, (hermes::vm::CellKind)42>::getCellKind()
Unexecuted instantiation: hermes::vm::JSTypedArray<double, (hermes::vm::CellKind)43>::getCellKind()
Unexecuted instantiation: hermes::vm::JSTypedArray<long, (hermes::vm::CellKind)44>::getCellKind()
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned long, (hermes::vm::CellKind)45>::getCellKind()
225
0
  static bool classof(const GCCell *cell) {
226
0
    return cell->getKind() == C;
227
0
  }
Unexecuted instantiation: hermes::vm::JSTypedArray<signed char, (hermes::vm::CellKind)35>::classof(hermes::vm::GCCell const*)
Unexecuted instantiation: hermes::vm::JSTypedArray<short, (hermes::vm::CellKind)36>::classof(hermes::vm::GCCell const*)
Unexecuted instantiation: hermes::vm::JSTypedArray<int, (hermes::vm::CellKind)37>::classof(hermes::vm::GCCell const*)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned char, (hermes::vm::CellKind)38>::classof(hermes::vm::GCCell const*)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned char, (hermes::vm::CellKind)39>::classof(hermes::vm::GCCell const*)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned short, (hermes::vm::CellKind)40>::classof(hermes::vm::GCCell const*)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned int, (hermes::vm::CellKind)41>::classof(hermes::vm::GCCell const*)
Unexecuted instantiation: hermes::vm::JSTypedArray<float, (hermes::vm::CellKind)42>::classof(hermes::vm::GCCell const*)
Unexecuted instantiation: hermes::vm::JSTypedArray<double, (hermes::vm::CellKind)43>::classof(hermes::vm::GCCell const*)
Unexecuted instantiation: hermes::vm::JSTypedArray<long, (hermes::vm::CellKind)44>::classof(hermes::vm::GCCell const*)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned long, (hermes::vm::CellKind)45>::classof(hermes::vm::GCCell const*)
228
229
  static PseudoHandle<JSTypedArray<T, C>> create(
230
      Runtime &runtime,
231
      Handle<JSObject> prototype);
232
233
0
  iterator begin(Runtime &runtime) {
234
0
    return reinterpret_cast<T *>(JSTypedArrayBase::begin(runtime));
235
0
  }
Unexecuted instantiation: hermes::vm::JSTypedArray<signed char, (hermes::vm::CellKind)35>::begin(hermes::vm::Runtime&)
Unexecuted instantiation: hermes::vm::JSTypedArray<short, (hermes::vm::CellKind)36>::begin(hermes::vm::Runtime&)
Unexecuted instantiation: hermes::vm::JSTypedArray<int, (hermes::vm::CellKind)37>::begin(hermes::vm::Runtime&)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned char, (hermes::vm::CellKind)38>::begin(hermes::vm::Runtime&)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned char, (hermes::vm::CellKind)39>::begin(hermes::vm::Runtime&)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned short, (hermes::vm::CellKind)40>::begin(hermes::vm::Runtime&)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned int, (hermes::vm::CellKind)41>::begin(hermes::vm::Runtime&)
Unexecuted instantiation: hermes::vm::JSTypedArray<float, (hermes::vm::CellKind)42>::begin(hermes::vm::Runtime&)
Unexecuted instantiation: hermes::vm::JSTypedArray<double, (hermes::vm::CellKind)43>::begin(hermes::vm::Runtime&)
Unexecuted instantiation: hermes::vm::JSTypedArray<long, (hermes::vm::CellKind)44>::begin(hermes::vm::Runtime&)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned long, (hermes::vm::CellKind)45>::begin(hermes::vm::Runtime&)
236
0
  iterator end(Runtime &runtime) {
237
0
    return begin(runtime) + length_;
238
0
  }
Unexecuted instantiation: hermes::vm::JSTypedArray<signed char, (hermes::vm::CellKind)35>::end(hermes::vm::Runtime&)
Unexecuted instantiation: hermes::vm::JSTypedArray<short, (hermes::vm::CellKind)36>::end(hermes::vm::Runtime&)
Unexecuted instantiation: hermes::vm::JSTypedArray<int, (hermes::vm::CellKind)37>::end(hermes::vm::Runtime&)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned char, (hermes::vm::CellKind)38>::end(hermes::vm::Runtime&)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned char, (hermes::vm::CellKind)39>::end(hermes::vm::Runtime&)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned short, (hermes::vm::CellKind)40>::end(hermes::vm::Runtime&)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned int, (hermes::vm::CellKind)41>::end(hermes::vm::Runtime&)
Unexecuted instantiation: hermes::vm::JSTypedArray<float, (hermes::vm::CellKind)42>::end(hermes::vm::Runtime&)
Unexecuted instantiation: hermes::vm::JSTypedArray<double, (hermes::vm::CellKind)43>::end(hermes::vm::Runtime&)
Unexecuted instantiation: hermes::vm::JSTypedArray<long, (hermes::vm::CellKind)44>::end(hermes::vm::Runtime&)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned long, (hermes::vm::CellKind)45>::end(hermes::vm::Runtime&)
239
240
  /// Retrieve the \p i'th element of the buffer.
241
  /// \pre
242
  ///   This cannot be called on a detached TypedArray.
243
  ///   i must be less than the length of the TypedArray.
244
0
  T &at(Runtime &runtime, size_type i) {
245
0
    assert(attached(runtime) && "at() requires a JSArrayBuffer");
246
0
    assert(i < getLength() && "That index is out of bounds of this TypedArray");
247
0
    return begin(runtime)[i];
248
0
  }
Unexecuted instantiation: hermes::vm::JSTypedArray<signed char, (hermes::vm::CellKind)35>::at(hermes::vm::Runtime&, unsigned int)
Unexecuted instantiation: hermes::vm::JSTypedArray<short, (hermes::vm::CellKind)36>::at(hermes::vm::Runtime&, unsigned int)
Unexecuted instantiation: hermes::vm::JSTypedArray<int, (hermes::vm::CellKind)37>::at(hermes::vm::Runtime&, unsigned int)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned char, (hermes::vm::CellKind)38>::at(hermes::vm::Runtime&, unsigned int)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned char, (hermes::vm::CellKind)39>::at(hermes::vm::Runtime&, unsigned int)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned short, (hermes::vm::CellKind)40>::at(hermes::vm::Runtime&, unsigned int)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned int, (hermes::vm::CellKind)41>::at(hermes::vm::Runtime&, unsigned int)
Unexecuted instantiation: hermes::vm::JSTypedArray<float, (hermes::vm::CellKind)42>::at(hermes::vm::Runtime&, unsigned int)
Unexecuted instantiation: hermes::vm::JSTypedArray<double, (hermes::vm::CellKind)43>::at(hermes::vm::Runtime&, unsigned int)
Unexecuted instantiation: hermes::vm::JSTypedArray<long, (hermes::vm::CellKind)44>::at(hermes::vm::Runtime&, unsigned int)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned long, (hermes::vm::CellKind)45>::at(hermes::vm::Runtime&, unsigned int)
249
250
  static Handle<JSObject> getPrototype(const Runtime &runtime);
251
  static Handle<Callable> getConstructor(const Runtime &runtime);
252
  static SymbolID getName(Runtime &runtime);
253
254
  /// Allocate a new instance of a TypedArray of this type.
255
  /// \p length the length of the TypedArray to create.
256
  static CallResult<Handle<JSTypedArrayBase>> allocate(
257
      Runtime &runtime,
258
      size_type length = 0);
259
260
  /// Allocate a new instance of a TypedArray from the species constructor
261
  /// of the given \p self, and forward the \p length parameter to that
262
  /// constructor.
263
  static CallResult<Handle<JSTypedArrayBase>> allocateSpecies(
264
      Handle<JSTypedArrayBase> self,
265
      Runtime &runtime,
266
      size_type length);
267
268
  /// Converts a \p numeric to the type used by this typed array.
269
  /// NOTE: this function has specializations for types which don't use a
270
  /// truncated int32 representation. See the bottom of this file for their
271
  /// implementations.
272
0
  static inline T toDestType(const HermesValue &numeric) {
273
0
    return hermes::truncateToInt32(numeric.getNumber());
274
0
  }
Unexecuted instantiation: hermes::vm::JSTypedArray<signed char, (hermes::vm::CellKind)35>::toDestType(hermes::vm::HermesValue const&)
Unexecuted instantiation: hermes::vm::JSTypedArray<short, (hermes::vm::CellKind)36>::toDestType(hermes::vm::HermesValue const&)
Unexecuted instantiation: hermes::vm::JSTypedArray<int, (hermes::vm::CellKind)37>::toDestType(hermes::vm::HermesValue const&)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned char, (hermes::vm::CellKind)38>::toDestType(hermes::vm::HermesValue const&)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned short, (hermes::vm::CellKind)40>::toDestType(hermes::vm::HermesValue const&)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned int, (hermes::vm::CellKind)41>::toDestType(hermes::vm::HermesValue const&)
275
276
 protected:
277
  /// Retrieve an indexed property.
278
  static HermesValue _getOwnIndexedImpl(
279
      PseudoHandle<JSObject> self,
280
      Runtime &runtime,
281
      uint32_t index);
282
  static CallResult<bool> _setOwnIndexedImpl(
283
      Handle<JSObject> selfHandle,
284
      Runtime &runtime,
285
      uint32_t index,
286
      Handle<> value);
287
288
 public:
289
  // NOTE: If any fields are ever added beyond the base class, then the
290
  // *BuildMeta functions must be updated to call addJSObjectOverlapSlots.
291
292
  explicit JSTypedArray(
293
      Runtime &runtime,
294
      Handle<JSObject> parent,
295
      Handle<HiddenClass> clazz);
296
};
297
298
/// @name toDestType specializations
299
/// @{
300
301
template <>
302
inline uint8_t
303
JSTypedArray<uint8_t, CellKind::Uint8ClampedArrayKind>::toDestType(
304
0
    const HermesValue &numeric) {
305
0
  return toUInt8Clamp(numeric.getNumber());
306
0
}
307
308
template <>
309
inline float JSTypedArray<float, CellKind::Float32ArrayKind>::toDestType(
310
    const HermesValue &numeric) LLVM_NO_SANITIZE("float-cast-overflow");
311
312
template <>
313
inline float JSTypedArray<float, CellKind::Float32ArrayKind>::toDestType(
314
0
    const HermesValue &numeric) {
315
  // This can overflow a float, but float overflow goes to Infinity
316
  // (the correct behavior) on all modern platforms.
317
0
  return numeric.getNumber();
318
0
}
319
320
template <>
321
inline double JSTypedArray<double, CellKind::Float64ArrayKind>::toDestType(
322
0
    const HermesValue &numeric) {
323
0
  return numeric.getNumber();
324
0
}
325
326
template <>
327
int64_t JSTypedArray<int64_t, CellKind::BigInt64ArrayKind>::toDestType(
328
    const HermesValue &numeric);
329
330
template <>
331
uint64_t JSTypedArray<uint64_t, CellKind::BigUint64ArrayKind>::toDestType(
332
    const HermesValue &numeric);
333
334
/// @}
335
#define TYPED_ARRAY(name, type) \
336
  using name##Array = JSTypedArray<type, CellKind::name##ArrayKind>;
337
#include "hermes/VM/TypedArrays.def"
338
339
} // namespace vm
340
} // namespace hermes
341
342
#endif