• 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::Value> value)103 BaseObject* BaseObject::FromJSObject(v8::Local<v8::Value> value) {
104   v8::Local<v8::Object> obj = value.As<v8::Object>();
105   DCHECK_GE(obj->InternalFieldCount(), BaseObject::kSlot);
106   return static_cast<BaseObject*>(
107       obj->GetAlignedPointerFromInternalField(BaseObject::kSlot));
108 }
109 
110 
111 template <typename T>
FromJSObject(v8::Local<v8::Value> object)112 T* BaseObject::FromJSObject(v8::Local<v8::Value> object) {
113   return static_cast<T*>(FromJSObject(object));
114 }
115 
116 
MakeWeak()117 void BaseObject::MakeWeak() {
118   if (has_pointer_data()) {
119     pointer_data()->wants_weak_jsobj = true;
120     if (pointer_data()->strong_ptr_count > 0) return;
121   }
122 
123   persistent_handle_.SetWeak(
124       this,
125       [](const v8::WeakCallbackInfo<BaseObject>& data) {
126         BaseObject* obj = data.GetParameter();
127         // Clear the persistent handle so that ~BaseObject() doesn't attempt
128         // to mess with internal fields, since the JS object may have
129         // transitioned into an invalid state.
130         // Refs: https://github.com/nodejs/node/issues/18897
131         obj->persistent_handle_.Reset();
132         CHECK_IMPLIES(obj->has_pointer_data(),
133                       obj->pointer_data()->strong_ptr_count == 0);
134         obj->OnGCCollect();
135       }, v8::WeakCallbackType::kParameter);
136 }
137 
OnGCCollect()138 void BaseObject::OnGCCollect() {
139   delete this;
140 }
141 
ClearWeak()142 void BaseObject::ClearWeak() {
143   if (has_pointer_data())
144     pointer_data()->wants_weak_jsobj = false;
145 
146   persistent_handle_.ClearWeak();
147 }
148 
IsWeakOrDetached()149 bool BaseObject::IsWeakOrDetached() const {
150   if (persistent_handle_.IsWeak()) return true;
151 
152   if (!has_pointer_data()) return false;
153   const PointerData* pd = const_cast<BaseObject*>(this)->pointer_data();
154   return pd->wants_weak_jsobj || pd->is_detached;
155 }
156 
157 v8::Local<v8::FunctionTemplate>
MakeLazilyInitializedJSTemplate(Environment * env)158 BaseObject::MakeLazilyInitializedJSTemplate(Environment* env) {
159   auto constructor = [](const v8::FunctionCallbackInfo<v8::Value>& args) {
160     DCHECK(args.IsConstructCall());
161     DCHECK_GT(args.This()->InternalFieldCount(), 0);
162     args.This()->SetAlignedPointerInInternalField(BaseObject::kSlot, nullptr);
163   };
164 
165   v8::Local<v8::FunctionTemplate> t = env->NewFunctionTemplate(constructor);
166   t->Inherit(BaseObject::GetConstructorTemplate(env));
167   t->InstanceTemplate()->SetInternalFieldCount(
168       BaseObject::kInternalFieldCount);
169   return t;
170 }
171 
172 template <int Field>
InternalFieldGet(v8::Local<v8::String> property,const v8::PropertyCallbackInfo<v8::Value> & info)173 void BaseObject::InternalFieldGet(
174     v8::Local<v8::String> property,
175     const v8::PropertyCallbackInfo<v8::Value>& info) {
176   info.GetReturnValue().Set(info.This()->GetInternalField(Field));
177 }
178 
179 template <int Field, bool (v8::Value::* typecheck)() const>
InternalFieldSet(v8::Local<v8::String> property,v8::Local<v8::Value> value,const v8::PropertyCallbackInfo<void> & info)180 void BaseObject::InternalFieldSet(v8::Local<v8::String> property,
181                                   v8::Local<v8::Value> value,
182                                   const v8::PropertyCallbackInfo<void>& info) {
183   // This could be e.g. value->IsFunction().
184   CHECK(((*value)->*typecheck)());
185   info.This()->SetInternalField(Field, value);
186 }
187 
has_pointer_data()188 bool BaseObject::has_pointer_data() const {
189   return pointer_data_ != nullptr;
190 }
191 
pointer_data()192 BaseObject::PointerData* BaseObject::pointer_data() {
193   if (!has_pointer_data()) {
194     PointerData* metadata = new PointerData();
195     metadata->wants_weak_jsobj = persistent_handle_.IsWeak();
196     metadata->self = this;
197     pointer_data_ = metadata;
198   }
199   CHECK(has_pointer_data());
200   return pointer_data_;
201 }
202 
decrease_refcount()203 void BaseObject::decrease_refcount() {
204   CHECK(has_pointer_data());
205   PointerData* metadata = pointer_data();
206   CHECK_GT(metadata->strong_ptr_count, 0);
207   unsigned int new_refcount = --metadata->strong_ptr_count;
208   if (new_refcount == 0) {
209     if (metadata->is_detached) {
210       OnGCCollect();
211     } else if (metadata->wants_weak_jsobj && !persistent_handle_.IsEmpty()) {
212       MakeWeak();
213     }
214   }
215 }
216 
increase_refcount()217 void BaseObject::increase_refcount() {
218   unsigned int prev_refcount = pointer_data()->strong_ptr_count++;
219   if (prev_refcount == 0 && !persistent_handle_.IsEmpty())
220     persistent_handle_.ClearWeak();
221 }
222 
223 template <typename T, bool kIsWeak>
224 BaseObject::PointerData*
pointer_data()225 BaseObjectPtrImpl<T, kIsWeak>::pointer_data() const {
226   if (kIsWeak) {
227     return data_.pointer_data;
228   }
229   if (get_base_object() == nullptr) {
230     return nullptr;
231   }
232   return get_base_object()->pointer_data();
233 }
234 
235 template <typename T, bool kIsWeak>
get_base_object()236 BaseObject* BaseObjectPtrImpl<T, kIsWeak>::get_base_object() const {
237   if (kIsWeak) {
238     if (pointer_data() == nullptr) {
239       return nullptr;
240     }
241     return pointer_data()->self;
242   }
243   return data_.target;
244 }
245 
246 template <typename T, bool kIsWeak>
~BaseObjectPtrImpl()247 BaseObjectPtrImpl<T, kIsWeak>::~BaseObjectPtrImpl() {
248   if (kIsWeak) {
249     if (pointer_data() != nullptr &&
250         --pointer_data()->weak_ptr_count == 0 &&
251         pointer_data()->self == nullptr) {
252       delete pointer_data();
253     }
254   } else if (get() != nullptr) {
255     get()->decrease_refcount();
256   }
257 }
258 
259 template <typename T, bool kIsWeak>
BaseObjectPtrImpl()260 BaseObjectPtrImpl<T, kIsWeak>::BaseObjectPtrImpl() {
261   data_.target = nullptr;
262 }
263 
264 template <typename T, bool kIsWeak>
BaseObjectPtrImpl(T * target)265 BaseObjectPtrImpl<T, kIsWeak>::BaseObjectPtrImpl(T* target)
266   : BaseObjectPtrImpl() {
267   if (target == nullptr) return;
268   if (kIsWeak) {
269     data_.pointer_data = target->pointer_data();
270     CHECK_NOT_NULL(pointer_data());
271     pointer_data()->weak_ptr_count++;
272   } else {
273     data_.target = target;
274     CHECK_NOT_NULL(pointer_data());
275     get()->increase_refcount();
276   }
277 }
278 
279 template <typename T, bool kIsWeak>
280 template <typename U, bool kW>
BaseObjectPtrImpl(const BaseObjectPtrImpl<U,kW> & other)281 BaseObjectPtrImpl<T, kIsWeak>::BaseObjectPtrImpl(
282     const BaseObjectPtrImpl<U, kW>& other)
283   : BaseObjectPtrImpl(other.get()) {}
284 
285 template <typename T, bool kIsWeak>
BaseObjectPtrImpl(const BaseObjectPtrImpl & other)286 BaseObjectPtrImpl<T, kIsWeak>::BaseObjectPtrImpl(const BaseObjectPtrImpl& other)
287   : BaseObjectPtrImpl(other.get()) {}
288 
289 template <typename T, bool kIsWeak>
290 template <typename U, bool kW>
291 BaseObjectPtrImpl<T, kIsWeak>& BaseObjectPtrImpl<T, kIsWeak>::operator=(
292     const BaseObjectPtrImpl<U, kW>& 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>
299 BaseObjectPtrImpl<T, kIsWeak>& BaseObjectPtrImpl<T, kIsWeak>::operator=(
300     const BaseObjectPtrImpl& other) {
301   if (other.get() == get()) return *this;
302   this->~BaseObjectPtrImpl();
303   return *new (this) BaseObjectPtrImpl(other);
304 }
305 
306 template <typename T, bool kIsWeak>
BaseObjectPtrImpl(BaseObjectPtrImpl && other)307 BaseObjectPtrImpl<T, kIsWeak>::BaseObjectPtrImpl(BaseObjectPtrImpl&& other)
308   : data_(other.data_) {
309   if (kIsWeak)
310     other.data_.target = nullptr;
311   else
312     other.data_.pointer_data = nullptr;
313 }
314 
315 template <typename T, bool kIsWeak>
316 BaseObjectPtrImpl<T, kIsWeak>& BaseObjectPtrImpl<T, kIsWeak>::operator=(
317     BaseObjectPtrImpl&& other) {
318   if (&other == this) return *this;
319   this->~BaseObjectPtrImpl();
320   return *new (this) BaseObjectPtrImpl(std::move(other));
321 }
322 
323 template <typename T, bool kIsWeak>
reset(T * ptr)324 void BaseObjectPtrImpl<T, kIsWeak>::reset(T* ptr) {
325   *this = BaseObjectPtrImpl(ptr);
326 }
327 
328 template <typename T, bool kIsWeak>
get()329 T* BaseObjectPtrImpl<T, kIsWeak>::get() const {
330   return static_cast<T*>(get_base_object());
331 }
332 
333 template <typename T, bool kIsWeak>
334 T& BaseObjectPtrImpl<T, kIsWeak>::operator*() const {
335   return *get();
336 }
337 
338 template <typename T, bool kIsWeak>
339 T* BaseObjectPtrImpl<T, kIsWeak>::operator->() const {
340   return get();
341 }
342 
343 template <typename T, bool kIsWeak>
344 BaseObjectPtrImpl<T, kIsWeak>::operator bool() const {
345   return get() != nullptr;
346 }
347 
348 template <typename T, bool kIsWeak>
349 template <typename U, bool kW>
350 bool BaseObjectPtrImpl<T, kIsWeak>::operator ==(
351     const BaseObjectPtrImpl<U, kW>& other) const {
352   return get() == other.get();
353 }
354 
355 template <typename T, bool kIsWeak>
356 template <typename U, bool kW>
357 bool BaseObjectPtrImpl<T, kIsWeak>::operator !=(
358     const BaseObjectPtrImpl<U, kW>& other) const {
359   return get() != other.get();
360 }
361 
362 template <typename T, typename... Args>
MakeBaseObject(Args &&...args)363 BaseObjectPtr<T> MakeBaseObject(Args&&... args) {
364   return BaseObjectPtr<T>(new T(std::forward<Args>(args)...));
365 }
366 
367 template <typename T, typename... Args>
MakeDetachedBaseObject(Args &&...args)368 BaseObjectPtr<T> MakeDetachedBaseObject(Args&&... args) {
369   BaseObjectPtr<T> target = MakeBaseObject<T>(std::forward<Args>(args)...);
370   target->Detach();
371   return target;
372 }
373 
374 }  // namespace node
375 
376 #endif  // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
377 
378 #endif  // SRC_BASE_OBJECT_INL_H_
379