Coverage Report

Created: 2026-01-21 08:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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