/* static */ void ArrayBufferObject::sweep(JSCompartment *compartment) { JSRuntime *rt = compartment->runtimeFromMainThread(); ArrayBufferVector &gcLiveArrayBuffers = compartment->gcLiveArrayBuffers; 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); 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. ArrayBufferViewObject *prevLiveView = nullptr; ArrayBufferViewObject *view = viewsHead; while (view) { JS_ASSERT(buffer->compartment() == view->compartment()); ArrayBufferViewObject *nextView = view->nextView(); if (!IsObjectAboutToBeFinalized(&view)) { view->setNextView(prevLiveView); prevLiveView = view; } view = UpdateObjectIfRelocated(rt, &nextView); } buffer->setViewList(prevLiveView); } gcLiveArrayBuffers.clear(); }
void ArrayBufferObject::changeContents(JSContext *cx, void *newData) { // Change buffer contents. uint8_t* oldDataPointer = dataPointer(); setNewOwnedData(cx->runtime()->defaultFreeOp(), newData); // Update all views. ArrayBufferViewObject *viewListHead = viewList(); for (ArrayBufferViewObject *view = viewListHead; view; view = view->nextView()) { // Watch out for NULL data pointers in views. This means that the view // is not fully initialized (in which case it'll be initialized later // with the correct pointer). uint8_t *viewDataPointer = view->dataPointer(); if (viewDataPointer) { JS_ASSERT(newData); ptrdiff_t offset = viewDataPointer - oldDataPointer; viewDataPointer = static_cast<uint8_t *>(newData) + offset; view->setPrivate(viewDataPointer); } // Notify compiled jit code that the base pointer has moved. MarkObjectStateChange(cx, view); } }
void ArrayBufferObject::changeContents(JSContext *cx, void *newData) { JS_ASSERT(!isAsmJSArrayBuffer()); JS_ASSERT(!isSharedArrayBuffer()); // Update all views. 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 // it'll be initialized later with the correct pointer) or // 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 = view->dataPointer(); if (viewDataPointer) { JS_ASSERT(newData); viewDataPointer += static_cast<uint8_t *>(newData) - dataPointer(); view->setPrivate(viewDataPointer); } // Notify compiled jit code that the base pointer has moved. MarkObjectStateChange(cx, view); } if (ownsData()) releaseData(cx->runtime()->defaultFreeOp()); setDataPointer(static_cast<uint8_t *>(newData), OwnsData); }
/* static */ void ArrayBufferObject::obj_trace(JSTracer *trc, JSObject *obj) { if (!IS_GC_MARKING_TRACER(trc) && !trc->runtime()->isHeapMinorCollecting() #ifdef JSGC_FJGENERATIONAL && !trc->runtime()->isFJMinorCollecting() #endif ) { return; } // ArrayBufferObjects need to maintain a list of possibly-weak pointers to // their views. The straightforward way to update the weak pointers would // be in the views' finalizers, but giving views finalizers means they // cannot be swept in the background. This results in a very high // performance cost. Instead, ArrayBufferObjects with a single view hold a // strong pointer to the view. This can entrain garbage when the single // view becomes otherwise unreachable while the buffer is still live, but // this is expected to be rare. ArrayBufferObjects with 0-1 views are // expected to be by far the most common cases. ArrayBufferObjects with // multiple views are collected into a linked list during collection, and // then swept to prune out their dead views. ArrayBufferObject &buffer = AsArrayBuffer(obj); ArrayBufferViewObject *viewsHead = buffer.viewList(); if (!viewsHead) return; 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. 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 (buffer.inLiveList()) { #ifdef DEBUG bool found = false; 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"); } } }
/* static */ void ArrayBufferObject::neuter(JSContext *cx, Handle<ArrayBufferObject*> buffer, void *newData) { 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(newData); // Notify compiled jit code that the base pointer has moved. MarkObjectStateChange(cx, view); } if (newData != buffer->dataPointer()) buffer->setNewOwnedData(cx->runtime()->defaultFreeOp(), newData); 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<bool> 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); } }