tor-browser

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

ArrayBufferViewObject.h (9976B)


      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_ArrayBufferViewObject_h
      8 #define vm_ArrayBufferViewObject_h
      9 
     10 #include "mozilla/Maybe.h"
     11 
     12 #include "builtin/TypedArrayConstants.h"
     13 #include "vm/ArrayBufferObject.h"
     14 #include "vm/NativeObject.h"
     15 #include "vm/SharedArrayObject.h"
     16 #include "vm/SharedMem.h"
     17 
     18 namespace js {
     19 
     20 class JS_PUBLIC_API GenericPrinter;
     21 class JSONPrinter;
     22 
     23 /*
     24 * ArrayBufferViewObject
     25 *
     26 * Common base class for all array buffer views (DataViewObject and
     27 * TypedArrayObject).
     28 */
     29 
     30 class ArrayBufferViewObject : public NativeObject {
     31 public:
     32  // Underlying (Shared)ArrayBufferObject. ObjectValue if there is
     33  // a buffer. Otherwise, the buffer is implicit because the data
     34  // is held inline, and the buffer slot will store the pinned status
     35  // (FalseValue or TrueValue).
     36  static constexpr size_t BUFFER_SLOT = 0;
     37  static_assert(BUFFER_SLOT == JS_TYPEDARRAYLAYOUT_BUFFER_SLOT,
     38                "self-hosted code with burned-in constants must get the "
     39                "right buffer slot");
     40 
     41  // Slot containing length of the view in number of typed elements.
     42  static constexpr size_t LENGTH_SLOT = 1;
     43 
     44  // Offset of view within underlying (Shared)ArrayBufferObject.
     45  static constexpr size_t BYTEOFFSET_SLOT = 2;
     46 
     47  // Pointer to raw buffer memory.
     48  static constexpr size_t DATA_SLOT = 3;
     49 
     50  static constexpr size_t RESERVED_SLOTS = 4;
     51 
     52  // Additional slots for views on resizable/growable (Shared)ArrayBufferObject.
     53 
     54  static const uint8_t AUTO_LENGTH_SLOT = 4;
     55  static const uint8_t INITIAL_LENGTH_SLOT = 5;
     56  static const uint8_t INITIAL_BYTE_OFFSET_SLOT = 6;
     57 
     58  static constexpr size_t RESIZABLE_RESERVED_SLOTS = 7;
     59 
     60 #ifdef DEBUG
     61  static const uint8_t ZeroLengthArrayData = 0x4A;
     62 #endif
     63 
     64  static constexpr int bufferOffset() {
     65    return NativeObject::getFixedSlotOffset(BUFFER_SLOT);
     66  }
     67  static constexpr int lengthOffset() {
     68    return NativeObject::getFixedSlotOffset(LENGTH_SLOT);
     69  }
     70  static constexpr int byteOffsetOffset() {
     71    return NativeObject::getFixedSlotOffset(BYTEOFFSET_SLOT);
     72  }
     73  static constexpr int dataOffset() {
     74    return NativeObject::getFixedSlotOffset(DATA_SLOT);
     75  }
     76  static constexpr int autoLengthOffset() {
     77    return NativeObject::getFixedSlotOffset(AUTO_LENGTH_SLOT);
     78  }
     79  static constexpr int initialLengthOffset() {
     80    return NativeObject::getFixedSlotOffset(INITIAL_LENGTH_SLOT);
     81  }
     82  static constexpr int initialByteOffsetOffset() {
     83    return NativeObject::getFixedSlotOffset(INITIAL_BYTE_OFFSET_SLOT);
     84  }
     85 
     86 private:
     87  void* dataPointerEither_() const {
     88    // Note, do not check whether shared or not
     89    // Keep synced with js::Get<Type>ArrayLengthAndData in jsfriendapi.h!
     90    return maybePtrFromReservedSlot<void>(DATA_SLOT);
     91  }
     92 
     93 public:
     94  [[nodiscard]] bool init(JSContext* cx, ArrayBufferObjectMaybeShared* buffer,
     95                          size_t byteOffset, size_t length,
     96                          uint32_t bytesPerElement);
     97 
     98  enum class AutoLength : bool { No, Yes };
     99 
    100  [[nodiscard]] bool initResizable(JSContext* cx,
    101                                   ArrayBufferObjectMaybeShared* buffer,
    102                                   size_t byteOffset, size_t length,
    103                                   uint32_t bytesPerElement,
    104                                   AutoLength autoLength);
    105 
    106  static ArrayBufferObjectMaybeShared* ensureBufferObject(
    107      JSContext* cx, Handle<ArrayBufferViewObject*> obj);
    108 
    109  void notifyBufferDetached();
    110  void notifyBufferResized();
    111  void notifyBufferMoved(uint8_t* srcBufStart, uint8_t* dstBufStart);
    112 
    113  void initDataPointer(SharedMem<uint8_t*> viewData) {
    114    // Install a pointer to the buffer location that corresponds
    115    // to offset zero within the typed array.
    116    //
    117    // The following unwrap is safe because the DATA_SLOT is
    118    // accessed only from jitted code and from the
    119    // dataPointerEither_() accessor above; in neither case does the
    120    // raw pointer escape untagged into C++ code.
    121    void* data = viewData.unwrap(/*safe - see above*/);
    122    initReservedSlot(DATA_SLOT, PrivateValue(data));
    123  }
    124 
    125  SharedMem<void*> dataPointerShared() const {
    126    return SharedMem<void*>::shared(dataPointerEither_());
    127  }
    128  SharedMem<void*> dataPointerEither() const {
    129    if (isSharedMemory()) {
    130      return SharedMem<void*>::shared(dataPointerEither_());
    131    }
    132    return SharedMem<void*>::unshared(dataPointerEither_());
    133  }
    134  void* dataPointerUnshared() const {
    135    MOZ_ASSERT(!isSharedMemory());
    136    return dataPointerEither_();
    137  }
    138 
    139  Value bufferValue() const { return getFixedSlot(BUFFER_SLOT); }
    140  bool hasBuffer() const { return bufferValue().isObject(); }
    141 
    142  ArrayBufferObject* bufferUnshared() const {
    143    MOZ_ASSERT(!isSharedMemory());
    144    ArrayBufferObjectMaybeShared* obj = bufferEither();
    145    if (!obj) {
    146      return nullptr;
    147    }
    148    return &obj->as<ArrayBufferObject>();
    149  }
    150  SharedArrayBufferObject* bufferShared() const {
    151    MOZ_ASSERT(isSharedMemory());
    152    ArrayBufferObjectMaybeShared* obj = bufferEither();
    153    if (!obj) {
    154      return nullptr;
    155    }
    156    return &obj->as<SharedArrayBufferObject>();
    157  }
    158  ArrayBufferObjectMaybeShared* bufferEither() const {
    159    JSObject* obj =
    160        bufferValue().isBoolean() ? nullptr : bufferValue().toObjectOrNull();
    161    if (!obj) {
    162      return nullptr;
    163    }
    164    MOZ_ASSERT(isSharedMemory() ? obj->is<SharedArrayBufferObject>()
    165                                : obj->is<ArrayBufferObject>());
    166    return &obj->as<ArrayBufferObjectMaybeShared>();
    167  }
    168 
    169  bool hasDetachedBuffer() const {
    170    // Shared buffers can't be detached.
    171    if (isSharedMemory()) {
    172      return false;
    173    }
    174 
    175    // A typed array with a null buffer has never had its buffer exposed to
    176    // become detached.
    177    ArrayBufferObject* buffer = bufferUnshared();
    178    if (!buffer) {
    179      return false;
    180    }
    181 
    182    return buffer->isDetached();
    183  }
    184 
    185  bool hasResizableBuffer() const;
    186 
    187  bool hasImmutableBuffer() const;
    188 
    189 private:
    190  bool hasDetachedBufferOrIsOutOfBounds() const {
    191    // Shared buffers can't be detached or get out-of-bounds.
    192    if (isSharedMemory()) {
    193      return false;
    194    }
    195 
    196    // A view with a null buffer has never had its buffer exposed to become
    197    // detached or get out-of-bounds.
    198    auto* buffer = bufferUnshared();
    199    if (!buffer) {
    200      return false;
    201    }
    202 
    203    return buffer->isDetached() || (buffer->isResizable() && isOutOfBounds());
    204  }
    205 
    206 public:
    207  bool isLengthPinned() const {
    208    Value buffer = bufferValue();
    209    if (buffer.isBoolean()) {
    210      return buffer.toBoolean();
    211    }
    212    if (isSharedMemory()) {
    213      return true;
    214    }
    215    return bufferUnshared()->isLengthPinned();
    216  }
    217 
    218  bool pinLength(bool pin) {
    219    if (isSharedMemory()) {
    220      // Always pinned, cannot change.
    221      return false;
    222    }
    223 
    224    if (hasBuffer()) {
    225      return bufferUnshared()->pinLength(pin);
    226    }
    227 
    228    // No ArrayBuffer (data is inline in the view). bufferValue() is a
    229    // BooleanValue saying whether the length is currently pinned.
    230    MOZ_ASSERT(bufferValue().isBoolean());
    231 
    232    bool wasPinned = bufferValue().toBoolean();
    233    if (wasPinned == pin) {
    234      return false;
    235    }
    236 
    237    setFixedSlot(BUFFER_SLOT, JS::BooleanValue(pin));
    238    return true;
    239  }
    240 
    241  static bool ensureNonInline(JSContext* cx,
    242                              JS::Handle<ArrayBufferViewObject*> view);
    243 
    244 private:
    245  void computeResizableLengthAndByteOffset(size_t bytesPerElement);
    246 
    247  size_t bytesPerElement() const;
    248 
    249 protected:
    250  size_t lengthSlotValue() const {
    251    return size_t(getFixedSlot(LENGTH_SLOT).toPrivate());
    252  }
    253 
    254  size_t byteOffsetSlotValue() const {
    255    return size_t(getFixedSlot(BYTEOFFSET_SLOT).toPrivate());
    256  }
    257 
    258  /**
    259   * Offset into the buffer's data-pointer. Different from |byteOffset| for
    260   * views on non-detached resizable buffers which are currently out-of-bounds.
    261   */
    262  size_t dataPointerOffset() const;
    263 
    264  /**
    265   * Return the current length, or |Nothing| if the view is detached or
    266   * out-of-bounds.
    267   */
    268  mozilla::Maybe<size_t> length() const;
    269 
    270 public:
    271  /**
    272   * Return the current byteOffset, or |Nothing| if the view is detached or
    273   * out-of-bounds.
    274   */
    275  mozilla::Maybe<size_t> byteOffset() const;
    276 
    277 private:
    278  size_t initialByteOffsetValue() const {
    279    // No assertion for resizable buffers here, because this method is called
    280    // from dataPointerOffset(), which can be called during tracing.
    281    return size_t(getFixedSlot(INITIAL_BYTE_OFFSET_SLOT).toPrivate());
    282  }
    283 
    284 public:
    285  // The following methods can only be called on views for resizable buffers.
    286 
    287  bool isAutoLength() const {
    288    MOZ_ASSERT(hasResizableBuffer());
    289    return getFixedSlot(AUTO_LENGTH_SLOT).toBoolean();
    290  }
    291 
    292  size_t initialLength() const {
    293    MOZ_ASSERT(hasResizableBuffer());
    294    return size_t(getFixedSlot(INITIAL_LENGTH_SLOT).toPrivate());
    295  }
    296 
    297  size_t initialByteOffset() const {
    298    MOZ_ASSERT(hasResizableBuffer());
    299    return initialByteOffsetValue();
    300  }
    301 
    302  bool isOutOfBounds() const {
    303    MOZ_ASSERT(hasResizableBuffer());
    304 
    305    // The view is out-of-bounds if the length and byteOffset slots are both set
    306    // to zero and the initial length or initial byteOffset are non-zero. If the
    307    // initial length and initial byteOffset are both zero, the view can never
    308    // get out-of-bounds.
    309    return lengthSlotValue() == 0 && byteOffsetSlotValue() == 0 &&
    310           (initialLength() > 0 || initialByteOffset() > 0);
    311  }
    312 
    313 public:
    314  static void trace(JSTracer* trc, JSObject* obj);
    315 
    316 #if defined(DEBUG) || defined(JS_JITSPEW)
    317  void dumpOwnFields(js::JSONPrinter& json) const;
    318  void dumpOwnStringContent(js::GenericPrinter& out) const;
    319 #endif
    320 };
    321 
    322 }  // namespace js
    323 
    324 template <>
    325 bool JSObject::is<js::ArrayBufferViewObject>() const;
    326 
    327 #endif  // vm_ArrayBufferViewObject_h