• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright Joyent, Inc. and other Node contributors.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21 
22 #ifndef SRC_BASE_OBJECT_INL_H_
23 #define SRC_BASE_OBJECT_INL_H_
24 
25 #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
26 
27 #include "base_object.h"
28 #include "env-inl.h"
29 #include "util.h"
30 
31 #if (__GNUC__ >= 8) && !defined(__clang__)
32 #pragma GCC diagnostic push
33 #pragma GCC diagnostic ignored "-Wcast-function-type"
34 #endif
35 #include "v8.h"
36 #if (__GNUC__ >= 8) && !defined(__clang__)
37 #pragma GCC diagnostic pop
38 #endif
39 
40 namespace node {
41 
BaseObject(Environment * env,v8::Local<v8::Object> object)42 BaseObject::BaseObject(Environment* env, v8::Local<v8::Object> object)
43     : persistent_handle_(env->isolate(), object), env_(env) {
44   CHECK_EQ(false, object.IsEmpty());
45   CHECK_GT(object->InternalFieldCount(), 0);
46   object->SetAlignedPointerInInternalField(
47       BaseObject::kSlot,
48       static_cast<void*>(this));
49   env->AddCleanupHook(DeleteMe, static_cast<void*>(this));
50   env->modify_base_object_count(1);
51 }
52 
~BaseObject()53 BaseObject::~BaseObject() {
54   env()->modify_base_object_count(-1);
55   env()->RemoveCleanupHook(DeleteMe, static_cast<void*>(this));
56 
57   if (UNLIKELY(has_pointer_data())) {
58     PointerData* metadata = pointer_data();
59     CHECK_EQ(metadata->strong_ptr_count, 0);
60     metadata->self = nullptr;
61     if (metadata->weak_ptr_count == 0)
62       delete metadata;
63   }
64 
65   if (persistent_handle_.IsEmpty()) {
66     // This most likely happened because the weak callback below cleared it.
67     return;
68   }
69 
70   {
71     v8::HandleScope handle_scope(env()->isolate());
72     object()->SetAlignedPointerInInternalField(BaseObject::kSlot, nullptr);
73   }
74 }
75 
Detach()76 void BaseObject::Detach() {
77   CHECK_GT(pointer_data()->strong_ptr_count, 0);
78   pointer_data()->is_detached = true;
79 }
80 
persistent()81 v8::Global<v8::Object>& BaseObject::persistent() {
82   return persistent_handle_;
83 }
84 
85 
object()86 v8::Local<v8::Object> BaseObject::object() const {
87   return PersistentToLocal::Default(env()->isolate(), persistent_handle_);
88 }
89 
object(v8::Isolate * isolate)90 v8::Local<v8::Object> BaseObject::object(v8::Isolate* isolate) const {
91   v8::Local<v8::Object> handle = object();
92 
93   DCHECK_EQ(handle->CreationContext()->GetIsolate(), isolate);
94   DCHECK_EQ(env()->isolate(), isolate);
95 
96   return handle;
97 }
98 
env()99 Environment* BaseObject::env() const {
100   return env_;
101 }
102 
FromJSObject(v8::Local<v8::Object> obj)103 BaseObject* BaseObject::FromJSObject(v8::Local<v8::Object> obj) {
104   CHECK_GT(obj->InternalFieldCount(), 0);
105   return static_cast<BaseObject*>(
106       obj->GetAlignedPointerFromInternalField(BaseObject::kSlot));
107 }
108 
109 
110 template <typename T>
FromJSObject(v8::Local<v8::Object> object)111 T* BaseObject::FromJSObject(v8::Local<v8::Object> object) {
112   return static_cast<T*>(FromJSObject(object));
113 }
114 
115 
MakeWeak()116 void BaseObject::MakeWeak() {
117   if (has_pointer_data()) {
118     pointer_data()->wants_weak_jsobj = true;
119     if (pointer_data()->strong_ptr_count > 0) return;
120   }
121 
122   persistent_handle_.SetWeak(
123       this,
124       [](const v8::WeakCallbackInfo<BaseObject>& data) {
125         BaseObject* obj = data.GetParameter();
126         // Clear the persistent handle so that ~BaseObject() doesn't attempt
127         // to mess with internal fields, since the JS object may have
128         // transitioned into an invalid state.
129         // Refs: https://github.com/nodejs/node/issues/18897
130         obj->persistent_handle_.Reset();
131         CHECK_IMPLIES(obj->has_pointer_data(),
132                       obj->pointer_data()->strong_ptr_count == 0);
133         obj->OnGCCollect();
134       }, v8::WeakCallbackType::kParameter);
135 }
136 
OnGCCollect()137 void BaseObject::OnGCCollect() {
138   delete this;
139 }
140 
ClearWeak()141 void BaseObject::ClearWeak() {
142   if (has_pointer_data())
143     pointer_data()->wants_weak_jsobj = false;
144 
145   persistent_handle_.ClearWeak();
146 }
147 
148 
149 v8::Local<v8::FunctionTemplate>
MakeLazilyInitializedJSTemplate(Environment * env)150 BaseObject::MakeLazilyInitializedJSTemplate(Environment* env) {
151   auto constructor = [](const v8::FunctionCallbackInfo<v8::Value>& args) {
152     DCHECK(args.IsConstructCall());
153     DCHECK_GT(args.This()->InternalFieldCount(), 0);
154     args.This()->SetAlignedPointerInInternalField(BaseObject::kSlot, nullptr);
155   };
156 
157   v8::Local<v8::FunctionTemplate> t = env->NewFunctionTemplate(constructor);
158   t->Inherit(BaseObject::GetConstructorTemplate(env));
159   t->InstanceTemplate()->SetInternalFieldCount(
160       BaseObject::kInternalFieldCount);
161   return t;
162 }
163 
164 template <int Field>
InternalFieldGet(v8::Local<v8::String> property,const v8::PropertyCallbackInfo<v8::Value> & info)165 void BaseObject::InternalFieldGet(
166     v8::Local<v8::String> property,
167     const v8::PropertyCallbackInfo<v8::Value>& info) {
168   info.GetReturnValue().Set(info.This()->GetInternalField(Field));
169 }
170 
171 template <int Field, bool (v8::Value::* typecheck)() const>
InternalFieldSet(v8::Local<v8::String> property,v8::Local<v8::Value> value,const v8::PropertyCallbackInfo<void> & info)172 void BaseObject::InternalFieldSet(v8::Local<v8::String> property,
173                                   v8::Local<v8::Value> value,
174                                   const v8::PropertyCallbackInfo<void>& info) {
175   // This could be e.g. value->IsFunction().
176   CHECK(((*value)->*typecheck)());
177   info.This()->SetInternalField(Field, value);
178 }
179 
has_pointer_data()180 bool BaseObject::has_pointer_data() const {
181   return pointer_data_ != nullptr;
182 }
183 
pointer_data()184 BaseObject::PointerData* BaseObject::pointer_data() {
185   if (!has_pointer_data()) {
186     PointerData* metadata = new PointerData();
187     metadata->wants_weak_jsobj = persistent_handle_.IsWeak();
188     metadata->self = this;
189     pointer_data_ = metadata;
190   }
191   CHECK(has_pointer_data());
192   return pointer_data_;
193 }
194 
decrease_refcount()195 void BaseObject::decrease_refcount() {
196   CHECK(has_pointer_data());
197   PointerData* metadata = pointer_data();
198   CHECK_GT(metadata->strong_ptr_count, 0);
199   unsigned int new_refcount = --metadata->strong_ptr_count;
200   if (new_refcount == 0) {
201     if (metadata->is_detached) {
202       delete this;
203     } else if (metadata->wants_weak_jsobj && !persistent_handle_.IsEmpty()) {
204       MakeWeak();
205     }
206   }
207 }
208 
increase_refcount()209 void BaseObject::increase_refcount() {
210   unsigned int prev_refcount = pointer_data()->strong_ptr_count++;
211   if (prev_refcount == 0 && !persistent_handle_.IsEmpty())
212     persistent_handle_.ClearWeak();
213 }
214 
215 template <typename T, bool kIsWeak>
216 BaseObject::PointerData*
pointer_data()217 BaseObjectPtrImpl<T, kIsWeak>::pointer_data() const {
218   if (kIsWeak) {
219     return data_.pointer_data;
220   }
221   if (get_base_object() == nullptr) {
222     return nullptr;
223   }
224   return get_base_object()->pointer_data();
225 }
226 
227 template <typename T, bool kIsWeak>
get_base_object()228 BaseObject* BaseObjectPtrImpl<T, kIsWeak>::get_base_object() const {
229   if (kIsWeak) {
230     if (pointer_data() == nullptr) {
231       return nullptr;
232     }
233     return pointer_data()->self;
234   }
235   return data_.target;
236 }
237 
238 template <typename T, bool kIsWeak>
~BaseObjectPtrImpl()239 BaseObjectPtrImpl<T, kIsWeak>::~BaseObjectPtrImpl() {
240   if (kIsWeak) {
241     if (pointer_data() != nullptr &&
242         --pointer_data()->weak_ptr_count == 0 &&
243         pointer_data()->self == nullptr) {
244       delete pointer_data();
245     }
246   } else if (get() != nullptr) {
247     get()->decrease_refcount();
248   }
249 }
250 
251 template <typename T, bool kIsWeak>
BaseObjectPtrImpl()252 BaseObjectPtrImpl<T, kIsWeak>::BaseObjectPtrImpl() {
253   data_.target = nullptr;
254 }
255 
256 template <typename T, bool kIsWeak>
BaseObjectPtrImpl(T * target)257 BaseObjectPtrImpl<T, kIsWeak>::BaseObjectPtrImpl(T* target)
258   : BaseObjectPtrImpl() {
259   if (target == nullptr) return;
260   if (kIsWeak) {
261     data_.pointer_data = target->pointer_data();
262     CHECK_NOT_NULL(pointer_data());
263     pointer_data()->weak_ptr_count++;
264   } else {
265     data_.target = target;
266     CHECK_NOT_NULL(pointer_data());
267     get()->increase_refcount();
268   }
269 }
270 
271 template <typename T, bool kIsWeak>
272 template <typename U, bool kW>
BaseObjectPtrImpl(const BaseObjectPtrImpl<U,kW> & other)273 BaseObjectPtrImpl<T, kIsWeak>::BaseObjectPtrImpl(
274     const BaseObjectPtrImpl<U, kW>& other)
275   : BaseObjectPtrImpl(other.get()) {}
276 
277 template <typename T, bool kIsWeak>
BaseObjectPtrImpl(const BaseObjectPtrImpl & other)278 BaseObjectPtrImpl<T, kIsWeak>::BaseObjectPtrImpl(const BaseObjectPtrImpl& other)
279   : BaseObjectPtrImpl(other.get()) {}
280 
281 template <typename T, bool kIsWeak>
282 template <typename U, bool kW>
283 BaseObjectPtrImpl<T, kIsWeak>& BaseObjectPtrImpl<T, kIsWeak>::operator=(
284     const BaseObjectPtrImpl<U, kW>& other) {
285   if (other.get() == get()) return *this;
286   this->~BaseObjectPtrImpl();
287   return *new (this) BaseObjectPtrImpl(other);
288 }
289 
290 template <typename T, bool kIsWeak>
291 BaseObjectPtrImpl<T, kIsWeak>& BaseObjectPtrImpl<T, kIsWeak>::operator=(
292     const BaseObjectPtrImpl& other) {
293   if (other.get() == get()) return *this;
294   this->~BaseObjectPtrImpl();
295   return *new (this) BaseObjectPtrImpl(other);
296 }
297 
298 template <typename T, bool kIsWeak>
BaseObjectPtrImpl(BaseObjectPtrImpl && other)299 BaseObjectPtrImpl<T, kIsWeak>::BaseObjectPtrImpl(BaseObjectPtrImpl&& other)
300   : data_(other.data_) {
301   if (kIsWeak)
302     other.data_.target = nullptr;
303   else
304     other.data_.pointer_data = nullptr;
305 }
306 
307 template <typename T, bool kIsWeak>
308 BaseObjectPtrImpl<T, kIsWeak>& BaseObjectPtrImpl<T, kIsWeak>::operator=(
309     BaseObjectPtrImpl&& other) {
310   if (&other == this) return *this;
311   this->~BaseObjectPtrImpl();
312   return *new (this) BaseObjectPtrImpl(std::move(other));
313 }
314 
315 template <typename T, bool kIsWeak>
reset(T * ptr)316 void BaseObjectPtrImpl<T, kIsWeak>::reset(T* ptr) {
317   *this = BaseObjectPtrImpl(ptr);
318 }
319 
320 template <typename T, bool kIsWeak>
get()321 T* BaseObjectPtrImpl<T, kIsWeak>::get() const {
322   return static_cast<T*>(get_base_object());
323 }
324 
325 template <typename T, bool kIsWeak>
326 T& BaseObjectPtrImpl<T, kIsWeak>::operator*() const {
327   return *get();
328 }
329 
330 template <typename T, bool kIsWeak>
331 T* BaseObjectPtrImpl<T, kIsWeak>::operator->() const {
332   return get();
333 }
334 
335 template <typename T, bool kIsWeak>
336 BaseObjectPtrImpl<T, kIsWeak>::operator bool() const {
337   return get() != nullptr;
338 }
339 
340 template <typename T, bool kIsWeak>
341 template <typename U, bool kW>
342 bool BaseObjectPtrImpl<T, kIsWeak>::operator ==(
343     const BaseObjectPtrImpl<U, kW>& other) const {
344   return get() == other.get();
345 }
346 
347 template <typename T, bool kIsWeak>
348 template <typename U, bool kW>
349 bool BaseObjectPtrImpl<T, kIsWeak>::operator !=(
350     const BaseObjectPtrImpl<U, kW>& other) const {
351   return get() != other.get();
352 }
353 
354 template <typename T, typename... Args>
MakeBaseObject(Args &&...args)355 BaseObjectPtr<T> MakeBaseObject(Args&&... args) {
356   return BaseObjectPtr<T>(new T(std::forward<Args>(args)...));
357 }
358 
359 template <typename T, typename... Args>
MakeDetachedBaseObject(Args &&...args)360 BaseObjectPtr<T> MakeDetachedBaseObject(Args&&... args) {
361   BaseObjectPtr<T> target = MakeBaseObject<T>(std::forward<Args>(args)...);
362   target->Detach();
363   return target;
364 }
365 
366 }  // namespace node
367 
368 #endif  // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
369 
370 #endif  // SRC_BASE_OBJECT_INL_H_
371