1#include "base_object-inl.h"
2#include "gtest/gtest.h"
3#include "node.h"
4#include "node_realm-inl.h"
5#include "node_test_fixture.h"
6
7using node::BaseObject;
8using node::BaseObjectPtr;
9using node::BaseObjectWeakPtr;
10using node::Environment;
11using node::MakeBaseObject;
12using node::MakeDetachedBaseObject;
13using node::Realm;
14using v8::HandleScope;
15using v8::Isolate;
16using v8::Local;
17using v8::Object;
18
19class BaseObjectPtrTest : public EnvironmentTestFixture {};
20
21class DummyBaseObject : public BaseObject {
22 public:
23  DummyBaseObject(Environment* env, Local<Object> obj) : BaseObject(env, obj) {}
24
25  static Local<Object> MakeJSObject(Environment* env) {
26    return BaseObject::MakeLazilyInitializedJSTemplate(env)
27        ->GetFunction(env->context()).ToLocalChecked()
28        ->NewInstance(env->context()).ToLocalChecked();
29  }
30
31  static BaseObjectPtr<DummyBaseObject> NewDetached(Environment* env) {
32    Local<Object> obj = MakeJSObject(env);
33    return MakeDetachedBaseObject<DummyBaseObject>(env, obj);
34  }
35
36  static BaseObjectPtr<DummyBaseObject> New(Environment* env) {
37    Local<Object> obj = MakeJSObject(env);
38    return MakeBaseObject<DummyBaseObject>(env, obj);
39  }
40
41  SET_NO_MEMORY_INFO()
42  SET_MEMORY_INFO_NAME(DummyBaseObject)
43  SET_SELF_SIZE(DummyBaseObject)
44};
45
46TEST_F(BaseObjectPtrTest, ScopedDetached) {
47  const HandleScope handle_scope(isolate_);
48  const Argv argv;
49  Env env_{handle_scope, argv};
50  Environment* env = *env_;
51  Realm* realm = env->principal_realm();
52
53  EXPECT_EQ(realm->base_object_created_after_bootstrap(), 0);
54  {
55    BaseObjectPtr<DummyBaseObject> ptr = DummyBaseObject::NewDetached(env);
56    EXPECT_EQ(realm->base_object_created_after_bootstrap(), 1);
57  }
58  EXPECT_EQ(realm->base_object_created_after_bootstrap(), 0);
59}
60
61TEST_F(BaseObjectPtrTest, ScopedDetachedWithWeak) {
62  const HandleScope handle_scope(isolate_);
63  const Argv argv;
64  Env env_{handle_scope, argv};
65  Environment* env = *env_;
66  Realm* realm = env->principal_realm();
67
68  BaseObjectWeakPtr<DummyBaseObject> weak_ptr;
69
70  EXPECT_EQ(realm->base_object_created_after_bootstrap(), 0);
71  {
72    BaseObjectPtr<DummyBaseObject> ptr = DummyBaseObject::NewDetached(env);
73    weak_ptr = ptr;
74    EXPECT_EQ(realm->base_object_created_after_bootstrap(), 1);
75  }
76  EXPECT_EQ(weak_ptr.get(), nullptr);
77  EXPECT_EQ(realm->base_object_created_after_bootstrap(), 0);
78}
79
80TEST_F(BaseObjectPtrTest, Undetached) {
81  const HandleScope handle_scope(isolate_);
82  const Argv argv;
83  Env env_{handle_scope, argv};
84  Environment* env = *env_;
85  Realm* realm = env->principal_realm();
86
87  node::AddEnvironmentCleanupHook(
88      isolate_,
89      [](void* arg) {
90        EXPECT_EQ(static_cast<Realm*>(arg)->base_object_count(), 0);
91      },
92      realm);
93
94  BaseObjectPtr<DummyBaseObject> ptr = DummyBaseObject::New(env);
95  EXPECT_EQ(realm->base_object_created_after_bootstrap(), 1);
96}
97
98TEST_F(BaseObjectPtrTest, GCWeak) {
99  const HandleScope handle_scope(isolate_);
100  const Argv argv;
101  Env env_{handle_scope, argv};
102  Environment* env = *env_;
103  Realm* realm = env->principal_realm();
104
105  BaseObjectWeakPtr<DummyBaseObject> weak_ptr;
106
107  {
108    const HandleScope handle_scope(isolate_);
109    BaseObjectPtr<DummyBaseObject> ptr = DummyBaseObject::New(env);
110    weak_ptr = ptr;
111    ptr->MakeWeak();
112
113    EXPECT_EQ(realm->base_object_created_after_bootstrap(), 1);
114    EXPECT_EQ(weak_ptr.get(), ptr.get());
115    EXPECT_EQ(weak_ptr->persistent().IsWeak(), false);
116
117    ptr.reset();
118  }
119
120  EXPECT_EQ(realm->base_object_created_after_bootstrap(), 1);
121  EXPECT_NE(weak_ptr.get(), nullptr);
122  EXPECT_EQ(weak_ptr->persistent().IsWeak(), true);
123
124  v8::V8::SetFlagsFromString("--expose-gc");
125  isolate_->RequestGarbageCollectionForTesting(Isolate::kFullGarbageCollection);
126
127  EXPECT_EQ(realm->base_object_created_after_bootstrap(), 0);
128  EXPECT_EQ(weak_ptr.get(), nullptr);
129}
130
131TEST_F(BaseObjectPtrTest, Moveable) {
132  const HandleScope handle_scope(isolate_);
133  const Argv argv;
134  Env env_{handle_scope, argv};
135  Environment* env = *env_;
136  Realm* realm = env->principal_realm();
137
138  BaseObjectPtr<DummyBaseObject> ptr = DummyBaseObject::NewDetached(env);
139  EXPECT_EQ(realm->base_object_created_after_bootstrap(), 1);
140  BaseObjectWeakPtr<DummyBaseObject> weak_ptr { ptr };
141  EXPECT_EQ(weak_ptr.get(), ptr.get());
142
143  BaseObjectPtr<DummyBaseObject> ptr2 = std::move(ptr);
144  EXPECT_EQ(weak_ptr.get(), ptr2.get());
145  EXPECT_EQ(ptr.get(), nullptr);
146
147  BaseObjectWeakPtr<DummyBaseObject> weak_ptr2 = std::move(weak_ptr);
148  EXPECT_EQ(weak_ptr2.get(), ptr2.get());
149  EXPECT_EQ(weak_ptr.get(), nullptr);
150  EXPECT_EQ(realm->base_object_created_after_bootstrap(), 1);
151
152  ptr2.reset();
153
154  EXPECT_EQ(weak_ptr2.get(), nullptr);
155  EXPECT_EQ(realm->base_object_created_after_bootstrap(), 0);
156}
157
158TEST_F(BaseObjectPtrTest, NestedClasses) {
159  class ObjectWithPtr : public BaseObject {
160   public:
161    ObjectWithPtr(Environment* env, Local<Object> obj) : BaseObject(env, obj) {}
162
163    BaseObjectPtr<BaseObject> ptr1;
164    BaseObjectPtr<BaseObject> ptr2;
165
166    SET_NO_MEMORY_INFO()
167    SET_MEMORY_INFO_NAME(ObjectWithPtr)
168    SET_SELF_SIZE(ObjectWithPtr)
169  };
170
171  const HandleScope handle_scope(isolate_);
172  const Argv argv;
173  Env env_{handle_scope, argv};
174  Environment* env = *env_;
175  Realm* realm = env->principal_realm();
176
177  node::AddEnvironmentCleanupHook(
178      isolate_,
179      [](void* arg) {
180        EXPECT_EQ(static_cast<Realm*>(arg)->base_object_count(), 0);
181      },
182      realm);
183
184  ObjectWithPtr* obj =
185      new ObjectWithPtr(env, DummyBaseObject::MakeJSObject(env));
186  obj->ptr1 = DummyBaseObject::NewDetached(env);
187  obj->ptr2 = DummyBaseObject::New(env);
188
189  EXPECT_EQ(realm->base_object_created_after_bootstrap(), 3);
190}
191