summaryrefslogtreecommitdiffstats
path: root/js/src/vm/ArrayBufferObject.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm/ArrayBufferObject.h')
-rw-r--r--js/src/vm/ArrayBufferObject.h641
1 files changed, 641 insertions, 0 deletions
diff --git a/js/src/vm/ArrayBufferObject.h b/js/src/vm/ArrayBufferObject.h
new file mode 100644
index 000000000..6614f5220
--- /dev/null
+++ b/js/src/vm/ArrayBufferObject.h
@@ -0,0 +1,641 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 "jsobj.h"
+
+#include "builtin/TypedObjectConstants.h"
+#include "js/GCHashTable.h"
+#include "vm/Runtime.h"
+#include "vm/SharedMem.h"
+
+typedef struct JSProperty JSProperty;
+
+namespace js {
+
+class ArrayBufferViewObject;
+class WasmArrayRawBuffer;
+
+// The inheritance hierarchy for the various classes relating to typed arrays
+// is as follows.
+//
+// - NativeObject
+// - ArrayBufferObjectMaybeShared
+// - ArrayBufferObject
+// - SharedArrayBufferObject
+// - DataViewObject
+// - TypedArrayObject (declared in vm/TypedArrayObject.h)
+// - TypedArrayObjectTemplate
+// - Int8ArrayObject
+// - Uint8ArrayObject
+// - ...
+// - JSObject
+// - ArrayBufferViewObject
+// - TypedObject (declared in builtin/TypedObject.h)
+//
+// Note that |TypedArrayObjectTemplate| is just an implementation
+// detail that makes implementing its various subclasses easier.
+//
+// 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.
+// (4) Data allocated inline with an InlineTypedObject.
+//
+// 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) and (4) may move. During a compacting GC, (2), (3),
+// and (4) may move.
+
+class ArrayBufferObjectMaybeShared;
+
+uint32_t AnyArrayBufferByteLength(const ArrayBufferObjectMaybeShared* buf);
+mozilla::Maybe<uint32_t> WasmArrayBufferMaxSize(const ArrayBufferObjectMaybeShared* buf);
+size_t WasmArrayBufferMappedSize(const ArrayBufferObjectMaybeShared* buf);
+bool WasmArrayBufferGrowForWasm(ArrayBufferObjectMaybeShared* buf, uint32_t delta);
+bool AnyArrayBufferIsPreparedForAsmJS(const ArrayBufferObjectMaybeShared* buf);
+ArrayBufferObjectMaybeShared& AsAnyArrayBuffer(HandleValue val);
+
+class ArrayBufferObjectMaybeShared : public NativeObject
+{
+ public:
+ uint32_t byteLength() {
+ return AnyArrayBufferByteLength(this);
+ }
+
+ inline bool isDetached() const;
+
+ inline SharedMem<uint8_t*> dataPointerEither();
+
+ // WebAssembly support:
+ // Note: the eventual goal is to remove this from ArrayBuffer and have
+ // (Shared)ArrayBuffers alias memory owned by some wasm::Memory object.
+
+ mozilla::Maybe<uint32_t> wasmMaxSize() const {
+ return WasmArrayBufferMaxSize(this);
+ }
+ size_t wasmMappedSize() const {
+ return WasmArrayBufferMappedSize(this);
+ }
+#ifndef WASM_HUGE_MEMORY
+ uint32_t wasmBoundsCheckLimit() const;
+#endif
+
+ bool isPreparedForAsmJS() const {
+ return AnyArrayBufferIsPreparedForAsmJS(this);
+ }
+};
+
+typedef Rooted<ArrayBufferObjectMaybeShared*> RootedArrayBufferObjectMaybeShared;
+typedef Handle<ArrayBufferObjectMaybeShared*> HandleArrayBufferObjectMaybeShared;
+typedef MutableHandle<ArrayBufferObjectMaybeShared*> MutableHandleArrayBufferObjectMaybeShared;
+
+/*
+ * ArrayBufferObject
+ *
+ * This class holds the underlying raw buffer that the various ArrayBufferViews
+ * (eg DataViewObject, the TypedArrays, TypedObjects) 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 or TypedObject that
+ * doesn't have an explicit buffer.
+ *
+ * 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 fun_slice_impl(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;
+
+ static const 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");
+
+ public:
+
+ enum OwnsState {
+ DoesntOwnData = 0,
+ OwnsData = 1,
+ };
+
+ enum BufferKind {
+ PLAIN = 0, // malloced or inline data
+ WASM = 1,
+ MAPPED = 2,
+
+ KIND_MASK = 0x3
+ };
+
+ protected:
+
+ enum ArrayBufferFlags {
+ // The flags also store the BufferKind
+ BUFFER_KIND_MASK = BufferKind::KIND_MASK,
+
+ DETACHED = 0x4,
+
+ // The dataPointer() is owned by this buffer and should be released
+ // when no longer in use. Releasing the pointer may be done by either
+ // freeing or unmapping it, and how to do this is determined by the
+ // buffer's other flags.
+ //
+ // Array buffers which do not own their data include buffers that
+ // allocate their data inline, and buffers that are created lazily for
+ // typed objects with inline storage, in which case the buffer points
+ // directly to the typed object's storage.
+ OWNS_DATA = 0x8,
+
+ // This array buffer was created lazily for a typed object with inline
+ // data. This implies both that the typed object owns the buffer's data
+ // and that the list of views sharing this buffer's data might be
+ // incomplete. Any missing views will be typed objects.
+ FOR_INLINE_TYPED_OBJECT = 0x10,
+
+ // Views of this buffer might include typed objects.
+ TYPED_OBJECT_VIEWS = 0x20,
+
+ // This PLAIN or WASM buffer has been prepared for asm.js and cannot
+ // henceforth be transferred/detached.
+ FOR_ASMJS = 0x40
+ };
+
+ static_assert(JS_ARRAYBUFFER_DETACHED_FLAG == DETACHED,
+ "self-hosted code with burned-in constants must use the "
+ "correct DETACHED bit value");
+ public:
+
+ class BufferContents {
+ uint8_t* data_;
+ BufferKind kind_;
+
+ friend class ArrayBufferObject;
+
+ BufferContents(uint8_t* data, BufferKind kind) : data_(data), kind_(kind) {
+ MOZ_ASSERT((kind_ & ~KIND_MASK) == 0);
+ }
+
+ public:
+
+ template<BufferKind Kind>
+ static BufferContents create(void* data)
+ {
+ return BufferContents(static_cast<uint8_t*>(data), Kind);
+ }
+
+ static BufferContents createPlain(void* data)
+ {
+ return BufferContents(static_cast<uint8_t*>(data), PLAIN);
+ }
+
+ uint8_t* data() const { return data_; }
+ BufferKind kind() const { return kind_; }
+
+ explicit operator bool() const { return data_ != nullptr; }
+ WasmArrayRawBuffer* wasmBuffer() const;
+ };
+
+ static const Class class_;
+
+ static bool byteLengthGetter(JSContext* cx, unsigned argc, Value* vp);
+
+ static bool fun_slice(JSContext* cx, unsigned argc, Value* vp);
+
+ static bool fun_isView(JSContext* cx, unsigned argc, Value* vp);
+
+ static bool fun_species(JSContext* cx, unsigned argc, Value* vp);
+
+ static bool class_constructor(JSContext* cx, unsigned argc, Value* vp);
+
+ static ArrayBufferObject* create(JSContext* cx, uint32_t nbytes,
+ BufferContents contents,
+ OwnsState ownsState = OwnsData,
+ HandleObject proto = nullptr,
+ NewObjectKind newKind = GenericObject);
+ static ArrayBufferObject* create(JSContext* cx, uint32_t nbytes,
+ HandleObject proto = nullptr,
+ NewObjectKind newKind = GenericObject);
+
+ // 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);
+
+ static bool createDataViewForThisImpl(JSContext* cx, const CallArgs& args);
+ static bool createDataViewForThis(JSContext* cx, unsigned argc, Value* vp);
+
+ template<typename T>
+ static bool createTypedArrayFromBufferImpl(JSContext* cx, const CallArgs& args);
+
+ template<typename T>
+ static bool createTypedArrayFromBuffer(JSContext* cx, unsigned argc, Value* vp);
+
+ static void copyData(Handle<ArrayBufferObject*> toBuffer,
+ Handle<ArrayBufferObject*> fromBuffer,
+ uint32_t fromIndex, uint32_t count);
+
+ static void trace(JSTracer* trc, JSObject* obj);
+ static void objectMoved(JSObject* obj, const JSObject* old);
+
+ static BufferContents externalizeContents(JSContext* cx,
+ Handle<ArrayBufferObject*> buffer,
+ bool hasStealableContents);
+ static BufferContents stealContents(JSContext* cx,
+ Handle<ArrayBufferObject*> buffer,
+ bool hasStealableContents);
+
+ bool hasStealableContents() const {
+ // Inline elements strictly adhere to the corresponding buffer.
+ return ownsData() && !isPreparedForAsmJS() && !isWasm();
+ }
+
+ static void addSizeOfExcludingThis(JSObject* obj, mozilla::MallocSizeOf mallocSizeOf,
+ JS::ClassInfo* info);
+
+ // 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.
+ ArrayBufferViewObject* firstView();
+
+ bool addView(JSContext* cx, JSObject* view);
+
+ void setNewData(FreeOp* fop, BufferContents newContents, OwnsState ownsState);
+ void changeContents(JSContext* cx, BufferContents newContents, OwnsState ownsState);
+
+ // 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, BufferContents newContents);
+
+ private:
+ void changeViewContents(JSContext* cx, ArrayBufferViewObject* view,
+ uint8_t* oldDataPointer, BufferContents newContents);
+ void setFirstView(ArrayBufferViewObject* view);
+
+ uint8_t* inlineDataPointer() const;
+
+ public:
+ uint8_t* dataPointer() const;
+ SharedMem<uint8_t*> dataPointerShared() const;
+ uint32_t byteLength() const;
+
+ BufferContents contents() const {
+ return BufferContents(dataPointer(), bufferKind());
+ }
+ bool hasInlineData() const {
+ return dataPointer() == inlineDataPointer();
+ }
+
+ void releaseData(FreeOp* fop);
+
+ /*
+ * Check if the arrayBuffer contains any data. This will return false for
+ * ArrayBuffer.prototype and detached ArrayBuffers.
+ */
+ bool hasData() const {
+ return getClass() == &class_;
+ }
+
+ BufferKind bufferKind() const { return BufferKind(flags() & BUFFER_KIND_MASK); }
+ bool isPlain() const { return bufferKind() == PLAIN; }
+ bool isWasm() const { return bufferKind() == WASM; }
+ bool isMapped() const { return bufferKind() == MAPPED; }
+ bool isDetached() const { return flags() & DETACHED; }
+ bool isPreparedForAsmJS() const { return flags() & FOR_ASMJS; }
+
+ // WebAssembly support:
+ static ArrayBufferObject* createForWasm(JSContext* cx, uint32_t initialSize,
+ mozilla::Maybe<uint32_t> maxSize);
+ static MOZ_MUST_USE bool prepareForAsmJS(JSContext* cx, Handle<ArrayBufferObject*> buffer,
+ bool needGuard);
+ size_t wasmMappedSize() const;
+ mozilla::Maybe<uint32_t> wasmMaxSize() const;
+ static MOZ_MUST_USE bool wasmGrowToSizeInPlace(uint32_t newSize,
+ Handle<ArrayBufferObject*> oldBuf,
+ MutableHandle<ArrayBufferObject*> newBuf,
+ JSContext* cx);
+#ifndef WASM_HUGE_MEMORY
+ static MOZ_MUST_USE bool wasmMovingGrowToSize(uint32_t newSize,
+ Handle<ArrayBufferObject*> oldBuf,
+ MutableHandle<ArrayBufferObject*> newBuf,
+ JSContext* cx);
+ uint32_t wasmBoundsCheckLimit() const;
+#endif
+
+ static void finalize(FreeOp* fop, JSObject* obj);
+
+ static BufferContents createMappedContents(int fd, size_t offset, size_t length);
+
+ static size_t offsetOfFlagsSlot() {
+ return getFixedSlotOffset(FLAGS_SLOT);
+ }
+ static size_t offsetOfDataSlot() {
+ return getFixedSlotOffset(DATA_SLOT);
+ }
+
+ void setForInlineTypedObject() {
+ setFlags(flags() | FOR_INLINE_TYPED_OBJECT);
+ }
+ void setHasTypedObjectViews() {
+ setFlags(flags() | TYPED_OBJECT_VIEWS);
+ }
+
+ bool forInlineTypedObject() const { return flags() & FOR_INLINE_TYPED_OBJECT; }
+
+ protected:
+ void setDataPointer(BufferContents contents, OwnsState ownsState);
+ void setByteLength(uint32_t length);
+
+ uint32_t flags() const;
+ void setFlags(uint32_t flags);
+
+ bool ownsData() const { return flags() & OWNS_DATA; }
+ void setOwnsData(OwnsState owns) {
+ setFlags(owns ? (flags() | OWNS_DATA) : (flags() & ~OWNS_DATA));
+ }
+
+ bool hasTypedObjectViews() const { return flags() & TYPED_OBJECT_VIEWS; }
+
+ void setIsDetached() { setFlags(flags() | DETACHED); }
+ void setIsPreparedForAsmJS() { setFlags(flags() | FOR_ASMJS); }
+
+ void initialize(size_t byteLength, BufferContents contents, OwnsState ownsState) {
+ setByteLength(byteLength);
+ setFlags(0);
+ setFirstView(nullptr);
+ setDataPointer(contents, ownsState);
+ }
+
+ // Note: initialize() may be called after initEmpty(); initEmpty() must
+ // only initialize the ArrayBufferObject to a safe, finalizable state.
+ void initEmpty() {
+ setByteLength(0);
+ setFlags(0);
+ setFirstView(nullptr);
+ setDataPointer(BufferContents::createPlain(nullptr), DoesntOwnData);
+ }
+};
+
+typedef Rooted<ArrayBufferObject*> RootedArrayBufferObject;
+typedef Handle<ArrayBufferObject*> HandleArrayBufferObject;
+typedef MutableHandle<ArrayBufferObject*> MutableHandleArrayBufferObject;
+
+/*
+ * ArrayBufferViewObject
+ *
+ * Common definitions shared by all array buffer views.
+ */
+
+class ArrayBufferViewObject : public JSObject
+{
+ public:
+ static ArrayBufferObjectMaybeShared* bufferObject(JSContext* cx, Handle<ArrayBufferViewObject*> obj);
+
+ void notifyBufferDetached(JSContext* cx, void* newData);
+
+#ifdef DEBUG
+ bool isSharedMemory();
+#endif
+
+ // By construction we only need unshared variants here. See
+ // comments in ArrayBufferObject.cpp.
+ uint8_t* dataPointerUnshared(const JS::AutoRequireNoGC&);
+ void setDataPointerUnshared(uint8_t* data);
+
+ static void trace(JSTracer* trc, JSObject* obj);
+};
+
+bool
+ToClampedIndex(JSContext* cx, HandleValue v, uint32_t length, uint32_t* out);
+
+/*
+ * Tests for ArrayBufferObject, like obj->is<ArrayBufferObject>().
+ */
+bool IsArrayBuffer(HandleValue v);
+bool IsArrayBuffer(HandleObject obj);
+bool IsArrayBuffer(JSObject* obj);
+ArrayBufferObject& AsArrayBuffer(HandleObject obj);
+ArrayBufferObject& AsArrayBuffer(JSObject* obj);
+
+extern uint32_t JS_FASTCALL
+ClampDoubleToUint8(const double x);
+
+struct uint8_clamped {
+ uint8_t val;
+
+ uint8_clamped() { }
+ uint8_clamped(const uint8_clamped& other) : val(other.val) { }
+
+ // invoke our assignment helpers for constructor conversion
+ explicit uint8_clamped(uint8_t x) { *this = x; }
+ explicit uint8_clamped(uint16_t x) { *this = x; }
+ explicit uint8_clamped(uint32_t x) { *this = x; }
+ explicit uint8_clamped(int8_t x) { *this = x; }
+ explicit uint8_clamped(int16_t x) { *this = x; }
+ explicit uint8_clamped(int32_t x) { *this = x; }
+ explicit uint8_clamped(double x) { *this = x; }
+
+ uint8_clamped& operator=(const uint8_clamped& x) {
+ val = x.val;
+ return *this;
+ }
+
+ uint8_clamped& operator=(uint8_t x) {
+ val = x;
+ return *this;
+ }
+
+ uint8_clamped& operator=(uint16_t x) {
+ val = (x > 255) ? 255 : uint8_t(x);
+ return *this;
+ }
+
+ uint8_clamped& operator=(uint32_t x) {
+ val = (x > 255) ? 255 : uint8_t(x);
+ return *this;
+ }
+
+ uint8_clamped& operator=(int8_t x) {
+ val = (x >= 0) ? uint8_t(x) : 0;
+ return *this;
+ }
+
+ uint8_clamped& operator=(int16_t x) {
+ val = (x >= 0)
+ ? ((x < 255)
+ ? uint8_t(x)
+ : 255)
+ : 0;
+ return *this;
+ }
+
+ uint8_clamped& operator=(int32_t x) {
+ val = (x >= 0)
+ ? ((x < 255)
+ ? uint8_t(x)
+ : 255)
+ : 0;
+ return *this;
+ }
+
+ uint8_clamped& operator=(const double x) {
+ val = uint8_t(ClampDoubleToUint8(x));
+ return *this;
+ }
+
+ operator uint8_t() const {
+ return val;
+ }
+
+ void staticAsserts() {
+ static_assert(sizeof(uint8_clamped) == 1,
+ "uint8_clamped must be layout-compatible with uint8_t");
+ }
+};
+
+/* Note that we can't use std::numeric_limits here due to uint8_clamped. */
+template<typename T> inline bool TypeIsFloatingPoint() { return false; }
+template<> inline bool TypeIsFloatingPoint<float>() { return true; }
+template<> inline bool TypeIsFloatingPoint<double>() { return true; }
+
+template<typename T> inline bool TypeIsUnsigned() { return false; }
+template<> inline bool TypeIsUnsigned<uint8_t>() { return true; }
+template<> inline bool TypeIsUnsigned<uint16_t>() { return true; }
+template<> inline bool TypeIsUnsigned<uint32_t>() { return true; }
+
+// Per-compartment table that manages the relationship between array buffers
+// and the views that use their storage.
+class InnerViewTable
+{
+ public:
+ typedef Vector<ArrayBufferViewObject*, 1, SystemAllocPolicy> ViewVector;
+
+ friend class ArrayBufferObject;
+ friend class WeakCacheBase<InnerViewTable>;
+
+ private:
+ struct MapGCPolicy {
+ static bool needsSweep(JSObject** key, ViewVector* value) {
+ return InnerViewTable::sweepEntry(key, *value);
+ }
+ };
+
+ // This key is a raw pointer and not a ReadBarriered 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 tenured 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.
+ typedef GCHashMap<JSObject*,
+ ViewVector,
+ MovableCellHasher<JSObject*>,
+ SystemAllocPolicy,
+ MapGCPolicy> Map;
+
+ // 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.
+ Map map;
+
+ // List of keys from innerViews 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
+ // sweepAfterMinorCollection must be careful to use MaybeForwarded before
+ // touching these pointers.
+ Vector<JSObject*, 0, SystemAllocPolicy> nurseryKeys;
+
+ // Whether nurseryKeys is a complete list.
+ bool nurseryKeysValid;
+
+ // Sweep an entry during GC, returning whether the entry should be removed.
+ static bool sweepEntry(JSObject** pkey, ViewVector& views);
+
+ bool addView(JSContext* cx, ArrayBufferObject* obj, ArrayBufferViewObject* view);
+ ViewVector* maybeViewsUnbarriered(ArrayBufferObject* obj);
+ void removeViews(ArrayBufferObject* obj);
+
+ public:
+ InnerViewTable()
+ : nurseryKeysValid(true)
+ {}
+
+ // Remove references to dead objects in the table and update table entries
+ // to reflect moved objects.
+ void sweep();
+ void sweepAfterMinorGC();
+
+ bool needsSweepAfterMinorGC() const {
+ return !nurseryKeys.empty() || !nurseryKeysValid;
+ }
+
+ size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
+};
+
+template <>
+class WeakCacheBase<InnerViewTable>
+{
+ InnerViewTable& table() {
+ return static_cast<JS::WeakCache<InnerViewTable>*>(this)->get();
+ }
+ const InnerViewTable& table() const {
+ return static_cast<const JS::WeakCache<InnerViewTable>*>(this)->get();
+ }
+
+ public:
+ InnerViewTable::ViewVector* maybeViewsUnbarriered(ArrayBufferObject* obj) {
+ return table().maybeViewsUnbarriered(obj);
+ }
+ void removeViews(ArrayBufferObject* obj) { table().removeViews(obj); }
+ void sweepAfterMinorGC() { table().sweepAfterMinorGC(); }
+ bool needsSweepAfterMinorGC() const { return table().needsSweepAfterMinorGC(); }
+ size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
+ return table().sizeOfExcludingThis(mallocSizeOf);
+ }
+};
+
+} // namespace js
+
+template <>
+bool
+JSObject::is<js::ArrayBufferViewObject>() const;
+
+template <>
+bool
+JSObject::is<js::ArrayBufferObjectMaybeShared>() const;
+
+#endif // vm_ArrayBufferObject_h