• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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