| 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_ |