xref: /third_party/node/src/base_object.cc (revision 1cb0ef41)
1#include "base_object.h"
2#include "env-inl.h"
3#include "node_realm-inl.h"
4
5namespace node {
6
7using v8::FunctionCallbackInfo;
8using v8::FunctionTemplate;
9using v8::HandleScope;
10using v8::Local;
11using v8::Object;
12using v8::Value;
13using v8::WeakCallbackInfo;
14using v8::WeakCallbackType;
15
16BaseObject::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
25BaseObject::~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
47void 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.
74uint16_t kNodeEmbedderId = 0x90de;
75
76void 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
83Local<FunctionTemplate> BaseObject::MakeLazilyInitializedJSTemplate(
84    Environment* env) {
85  return MakeLazilyInitializedJSTemplate(env->isolate_data());
86}
87
88Local<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
97BaseObject::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
108void 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
122void 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
128void 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
136bool BaseObject::IsDoneInitializing() const {
137  return true;
138}
139
140Local<Object> BaseObject::WrappedObject() const {
141  return object();
142}
143
144bool BaseObject::IsRootNode() const {
145  return !persistent_handle_.IsWeak();
146}
147
148Local<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
160bool BaseObject::IsNotIndicativeOfMemoryLeakAtExit() const {
161  return IsWeakOrDetached();
162}
163
164}  // namespace node
165