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