|  | // Copyright 2016 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 <stdint.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include "src/objects/managed.h" | 
|  |  | 
|  | #include "src/objects/objects-inl.h" | 
|  | #include "test/cctest/cctest.h" | 
|  |  | 
|  | namespace v8 { | 
|  | namespace internal { | 
|  |  | 
|  | class DeleteCounter { | 
|  | public: | 
|  | explicit DeleteCounter(int* deleted) : deleted_(deleted) { *deleted_ = 0; } | 
|  | ~DeleteCounter() { (*deleted_)++; } | 
|  | static void Deleter(void* arg) { | 
|  | delete reinterpret_cast<DeleteCounter*>(arg); | 
|  | } | 
|  |  | 
|  | private: | 
|  | int* deleted_; | 
|  | }; | 
|  |  | 
|  | TEST(GCCausesDestruction) { | 
|  | Isolate* isolate = CcTest::InitIsolateOnce(); | 
|  | int deleted1 = 0; | 
|  | int deleted2 = 0; | 
|  | DeleteCounter* d1 = new DeleteCounter(&deleted1); | 
|  | DeleteCounter* d2 = new DeleteCounter(&deleted2); | 
|  | { | 
|  | HandleScope scope(isolate); | 
|  | auto handle = Managed<DeleteCounter>::FromRawPtr(isolate, 0, d1); | 
|  | USE(handle); | 
|  | } | 
|  |  | 
|  | CcTest::CollectAllAvailableGarbage(); | 
|  |  | 
|  | CHECK_EQ(1, deleted1); | 
|  | CHECK_EQ(0, deleted2); | 
|  | delete d2; | 
|  | CHECK_EQ(1, deleted2); | 
|  | } | 
|  |  | 
|  | TEST(DisposeCausesDestruction1) { | 
|  | v8::Isolate::CreateParams create_params; | 
|  | create_params.array_buffer_allocator = | 
|  | CcTest::InitIsolateOnce()->array_buffer_allocator(); | 
|  |  | 
|  | v8::Isolate* isolate = v8::Isolate::New(create_params); | 
|  | Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); | 
|  | isolate->Enter(); | 
|  | int deleted1 = 0; | 
|  | DeleteCounter* d1 = new DeleteCounter(&deleted1); | 
|  | { | 
|  | HandleScope scope(i_isolate); | 
|  | auto handle = Managed<DeleteCounter>::FromRawPtr(i_isolate, 0, d1); | 
|  | USE(handle); | 
|  | } | 
|  | isolate->Exit(); | 
|  | isolate->Dispose(); | 
|  | CHECK_EQ(1, deleted1); | 
|  | } | 
|  |  | 
|  | TEST(DisposeCausesDestruction2) { | 
|  | v8::Isolate::CreateParams create_params; | 
|  | create_params.array_buffer_allocator = | 
|  | CcTest::InitIsolateOnce()->array_buffer_allocator(); | 
|  |  | 
|  | v8::Isolate* isolate = v8::Isolate::New(create_params); | 
|  | Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); | 
|  | isolate->Enter(); | 
|  | int deleted1 = 0; | 
|  | int deleted2 = 0; | 
|  | DeleteCounter* d1 = new DeleteCounter(&deleted1); | 
|  | DeleteCounter* d2 = new DeleteCounter(&deleted2); | 
|  | { | 
|  | HandleScope scope(i_isolate); | 
|  | auto handle = Managed<DeleteCounter>::FromRawPtr(i_isolate, 0, d1); | 
|  | USE(handle); | 
|  | } | 
|  | ManagedPtrDestructor* destructor = | 
|  | new ManagedPtrDestructor(0, d2, DeleteCounter::Deleter); | 
|  | i_isolate->RegisterManagedPtrDestructor(destructor); | 
|  |  | 
|  | isolate->Exit(); | 
|  | isolate->Dispose(); | 
|  | CHECK_EQ(1, deleted1); | 
|  | CHECK_EQ(1, deleted2); | 
|  | } | 
|  |  | 
|  | TEST(DisposeWithAnotherSharedPtr) { | 
|  | v8::Isolate::CreateParams create_params; | 
|  | create_params.array_buffer_allocator = | 
|  | CcTest::InitIsolateOnce()->array_buffer_allocator(); | 
|  |  | 
|  | v8::Isolate* isolate = v8::Isolate::New(create_params); | 
|  | Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); | 
|  | isolate->Enter(); | 
|  | int deleted1 = 0; | 
|  | DeleteCounter* d1 = new DeleteCounter(&deleted1); | 
|  | { | 
|  | std::shared_ptr<DeleteCounter> shared1(d1); | 
|  | { | 
|  | HandleScope scope(i_isolate); | 
|  | auto handle = | 
|  | Managed<DeleteCounter>::FromSharedPtr(i_isolate, 0, shared1); | 
|  | USE(handle); | 
|  | } | 
|  | isolate->Exit(); | 
|  | isolate->Dispose(); | 
|  | CHECK_EQ(0, deleted1); | 
|  | } | 
|  | // Should be deleted after the second shared pointer is destroyed. | 
|  | CHECK_EQ(1, deleted1); | 
|  | } | 
|  |  | 
|  | TEST(DisposeAcrossIsolates) { | 
|  | v8::Isolate::CreateParams create_params; | 
|  | create_params.array_buffer_allocator = | 
|  | CcTest::InitIsolateOnce()->array_buffer_allocator(); | 
|  |  | 
|  | int deleted = 0; | 
|  | DeleteCounter* delete_counter = new DeleteCounter(&deleted); | 
|  |  | 
|  | v8::Isolate* isolate1 = v8::Isolate::New(create_params); | 
|  | Isolate* i_isolate1 = reinterpret_cast<i::Isolate*>(isolate1); | 
|  | isolate1->Enter(); | 
|  | { | 
|  | HandleScope scope1(i_isolate1); | 
|  | auto handle1 = | 
|  | Managed<DeleteCounter>::FromRawPtr(i_isolate1, 0, delete_counter); | 
|  |  | 
|  | v8::Isolate* isolate2 = v8::Isolate::New(create_params); | 
|  | Isolate* i_isolate2 = reinterpret_cast<i::Isolate*>(isolate2); | 
|  | isolate2->Enter(); | 
|  | { | 
|  | HandleScope scope(i_isolate2); | 
|  | auto handle2 = | 
|  | Managed<DeleteCounter>::FromSharedPtr(i_isolate2, 0, handle1->get()); | 
|  | USE(handle2); | 
|  | } | 
|  | isolate2->Exit(); | 
|  | isolate2->Dispose(); | 
|  | CHECK_EQ(0, deleted); | 
|  | } | 
|  | // Should be deleted after the first isolate is destroyed. | 
|  | isolate1->Exit(); | 
|  | isolate1->Dispose(); | 
|  | CHECK_EQ(1, deleted); | 
|  | } | 
|  |  | 
|  | TEST(CollectAcrossIsolates) { | 
|  | v8::Isolate::CreateParams create_params; | 
|  | create_params.array_buffer_allocator = | 
|  | CcTest::InitIsolateOnce()->array_buffer_allocator(); | 
|  |  | 
|  | int deleted = 0; | 
|  | DeleteCounter* delete_counter = new DeleteCounter(&deleted); | 
|  |  | 
|  | v8::Isolate* isolate1 = v8::Isolate::New(create_params); | 
|  | Isolate* i_isolate1 = reinterpret_cast<i::Isolate*>(isolate1); | 
|  | isolate1->Enter(); | 
|  | { | 
|  | HandleScope scope1(i_isolate1); | 
|  | auto handle1 = | 
|  | Managed<DeleteCounter>::FromRawPtr(i_isolate1, 0, delete_counter); | 
|  |  | 
|  | v8::Isolate* isolate2 = v8::Isolate::New(create_params); | 
|  | Isolate* i_isolate2 = reinterpret_cast<i::Isolate*>(isolate2); | 
|  | isolate2->Enter(); | 
|  | { | 
|  | HandleScope scope(i_isolate2); | 
|  | auto handle2 = | 
|  | Managed<DeleteCounter>::FromSharedPtr(i_isolate2, 0, handle1->get()); | 
|  | USE(handle2); | 
|  | } | 
|  | i_isolate2->heap()->CollectAllAvailableGarbage( | 
|  | i::GarbageCollectionReason::kTesting); | 
|  | CHECK_EQ(0, deleted); | 
|  | isolate2->Exit(); | 
|  | isolate2->Dispose(); | 
|  | CHECK_EQ(0, deleted); | 
|  | } | 
|  | // Should be deleted after the first isolate is destroyed. | 
|  | i_isolate1->heap()->CollectAllAvailableGarbage( | 
|  | i::GarbageCollectionReason::kTesting); | 
|  | CHECK_EQ(1, deleted); | 
|  | isolate1->Exit(); | 
|  | isolate1->Dispose(); | 
|  | CHECK_EQ(1, deleted); | 
|  | } | 
|  |  | 
|  | }  // namespace internal | 
|  | }  // namespace v8 |