1 #include "base_object.h"
2 #include "env-inl.h"
3 #include "node_realm-inl.h"
4
5 namespace node {
6
7 using v8::FunctionCallbackInfo;
8 using v8::FunctionTemplate;
9 using v8::HandleScope;
10 using v8::Local;
11 using v8::Object;
12 using v8::Value;
13 using v8::WeakCallbackInfo;
14 using v8::WeakCallbackType;
15
BaseObject(Realm * realm,Local<Object> object)16 BaseObject::BaseObject(Realm* realm, Local<Object> object)
17 : persistent_handle_(realm->isolate(), object), realm_(realm) {
18 CHECK_EQ(false, object.IsEmpty());
19 CHECK_GE(object->InternalFieldCount(), BaseObject::kInternalFieldCount);
20 SetInternalFields(object, static_cast<void*>(this));
21 realm->AddCleanupHook(DeleteMe, static_cast<void*>(this));
22 realm->modify_base_object_count(1);
23 }
24
~BaseObject()25 BaseObject::~BaseObject() {
26 realm()->modify_base_object_count(-1);
27 realm()->RemoveCleanupHook(DeleteMe, static_cast<void*>(this));
28
29 if (UNLIKELY(has_pointer_data())) {
30 PointerData* metadata = pointer_data();
31 CHECK_EQ(metadata->strong_ptr_count, 0);
32 metadata->self = nullptr;
33 if (metadata->weak_ptr_count == 0) delete metadata;
34 }
35
36 if (persistent_handle_.IsEmpty()) {
37 // This most likely happened because the weak callback below cleared it.
38 return;
39 }
40
41 {
42 HandleScope handle_scope(realm()->isolate());
43 object()->SetAlignedPointerInInternalField(BaseObject::kSlot, nullptr);
44 }
45 }
46
MakeWeak()47 void BaseObject::MakeWeak() {
48 if (has_pointer_data()) {
49 pointer_data()->wants_weak_jsobj = true;
50 if (pointer_data()->strong_ptr_count > 0) return;
51 }
52
53 persistent_handle_.SetWeak(
54 this,
55 [](const WeakCallbackInfo<BaseObject>& data) {
56 BaseObject* obj = data.GetParameter();
57 // Clear the persistent handle so that ~BaseObject() doesn't attempt
58 // to mess with internal fields, since the JS object may have
59 // transitioned into an invalid state.
60 // Refs: https://github.com/nodejs/node/issues/18897
61 obj->persistent_handle_.Reset();
62 CHECK_IMPLIES(obj->has_pointer_data(),
63 obj->pointer_data()->strong_ptr_count == 0);
64 obj->OnGCCollect();
65 },
66 WeakCallbackType::kParameter);
67 }
68
69 // This just has to be different from the Chromium ones:
70 // https://source.chromium.org/chromium/chromium/src/+/main:gin/public/gin_embedders.h;l=18-23;drc=5a758a97032f0b656c3c36a3497560762495501a
71 // Otherwise, when Node is loaded in an isolate which uses cppgc, cppgc will
72 // misinterpret the data stored in the embedder fields and try to garbage
73 // collect them.
74 uint16_t kNodeEmbedderId = 0x90de;
75
LazilyInitializedJSTemplateConstructor(const FunctionCallbackInfo<Value> & args)76 void BaseObject::LazilyInitializedJSTemplateConstructor(
77 const FunctionCallbackInfo<Value>& args) {
78 DCHECK(args.IsConstructCall());
79 CHECK_GE(args.This()->InternalFieldCount(), BaseObject::kInternalFieldCount);
80 SetInternalFields(args.This(), nullptr);
81 }
82
MakeLazilyInitializedJSTemplate(Environment * env)83 Local<FunctionTemplate> BaseObject::MakeLazilyInitializedJSTemplate(
84 Environment* env) {
85 return MakeLazilyInitializedJSTemplate(env->isolate_data());
86 }
87
MakeLazilyInitializedJSTemplate(IsolateData * isolate_data)88 Local<FunctionTemplate> BaseObject::MakeLazilyInitializedJSTemplate(
89 IsolateData* isolate_data) {
90 Local<FunctionTemplate> t = NewFunctionTemplate(
91 isolate_data->isolate(), LazilyInitializedJSTemplateConstructor);
92 t->Inherit(BaseObject::GetConstructorTemplate(isolate_data));
93 t->InstanceTemplate()->SetInternalFieldCount(BaseObject::kInternalFieldCount);
94 return t;
95 }
96
pointer_data()97 BaseObject::PointerData* BaseObject::pointer_data() {
98 if (!has_pointer_data()) {
99 PointerData* metadata = new PointerData();
100 metadata->wants_weak_jsobj = persistent_handle_.IsWeak();
101 metadata->self = this;
102 pointer_data_ = metadata;
103 }
104 CHECK(has_pointer_data());
105 return pointer_data_;
106 }
107
decrease_refcount()108 void BaseObject::decrease_refcount() {
109 CHECK(has_pointer_data());
110 PointerData* metadata = pointer_data();
111 CHECK_GT(metadata->strong_ptr_count, 0);
112 unsigned int new_refcount = --metadata->strong_ptr_count;
113 if (new_refcount == 0) {
114 if (metadata->is_detached) {
115 OnGCCollect();
116 } else if (metadata->wants_weak_jsobj && !persistent_handle_.IsEmpty()) {
117 MakeWeak();
118 }
119 }
120 }
121
increase_refcount()122 void BaseObject::increase_refcount() {
123 unsigned int prev_refcount = pointer_data()->strong_ptr_count++;
124 if (prev_refcount == 0 && !persistent_handle_.IsEmpty())
125 persistent_handle_.ClearWeak();
126 }
127
DeleteMe(void * data)128 void BaseObject::DeleteMe(void* data) {
129 BaseObject* self = static_cast<BaseObject*>(data);
130 if (self->has_pointer_data() && self->pointer_data()->strong_ptr_count > 0) {
131 return self->Detach();
132 }
133 delete self;
134 }
135
IsDoneInitializing() const136 bool BaseObject::IsDoneInitializing() const {
137 return true;
138 }
139
WrappedObject() const140 Local<Object> BaseObject::WrappedObject() const {
141 return object();
142 }
143
IsRootNode() const144 bool BaseObject::IsRootNode() const {
145 return !persistent_handle_.IsWeak();
146 }
147
GetConstructorTemplate(IsolateData * isolate_data)148 Local<FunctionTemplate> BaseObject::GetConstructorTemplate(
149 IsolateData* isolate_data) {
150 Local<FunctionTemplate> tmpl = isolate_data->base_object_ctor_template();
151 if (tmpl.IsEmpty()) {
152 tmpl = NewFunctionTemplate(isolate_data->isolate(), nullptr);
153 tmpl->SetClassName(
154 FIXED_ONE_BYTE_STRING(isolate_data->isolate(), "BaseObject"));
155 isolate_data->set_base_object_ctor_template(tmpl);
156 }
157 return tmpl;
158 }
159
IsNotIndicativeOfMemoryLeakAtExit() const160 bool BaseObject::IsNotIndicativeOfMemoryLeakAtExit() const {
161 return IsWeakOrDetached();
162 }
163
164 } // namespace node
165