// Copyright 2017 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "include/v8.h" #include "src/api-inl.h" #include "src/objects-inl.h" #include "src/objects/module.h" #include "src/objects/script.h" #include "src/objects/shared-function-info.h" #include "test/cctest/cctest.h" namespace v8 { namespace internal { namespace heap { namespace { v8::Local ConstructTraceableJSApiObject( v8::Local context, void* first_field, void* second_field) { v8::EscapableHandleScope scope(context->GetIsolate()); v8::Local function_t = v8::FunctionTemplate::New(context->GetIsolate()); v8::Local instance_t = function_t->InstanceTemplate(); instance_t->SetInternalFieldCount(2); v8::Local function = function_t->GetFunction(context).ToLocalChecked(); v8::Local instance = function->NewInstance(context).ToLocalChecked(); instance->SetAlignedPointerInInternalField(0, first_field); instance->SetAlignedPointerInInternalField(1, second_field); CHECK(!instance.IsEmpty()); i::Handle js_obj = v8::Utils::OpenHandle(*instance); CHECK_EQ(i::JS_API_OBJECT_TYPE, js_obj->map()->instance_type()); return scope.Escape(instance); } class TestEmbedderHeapTracer final : public v8::EmbedderHeapTracer { public: explicit TestEmbedderHeapTracer(v8::Isolate* isolate) : isolate_(isolate) {} void RegisterV8References( const std::vector>& embedder_fields) final { registered_from_v8_.insert(registered_from_v8_.end(), embedder_fields.begin(), embedder_fields.end()); } void AddReferenceForTracing(v8::Persistent* persistent) { to_register_with_v8_.push_back(persistent); } bool AdvanceTracing(double deadline_in_ms, AdvanceTracingActions actions) final { for (auto persistent : to_register_with_v8_) { persistent->RegisterExternalReference(isolate_); } to_register_with_v8_.clear(); return false; } void TracePrologue() final {} void TraceEpilogue() final {} void AbortTracing() final {} void EnterFinalPause(EmbedderStackState) final {} bool IsRegisteredFromV8(void* first_field) const { for (auto pair : registered_from_v8_) { if (pair.first == first_field) return true; } return false; } private: v8::Isolate* const isolate_; std::vector> registered_from_v8_; std::vector*> to_register_with_v8_; }; class TemporaryEmbedderHeapTracerScope { public: TemporaryEmbedderHeapTracerScope(v8::Isolate* isolate, EmbedderHeapTracer* tracer) : isolate_(isolate) { isolate_->SetEmbedderHeapTracer(tracer); } ~TemporaryEmbedderHeapTracerScope() { isolate_->SetEmbedderHeapTracer(nullptr); } private: v8::Isolate* const isolate_; }; } // namespace TEST(V8RegisteringEmbedderReference) { // Tests that wrappers are properly registered with the embedder heap // tracer. ManualGCScope manual_gc; CcTest::InitializeVM(); v8::Isolate* isolate = CcTest::isolate(); TestEmbedderHeapTracer tracer(isolate); TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer); v8::HandleScope scope(isolate); v8::Local context = v8::Context::New(isolate); v8::Context::Scope context_scope(context); void* first_field = reinterpret_cast(0x2); v8::Local api_object = ConstructTraceableJSApiObject(context, first_field, nullptr); CHECK(!api_object.IsEmpty()); CcTest::CollectGarbage(i::OLD_SPACE); CHECK(tracer.IsRegisteredFromV8(first_field)); } TEST(EmbedderRegisteringV8Reference) { // Tests that references that are registered by the embedder heap tracer are // considered live by V8. ManualGCScope manual_gc; CcTest::InitializeVM(); v8::Isolate* isolate = CcTest::isolate(); TestEmbedderHeapTracer tracer(isolate); TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer); v8::HandleScope scope(isolate); v8::Local context = v8::Context::New(isolate); v8::Context::Scope context_scope(context); v8::Persistent g; { v8::HandleScope inner_scope(isolate); v8::Local o = v8::Local::New(isolate, v8::Object::New(isolate)); g.Reset(isolate, o); g.SetWeak(); } tracer.AddReferenceForTracing(&g); CcTest::CollectGarbage(i::OLD_SPACE); CHECK(!g.IsEmpty()); } namespace { void ResurrectingFinalizer( const v8::WeakCallbackInfo>& data) { data.GetParameter()->ClearWeak(); } } // namespace TEST(TracingInRevivedSubgraph) { // Tests that wrappers are traced when they are contained with in a subgraph // that is revived by a finalizer. ManualGCScope manual_gc; CcTest::InitializeVM(); v8::Isolate* isolate = CcTest::isolate(); TestEmbedderHeapTracer tracer(isolate); TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer); v8::HandleScope scope(isolate); v8::Local context = v8::Context::New(isolate); v8::Context::Scope context_scope(context); v8::Global g; void* first_field = reinterpret_cast(0x4); { v8::HandleScope inner_scope(isolate); v8::Local api_object = ConstructTraceableJSApiObject(context, first_field, nullptr); CHECK(!api_object.IsEmpty()); v8::Local o = v8::Local::New(isolate, v8::Object::New(isolate)); o->Set(context, v8_str("link"), api_object).FromJust(); g.Reset(isolate, o); g.SetWeak(&g, ResurrectingFinalizer, v8::WeakCallbackType::kFinalizer); } CcTest::CollectGarbage(i::OLD_SPACE); CHECK(tracer.IsRegisteredFromV8(first_field)); } TEST(TracingInEphemerons) { // Tests that wrappers that are part of ephemerons are traced. ManualGCScope manual_gc; CcTest::InitializeVM(); v8::Isolate* isolate = CcTest::isolate(); TestEmbedderHeapTracer tracer(isolate); TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer); v8::HandleScope scope(isolate); v8::Local context = v8::Context::New(isolate); v8::Context::Scope context_scope(context); v8::Local key = v8::Local::New(isolate, v8::Object::New(isolate)); void* first_field = reinterpret_cast(0x8); i::Isolate* i_isolate = reinterpret_cast(isolate); Handle weak_map = i_isolate->factory()->NewJSWeakMap(); { v8::HandleScope inner_scope(isolate); v8::Local api_object = ConstructTraceableJSApiObject(context, first_field, nullptr); CHECK(!api_object.IsEmpty()); Handle js_key = handle(JSObject::cast(*v8::Utils::OpenHandle(*key)), i_isolate); Handle js_api_object = v8::Utils::OpenHandle(*api_object); int32_t hash = js_key->GetOrCreateHash(i_isolate)->value(); JSWeakCollection::Set(weak_map, js_key, js_api_object, hash); } CcTest::CollectGarbage(i::OLD_SPACE); CHECK(tracer.IsRegisteredFromV8(first_field)); } TEST(FinalizeTracingIsNoopWhenNotMarking) { ManualGCScope manual_gc; CcTest::InitializeVM(); v8::Isolate* isolate = CcTest::isolate(); Isolate* i_isolate = CcTest::i_isolate(); TestEmbedderHeapTracer tracer(isolate); TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer); // Finalize a potentially running garbage collection. i_isolate->heap()->CollectGarbage(OLD_SPACE, GarbageCollectionReason::kTesting); CHECK(i_isolate->heap()->incremental_marking()->IsStopped()); int gc_counter = i_isolate->heap()->gc_count(); tracer.FinalizeTracing(); CHECK(i_isolate->heap()->incremental_marking()->IsStopped()); CHECK_EQ(gc_counter, i_isolate->heap()->gc_count()); } TEST(FinalizeTracingWhenMarking) { ManualGCScope manual_gc; CcTest::InitializeVM(); v8::Isolate* isolate = CcTest::isolate(); Isolate* i_isolate = CcTest::i_isolate(); TestEmbedderHeapTracer tracer(isolate); TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer); // Finalize a potentially running garbage collection. i_isolate->heap()->CollectGarbage(OLD_SPACE, GarbageCollectionReason::kTesting); if (i_isolate->heap()->mark_compact_collector()->sweeping_in_progress()) { i_isolate->heap()->mark_compact_collector()->EnsureSweepingCompleted(); } CHECK(i_isolate->heap()->incremental_marking()->IsStopped()); i::IncrementalMarking* marking = i_isolate->heap()->incremental_marking(); marking->Start(i::GarbageCollectionReason::kTesting); // Sweeping is not runing so we should immediately start marking. CHECK(marking->IsMarking()); tracer.FinalizeTracing(); CHECK(marking->IsStopped()); } TEST(GarbageCollectionForTesting) { ManualGCScope manual_gc; i::FLAG_expose_gc = true; CcTest::InitializeVM(); v8::Isolate* isolate = CcTest::isolate(); Isolate* i_isolate = CcTest::i_isolate(); TestEmbedderHeapTracer tracer(isolate); TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer); int saved_gc_counter = i_isolate->heap()->gc_count(); tracer.GarbageCollectionForTesting(EmbedderHeapTracer::kUnknown); CHECK_GT(i_isolate->heap()->gc_count(), saved_gc_counter); } } // namespace heap } // namespace internal } // namespace v8