Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/js/src/vm/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 33 kB image not shown  

SSL ArrayBufferObject.h   Interaktion und
PortierbarkeitC

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#ifndef vm_ArrayBufferObject_h
#define vm_ArrayBufferObject_h

#include "mozilla/Maybe.h"

#include <tuple>  // std::tuple

#include "builtin/TypedArrayConstants.h"
#include "gc/Memory.h"
#include "gc/ZoneAllocator.h"
#include "js/ArrayBuffer.h"
#include "js/GCHashTable.h"
#include "vm/JSFunction.h"
#include "vm/JSObject.h"
#include "vm/SharedMem.h"
#include "wasm/WasmMemory.h"

namespace js {

class JS_PUBLIC_API GenericPrinter;
class JSONPrinter;

class ArrayBufferViewObject;
class AutoSetNewObjectMetadata;
class WasmArrayRawBuffer;

namespace wasm {
struct MemoryDesc;
}  // namespace wasm

// Create a new mapping of size `mappedSize` with an initially committed prefix
// of size `initialCommittedSize`.  Both arguments denote bytes and must be
// multiples of the page size, with `initialCommittedSize` <= `mappedSize`.
// Returns nullptr on failure.
void* MapBufferMemory(wasm::AddressType, size_t mappedSize,
                      size_t initialCommittedSize);

// Commit additional memory in an existing mapping.  `dataEnd` must be the
// correct value for the end of the existing committed area, and `delta` must be
// a byte amount to grow the mapping by, and must be a multiple of the page
// size.  Returns false on failure.
bool CommitBufferMemory(void* dataEnd, size_t delta);

// Extend an existing mapping by adding uncommited pages to it.  `dataStart`
// must be the pointer to the start of the existing mapping, `mappedSize` the
// size of the existing mapping, and `newMappedSize` the size of the extended
// mapping (sizes in bytes), with `mappedSize` <= `newMappedSize`.  Both sizes
// must be divisible by the page size.  Returns false on failure.
bool ExtendBufferMapping(void* dataStart, size_t mappedSize,
                         size_t newMappedSize);

// Remove an existing mapping.  `dataStart` must be the pointer to the start of
// the mapping, and `mappedSize` the size of that mapping.
void UnmapBufferMemory(wasm::AddressType t, void* dataStart, size_t mappedSize);

// Return the number of bytes currently reserved for WebAssembly memory
uint64_t WasmReservedBytes();

// The inheritance hierarchy for the various classes relating to typed arrays
// is as follows.
//
//
// - JSObject
//   - NativeObject
//     - ArrayBufferObjectMaybeShared
//       - ArrayBufferObject
//         - FixedLengthArrayBufferObject
//         - ResizableArrayBufferObject
//       - SharedArrayBufferObject
//         - FixedLengthSharedArrayBufferObject
//         - GrowableSharedArrayBufferObject
//     - ArrayBufferViewObject
//       - DataViewObject
//         - FixedLengthDataViewObject
//         - ResizableDataViewObject
//       - TypedArrayObject (declared in vm/TypedArrayObject.h)
//         - FixedLengthTypedArrayObject
//           - FixedLengthTypedArrayObjectTemplate<NativeType>, also inheriting
//             from TypedArrayObjectTemplate<NativeType>
//             - FixedLengthTypedArrayObjectTemplate<int8_t>
//             - FixedLengthTypedArrayObjectTemplate<uint8_t>
//             - ...
//         - ResizableTypedArrayObject
//           - ResizableTypedArrayObjectTemplate<NativeType>, also inheriting
//             from TypedArrayObjectTemplate<NativeType>
//             - ResizableTypedArrayObjectTemplate<int8_t>
//             - ResizableTypedArrayObjectTemplate<uint8_t>
//             - ...
//
// Note that |{FixedLength,Resizable}TypedArrayObjectTemplate| is just an
// implementation detail that makes implementing its various subclasses easier.
//
// FixedLengthArrayBufferObject and ResizableArrayBufferObject are also
// implementation specific types to differentiate between fixed-length and
// resizable ArrayBuffers.
//
// ArrayBufferObject and SharedArrayBufferObject are unrelated data types:
// the racy memory of the latter cannot substitute for the non-racy memory of
// the former; the non-racy memory of the former cannot be used with the
// atomics; the former can be detached and the latter not.  Hence they have been
// separated completely.
//
// Most APIs will only accept ArrayBufferObject.  ArrayBufferObjectMaybeShared
// exists as a join point to allow APIs that can take or use either, notably
// AsmJS.
//
// In contrast with the separation of ArrayBufferObject and
// SharedArrayBufferObject, the TypedArray types can map either.
//
// The possible data ownership and reference relationships with ArrayBuffers
// and related classes are enumerated below. These are the possible locations
// for typed data:
//
// (1) malloc'ed or mmap'ed data owned by an ArrayBufferObject.
// (2) Data allocated inline with an ArrayBufferObject.
// (3) Data allocated inline with a TypedArrayObject.
//
// An ArrayBufferObject may point to any of these sources of data, except (3).
// All array buffer views may point to any of these sources of data, except
// that (3) may only be pointed to by the typed array the data is inline with.
//
// During a minor GC, (3) may move. During a compacting GC, (2) and (3) may
// move.

class ArrayBufferObjectMaybeShared;

wasm::AddressType WasmArrayBufferAddressType(
    const ArrayBufferObjectMaybeShared* buf);
wasm::Pages WasmArrayBufferPages(const ArrayBufferObjectMaybeShared* buf);
wasm::Pages WasmArrayBufferClampedMaxPages(
    const ArrayBufferObjectMaybeShared* buf);
mozilla::Maybe<wasm::Pages> WasmArrayBufferSourceMaxPages(
    const ArrayBufferObjectMaybeShared* buf);
size_t WasmArrayBufferMappedSize(const ArrayBufferObjectMaybeShared* buf);

class ArrayBufferObjectMaybeShared : public NativeObject {
 public:
  inline size_t byteLength() const;
  inline bool isDetached() const;
  inline bool isResizable() const;
  inline SharedMem<uint8_t*> dataPointerEither();

  inline bool pinLength(bool pin);

  // WebAssembly support:
  // Note: the eventual goal is to remove this from ArrayBuffer and have
  // (Shared)ArrayBuffers alias memory owned by some wasm::Memory object.

  wasm::AddressType wasmAddressType() const {
    return WasmArrayBufferAddressType(this);
  }
  wasm::Pages wasmPages() const { return WasmArrayBufferPages(this); }
  wasm::Pages wasmClampedMaxPages() const {
    return WasmArrayBufferClampedMaxPages(this);
  }
  mozilla::Maybe<wasm::Pages> wasmSourceMaxPages() const {
    return WasmArrayBufferSourceMaxPages(this);
  }
  size_t wasmMappedSize() const { return WasmArrayBufferMappedSize(this); }

  inline bool isPreparedForAsmJS() const;
  inline bool isWasm() const;
};

class FixedLengthArrayBufferObject;
class ResizableArrayBufferObject;

/*
 * ArrayBufferObject
 *
 * This class holds the underlying raw buffer that the various ArrayBufferViews
 * (DataViewObject and the TypedArrays) access. It can be created explicitly and
 * used to construct an ArrayBufferView, or can be created lazily when it is
 * first accessed for a TypedArrayObject that doesn't have an explicit buffer.
 *
 * ArrayBufferObject is an abstract base class and has exactly two concrete
 * subclasses, FixedLengthArrayBufferObject and ResizableArrayBufferObject.
 *
 * ArrayBufferObject (or really the underlying memory) /is not racy/: the
 * memory is private to a single worker.
 */

class ArrayBufferObject : public ArrayBufferObjectMaybeShared {
  static bool byteLengthGetterImpl(JSContext* cx, const CallArgs& args);
  static bool maxByteLengthGetterImpl(JSContext* cx, const CallArgs& args);
  static bool resizableGetterImpl(JSContext* cx, const CallArgs& args);
  static bool detachedGetterImpl(JSContext* cx, const CallArgs& args);
  static bool resizeImpl(JSContext* cx, const CallArgs& args);
  static bool transferImpl(JSContext* cx, const CallArgs& args);
  static bool transferToFixedLengthImpl(JSContext* cx, const CallArgs& args);

 public:
  static const uint8_t DATA_SLOT = 0;
  static const uint8_t BYTE_LENGTH_SLOT = 1;
  static const uint8_t FIRST_VIEW_SLOT = 2;
  static const uint8_t FLAGS_SLOT = 3;

  static const uint8_t RESERVED_SLOTS = 4;

  // Alignment for ArrayBuffer objects. Must match the largest possible
  // TypedArray scalar to ensure TypedArray and Atomics accesses are always
  // aligned.
  static constexpr size_t ARRAY_BUFFER_ALIGNMENT = 8;

  static_assert(FLAGS_SLOT == JS_ARRAYBUFFER_FLAGS_SLOT,
                "self-hosted code with burned-in constants must get the "
                "right flags slot");

  // The length of an ArrayBuffer or SharedArrayBuffer can be at most INT32_MAX
  // on 32-bit platforms. Allow a larger limit on 64-bit platforms.
  static constexpr size_t ByteLengthLimitForSmallBuffer = INT32_MAX;
#ifdef JS_64BIT
  static constexpr size_t ByteLengthLimit =
      size_t(8) * 1024 * 1024 * 1024;  // 8 GB.
#else
  static constexpr size_t ByteLengthLimit = ByteLengthLimitForSmallBuffer;
#endif

 public:
  enum BufferKind {
    /** Inline data kept in the repurposed slots of this ArrayBufferObject. */
    INLINE_DATA = 0b000,

    /*
     * Data allocated using the SpiderMonkey allocator, created within
     * js::ArrayBufferContentsArena.
     */

    MALLOCED_ARRAYBUFFER_CONTENTS_ARENA = 0b001,

    /**
     * No bytes are associated with this buffer.  (This could be because the
     * buffer is detached, because it's an internal, newborn buffer not yet
     * overwritten with user-exposable semantics, or some other reason.  The
     * point is, don't read precise language semantics into this kind.)
     */

    NO_DATA = 0b010,

    /**
     * User-owned memory.  The associated buffer must be manually detached
     * before the user invalidates (deallocates, reuses the storage of, &c.)
     * the user-owned memory.
     */

    USER_OWNED = 0b011,

    WASM = 0b100,
    MAPPED = 0b101,
    EXTERNAL = 0b110,

    /**
     * Data allocated using the SpiderMonkey allocator, created within an
     * unknown memory arena.
     */

    MALLOCED_UNKNOWN_ARENA = 0b111,

    KIND_MASK = 0b111
  };

 public:
  enum ArrayBufferFlags {
    // The flags also store the BufferKind
    BUFFER_KIND_MASK = BufferKind::KIND_MASK,

    DETACHED = 0b1000,

    // Resizable ArrayBuffer.
    RESIZABLE = 0b1'0000,

    // This MALLOCED, MAPPED, or EXTERNAL buffer has been prepared for asm.js
    // and cannot henceforth be transferred/detached.  (WASM, USER_OWNED, and
    // INLINE_DATA buffers can't be prepared for asm.js -- although if an
    // INLINE_DATA buffer is used with asm.js, it's silently rewritten into a
    // MALLOCED buffer which *can* be prepared.)
    FOR_ASMJS = 0b10'0000,

    // The length is temporarily pinned, so it should not be detached. In the
    // future, this will also prevent GrowableArrayBuffer/ResizeableArrayBuffer
    // from modifying the length while this is set.
    PINNED_LENGTH = 0b100'0000
  };

  static_assert(JS_ARRAYBUFFER_DETACHED_FLAG == DETACHED,
                "self-hosted code with burned-in constants must use the "
                "correct DETACHED bit value");

 protected:
  enum class FillContents { Zero, Uninitialized };

  template <class ArrayBufferType, FillContents FillType>
  static std::tuple<ArrayBufferType*, uint8_t*>
  createUninitializedBufferAndData(JSContext* cx, size_t nbytes,
                                   AutoSetNewObjectMetadata&,
                                   JS::Handle<JSObject*> proto);

  template <FillContents FillType>
  static std::tuple<ArrayBufferObject*, uint8_t*> createBufferAndData(
      JSContext* cx, size_t nbytes, AutoSetNewObjectMetadata& metadata,
      JS::Handle<JSObject*> proto = nullptr);

 public:
  class BufferContents {
    uint8_t* data_;
    BufferKind kind_;
    JS::BufferContentsFreeFunc free_;
    void* freeUserData_;

    friend class ArrayBufferObject;
    friend class ResizableArrayBufferObject;

    BufferContents(uint8_t* data, BufferKind kind,
                   JS::BufferContentsFreeFunc freeFunc = nullptr,
                   void* freeUserData = nullptr)
        : data_(data),
          kind_(kind),
          free_(freeFunc),
          freeUserData_(freeUserData) {
      MOZ_ASSERT((kind_ & ~KIND_MASK) == 0);
      MOZ_ASSERT_IF(free_ || freeUserData_, kind_ == EXTERNAL);

      // It is the caller's responsibility to ensure that the
      // BufferContents does not outlive the data.
    }

#ifdef DEBUG
    // Checks if the buffer contents are properly aligned.
    //
    // `malloc(0)` is implementation defined and may return a pointer which
    // isn't aligned to `max_align_t`, so we only require proper alignment when
    // `byteLength` is non-zero.
    //
    // jemalloc doesn't implement restriction, but instead uses `sizeof(void*)`
    // for its smallest allocation class. Larger allocations are guaranteed to
    // be eight byte aligned.
    bool isAligned(size_t byteLength) const {
      // `malloc(0)` has implementation defined behavior.
      if (byteLength == 0) {
        return true;
      }

      // Allow jemalloc tiny allocations to have smaller alignment requirements
      // than `std::malloc`.
      if (sizeof(void*) < ArrayBufferObject::ARRAY_BUFFER_ALIGNMENT) {
        if (byteLength <= sizeof(void*)) {
          return true;
        }
      }

      // `std::malloc` returns memory at least as strictly aligned as for
      // max_align_t and the alignment of max_align_t is a multiple of the array
      // buffer alignment.
      static_assert(alignof(std::max_align_t) %
                        ArrayBufferObject::ARRAY_BUFFER_ALIGNMENT ==
                    0);

      // Otherwise the memory must be correctly alignment.
      auto ptr = reinterpret_cast<uintptr_t>(data());
      return ptr % ArrayBufferObject::ARRAY_BUFFER_ALIGNMENT == 0;
    }
#endif

   public:
    static BufferContents createInlineData(void* data) {
      return BufferContents(static_cast<uint8_t*>(data), INLINE_DATA);
    }

    static BufferContents createMallocedArrayBufferContentsArena(void* data) {
      return BufferContents(static_cast<uint8_t*>(data),
                            MALLOCED_ARRAYBUFFER_CONTENTS_ARENA);
    }

    static BufferContents createMallocedUnknownArena(void* data) {
      return BufferContents(static_cast<uint8_t*>(data),
                            MALLOCED_UNKNOWN_ARENA);
    }

    static BufferContents createNoData() {
      return BufferContents(nullptr, NO_DATA);
    }

    static BufferContents createUserOwned(void* data) {
      return BufferContents(static_cast<uint8_t*>(data), USER_OWNED);
    }

    static BufferContents createWasm(void* data) {
      return BufferContents(static_cast<uint8_t*>(data), WASM);
    }

    static BufferContents createMapped(void* data) {
      return BufferContents(static_cast<uint8_t*>(data), MAPPED);
    }

    static BufferContents createExternal(void* data,
                                         JS::BufferContentsFreeFunc freeFunc,
                                         void* freeUserData = nullptr) {
      MOZ_ASSERT(freeFunc);
      return BufferContents(static_cast<uint8_t*>(data), EXTERNAL, freeFunc,
                            freeUserData);
    }

    static BufferContents createFailed() {
      // There's no harm in tagging this as MALLOCED_ARRAYBUFFER_CONTENTS_ARENA,
      // even tho obviously it isn't. And adding an extra tag purely for this
      // case is a complication that presently appears avoidable.
      return BufferContents(nullptr, MALLOCED_ARRAYBUFFER_CONTENTS_ARENA);
    }

    uint8_t* data() const { return data_; }
    BufferKind kind() const { return kind_; }
    JS::BufferContentsFreeFunc freeFunc() const { return free_; }
    void* freeUserData() const { return freeUserData_; }

    explicit operator bool() const { return data_ != nullptr; }
    WasmArrayRawBuffer* wasmBuffer() const;
  };

  static const JSClass protoClass_;

  static bool byteLengthGetter(JSContext* cx, unsigned argc, Value* vp);

  static bool maxByteLengthGetter(JSContext* cx, unsigned argc, Value* vp);

  static bool resizableGetter(JSContext* cx, unsigned argc, Value* vp);

  static bool detachedGetter(JSContext* cx, unsigned argc, Value* vp);

  static bool fun_isView(JSContext* cx, unsigned argc, Value* vp);

  static bool resize(JSContext* cx, unsigned argc, Value* vp);

  static bool transfer(JSContext* cx, unsigned argc, Value* vp);

  static bool transferToFixedLength(JSContext* cx, unsigned argc, Value* vp);

  static bool class_constructor(JSContext* cx, unsigned argc, Value* vp);

  static bool isOriginalByteLengthGetter(Native native) {
    return native == byteLengthGetter;
  }

  static ArrayBufferObject* createForContents(JSContext* cx, size_t nbytes,
                                              BufferContents contents);

  static ArrayBufferObject* copy(JSContext* cx, size_t newByteLength,
                                 JS::Handle<ArrayBufferObject*> source);

  static ArrayBufferObject* copyAndDetach(
      JSContext* cx, size_t newByteLength,
      JS::Handle<ArrayBufferObject*> source);

 private:
  static ArrayBufferObject* copyAndDetachSteal(
      JSContext* cx, JS::Handle<ArrayBufferObject*> source);

  static ArrayBufferObject* copyAndDetachRealloc(
      JSContext* cx, size_t newByteLength,
      JS::Handle<ArrayBufferObject*> source);

 public:
  static ArrayBufferObject* createZeroed(JSContext* cx, size_t nbytes,
                                         HandleObject proto = nullptr);

  // Create an ArrayBufferObject that is safely finalizable and can later be
  // initialize()d to become a real, content-visible ArrayBufferObject.
  static ArrayBufferObject* createEmpty(JSContext* cx);

  // Create an ArrayBufferObject using the provided buffer and size.  Assumes
  // ownership of |buffer| even in case of failure, i.e. on failure |buffer|
  // is deallocated.
  static ArrayBufferObject* createFromNewRawBuffer(JSContext* cx,
                                                   WasmArrayRawBuffer* buffer,
                                                   size_t initialSize);

  static void copyData(ArrayBufferObject* toBuffer, size_t toIndex,
                       ArrayBufferObject* fromBuffer, size_t fromIndex,
                       size_t count);

  template <class ArrayBufferType>
  static size_t objectMoved(JSObject* obj, JSObject* old);

  static uint8_t* stealMallocedContents(JSContext* cx,
                                        Handle<ArrayBufferObject*> buffer);

  static BufferContents extractStructuredCloneContents(
      JSContext* cx, Handle<ArrayBufferObject*> buffer);

  static void addSizeOfExcludingThis(JSObject* obj,
                                     mozilla::MallocSizeOf mallocSizeOf,
                                     JS::ClassInfo* info,
                                     JS::RuntimeSizes* runtimeSizes);

  // ArrayBufferObjects (strongly) store the first view added to them, while
  // later views are (weakly) stored in the compartment's InnerViewTable
  // below. Buffers usually only have one view, so this slot optimizes for
  // the common case. Avoiding entries in the InnerViewTable saves memory and
  // non-incrementalized sweep time.
  JSObject* firstView();

  bool addView(JSContext* cx, ArrayBufferViewObject* view);

  // Pin or unpin the length. Returns whether pinned status was changed.
  bool pinLength(bool pin) {
    if (bool(flags() & PINNED_LENGTH) == pin) {
      return false;
    }
    setFlags(flags() ^ PINNED_LENGTH);
    return true;
  }

  static bool ensureNonInline(JSContext* cx, Handle<ArrayBufferObject*> buffer);

  // Detach this buffer from its original memory.  (This necessarily makes
  // views of this buffer unusable for modifying that original memory.)
  static void detach(JSContext* cx, Handle<ArrayBufferObject*> buffer);

  static constexpr size_t offsetOfByteLengthSlot() {
    return getFixedSlotOffset(BYTE_LENGTH_SLOT);
  }
  static constexpr size_t offsetOfFlagsSlot() {
    return getFixedSlotOffset(FLAGS_SLOT);
  }

 protected:
  void setFirstView(ArrayBufferViewObject* view);

 private:
  struct FreeInfo {
    JS::BufferContentsFreeFunc freeFunc;
    void* freeUserData;
  };
  FreeInfo* freeInfo() const;

 public:
  uint8_t* dataPointer() const;
  SharedMem<uint8_t*> dataPointerShared() const;
  size_t byteLength() const;

  BufferContents contents() const {
    if (isExternal()) {
      return BufferContents(dataPointer(), EXTERNAL, freeInfo()->freeFunc,
                            freeInfo()->freeUserData);
    }
    return BufferContents(dataPointer(), bufferKind());
  }

  void releaseData(JS::GCContext* gcx);

  BufferKind bufferKind() const {
    return BufferKind(flags() & BUFFER_KIND_MASK);
  }

  bool isInlineData() const { return bufferKind() == INLINE_DATA; }
  bool isMalloced() const {
    return bufferKind() == MALLOCED_ARRAYBUFFER_CONTENTS_ARENA ||
           bufferKind() == MALLOCED_UNKNOWN_ARENA;
  }
  bool isNoData() const { return bufferKind() == NO_DATA; }
  bool hasUserOwnedData() const { return bufferKind() == USER_OWNED; }

  bool isWasm() const { return bufferKind() == WASM; }
  bool isMapped() const { return bufferKind() == MAPPED; }
  bool isExternal() const { return bufferKind() == EXTERNAL; }

  bool isDetached() const { return flags() & DETACHED; }
  bool isResizable() const { return flags() & RESIZABLE; }
  bool isLengthPinned() const { return flags() & PINNED_LENGTH; }
  bool isPreparedForAsmJS() const { return flags() & FOR_ASMJS; }

  // Only WASM and asm.js buffers have a non-undefined [[ArrayBufferDetachKey]].
  //
  // https://tc39.es/ecma262/#sec-properties-of-the-arraybuffer-instances
  bool hasDefinedDetachKey() const { return isWasm() || isPreparedForAsmJS(); }

  // WebAssembly support:

  /**
   * Prepare this ArrayBuffer for use with asm.js.  Returns true on success,
   * false on failure.  This function reports no errors.
   */

  [[nodiscard]] bool prepareForAsmJS();

  size_t wasmMappedSize() const;

  wasm::AddressType wasmAddressType() const;
  wasm::Pages wasmPages() const;
  wasm::Pages wasmClampedMaxPages() const;
  mozilla::Maybe<wasm::Pages> wasmSourceMaxPages() const;

  [[nodiscard]] static ArrayBufferObject* wasmGrowToPagesInPlace(
      wasm::AddressType t, wasm::Pages newPages,
      Handle<ArrayBufferObject*> oldBuf, JSContext* cx);
  [[nodiscard]] static ArrayBufferObject* wasmMovingGrowToPages(
      wasm::AddressType t, wasm::Pages newPages,
      Handle<ArrayBufferObject*> oldBuf, JSContext* cx);
  static void wasmDiscard(Handle<ArrayBufferObject*> buf, uint64_t byteOffset,
                          uint64_t byteLength);

  static void finalize(JS::GCContext* gcx, JSObject* obj);

  static BufferContents createMappedContents(int fd, size_t offset,
                                             size_t length);

 protected:
  void setDataPointer(BufferContents contents);
  void setByteLength(size_t length);

  /**
   * Return the byte length for fixed-length buffers or the maximum byte length
   * for resizable buffers.
   */

  inline size_t maxByteLength() const;

  size_t associatedBytes() const;

  uint32_t flags() const;
  void setFlags(uint32_t flags);

  void setIsDetached() {
    MOZ_ASSERT(!(flags() & PINNED_LENGTH));
    setFlags(flags() | DETACHED);
  }
  void setIsPreparedForAsmJS() {
    MOZ_ASSERT(!isWasm());
    MOZ_ASSERT(!hasUserOwnedData());
    MOZ_ASSERT(!isInlineData());
    MOZ_ASSERT(isMalloced() || isMapped() || isExternal());
    setFlags(flags() | FOR_ASMJS);
  }

  void initialize(size_t byteLength, BufferContents contents) {
    MOZ_ASSERT(contents.isAligned(byteLength));
    setByteLength(byteLength);
    setFlags(0);
    setFirstView(nullptr);
    setDataPointer(contents);
  }

 public:
#if defined(DEBUG) || defined(JS_JITSPEW)
  void dumpOwnFields(js::JSONPrinter& json) const;
  void dumpOwnStringContent(js::GenericPrinter& out) const;
#endif
};

/**
 * FixedLengthArrayBufferObject
 *
 * ArrayBuffer object with a fixed length. Its length is unmodifiable, except
 * when zeroing it for detached buffers. Supports all possible memory stores
 * for ArrayBuffer objects, including inline data, malloc'ed memory, mapped
 * memory, and user-owner memory.
 *
 * Fixed-length ArrayBuffers can be used for asm.js and WebAssembly.
 */

class FixedLengthArrayBufferObject : public ArrayBufferObject {
  friend class ArrayBufferObject;

  uint8_t* inlineDataPointer() const;

  bool hasInlineData() const { return dataPointer() == inlineDataPointer(); }

 public:
  // Fixed-length ArrayBuffer objects don't have any additional reserved slots.
  static const uint8_t RESERVED_SLOTS = ArrayBufferObject::RESERVED_SLOTS;

  /** The largest number of bytes that can be stored inline. */
  static constexpr size_t MaxInlineBytes =
      (NativeObject::MAX_FIXED_SLOTS - RESERVED_SLOTS) * sizeof(JS::Value);

  static const JSClass class_;
};

/**
 * ResizableArrayBufferObject
 *
 * ArrayBuffer object which can both grow and shrink. The maximum byte length it
 * can grow to is set when creating the object. The data of resizable
 * ArrayBuffer object is either stored inline or malloc'ed memory.
 *
 * When a resizable ArrayBuffer object is detached, its maximum byte length
 * slot is set to zero in addition to the byte length slot.
 *
 * Resizable ArrayBuffers can neither be used for asm.js nor WebAssembly.
 */

class ResizableArrayBufferObject : public ArrayBufferObject {
  friend class ArrayBufferObject;

  template <FillContents FillType>
  static std::tuple<ResizableArrayBufferObject*, uint8_t*> createBufferAndData(
      JSContext* cx, size_t byteLength, size_t maxByteLength,
      AutoSetNewObjectMetadata& metadata, Handle<JSObject*> proto);

  static ResizableArrayBufferObject* createEmpty(JSContext* cx);

 public:
  static ResizableArrayBufferObject* createZeroed(
      JSContext* cx, size_t byteLength, size_t maxByteLength,
      Handle<JSObject*> proto = nullptr);

 private:
  uint8_t* inlineDataPointer() const;

  bool hasInlineData() const { return dataPointer() == inlineDataPointer(); }

  void setMaxByteLength(size_t length) {
    MOZ_ASSERT(length <= ArrayBufferObject::ByteLengthLimit);
    setFixedSlot(MAX_BYTE_LENGTH_SLOT, PrivateValue(length));
  }

  void initialize(size_t byteLength, size_t maxByteLength,
                  BufferContents contents) {
    MOZ_ASSERT(contents.isAligned(byteLength));
    setByteLength(byteLength);
    setMaxByteLength(maxByteLength);
    setFlags(RESIZABLE);
    setFirstView(nullptr);
    setDataPointer(contents);
  }

  // Resize this buffer.
  void resize(size_t newByteLength);

  static ResizableArrayBufferObject* copy(
      JSContext* cx, size_t newByteLength,
      JS::Handle<ResizableArrayBufferObject*> source);

 public:
  static const uint8_t MAX_BYTE_LENGTH_SLOT = ArrayBufferObject::RESERVED_SLOTS;

  static const uint8_t RESERVED_SLOTS = ArrayBufferObject::RESERVED_SLOTS + 1;

  /** The largest number of bytes that can be stored inline. */
  static constexpr size_t MaxInlineBytes =
      (NativeObject::MAX_FIXED_SLOTS - RESERVED_SLOTS) * sizeof(JS::Value);

  static const JSClass class_;

  size_t maxByteLength() const {
    return size_t(getFixedSlot(MAX_BYTE_LENGTH_SLOT).toPrivate());
  }

  static ResizableArrayBufferObject* copyAndDetach(
      JSContext* cx, size_t newByteLength,
      JS::Handle<ResizableArrayBufferObject*> source);

 private:
  static ResizableArrayBufferObject* copyAndDetachSteal(
      JSContext* cx, size_t newByteLength,
      JS::Handle<ResizableArrayBufferObject*> source);
};

size_t ArrayBufferObject::maxByteLength() const {
  if (isResizable()) {
    return as<ResizableArrayBufferObject>().maxByteLength();
  }
  return byteLength();
}

// Create a buffer for a wasm memory, whose type is determined by
// memory.addressType().
ArrayBufferObjectMaybeShared* CreateWasmBuffer(JSContext* cx,
                                               const wasm::MemoryDesc& memory);

// Per-compartment table that manages the relationship between array buffers
// and the views that use their storage.
class InnerViewTable {
  // Store views in a vector such that all the tenured views come before any
  // nursery views. Maintain the index of the first nursery view so there is an
  // efficient way to access only the nursery views.
  using ViewVector =
      GCVector<UnsafeBarePtr<ArrayBufferViewObject*>, 1, ZoneAllocPolicy>;
  struct Views {
    ViewVector views;  // List of views with tenured views at the front.
    size_t firstNurseryView = 0;

    explicit Views(JS::Zone* zone) : views(zone) {}
    bool empty();
    bool hasNurseryViews();
    bool addView(ArrayBufferViewObject* view);

    bool traceWeak(JSTracer* trc, size_t startIndex = 0);
    bool sweepAfterMinorGC(JSTracer* trc);

    void check();
  };

  // For all objects sharing their storage with some other view, this maps
  // the object to the list of such views. All entries in this map are weak.
  //
  // This key is a raw pointer and not a WeakHeapPtr because the post-barrier
  // would hold nursery-allocated entries live unconditionally. It is a very
  // common pattern in low-level and performance-oriented JavaScript to create
  // hundreds or thousands of very short lived temporary views on a larger
  // buffer; having to tenure all of these would be a catastrophic performance
  // regression. Thus, it is vital that nursery pointers in this map not be held
  // live. Special support is required in the minor GC, implemented in
  // sweepAfterMinorGC.
  using ArrayBufferViewMap =
      GCHashMap<UnsafeBarePtr<ArrayBufferObject*>, Views,
                StableCellHasher<JSObject*>, ZoneAllocPolicy>;
  ArrayBufferViewMap map;

  // List of keys from map where either the source or at least one target is in
  // the nursery. The raw pointer to a JSObject is allowed here because this
  // vector is cleared after every minor collection. Users in sweepAfterMinorGC
  // must be careful to use MaybeForwarded before touching these pointers.
  using NurseryKeysVector =
      GCVector<UnsafeBarePtr<ArrayBufferObject*>, 0, SystemAllocPolicy>;
  NurseryKeysVector nurseryKeys;

  // Whether nurseryKeys is a complete list.
  bool nurseryKeysValid = true;

  bool sweepMapEntryAfterMinorGC(UnsafeBarePtr<JSObject*>& buffer,
                                 ViewVector& views);

 public:
  explicit InnerViewTable(Zone* zone) : map(zone) {}

  // Remove references to dead objects in the table and update table entries
  // to reflect moved objects.
  bool traceWeak(JSTracer* trc);
  void sweepAfterMinorGC(JSTracer* trc);

  bool empty() const { return map.empty(); }

  bool needsSweepAfterMinorGC() const {
    return !nurseryKeys.empty() || !nurseryKeysValid;
  }

  size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);

 private:
  friend class ArrayBufferObject;
  friend class ResizableArrayBufferObject;
  bool addView(JSContext* cx, ArrayBufferObject* buffer,
               ArrayBufferViewObject* view);
  ViewVector* maybeViewsUnbarriered(ArrayBufferObject* buffer);
  void removeViews(ArrayBufferObject* buffer);

  bool sweepViewsAfterMinorGC(JSTracer* trc, ArrayBufferObject* buffer,
                              Views& views);
};

template <typename Wrapper>
class MutableWrappedPtrOperations<InnerViewTable, Wrapper>
    : public WrappedPtrOperations<InnerViewTable, Wrapper> {
  InnerViewTable& table() { return static_cast<Wrapper*>(this)->get(); }

 public:
  size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
    return table().sizeOfExcludingThis(mallocSizeOf);
  }
};

class WasmArrayRawBuffer {
  wasm::AddressType addressType_;
  wasm::Pages clampedMaxPages_;
  mozilla::Maybe<wasm::Pages> sourceMaxPages_;
  size_t mappedSize_;  // Not including the header page
  size_t length_;

 protected:
  WasmArrayRawBuffer(wasm::AddressType addressType, uint8_t* buffer,
                     wasm::Pages clampedMaxPages,
                     const mozilla::Maybe<wasm::Pages>& sourceMaxPages,
                     size_t mappedSize, size_t length)
      : addressType_(addressType),
        clampedMaxPages_(clampedMaxPages),
        sourceMaxPages_(sourceMaxPages),
        mappedSize_(mappedSize),
        length_(length) {
    MOZ_ASSERT(buffer == dataPointer());
  }

 public:
  static WasmArrayRawBuffer* AllocateWasm(
      wasm::AddressType addressType, wasm::Pages initialPages,
      wasm::Pages clampedMaxPages,
      const mozilla::Maybe<wasm::Pages>& sourceMaxPages,
      const mozilla::Maybe<size_t>& mappedSize);
  static void Release(void* mem);

  uint8_t* dataPointer() {
    uint8_t* ptr = reinterpret_cast<uint8_t*>(this);
    return ptr + sizeof(WasmArrayRawBuffer);
  }

  static const WasmArrayRawBuffer* fromDataPtr(const uint8_t* dataPtr) {
    return reinterpret_cast<const WasmArrayRawBuffer*>(
        dataPtr - sizeof(WasmArrayRawBuffer));
  }

  static WasmArrayRawBuffer* fromDataPtr(uint8_t* dataPtr) {
    return reinterpret_cast<WasmArrayRawBuffer*>(dataPtr -
                                                 sizeof(WasmArrayRawBuffer));
  }

  wasm::AddressType addressType() const { return addressType_; }

  uint8_t* basePointer() { return dataPointer() - gc::SystemPageSize(); }

  size_t mappedSize() const { return mappedSize_; }

  size_t byteLength() const { return length_; }

  wasm::Pages pages() const {
    return wasm::Pages::fromByteLengthExact(length_);
  }

  wasm::Pages clampedMaxPages() const { return clampedMaxPages_; }

  mozilla::Maybe<wasm::Pages> sourceMaxPages() const { return sourceMaxPages_; }

  [[nodiscard]] bool growToPagesInPlace(wasm::Pages newPages);

  [[nodiscard]] bool extendMappedSize(wasm::Pages maxPages);

  // Try and grow the mapped region of memory. Does not change current size.
  // Does not move memory if no space to grow.
  void tryGrowMaxPagesInPlace(wasm::Pages deltaMaxPages);

  // Discard a region of memory, zeroing the pages and releasing physical memory
  // back to the operating system. byteOffset and byteLen must be wasm page
  // aligned and in bounds. A discard of zero bytes will have no effect.
  void discard(size_t byteOffset, size_t byteLen);
};

}  // namespace js

template <>
inline bool JSObject::is<js::ArrayBufferObject>() const {
  return is<js::FixedLengthArrayBufferObject>() ||
         is<js::ResizableArrayBufferObject>();
}

template <>
bool JSObject::is<js::ArrayBufferObjectMaybeShared>() const;

#endif  // vm_ArrayBufferObject_h

Messung V0.5
C=89 H=97 G=93

¤ Dauer der Verarbeitung: 0.17 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.