Line data Source code
1 : // Copyright 2017 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include "include/v8.h"
6 : #include "src/api-inl.h"
7 : #include "src/objects-inl.h"
8 : #include "src/objects/module.h"
9 : #include "src/objects/script.h"
10 : #include "src/objects/shared-function-info.h"
11 : #include "test/cctest/cctest.h"
12 :
13 : namespace v8 {
14 : namespace internal {
15 : namespace heap {
16 :
17 : namespace {
18 :
19 15 : v8::Local<v8::Object> ConstructTraceableJSApiObject(
20 : v8::Local<v8::Context> context, void* first_field, void* second_field) {
21 15 : v8::EscapableHandleScope scope(context->GetIsolate());
22 : v8::Local<v8::FunctionTemplate> function_t =
23 15 : v8::FunctionTemplate::New(context->GetIsolate());
24 15 : v8::Local<v8::ObjectTemplate> instance_t = function_t->InstanceTemplate();
25 15 : instance_t->SetInternalFieldCount(2);
26 : v8::Local<v8::Function> function =
27 15 : function_t->GetFunction(context).ToLocalChecked();
28 : v8::Local<v8::Object> instance =
29 : function->NewInstance(context).ToLocalChecked();
30 15 : instance->SetAlignedPointerInInternalField(0, first_field);
31 15 : instance->SetAlignedPointerInInternalField(1, second_field);
32 15 : CHECK(!instance.IsEmpty());
33 : i::Handle<i::JSReceiver> js_obj = v8::Utils::OpenHandle(*instance);
34 15 : CHECK_EQ(i::JS_API_OBJECT_TYPE, js_obj->map()->instance_type());
35 15 : return scope.Escape(instance);
36 : }
37 :
38 70 : class TestEmbedderHeapTracer final : public v8::EmbedderHeapTracer {
39 : public:
40 35 : explicit TestEmbedderHeapTracer(v8::Isolate* isolate) : isolate_(isolate) {}
41 :
42 15 : void RegisterV8References(
43 15 : const std::vector<std::pair<void*, void*>>& embedder_fields) final {
44 : registered_from_v8_.insert(registered_from_v8_.end(),
45 15 : embedder_fields.begin(), embedder_fields.end());
46 15 : }
47 :
48 : void AddReferenceForTracing(v8::Persistent<v8::Object>* persistent) {
49 5 : to_register_with_v8_.push_back(persistent);
50 : }
51 :
52 120 : bool AdvanceTracing(double deadline_in_ms) final {
53 245 : for (auto persistent : to_register_with_v8_) {
54 5 : persistent->RegisterExternalReference(isolate_);
55 : }
56 : to_register_with_v8_.clear();
57 120 : return true;
58 : }
59 :
60 400 : bool IsTracingDone() final { return to_register_with_v8_.empty(); }
61 :
62 40 : void TracePrologue() final {}
63 40 : void TraceEpilogue() final {}
64 0 : void AbortTracing() final {}
65 40 : void EnterFinalPause(EmbedderStackState) final {}
66 :
67 : bool IsRegisteredFromV8(void* first_field) const {
68 30 : for (auto pair : registered_from_v8_) {
69 15 : if (pair.first == first_field) return true;
70 : }
71 : return false;
72 : }
73 :
74 : private:
75 : v8::Isolate* const isolate_;
76 : std::vector<std::pair<void*, void*>> registered_from_v8_;
77 : std::vector<v8::Persistent<v8::Object>*> to_register_with_v8_;
78 : };
79 :
80 : class TemporaryEmbedderHeapTracerScope {
81 : public:
82 : TemporaryEmbedderHeapTracerScope(v8::Isolate* isolate,
83 : EmbedderHeapTracer* tracer)
84 : : isolate_(isolate) {
85 35 : isolate_->SetEmbedderHeapTracer(tracer);
86 : }
87 :
88 : ~TemporaryEmbedderHeapTracerScope() {
89 35 : isolate_->SetEmbedderHeapTracer(nullptr);
90 : }
91 :
92 : private:
93 : v8::Isolate* const isolate_;
94 : };
95 :
96 : } // namespace
97 :
98 28342 : TEST(V8RegisteringEmbedderReference) {
99 : // Tests that wrappers are properly registered with the embedder heap
100 : // tracer.
101 : ManualGCScope manual_gc;
102 5 : CcTest::InitializeVM();
103 5 : v8::Isolate* isolate = CcTest::isolate();
104 5 : TestEmbedderHeapTracer tracer(isolate);
105 : TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
106 10 : v8::HandleScope scope(isolate);
107 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
108 : v8::Context::Scope context_scope(context);
109 :
110 : void* first_field = reinterpret_cast<void*>(0x2);
111 : v8::Local<v8::Object> api_object =
112 5 : ConstructTraceableJSApiObject(context, first_field, nullptr);
113 5 : CHECK(!api_object.IsEmpty());
114 5 : CcTest::CollectGarbage(i::OLD_SPACE);
115 5 : CHECK(tracer.IsRegisteredFromV8(first_field));
116 5 : }
117 :
118 28342 : TEST(EmbedderRegisteringV8Reference) {
119 : // Tests that references that are registered by the embedder heap tracer are
120 : // considered live by V8.
121 : ManualGCScope manual_gc;
122 5 : CcTest::InitializeVM();
123 5 : v8::Isolate* isolate = CcTest::isolate();
124 5 : TestEmbedderHeapTracer tracer(isolate);
125 : TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
126 10 : v8::HandleScope scope(isolate);
127 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
128 : v8::Context::Scope context_scope(context);
129 :
130 : v8::Persistent<v8::Object> g;
131 : {
132 5 : v8::HandleScope inner_scope(isolate);
133 : v8::Local<v8::Object> o =
134 5 : v8::Local<v8::Object>::New(isolate, v8::Object::New(isolate));
135 : g.Reset(isolate, o);
136 5 : g.SetWeak();
137 : }
138 : tracer.AddReferenceForTracing(&g);
139 5 : CcTest::CollectGarbage(i::OLD_SPACE);
140 5 : CHECK(!g.IsEmpty());
141 5 : }
142 :
143 : namespace {
144 :
145 5 : void ResurrectingFinalizer(
146 5 : const v8::WeakCallbackInfo<v8::Global<v8::Object>>& data) {
147 : data.GetParameter()->ClearWeak();
148 5 : }
149 :
150 : } // namespace
151 :
152 28342 : TEST(TracingInRevivedSubgraph) {
153 : // Tests that wrappers are traced when they are contained with in a subgraph
154 : // that is revived by a finalizer.
155 : ManualGCScope manual_gc;
156 5 : CcTest::InitializeVM();
157 5 : v8::Isolate* isolate = CcTest::isolate();
158 5 : TestEmbedderHeapTracer tracer(isolate);
159 : TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
160 10 : v8::HandleScope scope(isolate);
161 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
162 : v8::Context::Scope context_scope(context);
163 :
164 : v8::Global<v8::Object> g;
165 : void* first_field = reinterpret_cast<void*>(0x4);
166 : {
167 5 : v8::HandleScope inner_scope(isolate);
168 : v8::Local<v8::Object> api_object =
169 5 : ConstructTraceableJSApiObject(context, first_field, nullptr);
170 5 : CHECK(!api_object.IsEmpty());
171 : v8::Local<v8::Object> o =
172 5 : v8::Local<v8::Object>::New(isolate, v8::Object::New(isolate));
173 15 : o->Set(context, v8_str("link"), api_object).FromJust();
174 : g.Reset(isolate, o);
175 5 : g.SetWeak(&g, ResurrectingFinalizer, v8::WeakCallbackType::kFinalizer);
176 : }
177 5 : CcTest::CollectGarbage(i::OLD_SPACE);
178 5 : CHECK(tracer.IsRegisteredFromV8(first_field));
179 5 : }
180 :
181 28342 : TEST(TracingInEphemerons) {
182 : // Tests that wrappers that are part of ephemerons are traced.
183 : ManualGCScope manual_gc;
184 5 : CcTest::InitializeVM();
185 5 : v8::Isolate* isolate = CcTest::isolate();
186 5 : TestEmbedderHeapTracer tracer(isolate);
187 : TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
188 10 : v8::HandleScope scope(isolate);
189 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
190 : v8::Context::Scope context_scope(context);
191 :
192 : v8::Local<v8::Object> key =
193 5 : v8::Local<v8::Object>::New(isolate, v8::Object::New(isolate));
194 : void* first_field = reinterpret_cast<void*>(0x8);
195 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
196 5 : Handle<JSWeakMap> weak_map = i_isolate->factory()->NewJSWeakMap();
197 : {
198 5 : v8::HandleScope inner_scope(isolate);
199 : v8::Local<v8::Object> api_object =
200 5 : ConstructTraceableJSApiObject(context, first_field, nullptr);
201 5 : CHECK(!api_object.IsEmpty());
202 : Handle<JSObject> js_key =
203 : handle(JSObject::cast(*v8::Utils::OpenHandle(*key)), i_isolate);
204 : Handle<JSReceiver> js_api_object = v8::Utils::OpenHandle(*api_object);
205 10 : int32_t hash = js_key->GetOrCreateHash(i_isolate)->value();
206 5 : JSWeakCollection::Set(weak_map, js_key, js_api_object, hash);
207 : }
208 5 : CcTest::CollectGarbage(i::OLD_SPACE);
209 5 : CHECK(tracer.IsRegisteredFromV8(first_field));
210 5 : }
211 :
212 28342 : TEST(FinalizeTracingIsNoopWhenNotMarking) {
213 : ManualGCScope manual_gc;
214 5 : CcTest::InitializeVM();
215 5 : v8::Isolate* isolate = CcTest::isolate();
216 : Isolate* i_isolate = CcTest::i_isolate();
217 5 : TestEmbedderHeapTracer tracer(isolate);
218 : TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
219 :
220 : // Finalize a potentially running garbage collection.
221 : i_isolate->heap()->CollectGarbage(OLD_SPACE,
222 5 : GarbageCollectionReason::kTesting);
223 10 : CHECK(i_isolate->heap()->incremental_marking()->IsStopped());
224 :
225 5 : int gc_counter = i_isolate->heap()->gc_count();
226 5 : tracer.FinalizeTracing();
227 10 : CHECK(i_isolate->heap()->incremental_marking()->IsStopped());
228 5 : CHECK_EQ(gc_counter, i_isolate->heap()->gc_count());
229 5 : }
230 :
231 28342 : TEST(FinalizeTracingWhenMarking) {
232 : ManualGCScope manual_gc;
233 5 : CcTest::InitializeVM();
234 5 : v8::Isolate* isolate = CcTest::isolate();
235 : Isolate* i_isolate = CcTest::i_isolate();
236 5 : TestEmbedderHeapTracer tracer(isolate);
237 : TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
238 :
239 : // Finalize a potentially running garbage collection.
240 : i_isolate->heap()->CollectGarbage(OLD_SPACE,
241 5 : GarbageCollectionReason::kTesting);
242 10 : if (i_isolate->heap()->mark_compact_collector()->sweeping_in_progress()) {
243 5 : i_isolate->heap()->mark_compact_collector()->EnsureSweepingCompleted();
244 : }
245 10 : CHECK(i_isolate->heap()->incremental_marking()->IsStopped());
246 :
247 : i::IncrementalMarking* marking = i_isolate->heap()->incremental_marking();
248 5 : marking->Start(i::GarbageCollectionReason::kTesting);
249 : // Sweeping is not runing so we should immediately start marking.
250 5 : CHECK(marking->IsMarking());
251 5 : tracer.FinalizeTracing();
252 5 : CHECK(marking->IsStopped());
253 5 : }
254 :
255 28342 : TEST(GarbageCollectionForTesting) {
256 : ManualGCScope manual_gc;
257 5 : i::FLAG_expose_gc = true;
258 5 : CcTest::InitializeVM();
259 5 : v8::Isolate* isolate = CcTest::isolate();
260 : Isolate* i_isolate = CcTest::i_isolate();
261 5 : TestEmbedderHeapTracer tracer(isolate);
262 : TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
263 :
264 5 : int saved_gc_counter = i_isolate->heap()->gc_count();
265 5 : tracer.GarbageCollectionForTesting(EmbedderHeapTracer::kUnknown);
266 10 : CHECK_GT(i_isolate->heap()->gc_count(), saved_gc_counter);
267 5 : }
268 :
269 : } // namespace heap
270 : } // namespace internal
271 85011 : } // namespace v8
|