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/isolate.h"
32 : #include "src/objects-inl.h"
33 : #include "test/cctest/cctest.h"
34 :
35 : namespace v8 {
36 : namespace internal {
37 :
38 : namespace {
39 :
40 45 : void InvokeScavenge() { CcTest::CollectGarbage(i::NEW_SPACE); }
41 :
42 75 : void InvokeMarkSweep() { CcTest::CollectAllGarbage(); }
43 :
44 130 : void SimpleCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
45 65 : info.GetReturnValue().Set(v8_num(0));
46 65 : }
47 :
48 : struct FlagAndPersistent {
49 : bool flag;
50 : v8::Global<v8::Object> handle;
51 : };
52 :
53 20 : void ResetHandleAndSetFlag(
54 40 : const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
55 : data.GetParameter()->handle.Reset();
56 20 : data.GetParameter()->flag = true;
57 20 : }
58 :
59 : using ConstructFunction = void (*)(v8::Isolate* isolate,
60 : v8::Local<v8::Context> context,
61 : FlagAndPersistent* flag_and_persistent);
62 :
63 15 : void ConstructJSObject(v8::Isolate* isolate, v8::Local<v8::Context> context,
64 : FlagAndPersistent* flag_and_persistent) {
65 15 : v8::HandleScope handle_scope(isolate);
66 15 : v8::Local<v8::Object> object(v8::Object::New(isolate));
67 15 : CHECK(!object.IsEmpty());
68 : flag_and_persistent->handle.Reset(isolate, object);
69 15 : CHECK(!flag_and_persistent->handle.IsEmpty());
70 15 : }
71 :
72 65 : void ConstructJSApiObject(v8::Isolate* isolate, v8::Local<v8::Context> context,
73 : FlagAndPersistent* flag_and_persistent) {
74 65 : v8::HandleScope handle_scope(isolate);
75 : v8::Local<v8::FunctionTemplate> fun =
76 65 : v8::FunctionTemplate::New(isolate, SimpleCallback);
77 : v8::Local<v8::Object> object = fun->GetFunction(context)
78 65 : .ToLocalChecked()
79 : ->NewInstance(context)
80 : .ToLocalChecked();
81 65 : CHECK(!object.IsEmpty());
82 : flag_and_persistent->handle.Reset(isolate, object);
83 65 : CHECK(!flag_and_persistent->handle.IsEmpty());
84 65 : }
85 :
86 : enum class SurvivalMode { kSurvives, kDies };
87 :
88 : template <typename ModifierFunction, typename GCFunction>
89 50 : void WeakHandleTest(v8::Isolate* isolate, ConstructFunction construct_function,
90 : ModifierFunction modifier_function, GCFunction gc_function,
91 : SurvivalMode survives) {
92 50 : v8::HandleScope scope(isolate);
93 50 : v8::Local<v8::Context> context = v8::Context::New(isolate);
94 : v8::Context::Scope context_scope(context);
95 :
96 : FlagAndPersistent fp;
97 50 : construct_function(isolate, context, &fp);
98 : {
99 50 : v8::HandleScope scope(isolate);
100 : v8::Local<v8::Object> tmp = v8::Local<v8::Object>::New(isolate, fp.handle);
101 50 : CHECK(i::Heap::InNewSpace(*v8::Utils::OpenHandle(*tmp)));
102 : }
103 :
104 : fp.handle.SetWeak(&fp, &ResetHandleAndSetFlag,
105 : v8::WeakCallbackType::kParameter);
106 50 : fp.flag = false;
107 20 : modifier_function(&fp);
108 : gc_function();
109 50 : CHECK_IMPLIES(survives == SurvivalMode::kSurvives, !fp.flag);
110 100 : CHECK_IMPLIES(survives == SurvivalMode::kDies, fp.flag);
111 50 : }
112 :
113 5 : void ResurrectingFinalizer(
114 5 : const v8::WeakCallbackInfo<v8::Global<v8::Object>>& data) {
115 : data.GetParameter()->ClearWeak();
116 5 : }
117 :
118 5 : void ResettingFinalizer(
119 5 : const v8::WeakCallbackInfo<v8::Global<v8::Object>>& data) {
120 : data.GetParameter()->Reset();
121 5 : }
122 :
123 0 : void EmptyWeakCallback(const v8::WeakCallbackInfo<void>& data) {}
124 :
125 5 : void ResurrectingFinalizerSettingProperty(
126 15 : const v8::WeakCallbackInfo<v8::Global<v8::Object>>& data) {
127 : data.GetParameter()->ClearWeak();
128 : v8::Local<v8::Object> o =
129 : v8::Local<v8::Object>::New(data.GetIsolate(), *data.GetParameter());
130 : o->Set(data.GetIsolate()->GetCurrentContext(), v8_str("finalizer"),
131 20 : v8_str("was here"))
132 10 : .FromJust();
133 5 : }
134 :
135 : } // namespace
136 :
137 28342 : TEST(EternalHandles) {
138 5 : CcTest::InitializeVM();
139 5 : Isolate* isolate = CcTest::i_isolate();
140 : v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
141 15 : EternalHandles* eternal_handles = isolate->eternal_handles();
142 :
143 : // Create a number of handles that will not be on a block boundary
144 : const int kArrayLength = 2048-1;
145 : int indices[kArrayLength];
146 10240 : v8::Eternal<v8::Value> eternals[kArrayLength];
147 :
148 5 : CHECK_EQ(0, eternal_handles->NumberOfHandles());
149 10235 : for (int i = 0; i < kArrayLength; i++) {
150 10235 : indices[i] = -1;
151 : HandleScope scope(isolate);
152 10235 : v8::Local<v8::Object> object = v8::Object::New(v8_isolate);
153 : object->Set(v8_isolate->GetCurrentContext(), i,
154 30705 : v8::Integer::New(v8_isolate, i))
155 20470 : .FromJust();
156 : // Create with internal api
157 : eternal_handles->Create(
158 20470 : isolate, *v8::Utils::OpenHandle(*object), &indices[i]);
159 : // Create with external api
160 20470 : CHECK(eternals[i].IsEmpty());
161 : eternals[i].Set(v8_isolate, object);
162 10235 : CHECK(!eternals[i].IsEmpty());
163 : }
164 :
165 5 : CcTest::CollectAllAvailableGarbage();
166 :
167 10240 : for (int i = 0; i < kArrayLength; i++) {
168 20470 : for (int j = 0; j < 2; j++) {
169 : HandleScope scope(isolate);
170 : v8::Local<v8::Value> local;
171 20470 : if (j == 0) {
172 : // Test internal api
173 10235 : local = v8::Utils::ToLocal(eternal_handles->Get(indices[i]));
174 : } else {
175 : // Test external api
176 10235 : local = eternals[i].Get(v8_isolate);
177 : }
178 : v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(local);
179 : v8::Local<v8::Value> value =
180 20470 : object->Get(v8_isolate->GetCurrentContext(), i).ToLocalChecked();
181 20470 : CHECK(value->IsInt32());
182 40940 : CHECK_EQ(i,
183 : value->Int32Value(v8_isolate->GetCurrentContext()).FromJust());
184 : }
185 : }
186 :
187 5 : CHECK_EQ(2*kArrayLength, eternal_handles->NumberOfHandles());
188 :
189 : // Create an eternal via the constructor
190 : {
191 : HandleScope scope(isolate);
192 5 : v8::Local<v8::Object> object = v8::Object::New(v8_isolate);
193 : v8::Eternal<v8::Object> eternal(v8_isolate, object);
194 5 : CHECK(!eternal.IsEmpty());
195 5 : CHECK(object == eternal.Get(v8_isolate));
196 : }
197 :
198 5 : CHECK_EQ(2*kArrayLength + 1, eternal_handles->NumberOfHandles());
199 5 : }
200 :
201 :
202 28342 : TEST(PersistentBaseGetLocal) {
203 5 : CcTest::InitializeVM();
204 5 : v8::Isolate* isolate = CcTest::isolate();
205 :
206 5 : v8::HandleScope scope(isolate);
207 5 : v8::Local<v8::Object> o = v8::Object::New(isolate);
208 5 : CHECK(!o.IsEmpty());
209 : v8::Persistent<v8::Object> p(isolate, o);
210 5 : CHECK(o == p.Get(isolate));
211 5 : CHECK(v8::Local<v8::Object>::New(isolate, p) == p.Get(isolate));
212 :
213 : v8::Global<v8::Object> g(isolate, o);
214 5 : CHECK(o == g.Get(isolate));
215 10 : CHECK(v8::Local<v8::Object>::New(isolate, g) == g.Get(isolate));
216 5 : }
217 :
218 28342 : TEST(WeakPersistentSmi) {
219 5 : CcTest::InitializeVM();
220 5 : v8::Isolate* isolate = CcTest::isolate();
221 :
222 5 : v8::HandleScope scope(isolate);
223 5 : v8::Local<v8::Number> n = v8::Number::New(isolate, 0);
224 : v8::Global<v8::Number> g(isolate, n);
225 :
226 : // Should not crash.
227 : g.SetWeak<void>(nullptr, &EmptyWeakCallback,
228 5 : v8::WeakCallbackType::kParameter);
229 5 : }
230 :
231 28342 : TEST(FinalizerWeakness) {
232 5 : CcTest::InitializeVM();
233 5 : v8::Isolate* isolate = CcTest::isolate();
234 :
235 : v8::Global<v8::Object> g;
236 : int identity;
237 :
238 : {
239 5 : v8::HandleScope scope(isolate);
240 5 : v8::Local<v8::Object> o = v8::Object::New(isolate);
241 5 : identity = o->GetIdentityHash();
242 : g.Reset(isolate, o);
243 : g.SetWeak(&g, &ResurrectingFinalizerSettingProperty,
244 5 : v8::WeakCallbackType::kFinalizer);
245 : }
246 :
247 5 : CcTest::CollectAllAvailableGarbage();
248 :
249 5 : CHECK(!g.IsEmpty());
250 10 : v8::HandleScope scope(isolate);
251 : v8::Local<v8::Object> o = v8::Local<v8::Object>::New(isolate, g);
252 5 : CHECK_EQ(identity, o->GetIdentityHash());
253 15 : CHECK(o->Has(isolate->GetCurrentContext(), v8_str("finalizer")).FromJust());
254 5 : }
255 :
256 28342 : TEST(PhatomHandlesWithoutCallbacks) {
257 5 : CcTest::InitializeVM();
258 5 : v8::Isolate* isolate = CcTest::isolate();
259 :
260 : v8::Global<v8::Object> g1, g2;
261 : {
262 5 : v8::HandleScope scope(isolate);
263 10 : g1.Reset(isolate, v8::Object::New(isolate));
264 : g1.SetWeak();
265 10 : g2.Reset(isolate, v8::Object::New(isolate));
266 5 : g2.SetWeak();
267 : }
268 :
269 5 : CHECK_EQ(0u, isolate->NumberOfPhantomHandleResetsSinceLastCall());
270 5 : CcTest::CollectAllAvailableGarbage();
271 5 : CHECK_EQ(2u, isolate->NumberOfPhantomHandleResetsSinceLastCall());
272 5 : CHECK_EQ(0u, isolate->NumberOfPhantomHandleResetsSinceLastCall());
273 5 : }
274 :
275 28342 : TEST(WeakHandleToUnmodifiedJSObjectSurvivesScavenge) {
276 5 : CcTest::InitializeVM();
277 : WeakHandleTest(
278 : CcTest::isolate(), &ConstructJSObject, [](FlagAndPersistent* fp) {},
279 5 : []() { InvokeScavenge(); }, SurvivalMode::kSurvives);
280 5 : }
281 :
282 28342 : TEST(WeakHandleToUnmodifiedJSObjectDiesOnMarkCompact) {
283 5 : CcTest::InitializeVM();
284 : WeakHandleTest(
285 : CcTest::isolate(), &ConstructJSObject, [](FlagAndPersistent* fp) {},
286 5 : []() { InvokeMarkSweep(); }, SurvivalMode::kDies);
287 5 : }
288 :
289 28342 : TEST(WeakHandleToUnmodifiedJSObjectSurvivesMarkCompactWhenInHandle) {
290 5 : CcTest::InitializeVM();
291 : WeakHandleTest(
292 : CcTest::isolate(), &ConstructJSObject,
293 5 : [](FlagAndPersistent* fp) {
294 : v8::Local<v8::Object> handle =
295 5 : v8::Local<v8::Object>::New(CcTest::isolate(), fp->handle);
296 : USE(handle);
297 5 : },
298 5 : []() { InvokeMarkSweep(); }, SurvivalMode::kSurvives);
299 5 : }
300 :
301 28342 : TEST(WeakHandleToUnmodifiedJSApiObjectDiesOnScavenge) {
302 5 : CcTest::InitializeVM();
303 : WeakHandleTest(
304 : CcTest::isolate(), &ConstructJSApiObject, [](FlagAndPersistent* fp) {},
305 5 : []() { InvokeScavenge(); }, SurvivalMode::kDies);
306 5 : }
307 :
308 28342 : TEST(WeakHandleToUnmodifiedJSApiObjectSurvivesScavengeWhenInHandle) {
309 5 : CcTest::InitializeVM();
310 : WeakHandleTest(
311 : CcTest::isolate(), &ConstructJSApiObject,
312 5 : [](FlagAndPersistent* fp) {
313 : v8::Local<v8::Object> handle =
314 5 : v8::Local<v8::Object>::New(CcTest::isolate(), fp->handle);
315 : USE(handle);
316 5 : },
317 5 : []() { InvokeScavenge(); }, SurvivalMode::kSurvives);
318 5 : }
319 :
320 28342 : TEST(WeakHandleToUnmodifiedJSApiObjectDiesOnMarkCompact) {
321 5 : CcTest::InitializeVM();
322 : WeakHandleTest(
323 : CcTest::isolate(), &ConstructJSApiObject, [](FlagAndPersistent* fp) {},
324 5 : []() { InvokeMarkSweep(); }, SurvivalMode::kDies);
325 5 : }
326 :
327 28342 : TEST(WeakHandleToUnmodifiedJSApiObjectSurvivesMarkCompactWhenInHandle) {
328 5 : CcTest::InitializeVM();
329 : WeakHandleTest(
330 : CcTest::isolate(), &ConstructJSApiObject,
331 5 : [](FlagAndPersistent* fp) {
332 : v8::Local<v8::Object> handle =
333 5 : v8::Local<v8::Object>::New(CcTest::isolate(), fp->handle);
334 : USE(handle);
335 5 : },
336 5 : []() { InvokeMarkSweep(); }, SurvivalMode::kSurvives);
337 5 : }
338 :
339 28342 : TEST(WeakHandleToActiveUnmodifiedJSApiObjectSurvivesScavenge) {
340 5 : CcTest::InitializeVM();
341 : WeakHandleTest(
342 : CcTest::isolate(), &ConstructJSApiObject,
343 : [](FlagAndPersistent* fp) { fp->handle.MarkActive(); },
344 5 : []() { InvokeScavenge(); }, SurvivalMode::kSurvives);
345 5 : }
346 :
347 28342 : TEST(WeakHandleToActiveUnmodifiedJSApiObjectDiesOnMarkCompact) {
348 5 : CcTest::InitializeVM();
349 : WeakHandleTest(
350 : CcTest::isolate(), &ConstructJSApiObject,
351 : [](FlagAndPersistent* fp) { fp->handle.MarkActive(); },
352 5 : []() { InvokeMarkSweep(); }, SurvivalMode::kDies);
353 5 : }
354 :
355 28342 : TEST(WeakHandleToActiveUnmodifiedJSApiObjectSurvivesMarkCompactWhenInHandle) {
356 5 : CcTest::InitializeVM();
357 : WeakHandleTest(
358 : CcTest::isolate(), &ConstructJSApiObject,
359 5 : [](FlagAndPersistent* fp) {
360 : fp->handle.MarkActive();
361 : v8::Local<v8::Object> handle =
362 5 : v8::Local<v8::Object>::New(CcTest::isolate(), fp->handle);
363 : USE(handle);
364 5 : },
365 5 : []() { InvokeMarkSweep(); }, SurvivalMode::kSurvives);
366 5 : }
367 :
368 28342 : TEST(FinalizerOnUnmodifiedJSApiObjectDoesNotCrash) {
369 : // See crbug.com/v8/8586.
370 5 : CcTest::InitializeVM();
371 5 : v8::Isolate* isolate = CcTest::isolate();
372 5 : v8::HandleScope scope(isolate);
373 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
374 : v8::Context::Scope context_scope(context);
375 :
376 : FlagAndPersistent fp;
377 : // Could use a regular object and MarkIndependent too.
378 5 : ConstructJSApiObject(isolate, context, &fp);
379 : fp.handle.SetWeak(&fp, &ResetHandleAndSetFlag,
380 : v8::WeakCallbackType::kFinalizer);
381 5 : fp.flag = false;
382 : {
383 5 : v8::HandleScope scope(isolate);
384 : v8::Local<v8::Object> tmp = v8::Local<v8::Object>::New(isolate, fp.handle);
385 : USE(tmp);
386 5 : InvokeScavenge();
387 5 : }
388 5 : }
389 :
390 : namespace {
391 :
392 10 : void ConstructFinalizerPointingPhantomHandle(
393 : v8::Isolate* isolate, v8::Global<v8::Object>* g1,
394 : v8::Global<v8::Object>* g2,
395 : typename v8::WeakCallbackInfo<v8::Global<v8::Object>>::Callback
396 : finalizer_for_g1) {
397 10 : v8::HandleScope scope(isolate);
398 : v8::Local<v8::Object> o1 =
399 10 : v8::Local<v8::Object>::New(isolate, v8::Object::New(isolate));
400 : v8::Local<v8::Object> o2 =
401 10 : v8::Local<v8::Object>::New(isolate, v8::Object::New(isolate));
402 30 : o1->Set(isolate->GetCurrentContext(), v8_str("link"), o2).FromJust();
403 : g1->Reset(isolate, o1);
404 : g2->Reset(isolate, o2);
405 : // g1 will be finalized but resurrected.
406 : g1->SetWeak(g1, finalizer_for_g1, v8::WeakCallbackType::kFinalizer);
407 : // g2 will be a phantom handle that is dependent on the finalizer handle
408 : // g1 as it is in its subgraph.
409 10 : g2->SetWeak();
410 10 : }
411 :
412 : } // namespace
413 :
414 28342 : TEST(FinalizerResurrectsAndKeepsPhantomAliveOnMarkCompact) {
415 : // See crbug.com/772299.
416 5 : CcTest::InitializeVM();
417 : v8::Global<v8::Object> g1, g2;
418 : ConstructFinalizerPointingPhantomHandle(CcTest::isolate(), &g1, &g2,
419 5 : ResurrectingFinalizer);
420 : InvokeMarkSweep();
421 : // Both, g1 and g2, should stay alive as the finalizer resurrects the root
422 : // object that transitively keeps the other one alive.
423 5 : CHECK(!g1.IsEmpty());
424 5 : CHECK(!g2.IsEmpty());
425 : InvokeMarkSweep();
426 : // The finalizer handle is now strong, so it should keep the objects alive.
427 5 : CHECK(!g1.IsEmpty());
428 5 : CHECK(!g2.IsEmpty());
429 5 : }
430 :
431 28342 : TEST(FinalizerDiesAndKeepsPhantomAliveOnMarkCompact) {
432 5 : CcTest::InitializeVM();
433 : v8::Global<v8::Object> g1, g2;
434 : ConstructFinalizerPointingPhantomHandle(CcTest::isolate(), &g1, &g2,
435 5 : ResettingFinalizer);
436 : InvokeMarkSweep();
437 : // Finalizer (g1) dies but the phantom handle (g2) is kept alive for one
438 : // more round as the underlying object only dies on the next GC.
439 5 : CHECK(g1.IsEmpty());
440 5 : CHECK(!g2.IsEmpty());
441 : InvokeMarkSweep();
442 : // Phantom handle dies after one more round.
443 5 : CHECK(g1.IsEmpty());
444 5 : CHECK(g2.IsEmpty());
445 5 : }
446 :
447 : namespace {
448 :
449 10 : void ForceScavenge2(const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
450 10 : data.GetParameter()->flag = true;
451 : InvokeScavenge();
452 10 : }
453 :
454 20 : void ForceScavenge1(const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
455 : data.GetParameter()->handle.Reset();
456 : data.SetSecondPassCallback(ForceScavenge2);
457 10 : }
458 :
459 10 : void ForceMarkSweep2(const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
460 10 : data.GetParameter()->flag = true;
461 : InvokeMarkSweep();
462 10 : }
463 :
464 20 : void ForceMarkSweep1(const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
465 : data.GetParameter()->handle.Reset();
466 : data.SetSecondPassCallback(ForceMarkSweep2);
467 10 : }
468 :
469 : } // namespace
470 :
471 28342 : TEST(GCFromWeakCallbacks) {
472 5 : v8::Isolate* isolate = CcTest::isolate();
473 5 : v8::Locker locker(CcTest::isolate());
474 10 : v8::HandleScope scope(isolate);
475 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
476 : v8::Context::Scope context_scope(context);
477 :
478 : static const int kNumberOfGCTypes = 2;
479 : typedef v8::WeakCallbackInfo<FlagAndPersistent>::Callback Callback;
480 : Callback gc_forcing_callback[kNumberOfGCTypes] = {&ForceScavenge1,
481 5 : &ForceMarkSweep1};
482 :
483 : typedef void (*GCInvoker)();
484 5 : GCInvoker invoke_gc[kNumberOfGCTypes] = {&InvokeScavenge, &InvokeMarkSweep};
485 :
486 15 : for (int outer_gc = 0; outer_gc < kNumberOfGCTypes; outer_gc++) {
487 20 : for (int inner_gc = 0; inner_gc < kNumberOfGCTypes; inner_gc++) {
488 : FlagAndPersistent fp;
489 20 : ConstructJSApiObject(isolate, context, &fp);
490 : {
491 20 : v8::HandleScope scope(isolate);
492 : v8::Local<v8::Object> tmp =
493 : v8::Local<v8::Object>::New(isolate, fp.handle);
494 20 : CHECK(i::Heap::InNewSpace(*v8::Utils::OpenHandle(*tmp)));
495 : }
496 20 : fp.flag = false;
497 : fp.handle.SetWeak(&fp, gc_forcing_callback[inner_gc],
498 20 : v8::WeakCallbackType::kParameter);
499 20 : invoke_gc[outer_gc]();
500 20 : EmptyMessageQueues(isolate);
501 20 : CHECK(fp.flag);
502 : }
503 5 : }
504 5 : }
505 :
506 : namespace {
507 :
508 5 : void SecondPassCallback(const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
509 5 : data.GetParameter()->flag = true;
510 5 : }
511 :
512 10 : void FirstPassCallback(const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
513 : data.GetParameter()->handle.Reset();
514 : data.SetSecondPassCallback(SecondPassCallback);
515 5 : }
516 :
517 : } // namespace
518 :
519 28342 : TEST(SecondPassPhantomCallbacks) {
520 5 : v8::Isolate* isolate = CcTest::isolate();
521 5 : v8::Locker locker(CcTest::isolate());
522 10 : v8::HandleScope scope(isolate);
523 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
524 : v8::Context::Scope context_scope(context);
525 : FlagAndPersistent fp;
526 5 : ConstructJSApiObject(isolate, context, &fp);
527 5 : fp.flag = false;
528 : fp.handle.SetWeak(&fp, FirstPassCallback, v8::WeakCallbackType::kParameter);
529 5 : CHECK(!fp.flag);
530 : InvokeMarkSweep();
531 : InvokeMarkSweep();
532 10 : CHECK(fp.flag);
533 5 : }
534 :
535 : } // namespace internal
536 85011 : } // namespace v8
|