/* -*- 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_TypedArrayObject_h #define vm_TypedArrayObject_h #include "mozilla/Attributes.h" #include "mozilla/Maybe.h" #include "mozilla/TextUtils.h" #include "gc/Barrier.h" #include "gc/MaybeRooted.h" #include "js/Class.h" #include "js/experimental/TypedData.h" // js::detail::TypedArrayLengthSlot #include "js/Result.h" #include "js/ScalarType.h" // js::Scalar::Type #include "vm/ArrayBufferObject.h" #include "vm/ArrayBufferViewObject.h" #include "vm/JSObject.h" #include "vm/SharedArrayObject.h" #define JS_FOR_EACH_TYPED_ARRAY(MACRO) \ MACRO(int8_t, Int8) \ MACRO(uint8_t, Uint8) \ MACRO(int16_t, Int16) \ MACRO(uint16_t, Uint16) \ MACRO(int32_t, Int32) \ MACRO(uint32_t, Uint32) \ MACRO(float, Float32) \ MACRO(double, Float64) \ MACRO(uint8_clamped, Uint8Clamped) \ MACRO(int64_t, BigInt64) \ MACRO(uint64_t, BigUint64) namespace js { /* * TypedArrayObject * * The non-templated base class for the specific typed implementations. * This class holds all the member variables that are used by * the subclasses. */ class TypedArrayObject : public ArrayBufferViewObject { public: static_assert(js::detail::TypedArrayLengthSlot == LENGTH_SLOT, "bad inlined constant in TypedData.h"); static bool sameBuffer(Handle a, Handle b) { // Inline buffers. if (!a->hasBuffer() || !b->hasBuffer()) { return a.get() == b.get(); } // Shared buffers. if (a->isSharedMemory() && b->isSharedMemory()) { return a->bufferShared()->globalID() == b->bufferShared()->globalID(); } return a->bufferEither() == b->bufferEither(); } static const JSClass classes[Scalar::MaxTypedArrayViewType]; static const JSClass protoClasses[Scalar::MaxTypedArrayViewType]; static const JSClass sharedTypedArrayPrototypeClass; static const JSClass* classForType(Scalar::Type type) { MOZ_ASSERT(type < Scalar::MaxTypedArrayViewType); return &classes[type]; } static const JSClass* protoClassForType(Scalar::Type type) { MOZ_ASSERT(type < Scalar::MaxTypedArrayViewType); return &protoClasses[type]; } static constexpr size_t FIXED_DATA_START = DATA_SLOT + 1; // For typed arrays which can store their data inline, the array buffer // object is created lazily. static constexpr uint32_t INLINE_BUFFER_LIMIT = (NativeObject::MAX_FIXED_SLOTS - FIXED_DATA_START) * sizeof(Value); static inline gc::AllocKind AllocKindForLazyBuffer(size_t nbytes); inline Scalar::Type type() const; inline size_t bytesPerElement() const; static bool ensureHasBuffer(JSContext* cx, Handle tarray); BufferSize byteLength() const { return BufferSize(length().get() * bytesPerElement()); } BufferSize length() const { return BufferSize(size_t(getFixedSlot(LENGTH_SLOT).toPrivate())); } Value byteLengthValue() const { size_t len = byteLength().get(); return NumberValue(len); } Value lengthValue() const { size_t len = length().get(); return NumberValue(len); } bool hasInlineElements() const; void setInlineElements(); uint8_t* elementsRaw() const { return *(uint8_t**)((((char*)this) + ArrayBufferViewObject::dataOffset())); } uint8_t* elements() const { assertZeroLengthArrayData(); return elementsRaw(); } #ifdef DEBUG void assertZeroLengthArrayData() const; #else void assertZeroLengthArrayData() const {}; #endif template bool getElement(JSContext* cx, size_t index, typename MaybeRooted::MutableHandleType val); bool getElementPure(size_t index, Value* vp); /* * Copy all elements from this typed array to vp. vp must point to rooted * memory. */ static bool getElements(JSContext* cx, Handle tarray, Value* vp); static bool GetTemplateObjectForNative(JSContext* cx, Native native, const JS::HandleValueArray args, MutableHandleObject res); /* * Maximum allowed byte length for any typed array. */ static size_t maxByteLength() { return ArrayBufferObject::maxBufferByteLength(); } static bool isOriginalLengthGetter(Native native); static bool isOriginalByteOffsetGetter(Native native); static void finalize(JSFreeOp* fop, JSObject* obj); static size_t objectMoved(JSObject* obj, JSObject* old); /* Initialization bits */ static const JSFunctionSpec protoFunctions[]; static const JSPropertySpec protoAccessors[]; static const JSFunctionSpec staticFunctions[]; static const JSPropertySpec staticProperties[]; /* Accessors and functions */ static bool is(HandleValue v); static bool set(JSContext* cx, unsigned argc, Value* vp); static bool copyWithin(JSContext* cx, unsigned argc, Value* vp); bool convertForSideEffect(JSContext* cx, HandleValue v) const; private: static bool set_impl(JSContext* cx, const CallArgs& args); static bool copyWithin_impl(JSContext* cx, const CallArgs& args); }; MOZ_MUST_USE bool TypedArray_bufferGetter(JSContext* cx, unsigned argc, Value* vp); extern TypedArrayObject* NewTypedArrayWithTemplateAndLength( JSContext* cx, HandleObject templateObj, int32_t len); extern TypedArrayObject* NewTypedArrayWithTemplateAndArray( JSContext* cx, HandleObject templateObj, HandleObject array); extern TypedArrayObject* NewTypedArrayWithTemplateAndBuffer( JSContext* cx, HandleObject templateObj, HandleObject arrayBuffer, HandleValue byteOffset, HandleValue length); inline bool IsTypedArrayClass(const JSClass* clasp) { return &TypedArrayObject::classes[0] <= clasp && clasp < &TypedArrayObject::classes[Scalar::MaxTypedArrayViewType]; } inline Scalar::Type GetTypedArrayClassType(const JSClass* clasp) { MOZ_ASSERT(IsTypedArrayClass(clasp)); return static_cast(clasp - &TypedArrayObject::classes[0]); } bool IsTypedArrayConstructor(const JSObject* obj); bool IsTypedArrayConstructor(HandleValue v, Scalar::Type type); JSNative TypedArrayConstructorNative(Scalar::Type type); // In WebIDL terminology, a BufferSource is either an ArrayBuffer or a typed // array view. In either case, extract the dataPointer/byteLength. bool IsBufferSource(JSObject* object, SharedMem* dataPointer, size_t* byteLength); inline Scalar::Type TypedArrayObject::type() const { return GetTypedArrayClassType(getClass()); } inline size_t TypedArrayObject::bytesPerElement() const { return Scalar::byteSize(type()); } // ES2020 draft rev a5375bdad264c8aa264d9c44f57408087761069e // 7.1.16 CanonicalNumericIndexString // // Checks whether or not the string is a canonical numeric index string. If the // string is a canonical numeric index which is not representable as a uint64_t, // the returned index is UINT64_MAX. template JS::Result> StringIsTypedArrayIndex( JSContext* cx, mozilla::Range s); // A string |s| is a TypedArray index (or: canonical numeric index string) iff // |s| is "-0" or |SameValue(ToString(ToNumber(s)), s)| is true. So check for // any characters which can start the string representation of a number, // including "NaN" and "Infinity". template inline bool CanStartTypedArrayIndex(CharT ch) { return mozilla::IsAsciiDigit(ch) || ch == '-' || ch == 'N' || ch == 'I'; } inline JS::Result> IsTypedArrayIndex(JSContext* cx, jsid id) { using ResultType = decltype(IsTypedArrayIndex(cx, id)); if (JSID_IS_INT(id)) { int32_t i = JSID_TO_INT(id); MOZ_ASSERT(i >= 0); return mozilla::Some(static_cast(i)); } if (MOZ_UNLIKELY(!JSID_IS_STRING(id))) { return ResultType(mozilla::Nothing()); } JS::AutoCheckCannotGC nogc; JSAtom* atom = JSID_TO_ATOM(id); if (atom->length() == 0) { return ResultType(mozilla::Nothing()); } if (atom->hasLatin1Chars()) { mozilla::Range chars = atom->latin1Range(nogc); if (!CanStartTypedArrayIndex(chars[0])) { return ResultType(mozilla::Nothing()); } return StringIsTypedArrayIndex(cx, chars); } mozilla::Range chars = atom->twoByteRange(nogc); if (!CanStartTypedArrayIndex(chars[0])) { return ResultType(mozilla::Nothing()); } return StringIsTypedArrayIndex(cx, chars); } bool SetTypedArrayElement(JSContext* cx, Handle obj, uint64_t index, HandleValue v, ObjectOpResult& result); /* * Implements [[DefineOwnProperty]] for TypedArrays when the property * key is a TypedArray index. */ bool DefineTypedArrayElement(JSContext* cx, Handle obj, uint64_t index, Handle desc, ObjectOpResult& result); static inline constexpr unsigned TypedArrayShift(Scalar::Type viewType) { switch (viewType) { case Scalar::Int8: case Scalar::Uint8: case Scalar::Uint8Clamped: return 0; case Scalar::Int16: case Scalar::Uint16: return 1; case Scalar::Int32: case Scalar::Uint32: case Scalar::Float32: return 2; case Scalar::BigInt64: case Scalar::BigUint64: case Scalar::Int64: case Scalar::Float64: return 3; default: MOZ_CRASH("Unexpected array type"); } } static inline unsigned TypedArrayElemSize(Scalar::Type viewType) { return 1u << TypedArrayShift(viewType); } } // namespace js template <> inline bool JSObject::is() const { return js::IsTypedArrayClass(getClass()); } #endif /* vm_TypedArrayObject_h */