2017-12-07 13:02:05 +00:00
|
|
|
// 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"
|
2018-07-23 11:42:37 +00:00
|
|
|
#include "src/api-inl.h"
|
2017-12-07 13:02:05 +00:00
|
|
|
#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);
|
|
|
|
}
|
|
|
|
|
2018-10-12 10:45:09 +00:00
|
|
|
bool AdvanceTracing(double deadline_in_ms) final {
|
2017-12-07 13:02:05 +00:00
|
|
|
for (auto persistent : to_register_with_v8_) {
|
|
|
|
persistent->RegisterExternalReference(isolate_);
|
|
|
|
}
|
|
|
|
to_register_with_v8_.clear();
|
2018-10-12 10:45:09 +00:00
|
|
|
return true;
|
2017-12-07 13:02:05 +00:00
|
|
|
}
|
|
|
|
|
2018-10-12 10:45:09 +00:00
|
|
|
bool IsTracingDone() final { return to_register_with_v8_.empty(); }
|
|
|
|
|
2017-12-07 13:02:05 +00:00
|
|
|
void TracePrologue() final {}
|
|
|
|
void TraceEpilogue() final {}
|
|
|
|
void AbortTracing() final {}
|
2018-08-21 17:14:19 +00:00
|
|
|
void EnterFinalPause(EmbedderStackState) final {}
|
2017-12-07 13:02:05 +00:00
|
|
|
|
|
|
|
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_;
|
|
|
|
};
|
|
|
|
|
2018-07-11 11:23:10 +00:00
|
|
|
class TemporaryEmbedderHeapTracerScope {
|
|
|
|
public:
|
|
|
|
TemporaryEmbedderHeapTracerScope(v8::Isolate* isolate,
|
|
|
|
EmbedderHeapTracer* tracer)
|
|
|
|
: isolate_(isolate) {
|
|
|
|
isolate_->SetEmbedderHeapTracer(tracer);
|
|
|
|
}
|
|
|
|
|
|
|
|
~TemporaryEmbedderHeapTracerScope() {
|
|
|
|
isolate_->SetEmbedderHeapTracer(nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
v8::Isolate* const isolate_;
|
|
|
|
};
|
|
|
|
|
2017-12-07 13:02:05 +00:00
|
|
|
} // 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);
|
2018-07-11 11:23:10 +00:00
|
|
|
TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
|
2017-12-07 13:02:05 +00:00
|
|
|
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);
|
2018-07-11 11:23:10 +00:00
|
|
|
TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
|
2017-12-07 13:02:05 +00:00
|
|
|
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);
|
2018-07-11 11:23:10 +00:00
|
|
|
TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
|
2017-12-07 13:02:05 +00:00
|
|
|
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);
|
2018-07-11 11:23:10 +00:00
|
|
|
TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
|
2017-12-07 13:02:05 +00:00
|
|
|
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 =
|
2018-06-11 09:53:20 +00:00
|
|
|
handle(JSObject::cast(*v8::Utils::OpenHandle(*key)), i_isolate);
|
2017-12-07 13:02:05 +00:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2018-07-11 11:23:10 +00:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
2018-08-21 17:14:19 +00:00
|
|
|
TEST(GarbageCollectionForTesting) {
|
|
|
|
ManualGCScope manual_gc;
|
2018-08-24 07:50:54 +00:00
|
|
|
i::FLAG_expose_gc = true;
|
2018-08-21 17:14:19 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-12-07 13:02:05 +00:00
|
|
|
} // namespace heap
|
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|