Open-Source Wikis

/

Node.js

/

Primitives

/

BaseObject and BindingData

nodejs/node

BaseObject and BindingData

BaseObject is the C++ side of every JS-visible Node native object. BindingData is per-Realm storage for a binding. Both are designed to interact correctly with V8 GC and with snapshots (which copy a V8 heap to/from disk and have to know which native objects need to be re-attached on the way back).

BaseObject

Defined in src/base_object.h / src/base_object.cc / src/base_object-inl.h.

Every *Wrap C++ class in Node descends from BaseObject:

BaseObject
├── AsyncWrap                   (src/async_wrap.h)
│   ├── HandleWrap              (src/handle_wrap.h)
│   │   ├── LibuvStreamWrap     (src/stream_wrap.h)
│   │   │   ├── ConnectionWrap<TCPWrap, uv_tcp_t>  (src/connection_wrap.h)
│   │   │   │   └── TCPWrap     (src/tcp_wrap.h)
│   │   │   └── PipeWrap        (src/pipe_wrap.h)
│   │   ├── UDPWrap             (src/udp_wrap.h)
│   │   ├── TimerWrap           (src/timer_wrap.h)
│   │   └── FSEventWrap, ProcessWrap, TTYWrap, ...
│   └── ReqWrap<T>              (src/req_wrap.h)
│       ├── WriteWrap, ShutdownWrap, ConnectWrap, GetAddrInfoReqWrap, ...
│       └── FSReqCallback, FSReqPromise, ...
├── TLSWrap                     (src/crypto/crypto_tls.h)
├── ContextifyContext           (src/node_contextify.h)
├── BlobBindingData             (src/node_blob.h)
├── ...

A BaseObject instance:

  • Holds a v8::Local/v8::Global to a JS object.
  • Knows the Realm* it belongs to.
  • Implements MemoryInfo for heap snapshotting (so the V8 heap snapshotter can attribute native memory to the right JS object).
  • Has a OnGCCollect hook that runs when the JS object is collected.

BaseObjectPtr<T> and BaseObjectWeakPtr<T>

Plain raw pointers across async boundaries are dangerous because V8 can collect the JS object at any time. BaseObjectPtr<T> keeps the JS object alive (strong reference); BaseObjectWeakPtr<T> does not but knows when the underlying object goes away.

Use these in any structure that outlives the call stack — async work jobs, scheduled callbacks, request structs.

Snapshot integration

When node_mksnapshot builds the startup snapshot, every reachable BaseObject has to declare how it wants to be serialized. The pattern (src/base_object_types.h):

  • The class registers a kInternalFieldType enum value.
  • It implements Serialize / Deserialize callbacks that round-trip the C++ state.
  • The V8 inspector's EmbedderGraph is told about the C++ side so heap snapshots show the right edges.

This is why most binding Initialize functions register an EmbedderGraph callback and a SerializeBindingData callback.

BindingData

BindingData (src/node_realm.h, src/node_realm-inl.h) is per-Realm storage for a binding's pre-computed state. Bindings call Realm::AddBindingData<MyBindingData>() in their Initialize to register a struct that:

  • Holds Persistent JS values used by the binding (constructor functions, error templates).
  • Is preserved across snapshots through BindingData::Serialize / Deserialize.
  • Is cheap to access via BindingData::FromV8Value (the binding's first-arg dispatch path).

Examples:

  • node::fs::BindingData — caches the JS strings for stat fields.
  • node::http2::Http2Statenghttp2 callback table.
  • node::quic::BindingData — QUIC binding constants.
  • node::contextify::ContextifyContext::BindingData — vm-related caches.

Lifecycle

sequenceDiagram
    participant Realm
    participant Init as binding Initialize fn
    participant BD as BindingData

    Realm->>Init: load this binding for the current Realm
    Init->>BD: Realm::AddBindingData<MyBindingData>()
    BD-->>Init: register V8 functions, store in BindingData
    Realm-->>Realm: snapshot? -> serialize BindingData
    Realm-->>Realm: deserialize -> rehydrate BindingData
    Realm->>BD: cleanup at Realm dispose

Where to put state

  • If state is global to the binding (e.g. registered constants), put it on BindingData.
  • If state is per-instance (e.g. one per fs.read call), put it on the per-call *Wrap BaseObject.

Entry points for modification

  • New native binding? Use NODE_BINDING_CONTEXT_AWARE_INTERNAL(name, Initialize) (src/node_binding.h) and add a BindingData if you need persistent state.
  • New *Wrap class? Inherit from the right level in the hierarchy: AsyncWrap for async-resource semantics, HandleWrap for libuv handles, ReqWrap<T> for one-shot requests, plain BaseObject if neither applies.
  • Snapshot bug? BaseObject::IsSnapshotable and the per-class Serialize/Deserialize are the place to look.

For deeper coverage of how snapshots and the bootstrap interact see Builtins and primordials and systems › bootstrap-and-startup.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

BaseObject and BindingData – Node.js wiki | Factory