//------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- #include "RuntimeLibraryPch.h" namespace Js { bool DelayedFreeArrayBuffer::HasAnyItem() { return !this->listOfBuffers.Empty(); } void DelayedFreeArrayBuffer::Push(ArrayBufferContentForDelayedFreeBase* item) { AssertOrFailFast(item); this->listOfBuffers.Push(item); } void Js::DelayedFreeArrayBuffer::ResetToNoMarkObject() { this->listOfBuffers.Map([](Js::ArrayBufferContentForDelayedFreeBase* item) { Assert(item != nullptr); item->SetMarkBit(false); }); } void DelayedFreeArrayBuffer::ReleaseOnlyNonMarkedObject() { FOREACH_SLIST_ENTRY_EDITING(ArrayBufferContentForDelayedFreeBase*, item, &this->listOfBuffers, iter) { if (!item->IsMarked()) { item->Release(); item->ClearSelfOnly(); iter.RemoveCurrent(); } else { // Reset the mark bit item->SetMarkBit(false); } } NEXT_SLIST_ENTRY_EDITING } void DelayedFreeArrayBuffer::CheckAndMarkObject(void * candidate) { this->listOfBuffers.Map([&](Js::ArrayBufferContentForDelayedFreeBase* item) { if (!item->IsMarked() && item->IsAddressPartOfBuffer(candidate)) { item->SetMarkBit(true); } }); } void DelayedFreeArrayBuffer::ClearAll() { if (HasAnyItem()) { this->listOfBuffers.Map([](Js::ArrayBufferContentForDelayedFreeBase* item) { item->Release(); item->ClearSelfOnly(); }); this->listOfBuffers.Clear(); } } void DelayedFreeArrayBuffer::ScanStack(void ** stackTop, size_t byteCount, void ** registers, size_t registersByteCount) { AssertOrFailFast(HasAnyItem()); ResetToNoMarkObject(); auto BufferFreeFunction = [&](void ** obj, size_t byteCount) { Assert(byteCount != 0); Assert(byteCount % sizeof(void *) == 0); void ** objEnd = obj + (byteCount / sizeof(void *)); do { // We need to ensure that the compiler does not reintroduce reads to the object after inlining. // This could cause the value to change after the marking checks (e.g., the null/low address check). // Intrinsics avoid the expensive memory barrier on ARM (due to /volatile:ms). #if defined(_M_ARM64) void * candidate = reinterpret_cast(__iso_volatile_load64(reinterpret_cast(obj))); #elif defined(_M_ARM) void * candidate = reinterpret_cast(__iso_volatile_load32(reinterpret_cast(obj))); #else void * candidate = *(static_cast(obj)); #endif CheckAndMarkObject(candidate); obj++; } while (obj != objEnd); }; BufferFreeFunction(registers, registersByteCount); BufferFreeFunction(stackTop, byteCount); ReleaseOnlyNonMarkedObject(); } void ArrayBufferContentForDelayedFreeBase::Release() { // this function will be called when we are releasing instance from the listOfBuffer which we have delayed. RefCountedBuffer *content = this->buffer; this->buffer = nullptr; long refCount = content->Release(); if (refCount == 0) { FreeTheBuffer(content->GetBuffer()); HeapDelete(content); } } bool ArrayBufferContentForDelayedFreeBase::IsAddressPartOfBuffer(void *obj) { void *start = this->buffer->GetBuffer(); void *end = this->buffer->GetBuffer() + this->bufferLength; return start <= obj && obj < end; } }