#include "config.h"
#include "Subspace.h"
#include "JSCInlines.h"
#include "MarkedAllocatorInlines.h"
#include "MarkedBlockInlines.h"
#include "PreventCollectionScope.h"
#include "SubspaceInlines.h"
namespace JSC {
namespace {
struct DestroyFunc {
ALWAYS_INLINE void operator()(VM& vm, JSCell* cell) const
{
ASSERT(cell->structureID());
ASSERT(cell->inlineTypeFlags() & StructureIsImmortal);
Structure* structure = cell->structure(vm);
const ClassInfo* classInfo = structure->classInfo();
MethodTable::DestroyFunctionPtr destroy = classInfo->methodTable.destroy;
destroy(cell);
}
};
}
Subspace::Subspace(CString name, Heap& heap, AllocatorAttributes attributes)
: m_space(heap.objectSpace())
, m_name(name)
, m_attributes(attributes)
{
PreventCollectionScope preventCollectionScope(heap);
heap.objectSpace().m_subspaces.append(this);
for (size_t i = MarkedSpace::numSizeClasses; i--;)
m_allocatorForSizeStep[i] = nullptr;
}
Subspace::~Subspace()
{
}
void Subspace::finishSweep(MarkedBlock::Handle& block, FreeList* freeList)
{
block.finishSweepKnowingSubspace(freeList, DestroyFunc());
}
void Subspace::destroy(VM& vm, JSCell* cell)
{
DestroyFunc()(vm, cell);
}
void* Subspace::allocate(size_t size)
{
void* result;
if (MarkedAllocator* allocator = tryAllocatorFor(size))
result = allocator->allocate();
else
result = allocateSlow(nullptr, size);
didAllocate(result);
return result;
}
void* Subspace::allocate(GCDeferralContext* deferralContext, size_t size)
{
void *result;
if (MarkedAllocator* allocator = tryAllocatorFor(size))
result = allocator->allocate(deferralContext);
else
result = allocateSlow(deferralContext, size);
didAllocate(result);
return result;
}
void* Subspace::tryAllocate(size_t size)
{
void* result;
if (MarkedAllocator* allocator = tryAllocatorFor(size))
result = allocator->tryAllocate();
else
result = tryAllocateSlow(nullptr, size);
didAllocate(result);
return result;
}
void* Subspace::tryAllocate(GCDeferralContext* deferralContext, size_t size)
{
void* result;
if (MarkedAllocator* allocator = tryAllocatorFor(size))
result = allocator->tryAllocate(deferralContext);
else
result = tryAllocateSlow(deferralContext, size);
didAllocate(result);
return result;
}
MarkedAllocator* Subspace::allocatorForSlow(size_t size)
{
size_t index = MarkedSpace::sizeClassToIndex(size);
size_t sizeClass = MarkedSpace::s_sizeClassForSizeStep[index];
if (!sizeClass)
return nullptr;
auto locker = holdLock(m_space.allocatorLock());
if (MarkedAllocator* allocator = m_allocatorForSizeStep[index])
return allocator;
if (false)
dataLog("Creating marked allocator for ", m_name, ", ", m_attributes, ", ", sizeClass, ".\n");
MarkedAllocator* allocator = m_space.addMarkedAllocator(locker, this, sizeClass);
index = MarkedSpace::sizeClassToIndex(sizeClass);
for (;;) {
if (MarkedSpace::s_sizeClassForSizeStep[index] != sizeClass)
break;
m_allocatorForSizeStep[index] = allocator;
if (!index--)
break;
}
allocator->setNextAllocatorInSubspace(m_firstAllocator);
WTF::storeStoreFence();
m_firstAllocator = allocator;
return allocator;
}
void* Subspace::allocateSlow(GCDeferralContext* deferralContext, size_t size)
{
void* result = tryAllocateSlow(deferralContext, size);
RELEASE_ASSERT(result);
return result;
}
void* Subspace::tryAllocateSlow(GCDeferralContext* deferralContext, size_t size)
{
if (MarkedAllocator* allocator = allocatorFor(size))
return allocator->tryAllocate(deferralContext);
if (size <= Options::largeAllocationCutoff()
&& size <= MarkedSpace::largeCutoff) {
dataLog("FATAL: attampting to allocate small object using large allocation.\n");
dataLog("Requested allocation size: ", size, "\n");
RELEASE_ASSERT_NOT_REACHED();
}
m_space.heap()->collectIfNecessaryOrDefer(deferralContext);
size = WTF::roundUpToMultipleOf<MarkedSpace::sizeStep>(size);
LargeAllocation* allocation = LargeAllocation::tryCreate(*m_space.m_heap, size, this);
if (!allocation)
return nullptr;
m_space.m_largeAllocations.append(allocation);
m_space.m_heap->didAllocate(size);
m_space.m_capacity += size;
m_largeAllocations.append(allocation);
return allocation->cell();
}
ALWAYS_INLINE void Subspace::didAllocate(void* ptr)
{
UNUSED_PARAM(ptr);
}
}