/src/node/src/base_object.cc
Line | Count | Source |
1 | | #include "base_object.h" |
2 | | #include "env-inl.h" |
3 | | #include "memory_tracker-inl.h" |
4 | | #include "node_messaging.h" |
5 | | #include "node_realm-inl.h" |
6 | | |
7 | | namespace node { |
8 | | |
9 | | using v8::Context; |
10 | | using v8::FunctionCallbackInfo; |
11 | | using v8::FunctionTemplate; |
12 | | using v8::HandleScope; |
13 | | using v8::Just; |
14 | | using v8::JustVoid; |
15 | | using v8::Local; |
16 | | using v8::Maybe; |
17 | | using v8::Object; |
18 | | using v8::Value; |
19 | | using v8::ValueDeserializer; |
20 | | using v8::WeakCallbackInfo; |
21 | | using v8::WeakCallbackType; |
22 | | |
23 | | BaseObject::BaseObject(Realm* realm, Local<Object> object) |
24 | 280 | : persistent_handle_(realm->isolate(), object), realm_(realm) { |
25 | 280 | CHECK_EQ(false, object.IsEmpty()); |
26 | 280 | CHECK_GE(object->InternalFieldCount(), BaseObject::kInternalFieldCount); |
27 | 280 | SetInternalFields(realm->isolate_data(), object, static_cast<void*>(this)); |
28 | 280 | realm->TrackBaseObject(this); |
29 | 280 | } |
30 | | |
31 | 280 | BaseObject::~BaseObject() { |
32 | 280 | realm()->UntrackBaseObject(this); |
33 | | |
34 | 280 | if (has_pointer_data()) [[unlikely]] { |
35 | 280 | PointerData* metadata = pointer_data(); |
36 | 280 | CHECK_EQ(metadata->strong_ptr_count, 0); |
37 | 280 | metadata->self = nullptr; |
38 | 280 | if (metadata->weak_ptr_count == 0) delete metadata; |
39 | 280 | } |
40 | | |
41 | 280 | if (persistent_handle_.IsEmpty()) { |
42 | | // This most likely happened because the weak callback below cleared it. |
43 | 0 | return; |
44 | 0 | } |
45 | | |
46 | 280 | { |
47 | 280 | HandleScope handle_scope(realm()->isolate()); |
48 | 280 | object()->SetAlignedPointerInInternalField( |
49 | 280 | BaseObject::kSlot, nullptr, EmbedderDataTag::kDefault); |
50 | 280 | } |
51 | 280 | } |
52 | | |
53 | 315 | void BaseObject::MakeWeak() { |
54 | 315 | if (has_pointer_data()) { |
55 | 0 | pointer_data()->wants_weak_jsobj = true; |
56 | 0 | if (pointer_data()->strong_ptr_count > 0) return; |
57 | 0 | } |
58 | | |
59 | 315 | persistent_handle_.SetWeak( |
60 | 315 | this, |
61 | 315 | [](const WeakCallbackInfo<BaseObject>& data) { |
62 | 0 | BaseObject* obj = data.GetParameter(); |
63 | | // Clear the persistent handle so that ~BaseObject() doesn't attempt |
64 | | // to mess with internal fields, since the JS object may have |
65 | | // transitioned into an invalid state. |
66 | | // Refs: https://github.com/nodejs/node/issues/18897 |
67 | 0 | obj->persistent_handle_.Reset(); |
68 | 0 | CHECK_IMPLIES(obj->has_pointer_data(), |
69 | 0 | obj->pointer_data()->strong_ptr_count == 0); |
70 | 0 | obj->OnGCCollect(); |
71 | 0 | }, |
72 | 315 | WeakCallbackType::kParameter); |
73 | 315 | } |
74 | | |
75 | | void BaseObject::LazilyInitializedJSTemplateConstructor( |
76 | 0 | const FunctionCallbackInfo<Value>& args) { |
77 | 0 | DCHECK(args.IsConstructCall()); |
78 | 0 | CHECK_GE(args.This()->InternalFieldCount(), BaseObject::kInternalFieldCount); |
79 | 0 | Environment* env = Environment::GetCurrent(args); |
80 | 0 | DCHECK_NOT_NULL(env); |
81 | 0 | SetInternalFields(env->isolate_data(), args.This(), nullptr); |
82 | 0 | } |
83 | | |
84 | | Local<FunctionTemplate> BaseObject::MakeLazilyInitializedJSTemplate( |
85 | 0 | Environment* env) { |
86 | 0 | return MakeLazilyInitializedJSTemplate(env->isolate_data()); |
87 | 0 | } |
88 | | |
89 | | Local<FunctionTemplate> BaseObject::MakeLazilyInitializedJSTemplate( |
90 | 35 | IsolateData* isolate_data) { |
91 | 35 | Local<FunctionTemplate> t = NewFunctionTemplate( |
92 | 35 | isolate_data->isolate(), LazilyInitializedJSTemplateConstructor); |
93 | 35 | t->InstanceTemplate()->SetInternalFieldCount(BaseObject::kInternalFieldCount); |
94 | 35 | return t; |
95 | 35 | } |
96 | | |
97 | 0 | BaseObject::TransferMode BaseObject::GetTransferMode() const { |
98 | 0 | return TransferMode::kDisallowCloneAndTransfer; |
99 | 0 | } |
100 | | |
101 | 0 | std::unique_ptr<worker::TransferData> BaseObject::TransferForMessaging() { |
102 | 0 | return {}; |
103 | 0 | } |
104 | | |
105 | 0 | std::unique_ptr<worker::TransferData> BaseObject::CloneForMessaging() const { |
106 | 0 | return {}; |
107 | 0 | } |
108 | | |
109 | | Maybe<std::vector<BaseObjectPtr<BaseObject>>> BaseObject::NestedTransferables() |
110 | 0 | const { |
111 | 0 | return Just(std::vector<BaseObjectPtr<BaseObject>>{}); |
112 | 0 | } |
113 | | |
114 | | Maybe<void> BaseObject::FinalizeTransferRead(Local<Context> context, |
115 | 0 | ValueDeserializer* deserializer) { |
116 | 0 | return JustVoid(); |
117 | 0 | } |
118 | | |
119 | 1.12k | BaseObject::PointerData* BaseObject::pointer_data() { |
120 | 1.12k | if (!has_pointer_data()) { |
121 | 280 | PointerData* metadata = new PointerData(); |
122 | 280 | metadata->wants_weak_jsobj = persistent_handle_.IsWeak(); |
123 | 280 | metadata->self = this; |
124 | 280 | pointer_data_ = metadata; |
125 | 280 | } |
126 | 1.12k | CHECK(has_pointer_data()); |
127 | 1.12k | return pointer_data_; |
128 | 1.12k | } |
129 | | |
130 | 0 | void BaseObject::decrease_refcount() { |
131 | 0 | CHECK(has_pointer_data()); |
132 | 0 | PointerData* metadata = pointer_data(); |
133 | 0 | CHECK_GT(metadata->strong_ptr_count, 0); |
134 | 0 | unsigned int new_refcount = --metadata->strong_ptr_count; |
135 | 0 | if (new_refcount == 0) { |
136 | 0 | if (metadata->is_detached) { |
137 | 0 | OnGCCollect(); |
138 | 0 | } else if (metadata->wants_weak_jsobj && !persistent_handle_.IsEmpty()) { |
139 | 0 | MakeWeak(); |
140 | 0 | } |
141 | 0 | } |
142 | 0 | } |
143 | | |
144 | 0 | void BaseObject::increase_refcount() { |
145 | 0 | unsigned int prev_refcount = pointer_data()->strong_ptr_count++; |
146 | 0 | if (prev_refcount == 0 && !persistent_handle_.IsEmpty()) |
147 | 0 | persistent_handle_.ClearWeak(); |
148 | 0 | } |
149 | | |
150 | 280 | void BaseObject::DeleteMe() { |
151 | 280 | if (has_pointer_data() && pointer_data()->strong_ptr_count > 0) { |
152 | 0 | return Detach(); |
153 | 0 | } |
154 | 280 | delete this; |
155 | 280 | } |
156 | | |
157 | 0 | bool BaseObject::IsDoneInitializing() const { |
158 | 0 | return true; |
159 | 0 | } |
160 | | |
161 | 0 | Local<Object> BaseObject::WrappedObject() const { |
162 | 0 | return object(); |
163 | 0 | } |
164 | | |
165 | 0 | bool BaseObject::IsNotIndicativeOfMemoryLeakAtExit() const { |
166 | 0 | return IsWeakOrDetached(); |
167 | 0 | } |
168 | | |
169 | 35 | void BaseObjectList::Cleanup() { |
170 | 315 | while (!IsEmpty()) { |
171 | 280 | BaseObject* bo = PopFront(); |
172 | 280 | bo->DeleteMe(); |
173 | 280 | } |
174 | 35 | } |
175 | | |
176 | 0 | void BaseObjectList::MemoryInfo(node::MemoryTracker* tracker) const { |
177 | 0 | for (auto bo : *this) { |
178 | 0 | if (bo->IsDoneInitializing()) tracker->Track(bo); |
179 | 0 | } |
180 | 0 | } |
181 | | |
182 | | } // namespace node |