mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
PR-URL: https://github.com/nodejs/node/pull/59805 Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: Richard Lau <richard.lau@ibm.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
163 lines
6.4 KiB
C++
163 lines
6.4 KiB
C++
#ifndef SRC_CPPGC_HELPERS_H_
|
|
#define SRC_CPPGC_HELPERS_H_
|
|
|
|
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
|
|
|
#include <type_traits> // std::remove_reference
|
|
#include "cppgc/garbage-collected.h"
|
|
#include "cppgc/name-provider.h"
|
|
#include "cppgc/persistent.h"
|
|
#include "memory_tracker.h"
|
|
#include "util.h"
|
|
#include "v8-cppgc.h"
|
|
#include "v8-sandbox.h"
|
|
#include "v8.h"
|
|
|
|
namespace node {
|
|
|
|
class Environment;
|
|
class Realm;
|
|
class CppgcWrapperListNode;
|
|
|
|
/**
|
|
* This is a helper mixin with a BaseObject-like interface to help
|
|
* implementing wrapper objects managed by V8's cppgc (Oilpan) library.
|
|
* cppgc-manged objects in Node.js internals should extend this mixin,
|
|
* while non-cppgc-managed objects typically extend BaseObject - the
|
|
* latter are being migrated to be cppgc-managed wherever it's beneficial
|
|
* and practical. Typically cppgc-managed objects are more efficient to
|
|
* keep track of (which lowers initialization cost) and work better
|
|
* with V8's GC scheduling.
|
|
*
|
|
* A cppgc-managed native wrapper should look something like this, note
|
|
* that per cppgc rules, CPPGC_MIXIN(MyWrap) must be at the left-most
|
|
* position in the hierarchy (which ensures cppgc::GarbageCollected
|
|
* is at the left-most position).
|
|
*
|
|
* class MyWrap final : CPPGC_MIXIN(MyWrap) {
|
|
* public:
|
|
* SET_CPPGC_NAME(MyWrap) // Sets the heap snapshot name to "Node / MyWrap"
|
|
* void Trace(cppgc::Visitor* visitor) const final {
|
|
* CppgcMixin::Trace(visitor);
|
|
* visitor->Trace(...); // Trace any additional owned traceable data
|
|
* }
|
|
* }
|
|
*
|
|
* If the wrapper needs to perform cleanups when it's destroyed and that
|
|
* cleanup relies on a living Node.js `Realm`, it should implement a
|
|
* pattern like this:
|
|
*
|
|
* ~MyWrap() { this->Destroy(); }
|
|
* void Clean(Realm* env) override {
|
|
* // Do cleanup that relies on a living Environemnt.
|
|
* }
|
|
*/
|
|
class CppgcMixin : public cppgc::GarbageCollectedMixin, public MemoryRetainer {
|
|
public:
|
|
// To help various callbacks access wrapper objects with different memory
|
|
// management, cppgc-managed objects share the same layout as BaseObjects.
|
|
enum InternalFields { kEmbedderType = 0, kSlot, kInternalFieldCount };
|
|
|
|
// The initialization cannot be done in the mixin constructor but has to be
|
|
// invoked from the child class constructor, per cppgc::GarbageCollectedMixin
|
|
// rules.
|
|
template <typename T>
|
|
static inline void Wrap(T* ptr, Realm* realm, v8::Local<v8::Object> obj);
|
|
template <typename T>
|
|
static inline void Wrap(T* ptr, Environment* env, v8::Local<v8::Object> obj);
|
|
|
|
inline v8::Local<v8::Object> object() const;
|
|
inline Environment* env() const;
|
|
inline Realm* realm() const { return realm_; }
|
|
inline v8::Local<v8::Object> object(v8::Isolate* isolate) const {
|
|
return traced_reference_.Get(isolate);
|
|
}
|
|
|
|
template <typename T>
|
|
static inline T* Unwrap(v8::Local<v8::Object> obj);
|
|
|
|
// Subclasses are expected to invoke CppgcMixin::Trace() in their own Trace()
|
|
// methods.
|
|
void Trace(cppgc::Visitor* visitor) const override {
|
|
visitor->Trace(traced_reference_);
|
|
}
|
|
|
|
// TODO(joyeecheung): use ObjectSizeTrait;
|
|
inline size_t SelfSize() const override { return sizeof(*this); }
|
|
inline bool IsCppgcWrapper() const override { return true; }
|
|
|
|
// This is run for all the remaining Cppgc wrappers tracked in the Realm
|
|
// during Realm shutdown. The destruction of the wrappers would happen later,
|
|
// when the final garbage collection is triggered when CppHeap is torn down as
|
|
// part of the Isolate teardown. If subclasses of CppgcMixin wish to perform
|
|
// cleanups that depend on the Realm during destruction, they should implment
|
|
// it in a Clean() override, and then call this->Finalize() from their
|
|
// destructor. Outside of Finalize(), subclasses should avoid calling
|
|
// into JavaScript or perform any operation that can trigger garbage
|
|
// collection during the destruction.
|
|
void Finalize() {
|
|
if (realm_ == nullptr) return;
|
|
this->Clean(realm_);
|
|
realm_ = nullptr;
|
|
}
|
|
|
|
// The default implementation of Clean() is a no-op. If subclasses wish
|
|
// to perform cleanup that require a living Realm, they should
|
|
// should put the cleanups in a Clean() override, and call this->Finalize()
|
|
// in the destructor, instead of doing those cleanups directly in the
|
|
// destructor.
|
|
virtual void Clean(Realm* realm) {}
|
|
|
|
inline ~CppgcMixin();
|
|
|
|
friend class CppgcWrapperListNode;
|
|
|
|
private:
|
|
Realm* realm_ = nullptr;
|
|
v8::TracedReference<v8::Object> traced_reference_;
|
|
};
|
|
|
|
// If the class doesn't have additional owned traceable data, use this macro to
|
|
// save the implementation of a custom Trace() method.
|
|
#define DEFAULT_CPPGC_TRACE() \
|
|
void Trace(cppgc::Visitor* visitor) const final { \
|
|
CppgcMixin::Trace(visitor); \
|
|
}
|
|
|
|
// This macro sets the node name in the heap snapshot with a "Node /" prefix.
|
|
// Classes that use this macro must extend cppgc::NameProvider.
|
|
#define SET_CPPGC_NAME(Klass) \
|
|
inline const char* GetHumanReadableName() const final { \
|
|
return "Node / " #Klass; \
|
|
} \
|
|
inline const char* MemoryInfoName() const override { return #Klass; }
|
|
|
|
/**
|
|
* Similar to ASSIGN_OR_RETURN_UNWRAP() but works on cppgc-managed types
|
|
* inheriting CppgcMixin.
|
|
*/
|
|
#define ASSIGN_OR_RETURN_UNWRAP_CPPGC(ptr, obj, ...) \
|
|
do { \
|
|
*ptr = CppgcMixin::Unwrap< \
|
|
typename std::remove_reference<decltype(**ptr)>::type>(obj); \
|
|
if (*ptr == nullptr) return __VA_ARGS__; \
|
|
} while (0)
|
|
} // namespace node
|
|
|
|
/**
|
|
* Helper macro the manage the cppgc-based wrapper hierarchy. This must
|
|
* be used at the left-most position - right after `:` in the class inheritance,
|
|
* like this:
|
|
* class Klass : CPPGC_MIXIN(Klass) ... {}
|
|
*
|
|
* This needs to disable linters because it will be at odds with
|
|
* clang-format.
|
|
*/
|
|
#define CPPGC_MIXIN(Klass) \
|
|
public /* NOLINT(whitespace/indent) */ \
|
|
v8::Object::Wrappable, public CppgcMixin
|
|
|
|
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
|
|
|
#endif // SRC_CPPGC_HELPERS_H_
|