src: tag v8 aligned pointer slots with embedder data type tags

PR-URL: https://github.com/nodejs/node/pull/60602
Fixes: https://github.com/nodejs/node/issues/60589
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Juan José Arboleda <soyjuanarbol@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
Chengzhong Wu
2025-11-08 14:27:25 +00:00
committed by GitHub
parent ca91969808
commit b8e264d3c3
18 changed files with 120 additions and 58 deletions

View File

@@ -126,10 +126,10 @@ Typical ways of working with internal fields are:
* `obj->GetInternalField(i)` to get a JavaScript value from an internal field.
* `obj->SetInternalField(i, v)` to store a JavaScript value in an
internal field.
* `obj->GetAlignedPointerFromInternalField(i)` to get a `void*` pointer from an
internal field.
* `obj->SetAlignedPointerInInternalField(i, p)` to store a `void*` pointer in an
internal field.
* `obj->GetAlignedPointerFromInternalField(i, EmbedderDataTag::kDefault)` to get
a `void*` pointer from an internal field.
* `obj->SetAlignedPointerInInternalField(i, p, EmbedderDataTag::kDefault)` to store
a `void*` pointer in an internal field.
[`Context`][]s provide the same feature under the name “embedder data”.

View File

@@ -74,8 +74,9 @@ bool BaseObject::IsBaseObject(IsolateData* isolate_data,
return false;
}
uint16_t* ptr = static_cast<uint16_t*>(
obj->GetAlignedPointerFromInternalField(BaseObject::kEmbedderType));
uint16_t* ptr =
static_cast<uint16_t*>(obj->GetAlignedPointerFromInternalField(
BaseObject::kEmbedderType, EmbedderDataTag::kEmbedderType));
return ptr == isolate_data->embedder_id_for_non_cppgc();
}
@@ -83,21 +84,24 @@ void BaseObject::TagBaseObject(IsolateData* isolate_data,
v8::Local<v8::Object> object) {
DCHECK_GE(object->InternalFieldCount(), BaseObject::kInternalFieldCount);
object->SetAlignedPointerInInternalField(
BaseObject::kEmbedderType, isolate_data->embedder_id_for_non_cppgc());
BaseObject::kEmbedderType,
isolate_data->embedder_id_for_non_cppgc(),
EmbedderDataTag::kEmbedderType);
}
void BaseObject::SetInternalFields(IsolateData* isolate_data,
v8::Local<v8::Object> object,
void* slot) {
TagBaseObject(isolate_data, object);
object->SetAlignedPointerInInternalField(BaseObject::kSlot, slot);
object->SetAlignedPointerInInternalField(
BaseObject::kSlot, slot, EmbedderDataTag::kDefault);
}
BaseObject* BaseObject::FromJSObject(v8::Local<v8::Value> value) {
v8::Local<v8::Object> obj = value.As<v8::Object>();
DCHECK_GE(obj->InternalFieldCount(), BaseObject::kInternalFieldCount);
return static_cast<BaseObject*>(
obj->GetAlignedPointerFromInternalField(BaseObject::kSlot));
return static_cast<BaseObject*>(obj->GetAlignedPointerFromInternalField(
BaseObject::kSlot, EmbedderDataTag::kDefault));
}
template <typename T>

View File

@@ -45,7 +45,8 @@ BaseObject::~BaseObject() {
{
HandleScope handle_scope(realm()->isolate());
object()->SetAlignedPointerInInternalField(BaseObject::kSlot, nullptr);
object()->SetAlignedPointerInInternalField(
BaseObject::kSlot, nullptr, EmbedderDataTag::kDefault);
}
}

View File

@@ -27,6 +27,7 @@
#include <type_traits> // std::remove_reference
#include "base_object_types.h"
#include "memory_tracker.h"
#include "node_v8_embedder.h"
#include "util.h"
#include "v8.h"

View File

@@ -19,8 +19,10 @@ void CppgcMixin::Wrap(T* ptr, Realm* realm, v8::Local<v8::Object> obj) {
v8::Object::Wrap<v8::CppHeapPointerTag::kDefaultTag>(isolate, obj, wrappable);
// Keep the layout consistent with BaseObjects.
obj->SetAlignedPointerInInternalField(
kEmbedderType, realm->isolate_data()->embedder_id_for_cppgc());
obj->SetAlignedPointerInInternalField(kSlot, ptr);
kEmbedderType,
realm->isolate_data()->embedder_id_for_cppgc(),
EmbedderDataTag::kEmbedderType);
obj->SetAlignedPointerInInternalField(kSlot, ptr, EmbedderDataTag::kDefault);
realm->TrackCppgcWrapper(ptr);
}
@@ -41,7 +43,8 @@ T* CppgcMixin::Unwrap(v8::Local<v8::Object> obj) {
if (obj->InternalFieldCount() != T::kInternalFieldCount) {
return nullptr;
}
T* ptr = static_cast<T*>(obj->GetAlignedPointerFromInternalField(T::kSlot));
T* ptr = static_cast<T*>(obj->GetAlignedPointerFromInternalField(
T::kSlot, EmbedderDataTag::kDefault));
return ptr;
}

View File

@@ -196,9 +196,8 @@ inline Environment* Environment::GetCurrent(v8::Local<v8::Context> context) {
if (!ContextEmbedderTag::IsNodeContext(context)) [[unlikely]] {
return nullptr;
}
return static_cast<Environment*>(
context->GetAlignedPointerFromEmbedderData(
ContextEmbedderIndex::kEnvironment));
return static_cast<Environment*>(context->GetAlignedPointerFromEmbedderData(
ContextEmbedderIndex::kEnvironment, EmbedderDataTag::kPerContextData));
}
inline Environment* Environment::GetCurrent(

View File

@@ -674,12 +674,16 @@ void Environment::AssignToContext(Local<v8::Context> context,
Realm* realm,
const ContextInfo& info) {
context->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kEnvironment,
this);
context->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kRealm, realm);
this,
EmbedderDataTag::kPerContextData);
context->SetAlignedPointerInEmbedderData(
ContextEmbedderIndex::kRealm, realm, EmbedderDataTag::kPerContextData);
// ContextifyContexts will update this to a pointer to the native object.
context->SetAlignedPointerInEmbedderData(
ContextEmbedderIndex::kContextifyContext, nullptr);
ContextEmbedderIndex::kContextifyContext,
nullptr,
EmbedderDataTag::kPerContextData);
// This must not be done before other context fields are initialized.
ContextEmbedderTag::TagNodeContext(context);
@@ -695,11 +699,15 @@ void Environment::AssignToContext(Local<v8::Context> context,
void Environment::UnassignFromContext(Local<v8::Context> context) {
if (!context.IsEmpty()) {
context->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kEnvironment,
nullptr);
nullptr,
EmbedderDataTag::kPerContextData);
context->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kRealm,
nullptr);
nullptr,
EmbedderDataTag::kPerContextData);
context->SetAlignedPointerInEmbedderData(
ContextEmbedderIndex::kContextifyContext, nullptr);
ContextEmbedderIndex::kContextifyContext,
nullptr,
EmbedderDataTag::kPerContextData);
}
UntrackContext(context);
}

View File

@@ -136,7 +136,8 @@ HistogramBase::HistogramBase(
MakeWeak();
wrap->SetAlignedPointerInInternalField(
HistogramImpl::InternalFields::kImplField,
static_cast<HistogramImpl*>(this));
static_cast<HistogramImpl*>(this),
EmbedderDataTag::kDefault);
}
HistogramBase::HistogramBase(
@@ -148,7 +149,8 @@ HistogramBase::HistogramBase(
MakeWeak();
wrap->SetAlignedPointerInInternalField(
HistogramImpl::InternalFields::kImplField,
static_cast<HistogramImpl*>(this));
static_cast<HistogramImpl*>(this),
EmbedderDataTag::kDefault);
}
void HistogramBase::MemoryInfo(MemoryTracker* tracker) const {
@@ -362,7 +364,8 @@ IntervalHistogram::IntervalHistogram(
MakeWeak();
wrap->SetAlignedPointerInInternalField(
HistogramImpl::InternalFields::kImplField,
static_cast<HistogramImpl*>(this));
static_cast<HistogramImpl*>(this),
EmbedderDataTag::kDefault);
uv_timer_init(env->event_loop(), &timer_);
}
@@ -600,8 +603,8 @@ double HistogramImpl::FastGetPercentile(Local<Value> receiver,
HistogramImpl* HistogramImpl::FromJSObject(Local<Value> value) {
auto obj = value.As<Object>();
DCHECK_GE(obj->InternalFieldCount(), HistogramImpl::kInternalFieldCount);
return static_cast<HistogramImpl*>(
obj->GetAlignedPointerFromInternalField(HistogramImpl::kImplField));
return static_cast<HistogramImpl*>(obj->GetAlignedPointerFromInternalField(
HistogramImpl::kImplField, EmbedderDataTag::kDefault));
}
std::unique_ptr<worker::TransferData>

View File

@@ -55,8 +55,9 @@ JSUDPWrap::JSUDPWrap(Environment* env, Local<Object> obj)
: AsyncWrap(env, obj, PROVIDER_JSUDPWRAP) {
MakeWeak();
obj->SetAlignedPointerInInternalField(
kUDPWrapBaseField, static_cast<UDPWrapBase*>(this));
obj->SetAlignedPointerInInternalField(kUDPWrapBaseField,
static_cast<UDPWrapBase*>(this),
EmbedderDataTag::kDefault);
}
int JSUDPWrap::RecvStart() {

View File

@@ -3,6 +3,7 @@
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#include "node_v8_embedder.h"
#include "util.h"
#include "v8.h"
@@ -135,7 +136,8 @@ class ContextEmbedderTag {
// context.
context->SetAlignedPointerInEmbedderData(
ContextEmbedderIndex::kContextTag,
ContextEmbedderTag::kNodeContextTagPtr);
ContextEmbedderTag::kNodeContextTagPtr,
EmbedderDataTag::kPerContextData);
}
static inline bool IsNodeContext(v8::Local<v8::Context> context) {
@@ -147,7 +149,8 @@ class ContextEmbedderTag {
return false;
}
if (context->GetAlignedPointerFromEmbedderData(
ContextEmbedderIndex::kContextTag) !=
ContextEmbedderIndex::kContextTag,
EmbedderDataTag::kPerContextData) !=
ContextEmbedderTag::kNodeContextTagPtr) [[unlikely]] {
return false;
}

View File

@@ -164,9 +164,12 @@ ContextifyContext::ContextifyContext(Environment* env,
// This should only be done after the initial initializations of the context
// global object is finished.
DCHECK_NULL(v8_context->GetAlignedPointerFromEmbedderData(
ContextEmbedderIndex::kContextifyContext));
ContextEmbedderIndex::kContextifyContext,
EmbedderDataTag::kPerContextData));
v8_context->SetAlignedPointerInEmbedderData(
ContextEmbedderIndex::kContextifyContext, this);
ContextEmbedderIndex::kContextifyContext,
this,
EmbedderDataTag::kPerContextData);
}
void ContextifyContext::InitializeGlobalTemplates(IsolateData* isolate_data) {
@@ -473,7 +476,8 @@ ContextifyContext* ContextifyContext::Get(Local<Object> object) {
}
return static_cast<ContextifyContext*>(
context->GetAlignedPointerFromEmbedderData(
ContextEmbedderIndex::kContextifyContext));
ContextEmbedderIndex::kContextifyContext,
EmbedderDataTag::kPerContextData));
}
bool ContextifyContext::IsStillInitializing(const ContextifyContext* ctx) {

View File

@@ -49,7 +49,8 @@ class ObjectWrap {
assert(handle->InternalFieldCount() > 0);
// Cast to ObjectWrap before casting to T. A direct cast from void
// to T won't work right when T has more than one base class.
void* ptr = handle->GetAlignedPointerFromInternalField(0);
void* ptr = handle->GetAlignedPointerFromInternalField(
0, v8::kEmbedderDataTypeTagDefault);
ObjectWrap* wrap = static_cast<ObjectWrap*>(ptr);
return static_cast<T*>(wrap);
}
@@ -75,7 +76,8 @@ class ObjectWrap {
inline void Wrap(v8::Local<v8::Object> handle) {
assert(persistent().IsEmpty());
assert(handle->InternalFieldCount() > 0);
handle->SetAlignedPointerInInternalField(0, this);
handle->SetAlignedPointerInInternalField(
0, this, v8::kEmbedderDataTypeTagDefault);
persistent().Reset(v8::Isolate::GetCurrent(), handle);
MakeWeak();
}

View File

@@ -673,7 +673,8 @@ void BindingData::RegisterExternalReferences(
BindingData* BindingData::FromV8Value(Local<Value> value) {
Local<Object> v8_object = value.As<Object>();
return static_cast<BindingData*>(
v8_object->GetAlignedPointerFromInternalField(BaseObject::kSlot));
v8_object->GetAlignedPointerFromInternalField(BaseObject::kSlot,
EmbedderDataTag::kDefault));
}
void BindingData::MemoryInfo(MemoryTracker* tracker) const {

View File

@@ -20,8 +20,8 @@ inline Realm* Realm::GetCurrent(v8::Local<v8::Context> context) {
if (!ContextEmbedderTag::IsNodeContext(context)) [[unlikely]] {
return nullptr;
}
return static_cast<Realm*>(
context->GetAlignedPointerFromEmbedderData(ContextEmbedderIndex::kRealm));
return static_cast<Realm*>(context->GetAlignedPointerFromEmbedderData(
ContextEmbedderIndex::kRealm, EmbedderDataTag::kPerContextData));
}
inline Realm* Realm::GetCurrent(

View File

@@ -1309,7 +1309,8 @@ StartupData SerializeNodeContextData(Local<Context> holder,
case ContextEmbedderIndex::kContextifyContext:
case ContextEmbedderIndex::kRealm:
case ContextEmbedderIndex::kContextTag: {
void* data = holder->GetAlignedPointerFromEmbedderData(index);
void* data = holder->GetAlignedPointerFromEmbedderData(
index, EmbedderDataTag::kPerContextData);
per_process::Debug(
DebugCategory::MKSNAPSHOT,
"Serialize context data, index=%d, holder=%p, ptr=%p\n",
@@ -1400,7 +1401,8 @@ StartupData SerializeNodeContextInternalFields(Local<Object> holder,
// For the moment we do not set any internal fields in ArrayBuffer
// or ArrayBufferViews, so just return nullptr.
if (holder->IsArrayBuffer() || holder->IsArrayBufferView()) {
CHECK_NULL(holder->GetAlignedPointerFromInternalField(index));
CHECK_NULL(holder->GetAlignedPointerFromInternalField(
index, EmbedderDataTag::kDefault));
return StartupData{nullptr, 0};
}
@@ -1419,8 +1421,9 @@ StartupData SerializeNodeContextInternalFields(Local<Object> holder,
static_cast<int>(index),
*holder);
BaseObject* object_ptr = static_cast<BaseObject*>(
holder->GetAlignedPointerFromInternalField(BaseObject::kSlot));
BaseObject* object_ptr =
static_cast<BaseObject*>(holder->GetAlignedPointerFromInternalField(
BaseObject::kSlot, EmbedderDataTag::kDefault));
// If the native object is already set to null, ignore it.
if (object_ptr == nullptr) {
return StartupData{nullptr, 0};

26
src/node_v8_embedder.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef SRC_NODE_V8_EMBEDDER_H_
#define SRC_NODE_V8_EMBEDDER_H_
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#include <cstdint>
namespace node {
// Type tags for different kinds of embedder data stored in V8 aligned pointer
// slots.
enum EmbedderDataTag : uint16_t {
// kDefault is used in slots that don't use V8 type tagging.
kDefault = 0,
// kEmbedderType is used by BaseObject to store the kEmbedderType value.
kEmbedderType,
// kPerContextData is used to store data on a `v8::Context`, including
// slots indexed by ContextEmbedderIndex.
kPerContextData,
};
} // namespace node
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#endif // SRC_NODE_V8_EMBEDDER_H_

View File

@@ -22,19 +22,19 @@ void StreamReq::AttachToObject(v8::Local<v8::Object> req_wrap_obj) {
StreamReq::kStreamReqField),
nullptr);
req_wrap_obj->SetAlignedPointerInInternalField(
StreamReq::kStreamReqField, this);
StreamReq::kStreamReqField, this, EmbedderDataTag::kDefault);
}
StreamReq* StreamReq::FromObject(v8::Local<v8::Object> req_wrap_obj) {
return static_cast<StreamReq*>(
req_wrap_obj->GetAlignedPointerFromInternalField(
StreamReq::kStreamReqField));
StreamReq::kStreamReqField, EmbedderDataTag::kDefault));
}
void StreamReq::Dispose() {
BaseObjectPtr<AsyncWrap> destroy_me{GetAsyncWrap()};
object()->SetAlignedPointerInInternalField(
StreamReq::kStreamReqField, nullptr);
StreamReq::kStreamReqField, nullptr, EmbedderDataTag::kDefault);
destroy_me->Detach();
}
@@ -120,16 +120,16 @@ SimpleWriteWrap<OtherBase>::SimpleWriteWrap(
void StreamBase::AttachToObject(v8::Local<v8::Object> obj) {
obj->SetAlignedPointerInInternalField(
StreamBase::kStreamBaseField, this);
StreamBase::kStreamBaseField, this, EmbedderDataTag::kDefault);
}
StreamBase* StreamBase::FromObject(v8::Local<v8::Object> obj) {
if (obj->GetAlignedPointerFromInternalField(StreamBase::kSlot) == nullptr)
if (obj->GetAlignedPointerFromInternalField(
StreamBase::kSlot, EmbedderDataTag::kDefault) == nullptr)
return nullptr;
return static_cast<StreamBase*>(
obj->GetAlignedPointerFromInternalField(
StreamBase::kStreamBaseField));
return static_cast<StreamBase*>(obj->GetAlignedPointerFromInternalField(
StreamBase::kStreamBaseField, EmbedderDataTag::kDefault));
}
WriteWrap* WriteWrap::FromObject(v8::Local<v8::Object> req_wrap_obj) {
@@ -162,8 +162,10 @@ void WriteWrap::SetBackingStore(std::unique_ptr<v8::BackingStore> bs) {
void StreamReq::ResetObject(v8::Local<v8::Object> obj) {
DCHECK_GT(obj->InternalFieldCount(), StreamReq::kStreamReqField);
obj->SetAlignedPointerInInternalField(StreamReq::kSlot, nullptr);
obj->SetAlignedPointerInInternalField(StreamReq::kStreamReqField, nullptr);
obj->SetAlignedPointerInInternalField(
StreamReq::kSlot, nullptr, EmbedderDataTag::kDefault);
obj->SetAlignedPointerInInternalField(
StreamReq::kStreamReqField, nullptr, EmbedderDataTag::kDefault);
}
} // namespace node

View File

@@ -127,8 +127,8 @@ void UDPWrapBase::set_listener(UDPListener* listener) {
UDPWrapBase* UDPWrapBase::FromObject(Local<Object> obj) {
CHECK_GT(obj->InternalFieldCount(), UDPWrapBase::kUDPWrapBaseField);
return static_cast<UDPWrapBase*>(
obj->GetAlignedPointerFromInternalField(UDPWrapBase::kUDPWrapBaseField));
return static_cast<UDPWrapBase*>(obj->GetAlignedPointerFromInternalField(
UDPWrapBase::kUDPWrapBaseField, EmbedderDataTag::kDefault));
}
void UDPWrapBase::AddMethods(Environment* env, Local<FunctionTemplate> t) {
@@ -147,8 +147,9 @@ UDPWrap::UDPWrap(Environment* env, Local<Object> object)
object,
reinterpret_cast<uv_handle_t*>(&handle_),
AsyncWrap::PROVIDER_UDPWRAP) {
object->SetAlignedPointerInInternalField(
UDPWrapBase::kUDPWrapBaseField, static_cast<UDPWrapBase*>(this));
object->SetAlignedPointerInInternalField(UDPWrapBase::kUDPWrapBaseField,
static_cast<UDPWrapBase*>(this),
EmbedderDataTag::kDefault);
int r = uv_udp_init(env->event_loop(), &handle_);
CHECK_EQ(r, 0); // can't fail anyway