diff --git a/content/base/src/nsXMLHttpRequest.cpp b/content/base/src/nsXMLHttpRequest.cpp index fcb55eddeb11..b9cc7ffb776e 100644 --- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -3817,8 +3817,7 @@ nsXMLHttpRequestXPCOMifier::GetInterface(const nsIID & aIID, void **aResult) namespace mozilla { ArrayBufferBuilder::ArrayBufferBuilder() - : mRawContents(nullptr), - mDataPtr(nullptr), + : mDataPtr(nullptr), mCapacity(0), mLength(0) { @@ -3832,20 +3831,22 @@ ArrayBufferBuilder::~ArrayBufferBuilder() void ArrayBufferBuilder::reset() { - if (mRawContents) { - JS_free(nullptr, mRawContents); + if (mDataPtr) { + JS_free(nullptr, mDataPtr); } - mRawContents = mDataPtr = nullptr; + mDataPtr = nullptr; mCapacity = mLength = 0; } bool ArrayBufferBuilder::setCapacity(uint32_t aNewCap) { - if (!JS_ReallocateArrayBufferContents(nullptr, aNewCap, &mRawContents, &mDataPtr)) { + uint8_t *newdata = (uint8_t *) JS_ReallocateArrayBufferContents(nullptr, aNewCap, mDataPtr, mCapacity); + if (!newdata) { return false; } + mDataPtr = newdata; mCapacity = aNewCap; if (mLength > aNewCap) { mLength = aNewCap; @@ -3903,12 +3904,12 @@ ArrayBufferBuilder::getArrayBuffer(JSContext* aCx) } } - JSObject* obj = JS_NewArrayBufferWithContents(aCx, mRawContents); + JSObject* obj = JS_NewArrayBufferWithContents(aCx, mLength, mDataPtr); if (!obj) { return nullptr; } - mRawContents = mDataPtr = nullptr; + mDataPtr = nullptr; mLength = mCapacity = 0; return obj; diff --git a/content/base/src/nsXMLHttpRequest.h b/content/base/src/nsXMLHttpRequest.h index eb3bb3b4d04d..e59213e84fd1 100644 --- a/content/base/src/nsXMLHttpRequest.h +++ b/content/base/src/nsXMLHttpRequest.h @@ -62,7 +62,6 @@ namespace mozilla { // or it can be reset explicitly at any point by calling reset(). class ArrayBufferBuilder { - void* mRawContents; uint8_t* mDataPtr; uint32_t mCapacity; uint32_t mLength; diff --git a/content/media/webaudio/AudioBuffer.cpp b/content/media/webaudio/AudioBuffer.cpp index f43d5d0e7da1..1faa80ace959 100644 --- a/content/media/webaudio/AudioBuffer.cpp +++ b/content/media/webaudio/AudioBuffer.cpp @@ -198,12 +198,11 @@ StealJSArrayDataIntoThreadSharedFloatArrayBufferList(JSContext* aJSContext, for (uint32_t i = 0; i < aJSArrays.Length(); ++i) { JS::Rooted arrayBuffer(aJSContext, JS_GetArrayBufferViewBuffer(aJSArrays[i])); - void* dataToFree = nullptr; - uint8_t* stolenData = nullptr; - if (arrayBuffer && - JS_StealArrayBufferContents(aJSContext, arrayBuffer, &dataToFree, - &stolenData)) { - result->SetData(i, dataToFree, reinterpret_cast(stolenData)); + uint8_t* stolenData = arrayBuffer + ? (uint8_t*) JS_StealArrayBufferContents(aJSContext, arrayBuffer) + : nullptr; + if (stolenData) { + result->SetData(i, stolenData, reinterpret_cast(stolenData)); } else { return nullptr; } diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index 9878936c92b6..ff34a5763538 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -1406,7 +1406,6 @@ TypedObject::createUnattachedWithClass(JSContext *cx, obj->initReservedSlot(JS_TYPEDOBJ_SLOT_BYTELENGTH, Int32Value(0)); obj->initReservedSlot(JS_TYPEDOBJ_SLOT_OWNER, NullValue()); obj->initReservedSlot(JS_TYPEDOBJ_SLOT_NEXT_VIEW, PrivateValue(nullptr)); - obj->initReservedSlot(JS_TYPEDOBJ_SLOT_NEXT_BUFFER, PrivateValue(UNSET_BUFFER_LINK)); obj->initReservedSlot(JS_TYPEDOBJ_SLOT_LENGTH, Int32Value(length)); obj->initReservedSlot(JS_TYPEDOBJ_SLOT_TYPE_DESCR, ObjectValue(*type)); @@ -1508,7 +1507,7 @@ TypedObject::createZeroed(JSContext *cx, { size_t totalSize = descr->as().size(); Rooted buffer(cx); - buffer = ArrayBufferObject::create(cx, totalSize, false); + buffer = ArrayBufferObject::create(cx, totalSize); if (!buffer) return nullptr; typeRepr->asSized()->initInstance(cx->runtime(), buffer->dataPointer(), 1); @@ -1529,7 +1528,7 @@ TypedObject::createZeroed(JSContext *cx, } Rooted buffer(cx); - buffer = ArrayBufferObject::create(cx, totalSize, false); + buffer = ArrayBufferObject::create(cx, totalSize); if (!buffer) return nullptr; @@ -1723,11 +1722,10 @@ TypedObject::obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, PropertyOp getter, StrictPropertyOp setter, unsigned attrs) { AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter); - - RootedObject delegate(cx, ArrayBufferDelegate(cx, obj)); - if (!delegate) + Rooted id(cx); + if (!IndexToId(cx, index, &id)) return false; - return baseops::DefineElement(cx, delegate, index, v, getter, setter, attrs); + return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs); } bool diff --git a/js/src/builtin/TypedObjectConstants.h b/js/src/builtin/TypedObjectConstants.h index 6cff93ebf3c7..1af0921fcfa7 100644 --- a/js/src/builtin/TypedObjectConstants.h +++ b/js/src/builtin/TypedObjectConstants.h @@ -109,15 +109,14 @@ #define JS_TYPEDOBJ_SLOT_BYTELENGTH 1 #define JS_TYPEDOBJ_SLOT_OWNER 2 #define JS_TYPEDOBJ_SLOT_NEXT_VIEW 3 -#define JS_TYPEDOBJ_SLOT_NEXT_BUFFER 4 -#define JS_DATAVIEW_SLOTS 5 // Number of slots for data views +#define JS_DATAVIEW_SLOTS 4 // Number of slots for data views -#define JS_TYPEDOBJ_SLOT_LENGTH 5 // Length of array (see (*) below) -#define JS_TYPEDOBJ_SLOT_TYPE_DESCR 6 // For typed objects, type descr +#define JS_TYPEDOBJ_SLOT_LENGTH 4 // Length of array (see (*) below) +#define JS_TYPEDOBJ_SLOT_TYPE_DESCR 5 // For typed objects, type descr #define JS_TYPEDOBJ_SLOT_DATA 7 // private slot, based on alloc kind -#define JS_TYPEDOBJ_SLOTS 7 // Number of slots for typed objs +#define JS_TYPEDOBJ_SLOTS 6 // Number of slots for typed objs // (*) The JS_TYPEDOBJ_SLOT_LENGTH slot stores the length for typed objects of // sized and unsized array type. The slot contains 0 for non-arrays. diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index d72f2ae19f67..da955a0a7f6f 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -278,24 +278,6 @@ js::Nursery::notifyInitialSlots(Cell *cell, HeapSlot *slots) } } -void -js::Nursery::notifyNewElements(gc::Cell *cell, ObjectElements *elements) -{ - JS_ASSERT(!isInside(elements)); - notifyInitialSlots(cell, reinterpret_cast(elements)); -} - -void -js::Nursery::notifyRemovedElements(gc::Cell *cell, ObjectElements *oldElements) -{ - JS_ASSERT(cell); - JS_ASSERT(oldElements); - JS_ASSERT(!isInside(oldElements)); - - if (isInside(cell)) - hugeSlots.remove(reinterpret_cast(oldElements)); -} - namespace js { namespace gc { @@ -620,25 +602,6 @@ js::Nursery::moveElementsToTenured(JSObject *dst, JSObject *src, AllocKind dstKi return 0; } - /* ArrayBuffer stores byte-length, not Value count. */ - if (src->is()) { - size_t nbytes; - if (src->hasDynamicElements()) { - nbytes = sizeof(ObjectElements) + srcHeader->initializedLength; - dstHeader = static_cast(zone->malloc_(nbytes)); - if (!dstHeader) - CrashAtUnhandlableOOM("Failed to allocate array buffer elements while tenuring."); - } else { - dst->setFixedElements(); - nbytes = GetGCKindSlots(dst->tenuredGetAllocKind()) * sizeof(HeapSlot); - dstHeader = dst->getElementsHeader(); - } - js_memcpy(dstHeader, srcHeader, nbytes); - setElementsForwardingPointer(srcHeader, dstHeader, nbytes / sizeof(HeapSlot)); - dst->elements = dstHeader->elements(); - return src->hasDynamicElements() ? nbytes : 0; - } - size_t nslots = ObjectElements::VALUES_PER_HEADER + srcHeader->capacity; /* Unlike other objects, Arrays can have fixed elements. */ @@ -784,7 +747,7 @@ js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason, TypeObjectList // Update the array buffer object's view lists. TIME_START(sweepArrayBufferViewList); for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { - if (c->gcLiveArrayBuffers) + if (!c->gcLiveArrayBuffers.empty()) ArrayBufferObject::sweep(c); } TIME_END(sweepArrayBufferViewList); diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h index 4d76ac4caf64..7b248cb01b67 100644 --- a/js/src/gc/Nursery.h +++ b/js/src/gc/Nursery.h @@ -106,12 +106,6 @@ class Nursery /* Add a slots to our tracking list if it is out-of-line. */ void notifyInitialSlots(gc::Cell *cell, HeapSlot *slots); - /* Add elements to our tracking list if it is out-of-line. */ - void notifyNewElements(gc::Cell *cell, ObjectElements *elements); - - /* Remove elements to our tracking list if it is out-of-line. */ - void notifyRemovedElements(gc::Cell *cell, ObjectElements *oldElements); - typedef Vector TypeObjectList; /* diff --git a/js/src/jsapi-tests/testArrayBuffer.cpp b/js/src/jsapi-tests/testArrayBuffer.cpp index 287d9999f018..b0adee9acefe 100644 --- a/js/src/jsapi-tests/testArrayBuffer.cpp +++ b/js/src/jsapi-tests/testArrayBuffer.cpp @@ -51,8 +51,7 @@ BEGIN_TEST(testArrayBuffer_bug720949_steal) CHECK_SAME(v, INT_TO_JSVAL(MAGIC_VALUE_2)); // Steal the contents - void *contents; - CHECK(JS_StealArrayBufferContents(cx, obj, &contents, &data)); + void *contents = JS_StealArrayBufferContents(cx, obj); CHECK(contents != nullptr); CHECK(data != nullptr); @@ -72,7 +71,7 @@ BEGIN_TEST(testArrayBuffer_bug720949_steal) CHECK_SAME(v, JSVAL_VOID); // Transfer to a new ArrayBuffer - JS::RootedObject dst(cx, JS_NewArrayBufferWithContents(cx, contents)); + JS::RootedObject dst(cx, JS_NewArrayBufferWithContents(cx, size, contents)); CHECK(JS_IsArrayBufferObject(dst)); data = JS_GetStableArrayBufferData(cx, obj); @@ -105,11 +104,8 @@ BEGIN_TEST(testArrayBuffer_bug720949_viewList) { buffer = JS_NewArrayBuffer(cx, 2000); JS::RootedObject view(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, 0, -1)); - void *contents; - uint8_t *data; - CHECK(JS_StealArrayBufferContents(cx, buffer, &contents, &data)); + void *contents = JS_StealArrayBufferContents(cx, buffer); CHECK(contents != nullptr); - CHECK(data != nullptr); JS_free(nullptr, contents); GC(cx); CHECK(isNeutered(view)); @@ -133,11 +129,8 @@ BEGIN_TEST(testArrayBuffer_bug720949_viewList) view2 = JS_NewUint8ArrayWithBuffer(cx, buffer, 1, 200); // Neuter - void *contents; - uint8_t *data; - CHECK(JS_StealArrayBufferContents(cx, buffer, &contents, &data)); + void *contents = JS_StealArrayBufferContents(cx, buffer); CHECK(contents != nullptr); - CHECK(data != nullptr); JS_free(nullptr, contents); CHECK(isNeutered(view1)); diff --git a/js/src/jsapi.h b/js/src/jsapi.h index c99d2cfa2a1b..2201d76f7282 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -3120,47 +3120,38 @@ JS_PUBLIC_API(void) JS_SetAllNonReservedSlotsToUndefined(JSContext *cx, JSObject *objArg); /* - * Create a new array buffer with the given contents, which must have been - * returned by JS_AllocateArrayBufferContents or JS_StealArrayBufferContents. - * The new array buffer takes ownership. After calling this function, do not - * free |contents| or use |contents| from another thread. + * Create a new array buffer with the given contents. The new array buffer + * takes ownership: after calling this function, do not free |contents| or use + * |contents| from another thread. */ extern JS_PUBLIC_API(JSObject *) -JS_NewArrayBufferWithContents(JSContext *cx, void *contents); +JS_NewArrayBufferWithContents(JSContext *cx, size_t nbytes, void *contents); /* * Steal the contents of the given array buffer. The array buffer has its * length set to 0 and its contents array cleared. The caller takes ownership - * of |*contents| and must free it or transfer ownership via + * of the return value and must free it or transfer ownership via * JS_NewArrayBufferWithContents when done using it. - * To free |*contents|, call free(). - * A pointer to the buffer's data is returned in |*data|. This pointer can - * be used until |*contents| is freed or has its ownership transferred. */ -extern JS_PUBLIC_API(bool) -JS_StealArrayBufferContents(JSContext *cx, JS::HandleObject obj, void **contents, uint8_t **data); +extern JS_PUBLIC_API(void *) +JS_StealArrayBufferContents(JSContext *cx, JS::HandleObject obj); /* * Allocate memory that may be eventually passed to * JS_NewArrayBufferWithContents. |maybecx| is optional; if a non-nullptr cx is * given, it will be used for memory accounting and OOM reporting. |nbytes| is - * the number of payload bytes required. The pointer to pass to - * JS_NewArrayBufferWithContents is returned in |contents|. The pointer to the - * |nbytes| of usable memory is returned in |data|. (*|contents| will contain a - * header before |data|.) The only legal operations on *|contents| are to pass - * it to either JS_NewArrayBufferWithContents or - * JS_ReallocateArrayBufferContents, or free it with js_free or JS_free. + * the number of payload bytes required. */ -extern JS_PUBLIC_API(bool) -JS_AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void **contents, uint8_t **data); +extern JS_PUBLIC_API(void *) +JS_AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes); /* * Reallocate memory allocated by JS_AllocateArrayBufferContents, growing or - * shrinking it as appropriate. The new data pointer will be returned in data. - * If *contents is nullptr, behaves like JS_AllocateArrayBufferContents. + * shrinking it as appropriate. If oldContents is null then this behaves like + * JS_AllocateArrayBufferContents. */ -extern JS_PUBLIC_API(bool) -JS_ReallocateArrayBufferContents(JSContext *cx, uint32_t nbytes, void **contents, uint8_t **data); +extern JS_PUBLIC_API(void *) +JS_ReallocateArrayBufferContents(JSContext *cx, uint32_t nbytes, void *oldContents, uint32_t oldNbytes); extern JS_PUBLIC_API(JSIdArray *) JS_Enumerate(JSContext *cx, JSObject *obj); diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 7101cda7bc14..90915e13aa5d 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -58,7 +58,6 @@ JSCompartment::JSCompartment(Zone *zone, const JS::CompartmentOptions &options = propertyTree(thisForCtor()), selfHostingScriptSource(nullptr), gcIncomingGrayPointers(nullptr), - gcLiveArrayBuffers(nullptr), gcWeakMapList(nullptr), debugModeBits(runtime_->debugMode ? DebugFromC : 0), rngState(0), diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 91865a7b0946..256ff8156e5e 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -280,8 +280,8 @@ struct JSCompartment */ JSObject *gcIncomingGrayPointers; - /* Linked list of live array buffers with >1 view. */ - js::ArrayBufferObject *gcLiveArrayBuffers; + /* During GC, list of live array buffers with >1 view accumulated during tracing. */ + js::ArrayBufferVector gcLiveArrayBuffers; /* Linked list of live weakmaps in this compartment. */ js::WeakMapBase *gcWeakMapList; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index bb52429930ff..fe5b1bc8b94b 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -3001,7 +3001,7 @@ BeginMarkPhase(JSRuntime *rt) } for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) { - JS_ASSERT(!c->gcLiveArrayBuffers); + JS_ASSERT(c->gcLiveArrayBuffers.empty()); c->marked = false; if (ShouldPreserveJITCode(c, currentTime)) c->zone()->setPreservingCode(true); @@ -4253,7 +4253,7 @@ EndSweepPhase(JSRuntime *rt, JSGCInvocationKind gckind, bool lastGC) #ifdef DEBUG for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { JS_ASSERT(!c->gcIncomingGrayPointers); - JS_ASSERT(!c->gcLiveArrayBuffers); + JS_ASSERT(c->gcLiveArrayBuffers.empty()); for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) { if (e.front().key().kind != CrossCompartmentKey::StringWrapper) @@ -4468,7 +4468,7 @@ ResetIncrementalGC(JSRuntime *rt, const char *reason) #ifdef DEBUG for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) - JS_ASSERT(!c->gcLiveArrayBuffers); + JS_ASSERT(c->gcLiveArrayBuffers.empty()); for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { JS_ASSERT(!zone->needsBarrier()); diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 73c7ea19d31c..bdb5b5d277d7 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -5898,17 +5898,7 @@ JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::Objects if (hasDynamicElements()) { js::ObjectElements *elements = getElementsHeader(); - if (MOZ_UNLIKELY(elements->isAsmJSArrayBuffer())) { -#if defined (JS_CPU_X64) - // On x64, ArrayBufferObject::prepareForAsmJS switches the - // ArrayBufferObject to use mmap'd storage. - sizes->nonHeapElementsAsmJS += as().byteLength(); -#else - sizes->mallocHeapElementsAsmJS += mallocSizeOf(elements); -#endif - } else { - sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(elements); - } + sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(elements); } // Other things may be measured in the future if DMD indicates it is worthwhile. @@ -5935,6 +5925,8 @@ JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::Objects sizes->mallocHeapRegExpStatics += as().sizeOfData(mallocSizeOf); } else if (is()) { sizes->mallocHeapPropertyIteratorData += as().sizeOfMisc(mallocSizeOf); + } else if (is() || is()) { + ArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, sizes); #ifdef JS_ION } else if (is()) { as().addSizeOfMisc(mallocSizeOf, &sizes->nonHeapCodeAsmJS, diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index d5976da40026..78c0066262dd 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -527,7 +527,7 @@ JSObject::create(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::Initi obj->privateRef(shape->numFixedSlots()) = nullptr; size_t span = shape->slotSpan(); - if (span && clasp != &js::ArrayBufferObject::class_) + if (span) obj->initializeSlotRange(0, span); return obj; @@ -573,12 +573,10 @@ JSObject::finish(js::FreeOp *fop) { if (hasDynamicSlots()) fop->free_(slots); + if (hasDynamicElements()) { js::ObjectElements *elements = getElementsHeader(); - if (MOZ_UNLIKELY(elements->isAsmJSArrayBuffer())) - js::ArrayBufferObject::releaseAsmJSArrayBuffer(fop, this); - else - fop->free_(elements); + fop->free_(elements); } } @@ -941,6 +939,17 @@ NewBuiltinClassInstance(ExclusiveContext *cx, NewObjectKind newKind = GenericObj return &obj->as(); } +template +inline T * +NewBuiltinClassInstance(ExclusiveContext *cx, gc::AllocKind allocKind, NewObjectKind newKind = GenericObject) +{ + JSObject *obj = NewBuiltinClassInstance(cx, &T::class_, allocKind, newKind); + if (!obj) + return nullptr; + + return &obj->as(); +} + // Used to optimize calls to (new Object()) bool NewObjectScriptedCall(JSContext *cx, MutableHandleObject obj); diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 8a97affdce74..913e066c2c6f 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -1025,7 +1025,6 @@ CacheEntry_setBytecode(JSContext *cx, HandleObject cache, uint8_t *buffer, uint3 if (!arrayBuffer || !ArrayBufferObject::ensureNonInline(cx, arrayBuffer)) return false; - memcpy(arrayBuffer->dataPointer(), buffer, length); SetReservedSlot(cache, CacheEntry_BYTECODE, OBJECT_TO_JSVAL(arrayBuffer)); return true; } @@ -1284,6 +1283,8 @@ Evaluate(JSContext *cx, unsigned argc, jsval *vp) if (!CacheEntry_setBytecode(cx, cacheEntry, saveBuffer, saveLength)) return false; + + saveBuffer.forget(); } return JS_WrapValue(cx, args.rval()); @@ -6220,6 +6221,11 @@ main(int argc, char **argv, char **envp) PR_JoinThread(workerThreads[i]); #endif +#ifdef JSGC_GENERATIONAL + if (!noggc.empty()) + noggc.destroy(); +#endif + JS_DestroyRuntime(rt); JS_ShutDown(); return result; diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp index aa53c73b86d2..dcdfce36fdfa 100644 --- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -32,6 +32,7 @@ #include "gc/Marking.h" #include "jit/AsmJS.h" #include "jit/AsmJSModule.h" +#include "js/MemoryMetrics.h" #include "vm/GlobalObject.h" #include "vm/Interpreter.h" #include "vm/NumericConversions.h" @@ -44,21 +45,12 @@ #include "vm/Shape-inl.h" +using mozilla::DebugOnly; + using namespace js; using namespace js::gc; using namespace js::types; -/* - * Allocate array buffers with the maximum number of fixed slots marked as - * reserved, so that the fixed slots may be used for the buffer's contents. - * The last fixed slot is kept for the object's private data. - */ -static const uint8_t ARRAYBUFFER_RESERVED_SLOTS = JSObject::MAX_FIXED_SLOTS - 1; - -// Sentinel value used to initialize ArrayBufferViewObjects' NEXT_BUFFER_SLOTs -// to show that they have not yet been added to any ArrayBufferObject list. -js::ArrayBufferObject * const js::UNSET_BUFFER_LINK = reinterpret_cast(0x2); - /* * Convert |v| to an array index for an array of length |length| per * the Typed Array Specification section 7.0, |subarray|. If successful, @@ -95,8 +87,6 @@ js::ToClampedIndex(JSContext *cx, HandleValue v, uint32_t length, uint32_t *out) const Class ArrayBufferObject::protoClass = { "ArrayBufferPrototype", - JSCLASS_HAS_PRIVATE | - JSCLASS_HAS_RESERVED_SLOTS(ARRAYBUFFER_RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer), JS_PropertyStub, /* addProperty */ JS_DeletePropertyStub, /* delProperty */ @@ -109,11 +99,10 @@ const Class ArrayBufferObject::protoClass = { const Class ArrayBufferObject::class_ = { "ArrayBuffer", - JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | - Class::NON_NATIVE | - JSCLASS_HAS_RESERVED_SLOTS(ARRAYBUFFER_RESERVED_SLOTS) | - JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer), + JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | + JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer) | + JSCLASS_BACKGROUND_FINALIZE, JS_PropertyStub, /* addProperty */ JS_DeletePropertyStub, /* delProperty */ JS_PropertyStub, /* getProperty */ @@ -121,35 +110,13 @@ const Class ArrayBufferObject::class_ = { JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, - nullptr, /* finalize */ + ArrayBufferObject::finalize, nullptr, /* call */ nullptr, /* hasInstance */ nullptr, /* construct */ ArrayBufferObject::obj_trace, JS_NULL_CLASS_SPEC, - JS_NULL_CLASS_EXT, - { - ArrayBufferObject::obj_lookupGeneric, - ArrayBufferObject::obj_lookupProperty, - ArrayBufferObject::obj_lookupElement, - ArrayBufferObject::obj_defineGeneric, - ArrayBufferObject::obj_defineProperty, - ArrayBufferObject::obj_defineElement, - ArrayBufferObject::obj_getGeneric, - ArrayBufferObject::obj_getProperty, - ArrayBufferObject::obj_getElement, - ArrayBufferObject::obj_setGeneric, - ArrayBufferObject::obj_setProperty, - ArrayBufferObject::obj_setElement, - ArrayBufferObject::obj_getGenericAttributes, - ArrayBufferObject::obj_setGenericAttributes, - ArrayBufferObject::obj_deleteProperty, - ArrayBufferObject::obj_deleteElement, - nullptr, nullptr, /* watch/unwatch */ - nullptr, /* slice */ - ArrayBufferObject::obj_enumerate, - nullptr, /* thisObject */ - } + JS_NULL_CLASS_EXT }; const JSFunctionSpec ArrayBufferObject::jsfuncs[] = { @@ -296,143 +263,114 @@ ArrayBufferObject::class_constructor(JSContext *cx, unsigned argc, Value *vp) /* * Note that some callers are allowed to pass in a nullptr cx, so we allocate * with the cx if available and fall back to the runtime. If oldptr is given, - * it's expected to be a previously-allocated ObjectElements* pointer that we - * then realloc. + * it's expected to be a previously-allocated contents pointer that we then + * realloc. */ -static ObjectElements * -AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void *oldptr = nullptr) +static void * +AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void *oldptr = nullptr, size_t oldnbytes = 0) { - uint32_t size = nbytes + sizeof(ObjectElements); - JS_ASSERT(size > nbytes); // be wary of rollover - ObjectElements *newheader; + void *p; // if oldptr is given, then we need to do a realloc if (oldptr) { - ObjectElements *oldheader = static_cast(oldptr); - uint32_t oldnbytes = ArrayBufferObject::headerInitializedLength(oldheader); - - void *p = maybecx ? maybecx->runtime()->reallocCanGC(oldptr, size) : js_realloc(oldptr, size); - newheader = static_cast(p); + p = maybecx ? maybecx->runtime()->reallocCanGC(oldptr, nbytes) : js_realloc(oldptr, nbytes); // if we grew the array, we need to set the new bytes to 0 - if (newheader && nbytes > oldnbytes) - memset(reinterpret_cast(newheader->elements()) + oldnbytes, 0, nbytes - oldnbytes); + if (p && nbytes > oldnbytes) + memset(reinterpret_cast(p) + oldnbytes, 0, nbytes - oldnbytes); } else { - void *p = maybecx ? maybecx->runtime()->callocCanGC(size) : js_calloc(size); - newheader = static_cast(p); - } - if (!newheader) { - if (maybecx) - js_ReportOutOfMemory(maybecx); - return nullptr; + p = maybecx ? maybecx->runtime()->callocCanGC(nbytes) : js_calloc(nbytes); } - ArrayBufferObject::updateElementsHeader(newheader, nbytes); + if (!p && maybecx) + js_ReportOutOfMemory(maybecx); - return newheader; + return p; } -// The list of views must be stored somewhere in the ArrayBufferObject, but -// the slots are already being used for the element storage and the private -// field is used for a delegate object. The ObjectElements header has space -// for it, but I don't want to mess around with adding unions to it with -// JS_USE_NEW_OBJECT_REPRESENTATION pending, since it will solve this much -// more cleanly. -struct OldObjectRepresentationHack { - uint32_t flags; - uint32_t initializedLength; - EncapsulatedPtr views; -}; - -static ArrayBufferViewObject * -GetViewList(ArrayBufferObject *obj) +ArrayBufferViewObject * +ArrayBufferObject::viewList() const { - return reinterpret_cast(obj->getElementsHeader())->views; + return reinterpret_cast(getSlot(VIEW_LIST_SLOT).toPrivate()); } -static void -SetViewList(ArrayBufferObject *obj, ArrayBufferViewObject *viewsHead) +void +ArrayBufferObject::setViewListNoBarrier(ArrayBufferViewObject *viewsHead) { - reinterpret_cast(obj->getElementsHeader())->views = viewsHead; - PostBarrierTypedArrayObject(obj); + setSlot(VIEW_LIST_SLOT, PrivateValue(viewsHead)); } -static void -InitViewList(ArrayBufferObject *obj, ArrayBufferViewObject *viewsHead) +void +ArrayBufferObject::setViewList(ArrayBufferViewObject *viewsHead) { - reinterpret_cast(obj->getElementsHeader())->views.init(viewsHead); - PostBarrierTypedArrayObject(obj); + if (ArrayBufferViewObject *oldHead = viewList()) + ArrayBufferViewObject::writeBarrierPre(oldHead); + setViewListNoBarrier(viewsHead); + PostBarrierTypedArrayObject(this); } -static EncapsulatedPtr & -GetViewListRef(ArrayBufferObject *obj) +bool +ArrayBufferObject::canNeuter(JSContext *cx) { - JS_ASSERT(obj->runtimeFromMainThread()->isHeapBusy()); - return reinterpret_cast(obj->getElementsHeader())->views; + if (isSharedArrayBuffer()) + return false; + + if (isAsmJSArrayBuffer()) { + if (!ArrayBufferObject::canNeuterAsmJSArrayBuffer(cx, *this)) + return false; + } + + return true; } -/* static */ bool -ArrayBufferObject::neuterViews(JSContext *cx, Handle buffer) +/* static */ void +ArrayBufferObject::neuter(JSContext *cx, Handle buffer, void *newData) { - ArrayBufferViewObject *view; - size_t numViews = 0; - for (view = GetViewList(buffer); view; view = view->nextView()) { - numViews++; + JS_ASSERT(buffer->canNeuter(cx)); + + // Neuter all views on the buffer, clear out the list of views and the + // buffer's data. + + for (ArrayBufferViewObject *view = buffer->viewList(); view; view = view->nextView()) { view->neuter(cx); // Notify compiled jit code that the base pointer has moved. MarkObjectStateChange(cx, view); } - // neuterAsmJSArrayBuffer adjusts state specific to the ArrayBuffer data - // itself, but it only affects the behavior of views - if (buffer->isAsmJSArrayBuffer()) { - if (!ArrayBufferObject::neuterAsmJSArrayBuffer(cx, *buffer)) - return false; - } + if (newData != buffer->dataPointer()) + buffer->changeContents(cx, newData); - // Remove buffer from the list of buffers with > 1 view. - if (numViews > 1 && GetViewList(buffer)->bufferLink() != UNSET_BUFFER_LINK) { - ArrayBufferObject *prev = buffer->compartment()->gcLiveArrayBuffers; - if (prev == buffer) { - buffer->compartment()->gcLiveArrayBuffers = GetViewList(prev)->bufferLink(); - } else { - for (ArrayBufferObject *b = GetViewList(prev)->bufferLink(); - b; - b = GetViewList(b)->bufferLink()) - { - if (b == buffer) { - GetViewList(prev)->setBufferLink(GetViewList(b)->bufferLink()); - break; - } - prev = b; + buffer->setByteLength(0); + buffer->setViewList(nullptr); + buffer->setIsNeutered(); + + // If this is happening during an incremental GC, remove the buffer from + // the list of live buffers with multiple views if necessary. + if (buffer->inLiveList()) { + ArrayBufferVector &gcLiveArrayBuffers = cx->compartment()->gcLiveArrayBuffers; + DebugOnly found = false; + for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) { + if (buffer == gcLiveArrayBuffers[i]) { + found = true; + gcLiveArrayBuffers[i] = gcLiveArrayBuffers.back(); + gcLiveArrayBuffers.popBack(); + break; } } + JS_ASSERT(found); + buffer->setInLiveList(false); } - - return true; -} - -uint8_t * -ArrayBufferObject::dataPointer() const { - if (isSharedArrayBuffer()) - return (uint8_t *)this->as().dataPointer(); - return (uint8_t *)elements; } void -ArrayBufferObject::changeContents(JSContext *cx, ObjectElements *newHeader) +ArrayBufferObject::changeContents(JSContext *cx, void *newData) { JS_ASSERT(!isAsmJSArrayBuffer()); JS_ASSERT(!isSharedArrayBuffer()); - // Grab out data before invalidating it. - uint32_t byteLengthCopy = byteLength(); - uintptr_t oldDataPointer = uintptr_t(dataPointer()); - ArrayBufferViewObject *viewListHead = GetViewList(this); - // Update all views. - uintptr_t newDataPointer = uintptr_t(newHeader->elements()); + ArrayBufferViewObject *viewListHead = viewList(); for (ArrayBufferViewObject *view = viewListHead; view; view = view->nextView()) { // Watch out for NULL data pointers in views. This either // means that the view is not fully initialized (in which case @@ -440,9 +378,10 @@ ArrayBufferObject::changeContents(JSContext *cx, ObjectElements *newHeader) // that the view has been neutered. In that case, the buffer // is "en route" to being neutered but the isNeuteredBuffer() // flag may not yet be set. - uint8_t *viewDataPointer = static_cast(view->getPrivate()); + uint8_t *viewDataPointer = view->dataPointer(); if (viewDataPointer) { - viewDataPointer += newDataPointer - oldDataPointer; + JS_ASSERT(newData); + viewDataPointer += static_cast(newData) - dataPointer(); view->setPrivate(viewDataPointer); } @@ -450,80 +389,19 @@ ArrayBufferObject::changeContents(JSContext *cx, ObjectElements *newHeader) MarkObjectStateChange(cx, view); } - // The list of views in the old header is reachable if the contents are - // being transferred, so null it out - SetViewList(this, nullptr); + if (ownsData()) + releaseData(cx->runtime()->defaultFreeOp()); -#ifdef JSGC_GENERATIONAL - ObjectElements *oldHeader = ObjectElements::fromElements(elements); - JS_ASSERT(oldHeader != newHeader); - JSRuntime *rt = runtimeFromMainThread(); - if (hasDynamicElements()) - rt->gcNursery.notifyRemovedElements(this, oldHeader); -#endif - - elements = newHeader->elements(); - -#ifdef JSGC_GENERATIONAL - if (hasDynamicElements()) - rt->gcNursery.notifyNewElements(this, newHeader); -#endif - - initElementsHeader(newHeader, byteLengthCopy); - InitViewList(this, viewListHead); -} - -void -ArrayBufferObject::neuter(ObjectElements *newHeader, JSContext *cx) -{ - MOZ_ASSERT(!isSharedArrayBuffer()); - - if (hasStealableContents()) { - MOZ_ASSERT(newHeader); - - ObjectElements *oldHeader = getElementsHeader(); - MOZ_ASSERT(newHeader != oldHeader); - - changeContents(cx, newHeader); - - FreeOp fop(cx->runtime(), false); - fop.free_(oldHeader); - } else { - elements = newHeader->elements(); - } - - uint32_t byteLen = 0; - updateElementsHeader(newHeader, byteLen); - - newHeader->setIsNeuteredBuffer(); -} - -/* static */ bool -ArrayBufferObject::ensureNonInline(JSContext *cx, Handle buffer) -{ - JS_ASSERT(!buffer->isSharedArrayBuffer()); - if (buffer->hasDynamicElements()) - return true; - - ObjectElements *newHeader = AllocateArrayBufferContents(cx, buffer->byteLength()); - if (!newHeader) - return false; - - void *newHeaderDataPointer = reinterpret_cast(newHeader->elements()); - memcpy(newHeaderDataPointer, buffer->dataPointer(), buffer->byteLength()); - - buffer->changeContents(cx, newHeader); - return true; + setDataPointer(static_cast(newData), OwnsData); } #if defined(JS_CPU_X64) // Refer to comment above AsmJSMappedSize in AsmJS.h. -JS_STATIC_ASSERT(sizeof(ObjectElements) < AsmJSPageSize); JS_STATIC_ASSERT(AsmJSAllocationGranularity == AsmJSPageSize); #endif #if defined(JS_ION) && defined(JS_CPU_X64) -bool +/* static */ bool ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle buffer) { if (buffer->isAsmJSArrayBuffer()) @@ -534,61 +412,55 @@ ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle buf return true; // Get the entire reserved region (with all pages inaccessible). - void *p; + void *data; # ifdef XP_WIN - p = VirtualAlloc(nullptr, AsmJSMappedSize, MEM_RESERVE, PAGE_NOACCESS); - if (!p) + data = VirtualAlloc(nullptr, AsmJSMappedSize, MEM_RESERVE, PAGE_NOACCESS); + if (!data) return false; # else - p = mmap(nullptr, AsmJSMappedSize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); - if (p == MAP_FAILED) + data = mmap(nullptr, AsmJSMappedSize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (data == MAP_FAILED) return false; # endif // Enable access to the valid region. JS_ASSERT(buffer->byteLength() % AsmJSAllocationGranularity == 0); # ifdef XP_WIN - if (!VirtualAlloc(p, AsmJSPageSize + buffer->byteLength(), MEM_COMMIT, PAGE_READWRITE)) { - VirtualFree(p, 0, MEM_RELEASE); + if (!VirtualAlloc(data, buffer->byteLength(), MEM_COMMIT, PAGE_READWRITE)) { + VirtualFree(data, 0, MEM_RELEASE); return false; } # else - if (mprotect(p, AsmJSPageSize + buffer->byteLength(), PROT_READ | PROT_WRITE)) { - munmap(p, AsmJSMappedSize); + if (mprotect(data, buffer->byteLength(), PROT_READ | PROT_WRITE)) { + munmap(data, AsmJSMappedSize); return false; } # endif // Copy over the current contents of the typed array. - uint8_t *data = reinterpret_cast(p) + AsmJSPageSize; memcpy(data, buffer->dataPointer(), buffer->byteLength()); // Swap the new elements into the ArrayBufferObject. - ObjectElements *newHeader = reinterpret_cast(data - sizeof(ObjectElements)); - ObjectElements *oldHeader = buffer->hasDynamicElements() ? buffer->getElementsHeader() - : nullptr; - buffer->changeContents(cx, newHeader); - js_free(oldHeader); + buffer->changeContents(cx, data); + JS_ASSERT(data == buffer->dataPointer()); // Mark the ArrayBufferObject so (1) we don't do this again, (2) we know not - // to js_free the header in the normal way. - newHeader->setIsAsmJSArrayBuffer(); - JS_ASSERT(data == buffer->dataPointer()); + // to js_free the data in the normal way. + buffer->setIsAsmJSArrayBuffer(); + return true; } void -ArrayBufferObject::releaseAsmJSArrayBuffer(FreeOp *fop, JSObject *obj) +ArrayBufferObject::releaseAsmJSArray(FreeOp *fop) { - ArrayBufferObject &buffer = obj->as(); - JS_ASSERT(buffer.isAsmJSArrayBuffer()); + void *data = dataPointer(); - uint8_t *p = buffer.dataPointer() - AsmJSPageSize ; - JS_ASSERT(uintptr_t(p) % AsmJSPageSize == 0); + JS_ASSERT(uintptr_t(data) % AsmJSPageSize == 0); # ifdef XP_WIN - VirtualFree(p, 0, MEM_RELEASE); + VirtualFree(data, 0, MEM_RELEASE); # else - munmap(p, AsmJSMappedSize); + munmap(data, AsmJSMappedSize); # endif } #else /* defined(JS_ION) && defined(JS_CPU_X64) */ @@ -604,20 +476,19 @@ ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle buf if (!ensureNonInline(cx, buffer)) return false; - JS_ASSERT(buffer->hasDynamicElements()); - buffer->getElementsHeader()->setIsAsmJSArrayBuffer(); + buffer->setIsAsmJSArrayBuffer(); return true; } void -ArrayBufferObject::releaseAsmJSArrayBuffer(FreeOp *fop, JSObject *obj) +ArrayBufferObject::releaseAsmJSArray(FreeOp *fop) { - fop->free_(obj->as().getElementsHeader()); + fop->free_(dataPointer()); } #endif bool -ArrayBufferObject::neuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer) +ArrayBufferObject::canNeuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer) { JS_ASSERT(!buffer.isSharedArrayBuffer()); #ifdef JS_ION @@ -629,7 +500,6 @@ ArrayBufferObject::neuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buff if (!act) return true; - js_ReportOverRecursed(cx); return false; #else return true; @@ -639,70 +509,114 @@ ArrayBufferObject::neuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buff void ArrayBufferObject::addView(ArrayBufferViewObject *view) { - // This view should never have been associated with a buffer before - JS_ASSERT(view->bufferLink() == UNSET_BUFFER_LINK); - // Note that pre-barriers are not needed here because either the list was // previously empty, in which case no pointer is being overwritten, or the // list was nonempty and will be made weak during this call (and weak // pointers cannot violate the snapshot-at-the-beginning invariant.) - ArrayBufferViewObject *viewsHead = GetViewList(this); + ArrayBufferViewObject *viewsHead = viewList(); if (viewsHead == nullptr) { // This ArrayBufferObject will have a single view at this point, so it // is a strong pointer (it will be marked during tracing.) JS_ASSERT(view->nextView() == nullptr); } else { - view->prependToViews(viewsHead); + view->setNextView(viewsHead); } - SetViewList(this, view); + setViewList(view); +} + +uint8_t * +ArrayBufferObject::dataPointer() const +{ + if (isSharedArrayBuffer()) + return (uint8_t *)this->as().dataPointer(); + return static_cast(getSlot(DATA_SLOT).toPrivate()); +} + +void +ArrayBufferObject::releaseData(FreeOp *fop) +{ + JS_ASSERT(ownsData()); + + if (isAsmJSArrayBuffer()) + releaseAsmJSArray(fop); + else + fop->free_(dataPointer()); +} + +void +ArrayBufferObject::setDataPointer(void *data, OwnsState ownsData) +{ + setSlot(DATA_SLOT, PrivateValue(data)); + setOwnsData(ownsData); +} + +size_t +ArrayBufferObject::byteLength() const +{ + return size_t(getSlot(BYTE_LENGTH_SLOT).toDouble()); +} + +void +ArrayBufferObject::setByteLength(size_t length) +{ + setSlot(BYTE_LENGTH_SLOT, DoubleValue(length)); +} + +uint32_t +ArrayBufferObject::flags() const +{ + return uint32_t(getSlot(FLAGS_SLOT).toInt32()); +} + +void +ArrayBufferObject::setFlags(uint32_t flags) +{ + setSlot(FLAGS_SLOT, Int32Value(flags)); } ArrayBufferObject * -ArrayBufferObject::create(JSContext *cx, uint32_t nbytes, bool clear /* = true */, +ArrayBufferObject::create(JSContext *cx, uint32_t nbytes, void *data /* = nullptr */, NewObjectKind newKind /* = GenericObject */) { - Rooted obj(cx, NewBuiltinClassInstance(cx, newKind)); - if (!obj) + // If we need to allocate data, try to use a larger object size class so + // that the array buffer's data can be allocated inline with the object. + size_t reservedSlots = JSCLASS_RESERVED_SLOTS(&class_); + + size_t nslots = reservedSlots; + if (!data) { + size_t usableSlots = JSObject::MAX_FIXED_SLOTS - reservedSlots; + if (nbytes <= usableSlots * sizeof(Value)) { + int newSlots = (nbytes - 1) / sizeof(Value) + 1; + JS_ASSERT(int(nbytes) <= newSlots * int(sizeof(Value))); + nslots = reservedSlots + newSlots; + } else { + data = AllocateArrayBufferContents(cx, nbytes); + if (!data) + return nullptr; + } + } + + JS_ASSERT(!(class_.flags & JSCLASS_HAS_PRIVATE)); + gc::AllocKind allocKind = GetGCObjectKind(nslots); + + Rooted obj(cx, NewBuiltinClassInstance(cx, allocKind, newKind)); + if (!obj) { + if (data) + js_free(data); return nullptr; - JS_ASSERT_IF(obj->isTenured(), obj->tenuredGetAllocKind() == gc::FINALIZE_OBJECT16_BACKGROUND); + } JS_ASSERT(obj->getClass() == &class_); - js::Shape *empty = EmptyShape::getInitialShape(cx, &class_, - obj->getProto(), obj->getParent(), obj->getMetadata(), - gc::FINALIZE_OBJECT16_BACKGROUND); - if (!empty) - return nullptr; - obj->setLastPropertyInfallible(empty); + JS_ASSERT(!gc::IsInsideNursery(cx->runtime(), obj)); - // ArrayBufferObjects delegate added properties to another JSObject, so - // their internal layout can use the object's fixed slots for storage. - // Set up the object to look like an array with an elements header. - JS_ASSERT(!obj->hasDynamicSlots()); - JS_ASSERT(!obj->hasDynamicElements()); - - // The beginning stores an ObjectElements header structure holding the - // length. The rest of it is a flat data store for the array buffer. - size_t usableSlots = ARRAYBUFFER_RESERVED_SLOTS - ObjectElements::VALUES_PER_HEADER; - - if (nbytes > sizeof(Value) * usableSlots) { - ObjectElements *header = AllocateArrayBufferContents(cx, nbytes); - if (!header) - return nullptr; - obj->elements = header->elements(); - -#ifdef JSGC_GENERATIONAL - JSRuntime *rt = obj->runtimeFromMainThread(); - rt->gcNursery.notifyNewElements(obj, header); -#endif - obj->initElementsHeader(obj->getElementsHeader(), nbytes); + if (data) { + obj->initialize(nbytes, data, OwnsData); } else { - // Elements header must be initialized before dataPointer() is callable. - obj->setFixedElements(); - obj->initElementsHeader(obj->getElementsHeader(), nbytes); - if (clear) - memset(obj->dataPointer(), 0, nbytes); + void *data = &obj->fixedSlots()[reservedSlots]; + memset(data, 0, nbytes); + obj->initialize(nbytes, data, DoesntOwnData); } return obj; @@ -720,7 +634,7 @@ ArrayBufferObject::createSlice(JSContext *cx, Handle arrayBu if (!arrayBuffer->hasData()) return create(cx, 0); - JSObject *slice = create(cx, length, false); + JSObject *slice = create(cx, length); if (!slice) return nullptr; memcpy(slice->as().dataPointer(), arrayBuffer->dataPointer() + begin, length); @@ -759,68 +673,78 @@ ArrayBufferObject::createDataViewForThis(JSContext *cx, unsigned argc, Value *vp } /* static */ bool -ArrayBufferObject::stealContents(JSContext *cx, Handle buffer, void **contents, - uint8_t **data) +ArrayBufferObject::ensureNonInline(JSContext *cx, Handle buffer) { - uint32_t byteLen = buffer->byteLength(); - - // If the ArrayBuffer's elements are transferrable, transfer ownership - // directly. Otherwise we have to copy the data into new elements. - ObjectElements *transferableHeader; - ObjectElements *newHeader; - bool stolen = buffer->hasStealableContents(); - if (stolen) { - transferableHeader = buffer->getElementsHeader(); - - newHeader = AllocateArrayBufferContents(cx, byteLen); - if (!newHeader) + if (!buffer->ownsData()) { + void *data = AllocateArrayBufferContents(cx, buffer->byteLength()); + if (!data) return false; - } else { - transferableHeader = AllocateArrayBufferContents(cx, byteLen); - if (!transferableHeader) - return false; - - initElementsHeader(transferableHeader, byteLen); - void *headerDataPointer = reinterpret_cast(transferableHeader->elements()); - memcpy(headerDataPointer, buffer->dataPointer(), byteLen); - - // Keep using the current elements. - newHeader = buffer->getElementsHeader(); + memcpy(data, buffer->dataPointer(), buffer->byteLength()); + buffer->changeContents(cx, data); } - JS_ASSERT(!IsInsideNursery(cx->runtime(), transferableHeader)); - *contents = transferableHeader; - *data = reinterpret_cast(transferableHeader + 1); - - // Neuter the views, which may also mprotect(PROT_NONE) the buffer. So do - // it after copying out the data. - if (!ArrayBufferObject::neuterViews(cx, buffer)) - return false; - - // If the elements were transferrable, revert the buffer back to using - // inline storage so it doesn't attempt to free the stolen elements when - // finalized. - if (stolen) - buffer->changeContents(cx, ObjectElements::fromElements(buffer->fixedElements())); - - buffer->neuter(newHeader, cx); return true; } -void -ArrayBufferObject::obj_trace(JSTracer *trc, JSObject *obj) +/* static */ void * +ArrayBufferObject::stealContents(JSContext *cx, Handle buffer) { - /* - * If this object changes, it will get marked via the private data barrier, - * so it's safe to leave it Unbarriered. - */ - JSObject *delegate = static_cast(obj->getPrivate()); - if (delegate) { - JS_SET_TRACING_LOCATION(trc, &obj->privateRef(obj->numFixedSlots())); - MarkObjectUnbarriered(trc, &delegate, "arraybuffer.delegate"); - obj->setPrivateUnbarriered(delegate); + if (!buffer->canNeuter(cx)) { + js_ReportOverRecursed(cx); + return nullptr; } + void *oldData = buffer->dataPointer(); + void *newData = AllocateArrayBufferContents(cx, buffer->byteLength()); + if (!newData) + return nullptr; + + if (buffer->hasStealableContents()) { + buffer->setOwnsData(DoesntOwnData); + ArrayBufferObject::neuter(cx, buffer, newData); + return oldData; + } else { + memcpy(newData, oldData, buffer->byteLength()); + ArrayBufferObject::neuter(cx, buffer, oldData); + return newData; + } + + return oldData; +} + +/* static */ void +ArrayBufferObject::addSizeOfExcludingThis(JSObject *obj, mozilla::MallocSizeOf mallocSizeOf, JS::ObjectsExtraSizes *sizes) +{ + ArrayBufferObject &buffer = AsArrayBuffer(obj); + + if (!buffer.ownsData()) + return; + + if (MOZ_UNLIKELY(buffer.isAsmJSArrayBuffer())) { +#if defined (JS_CPU_X64) + // On x64, ArrayBufferObject::prepareForAsmJS switches the + // ArrayBufferObject to use mmap'd storage. + sizes->nonHeapElementsAsmJS += buffer.byteLength(); +#else + sizes->mallocHeapElementsAsmJS += mallocSizeOf(buffer.dataPointer()); +#endif + } else if (buffer.dataPointer()) { + sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(buffer.dataPointer()); + } +} + +/* static */ void +ArrayBufferObject::finalize(FreeOp *fop, JSObject *obj) +{ + ArrayBufferObject &buffer = obj->as(); + + if (buffer.ownsData()) + buffer.releaseData(fop); +} + +/* static */ void +ArrayBufferObject::obj_trace(JSTracer *trc, JSObject *obj) +{ if (!IS_GC_MARKING_TRACER(trc) && !trc->runtime->isHeapMinorCollecting()) return; @@ -837,41 +761,35 @@ ArrayBufferObject::obj_trace(JSTracer *trc, JSObject *obj) // then swept to prune out their dead views. ArrayBufferObject &buffer = AsArrayBuffer(obj); - ArrayBufferViewObject *viewsHead = UpdateObjectIfRelocated(trc->runtime, - &GetViewListRef(&buffer)); + ArrayBufferViewObject *viewsHead = buffer.viewList(); if (!viewsHead) return; - viewsHead = UpdateObjectIfRelocated(trc->runtime, &GetViewListRef(&buffer)); - ArrayBufferViewObject *firstView = viewsHead; - if (firstView->nextView() == nullptr) { + buffer.setViewList(UpdateObjectIfRelocated(trc->runtime, &viewsHead)); + + if (viewsHead->nextView() == nullptr) { // Single view: mark it, but only if we're actually doing a GC pass // right now. Otherwise, the tracing pass for barrier verification will // fail if we add another view and the pointer becomes weak. - MarkObject(trc, &GetViewListRef(&buffer), "arraybuffer.singleview"); + MarkObjectUnbarriered(trc, &viewsHead, "arraybuffer.singleview"); + buffer.setViewListNoBarrier(viewsHead); } else { // Multiple views: do not mark, but append buffer to list. + ArrayBufferVector &gcLiveArrayBuffers = buffer.compartment()->gcLiveArrayBuffers; + // obj_trace may be called multiple times before sweep(), so avoid // adding this buffer to the list multiple times. - if (firstView->bufferLink() == UNSET_BUFFER_LINK) { - JS_ASSERT(obj->compartment() == firstView->compartment()); - ArrayBufferObject **bufList = &obj->compartment()->gcLiveArrayBuffers; - firstView->setBufferLink(*bufList); - *bufList = &AsArrayBuffer(obj); - } else { + if (buffer.inLiveList()) { #ifdef DEBUG bool found = false; - for (ArrayBufferObject *p = obj->compartment()->gcLiveArrayBuffers; - p; - p = GetViewList(p)->bufferLink()) - { - if (p == obj) - { - JS_ASSERT(!found); - found = true; - } - } + for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) + found |= gcLiveArrayBuffers[i] == &buffer; + JS_ASSERT(found); #endif + } else if (gcLiveArrayBuffers.append(&buffer)) { + buffer.setInLiveList(true); + } else { + CrashAtUnhandlableOOM("OOM while updating live array buffers"); } } } @@ -880,17 +798,17 @@ ArrayBufferObject::obj_trace(JSTracer *trc, JSObject *obj) ArrayBufferObject::sweep(JSCompartment *compartment) { JSRuntime *rt = compartment->runtimeFromMainThread(); - ArrayBufferObject *buffer = compartment->gcLiveArrayBuffers; - JS_ASSERT(buffer != UNSET_BUFFER_LINK); - compartment->gcLiveArrayBuffers = nullptr; + ArrayBufferVector &gcLiveArrayBuffers = compartment->gcLiveArrayBuffers; - while (buffer) { - ArrayBufferViewObject *viewsHead = UpdateObjectIfRelocated(rt, &GetViewListRef(buffer)); + for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) { + ArrayBufferObject *buffer = gcLiveArrayBuffers[i]; + + JS_ASSERT(buffer->inLiveList()); + buffer->setInLiveList(false); + + ArrayBufferViewObject *viewsHead = buffer->viewList(); JS_ASSERT(viewsHead); - - ArrayBufferObject *nextBuffer = viewsHead->bufferLink(); - JS_ASSERT(nextBuffer != UNSET_BUFFER_LINK); - viewsHead->setBufferLink(UNSET_BUFFER_LINK); + buffer->setViewList(UpdateObjectIfRelocated(rt, &viewsHead)); // Rebuild the list of views of the ArrayBufferObject, discarding dead // views. If there is only one view, it will have already been marked. @@ -905,278 +823,54 @@ ArrayBufferObject::sweep(JSCompartment *compartment) } view = UpdateObjectIfRelocated(rt, &nextView); } - SetViewList(buffer, prevLiveView); - buffer = nextBuffer; + buffer->setViewList(prevLiveView); } + + gcLiveArrayBuffers.clear(); } void ArrayBufferObject::resetArrayBufferList(JSCompartment *comp) { - ArrayBufferObject *buffer = comp->gcLiveArrayBuffers; - JS_ASSERT(buffer != UNSET_BUFFER_LINK); - comp->gcLiveArrayBuffers = nullptr; + ArrayBufferVector &gcLiveArrayBuffers = comp->gcLiveArrayBuffers; - while (buffer) { - ArrayBufferViewObject *view = GetViewList(buffer); - JS_ASSERT(view); + for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) { + ArrayBufferObject *buffer = gcLiveArrayBuffers[i]; - ArrayBufferObject *nextBuffer = view->bufferLink(); - JS_ASSERT(nextBuffer != UNSET_BUFFER_LINK); - - view->setBufferLink(UNSET_BUFFER_LINK); - buffer = nextBuffer; + JS_ASSERT(buffer->inLiveList()); + buffer->setInLiveList(false); } + + gcLiveArrayBuffers.clear(); } /* static */ bool ArrayBufferObject::saveArrayBufferList(JSCompartment *comp, ArrayBufferVector &vector) { - ArrayBufferObject *buffer = comp->gcLiveArrayBuffers; - while (buffer) { - JS_ASSERT(buffer != UNSET_BUFFER_LINK); - if (!vector.append(buffer)) - return false; + const ArrayBufferVector &gcLiveArrayBuffers = comp->gcLiveArrayBuffers; - ArrayBufferViewObject *view = GetViewList(buffer); - JS_ASSERT(view); - buffer = view->bufferLink(); + for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) { + if (!vector.append(gcLiveArrayBuffers[i])) + return false; } + return true; } /* static */ void ArrayBufferObject::restoreArrayBufferLists(ArrayBufferVector &vector) { - for (ArrayBufferObject **p = vector.begin(); p != vector.end(); p++) { - ArrayBufferObject *buffer = *p; - JSCompartment *comp = buffer->compartment(); - ArrayBufferViewObject *firstView = GetViewList(buffer); - JS_ASSERT(firstView); - JS_ASSERT(firstView->compartment() == comp); - JS_ASSERT(firstView->bufferLink() == UNSET_BUFFER_LINK); - firstView->setBufferLink(comp->gcLiveArrayBuffers); - comp->gcLiveArrayBuffers = buffer; + for (size_t i = 0; i < vector.length(); i++) { + ArrayBufferObject *buffer = vector[i]; + + JS_ASSERT(!buffer->inLiveList()); + buffer->setInLiveList(true); + + buffer->compartment()->gcLiveArrayBuffers.infallibleAppend(buffer); } } -bool -ArrayBufferObject::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id, - MutableHandleObject objp, MutableHandleShape propp) -{ - RootedObject delegate(cx, ArrayBufferDelegate(cx, obj)); - if (!delegate) - return false; - - bool delegateResult = JSObject::lookupGeneric(cx, delegate, id, objp, propp); - - /* If false, there was an error, so propagate it. - * Otherwise, if propp is non-null, the property - * was found. Otherwise it was not - * found so look in the prototype chain. - */ - if (!delegateResult) - return false; - - if (propp) { - if (objp == delegate) - objp.set(obj); - return true; - } - - RootedObject proto(cx, obj->getProto()); - if (!proto) { - objp.set(nullptr); - propp.set(nullptr); - return true; - } - - return JSObject::lookupGeneric(cx, proto, id, objp, propp); -} - -bool -ArrayBufferObject::obj_lookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, - MutableHandleObject objp, MutableHandleShape propp) -{ - Rooted id(cx, NameToId(name)); - return obj_lookupGeneric(cx, obj, id, objp, propp); -} - -bool -ArrayBufferObject::obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index, - MutableHandleObject objp, MutableHandleShape propp) -{ - RootedObject delegate(cx, ArrayBufferDelegate(cx, obj)); - if (!delegate) - return false; - - /* - * If false, there was an error, so propagate it. - * Otherwise, if propp is non-null, the property - * was found. Otherwise it was not - * found so look in the prototype chain. - */ - if (!JSObject::lookupElement(cx, delegate, index, objp, propp)) - return false; - - if (propp) { - if (objp == delegate) - objp.set(obj); - return true; - } - - RootedObject proto(cx, obj->getProto()); - if (proto) - return JSObject::lookupElement(cx, proto, index, objp, propp); - - objp.set(nullptr); - propp.set(nullptr); - return true; -} - -bool -ArrayBufferObject::obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v, - PropertyOp getter, StrictPropertyOp setter, unsigned attrs) -{ - AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter); - - RootedObject delegate(cx, ArrayBufferDelegate(cx, obj)); - if (!delegate) - return false; - return baseops::DefineGeneric(cx, delegate, id, v, getter, setter, attrs); -} - -bool -ArrayBufferObject::obj_defineProperty(JSContext *cx, HandleObject obj, - HandlePropertyName name, HandleValue v, - PropertyOp getter, StrictPropertyOp setter, unsigned attrs) -{ - Rooted id(cx, NameToId(name)); - return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs); -} - -bool -ArrayBufferObject::obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v, - PropertyOp getter, StrictPropertyOp setter, unsigned attrs) -{ - AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter); - - RootedObject delegate(cx, ArrayBufferDelegate(cx, obj)); - if (!delegate) - return false; - return baseops::DefineElement(cx, delegate, index, v, getter, setter, attrs); -} - -bool -ArrayBufferObject::obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver, - HandleId id, MutableHandleValue vp) -{ - RootedObject delegate(cx, ArrayBufferDelegate(cx, obj)); - if (!delegate) - return false; - return baseops::GetProperty(cx, delegate, receiver, id, vp); -} - -bool -ArrayBufferObject::obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver, - HandlePropertyName name, MutableHandleValue vp) -{ - RootedObject delegate(cx, ArrayBufferDelegate(cx, obj)); - if (!delegate) - return false; - Rooted id(cx, NameToId(name)); - return baseops::GetProperty(cx, delegate, receiver, id, vp); -} - -bool -ArrayBufferObject::obj_getElement(JSContext *cx, HandleObject obj, - HandleObject receiver, uint32_t index, MutableHandleValue vp) -{ - RootedObject delegate(cx, ArrayBufferDelegate(cx, obj)); - if (!delegate) - return false; - return baseops::GetElement(cx, delegate, receiver, index, vp); -} - -bool -ArrayBufferObject::obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id, - MutableHandleValue vp, bool strict) -{ - RootedObject delegate(cx, ArrayBufferDelegate(cx, obj)); - if (!delegate) - return false; - - return baseops::SetPropertyHelper(cx, delegate, obj, id, 0, vp, strict); -} - -bool -ArrayBufferObject::obj_setProperty(JSContext *cx, HandleObject obj, - HandlePropertyName name, MutableHandleValue vp, bool strict) -{ - Rooted id(cx, NameToId(name)); - return obj_setGeneric(cx, obj, id, vp, strict); -} - -bool -ArrayBufferObject::obj_setElement(JSContext *cx, HandleObject obj, - uint32_t index, MutableHandleValue vp, bool strict) -{ - RootedObject delegate(cx, ArrayBufferDelegate(cx, obj)); - if (!delegate) - return false; - - return baseops::SetElementHelper(cx, delegate, obj, index, 0, vp, strict); -} - -bool -ArrayBufferObject::obj_getGenericAttributes(JSContext *cx, HandleObject obj, - HandleId id, unsigned *attrsp) -{ - RootedObject delegate(cx, ArrayBufferDelegate(cx, obj)); - if (!delegate) - return false; - return baseops::GetAttributes(cx, delegate, id, attrsp); -} - -bool -ArrayBufferObject::obj_setGenericAttributes(JSContext *cx, HandleObject obj, - HandleId id, unsigned *attrsp) -{ - RootedObject delegate(cx, ArrayBufferDelegate(cx, obj)); - if (!delegate) - return false; - return baseops::SetAttributes(cx, delegate, id, attrsp); -} - -bool -ArrayBufferObject::obj_deleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, - bool *succeeded) -{ - RootedObject delegate(cx, ArrayBufferDelegate(cx, obj)); - if (!delegate) - return false; - return baseops::DeleteProperty(cx, delegate, name, succeeded); -} - -bool -ArrayBufferObject::obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index, - bool *succeeded) -{ - RootedObject delegate(cx, ArrayBufferDelegate(cx, obj)); - if (!delegate) - return false; - return baseops::DeleteElement(cx, delegate, index, succeeded); -} - -bool -ArrayBufferObject::obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op, - MutableHandleValue statep, MutableHandleId idp) -{ - statep.setNull(); - return true; -} - /* * ArrayBufferViewObject */ @@ -1197,7 +891,7 @@ ArrayBufferViewObject::trace(JSTracer *trc, JSObject *obj) * initialization, bufSlot may still be JSVAL_VOID. */ if (bufSlot.isObject()) { ArrayBufferObject &buf = AsArrayBuffer(&bufSlot.toObject()); - if (buf.getElementsHeader()->isNeuteredBuffer()) { + if (buf.isNeutered()) { // When a view is neutered, it is set to NULL JS_ASSERT(obj->getPrivate() == nullptr); } else { @@ -1210,17 +904,6 @@ ArrayBufferViewObject::trace(JSTracer *trc, JSObject *obj) IsSlotMarked(&obj->getReservedSlotRef(NEXT_VIEW_SLOT)); } -void -ArrayBufferViewObject::prependToViews(ArrayBufferViewObject *viewsHead) -{ - setNextView(viewsHead); - - // Move the multiview buffer list link into this view since we're - // prepending it to the list. - setBufferLink(viewsHead->bufferLink()); - viewsHead->setBufferLink(UNSET_BUFFER_LINK); -} - void ArrayBufferViewObject::neuter(JSContext *cx) { @@ -1281,32 +964,12 @@ JS_NeuterArrayBuffer(JSContext *cx, HandleObject obj) Rooted buffer(cx, &obj->as()); - ObjectElements *newHeader; - if (buffer->hasStealableContents()) { - // If we're "disposing" with the buffer contents, allocate zeroed - // memory of equal size and swap that in as contents. This ensures - // that stale indexes that assume the original length, won't index out - // of bounds. This is a temporary hack: when we're confident we've - // eradicated all stale accesses, we'll stop doing this. - newHeader = AllocateArrayBufferContents(cx, buffer->byteLength()); - if (!newHeader) - return false; - } else { - // This case neuters out the existing elements in-place, so use the - // old header as new. - newHeader = buffer->getElementsHeader(); - } - - // Mark all views of the ArrayBuffer as neutered. - if (!ArrayBufferObject::neuterViews(cx, buffer)) { - if (buffer->hasStealableContents()) { - FreeOp fop(cx->runtime(), false); - fop.free_(newHeader); - } + if (!buffer->canNeuter(cx)) { + js_ReportOverRecursed(cx); return false; } - buffer->neuter(newHeader, cx); + ArrayBufferObject::neuter(cx, buffer, buffer->dataPointer()); return true; } @@ -1318,53 +981,22 @@ JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes) } JS_PUBLIC_API(JSObject *) -JS_NewArrayBufferWithContents(JSContext *cx, void *contents) +JS_NewArrayBufferWithContents(JSContext *cx, size_t nbytes, void *contents) { JS_ASSERT(contents); - - // Do not allocate ArrayBuffers with an API-provided pointer in the nursery. - // These are likely to be long lived and the nursery does not know how to - // free the contents. - JSObject *obj = ArrayBufferObject::create(cx, 0, true, TenuredObject); - if (!obj) - return nullptr; - js::ObjectElements *elements = reinterpret_cast(contents); - obj->setDynamicElements(elements); - JS_ASSERT(GetViewList(&obj->as()) == nullptr); - -#ifdef JSGC_GENERATIONAL - cx->runtime()->gcNursery.notifyNewElements(obj, elements); -#endif - return obj; + return ArrayBufferObject::create(cx, nbytes, contents, TenuredObject); } -JS_PUBLIC_API(bool) -JS_AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, - void **contents, uint8_t **data) +JS_PUBLIC_API(void *) +JS_AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes) { - js::ObjectElements *header = AllocateArrayBufferContents(maybecx, nbytes); - if (!header) - return false; - - ArrayBufferObject::updateElementsHeader(header, nbytes); - - *contents = header; - *data = reinterpret_cast(header->elements()); - return true; + return AllocateArrayBufferContents(maybecx, nbytes); } -JS_PUBLIC_API(bool) -JS_ReallocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void **contents, uint8_t **data) +JS_PUBLIC_API(void *) +JS_ReallocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void *oldContents, uint32_t oldNbytes) { - js::ObjectElements *header = AllocateArrayBufferContents(maybecx, nbytes, *contents); - if (!header) - return false; - - ArrayBufferObject::initElementsHeader(header, nbytes); - - *contents = header; - *data = reinterpret_cast(header->elements()); - return true; + return AllocateArrayBufferContents(maybecx, nbytes, oldContents, oldNbytes); } JS_FRIEND_API(bool) @@ -1374,23 +1006,20 @@ JS_IsArrayBufferObject(JSObject *obj) return obj ? obj->is() : false; } -JS_PUBLIC_API(bool) -JS_StealArrayBufferContents(JSContext *cx, HandleObject objArg, void **contents, uint8_t **data) +JS_PUBLIC_API(void *) +JS_StealArrayBufferContents(JSContext *cx, HandleObject objArg) { JSObject *obj = CheckedUnwrap(objArg); if (!obj) - return false; + return nullptr; if (!obj->is()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); - return false; + return nullptr; } Rooted buffer(cx, &obj->as()); - if (!ArrayBufferObject::stealContents(cx, buffer, contents, data)) - return false; - - return true; + return ArrayBufferObject::stealContents(cx, buffer); } JS_FRIEND_API(void *) diff --git a/js/src/vm/ArrayBufferObject.h b/js/src/vm/ArrayBufferObject.h index f40dc579c421..f788c91c1846 100644 --- a/js/src/vm/ArrayBufferObject.h +++ b/js/src/vm/ArrayBufferObject.h @@ -53,6 +53,13 @@ class ArrayBufferObject : public JSObject static bool fun_slice_impl(JSContext *cx, CallArgs args); public: + static const uint8_t DATA_SLOT = 0; + static const uint8_t BYTE_LENGTH_SLOT = 1; + static const uint8_t VIEW_LIST_SLOT = 2; + static const uint8_t FLAGS_SLOT = 3; + + static const uint8_t RESERVED_SLOTS = 4; + static const Class class_; static const Class protoClass; @@ -67,7 +74,7 @@ class ArrayBufferObject : public JSObject static bool class_constructor(JSContext *cx, unsigned argc, Value *vp); - static ArrayBufferObject *create(JSContext *cx, uint32_t nbytes, bool clear = true, + static ArrayBufferObject *create(JSContext *cx, uint32_t nbytes, void *contents = nullptr, NewObjectKind newKind = GenericObject); static JSObject *createSlice(JSContext *cx, Handle arrayBuffer, @@ -84,57 +91,17 @@ class ArrayBufferObject : public JSObject static void obj_trace(JSTracer *trc, JSObject *obj); - static bool obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id, - MutableHandleObject objp, MutableHandleShape propp); - static bool obj_lookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, - MutableHandleObject objp, MutableHandleShape propp); - static bool obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index, - MutableHandleObject objp, MutableHandleShape propp); - - static bool obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v, - PropertyOp getter, StrictPropertyOp setter, unsigned attrs); - static bool obj_defineProperty(JSContext *cx, HandleObject obj, - HandlePropertyName name, HandleValue v, - PropertyOp getter, StrictPropertyOp setter, unsigned attrs); - static bool obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v, - PropertyOp getter, StrictPropertyOp setter, unsigned attrs); - - static bool obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver, - HandleId id, MutableHandleValue vp); - static bool obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver, - HandlePropertyName name, MutableHandleValue vp); - static bool obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver, - uint32_t index, MutableHandleValue vp); - - static bool obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id, - MutableHandleValue vp, bool strict); - static bool obj_setProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, - MutableHandleValue vp, bool strict); - static bool obj_setElement(JSContext *cx, HandleObject obj, uint32_t index, - MutableHandleValue vp, bool strict); - - static bool obj_getGenericAttributes(JSContext *cx, HandleObject obj, - HandleId id, unsigned *attrsp); - static bool obj_setGenericAttributes(JSContext *cx, HandleObject obj, - HandleId id, unsigned *attrsp); - - static bool obj_deleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, - bool *succeeded); - static bool obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index, - bool *succeeded); - - static bool obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op, - MutableHandleValue statep, MutableHandleId idp); - static void sweep(JSCompartment *rt); static void resetArrayBufferList(JSCompartment *rt); static bool saveArrayBufferList(JSCompartment *c, ArrayBufferVector &vector); static void restoreArrayBufferLists(ArrayBufferVector &vector); + static void *stealContents(JSContext *cx, Handle buffer); + bool hasStealableContents() const { // Inline elements strictly adhere to the corresponding buffer. - if (!hasDynamicElements()) + if (!ownsData()) return false; // asm.js buffer contents are transferred by copying, just like inline @@ -150,31 +117,12 @@ class ArrayBufferObject : public JSObject return !isNeutered(); } - static bool stealContents(JSContext *cx, Handle buffer, void **contents, - uint8_t **data); - - static void updateElementsHeader(js::ObjectElements *header, uint32_t bytes) { - header->initializedLength = bytes; - - // NB: one or both of these fields is clobbered by GetViewList to store - // the 'views' link. Set them to 0 to effectively initialize 'views' - // to nullptr. - header->length = 0; - header->capacity = 0; - } - - static void initElementsHeader(js::ObjectElements *header, uint32_t bytes) { - header->flags = 0; - updateElementsHeader(header, bytes); - } - - static uint32_t headerInitializedLength(const js::ObjectElements *header) { - return header->initializedLength; - } + static void addSizeOfExcludingThis(JSObject *obj, mozilla::MallocSizeOf mallocSizeOf, + JS::ObjectsExtraSizes *sizes); void addView(ArrayBufferViewObject *view); - void changeContents(JSContext *cx, ObjectElements *newHeader); + void changeContents(JSContext *cx, void *newData); /* * Ensure data is not stored inline in the object. Used when handing back a @@ -182,28 +130,15 @@ class ArrayBufferObject : public JSObject */ static bool ensureNonInline(JSContext *cx, Handle buffer); - uint32_t byteLength() const { - return getElementsHeader()->initializedLength; - } + bool canNeuter(JSContext *cx); - /* - * Neuter all views of an ArrayBuffer. - */ - static bool neuterViews(JSContext *cx, Handle buffer); + /* Neuter this buffer and all its views. */ + static void neuter(JSContext *cx, Handle buffer, void *newData); - uint8_t * dataPointer() const; + uint8_t *dataPointer() const; + size_t byteLength() const; - /* - * Discard the ArrayBuffer contents, and use |newHeader| for the buffer's - * new contents. (These new contents are zeroed, of identical size in - * memory as the current contents, but appear to be neutered and of zero - * length. This is purely precautionary against stale indexes that were - * in-bounds with respect to the initial length but would not be after - * neutering. This precaution will be removed once we're sure such stale - * indexing no longer happens.) For asm.js buffers, at least, should - * be called after neuterViews(). - */ - void neuter(ObjectElements *newHeader, JSContext *cx); + void releaseData(FreeOp *fop); /* * Check if the arrayBuffer contains any data. This will return false for @@ -213,19 +148,68 @@ class ArrayBufferObject : public JSObject return getClass() == &class_; } - bool isSharedArrayBuffer() const { - return getElementsHeader()->isSharedArrayBuffer(); + bool isAsmJSArrayBuffer() const { return flags() & ASMJS_BUFFER; } + bool isSharedArrayBuffer() const { return flags() & SHARED_BUFFER; } + bool isNeutered() const { return flags() & NEUTERED_BUFFER; } + + static bool prepareForAsmJS(JSContext *cx, Handle buffer); + static bool canNeuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer); + + static void finalize(FreeOp *fop, JSObject *obj); + + protected: + enum OwnsState { + DoesntOwnData = 0, + OwnsData = 1, + }; + + void setDataPointer(void *data, OwnsState ownsState); + void setByteLength(size_t length); + + ArrayBufferViewObject *viewList() const; + void setViewList(ArrayBufferViewObject *viewsHead); + void setViewListNoBarrier(ArrayBufferViewObject *viewsHead); + + enum ArrayBufferFlags { + // In the gcLiveArrayBuffers list. + IN_LIVE_LIST = 0x1, + + // The dataPointer() is owned by this buffer and should be released + // when no longer in use. Releasing the pointer may be done by either + // freeing or unmapping it, and how to do this is determined by the + // buffer's other flags. + OWNS_DATA = 0x2, + + ASMJS_BUFFER = 0x4, + SHARED_BUFFER = 0x8, + NEUTERED_BUFFER = 0x10 + }; + + uint32_t flags() const; + void setFlags(uint32_t flags); + + bool inLiveList() const { return flags() & IN_LIVE_LIST; } + void setInLiveList(bool value) { + setFlags(value ? (flags() | IN_LIVE_LIST) : (flags() & ~IN_LIVE_LIST)); } - bool isAsmJSArrayBuffer() const { - return getElementsHeader()->isAsmJSArrayBuffer(); + bool ownsData() const { return flags() & OWNS_DATA; } + void setOwnsData(OwnsState owns) { + setFlags(owns ? (flags() | OWNS_DATA) : (flags() & ~OWNS_DATA)); } - bool isNeutered() const { - return getElementsHeader()->isNeuteredBuffer(); + + void setIsAsmJSArrayBuffer() { setFlags(flags() | ASMJS_BUFFER); } + void setIsSharedArrayBuffer() { setFlags(flags() | SHARED_BUFFER); } + void setIsNeutered() { setFlags(flags() | NEUTERED_BUFFER); } + + void initialize(size_t byteLength, void *data, OwnsState ownsState) { + setByteLength(byteLength); + setFlags(0); + setViewListNoBarrier(nullptr); + setDataPointer(data, ownsState); } - static bool prepareForAsmJS(JSContext *cx, Handle buffer); - static bool neuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer); - static void releaseAsmJSArrayBuffer(FreeOp *fop, JSObject *obj); + + void releaseAsmJSArray(FreeOp *fop); }; /* @@ -249,35 +233,24 @@ class ArrayBufferViewObject : public JSObject /* ArrayBufferObjects point to a linked list of views, chained through this slot */ static const size_t NEXT_VIEW_SLOT = JS_TYPEDOBJ_SLOT_NEXT_VIEW; - /* - * When ArrayBufferObjects are traced during GC, they construct a linked - * list of ArrayBufferObjects with more than one view, chained through this - * slot of the first view of each ArrayBufferObject. - */ - static const size_t NEXT_BUFFER_SLOT = JS_TYPEDOBJ_SLOT_NEXT_BUFFER; - public: JSObject *bufferObject() const { return &getFixedSlot(BUFFER_SLOT).toObject(); } - ArrayBufferObject *bufferLink() { - return static_cast(getFixedSlot(NEXT_BUFFER_SLOT).toPrivate()); - } - - inline void setBufferLink(ArrayBufferObject *buffer); - ArrayBufferViewObject *nextView() const { return static_cast(getFixedSlot(NEXT_VIEW_SLOT).toPrivate()); } inline void setNextView(ArrayBufferViewObject *view); - void prependToViews(ArrayBufferViewObject *viewsHead); - void neuter(JSContext *cx); static void trace(JSTracer *trc, JSObject *obj); + + uint8_t *dataPointer() { + return static_cast(getPrivate()); + } }; bool @@ -323,13 +296,6 @@ bool IsArrayBuffer(JSObject *obj); ArrayBufferObject &AsArrayBuffer(HandleObject obj); ArrayBufferObject &AsArrayBuffer(JSObject *obj); -inline void -ArrayBufferViewObject::setBufferLink(ArrayBufferObject *buffer) -{ - setFixedSlot(NEXT_BUFFER_SLOT, PrivateValue(buffer)); - PostBarrierTypedArrayObject(this); -} - inline void ArrayBufferViewObject::setNextView(ArrayBufferViewObject *view) { diff --git a/js/src/vm/ObjectImpl.cpp b/js/src/vm/ObjectImpl.cpp index 1c4736e9da53..753ab147c966 100644 --- a/js/src/vm/ObjectImpl.cpp +++ b/js/src/vm/ObjectImpl.cpp @@ -383,20 +383,6 @@ js::ObjectImpl::markChildren(JSTracer *trc) } } -JSObject * -js::ArrayBufferDelegate(JSContext *cx, Handle obj) -{ - MOZ_ASSERT(obj->hasClass(&ArrayBufferObject::class_) || - obj->hasClass(&SharedArrayBufferObject::class_)); - - if (obj->getPrivate()) - return static_cast(obj->getPrivate()); - JSObject *delegate = NewObjectWithGivenProto(cx, &JSObject::class_, - obj->getTaggedProto(), nullptr); - obj->setPrivateGCThing(delegate); - return delegate; -} - void AutoPropDescRooter::trace(JSTracer *trc) { diff --git a/js/src/vm/ObjectImpl.h b/js/src/vm/ObjectImpl.h index 7093a1e86377..b819aa2f02b1 100644 --- a/js/src/vm/ObjectImpl.h +++ b/js/src/vm/ObjectImpl.h @@ -168,23 +168,16 @@ class ObjectElements public: enum Flags { CONVERT_DOUBLE_ELEMENTS = 0x1, - ASMJS_ARRAY_BUFFER = 0x2, - NEUTERED_BUFFER = 0x4, - SHARED_ARRAY_BUFFER = 0x8, // Present only if these elements correspond to an array with // non-writable length; never present for non-arrays. - NONWRITABLE_ARRAY_LENGTH = 0x10 + NONWRITABLE_ARRAY_LENGTH = 0x2 }; private: friend class ::JSObject; friend class ObjectImpl; friend class ArrayObject; - friend class ArrayBufferObject; - friend class ArrayBufferViewObject; - friend class SharedArrayBufferObject; - friend class TypedArrayObject; friend class Nursery; template @@ -231,24 +224,6 @@ class ObjectElements void clearShouldConvertDoubleElements() { flags &= ~CONVERT_DOUBLE_ELEMENTS; } - bool isAsmJSArrayBuffer() const { - return flags & ASMJS_ARRAY_BUFFER; - } - void setIsAsmJSArrayBuffer() { - flags |= ASMJS_ARRAY_BUFFER; - } - bool isNeuteredBuffer() const { - return flags & NEUTERED_BUFFER; - } - void setIsNeuteredBuffer() { - flags |= NEUTERED_BUFFER; - } - bool isSharedArrayBuffer() const { - return flags & SHARED_ARRAY_BUFFER; - } - void setIsSharedArrayBuffer() { - flags |= SHARED_ARRAY_BUFFER; - } bool hasNonwritableArrayLength() const { return flags & NONWRITABLE_ARRAY_LENGTH; } @@ -1039,9 +1014,6 @@ IsObjectValueInCompartment(js::Value v, JSCompartment *comp) } #endif -extern JSObject * -ArrayBufferDelegate(JSContext *cx, Handle obj); - } /* namespace js */ #endif /* vm_ObjectImpl_h */ diff --git a/js/src/vm/SharedArrayObject.cpp b/js/src/vm/SharedArrayObject.cpp index 0945405f0668..dcd32b13f2b0 100644 --- a/js/src/vm/SharedArrayObject.cpp +++ b/js/src/vm/SharedArrayObject.cpp @@ -23,8 +23,6 @@ using namespace js; using mozilla::IsNaN; using mozilla::PodCopy; -#define SHAREDARRAYBUFFER_RESERVED_SLOTS 15 - /* * SharedArrayRawBuffer */ @@ -179,53 +177,26 @@ SharedArrayBufferObject::New(JSContext *cx, uint32_t length) return nullptr; } - RootedObject obj(cx, NewBuiltinClassInstance(cx, &class_)); - if (!obj) - return nullptr; - - JS_ASSERT(obj->getClass() == &class_); - - Rooted empty(cx); - empty = EmptyShape::getInitialShape(cx, &class_, obj->getProto(), obj->getParent(), - obj->getMetadata(), gc::FINALIZE_OBJECT16_BACKGROUND); - if (!empty) - return nullptr; - obj->setLastPropertyInfallible(empty); - - obj->setFixedElements(); - obj->as().initElementsHeader(obj->getElementsHeader(), length); - obj->getElementsHeader()->setIsSharedArrayBuffer(); - SharedArrayRawBuffer *buffer = SharedArrayRawBuffer::New(length); if (!buffer) return nullptr; - obj->as().acceptRawBuffer(buffer); - return obj; + return New(cx, buffer); } JSObject * SharedArrayBufferObject::New(JSContext *cx, SharedArrayRawBuffer *buffer) { - RootedObject obj(cx, NewBuiltinClassInstance(cx, &class_)); + Rooted obj(cx, NewBuiltinClassInstance(cx)); if (!obj) return nullptr; JS_ASSERT(obj->getClass() == &class_); - Rooted empty(cx); - empty = EmptyShape::getInitialShape(cx, &class_, obj->getProto(), obj->getParent(), - obj->getMetadata(), gc::FINALIZE_OBJECT16_BACKGROUND); - if (!empty) - return nullptr; - obj->setLastPropertyInfallible(empty); + obj->initialize(buffer->byteLength(), nullptr, DoesntOwnData); - obj->setFixedElements(); - obj->as().initElementsHeader(obj->getElementsHeader(), - buffer->byteLength()); - obj->getElementsHeader()->setIsSharedArrayBuffer(); - - obj->as().acceptRawBuffer(buffer); + obj->acceptRawBuffer(buffer); + obj->setIsSharedArrayBuffer(); return obj; } @@ -283,8 +254,6 @@ SharedArrayBufferObject::Finalize(FreeOp *fop, JSObject *obj) const Class SharedArrayBufferObject::protoClass = { "SharedArrayBufferPrototype", - JSCLASS_HAS_PRIVATE | - JSCLASS_HAS_RESERVED_SLOTS(SHAREDARRAYBUFFER_RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer), JS_PropertyStub, /* addProperty */ JS_DeletePropertyStub, /* delProperty */ @@ -297,10 +266,8 @@ const Class SharedArrayBufferObject::protoClass = { const Class SharedArrayBufferObject::class_ = { "SharedArrayBuffer", - JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | - Class::NON_NATIVE | - JSCLASS_HAS_RESERVED_SLOTS(SHAREDARRAYBUFFER_RESERVED_SLOTS) | + JSCLASS_HAS_RESERVED_SLOTS(SharedArrayBufferObject::RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer), JS_PropertyStub, /* addProperty */ JS_DeletePropertyStub, /* delProperty */ @@ -315,29 +282,7 @@ const Class SharedArrayBufferObject::class_ = { nullptr, /* construct */ ArrayBufferObject::obj_trace, JS_NULL_CLASS_SPEC, - JS_NULL_CLASS_EXT, - { - ArrayBufferObject::obj_lookupGeneric, - ArrayBufferObject::obj_lookupProperty, - ArrayBufferObject::obj_lookupElement, - ArrayBufferObject::obj_defineGeneric, - ArrayBufferObject::obj_defineProperty, - ArrayBufferObject::obj_defineElement, - ArrayBufferObject::obj_getGeneric, - ArrayBufferObject::obj_getProperty, - ArrayBufferObject::obj_getElement, - ArrayBufferObject::obj_setGeneric, - ArrayBufferObject::obj_setProperty, - ArrayBufferObject::obj_setElement, - ArrayBufferObject::obj_getGenericAttributes, - ArrayBufferObject::obj_setGenericAttributes, - ArrayBufferObject::obj_deleteProperty, - ArrayBufferObject::obj_deleteElement, - nullptr, nullptr, /* watch/unwatch */ - nullptr, /* slice */ - ArrayBufferObject::obj_enumerate, - nullptr, /* thisObject */ - } + JS_NULL_CLASS_EXT }; JSObject * diff --git a/js/src/vm/SharedArrayObject.h b/js/src/vm/SharedArrayObject.h index a753f4d89cf7..92f942aca49a 100644 --- a/js/src/vm/SharedArrayObject.h +++ b/js/src/vm/SharedArrayObject.h @@ -74,8 +74,9 @@ class SharedArrayBufferObject : public ArrayBufferObject static const Class protoClass; // Slot used for storing a pointer to the SharedArrayRawBuffer. - // First two slots hold the ObjectElements. - static const int32_t RAWBUF_SLOT = 2; + static const uint8_t RAWBUF_SLOT = ArrayBufferObject::RESERVED_SLOTS; + + static const uint8_t RESERVED_SLOTS = ArrayBufferObject::RESERVED_SLOTS + 1; static bool class_constructor(JSContext *cx, unsigned argc, Value *vp); diff --git a/js/src/vm/StructuredClone.cpp b/js/src/vm/StructuredClone.cpp index 8c9a75b5cf9a..982188207cbf 100644 --- a/js/src/vm/StructuredClone.cpp +++ b/js/src/vm/StructuredClone.cpp @@ -977,6 +977,8 @@ JSStructuredCloneWriter::writeTransferMap() return false; if (!out.writePtr(nullptr)) // Pointer to ArrayBuffer contents or to SharedArrayRawBuffer. return false; + if (!out.write(0)) // |byteLength| for an ArrayBuffer, 0 for SharedArrayBuffer + return false; if (!out.write(0)) // |userdata|, intended to be passed to callbacks. return false; } @@ -1006,14 +1008,15 @@ JSStructuredCloneWriter::transferOwnership() MOZ_ASSERT(uint32_t(LittleEndian::readUint64(point)) == SCTAG_TM_UNFILLED); if (obj->is()) { - void *content; - uint8_t *data; - if (!JS_StealArrayBufferContents(context(), obj, &content, &data)) + size_t nbytes = obj->as().byteLength(); + void *contents = JS_StealArrayBufferContents(context(), obj); + if (!contents) return false; // Destructor will clean up the already-transferred data uint64_t entryTag = PairToUInt64(SCTAG_TRANSFER_MAP_ENTRY, SCTAG_TM_ALLOC_DATA); LittleEndian::writeUint64(point++, entryTag); - LittleEndian::writeUint64(point++, reinterpret_cast(content)); + LittleEndian::writeUint64(point++, reinterpret_cast(contents)); + LittleEndian::writeUint64(point++, nbytes); LittleEndian::writeUint64(point++, 0); } else { SharedArrayRawBuffer *rawbuf = obj->as().rawBufferObject(); @@ -1026,6 +1029,7 @@ JSStructuredCloneWriter::transferOwnership() LittleEndian::writeUint64(point++, entryTag); LittleEndian::writeUint64(point++, reinterpret_cast(rawbuf)); LittleEndian::writeUint64(point++, 0); + LittleEndian::writeUint64(point++, 0); } } @@ -1512,6 +1516,10 @@ JSStructuredCloneReader::readTransferMap() if (!in.readPtr(&content)) return false; + uint64_t nbytes; + if (!in.read(&nbytes)) + return false; + uint64_t userdata; if (!in.read(&userdata)) return false; @@ -1519,7 +1527,7 @@ JSStructuredCloneReader::readTransferMap() RootedObject obj(context()); if (data == SCTAG_TM_ALLOC_DATA) - obj = JS_NewArrayBufferWithContents(context(), content); + obj = JS_NewArrayBufferWithContents(context(), nbytes, content); else if (data == SCTAG_TM_SHARED_BUFFER) obj = SharedArrayBufferObject::New(context(), (SharedArrayRawBuffer *)content); diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index 444fa2717c4b..1906b0f4e988 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -349,7 +349,6 @@ class TypedArrayObjectTemplate : public TypedArrayObject obj->setSlot(BYTEOFFSET_SLOT, Int32Value(byteOffset)); obj->setSlot(BYTELENGTH_SLOT, Int32Value(len * sizeof(NativeType))); obj->setSlot(NEXT_VIEW_SLOT, PrivateValue(nullptr)); - obj->setSlot(NEXT_BUFFER_SLOT, PrivateValue(UNSET_BUFFER_LINK)); js::Shape *empty = EmptyShape::getInitialShape(cx, fastClass(), obj->getProto(), obj->getParent(), obj->getMetadata(), @@ -1344,7 +1343,6 @@ DataViewObject::create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength, dvobj.setFixedSlot(BYTELENGTH_SLOT, Int32Value(byteLength)); dvobj.setFixedSlot(BUFFER_SLOT, ObjectValue(*arrayBuffer)); dvobj.setFixedSlot(NEXT_VIEW_SLOT, PrivateValue(nullptr)); - dvobj.setFixedSlot(NEXT_BUFFER_SLOT, PrivateValue(UNSET_BUFFER_LINK)); InitArrayBufferViewDataPointer(&dvobj, arrayBuffer, byteOffset); JS_ASSERT(byteOffset + byteLength <= arrayBuffer->byteLength()); diff --git a/js/src/vm/TypedArrayObject.h b/js/src/vm/TypedArrayObject.h index ea3195bc862c..ea407516ee9a 100644 --- a/js/src/vm/TypedArrayObject.h +++ b/js/src/vm/TypedArrayObject.h @@ -337,7 +337,7 @@ ClampIntForUint8Array(int32_t x) return x; } -extern js::ArrayBufferObject * const UNSET_BUFFER_LINK; +bool ToDoubleForTypedArray(JSContext *cx, JS::HandleValue vp, double *d); } // namespace js diff --git a/toolkit/components/osfile/NativeOSFileInternals.cpp b/toolkit/components/osfile/NativeOSFileInternals.cpp index 5546872e1e4f..71b4caab9584 100644 --- a/toolkit/components/osfile/NativeOSFileInternals.cpp +++ b/toolkit/components/osfile/NativeOSFileInternals.cpp @@ -64,10 +64,6 @@ namespace { * and into JavaScript without copy. */ struct ArrayBufferContents { - /** - * The header of the ArrayBuffer. This is the pointer actually used by JSAPI. - */ - void* header; /** * The data of the ArrayBuffer. This is the pointer manipulated to * read/write the contents of the buffer. @@ -85,12 +81,11 @@ struct ArrayBufferContents { struct ScopedArrayBufferContentsTraits { typedef ArrayBufferContents type; const static type empty() { - type result = {0, 0, 0}; + type result = {0, 0}; return result; } const static void release(type ptr) { - js_free(ptr.header); - ptr.header = nullptr; + js_free(ptr.data); ptr.data = nullptr; ptr.nbytes = 0; } @@ -119,10 +114,9 @@ struct ScopedArrayBufferContents: public Scoped bool Allocate(uint32_t length) { dispose(); ArrayBufferContents& value = rwget(); - if (JS_AllocateArrayBufferContents(/*no context available*/nullptr, - length, - &value.header, - &value.data)) { + void *ptr = JS_AllocateArrayBufferContents(/*no context available*/nullptr, length); + if (ptr) { + value.data = (uint8_t *) ptr; value.nbytes = length; return true; } @@ -360,7 +354,7 @@ nsresult TypedArrayResult::GetCacheableResult(JSContext* cx, JS::MutableHandle aResult) { MOZ_ASSERT(NS_IsMainThread()); - // We cannot simply construct a typed array using contents.header as + // We cannot simply construct a typed array using contents.data as // this would allow us to have several otherwise unrelated // ArrayBuffers with the same underlying C buffer. As this would be // very unsafe, we need to cache the result once we have it. @@ -369,7 +363,7 @@ TypedArrayResult::GetCacheableResult(JSContext* cx, JS::MutableHandle MOZ_ASSERT(contents.data); JS::Rooted - arrayBuffer(cx, JS_NewArrayBufferWithContents(cx, contents.header)); + arrayBuffer(cx, JS_NewArrayBufferWithContents(cx, contents.nbytes, contents.data)); if (!arrayBuffer) { return NS_ERROR_OUT_OF_MEMORY; }