Index: Source/platform/heap/HeapTest.cpp |
diff --git a/Source/platform/heap/HeapTest.cpp b/Source/platform/heap/HeapTest.cpp |
index f15079494805380de6832ec0e87d57e2800f39a6..242d4453c63930d4cbeec3a53a8b93913f55df36 100644 |
--- a/Source/platform/heap/HeapTest.cpp |
+++ b/Source/platform/heap/HeapTest.cpp |
@@ -3887,6 +3887,104 @@ TEST(HeapTest, EmbeddedInDeque) |
EXPECT_EQ(8, SimpleFinalizedObject::s_destructorCalls); |
} |
+class InlinedVectorObject { |
+ ALLOW_ONLY_INLINE_ALLOCATION(); |
+public: |
+ InlinedVectorObject() |
+ { |
+ } |
+ ~InlinedVectorObject() |
+ { |
+ s_destructorCalls++; |
+ } |
+ DEFINE_INLINE_TRACE() |
+ { |
+ } |
+ |
+ static int s_destructorCalls; |
+}; |
+ |
+int InlinedVectorObject::s_destructorCalls = 0; |
+ |
+} // namespace blink |
+ |
+WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(blink::InlinedVectorObject); |
+ |
+namespace blink { |
+ |
+class InlinedVectorObjectWrapper final : public GarbageCollectedFinalized<InlinedVectorObjectWrapper> { |
+public: |
+ InlinedVectorObjectWrapper() |
+ { |
+ InlinedVectorObject i1, i2; |
+ m_vector1.append(i1); |
+ m_vector1.append(i2); |
+ m_vector2.append(i1); |
+ m_vector2.append(i2); // This allocates an out-of-line buffer. |
+ m_vector3.append(i1); |
+ m_vector3.append(i2); |
+ } |
+ |
+ DEFINE_INLINE_TRACE() |
+ { |
+ visitor->trace(m_vector1); |
+ visitor->trace(m_vector2); |
+ visitor->trace(m_vector3); |
+ } |
+ |
+private: |
+ HeapVector<InlinedVectorObject> m_vector1; |
+ HeapVector<InlinedVectorObject, 1> m_vector2; |
+ HeapVector<InlinedVectorObject, 2> m_vector3; |
+}; |
+ |
+TEST(HeapTest, VectorDestructors) |
+{ |
+ clearOutOldGarbage(); |
+ InlinedVectorObject::s_destructorCalls = 0; |
+ { |
+ HeapVector<InlinedVectorObject> vector; |
+ InlinedVectorObject i1, i2; |
+ vector.append(i1); |
+ vector.append(i2); |
+ } |
+ Heap::collectGarbage(ThreadState::NoHeapPointersOnStack, ThreadState::GCWithSweep, Heap::ForcedGC); |
+ // This is not EXPECT_EQ but EXPECT_LE because a HeapVectorBacking calls |
+ // destructors for all elements in (not the size but) the capacity of |
+ // the vector. Thus the number of destructors called becomes larger |
+ // than the actual number of objects in the vector. |
+ EXPECT_LE(4, InlinedVectorObject::s_destructorCalls); |
+ |
+ InlinedVectorObject::s_destructorCalls = 0; |
+ { |
+ HeapVector<InlinedVectorObject, 1> vector; |
+ InlinedVectorObject i1, i2; |
+ vector.append(i1); |
+ vector.append(i2); // This allocates an out-of-line buffer. |
+ } |
+ Heap::collectGarbage(ThreadState::NoHeapPointersOnStack, ThreadState::GCWithSweep, Heap::ForcedGC); |
+ EXPECT_LE(4, InlinedVectorObject::s_destructorCalls); |
+ |
+ InlinedVectorObject::s_destructorCalls = 0; |
+ { |
+ HeapVector<InlinedVectorObject, 2> vector; |
+ InlinedVectorObject i1, i2; |
+ vector.append(i1); |
+ vector.append(i2); |
+ } |
+ Heap::collectGarbage(ThreadState::NoHeapPointersOnStack, ThreadState::GCWithSweep, Heap::ForcedGC); |
+ EXPECT_LE(4, InlinedVectorObject::s_destructorCalls); |
+ |
+ InlinedVectorObject::s_destructorCalls = 0; |
+ { |
+ new InlinedVectorObjectWrapper(); |
+ Heap::collectGarbage(ThreadState::HeapPointersOnStack, ThreadState::GCWithSweep, Heap::ForcedGC); |
+ EXPECT_EQ(2, InlinedVectorObject::s_destructorCalls); |
+ } |
+ Heap::collectGarbage(ThreadState::NoHeapPointersOnStack, ThreadState::GCWithSweep, Heap::ForcedGC); |
+ EXPECT_LE(8, InlinedVectorObject::s_destructorCalls); |
+} |
+ |
template<typename Set> |
void rawPtrInHashHelper() |
{ |
@@ -5555,6 +5653,140 @@ TEST(HeapTest, DequeExpand) |
} |
} |
+class SimpleRefValue : public RefCounted<SimpleRefValue> { |
+public: |
+ static PassRefPtr<SimpleRefValue> create(int i) |
+ { |
+ return adoptRef(new SimpleRefValue(i)); |
+ } |
+ |
+ int value() const { return m_value; } |
+private: |
+ explicit SimpleRefValue(int value) |
+ : m_value(value) |
+ { |
+ } |
+ |
+ int m_value; |
+}; |
+ |
+class PartObjectWithRef { |
+ ALLOW_ONLY_INLINE_ALLOCATION(); |
+public: |
+ PartObjectWithRef(int i) |
+ : m_value(SimpleRefValue::create(i)) |
+ { |
+ } |
+ |
+ int value() const { return m_value->value(); } |
+ |
+private: |
+ RefPtr<SimpleRefValue> m_value; |
+}; |
+ |
+} // namespace blink |
+ |
+WTF_ALLOW_INIT_WITH_MEM_FUNCTIONS(blink::PartObjectWithRef); |
+ |
+namespace blink { |
+ |
+TEST(HeapTest, DequePartObjectsExpand) |
+{ |
+ // Test expansion of HeapDeque<PartObject> |
+ |
+ using PartDeque = HeapDeque<PartObjectWithRef>; |
+ |
+ Persistent<PartDeque> deque = new PartDeque(); |
+ // Auxillary Deque used to prevent 'inline' buffer expansion. |
+ Persistent<PartDeque> dequeUnused = new PartDeque(); |
+ |
+ // Append a sequence, bringing about repeated expansions of the |
+ // deque's buffer. |
+ int i = 0; |
+ for (; i < 60; ++i) { |
+ deque->append(PartObjectWithRef(i)); |
+ dequeUnused->append(PartObjectWithRef(i)); |
+ } |
+ |
+ EXPECT_EQ(60u, deque->size()); |
+ i = 0; |
+ for (const PartObjectWithRef& part : *deque) { |
+ EXPECT_EQ(i, part.value()); |
+ i++; |
+ } |
+ |
+ // Remove most of the queued objects and have the buffer's start index |
+ // 'point' somewhere into the buffer, just behind the end index. |
+ for (i = 0; i < 50; ++i) |
+ deque->takeFirst(); |
+ |
+ EXPECT_EQ(10u, deque->size()); |
+ i = 0; |
+ for (const PartObjectWithRef& part : *deque) { |
+ EXPECT_EQ(50 + i, part.value()); |
+ i++; |
+ } |
+ |
+ // Append even more, eventually causing an expansion of the underlying |
+ // buffer once the end index wraps around and reaches the start index. |
+ for (i = 0; i < 70; ++i) |
+ deque->append(PartObjectWithRef(60 + i)); |
+ |
+ // Verify that the final buffer expansion copied the start and end segments |
+ // of the old buffer to both ends of the expanded buffer, along with |
+ // re-adjusting both start&end indices in terms of that expanded buffer. |
+ EXPECT_EQ(80u, deque->size()); |
+ i = 0; |
+ for (const PartObjectWithRef& part : *deque) { |
+ EXPECT_EQ(i + 50, part.value()); |
+ i++; |
+ } |
+ |
+ for (i = 0; i < 70; ++i) |
+ deque->append(PartObjectWithRef(130 + i)); |
+ |
+ EXPECT_EQ(150u, deque->size()); |
+ i = 0; |
+ for (const PartObjectWithRef& part : *deque) { |
+ EXPECT_EQ(i + 50, part.value()); |
+ i++; |
+ } |
+} |
+ |
+TEST(HeapTest, HeapVectorPartObjects) |
+{ |
+ HeapVector<PartObjectWithRef> vector1; |
+ HeapVector<PartObjectWithRef> vector2; |
+ |
+ for (int i = 0; i < 10; ++i) { |
+ vector1.append(PartObjectWithRef(i)); |
+ vector2.append(PartObjectWithRef(i)); |
+ } |
+ |
+ vector1.reserveCapacity(150); |
+ EXPECT_EQ(150u, vector1.capacity()); |
+ EXPECT_EQ(10u, vector1.size()); |
+ |
+ vector2.reserveCapacity(100); |
+ EXPECT_EQ(100u, vector2.capacity()); |
+ EXPECT_EQ(10u, vector2.size()); |
+ |
+ for (int i = 0; i < 4; ++i) { |
+ vector1.append(PartObjectWithRef(10 + i)); |
+ vector2.append(PartObjectWithRef(10 + i)); |
+ vector2.append(PartObjectWithRef(10 + i)); |
+ } |
+ |
+ // Shrinking heap vector backing stores always succeeds, |
+ // so these two will not currently exercise the code path |
+ // where shrinking causes copying into a new, small buffer. |
+ vector2.shrinkToReasonableCapacity(); |
+ EXPECT_EQ(18u, vector2.size()); |
+ |
+ vector1.shrinkToReasonableCapacity(); |
+ EXPECT_EQ(14u, vector1.size()); |
+} |
+ |
namespace { |
enum GrowthDirection { |