From 5de8e81c1f0a5a65cbca5d41ee910fe359ad0901 Mon Sep 17 00:00:00 2001 From: "mstarzinger@chromium.org" Date: Fri, 12 Apr 2013 09:54:51 +0000 Subject: [PATCH] Add tests for GlobalHandles::IterateObjectGroups. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BUG=none TEST=cctest/test-global-handles Review URL: https://codereview.chromium.org/13952005 Patch from Marja Hölttä . git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14249 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/global-handles.h | 5 + test/cctest/cctest.gyp | 1 + test/cctest/test-global-handles.cc | 311 +++++++++++++++++++++++++++++ 3 files changed, 317 insertions(+) create mode 100644 test/cctest/test-global-handles.cc diff --git a/src/global-handles.h b/src/global-handles.h index ffec6c2044..d4041ae394 100644 --- a/src/global-handles.h +++ b/src/global-handles.h @@ -32,10 +32,15 @@ #include "../include/v8-profiler.h" #include "list.h" +#include "v8utils.h" namespace v8 { namespace internal { +class GCTracer; +class HeapStats; +class ObjectVisitor; + // Structure for tracking global handles. // A single list keeps all the allocated global handles. // Destroyed handles stay in the list but is added to the free list. diff --git a/test/cctest/cctest.gyp b/test/cctest/cctest.gyp index ee9995b866..f58d958248 100644 --- a/test/cctest/cctest.gyp +++ b/test/cctest/cctest.gyp @@ -70,6 +70,7 @@ 'test-fixed-dtoa.cc', 'test-flags.cc', 'test-func-name-inference.cc', + 'test-global-handles.cc', 'test-global-object.cc', 'test-hashing.cc', 'test-hashmap.cc', diff --git a/test/cctest/test-global-handles.cc b/test/cctest/test-global-handles.cc new file mode 100644 index 0000000000..324ea8db11 --- /dev/null +++ b/test/cctest/test-global-handles.cc @@ -0,0 +1,311 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "global-handles.h" + +#include "cctest.h" + +using namespace v8::internal; +using v8::UniqueId; + +static int NumberOfWeakCalls = 0; +static void WeakPointerCallback(v8::Isolate* isolate, + v8::Persistent handle, + void* id) { + ASSERT(id == reinterpret_cast(1234)); + NumberOfWeakCalls++; + handle.Dispose(isolate); +} + +static List skippable_objects; +static List can_skip_called_objects; + +static bool CanSkipCallback(Heap* heap, Object** pointer) { + can_skip_called_objects.Add(*pointer); + return skippable_objects.Contains(*pointer); +} + +static void ResetCanSkipData() { + skippable_objects.Clear(); + can_skip_called_objects.Clear(); +} + +class TestRetainedObjectInfo : public v8::RetainedObjectInfo { + public: + TestRetainedObjectInfo() : has_been_disposed_(false) {} + + bool has_been_disposed() { return has_been_disposed_; } + + virtual void Dispose() { + ASSERT(!has_been_disposed_); + has_been_disposed_ = true; + } + + virtual bool IsEquivalent(v8::RetainedObjectInfo* other) { + return other == this; + } + + virtual intptr_t GetHash() { return 0; } + + virtual const char* GetLabel() { return "whatever"; } + + private: + bool has_been_disposed_; +}; + +class TestObjectVisitor : public ObjectVisitor { + public: + virtual void VisitPointers(Object** start, Object** end) { + for (Object** o = start; o != end; ++o) + visited.Add(*o); + } + + List visited; +}; + +TEST(IterateObjectGroupsOldApi) { + CcTest::InitializeVM(); + GlobalHandles* global_handles = Isolate::Current()->global_handles(); + + v8::HandleScope handle_scope(CcTest::isolate()); + + Handle g1s1 = + global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked()); + Handle g1s2 = + global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked()); + global_handles->MakeWeak(g1s1.location(), + reinterpret_cast(1234), + NULL, + &WeakPointerCallback); + global_handles->MakeWeak(g1s2.location(), + reinterpret_cast(1234), + NULL, + &WeakPointerCallback); + + Handle g2s1 = + global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked()); + Handle g2s2 = + global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked()); + global_handles->MakeWeak(g2s1.location(), + reinterpret_cast(1234), + NULL, + &WeakPointerCallback); + global_handles->MakeWeak(g2s2.location(), + reinterpret_cast(1234), + NULL, + &WeakPointerCallback); + + TestRetainedObjectInfo info1; + TestRetainedObjectInfo info2; + { + Object** g1_objects[] = { g1s1.location(), g1s2.location() }; + Object** g2_objects[] = { g2s1.location(), g2s2.location() }; + + global_handles->AddObjectGroup(g1_objects, 2, &info1); + global_handles->AddObjectGroup(g2_objects, 2, &info2); + } + + // Iterate the object groups. First skip all. + { + ResetCanSkipData(); + skippable_objects.Add(*g1s1.location()); + skippable_objects.Add(*g1s2.location()); + skippable_objects.Add(*g2s1.location()); + skippable_objects.Add(*g2s2.location()); + TestObjectVisitor visitor; + global_handles->IterateObjectGroups(&visitor, &CanSkipCallback); + + // CanSkipCallback was called for all objects. + ASSERT(can_skip_called_objects.length() == 4); + ASSERT(can_skip_called_objects.Contains(*g1s1.location())); + ASSERT(can_skip_called_objects.Contains(*g1s2.location())); + ASSERT(can_skip_called_objects.Contains(*g2s1.location())); + ASSERT(can_skip_called_objects.Contains(*g2s2.location())); + + // Nothing was visited. + ASSERT(visitor.visited.length() == 0); + ASSERT(!info1.has_been_disposed()); + ASSERT(!info2.has_been_disposed()); + } + + // Iterate again, now only skip the second object group. + { + ResetCanSkipData(); + // The first grough should still be visited, since only one object is + // skipped. + skippable_objects.Add(*g1s1.location()); + skippable_objects.Add(*g2s1.location()); + skippable_objects.Add(*g2s2.location()); + TestObjectVisitor visitor; + global_handles->IterateObjectGroups(&visitor, &CanSkipCallback); + + // CanSkipCallback was called for all objects. + ASSERT(can_skip_called_objects.length() == 3 || + can_skip_called_objects.length() == 4); + ASSERT(can_skip_called_objects.Contains(*g1s2.location())); + ASSERT(can_skip_called_objects.Contains(*g2s1.location())); + ASSERT(can_skip_called_objects.Contains(*g2s2.location())); + + // The first group was visited. + ASSERT(visitor.visited.length() == 2); + ASSERT(visitor.visited.Contains(*g1s1.location())); + ASSERT(visitor.visited.Contains(*g1s2.location())); + ASSERT(info1.has_been_disposed()); + ASSERT(!info2.has_been_disposed()); + } + + // Iterate again, don't skip anything. + { + ResetCanSkipData(); + TestObjectVisitor visitor; + global_handles->IterateObjectGroups(&visitor, &CanSkipCallback); + + // CanSkipCallback was called for all objects. + fprintf(stderr, "can skip len %d\n", can_skip_called_objects.length()); + ASSERT(can_skip_called_objects.length() == 1); + ASSERT(can_skip_called_objects.Contains(*g2s1.location()) || + can_skip_called_objects.Contains(*g2s2.location())); + + // The second group was visited. + ASSERT(visitor.visited.length() == 2); + ASSERT(visitor.visited.Contains(*g2s1.location())); + ASSERT(visitor.visited.Contains(*g2s2.location())); + ASSERT(info2.has_been_disposed()); + } +} + +TEST(IterateObjectGroups) { + CcTest::InitializeVM(); + GlobalHandles* global_handles = Isolate::Current()->global_handles(); + + v8::HandleScope handle_scope(CcTest::isolate()); + + Handle g1s1 = + global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked()); + Handle g1s2 = + global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked()); + global_handles->MakeWeak(g1s1.location(), + reinterpret_cast(1234), + NULL, + &WeakPointerCallback); + global_handles->MakeWeak(g1s2.location(), + reinterpret_cast(1234), + NULL, + &WeakPointerCallback); + + Handle g2s1 = + global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked()); + Handle g2s2 = + global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked()); + global_handles->MakeWeak(g2s1.location(), + reinterpret_cast(1234), + NULL, + &WeakPointerCallback); + global_handles->MakeWeak(g2s2.location(), + reinterpret_cast(1234), + NULL, + &WeakPointerCallback); + + TestRetainedObjectInfo info1; + TestRetainedObjectInfo info2; + { + global_handles->SetObjectGroupId(g2s1.location(), UniqueId(2)); + global_handles->SetObjectGroupId(g2s2.location(), UniqueId(2)); + global_handles->SetRetainedObjectInfo(UniqueId(2), &info2); + global_handles->SetObjectGroupId(g1s1.location(), UniqueId(1)); + global_handles->SetObjectGroupId(g1s2.location(), UniqueId(1)); + global_handles->SetRetainedObjectInfo(UniqueId(1), &info1); + } + + // Iterate the object groups. First skip all. + { + ResetCanSkipData(); + skippable_objects.Add(*g1s1.location()); + skippable_objects.Add(*g1s2.location()); + skippable_objects.Add(*g2s1.location()); + skippable_objects.Add(*g2s2.location()); + TestObjectVisitor visitor; + global_handles->IterateObjectGroups(&visitor, &CanSkipCallback); + + // CanSkipCallback was called for all objects. + ASSERT(can_skip_called_objects.length() == 4); + ASSERT(can_skip_called_objects.Contains(*g1s1.location())); + ASSERT(can_skip_called_objects.Contains(*g1s2.location())); + ASSERT(can_skip_called_objects.Contains(*g2s1.location())); + ASSERT(can_skip_called_objects.Contains(*g2s2.location())); + + // Nothing was visited. + ASSERT(visitor.visited.length() == 0); + ASSERT(!info1.has_been_disposed()); + ASSERT(!info2.has_been_disposed()); + } + + // Iterate again, now only skip the second object group. + { + ResetCanSkipData(); + // The first grough should still be visited, since only one object is + // skipped. + skippable_objects.Add(*g1s1.location()); + skippable_objects.Add(*g2s1.location()); + skippable_objects.Add(*g2s2.location()); + TestObjectVisitor visitor; + global_handles->IterateObjectGroups(&visitor, &CanSkipCallback); + + // CanSkipCallback was called for all objects. + ASSERT(can_skip_called_objects.length() == 3 || + can_skip_called_objects.length() == 4); + ASSERT(can_skip_called_objects.Contains(*g1s2.location())); + ASSERT(can_skip_called_objects.Contains(*g2s1.location())); + ASSERT(can_skip_called_objects.Contains(*g2s2.location())); + + // The first group was visited. + ASSERT(visitor.visited.length() == 2); + ASSERT(visitor.visited.Contains(*g1s1.location())); + ASSERT(visitor.visited.Contains(*g1s2.location())); + ASSERT(info1.has_been_disposed()); + ASSERT(!info2.has_been_disposed()); + } + + // Iterate again, don't skip anything. + { + ResetCanSkipData(); + TestObjectVisitor visitor; + global_handles->IterateObjectGroups(&visitor, &CanSkipCallback); + + // CanSkipCallback was called for all objects. + fprintf(stderr, "can skip len %d\n", can_skip_called_objects.length()); + ASSERT(can_skip_called_objects.length() == 1); + ASSERT(can_skip_called_objects.Contains(*g2s1.location()) || + can_skip_called_objects.Contains(*g2s2.location())); + + // The second group was visited. + ASSERT(visitor.visited.length() == 2); + ASSERT(visitor.visited.Contains(*g2s1.location())); + ASSERT(visitor.visited.Contains(*g2s2.location())); + ASSERT(info2.has_been_disposed()); + } +}