michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "vm/ArrayBufferObject.h" michael@0: michael@0: #include "mozilla/Alignment.h" michael@0: #include "mozilla/FloatingPoint.h" michael@0: #include "mozilla/PodOperations.h" michael@0: michael@0: #include michael@0: #ifndef XP_WIN michael@0: # include michael@0: #endif michael@0: michael@0: #ifdef MOZ_VALGRIND michael@0: # include michael@0: #endif michael@0: michael@0: #include "jsapi.h" michael@0: #include "jsarray.h" michael@0: #include "jscntxt.h" michael@0: #include "jscpucfg.h" michael@0: #include "jsnum.h" michael@0: #include "jsobj.h" michael@0: #include "jstypes.h" michael@0: #include "jsutil.h" michael@0: #ifdef XP_WIN michael@0: # include "jswin.h" michael@0: #endif michael@0: #include "jswrapper.h" michael@0: michael@0: #include "gc/Barrier.h" michael@0: #include "gc/Marking.h" michael@0: #include "gc/Memory.h" michael@0: #include "jit/AsmJS.h" michael@0: #include "jit/AsmJSModule.h" michael@0: #include "js/MemoryMetrics.h" michael@0: #include "vm/GlobalObject.h" michael@0: #include "vm/Interpreter.h" michael@0: #include "vm/NumericConversions.h" michael@0: #include "vm/SharedArrayObject.h" michael@0: #include "vm/WrapperObject.h" michael@0: michael@0: #include "jsatominlines.h" michael@0: #include "jsinferinlines.h" michael@0: #include "jsobjinlines.h" michael@0: michael@0: #include "vm/Shape-inl.h" michael@0: michael@0: using mozilla::DebugOnly; michael@0: michael@0: using namespace js; michael@0: using namespace js::gc; michael@0: using namespace js::types; michael@0: michael@0: /* michael@0: * Convert |v| to an array index for an array of length |length| per michael@0: * the Typed Array Specification section 7.0, |subarray|. If successful, michael@0: * the output value is in the range [0, length]. michael@0: */ michael@0: bool michael@0: js::ToClampedIndex(JSContext *cx, HandleValue v, uint32_t length, uint32_t *out) michael@0: { michael@0: int32_t result; michael@0: if (!ToInt32(cx, v, &result)) michael@0: return false; michael@0: if (result < 0) { michael@0: result += length; michael@0: if (result < 0) michael@0: result = 0; michael@0: } else if (uint32_t(result) > length) { michael@0: result = length; michael@0: } michael@0: *out = uint32_t(result); michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: * ArrayBufferObject michael@0: * michael@0: * This class holds the underlying raw buffer that the TypedArrayObject classes michael@0: * access. It can be created explicitly and passed to a TypedArrayObject, or michael@0: * can be created implicitly by constructing a TypedArrayObject with a size. michael@0: */ michael@0: michael@0: /* michael@0: * ArrayBufferObject (base) michael@0: */ michael@0: michael@0: const Class ArrayBufferObject::protoClass = { michael@0: "ArrayBufferPrototype", michael@0: JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer), michael@0: JS_PropertyStub, /* addProperty */ michael@0: JS_DeletePropertyStub, /* delProperty */ michael@0: JS_PropertyStub, /* getProperty */ michael@0: JS_StrictPropertyStub, /* setProperty */ michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: JS_ConvertStub michael@0: }; michael@0: michael@0: const Class ArrayBufferObject::class_ = { michael@0: "ArrayBuffer", michael@0: JSCLASS_IMPLEMENTS_BARRIERS | michael@0: JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | michael@0: JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer) | michael@0: JSCLASS_BACKGROUND_FINALIZE, michael@0: JS_PropertyStub, /* addProperty */ michael@0: JS_DeletePropertyStub, /* delProperty */ michael@0: JS_PropertyStub, /* getProperty */ michael@0: JS_StrictPropertyStub, /* setProperty */ michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: JS_ConvertStub, michael@0: ArrayBufferObject::finalize, michael@0: nullptr, /* call */ michael@0: nullptr, /* hasInstance */ michael@0: nullptr, /* construct */ michael@0: ArrayBufferObject::obj_trace, michael@0: JS_NULL_CLASS_SPEC, michael@0: JS_NULL_CLASS_EXT michael@0: }; michael@0: michael@0: const JSFunctionSpec ArrayBufferObject::jsfuncs[] = { michael@0: JS_FN("slice", ArrayBufferObject::fun_slice, 2, JSFUN_GENERIC_NATIVE), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: const JSFunctionSpec ArrayBufferObject::jsstaticfuncs[] = { michael@0: JS_FN("isView", ArrayBufferObject::fun_isView, 1, 0), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: bool michael@0: js::IsArrayBuffer(HandleValue v) michael@0: { michael@0: return v.isObject() && michael@0: (v.toObject().is() || michael@0: v.toObject().is()); michael@0: } michael@0: michael@0: bool michael@0: js::IsArrayBuffer(HandleObject obj) michael@0: { michael@0: return obj->is() || obj->is(); michael@0: } michael@0: michael@0: bool michael@0: js::IsArrayBuffer(JSObject *obj) michael@0: { michael@0: return obj->is() || obj->is(); michael@0: } michael@0: michael@0: ArrayBufferObject & michael@0: js::AsArrayBuffer(HandleObject obj) michael@0: { michael@0: JS_ASSERT(IsArrayBuffer(obj)); michael@0: if (obj->is()) michael@0: return obj->as(); michael@0: return obj->as(); michael@0: } michael@0: michael@0: ArrayBufferObject & michael@0: js::AsArrayBuffer(JSObject *obj) michael@0: { michael@0: JS_ASSERT(IsArrayBuffer(obj)); michael@0: if (obj->is()) michael@0: return obj->as(); michael@0: return obj->as(); michael@0: } michael@0: michael@0: MOZ_ALWAYS_INLINE bool michael@0: ArrayBufferObject::byteLengthGetterImpl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(IsArrayBuffer(args.thisv())); michael@0: args.rval().setInt32(args.thisv().toObject().as().byteLength()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ArrayBufferObject::byteLengthGetter(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: bool michael@0: ArrayBufferObject::fun_slice_impl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(IsArrayBuffer(args.thisv())); michael@0: michael@0: Rooted thisObj(cx, &args.thisv().toObject().as()); michael@0: michael@0: // these are the default values michael@0: uint32_t length = thisObj->byteLength(); michael@0: uint32_t begin = 0, end = length; michael@0: michael@0: if (args.length() > 0) { michael@0: if (!ToClampedIndex(cx, args[0], length, &begin)) michael@0: return false; michael@0: michael@0: if (args.length() > 1) { michael@0: if (!ToClampedIndex(cx, args[1], length, &end)) michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (begin > end) michael@0: begin = end; michael@0: michael@0: JSObject *nobj = createSlice(cx, thisObj, begin, end); michael@0: if (!nobj) michael@0: return false; michael@0: args.rval().setObject(*nobj); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ArrayBufferObject::fun_slice(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: /* michael@0: * ArrayBuffer.isView(obj); ES6 (Dec 2013 draft) 24.1.3.1 michael@0: */ michael@0: bool michael@0: ArrayBufferObject::fun_isView(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: args.rval().setBoolean(args.get(0).isObject() && michael@0: JS_IsArrayBufferViewObject(&args.get(0).toObject())); michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: * new ArrayBuffer(byteLength) michael@0: */ michael@0: bool michael@0: ArrayBufferObject::class_constructor(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: int32_t nbytes = 0; michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (argc > 0 && !ToInt32(cx, args[0], &nbytes)) michael@0: return false; michael@0: michael@0: if (nbytes < 0) { michael@0: /* michael@0: * We're just not going to support arrays that are bigger than what will fit michael@0: * as an integer value; if someone actually ever complains (validly), then we michael@0: * can fix. michael@0: */ michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH); michael@0: return false; michael@0: } michael@0: michael@0: JSObject *bufobj = create(cx, uint32_t(nbytes)); michael@0: if (!bufobj) michael@0: return false; michael@0: args.rval().setObject(*bufobj); michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: * Note that some callers are allowed to pass in a nullptr cx, so we allocate michael@0: * with the cx if available and fall back to the runtime. If oldptr is given, michael@0: * it's expected to be a previously-allocated contents pointer that we then michael@0: * realloc. michael@0: */ michael@0: static void * michael@0: AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void *oldptr = nullptr, size_t oldnbytes = 0) michael@0: { michael@0: void *p; michael@0: michael@0: // if oldptr is given, then we need to do a realloc michael@0: if (oldptr) { michael@0: p = maybecx ? maybecx->runtime()->reallocCanGC(oldptr, nbytes) : js_realloc(oldptr, nbytes); michael@0: michael@0: // if we grew the array, we need to set the new bytes to 0 michael@0: if (p && nbytes > oldnbytes) michael@0: memset(reinterpret_cast(p) + oldnbytes, 0, nbytes - oldnbytes); michael@0: } else { michael@0: p = maybecx ? maybecx->runtime()->callocCanGC(nbytes) : js_calloc(nbytes); michael@0: } michael@0: michael@0: if (!p && maybecx) michael@0: js_ReportOutOfMemory(maybecx); michael@0: michael@0: return p; michael@0: } michael@0: michael@0: ArrayBufferViewObject * michael@0: ArrayBufferObject::viewList() const michael@0: { michael@0: return reinterpret_cast(getSlot(VIEW_LIST_SLOT).toPrivate()); michael@0: } michael@0: michael@0: void michael@0: ArrayBufferObject::setViewListNoBarrier(ArrayBufferViewObject *viewsHead) michael@0: { michael@0: setSlot(VIEW_LIST_SLOT, PrivateValue(viewsHead)); michael@0: } michael@0: michael@0: void michael@0: ArrayBufferObject::setViewList(ArrayBufferViewObject *viewsHead) michael@0: { michael@0: if (ArrayBufferViewObject *oldHead = viewList()) michael@0: ArrayBufferViewObject::writeBarrierPre(oldHead); michael@0: setViewListNoBarrier(viewsHead); michael@0: PostBarrierTypedArrayObject(this); michael@0: } michael@0: michael@0: bool michael@0: ArrayBufferObject::canNeuter(JSContext *cx) michael@0: { michael@0: if (isSharedArrayBuffer()) michael@0: return false; michael@0: michael@0: if (isAsmJSArrayBuffer()) { michael@0: if (!ArrayBufferObject::canNeuterAsmJSArrayBuffer(cx, *this)) michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /* static */ void michael@0: ArrayBufferObject::neuter(JSContext *cx, Handle buffer, void *newData) michael@0: { michael@0: JS_ASSERT(buffer->canNeuter(cx)); michael@0: michael@0: // Neuter all views on the buffer, clear out the list of views and the michael@0: // buffer's data. michael@0: michael@0: for (ArrayBufferViewObject *view = buffer->viewList(); view; view = view->nextView()) { michael@0: view->neuter(newData); michael@0: michael@0: // Notify compiled jit code that the base pointer has moved. michael@0: MarkObjectStateChange(cx, view); michael@0: } michael@0: michael@0: if (newData != buffer->dataPointer()) michael@0: buffer->setNewOwnedData(cx->runtime()->defaultFreeOp(), newData); michael@0: michael@0: buffer->setByteLength(0); michael@0: buffer->setViewList(nullptr); michael@0: buffer->setIsNeutered(); michael@0: michael@0: // If this is happening during an incremental GC, remove the buffer from michael@0: // the list of live buffers with multiple views if necessary. michael@0: if (buffer->inLiveList()) { michael@0: ArrayBufferVector &gcLiveArrayBuffers = cx->compartment()->gcLiveArrayBuffers; michael@0: DebugOnly found = false; michael@0: for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) { michael@0: if (buffer == gcLiveArrayBuffers[i]) { michael@0: found = true; michael@0: gcLiveArrayBuffers[i] = gcLiveArrayBuffers.back(); michael@0: gcLiveArrayBuffers.popBack(); michael@0: break; michael@0: } michael@0: } michael@0: JS_ASSERT(found); michael@0: buffer->setInLiveList(false); michael@0: } michael@0: } michael@0: michael@0: void michael@0: ArrayBufferObject::setNewOwnedData(FreeOp* fop, void *newData) michael@0: { michael@0: JS_ASSERT(!isAsmJSArrayBuffer()); michael@0: JS_ASSERT(!isSharedArrayBuffer()); michael@0: michael@0: if (ownsData()) { michael@0: JS_ASSERT(newData != dataPointer()); michael@0: releaseData(fop); michael@0: } michael@0: michael@0: setDataPointer(static_cast(newData), OwnsData); michael@0: } michael@0: michael@0: void michael@0: ArrayBufferObject::changeContents(JSContext *cx, void *newData) michael@0: { michael@0: // Change buffer contents. michael@0: uint8_t* oldDataPointer = dataPointer(); michael@0: setNewOwnedData(cx->runtime()->defaultFreeOp(), newData); michael@0: michael@0: // Update all views. michael@0: ArrayBufferViewObject *viewListHead = viewList(); michael@0: for (ArrayBufferViewObject *view = viewListHead; view; view = view->nextView()) { michael@0: // Watch out for NULL data pointers in views. This means that the view michael@0: // is not fully initialized (in which case it'll be initialized later michael@0: // with the correct pointer). michael@0: uint8_t *viewDataPointer = view->dataPointer(); michael@0: if (viewDataPointer) { michael@0: JS_ASSERT(newData); michael@0: ptrdiff_t offset = viewDataPointer - oldDataPointer; michael@0: viewDataPointer = static_cast(newData) + offset; michael@0: view->setPrivate(viewDataPointer); michael@0: } michael@0: michael@0: // Notify compiled jit code that the base pointer has moved. michael@0: MarkObjectStateChange(cx, view); michael@0: } michael@0: } michael@0: michael@0: #if defined(JS_CPU_X64) michael@0: // Refer to comment above AsmJSMappedSize in AsmJS.h. michael@0: JS_STATIC_ASSERT(AsmJSAllocationGranularity == AsmJSPageSize); michael@0: #endif michael@0: michael@0: #if defined(JS_ION) && defined(JS_CPU_X64) michael@0: /* static */ bool michael@0: ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle buffer) michael@0: { michael@0: if (buffer->isAsmJSArrayBuffer()) michael@0: return true; michael@0: michael@0: // SharedArrayBuffers are already created with AsmJS support in mind. michael@0: if (buffer->isSharedArrayBuffer()) michael@0: return true; michael@0: michael@0: // Get the entire reserved region (with all pages inaccessible). michael@0: void *data; michael@0: # ifdef XP_WIN michael@0: data = VirtualAlloc(nullptr, AsmJSMappedSize, MEM_RESERVE, PAGE_NOACCESS); michael@0: if (!data) michael@0: return false; michael@0: # else michael@0: data = mmap(nullptr, AsmJSMappedSize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); michael@0: if (data == MAP_FAILED) michael@0: return false; michael@0: # endif michael@0: michael@0: // Enable access to the valid region. michael@0: JS_ASSERT(buffer->byteLength() % AsmJSAllocationGranularity == 0); michael@0: # ifdef XP_WIN michael@0: if (!VirtualAlloc(data, buffer->byteLength(), MEM_COMMIT, PAGE_READWRITE)) { michael@0: VirtualFree(data, 0, MEM_RELEASE); michael@0: return false; michael@0: } michael@0: # else michael@0: size_t validLength = buffer->byteLength(); michael@0: if (mprotect(data, validLength, PROT_READ | PROT_WRITE)) { michael@0: munmap(data, AsmJSMappedSize); michael@0: return false; michael@0: } michael@0: # if defined(MOZ_VALGRIND) && defined(VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE) michael@0: // Tell Valgrind/Memcheck to not report accesses in the inaccessible region. michael@0: VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE((unsigned char*)data + validLength, michael@0: AsmJSMappedSize-validLength); michael@0: # endif michael@0: # endif michael@0: michael@0: // Copy over the current contents of the typed array. michael@0: memcpy(data, buffer->dataPointer(), buffer->byteLength()); michael@0: michael@0: // Swap the new elements into the ArrayBufferObject. michael@0: buffer->changeContents(cx, data); michael@0: JS_ASSERT(data == buffer->dataPointer()); michael@0: michael@0: // Mark the ArrayBufferObject so (1) we don't do this again, (2) we know not michael@0: // to js_free the data in the normal way. michael@0: buffer->setIsAsmJSArrayBuffer(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: ArrayBufferObject::releaseAsmJSArray(FreeOp *fop) michael@0: { michael@0: void *data = dataPointer(); michael@0: michael@0: JS_ASSERT(uintptr_t(data) % AsmJSPageSize == 0); michael@0: # ifdef XP_WIN michael@0: VirtualFree(data, 0, MEM_RELEASE); michael@0: # else michael@0: munmap(data, AsmJSMappedSize); michael@0: # if defined(MOZ_VALGRIND) && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE) michael@0: // Tell Valgrind/Memcheck to recommence reporting accesses in the michael@0: // previously-inaccessible region. michael@0: if (AsmJSMappedSize > 0) { michael@0: VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(data, AsmJSMappedSize); michael@0: } michael@0: # endif michael@0: # endif michael@0: } michael@0: #else /* defined(JS_ION) && defined(JS_CPU_X64) */ michael@0: bool michael@0: ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle buffer) michael@0: { michael@0: if (buffer->isAsmJSArrayBuffer()) michael@0: return true; michael@0: michael@0: if (buffer->isSharedArrayBuffer()) michael@0: return true; michael@0: michael@0: if (!ensureNonInline(cx, buffer)) michael@0: return false; michael@0: michael@0: buffer->setIsAsmJSArrayBuffer(); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: ArrayBufferObject::releaseAsmJSArray(FreeOp *fop) michael@0: { michael@0: fop->free_(dataPointer()); michael@0: } michael@0: #endif michael@0: michael@0: bool michael@0: ArrayBufferObject::canNeuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer) michael@0: { michael@0: JS_ASSERT(!buffer.isSharedArrayBuffer()); michael@0: #ifdef JS_ION michael@0: AsmJSActivation *act = cx->mainThread().asmJSActivationStackFromOwnerThread(); michael@0: for (; act; act = act->prevAsmJS()) { michael@0: if (act->module().maybeHeapBufferObject() == &buffer) michael@0: break; michael@0: } michael@0: if (!act) michael@0: return true; michael@0: michael@0: return false; michael@0: #else michael@0: return true; michael@0: #endif michael@0: } michael@0: michael@0: void * michael@0: ArrayBufferObject::createMappedContents(int fd, size_t offset, size_t length) michael@0: { michael@0: return AllocateMappedContent(fd, offset, length, ARRAY_BUFFER_ALIGNMENT); michael@0: } michael@0: michael@0: void michael@0: ArrayBufferObject::releaseMappedArray() michael@0: { michael@0: if(!isMappedArrayBuffer() || isNeutered()) michael@0: return; michael@0: michael@0: DeallocateMappedContent(dataPointer(), byteLength()); michael@0: } michael@0: michael@0: void michael@0: ArrayBufferObject::addView(ArrayBufferViewObject *view) michael@0: { michael@0: // Note that pre-barriers are not needed here because either the list was michael@0: // previously empty, in which case no pointer is being overwritten, or the michael@0: // list was nonempty and will be made weak during this call (and weak michael@0: // pointers cannot violate the snapshot-at-the-beginning invariant.) michael@0: michael@0: ArrayBufferViewObject *viewsHead = viewList(); michael@0: if (viewsHead == nullptr) { michael@0: // This ArrayBufferObject will have a single view at this point, so it michael@0: // is a strong pointer (it will be marked during tracing.) michael@0: JS_ASSERT(view->nextView() == nullptr); michael@0: } else { michael@0: view->setNextView(viewsHead); michael@0: } michael@0: michael@0: setViewList(view); michael@0: } michael@0: michael@0: uint8_t * michael@0: ArrayBufferObject::dataPointer() const michael@0: { michael@0: if (isSharedArrayBuffer()) michael@0: return (uint8_t *)this->as().dataPointer(); michael@0: return static_cast(getSlot(DATA_SLOT).toPrivate()); michael@0: } michael@0: michael@0: void michael@0: ArrayBufferObject::releaseData(FreeOp *fop) michael@0: { michael@0: JS_ASSERT(ownsData()); michael@0: michael@0: if (isAsmJSArrayBuffer()) michael@0: releaseAsmJSArray(fop); michael@0: else if (isMappedArrayBuffer()) michael@0: releaseMappedArray(); michael@0: else michael@0: fop->free_(dataPointer()); michael@0: } michael@0: michael@0: void michael@0: ArrayBufferObject::setDataPointer(void *data, OwnsState ownsData) michael@0: { michael@0: MOZ_ASSERT_IF(!is() && !isMappedArrayBuffer(), data != nullptr); michael@0: setSlot(DATA_SLOT, PrivateValue(data)); michael@0: setOwnsData(ownsData); michael@0: } michael@0: michael@0: size_t michael@0: ArrayBufferObject::byteLength() const michael@0: { michael@0: return size_t(getSlot(BYTE_LENGTH_SLOT).toDouble()); michael@0: } michael@0: michael@0: void michael@0: ArrayBufferObject::setByteLength(size_t length) michael@0: { michael@0: setSlot(BYTE_LENGTH_SLOT, DoubleValue(length)); michael@0: } michael@0: michael@0: uint32_t michael@0: ArrayBufferObject::flags() const michael@0: { michael@0: return uint32_t(getSlot(FLAGS_SLOT).toInt32()); michael@0: } michael@0: michael@0: void michael@0: ArrayBufferObject::setFlags(uint32_t flags) michael@0: { michael@0: setSlot(FLAGS_SLOT, Int32Value(flags)); michael@0: } michael@0: michael@0: ArrayBufferObject * michael@0: ArrayBufferObject::create(JSContext *cx, uint32_t nbytes, void *data /* = nullptr */, michael@0: NewObjectKind newKind /* = GenericObject */, michael@0: bool mapped /* = false */) michael@0: { michael@0: JS_ASSERT_IF(mapped, data); michael@0: michael@0: // If we need to allocate data, try to use a larger object size class so michael@0: // that the array buffer's data can be allocated inline with the object. michael@0: // The extra space will be left unused by the object's fixed slots and michael@0: // available for the buffer's data, see NewObject(). michael@0: size_t reservedSlots = JSCLASS_RESERVED_SLOTS(&class_); michael@0: michael@0: size_t nslots = reservedSlots; michael@0: if (!data) { michael@0: size_t usableSlots = JSObject::MAX_FIXED_SLOTS - reservedSlots; michael@0: if (nbytes <= usableSlots * sizeof(Value)) { michael@0: int newSlots = (nbytes - 1) / sizeof(Value) + 1; michael@0: JS_ASSERT(int(nbytes) <= newSlots * int(sizeof(Value))); michael@0: nslots = reservedSlots + newSlots; michael@0: } else { michael@0: data = AllocateArrayBufferContents(cx, nbytes); michael@0: if (!data) michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: JS_ASSERT(!(class_.flags & JSCLASS_HAS_PRIVATE)); michael@0: gc::AllocKind allocKind = GetGCObjectKind(nslots); michael@0: michael@0: Rooted obj(cx, NewBuiltinClassInstance(cx, allocKind, newKind)); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: JS_ASSERT(obj->getClass() == &class_); michael@0: michael@0: JS_ASSERT(!gc::IsInsideNursery(cx->runtime(), obj)); michael@0: michael@0: if (data) { michael@0: obj->initialize(nbytes, data, OwnsData); michael@0: if (mapped) michael@0: obj->setIsMappedArrayBuffer(); michael@0: } else { michael@0: void *data = obj->fixedData(reservedSlots); michael@0: memset(data, 0, nbytes); michael@0: obj->initialize(nbytes, data, DoesntOwnData); michael@0: } michael@0: michael@0: return obj; michael@0: } michael@0: michael@0: JSObject * michael@0: ArrayBufferObject::createSlice(JSContext *cx, Handle arrayBuffer, michael@0: uint32_t begin, uint32_t end) michael@0: { michael@0: uint32_t bufLength = arrayBuffer->byteLength(); michael@0: if (begin > bufLength || end > bufLength || begin > end) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPE_ERR_BAD_ARGS); michael@0: return nullptr; michael@0: } michael@0: michael@0: uint32_t length = end - begin; michael@0: michael@0: if (!arrayBuffer->hasData()) michael@0: return create(cx, 0); michael@0: michael@0: ArrayBufferObject *slice = create(cx, length); michael@0: if (!slice) michael@0: return nullptr; michael@0: memcpy(slice->dataPointer(), arrayBuffer->dataPointer() + begin, length); michael@0: return slice; michael@0: } michael@0: michael@0: bool michael@0: ArrayBufferObject::createDataViewForThisImpl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(IsArrayBuffer(args.thisv())); michael@0: michael@0: /* michael@0: * This method is only called for |DataView(alienBuf, ...)| which calls michael@0: * this as |createDataViewForThis.call(alienBuf, ..., DataView.prototype)|, michael@0: * ergo there must be at least two arguments. michael@0: */ michael@0: JS_ASSERT(args.length() >= 2); michael@0: michael@0: Rooted proto(cx, &args[args.length() - 1].toObject()); michael@0: michael@0: Rooted buffer(cx, &args.thisv().toObject()); michael@0: michael@0: /* michael@0: * Pop off the passed-along prototype and delegate to normal DataViewObject michael@0: * construction. michael@0: */ michael@0: CallArgs frobbedArgs = CallArgsFromVp(args.length() - 1, args.base()); michael@0: return DataViewObject::construct(cx, buffer, frobbedArgs, proto); michael@0: } michael@0: michael@0: bool michael@0: ArrayBufferObject::createDataViewForThis(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: /* static */ bool michael@0: ArrayBufferObject::ensureNonInline(JSContext *cx, Handle buffer) michael@0: { michael@0: if (!buffer->ownsData()) { michael@0: void *data = AllocateArrayBufferContents(cx, buffer->byteLength()); michael@0: if (!data) michael@0: return false; michael@0: memcpy(data, buffer->dataPointer(), buffer->byteLength()); michael@0: buffer->changeContents(cx, data); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /* static */ void * michael@0: ArrayBufferObject::stealContents(JSContext *cx, Handle buffer) michael@0: { michael@0: if (!buffer->canNeuter(cx)) { michael@0: js_ReportOverRecursed(cx); michael@0: return nullptr; michael@0: } michael@0: michael@0: void *oldData = buffer->dataPointer(); michael@0: void *newData = AllocateArrayBufferContents(cx, buffer->byteLength()); michael@0: if (!newData) michael@0: return nullptr; michael@0: michael@0: if (buffer->hasStealableContents()) { michael@0: buffer->setOwnsData(DoesntOwnData); michael@0: ArrayBufferObject::neuter(cx, buffer, newData); michael@0: return oldData; michael@0: } else { michael@0: memcpy(newData, oldData, buffer->byteLength()); michael@0: ArrayBufferObject::neuter(cx, buffer, oldData); michael@0: return newData; michael@0: } michael@0: michael@0: return oldData; michael@0: } michael@0: michael@0: /* static */ void michael@0: ArrayBufferObject::addSizeOfExcludingThis(JSObject *obj, mozilla::MallocSizeOf mallocSizeOf, JS::ObjectsExtraSizes *sizes) michael@0: { michael@0: ArrayBufferObject &buffer = AsArrayBuffer(obj); michael@0: michael@0: if (!buffer.ownsData()) michael@0: return; michael@0: michael@0: if (MOZ_UNLIKELY(buffer.isAsmJSArrayBuffer())) { michael@0: #if defined (JS_CPU_X64) michael@0: // On x64, ArrayBufferObject::prepareForAsmJS switches the michael@0: // ArrayBufferObject to use mmap'd storage. michael@0: sizes->nonHeapElementsAsmJS += buffer.byteLength(); michael@0: #else michael@0: sizes->mallocHeapElementsAsmJS += mallocSizeOf(buffer.dataPointer()); michael@0: #endif michael@0: } else if (MOZ_UNLIKELY(buffer.isMappedArrayBuffer())) { michael@0: sizes->nonHeapElementsMapped += buffer.byteLength(); michael@0: } else if (buffer.dataPointer()) { michael@0: sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(buffer.dataPointer()); michael@0: } michael@0: } michael@0: michael@0: /* static */ void michael@0: ArrayBufferObject::finalize(FreeOp *fop, JSObject *obj) michael@0: { michael@0: ArrayBufferObject &buffer = obj->as(); michael@0: michael@0: if (buffer.ownsData()) michael@0: buffer.releaseData(fop); michael@0: } michael@0: michael@0: /* static */ void michael@0: ArrayBufferObject::obj_trace(JSTracer *trc, JSObject *obj) michael@0: { michael@0: if (!IS_GC_MARKING_TRACER(trc) && !trc->runtime()->isHeapMinorCollecting()) michael@0: return; michael@0: michael@0: // ArrayBufferObjects need to maintain a list of possibly-weak pointers to michael@0: // their views. The straightforward way to update the weak pointers would michael@0: // be in the views' finalizers, but giving views finalizers means they michael@0: // cannot be swept in the background. This results in a very high michael@0: // performance cost. Instead, ArrayBufferObjects with a single view hold a michael@0: // strong pointer to the view. This can entrain garbage when the single michael@0: // view becomes otherwise unreachable while the buffer is still live, but michael@0: // this is expected to be rare. ArrayBufferObjects with 0-1 views are michael@0: // expected to be by far the most common cases. ArrayBufferObjects with michael@0: // multiple views are collected into a linked list during collection, and michael@0: // then swept to prune out their dead views. michael@0: michael@0: ArrayBufferObject &buffer = AsArrayBuffer(obj); michael@0: ArrayBufferViewObject *viewsHead = buffer.viewList(); michael@0: if (!viewsHead) michael@0: return; michael@0: michael@0: buffer.setViewList(UpdateObjectIfRelocated(trc->runtime(), &viewsHead)); michael@0: michael@0: if (viewsHead->nextView() == nullptr) { michael@0: // Single view: mark it, but only if we're actually doing a GC pass michael@0: // right now. Otherwise, the tracing pass for barrier verification will michael@0: // fail if we add another view and the pointer becomes weak. michael@0: MarkObjectUnbarriered(trc, &viewsHead, "arraybuffer.singleview"); michael@0: buffer.setViewListNoBarrier(viewsHead); michael@0: } else { michael@0: // Multiple views: do not mark, but append buffer to list. michael@0: ArrayBufferVector &gcLiveArrayBuffers = buffer.compartment()->gcLiveArrayBuffers; michael@0: michael@0: // obj_trace may be called multiple times before sweep(), so avoid michael@0: // adding this buffer to the list multiple times. michael@0: if (buffer.inLiveList()) { michael@0: #ifdef DEBUG michael@0: bool found = false; michael@0: for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) michael@0: found |= gcLiveArrayBuffers[i] == &buffer; michael@0: JS_ASSERT(found); michael@0: #endif michael@0: } else if (gcLiveArrayBuffers.append(&buffer)) { michael@0: buffer.setInLiveList(true); michael@0: } else { michael@0: CrashAtUnhandlableOOM("OOM while updating live array buffers"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* static */ void michael@0: ArrayBufferObject::sweep(JSCompartment *compartment) michael@0: { michael@0: JSRuntime *rt = compartment->runtimeFromMainThread(); michael@0: ArrayBufferVector &gcLiveArrayBuffers = compartment->gcLiveArrayBuffers; michael@0: michael@0: for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) { michael@0: ArrayBufferObject *buffer = gcLiveArrayBuffers[i]; michael@0: michael@0: JS_ASSERT(buffer->inLiveList()); michael@0: buffer->setInLiveList(false); michael@0: michael@0: ArrayBufferViewObject *viewsHead = buffer->viewList(); michael@0: JS_ASSERT(viewsHead); michael@0: buffer->setViewList(UpdateObjectIfRelocated(rt, &viewsHead)); michael@0: michael@0: // Rebuild the list of views of the ArrayBufferObject, discarding dead michael@0: // views. If there is only one view, it will have already been marked. michael@0: ArrayBufferViewObject *prevLiveView = nullptr; michael@0: ArrayBufferViewObject *view = viewsHead; michael@0: while (view) { michael@0: JS_ASSERT(buffer->compartment() == view->compartment()); michael@0: ArrayBufferViewObject *nextView = view->nextView(); michael@0: if (!IsObjectAboutToBeFinalized(&view)) { michael@0: view->setNextView(prevLiveView); michael@0: prevLiveView = view; michael@0: } michael@0: view = UpdateObjectIfRelocated(rt, &nextView); michael@0: } michael@0: michael@0: buffer->setViewList(prevLiveView); michael@0: } michael@0: michael@0: gcLiveArrayBuffers.clear(); michael@0: } michael@0: michael@0: void michael@0: ArrayBufferObject::resetArrayBufferList(JSCompartment *comp) michael@0: { michael@0: ArrayBufferVector &gcLiveArrayBuffers = comp->gcLiveArrayBuffers; michael@0: michael@0: for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) { michael@0: ArrayBufferObject *buffer = gcLiveArrayBuffers[i]; michael@0: michael@0: JS_ASSERT(buffer->inLiveList()); michael@0: buffer->setInLiveList(false); michael@0: } michael@0: michael@0: gcLiveArrayBuffers.clear(); michael@0: } michael@0: michael@0: /* static */ bool michael@0: ArrayBufferObject::saveArrayBufferList(JSCompartment *comp, ArrayBufferVector &vector) michael@0: { michael@0: const ArrayBufferVector &gcLiveArrayBuffers = comp->gcLiveArrayBuffers; michael@0: michael@0: for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) { michael@0: if (!vector.append(gcLiveArrayBuffers[i])) michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /* static */ void michael@0: ArrayBufferObject::restoreArrayBufferLists(ArrayBufferVector &vector) michael@0: { michael@0: for (size_t i = 0; i < vector.length(); i++) { michael@0: ArrayBufferObject *buffer = vector[i]; michael@0: michael@0: JS_ASSERT(!buffer->inLiveList()); michael@0: buffer->setInLiveList(true); michael@0: michael@0: buffer->compartment()->gcLiveArrayBuffers.infallibleAppend(buffer); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * ArrayBufferViewObject michael@0: */ michael@0: michael@0: /* michael@0: * This method is used to trace TypedArrayObjects and DataViewObjects. We need michael@0: * a custom tracer because some of an ArrayBufferViewObject's reserved slots michael@0: * are weak references, and some need to be updated specially during moving michael@0: * GCs. michael@0: */ michael@0: /* static */ void michael@0: ArrayBufferViewObject::trace(JSTracer *trc, JSObject *obj) michael@0: { michael@0: HeapSlot &bufSlot = obj->getReservedSlotRef(BUFFER_SLOT); michael@0: MarkSlot(trc, &bufSlot, "typedarray.buffer"); michael@0: michael@0: // Update obj's data pointer if the array buffer moved. Note that during michael@0: // initialization, bufSlot may still contain |undefined|. michael@0: if (bufSlot.isObject()) { michael@0: ArrayBufferObject &buf = AsArrayBuffer(&bufSlot.toObject()); michael@0: int32_t offset = obj->getReservedSlot(BYTEOFFSET_SLOT).toInt32(); michael@0: MOZ_ASSERT(buf.dataPointer() != nullptr); michael@0: obj->initPrivate(buf.dataPointer() + offset); michael@0: } michael@0: michael@0: /* Update NEXT_VIEW_SLOT, if the view moved. */ michael@0: IsSlotMarked(&obj->getReservedSlotRef(NEXT_VIEW_SLOT)); michael@0: } michael@0: michael@0: void michael@0: ArrayBufferViewObject::neuter(void *newData) michael@0: { michael@0: MOZ_ASSERT(newData != nullptr); michael@0: if (is()) michael@0: as().neuter(newData); michael@0: else if (is()) michael@0: as().neuter(newData); michael@0: else michael@0: as().neuter(newData); michael@0: } michael@0: michael@0: /* static */ ArrayBufferObject * michael@0: ArrayBufferViewObject::bufferObject(JSContext *cx, Handle thisObject) michael@0: { michael@0: if (thisObject->is()) { michael@0: Rooted typedArray(cx, &thisObject->as()); michael@0: if (!TypedArrayObject::ensureHasBuffer(cx, typedArray)) michael@0: return nullptr; michael@0: } michael@0: return &thisObject->getFixedSlot(BUFFER_SLOT).toObject().as(); michael@0: } michael@0: michael@0: /* JS Friend API */ michael@0: michael@0: JS_FRIEND_API(bool) michael@0: JS_IsArrayBufferViewObject(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: return obj ? obj->is() : false; michael@0: } michael@0: michael@0: JS_FRIEND_API(JSObject *) michael@0: js::UnwrapArrayBufferView(JSObject *obj) michael@0: { michael@0: if (JSObject *unwrapped = CheckedUnwrap(obj)) michael@0: return unwrapped->is() ? unwrapped : nullptr; michael@0: return nullptr; michael@0: } michael@0: michael@0: JS_FRIEND_API(uint32_t) michael@0: JS_GetArrayBufferByteLength(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: return obj ? AsArrayBuffer(obj).byteLength() : 0; michael@0: } michael@0: michael@0: JS_FRIEND_API(uint8_t *) michael@0: JS_GetArrayBufferData(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: if (!obj) michael@0: return nullptr; michael@0: return AsArrayBuffer(obj).dataPointer(); michael@0: } michael@0: michael@0: JS_FRIEND_API(uint8_t *) michael@0: JS_GetStableArrayBufferData(JSContext *cx, HandleObject objArg) michael@0: { michael@0: JSObject *obj = CheckedUnwrap(objArg); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: Rooted buffer(cx, &AsArrayBuffer(obj)); michael@0: if (!ArrayBufferObject::ensureNonInline(cx, buffer)) michael@0: return nullptr; michael@0: michael@0: return buffer->dataPointer(); michael@0: } michael@0: michael@0: JS_FRIEND_API(bool) michael@0: JS_NeuterArrayBuffer(JSContext *cx, HandleObject obj, michael@0: NeuterDataDisposition changeData) michael@0: { michael@0: if (!obj->is()) { michael@0: JS_ReportError(cx, "ArrayBuffer object required"); michael@0: return false; michael@0: } michael@0: michael@0: Rooted buffer(cx, &obj->as()); michael@0: michael@0: if (!buffer->canNeuter(cx)) { michael@0: js_ReportOverRecursed(cx); michael@0: return false; michael@0: } michael@0: michael@0: void *newData; michael@0: if (changeData == ChangeData && buffer->hasStealableContents()) { michael@0: newData = AllocateArrayBufferContents(cx, buffer->byteLength()); michael@0: if (!newData) michael@0: return false; michael@0: } else { michael@0: newData = buffer->dataPointer(); michael@0: } michael@0: michael@0: ArrayBufferObject::neuter(cx, buffer, newData); michael@0: return true; michael@0: } michael@0: michael@0: JS_FRIEND_API(JSObject *) michael@0: JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes) michael@0: { michael@0: JS_ASSERT(nbytes <= INT32_MAX); michael@0: return ArrayBufferObject::create(cx, nbytes); michael@0: } michael@0: michael@0: JS_PUBLIC_API(JSObject *) michael@0: JS_NewArrayBufferWithContents(JSContext *cx, size_t nbytes, void *contents) michael@0: { michael@0: JS_ASSERT(contents); michael@0: return ArrayBufferObject::create(cx, nbytes, contents, TenuredObject, false); michael@0: } michael@0: michael@0: JS_PUBLIC_API(void *) michael@0: JS_AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes) michael@0: { michael@0: return AllocateArrayBufferContents(maybecx, nbytes); michael@0: } michael@0: michael@0: JS_PUBLIC_API(void *) michael@0: JS_ReallocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void *oldContents, uint32_t oldNbytes) michael@0: { michael@0: return AllocateArrayBufferContents(maybecx, nbytes, oldContents, oldNbytes); michael@0: } michael@0: michael@0: JS_FRIEND_API(bool) michael@0: JS_IsArrayBufferObject(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: return obj ? obj->is() : false; michael@0: } michael@0: michael@0: JS_FRIEND_API(JSObject *) michael@0: js::UnwrapArrayBuffer(JSObject *obj) michael@0: { michael@0: if (JSObject *unwrapped = CheckedUnwrap(obj)) michael@0: return unwrapped->is() ? unwrapped : nullptr; michael@0: return nullptr; michael@0: } michael@0: michael@0: JS_PUBLIC_API(void *) michael@0: JS_StealArrayBufferContents(JSContext *cx, HandleObject objArg) michael@0: { michael@0: JSObject *obj = CheckedUnwrap(objArg); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: if (!obj->is()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return nullptr; michael@0: } michael@0: michael@0: Rooted buffer(cx, &obj->as()); michael@0: return ArrayBufferObject::stealContents(cx, buffer); michael@0: } michael@0: michael@0: JS_PUBLIC_API(JSObject *) michael@0: JS_NewMappedArrayBufferWithContents(JSContext *cx, size_t nbytes, void *contents) michael@0: { michael@0: JS_ASSERT(contents); michael@0: return ArrayBufferObject::create(cx, nbytes, contents, TenuredObject, true); michael@0: } michael@0: michael@0: JS_PUBLIC_API(void *) michael@0: JS_CreateMappedArrayBufferContents(int fd, size_t offset, size_t length) michael@0: { michael@0: return ArrayBufferObject::createMappedContents(fd, offset, length); michael@0: } michael@0: michael@0: JS_PUBLIC_API(void) michael@0: JS_ReleaseMappedArrayBufferContents(void *contents, size_t length) michael@0: { michael@0: DeallocateMappedContent(contents, length); michael@0: } michael@0: michael@0: JS_FRIEND_API(bool) michael@0: JS_IsMappedArrayBufferObject(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: return obj->is() michael@0: ? obj->as().isMappedArrayBuffer() michael@0: : false; michael@0: } michael@0: michael@0: JS_FRIEND_API(void *) michael@0: JS_GetArrayBufferViewData(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: if (!obj) michael@0: return nullptr; michael@0: return obj->is() ? obj->as().dataPointer() michael@0: : obj->as().viewData(); michael@0: } michael@0: michael@0: JS_FRIEND_API(JSObject *) michael@0: JS_GetArrayBufferViewBuffer(JSContext *cx, JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: if (!obj) michael@0: return nullptr; michael@0: Rooted viewObject(cx, &obj->as()); michael@0: return ArrayBufferViewObject::bufferObject(cx, viewObject); michael@0: } michael@0: michael@0: JS_FRIEND_API(uint32_t) michael@0: JS_GetArrayBufferViewByteLength(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: if (!obj) michael@0: return 0; michael@0: return obj->is() michael@0: ? obj->as().byteLength() michael@0: : obj->as().byteLength(); michael@0: } michael@0: michael@0: JS_FRIEND_API(JSObject *) michael@0: JS_GetObjectAsArrayBufferView(JSObject *obj, uint32_t *length, uint8_t **data) michael@0: { michael@0: if (!(obj = CheckedUnwrap(obj))) michael@0: return nullptr; michael@0: if (!(obj->is())) michael@0: return nullptr; michael@0: michael@0: *length = obj->is() michael@0: ? obj->as().byteLength() michael@0: : obj->as().byteLength(); michael@0: michael@0: *data = static_cast(obj->is() michael@0: ? obj->as().dataPointer() michael@0: : obj->as().viewData()); michael@0: return obj; michael@0: } michael@0: michael@0: JS_FRIEND_API(void) michael@0: js::GetArrayBufferViewLengthAndData(JSObject *obj, uint32_t *length, uint8_t **data) michael@0: { michael@0: MOZ_ASSERT(obj->is()); michael@0: michael@0: *length = obj->is() michael@0: ? obj->as().byteLength() michael@0: : obj->as().byteLength(); michael@0: michael@0: *data = static_cast(obj->is() michael@0: ? obj->as().dataPointer() michael@0: : obj->as().viewData()); michael@0: } michael@0: michael@0: JS_FRIEND_API(JSObject *) michael@0: JS_GetObjectAsArrayBuffer(JSObject *obj, uint32_t *length, uint8_t **data) michael@0: { michael@0: if (!(obj = CheckedUnwrap(obj))) michael@0: return nullptr; michael@0: if (!IsArrayBuffer(obj)) michael@0: return nullptr; michael@0: michael@0: *length = AsArrayBuffer(obj).byteLength(); michael@0: *data = AsArrayBuffer(obj).dataPointer(); michael@0: michael@0: return obj; michael@0: } michael@0: michael@0: JS_FRIEND_API(void) michael@0: js::GetArrayBufferLengthAndData(JSObject *obj, uint32_t *length, uint8_t **data) michael@0: { michael@0: MOZ_ASSERT(IsArrayBuffer(obj)); michael@0: *length = AsArrayBuffer(obj).byteLength(); michael@0: *data = AsArrayBuffer(obj).dataPointer(); michael@0: }