From 973287a4626f7a996cd5a4339161d8935ddc1181 Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Wed, 22 Feb 2023 06:43:25 +0800 Subject: [PATCH] src: per-realm binding data Binding data is inherited from BaseObject and created in a specific realm. They need to be tracked on a per-realm basis so that they can be released properly when a realm is disposed. PR-URL: https://github.com/nodejs/node/pull/46556 Reviewed-By: Joyee Cheung Reviewed-By: Colin Ihrig Reviewed-By: Matteo Collina --- src/README.md | 10 +++---- src/base_object-inl.h | 5 +++- src/dataqueue/queue.cc | 14 +++++----- src/env-inl.h | 42 ---------------------------- src/env.cc | 4 +-- src/env.h | 21 -------------- src/node_blob.cc | 21 ++++++-------- src/node_blob.h | 2 +- src/node_context_data.h | 6 ++-- src/node_file-inl.h | 2 +- src/node_file.cc | 55 +++++++++++++++++++------------------ src/node_file.h | 2 +- src/node_http2.cc | 11 ++++---- src/node_http2_state.h | 38 ++++++++++++------------- src/node_http_parser.cc | 10 +++---- src/node_process.h | 2 +- src/node_process_methods.cc | 24 ++++++++-------- src/node_realm-inl.h | 48 ++++++++++++++++++++++++++++++++ src/node_realm.cc | 1 + src/node_realm.h | 22 +++++++++++++++ src/node_snapshotable.cc | 5 ++-- src/node_snapshotable.h | 2 +- src/node_stat_watcher.cc | 3 +- src/node_util.cc | 20 ++++++-------- src/node_util.h | 4 +-- src/node_v8.cc | 38 +++++++++++++------------ src/node_v8.h | 2 +- 27 files changed, 211 insertions(+), 203 deletions(-) diff --git a/src/README.md b/src/README.md index 1b2b8a951d8852..58718935820ada 100644 --- a/src/README.md +++ b/src/README.md @@ -482,7 +482,7 @@ Which explains that the unregistered external reference is Some internal bindings, such as the HTTP parser, maintain internal state that only affects that particular binding. In that case, one common way to store -that state is through the use of `Environment::AddBindingData`, which gives +that state is through the use of `Realm::AddBindingData`, which gives binding functions access to an object for storing such state. That object is always a [`BaseObject`][]. @@ -507,7 +507,7 @@ class BindingData : public BaseObject { // Available for binding functions, e.g. the HTTP Parser constructor: static void New(const FunctionCallbackInfo& args) { - BindingData* binding_data = Environment::GetBindingData(args); + BindingData* binding_data = Realm::GetBindingData(args); new Parser(binding_data, args.This()); } @@ -517,12 +517,12 @@ void InitializeHttpParser(Local target, Local unused, Local context, void* priv) { - Environment* env = Environment::GetCurrent(context); + Realm* realm = Realm::GetCurrent(context); BindingData* const binding_data = - env->AddBindingData(context, target); + realm->AddBindingData(context, target); if (binding_data == nullptr) return; - Local t = env->NewFunctionTemplate(Parser::New); + Local t = NewFunctionTemplate(realm->isolate(), Parser::New); ... } ``` diff --git a/src/base_object-inl.h b/src/base_object-inl.h index 675a472dab48f1..f003f1390b864f 100644 --- a/src/base_object-inl.h +++ b/src/base_object-inl.h @@ -33,7 +33,10 @@ namespace node { BaseObject::BaseObject(Environment* env, v8::Local object) - : BaseObject(env->principal_realm(), object) {} + : BaseObject(env->principal_realm(), object) { + // TODO(legendecas): Check the shorthand is only used in the principal realm + // while allowing to create a BaseObject in a vm context. +} // static v8::Local BaseObject::GetConstructorTemplate( diff --git a/src/dataqueue/queue.cc b/src/dataqueue/queue.cc index 7d22d3b9cbb81c..260c70ebbf2d98 100644 --- a/src/dataqueue/queue.cc +++ b/src/dataqueue/queue.cc @@ -874,14 +874,14 @@ class FdEntry final : public EntryImpl { uv_fs_close(nullptr, &req, file, nullptr); return nullptr; } + Realm* realm = entry->env()->principal_realm(); return std::make_shared( - BaseObjectPtr( - fs::FileHandle::New(entry->env()->GetBindingData( - entry->env()->context()), - file, - Local(), - entry->start_, - entry->end_)), + BaseObjectPtr(fs::FileHandle::New( + realm->GetBindingData(realm->context()), + file, + Local(), + entry->start_, + entry->end_)), entry); } diff --git a/src/env-inl.h b/src/env-inl.h index 839833f684a4bb..d061ce5b993b6a 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -201,48 +201,6 @@ inline Environment* Environment::GetCurrent( return GetCurrent(info.GetIsolate()->GetCurrentContext()); } -template -inline T* Environment::GetBindingData(const v8::PropertyCallbackInfo& info) { - return GetBindingData(info.GetIsolate()->GetCurrentContext()); -} - -template -inline T* Environment::GetBindingData( - const v8::FunctionCallbackInfo& info) { - return GetBindingData(info.GetIsolate()->GetCurrentContext()); -} - -template -inline T* Environment::GetBindingData(v8::Local context) { - BindingDataStore* map = static_cast( - context->GetAlignedPointerFromEmbedderData( - ContextEmbedderIndex::kBindingListIndex)); - DCHECK_NOT_NULL(map); - auto it = map->find(T::type_name); - if (UNLIKELY(it == map->end())) return nullptr; - T* result = static_cast(it->second.get()); - DCHECK_NOT_NULL(result); - DCHECK_EQ(result->env(), GetCurrent(context)); - return result; -} - -template -inline T* Environment::AddBindingData( - v8::Local context, - v8::Local target) { - DCHECK_EQ(GetCurrent(context), this); - // This won't compile if T is not a BaseObject subclass. - BaseObjectPtr item = MakeDetachedBaseObject(this, target); - BindingDataStore* map = static_cast( - context->GetAlignedPointerFromEmbedderData( - ContextEmbedderIndex::kBindingListIndex)); - DCHECK_NOT_NULL(map); - auto result = map->emplace(T::type_name, item); - CHECK(result.second); - DCHECK_EQ(GetBindingData(context), item.get()); - return item.get(); -} - inline v8::Isolate* Environment::isolate() const { return isolate_; } diff --git a/src/env.cc b/src/env.cc index 341d64f044e72f..8b495734961c26 100644 --- a/src/env.cc +++ b/src/env.cc @@ -545,7 +545,8 @@ void Environment::AssignToContext(Local context, context->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kRealm, realm); // Used to retrieve bindings context->SetAlignedPointerInEmbedderData( - ContextEmbedderIndex::kBindingListIndex, &(this->bindings_)); + ContextEmbedderIndex::kBindingDataStoreIndex, + realm->binding_data_store()); // ContextifyContexts will update this to a pointer to the native object. context->SetAlignedPointerInEmbedderData( @@ -1018,7 +1019,6 @@ MaybeLocal Environment::RunSnapshotDeserializeMain() const { void Environment::RunCleanup() { started_cleanup_ = true; TRACE_EVENT0(TRACING_CATEGORY_NODE1(environment), "RunCleanup"); - bindings_.clear(); // Only BaseObject's cleanups are registered as per-realm cleanup hooks now. // Defer the BaseObject cleanup after handles are cleaned up. CleanupHandles(); diff --git a/src/env.h b/src/env.h index 2ea0d770863015..c5fb9ea5431ae9 100644 --- a/src/env.h +++ b/src/env.h @@ -587,25 +587,6 @@ class Environment : public MemoryRetainer { static inline Environment* GetCurrent( const v8::PropertyCallbackInfo& info); - // Methods created using SetMethod(), SetPrototypeMethod(), etc. inside - // this scope can access the created T* object using - // GetBindingData(args) later. - template - T* AddBindingData(v8::Local context, - v8::Local target); - template - static inline T* GetBindingData(const v8::PropertyCallbackInfo& info); - template - static inline T* GetBindingData( - const v8::FunctionCallbackInfo& info); - template - static inline T* GetBindingData(v8::Local context); - - typedef std::unordered_map< - FastStringKey, - BaseObjectPtr, - FastStringKey::Hash> BindingDataStore; - // Create an Environment without initializing a main Context. Use // InitializeMainContext() to initialize a main context for it. Environment(IsolateData* isolate_data, @@ -1124,8 +1105,6 @@ class Environment : public MemoryRetainer { void RequestInterruptFromV8(); static void CheckImmediate(uv_check_t* handle); - BindingDataStore bindings_; - CleanupQueue cleanup_queue_; bool started_cleanup_ = false; diff --git a/src/node_blob.cc b/src/node_blob.cc index b60e26a60ea942..c9e7c3f378262b 100644 --- a/src/node_blob.cc +++ b/src/node_blob.cc @@ -110,10 +110,10 @@ void Blob::Initialize( Local unused, Local context, void* priv) { - Environment* env = Environment::GetCurrent(context); + Realm* realm = Realm::GetCurrent(context); BlobBindingData* const binding_data = - env->AddBindingData(context, target); + realm->AddBindingData(context, target); if (binding_data == nullptr) return; SetMethod(context, target, "createBlob", New); @@ -394,8 +394,7 @@ std::unique_ptr Blob::CloneForMessaging() const { void Blob::StoreDataObject(const v8::FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - BlobBindingData* binding_data = - Environment::GetBindingData(args); + BlobBindingData* binding_data = Realm::GetBindingData(args); CHECK(args[0]->IsString()); // ID key CHECK(Blob::HasInstance(env, args[1])); // Blob @@ -418,8 +417,7 @@ void Blob::StoreDataObject(const v8::FunctionCallbackInfo& args) { } void Blob::RevokeDataObject(const v8::FunctionCallbackInfo& args) { - BlobBindingData* binding_data = - Environment::GetBindingData(args); + BlobBindingData* binding_data = Realm::GetBindingData(args); Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsString()); // ID key @@ -430,8 +428,7 @@ void Blob::RevokeDataObject(const v8::FunctionCallbackInfo& args) { } void Blob::GetDataObject(const v8::FunctionCallbackInfo& args) { - BlobBindingData* binding_data = - Environment::GetBindingData(args); + BlobBindingData* binding_data = Realm::GetBindingData(args); Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsString()); @@ -477,8 +474,8 @@ BlobBindingData::StoredDataObject::StoredDataObject( length(length_), type(type_) {} -BlobBindingData::BlobBindingData(Environment* env, Local wrap) - : SnapshotableObject(env, wrap, type_int) { +BlobBindingData::BlobBindingData(Realm* realm, Local wrap) + : SnapshotableObject(realm, wrap, type_int) { MakeWeak(); } @@ -516,9 +513,9 @@ void BlobBindingData::Deserialize(Local context, InternalFieldInfoBase* info) { DCHECK_EQ(index, BaseObject::kEmbedderType); HandleScope scope(context->GetIsolate()); - Environment* env = Environment::GetCurrent(context); + Realm* realm = Realm::GetCurrent(context); BlobBindingData* binding = - env->AddBindingData(context, holder); + realm->AddBindingData(context, holder); CHECK_NOT_NULL(binding); } diff --git a/src/node_blob.h b/src/node_blob.h index e56627cf35cf76..f6d5ad89f69792 100644 --- a/src/node_blob.h +++ b/src/node_blob.h @@ -111,7 +111,7 @@ class Blob : public BaseObject { class BlobBindingData : public SnapshotableObject { public: - explicit BlobBindingData(Environment* env, v8::Local wrap); + explicit BlobBindingData(Realm* realm, v8::Local wrap); using InternalFieldInfo = InternalFieldInfoBase; diff --git a/src/node_context_data.h b/src/node_context_data.h index 4278a17f4b6ad0..009d46c34dc4ef 100644 --- a/src/node_context_data.h +++ b/src/node_context_data.h @@ -24,8 +24,8 @@ namespace node { #define NODE_CONTEXT_ALLOW_WASM_CODE_GENERATION_INDEX 34 #endif -#ifndef NODE_BINDING_LIST -#define NODE_BINDING_LIST_INDEX 35 +#ifndef NODE_BINDING_DATA_STORE_INDEX +#define NODE_BINDING_DATA_STORE_INDEX 35 #endif #ifndef NODE_CONTEXT_ALLOW_CODE_GENERATION_FROM_STRINGS_INDEX @@ -51,7 +51,7 @@ enum ContextEmbedderIndex { kEnvironment = NODE_CONTEXT_EMBEDDER_DATA_INDEX, kSandboxObject = NODE_CONTEXT_SANDBOX_OBJECT_INDEX, kAllowWasmCodeGeneration = NODE_CONTEXT_ALLOW_WASM_CODE_GENERATION_INDEX, - kBindingListIndex = NODE_BINDING_LIST_INDEX, + kBindingDataStoreIndex = NODE_BINDING_DATA_STORE_INDEX, kAllowCodeGenerationFromStrings = NODE_CONTEXT_ALLOW_CODE_GENERATION_FROM_STRINGS_INDEX, kContextifyContext = NODE_CONTEXT_CONTEXTIFY_CONTEXT_INDEX, diff --git a/src/node_file-inl.h b/src/node_file-inl.h index adce4cab53eba6..2ba5906d614f1c 100644 --- a/src/node_file-inl.h +++ b/src/node_file-inl.h @@ -277,7 +277,7 @@ FSReqBase* GetReqWrap(const v8::FunctionCallbackInfo& args, return Unwrap(value.As()); } - BindingData* binding_data = Environment::GetBindingData(args); + BindingData* binding_data = Realm::GetBindingData(args); Environment* env = binding_data->env(); if (value->StrictEquals(env->fs_use_promises_symbol())) { if (use_bigint) { diff --git a/src/node_file.cc b/src/node_file.cc index 673d67e2d3c010..388d9bf32c34a6 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -259,7 +259,7 @@ FileHandle* FileHandle::New(BindingData* binding_data, } void FileHandle::New(const FunctionCallbackInfo& args) { - BindingData* binding_data = Environment::GetBindingData(args); + BindingData* binding_data = Realm::GetBindingData(args); Environment* env = binding_data->env(); CHECK(args.IsConstructCall()); CHECK(args[0]->IsInt32()); @@ -324,7 +324,7 @@ BaseObjectPtr FileHandle::TransferData::Deserialize( Environment* env, v8::Local context, std::unique_ptr self) { - BindingData* bd = Environment::GetBindingData(context); + BindingData* bd = Realm::GetBindingData(context); if (bd == nullptr) return {}; int fd = fd_; @@ -724,7 +724,7 @@ void FSReqCallback::SetReturnValue(const FunctionCallbackInfo& args) { void NewFSReqCallback(const FunctionCallbackInfo& args) { CHECK(args.IsConstructCall()); - BindingData* binding_data = Environment::GetBindingData(args); + BindingData* binding_data = Realm::GetBindingData(args); new FSReqCallback(binding_data, args.This(), args[0]->IsTrue()); } @@ -1131,7 +1131,7 @@ static void InternalModuleStat(const FunctionCallbackInfo& args) { } static void Stat(const FunctionCallbackInfo& args) { - BindingData* binding_data = Environment::GetBindingData(args); + BindingData* binding_data = Realm::GetBindingData(args); Environment* env = binding_data->env(); const int argc = args.Length(); @@ -1164,7 +1164,7 @@ static void Stat(const FunctionCallbackInfo& args) { } static void LStat(const FunctionCallbackInfo& args) { - BindingData* binding_data = Environment::GetBindingData(args); + BindingData* binding_data = Realm::GetBindingData(args); Environment* env = binding_data->env(); const int argc = args.Length(); @@ -1198,7 +1198,7 @@ static void LStat(const FunctionCallbackInfo& args) { } static void FStat(const FunctionCallbackInfo& args) { - BindingData* binding_data = Environment::GetBindingData(args); + BindingData* binding_data = Realm::GetBindingData(args); Environment* env = binding_data->env(); const int argc = args.Length(); @@ -1230,7 +1230,7 @@ static void FStat(const FunctionCallbackInfo& args) { } static void StatFs(const FunctionCallbackInfo& args) { - BindingData* binding_data = Environment::GetBindingData(args); + BindingData* binding_data = Realm::GetBindingData(args); Environment* env = binding_data->env(); const int argc = args.Length(); @@ -1945,7 +1945,7 @@ static void Open(const FunctionCallbackInfo& args) { } static void OpenFileHandle(const FunctionCallbackInfo& args) { - BindingData* binding_data = Environment::GetBindingData(args); + BindingData* binding_data = Realm::GetBindingData(args); Environment* env = binding_data->env(); Isolate* isolate = env->isolate(); @@ -2635,29 +2635,31 @@ void BindingData::MemoryInfo(MemoryTracker* tracker) const { file_handle_read_wrap_freelist); } -BindingData::BindingData(Environment* env, v8::Local wrap) - : SnapshotableObject(env, wrap, type_int), - stats_field_array(env->isolate(), kFsStatsBufferLength), - stats_field_bigint_array(env->isolate(), kFsStatsBufferLength), - statfs_field_array(env->isolate(), kFsStatFsBufferLength), - statfs_field_bigint_array(env->isolate(), kFsStatFsBufferLength) { - wrap->Set(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "statValues"), +BindingData::BindingData(Realm* realm, v8::Local wrap) + : SnapshotableObject(realm, wrap, type_int), + stats_field_array(realm->isolate(), kFsStatsBufferLength), + stats_field_bigint_array(realm->isolate(), kFsStatsBufferLength), + statfs_field_array(realm->isolate(), kFsStatFsBufferLength), + statfs_field_bigint_array(realm->isolate(), kFsStatFsBufferLength) { + Isolate* isolate = realm->isolate(); + Local context = realm->context(); + wrap->Set(context, + FIXED_ONE_BYTE_STRING(isolate, "statValues"), stats_field_array.GetJSArray()) .Check(); - wrap->Set(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "bigintStatValues"), + wrap->Set(context, + FIXED_ONE_BYTE_STRING(isolate, "bigintStatValues"), stats_field_bigint_array.GetJSArray()) .Check(); - wrap->Set(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "statFsValues"), + wrap->Set(context, + FIXED_ONE_BYTE_STRING(isolate, "statFsValues"), statfs_field_array.GetJSArray()) .Check(); - wrap->Set(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "bigintStatFsValues"), + wrap->Set(context, + FIXED_ONE_BYTE_STRING(isolate, "bigintStatFsValues"), statfs_field_bigint_array.GetJSArray()) .Check(); } @@ -2668,8 +2670,8 @@ void BindingData::Deserialize(Local context, InternalFieldInfoBase* info) { DCHECK_EQ(index, BaseObject::kEmbedderType); HandleScope scope(context->GetIsolate()); - Environment* env = Environment::GetCurrent(context); - BindingData* binding = env->AddBindingData(context, holder); + Realm* realm = Realm::GetCurrent(context); + BindingData* binding = realm->AddBindingData(context, holder); CHECK_NOT_NULL(binding); } @@ -2698,10 +2700,11 @@ void Initialize(Local target, Local unused, Local context, void* priv) { - Environment* env = Environment::GetCurrent(context); + Realm* realm = Realm::GetCurrent(context); + Environment* env = realm->env(); Isolate* isolate = env->isolate(); BindingData* const binding_data = - env->AddBindingData(context, target); + realm->AddBindingData(context, target); if (binding_data == nullptr) return; SetMethod(context, target, "access", Access); diff --git a/src/node_file.h b/src/node_file.h index 28c62bef799f81..e44b58c94ff74c 100644 --- a/src/node_file.h +++ b/src/node_file.h @@ -57,7 +57,7 @@ constexpr size_t kFsStatFsBufferLength = class BindingData : public SnapshotableObject { public: - explicit BindingData(Environment* env, v8::Local wrap); + explicit BindingData(Realm* realm, v8::Local wrap); AliasedFloat64Array stats_field_array; AliasedBigInt64Array stats_field_bigint_array; diff --git a/src/node_http2.cc b/src/node_http2.cc index 4209a1e1910d3b..7e8e04f440ae85 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -2568,7 +2568,7 @@ void HttpErrorString(const FunctionCallbackInfo& args) { // would be suitable, for instance, for creating the Base64 // output for an HTTP2-Settings header field. void PackSettings(const FunctionCallbackInfo& args) { - Http2State* state = Environment::GetBindingData(args); + Http2State* state = Realm::GetBindingData(args); args.GetReturnValue().Set(Http2Settings::Pack(state)); } @@ -2576,7 +2576,7 @@ void PackSettings(const FunctionCallbackInfo& args) { // default SETTINGS. RefreshDefaultSettings updates that TypedArray with the // default values. void RefreshDefaultSettings(const FunctionCallbackInfo& args) { - Http2State* state = Environment::GetBindingData(args); + Http2State* state = Realm::GetBindingData(args); Http2Settings::RefreshDefaults(state); } @@ -2659,7 +2659,7 @@ void Http2Session::RefreshState(const FunctionCallbackInfo& args) { // Constructor for new Http2Session instances. void Http2Session::New(const FunctionCallbackInfo& args) { - Http2State* state = Environment::GetBindingData(args); + Http2State* state = Realm::GetBindingData(args); Environment* env = state->env(); CHECK(args.IsConstructCall()); SessionType type = @@ -3187,11 +3187,12 @@ void Initialize(Local target, Local unused, Local context, void* priv) { - Environment* env = Environment::GetCurrent(context); + Realm* realm = Realm::GetCurrent(context); + Environment* env = realm->env(); Isolate* isolate = env->isolate(); HandleScope handle_scope(isolate); - Http2State* const state = env->AddBindingData(context, target); + Http2State* const state = realm->AddBindingData(context, target); if (state == nullptr) return; #define SET_STATE_TYPEDARRAY(name, field) \ diff --git a/src/node_http2_state.h b/src/node_http2_state.h index 7cf40ff1017ca3..75a98bf476b2f7 100644 --- a/src/node_http2_state.h +++ b/src/node_http2_state.h @@ -83,34 +83,32 @@ namespace http2 { class Http2State : public BaseObject { public: - Http2State(Environment* env, v8::Local obj) - : BaseObject(env, obj), - root_buffer(env->isolate(), sizeof(http2_state_internal)), + Http2State(Realm* realm, v8::Local obj) + : BaseObject(realm, obj), + root_buffer(realm->isolate(), sizeof(http2_state_internal)), session_state_buffer( - env->isolate(), + realm->isolate(), offsetof(http2_state_internal, session_state_buffer), IDX_SESSION_STATE_COUNT, root_buffer), - stream_state_buffer( - env->isolate(), - offsetof(http2_state_internal, stream_state_buffer), - IDX_STREAM_STATE_COUNT, - root_buffer), - stream_stats_buffer( - env->isolate(), - offsetof(http2_state_internal, stream_stats_buffer), - IDX_STREAM_STATS_COUNT, - root_buffer), + stream_state_buffer(realm->isolate(), + offsetof(http2_state_internal, stream_state_buffer), + IDX_STREAM_STATE_COUNT, + root_buffer), + stream_stats_buffer(realm->isolate(), + offsetof(http2_state_internal, stream_stats_buffer), + IDX_STREAM_STATS_COUNT, + root_buffer), session_stats_buffer( - env->isolate(), + realm->isolate(), offsetof(http2_state_internal, session_stats_buffer), IDX_SESSION_STATS_COUNT, root_buffer), - options_buffer(env->isolate(), - offsetof(http2_state_internal, options_buffer), - IDX_OPTIONS_FLAGS + 1, - root_buffer), - settings_buffer(env->isolate(), + options_buffer(realm->isolate(), + offsetof(http2_state_internal, options_buffer), + IDX_OPTIONS_FLAGS + 1, + root_buffer), + settings_buffer(realm->isolate(), offsetof(http2_state_internal, settings_buffer), IDX_SETTINGS_COUNT + 1, root_buffer) {} diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc index bb2019c7445785..a502fa0ccd9018 100644 --- a/src/node_http_parser.cc +++ b/src/node_http_parser.cc @@ -93,8 +93,7 @@ inline bool IsOWS(char c) { class BindingData : public BaseObject { public: - BindingData(Environment* env, Local obj) - : BaseObject(env, obj) {} + BindingData(Realm* realm, Local obj) : BaseObject(realm, obj) {} static constexpr FastStringKey type_name { "http_parser" }; @@ -531,7 +530,7 @@ class Parser : public AsyncWrap, public StreamListener { } static void New(const FunctionCallbackInfo& args) { - BindingData* binding_data = Environment::GetBindingData(args); + BindingData* binding_data = Realm::GetBindingData(args); new Parser(binding_data, args.This()); } @@ -1190,10 +1189,11 @@ void InitializeHttpParser(Local target, Local unused, Local context, void* priv) { - Environment* env = Environment::GetCurrent(context); + Realm* realm = Realm::GetCurrent(context); + Environment* env = realm->env(); Isolate* isolate = env->isolate(); BindingData* const binding_data = - env->AddBindingData(context, target); + realm->AddBindingData(context, target); if (binding_data == nullptr) return; Local t = NewFunctionTemplate(isolate, Parser::New); diff --git a/src/node_process.h b/src/node_process.h index 8065378960887d..30f655dfc71f23 100644 --- a/src/node_process.h +++ b/src/node_process.h @@ -58,7 +58,7 @@ class BindingData : public SnapshotableObject { static constexpr EmbedderObjectType type_int = EmbedderObjectType::k_process_binding_data; - BindingData(Environment* env, v8::Local object); + BindingData(Realm* realm, v8::Local object); void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(BindingData) diff --git a/src/node_process_methods.cc b/src/node_process_methods.cc index 778e4cd9dd966e..93657860379fdf 100644 --- a/src/node_process_methods.cc +++ b/src/node_process_methods.cc @@ -464,14 +464,13 @@ static void ReallyExit(const FunctionCallbackInfo& args) { namespace process { -BindingData::BindingData(Environment* env, v8::Local object) - : SnapshotableObject(env, object, type_int) { - Local ab = ArrayBuffer::New(env->isolate(), kBufferSize); - array_buffer_.Reset(env->isolate(), ab); - object - ->Set(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "hrtimeBuffer"), - ab) +BindingData::BindingData(Realm* realm, v8::Local object) + : SnapshotableObject(realm, object, type_int) { + Isolate* isolate = realm->isolate(); + Local context = realm->context(); + Local ab = ArrayBuffer::New(isolate, kBufferSize); + array_buffer_.Reset(isolate, ab); + object->Set(context, FIXED_ONE_BYTE_STRING(isolate, "hrtimeBuffer"), ab) .ToChecked(); backing_store_ = ab->GetBackingStore(); } @@ -564,9 +563,9 @@ void BindingData::Deserialize(Local context, InternalFieldInfoBase* info) { DCHECK_EQ(index, BaseObject::kEmbedderType); v8::HandleScope scope(context->GetIsolate()); - Environment* env = Environment::GetCurrent(context); + Realm* realm = Realm::GetCurrent(context); // Recreate the buffer in the constructor. - BindingData* binding = env->AddBindingData(context, holder); + BindingData* binding = realm->AddBindingData(context, holder); CHECK_NOT_NULL(binding); } @@ -574,9 +573,10 @@ static void Initialize(Local target, Local unused, Local context, void* priv) { - Environment* env = Environment::GetCurrent(context); + Realm* realm = Realm::GetCurrent(context); + Environment* env = realm->env(); BindingData* const binding_data = - env->AddBindingData(context, target); + realm->AddBindingData(context, target); if (binding_data == nullptr) return; binding_data->AddMethods(); diff --git a/src/node_realm-inl.h b/src/node_realm-inl.h index 95a09dafac76e3..01aca9382eebe1 100644 --- a/src/node_realm-inl.h +++ b/src/node_realm-inl.h @@ -46,6 +46,54 @@ inline bool Realm::has_run_bootstrapping_code() const { return has_run_bootstrapping_code_; } +// static +template +inline T* Realm::GetBindingData(const v8::PropertyCallbackInfo& info) { + return GetBindingData(info.GetIsolate()->GetCurrentContext()); +} + +// static +template +inline T* Realm::GetBindingData( + const v8::FunctionCallbackInfo& info) { + return GetBindingData(info.GetIsolate()->GetCurrentContext()); +} + +// static +template +inline T* Realm::GetBindingData(v8::Local context) { + BindingDataStore* map = + static_cast(context->GetAlignedPointerFromEmbedderData( + ContextEmbedderIndex::kBindingDataStoreIndex)); + DCHECK_NOT_NULL(map); + auto it = map->find(T::type_name); + if (UNLIKELY(it == map->end())) return nullptr; + T* result = static_cast(it->second.get()); + DCHECK_NOT_NULL(result); + DCHECK_EQ(result->realm(), GetCurrent(context)); + return result; +} + +template +inline T* Realm::AddBindingData(v8::Local context, + v8::Local target) { + DCHECK_EQ(GetCurrent(context), this); + // This won't compile if T is not a BaseObject subclass. + BaseObjectPtr item = MakeDetachedBaseObject(this, target); + BindingDataStore* map = + static_cast(context->GetAlignedPointerFromEmbedderData( + ContextEmbedderIndex::kBindingDataStoreIndex)); + DCHECK_NOT_NULL(map); + auto result = map->emplace(T::type_name, item); + CHECK(result.second); + DCHECK_EQ(GetBindingData(context), item.get()); + return item.get(); +} + +inline BindingDataStore* Realm::binding_data_store() { + return &binding_data_store_; +} + template void Realm::ForEachBaseObject(T&& iterator) const { cleanup_queue_.ForEachBaseObject(std::forward(iterator)); diff --git a/src/node_realm.cc b/src/node_realm.cc index e0fcd3a0775973..9452e336ea789f 100644 --- a/src/node_realm.cc +++ b/src/node_realm.cc @@ -302,6 +302,7 @@ void Realm::DoneBootstrapping() { void Realm::RunCleanup() { TRACE_EVENT0(TRACING_CATEGORY_NODE1(realm), "RunCleanup"); + binding_data_store_.clear(); cleanup_queue_.Drain(); } diff --git a/src/node_realm.h b/src/node_realm.h index c97a3a3e9ae082..cf37fe31bdd6e4 100644 --- a/src/node_realm.h +++ b/src/node_realm.h @@ -4,6 +4,7 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include +#include #include "cleanup_queue.h" #include "env_properties.h" #include "memory_tracker.h" @@ -20,6 +21,10 @@ struct RealmSerializeInfo { friend std::ostream& operator<<(std::ostream& o, const RealmSerializeInfo& i); }; +using BindingDataStore = std::unordered_map, + FastStringKey::Hash>; + /** * node::Realm is a container for a set of JavaScript objects and functions * that associated with a particular global environment. @@ -85,6 +90,21 @@ class Realm : public MemoryRetainer { inline v8::Local context() const; inline bool has_run_bootstrapping_code() const; + // Methods created using SetMethod(), SetPrototypeMethod(), etc. inside + // this scope can access the created T* object using + // GetBindingData(args) later. + template + T* AddBindingData(v8::Local context, + v8::Local target); + template + static inline T* GetBindingData(const v8::PropertyCallbackInfo& info); + template + static inline T* GetBindingData( + const v8::FunctionCallbackInfo& info); + template + static inline T* GetBindingData(v8::Local context); + inline BindingDataStore* binding_data_store(); + // The BaseObject count is a debugging helper that makes sure that there are // no memory leaks caused by BaseObjects staying alive longer than expected // (in particular, no circular BaseObjectPtr references). @@ -121,6 +141,8 @@ class Realm : public MemoryRetainer { int64_t base_object_count_ = 0; int64_t base_object_created_by_bootstrap_ = 0; + BindingDataStore binding_data_store_; + CleanupQueue cleanup_queue_; #define V(PropertyName, TypeName) v8::Global PropertyName##_; diff --git a/src/node_snapshotable.cc b/src/node_snapshotable.cc index 40971cf8fd28b3..325fae21bc2a06 100644 --- a/src/node_snapshotable.cc +++ b/src/node_snapshotable.cc @@ -1284,11 +1284,10 @@ ExitCode SnapshotBuilder::Generate(std::ostream& out, return exit_code; } -SnapshotableObject::SnapshotableObject(Environment* env, +SnapshotableObject::SnapshotableObject(Realm* realm, Local wrap, EmbedderObjectType type) - : BaseObject(env, wrap), type_(type) { -} + : BaseObject(realm, wrap), type_(type) {} std::string_view SnapshotableObject::GetTypeName() const { switch (type_) { diff --git a/src/node_snapshotable.h b/src/node_snapshotable.h index a825350806bfb0..3f4d0780131e20 100644 --- a/src/node_snapshotable.h +++ b/src/node_snapshotable.h @@ -98,7 +98,7 @@ struct InternalFieldInfoBase { // in the object. class SnapshotableObject : public BaseObject { public: - SnapshotableObject(Environment* env, + SnapshotableObject(Realm* realm, v8::Local wrap, EmbedderObjectType type); std::string_view GetTypeName() const; diff --git a/src/node_stat_watcher.cc b/src/node_stat_watcher.cc index de8c099ca58594..83b96e9dd2030e 100644 --- a/src/node_stat_watcher.cc +++ b/src/node_stat_watcher.cc @@ -97,8 +97,7 @@ void StatWatcher::Callback(uv_fs_poll_t* handle, void StatWatcher::New(const FunctionCallbackInfo& args) { CHECK(args.IsConstructCall()); - fs::BindingData* binding_data = - Environment::GetBindingData(args); + fs::BindingData* binding_data = Realm::GetBindingData(args); new StatWatcher(binding_data, args.This(), args[0]->IsTrue()); } diff --git a/src/node_util.cc b/src/node_util.cc index 59244f1ebb6d3b..f7467caf899d9c 100644 --- a/src/node_util.cc +++ b/src/node_util.cc @@ -178,20 +178,20 @@ void ArrayBufferViewHasBuffer(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(args[0].As()->HasBuffer()); } -WeakReference::WeakReference(Environment* env, +WeakReference::WeakReference(Realm* realm, Local object, Local target) - : WeakReference(env, object, target, 0) {} + : WeakReference(realm, object, target, 0) {} -WeakReference::WeakReference(Environment* env, +WeakReference::WeakReference(Realm* realm, Local object, Local target, uint64_t reference_count) - : SnapshotableObject(env, object, type_int), + : SnapshotableObject(realm, object, type_int), reference_count_(reference_count) { MakeWeak(); if (!target.IsEmpty()) { - target_.Reset(env->isolate(), target); + target_.Reset(realm->isolate(), target); if (reference_count_ == 0) { target_.SetWeak(); } @@ -244,17 +244,15 @@ void WeakReference::Deserialize(Local context, target = context->GetDataFromSnapshotOnce(weak_info->target) .ToLocalChecked(); } - new WeakReference(Environment::GetCurrent(context), - holder, - target, - weak_info->reference_count); + new WeakReference( + Realm::GetCurrent(context), holder, target, weak_info->reference_count); } void WeakReference::New(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); + Realm* realm = Realm::GetCurrent(args); CHECK(args.IsConstructCall()); CHECK(args[0]->IsObject()); - new WeakReference(env, args.This(), args[0].As()); + new WeakReference(realm, args.This(), args[0].As()); } void WeakReference::Get(const FunctionCallbackInfo& args) { diff --git a/src/node_util.h b/src/node_util.h index 9590842ae4764d..7192e080f2b08e 100644 --- a/src/node_util.h +++ b/src/node_util.h @@ -18,7 +18,7 @@ class WeakReference : public SnapshotableObject { static constexpr EmbedderObjectType type_int = EmbedderObjectType::k_util_weak_reference; - WeakReference(Environment* env, + WeakReference(Realm* realm, v8::Local object, v8::Local target); static void New(const v8::FunctionCallbackInfo& args); @@ -37,7 +37,7 @@ class WeakReference : public SnapshotableObject { }; private: - WeakReference(Environment* env, + WeakReference(Realm* realm, v8::Local object, v8::Local target, uint64_t reference_count); diff --git a/src/node_v8.cc b/src/node_v8.cc index 890f59eea673c5..f4900328cfe553 100644 --- a/src/node_v8.cc +++ b/src/node_v8.cc @@ -91,23 +91,24 @@ static const size_t kHeapCodeStatisticsPropertiesCount = HEAP_CODE_STATISTICS_PROPERTIES(V); #undef V -BindingData::BindingData(Environment* env, Local obj) - : SnapshotableObject(env, obj, type_int), - heap_statistics_buffer(env->isolate(), kHeapStatisticsPropertiesCount), - heap_space_statistics_buffer(env->isolate(), +BindingData::BindingData(Realm* realm, Local obj) + : SnapshotableObject(realm, obj, type_int), + heap_statistics_buffer(realm->isolate(), kHeapStatisticsPropertiesCount), + heap_space_statistics_buffer(realm->isolate(), kHeapSpaceStatisticsPropertiesCount), - heap_code_statistics_buffer(env->isolate(), + heap_code_statistics_buffer(realm->isolate(), kHeapCodeStatisticsPropertiesCount) { - obj->Set(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "heapStatisticsBuffer"), + Local context = realm->context(); + obj->Set(context, + FIXED_ONE_BYTE_STRING(realm->isolate(), "heapStatisticsBuffer"), heap_statistics_buffer.GetJSArray()) .Check(); - obj->Set(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "heapCodeStatisticsBuffer"), + obj->Set(context, + FIXED_ONE_BYTE_STRING(realm->isolate(), "heapCodeStatisticsBuffer"), heap_code_statistics_buffer.GetJSArray()) .Check(); - obj->Set(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "heapSpaceStatisticsBuffer"), + obj->Set(context, + FIXED_ONE_BYTE_STRING(realm->isolate(), "heapSpaceStatisticsBuffer"), heap_space_statistics_buffer.GetJSArray()) .Check(); } @@ -130,8 +131,8 @@ void BindingData::Deserialize(Local context, InternalFieldInfoBase* info) { DCHECK_EQ(index, BaseObject::kEmbedderType); HandleScope scope(context->GetIsolate()); - Environment* env = Environment::GetCurrent(context); - BindingData* binding = env->AddBindingData(context, holder); + Realm* realm = Realm::GetCurrent(context); + BindingData* binding = realm->AddBindingData(context, holder); CHECK_NOT_NULL(binding); } @@ -168,7 +169,7 @@ void SetHeapSnapshotNearHeapLimit(const FunctionCallbackInfo& args) { } void UpdateHeapStatisticsBuffer(const FunctionCallbackInfo& args) { - BindingData* data = Environment::GetBindingData(args); + BindingData* data = Realm::GetBindingData(args); HeapStatistics s; args.GetIsolate()->GetHeapStatistics(&s); AliasedFloat64Array& buffer = data->heap_statistics_buffer; @@ -179,7 +180,7 @@ void UpdateHeapStatisticsBuffer(const FunctionCallbackInfo& args) { void UpdateHeapSpaceStatisticsBuffer(const FunctionCallbackInfo& args) { - BindingData* data = Environment::GetBindingData(args); + BindingData* data = Realm::GetBindingData(args); HeapSpaceStatistics s; Isolate* const isolate = args.GetIsolate(); CHECK(args[0]->IsUint32()); @@ -194,7 +195,7 @@ void UpdateHeapSpaceStatisticsBuffer(const FunctionCallbackInfo& args) { } void UpdateHeapCodeStatisticsBuffer(const FunctionCallbackInfo& args) { - BindingData* data = Environment::GetBindingData(args); + BindingData* data = Realm::GetBindingData(args); HeapCodeStatistics s; args.GetIsolate()->GetHeapCodeAndMetadataStatistics(&s); AliasedFloat64Array& buffer = data->heap_code_statistics_buffer; @@ -393,9 +394,10 @@ void Initialize(Local target, Local unused, Local context, void* priv) { - Environment* env = Environment::GetCurrent(context); + Realm* realm = Realm::GetCurrent(context); + Environment* env = realm->env(); BindingData* const binding_data = - env->AddBindingData(context, target); + realm->AddBindingData(context, target); if (binding_data == nullptr) return; SetMethodNoSideEffect( diff --git a/src/node_v8.h b/src/node_v8.h index ecab454603b36b..2d95002bcfc473 100644 --- a/src/node_v8.h +++ b/src/node_v8.h @@ -18,7 +18,7 @@ struct InternalFieldInfoBase; namespace v8_utils { class BindingData : public SnapshotableObject { public: - BindingData(Environment* env, v8::Local obj); + BindingData(Realm* realm, v8::Local obj); using InternalFieldInfo = InternalFieldInfoBase;