diff --git a/src/env-inl.h b/src/env-inl.h index 47aa31ca9752..fc2a56647b65 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -866,6 +866,23 @@ void Environment::set_process_exit_handler( #undef V #undef VM +inline v8::Local IsolateData::ffi_fast_arguments_symbol() const { + return ffi_fast_arguments_symbol_.Get(isolate_); +} +inline void IsolateData::set_ffi_fast_arguments_symbol( + v8::Local value) { + CHECK(ffi_fast_arguments_symbol_.IsEmpty()); + ffi_fast_arguments_symbol_.Set(isolate_, value); +} +inline v8::Local IsolateData::ffi_fast_buffer_invoke_symbol() const { + return ffi_fast_buffer_invoke_symbol_.Get(isolate_); +} +inline void IsolateData::set_ffi_fast_buffer_invoke_symbol( + v8::Local value) { + CHECK(ffi_fast_buffer_invoke_symbol_.IsEmpty()); + ffi_fast_buffer_invoke_symbol_.Set(isolate_, value); +} + #define VP(PropertyName, StringValue) V(v8::Private, PropertyName) #define VY(PropertyName, StringValue) V(v8::Symbol, PropertyName) #define VS(PropertyName, StringValue) V(v8::String, PropertyName) @@ -881,6 +898,21 @@ void Environment::set_process_exit_handler( #undef VY #undef VP +inline v8::Local Environment::ffi_fast_arguments_symbol() const { + return isolate_data()->ffi_fast_arguments_symbol(); +} +inline void Environment::set_ffi_fast_arguments_symbol( + v8::Local value) { + isolate_data()->set_ffi_fast_arguments_symbol(value); +} +inline v8::Local Environment::ffi_fast_buffer_invoke_symbol() const { + return isolate_data()->ffi_fast_buffer_invoke_symbol(); +} +inline void Environment::set_ffi_fast_buffer_invoke_symbol( + v8::Local value) { + isolate_data()->set_ffi_fast_buffer_invoke_symbol(value); +} + #define V(Name, label, _, __) \ inline v8::Local Environment::Name##_permission_string() const { \ return isolate_data()->Name##_permission_string(); \ diff --git a/src/env.h b/src/env.h index f96feed36f8c..21a1c31511ce 100644 --- a/src/env.h +++ b/src/env.h @@ -205,6 +205,17 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer { inline v8::Local async_wrap_provider(int index) const; + // Symbols used by the FFI fast-call API to key per-function metadata on raw + // FFI functions. Kept out of env_properties.h so they are created lazily at + // runtime, not while the startup snapshot is built (allocating Symbols during + // serialization advances the isolate's identity-hash RNG, which can shift the + // snapshot hashes for Object.prototype/Function.prototype and make a function + // map and a plain-object map collide in V8's NormalizedMapCache). + inline v8::Local ffi_fast_arguments_symbol() const; + inline void set_ffi_fast_arguments_symbol(v8::Local value); + inline v8::Local ffi_fast_buffer_invoke_symbol() const; + inline void set_ffi_fast_buffer_invoke_symbol(v8::Local value); + size_t max_young_gen_size = 1; std::unordered_map> static_str_map; @@ -245,6 +256,9 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer { PERMISSIONS(V) #undef V + v8::Eternal ffi_fast_arguments_symbol_; + v8::Eternal ffi_fast_buffer_invoke_symbol_; + // Keep a list of all Persistent strings used for AsyncWrap Provider types. std::array, AsyncWrap::PROVIDERS_LENGTH> async_wrap_providers_; @@ -892,6 +906,12 @@ class Environment final : public MemoryRetainer { #undef VY #undef VP + // Runtime-created FFI fast-call API Symbols (see IsolateData). + inline v8::Local ffi_fast_arguments_symbol() const; + inline void set_ffi_fast_arguments_symbol(v8::Local value); + inline v8::Local ffi_fast_buffer_invoke_symbol() const; + inline void set_ffi_fast_buffer_invoke_symbol(v8::Local value); + #define V(Name, label, _, __) \ inline v8::Local Name##_permission_string() const; PERMISSIONS(V) diff --git a/src/env_properties.h b/src/env_properties.h index eb26d3b6cf05..063eb2f53296 100644 --- a/src/env_properties.h +++ b/src/env_properties.h @@ -50,8 +50,6 @@ V(ffi_sb_invoke_slow_symbol, "ffi_sb_invoke_slow_symbol") \ V(ffi_sb_arguments_symbol, "ffi_sb_arguments_symbol") \ V(ffi_sb_return_symbol, "ffi_sb_return_symbol") \ - V(ffi_fast_arguments_symbol, "ffi_fast_arguments_symbol") \ - V(ffi_fast_buffer_invoke_symbol, "ffi_fast_buffer_invoke_symbol") \ V(constructor_key_symbol, "constructor_key_symbol") \ V(handle_onclose_symbol, "handle_onclose") \ V(no_message_symbol, "no_message_symbol") \ diff --git a/src/node_ffi.cc b/src/node_ffi.cc index 7e3da95b958f..b5014b10c2f2 100644 --- a/src/node_ffi.cc +++ b/src/node_ffi.cc @@ -1378,7 +1378,21 @@ static void Initialize(Local target, env->ffi_sb_return_symbol()) .Check(); // Fast API wrappers use separate metadata Symbols so pointer-conversion - // routing does not depend on SharedBuffer internals. + // routing does not depend on SharedBuffer internals. These are created here + // (at runtime, on first `internalBinding('ffi')`) instead of being declared + // in env_properties.h, so they are not allocated while the startup snapshot + // is being built. Allocating Symbols during snapshot serialization advances + // the isolate's identity-hash RNG and shifts the identity hashes baked into + // the snapshot for Object.prototype / Function.prototype, which can make a + // function map and a plain-object map collide in V8's NormalizedMapCache. + if (env->ffi_fast_arguments_symbol().IsEmpty()) { + env->set_ffi_fast_arguments_symbol(v8::Symbol::New( + isolate, FIXED_ONE_BYTE_STRING(isolate, "ffi_fast_arguments_symbol"))); + } + if (env->ffi_fast_buffer_invoke_symbol().IsEmpty()) { + env->set_ffi_fast_buffer_invoke_symbol(v8::Symbol::New( + isolate, FIXED_ONE_BYTE_STRING(isolate, "ffi_fast_buffer_invoke_symbol"))); + } target ->Set(context, FIXED_ONE_BYTE_STRING(isolate, "kFastArguments"),