blob: 47f89204ebe112d967ae512f84e0e7db5baf5391 [file] [log] [blame]
// Copyright 2018 The Chromium 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 "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_gc_controller.h"
#include "third_party/blink/renderer/core/testing/death_aware_script_wrappable.h"
#include "third_party/blink/renderer/core/testing/gc_object_liveness_observer.h"
#include "v8/include/v8.h"
namespace blink {
namespace v8_gc_integration_test {
void PreciselyCollectGarbage() {
ThreadState::Current()->CollectAllGarbage();
}
// The following directly calls testing GCs in V8 to avoid cluttering a globally
// visible interface with calls that have to be carefully staged.
void RunV8MinorGC(v8::Isolate* isolate) {
CHECK(isolate);
isolate->RequestGarbageCollectionForTesting(
v8::Isolate::GarbageCollectionType::kMinorGarbageCollection);
}
void RunV8FullGCWithoutScanningOilpanStack(v8::Isolate* isolate) {
CHECK(isolate);
V8GCController::CollectAllGarbageForTesting(
isolate, v8::EmbedderHeapTracer::EmbedderStackState::kEmpty);
}
} // namespace v8_gc_integration_test
// =============================================================================
// Tests that ScriptWrappable and its wrapper survive or are reclaimed in
// certain garbage collection scenarios.
// =============================================================================
TEST(ScriptWrappableV8GCIntegrationTest, V8ReportsLiveObjectsDuringFullGc) {
V8TestingScope scope;
v8::Isolate* isolate = scope.GetIsolate();
v8::Persistent<v8::Value> holder;
GCObjectLivenessObserver<DeathAwareScriptWrappable> observer;
{
v8::HandleScope handle_scope(isolate);
DeathAwareScriptWrappable* object = DeathAwareScriptWrappable::Create();
observer.Observe(object);
holder.Reset(isolate, ToV8(object, scope.GetContext()->Global(), isolate));
}
v8_gc_integration_test::RunV8MinorGC(isolate);
v8_gc_integration_test::PreciselyCollectGarbage();
EXPECT_FALSE(observer.WasCollected());
holder.Reset();
}
TEST(ScriptWrappableV8GCIntegrationTest, V8ReportsLiveObjectsDuringScavenger) {
V8TestingScope scope;
v8::Isolate* isolate = scope.GetIsolate();
GCObjectLivenessObserver<DeathAwareScriptWrappable> observer;
{
v8::HandleScope handle_scope(isolate);
DeathAwareScriptWrappable* object = DeathAwareScriptWrappable::Create();
observer.Observe(object);
v8::Local<v8::Value> wrapper =
ToV8(object, scope.GetContext()->Global(), isolate);
EXPECT_TRUE(wrapper->IsObject());
v8::Local<v8::Object> wrapper_object =
wrapper->ToObject(scope.GetContext()).ToLocalChecked();
// V8 collects wrappers with unmodified maps (as they can be recreated
// without losing any data if needed). We need to create some property on
// wrapper so V8 will not see it as unmodified.
EXPECT_TRUE(
wrapper_object->CreateDataProperty(scope.GetContext(), 1, wrapper)
.IsJust());
}
// Scavenger should not collect JavaScript wrappers that are modified, even if
// they are otherwise unreachable.
v8_gc_integration_test::RunV8MinorGC(isolate);
v8_gc_integration_test::PreciselyCollectGarbage();
EXPECT_FALSE(observer.WasCollected());
}
TEST(ScriptWrappableV8GCIntegrationTest,
OilpanDoesntCollectObjectsReachableFromV8) {
V8TestingScope scope;
v8::Isolate* isolate = scope.GetIsolate();
v8::Persistent<v8::Value> holder;
GCObjectLivenessObserver<DeathAwareScriptWrappable> observer;
{
v8::HandleScope handle_scope(isolate);
DeathAwareScriptWrappable* object = DeathAwareScriptWrappable::Create();
observer.Observe(object);
// Creates new V8 wrapper and associates it with global scope
holder.Reset(isolate, ToV8(object, scope.GetContext()->Global(), isolate));
}
v8_gc_integration_test::RunV8MinorGC(isolate);
v8_gc_integration_test::RunV8FullGCWithoutScanningOilpanStack(isolate);
v8_gc_integration_test::PreciselyCollectGarbage();
EXPECT_FALSE(observer.WasCollected());
holder.Reset();
}
TEST(ScriptWrappableV8GCIntegrationTest,
OilpanCollectObjectsNotReachableFromV8) {
V8TestingScope scope;
v8::Isolate* isolate = scope.GetIsolate();
GCObjectLivenessObserver<DeathAwareScriptWrappable> observer;
{
v8::HandleScope handle_scope(isolate);
DeathAwareScriptWrappable* object = DeathAwareScriptWrappable::Create();
observer.Observe(object);
// Creates new V8 wrapper and associates it with global scope
ToV8(object, scope.GetContext()->Global(), isolate);
}
v8_gc_integration_test::RunV8MinorGC(isolate);
v8_gc_integration_test::RunV8FullGCWithoutScanningOilpanStack(isolate);
v8_gc_integration_test::PreciselyCollectGarbage();
EXPECT_TRUE(observer.WasCollected());
}
} // namespace blink