From 0c7e2a59b651b838e1dfc0e0db8f62e2835083a5 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Sat, 2 Dec 2023 18:15:24 +0100 Subject: [PATCH 1/3] src: add utilities for cppgc classes --- node.gyp | 1 + src/cppgc_helpers.h | 81 +++++++++++++++++++++++++++++++++++++++++++++ src/heap_utils.cc | 34 ++++++++++++++----- src/util.h | 24 ++++++++++++++ 4 files changed, 131 insertions(+), 9 deletions(-) create mode 100644 src/cppgc_helpers.h diff --git a/node.gyp b/node.gyp index c69670c49b660d..fb8c02c86dcc83 100644 --- a/node.gyp +++ b/node.gyp @@ -202,6 +202,7 @@ 'src/compile_cache.h', 'src/connect_wrap.h', 'src/connection_wrap.h', + 'src/cppgc_helpers.h', 'src/dataqueue/queue.h', 'src/debug_utils.h', 'src/debug_utils-inl.h', diff --git a/src/cppgc_helpers.h b/src/cppgc_helpers.h new file mode 100644 index 00000000000000..96633d8a014b09 --- /dev/null +++ b/src/cppgc_helpers.h @@ -0,0 +1,81 @@ +#ifndef SRC_CPPGC_HELPERS_H_ +#define SRC_CPPGC_HELPERS_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include // std::remove_reference +#include "cppgc/garbage-collected.h" +#include "cppgc/name-provider.h" +#include "env.h" +#include "memory_tracker.h" +#include "v8-cppgc.h" +#include "v8.h" + +namespace node { + +#define ASSIGN_OR_RETURN_UNWRAP_CPPGC(ptr, obj, ...) \ + do { \ + *ptr = CppgcMixin::Unwrap< \ + typename std::remove_reference::type>(obj); \ + if (*ptr == nullptr) return __VA_ARGS__; \ + } while (0) + +// TODO(joyeecheung): make it a class template? +class CppgcMixin : public cppgc::GarbageCollectedMixin { + public: + enum InternalFields { kEmbedderType = 0, kSlot, kInternalFieldCount }; + + // This must not be a constructor but called in the child class constructor, + // per cppgc::GarbageCollectedMixin rules. + template + void InitializeCppgc(T* ptr, Environment* env, v8::Local obj) { + env_ = env; + traced_reference_ = v8::TracedReference(env->isolate(), obj); + SetCppgcReference(env->isolate(), obj, ptr); + // We are adding this additional slot to that BaseObject and cppgc-managed + // objects share the same layout. + CHECK_GE(obj->InternalFieldCount(), T::kInternalFieldCount); + obj->SetAlignedPointerInInternalField(T::kSlot, ptr); + } + + v8::Local object() const { + return traced_reference_.Get(env_->isolate()); + } + + Environment* env() const { return env_; } + + template + static T* Unwrap(v8::Local obj) { + // We are not using v8::Object::Unwrap currently because that requires + // access to isolate which the ASSIGN_OR_RETURN_UNWRAP macro that we'll shim + // with cppgc-allocated types doesn't take. + if (obj->InternalFieldCount() != T::kInternalFieldCount) { + return nullptr; + } + T* ptr = static_cast(obj->GetAlignedPointerFromInternalField(T::kSlot)); + return ptr; + } + + void Trace(cppgc::Visitor* visitor) const override { + visitor->Trace(traced_reference_); + } + + private: + Environment* env_; + v8::TracedReference traced_reference_; +}; + +#define DEFAULT_CPPGC_TRACE() \ + void Trace(cppgc::Visitor* visitor) const final { \ + CppgcMixin::Trace(visitor); \ + } + +#define SET_CPPGC_NAME(Klass) \ + inline const char* GetHumanReadableName() const final { \ + return "Node / " #Klass; \ + } +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_BASE_OBJECT_H_ diff --git a/src/heap_utils.cc b/src/heap_utils.cc index f55255be5fd428..ef12ae5057615d 100644 --- a/src/heap_utils.cc +++ b/src/heap_utils.cc @@ -21,6 +21,7 @@ using v8::Array; using v8::Boolean; using v8::Context; +using v8::Data; using v8::EmbedderGraph; using v8::EscapableHandleScope; using v8::FunctionCallbackInfo; @@ -50,18 +51,20 @@ class JSGraphJSNode : public EmbedderGraph::Node { const char* Name() override { return ""; } size_t SizeInBytes() override { return 0; } bool IsEmbedderNode() override { return false; } - Local JSValue() { return PersistentToLocal::Strong(persistent_); } + Local V8Value() { return PersistentToLocal::Strong(persistent_); } int IdentityHash() { - Local v = JSValue(); + Local d = V8Value(); + // TODO(joyeecheung): return something better? + if (!d->IsValue()) return reinterpret_cast(this); + Local v = d.As(); if (v->IsObject()) return v.As()->GetIdentityHash(); if (v->IsName()) return v.As()->GetIdentityHash(); if (v->IsInt32()) return v.As()->Value(); return 0; } - JSGraphJSNode(Isolate* isolate, Local val) - : persistent_(isolate, val) { + JSGraphJSNode(Isolate* isolate, Local val) : persistent_(isolate, val) { CHECK(!val.IsEmpty()); } @@ -73,19 +76,27 @@ class JSGraphJSNode : public EmbedderGraph::Node { struct Equal { inline bool operator()(JSGraphJSNode* a, JSGraphJSNode* b) const { - return a->JSValue()->SameValue(b->JSValue()); + Local data_a = a->V8Value(); + Local data_b = a->V8Value(); + if (data_a->IsValue()) { + if (!data_b->IsValue()) { + return false; + } + return data_a.As()->SameValue(data_b.As()); + } + return data_a == data_b; } }; private: - Global persistent_; + Global persistent_; }; class JSGraph : public EmbedderGraph { public: explicit JSGraph(Isolate* isolate) : isolate_(isolate) {} - Node* V8Node(const Local& value) override { + Node* V8Node(const Local& value) override { std::unique_ptr n { new JSGraphJSNode(isolate_, value) }; auto it = engine_nodes_.find(n.get()); if (it != engine_nodes_.end()) @@ -94,6 +105,10 @@ class JSGraph : public EmbedderGraph { return AddNode(std::unique_ptr(n.release())); } + Node* V8Node(const Local& value) override { + return V8Node(value.As()); + } + Node* AddNode(std::unique_ptr node) override { Node* n = node.get(); nodes_.emplace(std::move(node)); @@ -154,8 +169,9 @@ class JSGraph : public EmbedderGraph { if (nodes->Set(context, i++, obj).IsNothing()) return MaybeLocal(); if (!n->IsEmbedderNode()) { - value = static_cast(n.get())->JSValue(); - if (obj->Set(context, value_string, value).IsNothing()) + Local data = static_cast(n.get())->V8Value(); + if (data->IsValue() && + obj->Set(context, value_string, data.As()).IsNothing()) return MaybeLocal(); } } diff --git a/src/util.h b/src/util.h index a3fa79f749d94e..6b460ca592d706 100644 --- a/src/util.h +++ b/src/util.h @@ -970,6 +970,30 @@ void SetConstructorFunction(v8::Isolate* isolate, SetConstructorFunctionFlag flag = SetConstructorFunctionFlag::SET_CLASS_NAME); +#define GET_OR_SET_CONSTRUCTOR_TEMPLATE(tmpl, isolate_data, id, name) \ + do { \ + v8::Isolate* isolate = isolate_data->isolate(); \ + tmpl = isolate_data->id##_constructor_template(); \ + if (tmpl.IsEmpty()) { \ + tmpl = v8::FunctionTemplate::New(isolate, New); \ + tmpl->InstanceTemplate()->SetInternalFieldCount(kInternalFieldCount); \ + tmpl->SetClassName(FIXED_ONE_BYTE_STRING(isolate, #name)); \ + isolate_data->set_##id##_constructor_template(tmpl); \ + } \ + } while (0); + +#define CONSTRUCTOR_TEMPLATE_GENERATOR(id, name) \ + static v8::Local GetConstructorTemplate( \ + IsolateData* isolate_data) { \ + v8::Local tmpl; \ + GET_OR_SET_CONSTRUCTOR_TEMPLATE(tmpl, isolate_data, id, name); \ + return tmpl; \ + } + +#define DECL_CONSTRUCTOR_TEMPLATE_GENERATOR() \ + static v8::Local GetConstructorTemplate( \ + IsolateData* isolate_data); + // Like RAIIIsolate, except doesn't enter the isolate while it's in scope. class RAIIIsolateWithoutEntering { public: From 82846133f161d353b072910b5e3ee4f729b383a2 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Sun, 18 Aug 2024 03:47:01 +0200 Subject: [PATCH 2/3] deps: V8: cherry-pick fa7509f48d15 Original commit message: [cppgc] add cppgc::Visitor::TraceExternal() This allows tracking externally-managed memory owned by cppgc-managed objects in the heap snapshots. To accompany this API, a class cppgc::External is added to abstract over externally-managed structures. Design doc: https://docs.google.com/document/d/1-kHbj9SNL3wMXZz_GZiDw6kHUJ1IvicFzEmqC_um0Wg/edit#heading=h.n1atlriavj6v Change-Id: I7a27eaa45e29d3c14936997d115a112a6e54458c Refs: https://github.com/v8/v8/commit/fa7509f48d15fa13048b2ce972af7eab1a587fd5 --- common.gypi | 2 +- deps/v8/BUILD.bazel | 1 + deps/v8/BUILD.gn | 1 + deps/v8/include/cppgc/external.h | 23 ++ deps/v8/include/cppgc/visitor.h | 8 + deps/v8/src/heap/cppgc-js/cpp-snapshot.cc | 359 ++++++++++++------ .../cppgc-js/unified-heap-marking-visitor.cc | 4 + .../cppgc-js/unified-heap-marking-visitor.h | 1 + deps/v8/src/heap/cppgc/marking-state.h | 13 + deps/v8/src/heap/cppgc/marking-visitor.cc | 4 + deps/v8/src/heap/cppgc/stats-collector.cc | 3 + .../unified-heap-snapshot-unittest.cc | 73 ++++ .../heap/cppgc-js/unified-heap-utils.cc | 5 + .../heap/cppgc-js/unified-heap-utils.h | 2 +- 14 files changed, 383 insertions(+), 116 deletions(-) create mode 100644 deps/v8/include/cppgc/external.h diff --git a/common.gypi b/common.gypi index c72fd626503612..d4c6fb9a8033cb 100644 --- a/common.gypi +++ b/common.gypi @@ -36,7 +36,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.13', + 'v8_embedder_string': '-node.14', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/BUILD.bazel b/deps/v8/BUILD.bazel index 0718b28b052946..4e7b81fdcaa06a 100644 --- a/deps/v8/BUILD.bazel +++ b/deps/v8/BUILD.bazel @@ -567,6 +567,7 @@ filegroup( "include/cppgc/default-platform.h", "include/cppgc/ephemeron-pair.h", "include/cppgc/explicit-management.h", + "include/cppgc/external.h", "include/cppgc/garbage-collected.h", "include/cppgc/heap.h", "include/cppgc/heap-consistency.h", diff --git a/deps/v8/BUILD.gn b/deps/v8/BUILD.gn index 92e8541951384f..28260468f28564 100644 --- a/deps/v8/BUILD.gn +++ b/deps/v8/BUILD.gn @@ -6920,6 +6920,7 @@ v8_header_set("cppgc_headers") { "include/cppgc/default-platform.h", "include/cppgc/ephemeron-pair.h", "include/cppgc/explicit-management.h", + "include/cppgc/external.h", "include/cppgc/garbage-collected.h", "include/cppgc/heap-consistency.h", "include/cppgc/heap-handle.h", diff --git a/deps/v8/include/cppgc/external.h b/deps/v8/include/cppgc/external.h new file mode 100644 index 00000000000000..6b55fc8bc79edb --- /dev/null +++ b/deps/v8/include/cppgc/external.h @@ -0,0 +1,23 @@ +// Copyright 2024 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_EXTERNAL_H_ +#define INCLUDE_CPPGC_EXTERNAL_H_ + +#include + +namespace cppgc { + +class Visitor; + +class External { + public: + virtual void Trace(cppgc::Visitor*) const {} + virtual const char* GetHumanReadableName() const = 0; + virtual size_t GetSize() const = 0; +}; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_EXTERNAL_H_ diff --git a/deps/v8/include/cppgc/visitor.h b/deps/v8/include/cppgc/visitor.h index 1d6b39a14fd16a..584feb4610433b 100644 --- a/deps/v8/include/cppgc/visitor.h +++ b/deps/v8/include/cppgc/visitor.h @@ -22,6 +22,8 @@ namespace cppgc { +class External; + namespace internal { template @@ -306,6 +308,11 @@ class V8_EXPORT Visitor { callback_data); } + /** + * Trace method for members with externally-managed memory. + */ + void TraceExternal(const External* external) { VisitExternal(external); } + /** * Registers a slot containing a reference to an object allocated on a * compactable space. Such references maybe be arbitrarily moved by the GC. @@ -359,6 +366,7 @@ class V8_EXPORT Visitor { virtual void VisitWeakContainer(const void* self, TraceDescriptor strong_desc, TraceDescriptor weak_desc, WeakCallback callback, const void* data) {} + virtual void VisitExternal(const External* external) {} virtual void HandleMovableReference(const void**) {} virtual void VisitMultipleUncompressedMember( diff --git a/deps/v8/src/heap/cppgc-js/cpp-snapshot.cc b/deps/v8/src/heap/cppgc-js/cpp-snapshot.cc index e4054838e26b88..d3185d83b7f626 100644 --- a/deps/v8/src/heap/cppgc-js/cpp-snapshot.cc +++ b/deps/v8/src/heap/cppgc-js/cpp-snapshot.cc @@ -30,15 +30,16 @@ namespace internal { class CppGraphBuilderImpl; class StateStorage; class State; +class ExternalState; using cppgc::internal::HeapObjectHeader; // Node representing a C++ object on the heap. class EmbedderNode : public v8::EmbedderGraph::Node { public: - EmbedderNode(const HeapObjectHeader* header_address, - cppgc::internal::HeapObjectName name, size_t size) - : header_address_(header_address), + EmbedderNode(const void* address, cppgc::internal::HeapObjectName name, + size_t size) + : address_(address), name_(name.value), size_(name.name_was_hidden ? 0 : size) {} ~EmbedderNode() override = default; @@ -75,10 +76,10 @@ class EmbedderNode : public v8::EmbedderGraph::Node { return named_edge_str; } - const void* GetAddress() override { return header_address_; } + const void* GetAddress() override { return address_; } private: - const void* header_address_; + const void* address_; const char* name_; size_t size_; Node* wrapper_node_ = nullptr; @@ -109,12 +110,19 @@ class StateBase { kVisible, }; + enum class Type { + kRegular, + kExternal, + kRoot, + }; + StateBase(const void* key, size_t state_count, Visibility visibility, - EmbedderNode* node, bool visited) + EmbedderNode* node, bool visited, Type type) : key_(key), state_count_(state_count), visibility_(visibility), node_(node), + type_(type), visited_(visited) { DCHECK_NE(Visibility::kDependentVisibility, visibility); } @@ -144,6 +152,61 @@ class StateBase { return node_; } + Type type() const { return type_; } + + void MarkVisited() { visited_ = true; } + + void MarkPending() { pending_ = true; } + void UnmarkPending() { pending_ = false; } + + void MarkVisible() { + visibility_ = Visibility::kVisible; + visibility_dependency_ = nullptr; + } + + void MarkDependentVisibility(StateBase* dependency) { + // Follow and update dependencies as much as possible. + dependency = dependency->FollowDependencies(); + DCHECK(dependency->IsVisited()); + if (visibility_ == StateBase::Visibility::kVisible) { + // Already visible, no dependency needed. + DCHECK_NULL(visibility_dependency_); + return; + } + if (dependency->visibility_ == Visibility::kVisible) { + // Simple case: Dependency is visible. + visibility_ = Visibility::kVisible; + visibility_dependency_ = nullptr; + return; + } + if ((visibility_dependency_ && + (visibility_dependency_->state_count_ > dependency->state_count_)) || + (!visibility_dependency_ && + (state_count_ > dependency->state_count_))) { + // Only update when new state_count_ < original state_count_. This + // ensures that we pick an ancestor as dependency and not a child which + // is guaranteed to converge to an answer. + // + // Dependency is now + // a) either pending with unknown visibility (same call chain), or + // b) not pending and has defined visibility. + // + // It's not possible to point to a state that is not pending but has + // dependent visibility because dependencies are updated to the top-most + // dependency at the beginning of method. + if (dependency->IsPending()) { + visibility_ = Visibility::kDependentVisibility; + visibility_dependency_ = dependency; + } else { + CHECK_NE(Visibility::kDependentVisibility, dependency->visibility_); + if (dependency->visibility_ == Visibility::kVisible) { + visibility_ = Visibility::kVisible; + visibility_dependency_ = nullptr; + } + } + } + } + protected: const void* key_; // State count keeps track of node processing order. It is used to create only @@ -154,6 +217,7 @@ class StateBase { Visibility visibility_; StateBase* visibility_dependency_ = nullptr; EmbedderNode* node_; + Type type_; bool visited_; bool pending_ = false; @@ -197,71 +261,19 @@ class StateBase { } friend class State; + friend class ExternalState; }; class State final : public StateBase { public: State(const HeapObjectHeader& header, size_t state_count) - : StateBase(&header, state_count, Visibility::kHidden, nullptr, false) {} + : StateBase(&header, state_count, Visibility::kHidden, nullptr, false, + Type::kRegular) {} ~State() final = default; const HeapObjectHeader* header() const { return static_cast(key_); } - - void MarkVisited() { visited_ = true; } - - void MarkPending() { pending_ = true; } - void UnmarkPending() { pending_ = false; } - - void MarkVisible() { - visibility_ = Visibility::kVisible; - visibility_dependency_ = nullptr; - } - - void MarkDependentVisibility(StateBase* dependency) { - // Follow and update dependencies as much as possible. - dependency = dependency->FollowDependencies(); - DCHECK(dependency->IsVisited()); - if (visibility_ == StateBase::Visibility::kVisible) { - // Already visible, no dependency needed. - DCHECK_NULL(visibility_dependency_); - return; - } - if (dependency->visibility_ == Visibility::kVisible) { - // Simple case: Dependency is visible. - visibility_ = Visibility::kVisible; - visibility_dependency_ = nullptr; - return; - } - if ((visibility_dependency_ && - (visibility_dependency_->state_count_ > dependency->state_count_)) || - (!visibility_dependency_ && - (state_count_ > dependency->state_count_))) { - // Only update when new state_count_ < original state_count_. This - // ensures that we pick an ancestor as dependency and not a child which - // is guaranteed to converge to an answer. - // - // Dependency is now - // a) either pending with unknown visibility (same call chain), or - // b) not pending and has defined visibility. - // - // It's not possible to point to a state that is not pending but has - // dependent visibility because dependencies are updated to the top-most - // dependency at the beginning of method. - if (dependency->IsPending()) { - visibility_ = Visibility::kDependentVisibility; - visibility_dependency_ = dependency; - } else { - CHECK_NE(Visibility::kDependentVisibility, dependency->visibility_); - if (dependency->visibility_ == Visibility::kVisible) { - visibility_ = Visibility::kVisible; - visibility_dependency_ = nullptr; - } - } - } - } - void MarkAsWeakContainer() { is_weak_container_ = true; } bool IsWeakContainer() const { return is_weak_container_; } @@ -324,10 +336,23 @@ class RootState final : public StateBase { public: RootState(EmbedderRootNode* node, size_t state_count) // Root states are always visited, visible, and have a node attached. - : StateBase(node, state_count, Visibility::kVisible, node, true) {} + : StateBase(node, state_count, Visibility::kVisible, node, true, + Type::kRoot) {} ~RootState() final = default; }; +class ExternalState final : public StateBase { + public: + ExternalState(const cppgc::External* external, size_t state_count) + : StateBase(external, state_count, Visibility::kVisible, nullptr, false, + Type::kExternal) {} + ~ExternalState() final = default; + + const cppgc::External* external() const { + return static_cast(key_); + } +}; + // Abstraction for storing states. Storage allows for creation and lookup of // different state objects. class StateStorage final { @@ -355,6 +380,16 @@ class StateStorage final { return GetExistingState(header); } + ExternalState& GetOrCreateExternalState(const cppgc::External* ref) { + if (!StateExists(ref)) { + auto it = states_.insert(std::make_pair( + ref, std::make_unique(ref, ++state_count_))); + DCHECK(it.second); + USE(it); + } + return static_cast(GetExistingState(ref)); + } + RootState& CreateRootState(EmbedderRootNode* root_node) { CHECK(!StateExists(root_node)); auto it = states_.insert(std::make_pair( @@ -445,10 +480,11 @@ class CppGraphBuilderImpl final { void Run(); - void VisitForVisibility(State* parent, const HeapObjectHeader&); - void VisitForVisibility(State& parent, const TracedReferenceBase&); + void VisitForVisibility(StateBase* parent, const HeapObjectHeader&); + void VisitForVisibility(StateBase& parent, const TracedReferenceBase&); void VisitEphemeronForVisibility(const HeapObjectHeader& key, const HeapObjectHeader& value); + void VisitExternalForVisibility(const cppgc::External* ref); void VisitEphemeronWithNonGarbageCollectedValueForVisibility( const HeapObjectHeader& key, const void* value, cppgc::TraceDescriptor value_desc); @@ -471,7 +507,52 @@ class CppGraphBuilderImpl final { &header, header.GetName(), header.AllocatedSize())})); } - void AddEdge(State& parent, const HeapObjectHeader& header, + EmbedderNode* AddNode(const cppgc::External* external) { + return static_cast(graph_.AddNode( + std::unique_ptr(new EmbedderNode( + external, {external->GetHumanReadableName(), false}, + external->GetSize())))); + } + + EmbedderNode* GetOrCreateNode(StateBase& state) { + EmbedderNode* node = state.get_node(); + if (node) { + return node; + } + switch (state.type()) { + case StateBase::Type::kRegular: { + node = AddNode(*static_cast(state).header()); + break; + } + case StateBase::Type::kExternal: { + node = AddNode(static_cast(state).external()); + break; + } + case StateBase::Type::kRoot: { + UNREACHABLE(); // Root nodes should always have node already creaetd. + } + } + state.set_node(node); + return node; + } + + void AddEdge(StateBase& parent, const cppgc::External* ref, + const std::string& edge_name) { + DCHECK(parent.IsVisibleNotDependent()); + GetOrCreateNode(parent); + auto& current = states_.GetOrCreateExternalState(ref); + EmbedderNode* child = GetOrCreateNode(current); + + if (!edge_name.empty()) { + graph_.AddEdge(parent.get_node(), child, + parent.get_node()->InternalizeEdgeName(edge_name)); + } else { + graph_.AddEdge(parent.get_node(), child); + } + } + + // TODO(joyee): accept StateBase to handle external parent? + void AddEdge(StateBase& parent, const HeapObjectHeader& header, const std::string& edge_name) { DCHECK(parent.IsVisibleNotDependent()); auto& current = states_.GetExistingState(header); @@ -479,12 +560,8 @@ class CppGraphBuilderImpl final { // Both states are visible. Create nodes in case this is the first edge // created for any of them. - if (!parent.get_node()) { - parent.set_node(AddNode(*parent.header())); - } - if (!current.get_node()) { - current.set_node(AddNode(header)); - } + GetOrCreateNode(parent); + GetOrCreateNode(current); if (!edge_name.empty()) { graph_.AddEdge(parent.get_node(), current.get_node(), @@ -494,16 +571,15 @@ class CppGraphBuilderImpl final { } } - void AddEdge(State& parent, const TracedReferenceBase& ref, + // TODO(joyee): this shouldn't accept ExternalState as parent? + void AddEdge(StateBase& parent, const TracedReferenceBase& ref, const std::string& edge_name) { DCHECK(parent.IsVisibleNotDependent()); v8::Local v8_data = ref.Get(reinterpret_cast(cpp_heap_.isolate())); if (v8_data.IsEmpty()) return; - if (!parent.get_node()) { - parent.set_node(AddNode(*parent.header())); - } + GetOrCreateNode(parent); auto* v8_node = graph_.V8Node(v8_data); if (!edge_name.empty()) { graph_.AddEdge(parent.get_node(), v8_node, @@ -529,7 +605,9 @@ class CppGraphBuilderImpl final { // If the back reference doesn't point to the same header, just return. In // such a case we have stand-alone references to a wrapper. - if (parent.header() != back_state.header()) return; + if (parent.type() == StateBase::Type::kRegular) { + if (static_cast(parent).header() != back_state.header()) return; + } // Back reference points to parents header. In this case, the nodes should // be merged and query the detachedness state of the embedder. @@ -605,9 +683,20 @@ class ParentScope final { explicit ParentScope(StateBase& parent) : parent_(parent) {} RootState& ParentAsRootState() const { + DCHECK_EQ(parent_.type(), StateBase::Type::kRoot); return static_cast(parent_); } - State& ParentAsRegularState() const { return static_cast(parent_); } + State& ParentAsRegularState() const { + DCHECK_EQ(parent_.type(), StateBase::Type::kRegular); + return static_cast(parent_); + } + + ExternalState& ParentAExternalState() const { + DCHECK_EQ(parent_.type(), StateBase::Type::kExternal); + return static_cast(parent_); + } + + StateBase& parent() const { return parent_; } private: StateBase& parent_; @@ -673,6 +762,10 @@ class WeakVisitor : public JSVisitor { key_header, HeapObjectHeader::FromObject(value)); } + void VisitExternal(const cppgc::External* external) final { + graph_builder_.VisitExternalForVisibility(external); + } + protected: class WeakContainerScope { public: @@ -706,14 +799,13 @@ class VisiblityVisitor final : public WeakVisitor { // C++ handling. void Visit(const void*, cppgc::TraceDescriptor desc) final { graph_builder_.VisitForVisibility( - &parent_scope_.ParentAsRegularState(), + &parent_scope_.parent(), HeapObjectHeader::FromObject(desc.base_object_payload)); } // JS handling. void Visit(const TracedReferenceBase& ref) final { - graph_builder_.VisitForVisibility(parent_scope_.ParentAsRegularState(), - ref); + graph_builder_.VisitForVisibility(parent_scope_.parent(), ref); } private: @@ -749,7 +841,7 @@ class GraphBuildingVisitor final : public JSVisitor { // C++ handling. void Visit(const void*, cppgc::TraceDescriptor desc) final { graph_builder_.AddEdge( - parent_scope_.ParentAsRegularState(), + parent_scope_.parent(), HeapObjectHeader::FromObject(desc.base_object_payload), edge_name_); } void VisitWeakContainer(const void* object, @@ -759,15 +851,19 @@ class GraphBuildingVisitor final : public JSVisitor { // Add an edge from the object holding the weak container to the weak // container itself. graph_builder_.AddEdge( - parent_scope_.ParentAsRegularState(), + parent_scope_.parent(), HeapObjectHeader::FromObject(strong_desc.base_object_payload), edge_name_); } // JS handling. void Visit(const TracedReferenceBase& ref) final { - graph_builder_.AddEdge(parent_scope_.ParentAsRegularState(), ref, - edge_name_); + graph_builder_.AddEdge(parent_scope_.parent(), ref, edge_name_); + } + + // External handling. + void VisitExternal(const cppgc::External* ref) final { + graph_builder_.AddEdge(parent_scope_.parent(), ref, edge_name_); } void set_edge_name(std::string edge_name) { @@ -784,15 +880,15 @@ class GraphBuildingVisitor final : public JSVisitor { // in stack fashion. class CppGraphBuilderImpl::WorkstackItemBase { public: - WorkstackItemBase(State* parent, State& current) + WorkstackItemBase(StateBase* parent, StateBase& current) : parent_(parent), current_(current) {} virtual ~WorkstackItemBase() = default; virtual void Process(CppGraphBuilderImpl&) = 0; protected: - State* parent_; - State& current_; + StateBase* parent_; + StateBase& current_; }; void CppGraphBuilderImpl::ProcessPendingObjects() { @@ -807,7 +903,7 @@ void CppGraphBuilderImpl::ProcessPendingObjects() { // been processed first. class CppGraphBuilderImpl::VisitationDoneItem final : public WorkstackItemBase { public: - VisitationDoneItem(State* parent, State& current) + VisitationDoneItem(StateBase* parent, StateBase& current) : WorkstackItemBase(parent, current) {} void Process(CppGraphBuilderImpl& graph_builder) final { @@ -819,7 +915,7 @@ class CppGraphBuilderImpl::VisitationDoneItem final : public WorkstackItemBase { class CppGraphBuilderImpl::VisitationItem final : public WorkstackItemBase { public: - VisitationItem(State* parent, State& current) + VisitationItem(StateBase* parent, StateBase& current) : WorkstackItemBase(parent, current) {} void Process(CppGraphBuilderImpl& graph_builder) final { @@ -831,9 +927,16 @@ class CppGraphBuilderImpl::VisitationItem final : public WorkstackItemBase { } ParentScope parent_scope(current_); VisiblityVisitor object_visitor(graph_builder, parent_scope); - if (!current_.header()->IsInConstruction()) { - // TODO(mlippautz): Handle in construction objects. - current_.header()->Trace(&object_visitor); + if (current_.type() == StateBase::Type::kRegular) { + auto& current = static_cast(current_); + if (!current.header()->IsInConstruction()) { + // TODO(mlippautz): Handle in construction objects. + current.header()->Trace(&object_visitor); + } + } else { + DCHECK_EQ(current_.type(), StateBase::Type::kExternal); + auto& current = static_cast(current_); + current.external()->Trace(&object_visitor); } if (!parent_) { current_.UnmarkPending(); @@ -841,7 +944,20 @@ class CppGraphBuilderImpl::VisitationItem final : public WorkstackItemBase { } }; -void CppGraphBuilderImpl::VisitForVisibility(State* parent, +void CppGraphBuilderImpl::VisitExternalForVisibility( + const cppgc::External* ref) { + auto& current = states_.GetOrCreateExternalState(ref); + if (current.IsVisited()) { + return; + } + current.MarkVisited(); + // If we reach here, parent is visible. + current.MarkVisible(); + WeakVisitor weak_visitor(*this); + ref->Trace(&weak_visitor); +} + +void CppGraphBuilderImpl::VisitForVisibility(StateBase* parent, const HeapObjectHeader& header) { auto& current = states_.GetOrCreateState(header); @@ -920,7 +1036,7 @@ void CppGraphBuilderImpl::AddConservativeEphemeronKeyEdgesIfNeeded( }); } -void CppGraphBuilderImpl::VisitForVisibility(State& parent, +void CppGraphBuilderImpl::VisitForVisibility(StateBase& parent, const TracedReferenceBase& ref) { v8::Local v8_value = ref.Get(reinterpret_cast(cpp_heap_.isolate())); @@ -995,29 +1111,44 @@ void CppGraphBuilderImpl::Run() { visitor.Traverse(cpp_heap_.raw_heap()); // Second pass: Add graph nodes for objects that must be shown. states_.ForAllVisibleStates([this](StateBase* state_base) { - // No roots have been created so far, so all StateBase objects are State. - State& state = *static_cast(state_base); - - // Emit no edges for the contents of the weak containers. For both, fully - // weak and ephemeron containers, the contents should be retained from - // somewhere else. - if (state.IsWeakContainer()) return; + StateBase::Type type = state_base->type(); + // No roots have been created so far. + CHECK_NE(type, StateBase::Type::kRoot); + if (type == StateBase::Type::kRegular) { + State& state = *static_cast(state_base); + + // Emit no edges for the contents of the weak containers. For both, fully + // weak and ephemeron containers, the contents should be retained from + // somewhere else. + if (state.IsWeakContainer()) return; + } - ParentScope parent_scope(state); + ParentScope parent_scope(*state_base); GraphBuildingVisitor object_visitor(*this, parent_scope); - if (!state.header()->IsInConstruction()) { - // TODO(mlippautz): Handle in-construction objects. - state.header()->Trace(&object_visitor); + + if (type == StateBase::Type::kRegular) { + State& state = *static_cast(state_base); + if (!state.header()->IsInConstruction()) { + // TODO(mlippautz): Handle in-construction objects. + state.header()->Trace(&object_visitor); + } + state.ForAllEphemeronEdges([this, &state](const HeapObjectHeader& value) { + AddEdge(state, value, "part of key -> value pair in ephemeron table"); + }); + object_visitor.set_edge_name( + "part of key -> value pair in ephemeron table"); + state.ForAllEagerEphemeronEdges( + [&object_visitor](const void* value, cppgc::TraceCallback callback) { + callback(&object_visitor, value); + }); + } else { + DCHECK_EQ(type, StateBase::Type::kExternal); + // TODO(joyeecheung): the visitor for externals may be limited in what + // it can trace. + static_cast(state_base) + ->external() + ->Trace(&object_visitor); } - state.ForAllEphemeronEdges([this, &state](const HeapObjectHeader& value) { - AddEdge(state, value, "part of key -> value pair in ephemeron table"); - }); - object_visitor.set_edge_name( - "part of key -> value pair in ephemeron table"); - state.ForAllEagerEphemeronEdges( - [&object_visitor](const void* value, cppgc::TraceCallback callback) { - callback(&object_visitor, value); - }); }); // Add roots. { diff --git a/deps/v8/src/heap/cppgc-js/unified-heap-marking-visitor.cc b/deps/v8/src/heap/cppgc-js/unified-heap-marking-visitor.cc index 454cd7726c40c3..042a1c7c656bd8 100644 --- a/deps/v8/src/heap/cppgc-js/unified-heap-marking-visitor.cc +++ b/deps/v8/src/heap/cppgc-js/unified-heap-marking-visitor.cc @@ -89,6 +89,10 @@ void UnifiedHeapMarkingVisitorBase::VisitEphemeron(const void* key, marking_state_.ProcessEphemeron(key, value, value_desc, *this); } +void UnifiedHeapMarkingVisitorBase::VisitExternal(const cppgc::External* ref) { + marking_state_.AccountCppgcExternal(ref); +} + void UnifiedHeapMarkingVisitorBase::VisitWeakContainer( const void* self, TraceDescriptor strong_desc, TraceDescriptor weak_desc, WeakCallback callback, const void* data) { diff --git a/deps/v8/src/heap/cppgc-js/unified-heap-marking-visitor.h b/deps/v8/src/heap/cppgc-js/unified-heap-marking-visitor.h index 38ab4adbb83d5e..9f887e34e266e9 100644 --- a/deps/v8/src/heap/cppgc-js/unified-heap-marking-visitor.h +++ b/deps/v8/src/heap/cppgc-js/unified-heap-marking-visitor.h @@ -51,6 +51,7 @@ class V8_EXPORT_PRIVATE UnifiedHeapMarkingVisitorBase : public JSVisitor { #endif // defined(CPPGC_POINTER_COMPRESSION) void VisitWeak(const void*, TraceDescriptor, WeakCallback, const void*) final; void VisitEphemeron(const void*, const void*, TraceDescriptor) final; + void VisitExternal(const cppgc::External* ref) final; void VisitWeakContainer(const void* self, TraceDescriptor strong_desc, TraceDescriptor weak_desc, WeakCallback callback, const void* data) final; diff --git a/deps/v8/src/heap/cppgc/marking-state.h b/deps/v8/src/heap/cppgc/marking-state.h index d3533274ba725b..65288aa51cd52f 100644 --- a/deps/v8/src/heap/cppgc/marking-state.h +++ b/deps/v8/src/heap/cppgc/marking-state.h @@ -7,6 +7,7 @@ #include +#include "include/cppgc/external.h" #include "include/cppgc/trace-trait.h" #include "include/cppgc/visitor.h" #include "src/base/logging.h" @@ -147,6 +148,11 @@ class BasicMarkingState : public MarkingStateBase { inline void AccountMarkedBytes(BasePage*, size_t); size_t marked_bytes() const { return marked_bytes_; } + inline void AccountCppgcExternal(const cppgc::External* ref); + size_t externally_managed_bytes_from_cppgc() { + return externally_managed_bytes_from_cppgc_; + } + V8_EXPORT_PRIVATE void Publish() override; MarkingWorklists::PreviouslyNotFullyConstructedWorklist::Local& @@ -224,6 +230,7 @@ class BasicMarkingState : public MarkingStateBase { movable_slots_worklist_; size_t marked_bytes_ = 0; + size_t externally_managed_bytes_from_cppgc_ = 0; bool in_ephemeron_processing_ = false; bool discovered_new_ephemeron_pairs_ = false; bool in_atomic_pause_ = false; @@ -342,6 +349,12 @@ void BasicMarkingState::AccountMarkedBytes(const HeapObjectHeader& header) { AccountMarkedBytes(base_page, marked_bytes); } +void BasicMarkingState::AccountCppgcExternal(const cppgc::External* ref) { + // TODO(joyee): maybe track the last reported size in cppgc::External so + // that we can compute the delta on the go? + externally_managed_bytes_from_cppgc_ += ref->GetSize(); +} + void BasicMarkingState::AccountMarkedBytes(BasePage* base_page, size_t marked_bytes) { marked_bytes_ += marked_bytes; diff --git a/deps/v8/src/heap/cppgc/marking-visitor.cc b/deps/v8/src/heap/cppgc/marking-visitor.cc index db8e5338783c5a..923394ff80f0dd 100644 --- a/deps/v8/src/heap/cppgc/marking-visitor.cc +++ b/deps/v8/src/heap/cppgc/marking-visitor.cc @@ -67,6 +67,10 @@ void MarkingVisitorBase::VisitEphemeron(const void* key, const void* value, marking_state_.ProcessEphemeron(key, value, value_desc, *this); } +// void MarkingVisitorBase::VisitExternal(const cppgc::External* ref) { +// marking_state_.AccountCppgcExternal(ref); +// } + void MarkingVisitorBase::VisitWeakContainer(const void* object, TraceDescriptor strong_desc, TraceDescriptor weak_desc, diff --git a/deps/v8/src/heap/cppgc/stats-collector.cc b/deps/v8/src/heap/cppgc/stats-collector.cc index ccad82c81d65e7..e08181f71ac611 100644 --- a/deps/v8/src/heap/cppgc/stats-collector.cc +++ b/deps/v8/src/heap/cppgc/stats-collector.cc @@ -126,6 +126,9 @@ void StatsCollector::NotifyMarkingStarted(CollectionType collection_type, } void StatsCollector::NotifyMarkingCompleted(size_t marked_bytes) { + // TODO(joyee): track externally managed bytes by cppgc here? + // It's not clear whether keeping a total per marking cycle is + // accurate enough for adding it into ExternalMemoryAccounting. DCHECK_EQ(GarbageCollectionState::kMarking, gc_state_); gc_state_ = GarbageCollectionState::kSweeping; current_.marked_bytes = marked_bytes; diff --git a/deps/v8/test/unittests/heap/cppgc-js/unified-heap-snapshot-unittest.cc b/deps/v8/test/unittests/heap/cppgc-js/unified-heap-snapshot-unittest.cc index e2a6eaef6650a5..ec493f7b96361f 100644 --- a/deps/v8/test/unittests/heap/cppgc-js/unified-heap-snapshot-unittest.cc +++ b/deps/v8/test/unittests/heap/cppgc-js/unified-heap-snapshot-unittest.cc @@ -823,5 +823,78 @@ TEST_F(UnifiedHeapSnapshotTest, DynamicName) { ContainsRetainingPath(*snapshot, {kExpectedCppRootsName, "static name"})); } +namespace { + +struct ExternalBytes : public cppgc::External { + explicit ExternalBytes(size_t size) : data(new char[size]), data_size(size) {} + ~ExternalBytes() { delete[] data; } + + virtual size_t GetSize() const { return data_size; } + virtual const char* GetHumanReadableName() const { return "ExternalBytes"; } + virtual void Trace(cppgc::Visitor* v) const {} + + char* data = nullptr; + size_t data_size = 0; +}; + +struct GCedWithExternal : public cppgc::GarbageCollected, + public cppgc::NameProvider { + public: + explicit GCedWithExternal(size_t size) : bytes_(size) {} + + virtual void Trace(cppgc::Visitor* v) const { v->TraceExternal(&bytes_); } + + const char* GetHumanReadableName() const final { return "GCedWithExternal"; } + + ExternalBytes bytes_; +}; + +std::vector FindEdges(HeapEntry* parent, + const char* child_type_name) { + std::vector results; + for (int i = 0; i < parent->children_count(); i++) { + HeapGraphEdge* edge = parent->child(i); + if (0 == strcmp(edge->to()->name(), child_type_name)) { + results.push_back(edge); + } + } + return results; +} +} // namespace + +TEST_F(UnifiedHeapSnapshotTest, ExternalMember) { + v8::Isolate* isolate = v8_isolate(); + JsTestingScope testing_scope(isolate); + + cppgc::Persistent ref = + cppgc::MakeGarbageCollected( + isolate->GetCppHeap()->GetAllocationHandle(), 10); + + const v8::HeapSnapshot* snapshot = TakeHeapSnapshot(); + EXPECT_TRUE(IsValidSnapshot(snapshot)); + + const HeapSnapshot* i_snapshot = + reinterpret_cast(snapshot); + auto cpp_roots = FindEdges(i_snapshot->root(), kExpectedCppRootsName); + CHECK(!cpp_roots.empty()); + + std::vector ref_edges; + for (auto edge : cpp_roots) { + std::vector results = + FindEdges(edge->to(), ref->GetHumanReadableName()); + ref_edges.insert(ref_edges.end(), results.begin(), results.end()); + } + + CHECK_EQ(1, ref_edges.size()); + HeapEntry* wrap_entry = ref_edges[0]->to(); + + std::vector external_edges = + FindEdges(wrap_entry, "ExternalBytes"); + CHECK_EQ(1, external_edges.size()); + HeapGraphEdge* edge = external_edges[0]; + HeapEntry* external_entry = edge->to(); + CHECK_EQ(10, external_entry->self_size()); +} + } // namespace internal } // namespace v8 diff --git a/deps/v8/test/unittests/heap/cppgc-js/unified-heap-utils.cc b/deps/v8/test/unittests/heap/cppgc-js/unified-heap-utils.cc index 32223d3860a926..cf55b5f2a67b3f 100644 --- a/deps/v8/test/unittests/heap/cppgc-js/unified-heap-utils.cc +++ b/deps/v8/test/unittests/heap/cppgc-js/unified-heap-utils.cc @@ -31,6 +31,11 @@ UnifiedHeapTest::UnifiedHeapTest( isolate()->heap()->AttachCppHeap(cpp_heap_.get()); } +UnifiedHeapTest::~UnifiedHeapTest() { + isolate()->heap()->DetachCppHeap(); + cpp_heap_->Terminate(); +} + void UnifiedHeapTest::CollectGarbageWithEmbedderStack( cppgc::Heap::SweepingType sweeping_type) { EmbedderStackStateScope stack_scope( diff --git a/deps/v8/test/unittests/heap/cppgc-js/unified-heap-utils.h b/deps/v8/test/unittests/heap/cppgc-js/unified-heap-utils.h index 1775fb4bb919d0..749475dddb0769 100644 --- a/deps/v8/test/unittests/heap/cppgc-js/unified-heap-utils.h +++ b/deps/v8/test/unittests/heap/cppgc-js/unified-heap-utils.h @@ -24,7 +24,7 @@ class UnifiedHeapTest : public TestWithHeapInternalsAndContext { UnifiedHeapTest(); explicit UnifiedHeapTest( std::vector>); - ~UnifiedHeapTest() override = default; + ~UnifiedHeapTest() override; void CollectGarbageWithEmbedderStack(cppgc::Heap::SweepingType sweeping_type = cppgc::Heap::SweepingType::kAtomic); From 0067a0bf8bc8b68870e1e020080af1e116f43094 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Sat, 2 Dec 2023 19:25:18 +0100 Subject: [PATCH 3/3] crypto: use cppgc to manage Hash --- src/crypto/crypto_hash.cc | 20 ++++++++++++++------ src/crypto/crypto_hash.h | 36 +++++++++++++++++++++++++++--------- src/crypto/crypto_util.h | 17 +++++++++++++++-- 3 files changed, 56 insertions(+), 17 deletions(-) diff --git a/src/crypto/crypto_hash.cc b/src/crypto/crypto_hash.cc index 09ed200299646a..e0bf7604092b91 100644 --- a/src/crypto/crypto_hash.cc +++ b/src/crypto/crypto_hash.cc @@ -1,6 +1,7 @@ #include "crypto/crypto_hash.h" #include "async_wrap-inl.h" #include "base_object-inl.h" +#include "cppgc/allocation.h" #include "env-inl.h" #include "memory_tracker-inl.h" #include "string_bytes.h" @@ -26,14 +27,20 @@ using v8::Object; using v8::Uint32; using v8::Value; +#ifdef ASSIGN_OR_RETURN_UNWRAP +#undef ASSIGN_OR_RETURN_UNWRAP +#endif + +#define ASSIGN_OR_RETURN_UNWRAP ASSIGN_OR_RETURN_UNWRAP_CPPGC namespace crypto { -Hash::Hash(Environment* env, Local wrap) : BaseObject(env, wrap) { - MakeWeak(); +Hash::Hash(Environment* env, Local wrap) { + InitializeCppgc(this, env, wrap); } -void Hash::MemoryInfo(MemoryTracker* tracker) const { - tracker->TrackFieldWithSize("mdctx", mdctx_ ? kSizeOf_EVP_MD_CTX : 0); - tracker->TrackFieldWithSize("md", digest_ ? md_len_ : 0); +void Hash::Trace(cppgc::Visitor* visitor) const { + CppgcMixin::Trace(visitor); + visitor->TraceExternal(&mdctx_); + visitor->TraceExternal(&digest_); } #if OPENSSL_VERSION_MAJOR >= 3 @@ -321,7 +328,8 @@ void Hash::New(const FunctionCallbackInfo& args) { xof_md_len = Just(args[1].As()->Value()); } - Hash* hash = new Hash(env, args.This()); + Hash* hash = cppgc::MakeGarbageCollected( + env->isolate()->GetCppHeap()->GetAllocationHandle(), env, args.This()); if (md == nullptr || !hash->HashInit(md, xof_md_len)) { return ThrowCryptoError(env, ERR_get_error(), "Digest method not supported"); diff --git a/src/crypto/crypto_hash.h b/src/crypto/crypto_hash.h index 07e3a2ae4635b8..1883901c831caf 100644 --- a/src/crypto/crypto_hash.h +++ b/src/crypto/crypto_hash.h @@ -3,23 +3,41 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#include "base_object.h" +#include "cppgc_helpers.h" #include "crypto/crypto_keys.h" #include "crypto/crypto_util.h" #include "env.h" #include "memory_tracker.h" #include "v8.h" +#include "cppgc/external.h" namespace node { namespace crypto { -class Hash final : public BaseObject { + +class ExternalEVPCtx final : public cppgc::External { + public: + virtual size_t GetSize() const override { + return ptr_ ? kSizeOf_EVP_MD_CTX : 0; + } + virtual const char* GetHumanReadableName() const override { return "EVP_MD_CTX"; } + virtual void Trace(cppgc::Visitor* v) const override {} + + EVP_MD_CTX* get() const { return ptr_.get(); } + void reset(EVP_MD_CTX* ptr = nullptr) { ptr_.reset(ptr); } + explicit operator bool() const { return !!ptr_; } + + private: + EVPMDCtxPointer ptr_{}; +}; + +class Hash final : public cppgc::GarbageCollected, + public cppgc::NameProvider, + public CppgcMixin { public: static void Initialize(Environment* env, v8::Local target); static void RegisterExternalReferences(ExternalReferenceRegistry* registry); - - void MemoryInfo(MemoryTracker* tracker) const override; - SET_MEMORY_INFO_NAME(Hash) - SET_SELF_SIZE(Hash) + const char* GetHumanReadableName() const final { return "Node / Hash"; } + void Trace(cppgc::Visitor* visitor) const final; bool HashInit(const EVP_MD* md, v8::Maybe xof_md_len); bool HashUpdate(const char* data, size_t len); @@ -28,15 +46,15 @@ class Hash final : public BaseObject { static void GetCachedAliases(const v8::FunctionCallbackInfo& args); static void OneShotDigest(const v8::FunctionCallbackInfo& args); + Hash(Environment* env, v8::Local wrap); + protected: static void New(const v8::FunctionCallbackInfo& args); static void HashUpdate(const v8::FunctionCallbackInfo& args); static void HashDigest(const v8::FunctionCallbackInfo& args); - Hash(Environment* env, v8::Local wrap); - private: - EVPMDCtxPointer mdctx_{}; + ExternalEVPCtx mdctx_{}; unsigned int md_len_ = 0; ByteSource digest_; }; diff --git a/src/crypto/crypto_util.h b/src/crypto/crypto_util.h index 680c30f42ab727..7f9eaef23adf53 100644 --- a/src/crypto/crypto_util.h +++ b/src/crypto/crypto_util.h @@ -4,6 +4,7 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include "async_wrap.h" +#include "cppgc_helpers.h" #include "env.h" #include "node_errors.h" #include "node_external_reference.h" @@ -11,6 +12,7 @@ #include "string_bytes.h" #include "util.h" #include "v8.h" +#include "cppgc/external.h" #include "ncrypto.h" @@ -101,7 +103,12 @@ void Decode(const v8::FunctionCallbackInfo& args, void (*callback)(T*, const v8::FunctionCallbackInfo&, const char*, size_t)) { T* ctx; - ASSIGN_OR_RETURN_UNWRAP(&ctx, args.This()); + if constexpr (std::is_base_of_v) { + ASSIGN_OR_RETURN_UNWRAP(&ctx, args.This()); + } else { + ctx = CppgcMixin::Unwrap(args.This()); + if (ctx == nullptr) return; + } if (args[0]->IsString()) { StringBytes::InlineDecoder decoder; @@ -192,7 +199,7 @@ T* MallocOpenSSL(size_t count) { // A helper class representing a read-only byte array. When deallocated, its // contents are zeroed. -class ByteSource { +class ByteSource : public cppgc::External { public: class Builder { public: @@ -307,6 +314,12 @@ class ByteSource { static ByteSource FromSecretKeyBytes( Environment* env, v8::Local value); + virtual size_t GetSize() const { + return size_; + } + virtual const char* GetHumanReadableName() const { return "Node / ByteSource"; } + virtual void Trace(cppgc::Visitor* v) const {} + private: const void* data_ = nullptr; void* allocated_data_ = nullptr;