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_TypedArrayObject_h michael@0: #define vm_TypedArrayObject_h michael@0: michael@0: #include "jsobj.h" michael@0: michael@0: #include "builtin/TypedObject.h" michael@0: #include "gc/Barrier.h" michael@0: #include "js/Class.h" michael@0: #include "vm/ArrayBufferObject.h" michael@0: michael@0: typedef struct JSProperty JSProperty; michael@0: michael@0: namespace js { michael@0: michael@0: /* michael@0: * TypedArrayObject michael@0: * michael@0: * The non-templated base class for the specific typed implementations. michael@0: * This class holds all the member variables that are used by michael@0: * the subclasses. michael@0: */ michael@0: michael@0: class TypedArrayObject : public ArrayBufferViewObject michael@0: { michael@0: protected: michael@0: // Typed array properties stored in slots, beyond those shared by all michael@0: // ArrayBufferViews. michael@0: static const size_t LENGTH_SLOT = JS_TYPEDOBJ_SLOT_LENGTH; michael@0: static const size_t TYPE_SLOT = JS_TYPEDOBJ_SLOT_TYPE_DESCR; michael@0: static const size_t RESERVED_SLOTS = JS_TYPEDOBJ_SLOTS; michael@0: static const size_t DATA_SLOT = JS_TYPEDOBJ_SLOT_DATA; michael@0: michael@0: static_assert(js::detail::TypedArrayLengthSlot == LENGTH_SLOT, michael@0: "bad inlined constant in jsfriendapi.h"); michael@0: michael@0: public: michael@0: static const Class classes[ScalarTypeDescr::TYPE_MAX]; michael@0: static const Class protoClasses[ScalarTypeDescr::TYPE_MAX]; michael@0: michael@0: static const size_t FIXED_DATA_START = DATA_SLOT + 1; michael@0: michael@0: // For typed arrays which can store their data inline, the array buffer michael@0: // object is created lazily. michael@0: static const uint32_t INLINE_BUFFER_LIMIT = michael@0: (JSObject::MAX_FIXED_SLOTS - FIXED_DATA_START) * sizeof(Value); michael@0: michael@0: static gc::AllocKind michael@0: AllocKindForLazyBuffer(size_t nbytes) michael@0: { michael@0: JS_ASSERT(nbytes <= INLINE_BUFFER_LIMIT); michael@0: /* For GGC we need at least one slot in which to store a forwarding pointer. */ michael@0: size_t dataSlots = Max(size_t(1), AlignBytes(nbytes, sizeof(Value)) / sizeof(Value)); michael@0: JS_ASSERT(nbytes <= dataSlots * sizeof(Value)); michael@0: return gc::GetGCObjectKind(FIXED_DATA_START + dataSlots); michael@0: } michael@0: michael@0: static Value bufferValue(TypedArrayObject *tarr) { michael@0: return tarr->getFixedSlot(BUFFER_SLOT); michael@0: } michael@0: static Value byteOffsetValue(TypedArrayObject *tarr) { michael@0: return tarr->getFixedSlot(BYTEOFFSET_SLOT); michael@0: } michael@0: static Value byteLengthValue(TypedArrayObject *tarr) { michael@0: return tarr->getFixedSlot(BYTELENGTH_SLOT); michael@0: } michael@0: static Value lengthValue(TypedArrayObject *tarr) { michael@0: return tarr->getFixedSlot(LENGTH_SLOT); michael@0: } michael@0: michael@0: static bool michael@0: ensureHasBuffer(JSContext *cx, Handle tarray); michael@0: michael@0: ArrayBufferObject *sharedBuffer() const; michael@0: ArrayBufferObject *buffer() const { michael@0: JSObject *obj = bufferValue(const_cast(this)).toObjectOrNull(); michael@0: if (!obj) michael@0: return nullptr; michael@0: if (obj->is()) michael@0: return &obj->as(); michael@0: return sharedBuffer(); michael@0: } michael@0: uint32_t byteOffset() const { michael@0: return byteOffsetValue(const_cast(this)).toInt32(); michael@0: } michael@0: uint32_t byteLength() const { michael@0: return byteLengthValue(const_cast(this)).toInt32(); michael@0: } michael@0: uint32_t length() const { michael@0: return lengthValue(const_cast(this)).toInt32(); michael@0: } michael@0: michael@0: uint32_t type() const { michael@0: return getFixedSlot(TYPE_SLOT).toInt32(); michael@0: } michael@0: void *viewData() const { michael@0: // Keep synced with js::GetArrayLengthAndData in jsfriendapi.h! michael@0: return static_cast(getPrivate(DATA_SLOT)); michael@0: } michael@0: michael@0: Value getElement(uint32_t index); michael@0: static void setElement(TypedArrayObject &obj, uint32_t index, double d); michael@0: michael@0: void neuter(void *newData); michael@0: michael@0: static uint32_t slotWidth(int atype) { michael@0: switch (atype) { michael@0: case ScalarTypeDescr::TYPE_INT8: michael@0: case ScalarTypeDescr::TYPE_UINT8: michael@0: case ScalarTypeDescr::TYPE_UINT8_CLAMPED: michael@0: return 1; michael@0: case ScalarTypeDescr::TYPE_INT16: michael@0: case ScalarTypeDescr::TYPE_UINT16: michael@0: return 2; michael@0: case ScalarTypeDescr::TYPE_INT32: michael@0: case ScalarTypeDescr::TYPE_UINT32: michael@0: case ScalarTypeDescr::TYPE_FLOAT32: michael@0: return 4; michael@0: case ScalarTypeDescr::TYPE_FLOAT64: michael@0: return 8; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("invalid typed array type"); michael@0: } michael@0: } michael@0: michael@0: int slotWidth() { michael@0: return slotWidth(type()); michael@0: } michael@0: michael@0: /* michael@0: * Byte length above which created typed arrays and data views will have michael@0: * singleton types regardless of the context in which they are created. michael@0: */ michael@0: static const uint32_t SINGLETON_TYPE_BYTE_LENGTH = 1024 * 1024 * 10; michael@0: michael@0: static int lengthOffset(); michael@0: static int dataOffset(); michael@0: }; michael@0: michael@0: inline bool michael@0: IsTypedArrayClass(const Class *clasp) michael@0: { michael@0: return &TypedArrayObject::classes[0] <= clasp && michael@0: clasp < &TypedArrayObject::classes[ScalarTypeDescr::TYPE_MAX]; michael@0: } michael@0: michael@0: inline bool michael@0: IsTypedArrayProtoClass(const Class *clasp) michael@0: { michael@0: return &TypedArrayObject::protoClasses[0] <= clasp && michael@0: clasp < &TypedArrayObject::protoClasses[ScalarTypeDescr::TYPE_MAX]; michael@0: } michael@0: michael@0: bool michael@0: IsTypedArrayConstructor(HandleValue v, uint32_t type); michael@0: michael@0: bool michael@0: IsTypedArrayBuffer(HandleValue v); michael@0: michael@0: ArrayBufferObject & michael@0: AsTypedArrayBuffer(HandleValue v); michael@0: michael@0: // Return value is whether the string is some integer. If the string is an michael@0: // integer which is not representable as a uint64_t, the return value is true michael@0: // and the resulting index is UINT64_MAX. michael@0: bool michael@0: StringIsTypedArrayIndex(JSLinearString *str, uint64_t *indexp); michael@0: michael@0: inline bool michael@0: IsTypedArrayIndex(jsid id, uint64_t *indexp) michael@0: { michael@0: if (JSID_IS_INT(id)) { michael@0: int32_t i = JSID_TO_INT(id); michael@0: JS_ASSERT(i >= 0); michael@0: *indexp = (double)i; michael@0: return true; michael@0: } michael@0: michael@0: if (MOZ_UNLIKELY(!JSID_IS_STRING(id))) michael@0: return false; michael@0: michael@0: JSAtom *atom = JSID_TO_ATOM(id); michael@0: michael@0: jschar c = atom->chars()[0]; michael@0: if (!JS7_ISDEC(c) && c != '-') michael@0: return false; michael@0: michael@0: return StringIsTypedArrayIndex(atom, indexp); michael@0: } michael@0: michael@0: static inline unsigned michael@0: TypedArrayShift(ArrayBufferView::ViewType viewType) michael@0: { michael@0: switch (viewType) { michael@0: case ArrayBufferView::TYPE_INT8: michael@0: case ArrayBufferView::TYPE_UINT8: michael@0: case ArrayBufferView::TYPE_UINT8_CLAMPED: michael@0: return 0; michael@0: case ArrayBufferView::TYPE_INT16: michael@0: case ArrayBufferView::TYPE_UINT16: michael@0: return 1; michael@0: case ArrayBufferView::TYPE_INT32: michael@0: case ArrayBufferView::TYPE_UINT32: michael@0: case ArrayBufferView::TYPE_FLOAT32: michael@0: return 2; michael@0: case ArrayBufferView::TYPE_FLOAT64: michael@0: return 3; michael@0: default:; michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected array type"); michael@0: } michael@0: michael@0: class DataViewObject : public ArrayBufferViewObject michael@0: { michael@0: static const size_t RESERVED_SLOTS = JS_DATAVIEW_SLOTS; michael@0: static const size_t DATA_SLOT = JS_TYPEDOBJ_SLOT_DATA; michael@0: michael@0: private: michael@0: static const Class protoClass; michael@0: michael@0: static bool is(HandleValue v) { michael@0: return v.isObject() && v.toObject().hasClass(&class_); michael@0: } michael@0: michael@0: template michael@0: static uint8_t * michael@0: getDataPointer(JSContext *cx, Handle obj, uint32_t offset); michael@0: michael@0: template michael@0: static bool michael@0: getterImpl(JSContext *cx, CallArgs args); michael@0: michael@0: template michael@0: static bool michael@0: getter(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: template michael@0: static bool michael@0: defineGetter(JSContext *cx, PropertyName *name, HandleObject proto); michael@0: michael@0: public: michael@0: static const Class class_; michael@0: michael@0: static Value byteOffsetValue(DataViewObject *view) { michael@0: Value v = view->getReservedSlot(BYTEOFFSET_SLOT); michael@0: JS_ASSERT(v.toInt32() >= 0); michael@0: return v; michael@0: } michael@0: michael@0: static Value byteLengthValue(DataViewObject *view) { michael@0: Value v = view->getReservedSlot(BYTELENGTH_SLOT); michael@0: JS_ASSERT(v.toInt32() >= 0); michael@0: return v; michael@0: } michael@0: michael@0: static Value bufferValue(DataViewObject *view) { michael@0: return view->getReservedSlot(BUFFER_SLOT); michael@0: } michael@0: michael@0: uint32_t byteOffset() const { michael@0: return byteOffsetValue(const_cast(this)).toInt32(); michael@0: } michael@0: michael@0: uint32_t byteLength() const { michael@0: return byteLengthValue(const_cast(this)).toInt32(); michael@0: } michael@0: michael@0: ArrayBufferObject &arrayBuffer() const { michael@0: return bufferValue(const_cast(this)).toObject().as(); michael@0: } michael@0: michael@0: void *dataPointer() const { michael@0: return getPrivate(); michael@0: } michael@0: michael@0: static bool class_constructor(JSContext *cx, unsigned argc, Value *vp); michael@0: static bool constructWithProto(JSContext *cx, unsigned argc, Value *vp); michael@0: static bool construct(JSContext *cx, JSObject *bufobj, const CallArgs &args, michael@0: HandleObject proto); michael@0: michael@0: static inline DataViewObject * michael@0: create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength, michael@0: Handle arrayBuffer, JSObject *proto); michael@0: michael@0: static bool getInt8Impl(JSContext *cx, CallArgs args); michael@0: static bool fun_getInt8(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: static bool getUint8Impl(JSContext *cx, CallArgs args); michael@0: static bool fun_getUint8(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: static bool getInt16Impl(JSContext *cx, CallArgs args); michael@0: static bool fun_getInt16(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: static bool getUint16Impl(JSContext *cx, CallArgs args); michael@0: static bool fun_getUint16(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: static bool getInt32Impl(JSContext *cx, CallArgs args); michael@0: static bool fun_getInt32(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: static bool getUint32Impl(JSContext *cx, CallArgs args); michael@0: static bool fun_getUint32(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: static bool getFloat32Impl(JSContext *cx, CallArgs args); michael@0: static bool fun_getFloat32(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: static bool getFloat64Impl(JSContext *cx, CallArgs args); michael@0: static bool fun_getFloat64(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: static bool setInt8Impl(JSContext *cx, CallArgs args); michael@0: static bool fun_setInt8(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: static bool setUint8Impl(JSContext *cx, CallArgs args); michael@0: static bool fun_setUint8(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: static bool setInt16Impl(JSContext *cx, CallArgs args); michael@0: static bool fun_setInt16(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: static bool setUint16Impl(JSContext *cx, CallArgs args); michael@0: static bool fun_setUint16(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: static bool setInt32Impl(JSContext *cx, CallArgs args); michael@0: static bool fun_setInt32(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: static bool setUint32Impl(JSContext *cx, CallArgs args); michael@0: static bool fun_setUint32(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: static bool setFloat32Impl(JSContext *cx, CallArgs args); michael@0: static bool fun_setFloat32(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: static bool setFloat64Impl(JSContext *cx, CallArgs args); michael@0: static bool fun_setFloat64(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: static bool initClass(JSContext *cx); michael@0: static void neuter(JSObject *view); michael@0: template michael@0: static bool read(JSContext *cx, Handle obj, michael@0: CallArgs &args, NativeType *val, const char *method); michael@0: template michael@0: static bool write(JSContext *cx, Handle obj, michael@0: CallArgs &args, const char *method); michael@0: michael@0: void neuter(void *newData); michael@0: michael@0: private: michael@0: static const JSFunctionSpec jsfuncs[]; michael@0: }; michael@0: michael@0: static inline int32_t michael@0: ClampIntForUint8Array(int32_t x) michael@0: { michael@0: if (x < 0) michael@0: return 0; michael@0: if (x > 255) michael@0: return 255; michael@0: return x; michael@0: } michael@0: michael@0: } // namespace js michael@0: michael@0: template <> michael@0: inline bool michael@0: JSObject::is() const michael@0: { michael@0: return js::IsTypedArrayClass(getClass()); michael@0: } michael@0: michael@0: template <> michael@0: inline bool michael@0: JSObject::is() const michael@0: { michael@0: return is() || is() || michael@0: IsTypedObjectClass(getClass()); michael@0: } michael@0: michael@0: #endif /* vm_TypedArrayObject_h */