tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TypedArrayObject.h (16697B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef vm_TypedArrayObject_h
      8 #define vm_TypedArrayObject_h
      9 
     10 #include "mozilla/Maybe.h"
     11 #include "mozilla/TextUtils.h"
     12 
     13 #include "gc/AllocKind.h"
     14 #include "gc/MaybeRooted.h"
     15 #include "js/Class.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"
     22 
     23 namespace js {
     24 
     25 enum class ArraySortResult : uint32_t;
     26 
     27 namespace jit {
     28 class TrampolineNativeFrameLayout;
     29 }
     30 
     31 /*
     32 * TypedArrayObject
     33 *
     34 * The non-templated base class for the specific typed implementations.
     35 * This class holds all the member variables that are used by
     36 * the subclasses.
     37 */
     38 
     39 class TypedArrayObject : public ArrayBufferViewObject {
     40 public:
     41  static_assert(js::detail::TypedArrayLengthSlot == LENGTH_SLOT,
     42                "bad inlined constant in TypedData.h");
     43  static_assert(js::detail::TypedArrayDataSlot == DATA_SLOT,
     44                "bad inlined constant in TypedData.h");
     45 
     46  static bool sameBuffer(const TypedArrayObject* a, const TypedArrayObject* b) {
     47    // Inline buffers.
     48    if (!a->hasBuffer() || !b->hasBuffer()) {
     49      return a == b;
     50    }
     51 
     52    // Shared buffers.
     53    if (a->isSharedMemory() && b->isSharedMemory()) {
     54      return a->bufferShared()->globalID() == b->bufferShared()->globalID();
     55    }
     56 
     57    return a->bufferEither() == b->bufferEither();
     58  }
     59 
     60  static const JSClass anyClasses[3][Scalar::MaxTypedArrayViewType];
     61  static constexpr const JSClass (
     62      &fixedLengthClasses)[Scalar::MaxTypedArrayViewType] =
     63      TypedArrayObject::anyClasses[0];
     64  static constexpr const JSClass (
     65      &immutableClasses)[Scalar::MaxTypedArrayViewType] =
     66      TypedArrayObject::anyClasses[1];
     67  static constexpr const JSClass (
     68      &resizableClasses)[Scalar::MaxTypedArrayViewType] =
     69      TypedArrayObject::anyClasses[2];
     70  static const JSClass protoClasses[Scalar::MaxTypedArrayViewType];
     71  static const JSClass sharedTypedArrayPrototypeClass;
     72 
     73  static const JSClass* protoClassForType(Scalar::Type type) {
     74    MOZ_ASSERT(type < Scalar::MaxTypedArrayViewType);
     75    return &protoClasses[type];
     76  }
     77 
     78  inline Scalar::Type type() const;
     79  inline size_t bytesPerElement() const;
     80 
     81  static bool ensureHasBuffer(JSContext* cx,
     82                              Handle<TypedArrayObject*> typedArray);
     83 
     84 public:
     85  /**
     86   * Return the current length, or |Nothing| if the TypedArray is detached or
     87   * out-of-bounds.
     88   */
     89  mozilla::Maybe<size_t> length() const {
     90    return ArrayBufferViewObject::length();
     91  }
     92 
     93  /**
     94   * Return the current byteLength, or |Nothing| if the TypedArray is detached
     95   * or out-of-bounds.
     96   */
     97  mozilla::Maybe<size_t> byteLength() const {
     98    return length().map(
     99        [this](size_t value) { return value * bytesPerElement(); });
    100  }
    101 
    102  // Self-hosted TypedArraySubarray function needs to read [[ByteOffset]], even
    103  // when it's currently out-of-bounds.
    104  size_t byteOffsetMaybeOutOfBounds() const {
    105    // dataPointerOffset() returns the [[ByteOffset]] spec value, except when
    106    // the buffer is detached. (bug 1840991)
    107    return ArrayBufferViewObject::dataPointerOffset();
    108  }
    109 
    110  template <AllowGC allowGC>
    111  bool getElement(JSContext* cx, size_t index,
    112                  typename MaybeRooted<Value, allowGC>::MutableHandleType val);
    113  bool getElementPure(size_t index, Value* vp);
    114 
    115  /*
    116   * Copy |length| elements from this typed array to vp. vp must point to rooted
    117   * memory. |length| must not exceed the typed array's current length.
    118   */
    119  static bool getElements(JSContext* cx, Handle<TypedArrayObject*> tarray,
    120                          size_t length, Value* vp);
    121 
    122  static bool GetTemplateObjectForLength(JSContext* cx, Scalar::Type type,
    123                                         int32_t length,
    124                                         MutableHandle<TypedArrayObject*> res);
    125 
    126  static TypedArrayObject* GetTemplateObjectForBuffer(
    127      JSContext* cx, Scalar::Type type,
    128      Handle<ArrayBufferObjectMaybeShared*> buffer);
    129 
    130  static TypedArrayObject* GetTemplateObjectForBufferView(
    131      JSContext* cx, Handle<TypedArrayObject*> bufferView);
    132 
    133  static TypedArrayObject* GetTemplateObjectForArrayLike(
    134      JSContext* cx, Scalar::Type type, Handle<JSObject*> arrayLike);
    135 
    136  // Maximum allowed byte length for any typed array.
    137  static constexpr size_t ByteLengthLimit = ArrayBufferObject::ByteLengthLimit;
    138 
    139  /* Accessors and functions */
    140 
    141  static bool sort(JSContext* cx, unsigned argc, Value* vp);
    142 
    143  bool convertValue(JSContext* cx, HandleValue v,
    144                    MutableHandleValue result) const;
    145 
    146  /* Initialization bits */
    147 
    148  static const JSFunctionSpec protoFunctions[];
    149  static const JSPropertySpec protoAccessors[];
    150  static const JSFunctionSpec staticFunctions[];
    151  static const JSPropertySpec staticProperties[];
    152 };
    153 
    154 class FixedLengthTypedArrayObject : public TypedArrayObject {
    155 public:
    156  static constexpr size_t FIXED_DATA_START = RESERVED_SLOTS;
    157 
    158  // For typed arrays which can store their data inline, the array buffer
    159  // object is created lazily.
    160  static constexpr uint32_t INLINE_BUFFER_LIMIT =
    161      (NativeObject::MAX_FIXED_SLOTS - FIXED_DATA_START) * sizeof(Value);
    162 
    163  inline gc::AllocKind allocKindForTenure() const;
    164  static inline gc::AllocKind AllocKindForLazyBuffer(size_t nbytes);
    165 
    166  size_t byteOffset() const {
    167    return ArrayBufferViewObject::byteOffsetSlotValue();
    168  }
    169 
    170  size_t byteLength() const { return length() * bytesPerElement(); }
    171 
    172  size_t length() const { return ArrayBufferViewObject::lengthSlotValue(); }
    173 
    174  bool hasInlineElements() const;
    175  void setInlineElements();
    176  uint8_t* elementsRaw() const {
    177    return maybePtrFromReservedSlot<uint8_t>(DATA_SLOT);
    178  }
    179  uint8_t* elements() const {
    180    assertZeroLengthArrayData();
    181    return elementsRaw();
    182  }
    183 
    184  bool hasMallocedElements(JSContext* cx) const;
    185 
    186 #ifdef DEBUG
    187  void assertZeroLengthArrayData() const;
    188 #else
    189  void assertZeroLengthArrayData() const {};
    190 #endif
    191 
    192  static void finalize(JS::GCContext* gcx, JSObject* obj);
    193  static size_t objectMoved(JSObject* obj, JSObject* old);
    194 };
    195 
    196 class ResizableTypedArrayObject : public TypedArrayObject {
    197 public:
    198  static const uint8_t RESERVED_SLOTS = RESIZABLE_RESERVED_SLOTS;
    199 };
    200 
    201 class ImmutableTypedArrayObject : public TypedArrayObject {};
    202 
    203 extern TypedArrayObject* NewTypedArrayWithTemplateAndLength(
    204    JSContext* cx, HandleObject templateObj, int32_t len);
    205 
    206 extern TypedArrayObject* NewTypedArrayWithTemplateAndArray(
    207    JSContext* cx, HandleObject templateObj, HandleObject array);
    208 
    209 extern TypedArrayObject* NewTypedArrayWithTemplateAndBuffer(
    210    JSContext* cx, HandleObject templateObj, HandleObject arrayBuffer,
    211    HandleValue byteOffset, HandleValue length);
    212 
    213 extern TypedArrayObject* NewUint8ArrayWithLength(
    214    JSContext* cx, int32_t len, gc::Heap heap = gc::Heap::Default);
    215 
    216 inline bool IsFixedLengthTypedArrayClass(const JSClass* clasp) {
    217  return std::begin(TypedArrayObject::fixedLengthClasses) <= clasp &&
    218         clasp < std::end(TypedArrayObject::fixedLengthClasses);
    219 }
    220 
    221 inline bool IsResizableTypedArrayClass(const JSClass* clasp) {
    222  return std::begin(TypedArrayObject::resizableClasses) <= clasp &&
    223         clasp < std::end(TypedArrayObject::resizableClasses);
    224 }
    225 
    226 inline bool IsImmutableTypedArrayClass(const JSClass* clasp) {
    227  return std::begin(TypedArrayObject::immutableClasses) <= clasp &&
    228         clasp < std::end(TypedArrayObject::immutableClasses);
    229 }
    230 
    231 inline bool IsTypedArrayClass(const JSClass* clasp) {
    232  MOZ_ASSERT(std::end(TypedArrayObject::fixedLengthClasses) ==
    233                     std::begin(TypedArrayObject::immutableClasses) &&
    234                 std::end(TypedArrayObject::immutableClasses) ==
    235                     std::begin(TypedArrayObject::resizableClasses),
    236             "TypedArray classes are in contiguous memory");
    237  return std::begin(TypedArrayObject::fixedLengthClasses) <= clasp &&
    238         clasp < std::end(TypedArrayObject::resizableClasses);
    239 }
    240 
    241 inline Scalar::Type GetTypedArrayClassType(const JSClass* clasp) {
    242  MOZ_ASSERT(IsTypedArrayClass(clasp));
    243  if (clasp < std::end(TypedArrayObject::fixedLengthClasses)) {
    244    return static_cast<Scalar::Type>(clasp -
    245                                     &TypedArrayObject::fixedLengthClasses[0]);
    246  }
    247  if (clasp < std::end(TypedArrayObject::immutableClasses)) {
    248    return static_cast<Scalar::Type>(clasp -
    249                                     &TypedArrayObject::immutableClasses[0]);
    250  }
    251  return static_cast<Scalar::Type>(clasp -
    252                                   &TypedArrayObject::resizableClasses[0]);
    253 }
    254 
    255 bool IsTypedArrayConstructor(const JSObject* obj);
    256 
    257 bool IsTypedArrayConstructor(HandleValue v, Scalar::Type type);
    258 
    259 JSNative TypedArrayConstructorNative(Scalar::Type type);
    260 
    261 Scalar::Type TypedArrayConstructorType(const JSFunction* fun);
    262 
    263 // In WebIDL terminology, a BufferSource is either an ArrayBuffer or a typed
    264 // array view. In either case, extract the dataPointer/byteLength.
    265 //
    266 // If [AllowShared] is true, then the buffer may be backed by a shared array
    267 //   buffer.
    268 // If [AllowResizable] is true, then the buffer may be backed by a resizable
    269 //   or growable array buffer.
    270 bool IsBufferSource(JSContext* cx, JSObject* object, bool allowShared,
    271                    bool allowResizable, SharedMem<uint8_t*>* dataPointer,
    272                    size_t* byteLength);
    273 
    274 inline Scalar::Type TypedArrayObject::type() const {
    275  return GetTypedArrayClassType(getClass());
    276 }
    277 
    278 inline size_t TypedArrayObject::bytesPerElement() const {
    279  return Scalar::byteSize(type());
    280 }
    281 
    282 // ES2020 draft rev a5375bdad264c8aa264d9c44f57408087761069e
    283 // 7.1.16 CanonicalNumericIndexString
    284 //
    285 // Checks whether or not the string is a canonical numeric index string. If the
    286 // string is a canonical numeric index which is not representable as a uint64_t,
    287 // the returned index is UINT64_MAX.
    288 template <typename CharT>
    289 mozilla::Maybe<uint64_t> StringToTypedArrayIndex(mozilla::Range<const CharT> s);
    290 
    291 // A string |s| is a TypedArray index (or: canonical numeric index string) iff
    292 // |s| is "-0" or |SameValue(ToString(ToNumber(s)), s)| is true. So check for
    293 // any characters which can start the string representation of a number,
    294 // including "NaN" and "Infinity".
    295 template <typename CharT>
    296 inline bool CanStartTypedArrayIndex(CharT ch) {
    297  return mozilla::IsAsciiDigit(ch) || ch == '-' || ch == 'N' || ch == 'I';
    298 }
    299 
    300 [[nodiscard]] inline mozilla::Maybe<uint64_t> ToTypedArrayIndex(jsid id) {
    301  if (id.isInt()) {
    302    int32_t i = id.toInt();
    303    MOZ_ASSERT(i >= 0);
    304    return mozilla::Some(i);
    305  }
    306 
    307  if (MOZ_UNLIKELY(!id.isString())) {
    308    return mozilla::Nothing();
    309  }
    310 
    311  JS::AutoCheckCannotGC nogc;
    312  JSAtom* atom = id.toAtom();
    313 
    314  if (atom->empty() || !CanStartTypedArrayIndex(atom->latin1OrTwoByteChar(0))) {
    315    return mozilla::Nothing();
    316  }
    317 
    318  if (atom->hasLatin1Chars()) {
    319    mozilla::Range<const Latin1Char> chars = atom->latin1Range(nogc);
    320    return StringToTypedArrayIndex(chars);
    321  }
    322 
    323  mozilla::Range<const char16_t> chars = atom->twoByteRange(nogc);
    324  return StringToTypedArrayIndex(chars);
    325 }
    326 
    327 bool SetTypedArrayElement(JSContext* cx, Handle<TypedArrayObject*> obj,
    328                          uint64_t index, HandleValue v,
    329                          ObjectOpResult& result);
    330 
    331 /*
    332 * Implements [[DefineOwnProperty]] for TypedArrays when the property
    333 * key is a TypedArray index.
    334 */
    335 bool DefineTypedArrayElement(JSContext* cx, Handle<TypedArrayObject*> obj,
    336                             uint64_t index, Handle<PropertyDescriptor> desc,
    337                             ObjectOpResult& result);
    338 
    339 void TypedArrayFillInt32(TypedArrayObject* obj, int32_t fillValue,
    340                         intptr_t start, intptr_t end);
    341 
    342 void TypedArrayFillInt64(TypedArrayObject* obj, int64_t fillValue,
    343                         intptr_t start, intptr_t end);
    344 
    345 void TypedArrayFillDouble(TypedArrayObject* obj, double fillValue,
    346                          intptr_t start, intptr_t end);
    347 
    348 void TypedArrayFillFloat32(TypedArrayObject* obj, float fillValue,
    349                           intptr_t start, intptr_t end);
    350 
    351 void TypedArrayFillBigInt(TypedArrayObject* obj, BigInt* fillValue,
    352                          intptr_t start, intptr_t end);
    353 
    354 bool TypedArraySet(JSContext* cx, TypedArrayObject* target,
    355                   TypedArrayObject* source, intptr_t offset);
    356 
    357 void TypedArraySetInfallible(TypedArrayObject* target, TypedArrayObject* source,
    358                             intptr_t offset);
    359 
    360 bool TypedArraySetFromSubarray(JSContext* cx, TypedArrayObject* target,
    361                               TypedArrayObject* source, intptr_t offset,
    362                               intptr_t sourceOffset, intptr_t sourceLength);
    363 
    364 void TypedArraySetFromSubarrayInfallible(TypedArrayObject* target,
    365                                         TypedArrayObject* source,
    366                                         intptr_t offset, intptr_t sourceOffset,
    367                                         intptr_t sourceLength);
    368 
    369 TypedArrayObject* TypedArraySubarray(JSContext* cx,
    370                                     Handle<TypedArrayObject*> obj,
    371                                     intptr_t start, intptr_t end);
    372 
    373 TypedArrayObject* TypedArraySubarrayWithLength(JSContext* cx,
    374                                               Handle<TypedArrayObject*> obj,
    375                                               intptr_t start, intptr_t length);
    376 
    377 TypedArrayObject* TypedArraySubarrayRecover(JSContext* cx,
    378                                            Handle<TypedArrayObject*> obj,
    379                                            intptr_t start, intptr_t length);
    380 
    381 static inline constexpr unsigned TypedArrayShift(Scalar::Type viewType) {
    382  switch (viewType) {
    383    case Scalar::Int8:
    384    case Scalar::Uint8:
    385    case Scalar::Uint8Clamped:
    386      return 0;
    387    case Scalar::Int16:
    388    case Scalar::Uint16:
    389    case Scalar::Float16:
    390      return 1;
    391    case Scalar::Int32:
    392    case Scalar::Uint32:
    393    case Scalar::Float32:
    394      return 2;
    395    case Scalar::BigInt64:
    396    case Scalar::BigUint64:
    397    case Scalar::Int64:
    398    case Scalar::Float64:
    399      return 3;
    400    default:
    401      MOZ_CRASH("Unexpected array type");
    402  }
    403 }
    404 
    405 static inline constexpr unsigned TypedArrayElemSize(Scalar::Type viewType) {
    406  return 1u << TypedArrayShift(viewType);
    407 }
    408 
    409 /**
    410 * Check if |targetType| and |sourceType| have compatible bit-level
    411 * representations to allow bitwise copying.
    412 */
    413 constexpr bool CanUseBitwiseCopy(Scalar::Type targetType,
    414                                 Scalar::Type sourceType) {
    415  switch (targetType) {
    416    case Scalar::Int8:
    417    case Scalar::Uint8:
    418      return sourceType == Scalar::Int8 || sourceType == Scalar::Uint8 ||
    419             sourceType == Scalar::Uint8Clamped;
    420 
    421    case Scalar::Uint8Clamped:
    422      return sourceType == Scalar::Uint8 || sourceType == Scalar::Uint8Clamped;
    423 
    424    case Scalar::Int16:
    425    case Scalar::Uint16:
    426      return sourceType == Scalar::Int16 || sourceType == Scalar::Uint16;
    427 
    428    case Scalar::Int32:
    429    case Scalar::Uint32:
    430      return sourceType == Scalar::Int32 || sourceType == Scalar::Uint32;
    431 
    432    case Scalar::Float16:
    433      return sourceType == Scalar::Float16;
    434 
    435    case Scalar::Float32:
    436      return sourceType == Scalar::Float32;
    437 
    438    case Scalar::Float64:
    439      return sourceType == Scalar::Float64;
    440 
    441    case Scalar::BigInt64:
    442    case Scalar::BigUint64:
    443      return sourceType == Scalar::BigInt64 || sourceType == Scalar::BigUint64;
    444 
    445    case Scalar::MaxTypedArrayViewType:
    446    case Scalar::Int64:
    447    case Scalar::Simd128:
    448      // GCC8 doesn't like MOZ_CRASH in constexpr functions, so we can't use it
    449      // here to catch invalid typed array types.
    450      break;
    451  }
    452  return false;
    453 }
    454 
    455 extern ArraySortResult TypedArraySortFromJit(
    456    JSContext* cx, jit::TrampolineNativeFrameLayout* frame);
    457 
    458 }  // namespace js
    459 
    460 template <>
    461 inline bool JSObject::is<js::TypedArrayObject>() const {
    462  return js::IsTypedArrayClass(getClass());
    463 }
    464 
    465 template <>
    466 inline bool JSObject::is<js::FixedLengthTypedArrayObject>() const {
    467  return js::IsFixedLengthTypedArrayClass(getClass());
    468 }
    469 
    470 template <>
    471 inline bool JSObject::is<js::ResizableTypedArrayObject>() const {
    472  return js::IsResizableTypedArrayClass(getClass());
    473 }
    474 
    475 template <>
    476 inline bool JSObject::is<js::ImmutableTypedArrayObject>() const {
    477  return js::IsImmutableTypedArrayClass(getClass());
    478 }
    479 
    480 #endif /* vm_TypedArrayObject_h */