607 lines
20 KiB
C++
607 lines
20 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* 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 "jsobj.h"
|
|
|
|
#include "gc/Barrier.h"
|
|
#include "js/Class.h"
|
|
#include "vm/ArrayBufferObject.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)
|
|
|
|
typedef struct JSProperty JSProperty;
|
|
|
|
namespace js {
|
|
|
|
enum class TypedArrayLength { Fixed, Dynamic };
|
|
|
|
/*
|
|
* 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 NativeObject
|
|
{
|
|
public:
|
|
// Underlying (Shared)ArrayBufferObject.
|
|
static const size_t BUFFER_SLOT = 0;
|
|
static_assert(BUFFER_SLOT == JS_TYPEDARRAYLAYOUT_BUFFER_SLOT,
|
|
"self-hosted code with burned-in constants must get the "
|
|
"right buffer slot");
|
|
|
|
// Slot containing length of the view in number of typed elements.
|
|
static const size_t LENGTH_SLOT = 1;
|
|
static_assert(LENGTH_SLOT == JS_TYPEDARRAYLAYOUT_LENGTH_SLOT,
|
|
"self-hosted code with burned-in constants must get the "
|
|
"right length slot");
|
|
|
|
// Offset of view within underlying (Shared)ArrayBufferObject.
|
|
static const size_t BYTEOFFSET_SLOT = 2;
|
|
static_assert(BYTEOFFSET_SLOT == JS_TYPEDARRAYLAYOUT_BYTEOFFSET_SLOT,
|
|
"self-hosted code with burned-in constants must get the "
|
|
"right byteOffset slot");
|
|
|
|
static const size_t RESERVED_SLOTS = 3;
|
|
|
|
#ifdef DEBUG
|
|
static const uint8_t ZeroLengthArrayData = 0x4A;
|
|
#endif
|
|
|
|
static int lengthOffset();
|
|
static int dataOffset();
|
|
|
|
// The raw pointer to the buffer memory, the "private" value.
|
|
//
|
|
// This offset is exposed for performance reasons - so that it
|
|
// need not be looked up on accesses.
|
|
static const size_t DATA_SLOT = 3;
|
|
|
|
static_assert(js::detail::TypedArrayLengthSlot == LENGTH_SLOT,
|
|
"bad inlined constant in jsfriendapi.h");
|
|
|
|
typedef TypedArrayObject SomeTypedArray;
|
|
typedef ArrayBufferObject BufferType;
|
|
|
|
template<typename T> struct OfType;
|
|
|
|
static bool sameBuffer(Handle<TypedArrayObject*> a, Handle<TypedArrayObject*> b) {
|
|
// Inline buffers.
|
|
if (!a->hasBuffer() || !b->hasBuffer())
|
|
return a.get() == b.get();
|
|
|
|
// Shared buffers.
|
|
if (a->isSharedMemory() && b->isSharedMemory()) {
|
|
return (a->bufferObject()->as<SharedArrayBufferObject>().globalID() ==
|
|
b->bufferObject()->as<SharedArrayBufferObject>().globalID());
|
|
}
|
|
|
|
return a->bufferObject() == b->bufferObject();
|
|
}
|
|
|
|
static const Class classes[Scalar::MaxTypedArrayViewType];
|
|
static const Class protoClasses[Scalar::MaxTypedArrayViewType];
|
|
static const Class sharedTypedArrayPrototypeClass;
|
|
|
|
static const Class* classForType(Scalar::Type type) {
|
|
MOZ_ASSERT(type < Scalar::MaxTypedArrayViewType);
|
|
return &classes[type];
|
|
}
|
|
|
|
static const Class* protoClassForType(Scalar::Type type) {
|
|
MOZ_ASSERT(type < Scalar::MaxTypedArrayViewType);
|
|
return &protoClasses[type];
|
|
}
|
|
|
|
static const 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 const uint32_t INLINE_BUFFER_LIMIT =
|
|
(NativeObject::MAX_FIXED_SLOTS - FIXED_DATA_START) * sizeof(Value);
|
|
|
|
static gc::AllocKind
|
|
AllocKindForLazyBuffer(size_t nbytes)
|
|
{
|
|
MOZ_ASSERT(nbytes <= INLINE_BUFFER_LIMIT);
|
|
if (nbytes == 0)
|
|
nbytes += sizeof(uint8_t);
|
|
size_t dataSlots = AlignBytes(nbytes, sizeof(Value)) / sizeof(Value);
|
|
MOZ_ASSERT(nbytes <= dataSlots * sizeof(Value));
|
|
return gc::GetGCObjectKind(FIXED_DATA_START + dataSlots);
|
|
}
|
|
|
|
inline Scalar::Type type() const;
|
|
inline size_t bytesPerElement() const;
|
|
|
|
static Value bufferValue(TypedArrayObject* tarr) {
|
|
return tarr->getFixedSlot(BUFFER_SLOT);
|
|
}
|
|
static Value byteOffsetValue(TypedArrayObject* tarr) {
|
|
Value v = tarr->getFixedSlot(BYTEOFFSET_SLOT);
|
|
MOZ_ASSERT(v.toInt32() >= 0);
|
|
return v;
|
|
}
|
|
static Value byteLengthValue(TypedArrayObject* tarr) {
|
|
return Int32Value(tarr->getFixedSlot(LENGTH_SLOT).toInt32() * tarr->bytesPerElement());
|
|
}
|
|
static Value lengthValue(TypedArrayObject* tarr) {
|
|
return tarr->getFixedSlot(LENGTH_SLOT);
|
|
}
|
|
|
|
static bool
|
|
ensureHasBuffer(JSContext* cx, Handle<TypedArrayObject*> tarray);
|
|
|
|
bool hasBuffer() const {
|
|
return bufferValue(const_cast<TypedArrayObject*>(this)).isObject();
|
|
}
|
|
JSObject* bufferObject() const {
|
|
return bufferValue(const_cast<TypedArrayObject*>(this)).toObjectOrNull();
|
|
}
|
|
uint32_t byteOffset() const {
|
|
return byteOffsetValue(const_cast<TypedArrayObject*>(this)).toInt32();
|
|
}
|
|
uint32_t byteLength() const {
|
|
return byteLengthValue(const_cast<TypedArrayObject*>(this)).toInt32();
|
|
}
|
|
uint32_t length() const {
|
|
return lengthValue(const_cast<TypedArrayObject*>(this)).toInt32();
|
|
}
|
|
|
|
bool hasInlineElements() const;
|
|
void setInlineElements();
|
|
uint8_t* elementsRaw() const {
|
|
return *(uint8_t **)((((char *)this) + this->dataOffset()));
|
|
}
|
|
uint8_t* elements() const {
|
|
assertZeroLengthArrayData();
|
|
return elementsRaw();
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void assertZeroLengthArrayData() const;
|
|
#else
|
|
void assertZeroLengthArrayData() const {};
|
|
#endif
|
|
|
|
Value getElement(uint32_t index);
|
|
static void setElement(TypedArrayObject& obj, uint32_t index, double d);
|
|
|
|
void notifyBufferDetached(JSContext* cx, void* newData);
|
|
|
|
static bool
|
|
GetTemplateObjectForNative(JSContext* cx, Native native, uint32_t len,
|
|
MutableHandleObject res);
|
|
|
|
/*
|
|
* Byte length above which created typed arrays and data views will have
|
|
* singleton types regardless of the context in which they are created.
|
|
*/
|
|
static const uint32_t SINGLETON_BYTE_LENGTH = 1024 * 1024 * 10;
|
|
|
|
static bool isOriginalLengthGetter(Native native);
|
|
|
|
ArrayBufferObject* bufferUnshared() const {
|
|
MOZ_ASSERT(!isSharedMemory());
|
|
JSObject* obj = bufferValue(const_cast<TypedArrayObject*>(this)).toObjectOrNull();
|
|
if (!obj)
|
|
return nullptr;
|
|
return &obj->as<ArrayBufferObject>();
|
|
}
|
|
SharedArrayBufferObject* bufferShared() const {
|
|
MOZ_ASSERT(isSharedMemory());
|
|
JSObject* obj = bufferValue(const_cast<TypedArrayObject*>(this)).toObjectOrNull();
|
|
if (!obj)
|
|
return nullptr;
|
|
return &obj->as<SharedArrayBufferObject>();
|
|
}
|
|
ArrayBufferObjectMaybeShared* bufferEither() const {
|
|
JSObject* obj = bufferValue(const_cast<TypedArrayObject*>(this)).toObjectOrNull();
|
|
if (!obj)
|
|
return nullptr;
|
|
if (isSharedMemory())
|
|
return &obj->as<SharedArrayBufferObject>();
|
|
return &obj->as<ArrayBufferObject>();
|
|
}
|
|
|
|
SharedMem<void*> viewDataShared() const {
|
|
return SharedMem<void*>::shared(viewDataEither_());
|
|
}
|
|
SharedMem<void*> viewDataEither() const {
|
|
if (isSharedMemory())
|
|
return SharedMem<void*>::shared(viewDataEither_());
|
|
return SharedMem<void*>::unshared(viewDataEither_());
|
|
}
|
|
void initViewData(SharedMem<uint8_t*> viewData) {
|
|
// Install a pointer to the buffer location that corresponds
|
|
// to offset zero within the typed array.
|
|
//
|
|
// The following unwrap is safe because the DATA_SLOT is
|
|
// accessed only from jitted code and from the
|
|
// viewDataEither_() accessor below; in neither case does the
|
|
// raw pointer escape untagged into C++ code.
|
|
initPrivate(viewData.unwrap(/*safe - see above*/));
|
|
}
|
|
void* viewDataUnshared() const {
|
|
MOZ_ASSERT(!isSharedMemory());
|
|
return viewDataEither_();
|
|
}
|
|
|
|
bool hasDetachedBuffer() const {
|
|
// Shared buffers can't be detached.
|
|
if (isSharedMemory())
|
|
return false;
|
|
|
|
// A typed array with a null buffer has never had its buffer exposed to
|
|
// become detached.
|
|
ArrayBufferObject* buffer = bufferUnshared();
|
|
if (!buffer)
|
|
return false;
|
|
|
|
return buffer->isDetached();
|
|
}
|
|
|
|
private:
|
|
void* viewDataEither_() const {
|
|
// Note, do not check whether shared or not
|
|
// Keep synced with js::Get<Type>ArrayLengthAndData in jsfriendapi.h!
|
|
return static_cast<void*>(getPrivate(DATA_SLOT));
|
|
}
|
|
|
|
public:
|
|
static void trace(JSTracer* trc, JSObject* obj);
|
|
static void finalize(FreeOp* fop, JSObject* obj);
|
|
static void objectMoved(JSObject* obj, const JSObject* old);
|
|
static size_t objectMovedDuringMinorGC(JSTracer* trc, JSObject* obj, const JSObject* old,
|
|
gc::AllocKind allocKind);
|
|
|
|
/* Initialization bits */
|
|
|
|
template<Value ValueGetter(TypedArrayObject* tarr)>
|
|
static bool
|
|
GetterImpl(JSContext* cx, const CallArgs& args)
|
|
{
|
|
MOZ_ASSERT(is(args.thisv()));
|
|
args.rval().set(ValueGetter(&args.thisv().toObject().as<TypedArrayObject>()));
|
|
return true;
|
|
}
|
|
|
|
// ValueGetter is a function that takes an unwrapped typed array object and
|
|
// returns a Value. Given such a function, Getter<> is a native that
|
|
// retrieves a given Value, probably from a slot on the object.
|
|
template<Value ValueGetter(TypedArrayObject* tarr)>
|
|
static bool
|
|
Getter(JSContext* cx, unsigned argc, Value* vp)
|
|
{
|
|
CallArgs args = CallArgsFromVp(argc, vp);
|
|
return CallNonGenericMethod<is, GetterImpl<ValueGetter>>(cx, args);
|
|
}
|
|
|
|
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);
|
|
};
|
|
|
|
MOZ_MUST_USE bool TypedArray_bufferGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
extern TypedArrayObject*
|
|
TypedArrayCreateWithTemplate(JSContext* cx, HandleObject templateObj, int32_t len);
|
|
|
|
inline bool
|
|
IsTypedArrayClass(const Class* clasp)
|
|
{
|
|
return &TypedArrayObject::classes[0] <= clasp &&
|
|
clasp < &TypedArrayObject::classes[Scalar::MaxTypedArrayViewType];
|
|
}
|
|
|
|
bool
|
|
IsTypedArrayConstructor(HandleValue v, uint32_t type);
|
|
|
|
inline Scalar::Type
|
|
TypedArrayObject::type() const
|
|
{
|
|
MOZ_ASSERT(IsTypedArrayClass(getClass()));
|
|
return static_cast<Scalar::Type>(getClass() - &classes[0]);
|
|
}
|
|
|
|
inline size_t
|
|
TypedArrayObject::bytesPerElement() const
|
|
{
|
|
return Scalar::byteSize(type());
|
|
}
|
|
|
|
// Return value is whether the string is some integer. If the string is an
|
|
// integer which is not representable as a uint64_t, the return value is true
|
|
// and the resulting index is UINT64_MAX.
|
|
template <typename CharT>
|
|
bool
|
|
StringIsTypedArrayIndex(const CharT* s, size_t length, uint64_t* indexp);
|
|
|
|
inline bool
|
|
IsTypedArrayIndex(jsid id, uint64_t* indexp)
|
|
{
|
|
if (JSID_IS_INT(id)) {
|
|
int32_t i = JSID_TO_INT(id);
|
|
MOZ_ASSERT(i >= 0);
|
|
*indexp = (double)i;
|
|
return true;
|
|
}
|
|
|
|
if (MOZ_UNLIKELY(!JSID_IS_STRING(id)))
|
|
return false;
|
|
|
|
JS::AutoCheckCannotGC nogc;
|
|
JSAtom* atom = JSID_TO_ATOM(id);
|
|
size_t length = atom->length();
|
|
|
|
if (atom->hasLatin1Chars()) {
|
|
const Latin1Char* s = atom->latin1Chars(nogc);
|
|
if (!JS7_ISDEC(*s) && *s != '-')
|
|
return false;
|
|
return StringIsTypedArrayIndex(s, length, indexp);
|
|
}
|
|
|
|
const char16_t* s = atom->twoByteChars(nogc);
|
|
if (!JS7_ISDEC(*s) && *s != '-')
|
|
return false;
|
|
return StringIsTypedArrayIndex(s, length, indexp);
|
|
}
|
|
|
|
/*
|
|
* Implements [[DefineOwnProperty]] for TypedArrays when the property
|
|
* key is a TypedArray index.
|
|
*/
|
|
bool
|
|
DefineTypedArrayElement(JSContext* cx, HandleObject arr, uint64_t index,
|
|
Handle<PropertyDescriptor> desc, ObjectOpResult& result);
|
|
|
|
static inline 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::Int64:
|
|
case Scalar::Float64:
|
|
return 3;
|
|
case Scalar::Float32x4:
|
|
case Scalar::Int8x16:
|
|
case Scalar::Int16x8:
|
|
case Scalar::Int32x4:
|
|
return 4;
|
|
default:;
|
|
}
|
|
MOZ_CRASH("Unexpected array type");
|
|
}
|
|
|
|
static inline unsigned
|
|
TypedArrayElemSize(Scalar::Type viewType)
|
|
{
|
|
return 1u << TypedArrayShift(viewType);
|
|
}
|
|
|
|
// Assign
|
|
//
|
|
// target[targetOffset] = unsafeSrcCrossCompartment[0]
|
|
// ...
|
|
// target[targetOffset + unsafeSrcCrossCompartment.length - 1] =
|
|
// unsafeSrcCrossCompartment[unsafeSrcCrossCompartment.length - 1]
|
|
//
|
|
// where the source element range doesn't overlap the target element range in
|
|
// memory.
|
|
extern void
|
|
SetDisjointTypedElements(TypedArrayObject* target, uint32_t targetOffset,
|
|
TypedArrayObject* unsafeSrcCrossCompartment);
|
|
|
|
extern JSObject*
|
|
InitDataViewClass(JSContext* cx, HandleObject obj);
|
|
|
|
class DataViewObject : public NativeObject
|
|
{
|
|
private:
|
|
static const Class protoClass;
|
|
|
|
static bool is(HandleValue v) {
|
|
return v.isObject() && v.toObject().hasClass(&class_);
|
|
}
|
|
|
|
template <typename NativeType>
|
|
static uint8_t*
|
|
getDataPointer(JSContext* cx, Handle<DataViewObject*> obj, double offset);
|
|
|
|
template<Value ValueGetter(DataViewObject* view)>
|
|
static bool
|
|
getterImpl(JSContext* cx, const CallArgs& args);
|
|
|
|
template<Value ValueGetter(DataViewObject* view)>
|
|
static bool
|
|
getter(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
template<Value ValueGetter(DataViewObject* view)>
|
|
static bool
|
|
defineGetter(JSContext* cx, PropertyName* name, HandleNativeObject proto);
|
|
|
|
static bool getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, const CallArgs& args,
|
|
uint32_t *byteOffset, uint32_t* byteLength);
|
|
static bool constructSameCompartment(JSContext* cx, HandleObject bufobj, const CallArgs& args);
|
|
static bool constructWrapped(JSContext* cx, HandleObject bufobj, const CallArgs& args);
|
|
|
|
friend bool ArrayBufferObject::createDataViewForThisImpl(JSContext* cx, const CallArgs& args);
|
|
static DataViewObject*
|
|
create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
|
|
Handle<ArrayBufferObject*> arrayBuffer, JSObject* proto);
|
|
|
|
public:
|
|
static const Class class_;
|
|
|
|
static Value byteOffsetValue(DataViewObject* view) {
|
|
Value v = view->getFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT);
|
|
MOZ_ASSERT(v.toInt32() >= 0);
|
|
return v;
|
|
}
|
|
|
|
static Value byteLengthValue(DataViewObject* view) {
|
|
Value v = view->getFixedSlot(TypedArrayObject::LENGTH_SLOT);
|
|
MOZ_ASSERT(v.toInt32() >= 0);
|
|
return v;
|
|
}
|
|
|
|
static Value bufferValue(DataViewObject* view) {
|
|
return view->getFixedSlot(TypedArrayObject::BUFFER_SLOT);
|
|
}
|
|
|
|
uint32_t byteOffset() const {
|
|
return byteOffsetValue(const_cast<DataViewObject*>(this)).toInt32();
|
|
}
|
|
|
|
uint32_t byteLength() const {
|
|
return byteLengthValue(const_cast<DataViewObject*>(this)).toInt32();
|
|
}
|
|
|
|
ArrayBufferObject& arrayBuffer() const {
|
|
return bufferValue(const_cast<DataViewObject*>(this)).toObject().as<ArrayBufferObject>();
|
|
}
|
|
|
|
void* dataPointer() const {
|
|
return getPrivate();
|
|
}
|
|
|
|
static bool class_constructor(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
static bool getInt8Impl(JSContext* cx, const CallArgs& args);
|
|
static bool fun_getInt8(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
static bool getUint8Impl(JSContext* cx, const CallArgs& args);
|
|
static bool fun_getUint8(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
static bool getInt16Impl(JSContext* cx, const CallArgs& args);
|
|
static bool fun_getInt16(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
static bool getUint16Impl(JSContext* cx, const CallArgs& args);
|
|
static bool fun_getUint16(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
static bool getInt32Impl(JSContext* cx, const CallArgs& args);
|
|
static bool fun_getInt32(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
static bool getUint32Impl(JSContext* cx, const CallArgs& args);
|
|
static bool fun_getUint32(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
static bool getFloat32Impl(JSContext* cx, const CallArgs& args);
|
|
static bool fun_getFloat32(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
static bool getFloat64Impl(JSContext* cx, const CallArgs& args);
|
|
static bool fun_getFloat64(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
static bool setInt8Impl(JSContext* cx, const CallArgs& args);
|
|
static bool fun_setInt8(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
static bool setUint8Impl(JSContext* cx, const CallArgs& args);
|
|
static bool fun_setUint8(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
static bool setInt16Impl(JSContext* cx, const CallArgs& args);
|
|
static bool fun_setInt16(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
static bool setUint16Impl(JSContext* cx, const CallArgs& args);
|
|
static bool fun_setUint16(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
static bool setInt32Impl(JSContext* cx, const CallArgs& args);
|
|
static bool fun_setInt32(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
static bool setUint32Impl(JSContext* cx, const CallArgs& args);
|
|
static bool fun_setUint32(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
static bool setFloat32Impl(JSContext* cx, const CallArgs& args);
|
|
static bool fun_setFloat32(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
static bool setFloat64Impl(JSContext* cx, const CallArgs& args);
|
|
static bool fun_setFloat64(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
static bool initClass(JSContext* cx);
|
|
static void notifyBufferDetached(JSObject* view);
|
|
template<typename NativeType>
|
|
static bool read(JSContext* cx, Handle<DataViewObject*> obj,
|
|
const CallArgs& args, NativeType* val, const char* method);
|
|
template<typename NativeType>
|
|
static bool write(JSContext* cx, Handle<DataViewObject*> obj,
|
|
const CallArgs& args, const char* method);
|
|
|
|
void notifyBufferDetached(void* newData);
|
|
|
|
private:
|
|
static const JSFunctionSpec jsfuncs[];
|
|
};
|
|
|
|
static inline int32_t
|
|
ClampIntForUint8Array(int32_t x)
|
|
{
|
|
if (x < 0)
|
|
return 0;
|
|
if (x > 255)
|
|
return 255;
|
|
return x;
|
|
}
|
|
|
|
static inline bool
|
|
IsAnyArrayBuffer(HandleObject obj)
|
|
{
|
|
return IsArrayBuffer(obj) || IsSharedArrayBuffer(obj);
|
|
}
|
|
|
|
static inline bool
|
|
IsAnyArrayBuffer(JSObject* obj)
|
|
{
|
|
return IsArrayBuffer(obj) || IsSharedArrayBuffer(obj);
|
|
}
|
|
|
|
static inline bool
|
|
IsAnyArrayBuffer(HandleValue v)
|
|
{
|
|
return v.isObject() && IsAnyArrayBuffer(&v.toObject());
|
|
}
|
|
|
|
} // namespace js
|
|
|
|
template <>
|
|
inline bool
|
|
JSObject::is<js::TypedArrayObject>() const
|
|
{
|
|
return js::IsTypedArrayClass(getClass());
|
|
}
|
|
|
|
#endif /* vm_TypedArrayObject_h */
|