[heap] Increase test coverage for embedder tracing

The tests illustrate the use of v8::EmbedderHeapTracer.

Bug: v8:7176
Change-Id: Ic383c968691fddb0ec96d66cb33ee42b9c304a75
Reviewed-on: https://chromium-review.googlesource.com/811924
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49934}
This commit is contained in:
Michael Lippautz 2017-12-07 14:02:05 +01:00 committed by Commit Bot
parent 12afb22458
commit 2abbc50c6e
7 changed files with 222 additions and 20 deletions

View File

@ -1976,6 +1976,18 @@ void Factory::NewJSArrayStorage(Handle<JSArray> array,
array->set_length(Smi::FromInt(length));
}
Handle<JSWeakMap> Factory::NewJSWeakMap() {
Context* native_context = isolate()->raw_native_context();
Handle<Map> map(native_context->js_weak_map_fun()->initial_map());
Handle<JSWeakMap> weakmap(JSWeakMap::cast(*NewJSObjectFromMap(map)));
{
// Do not leak handles for the hash table, it would make entries strong.
HandleScope scope(isolate());
JSWeakCollection::Initialize(weakmap, isolate());
}
return weakmap;
}
Handle<JSModuleNamespace> Factory::NewJSModuleNamespace() {
Handle<Map> map = isolate()->js_module_namespace_map();
Handle<JSModuleNamespace> module_namespace(

View File

@ -36,6 +36,7 @@ class JSMapIterator;
class JSModuleNamespace;
class JSSet;
class JSSetIterator;
class JSWeakMap;
class NewFunctionArgs;
struct SourceRange;
class PreParsedScopeData;
@ -555,6 +556,8 @@ class V8_EXPORT_PRIVATE Factory final {
int capacity,
ArrayStorageAllocationMode mode = DONT_INITIALIZE_ARRAY_ELEMENTS);
Handle<JSWeakMap> NewJSWeakMap();
Handle<JSGeneratorObject> NewJSGeneratorObject(Handle<JSFunction> function);
Handle<JSModuleNamespace> NewJSModuleNamespace();

View File

@ -103,6 +103,7 @@ v8_source_set("cctest_sources") {
"heap/test-array-buffer-tracker.cc",
"heap/test-compaction.cc",
"heap/test-concurrent-marking.cc",
"heap/test-embedder-tracing.cc",
"heap/test-heap.cc",
"heap/test-incremental-marking.cc",
"heap/test-invalidated-slots.cc",

View File

@ -93,6 +93,7 @@
'heap/test-array-buffer-tracker.cc',
'heap/test-compaction.cc',
'heap/test-concurrent-marking.cc',
'heap/test-embedder-tracing.cc',
'heap/test-heap.cc',
'heap/test-incremental-marking.cc',
'heap/test-invalidated-slots.cc',

View File

@ -88,6 +88,9 @@
# BUG(2340). Preprocessing stack traces is disabled at the moment.
'test-heap/PreprocessStackTrace': [FAIL],
# BUG(7176). Embedder heap tracer doesn't support finalizers.
'test-embedder-tracing/TracingInRevivedSubgraph': [FAIL],
# BUG(4333). Function name inferrer does not work for ES6 clases.
'test-func-name-inference/UpperCaseClass': [FAIL],
'test-func-name-inference/LowerCaseClass': [FAIL],

View File

@ -0,0 +1,197 @@
// 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.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<v8::Object> ConstructTraceableJSApiObject(
v8::Local<v8::Context> context, void* first_field, void* second_field) {
v8::EscapableHandleScope scope(context->GetIsolate());
v8::Local<v8::FunctionTemplate> function_t =
v8::FunctionTemplate::New(context->GetIsolate());
v8::Local<v8::ObjectTemplate> instance_t = function_t->InstanceTemplate();
instance_t->SetInternalFieldCount(2);
v8::Local<v8::Function> function =
function_t->GetFunction(context).ToLocalChecked();
v8::Local<v8::Object> instance =
function->NewInstance(context).ToLocalChecked();
instance->SetAlignedPointerInInternalField(0, first_field);
instance->SetAlignedPointerInInternalField(1, second_field);
CHECK(!instance.IsEmpty());
i::Handle<i::JSReceiver> 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<std::pair<void*, void*>>& embedder_fields) final {
registered_from_v8_.insert(registered_from_v8_.end(),
embedder_fields.begin(), embedder_fields.end());
}
void AddReferenceForTracing(v8::Persistent<v8::Object>* 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() 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<std::pair<void*, void*>> registered_from_v8_;
std::vector<v8::Persistent<v8::Object>*> to_register_with_v8_;
};
} // 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);
isolate->SetEmbedderHeapTracer(&tracer);
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
void* first_field = reinterpret_cast<void*>(0x2);
v8::Local<v8::Object> 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);
isolate->SetEmbedderHeapTracer(&tracer);
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
v8::Persistent<v8::Object> g;
{
v8::HandleScope inner_scope(isolate);
v8::Local<v8::Object> o =
v8::Local<v8::Object>::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<v8::Global<v8::Object>>& 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);
isolate->SetEmbedderHeapTracer(&tracer);
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
v8::Global<v8::Object> g;
void* first_field = reinterpret_cast<void*>(0x4);
{
v8::HandleScope inner_scope(isolate);
v8::Local<v8::Object> api_object =
ConstructTraceableJSApiObject(context, first_field, nullptr);
CHECK(!api_object.IsEmpty());
v8::Local<v8::Object> o =
v8::Local<v8::Object>::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);
isolate->SetEmbedderHeapTracer(&tracer);
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
v8::Local<v8::Object> key =
v8::Local<v8::Object>::New(isolate, v8::Object::New(isolate));
void* first_field = reinterpret_cast<void*>(0x8);
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
Handle<JSWeakMap> weak_map = i_isolate->factory()->NewJSWeakMap();
{
v8::HandleScope inner_scope(isolate);
v8::Local<v8::Object> api_object =
ConstructTraceableJSApiObject(context, first_field, nullptr);
CHECK(!api_object.IsEmpty());
Handle<JSObject> js_key =
handle(JSObject::cast(*v8::Utils::OpenHandle(*key)));
Handle<JSReceiver> 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));
}
} // namespace heap
} // namespace internal
} // namespace v8

View File

@ -42,21 +42,6 @@ static Isolate* GetIsolateFrom(LocalContext* context) {
return reinterpret_cast<Isolate*>((*context)->GetIsolate());
}
static Handle<JSWeakMap> AllocateJSWeakMap(Isolate* isolate) {
Handle<Map> map =
isolate->factory()->NewMap(JS_WEAK_MAP_TYPE, JSWeakMap::kSize);
Handle<JSObject> weakmap_obj = isolate->factory()->NewJSObjectFromMap(map);
Handle<JSWeakMap> weakmap(JSWeakMap::cast(*weakmap_obj));
// Do not leak handles for the hash table, it would make entries strong.
{
HandleScope scope(isolate);
Handle<ObjectHashTable> table = ObjectHashTable::New(isolate, 1);
weakmap->set_table(*table);
}
return weakmap;
}
static int NumberOfWeakCalls = 0;
static void WeakPointerCallback(const v8::WeakCallbackInfo<void>& data) {
std::pair<v8::Persistent<v8::Value>*, int>* p =
@ -74,7 +59,7 @@ TEST(Weakness) {
Isolate* isolate = GetIsolateFrom(&context);
Factory* factory = isolate->factory();
HandleScope scope(isolate);
Handle<JSWeakMap> weakmap = AllocateJSWeakMap(isolate);
Handle<JSWeakMap> weakmap = isolate->factory()->NewJSWeakMap();
GlobalHandles* global_handles = isolate->global_handles();
// Keep global reference to the key.
@ -127,7 +112,7 @@ TEST(Shrinking) {
Isolate* isolate = GetIsolateFrom(&context);
Factory* factory = isolate->factory();
HandleScope scope(isolate);
Handle<JSWeakMap> weakmap = AllocateJSWeakMap(isolate);
Handle<JSWeakMap> weakmap = isolate->factory()->NewJSWeakMap();
// Check initial capacity.
CHECK_EQ(32, ObjectHashTable::cast(weakmap->table())->Capacity());
@ -174,7 +159,7 @@ TEST(Regress2060a) {
Handle<JSFunction> function =
factory->NewFunctionForTest(factory->function_string());
Handle<JSObject> key = factory->NewJSObject(function);
Handle<JSWeakMap> weakmap = AllocateJSWeakMap(isolate);
Handle<JSWeakMap> weakmap = isolate->factory()->NewJSWeakMap();
// Start second old-space page so that values land on evacuation candidate.
Page* first_page = heap->old_space()->anchor()->next_page();
@ -226,7 +211,7 @@ TEST(Regress2060b) {
CHECK(!heap->InNewSpace(*keys[i]));
CHECK(!first_page->Contains(keys[i]->address()));
}
Handle<JSWeakMap> weakmap = AllocateJSWeakMap(isolate);
Handle<JSWeakMap> weakmap = isolate->factory()->NewJSWeakMap();
for (int i = 0; i < 32; i++) {
Handle<Smi> smi(Smi::FromInt(i), isolate);
int32_t hash = keys[i]->GetOrCreateHash(isolate)->value();
@ -250,7 +235,7 @@ TEST(Regress399527) {
Heap* heap = isolate->heap();
{
HandleScope scope(isolate);
AllocateJSWeakMap(isolate);
isolate->factory()->NewJSWeakMap();
heap::SimulateIncrementalMarking(heap);
}
// The weak map is marked black here but leaving the handle scope will make