File: | out/../src/base_object-inl.h |
Warning: | line 239, column 3 Use of memory after it is freed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | ||||
19 | using v8::Array; | |||
20 | using v8::Boolean; | |||
21 | using v8::Context; | |||
22 | using v8::EmbedderGraph; | |||
23 | using v8::EscapableHandleScope; | |||
24 | using v8::FunctionCallbackInfo; | |||
25 | using v8::FunctionTemplate; | |||
26 | using v8::Global; | |||
27 | using v8::HandleScope; | |||
28 | using v8::HeapSnapshot; | |||
29 | using v8::Isolate; | |||
30 | using v8::JustVoid; | |||
31 | using v8::Local; | |||
32 | using v8::Maybe; | |||
33 | using v8::MaybeLocal; | |||
34 | using v8::Nothing; | |||
35 | using v8::Number; | |||
36 | using v8::Object; | |||
37 | using v8::ObjectTemplate; | |||
38 | using v8::String; | |||
39 | using v8::Value; | |||
40 | ||||
41 | namespace node { | |||
42 | namespace heap { | |||
43 | ||||
44 | class 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 | ||||
80 | class 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 | ||||
211 | void 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 | ||||
220 | namespace { | |||
221 | class 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 | ||||
263 | class 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 | ||||
343 | inline 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 | ||||
351 | Maybe<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 | ||||
384 | void DeleteHeapSnapshot(const HeapSnapshot* snapshot) { | |||
385 | const_cast<HeapSnapshot*>(snapshot)->Delete(); | |||
386 | } | |||
387 | ||||
388 | BaseObjectPtr<AsyncWrap> CreateHeapSnapshotStream( | |||
389 | Environment* env, HeapSnapshotPointer&& snapshot) { | |||
390 | HandleScope scope(env->isolate()); | |||
391 | ||||
392 | if (env->streambaseoutputstream_constructor_template().IsEmpty()) { | |||
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() | |||
406 | ->NewInstance(env->context()) | |||
407 | .ToLocal(&obj)) { | |||
408 | return {}; | |||
409 | } | |||
410 | return MakeBaseObject<HeapSnapshotStream>(env, std::move(snapshot), obj); | |||
411 | } | |||
412 | ||||
413 | void 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); | |||
| ||||
418 | BaseObjectPtr<AsyncWrap> stream = | |||
419 | CreateHeapSnapshotStream(env, std::move(snapshot)); | |||
420 | if (stream) | |||
421 | args.GetReturnValue().Set(stream->object()); | |||
422 | } | |||
423 | ||||
424 | void 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 | ||||
447 | void 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 | ||||
458 | void RegisterExternalReferences(ExternalReferenceRegistry* registry) { | |||
459 | registry->Register(BuildEmbedderGraph); | |||
460 | registry->Register(TriggerHeapSnapshot); | |||
461 | registry->Register(CreateHeapSnapshotStream); | |||
462 | } | |||
463 | ||||
464 | } // namespace heap | |||
465 | } // namespace node | |||
466 | ||||
467 | NODE_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); } | |||
468 | NODE_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 ); } |
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 | |||||
33 | namespace node { | ||||
34 | |||||
35 | BaseObject::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 | |||||
46 | BaseObject::~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 | |||||
69 | void 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 | |||||
74 | v8::Global<v8::Object>& BaseObject::persistent() { | ||||
75 | return persistent_handle_; | ||||
76 | } | ||||
77 | |||||
78 | |||||
79 | v8::Local<v8::Object> BaseObject::object() const { | ||||
80 | return PersistentToLocal::Default(env()->isolate(), persistent_handle_); | ||||
81 | } | ||||
82 | |||||
83 | v8::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 | |||||
93 | Environment* BaseObject::env() const { | ||||
94 | return env_; | ||||
95 | } | ||||
96 | |||||
97 | BaseObject* 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 | |||||
105 | template <typename T> | ||||
106 | T* BaseObject::FromJSObject(v8::Local<v8::Value> object) { | ||||
107 | return static_cast<T*>(FromJSObject(object)); | ||||
108 | } | ||||
109 | |||||
110 | |||||
111 | void 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 | |||||
132 | void BaseObject::OnGCCollect() { | ||||
133 | delete this; | ||||
134 | } | ||||
135 | |||||
136 | void BaseObject::ClearWeak() { | ||||
137 | if (has_pointer_data()) | ||||
138 | pointer_data()->wants_weak_jsobj = false; | ||||
139 | |||||
140 | persistent_handle_.ClearWeak(); | ||||
141 | } | ||||
142 | |||||
143 | bool 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 | |||||
151 | void 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 | |||||
158 | v8::Local<v8::FunctionTemplate> | ||||
159 | BaseObject::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 | |||||
168 | template <int Field> | ||||
169 | void 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 | |||||
175 | template <int Field, bool (v8::Value::* typecheck)() const> | ||||
176 | void 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 | |||||
184 | bool BaseObject::has_pointer_data() const { | ||||
185 | return pointer_data_ != nullptr; | ||||
186 | } | ||||
187 | |||||
188 | BaseObject::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 | |||||
199 | void 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); | ||||
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); | ||||
203 | unsigned int new_refcount = --metadata->strong_ptr_count; | ||||
204 | if (new_refcount
| ||||
205 | if (metadata->is_detached) { | ||||
206 | OnGCCollect(); | ||||
207 | } else if (metadata->wants_weak_jsobj && !persistent_handle_.IsEmpty()) { | ||||
208 | MakeWeak(); | ||||
209 | } | ||||
210 | } | ||||
211 | } | ||||
212 | |||||
213 | void 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 | |||||
219 | template <typename T, bool kIsWeak> | ||||
220 | BaseObject::PointerData* | ||||
221 | BaseObjectPtrImpl<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 | |||||
231 | template <typename T, bool kIsWeak> | ||||
232 | BaseObject* BaseObjectPtrImpl<T, kIsWeak>::get_base_object() const { | ||||
233 | if (kIsWeak) { | ||||
234 | if (pointer_data() == nullptr) { | ||||
235 | return nullptr; | ||||
236 | } | ||||
237 | return pointer_data()->self; | ||||
238 | } | ||||
239 | return data_.target; | ||||
| |||||
240 | } | ||||
241 | |||||
242 | template <typename T, bool kIsWeak> | ||||
243 | BaseObjectPtrImpl<T, kIsWeak>::~BaseObjectPtrImpl() { | ||||
244 | if (kIsWeak) { | ||||
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) { | ||||
251 | get()->decrease_refcount(); | ||||
252 | } | ||||
253 | } | ||||
254 | |||||
255 | template <typename T, bool kIsWeak> | ||||
256 | BaseObjectPtrImpl<T, kIsWeak>::BaseObjectPtrImpl() { | ||||
257 | data_.target = nullptr; | ||||
258 | } | ||||
259 | |||||
260 | template <typename T, bool kIsWeak> | ||||
261 | BaseObjectPtrImpl<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 | |||||
275 | template <typename T, bool kIsWeak> | ||||
276 | template <typename U, bool kW> | ||||
277 | BaseObjectPtrImpl<T, kIsWeak>::BaseObjectPtrImpl( | ||||
278 | const BaseObjectPtrImpl<U, kW>& other) | ||||
279 | : BaseObjectPtrImpl(other.get()) {} | ||||
280 | |||||
281 | template <typename T, bool kIsWeak> | ||||
282 | BaseObjectPtrImpl<T, kIsWeak>::BaseObjectPtrImpl(const BaseObjectPtrImpl& other) | ||||
283 | : BaseObjectPtrImpl(other.get()) {} | ||||
284 | |||||
285 | template <typename T, bool kIsWeak> | ||||
286 | template <typename U, bool kW> | ||||
287 | BaseObjectPtrImpl<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 | |||||
294 | template <typename T, bool kIsWeak> | ||||
295 | BaseObjectPtrImpl<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 | |||||
302 | template <typename T, bool kIsWeak> | ||||
303 | BaseObjectPtrImpl<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 | |||||
311 | template <typename T, bool kIsWeak> | ||||
312 | BaseObjectPtrImpl<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 | |||||
319 | template <typename T, bool kIsWeak> | ||||
320 | void BaseObjectPtrImpl<T, kIsWeak>::reset(T* ptr) { | ||||
321 | *this = BaseObjectPtrImpl(ptr); | ||||
322 | } | ||||
323 | |||||
324 | template <typename T, bool kIsWeak> | ||||
325 | T* BaseObjectPtrImpl<T, kIsWeak>::get() const { | ||||
326 | return static_cast<T*>(get_base_object()); | ||||
327 | } | ||||
328 | |||||
329 | template <typename T, bool kIsWeak> | ||||
330 | T& BaseObjectPtrImpl<T, kIsWeak>::operator*() const { | ||||
331 | return *get(); | ||||
332 | } | ||||
333 | |||||
334 | template <typename T, bool kIsWeak> | ||||
335 | T* BaseObjectPtrImpl<T, kIsWeak>::operator->() const { | ||||
336 | return get(); | ||||
337 | } | ||||
338 | |||||
339 | template <typename T, bool kIsWeak> | ||||
340 | BaseObjectPtrImpl<T, kIsWeak>::operator bool() const { | ||||
341 | return get() != nullptr; | ||||
342 | } | ||||
343 | |||||
344 | template <typename T, bool kIsWeak> | ||||
345 | template <typename U, bool kW> | ||||
346 | bool BaseObjectPtrImpl<T, kIsWeak>::operator ==( | ||||
347 | const BaseObjectPtrImpl<U, kW>& other) const { | ||||
348 | return get() == other.get(); | ||||
349 | } | ||||
350 | |||||
351 | template <typename T, bool kIsWeak> | ||||
352 | template <typename U, bool kW> | ||||
353 | bool BaseObjectPtrImpl<T, kIsWeak>::operator !=( | ||||
354 | const BaseObjectPtrImpl<U, kW>& other) const { | ||||
355 | return get() != other.get(); | ||||
356 | } | ||||
357 | |||||
358 | template <typename T, typename... Args> | ||||
359 | BaseObjectPtr<T> MakeBaseObject(Args&&... args) { | ||||
360 | return BaseObjectPtr<T>(new T(std::forward<Args>(args)...)); | ||||
361 | } | ||||
362 | |||||
363 | template <typename T, typename... Args> | ||||
364 | BaseObjectPtr<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_ |