Commit 76c93685 authored by Michael Lippautz's avatar Michael Lippautz Committed by Commit Bot

[api, global-handles] Add TracedGlobal

TracedGlobal integrates with the use case of EmbedderHeapTracer and replaces
regular weak Global or Persistent nodes for such cases. This allows to simplify
the case for regular weak handles in a sense that they follow regular weak
semantics (if the underlying object is otherwise unreachable the weak handle
will be reset).

TracedGlobal requires slightly different semantics in the sense that it can be
required to keep them alive on Scavenge garbage collections because there's a
transitive path that is only known when using the EmbedderHeapTracer.
TracedGlobal accomodates that use case.

TracedGlobal follows move semantics and can thus be used in regular std
containers without wrapping data structure.

The internal state uses 20% less memory and allows for only iterating those
nodes when necessary. The design trades the virtual call when iterating
interesting persistents in the GC prologue with calling out through the
EmbedderHeapTracer for each node which is also a virtual call. There is one less
iteration over the set of handles required though and the design is robust
against recursive GCs that mutate the embedder state during the prologue
callback.

Bug: chromium:923361
Change-Id: Idbacfbe4723cd12af9de21058a4792e51dc4df74
Reviewed-on: https://chromium-review.googlesource.com/c/1425523
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59183}
parent d8baf215
This diff is collapsed.
...@@ -8,6 +8,7 @@ include_rules = [ ...@@ -8,6 +8,7 @@ include_rules = [
"+src/compiler/code-assembler.h", "+src/compiler/code-assembler.h",
"+src/compiler/wasm-compiler.h", "+src/compiler/wasm-compiler.h",
"-src/heap", "-src/heap",
"+src/heap/embedder-tracing.h",
"+src/heap/factory.h", "+src/heap/factory.h",
"+src/heap/factory-inl.h", "+src/heap/factory-inl.h",
"+src/heap/heap.h", "+src/heap/heap.h",
......
...@@ -1012,6 +1012,19 @@ i::Address* V8::GlobalizeReference(i::Isolate* isolate, i::Address* obj) { ...@@ -1012,6 +1012,19 @@ i::Address* V8::GlobalizeReference(i::Isolate* isolate, i::Address* obj) {
return result.location(); return result.location();
} }
i::Address* V8::GlobalizeTracedReference(i::Isolate* isolate, i::Address* obj,
internal::Address* slot) {
LOG_API(isolate, TracedGlobal, New);
i::Handle<i::Object> result =
isolate->global_handles()->CreateTraced(*obj, slot);
#ifdef VERIFY_HEAP
if (i::FLAG_verify_heap) {
i::Object(*obj)->ObjectVerify(isolate);
}
#endif // VERIFY_HEAP
return result.location();
}
i::Address* V8::CopyGlobalReference(i::Address* from) { i::Address* V8::CopyGlobalReference(i::Address* from) {
i::Handle<i::Object> result = i::GlobalHandles::CopyGlobal(from); i::Handle<i::Object> result = i::GlobalHandles::CopyGlobal(from);
return result.location(); return result.location();
...@@ -1021,6 +1034,11 @@ void V8::MoveGlobalReference(internal::Address** from, internal::Address** to) { ...@@ -1021,6 +1034,11 @@ void V8::MoveGlobalReference(internal::Address** from, internal::Address** to) {
i::GlobalHandles::MoveGlobal(from, to); i::GlobalHandles::MoveGlobal(from, to);
} }
void V8::MoveTracedGlobalReference(internal::Address** from,
internal::Address** to) {
i::GlobalHandles::MoveTracedGlobal(from, to);
}
void V8::RegisterExternallyReferencedObject(i::Address* location, void V8::RegisterExternallyReferencedObject(i::Address* location,
i::Isolate* isolate) { i::Isolate* isolate) {
isolate->heap()->RegisterExternallyReferencedObject(location); isolate->heap()->RegisterExternallyReferencedObject(location);
...@@ -1048,6 +1066,10 @@ void V8::DisposeGlobal(i::Address* location) { ...@@ -1048,6 +1066,10 @@ void V8::DisposeGlobal(i::Address* location) {
i::GlobalHandles::Destroy(location); i::GlobalHandles::Destroy(location);
} }
void V8::DisposeTracedGlobal(internal::Address* location) {
i::GlobalHandles::DestroyTraced(location);
}
Value* V8::Eternalize(Isolate* v8_isolate, Value* value) { Value* V8::Eternalize(Isolate* v8_isolate, Value* value) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate); i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
i::Object object = *Utils::OpenHandle(value); i::Object object = *Utils::OpenHandle(value);
...@@ -10469,6 +10491,22 @@ void EmbedderHeapTracer::GarbageCollectionForTesting( ...@@ -10469,6 +10491,22 @@ void EmbedderHeapTracer::GarbageCollectionForTesting(
kGCCallbackFlagForced); kGCCallbackFlagForced);
} }
void EmbedderHeapTracer::RegisterEmbedderReference(
const TracedGlobal<v8::Value>& ref) {
if (ref.IsEmpty()) return;
i::Heap* const heap = reinterpret_cast<i::Isolate*>(isolate_)->heap();
heap->RegisterExternallyReferencedObject(
reinterpret_cast<i::Address*>(ref.val_));
}
void EmbedderHeapTracer::IterateTracedGlobalHandles(
TracedGlobalHandleVisitor* visitor) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(isolate_);
i::DisallowHeapAllocation no_allocation;
isolate->global_handles()->IterateTracedNodes(visitor);
}
namespace internal { namespace internal {
const size_t HandleScopeImplementer::kEnteredContextsOffset = const size_t HandleScopeImplementer::kEnteredContextsOffset =
......
...@@ -839,6 +839,7 @@ class RuntimeCallTimer final { ...@@ -839,6 +839,7 @@ class RuntimeCallTimer final {
V(SymbolObject_New) \ V(SymbolObject_New) \
V(SymbolObject_SymbolValue) \ V(SymbolObject_SymbolValue) \
V(SyntaxError_New) \ V(SyntaxError_New) \
V(TracedGlobal_New) \
V(TryCatch_StackTrace) \ V(TryCatch_StackTrace) \
V(TypeError_New) \ V(TypeError_New) \
V(Uint16Array_New) \ V(Uint16Array_New) \
......
This diff is collapsed.
...@@ -40,14 +40,19 @@ enum WeaknessType { ...@@ -40,14 +40,19 @@ enum WeaknessType {
// callbacks and finalizers attached to them. // callbacks and finalizers attached to them.
class GlobalHandles final { class GlobalHandles final {
public: public:
template <class NodeType>
class NodeBlock;
// Move a global handle. // Move a global handle.
static void MoveGlobal(Address** from, Address** to); static void MoveGlobal(Address** from, Address** to);
static void MoveTracedGlobal(Address** from, Address** to);
// Copy a global handle.
static Handle<Object> CopyGlobal(Address* location);
// Destroy a global handle. // Destroy a global handle.
static void Destroy(Address* location); static void Destroy(Address* location);
static void DestroyTraced(Address* location);
// Copy a global handle.
static Handle<Object> CopyGlobal(Address* location);
// Make the global handle weak and set the callback parameter for the // Make the global handle weak and set the callback parameter for the
// handle. When the garbage collector recognizes that only weak global // handle. When the garbage collector recognizes that only weak global
...@@ -90,6 +95,9 @@ class GlobalHandles final { ...@@ -90,6 +95,9 @@ class GlobalHandles final {
return Handle<T>::cast(Create(Object(value))); return Handle<T>::cast(Create(Object(value)));
} }
Handle<Object> CreateTraced(Object value, Address* slot);
Handle<Object> CreateTraced(Address value, Address* slot);
void RecordStats(HeapStats* stats); void RecordStats(HeapStats* stats);
size_t InvokeFirstPassWeakCallbacks(); size_t InvokeFirstPassWeakCallbacks();
...@@ -103,7 +111,6 @@ class GlobalHandles final { ...@@ -103,7 +111,6 @@ class GlobalHandles final {
void IterateStrongRoots(RootVisitor* v); void IterateStrongRoots(RootVisitor* v);
void IterateWeakRoots(RootVisitor* v); void IterateWeakRoots(RootVisitor* v);
void IterateAllRoots(RootVisitor* v); void IterateAllRoots(RootVisitor* v);
void IterateAllNewSpaceRoots(RootVisitor* v); void IterateAllNewSpaceRoots(RootVisitor* v);
// Iterates over all handles that have embedder-assigned class ID. // Iterates over all handles that have embedder-assigned class ID.
...@@ -117,15 +124,22 @@ class GlobalHandles final { ...@@ -117,15 +124,22 @@ class GlobalHandles final {
// and have class IDs // and have class IDs
void IterateWeakRootsInNewSpaceWithClassIds(v8::PersistentHandleVisitor* v); void IterateWeakRootsInNewSpaceWithClassIds(v8::PersistentHandleVisitor* v);
// Iterates over weak roots on the heap. // Iterates over all traces handles represented by TracedGlobal.
void IterateTracedNodes(
v8::EmbedderHeapTracer::TracedGlobalHandleVisitor* visitor);
// Marks handles with finalizers on the predicate |should_reset_handle| as
// pending.
void IterateWeakRootsIdentifyFinalizers(
WeakSlotCallbackWithHeap should_reset_handle);
// Uses the provided visitor |v| to mark handles with finalizers that are
// pending.
void IterateWeakRootsForFinalizers(RootVisitor* v); void IterateWeakRootsForFinalizers(RootVisitor* v);
// Marks handles that are phantom or have callbacks based on the predicate
// |should_reset_handle| as pending.
void IterateWeakRootsForPhantomHandles( void IterateWeakRootsForPhantomHandles(
WeakSlotCallbackWithHeap should_reset_handle); WeakSlotCallbackWithHeap should_reset_handle);
// Marks all handles that should be finalized based on the predicate
// |should_reset_handle| as pending.
void IdentifyWeakHandles(WeakSlotCallbackWithHeap should_reset_handle);
// Note: The following *NewSpace* methods are used for the Scavenger to // Note: The following *NewSpace* methods are used for the Scavenger to
// identify and process handles in new space. The set of new space handles is // identify and process handles in new space. The set of new space handles is
// complete but the methods may encounter handles that are already in old // complete but the methods may encounter handles that are already in old
...@@ -167,13 +181,12 @@ class GlobalHandles final { ...@@ -167,13 +181,12 @@ class GlobalHandles final {
private: private:
// Internal node structures. // Internal node structures.
class Node; class Node;
template <class NodeType>
class NodeBlock;
template <class BlockType> template <class BlockType>
class NodeIterator; class NodeIterator;
template <class NodeType> template <class NodeType>
class NodeSpace; class NodeSpace;
class PendingPhantomCallback; class PendingPhantomCallback;
class TracedNode;
bool InRecursiveGC(unsigned gc_processing_counter); bool InRecursiveGC(unsigned gc_processing_counter);
...@@ -182,6 +195,8 @@ class GlobalHandles final { ...@@ -182,6 +195,8 @@ class GlobalHandles final {
size_t PostScavengeProcessing(unsigned post_processing_count); size_t PostScavengeProcessing(unsigned post_processing_count);
size_t PostMarkSweepProcessing(unsigned post_processing_count); size_t PostMarkSweepProcessing(unsigned post_processing_count);
template <typename T>
void UpdateAndCompactListOfNewSpaceNode(std::vector<T*>* node_list);
void UpdateListOfNewSpaceNodes(); void UpdateListOfNewSpaceNodes();
void ApplyPersistentHandleVisitor(v8::PersistentHandleVisitor* visitor, void ApplyPersistentHandleVisitor(v8::PersistentHandleVisitor* visitor,
...@@ -194,6 +209,9 @@ class GlobalHandles final { ...@@ -194,6 +209,9 @@ class GlobalHandles final {
// is accessed, some of the objects may have been promoted already. // is accessed, some of the objects may have been promoted already.
std::vector<Node*> new_space_nodes_; std::vector<Node*> new_space_nodes_;
std::unique_ptr<NodeSpace<TracedNode>> traced_nodes_;
std::vector<TracedNode*> traced_new_space_nodes_;
// Field always containing the number of handles to global objects. // Field always containing the number of handles to global objects.
size_t handles_count_ = 0; size_t handles_count_ = 0;
size_t number_of_phantom_handle_resets_ = 0; size_t number_of_phantom_handle_resets_ = 0;
......
...@@ -54,6 +54,10 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final { ...@@ -54,6 +54,10 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
bool Trace(double deadline); bool Trace(double deadline);
bool IsRemoteTracingDone(); bool IsRemoteTracingDone();
bool IsRootForNonTracingGC(const v8::TracedGlobal<v8::Value>& handle) {
return !InUse() || remote_tracer_->IsRootForNonTracingGC(handle);
}
void NotifyV8MarkingWorklistWasEmpty() { void NotifyV8MarkingWorklistWasEmpty() {
num_v8_marking_worklist_was_empty_++; num_v8_marking_worklist_was_empty_++;
} }
......
...@@ -1849,7 +1849,7 @@ void MarkCompactCollector::MarkLiveObjects() { ...@@ -1849,7 +1849,7 @@ void MarkCompactCollector::MarkLiveObjects() {
{ {
TRACE_GC(heap()->tracer(), TRACE_GC(heap()->tracer(),
GCTracer::Scope::MC_MARK_WEAK_CLOSURE_WEAK_HANDLES); GCTracer::Scope::MC_MARK_WEAK_CLOSURE_WEAK_HANDLES);
heap()->isolate()->global_handles()->IdentifyWeakHandles( heap()->isolate()->global_handles()->IterateWeakRootsIdentifyFinalizers(
&IsUnmarkedHeapObject); &IsUnmarkedHeapObject);
ProcessMarkingWorklist(); ProcessMarkingWorklist();
} }
......
...@@ -15,6 +15,10 @@ namespace v8 { ...@@ -15,6 +15,10 @@ namespace v8 {
namespace internal { namespace internal {
namespace heap { namespace heap {
void InvokeScavenge() { CcTest::CollectGarbage(i::NEW_SPACE); }
void InvokeMarkSweep() { CcTest::CollectAllGarbage(); }
void SealCurrentObjects(Heap* heap) { void SealCurrentObjects(Heap* heap) {
CcTest::CollectAllGarbage(); CcTest::CollectAllGarbage();
CcTest::CollectAllGarbage(); CcTest::CollectAllGarbage();
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef HEAP_HEAP_UTILS_H_ #ifndef HEAP_HEAP_UTILS_H_
#define HEAP_HEAP_UTILS_H_ #define HEAP_HEAP_UTILS_H_
#include "src/api-inl.h"
#include "src/heap/heap.h" #include "src/heap/heap.h"
namespace v8 { namespace v8 {
...@@ -52,6 +53,17 @@ void GcAndSweep(Heap* heap, AllocationSpace space); ...@@ -52,6 +53,17 @@ void GcAndSweep(Heap* heap, AllocationSpace space);
void ForceEvacuationCandidate(Page* page); void ForceEvacuationCandidate(Page* page);
void InvokeScavenge();
void InvokeMarkSweep();
template <typename GlobalOrPersistent>
bool InNewSpace(v8::Isolate* isolate, const GlobalOrPersistent& global) {
v8::HandleScope scope(isolate);
auto tmp = global.Get(isolate);
return i::Heap::InNewSpace(*v8::Utils::OpenHandle(*tmp));
}
} // namespace heap } // namespace heap
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include <unordered_map>
#include <vector>
#include "include/v8.h" #include "include/v8.h"
#include "src/api-inl.h" #include "src/api-inl.h"
#include "src/objects-inl.h" #include "src/objects-inl.h"
...@@ -9,6 +12,7 @@ ...@@ -9,6 +12,7 @@
#include "src/objects/script.h" #include "src/objects/script.h"
#include "src/objects/shared-function-info.h" #include "src/objects/shared-function-info.h"
#include "test/cctest/cctest.h" #include "test/cctest/cctest.h"
#include "test/cctest/heap/heap-utils.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -70,10 +74,19 @@ class TestEmbedderHeapTracer final : public v8::EmbedderHeapTracer { ...@@ -70,10 +74,19 @@ class TestEmbedderHeapTracer final : public v8::EmbedderHeapTracer {
return false; return false;
} }
void ConsiderTracedGlobalAsRoot(bool value) {
consider_traced_global_as_root_ = value;
}
bool IsRootForNonTracingGC(const v8::TracedGlobal<v8::Value>& handle) final {
return consider_traced_global_as_root_;
}
private: private:
v8::Isolate* const isolate_; v8::Isolate* const isolate_;
std::vector<std::pair<void*, void*>> registered_from_v8_; std::vector<std::pair<void*, void*>> registered_from_v8_;
std::vector<v8::Persistent<v8::Object>*> to_register_with_v8_; std::vector<v8::Persistent<v8::Object>*> to_register_with_v8_;
bool consider_traced_global_as_root_ = true;
}; };
class TemporaryEmbedderHeapTracerScope { class TemporaryEmbedderHeapTracerScope {
...@@ -265,6 +278,218 @@ TEST(GarbageCollectionForTesting) { ...@@ -265,6 +278,218 @@ TEST(GarbageCollectionForTesting) {
CHECK_GT(i_isolate->heap()->gc_count(), saved_gc_counter); CHECK_GT(i_isolate->heap()->gc_count(), saved_gc_counter);
} }
namespace {
void ConstructJSObject(v8::Isolate* isolate, v8::Local<v8::Context> context,
v8::TracedGlobal<v8::Object>* global) {
v8::HandleScope scope(isolate);
v8::Local<v8::Object> object(v8::Object::New(isolate));
CHECK(!object.IsEmpty());
*global = v8::TracedGlobal<v8::Object>(isolate, object);
CHECK(!global->IsEmpty());
}
void ConstructJSApiObject(v8::Isolate* isolate, v8::Local<v8::Context> context,
v8::TracedGlobal<v8::Object>* global) {
v8::HandleScope scope(isolate);
v8::Local<v8::Object> object(
ConstructTraceableJSApiObject(context, nullptr, nullptr));
CHECK(!object.IsEmpty());
*global = v8::TracedGlobal<v8::Object>(isolate, object);
CHECK(!global->IsEmpty());
}
enum class SurvivalMode { kSurvives, kDies };
template <typename ModifierFunction, typename ConstructTracedGlobalFunction>
void TracedGlobalTest(v8::Isolate* isolate,
ConstructTracedGlobalFunction construct_function,
ModifierFunction modifier_function, void (*gc_function)(),
SurvivalMode survives) {
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
v8::TracedGlobal<v8::Object> global;
construct_function(isolate, context, &global);
CHECK(InNewSpace(isolate, global));
modifier_function(global);
gc_function();
CHECK_IMPLIES(survives == SurvivalMode::kSurvives, !global.IsEmpty());
CHECK_IMPLIES(survives == SurvivalMode::kDies, global.IsEmpty());
}
} // namespace
TEST(TracedGlobalReset) {
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::TracedGlobal<v8::Object> traced;
ConstructJSObject(isolate, isolate->GetCurrentContext(), &traced);
CHECK(!traced.IsEmpty());
traced.Reset();
CHECK(traced.IsEmpty());
}
TEST(TracedGlobalInStdVector) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
std::vector<v8::TracedGlobal<v8::Object>> vec;
{
v8::HandleScope scope(isolate);
vec.emplace_back(isolate, v8::Object::New(isolate));
}
CHECK(!vec[0].IsEmpty());
InvokeMarkSweep();
CHECK(vec[0].IsEmpty());
}
TEST(TracedGlobalInStdUnorderedMap) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
std::unordered_map<int, v8::TracedGlobal<v8::Object>> map;
{
v8::HandleScope scope(isolate);
map.emplace(std::piecewise_construct, std::forward_as_tuple(1),
std::forward_as_tuple(isolate, v8::Object::New(isolate)));
}
CHECK(!map[1].IsEmpty());
InvokeMarkSweep();
CHECK(map[1].IsEmpty());
}
TEST(TracedGlobalToUnmodifiedJSObjectDiesOnMarkSweep) {
CcTest::InitializeVM();
TracedGlobalTest(
CcTest::isolate(), ConstructJSObject,
[](const TracedGlobal<v8::Object>& global) {}, InvokeMarkSweep,
SurvivalMode::kDies);
}
TEST(TracedGlobalToUnmodifiedJSObjectSurvivesMarkSweepWhenHeldAliveOtherwise) {
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::Global<v8::Object> strong_global;
TracedGlobalTest(
CcTest::isolate(), ConstructJSObject,
[isolate, &strong_global](const TracedGlobal<v8::Object>& global) {
v8::HandleScope scope(isolate);
strong_global = v8::Global<v8::Object>(isolate, global.Get(isolate));
},
InvokeMarkSweep, SurvivalMode::kSurvives);
}
TEST(TracedGlobalToUnmodifiedJSObjectSurvivesScavenge) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
TracedGlobalTest(
CcTest::isolate(), ConstructJSObject,
[](const TracedGlobal<v8::Object>& global) {}, InvokeScavenge,
SurvivalMode::kSurvives);
}
TEST(TracedGlobalToUnmodifiedJSObjectSurvivesScavengeWhenExcludedFromRoots) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
TestEmbedderHeapTracer tracer(isolate);
TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
tracer.ConsiderTracedGlobalAsRoot(false);
TracedGlobalTest(
CcTest::isolate(), ConstructJSObject,
[](const TracedGlobal<v8::Object>& global) {}, InvokeScavenge,
SurvivalMode::kSurvives);
}
TEST(TracedGlobalToUnmodifiedJSApiObjectSurvivesScavengePerDefault) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
TestEmbedderHeapTracer tracer(isolate);
TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
tracer.ConsiderTracedGlobalAsRoot(true);
TracedGlobalTest(
CcTest::isolate(), ConstructJSApiObject,
[](const TracedGlobal<v8::Object>& global) {}, InvokeScavenge,
SurvivalMode::kSurvives);
}
TEST(TracedGlobalToUnmodifiedJSApiObjectDiesOnScavengeWhenExcludedFromRoots) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
TestEmbedderHeapTracer tracer(isolate);
TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
tracer.ConsiderTracedGlobalAsRoot(false);
TracedGlobalTest(
CcTest::isolate(), ConstructJSApiObject,
[](const TracedGlobal<v8::Object>& global) {}, InvokeScavenge,
SurvivalMode::kDies);
}
TEST(TracedGlobalWrapperClassId) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
TestEmbedderHeapTracer tracer(isolate);
TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
v8::TracedGlobal<v8::Object> traced;
ConstructJSObject(isolate, isolate->GetCurrentContext(), &traced);
CHECK_EQ(0, traced.WrapperClassId());
traced.SetWrapperClassId(17);
CHECK_EQ(17, traced.WrapperClassId());
}
namespace {
class TracedGlobalVisitor final
: public v8::EmbedderHeapTracer::TracedGlobalHandleVisitor {
public:
~TracedGlobalVisitor() override = default;
void VisitTracedGlobalHandle(const TracedGlobal<Value>& value) final {
if (value.WrapperClassId() == 57) {
count_++;
}
}
size_t count() const { return count_; }
private:
size_t count_ = 0;
};
} // namespace
TEST(TracedGlobalIteration) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
TestEmbedderHeapTracer tracer(isolate);
TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
v8::TracedGlobal<v8::Object> traced;
ConstructJSObject(isolate, isolate->GetCurrentContext(), &traced);
CHECK(!traced.IsEmpty());
traced.SetWrapperClassId(57);
TracedGlobalVisitor visitor;
{
v8::HandleScope scope(isolate);
tracer.IterateTracedGlobalHandles(&visitor);
}
CHECK_EQ(1, visitor.count());
}
} // namespace heap } // namespace heap
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "src/isolate.h" #include "src/isolate.h"
#include "src/objects-inl.h" #include "src/objects-inl.h"
#include "test/cctest/cctest.h" #include "test/cctest/cctest.h"
#include "test/cctest/heap/heap-utils.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -103,12 +104,7 @@ void WeakHandleTest(v8::Isolate* isolate, ConstructFunction construct_function, ...@@ -103,12 +104,7 @@ void WeakHandleTest(v8::Isolate* isolate, ConstructFunction construct_function,
FlagAndPersistent fp; FlagAndPersistent fp;
construct_function(isolate, context, &fp); construct_function(isolate, context, &fp);
{ CHECK(heap::InNewSpace(isolate, fp.handle));
v8::HandleScope scope(isolate);
v8::Local<v8::Object> tmp = v8::Local<v8::Object>::New(isolate, fp.handle);
CHECK(i::Heap::InNewSpace(*v8::Utils::OpenHandle(*tmp)));
}
fp.handle.SetWeak(&fp, &ResetHandleAndSetFlag, fp.handle.SetWeak(&fp, &ResetHandleAndSetFlag,
v8::WeakCallbackType::kParameter); v8::WeakCallbackType::kParameter);
fp.flag = false; fp.flag = false;
...@@ -495,12 +491,7 @@ TEST(GCFromWeakCallbacks) { ...@@ -495,12 +491,7 @@ TEST(GCFromWeakCallbacks) {
for (int inner_gc = 0; inner_gc < kNumberOfGCTypes; inner_gc++) { for (int inner_gc = 0; inner_gc < kNumberOfGCTypes; inner_gc++) {
FlagAndPersistent fp; FlagAndPersistent fp;
ConstructJSApiObject(isolate, context, &fp); ConstructJSApiObject(isolate, context, &fp);
{ CHECK(heap::InNewSpace(isolate, fp.handle));
v8::HandleScope scope(isolate);
v8::Local<v8::Object> tmp =
v8::Local<v8::Object>::New(isolate, fp.handle);
CHECK(i::Heap::InNewSpace(*v8::Utils::OpenHandle(*tmp)));
}
fp.flag = false; fp.flag = false;
fp.handle.SetWeak(&fp, gc_forcing_callback[inner_gc], fp.handle.SetWeak(&fp, gc_forcing_callback[inner_gc],
v8::WeakCallbackType::kParameter); v8::WeakCallbackType::kParameter);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment