Bug Summary

File:out/../src/base_object-inl.h
Warning:line 239, column 3
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name heap_utils.cc -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/home/maurizio/node-v18.6.0/out -resource-dir /usr/local/lib/clang/16.0.0 -D V8_DEPRECATION_WARNINGS -D V8_IMMINENT_DEPRECATION_WARNINGS -D _GLIBCXX_USE_CXX11_ABI=1 -D NODE_OPENSSL_CONF_NAME=nodejs_conf -D NODE_OPENSSL_HAS_QUIC -D __STDC_FORMAT_MACROS -D OPENSSL_NO_PINSHARED -D OPENSSL_THREADS -D NODE_ARCH="x64" -D NODE_PLATFORM="linux" -D NODE_WANT_INTERNALS=1 -D V8_DEPRECATION_WARNINGS=1 -D NODE_OPENSSL_SYSTEM_CERT_PATH="" -D NODE_USE_NODE_CODE_CACHE=1 -D HAVE_INSPECTOR=1 -D NODE_ENABLE_LARGE_CODE_PAGES=1 -D __POSIX__ -D NODE_USE_V8_PLATFORM=1 -D NODE_HAVE_I18N_SUPPORT=1 -D HAVE_OPENSSL=1 -D OPENSSL_API_COMPAT=0x10100000L -D UCONFIG_NO_SERVICE=1 -D U_ENABLE_DYLOAD=0 -D U_STATIC_IMPLEMENTATION=1 -D U_HAVE_STD_STRING=1 -D UCONFIG_NO_BREAK_ITERATION=0 -D _LARGEFILE_SOURCE -D _FILE_OFFSET_BITS=64 -D _POSIX_C_SOURCE=200112 -D NGHTTP2_STATICLIB -D NDEBUG -D OPENSSL_USE_NODELETE -D L_ENDIAN -D OPENSSL_BUILDING_OPENSSL -D AES_ASM -D BSAES_ASM -D CMLL_ASM -D ECP_NISTZ256_ASM -D GHASH_ASM -D KECCAK1600_ASM -D MD5_ASM -D OPENSSL_BN_ASM_GF2m -D OPENSSL_BN_ASM_MONT -D OPENSSL_BN_ASM_MONT5 -D OPENSSL_CPUID_OBJ -D OPENSSL_IA32_SSE2 -D PADLOCK_ASM -D POLY1305_ASM -D SHA1_ASM -D SHA256_ASM -D SHA512_ASM -D VPAES_ASM -D WHIRLPOOL_ASM -D X25519_ASM -D OPENSSL_PIC -D NGTCP2_STATICLIB -D NGHTTP3_STATICLIB -I ../src -I /home/maurizio/node-v18.6.0/out/Release/obj/gen -I /home/maurizio/node-v18.6.0/out/Release/obj/gen/include -I /home/maurizio/node-v18.6.0/out/Release/obj/gen/src -I ../deps/googletest/include -I ../deps/histogram/src -I ../deps/uvwasi/include -I ../deps/v8/include -I ../deps/icu-small/source/i18n -I ../deps/icu-small/source/common -I ../deps/zlib -I ../deps/llhttp/include -I ../deps/cares/include -I ../deps/uv/include -I ../deps/nghttp2/lib/includes -I ../deps/brotli/c/include -I ../deps/openssl/openssl/include -I ../deps/openssl/openssl/crypto/include -I ../deps/openssl/config/archs/linux-x86_64/asm/include -I ../deps/openssl/config/archs/linux-x86_64/asm -I ../deps/ngtcp2 -I ../deps/ngtcp2/ngtcp2/lib/includes -I ../deps/ngtcp2/ngtcp2/crypto/includes -I ../deps/ngtcp2/nghttp3/lib/includes -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8 -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8/x86_64-redhat-linux -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8/backward -internal-isystem /usr/local/lib/clang/16.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -Wno-unused-parameter -Wno-unused-parameter -std=gnu++17 -fdeprecated-macro -fdebug-compilation-dir=/home/maurizio/node-v18.6.0/out -ferror-limit 19 -fno-rtti -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-08-22-142216-507842-1 -x c++ ../src/heap_utils.cc

../src/heap_utils.cc

1#include "diagnosticfilename-inl.h"
2#include "env-inl.h"
3#include "memory_tracker-inl.h"
4#include "node_external_reference.h"
5#include "stream_base-inl.h"
6#include "util-inl.h"
7
8// Copied from https://github.com/nodejs/node/blob/b07dc4d19fdbc15b4f76557dc45b3ce3a43ad0c3/src/util.cc#L36-L41.
9#ifdef _WIN32
10#include <io.h> // _S_IREAD _S_IWRITE
11#ifndef S_IRUSR0400
12#define S_IRUSR0400 _S_IREAD
13#endif // S_IRUSR
14#ifndef S_IWUSR0200
15#define S_IWUSR0200 _S_IWRITE
16#endif // S_IWUSR
17#endif
18
19using v8::Array;
20using v8::Boolean;
21using v8::Context;
22using v8::EmbedderGraph;
23using v8::EscapableHandleScope;
24using v8::FunctionCallbackInfo;
25using v8::FunctionTemplate;
26using v8::Global;
27using v8::HandleScope;
28using v8::HeapSnapshot;
29using v8::Isolate;
30using v8::JustVoid;
31using v8::Local;
32using v8::Maybe;
33using v8::MaybeLocal;
34using v8::Nothing;
35using v8::Number;
36using v8::Object;
37using v8::ObjectTemplate;
38using v8::String;
39using v8::Value;
40
41namespace node {
42namespace heap {
43
44class JSGraphJSNode : public EmbedderGraph::Node {
45 public:
46 const char* Name() override { return "<JS Node>"; }
47 size_t SizeInBytes() override { return 0; }
48 bool IsEmbedderNode() override { return false; }
49 Local<Value> JSValue() { return PersistentToLocal::Strong(persistent_); }
50
51 int IdentityHash() {
52 Local<Value> v = JSValue();
53 if (v->IsObject()) return v.As<Object>()->GetIdentityHash();
54 if (v->IsName()) return v.As<v8::Name>()->GetIdentityHash();
55 if (v->IsInt32()) return v.As<v8::Int32>()->Value();
56 return 0;
57 }
58
59 JSGraphJSNode(Isolate* isolate, Local<Value> val)
60 : persistent_(isolate, val) {
61 CHECK(!val.IsEmpty())do { if (__builtin_expect(!!(!(!val.IsEmpty())), 0)) { do { static
const node::AssertionInfo args = { "../src/heap_utils.cc" ":"
"61", "!val.IsEmpty()", __PRETTY_FUNCTION__ }; node::Assert(
args); } while (0); } } while (0)
;
62 }
63
64 struct Hash {
65 inline size_t operator()(JSGraphJSNode* n) const {
66 return static_cast<size_t>(n->IdentityHash());
67 }
68 };
69
70 struct Equal {
71 inline bool operator()(JSGraphJSNode* a, JSGraphJSNode* b) const {
72 return a->JSValue()->SameValue(b->JSValue());
73 }
74 };
75
76 private:
77 Global<Value> persistent_;
78};
79
80class JSGraph : public EmbedderGraph {
81 public:
82 explicit JSGraph(Isolate* isolate) : isolate_(isolate) {}
83
84 Node* V8Node(const Local<Value>& value) override {
85 std::unique_ptr<JSGraphJSNode> n { new JSGraphJSNode(isolate_, value) };
86 auto it = engine_nodes_.find(n.get());
87 if (it != engine_nodes_.end())
88 return *it;
89 engine_nodes_.insert(n.get());
90 return AddNode(std::unique_ptr<Node>(n.release()));
91 }
92
93 Node* AddNode(std::unique_ptr<Node> node) override {
94 Node* n = node.get();
95 nodes_.emplace(std::move(node));
96 return n;
97 }
98
99 void AddEdge(Node* from, Node* to, const char* name = nullptr) override {
100 edges_[from].insert(std::make_pair(name, to));
101 }
102
103 MaybeLocal<Array> CreateObject() const {
104 EscapableHandleScope handle_scope(isolate_);
105 Local<Context> context = isolate_->GetCurrentContext();
106 Environment* env = Environment::GetCurrent(context);
107
108 std::unordered_map<Node*, Local<Object>> info_objects;
109 Local<Array> nodes = Array::New(isolate_, nodes_.size());
110 Local<String> edges_string = FIXED_ONE_BYTE_STRING(isolate_, "edges");
111 Local<String> is_root_string = FIXED_ONE_BYTE_STRING(isolate_, "isRoot");
112 Local<String> name_string = env->name_string();
113 Local<String> size_string = env->size_string();
114 Local<String> value_string = env->value_string();
115 Local<String> wraps_string = FIXED_ONE_BYTE_STRING(isolate_, "wraps");
116 Local<String> to_string = FIXED_ONE_BYTE_STRING(isolate_, "to");
117
118 for (const std::unique_ptr<Node>& n : nodes_)
119 info_objects[n.get()] = Object::New(isolate_);
120
121 {
122 HandleScope handle_scope(isolate_);
123 size_t i = 0;
124 for (const std::unique_ptr<Node>& n : nodes_) {
125 Local<Object> obj = info_objects[n.get()];
126 Local<Value> value;
127 std::string name_str;
128 const char* prefix = n->NamePrefix();
129 if (prefix == nullptr) {
130 name_str = n->Name();
131 } else {
132 name_str = n->NamePrefix();
133 name_str += " ";
134 name_str += n->Name();
135 }
136 if (!String::NewFromUtf8(isolate_, name_str.c_str()).ToLocal(&value) ||
137 obj->Set(context, name_string, value).IsNothing() ||
138 obj->Set(context,
139 is_root_string,
140 Boolean::New(isolate_, n->IsRootNode()))
141 .IsNothing() ||
142 obj->Set(
143 context,
144 size_string,
145 Number::New(isolate_, static_cast<double>(n->SizeInBytes())))
146 .IsNothing() ||
147 obj->Set(context, edges_string, Array::New(isolate_)).IsNothing()) {
148 return MaybeLocal<Array>();
149 }
150 if (nodes->Set(context, i++, obj).IsNothing())
151 return MaybeLocal<Array>();
152 if (!n->IsEmbedderNode()) {
153 value = static_cast<JSGraphJSNode*>(n.get())->JSValue();
154 if (obj->Set(context, value_string, value).IsNothing())
155 return MaybeLocal<Array>();
156 }
157 }
158 }
159
160 for (const std::unique_ptr<Node>& n : nodes_) {
161 Node* wraps = n->WrapperNode();
162 if (wraps == nullptr) continue;
163 Local<Object> from = info_objects[n.get()];
164 Local<Object> to = info_objects[wraps];
165 if (from->Set(context, wraps_string, to).IsNothing())
166 return MaybeLocal<Array>();
167 }
168
169 for (const auto& edge_info : edges_) {
170 Node* source = edge_info.first;
171 Local<Value> edges;
172 if (!info_objects[source]->Get(context, edges_string).ToLocal(&edges) ||
173 !edges->IsArray()) {
174 return MaybeLocal<Array>();
175 }
176
177 size_t i = 0;
178 size_t j = 0;
179 for (const auto& edge : edge_info.second) {
180 Local<Object> to_object = info_objects[edge.second];
181 Local<Object> edge_obj = Object::New(isolate_);
182 Local<Value> edge_name_value;
183 const char* edge_name = edge.first;
184 if (edge_name != nullptr) {
185 if (!String::NewFromUtf8(isolate_, edge_name)
186 .ToLocal(&edge_name_value)) {
187 return MaybeLocal<Array>();
188 }
189 } else {
190 edge_name_value = Number::New(isolate_, static_cast<double>(j++));
191 }
192 if (edge_obj->Set(context, name_string, edge_name_value).IsNothing() ||
193 edge_obj->Set(context, to_string, to_object).IsNothing() ||
194 edges.As<Array>()->Set(context, i++, edge_obj).IsNothing()) {
195 return MaybeLocal<Array>();
196 }
197 }
198 }
199
200 return handle_scope.Escape(nodes);
201 }
202
203 private:
204 Isolate* isolate_;
205 std::unordered_set<std::unique_ptr<Node>> nodes_;
206 std::unordered_set<JSGraphJSNode*, JSGraphJSNode::Hash, JSGraphJSNode::Equal>
207 engine_nodes_;
208 std::unordered_map<Node*, std::set<std::pair<const char*, Node*>>> edges_;
209};
210
211void BuildEmbedderGraph(const FunctionCallbackInfo<Value>& args) {
212 Environment* env = Environment::GetCurrent(args);
213 JSGraph graph(env->isolate());
214 Environment::BuildEmbedderGraph(env->isolate(), &graph, env);
215 Local<Array> ret;
216 if (graph.CreateObject().ToLocal(&ret))
217 args.GetReturnValue().Set(ret);
218}
219
220namespace {
221class FileOutputStream : public v8::OutputStream {
222 public:
223 FileOutputStream(const int fd, uv_fs_t* req) : fd_(fd), req_(req) {}
224
225 int GetChunkSize() override {
226 return 65536; // big chunks == faster
227 }
228
229 void EndOfStream() override {}
230
231 WriteResult WriteAsciiChunk(char* data, const int size) override {
232 DCHECK_EQ(status_, 0);
233 int offset = 0;
234 while (offset < size) {
235 const uv_buf_t buf = uv_buf_init(data + offset, size - offset);
236 const int num_bytes_written = uv_fs_write(nullptr,
237 req_,
238 fd_,
239 &buf,
240 1,
241 -1,
242 nullptr);
243 uv_fs_req_cleanup(req_);
244 if (num_bytes_written < 0) {
245 status_ = num_bytes_written;
246 return kAbort;
247 }
248 DCHECK_LE(static_cast<size_t>(num_bytes_written), buf.len);
249 offset += num_bytes_written;
250 }
251 DCHECK_EQ(offset, size);
252 return kContinue;
253 }
254
255 int status() const { return status_; }
256
257 private:
258 const int fd_;
259 uv_fs_t* req_;
260 int status_ = 0;
261};
262
263class HeapSnapshotStream : public AsyncWrap,
264 public StreamBase,
265 public v8::OutputStream {
266 public:
267 HeapSnapshotStream(
268 Environment* env,
269 HeapSnapshotPointer&& snapshot,
270 Local<Object> obj) :
271 AsyncWrap(env, obj, AsyncWrap::PROVIDER_HEAPSNAPSHOT),
272 StreamBase(env),
273 snapshot_(std::move(snapshot)) {
274 MakeWeak();
275 StreamBase::AttachToObject(GetObject());
276 }
277
278 ~HeapSnapshotStream() override {}
279
280 int GetChunkSize() override {
281 return 65536; // big chunks == faster
282 }
283
284 void EndOfStream() override {
285 EmitRead(UV_EOF);
286 snapshot_.reset();
287 }
288
289 WriteResult WriteAsciiChunk(char* data, int size) override {
290 int len = size;
291 while (len != 0) {
292 uv_buf_t buf = EmitAlloc(size);
293 ssize_t avail = len;
294 if (static_cast<ssize_t>(buf.len) < avail)
295 avail = buf.len;
296 memcpy(buf.base, data, avail);
297 data += avail;
298 len -= static_cast<int>(avail);
299 EmitRead(size, buf);
300 }
301 return kContinue;
302 }
303
304 int ReadStart() override {
305 CHECK_NE(snapshot_, nullptr)do { if (__builtin_expect(!!(!((snapshot_) != (nullptr))), 0)
) { do { static const node::AssertionInfo args = { "../src/heap_utils.cc"
":" "305", "(snapshot_) != (nullptr)", __PRETTY_FUNCTION__ }
; node::Assert(args); } while (0); } } while (0)
;
306 snapshot_->Serialize(this, HeapSnapshot::kJSON);
307 return 0;
308 }
309
310 int ReadStop() override {
311 return 0;
312 }
313
314 int DoShutdown(ShutdownWrap* req_wrap) override {
315 UNREACHABLE()do { static const node::AssertionInfo args = { "../src/heap_utils.cc"
":" "315", "\"Unreachable code reached\"", __PRETTY_FUNCTION__
}; node::Assert(args); } while (0)
;
316 }
317
318 int DoWrite(WriteWrap* w,
319 uv_buf_t* bufs,
320 size_t count,
321 uv_stream_t* send_handle) override {
322 UNREACHABLE()do { static const node::AssertionInfo args = { "../src/heap_utils.cc"
":" "322", "\"Unreachable code reached\"", __PRETTY_FUNCTION__
}; node::Assert(args); } while (0)
;
323 }
324
325 bool IsAlive() override { return snapshot_ != nullptr; }
326 bool IsClosing() override { return snapshot_ == nullptr; }
327 AsyncWrap* GetAsyncWrap() override { return this; }
328
329 void MemoryInfo(MemoryTracker* tracker) const override {
330 if (snapshot_ != nullptr) {
331 tracker->TrackFieldWithSize(
332 "snapshot", sizeof(*snapshot_), "HeapSnapshot");
333 }
334 }
335
336 SET_MEMORY_INFO_NAME(HeapSnapshotStream)inline std::string MemoryInfoName() const override { return "HeapSnapshotStream"
; }
337 SET_SELF_SIZE(HeapSnapshotStream)inline size_t SelfSize() const override { return sizeof(HeapSnapshotStream
); }
338
339 private:
340 HeapSnapshotPointer snapshot_;
341};
342
343inline void TakeSnapshot(Environment* env, v8::OutputStream* out) {
344 HeapSnapshotPointer snapshot {
345 env->isolate()->GetHeapProfiler()->TakeHeapSnapshot() };
346 snapshot->Serialize(out, HeapSnapshot::kJSON);
347}
348
349} // namespace
350
351Maybe<void> WriteSnapshot(Environment* env, const char* filename) {
352 uv_fs_t req;
353 int err;
354
355 const int fd = uv_fs_open(nullptr,
356 &req,
357 filename,
358 O_WRONLY01 | O_CREAT0100 | O_TRUNC01000,
359 S_IWUSR0200 | S_IRUSR0400,
360 nullptr);
361 uv_fs_req_cleanup(&req);
362 if ((err = fd) < 0) {
363 env->ThrowUVException(err, "open", nullptr, filename);
364 return Nothing<void>();
365 }
366
367 FileOutputStream stream(fd, &req);
368 TakeSnapshot(env, &stream);
369 if ((err = stream.status()) < 0) {
370 env->ThrowUVException(err, "write", nullptr, filename);
371 return Nothing<void>();
372 }
373
374 err = uv_fs_close(nullptr, &req, fd, nullptr);
375 uv_fs_req_cleanup(&req);
376 if (err < 0) {
377 env->ThrowUVException(err, "close", nullptr, filename);
378 return Nothing<void>();
379 }
380
381 return JustVoid();
382}
383
384void DeleteHeapSnapshot(const HeapSnapshot* snapshot) {
385 const_cast<HeapSnapshot*>(snapshot)->Delete();
386}
387
388BaseObjectPtr<AsyncWrap> CreateHeapSnapshotStream(
389 Environment* env, HeapSnapshotPointer&& snapshot) {
390 HandleScope scope(env->isolate());
391
392 if (env->streambaseoutputstream_constructor_template().IsEmpty()) {
4
Taking false branch
393 // Create FunctionTemplate for HeapSnapshotStream
394 Local<FunctionTemplate> os = FunctionTemplate::New(env->isolate());
395 os->Inherit(AsyncWrap::GetConstructorTemplate(env));
396 Local<ObjectTemplate> ost = os->InstanceTemplate();
397 ost->SetInternalFieldCount(StreamBase::kInternalFieldCount);
398 os->SetClassName(
399 FIXED_ONE_BYTE_STRING(env->isolate(), "HeapSnapshotStream"));
400 StreamBase::AddMethods(env, os);
401 env->set_streambaseoutputstream_constructor_template(ost);
402 }
403
404 Local<Object> obj;
405 if (!env->streambaseoutputstream_constructor_template()
5
Taking false branch
406 ->NewInstance(env->context())
407 .ToLocal(&obj)) {
408 return {};
409 }
410 return MakeBaseObject<HeapSnapshotStream>(env, std::move(snapshot), obj);
6
Calling 'MakeBaseObject<node::heap::(anonymous namespace)::HeapSnapshotStream, node::Environment *&, std::unique_ptr<const v8::HeapSnapshot, node::FunctionDeleter<const v8::HeapSnapshot, &node::heap::DeleteHeapSnapshot>>, v8::Local<v8::Object> &>'
8
Returned allocated memory
9
Calling '~BaseObjectPtrImpl'
24
Returning from '~BaseObjectPtrImpl'
411}
412
413void CreateHeapSnapshotStream(const FunctionCallbackInfo<Value>& args) {
414 Environment* env = Environment::GetCurrent(args);
415 HeapSnapshotPointer snapshot {
416 env->isolate()->GetHeapProfiler()->TakeHeapSnapshot() };
417 CHECK(snapshot)do { if (__builtin_expect(!!(!(snapshot)), 0)) { do { static const
node::AssertionInfo args = { "../src/heap_utils.cc" ":" "417"
, "snapshot", __PRETTY_FUNCTION__ }; node::Assert(args); } while
(0); } } while (0)
;
1
Taking false branch
2
Loop condition is false. Exiting loop
418 BaseObjectPtr<AsyncWrap> stream =
419 CreateHeapSnapshotStream(env, std::move(snapshot));
3
Calling 'CreateHeapSnapshotStream'
25
Returning; memory was released
420 if (stream)
26
Calling 'BaseObjectPtrImpl::operator bool'
421 args.GetReturnValue().Set(stream->object());
422}
423
424void TriggerHeapSnapshot(const FunctionCallbackInfo<Value>& args) {
425 Environment* env = Environment::GetCurrent(args);
426 Isolate* isolate = args.GetIsolate();
427
428 Local<Value> filename_v = args[0];
429
430 if (filename_v->IsUndefined()) {
431 DiagnosticFilename name(env, "Heap", "heapsnapshot");
432 if (WriteSnapshot(env, *name).IsNothing())
433 return;
434 if (String::NewFromUtf8(isolate, *name).ToLocal(&filename_v)) {
435 args.GetReturnValue().Set(filename_v);
436 }
437 return;
438 }
439
440 BufferValue path(isolate, filename_v);
441 CHECK_NOT_NULL(*path)do { if (__builtin_expect(!!(!((*path) != nullptr)), 0)) { do
{ static const node::AssertionInfo args = { "../src/heap_utils.cc"
":" "441", "(*path) != nullptr", __PRETTY_FUNCTION__ }; node
::Assert(args); } while (0); } } while (0)
;
442 if (WriteSnapshot(env, *path).IsNothing())
443 return;
444 return args.GetReturnValue().Set(filename_v);
445}
446
447void Initialize(Local<Object> target,
448 Local<Value> unused,
449 Local<Context> context,
450 void* priv) {
451 Environment* env = Environment::GetCurrent(context);
452
453 env->SetMethod(target, "buildEmbedderGraph", BuildEmbedderGraph);
454 env->SetMethod(target, "triggerHeapSnapshot", TriggerHeapSnapshot);
455 env->SetMethod(target, "createHeapSnapshotStream", CreateHeapSnapshotStream);
456}
457
458void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
459 registry->Register(BuildEmbedderGraph);
460 registry->Register(TriggerHeapSnapshot);
461 registry->Register(CreateHeapSnapshotStream);
462}
463
464} // namespace heap
465} // namespace node
466
467NODE_MODULE_CONTEXT_AWARE_INTERNAL(heap_utils, node::heap::Initialize)static node::node_module _module = { 108, NM_F_INTERNAL, nullptr
, "../src/heap_utils.cc", nullptr, (node::addon_context_register_func
)(node::heap::Initialize), "heap_utils", nullptr, nullptr}; void
_register_heap_utils() { node_module_register(&_module);
}
468NODE_MODULE_EXTERNAL_REFERENCE(heap_utils,void _register_external_reference_heap_utils( node::ExternalReferenceRegistry
* registry) { node::heap::RegisterExternalReferences(registry
); }
469 node::heap::RegisterExternalReferences)void _register_external_reference_heap_utils( node::ExternalReferenceRegistry
* registry) { node::heap::RegisterExternalReferences(registry
); }

../src/base_object-inl.h

1// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22#ifndef SRC_BASE_OBJECT_INL_H_
23#define SRC_BASE_OBJECT_INL_H_
24
25#if defined(NODE_WANT_INTERNALS1) && NODE_WANT_INTERNALS1
26
27#include "base_object.h"
28#include "env-inl.h"
29#include "util.h"
30
31#include "v8.h"
32
33namespace node {
34
35BaseObject::BaseObject(Environment* env, v8::Local<v8::Object> object)
36 : persistent_handle_(env->isolate(), object), env_(env) {
37 CHECK_EQ(false, object.IsEmpty())do { if (__builtin_expect(!!(!((false) == (object.IsEmpty()))
), 0)) { do { static const node::AssertionInfo args = { "../src/base_object-inl.h"
":" "37", "(false) == (object.IsEmpty())", __PRETTY_FUNCTION__
}; node::Assert(args); } while (0); } } while (0)
;
38 CHECK_GT(object->InternalFieldCount(), 0)do { if (__builtin_expect(!!(!((object->InternalFieldCount
()) > (0))), 0)) { do { static const node::AssertionInfo args
= { "../src/base_object-inl.h" ":" "38", "(object->InternalFieldCount()) > (0)"
, __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } }
while (0)
;
39 object->SetAlignedPointerInInternalField(
40 BaseObject::kSlot,
41 static_cast<void*>(this));
42 env->AddCleanupHook(DeleteMe, static_cast<void*>(this));
43 env->modify_base_object_count(1);
44}
45
46BaseObject::~BaseObject() {
47 env()->modify_base_object_count(-1);
48 env()->RemoveCleanupHook(DeleteMe, static_cast<void*>(this));
49
50 if (UNLIKELY(has_pointer_data())__builtin_expect(!!(has_pointer_data()), 0)) {
51 PointerData* metadata = pointer_data();
52 CHECK_EQ(metadata->strong_ptr_count, 0)do { if (__builtin_expect(!!(!((metadata->strong_ptr_count
) == (0))), 0)) { do { static const node::AssertionInfo args =
{ "../src/base_object-inl.h" ":" "52", "(metadata->strong_ptr_count) == (0)"
, __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } }
while (0)
;
53 metadata->self = nullptr;
54 if (metadata->weak_ptr_count == 0)
55 delete metadata;
56 }
57
58 if (persistent_handle_.IsEmpty()) {
59 // This most likely happened because the weak callback below cleared it.
60 return;
61 }
62
63 {
64 v8::HandleScope handle_scope(env()->isolate());
65 object()->SetAlignedPointerInInternalField(BaseObject::kSlot, nullptr);
66 }
67}
68
69void BaseObject::Detach() {
70 CHECK_GT(pointer_data()->strong_ptr_count, 0)do { if (__builtin_expect(!!(!((pointer_data()->strong_ptr_count
) > (0))), 0)) { do { static const node::AssertionInfo args
= { "../src/base_object-inl.h" ":" "70", "(pointer_data()->strong_ptr_count) > (0)"
, __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } }
while (0)
;
71 pointer_data()->is_detached = true;
72}
73
74v8::Global<v8::Object>& BaseObject::persistent() {
75 return persistent_handle_;
76}
77
78
79v8::Local<v8::Object> BaseObject::object() const {
80 return PersistentToLocal::Default(env()->isolate(), persistent_handle_);
81}
82
83v8::Local<v8::Object> BaseObject::object(v8::Isolate* isolate) const {
84 v8::Local<v8::Object> handle = object();
85
86 DCHECK_EQ(handle->GetCreationContext().ToLocalChecked()->GetIsolate(),
87 isolate);
88 DCHECK_EQ(env()->isolate(), isolate);
89
90 return handle;
91}
92
93Environment* BaseObject::env() const {
94 return env_;
95}
96
97BaseObject* BaseObject::FromJSObject(v8::Local<v8::Value> value) {
98 v8::Local<v8::Object> obj = value.As<v8::Object>();
99 DCHECK_GE(obj->InternalFieldCount(), BaseObject::kSlot);
100 return static_cast<BaseObject*>(
101 obj->GetAlignedPointerFromInternalField(BaseObject::kSlot));
102}
103
104
105template <typename T>
106T* BaseObject::FromJSObject(v8::Local<v8::Value> object) {
107 return static_cast<T*>(FromJSObject(object));
108}
109
110
111void BaseObject::MakeWeak() {
112 if (has_pointer_data()) {
113 pointer_data()->wants_weak_jsobj = true;
114 if (pointer_data()->strong_ptr_count > 0) return;
115 }
116
117 persistent_handle_.SetWeak(
118 this,
119 [](const v8::WeakCallbackInfo<BaseObject>& data) {
120 BaseObject* obj = data.GetParameter();
121 // Clear the persistent handle so that ~BaseObject() doesn't attempt
122 // to mess with internal fields, since the JS object may have
123 // transitioned into an invalid state.
124 // Refs: https://github.com/nodejs/node/issues/18897
125 obj->persistent_handle_.Reset();
126 CHECK_IMPLIES(obj->has_pointer_data(),do { if (__builtin_expect(!!(!(!(obj->has_pointer_data()) ||
(obj->pointer_data()->strong_ptr_count == 0))), 0)) { do
{ static const node::AssertionInfo args = { "../src/base_object-inl.h"
":" "127", "!(obj->has_pointer_data()) || (obj->pointer_data()->strong_ptr_count == 0)"
, __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } }
while (0)
127 obj->pointer_data()->strong_ptr_count == 0)do { if (__builtin_expect(!!(!(!(obj->has_pointer_data()) ||
(obj->pointer_data()->strong_ptr_count == 0))), 0)) { do
{ static const node::AssertionInfo args = { "../src/base_object-inl.h"
":" "127", "!(obj->has_pointer_data()) || (obj->pointer_data()->strong_ptr_count == 0)"
, __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } }
while (0)
;
128 obj->OnGCCollect();
129 }, v8::WeakCallbackType::kParameter);
130}
131
132void BaseObject::OnGCCollect() {
133 delete this;
21
Memory is released
134}
135
136void BaseObject::ClearWeak() {
137 if (has_pointer_data())
138 pointer_data()->wants_weak_jsobj = false;
139
140 persistent_handle_.ClearWeak();
141}
142
143bool BaseObject::IsWeakOrDetached() const {
144 if (persistent_handle_.IsWeak()) return true;
145
146 if (!has_pointer_data()) return false;
147 const PointerData* pd = const_cast<BaseObject*>(this)->pointer_data();
148 return pd->wants_weak_jsobj || pd->is_detached;
149}
150
151void BaseObject::LazilyInitializedJSTemplateConstructor(
152 const v8::FunctionCallbackInfo<v8::Value>& args) {
153 DCHECK(args.IsConstructCall());
154 DCHECK_GT(args.This()->InternalFieldCount(), 0);
155 args.This()->SetAlignedPointerInInternalField(BaseObject::kSlot, nullptr);
156}
157
158v8::Local<v8::FunctionTemplate>
159BaseObject::MakeLazilyInitializedJSTemplate(Environment* env) {
160 v8::Local<v8::FunctionTemplate> t =
161 env->NewFunctionTemplate(LazilyInitializedJSTemplateConstructor);
162 t->Inherit(BaseObject::GetConstructorTemplate(env));
163 t->InstanceTemplate()->SetInternalFieldCount(
164 BaseObject::kInternalFieldCount);
165 return t;
166}
167
168template <int Field>
169void BaseObject::InternalFieldGet(
170 v8::Local<v8::String> property,
171 const v8::PropertyCallbackInfo<v8::Value>& info) {
172 info.GetReturnValue().Set(info.This()->GetInternalField(Field));
173}
174
175template <int Field, bool (v8::Value::* typecheck)() const>
176void BaseObject::InternalFieldSet(v8::Local<v8::String> property,
177 v8::Local<v8::Value> value,
178 const v8::PropertyCallbackInfo<void>& info) {
179 // This could be e.g. value->IsFunction().
180 CHECK(((*value)->*typecheck)())do { if (__builtin_expect(!!(!(((*value)->*typecheck)())),
0)) { do { static const node::AssertionInfo args = { "../src/base_object-inl.h"
":" "180", "((*value)->*typecheck)()", __PRETTY_FUNCTION__
}; node::Assert(args); } while (0); } } while (0)
;
181 info.This()->SetInternalField(Field, value);
182}
183
184bool BaseObject::has_pointer_data() const {
185 return pointer_data_ != nullptr;
186}
187
188BaseObject::PointerData* BaseObject::pointer_data() {
189 if (!has_pointer_data()) {
190 PointerData* metadata = new PointerData();
191 metadata->wants_weak_jsobj = persistent_handle_.IsWeak();
192 metadata->self = this;
193 pointer_data_ = metadata;
194 }
195 CHECK(has_pointer_data())do { if (__builtin_expect(!!(!(has_pointer_data())), 0)) { do
{ static const node::AssertionInfo args = { "../src/base_object-inl.h"
":" "195", "has_pointer_data()", __PRETTY_FUNCTION__ }; node
::Assert(args); } while (0); } } while (0)
;
196 return pointer_data_;
197}
198
199void BaseObject::decrease_refcount() {
200 CHECK(has_pointer_data())do { if (__builtin_expect(!!(!(has_pointer_data())), 0)) { do
{ static const node::AssertionInfo args = { "../src/base_object-inl.h"
":" "200", "has_pointer_data()", __PRETTY_FUNCTION__ }; node
::Assert(args); } while (0); } } while (0)
;
13
Taking false branch
14
Loop condition is false. Exiting loop
201 PointerData* metadata = pointer_data();
202 CHECK_GT(metadata->strong_ptr_count, 0)do { if (__builtin_expect(!!(!((metadata->strong_ptr_count
) > (0))), 0)) { do { static const node::AssertionInfo args
= { "../src/base_object-inl.h" ":" "202", "(metadata->strong_ptr_count) > (0)"
, __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } }
while (0)
;
15
Taking false branch
16
Loop condition is false. Exiting loop
203 unsigned int new_refcount = --metadata->strong_ptr_count;
204 if (new_refcount
16.1
'new_refcount' is equal to 0
16.1
'new_refcount' is equal to 0
== 0) {
17
Taking true branch
205 if (metadata->is_detached) {
18
Assuming field 'is_detached' is true
19
Taking true branch
206 OnGCCollect();
20
Calling 'BaseObject::OnGCCollect'
22
Returning; memory was released
207 } else if (metadata->wants_weak_jsobj && !persistent_handle_.IsEmpty()) {
208 MakeWeak();
209 }
210 }
211}
212
213void BaseObject::increase_refcount() {
214 unsigned int prev_refcount = pointer_data()->strong_ptr_count++;
215 if (prev_refcount == 0 && !persistent_handle_.IsEmpty())
216 persistent_handle_.ClearWeak();
217}
218
219template <typename T, bool kIsWeak>
220BaseObject::PointerData*
221BaseObjectPtrImpl<T, kIsWeak>::pointer_data() const {
222 if (kIsWeak) {
223 return data_.pointer_data;
224 }
225 if (get_base_object() == nullptr) {
226 return nullptr;
227 }
228 return get_base_object()->pointer_data();
229}
230
231template <typename T, bool kIsWeak>
232BaseObject* BaseObjectPtrImpl<T, kIsWeak>::get_base_object() const {
233 if (kIsWeak) {
29
Taking false branch
234 if (pointer_data() == nullptr) {
235 return nullptr;
236 }
237 return pointer_data()->self;
238 }
239 return data_.target;
30
Use of memory after it is freed
240}
241
242template <typename T, bool kIsWeak>
243BaseObjectPtrImpl<T, kIsWeak>::~BaseObjectPtrImpl() {
244 if (kIsWeak) {
10
Taking false branch
245 if (pointer_data() != nullptr &&
246 --pointer_data()->weak_ptr_count == 0 &&
247 pointer_data()->self == nullptr) {
248 delete pointer_data();
249 }
250 } else if (get() != nullptr) {
11
Taking true branch
251 get()->decrease_refcount();
12
Calling 'BaseObject::decrease_refcount'
23
Returning; memory was released
252 }
253}
254
255template <typename T, bool kIsWeak>
256BaseObjectPtrImpl<T, kIsWeak>::BaseObjectPtrImpl() {
257 data_.target = nullptr;
258}
259
260template <typename T, bool kIsWeak>
261BaseObjectPtrImpl<T, kIsWeak>::BaseObjectPtrImpl(T* target)
262 : BaseObjectPtrImpl() {
263 if (target == nullptr) return;
264 if (kIsWeak) {
265 data_.pointer_data = target->pointer_data();
266 CHECK_NOT_NULL(pointer_data())do { if (__builtin_expect(!!(!((pointer_data()) != nullptr)),
0)) { do { static const node::AssertionInfo args = { "../src/base_object-inl.h"
":" "266", "(pointer_data()) != nullptr", __PRETTY_FUNCTION__
}; node::Assert(args); } while (0); } } while (0)
;
267 pointer_data()->weak_ptr_count++;
268 } else {
269 data_.target = target;
270 CHECK_NOT_NULL(pointer_data())do { if (__builtin_expect(!!(!((pointer_data()) != nullptr)),
0)) { do { static const node::AssertionInfo args = { "../src/base_object-inl.h"
":" "270", "(pointer_data()) != nullptr", __PRETTY_FUNCTION__
}; node::Assert(args); } while (0); } } while (0)
;
271 get()->increase_refcount();
272 }
273}
274
275template <typename T, bool kIsWeak>
276template <typename U, bool kW>
277BaseObjectPtrImpl<T, kIsWeak>::BaseObjectPtrImpl(
278 const BaseObjectPtrImpl<U, kW>& other)
279 : BaseObjectPtrImpl(other.get()) {}
280
281template <typename T, bool kIsWeak>
282BaseObjectPtrImpl<T, kIsWeak>::BaseObjectPtrImpl(const BaseObjectPtrImpl& other)
283 : BaseObjectPtrImpl(other.get()) {}
284
285template <typename T, bool kIsWeak>
286template <typename U, bool kW>
287BaseObjectPtrImpl<T, kIsWeak>& BaseObjectPtrImpl<T, kIsWeak>::operator=(
288 const BaseObjectPtrImpl<U, kW>& other) {
289 if (other.get() == get()) return *this;
290 this->~BaseObjectPtrImpl();
291 return *new (this) BaseObjectPtrImpl(other);
292}
293
294template <typename T, bool kIsWeak>
295BaseObjectPtrImpl<T, kIsWeak>& BaseObjectPtrImpl<T, kIsWeak>::operator=(
296 const BaseObjectPtrImpl& other) {
297 if (other.get() == get()) return *this;
298 this->~BaseObjectPtrImpl();
299 return *new (this) BaseObjectPtrImpl(other);
300}
301
302template <typename T, bool kIsWeak>
303BaseObjectPtrImpl<T, kIsWeak>::BaseObjectPtrImpl(BaseObjectPtrImpl&& other)
304 : data_(other.data_) {
305 if (kIsWeak)
306 other.data_.target = nullptr;
307 else
308 other.data_.pointer_data = nullptr;
309}
310
311template <typename T, bool kIsWeak>
312BaseObjectPtrImpl<T, kIsWeak>& BaseObjectPtrImpl<T, kIsWeak>::operator=(
313 BaseObjectPtrImpl&& other) {
314 if (&other == this) return *this;
315 this->~BaseObjectPtrImpl();
316 return *new (this) BaseObjectPtrImpl(std::move(other));
317}
318
319template <typename T, bool kIsWeak>
320void BaseObjectPtrImpl<T, kIsWeak>::reset(T* ptr) {
321 *this = BaseObjectPtrImpl(ptr);
322}
323
324template <typename T, bool kIsWeak>
325T* BaseObjectPtrImpl<T, kIsWeak>::get() const {
326 return static_cast<T*>(get_base_object());
28
Calling 'BaseObjectPtrImpl::get_base_object'
327}
328
329template <typename T, bool kIsWeak>
330T& BaseObjectPtrImpl<T, kIsWeak>::operator*() const {
331 return *get();
332}
333
334template <typename T, bool kIsWeak>
335T* BaseObjectPtrImpl<T, kIsWeak>::operator->() const {
336 return get();
337}
338
339template <typename T, bool kIsWeak>
340BaseObjectPtrImpl<T, kIsWeak>::operator bool() const {
341 return get() != nullptr;
27
Calling 'BaseObjectPtrImpl::get'
342}
343
344template <typename T, bool kIsWeak>
345template <typename U, bool kW>
346bool BaseObjectPtrImpl<T, kIsWeak>::operator ==(
347 const BaseObjectPtrImpl<U, kW>& other) const {
348 return get() == other.get();
349}
350
351template <typename T, bool kIsWeak>
352template <typename U, bool kW>
353bool BaseObjectPtrImpl<T, kIsWeak>::operator !=(
354 const BaseObjectPtrImpl<U, kW>& other) const {
355 return get() != other.get();
356}
357
358template <typename T, typename... Args>
359BaseObjectPtr<T> MakeBaseObject(Args&&... args) {
360 return BaseObjectPtr<T>(new T(std::forward<Args>(args)...));
7
Memory is allocated
361}
362
363template <typename T, typename... Args>
364BaseObjectPtr<T> MakeDetachedBaseObject(Args&&... args) {
365 BaseObjectPtr<T> target = MakeBaseObject<T>(std::forward<Args>(args)...);
366 target->Detach();
367 return target;
368}
369
370} // namespace node
371
372#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
373
374#endif // SRC_BASE_OBJECT_INL_H_