This source file includes following definitions.
- m_num
- m_num
- m_num
- isHashTableDeletedValue
- m_safePointScope
- getHeapStats
- mark
- mark
- mark
- registerWeakMembers
- registerWeakCell
- isMarked
- FOR_EACH_TYPED_HEAP
- reset
- create
- trace
- getPayload
- virtualMethod
- create
- trace
- create
- at
- trace
- clearOutOldGarbage
- create
- trace
- value
- hash
- test
- m_threadsToFinish
- done
- threadFunc
- test
- runThread
- test
- runThread
- CheckWithSlack
- create
- trace
- traceCount
- create
- trace
- traceCount
- create
- trace
- create
- trace
- create
- finalizeGarbageCollectedObject
- hasBeenFinalized
- trace
- create
- trace
- clear
- willFinalize
- create
- create
- trace
- m_pointsToFoo
- m_pointsToFoo
- create
- trace
- getWidth
- create
- trace
- create
- get
- set
- length
- trace
- create
- ref
- deref
- trace
- create
- trace
- m_expectedObjects
- mark
- markNoTrace
- mark
- mark
- mark
- validate
- reset
- FOR_EACH_TYPED_HEAP
- create
- trace
- zapWeakMembers
- strongIsThere
- weakIsThere
- m_weakBar
- zapWeakMembers
- create
- trace
- strongIsThere
- weakIsThere
- m_weakBar
- create
- trace
- willFinalize
- m_wasDestructed
- create
- didCallWillFinalize
- trace
- m_didCallWillFinalize
- zapWeakMembers
- observe
- observers
- create
- setBackPointer
- backPointer
- trace
- create
- doStuff
- trace
- pointsBack
- trace
- create
- trace
- m_data
- create
- trace
- trace
- getPayload
- USING_GARBAGE_COLLECTED_MIXIN
- trace
- trace
- m_isLast
- trace
- isLastInArray
- setLastInArray
- payload
- data
- trace
- create
- get
- trace
- trace
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- create
- trace
- create
- trace
- trace
- TEST
- TEST
- TEST
- MapIteratorCheck
- SetIteratorCheck
- TEST
- value
- hash
- heapMapDestructorHelper
- TEST
- checkPairSets
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
#include "config.h"
#include "platform/heap/Handle.h"
#include "platform/heap/Heap.h"
#include "platform/heap/HeapLinkedStack.h"
#include "platform/heap/HeapTerminatedArrayBuilder.h"
#include "platform/heap/ThreadState.h"
#include "platform/heap/Visitor.h"
#include "wtf/HashTraits.h"
#include <gtest/gtest.h>
namespace WebCore {
class ThreadMarker {
public:
ThreadMarker() : m_creatingThread(reinterpret_cast<ThreadState*>(0)), m_num(0) { }
ThreadMarker(unsigned i) : m_creatingThread(ThreadState::current()), m_num(i) { }
ThreadMarker(WTF::HashTableDeletedValueType deleted) : m_creatingThread(reinterpret_cast<ThreadState*>(-1)), m_num(0) { }
~ThreadMarker()
{
EXPECT_TRUE((m_creatingThread == ThreadState::current())
|| (m_creatingThread == reinterpret_cast<ThreadState*>(0))
|| (m_creatingThread == reinterpret_cast<ThreadState*>(-1)));
}
bool isHashTableDeletedValue() const { return m_creatingThread == reinterpret_cast<ThreadState*>(-1); }
bool operator==(const ThreadMarker& other) const { return other.m_creatingThread == m_creatingThread && other.m_num == m_num; }
ThreadState* m_creatingThread;
unsigned m_num;
};
struct ThreadMarkerHash {
static unsigned hash(const ThreadMarker& key)
{
return static_cast<unsigned>(reinterpret_cast<uintptr_t>(key.m_creatingThread) + key.m_num);
}
static bool equal(const ThreadMarker& a, const ThreadMarker& b)
{
return a == b;
}
static const bool safeToCompareToEmptyOrDeleted = false;
};
}
namespace WTF {
template<typename T> struct DefaultHash;
template<> struct DefaultHash<WebCore::ThreadMarker> {
typedef WebCore::ThreadMarkerHash Hash;
};
template<> struct HashTraits<WebCore::ThreadMarker> : GenericHashTraits<WebCore::ThreadMarker> {
static const bool emptyValueIsZero = true;
static void constructDeletedValue(WebCore::ThreadMarker& slot) { new (NotNull, &slot) WebCore::ThreadMarker(HashTableDeletedValue); }
static bool isDeletedValue(const WebCore::ThreadMarker& slot) { return slot.isHashTableDeletedValue(); }
};
}
namespace WebCore {
class TestGCScope {
public:
explicit TestGCScope(ThreadState::StackState state)
: m_state(ThreadState::current())
, m_safePointScope(state)
{
m_state->checkThread();
ASSERT(!m_state->isInGC());
ThreadState::stopThreads();
m_state->enterGC();
}
~TestGCScope()
{
m_state->leaveGC();
ASSERT(!m_state->isInGC());
ThreadState::resumeThreads();
}
private:
ThreadState* m_state;
ThreadState::SafePointScope m_safePointScope;
};
static void getHeapStats(HeapStats* stats)
{
TestGCScope scope(ThreadState::NoHeapPointersOnStack);
Heap::getStats(stats);
}
#define DEFINE_VISITOR_METHODS(Type) \
virtual void mark(const Type* object, TraceCallback callback) OVERRIDE \
{ \
if (object) \
m_count++; \
} \
virtual bool isMarked(const Type*) OVERRIDE { return false; }
class CountingVisitor : public Visitor {
public:
CountingVisitor()
: m_count(0)
{
}
virtual void mark(const void* object, TraceCallback) OVERRIDE
{
if (object)
m_count++;
}
virtual void mark(HeapObjectHeader* header, TraceCallback callback) OVERRIDE
{
ASSERT(header->payload());
m_count++;
}
virtual void mark(FinalizedHeapObjectHeader* header, TraceCallback callback) OVERRIDE
{
ASSERT(header->payload());
m_count++;
}
virtual void registerWeakMembers(const void*, const void*, WeakPointerCallback) OVERRIDE { }
virtual void registerWeakCell(void**, WeakPointerCallback) OVERRIDE { }
virtual bool isMarked(const void*) OVERRIDE { return false; }
FOR_EACH_TYPED_HEAP(DEFINE_VISITOR_METHODS)
size_t count() { return m_count; }
void reset() { m_count = 0; }
private:
size_t m_count;
};
class SimpleObject : public GarbageCollected<SimpleObject> {
public:
static SimpleObject* create() { return new SimpleObject(); }
void trace(Visitor*) { }
char getPayload(int i) { return payload[i]; }
virtual void virtualMethod() { }
protected:
SimpleObject() { }
char payload[64];
};
#undef DEFINE_VISITOR_METHODS
class HeapTestSuperClass : public GarbageCollectedFinalized<HeapTestSuperClass> {
public:
static HeapTestSuperClass* create()
{
return new HeapTestSuperClass();
}
virtual ~HeapTestSuperClass()
{
++s_destructorCalls;
}
static int s_destructorCalls;
void trace(Visitor*) { }
protected:
HeapTestSuperClass() { }
};
int HeapTestSuperClass::s_destructorCalls = 0;
class HeapTestOtherSuperClass {
public:
int payload;
};
static const size_t classMagic = 0xABCDDBCA;
class HeapTestSubClass : public HeapTestOtherSuperClass, public HeapTestSuperClass {
public:
static HeapTestSubClass* create()
{
return new HeapTestSubClass();
}
virtual ~HeapTestSubClass()
{
EXPECT_EQ(classMagic, m_magic);
++s_destructorCalls;
}
static int s_destructorCalls;
private:
HeapTestSubClass() : m_magic(classMagic) { }
const size_t m_magic;
};
int HeapTestSubClass::s_destructorCalls = 0;
class HeapAllocatedArray : public GarbageCollected<HeapAllocatedArray> {
public:
HeapAllocatedArray()
{
for (int i = 0; i < s_arraySize; ++i) {
m_array[i] = i % 128;
}
}
int8_t at(size_t i) { return m_array[i]; }
void trace(Visitor*) { }
private:
static const int s_arraySize = 1000;
int8_t m_array[s_arraySize];
};
static void clearOutOldGarbage(HeapStats* heapStats)
{
while (true) {
getHeapStats(heapStats);
size_t used = heapStats->totalObjectSpace();
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
getHeapStats(heapStats);
if (heapStats->totalObjectSpace() >= used)
break;
}
}
class IntWrapper : public GarbageCollectedFinalized<IntWrapper> {
public:
static IntWrapper* create(int x)
{
return new IntWrapper(x);
}
virtual ~IntWrapper()
{
++s_destructorCalls;
}
static int s_destructorCalls;
static void trace(Visitor*) { }
int value() const { return m_x; }
bool operator==(const IntWrapper& other) const { return other.value() == value(); }
unsigned hash() { return IntHash<int>::hash(m_x); }
protected:
IntWrapper(int x) : m_x(x) { }
private:
IntWrapper();
int m_x;
};
USED_FROM_MULTIPLE_THREADS(IntWrapper);
int IntWrapper::s_destructorCalls = 0;
class ThreadedTesterBase {
protected:
static void test(ThreadedTesterBase* tester)
{
for (int i = 0; i < numberOfThreads; i++)
createThread(&threadFunc, tester, "testing thread");
while (tester->m_threadsToFinish) {
ThreadState::current()->safePoint(ThreadState::NoHeapPointersOnStack);
yield();
}
delete tester;
}
virtual void runThread() = 0;
protected:
static const int numberOfThreads = 10;
static const int gcPerThread = 5;
static const int numberOfAllocations = 50;
ThreadedTesterBase() : m_gcCount(0), m_threadsToFinish(numberOfThreads)
{
}
virtual ~ThreadedTesterBase()
{
}
inline bool done() const { return m_gcCount >= numberOfThreads * gcPerThread; }
volatile int m_gcCount;
volatile int m_threadsToFinish;
private:
static void threadFunc(void* data)
{
reinterpret_cast<ThreadedTesterBase*>(data)->runThread();
}
};
class ThreadedHeapTester : public ThreadedTesterBase {
public:
static void test()
{
ThreadedTesterBase::test(new ThreadedHeapTester);
}
protected:
virtual void runThread() OVERRIDE
{
ThreadState::attach();
int gcCount = 0;
while (!done()) {
ThreadState::current()->safePoint(ThreadState::NoHeapPointersOnStack);
{
Persistent<IntWrapper> wrapper;
typedef Persistent<IntWrapper, GlobalPersistents> GlobalIntWrapperPersistent;
OwnPtr<GlobalIntWrapperPersistent> globalPersistent = adoptPtr(new GlobalIntWrapperPersistent(IntWrapper::create(0x0ed0cabb)));
for (int i = 0; i < numberOfAllocations; i++) {
wrapper = IntWrapper::create(0x0bbac0de);
if (!(i % 10)) {
globalPersistent = adoptPtr(new GlobalIntWrapperPersistent(IntWrapper::create(0x0ed0cabb)));
ThreadState::current()->safePoint(ThreadState::NoHeapPointersOnStack);
}
yield();
}
if (gcCount < gcPerThread) {
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
gcCount++;
atomicIncrement(&m_gcCount);
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(wrapper->value(), 0x0bbac0de);
EXPECT_EQ((*globalPersistent)->value(), 0x0ed0cabb);
}
yield();
}
ThreadState::detach();
atomicDecrement(&m_threadsToFinish);
}
};
class ThreadedWeaknessTester : public ThreadedTesterBase {
public:
static void test()
{
ThreadedTesterBase::test(new ThreadedWeaknessTester);
}
private:
virtual void runThread() OVERRIDE
{
ThreadState::attach();
int gcCount = 0;
while (!done()) {
ThreadState::current()->safePoint(ThreadState::NoHeapPointersOnStack);
{
Persistent<HeapHashMap<ThreadMarker, WeakMember<IntWrapper> > > weakMap = new HeapHashMap<ThreadMarker, WeakMember<IntWrapper> >;
PersistentHeapHashMap<ThreadMarker, WeakMember<IntWrapper> > weakMap2;
for (int i = 0; i < numberOfAllocations; i++) {
weakMap->add(static_cast<unsigned>(i), IntWrapper::create(0));
weakMap2.add(static_cast<unsigned>(i), IntWrapper::create(0));
if (!(i % 10)) {
ThreadState::current()->safePoint(ThreadState::NoHeapPointersOnStack);
}
yield();
}
if (gcCount < gcPerThread) {
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
gcCount++;
atomicIncrement(&m_gcCount);
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_TRUE(weakMap->isEmpty());
EXPECT_TRUE(weakMap2.isEmpty());
}
yield();
}
ThreadState::detach();
atomicDecrement(&m_threadsToFinish);
}
};
template<typename T>
void CheckWithSlack(T expected, T actual, int slack)
{
EXPECT_LE(expected, actual);
EXPECT_GE((intptr_t)expected + slack, (intptr_t)actual);
}
class TraceCounter : public GarbageCollectedFinalized<TraceCounter> {
public:
static TraceCounter* create()
{
return new TraceCounter();
}
void trace(Visitor*) { m_traceCount++; }
int traceCount() { return m_traceCount; }
private:
TraceCounter()
: m_traceCount(0)
{
}
int m_traceCount;
};
class ClassWithMember : public GarbageCollected<ClassWithMember> {
public:
static ClassWithMember* create()
{
return new ClassWithMember();
}
void trace(Visitor* visitor)
{
EXPECT_TRUE(visitor->isMarked(this));
if (!traceCount())
EXPECT_FALSE(visitor->isMarked(m_traceCounter));
else
EXPECT_TRUE(visitor->isMarked(m_traceCounter));
visitor->trace(m_traceCounter);
}
int traceCount() { return m_traceCounter->traceCount(); }
private:
ClassWithMember()
: m_traceCounter(TraceCounter::create())
{ }
Member<TraceCounter> m_traceCounter;
};
class SimpleFinalizedObject : public GarbageCollectedFinalized<SimpleFinalizedObject> {
public:
static SimpleFinalizedObject* create()
{
return new SimpleFinalizedObject();
}
~SimpleFinalizedObject()
{
++s_destructorCalls;
}
static int s_destructorCalls;
void trace(Visitor*) { }
private:
SimpleFinalizedObject() { }
};
int SimpleFinalizedObject::s_destructorCalls = 0;
class TestTypedHeapClass : public GarbageCollected<TestTypedHeapClass> {
public:
static TestTypedHeapClass* create()
{
return new TestTypedHeapClass();
}
void trace(Visitor*) { }
private:
TestTypedHeapClass() { }
};
class Bar : public GarbageCollectedFinalized<Bar> {
public:
static Bar* create()
{
return new Bar();
}
void finalizeGarbageCollectedObject()
{
EXPECT_TRUE(m_magic == magic);
m_magic = 0;
s_live--;
}
bool hasBeenFinalized() const { return !m_magic; }
virtual void trace(Visitor* visitor) { }
static unsigned s_live;
protected:
static const int magic = 1337;
int m_magic;
Bar()
: m_magic(magic)
{
s_live++;
}
};
unsigned Bar::s_live = 0;
class Baz : public GarbageCollected<Baz> {
public:
static Baz* create(Bar* bar)
{
return new Baz(bar);
}
void trace(Visitor* visitor)
{
visitor->trace(m_bar);
}
void clear() { m_bar.release(); }
void willFinalize()
{
EXPECT_TRUE(!m_bar->hasBeenFinalized());
}
private:
explicit Baz(Bar* bar)
: m_bar(bar)
{
}
Member<Bar> m_bar;
};
class Foo : public Bar {
public:
static Foo* create(Bar* bar)
{
return new Foo(bar);
}
static Foo* create(Foo* foo)
{
return new Foo(foo);
}
virtual void trace(Visitor* visitor) OVERRIDE
{
if (m_pointsToFoo)
visitor->mark(static_cast<Foo*>(m_bar));
else
visitor->mark(m_bar);
}
private:
Foo(Bar* bar)
: Bar()
, m_bar(bar)
, m_pointsToFoo(false)
{
}
Foo(Foo* foo)
: Bar()
, m_bar(foo)
, m_pointsToFoo(true)
{
}
Bar* m_bar;
bool m_pointsToFoo;
};
class Bars : public Bar {
public:
static Bars* create()
{
return new Bars();
}
virtual void trace(Visitor* visitor) OVERRIDE
{
for (unsigned i = 0; i < m_width; i++)
visitor->trace(m_bars[i]);
}
unsigned getWidth() const
{
return m_width;
}
static const unsigned width = 7500;
private:
Bars() : m_width(0)
{
for (unsigned i = 0; i < width; i++) {
m_bars[i] = Bar::create();
m_width++;
}
}
unsigned m_width;
Member<Bar> m_bars[width];
};
class ConstructorAllocation : public GarbageCollected<ConstructorAllocation> {
public:
static ConstructorAllocation* create() { return new ConstructorAllocation(); }
void trace(Visitor* visitor) { visitor->trace(m_intWrapper); }
private:
ConstructorAllocation()
{
m_intWrapper = IntWrapper::create(42);
}
Member<IntWrapper> m_intWrapper;
};
class LargeObject : public GarbageCollectedFinalized<LargeObject> {
public:
~LargeObject()
{
s_destructorCalls++;
}
static LargeObject* create() { return new LargeObject(); }
char get(size_t i) { return m_data[i]; }
void set(size_t i, char c) { m_data[i] = c; }
size_t length() { return s_length; }
void trace(Visitor* visitor)
{
visitor->trace(m_intWrapper);
}
static int s_destructorCalls;
private:
static const size_t s_length = 1024*1024;
LargeObject()
{
m_intWrapper = IntWrapper::create(23);
}
Member<IntWrapper> m_intWrapper;
char m_data[s_length];
};
int LargeObject::s_destructorCalls = 0;
class RefCountedAndGarbageCollected : public RefCountedGarbageCollected<RefCountedAndGarbageCollected> {
public:
static PassRefPtr<RefCountedAndGarbageCollected> create()
{
return adoptRef(new RefCountedAndGarbageCollected());
}
~RefCountedAndGarbageCollected()
{
++s_destructorCalls;
}
void ref() { RefCountedGarbageCollected<RefCountedAndGarbageCollected>::ref(); }
void deref() { RefCountedGarbageCollected<RefCountedAndGarbageCollected>::deref(); }
void trace(Visitor*) { }
static int s_destructorCalls;
private:
RefCountedAndGarbageCollected()
{
}
};
int RefCountedAndGarbageCollected::s_destructorCalls = 0;
class RefCountedAndGarbageCollected2 : public HeapTestOtherSuperClass, public RefCountedGarbageCollected<RefCountedAndGarbageCollected2> {
public:
static RefCountedAndGarbageCollected2* create()
{
return adoptRefCountedGarbageCollected(new RefCountedAndGarbageCollected2());
}
~RefCountedAndGarbageCollected2()
{
++s_destructorCalls;
}
void trace(Visitor*) { }
static int s_destructorCalls;
private:
RefCountedAndGarbageCollected2()
{
}
};
int RefCountedAndGarbageCollected2::s_destructorCalls = 0;
#define DEFINE_VISITOR_METHODS(Type) \
virtual void mark(const Type* object, TraceCallback callback) OVERRIDE \
{ \
mark(object); \
} \
class RefCountedGarbageCollectedVisitor : public CountingVisitor {
public:
RefCountedGarbageCollectedVisitor(int expected, void** objects)
: m_count(0)
, m_expectedCount(expected)
, m_expectedObjects(objects)
{
}
void mark(const void* ptr) { markNoTrace(ptr); }
virtual void markNoTrace(const void* ptr)
{
if (!ptr)
return;
if (m_count < m_expectedCount)
EXPECT_TRUE(expectedObject(ptr));
else
EXPECT_FALSE(expectedObject(ptr));
m_count++;
}
virtual void mark(const void* ptr, TraceCallback) OVERRIDE
{
mark(ptr);
}
virtual void mark(HeapObjectHeader* header, TraceCallback callback) OVERRIDE
{
mark(header->payload());
}
virtual void mark(FinalizedHeapObjectHeader* header, TraceCallback callback) OVERRIDE
{
mark(header->payload());
}
bool validate() { return m_count >= m_expectedCount; }
void reset() { m_count = 0; }
FOR_EACH_TYPED_HEAP(DEFINE_VISITOR_METHODS)
private:
bool expectedObject(const void* ptr)
{
for (int i = 0; i < m_expectedCount; i++) {
if (m_expectedObjects[i] == ptr)
return true;
}
return false;
}
int m_count;
int m_expectedCount;
void** m_expectedObjects;
};
#undef DEFINE_VISITOR_METHODS
class Weak : public Bar {
public:
static Weak* create(Bar* strong, Bar* weak)
{
return new Weak(strong, weak);
}
virtual void trace(Visitor* visitor) OVERRIDE
{
visitor->trace(m_strongBar);
visitor->registerWeakMembers(this, zapWeakMembers);
}
static void zapWeakMembers(Visitor* visitor, void* self)
{
reinterpret_cast<Weak*>(self)->zapWeakMembers(visitor);
}
bool strongIsThere() { return !!m_strongBar; }
bool weakIsThere() { return !!m_weakBar; }
private:
Weak(Bar* strongBar, Bar* weakBar)
: Bar()
, m_strongBar(strongBar)
, m_weakBar(weakBar)
{
}
void zapWeakMembers(Visitor* visitor)
{
if (m_weakBar && !visitor->isAlive(m_weakBar))
m_weakBar = 0;
}
Member<Bar> m_strongBar;
Bar* m_weakBar;
};
class WithWeakMember : public Bar {
public:
static WithWeakMember* create(Bar* strong, Bar* weak)
{
return new WithWeakMember(strong, weak);
}
virtual void trace(Visitor* visitor) OVERRIDE
{
visitor->trace(m_strongBar);
visitor->trace(m_weakBar);
}
bool strongIsThere() { return !!m_strongBar; }
bool weakIsThere() { return !!m_weakBar; }
private:
WithWeakMember(Bar* strongBar, Bar* weakBar)
: Bar()
, m_strongBar(strongBar)
, m_weakBar(weakBar)
{
}
Member<Bar> m_strongBar;
WeakMember<Bar> m_weakBar;
};
class Observable : public GarbageCollectedFinalized<Observable> {
public:
static Observable* create(Bar* bar) { return new Observable(bar); }
~Observable() { m_wasDestructed = true; }
void trace(Visitor* visitor) { visitor->trace(m_bar); }
void willFinalize()
{
EXPECT_FALSE(m_wasDestructed);
EXPECT_FALSE(m_bar->hasBeenFinalized());
}
private:
explicit Observable(Bar* bar)
: m_bar(bar)
, m_wasDestructed(false)
{
}
Member<Bar> m_bar;
bool m_wasDestructed;
};
template <typename T> class FinalizationObserver : public GarbageCollected<FinalizationObserver<T> > {
public:
static FinalizationObserver* create(T* data) { return new FinalizationObserver(data); }
bool didCallWillFinalize() const { return m_didCallWillFinalize; }
void trace(Visitor* visitor)
{
visitor->registerWeakMembers(this, zapWeakMembers);
}
private:
FinalizationObserver(T* data)
: m_data(data)
, m_didCallWillFinalize(false)
{
}
static void zapWeakMembers(Visitor* visitor, void* self)
{
FinalizationObserver* o = reinterpret_cast<FinalizationObserver*>(self);
if (o->m_data && !visitor->isAlive(o->m_data)) {
o->m_data->willFinalize();
o->m_data = nullptr;
o->m_didCallWillFinalize = true;
}
}
WeakMember<T> m_data;
bool m_didCallWillFinalize;
};
class FinalizationObserverWithHashMap {
public:
typedef HeapHashMap<WeakMember<Observable>, OwnPtr<FinalizationObserverWithHashMap> > ObserverMap;
explicit FinalizationObserverWithHashMap(Observable& target) : m_target(target) { }
~FinalizationObserverWithHashMap()
{
m_target.willFinalize();
s_didCallWillFinalize = true;
}
static ObserverMap& observe(Observable& target)
{
ObserverMap& map = observers();
ObserverMap::AddResult result = map.add(&target, nullptr);
if (result.isNewEntry)
result.storedValue->value = adoptPtr(new FinalizationObserverWithHashMap(target));
else
ASSERT(result.storedValue->value);
return map;
}
static bool s_didCallWillFinalize;
private:
static ObserverMap& observers()
{
DEFINE_STATIC_LOCAL(Persistent<ObserverMap>, observerMap, ());
if (!observerMap)
observerMap = new ObserverMap();
return *observerMap;
}
Observable& m_target;
};
bool FinalizationObserverWithHashMap::s_didCallWillFinalize = false;
class SuperClass;
class PointsBack : public RefCountedWillBeGarbageCollectedFinalized<PointsBack> {
public:
static PassRefPtrWillBeRawPtr<PointsBack> create()
{
return adoptRefWillBeNoop(new PointsBack());
}
~PointsBack()
{
--s_aliveCount;
}
void setBackPointer(SuperClass* backPointer)
{
m_backPointer = backPointer;
}
SuperClass* backPointer() const { return m_backPointer; }
void trace(Visitor* visitor)
{
#if ENABLE_OILPAN
visitor->trace(m_backPointer);
#endif
}
static int s_aliveCount;
private:
PointsBack() : m_backPointer(nullptr)
{
++s_aliveCount;
}
RawPtrWillBeWeakMember<SuperClass> m_backPointer;
};
int PointsBack::s_aliveCount = 0;
class SuperClass : public RefCountedWillBeGarbageCollectedFinalized<SuperClass> {
public:
static PassRefPtrWillBeRawPtr<SuperClass> create(PassRefPtrWillBeRawPtr<PointsBack> pointsBack)
{
return adoptRefWillBeNoop(new SuperClass(pointsBack));
}
virtual ~SuperClass()
{
#if !ENABLE_OILPAN
m_pointsBack->setBackPointer(0);
#endif
--s_aliveCount;
}
void doStuff(PassRefPtrWillBeRawPtr<SuperClass> targetPass, PointsBack* pointsBack, int superClassCount)
{
RefPtrWillBeRawPtr<SuperClass> target = targetPass;
Heap::collectGarbage(ThreadState::HeapPointersOnStack);
EXPECT_EQ(pointsBack, target->pointsBack());
EXPECT_EQ(superClassCount, SuperClass::s_aliveCount);
}
virtual void trace(Visitor* visitor)
{
#if ENABLE_OILPAN
visitor->trace(m_pointsBack);
#endif
}
PointsBack* pointsBack() const { return m_pointsBack.get(); }
static int s_aliveCount;
protected:
explicit SuperClass(PassRefPtrWillBeRawPtr<PointsBack> pointsBack)
: m_pointsBack(pointsBack)
{
m_pointsBack->setBackPointer(this);
++s_aliveCount;
}
private:
RefPtrWillBeMember<PointsBack> m_pointsBack;
};
int SuperClass::s_aliveCount = 0;
class SubData : public NoBaseWillBeGarbageCollectedFinalized<SubData> {
public:
SubData() { ++s_aliveCount; }
~SubData() { --s_aliveCount; }
void trace(Visitor*) { }
static int s_aliveCount;
};
int SubData::s_aliveCount = 0;
class SubClass : public SuperClass {
public:
static PassRefPtrWillBeRawPtr<SubClass> create(PassRefPtrWillBeRawPtr<PointsBack> pointsBack)
{
return adoptRefWillBeNoop(new SubClass(pointsBack));
}
virtual ~SubClass()
{
--s_aliveCount;
}
virtual void trace(Visitor* visitor)
{
#if ENABLE_OILPAN
SuperClass::trace(visitor);
visitor->trace(m_data);
#endif
}
static int s_aliveCount;
private:
explicit SubClass(PassRefPtrWillBeRawPtr<PointsBack> pointsBack)
: SuperClass(pointsBack)
, m_data(adoptPtrWillBeNoop(new SubData()))
{
++s_aliveCount;
}
private:
OwnPtrWillBeMember<SubData> m_data;
};
int SubClass::s_aliveCount = 0;
class TransitionRefCounted : public RefCountedWillBeRefCountedGarbageCollected<TransitionRefCounted> {
public:
static PassRefPtrWillBeRawPtr<TransitionRefCounted> create()
{
return adoptRefWillBeRefCountedGarbageCollected(new TransitionRefCounted());
}
~TransitionRefCounted()
{
--s_aliveCount;
}
void trace(Visitor* visitor) { }
static int s_aliveCount;
private:
TransitionRefCounted()
{
++s_aliveCount;
}
};
int TransitionRefCounted::s_aliveCount = 0;
class Mixin : public GarbageCollectedMixin {
public:
virtual void trace(Visitor* visitor) { }
virtual char getPayload(int i) { return m_padding[i]; }
protected:
int m_padding[8];
};
class UseMixin : public SimpleObject, public Mixin {
USING_GARBAGE_COLLECTED_MIXIN(UseMixin)
public:
static UseMixin* create()
{
return new UseMixin();
}
static int s_traceCount;
virtual void trace(Visitor* visitor)
{
SimpleObject::trace(visitor);
Mixin::trace(visitor);
++s_traceCount;
}
private:
UseMixin()
{
s_traceCount = 0;
}
};
int UseMixin::s_traceCount = 0;
class VectorObject {
ALLOW_ONLY_INLINE_ALLOCATION();
public:
VectorObject()
{
m_value = SimpleFinalizedObject::create();
}
void trace(Visitor* visitor)
{
visitor->trace(m_value);
}
private:
Member<SimpleFinalizedObject> m_value;
};
class VectorObjectInheritedTrace : public VectorObject { };
class VectorObjectNoTrace {
ALLOW_ONLY_INLINE_ALLOCATION();
public:
VectorObjectNoTrace()
{
m_value = SimpleFinalizedObject::create();
}
private:
Member<SimpleFinalizedObject> m_value;
};
class TerminatedArrayItem {
ALLOW_ONLY_INLINE_ALLOCATION();
public:
TerminatedArrayItem(IntWrapper* payload) : m_payload(payload), m_isLast(false) { }
void trace(Visitor* visitor) { visitor->trace(m_payload); }
bool isLastInArray() const { return m_isLast; }
void setLastInArray(bool value) { m_isLast = value; }
IntWrapper* payload() const { return m_payload; }
private:
Member<IntWrapper> m_payload;
bool m_isLast;
};
}
namespace WTF {
template<> struct VectorTraits<WebCore::VectorObject> : public SimpleClassVectorTraits<WebCore::VectorObject> { };
template<> struct VectorTraits<WebCore::VectorObjectInheritedTrace> : public SimpleClassVectorTraits<WebCore::VectorObjectInheritedTrace> { };
template<> struct VectorTraits<WebCore::VectorObjectNoTrace> : public SimpleClassVectorTraits<WebCore::VectorObjectNoTrace> { };
}
namespace WebCore {
class OneKiloByteObject : public GarbageCollectedFinalized<OneKiloByteObject> {
public:
~OneKiloByteObject() { s_destructorCalls++; }
char* data() { return m_data; }
void trace(Visitor* visitor) { }
static int s_destructorCalls;
private:
static const size_t s_length = 1024;
char m_data[s_length];
};
int OneKiloByteObject::s_destructorCalls = 0;
class DynamicallySizedObject : public GarbageCollected<DynamicallySizedObject> {
public:
static DynamicallySizedObject* create(size_t size)
{
void* slot = Heap::allocate<DynamicallySizedObject>(size);
return new (slot) DynamicallySizedObject();
}
void* operator new(std::size_t, void* location)
{
return location;
}
uint8_t get(int i)
{
return *(reinterpret_cast<uint8_t*>(this) + i);
}
void trace(Visitor*) { }
private:
DynamicallySizedObject() { }
};
class FinalizationAllocator : public GarbageCollectedFinalized<FinalizationAllocator> {
public:
FinalizationAllocator(Persistent<IntWrapper>* wrapper)
: m_wrapper(wrapper)
{
}
~FinalizationAllocator()
{
for (int i = 0; i < 10; ++i)
*m_wrapper = IntWrapper::create(42);
for (int i = 0; i < 512; ++i)
new OneKiloByteObject();
}
void trace(Visitor*) { }
private:
Persistent<IntWrapper>* m_wrapper;
};
TEST(HeapTest, Transition)
{
{
RefPtr<TransitionRefCounted> refCounted = TransitionRefCounted::create();
EXPECT_EQ(1, TransitionRefCounted::s_aliveCount);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(1, TransitionRefCounted::s_aliveCount);
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(0, TransitionRefCounted::s_aliveCount);
RefPtrWillBePersistent<PointsBack> pointsBack1 = PointsBack::create();
RefPtrWillBePersistent<PointsBack> pointsBack2 = PointsBack::create();
RefPtrWillBePersistent<SuperClass> superClass = SuperClass::create(pointsBack1);
RefPtrWillBePersistent<SubClass> subClass = SubClass::create(pointsBack2);
EXPECT_EQ(2, PointsBack::s_aliveCount);
EXPECT_EQ(2, SuperClass::s_aliveCount);
EXPECT_EQ(1, SubClass::s_aliveCount);
EXPECT_EQ(1, SubData::s_aliveCount);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(0, TransitionRefCounted::s_aliveCount);
EXPECT_EQ(2, PointsBack::s_aliveCount);
EXPECT_EQ(2, SuperClass::s_aliveCount);
EXPECT_EQ(1, SubClass::s_aliveCount);
EXPECT_EQ(1, SubData::s_aliveCount);
superClass->doStuff(superClass.release(), pointsBack1.get(), 2);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(2, PointsBack::s_aliveCount);
EXPECT_EQ(1, SuperClass::s_aliveCount);
EXPECT_EQ(1, SubClass::s_aliveCount);
EXPECT_EQ(1, SubData::s_aliveCount);
EXPECT_EQ(0, pointsBack1->backPointer());
pointsBack1.release();
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(1, PointsBack::s_aliveCount);
EXPECT_EQ(1, SuperClass::s_aliveCount);
EXPECT_EQ(1, SubClass::s_aliveCount);
EXPECT_EQ(1, SubData::s_aliveCount);
subClass->doStuff(subClass.release(), pointsBack2.get(), 1);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(1, PointsBack::s_aliveCount);
EXPECT_EQ(0, SuperClass::s_aliveCount);
EXPECT_EQ(0, SubClass::s_aliveCount);
EXPECT_EQ(0, SubData::s_aliveCount);
EXPECT_EQ(0, pointsBack2->backPointer());
pointsBack2.release();
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(0, PointsBack::s_aliveCount);
EXPECT_EQ(0, SuperClass::s_aliveCount);
EXPECT_EQ(0, SubClass::s_aliveCount);
EXPECT_EQ(0, SubData::s_aliveCount);
EXPECT_TRUE(superClass == subClass);
}
TEST(HeapTest, Threading)
{
ThreadedHeapTester::test();
}
TEST(HeapTest, ThreadedWeakness)
{
ThreadedWeaknessTester::test();
}
TEST(HeapTest, BasicFunctionality)
{
HeapStats heapStats;
clearOutOldGarbage(&heapStats);
{
size_t slack = 0;
size_t baseLevel = heapStats.totalObjectSpace();
bool testPagesAllocated = !baseLevel;
if (testPagesAllocated)
EXPECT_EQ(heapStats.totalAllocatedSpace(), 0ul);
DynamicallySizedObject* alloc32 = DynamicallySizedObject::create(32);
slack += 4;
memset(alloc32, 40, 32);
DynamicallySizedObject* alloc64 = DynamicallySizedObject::create(64);
slack += 4;
memset(alloc64, 27, 64);
size_t total = 96;
getHeapStats(&heapStats);
CheckWithSlack(baseLevel + total, heapStats.totalObjectSpace(), slack);
if (testPagesAllocated)
EXPECT_EQ(heapStats.totalAllocatedSpace(), blinkPageSize);
CheckWithSlack(alloc32 + 32 + sizeof(HeapObjectHeader), alloc64, slack);
EXPECT_EQ(alloc32->get(0), 40);
EXPECT_EQ(alloc32->get(31), 40);
EXPECT_EQ(alloc64->get(0), 27);
EXPECT_EQ(alloc64->get(63), 27);
Heap::collectGarbage(ThreadState::HeapPointersOnStack);
EXPECT_EQ(alloc32->get(0), 40);
EXPECT_EQ(alloc32->get(31), 40);
EXPECT_EQ(alloc64->get(0), 27);
EXPECT_EQ(alloc64->get(63), 27);
}
clearOutOldGarbage(&heapStats);
size_t total = 0;
size_t slack = 0;
size_t baseLevel = heapStats.totalObjectSpace();
bool testPagesAllocated = !baseLevel;
if (testPagesAllocated)
EXPECT_EQ(heapStats.totalAllocatedSpace(), 0ul);
size_t big = 1008;
Persistent<DynamicallySizedObject> bigArea = DynamicallySizedObject::create(big);
total += big;
slack += 4;
size_t persistentCount = 0;
const size_t numPersistents = 100000;
Persistent<DynamicallySizedObject>* persistents[numPersistents];
for (int i = 0; i < 1000; i++) {
size_t size = 128 + i * 8;
total += size;
persistents[persistentCount++] = new Persistent<DynamicallySizedObject>(DynamicallySizedObject::create(size));
slack += 4;
getHeapStats(&heapStats);
CheckWithSlack(baseLevel + total, heapStats.totalObjectSpace(), slack);
if (testPagesAllocated)
EXPECT_EQ(0ul, heapStats.totalAllocatedSpace() & (blinkPageSize - 1));
}
{
DynamicallySizedObject* alloc32b(DynamicallySizedObject::create(32));
slack += 4;
memset(alloc32b, 40, 32);
DynamicallySizedObject* alloc64b(DynamicallySizedObject::create(64));
slack += 4;
memset(alloc64b, 27, 64);
EXPECT_TRUE(alloc32b != alloc64b);
total += 96;
getHeapStats(&heapStats);
CheckWithSlack(baseLevel + total, heapStats.totalObjectSpace(), slack);
if (testPagesAllocated)
EXPECT_EQ(0ul, heapStats.totalAllocatedSpace() & (blinkPageSize - 1));
}
clearOutOldGarbage(&heapStats);
total -= 96;
slack -= 8;
if (testPagesAllocated)
EXPECT_EQ(0ul, heapStats.totalAllocatedSpace() & (blinkPageSize - 1));
DynamicallySizedObject* bigAreaRaw = bigArea;
bigArea.release();
clearOutOldGarbage(&heapStats);
total -= big;
slack -= 4;
getHeapStats(&heapStats);
CheckWithSlack(baseLevel + total, heapStats.totalObjectSpace(), slack);
if (testPagesAllocated)
EXPECT_EQ(0ul, heapStats.totalAllocatedSpace() & (blinkPageSize - 1));
while (true) {
Persistent<DynamicallySizedObject>* alloc = new Persistent<DynamicallySizedObject>(DynamicallySizedObject::create(big / 2));
slack += 4;
persistents[persistentCount++] = alloc;
EXPECT_LT(persistentCount, numPersistents);
total += big / 2;
if (bigAreaRaw == alloc->get())
break;
}
getHeapStats(&heapStats);
CheckWithSlack(baseLevel + total, heapStats.totalObjectSpace(), slack);
if (testPagesAllocated)
EXPECT_EQ(0ul, heapStats.totalAllocatedSpace() & (blinkPageSize - 1));
for (size_t i = 0; i < persistentCount; i++) {
delete persistents[i];
persistents[i] = 0;
}
uint8_t* address = reinterpret_cast<uint8_t*>(Heap::reallocate<DynamicallySizedObject>(0, 100));
for (int i = 0; i < 100; i++)
address[i] = i;
address = reinterpret_cast<uint8_t*>(Heap::reallocate<DynamicallySizedObject>(address, 100000));
for (int i = 0; i < 100; i++)
EXPECT_EQ(address[i], i);
address = reinterpret_cast<uint8_t*>(Heap::reallocate<DynamicallySizedObject>(address, 50));
for (int i = 0; i < 50; i++)
EXPECT_EQ(address[i], i);
EXPECT_EQ(reinterpret_cast<uintptr_t>(Heap::reallocate<DynamicallySizedObject>(address, 0)), 0ul);
EXPECT_EQ(reinterpret_cast<uintptr_t>(Heap::reallocate<DynamicallySizedObject>(0, 0)), 0ul);
}
TEST(HeapTest, SimpleAllocation)
{
HeapStats initialHeapStats;
clearOutOldGarbage(&initialHeapStats);
EXPECT_EQ(0ul, initialHeapStats.totalObjectSpace());
HeapAllocatedArray* array = new HeapAllocatedArray();
HeapStats statsAfterAllocation;
getHeapStats(&statsAfterAllocation);
EXPECT_TRUE(statsAfterAllocation.totalObjectSpace() >= sizeof(HeapAllocatedArray));
EXPECT_EQ(0, array->at(0));
EXPECT_EQ(42, array->at(42));
EXPECT_EQ(0, array->at(128));
EXPECT_EQ(999 % 128, array->at(999));
}
TEST(HeapTest, SimplePersistent)
{
Persistent<TraceCounter> traceCounter = TraceCounter::create();
EXPECT_EQ(0, traceCounter->traceCount());
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(1, traceCounter->traceCount());
Persistent<ClassWithMember> classWithMember = ClassWithMember::create();
EXPECT_EQ(0, classWithMember->traceCount());
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(1, classWithMember->traceCount());
EXPECT_EQ(2, traceCounter->traceCount());
}
TEST(HeapTest, SimpleFinalization)
{
{
Persistent<SimpleFinalizedObject> finalized = SimpleFinalizedObject::create();
EXPECT_EQ(0, SimpleFinalizedObject::s_destructorCalls);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(0, SimpleFinalizedObject::s_destructorCalls);
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(1, SimpleFinalizedObject::s_destructorCalls);
}
TEST(HeapTest, Finalization)
{
{
HeapTestSubClass* t1 = HeapTestSubClass::create();
HeapTestSubClass* t2 = HeapTestSubClass::create();
HeapTestSuperClass* t3 = HeapTestSuperClass::create();
(void)t1;
(void)t2;
(void)t3;
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(2, HeapTestSubClass::s_destructorCalls);
EXPECT_EQ(3, HeapTestSuperClass::s_destructorCalls);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(2, HeapTestSubClass::s_destructorCalls);
EXPECT_EQ(3, HeapTestSuperClass::s_destructorCalls);
}
TEST(HeapTest, TypedHeapSanity)
{
Persistent<TraceCounter> generalHeapObject = TraceCounter::create();
Persistent<TestTypedHeapClass> typedHeapObject = TestTypedHeapClass::create();
EXPECT_NE(pageHeaderAddress(reinterpret_cast<Address>(generalHeapObject.get())),
pageHeaderAddress(reinterpret_cast<Address>(typedHeapObject.get())));
}
TEST(HeapTest, NoAllocation)
{
EXPECT_TRUE(ThreadState::current()->isAllocationAllowed());
{
NoAllocationScope<AnyThread> noAllocationScope;
EXPECT_FALSE(ThreadState::current()->isAllocationAllowed());
}
EXPECT_TRUE(ThreadState::current()->isAllocationAllowed());
}
TEST(HeapTest, Members)
{
Bar::s_live = 0;
{
Persistent<Baz> h1;
Persistent<Baz> h2;
{
h1 = Baz::create(Bar::create());
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(1u, Bar::s_live);
h2 = Baz::create(Bar::create());
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(2u, Bar::s_live);
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(2u, Bar::s_live);
h1->clear();
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(1u, Bar::s_live);
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(0u, Bar::s_live);
}
TEST(HeapTest, MarkTest)
{
{
Bar::s_live = 0;
Persistent<Bar> bar = Bar::create();
EXPECT_TRUE(ThreadState::current()->contains(bar));
EXPECT_EQ(1u, Bar::s_live);
{
Foo* foo = Foo::create(bar);
EXPECT_TRUE(ThreadState::current()->contains(foo));
EXPECT_EQ(2u, Bar::s_live);
EXPECT_TRUE(reinterpret_cast<Address>(foo) != reinterpret_cast<Address>(bar.get()));
Heap::collectGarbage(ThreadState::HeapPointersOnStack);
EXPECT_TRUE(foo != bar);
EXPECT_EQ(2u, Bar::s_live);
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(1u, Bar::s_live);
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(0u, Bar::s_live);
}
TEST(HeapTest, DeepTest)
{
const unsigned depth = 100000;
Bar::s_live = 0;
{
Bar* bar = Bar::create();
EXPECT_TRUE(ThreadState::current()->contains(bar));
Foo* foo = Foo::create(bar);
EXPECT_TRUE(ThreadState::current()->contains(foo));
EXPECT_EQ(2u, Bar::s_live);
for (unsigned i = 0; i < depth; i++) {
Foo* foo2 = Foo::create(foo);
foo = foo2;
EXPECT_TRUE(ThreadState::current()->contains(foo));
}
EXPECT_EQ(depth + 2, Bar::s_live);
Heap::collectGarbage(ThreadState::HeapPointersOnStack);
EXPECT_TRUE(foo != bar);
EXPECT_EQ(depth + 2, Bar::s_live);
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(0u, Bar::s_live);
}
TEST(HeapTest, WideTest)
{
Bar::s_live = 0;
{
Bars* bars = Bars::create();
unsigned width = Bars::width;
EXPECT_EQ(width + 1, Bar::s_live);
Heap::collectGarbage(ThreadState::HeapPointersOnStack);
EXPECT_EQ(width + 1, Bar::s_live);
EXPECT_EQ(width, bars->getWidth());
}
EXPECT_EQ(Bars::width + 1, Bar::s_live);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(0u, Bar::s_live);
}
TEST(HeapTest, HashMapOfMembers)
{
HeapStats initialHeapSize;
IntWrapper::s_destructorCalls = 0;
clearOutOldGarbage(&initialHeapSize);
{
typedef HeapHashMap<
Member<IntWrapper>,
Member<IntWrapper>,
DefaultHash<Member<IntWrapper> >::Hash,
HashTraits<Member<IntWrapper> >,
HashTraits<Member<IntWrapper> > > HeapObjectIdentityMap;
Persistent<HeapObjectIdentityMap> map = new HeapObjectIdentityMap();
map->clear();
HeapStats afterSetWasCreated;
getHeapStats(&afterSetWasCreated);
EXPECT_TRUE(afterSetWasCreated.totalObjectSpace() > initialHeapSize.totalObjectSpace());
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
HeapStats afterGC;
getHeapStats(&afterGC);
EXPECT_EQ(afterGC.totalObjectSpace(), afterSetWasCreated.totalObjectSpace());
IntWrapper* one(IntWrapper::create(1));
IntWrapper* anotherOne(IntWrapper::create(1));
map->add(one, one);
HeapStats afterOneAdd;
getHeapStats(&afterOneAdd);
EXPECT_TRUE(afterOneAdd.totalObjectSpace() > afterGC.totalObjectSpace());
HeapObjectIdentityMap::iterator it(map->begin());
HeapObjectIdentityMap::iterator it2(map->begin());
++it;
++it2;
map->add(anotherOne, one);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
HeapStats afterAddAndGC;
getHeapStats(&afterAddAndGC);
EXPECT_TRUE(afterAddAndGC.totalObjectSpace() >= afterOneAdd.totalObjectSpace());
EXPECT_EQ(map->size(), 2u);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_TRUE(map->contains(one));
EXPECT_TRUE(map->contains(anotherOne));
IntWrapper* gotten(map->get(one));
EXPECT_EQ(gotten->value(), one->value());
EXPECT_EQ(gotten, one);
HeapStats afterGC2;
getHeapStats(&afterGC2);
EXPECT_EQ(afterGC2.totalObjectSpace(), afterAddAndGC.totalObjectSpace());
IntWrapper* dozen = 0;
for (int i = 1; i < 1000; i++) {
IntWrapper* iWrapper(IntWrapper::create(i));
IntWrapper* iSquared(IntWrapper::create(i * i));
map->add(iWrapper, iSquared);
if (i == 12)
dozen = iWrapper;
}
HeapStats afterAdding1000;
getHeapStats(&afterAdding1000);
EXPECT_TRUE(afterAdding1000.totalObjectSpace() > afterGC2.totalObjectSpace());
IntWrapper* gross(map->get(dozen));
EXPECT_EQ(gross->value(), 144);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
HeapStats afterGC3;
getHeapStats(&afterGC3);
EXPECT_TRUE(afterGC3.totalObjectSpace() < afterAdding1000.totalObjectSpace());
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(IntWrapper::s_destructorCalls, 2000);
HeapStats afterGC4;
getHeapStats(&afterGC4);
EXPECT_EQ(afterGC4.totalObjectSpace(), initialHeapSize.totalObjectSpace());
}
TEST(HeapTest, NestedAllocation)
{
HeapStats initialHeapSize;
clearOutOldGarbage(&initialHeapSize);
{
Persistent<ConstructorAllocation> constructorAllocation = ConstructorAllocation::create();
}
HeapStats afterFree;
clearOutOldGarbage(&afterFree);
EXPECT_TRUE(initialHeapSize == afterFree);
}
TEST(HeapTest, LargeObjects)
{
HeapStats initialHeapSize;
clearOutOldGarbage(&initialHeapSize);
IntWrapper::s_destructorCalls = 0;
LargeObject::s_destructorCalls = 0;
{
int slack = 8;
Persistent<LargeObject> object = LargeObject::create();
HeapStats afterAllocation;
clearOutOldGarbage(&afterAllocation);
{
object->set(0, 'a');
EXPECT_EQ('a', object->get(0));
object->set(object->length() - 1, 'b');
EXPECT_EQ('b', object->get(object->length() - 1));
size_t expectedObjectSpace = sizeof(LargeObject) + sizeof(IntWrapper);
size_t actualObjectSpace =
afterAllocation.totalObjectSpace() - initialHeapSize.totalObjectSpace();
CheckWithSlack(expectedObjectSpace, actualObjectSpace, slack);
size_t largeObjectAllocationSize =
sizeof(LargeObject) + sizeof(LargeHeapObject<FinalizedHeapObjectHeader>) + sizeof(FinalizedHeapObjectHeader);
size_t allocatedSpaceLowerBound =
initialHeapSize.totalAllocatedSpace() + largeObjectAllocationSize;
size_t allocatedSpaceUpperBound = allocatedSpaceLowerBound + slack + blinkPageSize;
EXPECT_LE(allocatedSpaceLowerBound, afterAllocation.totalAllocatedSpace());
EXPECT_LE(afterAllocation.totalAllocatedSpace(), allocatedSpaceUpperBound);
EXPECT_EQ(0, IntWrapper::s_destructorCalls);
EXPECT_EQ(0, LargeObject::s_destructorCalls);
for (int i = 0; i < 10; i++)
object = LargeObject::create();
}
HeapStats oneLargeObject;
clearOutOldGarbage(&oneLargeObject);
EXPECT_TRUE(oneLargeObject == afterAllocation);
EXPECT_EQ(10, IntWrapper::s_destructorCalls);
EXPECT_EQ(10, LargeObject::s_destructorCalls);
}
HeapStats backToInitial;
clearOutOldGarbage(&backToInitial);
EXPECT_TRUE(initialHeapSize == backToInitial);
EXPECT_EQ(11, IntWrapper::s_destructorCalls);
EXPECT_EQ(11, LargeObject::s_destructorCalls);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
}
typedef std::pair<Member<IntWrapper>, int> PairWrappedUnwrapped;
typedef std::pair<int, Member<IntWrapper> > PairUnwrappedWrapped;
typedef std::pair<WeakMember<IntWrapper>, Member<IntWrapper> > PairWeakStrong;
typedef std::pair<Member<IntWrapper>, WeakMember<IntWrapper> > PairStrongWeak;
typedef std::pair<WeakMember<IntWrapper>, int> PairWeakUnwrapped;
typedef std::pair<int, WeakMember<IntWrapper> > PairUnwrappedWeak;
class Container : public GarbageCollected<Container> {
public:
static Container* create() { return new Container(); }
HeapHashMap<Member<IntWrapper>, Member<IntWrapper> > map;
HeapHashSet<Member<IntWrapper> > set;
HeapHashSet<Member<IntWrapper> > set2;
HeapVector<Member<IntWrapper>, 2> vector;
HeapVector<PairWrappedUnwrapped, 2> vectorWU;
HeapVector<PairUnwrappedWrapped, 2> vectorUW;
void trace(Visitor* visitor)
{
visitor->trace(map);
visitor->trace(set);
visitor->trace(set2);
visitor->trace(vector);
}
};
struct ShouldBeTraced {
explicit ShouldBeTraced(IntWrapper* wrapper) : m_wrapper(wrapper) { }
void trace(Visitor* visitor) { visitor->trace(m_wrapper); }
Member<IntWrapper> m_wrapper;
};
class OffHeapContainer : public GarbageCollectedFinalized<OffHeapContainer> {
public:
static OffHeapContainer* create() { return new OffHeapContainer(); }
OffHeapContainer()
{
m_deque1.append(ShouldBeTraced(IntWrapper::create(1)));
m_vector1.append(ShouldBeTraced(IntWrapper::create(2)));
m_deque2.append(IntWrapper::create(3));
m_vector2.append(IntWrapper::create(4));
m_hashSet.add(IntWrapper::create(5));
m_hashMap.add(this, IntWrapper::create(6));
m_listHashSet.add(IntWrapper::create(7));
}
void trace(Visitor* visitor)
{
visitor->trace(m_deque1);
visitor->trace(m_vector1);
visitor->trace(m_deque2);
visitor->trace(m_vector2);
visitor->trace(m_hashSet);
visitor->trace(m_hashMap);
visitor->trace(m_listHashSet);
}
Deque<ShouldBeTraced> m_deque1;
Vector<ShouldBeTraced> m_vector1;
Deque<Member<IntWrapper> > m_deque2;
Vector<Member<IntWrapper> > m_vector2;
HashSet<Member<IntWrapper> > m_hashSet;
HashMap<void*, Member<IntWrapper> > m_hashMap;
ListHashSet<Member<IntWrapper> > m_listHashSet;
};
class A : public WillBeGarbageCollectedMixin {
};
class B : public NoBaseWillBeGarbageCollected<B>, public A {
WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(B);
public:
void trace(Visitor*) { }
};
TEST(HeapTest, HeapVectorFilledWithValue)
{
IntWrapper* val = IntWrapper::create(1);
HeapVector<Member<IntWrapper> > vector(10, val);
EXPECT_EQ(10u, vector.size());
for (size_t i = 0; i < vector.size(); i++)
EXPECT_EQ(val, vector[i]);
}
TEST(HeapTest, HeapVectorWithInlineCapacity)
{
IntWrapper* one = IntWrapper::create(1);
IntWrapper* two = IntWrapper::create(2);
IntWrapper* three = IntWrapper::create(3);
IntWrapper* four = IntWrapper::create(4);
IntWrapper* five = IntWrapper::create(5);
IntWrapper* six = IntWrapper::create(6);
{
HeapVector<Member<IntWrapper>, 2> vector;
vector.append(one);
vector.append(two);
Heap::collectGarbage(ThreadState::HeapPointersOnStack);
EXPECT_TRUE(vector.contains(one));
EXPECT_TRUE(vector.contains(two));
vector.append(three);
vector.append(four);
Heap::collectGarbage(ThreadState::HeapPointersOnStack);
EXPECT_TRUE(vector.contains(one));
EXPECT_TRUE(vector.contains(two));
EXPECT_TRUE(vector.contains(three));
EXPECT_TRUE(vector.contains(four));
vector.shrink(1);
Heap::collectGarbage(ThreadState::HeapPointersOnStack);
EXPECT_TRUE(vector.contains(one));
EXPECT_FALSE(vector.contains(two));
EXPECT_FALSE(vector.contains(three));
EXPECT_FALSE(vector.contains(four));
}
{
HeapVector<Member<IntWrapper>, 2> vector1;
HeapVector<Member<IntWrapper>, 2> vector2;
vector1.append(one);
vector2.append(two);
vector1.swap(vector2);
Heap::collectGarbage(ThreadState::HeapPointersOnStack);
EXPECT_TRUE(vector1.contains(two));
EXPECT_TRUE(vector2.contains(one));
}
{
HeapVector<Member<IntWrapper>, 2> vector1;
HeapVector<Member<IntWrapper>, 2> vector2;
vector1.append(one);
vector1.append(two);
vector2.append(three);
vector2.append(four);
vector2.append(five);
vector2.append(six);
vector1.swap(vector2);
Heap::collectGarbage(ThreadState::HeapPointersOnStack);
EXPECT_TRUE(vector1.contains(three));
EXPECT_TRUE(vector1.contains(four));
EXPECT_TRUE(vector1.contains(five));
EXPECT_TRUE(vector1.contains(six));
EXPECT_TRUE(vector2.contains(one));
EXPECT_TRUE(vector2.contains(two));
}
}
TEST(HeapTest, HeapCollectionTypes)
{
HeapStats initialHeapSize;
IntWrapper::s_destructorCalls = 0;
typedef HeapHashMap<Member<IntWrapper>, Member<IntWrapper> > MemberMember;
typedef HeapHashMap<Member<IntWrapper>, int> MemberPrimitive;
typedef HeapHashMap<int, Member<IntWrapper> > PrimitiveMember;
typedef HeapHashSet<Member<IntWrapper> > MemberSet;
typedef HeapHashSet<WeakMember<IntWrapper> > WeakMemberSet;
typedef HeapVector<Member<IntWrapper>, 2> MemberVector;
typedef HeapVector<PairWrappedUnwrapped, 2> VectorWU;
typedef HeapVector<PairUnwrappedWrapped, 2> VectorUW;
Persistent<MemberMember> memberMember = new MemberMember();
Persistent<MemberMember> memberMember2 = new MemberMember();
Persistent<MemberMember> memberMember3 = new MemberMember();
Persistent<MemberPrimitive> memberPrimitive = new MemberPrimitive();
Persistent<PrimitiveMember> primitiveMember = new PrimitiveMember();
Persistent<MemberSet> set = new MemberSet();
Persistent<MemberSet> set2 = new MemberSet();
Persistent<MemberVector> vector = new MemberVector();
Persistent<MemberVector> vector2 = new MemberVector();
Persistent<VectorWU> vectorWU = new VectorWU();
Persistent<VectorWU> vectorWU2 = new VectorWU();
Persistent<VectorUW> vectorUW = new VectorUW();
Persistent<VectorUW> vectorUW2 = new VectorUW();
Persistent<Container> container = Container::create();
clearOutOldGarbage(&initialHeapSize);
{
Persistent<IntWrapper> one(IntWrapper::create(1));
Persistent<IntWrapper> two(IntWrapper::create(2));
Persistent<IntWrapper> oneB(IntWrapper::create(1));
Persistent<IntWrapper> twoB(IntWrapper::create(2));
Persistent<IntWrapper> oneC(IntWrapper::create(1));
Persistent<IntWrapper> oneD(IntWrapper::create(1));
{
IntWrapper* three(IntWrapper::create(3));
IntWrapper* four(IntWrapper::create(4));
IntWrapper* threeB(IntWrapper::create(3));
IntWrapper* fourB(IntWrapper::create(4));
IntWrapper* threeC(IntWrapper::create(3));
IntWrapper* fourC(IntWrapper::create(4));
IntWrapper* fiveC(IntWrapper::create(5));
IntWrapper* threeD(IntWrapper::create(3));
IntWrapper* fourD(IntWrapper::create(4));
IntWrapper* fiveD(IntWrapper::create(5));
memberMember2->add(one, two);
memberMember2->add(two, three);
memberMember2->add(three, four);
memberMember2->add(four, one);
primitiveMember->add(1, two);
primitiveMember->add(2, three);
primitiveMember->add(3, four);
primitiveMember->add(4, one);
memberPrimitive->add(one, 2);
memberPrimitive->add(two, 3);
memberPrimitive->add(three, 4);
memberPrimitive->add(four, 1);
set2->add(one);
set2->add(two);
set2->add(three);
set2->add(four);
set->add(oneB);
vector->append(oneB);
vector2->append(threeB);
vector2->append(fourB);
vectorWU->append(PairWrappedUnwrapped(&*oneC, 42));
vectorWU2->append(PairWrappedUnwrapped(&*threeC, 43));
vectorWU2->append(PairWrappedUnwrapped(&*fourC, 44));
vectorWU2->append(PairWrappedUnwrapped(&*fiveC, 45));
vectorUW->append(PairUnwrappedWrapped(1, &*oneD));
vectorUW2->append(PairUnwrappedWrapped(103, &*threeD));
vectorUW2->append(PairUnwrappedWrapped(104, &*fourD));
vectorUW2->append(PairUnwrappedWrapped(105, &*fiveD));
Heap::collectGarbage(ThreadState::HeapPointersOnStack);
EXPECT_EQ(0u, memberMember->size());
EXPECT_EQ(4u, memberMember2->size());
EXPECT_EQ(4u, primitiveMember->size());
EXPECT_EQ(4u, memberPrimitive->size());
EXPECT_EQ(1u, set->size());
EXPECT_EQ(4u, set2->size());
EXPECT_EQ(1u, vector->size());
EXPECT_EQ(2u, vector2->size());
EXPECT_EQ(1u, vectorWU->size());
EXPECT_EQ(3u, vectorWU2->size());
EXPECT_EQ(1u, vectorUW->size());
EXPECT_EQ(3u, vectorUW2->size());
MemberVector& cvec = container->vector;
cvec.swap(*vector.get());
vector2->swap(cvec);
vector->swap(cvec);
VectorWU& cvecWU = container->vectorWU;
cvecWU.swap(*vectorWU.get());
vectorWU2->swap(cvecWU);
vectorWU->swap(cvecWU);
VectorUW& cvecUW = container->vectorUW;
cvecUW.swap(*vectorUW.get());
vectorUW2->swap(cvecUW);
vectorUW->swap(cvecUW);
MemberSet& cset1 = container->set;
MemberSet& cset2 = container->set2;
set->swap(cset1);
set2->swap(cset2);
set->swap(cset2);
cset1.swap(cset2);
cset2.swap(set2);
container->map.swap(memberMember2);
MemberMember& containedMap = container->map;
memberMember3->swap(containedMap);
memberMember3->swap(memberMember);
EXPECT_TRUE(memberMember->get(one) == two);
EXPECT_TRUE(memberMember->get(two) == three);
EXPECT_TRUE(memberMember->get(three) == four);
EXPECT_TRUE(memberMember->get(four) == one);
EXPECT_TRUE(primitiveMember->get(1) == two);
EXPECT_TRUE(primitiveMember->get(2) == three);
EXPECT_TRUE(primitiveMember->get(3) == four);
EXPECT_TRUE(primitiveMember->get(4) == one);
EXPECT_EQ(1, memberPrimitive->get(four));
EXPECT_EQ(2, memberPrimitive->get(one));
EXPECT_EQ(3, memberPrimitive->get(two));
EXPECT_EQ(4, memberPrimitive->get(three));
EXPECT_TRUE(set->contains(one));
EXPECT_TRUE(set->contains(two));
EXPECT_TRUE(set->contains(three));
EXPECT_TRUE(set->contains(four));
EXPECT_TRUE(set2->contains(oneB));
EXPECT_TRUE(vector->contains(threeB));
EXPECT_TRUE(vector->contains(fourB));
EXPECT_TRUE(vector2->contains(oneB));
EXPECT_FALSE(vector2->contains(threeB));
EXPECT_TRUE(vectorWU->contains(PairWrappedUnwrapped(&*threeC, 43)));
EXPECT_TRUE(vectorWU->contains(PairWrappedUnwrapped(&*fourC, 44)));
EXPECT_TRUE(vectorWU->contains(PairWrappedUnwrapped(&*fiveC, 45)));
EXPECT_TRUE(vectorWU2->contains(PairWrappedUnwrapped(&*oneC, 42)));
EXPECT_FALSE(vectorWU2->contains(PairWrappedUnwrapped(&*threeC, 43)));
EXPECT_TRUE(vectorUW->contains(PairUnwrappedWrapped(103, &*threeD)));
EXPECT_TRUE(vectorUW->contains(PairUnwrappedWrapped(104, &*fourD)));
EXPECT_TRUE(vectorUW->contains(PairUnwrappedWrapped(105, &*fiveD)));
EXPECT_TRUE(vectorUW2->contains(PairUnwrappedWrapped(1, &*oneD)));
EXPECT_FALSE(vectorUW2->contains(PairUnwrappedWrapped(103, &*threeD)));
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(4u, memberMember->size());
EXPECT_EQ(0u, memberMember2->size());
EXPECT_EQ(4u, primitiveMember->size());
EXPECT_EQ(4u, memberPrimitive->size());
EXPECT_EQ(4u, set->size());
EXPECT_EQ(1u, set2->size());
EXPECT_EQ(2u, vector->size());
EXPECT_EQ(1u, vector2->size());
EXPECT_EQ(3u, vectorUW->size());
EXPECT_EQ(1u, vector2->size());
EXPECT_TRUE(memberMember->get(one) == two);
EXPECT_TRUE(primitiveMember->get(1) == two);
EXPECT_TRUE(primitiveMember->get(4) == one);
EXPECT_EQ(2, memberPrimitive->get(one));
EXPECT_EQ(3, memberPrimitive->get(two));
EXPECT_TRUE(set->contains(one));
EXPECT_TRUE(set->contains(two));
EXPECT_FALSE(set->contains(oneB));
EXPECT_TRUE(set2->contains(oneB));
EXPECT_EQ(3, vector->at(0)->value());
EXPECT_EQ(4, vector->at(1)->value());
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(4u, memberMember->size());
EXPECT_EQ(4u, primitiveMember->size());
EXPECT_EQ(4u, memberPrimitive->size());
EXPECT_EQ(4u, set->size());
EXPECT_EQ(1u, set2->size());
EXPECT_EQ(2u, vector->size());
EXPECT_EQ(1u, vector2->size());
EXPECT_EQ(3u, vectorWU->size());
EXPECT_EQ(1u, vectorWU2->size());
EXPECT_EQ(3u, vectorUW->size());
EXPECT_EQ(1u, vectorUW2->size());
}
template<typename T>
void MapIteratorCheck(T& it, const T& end, int expected)
{
int found = 0;
while (it != end) {
found++;
int key = it->key->value();
int value = it->value->value();
EXPECT_TRUE(key >= 0 && key < 1100);
EXPECT_TRUE(value >= 0 && value < 1100);
++it;
}
EXPECT_EQ(expected, found);
}
template<typename T>
void SetIteratorCheck(T& it, const T& end, int expected)
{
int found = 0;
while (it != end) {
found++;
int value = (*it)->value();
EXPECT_TRUE(value >= 0 && value < 1100);
++it;
}
EXPECT_EQ(expected, found);
}
TEST(HeapTest, HeapWeakCollectionSimple)
{
HeapStats initialHeapStats;
clearOutOldGarbage(&initialHeapStats);
IntWrapper::s_destructorCalls = 0;
PersistentHeapVector<Member<IntWrapper> > keepNumbersAlive;
typedef HeapHashMap<WeakMember<IntWrapper>, Member<IntWrapper> > WeakStrong;
typedef HeapHashMap<Member<IntWrapper>, WeakMember<IntWrapper> > StrongWeak;
typedef HeapHashMap<WeakMember<IntWrapper>, WeakMember<IntWrapper> > WeakWeak;
typedef HeapHashSet<WeakMember<IntWrapper> > WeakSet;
Persistent<WeakStrong> weakStrong = new WeakStrong();
Persistent<StrongWeak> strongWeak = new StrongWeak();
Persistent<WeakWeak> weakWeak = new WeakWeak();
Persistent<WeakSet> weakSet = new WeakSet();
Persistent<IntWrapper> two = IntWrapper::create(2);
keepNumbersAlive.append(IntWrapper::create(103));
keepNumbersAlive.append(IntWrapper::create(10));
{
weakStrong->add(IntWrapper::create(1), two);
strongWeak->add(two, IntWrapper::create(1));
weakWeak->add(two, IntWrapper::create(42));
weakWeak->add(IntWrapper::create(42), two);
weakSet->add(IntWrapper::create(0));
weakSet->add(two);
weakSet->add(keepNumbersAlive[0]);
weakSet->add(keepNumbersAlive[1]);
EXPECT_EQ(1u, weakStrong->size());
EXPECT_EQ(1u, strongWeak->size());
EXPECT_EQ(2u, weakWeak->size());
EXPECT_EQ(4u, weakSet->size());
}
keepNumbersAlive[0] = nullptr;
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(0u, weakStrong->size());
EXPECT_EQ(0u, strongWeak->size());
EXPECT_EQ(0u, weakWeak->size());
EXPECT_EQ(2u, weakSet->size());
}
class ThingWithDestructor {
public:
ThingWithDestructor()
: m_x(emptyValue)
{
s_liveThingsWithDestructor++;
}
ThingWithDestructor(int x)
: m_x(x)
{
s_liveThingsWithDestructor++;
}
ThingWithDestructor(const ThingWithDestructor&other)
{
*this = other;
s_liveThingsWithDestructor++;
}
~ThingWithDestructor()
{
s_liveThingsWithDestructor--;
}
int value() { return m_x; }
static int s_liveThingsWithDestructor;
unsigned hash() { return IntHash<int>::hash(m_x); }
private:
static const int emptyValue = 0;
int m_x;
};
int ThingWithDestructor::s_liveThingsWithDestructor;
struct ThingWithDestructorTraits : public HashTraits<ThingWithDestructor> {
static const bool needsDestruction = true;
};
static void heapMapDestructorHelper(bool clearMaps)
{
HeapStats initialHeapStats;
clearOutOldGarbage(&initialHeapStats);
ThingWithDestructor::s_liveThingsWithDestructor = 0;
typedef HeapHashMap<WeakMember<IntWrapper>, RefPtr<RefCountedAndGarbageCollected> > RefMap;
typedef HeapHashMap<
WeakMember<IntWrapper>,
ThingWithDestructor,
DefaultHash<WeakMember<IntWrapper> >::Hash,
HashTraits<WeakMember<IntWrapper> >,
ThingWithDestructorTraits> Map;
Persistent<Map> map(new Map());
Persistent<RefMap> refMap(new RefMap());
Persistent<IntWrapper> luck(IntWrapper::create(103));
int baseLine, refBaseLine;
{
Map stackMap;
RefMap stackRefMap;
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
stackMap.add(IntWrapper::create(42), ThingWithDestructor(1729));
stackMap.add(luck, ThingWithDestructor(8128));
stackRefMap.add(IntWrapper::create(42), RefCountedAndGarbageCollected::create());
stackRefMap.add(luck, RefCountedAndGarbageCollected::create());
baseLine = ThingWithDestructor::s_liveThingsWithDestructor;
refBaseLine = RefCountedAndGarbageCollected::s_destructorCalls;
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(baseLine - 2, ThingWithDestructor::s_liveThingsWithDestructor);
EXPECT_EQ(refBaseLine + 2, RefCountedAndGarbageCollected::s_destructorCalls);
map->add(IntWrapper::create(42), ThingWithDestructor(1729));
map->add(luck, ThingWithDestructor(8128));
refMap->add(IntWrapper::create(42), RefCountedAndGarbageCollected::create());
refMap->add(luck, RefCountedAndGarbageCollected::create());
baseLine = ThingWithDestructor::s_liveThingsWithDestructor;
refBaseLine = RefCountedAndGarbageCollected::s_destructorCalls;
luck.clear();
if (clearMaps) {
map->clear();
refMap->clear();
} else {
map.clear();
refMap.clear();
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
}
EXPECT_EQ(baseLine - 2, ThingWithDestructor::s_liveThingsWithDestructor);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(refBaseLine + 2, RefCountedAndGarbageCollected::s_destructorCalls);
}
TEST(HeapTest, HeapMapDestructor)
{
heapMapDestructorHelper(true);
heapMapDestructorHelper(false);
}
typedef HeapHashSet<PairWeakStrong> WeakStrongSet;
typedef HeapHashSet<PairWeakUnwrapped> WeakUnwrappedSet;
typedef HeapHashSet<PairStrongWeak> StrongWeakSet;
typedef HeapHashSet<PairUnwrappedWeak> UnwrappedWeakSet;
void checkPairSets(
Persistent<WeakStrongSet>& weakStrong,
Persistent<StrongWeakSet>& strongWeak,
Persistent<WeakUnwrappedSet>& weakUnwrapped,
Persistent<UnwrappedWeakSet>& unwrappedWeak,
bool ones,
Persistent<IntWrapper>& two)
{
WeakStrongSet::iterator itWS = weakStrong->begin();
StrongWeakSet::iterator itSW = strongWeak->begin();
WeakUnwrappedSet::iterator itWU = weakUnwrapped->begin();
UnwrappedWeakSet::iterator itUW = unwrappedWeak->begin();
EXPECT_EQ(2u, weakStrong->size());
EXPECT_EQ(2u, strongWeak->size());
EXPECT_EQ(2u, weakUnwrapped->size());
EXPECT_EQ(2u, unwrappedWeak->size());
PairWeakStrong p = *itWS;
PairStrongWeak p2 = *itSW;
PairWeakUnwrapped p3 = *itWU;
PairUnwrappedWeak p4 = *itUW;
if (p.first == two && p.second == two)
++itWS;
if (p2.first == two && p2.second == two)
++itSW;
if (p3.first == two && p3.second == 2)
++itWU;
if (p4.first == 2 && p4.second == two)
++itUW;
p = *itWS;
p2 = *itSW;
p3 = *itWU;
p4 = *itUW;
IntWrapper* nullWrapper = 0;
if (ones) {
EXPECT_EQ(p.first->value(), 1);
EXPECT_EQ(p2.second->value(), 1);
EXPECT_EQ(p3.first->value(), 1);
EXPECT_EQ(p4.second->value(), 1);
} else {
EXPECT_EQ(p.first, nullWrapper);
EXPECT_EQ(p2.second, nullWrapper);
EXPECT_EQ(p3.first, nullWrapper);
EXPECT_EQ(p4.second, nullWrapper);
}
EXPECT_EQ(p.second->value(), 2);
EXPECT_EQ(p2.first->value(), 2);
EXPECT_EQ(p3.second, 2);
EXPECT_EQ(p4.first, 2);
EXPECT_TRUE(weakStrong->contains(PairWeakStrong(&*two, &*two)));
EXPECT_TRUE(strongWeak->contains(PairStrongWeak(&*two, &*two)));
EXPECT_TRUE(weakUnwrapped->contains(PairWeakUnwrapped(&*two, 2)));
EXPECT_TRUE(unwrappedWeak->contains(PairUnwrappedWeak(2, &*two)));
}
TEST(HeapTest, HeapWeakPairs)
{
IntWrapper::s_destructorCalls = 0;
PersistentHeapVector<Member<IntWrapper> > keepNumbersAlive;
Persistent<WeakStrongSet> weakStrong = new WeakStrongSet();
Persistent<StrongWeakSet> strongWeak = new StrongWeakSet();
Persistent<WeakUnwrappedSet> weakUnwrapped = new WeakUnwrappedSet();
Persistent<UnwrappedWeakSet> unwrappedWeak = new UnwrappedWeakSet();
Persistent<IntWrapper> two = IntWrapper::create(2);
weakStrong->add(PairWeakStrong(IntWrapper::create(1), &*two));
weakStrong->add(PairWeakStrong(&*two, &*two));
strongWeak->add(PairStrongWeak(&*two, IntWrapper::create(1)));
strongWeak->add(PairStrongWeak(&*two, &*two));
weakUnwrapped->add(PairWeakUnwrapped(IntWrapper::create(1), 2));
weakUnwrapped->add(PairWeakUnwrapped(&*two, 2));
unwrappedWeak->add(PairUnwrappedWeak(2, IntWrapper::create(1)));
unwrappedWeak->add(PairUnwrappedWeak(2, &*two));
checkPairSets(weakStrong, strongWeak, weakUnwrapped, unwrappedWeak, true, two);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
checkPairSets(weakStrong, strongWeak, weakUnwrapped, unwrappedWeak, false, two);
}
TEST(HeapTest, HeapWeakCollectionTypes)
{
HeapStats initialHeapSize;
IntWrapper::s_destructorCalls = 0;
typedef HeapHashMap<WeakMember<IntWrapper>, Member<IntWrapper> > WeakStrong;
typedef HeapHashMap<Member<IntWrapper>, WeakMember<IntWrapper> > StrongWeak;
typedef HeapHashMap<WeakMember<IntWrapper>, WeakMember<IntWrapper> > WeakWeak;
typedef HeapHashSet<WeakMember<IntWrapper> > WeakSet;
clearOutOldGarbage(&initialHeapSize);
const int weakStrongIndex = 0;
const int strongWeakIndex = 1;
const int weakWeakIndex = 2;
const int numberOfMapIndices = 3;
const int weakSetIndex = 3;
const int numberOfCollections = 4;
for (int testRun = 0; testRun < 4; testRun++) {
for (int collectionNumber = 0; collectionNumber < numberOfCollections; collectionNumber++) {
bool testThatIteratorsMakeStrong = (testRun == weakSetIndex);
bool deleteAfterwards = (testRun == 1);
bool addAfterwards = (testRun == weakWeakIndex);
if (deleteAfterwards && collectionNumber == strongWeakIndex)
continue;
unsigned added = addAfterwards ? 100 : 0;
Persistent<WeakStrong> weakStrong = new WeakStrong();
Persistent<StrongWeak> strongWeak = new StrongWeak();
Persistent<WeakWeak> weakWeak = new WeakWeak();
Persistent<WeakSet> weakSet = new WeakSet();
PersistentHeapVector<Member<IntWrapper> > keepNumbersAlive;
for (int i = 0; i < 128; i += 2) {
IntWrapper* wrapped = IntWrapper::create(i);
IntWrapper* wrapped2 = IntWrapper::create(i + 1);
keepNumbersAlive.append(wrapped);
keepNumbersAlive.append(wrapped2);
weakStrong->add(wrapped, wrapped2);
strongWeak->add(wrapped2, wrapped);
weakWeak->add(wrapped, wrapped2);
weakSet->add(wrapped);
}
EXPECT_EQ(64u, weakStrong->size());
EXPECT_EQ(64u, strongWeak->size());
EXPECT_EQ(64u, weakWeak->size());
EXPECT_EQ(64u, weakSet->size());
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(64u, weakStrong->size());
EXPECT_EQ(64u, strongWeak->size());
EXPECT_EQ(64u, weakWeak->size());
EXPECT_EQ(64u, weakSet->size());
for (int i = 0; i < 128; i += 2) {
IntWrapper* wrapped = keepNumbersAlive[i];
IntWrapper* wrapped2 = keepNumbersAlive[i + 1];
EXPECT_EQ(wrapped2, weakStrong->get(wrapped));
EXPECT_EQ(wrapped, strongWeak->get(wrapped2));
EXPECT_EQ(wrapped2, weakWeak->get(wrapped));
EXPECT_TRUE(weakSet->contains(wrapped));
}
for (int i = 0; i < 128; i += 3)
keepNumbersAlive[i] = nullptr;
if (collectionNumber != weakStrongIndex)
weakStrong->clear();
if (collectionNumber != strongWeakIndex)
strongWeak->clear();
if (collectionNumber != weakWeakIndex)
weakWeak->clear();
if (collectionNumber != weakSetIndex)
weakSet->clear();
if (testThatIteratorsMakeStrong) {
WeakStrong::iterator it1 = weakStrong->begin();
StrongWeak::iterator it2 = strongWeak->begin();
WeakWeak::iterator it3 = weakWeak->begin();
WeakSet::iterator it4 = weakSet->begin();
Heap::collectGarbage(ThreadState::HeapPointersOnStack);
if (collectionNumber == weakStrongIndex) {
EXPECT_EQ(64u, weakStrong->size());
MapIteratorCheck(it1, weakStrong->end(), 64);
} else if (collectionNumber == strongWeakIndex) {
EXPECT_EQ(64u, strongWeak->size());
MapIteratorCheck(it2, strongWeak->end(), 64);
} else if (collectionNumber == weakWeakIndex) {
EXPECT_EQ(64u, weakWeak->size());
MapIteratorCheck(it3, weakWeak->end(), 64);
} else if (collectionNumber == weakSetIndex) {
EXPECT_EQ(64u, weakSet->size());
SetIteratorCheck(it4, weakSet->end(), 64);
}
} else {
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
unsigned count = 0;
for (int i = 0; i < 128; i += 2) {
bool firstAlive = keepNumbersAlive[i];
bool secondAlive = keepNumbersAlive[i + 1];
if (firstAlive && (collectionNumber == weakStrongIndex || collectionNumber == strongWeakIndex))
secondAlive = true;
if (firstAlive && secondAlive && collectionNumber < numberOfMapIndices) {
if (collectionNumber == weakStrongIndex) {
if (deleteAfterwards)
EXPECT_EQ(i + 1, weakStrong->take(keepNumbersAlive[i])->value());
} else if (collectionNumber == strongWeakIndex) {
if (deleteAfterwards)
EXPECT_EQ(i, strongWeak->take(keepNumbersAlive[i + 1])->value());
} else if (collectionNumber == weakWeakIndex) {
if (deleteAfterwards)
EXPECT_EQ(i + 1, weakWeak->take(keepNumbersAlive[i])->value());
}
if (!deleteAfterwards)
count++;
} else if (collectionNumber == weakSetIndex && firstAlive) {
ASSERT_TRUE(weakSet->contains(keepNumbersAlive[i]));
if (deleteAfterwards)
weakSet->remove(keepNumbersAlive[i]);
else
count++;
}
}
if (addAfterwards) {
for (int i = 1000; i < 1100; i++) {
IntWrapper* wrapped = IntWrapper::create(i);
keepNumbersAlive.append(wrapped);
weakStrong->add(wrapped, wrapped);
strongWeak->add(wrapped, wrapped);
weakWeak->add(wrapped, wrapped);
weakSet->add(wrapped);
}
}
if (collectionNumber == weakStrongIndex)
EXPECT_EQ(count + added, weakStrong->size());
else if (collectionNumber == strongWeakIndex)
EXPECT_EQ(count + added, strongWeak->size());
else if (collectionNumber == weakWeakIndex)
EXPECT_EQ(count + added, weakWeak->size());
else if (collectionNumber == weakSetIndex)
EXPECT_EQ(count + added, weakSet->size());
WeakStrong::iterator it1 = weakStrong->begin();
StrongWeak::iterator it2 = strongWeak->begin();
WeakWeak::iterator it3 = weakWeak->begin();
WeakSet::iterator it4 = weakSet->begin();
MapIteratorCheck(it1, weakStrong->end(), (collectionNumber == weakStrongIndex ? count : 0) + added);
MapIteratorCheck(it2, strongWeak->end(), (collectionNumber == strongWeakIndex ? count : 0) + added);
MapIteratorCheck(it3, weakWeak->end(), (collectionNumber == weakWeakIndex ? count : 0) + added);
SetIteratorCheck(it4, weakSet->end(), (collectionNumber == weakSetIndex ? count : 0) + added);
}
for (unsigned i = 0; i < 128 + added; i++)
keepNumbersAlive[i] = nullptr;
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(added, weakStrong->size());
EXPECT_EQ(added, strongWeak->size());
EXPECT_EQ(added, weakWeak->size());
EXPECT_EQ(added, weakSet->size());
}
}
}
TEST(HeapTest, RefCountedGarbageCollected)
{
RefCountedAndGarbageCollected::s_destructorCalls = 0;
{
RefPtr<RefCountedAndGarbageCollected> refPtr3;
{
Persistent<RefCountedAndGarbageCollected> persistent;
{
RefPtr<RefCountedAndGarbageCollected> refPtr1 = RefCountedAndGarbageCollected::create();
RefPtr<RefCountedAndGarbageCollected> refPtr2 = RefCountedAndGarbageCollected::create();
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(0, RefCountedAndGarbageCollected::s_destructorCalls);
persistent = refPtr1.get();
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(1, RefCountedAndGarbageCollected::s_destructorCalls);
refPtr3 = persistent;
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(1, RefCountedAndGarbageCollected::s_destructorCalls);
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(2, RefCountedAndGarbageCollected::s_destructorCalls);
}
TEST(HeapTest, RefCountedGarbageCollectedWithStackPointers)
{
RefCountedAndGarbageCollected::s_destructorCalls = 0;
RefCountedAndGarbageCollected2::s_destructorCalls = 0;
{
RefCountedAndGarbageCollected* pointer1 = 0;
RefCountedAndGarbageCollected2* pointer2 = 0;
{
RefPtr<RefCountedAndGarbageCollected> object1 = RefCountedAndGarbageCollected::create();
RefPtr<RefCountedAndGarbageCollected2> object2 = RefCountedAndGarbageCollected2::create();
pointer1 = object1.get();
pointer2 = object2.get();
void* objects[2] = { object1.get(), object2.get() };
RefCountedGarbageCollectedVisitor visitor(2, objects);
ThreadState::current()->visitPersistents(&visitor);
EXPECT_TRUE(visitor.validate());
Heap::collectGarbage(ThreadState::HeapPointersOnStack);
EXPECT_EQ(0, RefCountedAndGarbageCollected::s_destructorCalls);
EXPECT_EQ(0, RefCountedAndGarbageCollected2::s_destructorCalls);
}
Heap::collectGarbage(ThreadState::HeapPointersOnStack);
EXPECT_EQ(0, RefCountedAndGarbageCollected::s_destructorCalls);
EXPECT_EQ(0, RefCountedAndGarbageCollected2::s_destructorCalls);
void* objects[] = { 0 };
RefCountedGarbageCollectedVisitor visitor(0, objects);
ThreadState::current()->visitPersistents(&visitor);
EXPECT_TRUE(visitor.validate());
{
RefPtr<RefCountedAndGarbageCollected> object1(pointer1);
RefPtr<RefCountedAndGarbageCollected2> object2(pointer2);
void* objects[2] = { object1.get(), object2.get() };
RefCountedGarbageCollectedVisitor visitor(2, objects);
ThreadState::current()->visitPersistents(&visitor);
EXPECT_TRUE(visitor.validate());
Heap::collectGarbage(ThreadState::HeapPointersOnStack);
EXPECT_EQ(0, RefCountedAndGarbageCollected::s_destructorCalls);
EXPECT_EQ(0, RefCountedAndGarbageCollected2::s_destructorCalls);
}
Heap::collectGarbage(ThreadState::HeapPointersOnStack);
EXPECT_EQ(0, RefCountedAndGarbageCollected::s_destructorCalls);
EXPECT_EQ(0, RefCountedAndGarbageCollected2::s_destructorCalls);
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(1, RefCountedAndGarbageCollected::s_destructorCalls);
EXPECT_EQ(1, RefCountedAndGarbageCollected2::s_destructorCalls);
}
TEST(HeapTest, WeakMembers)
{
Bar::s_live = 0;
{
Persistent<Bar> h1 = Bar::create();
Persistent<Weak> h4;
Persistent<WithWeakMember> h5;
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
ASSERT_EQ(1u, Bar::s_live);
{
Bar* h2 = Bar::create();
Bar* h3 = Bar::create();
h4 = Weak::create(h2, h3);
h5 = WithWeakMember::create(h2, h3);
Heap::collectGarbage(ThreadState::HeapPointersOnStack);
EXPECT_EQ(5u, Bar::s_live);
EXPECT_TRUE(h4->strongIsThere());
EXPECT_TRUE(h4->weakIsThere());
EXPECT_TRUE(h5->strongIsThere());
EXPECT_TRUE(h5->weakIsThere());
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(4u, Bar::s_live);
EXPECT_TRUE(h4->strongIsThere());
EXPECT_FALSE(h4->weakIsThere());
EXPECT_TRUE(h5->strongIsThere());
EXPECT_FALSE(h5->weakIsThere());
h1.release();
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(3u, Bar::s_live);
EXPECT_TRUE(h4->strongIsThere());
EXPECT_TRUE(h5->strongIsThere());
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(0u, Bar::s_live);
}
TEST(HeapTest, FinalizationObserver)
{
Persistent<FinalizationObserver<Observable> > o;
{
Observable* foo = Observable::create(Bar::create());
o = FinalizationObserver<Observable>::create(foo);
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(0u, Bar::s_live);
EXPECT_TRUE(o->didCallWillFinalize());
FinalizationObserverWithHashMap::s_didCallWillFinalize = false;
Observable* foo = Observable::create(Bar::create());
FinalizationObserverWithHashMap::ObserverMap& map = FinalizationObserverWithHashMap::observe(*foo);
EXPECT_EQ(1u, map.size());
foo = 0;
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(0u, Bar::s_live);
EXPECT_EQ(0u, map.size());
EXPECT_TRUE(FinalizationObserverWithHashMap::s_didCallWillFinalize);
}
TEST(HeapTest, Comparisons)
{
Persistent<Bar> barPersistent = Bar::create();
Persistent<Foo> fooPersistent = Foo::create(barPersistent);
EXPECT_TRUE(barPersistent != fooPersistent);
barPersistent = fooPersistent;
EXPECT_TRUE(barPersistent == fooPersistent);
}
TEST(HeapTest, CheckAndMarkPointer)
{
HeapStats initialHeapStats;
clearOutOldGarbage(&initialHeapStats);
Vector<Address> objectAddresses;
Vector<Address> endAddresses;
Address largeObjectAddress;
Address largeObjectEndAddress;
CountingVisitor visitor;
for (int i = 0; i < 10; i++) {
SimpleObject* object = SimpleObject::create();
Address objectAddress = reinterpret_cast<Address>(object);
objectAddresses.append(objectAddress);
endAddresses.append(objectAddress + sizeof(SimpleObject) - 1);
}
LargeObject* largeObject = LargeObject::create();
largeObjectAddress = reinterpret_cast<Address>(largeObject);
largeObjectEndAddress = largeObjectAddress + sizeof(LargeObject) - 1;
{
TestGCScope scope(ThreadState::HeapPointersOnStack);
Heap::makeConsistentForGC();
for (size_t i = 0; i < objectAddresses.size(); i++) {
EXPECT_TRUE(Heap::checkAndMarkPointer(&visitor, objectAddresses[i]));
EXPECT_TRUE(Heap::checkAndMarkPointer(&visitor, endAddresses[i]));
}
EXPECT_EQ(objectAddresses.size() * 2, visitor.count());
visitor.reset();
EXPECT_TRUE(Heap::checkAndMarkPointer(&visitor, largeObjectAddress));
EXPECT_TRUE(Heap::checkAndMarkPointer(&visitor, largeObjectEndAddress));
EXPECT_EQ(2ul, visitor.count());
visitor.reset();
}
clearOutOldGarbage(&initialHeapStats);
{
TestGCScope scope(ThreadState::HeapPointersOnStack);
Heap::makeConsistentForGC();
for (size_t i = 0; i < objectAddresses.size(); i++) {
EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, objectAddresses[i]));
EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, endAddresses[i]));
}
EXPECT_EQ(0ul, visitor.count());
EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, largeObjectAddress));
EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, largeObjectEndAddress));
EXPECT_EQ(0ul, visitor.count());
}
clearOutOldGarbage(&initialHeapStats);
}
TEST(HeapTest, VisitOffHeapCollections)
{
HeapStats initialHeapStats;
clearOutOldGarbage(&initialHeapStats);
IntWrapper::s_destructorCalls = 0;
Persistent<OffHeapContainer> container = OffHeapContainer::create();
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(0, IntWrapper::s_destructorCalls);
container = nullptr;
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(7, IntWrapper::s_destructorCalls);
}
TEST(HeapTest, PersistentHeapCollectionTypes)
{
HeapStats initialHeapSize;
IntWrapper::s_destructorCalls = 0;
typedef HeapVector<Member<IntWrapper> > Vec;
typedef PersistentHeapVector<Member<IntWrapper> > PVec;
typedef PersistentHeapHashSet<Member<IntWrapper> > PSet;
typedef PersistentHeapHashMap<Member<IntWrapper>, Member<IntWrapper> > PMap;
clearOutOldGarbage(&initialHeapSize);
{
PVec pVec;
PSet pSet;
PMap pMap;
IntWrapper* one(IntWrapper::create(1));
IntWrapper* two(IntWrapper::create(2));
IntWrapper* three(IntWrapper::create(3));
IntWrapper* four(IntWrapper::create(4));
IntWrapper* five(IntWrapper::create(5));
IntWrapper* six(IntWrapper::create(6));
pVec.append(one);
pVec.append(two);
Vec* vec = new Vec();
vec->swap(pVec);
pVec.append(two);
pVec.append(three);
pSet.add(four);
pMap.add(five, six);
vec = 0;
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(1, IntWrapper::s_destructorCalls);
EXPECT_EQ(2u, pVec.size());
EXPECT_TRUE(pVec.at(0) == two);
EXPECT_TRUE(pVec.at(1) == three);
EXPECT_EQ(1u, pSet.size());
EXPECT_TRUE(pSet.contains(four));
EXPECT_EQ(1u, pMap.size());
EXPECT_TRUE(pMap.get(five) == six);
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(6, IntWrapper::s_destructorCalls);
}
TEST(HeapTest, CollectionNesting)
{
HeapStats initialStats;
clearOutOldGarbage(&initialStats);
void* key = &IntWrapper::s_destructorCalls;
IntWrapper::s_destructorCalls = 0;
typedef HeapVector<Member<IntWrapper> > IntVector;
HeapHashMap<void*, IntVector>* map = new HeapHashMap<void*, IntVector>();
map->add(key, IntVector());
HeapHashMap<void*, IntVector>::iterator it = map->find(key);
EXPECT_EQ(0u, map->get(key).size());
it->value.append(IntWrapper::create(42));
EXPECT_EQ(1u, map->get(key).size());
Persistent<HeapHashMap<void*, IntVector> > keepAlive(map);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(1u, map->get(key).size());
EXPECT_EQ(0, IntWrapper::s_destructorCalls);
keepAlive = nullptr;
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(1, IntWrapper::s_destructorCalls);
}
TEST(HeapTest, GarbageCollectedMixin)
{
HeapStats initialHeapStats;
clearOutOldGarbage(&initialHeapStats);
Persistent<UseMixin> usemixin = UseMixin::create();
EXPECT_EQ(0, UseMixin::s_traceCount);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(1, UseMixin::s_traceCount);
Persistent<Mixin> mixin = usemixin;
usemixin = nullptr;
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(2, UseMixin::s_traceCount);
PersistentHeapHashSet<WeakMember<Mixin> > weakMap;
weakMap.add(UseMixin::create());
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(0u, weakMap.size());
}
TEST(HeapTest, CollectionNesting2)
{
HeapStats initialStats;
clearOutOldGarbage(&initialStats);
void* key = &IntWrapper::s_destructorCalls;
IntWrapper::s_destructorCalls = 0;
typedef HeapHashSet<Member<IntWrapper> > IntSet;
HeapHashMap<void*, IntSet>* map = new HeapHashMap<void*, IntSet>();
map->add(key, IntSet());
HeapHashMap<void*, IntSet>::iterator it = map->find(key);
EXPECT_EQ(0u, map->get(key).size());
it->value.add(IntWrapper::create(42));
EXPECT_EQ(1u, map->get(key).size());
Persistent<HeapHashMap<void*, IntSet> > keepAlive(map);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(1u, map->get(key).size());
EXPECT_EQ(0, IntWrapper::s_destructorCalls);
}
TEST(HeapTest, CollectionNesting3)
{
HeapStats initialStats;
clearOutOldGarbage(&initialStats);
IntWrapper::s_destructorCalls = 0;
typedef HeapVector<Member<IntWrapper> > IntVector;
HeapVector<IntVector>* vector = new HeapVector<IntVector>();
vector->append(IntVector());
HeapVector<IntVector>::iterator it = vector->begin();
EXPECT_EQ(0u, it->size());
it->append(IntWrapper::create(42));
EXPECT_EQ(1u, it->size());
Persistent<HeapVector<IntVector> > keepAlive(vector);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(1u, it->size());
EXPECT_EQ(0, IntWrapper::s_destructorCalls);
}
TEST(HeapTest, EmbeddedInVector)
{
HeapStats initialStats;
clearOutOldGarbage(&initialStats);
SimpleFinalizedObject::s_destructorCalls = 0;
{
PersistentHeapVector<VectorObject, 2> inlineVector;
PersistentHeapVector<VectorObject> outlineVector;
VectorObject i1, i2;
inlineVector.append(i1);
inlineVector.append(i2);
VectorObject o1, o2;
outlineVector.append(o1);
outlineVector.append(o2);
PersistentHeapVector<VectorObjectInheritedTrace> vectorInheritedTrace;
VectorObjectInheritedTrace it1, it2;
vectorInheritedTrace.append(it1);
vectorInheritedTrace.append(it2);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(0, SimpleFinalizedObject::s_destructorCalls);
PersistentHeapVector<VectorObjectNoTrace> vectorNoTrace;
VectorObjectNoTrace n1, n2;
vectorNoTrace.append(n1);
vectorNoTrace.append(n2);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(2, SimpleFinalizedObject::s_destructorCalls);
}
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(8, SimpleFinalizedObject::s_destructorCalls);
}
TEST(HeapTest, RawPtrInHash)
{
HashSet<RawPtr<int> > set;
set.add(new int(42));
set.add(new int(42));
EXPECT_EQ(2u, set.size());
for (HashSet<RawPtr<int> >::iterator it = set.begin(); it != set.end(); ++it)
EXPECT_EQ(42, **it);
}
TEST(HeapTest, HeapTerminatedArray)
{
HeapStats initialHeapSize;
clearOutOldGarbage(&initialHeapSize);
IntWrapper::s_destructorCalls = 0;
HeapTerminatedArray<TerminatedArrayItem>* arr = 0;
const size_t prefixSize = 4;
const size_t suffixSize = 4;
{
HeapTerminatedArrayBuilder<TerminatedArrayItem> builder(arr);
builder.grow(prefixSize);
for (size_t i = 0; i < prefixSize; i++)
builder.append(TerminatedArrayItem(IntWrapper::create(i)));
arr = builder.release();
}
Heap::collectGarbage(ThreadState::HeapPointersOnStack);
EXPECT_EQ(0, IntWrapper::s_destructorCalls);
EXPECT_EQ(prefixSize, arr->size());
for (size_t i = 0; i < prefixSize; i++)
EXPECT_EQ(i, static_cast<size_t>(arr->at(i).payload()->value()));
{
HeapTerminatedArrayBuilder<TerminatedArrayItem> builder(arr);
builder.grow(suffixSize);
for (size_t i = 0; i < suffixSize; i++)
builder.append(TerminatedArrayItem(IntWrapper::create(prefixSize + i)));
arr = builder.release();
}
Heap::collectGarbage(ThreadState::HeapPointersOnStack);
EXPECT_EQ(0, IntWrapper::s_destructorCalls);
EXPECT_EQ(prefixSize + suffixSize, arr->size());
for (size_t i = 0; i < prefixSize + suffixSize; i++)
EXPECT_EQ(i, static_cast<size_t>(arr->at(i).payload()->value()));
{
Persistent<HeapTerminatedArray<TerminatedArrayItem> > persistentArr = arr;
arr = 0;
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
arr = persistentArr.get();
EXPECT_EQ(0, IntWrapper::s_destructorCalls);
EXPECT_EQ(prefixSize + suffixSize, arr->size());
for (size_t i = 0; i < prefixSize + suffixSize; i++)
EXPECT_EQ(i, static_cast<size_t>(arr->at(i).payload()->value()));
}
arr = 0;
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(8, IntWrapper::s_destructorCalls);
}
TEST(HeapTest, HeapLinkedStack)
{
HeapStats initialHeapSize;
clearOutOldGarbage(&initialHeapSize);
IntWrapper::s_destructorCalls = 0;
HeapLinkedStack<TerminatedArrayItem>* stack = new HeapLinkedStack<TerminatedArrayItem>();
const size_t stackSize = 10;
for (size_t i = 0; i < stackSize; i++)
stack->push(TerminatedArrayItem(IntWrapper::create(i)));
Heap::collectGarbage(ThreadState::HeapPointersOnStack);
EXPECT_EQ(0, IntWrapper::s_destructorCalls);
EXPECT_EQ(stackSize, stack->size());
while (!stack->isEmpty()) {
EXPECT_EQ(stack->size() - 1, static_cast<size_t>(stack->peek().payload()->value()));
stack->pop();
}
Persistent<HeapLinkedStack<TerminatedArrayItem> > pStack = stack;
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(stackSize, static_cast<size_t>(IntWrapper::s_destructorCalls));
EXPECT_EQ(0u, pStack->size());
}
TEST(HeapTest, AllocationDuringFinalization)
{
HeapStats initialHeapSize;
clearOutOldGarbage(&initialHeapSize);
IntWrapper::s_destructorCalls = 0;
OneKiloByteObject::s_destructorCalls = 0;
Persistent<IntWrapper> wrapper;
new FinalizationAllocator(&wrapper);
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(0, IntWrapper::s_destructorCalls);
EXPECT_EQ(42, wrapper->value());
wrapper.clear();
Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
EXPECT_EQ(10, IntWrapper::s_destructorCalls);
EXPECT_EQ(512, OneKiloByteObject::s_destructorCalls);
}
class SimpleClassWithDestructor {
public:
SimpleClassWithDestructor() { }
~SimpleClassWithDestructor()
{
ASSERT(!s_wasDestructed);
s_wasDestructed = true;
}
static bool s_wasDestructed;
};
bool SimpleClassWithDestructor::s_wasDestructed;
TEST(HeapTest, DestructorsCalledOnMapClear)
{
HeapHashMap<SimpleClassWithDestructor*, OwnPtr<SimpleClassWithDestructor> > map;
SimpleClassWithDestructor* hasDestructor = new SimpleClassWithDestructor();
map.add(hasDestructor, adoptPtr(hasDestructor));
SimpleClassWithDestructor::s_wasDestructed = false;
map.clear();
ASSERT(SimpleClassWithDestructor::s_wasDestructed);
}
}