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 <unordered_map>
6 : #include <vector>
7 :
8 : #include "include/v8.h"
9 : #include "src/api-inl.h"
10 : #include "src/heap/heap-inl.h"
11 : #include "src/objects-inl.h"
12 : #include "src/objects/module.h"
13 : #include "src/objects/script.h"
14 : #include "src/objects/shared-function-info.h"
15 : #include "test/cctest/cctest.h"
16 : #include "test/cctest/heap/heap-utils.h"
17 :
18 : namespace v8 {
19 : namespace internal {
20 : namespace heap {
21 :
22 : namespace {
23 :
24 35 : v8::Local<v8::Object> ConstructTraceableJSApiObject(
25 : v8::Local<v8::Context> context, void* first_field, void* second_field) {
26 35 : v8::EscapableHandleScope scope(context->GetIsolate());
27 : v8::Local<v8::FunctionTemplate> function_t =
28 35 : v8::FunctionTemplate::New(context->GetIsolate());
29 35 : v8::Local<v8::ObjectTemplate> instance_t = function_t->InstanceTemplate();
30 35 : instance_t->SetInternalFieldCount(2);
31 : v8::Local<v8::Function> function =
32 35 : function_t->GetFunction(context).ToLocalChecked();
33 : v8::Local<v8::Object> instance =
34 : function->NewInstance(context).ToLocalChecked();
35 35 : instance->SetAlignedPointerInInternalField(0, first_field);
36 35 : instance->SetAlignedPointerInInternalField(1, second_field);
37 35 : CHECK(!instance.IsEmpty());
38 : i::Handle<i::JSReceiver> js_obj = v8::Utils::OpenHandle(*instance);
39 35 : CHECK_EQ(i::JS_API_OBJECT_TYPE, js_obj->map()->instance_type());
40 35 : return scope.Escape(instance);
41 : }
42 :
43 : enum class TracePrologueBehavior { kNoop, kCallV8WriteBarrier };
44 :
45 150 : class TestEmbedderHeapTracer final : public v8::EmbedderHeapTracer {
46 : public:
47 140 : TestEmbedderHeapTracer() = default;
48 5 : TestEmbedderHeapTracer(TracePrologueBehavior prologue_behavior,
49 : v8::Global<v8::Array> array)
50 15 : : prologue_behavior_(prologue_behavior), array_(std::move(array)) {}
51 :
52 15 : void RegisterV8References(
53 : const std::vector<std::pair<void*, void*>>& embedder_fields) final {
54 : registered_from_v8_.insert(registered_from_v8_.end(),
55 15 : embedder_fields.begin(), embedder_fields.end());
56 15 : }
57 :
58 : void AddReferenceForTracing(v8::TracedGlobal<v8::Object>* global) {
59 5 : to_register_with_v8_.push_back(global);
60 : }
61 :
62 135 : bool AdvanceTracing(double deadline_in_ms) final {
63 140 : for (auto global : to_register_with_v8_) {
64 5 : RegisterEmbedderReference(global->As<v8::Value>());
65 : }
66 : to_register_with_v8_.clear();
67 135 : return true;
68 : }
69 :
70 510 : bool IsTracingDone() final { return to_register_with_v8_.empty(); }
71 :
72 50 : void TracePrologue() final {
73 50 : if (prologue_behavior_ == TracePrologueBehavior::kCallV8WriteBarrier) {
74 : auto local = array_.Get(isolate());
75 15 : local->Set(local->CreationContext(), 0, v8::Object::New(isolate()))
76 : .Check();
77 : }
78 50 : }
79 :
80 45 : void TraceEpilogue() final {}
81 45 : void EnterFinalPause(EmbedderStackState) final {}
82 :
83 : bool IsRegisteredFromV8(void* first_field) const {
84 15 : for (auto pair : registered_from_v8_) {
85 15 : if (pair.first == first_field) return true;
86 : }
87 : return false;
88 : }
89 :
90 : void ConsiderTracedGlobalAsRoot(bool value) {
91 20 : consider_traced_global_as_root_ = value;
92 : }
93 :
94 15 : bool IsRootForNonTracingGC(const v8::TracedGlobal<v8::Value>& handle) final {
95 15 : return consider_traced_global_as_root_;
96 : }
97 :
98 : private:
99 : std::vector<std::pair<void*, void*>> registered_from_v8_;
100 : std::vector<v8::TracedGlobal<v8::Object>*> to_register_with_v8_;
101 : bool consider_traced_global_as_root_ = true;
102 : TracePrologueBehavior prologue_behavior_ = TracePrologueBehavior::kNoop;
103 : v8::Global<v8::Array> array_;
104 : };
105 :
106 : class TemporaryEmbedderHeapTracerScope {
107 : public:
108 : TemporaryEmbedderHeapTracerScope(v8::Isolate* isolate,
109 : EmbedderHeapTracer* tracer)
110 : : isolate_(isolate) {
111 75 : isolate_->SetEmbedderHeapTracer(tracer);
112 : }
113 :
114 : ~TemporaryEmbedderHeapTracerScope() {
115 75 : isolate_->SetEmbedderHeapTracer(nullptr);
116 : }
117 :
118 : private:
119 : v8::Isolate* const isolate_;
120 : };
121 :
122 : } // namespace
123 :
124 26644 : TEST(V8RegisteringEmbedderReference) {
125 : // Tests that wrappers are properly registered with the embedder heap
126 : // tracer.
127 : ManualGCScope manual_gc;
128 5 : CcTest::InitializeVM();
129 5 : v8::Isolate* isolate = CcTest::isolate();
130 5 : TestEmbedderHeapTracer tracer;
131 : TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
132 10 : v8::HandleScope scope(isolate);
133 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
134 : v8::Context::Scope context_scope(context);
135 :
136 : void* first_field = reinterpret_cast<void*>(0x2);
137 : v8::Local<v8::Object> api_object =
138 5 : ConstructTraceableJSApiObject(context, first_field, nullptr);
139 5 : CHECK(!api_object.IsEmpty());
140 5 : CcTest::CollectGarbage(i::OLD_SPACE);
141 5 : CHECK(tracer.IsRegisteredFromV8(first_field));
142 5 : }
143 :
144 26644 : TEST(EmbedderRegisteringV8Reference) {
145 : // Tests that references that are registered by the embedder heap tracer are
146 : // considered live by V8.
147 : ManualGCScope manual_gc;
148 5 : CcTest::InitializeVM();
149 5 : v8::Isolate* isolate = CcTest::isolate();
150 5 : TestEmbedderHeapTracer tracer;
151 : TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
152 10 : v8::HandleScope scope(isolate);
153 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
154 : v8::Context::Scope context_scope(context);
155 :
156 5 : v8::TracedGlobal<v8::Object> g;
157 : {
158 10 : v8::HandleScope inner_scope(isolate);
159 : v8::Local<v8::Object> o =
160 5 : v8::Local<v8::Object>::New(isolate, v8::Object::New(isolate));
161 : g.Reset(isolate, o);
162 : }
163 : tracer.AddReferenceForTracing(&g);
164 5 : CcTest::CollectGarbage(i::OLD_SPACE);
165 5 : CHECK(!g.IsEmpty());
166 5 : }
167 :
168 : namespace {
169 :
170 5 : void ResurrectingFinalizer(
171 : const v8::WeakCallbackInfo<v8::Global<v8::Object>>& data) {
172 : data.GetParameter()->ClearWeak();
173 5 : }
174 :
175 : } // namespace
176 :
177 26644 : TEST(TracingInRevivedSubgraph) {
178 : // Tests that wrappers are traced when they are contained with in a subgraph
179 : // that is revived by a finalizer.
180 : ManualGCScope manual_gc;
181 5 : CcTest::InitializeVM();
182 5 : v8::Isolate* isolate = CcTest::isolate();
183 5 : TestEmbedderHeapTracer tracer;
184 : TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
185 10 : v8::HandleScope scope(isolate);
186 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
187 : v8::Context::Scope context_scope(context);
188 :
189 : v8::Global<v8::Object> g;
190 : void* first_field = reinterpret_cast<void*>(0x4);
191 : {
192 10 : v8::HandleScope inner_scope(isolate);
193 : v8::Local<v8::Object> api_object =
194 5 : ConstructTraceableJSApiObject(context, first_field, nullptr);
195 5 : CHECK(!api_object.IsEmpty());
196 : v8::Local<v8::Object> o =
197 5 : v8::Local<v8::Object>::New(isolate, v8::Object::New(isolate));
198 15 : o->Set(context, v8_str("link"), api_object).FromJust();
199 : g.Reset(isolate, o);
200 : g.SetWeak(&g, ResurrectingFinalizer, v8::WeakCallbackType::kFinalizer);
201 : }
202 5 : CcTest::CollectGarbage(i::OLD_SPACE);
203 5 : CHECK(tracer.IsRegisteredFromV8(first_field));
204 5 : }
205 :
206 26644 : TEST(TracingInEphemerons) {
207 : // Tests that wrappers that are part of ephemerons are traced.
208 : ManualGCScope manual_gc;
209 5 : CcTest::InitializeVM();
210 5 : v8::Isolate* isolate = CcTest::isolate();
211 5 : TestEmbedderHeapTracer tracer;
212 : TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
213 10 : v8::HandleScope scope(isolate);
214 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
215 : v8::Context::Scope context_scope(context);
216 :
217 : v8::Local<v8::Object> key =
218 5 : v8::Local<v8::Object>::New(isolate, v8::Object::New(isolate));
219 : void* first_field = reinterpret_cast<void*>(0x8);
220 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
221 5 : Handle<JSWeakMap> weak_map = i_isolate->factory()->NewJSWeakMap();
222 : {
223 10 : v8::HandleScope inner_scope(isolate);
224 : v8::Local<v8::Object> api_object =
225 5 : ConstructTraceableJSApiObject(context, first_field, nullptr);
226 5 : CHECK(!api_object.IsEmpty());
227 : Handle<JSObject> js_key =
228 : handle(JSObject::cast(*v8::Utils::OpenHandle(*key)), i_isolate);
229 : Handle<JSReceiver> js_api_object = v8::Utils::OpenHandle(*api_object);
230 10 : int32_t hash = js_key->GetOrCreateHash(i_isolate)->value();
231 5 : JSWeakCollection::Set(weak_map, js_key, js_api_object, hash);
232 : }
233 5 : CcTest::CollectGarbage(i::OLD_SPACE);
234 5 : CHECK(tracer.IsRegisteredFromV8(first_field));
235 5 : }
236 :
237 26644 : TEST(FinalizeTracingIsNoopWhenNotMarking) {
238 : ManualGCScope manual_gc;
239 5 : CcTest::InitializeVM();
240 5 : v8::Isolate* isolate = CcTest::isolate();
241 : Isolate* i_isolate = CcTest::i_isolate();
242 5 : TestEmbedderHeapTracer tracer;
243 : TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
244 :
245 : // Finalize a potentially running garbage collection.
246 : i_isolate->heap()->CollectGarbage(OLD_SPACE,
247 5 : GarbageCollectionReason::kTesting);
248 5 : CHECK(i_isolate->heap()->incremental_marking()->IsStopped());
249 :
250 : int gc_counter = i_isolate->heap()->gc_count();
251 5 : tracer.FinalizeTracing();
252 5 : CHECK(i_isolate->heap()->incremental_marking()->IsStopped());
253 5 : CHECK_EQ(gc_counter, i_isolate->heap()->gc_count());
254 5 : }
255 :
256 26644 : TEST(FinalizeTracingWhenMarking) {
257 : ManualGCScope manual_gc;
258 5 : CcTest::InitializeVM();
259 5 : v8::Isolate* isolate = CcTest::isolate();
260 : Isolate* i_isolate = CcTest::i_isolate();
261 5 : TestEmbedderHeapTracer tracer;
262 : TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
263 :
264 : // Finalize a potentially running garbage collection.
265 : i_isolate->heap()->CollectGarbage(OLD_SPACE,
266 5 : GarbageCollectionReason::kTesting);
267 5 : if (i_isolate->heap()->mark_compact_collector()->sweeping_in_progress()) {
268 5 : i_isolate->heap()->mark_compact_collector()->EnsureSweepingCompleted();
269 : }
270 5 : CHECK(i_isolate->heap()->incremental_marking()->IsStopped());
271 :
272 : i::IncrementalMarking* marking = i_isolate->heap()->incremental_marking();
273 5 : marking->Start(i::GarbageCollectionReason::kTesting);
274 : // Sweeping is not runing so we should immediately start marking.
275 5 : CHECK(marking->IsMarking());
276 5 : tracer.FinalizeTracing();
277 5 : CHECK(marking->IsStopped());
278 5 : }
279 :
280 26644 : TEST(GarbageCollectionForTesting) {
281 : ManualGCScope manual_gc;
282 5 : i::FLAG_expose_gc = true;
283 5 : CcTest::InitializeVM();
284 5 : v8::Isolate* isolate = CcTest::isolate();
285 : Isolate* i_isolate = CcTest::i_isolate();
286 5 : TestEmbedderHeapTracer tracer;
287 : TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
288 :
289 : int saved_gc_counter = i_isolate->heap()->gc_count();
290 5 : tracer.GarbageCollectionForTesting(EmbedderHeapTracer::kUnknown);
291 5 : CHECK_GT(i_isolate->heap()->gc_count(), saved_gc_counter);
292 5 : }
293 :
294 : namespace {
295 :
296 35 : void ConstructJSObject(v8::Isolate* isolate, v8::Local<v8::Context> context,
297 : v8::TracedGlobal<v8::Object>* global) {
298 70 : v8::HandleScope scope(isolate);
299 35 : v8::Local<v8::Object> object(v8::Object::New(isolate));
300 35 : CHECK(!object.IsEmpty());
301 35 : *global = v8::TracedGlobal<v8::Object>(isolate, object);
302 35 : CHECK(!global->IsEmpty());
303 35 : }
304 :
305 20 : void ConstructJSApiObject(v8::Isolate* isolate, v8::Local<v8::Context> context,
306 : v8::TracedGlobal<v8::Object>* global) {
307 40 : v8::HandleScope scope(isolate);
308 : v8::Local<v8::Object> object(
309 20 : ConstructTraceableJSApiObject(context, nullptr, nullptr));
310 20 : CHECK(!object.IsEmpty());
311 20 : *global = v8::TracedGlobal<v8::Object>(isolate, object);
312 20 : CHECK(!global->IsEmpty());
313 20 : }
314 :
315 : enum class SurvivalMode { kSurvives, kDies };
316 :
317 : template <typename ModifierFunction, typename ConstructTracedGlobalFunction>
318 30 : void TracedGlobalTest(v8::Isolate* isolate,
319 : ConstructTracedGlobalFunction construct_function,
320 : ModifierFunction modifier_function, void (*gc_function)(),
321 : SurvivalMode survives) {
322 60 : v8::HandleScope scope(isolate);
323 30 : v8::Local<v8::Context> context = v8::Context::New(isolate);
324 : v8::Context::Scope context_scope(context);
325 :
326 30 : v8::TracedGlobal<v8::Object> global;
327 30 : construct_function(isolate, context, &global);
328 30 : CHECK(InYoungGeneration(isolate, global));
329 5 : modifier_function(global);
330 30 : gc_function();
331 50 : CHECK_IMPLIES(survives == SurvivalMode::kSurvives, !global.IsEmpty());
332 40 : CHECK_IMPLIES(survives == SurvivalMode::kDies, global.IsEmpty());
333 30 : }
334 :
335 : } // namespace
336 :
337 26644 : TEST(TracedGlobalReset) {
338 5 : CcTest::InitializeVM();
339 5 : v8::Isolate* isolate = CcTest::isolate();
340 10 : v8::HandleScope scope(isolate);
341 :
342 5 : v8::TracedGlobal<v8::Object> traced;
343 5 : ConstructJSObject(isolate, isolate->GetCurrentContext(), &traced);
344 5 : CHECK(!traced.IsEmpty());
345 : traced.Reset();
346 5 : CHECK(traced.IsEmpty());
347 5 : }
348 :
349 26644 : TEST(TracedGlobalInStdVector) {
350 : ManualGCScope manual_gc;
351 5 : CcTest::InitializeVM();
352 5 : v8::Isolate* isolate = CcTest::isolate();
353 10 : v8::HandleScope scope(isolate);
354 :
355 5 : std::vector<v8::TracedGlobal<v8::Object>> vec;
356 : {
357 10 : v8::HandleScope scope(isolate);
358 5 : vec.emplace_back(isolate, v8::Object::New(isolate));
359 : }
360 5 : CHECK(!vec[0].IsEmpty());
361 5 : InvokeMarkSweep();
362 5 : CHECK(vec[0].IsEmpty());
363 5 : }
364 :
365 26644 : TEST(TracedGlobalInStdUnorderedMap) {
366 : ManualGCScope manual_gc;
367 5 : CcTest::InitializeVM();
368 5 : v8::Isolate* isolate = CcTest::isolate();
369 10 : v8::HandleScope scope(isolate);
370 :
371 : std::unordered_map<int, v8::TracedGlobal<v8::Object>> map;
372 : {
373 10 : v8::HandleScope scope(isolate);
374 10 : map.emplace(std::piecewise_construct, std::forward_as_tuple(1),
375 10 : std::forward_as_tuple(isolate, v8::Object::New(isolate)));
376 : }
377 10 : CHECK(!map[1].IsEmpty());
378 5 : InvokeMarkSweep();
379 10 : CHECK(map[1].IsEmpty());
380 5 : }
381 :
382 26644 : TEST(TracedGlobalToUnmodifiedJSObjectDiesOnMarkSweep) {
383 5 : CcTest::InitializeVM();
384 5 : TracedGlobalTest(
385 : CcTest::isolate(), ConstructJSObject,
386 : [](const TracedGlobal<v8::Object>& global) {}, InvokeMarkSweep,
387 5 : SurvivalMode::kDies);
388 5 : }
389 :
390 26644 : TEST(TracedGlobalToUnmodifiedJSObjectSurvivesMarkSweepWhenHeldAliveOtherwise) {
391 5 : CcTest::InitializeVM();
392 5 : v8::Isolate* isolate = CcTest::isolate();
393 : v8::Global<v8::Object> strong_global;
394 5 : TracedGlobalTest(
395 : CcTest::isolate(), ConstructJSObject,
396 20 : [isolate, &strong_global](const TracedGlobal<v8::Object>& global) {
397 10 : v8::HandleScope scope(isolate);
398 5 : strong_global = v8::Global<v8::Object>(isolate, global.Get(isolate));
399 5 : },
400 10 : InvokeMarkSweep, SurvivalMode::kSurvives);
401 5 : }
402 :
403 26644 : TEST(TracedGlobalToUnmodifiedJSObjectSurvivesScavenge) {
404 : ManualGCScope manual_gc;
405 5 : CcTest::InitializeVM();
406 5 : TracedGlobalTest(
407 : CcTest::isolate(), ConstructJSObject,
408 : [](const TracedGlobal<v8::Object>& global) {}, InvokeScavenge,
409 5 : SurvivalMode::kSurvives);
410 5 : }
411 :
412 26644 : TEST(TracedGlobalToUnmodifiedJSObjectSurvivesScavengeWhenExcludedFromRoots) {
413 : ManualGCScope manual_gc;
414 5 : CcTest::InitializeVM();
415 5 : v8::Isolate* isolate = CcTest::isolate();
416 5 : TestEmbedderHeapTracer tracer;
417 : TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
418 : tracer.ConsiderTracedGlobalAsRoot(false);
419 5 : TracedGlobalTest(
420 : CcTest::isolate(), ConstructJSObject,
421 : [](const TracedGlobal<v8::Object>& global) {}, InvokeScavenge,
422 5 : SurvivalMode::kSurvives);
423 5 : }
424 :
425 26644 : TEST(TracedGlobalToUnmodifiedJSApiObjectSurvivesScavengePerDefault) {
426 : ManualGCScope manual_gc;
427 5 : CcTest::InitializeVM();
428 5 : v8::Isolate* isolate = CcTest::isolate();
429 5 : TestEmbedderHeapTracer tracer;
430 : TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
431 : tracer.ConsiderTracedGlobalAsRoot(true);
432 5 : TracedGlobalTest(
433 : CcTest::isolate(), ConstructJSApiObject,
434 : [](const TracedGlobal<v8::Object>& global) {}, InvokeScavenge,
435 5 : SurvivalMode::kSurvives);
436 5 : }
437 :
438 26644 : TEST(TracedGlobalToUnmodifiedJSApiObjectDiesOnScavengeWhenExcludedFromRoots) {
439 : ManualGCScope manual_gc;
440 5 : CcTest::InitializeVM();
441 5 : v8::Isolate* isolate = CcTest::isolate();
442 5 : TestEmbedderHeapTracer tracer;
443 : TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
444 : tracer.ConsiderTracedGlobalAsRoot(false);
445 5 : TracedGlobalTest(
446 : CcTest::isolate(), ConstructJSApiObject,
447 : [](const TracedGlobal<v8::Object>& global) {}, InvokeScavenge,
448 5 : SurvivalMode::kDies);
449 5 : }
450 :
451 26644 : TEST(TracedGlobalWrapperClassId) {
452 : ManualGCScope manual_gc;
453 5 : CcTest::InitializeVM();
454 5 : v8::Isolate* isolate = CcTest::isolate();
455 10 : v8::HandleScope scope(isolate);
456 5 : TestEmbedderHeapTracer tracer;
457 : TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
458 :
459 5 : v8::TracedGlobal<v8::Object> traced;
460 5 : ConstructJSObject(isolate, isolate->GetCurrentContext(), &traced);
461 5 : CHECK_EQ(0, traced.WrapperClassId());
462 : traced.SetWrapperClassId(17);
463 5 : CHECK_EQ(17, traced.WrapperClassId());
464 5 : }
465 :
466 : namespace {
467 :
468 : class TracedGlobalVisitor final
469 : : public v8::EmbedderHeapTracer::TracedGlobalHandleVisitor {
470 : public:
471 10 : ~TracedGlobalVisitor() override = default;
472 5 : void VisitTracedGlobalHandle(const TracedGlobal<Value>& value) final {
473 5 : if (value.WrapperClassId() == 57) {
474 5 : count_++;
475 : }
476 5 : }
477 :
478 : size_t count() const { return count_; }
479 :
480 : private:
481 : size_t count_ = 0;
482 : };
483 :
484 : } // namespace
485 :
486 26644 : TEST(TracedGlobalIteration) {
487 : ManualGCScope manual_gc;
488 5 : CcTest::InitializeVM();
489 5 : v8::Isolate* isolate = CcTest::isolate();
490 10 : v8::HandleScope scope(isolate);
491 5 : TestEmbedderHeapTracer tracer;
492 : TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
493 :
494 5 : v8::TracedGlobal<v8::Object> traced;
495 5 : ConstructJSObject(isolate, isolate->GetCurrentContext(), &traced);
496 5 : CHECK(!traced.IsEmpty());
497 : traced.SetWrapperClassId(57);
498 5 : TracedGlobalVisitor visitor;
499 : {
500 10 : v8::HandleScope scope(isolate);
501 5 : tracer.IterateTracedGlobalHandles(&visitor);
502 : }
503 5 : CHECK_EQ(1, visitor.count());
504 5 : }
505 :
506 : namespace {
507 :
508 10 : void FinalizationCallback(const WeakCallbackInfo<void>& data) {
509 : v8::TracedGlobal<v8::Object>* traced =
510 : reinterpret_cast<v8::TracedGlobal<v8::Object>*>(data.GetParameter());
511 10 : CHECK_EQ(reinterpret_cast<void*>(0x4), data.GetInternalField(0));
512 10 : CHECK_EQ(reinterpret_cast<void*>(0x8), data.GetInternalField(1));
513 : traced->Reset();
514 10 : }
515 :
516 : } // namespace
517 :
518 26644 : TEST(TracedGlobalSetFinalizationCallbackScavenge) {
519 : ManualGCScope manual_gc;
520 5 : CcTest::InitializeVM();
521 5 : v8::Isolate* isolate = CcTest::isolate();
522 10 : v8::HandleScope scope(isolate);
523 5 : TestEmbedderHeapTracer tracer;
524 : tracer.ConsiderTracedGlobalAsRoot(false);
525 : TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
526 :
527 5 : v8::TracedGlobal<v8::Object> traced;
528 5 : ConstructJSApiObject(isolate, isolate->GetCurrentContext(), &traced);
529 5 : CHECK(!traced.IsEmpty());
530 : {
531 10 : v8::HandleScope scope(isolate);
532 : auto local = traced.Get(isolate);
533 5 : local->SetAlignedPointerInInternalField(0, reinterpret_cast<void*>(0x4));
534 5 : local->SetAlignedPointerInInternalField(1, reinterpret_cast<void*>(0x8));
535 : }
536 : traced.SetFinalizationCallback(&traced, FinalizationCallback);
537 5 : heap::InvokeScavenge();
538 5 : CHECK(traced.IsEmpty());
539 5 : }
540 :
541 26644 : TEST(TracedGlobalSetFinalizationCallbackMarkSweep) {
542 : ManualGCScope manual_gc;
543 5 : CcTest::InitializeVM();
544 5 : v8::Isolate* isolate = CcTest::isolate();
545 10 : v8::HandleScope scope(isolate);
546 5 : TestEmbedderHeapTracer tracer;
547 : TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
548 :
549 5 : v8::TracedGlobal<v8::Object> traced;
550 5 : ConstructJSApiObject(isolate, isolate->GetCurrentContext(), &traced);
551 5 : CHECK(!traced.IsEmpty());
552 : {
553 10 : v8::HandleScope scope(isolate);
554 : auto local = traced.Get(isolate);
555 5 : local->SetAlignedPointerInInternalField(0, reinterpret_cast<void*>(0x4));
556 5 : local->SetAlignedPointerInInternalField(1, reinterpret_cast<void*>(0x8));
557 : }
558 : traced.SetFinalizationCallback(&traced, FinalizationCallback);
559 5 : heap::InvokeMarkSweep();
560 5 : CHECK(traced.IsEmpty());
561 5 : }
562 :
563 26644 : TEST(TracePrologueCallingIntoV8WriteBarrier) {
564 : // Regression test: https://crbug.com/940003
565 : ManualGCScope manual_gc;
566 5 : CcTest::InitializeVM();
567 5 : v8::Isolate* isolate = CcTest::isolate();
568 10 : v8::HandleScope scope(isolate);
569 : v8::Global<v8::Array> global;
570 : {
571 10 : v8::HandleScope scope(isolate);
572 5 : auto local = v8::Array::New(isolate, 10);
573 : global.Reset(isolate, local);
574 : }
575 : TestEmbedderHeapTracer tracer(TracePrologueBehavior::kCallV8WriteBarrier,
576 15 : std::move(global));
577 : TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
578 5 : SimulateIncrementalMarking(CcTest::i_isolate()->heap());
579 5 : }
580 :
581 : } // namespace heap
582 : } // namespace internal
583 79917 : } // namespace v8
|