summaryrefslogtreecommitdiffstats
path: root/js/src/vm/TypedArrayObject.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /js/src/vm/TypedArrayObject.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/vm/TypedArrayObject.cpp')
-rw-r--r--js/src/vm/TypedArrayObject.cpp3315
1 files changed, 3315 insertions, 0 deletions
diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp
new file mode 100644
index 000000000..9d4ee94c6
--- /dev/null
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -0,0 +1,3315 @@
+/* -*- 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/. */
+
+#include "vm/TypedArrayObject.h"
+
+#include "mozilla/Alignment.h"
+#include "mozilla/Casting.h"
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/PodOperations.h"
+
+#include <string.h>
+#ifndef XP_WIN
+# include <sys/mman.h>
+#endif
+
+#include "jsapi.h"
+#include "jsarray.h"
+#include "jscntxt.h"
+#include "jscpucfg.h"
+#include "jsnum.h"
+#include "jsobj.h"
+#include "jstypes.h"
+#include "jsutil.h"
+#ifdef XP_WIN
+# include "jswin.h"
+#endif
+#include "jswrapper.h"
+
+#include "builtin/TypedObjectConstants.h"
+#include "gc/Barrier.h"
+#include "gc/Marking.h"
+#include "jit/InlinableNatives.h"
+#include "js/Conversions.h"
+#include "vm/ArrayBufferObject.h"
+#include "vm/GlobalObject.h"
+#include "vm/Interpreter.h"
+#include "vm/PIC.h"
+#include "vm/SelfHosting.h"
+#include "vm/TypedArrayCommon.h"
+#include "vm/WrapperObject.h"
+
+#include "jsatominlines.h"
+
+#include "gc/Nursery-inl.h"
+#include "gc/StoreBuffer-inl.h"
+#include "vm/ArrayBufferObject-inl.h"
+#include "vm/NativeObject-inl.h"
+#include "vm/Shape-inl.h"
+
+using namespace js;
+using namespace js::gc;
+
+using mozilla::AssertedCast;
+using JS::CanonicalizeNaN;
+using JS::ToInt32;
+using JS::ToUint32;
+
+/*
+ * TypedArrayObject
+ *
+ * The non-templated base class for the specific typed implementations.
+ * This class holds all the member variables that are used by
+ * the subclasses.
+ */
+
+/* static */ int
+TypedArrayObject::lengthOffset()
+{
+ return NativeObject::getFixedSlotOffset(LENGTH_SLOT);
+}
+
+/* static */ int
+TypedArrayObject::dataOffset()
+{
+ return NativeObject::getPrivateDataOffset(DATA_SLOT);
+}
+
+void
+TypedArrayObject::notifyBufferDetached(JSContext* cx, void* newData)
+{
+ MOZ_ASSERT(!isSharedMemory());
+ setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(0));
+ setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(0));
+
+ // If the object is in the nursery, the buffer will be freed by the next
+ // nursery GC. Free the data slot pointer if the object has no inline data.
+ Nursery& nursery = cx->runtime()->gc.nursery;
+ if (isTenured() && !hasBuffer() && !hasInlineElements() &&
+ !nursery.isInside(elements()))
+ {
+ js_free(elements());
+ }
+
+ setPrivate(newData);
+}
+
+/* static */ bool
+TypedArrayObject::is(HandleValue v)
+{
+ return v.isObject() && v.toObject().is<TypedArrayObject>();
+}
+
+/* static */ bool
+TypedArrayObject::ensureHasBuffer(JSContext* cx, Handle<TypedArrayObject*> tarray)
+{
+ if (tarray->hasBuffer())
+ return true;
+
+ Rooted<ArrayBufferObject*> buffer(cx, ArrayBufferObject::create(cx, tarray->byteLength()));
+ if (!buffer)
+ return false;
+
+ if (!buffer->addView(cx, tarray))
+ return false;
+
+ // tarray is not shared, because if it were it would have a buffer.
+ memcpy(buffer->dataPointer(), tarray->viewDataUnshared(), tarray->byteLength());
+
+ // If the object is in the nursery, the buffer will be freed by the next
+ // nursery GC. Free the data slot pointer if the object has no inline data.
+ Nursery& nursery = cx->runtime()->gc.nursery;
+ if (tarray->isTenured() && !tarray->hasInlineElements() &&
+ !nursery.isInside(tarray->elements()))
+ {
+ js_free(tarray->elements());
+ }
+
+ tarray->setPrivate(buffer->dataPointer());
+
+ tarray->setFixedSlot(TypedArrayObject::BUFFER_SLOT, ObjectValue(*buffer));
+
+ // Notify compiled jit code that the base pointer has moved.
+ MarkObjectStateChange(cx, tarray);
+
+ return true;
+}
+
+#ifdef DEBUG
+void
+TypedArrayObject::assertZeroLengthArrayData() const
+{
+ if (length() == 0 && !hasBuffer()) {
+ uint8_t* end = fixedData(TypedArrayObject::FIXED_DATA_START);
+ MOZ_ASSERT(end[0] == ZeroLengthArrayData);
+ }
+}
+#endif
+
+/* static */ void
+TypedArrayObject::trace(JSTracer* trc, JSObject* objArg)
+{
+ // Handle all tracing required when the object has a buffer.
+ ArrayBufferViewObject::trace(trc, objArg);
+}
+
+void
+TypedArrayObject::finalize(FreeOp* fop, JSObject* obj)
+{
+ MOZ_ASSERT(!IsInsideNursery(obj));
+ TypedArrayObject* curObj = &obj->as<TypedArrayObject>();
+
+ // Template objects or discarded objects (which didn't have enough room
+ // for inner elements). Don't have anything to free.
+ if (!curObj->elementsRaw())
+ return;
+
+ curObj->assertZeroLengthArrayData();
+
+ // Typed arrays with a buffer object do not need to be free'd
+ if (curObj->hasBuffer())
+ return;
+
+ // Free the data slot pointer if it does not point into the old JSObject.
+ if (!curObj->hasInlineElements())
+ js_free(curObj->elements());
+}
+
+/* static */ void
+TypedArrayObject::objectMoved(JSObject* obj, const JSObject* old)
+{
+ TypedArrayObject* newObj = &obj->as<TypedArrayObject>();
+ const TypedArrayObject* oldObj = &old->as<TypedArrayObject>();
+
+ // Typed arrays with a buffer object do not need an update.
+ if (oldObj->hasBuffer())
+ return;
+
+ // Update the data slot pointer if it points to the old JSObject.
+ if (oldObj->hasInlineElements())
+ newObj->setInlineElements();
+}
+
+/* static */ size_t
+TypedArrayObject::objectMovedDuringMinorGC(JSTracer* trc, JSObject* obj, const JSObject* old,
+ gc::AllocKind newAllocKind)
+{
+ TypedArrayObject* newObj = &obj->as<TypedArrayObject>();
+ const TypedArrayObject* oldObj = &old->as<TypedArrayObject>();
+ MOZ_ASSERT(newObj->elementsRaw() == oldObj->elementsRaw());
+ MOZ_ASSERT(obj->isTenured());
+
+ // Typed arrays with a buffer object do not need an update.
+ if (oldObj->hasBuffer())
+ return 0;
+
+ Nursery& nursery = trc->runtime()->gc.nursery;
+ void* buf = oldObj->elements();
+
+ if (!nursery.isInside(buf)) {
+ nursery.removeMallocedBuffer(buf);
+ return 0;
+ }
+
+ // Determine if we can use inline data for the target array. If this is
+ // possible, the nursery will have picked an allocation size that is large
+ // enough.
+ size_t nbytes = 0;
+ switch (oldObj->type()) {
+#define OBJECT_MOVED_TYPED_ARRAY(T, N) \
+ case Scalar::N: \
+ nbytes = oldObj->length() * sizeof(T); \
+ break;
+JS_FOR_EACH_TYPED_ARRAY(OBJECT_MOVED_TYPED_ARRAY)
+#undef OBJECT_MOVED_TYPED_ARRAY
+ default:
+ MOZ_CRASH("Unsupported TypedArray type");
+ }
+
+ size_t headerSize = dataOffset() + sizeof(HeapSlot);
+
+ // See AllocKindForLazyBuffer.
+ MOZ_ASSERT_IF(nbytes == 0, headerSize + sizeof(uint8_t) <= GetGCKindBytes(newAllocKind));
+
+ if (headerSize + nbytes <= GetGCKindBytes(newAllocKind)) {
+ MOZ_ASSERT(oldObj->hasInlineElements());
+#ifdef DEBUG
+ if (nbytes == 0) {
+ uint8_t* output = newObj->fixedData(TypedArrayObject::FIXED_DATA_START);
+ output[0] = ZeroLengthArrayData;
+ }
+#endif
+ newObj->setInlineElements();
+ } else {
+ MOZ_ASSERT(!oldObj->hasInlineElements());
+ AutoEnterOOMUnsafeRegion oomUnsafe;
+ nbytes = JS_ROUNDUP(nbytes, sizeof(Value));
+ void* data = newObj->zone()->pod_malloc<uint8_t>(nbytes);
+ if (!data)
+ oomUnsafe.crash("Failed to allocate typed array elements while tenuring.");
+ MOZ_ASSERT(!nursery.isInside(data));
+ newObj->initPrivate(data);
+ }
+
+ mozilla::PodCopy(newObj->elements(), oldObj->elements(), nbytes);
+
+ // Set a forwarding pointer for the element buffers in case they were
+ // preserved on the stack by Ion.
+ nursery.maybeSetForwardingPointer(trc, oldObj->elements(), newObj->elements(),
+ /* direct = */nbytes >= sizeof(uintptr_t));
+
+ return newObj->hasInlineElements() ? 0 : nbytes;
+}
+
+bool
+TypedArrayObject::hasInlineElements() const
+{
+ return elements() == this->fixedData(TypedArrayObject::FIXED_DATA_START) &&
+ byteLength() <= TypedArrayObject::INLINE_BUFFER_LIMIT;
+}
+
+void
+TypedArrayObject::setInlineElements()
+{
+ char* dataSlot = reinterpret_cast<char*>(this) + this->dataOffset();
+ *reinterpret_cast<void**>(dataSlot) = this->fixedData(TypedArrayObject::FIXED_DATA_START);
+}
+
+/* Helper clamped uint8_t type */
+
+uint32_t JS_FASTCALL
+js::ClampDoubleToUint8(const double x)
+{
+ // Not < so that NaN coerces to 0
+ if (!(x >= 0))
+ return 0;
+
+ if (x > 255)
+ return 255;
+
+ double toTruncate = x + 0.5;
+ uint8_t y = uint8_t(toTruncate);
+
+ /*
+ * now val is rounded to nearest, ties rounded up. We want
+ * rounded to nearest ties to even, so check whether we had a
+ * tie.
+ */
+ if (y == toTruncate) {
+ /*
+ * It was a tie (since adding 0.5 gave us the exact integer
+ * we want). Since we rounded up, we either already have an
+ * even number or we have an odd number but the number we
+ * want is one less. So just unconditionally masking out the
+ * ones bit should do the trick to get us the value we
+ * want.
+ */
+ return y & ~1;
+ }
+
+ return y;
+}
+
+template<typename ElementType>
+static inline JSObject*
+NewArray(JSContext* cx, uint32_t nelements);
+
+namespace {
+
+// We allow nullptr for newTarget for all the creation methods, to allow for
+// JSFriendAPI functions that don't care about subclassing
+static bool
+GetPrototypeForInstance(JSContext* cx, HandleObject newTarget, MutableHandleObject proto)
+{
+ if (newTarget) {
+ if (!GetPrototypeFromConstructor(cx, newTarget, proto))
+ return false;
+ } else {
+ proto.set(nullptr);
+ }
+ return true;
+}
+
+enum class SpeciesConstructorOverride {
+ None,
+ ArrayBuffer
+};
+
+template<typename NativeType>
+class TypedArrayObjectTemplate : public TypedArrayObject
+{
+ friend class TypedArrayObject;
+
+ public:
+ typedef NativeType ElementType;
+
+ static constexpr Scalar::Type ArrayTypeID() { return TypeIDOfType<NativeType>::id; }
+ static bool ArrayTypeIsUnsigned() { return TypeIsUnsigned<NativeType>(); }
+ static bool ArrayTypeIsFloatingPoint() { return TypeIsFloatingPoint<NativeType>(); }
+
+ static const size_t BYTES_PER_ELEMENT = sizeof(NativeType);
+
+ static JSObject*
+ createPrototype(JSContext* cx, JSProtoKey key)
+ {
+ Handle<GlobalObject*> global = cx->global();
+ RootedObject typedArrayProto(cx, GlobalObject::getOrCreateTypedArrayPrototype(cx, global));
+ if (!typedArrayProto)
+ return nullptr;
+
+ const Class* clasp = TypedArrayObject::protoClassForType(ArrayTypeID());
+ return global->createBlankPrototypeInheriting(cx, clasp, typedArrayProto);
+ }
+
+ static JSObject*
+ createConstructor(JSContext* cx, JSProtoKey key)
+ {
+ Handle<GlobalObject*> global = cx->global();
+ RootedFunction ctorProto(cx, GlobalObject::getOrCreateTypedArrayConstructor(cx, global));
+ if (!ctorProto)
+ return nullptr;
+
+ JSFunction* fun = NewFunctionWithProto(cx, class_constructor, 3,
+ JSFunction::NATIVE_CTOR, nullptr,
+ ClassName(key, cx),
+ ctorProto, gc::AllocKind::FUNCTION,
+ SingletonObject);
+
+ if (fun)
+ fun->setJitInfo(&jit::JitInfo_TypedArrayConstructor);
+
+ return fun;
+ }
+
+ static bool
+ getOrCreateCreateArrayFromBufferFunction(JSContext* cx, MutableHandleValue fval)
+ {
+ RootedValue cache(cx, cx->global()->createArrayFromBuffer<NativeType>());
+ if (cache.isObject()) {
+ MOZ_ASSERT(cache.toObject().is<JSFunction>());
+ fval.set(cache);
+ return true;
+ }
+
+ RootedFunction fun(cx);
+ fun = NewNativeFunction(cx, ArrayBufferObject::createTypedArrayFromBuffer<NativeType>,
+ 0, nullptr);
+ if (!fun)
+ return false;
+
+ cx->global()->setCreateArrayFromBuffer<NativeType>(fun);
+
+ fval.setObject(*fun);
+ return true;
+ }
+
+ static inline const Class* instanceClass()
+ {
+ return TypedArrayObject::classForType(ArrayTypeID());
+ }
+
+ static bool is(HandleValue v) {
+ return v.isObject() && v.toObject().hasClass(instanceClass());
+ }
+
+ static void
+ setIndexValue(TypedArrayObject& tarray, uint32_t index, double d)
+ {
+ // If the array is an integer array, we only handle up to
+ // 32-bit ints from this point on. if we want to handle
+ // 64-bit ints, we'll need some changes.
+
+ // Assign based on characteristics of the destination type
+ if (ArrayTypeIsFloatingPoint()) {
+ setIndex(tarray, index, NativeType(d));
+ } else if (ArrayTypeIsUnsigned()) {
+ MOZ_ASSERT(sizeof(NativeType) <= 4);
+ uint32_t n = ToUint32(d);
+ setIndex(tarray, index, NativeType(n));
+ } else if (ArrayTypeID() == Scalar::Uint8Clamped) {
+ // The uint8_clamped type has a special rounding converter
+ // for doubles.
+ setIndex(tarray, index, NativeType(d));
+ } else {
+ MOZ_ASSERT(sizeof(NativeType) <= 4);
+ int32_t n = ToInt32(d);
+ setIndex(tarray, index, NativeType(n));
+ }
+ }
+
+ static TypedArrayObject*
+ makeProtoInstance(JSContext* cx, HandleObject proto, AllocKind allocKind)
+ {
+ MOZ_ASSERT(proto);
+
+ JSObject* obj = NewObjectWithClassProto(cx, instanceClass(), proto, allocKind);
+ return obj ? &obj->as<TypedArrayObject>() : nullptr;
+ }
+
+ static TypedArrayObject*
+ makeTypedInstance(JSContext* cx, uint32_t len, gc::AllocKind allocKind)
+ {
+ const Class* clasp = instanceClass();
+ if (len * sizeof(NativeType) >= TypedArrayObject::SINGLETON_BYTE_LENGTH) {
+ JSObject* obj = NewBuiltinClassInstance(cx, clasp, allocKind, SingletonObject);
+ if (!obj)
+ return nullptr;
+ return &obj->as<TypedArrayObject>();
+ }
+
+ jsbytecode* pc;
+ RootedScript script(cx, cx->currentScript(&pc));
+ NewObjectKind newKind = GenericObject;
+ if (script && ObjectGroup::useSingletonForAllocationSite(script, pc, clasp))
+ newKind = SingletonObject;
+ RootedObject obj(cx, NewBuiltinClassInstance(cx, clasp, allocKind, newKind));
+ if (!obj)
+ return nullptr;
+
+ if (script && !ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, obj,
+ newKind == SingletonObject))
+ {
+ return nullptr;
+ }
+
+ return &obj->as<TypedArrayObject>();
+ }
+
+ static TypedArrayObject*
+ makeInstance(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer, uint32_t byteOffset, uint32_t len,
+ HandleObject proto)
+ {
+ MOZ_ASSERT_IF(!buffer, byteOffset == 0);
+
+ gc::AllocKind allocKind = buffer
+ ? GetGCObjectKind(instanceClass())
+ : AllocKindForLazyBuffer(len * sizeof(NativeType));
+
+ // Subclassing mandates that we hand in the proto every time. Most of
+ // the time, though, that [[Prototype]] will not be interesting. If
+ // it isn't, we can do some more TI optimizations.
+ RootedObject checkProto(cx);
+ if (!GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &checkProto))
+ return nullptr;
+
+ AutoSetNewObjectMetadata metadata(cx);
+ Rooted<TypedArrayObject*> obj(cx);
+ if (proto && proto != checkProto)
+ obj = makeProtoInstance(cx, proto, allocKind);
+ else
+ obj = makeTypedInstance(cx, len, allocKind);
+ if (!obj)
+ return nullptr;
+
+ bool isSharedMemory = buffer && IsSharedArrayBuffer(buffer.get());
+
+ obj->setFixedSlot(TypedArrayObject::BUFFER_SLOT, ObjectOrNullValue(buffer));
+ // This is invariant. Self-hosting code that sets BUFFER_SLOT
+ // (if it does) must maintain it, should it need to.
+ if (isSharedMemory)
+ obj->setIsSharedMemory();
+
+ if (buffer) {
+ obj->initViewData(buffer->dataPointerEither() + byteOffset);
+
+ // If the buffer is for an inline typed object, the data pointer
+ // may be in the nursery, so include a barrier to make sure this
+ // object is updated if that typed object moves.
+ auto ptr = buffer->dataPointerEither();
+ if (!IsInsideNursery(obj) && cx->runtime()->gc.nursery.isInside(ptr)) {
+ // Shared buffer data should never be nursery-allocated, so we
+ // need to fail here if isSharedMemory. However, mmap() can
+ // place a SharedArrayRawBuffer up against the bottom end of a
+ // nursery chunk, and a zero-length buffer will erroneously be
+ // perceived as being inside the nursery; sidestep that.
+ if (isSharedMemory) {
+ MOZ_ASSERT(buffer->byteLength() == 0 &&
+ (uintptr_t(ptr.unwrapValue()) & gc::ChunkMask) == 0);
+ } else {
+ cx->runtime()->gc.storeBuffer.putWholeCell(obj);
+ }
+ }
+ } else {
+ void* data = obj->fixedData(FIXED_DATA_START);
+ obj->initPrivate(data);
+ memset(data, 0, len * sizeof(NativeType));
+#ifdef DEBUG
+ if (len == 0) {
+ uint8_t* elements = static_cast<uint8_t*>(data);
+ elements[0] = ZeroLengthArrayData;
+ }
+#endif
+ }
+
+ obj->setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(len));
+ obj->setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(byteOffset));
+
+#ifdef DEBUG
+ if (buffer) {
+ uint32_t arrayByteLength = obj->byteLength();
+ uint32_t arrayByteOffset = obj->byteOffset();
+ uint32_t bufferByteLength = buffer->byteLength();
+ // Unwraps are safe: both are for the pointer value.
+ if (IsArrayBuffer(buffer.get())) {
+ MOZ_ASSERT_IF(!AsArrayBuffer(buffer.get()).isDetached(),
+ buffer->dataPointerEither().unwrap(/*safe*/) <= obj->viewDataEither().unwrap(/*safe*/));
+ }
+ MOZ_ASSERT(bufferByteLength - arrayByteOffset >= arrayByteLength);
+ MOZ_ASSERT(arrayByteOffset <= bufferByteLength);
+ }
+
+ // Verify that the private slot is at the expected place
+ MOZ_ASSERT(obj->numFixedSlots() == TypedArrayObject::DATA_SLOT);
+#endif
+
+ // ArrayBufferObjects track their views to support detaching.
+ if (buffer && buffer->is<ArrayBufferObject>()) {
+ if (!buffer->as<ArrayBufferObject>().addView(cx, obj))
+ return nullptr;
+ }
+
+ return obj;
+ }
+
+ static TypedArrayObject*
+ makeInstance(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer,
+ uint32_t byteOffset, uint32_t len)
+ {
+ RootedObject proto(cx, nullptr);
+ return makeInstance(cx, buffer, byteOffset, len, proto);
+ }
+
+ static TypedArrayObject*
+ makeTemplateObject(JSContext* cx, int32_t len)
+ {
+ MOZ_ASSERT(len >= 0);
+ size_t nbytes;
+ MOZ_ALWAYS_TRUE(CalculateAllocSize<NativeType>(len, &nbytes));
+ MOZ_ASSERT(nbytes < TypedArrayObject::SINGLETON_BYTE_LENGTH);
+ NewObjectKind newKind = TenuredObject;
+ bool fitsInline = nbytes <= INLINE_BUFFER_LIMIT;
+ const Class* clasp = instanceClass();
+ gc::AllocKind allocKind = !fitsInline
+ ? GetGCObjectKind(clasp)
+ : AllocKindForLazyBuffer(nbytes);
+ MOZ_ASSERT(CanBeFinalizedInBackground(allocKind, clasp));
+ allocKind = GetBackgroundAllocKind(allocKind);
+
+ AutoSetNewObjectMetadata metadata(cx);
+ jsbytecode* pc;
+ RootedScript script(cx, cx->currentScript(&pc));
+ if (script && ObjectGroup::useSingletonForAllocationSite(script, pc, clasp))
+ newKind = SingletonObject;
+ JSObject* tmp = NewBuiltinClassInstance(cx, clasp, allocKind, newKind);
+ if (!tmp)
+ return nullptr;
+
+ Rooted<TypedArrayObject*> tarray(cx, &tmp->as<TypedArrayObject>());
+ initTypedArraySlots(cx, tarray, len);
+
+ // Template objects do not need memory for its elements, since there
+ // won't be any elements to store. Therefore, we set the pointer to
+ // nullptr and avoid allocating memory that will never be used.
+ tarray->initPrivate(nullptr);
+
+ if (script && !ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, tarray,
+ newKind == SingletonObject))
+ {
+ return nullptr;
+ }
+
+ return tarray;
+ }
+
+ static void
+ initTypedArraySlots(JSContext* cx, TypedArrayObject* tarray, int32_t len)
+ {
+ MOZ_ASSERT(len >= 0);
+ tarray->setFixedSlot(TypedArrayObject::BUFFER_SLOT, NullValue());
+ tarray->setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(AssertedCast<int32_t>(len)));
+ tarray->setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(0));
+
+ // Verify that the private slot is at the expected place.
+ MOZ_ASSERT(tarray->numFixedSlots() == TypedArrayObject::DATA_SLOT);
+
+#ifdef DEBUG
+ if (len == 0) {
+ uint8_t* output = tarray->fixedData(TypedArrayObject::FIXED_DATA_START);
+ output[0] = TypedArrayObject::ZeroLengthArrayData;
+ }
+#endif
+ }
+
+ static void
+ initTypedArrayData(JSContext* cx, TypedArrayObject* tarray, int32_t len,
+ void* buf, AllocKind allocKind)
+ {
+ if (buf) {
+#ifdef DEBUG
+ Nursery& nursery = cx->runtime()->gc.nursery;
+ MOZ_ASSERT_IF(!nursery.isInside(buf) && !tarray->hasInlineElements(),
+ tarray->isTenured());
+#endif
+ tarray->initPrivate(buf);
+ } else {
+ size_t nbytes = len * sizeof(NativeType);
+#ifdef DEBUG
+ size_t dataOffset = TypedArrayObject::dataOffset();
+ size_t offset = dataOffset + sizeof(HeapSlot);
+ MOZ_ASSERT(offset + nbytes <= GetGCKindBytes(allocKind));
+#endif
+
+ void* data = tarray->fixedData(FIXED_DATA_START);
+ tarray->initPrivate(data);
+ memset(data, 0, nbytes);
+ }
+ }
+
+ static TypedArrayObject*
+ makeTypedArrayWithTemplate(JSContext* cx, TypedArrayObject* templateObj, int32_t len)
+ {
+ if (len < 0 || uint32_t(len) >= INT32_MAX / sizeof(NativeType)) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
+ return nullptr;
+ }
+
+ size_t nbytes;
+ MOZ_ALWAYS_TRUE(js::CalculateAllocSize<NativeType>(len, &nbytes));
+
+ bool fitsInline = nbytes <= INLINE_BUFFER_LIMIT;
+
+ AutoSetNewObjectMetadata metadata(cx);
+
+ const Class* clasp = templateObj->group()->clasp();
+ gc::AllocKind allocKind = !fitsInline
+ ? GetGCObjectKind(clasp)
+ : AllocKindForLazyBuffer(nbytes);
+ MOZ_ASSERT(CanBeFinalizedInBackground(allocKind, clasp));
+ allocKind = GetBackgroundAllocKind(allocKind);
+ RootedObjectGroup group(cx, templateObj->group());
+
+ NewObjectKind newKind = TenuredObject;
+
+ ScopedJSFreePtr<void> buf;
+ if (!fitsInline && len > 0) {
+ buf = cx->zone()->pod_malloc<uint8_t>(nbytes);
+ if (!buf) {
+ ReportOutOfMemory(cx);
+ return nullptr;
+ }
+
+ memset(buf, 0, nbytes);
+ }
+
+ RootedObject tmp(cx, NewObjectWithGroup<TypedArrayObject>(cx, group, allocKind, newKind));
+ if (!tmp)
+ return nullptr;
+
+ TypedArrayObject* obj = &tmp->as<TypedArrayObject>();
+ initTypedArraySlots(cx, obj, len);
+ initTypedArrayData(cx, obj, len, buf.forget(), allocKind);
+
+ return obj;
+ }
+
+ /*
+ * new [Type]Array(length)
+ * new [Type]Array(otherTypedArray)
+ * new [Type]Array(JSArray)
+ * new [Type]Array(ArrayBuffer, [optional] byteOffset, [optional] length)
+ */
+ static bool
+ class_constructor(JSContext* cx, unsigned argc, Value* vp)
+ {
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ if (!ThrowIfNotConstructing(cx, args, "typed array"))
+ return false;
+
+ JSObject* obj = create(cx, args);
+ if (!obj)
+ return false;
+ args.rval().setObject(*obj);
+ return true;
+ }
+
+ static JSObject*
+ create(JSContext* cx, const CallArgs& args)
+ {
+ MOZ_ASSERT(args.isConstructing());
+ RootedObject newTarget(cx, &args.newTarget().toObject());
+
+ /* () or (number) */
+ uint32_t len = 0;
+ if (args.length() == 0 || ValueIsLength(args[0], &len))
+ return fromLength(cx, len, newTarget);
+
+ /* (not an object) */
+ if (!args[0].isObject()) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
+ return nullptr;
+ }
+
+ RootedObject dataObj(cx, &args.get(0).toObject());
+
+ /*
+ * (typedArray)
+ * (sharedTypedArray)
+ * (type[] array)
+ *
+ * Otherwise create a new typed array and copy elements 0..len-1
+ * properties from the object, treating it as some sort of array.
+ * Note that offset and length will be ignored. Note that a
+ * shared array's values are copied here.
+ */
+ if (!UncheckedUnwrap(dataObj)->is<ArrayBufferObjectMaybeShared>())
+ return fromArray(cx, dataObj, newTarget);
+
+ /* (ArrayBuffer, [byteOffset, [length]]) */
+ RootedObject proto(cx);
+ if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
+ return nullptr;
+
+ int32_t byteOffset = 0;
+ if (args.hasDefined(1)) {
+ if (!ToInt32(cx, args[1], &byteOffset))
+ return nullptr;
+ if (byteOffset < 0) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_TYPED_ARRAY_NEGATIVE_ARG,
+ "1");
+ return nullptr;
+ }
+ }
+
+ int32_t length = -1;
+ if (args.hasDefined(2)) {
+ if (!ToInt32(cx, args[2], &length))
+ return nullptr;
+ if (length < 0) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_TYPED_ARRAY_NEGATIVE_ARG,
+ "2");
+ return nullptr;
+ }
+ }
+
+ return fromBufferWithProto(cx, dataObj, byteOffset, length, proto);
+ }
+
+ public:
+ static JSObject*
+ fromBuffer(JSContext* cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt) {
+ return fromBufferWithProto(cx, bufobj, byteOffset, lengthInt, nullptr);
+ }
+
+ static JSObject*
+ fromBufferWithProto(JSContext* cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt,
+ HandleObject proto)
+ {
+ if (bufobj->is<ProxyObject>()) {
+ /*
+ * Normally, NonGenericMethodGuard handles the case of transparent
+ * wrappers. However, we have a peculiar situation: we want to
+ * construct the new typed array in the compartment of the buffer,
+ * so that the typed array can point directly at their buffer's
+ * data without crossing compartment boundaries. So we use the
+ * machinery underlying NonGenericMethodGuard directly to proxy the
+ * native call. We will end up with a wrapper in the origin
+ * compartment for a view in the target compartment referencing the
+ * ArrayBufferObject in that same compartment.
+ */
+ JSObject* wrapped = CheckedUnwrap(bufobj);
+ if (!wrapped) {
+ JS_ReportErrorASCII(cx, "Permission denied to access object");
+ return nullptr;
+ }
+
+ if (!IsAnyArrayBuffer(wrapped)) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
+ return nullptr; // must be arrayBuffer
+ }
+
+ /*
+ * And for even more fun, the new view's prototype should be
+ * set to the origin compartment's prototype object, not the
+ * target's (specifically, the actual view in the target
+ * compartment will use as its prototype a wrapper around the
+ * origin compartment's view.prototype object).
+ *
+ * Rather than hack some crazy solution together, implement
+ * this all using a private helper function, created when
+ * ArrayBufferObject was initialized and cached in the global.
+ * This reuses all the existing cross-compartment crazy so we
+ * don't have to do anything *uniquely* crazy here.
+ */
+
+ RootedObject protoRoot(cx, proto);
+ if (!protoRoot) {
+ if (!GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &protoRoot))
+ return nullptr;
+ }
+
+ FixedInvokeArgs<3> args(cx);
+
+ args[0].setNumber(byteOffset);
+ args[1].setInt32(lengthInt);
+ args[2].setObject(*protoRoot);
+
+ RootedValue fval(cx);
+ if (!getOrCreateCreateArrayFromBufferFunction(cx, &fval))
+ return nullptr;
+
+ RootedValue thisv(cx, ObjectValue(*bufobj));
+ RootedValue rval(cx);
+ if (!js::Call(cx, fval, thisv, args, &rval))
+ return nullptr;
+
+ return &rval.toObject();
+ }
+
+ if (!IsAnyArrayBuffer(bufobj)) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
+ return nullptr; // must be arrayBuffer
+ }
+
+ Rooted<ArrayBufferObjectMaybeShared*> buffer(cx);
+ if (IsArrayBuffer(bufobj)) {
+ ArrayBufferObject& buf = AsArrayBuffer(bufobj);
+ if (buf.isDetached()) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
+ return nullptr;
+ }
+
+ buffer = static_cast<ArrayBufferObjectMaybeShared*>(&buf);
+ } else {
+ buffer = static_cast<ArrayBufferObjectMaybeShared*>(&AsSharedArrayBuffer(bufobj));
+ }
+
+ if (byteOffset > buffer->byteLength() || byteOffset % sizeof(NativeType) != 0) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
+ return nullptr; // invalid byteOffset
+ }
+
+ uint32_t len;
+ if (lengthInt == -1) {
+ len = (buffer->byteLength() - byteOffset) / sizeof(NativeType);
+ if (len * sizeof(NativeType) != buffer->byteLength() - byteOffset) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
+ return nullptr; // given byte array doesn't map exactly to sizeof(NativeType) * N
+ }
+ } else {
+ len = uint32_t(lengthInt);
+ }
+
+ // Go slowly and check for overflow.
+ uint32_t arrayByteLength = len * sizeof(NativeType);
+ if (len >= INT32_MAX / sizeof(NativeType) || byteOffset >= INT32_MAX - arrayByteLength) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
+ return nullptr; // overflow when calculating byteOffset + len * sizeof(NativeType)
+ }
+
+ if (arrayByteLength + byteOffset > buffer->byteLength()) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
+ return nullptr; // byteOffset + len is too big for the arraybuffer
+ }
+
+ return makeInstance(cx, buffer, byteOffset, len, proto);
+ }
+
+ static bool
+ maybeCreateArrayBuffer(JSContext* cx, uint32_t count, uint32_t unit,
+ HandleObject nonDefaultProto,
+ MutableHandle<ArrayBufferObject*> buffer)
+ {
+ if (count >= INT32_MAX / unit) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NEED_DIET,
+ "size and count");
+ return false;
+ }
+ uint32_t byteLength = count * unit;
+
+ MOZ_ASSERT(byteLength < INT32_MAX);
+ static_assert(INLINE_BUFFER_LIMIT % sizeof(NativeType) == 0,
+ "ArrayBuffer inline storage shouldn't waste any space");
+
+ if (!nonDefaultProto && byteLength <= INLINE_BUFFER_LIMIT) {
+ // The array's data can be inline, and the buffer created lazily.
+ return true;
+ }
+
+ ArrayBufferObject* buf = ArrayBufferObject::create(cx, byteLength, nonDefaultProto);
+ if (!buf)
+ return false;
+
+ buffer.set(buf);
+ return true;
+ }
+
+ static JSObject*
+ fromLength(JSContext* cx, uint32_t nelements, HandleObject newTarget = nullptr)
+ {
+ RootedObject proto(cx);
+ if (!GetPrototypeForInstance(cx, newTarget, &proto))
+ return nullptr;
+
+ Rooted<ArrayBufferObject*> buffer(cx);
+ if (!maybeCreateArrayBuffer(cx, nelements, BYTES_PER_ELEMENT, nullptr, &buffer))
+ return nullptr;
+
+ return makeInstance(cx, buffer, 0, nelements, proto);
+ }
+
+ static bool
+ AllocateArrayBuffer(JSContext* cx, HandleValue ctor,
+ uint32_t count, uint32_t unit,
+ MutableHandle<ArrayBufferObject*> buffer);
+
+ static bool
+ CloneArrayBufferNoCopy(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> srcBuffer,
+ bool isWrapped, uint32_t srcByteOffset, uint32_t srcLength,
+ SpeciesConstructorOverride override,
+ MutableHandle<ArrayBufferObject*> buffer);
+
+ static JSObject*
+ fromArray(JSContext* cx, HandleObject other, HandleObject newTarget = nullptr);
+
+ static JSObject*
+ fromTypedArray(JSContext* cx, HandleObject other, bool isWrapped, HandleObject newTarget);
+
+ static JSObject*
+ fromObject(JSContext* cx, HandleObject other, HandleObject newTarget);
+
+ static const NativeType
+ getIndex(JSObject* obj, uint32_t index)
+ {
+ TypedArrayObject& tarray = obj->as<TypedArrayObject>();
+ MOZ_ASSERT(index < tarray.length());
+ return jit::AtomicOperations::loadSafeWhenRacy(tarray.viewDataEither().cast<NativeType*>() + index);
+ }
+
+ static void
+ setIndex(TypedArrayObject& tarray, uint32_t index, NativeType val)
+ {
+ MOZ_ASSERT(index < tarray.length());
+ jit::AtomicOperations::storeSafeWhenRacy(tarray.viewDataEither().cast<NativeType*>() + index, val);
+ }
+
+ static Value getIndexValue(JSObject* tarray, uint32_t index);
+};
+
+#define CREATE_TYPE_FOR_TYPED_ARRAY(T, N) \
+ typedef TypedArrayObjectTemplate<T> N##Array;
+JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPE_FOR_TYPED_ARRAY)
+#undef CREATE_TYPE_FOR_TYPED_ARRAY
+
+} /* anonymous namespace */
+
+TypedArrayObject*
+js::TypedArrayCreateWithTemplate(JSContext* cx, HandleObject templateObj, int32_t len)
+{
+ MOZ_ASSERT(templateObj->is<TypedArrayObject>());
+ TypedArrayObject* tobj = &templateObj->as<TypedArrayObject>();
+
+ switch (tobj->type()) {
+#define CREATE_TYPED_ARRAY(T, N) \
+ case Scalar::N: \
+ return TypedArrayObjectTemplate<T>::makeTypedArrayWithTemplate(cx, tobj, len);
+JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPED_ARRAY)
+#undef CREATE_TYPED_ARRAY
+ default:
+ MOZ_CRASH("Unsupported TypedArray type");
+ }
+}
+
+template<typename T>
+struct TypedArrayObject::OfType
+{
+ typedef TypedArrayObjectTemplate<T> Type;
+};
+
+// ES 2016 draft Mar 25, 2016 24.1.1.1.
+// byteLength = count * unit
+template<typename T>
+/* static */ bool
+TypedArrayObjectTemplate<T>::AllocateArrayBuffer(JSContext* cx, HandleValue ctor,
+ uint32_t count, uint32_t unit,
+ MutableHandle<ArrayBufferObject*> buffer)
+{
+ // ES 2016 draft Mar 25, 2016 24.1.1.1 step 1 (partially).
+ // ES 2016 draft Mar 25, 2016 9.1.14 steps 1-2.
+ MOZ_ASSERT(ctor.isObject());
+ RootedObject proto(cx);
+ RootedObject ctorObj(cx, &ctor.toObject());
+ if (!GetPrototypeFromConstructor(cx, ctorObj, &proto))
+ return false;
+ JSObject* arrayBufferProto = GlobalObject::getOrCreateArrayBufferPrototype(cx, cx->global());
+ if (!arrayBufferProto)
+ return false;
+ if (proto == arrayBufferProto)
+ proto = nullptr;
+
+ // ES 2016 draft Mar 25, 2016 24.1.1.1 steps 1 (remaining part), 2-6.
+ if (!maybeCreateArrayBuffer(cx, count, unit, proto, buffer))
+ return false;
+
+ return true;
+}
+
+static bool
+IsArrayBufferConstructor(const Value& v)
+{
+ return v.isObject() &&
+ v.toObject().is<JSFunction>() &&
+ v.toObject().as<JSFunction>().isNative() &&
+ v.toObject().as<JSFunction>().native() == ArrayBufferObject::class_constructor;
+}
+
+static bool
+IsArrayBufferSpecies(JSContext* cx, HandleObject origBuffer)
+{
+ RootedValue ctor(cx);
+ if (!GetPropertyPure(cx, origBuffer, NameToId(cx->names().constructor), ctor.address()))
+ return false;
+
+ if (!IsArrayBufferConstructor(ctor))
+ return false;
+
+ RootedObject ctorObj(cx, &ctor.toObject());
+ RootedId speciesId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().species));
+ JSFunction* getter;
+ if (!GetGetterPure(cx, ctorObj, speciesId, &getter))
+ return false;
+
+ if (!getter)
+ return false;
+
+ return IsSelfHostedFunctionWithName(getter, cx->names().ArrayBufferSpecies);
+}
+
+static bool
+GetSpeciesConstructor(JSContext* cx, HandleObject obj, bool isWrapped,
+ SpeciesConstructorOverride override, MutableHandleValue ctor)
+{
+ if (!isWrapped) {
+ if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_ArrayBuffer))
+ return false;
+ RootedValue defaultCtor(cx, cx->global()->getConstructor(JSProto_ArrayBuffer));
+ // The second disjunct is an optimization.
+ if (override == SpeciesConstructorOverride::ArrayBuffer || IsArrayBufferSpecies(cx, obj))
+ ctor.set(defaultCtor);
+ else if (!SpeciesConstructor(cx, obj, defaultCtor, ctor))
+ return false;
+
+ return true;
+ }
+
+ {
+ JSAutoCompartment ac(cx, obj);
+ if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_ArrayBuffer))
+ return false;
+ RootedValue defaultCtor(cx, cx->global()->getConstructor(JSProto_ArrayBuffer));
+ if (override == SpeciesConstructorOverride::ArrayBuffer)
+ ctor.set(defaultCtor);
+ else if (!SpeciesConstructor(cx, obj, defaultCtor, ctor))
+ return false;
+ }
+
+ return JS_WrapValue(cx, ctor);
+}
+
+// ES 2017 draft rev 8633ffd9394b203b8876bb23cb79aff13eb07310 24.1.1.4.
+template<typename T>
+/* static */ bool
+TypedArrayObjectTemplate<T>::CloneArrayBufferNoCopy(JSContext* cx,
+ Handle<ArrayBufferObjectMaybeShared*> srcBuffer,
+ bool isWrapped, uint32_t srcByteOffset,
+ uint32_t srcLength,
+ SpeciesConstructorOverride override,
+ MutableHandle<ArrayBufferObject*> buffer)
+{
+ // Step 1 (skipped).
+
+ // Step 2.a.
+ RootedValue cloneCtor(cx);
+ if (!GetSpeciesConstructor(cx, srcBuffer, isWrapped, override, &cloneCtor))
+ return false;
+
+ // Step 2.b.
+ if (srcBuffer->isDetached()) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
+ return false;
+ }
+
+ // Steps 3-4 (skipped).
+
+ // Steps 5.
+ if (!AllocateArrayBuffer(cx, cloneCtor, srcLength, 1, buffer))
+ return false;
+
+ // Step 6.
+ if (srcBuffer->isDetached()) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
+ return false;
+ }
+
+ // Steps 7-8 (done in caller).
+
+ // Step 9.
+ return true;
+}
+
+template<typename T>
+/* static */ JSObject*
+TypedArrayObjectTemplate<T>::fromArray(JSContext* cx, HandleObject other,
+ HandleObject newTarget /* = nullptr */)
+{
+ // Allow nullptr newTarget for FriendAPI methods, which don't care about
+ // subclassing.
+ if (other->is<TypedArrayObject>())
+ return fromTypedArray(cx, other, /* wrapped= */ false, newTarget);
+
+ if (other->is<WrapperObject>() && UncheckedUnwrap(other)->is<TypedArrayObject>())
+ return fromTypedArray(cx, other, /* wrapped= */ true, newTarget);
+
+ return fromObject(cx, other, newTarget);
+}
+
+// ES 2017 draft rev 8633ffd9394b203b8876bb23cb79aff13eb07310 22.2.4.3.
+template<typename T>
+/* static */ JSObject*
+TypedArrayObjectTemplate<T>::fromTypedArray(JSContext* cx, HandleObject other, bool isWrapped,
+ HandleObject newTarget)
+{
+ // Step 1.
+ MOZ_ASSERT_IF(!isWrapped, other->is<TypedArrayObject>());
+ MOZ_ASSERT_IF(isWrapped,
+ other->is<WrapperObject>() &&
+ UncheckedUnwrap(other)->is<TypedArrayObject>());
+
+ // Step 2 (done in caller).
+
+ // Step 4 (partially).
+ RootedObject proto(cx);
+ if (!GetPrototypeForInstance(cx, newTarget, &proto))
+ return nullptr;
+
+ // Step 5.
+ Rooted<TypedArrayObject*> srcArray(cx);
+ if (!isWrapped) {
+ srcArray = &other->as<TypedArrayObject>();
+ if (!TypedArrayObject::ensureHasBuffer(cx, srcArray))
+ return nullptr;
+ } else {
+ RootedObject unwrapped(cx, CheckedUnwrap(other));
+ if (!unwrapped) {
+ JS_ReportErrorASCII(cx, "Permission denied to access object");
+ return nullptr;
+ }
+
+ JSAutoCompartment ac(cx, unwrapped);
+
+ srcArray = &unwrapped->as<TypedArrayObject>();
+ if (!TypedArrayObject::ensureHasBuffer(cx, srcArray))
+ return nullptr;
+ }
+
+ // Step 6.
+ Rooted<ArrayBufferObjectMaybeShared*> srcData(cx, srcArray->bufferEither());
+
+ // Step 7.
+ if (srcData->isDetached()) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
+ return nullptr;
+ }
+
+ // Steps 10.
+ uint32_t elementLength = srcArray->length();
+
+ // Steps 11-12.
+ Scalar::Type srcType = srcArray->type();
+
+ // Step 13 (skipped).
+
+ // Step 14.
+ uint32_t srcByteOffset = srcArray->byteOffset();
+
+ // Step 17, modified for SharedArrayBuffer.
+ bool isShared = srcArray->isSharedMemory();
+ SpeciesConstructorOverride override = isShared ? SpeciesConstructorOverride::ArrayBuffer
+ : SpeciesConstructorOverride::None;
+
+ // Steps 8-9, 17.
+ Rooted<ArrayBufferObject*> buffer(cx);
+ if (ArrayTypeID() == srcType) {
+ // Step 17.a.
+ uint32_t srcLength = srcArray->byteLength();
+
+ // Step 17.b, modified for SharedArrayBuffer
+ if (!CloneArrayBufferNoCopy(cx, srcData, isWrapped, srcByteOffset, srcLength, override,
+ &buffer))
+ {
+ return nullptr;
+ }
+ } else {
+ // Step 18.a, modified for SharedArrayBuffer
+ RootedValue bufferCtor(cx);
+ if (!GetSpeciesConstructor(cx, srcData, isWrapped, override, &bufferCtor))
+ return nullptr;
+
+ // Step 15-16, 18.b.
+ if (!AllocateArrayBuffer(cx, bufferCtor, elementLength, BYTES_PER_ELEMENT, &buffer))
+ return nullptr;
+
+ // Step 18.c.
+ if (srcArray->hasDetachedBuffer()) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
+ return nullptr;
+ }
+ }
+
+ // Steps 3, 4 (remaining part), 19-22.
+ Rooted<TypedArrayObject*> obj(cx, makeInstance(cx, buffer, 0, elementLength, proto));
+ if (!obj)
+ return nullptr;
+
+ // Step 18.d-g or 24.1.1.4 step 11.
+ if (!TypedArrayMethods<TypedArrayObject>::setFromTypedArray(cx, obj, srcArray))
+ return nullptr;
+
+ // Step 23.
+ return obj;
+}
+
+static MOZ_ALWAYS_INLINE bool
+IsOptimizableInit(JSContext* cx, HandleObject iterable, bool* optimized)
+{
+ MOZ_ASSERT(!*optimized);
+
+ if (!IsPackedArray(iterable))
+ return true;
+
+ ForOfPIC::Chain* stubChain = ForOfPIC::getOrCreate(cx);
+ if (!stubChain)
+ return false;
+
+ return stubChain->tryOptimizeArray(cx, iterable.as<ArrayObject>(), optimized);
+}
+
+// ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e
+// 22.2.4.4 TypedArray ( object )
+template<typename T>
+/* static */ JSObject*
+TypedArrayObjectTemplate<T>::fromObject(JSContext* cx, HandleObject other, HandleObject newTarget)
+{
+ // Steps 1-2 (Already performed in caller).
+
+ // Steps 3-4 (Allocation deferred until later).
+ RootedObject proto(cx);
+ if (!GetPrototypeForInstance(cx, newTarget, &proto))
+ return nullptr;
+
+ bool optimized = false;
+ if (!IsOptimizableInit(cx, other, &optimized))
+ return nullptr;
+
+ // Fast path when iterable is a packed array using the default iterator.
+ if (optimized) {
+ // Step 6.a (We don't need to call IterableToList for the fast path).
+ RootedArrayObject array(cx, &other->as<ArrayObject>());
+
+ // Step 6.b.
+ uint32_t len = array->getDenseInitializedLength();
+
+ // Step 6.c.
+ Rooted<ArrayBufferObject*> buffer(cx);
+ if (!maybeCreateArrayBuffer(cx, len, BYTES_PER_ELEMENT, nullptr, &buffer))
+ return nullptr;
+
+ Rooted<TypedArrayObject*> obj(cx, makeInstance(cx, buffer, 0, len, proto));
+ if (!obj)
+ return nullptr;
+
+ // Steps 6.d-e.
+ if (!TypedArrayMethods<TypedArrayObject>::initFromIterablePackedArray(cx, obj, array))
+ return nullptr;
+
+ // Step 6.f (The assertion isn't applicable for the fast path).
+
+ // Step 6.g.
+ return obj;
+ }
+
+ // Step 5.
+ RootedValue callee(cx);
+ RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator));
+ if (!GetProperty(cx, other, other, iteratorId, &callee))
+ return nullptr;
+
+ // Steps 6-8.
+ RootedObject arrayLike(cx);
+ if (!callee.isNullOrUndefined()) {
+ // Throw if other[Symbol.iterator] isn't callable.
+ if (!callee.isObject() || !callee.toObject().isCallable()) {
+ RootedValue otherVal(cx, ObjectValue(*other));
+ UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, otherVal, nullptr);
+ if (!bytes)
+ return nullptr;
+ JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE,
+ bytes.get());
+ return nullptr;
+ }
+
+ FixedInvokeArgs<2> args2(cx);
+ args2[0].setObject(*other);
+ args2[1].set(callee);
+
+ // Step 6.a.
+ RootedValue rval(cx);
+ if (!CallSelfHostedFunction(cx, cx->names().IterableToList, UndefinedHandleValue, args2,
+ &rval))
+ {
+ return nullptr;
+ }
+
+ // Steps 6.b-g (Implemented in steps 9-13 below).
+ arrayLike = &rval.toObject();
+ } else {
+ // Step 7 is an assertion: object is not an Iterator. Testing this is
+ // literally the very last thing we did, so we don't assert here.
+
+ // Step 8.
+ arrayLike = other;
+ }
+
+ // Step 9.
+ uint32_t len;
+ if (!GetLengthProperty(cx, arrayLike, &len))
+ return nullptr;
+
+ // Step 10.
+ Rooted<ArrayBufferObject*> buffer(cx);
+ if (!maybeCreateArrayBuffer(cx, len, BYTES_PER_ELEMENT, nullptr, &buffer))
+ return nullptr;
+
+ Rooted<TypedArrayObject*> obj(cx, makeInstance(cx, buffer, 0, len, proto));
+ if (!obj)
+ return nullptr;
+
+ // Steps 11-12.
+ if (!TypedArrayMethods<TypedArrayObject>::setFromNonTypedArray(cx, obj, arrayLike, len))
+ return nullptr;
+
+ // Step 13.
+ return obj;
+}
+
+bool
+TypedArrayConstructor(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_CALL_OR_CONSTRUCT,
+ args.isConstructing() ? "construct" : "call");
+ return false;
+}
+
+/* static */ bool
+TypedArrayObject::GetTemplateObjectForNative(JSContext* cx, Native native, uint32_t len,
+ MutableHandleObject res)
+{
+#define CHECK_TYPED_ARRAY_CONSTRUCTOR(T, N) \
+ if (native == &TypedArrayObjectTemplate<T>::class_constructor) { \
+ size_t nbytes; \
+ if (!js::CalculateAllocSize<T>(len, &nbytes)) \
+ return true; \
+ \
+ if (nbytes < TypedArrayObject::SINGLETON_BYTE_LENGTH) { \
+ res.set(TypedArrayObjectTemplate<T>::makeTemplateObject(cx, len)); \
+ return !!res; \
+ } \
+ }
+JS_FOR_EACH_TYPED_ARRAY(CHECK_TYPED_ARRAY_CONSTRUCTOR)
+#undef CHECK_TYPED_ARRAY_CONSTRUCTOR
+ return true;
+}
+
+/*
+ * These next 3 functions are brought to you by the buggy GCC we use to build
+ * B2G ICS. Older GCC versions have a bug in which they fail to compile
+ * reinterpret_casts of templated functions with the message: "insufficient
+ * contextual information to determine type". JS_PSG needs to
+ * reinterpret_cast<JSGetterOp>, so this causes problems for us here.
+ *
+ * We could restructure all this code to make this nicer, but since ICS isn't
+ * going to be around forever (and since this bug is fixed with the newer GCC
+ * versions we use on JB and KK), the workaround here is designed for ease of
+ * removal. When you stop seeing ICS Emulator builds on TBPL, remove these 3
+ * JSNatives and insert the templated callee directly into the JS_PSG below.
+ */
+static bool
+TypedArray_lengthGetter(JSContext* cx, unsigned argc, Value* vp)
+{
+ return TypedArrayObject::Getter<TypedArrayObject::lengthValue>(cx, argc, vp); \
+}
+
+static bool
+TypedArray_byteLengthGetter(JSContext* cx, unsigned argc, Value* vp)
+{
+ return TypedArrayObject::Getter<TypedArrayObject::byteLengthValue>(cx, argc, vp);
+}
+
+static bool
+TypedArray_byteOffsetGetter(JSContext* cx, unsigned argc, Value* vp)
+{
+ return TypedArrayObject::Getter<TypedArrayObject::byteOffsetValue>(cx, argc, vp);
+}
+
+bool
+BufferGetterImpl(JSContext* cx, const CallArgs& args)
+{
+ MOZ_ASSERT(TypedArrayObject::is(args.thisv()));
+ Rooted<TypedArrayObject*> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>());
+ if (!TypedArrayObject::ensureHasBuffer(cx, tarray))
+ return false;
+ args.rval().set(TypedArrayObject::bufferValue(tarray));
+ return true;
+}
+
+/*static*/ bool
+js::TypedArray_bufferGetter(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<TypedArrayObject::is, BufferGetterImpl>(cx, args);
+}
+
+/* static */ const JSPropertySpec
+TypedArrayObject::protoAccessors[] = {
+ JS_PSG("length", TypedArray_lengthGetter, 0),
+ JS_PSG("buffer", TypedArray_bufferGetter, 0),
+ JS_PSG("byteLength", TypedArray_byteLengthGetter, 0),
+ JS_PSG("byteOffset", TypedArray_byteOffsetGetter, 0),
+ JS_SELF_HOSTED_SYM_GET(toStringTag, "TypedArrayToStringTag", 0),
+ JS_PS_END
+};
+
+/* static */ bool
+TypedArrayObject::set(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<TypedArrayObject::is,
+ TypedArrayMethods<TypedArrayObject>::set>(cx, args);
+}
+
+/* static */ const JSFunctionSpec
+TypedArrayObject::protoFunctions[] = {
+ JS_SELF_HOSTED_FN("subarray", "TypedArraySubarray", 2, 0),
+#if 0 /* disabled until perf-testing is completed */
+ JS_SELF_HOSTED_FN("set", "TypedArraySet", 2, 0),
+#else
+ JS_FN("set", TypedArrayObject::set, 1, 0),
+#endif
+ JS_SELF_HOSTED_FN("copyWithin", "TypedArrayCopyWithin", 3, 0),
+ JS_SELF_HOSTED_FN("every", "TypedArrayEvery", 1, 0),
+ JS_SELF_HOSTED_FN("fill", "TypedArrayFill", 3, 0),
+ JS_SELF_HOSTED_FN("filter", "TypedArrayFilter", 1, 0),
+ JS_SELF_HOSTED_FN("find", "TypedArrayFind", 1, 0),
+ JS_SELF_HOSTED_FN("findIndex", "TypedArrayFindIndex", 1, 0),
+ JS_SELF_HOSTED_FN("forEach", "TypedArrayForEach", 1, 0),
+ JS_SELF_HOSTED_FN("indexOf", "TypedArrayIndexOf", 2, 0),
+ JS_SELF_HOSTED_FN("join", "TypedArrayJoin", 1, 0),
+ JS_SELF_HOSTED_FN("lastIndexOf", "TypedArrayLastIndexOf", 2, 0),
+ JS_SELF_HOSTED_FN("map", "TypedArrayMap", 1, 0),
+ JS_SELF_HOSTED_FN("reduce", "TypedArrayReduce", 1, 0),
+ JS_SELF_HOSTED_FN("reduceRight", "TypedArrayReduceRight", 1, 0),
+ JS_SELF_HOSTED_FN("reverse", "TypedArrayReverse", 0, 0),
+ JS_SELF_HOSTED_FN("slice", "TypedArraySlice", 2, 0),
+ JS_SELF_HOSTED_FN("some", "TypedArraySome", 1, 0),
+ JS_SELF_HOSTED_FN("sort", "TypedArraySort", 1, 0),
+ JS_SELF_HOSTED_FN("entries", "TypedArrayEntries", 0, 0),
+ JS_SELF_HOSTED_FN("keys", "TypedArrayKeys", 0, 0),
+ JS_SELF_HOSTED_FN("values", "TypedArrayValues", 0, 0),
+ JS_SELF_HOSTED_SYM_FN(iterator, "TypedArrayValues", 0, 0),
+ JS_SELF_HOSTED_FN("includes", "TypedArrayIncludes", 2, 0),
+ JS_SELF_HOSTED_FN("toString", "ArrayToString", 0, 0),
+ JS_SELF_HOSTED_FN("toLocaleString", "TypedArrayToLocaleString", 2, 0),
+ JS_FS_END
+};
+
+/* static */ const JSFunctionSpec
+TypedArrayObject::staticFunctions[] = {
+ JS_SELF_HOSTED_FN("from", "TypedArrayStaticFrom", 3, 0),
+ JS_SELF_HOSTED_FN("of", "TypedArrayStaticOf", 0, 0),
+ JS_FS_END
+};
+
+/* static */ const JSPropertySpec
+TypedArrayObject::staticProperties[] = {
+ JS_SELF_HOSTED_SYM_GET(species, "TypedArraySpecies", 0),
+ JS_PS_END
+};
+
+static const ClassSpec
+TypedArrayObjectSharedTypedArrayPrototypeClassSpec = {
+ GenericCreateConstructor<TypedArrayConstructor, 3, gc::AllocKind::FUNCTION>,
+ GenericCreatePrototype,
+ TypedArrayObject::staticFunctions,
+ TypedArrayObject::staticProperties,
+ TypedArrayObject::protoFunctions,
+ TypedArrayObject::protoAccessors,
+ nullptr,
+ ClassSpec::DontDefineConstructor
+};
+
+/* static */ const Class
+TypedArrayObject::sharedTypedArrayPrototypeClass = {
+ // Actually ({}).toString.call(%TypedArray%.prototype) should throw,
+ // because %TypedArray%.prototype lacks the the typed array internal
+ // slots. (It's not clear this is desirable -- particularly applied to
+ // the actual typed array prototypes, see below -- but it's what ES6
+ // draft 20140824 requires.) But this is about as much as we can do
+ // until we implement @@toStringTag.
+ "???",
+ JSCLASS_HAS_CACHED_PROTO(JSProto_TypedArray),
+ JS_NULL_CLASS_OPS,
+ &TypedArrayObjectSharedTypedArrayPrototypeClassSpec
+};
+
+template<typename T>
+bool
+ArrayBufferObject::createTypedArrayFromBufferImpl(JSContext* cx, const CallArgs& args)
+{
+ typedef TypedArrayObjectTemplate<T> ArrayType;
+ MOZ_ASSERT(IsAnyArrayBuffer(args.thisv()));
+ MOZ_ASSERT(args.length() == 3);
+
+ Rooted<JSObject*> buffer(cx, &args.thisv().toObject());
+ Rooted<JSObject*> proto(cx, &args[2].toObject());
+
+ Rooted<JSObject*> obj(cx);
+ double byteOffset = args[0].toNumber();
+ MOZ_ASSERT(0 <= byteOffset);
+ MOZ_ASSERT(byteOffset <= UINT32_MAX);
+ MOZ_ASSERT(byteOffset == uint32_t(byteOffset));
+ obj = ArrayType::fromBufferWithProto(cx, buffer, uint32_t(byteOffset), args[1].toInt32(),
+ proto);
+ if (!obj)
+ return false;
+ args.rval().setObject(*obj);
+ return true;
+}
+
+template<typename T>
+bool
+ArrayBufferObject::createTypedArrayFromBuffer(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<IsAnyArrayBuffer, createTypedArrayFromBufferImpl<T> >(cx, args);
+}
+
+// this default implementation is only valid for integer types
+// less than 32-bits in size.
+template<typename NativeType>
+Value
+TypedArrayObjectTemplate<NativeType>::getIndexValue(JSObject* tarray, uint32_t index)
+{
+ static_assert(sizeof(NativeType) < 4,
+ "this method must only handle NativeType values that are "
+ "always exact int32_t values");
+
+ return Int32Value(getIndex(tarray, index));
+}
+
+namespace {
+
+// and we need to specialize for 32-bit integers and floats
+template<>
+Value
+TypedArrayObjectTemplate<int32_t>::getIndexValue(JSObject* tarray, uint32_t index)
+{
+ return Int32Value(getIndex(tarray, index));
+}
+
+template<>
+Value
+TypedArrayObjectTemplate<uint32_t>::getIndexValue(JSObject* tarray, uint32_t index)
+{
+ uint32_t val = getIndex(tarray, index);
+ return NumberValue(val);
+}
+
+template<>
+Value
+TypedArrayObjectTemplate<float>::getIndexValue(JSObject* tarray, uint32_t index)
+{
+ float val = getIndex(tarray, index);
+ double dval = val;
+
+ /*
+ * Doubles in typed arrays could be typed-punned arrays of integers. This
+ * could allow user code to break the engine-wide invariant that only
+ * canonical nans are stored into jsvals, which means user code could
+ * confuse the engine into interpreting a double-typed jsval as an
+ * object-typed jsval.
+ *
+ * This could be removed for platforms/compilers known to convert a 32-bit
+ * non-canonical nan to a 64-bit canonical nan.
+ */
+ return DoubleValue(CanonicalizeNaN(dval));
+}
+
+template<>
+Value
+TypedArrayObjectTemplate<double>::getIndexValue(JSObject* tarray, uint32_t index)
+{
+ double val = getIndex(tarray, index);
+
+ /*
+ * Doubles in typed arrays could be typed-punned arrays of integers. This
+ * could allow user code to break the engine-wide invariant that only
+ * canonical nans are stored into jsvals, which means user code could
+ * confuse the engine into interpreting a double-typed jsval as an
+ * object-typed jsval.
+ */
+ return DoubleValue(CanonicalizeNaN(val));
+}
+
+} /* anonymous namespace */
+
+static NewObjectKind
+DataViewNewObjectKind(JSContext* cx, uint32_t byteLength, JSObject* proto)
+{
+ if (!proto && byteLength >= TypedArrayObject::SINGLETON_BYTE_LENGTH)
+ return SingletonObject;
+ jsbytecode* pc;
+ JSScript* script = cx->currentScript(&pc);
+ if (script && ObjectGroup::useSingletonForAllocationSite(script, pc, &DataViewObject::class_))
+ return SingletonObject;
+ return GenericObject;
+}
+
+DataViewObject*
+DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
+ Handle<ArrayBufferObject*> arrayBuffer, JSObject* protoArg)
+{
+ if (arrayBuffer->isDetached()) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
+ return nullptr;
+ }
+
+ MOZ_ASSERT(byteOffset <= INT32_MAX);
+ MOZ_ASSERT(byteLength <= INT32_MAX);
+ MOZ_ASSERT(byteOffset + byteLength < UINT32_MAX);
+ MOZ_ASSERT(!arrayBuffer || !arrayBuffer->is<SharedArrayBufferObject>());
+
+ RootedObject proto(cx, protoArg);
+ RootedObject obj(cx);
+
+ NewObjectKind newKind = DataViewNewObjectKind(cx, byteLength, proto);
+ obj = NewObjectWithClassProto(cx, &class_, proto, newKind);
+ if (!obj)
+ return nullptr;
+
+ if (!proto) {
+ if (byteLength >= TypedArrayObject::SINGLETON_BYTE_LENGTH) {
+ MOZ_ASSERT(obj->isSingleton());
+ } else {
+ jsbytecode* pc;
+ RootedScript script(cx, cx->currentScript(&pc));
+ if (script && !ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, obj,
+ newKind == SingletonObject))
+ {
+ return nullptr;
+ }
+ }
+ }
+
+ // Caller should have established these preconditions, and no
+ // (non-self-hosted) JS code has had an opportunity to run so nothing can
+ // have invalidated them.
+ MOZ_ASSERT(byteOffset <= arrayBuffer->byteLength());
+ MOZ_ASSERT(byteOffset + byteLength <= arrayBuffer->byteLength());
+
+ DataViewObject& dvobj = obj->as<DataViewObject>();
+ dvobj.setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(byteOffset));
+ dvobj.setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(byteLength));
+ dvobj.setFixedSlot(TypedArrayObject::BUFFER_SLOT, ObjectValue(*arrayBuffer));
+ dvobj.initPrivate(arrayBuffer->dataPointer() + byteOffset);
+
+ // Include a barrier if the data view's data pointer is in the nursery, as
+ // is done for typed arrays.
+ if (!IsInsideNursery(obj) && cx->runtime()->gc.nursery.isInside(arrayBuffer->dataPointer()))
+ cx->runtime()->gc.storeBuffer.putWholeCell(obj);
+
+ // Verify that the private slot is at the expected place
+ MOZ_ASSERT(dvobj.numFixedSlots() == TypedArrayObject::DATA_SLOT);
+
+ if (!arrayBuffer->addView(cx, &dvobj))
+ return nullptr;
+
+ return &dvobj;
+}
+
+bool
+DataViewObject::getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, const CallArgs& args,
+ uint32_t* byteOffsetPtr, uint32_t* byteLengthPtr)
+{
+ if (!IsArrayBuffer(bufobj)) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
+ "DataView", "ArrayBuffer", bufobj->getClass()->name);
+ return false;
+ }
+
+ Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(bufobj));
+ uint32_t byteOffset = 0;
+ uint32_t byteLength = buffer->byteLength();
+
+ if (args.length() > 1) {
+ if (!ToUint32(cx, args[1], &byteOffset))
+ return false;
+ if (byteOffset > INT32_MAX) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE,
+ "1");
+ return false;
+ }
+ }
+
+ if (buffer->isDetached()) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
+ return false;
+ }
+
+ if (args.length() > 1) {
+ if (byteOffset > byteLength) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE,
+ "1");
+ return false;
+ }
+
+ if (args.get(2).isUndefined()) {
+ byteLength -= byteOffset;
+ } else {
+ if (!ToUint32(cx, args[2], &byteLength))
+ return false;
+ if (byteLength > INT32_MAX) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_ARG_INDEX_OUT_OF_RANGE, "2");
+ return false;
+ }
+
+ MOZ_ASSERT(byteOffset + byteLength >= byteOffset,
+ "can't overflow: both numbers are less than INT32_MAX");
+ if (byteOffset + byteLength > buffer->byteLength()) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_ARG_INDEX_OUT_OF_RANGE, "1");
+ return false;
+ }
+ }
+ }
+
+ /* The sum of these cannot overflow a uint32_t */
+ MOZ_ASSERT(byteOffset <= INT32_MAX);
+ MOZ_ASSERT(byteLength <= INT32_MAX);
+
+
+ *byteOffsetPtr = byteOffset;
+ *byteLengthPtr = byteLength;
+
+ return true;
+}
+
+bool
+DataViewObject::constructSameCompartment(JSContext* cx, HandleObject bufobj, const CallArgs& args)
+{
+ MOZ_ASSERT(args.isConstructing());
+ assertSameCompartment(cx, bufobj);
+
+ uint32_t byteOffset, byteLength;
+ if (!getAndCheckConstructorArgs(cx, bufobj, args, &byteOffset, &byteLength))
+ return false;
+
+ RootedObject proto(cx);
+ RootedObject newTarget(cx, &args.newTarget().toObject());
+ if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
+ return false;
+
+ Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(bufobj));
+ JSObject* obj = DataViewObject::create(cx, byteOffset, byteLength, buffer, proto);
+ if (!obj)
+ return false;
+ args.rval().setObject(*obj);
+ return true;
+}
+
+// Create a DataView object in another compartment.
+//
+// ES6 supports creating a DataView in global A (using global A's DataView
+// constructor) backed by an ArrayBuffer created in global B.
+//
+// Our DataViewObject implementation doesn't support a DataView in
+// compartment A backed by an ArrayBuffer in compartment B. So in this case,
+// we create the DataView in B (!) and return a cross-compartment wrapper.
+//
+// Extra twist: the spec says the new DataView's [[Prototype]] must be
+// A's DataView.prototype. So even though we're creating the DataView in B,
+// its [[Prototype]] must be (a cross-compartment wrapper for) the
+// DataView.prototype in A.
+//
+// As if this were not confusing enough, the way we actually do this is also
+// tricky. We call compartment A's createDataViewForThis method, passing it
+// bufobj as `this`. That calls ArrayBufferObject::createDataViewForThis(),
+// which uses CallNonGenericMethod to switch to compartment B so that
+// the new DataView is created there.
+bool
+DataViewObject::constructWrapped(JSContext* cx, HandleObject bufobj, const CallArgs& args)
+{
+ MOZ_ASSERT(args.isConstructing());
+ MOZ_ASSERT(bufobj->is<WrapperObject>());
+
+ JSObject* unwrapped = CheckedUnwrap(bufobj);
+ if (!unwrapped) {
+ JS_ReportErrorASCII(cx, "Permission denied to access object");
+ return false;
+ }
+
+ // NB: This entails the IsArrayBuffer check
+ uint32_t byteOffset, byteLength;
+ if (!getAndCheckConstructorArgs(cx, unwrapped, args, &byteOffset, &byteLength))
+ return false;
+
+ // Make sure to get the [[Prototype]] for the created view from this
+ // compartment.
+ RootedObject proto(cx);
+ RootedObject newTarget(cx, &args.newTarget().toObject());
+ if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
+ return false;
+
+ Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal());
+ if (!proto) {
+ proto = global->getOrCreateDataViewPrototype(cx);
+ if (!proto)
+ return false;
+ }
+
+ FixedInvokeArgs<3> args2(cx);
+
+ args2[0].set(PrivateUint32Value(byteOffset));
+ args2[1].set(PrivateUint32Value(byteLength));
+ args2[2].setObject(*proto);
+
+ RootedValue fval(cx, global->createDataViewForThis());
+ RootedValue thisv(cx, ObjectValue(*bufobj));
+ return js::Call(cx, fval, thisv, args2, args.rval());
+}
+
+bool
+DataViewObject::class_constructor(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ if (!ThrowIfNotConstructing(cx, args, "DataView"))
+ return false;
+
+ RootedObject bufobj(cx);
+ if (!GetFirstArgumentAsObject(cx, args, "DataView constructor", &bufobj))
+ return false;
+
+ if (bufobj->is<WrapperObject>())
+ return constructWrapped(cx, bufobj, args);
+ return constructSameCompartment(cx, bufobj, args);
+}
+
+template <typename NativeType>
+/* static */ uint8_t*
+DataViewObject::getDataPointer(JSContext* cx, Handle<DataViewObject*> obj, double offset)
+{
+ MOZ_ASSERT(offset >= 0);
+
+ const size_t TypeSize = sizeof(NativeType);
+ if (offset > UINT32_MAX - TypeSize || offset + TypeSize > obj->byteLength()) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE,
+ "1");
+ return nullptr;
+ }
+
+ MOZ_ASSERT(offset < UINT32_MAX);
+ return static_cast<uint8_t*>(obj->dataPointer()) + uint32_t(offset);
+}
+
+static inline bool
+needToSwapBytes(bool littleEndian)
+{
+#if MOZ_LITTLE_ENDIAN
+ return !littleEndian;
+#else
+ return littleEndian;
+#endif
+}
+
+static inline uint8_t
+swapBytes(uint8_t x)
+{
+ return x;
+}
+
+static inline uint16_t
+swapBytes(uint16_t x)
+{
+ return ((x & 0xff) << 8) | (x >> 8);
+}
+
+static inline uint32_t
+swapBytes(uint32_t x)
+{
+ return ((x & 0xff) << 24) |
+ ((x & 0xff00) << 8) |
+ ((x & 0xff0000) >> 8) |
+ ((x & 0xff000000) >> 24);
+}
+
+static inline uint64_t
+swapBytes(uint64_t x)
+{
+ uint32_t a = x & UINT32_MAX;
+ uint32_t b = x >> 32;
+ return (uint64_t(swapBytes(a)) << 32) | swapBytes(b);
+}
+
+template <typename DataType> struct DataToRepType { typedef DataType result; };
+template <> struct DataToRepType<int8_t> { typedef uint8_t result; };
+template <> struct DataToRepType<uint8_t> { typedef uint8_t result; };
+template <> struct DataToRepType<int16_t> { typedef uint16_t result; };
+template <> struct DataToRepType<uint16_t> { typedef uint16_t result; };
+template <> struct DataToRepType<int32_t> { typedef uint32_t result; };
+template <> struct DataToRepType<uint32_t> { typedef uint32_t result; };
+template <> struct DataToRepType<float> { typedef uint32_t result; };
+template <> struct DataToRepType<double> { typedef uint64_t result; };
+
+template <typename DataType>
+struct DataViewIO
+{
+ typedef typename DataToRepType<DataType>::result ReadWriteType;
+
+ static void fromBuffer(DataType* dest, const uint8_t* unalignedBuffer, bool wantSwap)
+ {
+ MOZ_ASSERT((reinterpret_cast<uintptr_t>(dest) & (Min<size_t>(MOZ_ALIGNOF(void*), sizeof(DataType)) - 1)) == 0);
+ memcpy((void*) dest, unalignedBuffer, sizeof(ReadWriteType));
+ if (wantSwap) {
+ ReadWriteType* rwDest = reinterpret_cast<ReadWriteType*>(dest);
+ *rwDest = swapBytes(*rwDest);
+ }
+ }
+
+ static void toBuffer(uint8_t* unalignedBuffer, const DataType* src, bool wantSwap)
+ {
+ MOZ_ASSERT((reinterpret_cast<uintptr_t>(src) & (Min<size_t>(MOZ_ALIGNOF(void*), sizeof(DataType)) - 1)) == 0);
+ ReadWriteType temp = *reinterpret_cast<const ReadWriteType*>(src);
+ if (wantSwap)
+ temp = swapBytes(temp);
+ memcpy(unalignedBuffer, (void*) &temp, sizeof(ReadWriteType));
+ }
+};
+
+static bool
+ToIndex(JSContext* cx, HandleValue v, double* index)
+{
+ if (v.isUndefined()) {
+ *index = 0.0;
+ return true;
+ }
+
+ double integerIndex;
+ if (!ToInteger(cx, v, &integerIndex))
+ return false;
+
+ // Inlined version of ToLength.
+ // 1. Already an integer
+ // 2. Step eliminates < 0, +0 == -0 with SameValueZero
+ // 3/4. Limit to <= 2^53-1, so everything above should fail.
+ if (integerIndex < 0 || integerIndex > 9007199254740991) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
+ return false;
+ }
+
+ *index = integerIndex;
+ return true;
+}
+
+template<typename NativeType>
+/* static */ bool
+DataViewObject::read(JSContext* cx, Handle<DataViewObject*> obj,
+ const CallArgs& args, NativeType* val, const char* method)
+{
+ // Steps 1-2. done by the caller
+ // Step 3. unnecessary assert
+
+ // Step 4.
+ double getIndex;
+ if (!ToIndex(cx, args.get(0), &getIndex))
+ return false;
+
+ // Step 5.
+ bool isLittleEndian = args.length() >= 2 && ToBoolean(args[1]);
+
+ // Steps 6-7.
+ if (obj->arrayBuffer().isDetached()) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
+ return false;
+ }
+
+ // Steps 8-12.
+ uint8_t* data = DataViewObject::getDataPointer<NativeType>(cx, obj, getIndex);
+ if (!data)
+ return false;
+
+ // Step 13.
+ DataViewIO<NativeType>::fromBuffer(val, data, needToSwapBytes(isLittleEndian));
+ return true;
+}
+
+template <typename NativeType>
+static inline bool
+WebIDLCast(JSContext* cx, HandleValue value, NativeType* out)
+{
+ int32_t temp;
+ if (!ToInt32(cx, value, &temp))
+ return false;
+ // Technically, the behavior of assigning an out of range value to a signed
+ // variable is undefined. In practice, compilers seem to do what we want
+ // without issuing any warnings.
+ *out = static_cast<NativeType>(temp);
+ return true;
+}
+
+template <>
+inline bool
+WebIDLCast<float>(JSContext* cx, HandleValue value, float* out)
+{
+ double temp;
+ if (!ToNumber(cx, value, &temp))
+ return false;
+ *out = static_cast<float>(temp);
+ return true;
+}
+
+template <>
+inline bool
+WebIDLCast<double>(JSContext* cx, HandleValue value, double* out)
+{
+ return ToNumber(cx, value, out);
+}
+
+template<typename NativeType>
+/* static */ bool
+DataViewObject::write(JSContext* cx, Handle<DataViewObject*> obj,
+ const CallArgs& args, const char* method)
+{
+ // Steps 1-2. done by the caller
+ // Step 3. unnecessary assert
+
+ // Step 4.
+ double getIndex;
+ if (!ToIndex(cx, args.get(0), &getIndex))
+ return false;
+
+ // Step 5. Should just call ToNumber (unobservable)
+ NativeType value;
+ if (!WebIDLCast(cx, args.get(1), &value))
+ return false;
+
+#ifdef JS_MORE_DETERMINISTIC
+ // See the comment in ElementSpecific::doubleToNative.
+ if (TypeIsFloatingPoint<NativeType>())
+ value = JS::CanonicalizeNaN(value);
+#endif
+
+ // Step 6.
+ bool isLittleEndian = args.length() >= 3 && ToBoolean(args[2]);
+
+ // Steps 7-8.
+ if (obj->arrayBuffer().isDetached()) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
+ return false;
+ }
+
+ // Steps 9-13.
+ uint8_t* data = DataViewObject::getDataPointer<NativeType>(cx, obj, getIndex);
+ if (!data)
+ return false;
+
+ // Step 14.
+ DataViewIO<NativeType>::toBuffer(data, &value, needToSwapBytes(isLittleEndian));
+ return true;
+}
+
+bool
+DataViewObject::getInt8Impl(JSContext* cx, const CallArgs& args)
+{
+ MOZ_ASSERT(is(args.thisv()));
+
+ Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
+
+ int8_t val;
+ if (!read(cx, thisView, args, &val, "getInt8"))
+ return false;
+ args.rval().setInt32(val);
+ return true;
+}
+
+bool
+DataViewObject::fun_getInt8(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<is, getInt8Impl>(cx, args);
+}
+
+bool
+DataViewObject::getUint8Impl(JSContext* cx, const CallArgs& args)
+{
+ MOZ_ASSERT(is(args.thisv()));
+
+ Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
+
+ uint8_t val;
+ if (!read(cx, thisView, args, &val, "getUint8"))
+ return false;
+ args.rval().setInt32(val);
+ return true;
+}
+
+bool
+DataViewObject::fun_getUint8(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<is, getUint8Impl>(cx, args);
+}
+
+bool
+DataViewObject::getInt16Impl(JSContext* cx, const CallArgs& args)
+{
+ MOZ_ASSERT(is(args.thisv()));
+
+ Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
+
+ int16_t val;
+ if (!read(cx, thisView, args, &val, "getInt16"))
+ return false;
+ args.rval().setInt32(val);
+ return true;
+}
+
+bool
+DataViewObject::fun_getInt16(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<is, getInt16Impl>(cx, args);
+}
+
+bool
+DataViewObject::getUint16Impl(JSContext* cx, const CallArgs& args)
+{
+ MOZ_ASSERT(is(args.thisv()));
+
+ Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
+
+ uint16_t val;
+ if (!read(cx, thisView, args, &val, "getUint16"))
+ return false;
+ args.rval().setInt32(val);
+ return true;
+}
+
+bool
+DataViewObject::fun_getUint16(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<is, getUint16Impl>(cx, args);
+}
+
+bool
+DataViewObject::getInt32Impl(JSContext* cx, const CallArgs& args)
+{
+ MOZ_ASSERT(is(args.thisv()));
+
+ Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
+
+ int32_t val;
+ if (!read(cx, thisView, args, &val, "getInt32"))
+ return false;
+ args.rval().setInt32(val);
+ return true;
+}
+
+bool
+DataViewObject::fun_getInt32(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<is, getInt32Impl>(cx, args);
+}
+
+bool
+DataViewObject::getUint32Impl(JSContext* cx, const CallArgs& args)
+{
+ MOZ_ASSERT(is(args.thisv()));
+
+ Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
+
+ uint32_t val;
+ if (!read(cx, thisView, args, &val, "getUint32"))
+ return false;
+ args.rval().setNumber(val);
+ return true;
+}
+
+bool
+DataViewObject::fun_getUint32(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<is, getUint32Impl>(cx, args);
+}
+
+bool
+DataViewObject::getFloat32Impl(JSContext* cx, const CallArgs& args)
+{
+ MOZ_ASSERT(is(args.thisv()));
+
+ Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
+
+ float val;
+ if (!read(cx, thisView, args, &val, "getFloat32"))
+ return false;
+
+ args.rval().setDouble(CanonicalizeNaN(val));
+ return true;
+}
+
+bool
+DataViewObject::fun_getFloat32(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<is, getFloat32Impl>(cx, args);
+}
+
+bool
+DataViewObject::getFloat64Impl(JSContext* cx, const CallArgs& args)
+{
+ MOZ_ASSERT(is(args.thisv()));
+
+ Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
+
+ double val;
+ if (!read(cx, thisView, args, &val, "getFloat64"))
+ return false;
+
+ args.rval().setDouble(CanonicalizeNaN(val));
+ return true;
+}
+
+bool
+DataViewObject::fun_getFloat64(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<is, getFloat64Impl>(cx, args);
+}
+
+bool
+DataViewObject::setInt8Impl(JSContext* cx, const CallArgs& args)
+{
+ MOZ_ASSERT(is(args.thisv()));
+
+ Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
+
+ if (!write<int8_t>(cx, thisView, args, "setInt8"))
+ return false;
+ args.rval().setUndefined();
+ return true;
+}
+
+bool
+DataViewObject::fun_setInt8(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<is, setInt8Impl>(cx, args);
+}
+
+bool
+DataViewObject::setUint8Impl(JSContext* cx, const CallArgs& args)
+{
+ MOZ_ASSERT(is(args.thisv()));
+
+ Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
+
+ if (!write<uint8_t>(cx, thisView, args, "setUint8"))
+ return false;
+ args.rval().setUndefined();
+ return true;
+}
+
+bool
+DataViewObject::fun_setUint8(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<is, setUint8Impl>(cx, args);
+}
+
+bool
+DataViewObject::setInt16Impl(JSContext* cx, const CallArgs& args)
+{
+ MOZ_ASSERT(is(args.thisv()));
+
+ Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
+
+ if (!write<int16_t>(cx, thisView, args, "setInt16"))
+ return false;
+ args.rval().setUndefined();
+ return true;
+}
+
+bool
+DataViewObject::fun_setInt16(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<is, setInt16Impl>(cx, args);
+}
+
+bool
+DataViewObject::setUint16Impl(JSContext* cx, const CallArgs& args)
+{
+ MOZ_ASSERT(is(args.thisv()));
+
+ Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
+
+ if (!write<uint16_t>(cx, thisView, args, "setUint16"))
+ return false;
+ args.rval().setUndefined();
+ return true;
+}
+
+bool
+DataViewObject::fun_setUint16(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<is, setUint16Impl>(cx, args);
+}
+
+bool
+DataViewObject::setInt32Impl(JSContext* cx, const CallArgs& args)
+{
+ MOZ_ASSERT(is(args.thisv()));
+
+ Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
+
+ if (!write<int32_t>(cx, thisView, args, "setInt32"))
+ return false;
+ args.rval().setUndefined();
+ return true;
+}
+
+bool
+DataViewObject::fun_setInt32(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<is, setInt32Impl>(cx, args);
+}
+
+bool
+DataViewObject::setUint32Impl(JSContext* cx, const CallArgs& args)
+{
+ MOZ_ASSERT(is(args.thisv()));
+
+ Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
+
+ if (!write<uint32_t>(cx, thisView, args, "setUint32"))
+ return false;
+ args.rval().setUndefined();
+ return true;
+}
+
+bool
+DataViewObject::fun_setUint32(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<is, setUint32Impl>(cx, args);
+}
+
+bool
+DataViewObject::setFloat32Impl(JSContext* cx, const CallArgs& args)
+{
+ MOZ_ASSERT(is(args.thisv()));
+
+ Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
+
+ if (!write<float>(cx, thisView, args, "setFloat32"))
+ return false;
+ args.rval().setUndefined();
+ return true;
+}
+
+bool
+DataViewObject::fun_setFloat32(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<is, setFloat32Impl>(cx, args);
+}
+
+bool
+DataViewObject::setFloat64Impl(JSContext* cx, const CallArgs& args)
+{
+ MOZ_ASSERT(is(args.thisv()));
+
+ Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
+
+ if (!write<double>(cx, thisView, args, "setFloat64"))
+ return false;
+ args.rval().setUndefined();
+ return true;
+}
+
+bool
+DataViewObject::fun_setFloat64(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<is, setFloat64Impl>(cx, args);
+}
+
+Value
+TypedArrayObject::getElement(uint32_t index)
+{
+ switch (type()) {
+ case Scalar::Int8:
+ return Int8Array::getIndexValue(this, index);
+ case Scalar::Uint8:
+ return Uint8Array::getIndexValue(this, index);
+ case Scalar::Int16:
+ return Int16Array::getIndexValue(this, index);
+ case Scalar::Uint16:
+ return Uint16Array::getIndexValue(this, index);
+ case Scalar::Int32:
+ return Int32Array::getIndexValue(this, index);
+ case Scalar::Uint32:
+ return Uint32Array::getIndexValue(this, index);
+ case Scalar::Float32:
+ return Float32Array::getIndexValue(this, index);
+ case Scalar::Float64:
+ return Float64Array::getIndexValue(this, index);
+ case Scalar::Uint8Clamped:
+ return Uint8ClampedArray::getIndexValue(this, index);
+ case Scalar::Int64:
+ case Scalar::Float32x4:
+ case Scalar::Int8x16:
+ case Scalar::Int16x8:
+ case Scalar::Int32x4:
+ case Scalar::MaxTypedArrayViewType:
+ break;
+ }
+
+ MOZ_CRASH("Unknown TypedArray type");
+}
+
+void
+TypedArrayObject::setElement(TypedArrayObject& obj, uint32_t index, double d)
+{
+ MOZ_ASSERT(index < obj.length());
+
+#ifdef JS_MORE_DETERMINISTIC
+ // See the comment in ElementSpecific::doubleToNative.
+ d = JS::CanonicalizeNaN(d);
+#endif
+
+ switch (obj.type()) {
+ case Scalar::Int8:
+ Int8Array::setIndexValue(obj, index, d);
+ return;
+ case Scalar::Uint8:
+ Uint8Array::setIndexValue(obj, index, d);
+ return;
+ case Scalar::Uint8Clamped:
+ Uint8ClampedArray::setIndexValue(obj, index, d);
+ return;
+ case Scalar::Int16:
+ Int16Array::setIndexValue(obj, index, d);
+ return;
+ case Scalar::Uint16:
+ Uint16Array::setIndexValue(obj, index, d);
+ return;
+ case Scalar::Int32:
+ Int32Array::setIndexValue(obj, index, d);
+ return;
+ case Scalar::Uint32:
+ Uint32Array::setIndexValue(obj, index, d);
+ return;
+ case Scalar::Float32:
+ Float32Array::setIndexValue(obj, index, d);
+ return;
+ case Scalar::Float64:
+ Float64Array::setIndexValue(obj, index, d);
+ return;
+ case Scalar::Int64:
+ case Scalar::Float32x4:
+ case Scalar::Int8x16:
+ case Scalar::Int16x8:
+ case Scalar::Int32x4:
+ case Scalar::MaxTypedArrayViewType:
+ break;
+ }
+
+ MOZ_CRASH("Unknown TypedArray type");
+}
+
+/***
+ *** JS impl
+ ***/
+
+/*
+ * TypedArrayObject boilerplate
+ */
+
+#define IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Name,NativeType) \
+ JS_FRIEND_API(JSObject*) JS_New ## Name ## Array(JSContext* cx, uint32_t nelements) \
+ { \
+ return TypedArrayObjectTemplate<NativeType>::fromLength(cx, nelements); \
+ } \
+ JS_FRIEND_API(JSObject*) JS_New ## Name ## ArrayFromArray(JSContext* cx, HandleObject other) \
+ { \
+ return TypedArrayObjectTemplate<NativeType>::fromArray(cx, other); \
+ } \
+ JS_FRIEND_API(JSObject*) JS_New ## Name ## ArrayWithBuffer(JSContext* cx, \
+ HandleObject arrayBuffer, uint32_t byteOffset, int32_t length) \
+ { \
+ return TypedArrayObjectTemplate<NativeType>::fromBuffer(cx, arrayBuffer, byteOffset, \
+ length); \
+ } \
+ JS_FRIEND_API(bool) JS_Is ## Name ## Array(JSObject* obj) \
+ { \
+ if (!(obj = CheckedUnwrap(obj))) \
+ return false; \
+ const Class* clasp = obj->getClass(); \
+ return clasp == TypedArrayObjectTemplate<NativeType>::instanceClass(); \
+ } \
+ JS_FRIEND_API(JSObject*) js::Unwrap ## Name ## Array(JSObject* obj) \
+ { \
+ obj = CheckedUnwrap(obj); \
+ if (!obj) \
+ return nullptr; \
+ const Class* clasp = obj->getClass(); \
+ if (clasp == TypedArrayObjectTemplate<NativeType>::instanceClass()) \
+ return obj; \
+ return nullptr; \
+ } \
+ const js::Class* const js::detail::Name ## ArrayClassPtr = \
+ &js::TypedArrayObject::classes[TypedArrayObjectTemplate<NativeType>::ArrayTypeID()];
+
+IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int8, int8_t)
+IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint8, uint8_t)
+IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint8Clamped, uint8_clamped)
+IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int16, int16_t)
+IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint16, uint16_t)
+IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int32, int32_t)
+IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint32, uint32_t)
+IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float32, float)
+IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float64, double)
+
+#define IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Name, ExternalType, InternalType) \
+ JS_FRIEND_API(JSObject*) JS_GetObjectAs ## Name ## Array(JSObject* obj, \
+ uint32_t* length, \
+ bool* isShared, \
+ ExternalType** data) \
+ { \
+ if (!(obj = CheckedUnwrap(obj))) \
+ return nullptr; \
+ \
+ const Class* clasp = obj->getClass(); \
+ if (clasp != TypedArrayObjectTemplate<InternalType>::instanceClass()) \
+ return nullptr; \
+ \
+ TypedArrayObject* tarr = &obj->as<TypedArrayObject>(); \
+ *length = tarr->length(); \
+ *isShared = tarr->isSharedMemory(); \
+ *data = static_cast<ExternalType*>(tarr->viewDataEither().unwrap(/*safe - caller sees isShared flag*/)); \
+ \
+ return obj; \
+ }
+
+IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int8, int8_t, int8_t)
+IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint8, uint8_t, uint8_t)
+IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint8Clamped, uint8_t, uint8_clamped)
+IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int16, int16_t, int16_t)
+IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint16, uint16_t, uint16_t)
+IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int32, int32_t, int32_t)
+IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint32, uint32_t, uint32_t)
+IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float32, float, float)
+IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float64, double, double)
+
+static const ClassOps TypedArrayClassOps = {
+ nullptr, /* addProperty */
+ nullptr, /* delProperty */
+ nullptr, /* getProperty */
+ nullptr, /* setProperty */
+ nullptr, /* enumerate */
+ nullptr, /* resolve */
+ nullptr, /* mayResolve */
+ TypedArrayObject::finalize, /* finalize */
+ nullptr, /* call */
+ nullptr, /* hasInstance */
+ nullptr, /* construct */
+ TypedArrayObject::trace, /* trace */
+};
+
+static const ClassExtension TypedArrayClassExtension = {
+ nullptr,
+ TypedArrayObject::objectMoved,
+};
+
+#define IMPL_TYPED_ARRAY_PROPERTIES(_type) \
+{ \
+JS_INT32_PS("BYTES_PER_ELEMENT", _type##Array::BYTES_PER_ELEMENT, \
+ JSPROP_READONLY | JSPROP_PERMANENT), \
+JS_PS_END \
+}
+
+static const JSPropertySpec static_prototype_properties[Scalar::MaxTypedArrayViewType][2] = {
+ IMPL_TYPED_ARRAY_PROPERTIES(Int8),
+ IMPL_TYPED_ARRAY_PROPERTIES(Uint8),
+ IMPL_TYPED_ARRAY_PROPERTIES(Int16),
+ IMPL_TYPED_ARRAY_PROPERTIES(Uint16),
+ IMPL_TYPED_ARRAY_PROPERTIES(Int32),
+ IMPL_TYPED_ARRAY_PROPERTIES(Uint32),
+ IMPL_TYPED_ARRAY_PROPERTIES(Float32),
+ IMPL_TYPED_ARRAY_PROPERTIES(Float64),
+ IMPL_TYPED_ARRAY_PROPERTIES(Uint8Clamped)
+};
+
+#define IMPL_TYPED_ARRAY_CLASS_SPEC(_type) \
+{ \
+ _type##Array::createConstructor, \
+ _type##Array::createPrototype, \
+ nullptr, \
+ static_prototype_properties[Scalar::Type::_type], \
+ nullptr, \
+ static_prototype_properties[Scalar::Type::_type], \
+ nullptr, \
+ JSProto_TypedArray \
+}
+
+static const ClassSpec TypedArrayObjectClassSpecs[Scalar::MaxTypedArrayViewType] = {
+ IMPL_TYPED_ARRAY_CLASS_SPEC(Int8),
+ IMPL_TYPED_ARRAY_CLASS_SPEC(Uint8),
+ IMPL_TYPED_ARRAY_CLASS_SPEC(Int16),
+ IMPL_TYPED_ARRAY_CLASS_SPEC(Uint16),
+ IMPL_TYPED_ARRAY_CLASS_SPEC(Int32),
+ IMPL_TYPED_ARRAY_CLASS_SPEC(Uint32),
+ IMPL_TYPED_ARRAY_CLASS_SPEC(Float32),
+ IMPL_TYPED_ARRAY_CLASS_SPEC(Float64),
+ IMPL_TYPED_ARRAY_CLASS_SPEC(Uint8Clamped)
+};
+
+#define IMPL_TYPED_ARRAY_CLASS(_type) \
+{ \
+ #_type "Array", \
+ JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) | \
+ JSCLASS_HAS_PRIVATE | \
+ JSCLASS_HAS_CACHED_PROTO(JSProto_##_type##Array) | \
+ JSCLASS_DELAY_METADATA_BUILDER | \
+ JSCLASS_SKIP_NURSERY_FINALIZE | \
+ JSCLASS_BACKGROUND_FINALIZE, \
+ &TypedArrayClassOps, \
+ &TypedArrayObjectClassSpecs[Scalar::Type::_type], \
+ &TypedArrayClassExtension \
+}
+
+const Class TypedArrayObject::classes[Scalar::MaxTypedArrayViewType] = {
+ IMPL_TYPED_ARRAY_CLASS(Int8),
+ IMPL_TYPED_ARRAY_CLASS(Uint8),
+ IMPL_TYPED_ARRAY_CLASS(Int16),
+ IMPL_TYPED_ARRAY_CLASS(Uint16),
+ IMPL_TYPED_ARRAY_CLASS(Int32),
+ IMPL_TYPED_ARRAY_CLASS(Uint32),
+ IMPL_TYPED_ARRAY_CLASS(Float32),
+ IMPL_TYPED_ARRAY_CLASS(Float64),
+ IMPL_TYPED_ARRAY_CLASS(Uint8Clamped)
+};
+
+#define IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(_type) \
+{ \
+ DELEGATED_CLASSSPEC(TypedArrayObject::classes[Scalar::Type::_type].spec), \
+ nullptr, \
+ nullptr, \
+ nullptr, \
+ nullptr, \
+ nullptr, \
+ nullptr, \
+ JSProto_TypedArray | ClassSpec::IsDelegated \
+}
+
+static const ClassSpec TypedArrayObjectProtoClassSpecs[Scalar::MaxTypedArrayViewType] = {
+ IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Int8),
+ IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Uint8),
+ IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Int16),
+ IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Uint16),
+ IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Int32),
+ IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Uint32),
+ IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Float32),
+ IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Float64),
+ IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Uint8Clamped)
+};
+
+// The various typed array prototypes are supposed to 1) be normal objects,
+// 2) stringify to "[object <name of constructor>]", and 3) (Gecko-specific)
+// be xrayable. The first and second requirements mandate (in the absence of
+// @@toStringTag) a custom class. The third requirement mandates that each
+// prototype's class have the relevant typed array's cached JSProtoKey in them.
+// Thus we need one class with cached prototype per kind of typed array, with a
+// delegated ClassSpec.
+#define IMPL_TYPED_ARRAY_PROTO_CLASS(_type) \
+{ \
+ /*
+ * Actually ({}).toString.call(Uint8Array.prototype) should throw, because
+ * Uint8Array.prototype lacks the the typed array internal slots. (Same as
+ * with %TypedArray%.prototype.) It's not clear this is desirable (see
+ * above), but it's what we've always done, so keep doing it till we
+ * implement @@toStringTag or ES6 changes.
+ */ \
+ #_type "ArrayPrototype", \
+ JSCLASS_HAS_CACHED_PROTO(JSProto_##_type##Array), \
+ JS_NULL_CLASS_OPS, \
+ &TypedArrayObjectProtoClassSpecs[Scalar::Type::_type] \
+}
+
+const Class TypedArrayObject::protoClasses[Scalar::MaxTypedArrayViewType] = {
+ IMPL_TYPED_ARRAY_PROTO_CLASS(Int8),
+ IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8),
+ IMPL_TYPED_ARRAY_PROTO_CLASS(Int16),
+ IMPL_TYPED_ARRAY_PROTO_CLASS(Uint16),
+ IMPL_TYPED_ARRAY_PROTO_CLASS(Int32),
+ IMPL_TYPED_ARRAY_PROTO_CLASS(Uint32),
+ IMPL_TYPED_ARRAY_PROTO_CLASS(Float32),
+ IMPL_TYPED_ARRAY_PROTO_CLASS(Float64),
+ IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8Clamped)
+};
+
+/* static */ bool
+TypedArrayObject::isOriginalLengthGetter(Native native)
+{
+ return native == TypedArray_lengthGetter;
+}
+
+const Class DataViewObject::protoClass = {
+ "DataViewPrototype",
+ JSCLASS_HAS_PRIVATE |
+ JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) |
+ JSCLASS_HAS_CACHED_PROTO(JSProto_DataView)
+};
+
+static const ClassOps DataViewObjectClassOps = {
+ nullptr, /* addProperty */
+ nullptr, /* delProperty */
+ nullptr, /* getProperty */
+ nullptr, /* setProperty */
+ nullptr, /* enumerate */
+ nullptr, /* resolve */
+ nullptr, /* mayResolve */
+ nullptr, /* finalize */
+ nullptr, /* call */
+ nullptr, /* hasInstance */
+ nullptr, /* construct */
+ ArrayBufferViewObject::trace
+};
+
+const Class DataViewObject::class_ = {
+ "DataView",
+ JSCLASS_HAS_PRIVATE |
+ JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) |
+ JSCLASS_HAS_CACHED_PROTO(JSProto_DataView),
+ &DataViewObjectClassOps
+};
+
+const JSFunctionSpec DataViewObject::jsfuncs[] = {
+ JS_FN("getInt8", DataViewObject::fun_getInt8, 1,0),
+ JS_FN("getUint8", DataViewObject::fun_getUint8, 1,0),
+ JS_FN("getInt16", DataViewObject::fun_getInt16, 1,0),
+ JS_FN("getUint16", DataViewObject::fun_getUint16, 1,0),
+ JS_FN("getInt32", DataViewObject::fun_getInt32, 1,0),
+ JS_FN("getUint32", DataViewObject::fun_getUint32, 1,0),
+ JS_FN("getFloat32", DataViewObject::fun_getFloat32, 1,0),
+ JS_FN("getFloat64", DataViewObject::fun_getFloat64, 1,0),
+ JS_FN("setInt8", DataViewObject::fun_setInt8, 2,0),
+ JS_FN("setUint8", DataViewObject::fun_setUint8, 2,0),
+ JS_FN("setInt16", DataViewObject::fun_setInt16, 2,0),
+ JS_FN("setUint16", DataViewObject::fun_setUint16, 2,0),
+ JS_FN("setInt32", DataViewObject::fun_setInt32, 2,0),
+ JS_FN("setUint32", DataViewObject::fun_setUint32, 2,0),
+ JS_FN("setFloat32", DataViewObject::fun_setFloat32, 2,0),
+ JS_FN("setFloat64", DataViewObject::fun_setFloat64, 2,0),
+ JS_FS_END
+};
+
+template<Value ValueGetter(DataViewObject* view)>
+bool
+DataViewObject::getterImpl(JSContext* cx, const CallArgs& args)
+{
+ args.rval().set(ValueGetter(&args.thisv().toObject().as<DataViewObject>()));
+ return true;
+}
+
+template<Value ValueGetter(DataViewObject* view)>
+bool
+DataViewObject::getter(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<is, getterImpl<ValueGetter> >(cx, args);
+}
+
+template<Value ValueGetter(DataViewObject* view)>
+bool
+DataViewObject::defineGetter(JSContext* cx, PropertyName* name, HandleNativeObject proto)
+{
+ RootedId id(cx, NameToId(name));
+ RootedAtom atom(cx, IdToFunctionName(cx, id, "get"));
+ if (!atom)
+ return false;
+ unsigned attrs = JSPROP_SHARED | JSPROP_GETTER;
+
+ Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal());
+ JSObject* getter =
+ NewNativeFunction(cx, DataViewObject::getter<ValueGetter>, 0, atom);
+ if (!getter)
+ return false;
+
+ return NativeDefineProperty(cx, proto, id, UndefinedHandleValue,
+ JS_DATA_TO_FUNC_PTR(GetterOp, getter), nullptr, attrs);
+}
+
+/* static */ bool
+DataViewObject::initClass(JSContext* cx)
+{
+ Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal());
+ if (global->isStandardClassResolved(JSProto_DataView))
+ return true;
+
+ RootedNativeObject proto(cx, global->createBlankPrototype(cx, &DataViewObject::protoClass));
+ if (!proto)
+ return false;
+
+ RootedFunction ctor(cx, global->createConstructor(cx, DataViewObject::class_constructor,
+ cx->names().DataView, 3));
+ if (!ctor)
+ return false;
+
+ if (!LinkConstructorAndPrototype(cx, ctor, proto))
+ return false;
+
+ if (!defineGetter<bufferValue>(cx, cx->names().buffer, proto))
+ return false;
+
+ if (!defineGetter<byteLengthValue>(cx, cx->names().byteLength, proto))
+ return false;
+
+ if (!defineGetter<byteOffsetValue>(cx, cx->names().byteOffset, proto))
+ return false;
+
+ if (!JS_DefineFunctions(cx, proto, DataViewObject::jsfuncs))
+ return false;
+
+ if (!DefineToStringTag(cx, proto, cx->names().DataView))
+ return false;
+
+ /*
+ * Create a helper function to implement the craziness of
+ * |new DataView(new otherWindow.ArrayBuffer())|, and install it in the
+ * global for use by the DataViewObject constructor.
+ */
+ RootedFunction fun(cx, NewNativeFunction(cx, ArrayBufferObject::createDataViewForThis,
+ 0, nullptr));
+ if (!fun)
+ return false;
+
+ if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_DataView, ctor, proto))
+ return false;
+
+ global->setCreateDataViewForThis(fun);
+
+ return true;
+}
+
+void
+DataViewObject::notifyBufferDetached(void* newData)
+{
+ setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(0));
+ setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(0));
+ setPrivate(newData);
+}
+
+JSObject*
+js::InitDataViewClass(JSContext* cx, HandleObject obj)
+{
+ if (!DataViewObject::initClass(cx))
+ return nullptr;
+ return &cx->global()->getPrototype(JSProto_DataView).toObject();
+}
+
+bool
+js::IsTypedArrayConstructor(HandleValue v, uint32_t type)
+{
+ switch (type) {
+ case Scalar::Int8:
+ return IsNativeFunction(v, Int8Array::class_constructor);
+ case Scalar::Uint8:
+ return IsNativeFunction(v, Uint8Array::class_constructor);
+ case Scalar::Int16:
+ return IsNativeFunction(v, Int16Array::class_constructor);
+ case Scalar::Uint16:
+ return IsNativeFunction(v, Uint16Array::class_constructor);
+ case Scalar::Int32:
+ return IsNativeFunction(v, Int32Array::class_constructor);
+ case Scalar::Uint32:
+ return IsNativeFunction(v, Uint32Array::class_constructor);
+ case Scalar::Float32:
+ return IsNativeFunction(v, Float32Array::class_constructor);
+ case Scalar::Float64:
+ return IsNativeFunction(v, Float64Array::class_constructor);
+ case Scalar::Uint8Clamped:
+ return IsNativeFunction(v, Uint8ClampedArray::class_constructor);
+ case Scalar::MaxTypedArrayViewType:
+ break;
+ }
+ MOZ_CRASH("unexpected typed array type");
+}
+
+template <typename CharT>
+bool
+js::StringIsTypedArrayIndex(const CharT* s, size_t length, uint64_t* indexp)
+{
+ const CharT* end = s + length;
+
+ if (s == end)
+ return false;
+
+ bool negative = false;
+ if (*s == '-') {
+ negative = true;
+ if (++s == end)
+ return false;
+ }
+
+ if (!JS7_ISDEC(*s))
+ return false;
+
+ uint64_t index = 0;
+ uint32_t digit = JS7_UNDEC(*s++);
+
+ /* Don't allow leading zeros. */
+ if (digit == 0 && s != end)
+ return false;
+
+ index = digit;
+
+ for (; s < end; s++) {
+ if (!JS7_ISDEC(*s))
+ return false;
+
+ digit = JS7_UNDEC(*s);
+
+ /* Watch for overflows. */
+ if ((UINT64_MAX - digit) / 10 < index)
+ index = UINT64_MAX;
+ else
+ index = 10 * index + digit;
+ }
+
+ if (negative)
+ *indexp = UINT64_MAX;
+ else
+ *indexp = index;
+ return true;
+}
+
+template bool
+js::StringIsTypedArrayIndex(const char16_t* s, size_t length, uint64_t* indexp);
+
+template bool
+js::StringIsTypedArrayIndex(const Latin1Char* s, size_t length, uint64_t* indexp);
+
+/* ES6 draft rev 34 (2015 Feb 20) 9.4.5.3 [[DefineOwnProperty]] step 3.c. */
+bool
+js::DefineTypedArrayElement(JSContext* cx, HandleObject obj, uint64_t index,
+ Handle<PropertyDescriptor> desc, ObjectOpResult& result)
+{
+ MOZ_ASSERT(obj->is<TypedArrayObject>());
+
+ // These are all substeps of 3.b.
+
+ // Steps i-iii are handled by the caller.
+
+ // Steps iv-v.
+ // We (wrongly) ignore out of range defines with a value.
+ uint32_t length = obj->as<TypedArrayObject>().length();
+ if (index >= length)
+ return result.succeed();
+
+ // Step vi.
+ if (desc.isAccessorDescriptor())
+ return result.fail(JSMSG_CANT_REDEFINE_PROP);
+
+ // Step vii.
+ if (desc.hasConfigurable() && desc.configurable())
+ return result.fail(JSMSG_CANT_REDEFINE_PROP);
+
+ // Step viii.
+ if (desc.hasEnumerable() && !desc.enumerable())
+ return result.fail(JSMSG_CANT_REDEFINE_PROP);
+
+ // Step ix.
+ if (desc.hasWritable() && !desc.writable())
+ return result.fail(JSMSG_CANT_REDEFINE_PROP);
+
+ // Step x.
+ if (desc.hasValue()) {
+ // The following step numbers refer to 9.4.5.9
+ // IntegerIndexedElementSet.
+
+ // Steps 1-2 are enforced by the caller.
+
+ // Step 3.
+ double numValue;
+ if (!ToNumber(cx, desc.value(), &numValue))
+ return false;
+
+ // Steps 4-5, 8-9.
+ if (obj->as<TypedArrayObject>().hasDetachedBuffer())
+ return result.fail(JSMSG_TYPED_ARRAY_DETACHED);
+
+ // Steps 10-16.
+ TypedArrayObject::setElement(obj->as<TypedArrayObject>(), index, numValue);
+ }
+
+ // Step xii.
+ return result.succeed();
+}
+
+/* JS Friend API */
+
+JS_FRIEND_API(bool)
+JS_IsTypedArrayObject(JSObject* obj)
+{
+ obj = CheckedUnwrap(obj);
+ return obj ? obj->is<TypedArrayObject>() : false;
+}
+
+JS_FRIEND_API(uint32_t)
+JS_GetTypedArrayLength(JSObject* obj)
+{
+ obj = CheckedUnwrap(obj);
+ if (!obj)
+ return 0;
+ return obj->as<TypedArrayObject>().length();
+}
+
+JS_FRIEND_API(uint32_t)
+JS_GetTypedArrayByteOffset(JSObject* obj)
+{
+ obj = CheckedUnwrap(obj);
+ if (!obj)
+ return 0;
+ return obj->as<TypedArrayObject>().byteOffset();
+}
+
+JS_FRIEND_API(uint32_t)
+JS_GetTypedArrayByteLength(JSObject* obj)
+{
+ obj = CheckedUnwrap(obj);
+ if (!obj)
+ return 0;
+ return obj->as<TypedArrayObject>().byteLength();
+}
+
+JS_FRIEND_API(bool)
+JS_GetTypedArraySharedness(JSObject* obj)
+{
+ obj = CheckedUnwrap(obj);
+ if (!obj)
+ return false;
+ return obj->as<TypedArrayObject>().isSharedMemory();
+}
+
+JS_FRIEND_API(js::Scalar::Type)
+JS_GetArrayBufferViewType(JSObject* obj)
+{
+ obj = CheckedUnwrap(obj);
+ if (!obj)
+ return Scalar::MaxTypedArrayViewType;
+
+ if (obj->is<TypedArrayObject>())
+ return obj->as<TypedArrayObject>().type();
+ if (obj->is<DataViewObject>())
+ return Scalar::MaxTypedArrayViewType;
+ MOZ_CRASH("invalid ArrayBufferView type");
+}
+
+JS_FRIEND_API(int8_t*)
+JS_GetInt8ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
+{
+ obj = CheckedUnwrap(obj);
+ if (!obj)
+ return nullptr;
+ TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
+ MOZ_ASSERT((int32_t) tarr->type() == Scalar::Int8);
+ *isSharedMemory = tarr->isSharedMemory();
+ return static_cast<int8_t*>(tarr->viewDataEither().unwrap(/*safe - caller sees isShared*/));
+}
+
+JS_FRIEND_API(uint8_t*)
+JS_GetUint8ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
+{
+ obj = CheckedUnwrap(obj);
+ if (!obj)
+ return nullptr;
+ TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
+ MOZ_ASSERT((int32_t) tarr->type() == Scalar::Uint8);
+ *isSharedMemory = tarr->isSharedMemory();
+ return static_cast<uint8_t*>(tarr->viewDataEither().unwrap(/*safe - caller sees isSharedMemory*/));
+}
+
+JS_FRIEND_API(uint8_t*)
+JS_GetUint8ClampedArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
+{
+ obj = CheckedUnwrap(obj);
+ if (!obj)
+ return nullptr;
+ TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
+ MOZ_ASSERT((int32_t) tarr->type() == Scalar::Uint8Clamped);
+ *isSharedMemory = tarr->isSharedMemory();
+ return static_cast<uint8_t*>(tarr->viewDataEither().unwrap(/*safe - caller sees isSharedMemory*/));
+}
+
+JS_FRIEND_API(int16_t*)
+JS_GetInt16ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
+{
+ obj = CheckedUnwrap(obj);
+ if (!obj)
+ return nullptr;
+ TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
+ MOZ_ASSERT((int32_t) tarr->type() == Scalar::Int16);
+ *isSharedMemory = tarr->isSharedMemory();
+ return static_cast<int16_t*>(tarr->viewDataEither().unwrap(/*safe - caller sees isSharedMemory*/));
+}
+
+JS_FRIEND_API(uint16_t*)
+JS_GetUint16ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
+{
+ obj = CheckedUnwrap(obj);
+ if (!obj)
+ return nullptr;
+ TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
+ MOZ_ASSERT((int32_t) tarr->type() == Scalar::Uint16);
+ *isSharedMemory = tarr->isSharedMemory();
+ return static_cast<uint16_t*>(tarr->viewDataEither().unwrap(/*safe - caller sees isSharedMemory*/));
+}
+
+JS_FRIEND_API(int32_t*)
+JS_GetInt32ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
+{
+ obj = CheckedUnwrap(obj);
+ if (!obj)
+ return nullptr;
+ TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
+ MOZ_ASSERT((int32_t) tarr->type() == Scalar::Int32);
+ *isSharedMemory = tarr->isSharedMemory();
+ return static_cast<int32_t*>(tarr->viewDataEither().unwrap(/*safe - caller sees isSharedMemory*/));
+}
+
+JS_FRIEND_API(uint32_t*)
+JS_GetUint32ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
+{
+ obj = CheckedUnwrap(obj);
+ if (!obj)
+ return nullptr;
+ TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
+ MOZ_ASSERT((int32_t) tarr->type() == Scalar::Uint32);
+ *isSharedMemory = tarr->isSharedMemory();
+ return static_cast<uint32_t*>(tarr->viewDataEither().unwrap(/*safe - caller sees isSharedMemory*/));
+}
+
+JS_FRIEND_API(float*)
+JS_GetFloat32ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
+{
+ obj = CheckedUnwrap(obj);
+ if (!obj)
+ return nullptr;
+ TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
+ MOZ_ASSERT((int32_t) tarr->type() == Scalar::Float32);
+ *isSharedMemory = tarr->isSharedMemory();
+ return static_cast<float*>(tarr->viewDataEither().unwrap(/*safe - caller sees isSharedMemory*/));
+}
+
+JS_FRIEND_API(double*)
+JS_GetFloat64ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
+{
+ obj = CheckedUnwrap(obj);
+ if (!obj)
+ return nullptr;
+ TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
+ MOZ_ASSERT((int32_t) tarr->type() == Scalar::Float64);
+ *isSharedMemory = tarr->isSharedMemory();
+ return static_cast<double*>(tarr->viewDataEither().unwrap(/*safe - caller sees isSharedMemory*/));
+}
+
+JS_FRIEND_API(bool)
+JS_IsDataViewObject(JSObject* obj)
+{
+ obj = CheckedUnwrap(obj);
+ return obj ? obj->is<DataViewObject>() : false;
+}
+
+JS_FRIEND_API(uint32_t)
+JS_GetDataViewByteOffset(JSObject* obj)
+{
+ obj = CheckedUnwrap(obj);
+ if (!obj)
+ return 0;
+ return obj->as<DataViewObject>().byteOffset();
+}
+
+JS_FRIEND_API(void*)
+JS_GetDataViewData(JSObject* obj, const JS::AutoCheckCannotGC&)
+{
+ obj = CheckedUnwrap(obj);
+ if (!obj)
+ return nullptr;
+ return obj->as<DataViewObject>().dataPointer();
+}
+
+JS_FRIEND_API(uint32_t)
+JS_GetDataViewByteLength(JSObject* obj)
+{
+ obj = CheckedUnwrap(obj);
+ if (!obj)
+ return 0;
+ return obj->as<DataViewObject>().byteLength();
+}
+
+JS_FRIEND_API(JSObject*)
+JS_NewDataView(JSContext* cx, HandleObject arrayBuffer, uint32_t byteOffset, int32_t byteLength)
+{
+ RootedObject constructor(cx);
+ JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(&DataViewObject::class_);
+ if (!GetBuiltinConstructor(cx, key, &constructor))
+ return nullptr;
+
+ FixedConstructArgs<3> cargs(cx);
+
+ cargs[0].setObject(*arrayBuffer);
+ cargs[1].setNumber(byteOffset);
+ cargs[2].setInt32(byteLength);
+
+ RootedValue fun(cx, ObjectValue(*constructor));
+ RootedObject obj(cx);
+ if (!Construct(cx, fun, cargs, fun, &obj))
+ return nullptr;
+ return obj;
+}