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