michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef vm_ArrayBufferObject_h michael@0: #define vm_ArrayBufferObject_h michael@0: michael@0: #include "jsobj.h" michael@0: michael@0: #include "builtin/TypedObjectConstants.h" michael@0: #include "vm/Runtime.h" michael@0: michael@0: typedef struct JSProperty JSProperty; michael@0: michael@0: namespace js { michael@0: michael@0: class ArrayBufferViewObject; michael@0: michael@0: // The inheritance hierarchy for the various classes relating to typed arrays michael@0: // is as follows. michael@0: // michael@0: // - JSObject michael@0: // - ArrayBufferObject michael@0: // - SharedArrayBufferObject michael@0: // - ArrayBufferViewObject michael@0: // - DataViewObject michael@0: // - TypedArrayObject (declared in vm/TypedArrayObject.h) michael@0: // - TypedArrayObjectTemplate michael@0: // - Int8ArrayObject michael@0: // - Uint8ArrayObject michael@0: // - ... michael@0: // - TypedObject (declared in builtin/TypedObject.h) michael@0: // michael@0: // Note that |TypedArrayObjectTemplate| is just an implementation michael@0: // detail that makes implementing its various subclasses easier. michael@0: michael@0: typedef Vector ArrayBufferVector; michael@0: michael@0: /* michael@0: * ArrayBufferObject michael@0: * michael@0: * This class holds the underlying raw buffer that the various michael@0: * ArrayBufferViewObject subclasses (DataViewObject and the TypedArrays) michael@0: * access. It can be created explicitly and passed to an ArrayBufferViewObject michael@0: * subclass, or can be created implicitly by constructing a TypedArrayObject michael@0: * with a size. michael@0: */ michael@0: class ArrayBufferObject : public JSObject michael@0: { michael@0: static bool byteLengthGetterImpl(JSContext *cx, CallArgs args); michael@0: static bool fun_slice_impl(JSContext *cx, CallArgs args); michael@0: michael@0: public: michael@0: static const uint8_t DATA_SLOT = 0; michael@0: static const uint8_t BYTE_LENGTH_SLOT = 1; michael@0: static const uint8_t VIEW_LIST_SLOT = 2; michael@0: static const uint8_t FLAGS_SLOT = 3; michael@0: michael@0: static const uint8_t RESERVED_SLOTS = 4; michael@0: michael@0: static const size_t ARRAY_BUFFER_ALIGNMENT = 8; michael@0: michael@0: static const Class class_; michael@0: michael@0: static const Class protoClass; michael@0: static const JSFunctionSpec jsfuncs[]; michael@0: static const JSFunctionSpec jsstaticfuncs[]; michael@0: michael@0: static bool byteLengthGetter(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: static bool fun_slice(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: static bool fun_isView(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: static bool class_constructor(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: static ArrayBufferObject *create(JSContext *cx, uint32_t nbytes, void *contents = nullptr, michael@0: NewObjectKind newKind = GenericObject, bool mapped = false); michael@0: michael@0: static JSObject *createSlice(JSContext *cx, Handle arrayBuffer, michael@0: uint32_t begin, uint32_t end); michael@0: michael@0: static bool createDataViewForThisImpl(JSContext *cx, CallArgs args); michael@0: static bool createDataViewForThis(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: template michael@0: static bool createTypedArrayFromBufferImpl(JSContext *cx, CallArgs args); michael@0: michael@0: template michael@0: static bool createTypedArrayFromBuffer(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: static void obj_trace(JSTracer *trc, JSObject *obj); michael@0: michael@0: static void sweep(JSCompartment *rt); michael@0: michael@0: static void resetArrayBufferList(JSCompartment *rt); michael@0: static bool saveArrayBufferList(JSCompartment *c, ArrayBufferVector &vector); michael@0: static void restoreArrayBufferLists(ArrayBufferVector &vector); michael@0: michael@0: static void *stealContents(JSContext *cx, Handle buffer); michael@0: michael@0: bool hasStealableContents() const { michael@0: // Inline elements strictly adhere to the corresponding buffer. michael@0: if (!ownsData()) michael@0: return false; michael@0: michael@0: // asm.js buffer contents are transferred by copying, just like inline michael@0: // elements. michael@0: if (isAsmJSArrayBuffer()) michael@0: return false; michael@0: michael@0: // Neutered contents aren't transferrable because we want a neutered michael@0: // array's contents to be backed by zeroed memory equal in length to michael@0: // the original buffer contents. Transferring these contents would michael@0: // allocate new ones based on the current byteLength, which is 0 for a michael@0: // neutered array -- not the original byteLength. michael@0: return !isNeutered(); michael@0: } michael@0: michael@0: static void addSizeOfExcludingThis(JSObject *obj, mozilla::MallocSizeOf mallocSizeOf, michael@0: JS::ObjectsExtraSizes *sizes); michael@0: michael@0: void addView(ArrayBufferViewObject *view); michael@0: michael@0: void setNewOwnedData(FreeOp* fop, void *newData); michael@0: void changeContents(JSContext *cx, void *newData); michael@0: michael@0: /* michael@0: * Ensure data is not stored inline in the object. Used when handing back a michael@0: * GC-safe pointer. michael@0: */ michael@0: static bool ensureNonInline(JSContext *cx, Handle buffer); michael@0: michael@0: bool canNeuter(JSContext *cx); michael@0: michael@0: /* Neuter this buffer and all its views. */ michael@0: static void neuter(JSContext *cx, Handle buffer, void *newData); michael@0: michael@0: uint8_t *dataPointer() const; michael@0: size_t byteLength() const; michael@0: michael@0: void releaseData(FreeOp *fop); michael@0: michael@0: /* michael@0: * Check if the arrayBuffer contains any data. This will return false for michael@0: * ArrayBuffer.prototype and neutered ArrayBuffers. michael@0: */ michael@0: bool hasData() const { michael@0: return getClass() == &class_; michael@0: } michael@0: michael@0: bool isAsmJSArrayBuffer() const { return flags() & ASMJS_BUFFER; } michael@0: bool isSharedArrayBuffer() const { return flags() & SHARED_BUFFER; } michael@0: bool isMappedArrayBuffer() const { return flags() & MAPPED_BUFFER; } michael@0: bool isNeutered() const { return flags() & NEUTERED_BUFFER; } michael@0: michael@0: static bool prepareForAsmJS(JSContext *cx, Handle buffer); michael@0: static bool canNeuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer); michael@0: michael@0: static void finalize(FreeOp *fop, JSObject *obj); michael@0: michael@0: static void *createMappedContents(int fd, size_t offset, size_t length); michael@0: michael@0: static size_t flagsOffset() { michael@0: return getFixedSlotOffset(FLAGS_SLOT); michael@0: } michael@0: michael@0: static uint32_t neuteredFlag() { return NEUTERED_BUFFER; } michael@0: michael@0: protected: michael@0: enum OwnsState { michael@0: DoesntOwnData = 0, michael@0: OwnsData = 1, michael@0: }; michael@0: michael@0: void setDataPointer(void *data, OwnsState ownsState); michael@0: void setByteLength(size_t length); michael@0: michael@0: ArrayBufferViewObject *viewList() const; michael@0: void setViewList(ArrayBufferViewObject *viewsHead); michael@0: void setViewListNoBarrier(ArrayBufferViewObject *viewsHead); michael@0: michael@0: enum ArrayBufferFlags { michael@0: // In the gcLiveArrayBuffers list. michael@0: IN_LIVE_LIST = 0x1, michael@0: michael@0: // The dataPointer() is owned by this buffer and should be released michael@0: // when no longer in use. Releasing the pointer may be done by either michael@0: // freeing or unmapping it, and how to do this is determined by the michael@0: // buffer's other flags. michael@0: OWNS_DATA = 0x2, michael@0: michael@0: ASMJS_BUFFER = 0x4, michael@0: SHARED_BUFFER = 0x8, michael@0: MAPPED_BUFFER = 0x10, michael@0: NEUTERED_BUFFER = 0x20 michael@0: }; michael@0: michael@0: uint32_t flags() const; michael@0: void setFlags(uint32_t flags); michael@0: michael@0: bool inLiveList() const { return flags() & IN_LIVE_LIST; } michael@0: void setInLiveList(bool value) { michael@0: setFlags(value ? (flags() | IN_LIVE_LIST) : (flags() & ~IN_LIVE_LIST)); michael@0: } michael@0: michael@0: bool ownsData() const { return flags() & OWNS_DATA; } michael@0: void setOwnsData(OwnsState owns) { michael@0: setFlags(owns ? (flags() | OWNS_DATA) : (flags() & ~OWNS_DATA)); michael@0: } michael@0: michael@0: void setIsAsmJSArrayBuffer() { setFlags(flags() | ASMJS_BUFFER); } michael@0: void setIsSharedArrayBuffer() { setFlags(flags() | SHARED_BUFFER); } michael@0: void setIsMappedArrayBuffer() { setFlags(flags() | MAPPED_BUFFER); } michael@0: void setIsNeutered() { setFlags(flags() | NEUTERED_BUFFER); } michael@0: michael@0: void initialize(size_t byteLength, void *data, OwnsState ownsState) { michael@0: setByteLength(byteLength); michael@0: setFlags(0); michael@0: setViewListNoBarrier(nullptr); michael@0: setDataPointer(data, ownsState); michael@0: } michael@0: michael@0: void releaseAsmJSArray(FreeOp *fop); michael@0: void releaseMappedArray(); michael@0: }; michael@0: michael@0: /* michael@0: * ArrayBufferViewObject michael@0: * michael@0: * Common definitions shared by all ArrayBufferViews. michael@0: */ michael@0: michael@0: class ArrayBufferViewObject : public JSObject michael@0: { michael@0: protected: michael@0: /* Offset of view in underlying ArrayBufferObject */ michael@0: static const size_t BYTEOFFSET_SLOT = JS_TYPEDOBJ_SLOT_BYTEOFFSET; michael@0: michael@0: /* Byte length of view */ michael@0: static const size_t BYTELENGTH_SLOT = JS_TYPEDOBJ_SLOT_BYTELENGTH; michael@0: michael@0: /* Underlying ArrayBufferObject */ michael@0: static const size_t BUFFER_SLOT = JS_TYPEDOBJ_SLOT_OWNER; michael@0: michael@0: /* ArrayBufferObjects point to a linked list of views, chained through this slot */ michael@0: static const size_t NEXT_VIEW_SLOT = JS_TYPEDOBJ_SLOT_NEXT_VIEW; michael@0: michael@0: public: michael@0: static ArrayBufferObject *bufferObject(JSContext *cx, Handle obj); michael@0: michael@0: ArrayBufferViewObject *nextView() const { michael@0: return static_cast(getFixedSlot(NEXT_VIEW_SLOT).toPrivate()); michael@0: } michael@0: michael@0: inline void setNextView(ArrayBufferViewObject *view); michael@0: michael@0: void neuter(void *newData); michael@0: michael@0: static void trace(JSTracer *trc, JSObject *obj); michael@0: michael@0: uint8_t *dataPointer() { michael@0: return static_cast(getPrivate()); michael@0: } michael@0: }; michael@0: michael@0: bool michael@0: ToClampedIndex(JSContext *cx, HandleValue v, uint32_t length, uint32_t *out); michael@0: michael@0: inline void michael@0: PostBarrierTypedArrayObject(JSObject *obj) michael@0: { michael@0: #ifdef JSGC_GENERATIONAL michael@0: JS_ASSERT(obj); michael@0: JSRuntime *rt = obj->runtimeFromMainThread(); michael@0: if (!rt->isHeapBusy() && !IsInsideNursery(rt, obj)) michael@0: rt->gcStoreBuffer.putWholeCell(obj); michael@0: #endif michael@0: } michael@0: michael@0: inline void michael@0: InitArrayBufferViewDataPointer(ArrayBufferViewObject *obj, ArrayBufferObject *buffer, size_t byteOffset) michael@0: { michael@0: /* michael@0: * N.B. The base of the array's data is stored in the object's michael@0: * private data rather than a slot to avoid alignment restrictions michael@0: * on private Values. michael@0: */ michael@0: MOZ_ASSERT(buffer->dataPointer() != nullptr); michael@0: obj->initPrivate(buffer->dataPointer() + byteOffset); michael@0: michael@0: PostBarrierTypedArrayObject(obj); michael@0: } michael@0: michael@0: /* michael@0: * Tests for either ArrayBufferObject or SharedArrayBufferObject. michael@0: * For specific class testing, use e.g., obj->is(). michael@0: */ michael@0: bool IsArrayBuffer(HandleValue v); michael@0: bool IsArrayBuffer(HandleObject obj); michael@0: bool IsArrayBuffer(JSObject *obj); michael@0: ArrayBufferObject &AsArrayBuffer(HandleObject obj); michael@0: ArrayBufferObject &AsArrayBuffer(JSObject *obj); michael@0: michael@0: inline void michael@0: ArrayBufferViewObject::setNextView(ArrayBufferViewObject *view) michael@0: { michael@0: setFixedSlot(NEXT_VIEW_SLOT, PrivateValue(view)); michael@0: PostBarrierTypedArrayObject(this); michael@0: } michael@0: michael@0: extern uint32_t JS_FASTCALL michael@0: ClampDoubleToUint8(const double x); michael@0: michael@0: struct uint8_clamped { michael@0: uint8_t val; michael@0: michael@0: uint8_clamped() { } michael@0: uint8_clamped(const uint8_clamped& other) : val(other.val) { } michael@0: michael@0: // invoke our assignment helpers for constructor conversion michael@0: uint8_clamped(uint8_t x) { *this = x; } michael@0: uint8_clamped(uint16_t x) { *this = x; } michael@0: uint8_clamped(uint32_t x) { *this = x; } michael@0: uint8_clamped(int8_t x) { *this = x; } michael@0: uint8_clamped(int16_t x) { *this = x; } michael@0: uint8_clamped(int32_t x) { *this = x; } michael@0: uint8_clamped(double x) { *this = x; } michael@0: michael@0: uint8_clamped& operator=(const uint8_clamped& x) { michael@0: val = x.val; michael@0: return *this; michael@0: } michael@0: michael@0: uint8_clamped& operator=(uint8_t x) { michael@0: val = x; michael@0: return *this; michael@0: } michael@0: michael@0: uint8_clamped& operator=(uint16_t x) { michael@0: val = (x > 255) ? 255 : uint8_t(x); michael@0: return *this; michael@0: } michael@0: michael@0: uint8_clamped& operator=(uint32_t x) { michael@0: val = (x > 255) ? 255 : uint8_t(x); michael@0: return *this; michael@0: } michael@0: michael@0: uint8_clamped& operator=(int8_t x) { michael@0: val = (x >= 0) ? uint8_t(x) : 0; michael@0: return *this; michael@0: } michael@0: michael@0: uint8_clamped& operator=(int16_t x) { michael@0: val = (x >= 0) michael@0: ? ((x < 255) michael@0: ? uint8_t(x) michael@0: : 255) michael@0: : 0; michael@0: return *this; michael@0: } michael@0: michael@0: uint8_clamped& operator=(int32_t x) { michael@0: val = (x >= 0) michael@0: ? ((x < 255) michael@0: ? uint8_t(x) michael@0: : 255) michael@0: : 0; michael@0: return *this; michael@0: } michael@0: michael@0: uint8_clamped& operator=(const double x) { michael@0: val = uint8_t(ClampDoubleToUint8(x)); michael@0: return *this; michael@0: } michael@0: michael@0: operator uint8_t() const { michael@0: return val; michael@0: } michael@0: michael@0: void staticAsserts() { michael@0: static_assert(sizeof(uint8_clamped) == 1, michael@0: "uint8_clamped must be layout-compatible with uint8_t"); michael@0: } michael@0: }; michael@0: michael@0: /* Note that we can't use std::numeric_limits here due to uint8_clamped. */ michael@0: template inline const bool TypeIsFloatingPoint() { return false; } michael@0: template<> inline const bool TypeIsFloatingPoint() { return true; } michael@0: template<> inline const bool TypeIsFloatingPoint() { return true; } michael@0: michael@0: template inline const bool TypeIsUnsigned() { return false; } michael@0: template<> inline const bool TypeIsUnsigned() { return true; } michael@0: template<> inline const bool TypeIsUnsigned() { return true; } michael@0: template<> inline const bool TypeIsUnsigned() { return true; } michael@0: michael@0: } // namespace js michael@0: michael@0: #endif // vm_ArrayBufferObject_h