Line data Source code
1 : // Copyright 2013 the V8 project authors. All rights reserved.
2 : // Redistribution and use in source and binary forms, with or without
3 : // modification, are permitted provided that the following conditions are
4 : // met:
5 : //
6 : // * Redistributions of source code must retain the above copyright
7 : // notice, this list of conditions and the following disclaimer.
8 : // * Redistributions in binary form must reproduce the above
9 : // copyright notice, this list of conditions and the following
10 : // disclaimer in the documentation and/or other materials provided
11 : // with the distribution.
12 : // * Neither the name of Google Inc. nor the names of its
13 : // contributors may be used to endorse or promote products derived
14 : // from this software without specific prior written permission.
15 : //
16 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 : // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 : // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 : // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 : // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 : // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 : // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 : // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 : // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 : // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 :
28 : #include "src/api-inl.h"
29 : #include "src/global-handles.h"
30 : #include "src/heap/factory.h"
31 : #include "src/heap/heap-inl.h"
32 : #include "src/isolate.h"
33 : #include "src/objects-inl.h"
34 : #include "test/cctest/cctest.h"
35 : #include "test/cctest/heap/heap-utils.h"
36 :
37 : namespace v8 {
38 : namespace internal {
39 :
40 : namespace {
41 :
42 50 : void InvokeScavenge() { CcTest::CollectGarbage(i::NEW_SPACE); }
43 :
44 95 : void InvokeMarkSweep() { CcTest::CollectAllGarbage(); }
45 :
46 70 : void SimpleCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
47 70 : info.GetReturnValue().Set(v8_num(0));
48 70 : }
49 :
50 85 : struct FlagAndPersistent {
51 : bool flag;
52 : v8::Global<v8::Object> handle;
53 : };
54 :
55 20 : void ResetHandleAndSetFlag(
56 : const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
57 : data.GetParameter()->handle.Reset();
58 20 : data.GetParameter()->flag = true;
59 20 : }
60 :
61 : using ConstructFunction = void (*)(v8::Isolate* isolate,
62 : v8::Local<v8::Context> context,
63 : FlagAndPersistent* flag_and_persistent);
64 :
65 15 : void ConstructJSObject(v8::Isolate* isolate, v8::Local<v8::Context> context,
66 : FlagAndPersistent* flag_and_persistent) {
67 30 : v8::HandleScope handle_scope(isolate);
68 15 : v8::Local<v8::Object> object(v8::Object::New(isolate));
69 15 : CHECK(!object.IsEmpty());
70 : flag_and_persistent->handle.Reset(isolate, object);
71 15 : CHECK(!flag_and_persistent->handle.IsEmpty());
72 15 : }
73 :
74 10 : void ConstructJSObject(v8::Isolate* isolate, v8::Global<v8::Object>* global) {
75 20 : v8::HandleScope scope(isolate);
76 10 : v8::Local<v8::Object> object(v8::Object::New(isolate));
77 10 : CHECK(!object.IsEmpty());
78 10 : *global = v8::Global<v8::Object>(isolate, object);
79 10 : CHECK(!global->IsEmpty());
80 10 : }
81 :
82 70 : void ConstructJSApiObject(v8::Isolate* isolate, v8::Local<v8::Context> context,
83 : FlagAndPersistent* flag_and_persistent) {
84 140 : v8::HandleScope handle_scope(isolate);
85 : v8::Local<v8::FunctionTemplate> fun =
86 70 : v8::FunctionTemplate::New(isolate, SimpleCallback);
87 70 : v8::Local<v8::Object> object = fun->GetFunction(context)
88 : .ToLocalChecked()
89 : ->NewInstance(context)
90 : .ToLocalChecked();
91 70 : CHECK(!object.IsEmpty());
92 : flag_and_persistent->handle.Reset(isolate, object);
93 70 : CHECK(!flag_and_persistent->handle.IsEmpty());
94 70 : }
95 :
96 : enum class SurvivalMode { kSurvives, kDies };
97 :
98 : template <typename ModifierFunction, typename GCFunction>
99 55 : void WeakHandleTest(v8::Isolate* isolate, ConstructFunction construct_function,
100 : ModifierFunction modifier_function, GCFunction gc_function,
101 : SurvivalMode survives) {
102 110 : v8::HandleScope scope(isolate);
103 55 : v8::Local<v8::Context> context = v8::Context::New(isolate);
104 : v8::Context::Scope context_scope(context);
105 :
106 : FlagAndPersistent fp;
107 55 : construct_function(isolate, context, &fp);
108 55 : CHECK(heap::InYoungGeneration(isolate, fp.handle));
109 : fp.handle.SetWeak(&fp, &ResetHandleAndSetFlag,
110 : v8::WeakCallbackType::kParameter);
111 55 : fp.flag = false;
112 25 : modifier_function(&fp);
113 : gc_function();
114 55 : CHECK_IMPLIES(survives == SurvivalMode::kSurvives, !fp.flag);
115 55 : CHECK_IMPLIES(survives == SurvivalMode::kDies, fp.flag);
116 55 : }
117 :
118 5 : void ResurrectingFinalizer(
119 : const v8::WeakCallbackInfo<v8::Global<v8::Object>>& data) {
120 : data.GetParameter()->ClearWeak();
121 5 : }
122 :
123 5 : void ResettingFinalizer(
124 : const v8::WeakCallbackInfo<v8::Global<v8::Object>>& data) {
125 : data.GetParameter()->Reset();
126 5 : }
127 :
128 0 : void EmptyWeakCallback(const v8::WeakCallbackInfo<void>& data) {}
129 :
130 5 : void ResurrectingFinalizerSettingProperty(
131 : const v8::WeakCallbackInfo<v8::Global<v8::Object>>& data) {
132 : data.GetParameter()->ClearWeak();
133 : v8::Local<v8::Object> o =
134 : v8::Local<v8::Object>::New(data.GetIsolate(), *data.GetParameter());
135 10 : o->Set(data.GetIsolate()->GetCurrentContext(), v8_str("finalizer"),
136 20 : v8_str("was here"))
137 : .FromJust();
138 5 : }
139 :
140 : } // namespace
141 :
142 26661 : TEST(EternalHandles) {
143 5 : CcTest::InitializeVM();
144 : Isolate* isolate = CcTest::i_isolate();
145 : v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
146 : EternalHandles* eternal_handles = isolate->eternal_handles();
147 :
148 : // Create a number of handles that will not be on a block boundary
149 : const int kArrayLength = 2048-1;
150 : int indices[kArrayLength];
151 20475 : v8::Eternal<v8::Value> eternals[kArrayLength];
152 :
153 5 : CHECK_EQ(0, eternal_handles->handles_count());
154 20475 : for (int i = 0; i < kArrayLength; i++) {
155 10235 : indices[i] = -1;
156 : HandleScope scope(isolate);
157 10235 : v8::Local<v8::Object> object = v8::Object::New(v8_isolate);
158 20470 : object->Set(v8_isolate->GetCurrentContext(), i,
159 30705 : v8::Integer::New(v8_isolate, i))
160 : .FromJust();
161 : // Create with internal api
162 30705 : eternal_handles->Create(
163 10235 : isolate, *v8::Utils::OpenHandle(*object), &indices[i]);
164 : // Create with external api
165 10235 : CHECK(eternals[i].IsEmpty());
166 : eternals[i].Set(v8_isolate, object);
167 10235 : CHECK(!eternals[i].IsEmpty());
168 : }
169 :
170 5 : CcTest::CollectAllAvailableGarbage();
171 :
172 20475 : for (int i = 0; i < kArrayLength; i++) {
173 51175 : for (int j = 0; j < 2; j++) {
174 : HandleScope scope(isolate);
175 : v8::Local<v8::Value> local;
176 20470 : if (j == 0) {
177 : // Test internal api
178 10235 : local = v8::Utils::ToLocal(eternal_handles->Get(indices[i]));
179 : } else {
180 : // Test external api
181 : local = eternals[i].Get(v8_isolate);
182 : }
183 : v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(local);
184 : v8::Local<v8::Value> value =
185 20470 : object->Get(v8_isolate->GetCurrentContext(), i).ToLocalChecked();
186 20470 : CHECK(value->IsInt32());
187 40940 : CHECK_EQ(i,
188 : value->Int32Value(v8_isolate->GetCurrentContext()).FromJust());
189 : }
190 : }
191 :
192 5 : CHECK_EQ(2 * kArrayLength, eternal_handles->handles_count());
193 :
194 : // Create an eternal via the constructor
195 : {
196 : HandleScope scope(isolate);
197 5 : v8::Local<v8::Object> object = v8::Object::New(v8_isolate);
198 : v8::Eternal<v8::Object> eternal(v8_isolate, object);
199 5 : CHECK(!eternal.IsEmpty());
200 5 : CHECK(object == eternal.Get(v8_isolate));
201 : }
202 :
203 5 : CHECK_EQ(2 * kArrayLength + 1, eternal_handles->handles_count());
204 5 : }
205 :
206 :
207 26661 : TEST(PersistentBaseGetLocal) {
208 5 : CcTest::InitializeVM();
209 5 : v8::Isolate* isolate = CcTest::isolate();
210 :
211 10 : v8::HandleScope scope(isolate);
212 5 : v8::Local<v8::Object> o = v8::Object::New(isolate);
213 5 : CHECK(!o.IsEmpty());
214 : v8::Persistent<v8::Object> p(isolate, o);
215 5 : CHECK(o == p.Get(isolate));
216 5 : CHECK(v8::Local<v8::Object>::New(isolate, p) == p.Get(isolate));
217 :
218 : v8::Global<v8::Object> g(isolate, o);
219 5 : CHECK(o == g.Get(isolate));
220 5 : CHECK(v8::Local<v8::Object>::New(isolate, g) == g.Get(isolate));
221 5 : }
222 :
223 26661 : TEST(WeakPersistentSmi) {
224 5 : CcTest::InitializeVM();
225 5 : v8::Isolate* isolate = CcTest::isolate();
226 :
227 10 : v8::HandleScope scope(isolate);
228 5 : v8::Local<v8::Number> n = v8::Number::New(isolate, 0);
229 : v8::Global<v8::Number> g(isolate, n);
230 :
231 : // Should not crash.
232 : g.SetWeak<void>(nullptr, &EmptyWeakCallback,
233 : v8::WeakCallbackType::kParameter);
234 5 : }
235 :
236 26661 : TEST(FinalizerWeakness) {
237 5 : CcTest::InitializeVM();
238 5 : v8::Isolate* isolate = CcTest::isolate();
239 :
240 : v8::Global<v8::Object> g;
241 : int identity;
242 :
243 : {
244 10 : v8::HandleScope scope(isolate);
245 5 : v8::Local<v8::Object> o = v8::Object::New(isolate);
246 5 : identity = o->GetIdentityHash();
247 : g.Reset(isolate, o);
248 : g.SetWeak(&g, &ResurrectingFinalizerSettingProperty,
249 : v8::WeakCallbackType::kFinalizer);
250 : }
251 :
252 5 : CcTest::CollectAllAvailableGarbage();
253 :
254 5 : CHECK(!g.IsEmpty());
255 10 : v8::HandleScope scope(isolate);
256 : v8::Local<v8::Object> o = v8::Local<v8::Object>::New(isolate, g);
257 5 : CHECK_EQ(identity, o->GetIdentityHash());
258 15 : CHECK(o->Has(isolate->GetCurrentContext(), v8_str("finalizer")).FromJust());
259 5 : }
260 :
261 26661 : TEST(PhatomHandlesWithoutCallbacks) {
262 5 : CcTest::InitializeVM();
263 5 : v8::Isolate* isolate = CcTest::isolate();
264 :
265 : v8::Global<v8::Object> g1, g2;
266 : {
267 10 : v8::HandleScope scope(isolate);
268 10 : g1.Reset(isolate, v8::Object::New(isolate));
269 : g1.SetWeak();
270 10 : g2.Reset(isolate, v8::Object::New(isolate));
271 : g2.SetWeak();
272 : }
273 :
274 5 : CHECK_EQ(0u, isolate->NumberOfPhantomHandleResetsSinceLastCall());
275 5 : CcTest::CollectAllAvailableGarbage();
276 5 : CHECK_EQ(2u, isolate->NumberOfPhantomHandleResetsSinceLastCall());
277 5 : CHECK_EQ(0u, isolate->NumberOfPhantomHandleResetsSinceLastCall());
278 5 : }
279 :
280 26661 : TEST(WeakHandleToUnmodifiedJSObjectSurvivesScavenge) {
281 5 : CcTest::InitializeVM();
282 5 : WeakHandleTest(
283 : CcTest::isolate(), &ConstructJSObject, [](FlagAndPersistent* fp) {},
284 5 : []() { InvokeScavenge(); }, SurvivalMode::kSurvives);
285 5 : }
286 :
287 26661 : TEST(WeakHandleToUnmodifiedJSObjectDiesOnMarkCompact) {
288 5 : CcTest::InitializeVM();
289 5 : WeakHandleTest(
290 : CcTest::isolate(), &ConstructJSObject, [](FlagAndPersistent* fp) {},
291 5 : []() { InvokeMarkSweep(); }, SurvivalMode::kDies);
292 5 : }
293 :
294 26661 : TEST(WeakHandleToUnmodifiedJSObjectSurvivesMarkCompactWhenInHandle) {
295 5 : CcTest::InitializeVM();
296 5 : WeakHandleTest(
297 : CcTest::isolate(), &ConstructJSObject,
298 5 : [](FlagAndPersistent* fp) {
299 : v8::Local<v8::Object> handle =
300 5 : v8::Local<v8::Object>::New(CcTest::isolate(), fp->handle);
301 : USE(handle);
302 5 : },
303 5 : []() { InvokeMarkSweep(); }, SurvivalMode::kSurvives);
304 5 : }
305 :
306 26661 : TEST(WeakHandleToUnmodifiedJSApiObjectDiesOnScavenge) {
307 5 : CcTest::InitializeVM();
308 5 : WeakHandleTest(
309 : CcTest::isolate(), &ConstructJSApiObject, [](FlagAndPersistent* fp) {},
310 5 : []() { InvokeScavenge(); }, SurvivalMode::kDies);
311 5 : }
312 :
313 26661 : TEST(WeakHandleToJSApiObjectWithIdentityHashSurvivesScavenge) {
314 5 : CcTest::InitializeVM();
315 : Isolate* i_isolate = CcTest::i_isolate();
316 : HandleScope scope(i_isolate);
317 5 : Handle<JSWeakMap> weakmap = i_isolate->factory()->NewJSWeakMap();
318 :
319 5 : WeakHandleTest(
320 : CcTest::isolate(), &ConstructJSApiObject,
321 20 : [&weakmap, i_isolate](FlagAndPersistent* fp) {
322 10 : v8::HandleScope scope(CcTest::isolate());
323 : Handle<JSReceiver> key =
324 5 : Utils::OpenHandle(*fp->handle.Get(CcTest::isolate()));
325 : Handle<Smi> smi(Smi::FromInt(23), i_isolate);
326 10 : int32_t hash = key->GetOrCreateHash(i_isolate)->value();
327 5 : JSWeakCollection::Set(weakmap, key, smi, hash);
328 5 : },
329 10 : []() { InvokeScavenge(); }, SurvivalMode::kSurvives);
330 5 : }
331 :
332 26661 : TEST(WeakHandleToUnmodifiedJSApiObjectSurvivesScavengeWhenInHandle) {
333 5 : CcTest::InitializeVM();
334 5 : WeakHandleTest(
335 : CcTest::isolate(), &ConstructJSApiObject,
336 5 : [](FlagAndPersistent* fp) {
337 : v8::Local<v8::Object> handle =
338 5 : v8::Local<v8::Object>::New(CcTest::isolate(), fp->handle);
339 : USE(handle);
340 5 : },
341 5 : []() { InvokeScavenge(); }, SurvivalMode::kSurvives);
342 5 : }
343 :
344 26661 : TEST(WeakHandleToUnmodifiedJSApiObjectDiesOnMarkCompact) {
345 5 : CcTest::InitializeVM();
346 5 : WeakHandleTest(
347 : CcTest::isolate(), &ConstructJSApiObject, [](FlagAndPersistent* fp) {},
348 5 : []() { InvokeMarkSweep(); }, SurvivalMode::kDies);
349 5 : }
350 :
351 26661 : TEST(WeakHandleToUnmodifiedJSApiObjectSurvivesMarkCompactWhenInHandle) {
352 5 : CcTest::InitializeVM();
353 5 : WeakHandleTest(
354 : CcTest::isolate(), &ConstructJSApiObject,
355 5 : [](FlagAndPersistent* fp) {
356 : v8::Local<v8::Object> handle =
357 5 : v8::Local<v8::Object>::New(CcTest::isolate(), fp->handle);
358 : USE(handle);
359 5 : },
360 5 : []() { InvokeMarkSweep(); }, SurvivalMode::kSurvives);
361 5 : }
362 :
363 26661 : TEST(WeakHandleToActiveUnmodifiedJSApiObjectSurvivesScavenge) {
364 5 : CcTest::InitializeVM();
365 5 : WeakHandleTest(
366 : CcTest::isolate(), &ConstructJSApiObject,
367 : [](FlagAndPersistent* fp) {
368 : #if __clang__
369 : #pragma clang diagnostic push
370 : #pragma clang diagnostic ignored "-Wdeprecated"
371 : #endif
372 : fp->handle.MarkActive();
373 : #if __clang__
374 : #pragma clang diagnostic pop
375 : #endif
376 : },
377 5 : []() { InvokeScavenge(); }, SurvivalMode::kSurvives);
378 5 : }
379 :
380 26661 : TEST(WeakHandleToActiveUnmodifiedJSApiObjectDiesOnMarkCompact) {
381 5 : CcTest::InitializeVM();
382 5 : WeakHandleTest(
383 : CcTest::isolate(), &ConstructJSApiObject,
384 : [](FlagAndPersistent* fp) {
385 : #if __clang__
386 : #pragma clang diagnostic push
387 : #pragma clang diagnostic ignored "-Wdeprecated"
388 : #endif
389 : fp->handle.MarkActive();
390 : #if __clang__
391 : #pragma clang diagnostic pop
392 : #endif
393 : },
394 5 : []() { InvokeMarkSweep(); }, SurvivalMode::kDies);
395 5 : }
396 :
397 26661 : TEST(WeakHandleToActiveUnmodifiedJSApiObjectSurvivesMarkCompactWhenInHandle) {
398 5 : CcTest::InitializeVM();
399 5 : WeakHandleTest(
400 : CcTest::isolate(), &ConstructJSApiObject,
401 5 : [](FlagAndPersistent* fp) {
402 : #if __clang__
403 : #pragma clang diagnostic push
404 : #pragma clang diagnostic ignored "-Wdeprecated"
405 : #endif
406 : fp->handle.MarkActive();
407 : #if __clang__
408 : #pragma clang diagnostic pop
409 : #endif
410 : v8::Local<v8::Object> handle =
411 5 : v8::Local<v8::Object>::New(CcTest::isolate(), fp->handle);
412 : USE(handle);
413 5 : },
414 5 : []() { InvokeMarkSweep(); }, SurvivalMode::kSurvives);
415 5 : }
416 :
417 26661 : TEST(FinalizerOnUnmodifiedJSApiObjectDoesNotCrash) {
418 : // See crbug.com/v8/8586.
419 5 : CcTest::InitializeVM();
420 5 : v8::Isolate* isolate = CcTest::isolate();
421 10 : v8::HandleScope scope(isolate);
422 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
423 : v8::Context::Scope context_scope(context);
424 :
425 : FlagAndPersistent fp;
426 : // Could use a regular object and MarkIndependent too.
427 5 : ConstructJSApiObject(isolate, context, &fp);
428 : fp.handle.SetWeak(&fp, &ResetHandleAndSetFlag,
429 : v8::WeakCallbackType::kFinalizer);
430 5 : fp.flag = false;
431 : {
432 10 : v8::HandleScope scope(isolate);
433 : v8::Local<v8::Object> tmp = v8::Local<v8::Object>::New(isolate, fp.handle);
434 : USE(tmp);
435 : InvokeScavenge();
436 : }
437 5 : }
438 :
439 : namespace {
440 :
441 10 : void ConstructFinalizerPointingPhantomHandle(
442 : v8::Isolate* isolate, v8::Global<v8::Object>* g1,
443 : v8::Global<v8::Object>* g2,
444 : typename v8::WeakCallbackInfo<v8::Global<v8::Object>>::Callback
445 : finalizer_for_g1) {
446 20 : v8::HandleScope scope(isolate);
447 : v8::Local<v8::Object> o1 =
448 10 : v8::Local<v8::Object>::New(isolate, v8::Object::New(isolate));
449 : v8::Local<v8::Object> o2 =
450 10 : v8::Local<v8::Object>::New(isolate, v8::Object::New(isolate));
451 30 : o1->Set(isolate->GetCurrentContext(), v8_str("link"), o2).FromJust();
452 : g1->Reset(isolate, o1);
453 : g2->Reset(isolate, o2);
454 : // g1 will be finalized but resurrected.
455 : g1->SetWeak(g1, finalizer_for_g1, v8::WeakCallbackType::kFinalizer);
456 : // g2 will be a phantom handle that is dependent on the finalizer handle
457 : // g1 as it is in its subgraph.
458 : g2->SetWeak();
459 10 : }
460 :
461 : } // namespace
462 :
463 26661 : TEST(FinalizerResurrectsAndKeepsPhantomAliveOnMarkCompact) {
464 : // See crbug.com/772299.
465 5 : CcTest::InitializeVM();
466 : v8::Global<v8::Object> g1, g2;
467 5 : ConstructFinalizerPointingPhantomHandle(CcTest::isolate(), &g1, &g2,
468 5 : ResurrectingFinalizer);
469 : InvokeMarkSweep();
470 : // Both, g1 and g2, should stay alive as the finalizer resurrects the root
471 : // object that transitively keeps the other one alive.
472 5 : CHECK(!g1.IsEmpty());
473 5 : CHECK(!g2.IsEmpty());
474 : InvokeMarkSweep();
475 : // The finalizer handle is now strong, so it should keep the objects alive.
476 5 : CHECK(!g1.IsEmpty());
477 5 : CHECK(!g2.IsEmpty());
478 5 : }
479 :
480 26661 : TEST(FinalizerDiesAndKeepsPhantomAliveOnMarkCompact) {
481 5 : CcTest::InitializeVM();
482 : v8::Global<v8::Object> g1, g2;
483 5 : ConstructFinalizerPointingPhantomHandle(CcTest::isolate(), &g1, &g2,
484 5 : ResettingFinalizer);
485 : InvokeMarkSweep();
486 : // Finalizer (g1) dies but the phantom handle (g2) is kept alive for one
487 : // more round as the underlying object only dies on the next GC.
488 5 : CHECK(g1.IsEmpty());
489 5 : CHECK(!g2.IsEmpty());
490 : InvokeMarkSweep();
491 : // Phantom handle dies after one more round.
492 5 : CHECK(g1.IsEmpty());
493 5 : CHECK(g2.IsEmpty());
494 5 : }
495 :
496 : namespace {
497 :
498 10 : void ForceScavenge2(const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
499 10 : data.GetParameter()->flag = true;
500 : InvokeScavenge();
501 10 : }
502 :
503 10 : void ForceScavenge1(const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
504 : data.GetParameter()->handle.Reset();
505 : data.SetSecondPassCallback(ForceScavenge2);
506 10 : }
507 :
508 10 : void ForceMarkSweep2(const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
509 10 : data.GetParameter()->flag = true;
510 : InvokeMarkSweep();
511 10 : }
512 :
513 10 : void ForceMarkSweep1(const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
514 : data.GetParameter()->handle.Reset();
515 : data.SetSecondPassCallback(ForceMarkSweep2);
516 10 : }
517 :
518 : } // namespace
519 :
520 26661 : TEST(GCFromWeakCallbacks) {
521 5 : v8::Isolate* isolate = CcTest::isolate();
522 10 : v8::Locker locker(CcTest::isolate());
523 10 : v8::HandleScope scope(isolate);
524 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
525 : v8::Context::Scope context_scope(context);
526 :
527 : static const int kNumberOfGCTypes = 2;
528 : typedef v8::WeakCallbackInfo<FlagAndPersistent>::Callback Callback;
529 : Callback gc_forcing_callback[kNumberOfGCTypes] = {&ForceScavenge1,
530 5 : &ForceMarkSweep1};
531 :
532 : typedef void (*GCInvoker)();
533 5 : GCInvoker invoke_gc[kNumberOfGCTypes] = {&InvokeScavenge, &InvokeMarkSweep};
534 :
535 25 : for (int outer_gc = 0; outer_gc < kNumberOfGCTypes; outer_gc++) {
536 50 : for (int inner_gc = 0; inner_gc < kNumberOfGCTypes; inner_gc++) {
537 : FlagAndPersistent fp;
538 20 : ConstructJSApiObject(isolate, context, &fp);
539 20 : CHECK(heap::InYoungGeneration(isolate, fp.handle));
540 20 : fp.flag = false;
541 20 : fp.handle.SetWeak(&fp, gc_forcing_callback[inner_gc],
542 : v8::WeakCallbackType::kParameter);
543 20 : invoke_gc[outer_gc]();
544 20 : EmptyMessageQueues(isolate);
545 20 : CHECK(fp.flag);
546 : }
547 : }
548 5 : }
549 :
550 : namespace {
551 :
552 5 : void SecondPassCallback(const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
553 5 : data.GetParameter()->flag = true;
554 5 : }
555 :
556 5 : void FirstPassCallback(const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
557 : data.GetParameter()->handle.Reset();
558 : data.SetSecondPassCallback(SecondPassCallback);
559 5 : }
560 :
561 : } // namespace
562 :
563 26661 : TEST(SecondPassPhantomCallbacks) {
564 5 : v8::Isolate* isolate = CcTest::isolate();
565 10 : v8::Locker locker(CcTest::isolate());
566 10 : v8::HandleScope scope(isolate);
567 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
568 : v8::Context::Scope context_scope(context);
569 : FlagAndPersistent fp;
570 5 : ConstructJSApiObject(isolate, context, &fp);
571 5 : fp.flag = false;
572 : fp.handle.SetWeak(&fp, FirstPassCallback, v8::WeakCallbackType::kParameter);
573 5 : CHECK(!fp.flag);
574 : InvokeMarkSweep();
575 : InvokeMarkSweep();
576 5 : CHECK(fp.flag);
577 5 : }
578 :
579 26661 : TEST(MoveStrongGlobal) {
580 5 : CcTest::InitializeVM();
581 5 : v8::Isolate* isolate = CcTest::isolate();
582 10 : v8::HandleScope scope(isolate);
583 :
584 5 : v8::Global<v8::Object>* global = new Global<v8::Object>();
585 5 : ConstructJSObject(isolate, global);
586 : InvokeMarkSweep();
587 : v8::Global<v8::Object> global2(std::move(*global));
588 10 : delete global;
589 : InvokeMarkSweep();
590 5 : }
591 :
592 26661 : TEST(MoveWeakGlobal) {
593 5 : CcTest::InitializeVM();
594 5 : v8::Isolate* isolate = CcTest::isolate();
595 10 : v8::HandleScope scope(isolate);
596 :
597 5 : v8::Global<v8::Object>* global = new Global<v8::Object>();
598 5 : ConstructJSObject(isolate, global);
599 : InvokeMarkSweep();
600 : global->SetWeak();
601 : v8::Global<v8::Object> global2(std::move(*global));
602 10 : delete global;
603 : InvokeMarkSweep();
604 5 : }
605 :
606 : } // namespace internal
607 79968 : } // namespace v8
|