From db535b6caaef0fd8313be5192556d0c9e4a709fa Mon Sep 17 00:00:00 2001 From: legendecas Date: Wed, 26 Oct 2022 00:48:55 +0800 Subject: [PATCH] src: define per-isolate internal bindings registration callback Bindings that need to be loaded in distinct contexts of node::Realm in the same node::Environment instance needs to share the per-isolate templates. Add a new binding registration callback, which is called with per-IsolateData. This allows bindings to define templates and share them across realms eagerly, and avoid accidental modification on the templates when the per-context callback is called multiple times. This also tracks loaded bindings and built-in modules with node::Realm. PR-URL: https://github.com/nodejs/node/pull/45547 Refs: https://github.com/nodejs/node/issues/42528 Reviewed-By: Joyee Cheung Reviewed-By: Colin Ihrig Reviewed-By: James M Snell --- src/env-inl.h | 3 + src/env.cc | 26 ++------ src/env.h | 15 ++--- src/node_binding.cc | 95 ++++++++++++++++++++------- src/node_binding.h | 15 +++++ src/node_builtins.cc | 137 +++++++++++++++++++-------------------- src/node_builtins.h | 18 ++--- src/node_realm-inl.h | 4 ++ src/node_realm.cc | 23 +++++++ src/node_realm.h | 8 +++ src/node_snapshotable.cc | 14 ++-- src/util.cc | 17 +++++ src/util.h | 5 ++ 13 files changed, 239 insertions(+), 141 deletions(-) diff --git a/src/env-inl.h b/src/env-inl.h index 4446b6057d5977..cb7fe36ae23f56 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -821,6 +821,7 @@ void Environment::set_process_exit_handler( #undef VY #undef VP +#define VM(PropertyName) V(PropertyName##_binding, v8::FunctionTemplate) #define V(PropertyName, TypeName) \ inline v8::Local IsolateData::PropertyName() const { \ return PropertyName##_.Get(isolate_); \ @@ -829,7 +830,9 @@ void Environment::set_process_exit_handler( PropertyName##_.Set(isolate_, value); \ } PER_ISOLATE_TEMPLATE_PROPERTIES(V) + NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM) #undef V +#undef VM #define VP(PropertyName, StringValue) V(v8::Private, PropertyName) #define VY(PropertyName, StringValue) V(v8::Symbol, PropertyName) diff --git a/src/env.cc b/src/env.cc index 9e83b3ddebbb98..4e2aeda0ad1a06 100644 --- a/src/env.cc +++ b/src/env.cc @@ -314,6 +314,7 @@ IsolateDataSerializeInfo IsolateData::Serialize(SnapshotCreator* creator) { info.primitive_values.push_back(creator->AddData(async_wrap_provider(i))); uint32_t id = 0; +#define VM(PropertyName) V(PropertyName##_binding, FunctionTemplate) #define V(PropertyName, TypeName) \ do { \ Local field = PropertyName(); \ @@ -324,6 +325,7 @@ IsolateDataSerializeInfo IsolateData::Serialize(SnapshotCreator* creator) { id++; \ } while (0); PER_ISOLATE_TEMPLATE_PROPERTIES(V) + NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM) #undef V return info; @@ -368,6 +370,7 @@ void IsolateData::DeserializeProperties(const IsolateDataSerializeInfo* info) { const std::vector& values = info->template_values; i = 0; // index to the array uint32_t id = 0; +#define VM(PropertyName) V(PropertyName##_binding, FunctionTemplate) #define V(PropertyName, TypeName) \ do { \ if (values.size() > i && id == values[i].id) { \ @@ -388,6 +391,7 @@ void IsolateData::DeserializeProperties(const IsolateDataSerializeInfo* info) { } while (0); PER_ISOLATE_TEMPLATE_PROPERTIES(V); + NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM); #undef V } @@ -454,12 +458,12 @@ void IsolateData::CreateProperties() { NODE_ASYNC_PROVIDER_TYPES(V) #undef V - // TODO(legendecas): eagerly create per isolate templates. Local templ = FunctionTemplate::New(isolate()); templ->InstanceTemplate()->SetInternalFieldCount( BaseObject::kInternalFieldCount); templ->Inherit(BaseObject::GetConstructorTemplate(this)); set_binding_data_ctor_template(templ); + binding::CreateInternalBindingTemplates(this); contextify::ContextifyContext::InitializeGlobalTemplates(this); } @@ -1580,18 +1584,6 @@ void Environment::PrintInfoForSnapshotIfDebug() { if (enabled_debug_list()->enabled(DebugCategory::MKSNAPSHOT)) { fprintf(stderr, "At the exit of the Environment:\n"); principal_realm()->PrintInfoForSnapshot(); - fprintf(stderr, "\nBuiltins without cache:\n"); - for (const auto& s : builtins_without_cache) { - fprintf(stderr, "%s\n", s.c_str()); - } - fprintf(stderr, "\nBuiltins with cache:\n"); - for (const auto& s : builtins_with_cache) { - fprintf(stderr, "%s\n", s.c_str()); - } - fprintf(stderr, "\nStatic bindings (need to be registered):\n"); - for (const auto mod : internal_bindings) { - fprintf(stderr, "%s:%s\n", mod->nm_filename, mod->nm_modname); - } } } @@ -1599,11 +1591,6 @@ EnvSerializeInfo Environment::Serialize(SnapshotCreator* creator) { EnvSerializeInfo info; Local ctx = context(); - // Currently all modules are compiled without cache in builtin snapshot - // builder. - info.builtins = std::vector(builtins_without_cache.begin(), - builtins_without_cache.end()); - info.async_hooks = async_hooks_.Serialize(ctx, creator); info.immediate_info = immediate_info_.Serialize(ctx, creator); info.tick_info = tick_info_.Serialize(ctx, creator); @@ -1649,7 +1636,6 @@ void Environment::DeserializeProperties(const EnvSerializeInfo* info) { RunDeserializeRequests(); - builtins_in_snapshot = info->builtins; async_hooks_.Deserialize(ctx); immediate_info_.Deserialize(ctx); tick_info_.Deserialize(ctx); @@ -1840,8 +1826,6 @@ void Environment::MemoryInfo(MemoryTracker* tracker) const { // Iteratable STLs have their own sizes subtracted from the parent // by default. tracker->TrackField("isolate_data", isolate_data_); - tracker->TrackField("builtins_with_cache", builtins_with_cache); - tracker->TrackField("builtins_without_cache", builtins_without_cache); tracker->TrackField("destroy_async_id_list", destroy_async_id_list_); tracker->TrackField("exec_argv", exec_argv_); tracker->TrackField("exiting", exiting_); diff --git a/src/env.h b/src/env.h index 1120bd130edccf..e9bec3ba8d02b6 100644 --- a/src/env.h +++ b/src/env.h @@ -155,11 +155,14 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer { #undef VS #undef VP +#define VM(PropertyName) V(PropertyName##_binding, v8::FunctionTemplate) #define V(PropertyName, TypeName) \ inline v8::Local PropertyName() const; \ inline void set_##PropertyName(v8::Local value); PER_ISOLATE_TEMPLATE_PROPERTIES(V) + NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM) #undef V +#undef VM inline v8::Local async_wrap_provider(int index) const; @@ -179,6 +182,7 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer { #define VP(PropertyName, StringValue) V(v8::Private, PropertyName) #define VY(PropertyName, StringValue) V(v8::Symbol, PropertyName) #define VS(PropertyName, StringValue) V(v8::String, PropertyName) +#define VM(PropertyName) V(v8::FunctionTemplate, PropertyName##_binding) #define VT(PropertyName, TypeName) V(TypeName, PropertyName) #define V(TypeName, PropertyName) \ v8::Eternal PropertyName ## _; @@ -186,8 +190,9 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer { PER_ISOLATE_SYMBOL_PROPERTIES(VY) PER_ISOLATE_STRING_PROPERTIES(VS) PER_ISOLATE_TEMPLATE_PROPERTIES(VT) + NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM) #undef V -#undef V +#undef VM #undef VT #undef VS #undef VY @@ -457,7 +462,6 @@ struct DeserializeRequest { }; struct EnvSerializeInfo { - std::vector builtins; AsyncHooks::SerializeInfo async_hooks; TickInfo::SerializeInfo tick_info; ImmediateInfo::SerializeInfo immediate_info; @@ -713,13 +717,6 @@ class Environment : public MemoryRetainer { // List of id's that have been destroyed and need the destroy() cb called. inline std::vector* destroy_async_id_list(); - std::set internal_bindings; - std::set builtins_with_cache; - std::set builtins_without_cache; - // This is only filled during deserialization. We use a vector since - // it's only used for tests. - std::vector builtins_in_snapshot; - std::unordered_multimap hash_to_module_map; std::unordered_map id_to_module_map; std::unordered_map diff --git a/src/node_binding.cc b/src/node_binding.cc index ab25501dcbae96..dc5ddb7b33f221 100644 --- a/src/node_binding.cc +++ b/src/node_binding.cc @@ -102,6 +102,12 @@ NODE_BUILTIN_BINDINGS(V) #undef V +#define V(modname) \ + void _register_isolate_##modname(node::IsolateData* isolate_data, \ + v8::Local target); +NODE_BINDINGS_WITH_PER_ISOLATE_INIT(V) +#undef V + #ifdef _AIX // On AIX, dlopen() behaves differently from other operating systems, in that // it returns unique values from each call, rather than identical values, when @@ -229,9 +235,12 @@ static bool libc_may_be_musl() { return false; } namespace node { using v8::Context; +using v8::EscapableHandleScope; using v8::Exception; -using v8::Function; using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; +using v8::HandleScope; +using v8::Isolate; using v8::Local; using v8::Object; using v8::String; @@ -552,50 +561,86 @@ inline struct node_module* FindModule(struct node_module* list, return mp; } -static Local InitInternalBinding(Environment* env, - node_module* mod, - Local module) { - // Internal bindings don't have a "module" object, only exports. - Local ctor = env->binding_data_ctor_template() - ->GetFunction(env->context()) - .ToLocalChecked(); - Local exports = ctor->NewInstance(env->context()).ToLocalChecked(); +void CreateInternalBindingTemplates(IsolateData* isolate_data) { +#define V(modname) \ + do { \ + Local templ = \ + FunctionTemplate::New(isolate_data->isolate()); \ + templ->InstanceTemplate()->SetInternalFieldCount( \ + BaseObject::kInternalFieldCount); \ + templ->Inherit(BaseObject::GetConstructorTemplate(isolate_data)); \ + _register_isolate_##modname(isolate_data, templ); \ + isolate_data->set_##modname##_binding(templ); \ + } while (0); + NODE_BINDINGS_WITH_PER_ISOLATE_INIT(V) +#undef V +} + +static Local GetInternalBindingExportObject(IsolateData* isolate_data, + const char* mod_name, + Local context) { + Local ctor; +#define V(name) \ + if (strcmp(mod_name, #name) == 0) { \ + ctor = isolate_data->name##_binding(); \ + } else // NOLINT(readability/braces) + NODE_BINDINGS_WITH_PER_ISOLATE_INIT(V) +#undef V + { + ctor = isolate_data->binding_data_ctor_template(); + } + + Local obj = ctor->GetFunction(context) + .ToLocalChecked() + ->NewInstance(context) + .ToLocalChecked(); + return obj; +} + +static Local InitInternalBinding(Realm* realm, node_module* mod) { + EscapableHandleScope scope(realm->isolate()); + Local context = realm->context(); + Local exports = GetInternalBindingExportObject( + realm->isolate_data(), mod->nm_modname, context); CHECK_NULL(mod->nm_register_func); CHECK_NOT_NULL(mod->nm_context_register_func); - Local unused = Undefined(env->isolate()); - mod->nm_context_register_func(exports, unused, env->context(), mod->nm_priv); - return exports; + Local unused = Undefined(realm->isolate()); + // Internal bindings don't have a "module" object, only exports. + mod->nm_context_register_func(exports, unused, context, mod->nm_priv); + return scope.Escape(exports); } void GetInternalBinding(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); + Realm* realm = Realm::GetCurrent(args); + Isolate* isolate = realm->isolate(); + HandleScope scope(isolate); + Local context = realm->context(); CHECK(args[0]->IsString()); Local module = args[0].As(); - node::Utf8Value module_v(env->isolate(), module); + node::Utf8Value module_v(isolate, module); Local exports; node_module* mod = FindModule(modlist_internal, *module_v, NM_F_INTERNAL); if (mod != nullptr) { - exports = InitInternalBinding(env, mod, module); - env->internal_bindings.insert(mod); + exports = InitInternalBinding(realm, mod); + realm->internal_bindings.insert(mod); } else if (!strcmp(*module_v, "constants")) { - exports = Object::New(env->isolate()); - CHECK( - exports->SetPrototype(env->context(), Null(env->isolate())).FromJust()); - DefineConstants(env->isolate(), exports); + exports = Object::New(isolate); + CHECK(exports->SetPrototype(context, Null(isolate)).FromJust()); + DefineConstants(isolate, exports); } else if (!strcmp(*module_v, "natives")) { - exports = builtins::BuiltinLoader::GetSourceObject(env->context()); + exports = builtins::BuiltinLoader::GetSourceObject(context); // Legacy feature: process.binding('natives').config contains stringified // config.gypi CHECK(exports - ->Set(env->context(), - env->config_string(), - builtins::BuiltinLoader::GetConfigString(env->isolate())) + ->Set(context, + realm->isolate_data()->config_string(), + builtins::BuiltinLoader::GetConfigString(isolate)) .FromJust()); } else { - return THROW_ERR_INVALID_MODULE(env, "No such binding: %s", *module_v); + return THROW_ERR_INVALID_MODULE(isolate, "No such binding: %s", *module_v); } args.GetReturnValue().Set(exports); diff --git a/src/node_binding.h b/src/node_binding.h index 13681bac67c64f..b403bf26b81d91 100644 --- a/src/node_binding.h +++ b/src/node_binding.h @@ -24,6 +24,8 @@ static_assert(static_cast(NM_F_LINKED) == static_cast(node::ModuleFlags::kLinked), "NM_F_LINKED != node::ModuleFlags::kLinked"); +#define NODE_BINDINGS_WITH_PER_ISOLATE_INIT(V) V(builtins) + #define NODE_BINDING_CONTEXT_AWARE_CPP(modname, regfunc, priv, flags) \ static node::node_module _module = { \ NODE_MODULE_VERSION, \ @@ -44,9 +46,20 @@ void napi_module_register_by_symbol(v8::Local exports, namespace node { +// Define a node internal binding that may be loaded in a context of +// a node::Environment. +// If an internal binding needs initializing per-isolate templates, define +// with NODE_BINDING_PER_ISOLATE_INIT too. #define NODE_BINDING_CONTEXT_AWARE_INTERNAL(modname, regfunc) \ NODE_BINDING_CONTEXT_AWARE_CPP(modname, regfunc, nullptr, NM_F_INTERNAL) +// Define a per-isolate initialization function for a node internal binding. +#define NODE_BINDING_PER_ISOLATE_INIT(modname, per_isolate_func) \ + void _register_isolate_##modname(node::IsolateData* isolate_data, \ + v8::Local target) { \ + per_isolate_func(isolate_data, target); \ + } + // Globals per process // This is set by node::Init() which is used by embedders extern bool node_is_initialized; @@ -87,6 +100,8 @@ class DLib { // use the __attribute__((constructor)). Need to // explicitly call the _register* functions. void RegisterBuiltinBindings(); +// Create per-isolate templates for the internal bindings. +void CreateInternalBindingTemplates(IsolateData* isolate_data); void GetInternalBinding(const v8::FunctionCallbackInfo& args); void GetLinkedBinding(const v8::FunctionCallbackInfo& args); void DLOpen(const v8::FunctionCallbackInfo& args); diff --git a/src/node_builtins.cc b/src/node_builtins.cc index 77fa5741de43d6..e68cc71bfcb827 100644 --- a/src/node_builtins.cc +++ b/src/node_builtins.cc @@ -13,6 +13,7 @@ using v8::DEFAULT; using v8::EscapableHandleScope; using v8::Function; using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; using v8::IntegrityLevel; using v8::Isolate; using v8::Local; @@ -20,6 +21,7 @@ using v8::MaybeLocal; using v8::Name; using v8::None; using v8::Object; +using v8::ObjectTemplate; using v8::PropertyCallbackInfo; using v8::ScriptCompiler; using v8::ScriptOrigin; @@ -371,8 +373,9 @@ MaybeLocal BuiltinLoader::LookupAndCompileInternal( // Returns Local of the compiled module if return_code_cache // is false (we are only compiling the function). // Otherwise return a Local containing the cache. -MaybeLocal BuiltinLoader::LookupAndCompile( - Local context, const char* id, Environment* optional_env) { +MaybeLocal BuiltinLoader::LookupAndCompile(Local context, + const char* id, + Realm* optional_realm) { Result result; std::vector> parameters; Isolate* isolate = context->GetIsolate(); @@ -426,8 +429,8 @@ MaybeLocal BuiltinLoader::LookupAndCompile( MaybeLocal maybe = GetInstance()->LookupAndCompileInternal( context, id, ¶meters, &result); - if (optional_env != nullptr) { - RecordResult(id, result, optional_env); + if (optional_realm != nullptr) { + RecordResult(id, result, optional_realm); } return maybe; } @@ -482,18 +485,17 @@ MaybeLocal BuiltinLoader::CompileAndCall(Local context, // all the other cases: the arguments are generated in the JS-land loader. UNREACHABLE(); } - return CompileAndCall( - context, id, arguments.size(), arguments.data(), realm->env()); + return CompileAndCall(context, id, arguments.size(), arguments.data(), realm); } MaybeLocal BuiltinLoader::CompileAndCall(Local context, const char* id, int argc, Local argv[], - Environment* optional_env) { + Realm* optional_realm) { // Arguments must match the parameters specified in // BuiltinLoader::LookupAndCompile(). - MaybeLocal maybe_fn = LookupAndCompile(context, id, optional_env); + MaybeLocal maybe_fn = LookupAndCompile(context, id, optional_realm); Local fn; if (!maybe_fn.ToLocal(&fn)) { return MaybeLocal(); @@ -596,44 +598,44 @@ void BuiltinLoader::GetBuiltinCategories( } void BuiltinLoader::GetCacheUsage(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - Isolate* isolate = env->isolate(); - Local context = env->context(); + Realm* realm = Realm::GetCurrent(args); + Isolate* isolate = realm->isolate(); + Local context = realm->context(); Local result = Object::New(isolate); Local builtins_with_cache_js; Local builtins_without_cache_js; Local builtins_in_snapshot_js; - if (!ToV8Value(context, env->builtins_with_cache) + if (!ToV8Value(context, realm->builtins_with_cache) .ToLocal(&builtins_with_cache_js)) { return; } if (result - ->Set(env->context(), + ->Set(context, OneByteString(isolate, "compiledWithCache"), builtins_with_cache_js) .IsNothing()) { return; } - if (!ToV8Value(context, env->builtins_without_cache) + if (!ToV8Value(context, realm->builtins_without_cache) .ToLocal(&builtins_without_cache_js)) { return; } if (result - ->Set(env->context(), + ->Set(context, OneByteString(isolate, "compiledWithoutCache"), builtins_without_cache_js) .IsNothing()) { return; } - if (!ToV8Value(context, env->builtins_in_snapshot) + if (!ToV8Value(context, realm->builtins_in_snapshot) .ToLocal(&builtins_without_cache_js)) { return; } if (result - ->Set(env->context(), + ->Set(context, OneByteString(isolate, "compiledInSnapshot"), builtins_without_cache_js) .IsNothing()) { @@ -659,21 +661,21 @@ void BuiltinLoader::ConfigStringGetter( void BuiltinLoader::RecordResult(const char* id, BuiltinLoader::Result result, - Environment* env) { + Realm* realm) { if (result == BuiltinLoader::Result::kWithCache) { - env->builtins_with_cache.insert(id); + realm->builtins_with_cache.insert(id); } else { - env->builtins_without_cache.insert(id); + realm->builtins_without_cache.insert(id); } } void BuiltinLoader::CompileFunction(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); + Realm* realm = Realm::GetCurrent(args); CHECK(args[0]->IsString()); - node::Utf8Value id_v(env->isolate(), args[0].As()); + node::Utf8Value id_v(realm->isolate(), args[0].As()); const char* id = *id_v; MaybeLocal maybe = - GetInstance()->LookupAndCompile(env->context(), id, env); + GetInstance()->LookupAndCompile(realm->context(), id, realm); Local fn; if (maybe.ToLocal(&fn)) { args.GetReturnValue().Set(fn); @@ -685,51 +687,44 @@ void BuiltinLoader::HasCachedBuiltins(const FunctionCallbackInfo& args) { v8::Boolean::New(args.GetIsolate(), GetInstance()->has_code_cache_)); } -// TODO(joyeecheung): It is somewhat confusing that Class::Initialize -// is used to initialize to the binding, but it is the current convention. -// Rename this across the code base to something that makes more sense. -void BuiltinLoader::Initialize(Local target, - Local unused, - Local context, - void* priv) { - Environment* env = Environment::GetCurrent(context); - Isolate* isolate = env->isolate(); - - target - ->SetAccessor(context, - env->config_string(), - ConfigStringGetter, - nullptr, - MaybeLocal(), - DEFAULT, - None, - SideEffectType::kHasNoSideEffect) - .Check(); - target - ->SetAccessor(context, - FIXED_ONE_BYTE_STRING(isolate, "builtinIds"), - BuiltinIdsGetter, - nullptr, - MaybeLocal(), - DEFAULT, - None, - SideEffectType::kHasNoSideEffect) - .Check(); - - target - ->SetAccessor(context, - FIXED_ONE_BYTE_STRING(isolate, "builtinCategories"), - GetBuiltinCategories, - nullptr, - Local(), - DEFAULT, - None, - SideEffectType::kHasNoSideEffect) - .Check(); - - SetMethod(context, target, "getCacheUsage", BuiltinLoader::GetCacheUsage); - SetMethod(context, target, "compileFunction", BuiltinLoader::CompileFunction); - SetMethod(context, target, "hasCachedBuiltins", HasCachedBuiltins); +void BuiltinLoader::CreatePerIsolateProperties(IsolateData* isolate_data, + Local target) { + Isolate* isolate = isolate_data->isolate(); + Local proto = target->PrototypeTemplate(); + + proto->SetAccessor(isolate_data->config_string(), + ConfigStringGetter, + nullptr, + Local(), + DEFAULT, + None, + SideEffectType::kHasNoSideEffect); + + proto->SetAccessor(FIXED_ONE_BYTE_STRING(isolate, "builtinIds"), + BuiltinIdsGetter, + nullptr, + Local(), + DEFAULT, + None, + SideEffectType::kHasNoSideEffect); + + proto->SetAccessor(FIXED_ONE_BYTE_STRING(isolate, "builtinCategories"), + GetBuiltinCategories, + nullptr, + Local(), + DEFAULT, + None, + SideEffectType::kHasNoSideEffect); + + SetMethod(isolate, proto, "getCacheUsage", BuiltinLoader::GetCacheUsage); + SetMethod(isolate, proto, "compileFunction", BuiltinLoader::CompileFunction); + SetMethod(isolate, proto, "hasCachedBuiltins", HasCachedBuiltins); +} + +void BuiltinLoader::CreatePerContextProperties(Local target, + Local unused, + Local context, + void* priv) { // internalBinding('builtins') should be frozen target->SetIntegrityLevel(context, IntegrityLevel::kFrozen).FromJust(); } @@ -747,7 +742,9 @@ void BuiltinLoader::RegisterExternalReferences( } // namespace builtins } // namespace node -NODE_BINDING_CONTEXT_AWARE_INTERNAL(builtins, - node::builtins::BuiltinLoader::Initialize) +NODE_BINDING_PER_ISOLATE_INIT( + builtins, node::builtins::BuiltinLoader::CreatePerIsolateProperties) +NODE_BINDING_CONTEXT_AWARE_INTERNAL( + builtins, node::builtins::BuiltinLoader::CreatePerContextProperties) NODE_BINDING_EXTERNAL_REFERENCE( builtins, node::builtins::BuiltinLoader::RegisterExternalReferences) diff --git a/src/node_builtins.h b/src/node_builtins.h index 42338350765874..2f95e04109fdbd 100644 --- a/src/node_builtins.h +++ b/src/node_builtins.h @@ -44,24 +44,24 @@ class NODE_EXTERN_PRIVATE BuiltinLoader { BuiltinLoader& operator=(const BuiltinLoader&) = delete; static void RegisterExternalReferences(ExternalReferenceRegistry* registry); - static void Initialize(v8::Local target, - v8::Local unused, - v8::Local context, - void* priv); + static void CreatePerIsolateProperties( + IsolateData* isolate_data, v8::Local target); + static void CreatePerContextProperties(v8::Local target, + v8::Local unused, + v8::Local context, + void* priv); // The parameters used to compile the scripts are detected based on // the pattern of the id. static v8::MaybeLocal LookupAndCompile( - v8::Local context, - const char* id, - Environment* optional_env); + v8::Local context, const char* id, Realm* optional_realm); static v8::MaybeLocal CompileAndCall( v8::Local context, const char* id, int argc, v8::Local argv[], - Environment* optional_env); + Realm* optional_realm); static v8::MaybeLocal CompileAndCall( v8::Local context, const char* id, Realm* realm); @@ -118,7 +118,7 @@ class NODE_EXTERN_PRIVATE BuiltinLoader { static void RecordResult(const char* id, BuiltinLoader::Result result, - Environment* env); + Realm* realm); static void GetBuiltinCategories( v8::Local property, const v8::PropertyCallbackInfo& info); diff --git a/src/node_realm-inl.h b/src/node_realm-inl.h index 743071315fcceb..95a09dafac76e3 100644 --- a/src/node_realm-inl.h +++ b/src/node_realm-inl.h @@ -30,6 +30,10 @@ inline Realm* Realm::GetCurrent(const v8::PropertyCallbackInfo& info) { return GetCurrent(info.GetIsolate()->GetCurrentContext()); } +inline IsolateData* Realm::isolate_data() const { + return env_->isolate_data(); +} + inline Environment* Realm::env() const { return env_; } diff --git a/src/node_realm.cc b/src/node_realm.cc index 182fc9b2c4b249..42d80ac467fa97 100644 --- a/src/node_realm.cc +++ b/src/node_realm.cc @@ -46,6 +46,8 @@ void Realm::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("env", env_); tracker->TrackField("cleanup_queue", cleanup_queue_); + tracker->TrackField("builtins_with_cache", builtins_with_cache); + tracker->TrackField("builtins_without_cache", builtins_without_cache); ForEachBaseObject([&](BaseObject* obj) { if (obj->IsDoneInitializing()) { @@ -102,6 +104,11 @@ RealmSerializeInfo Realm::Serialize(SnapshotCreator* creator) { RealmSerializeInfo info; Local ctx = context(); + // Currently all modules are compiled without cache in builtin snapshot + // builder. + info.builtins = std::vector(builtins_without_cache.begin(), + builtins_without_cache.end()); + uint32_t id = 0; #define V(PropertyName, TypeName) \ do { \ @@ -126,6 +133,8 @@ RealmSerializeInfo Realm::Serialize(SnapshotCreator* creator) { void Realm::DeserializeProperties(const RealmSerializeInfo* info) { Local ctx = context(); + builtins_in_snapshot = info->builtins; + const std::vector& values = info->persistent_values; size_t i = 0; // index to the array uint32_t id = 0; @@ -305,6 +314,20 @@ void Realm::PrintInfoForSnapshot() { std::cout << "#" << i++ << " " << obj << ": " << obj->MemoryInfoName() << "\n"; }); + + fprintf(stderr, "\nnBuiltins without cache:\n"); + for (const auto& s : builtins_without_cache) { + fprintf(stderr, "%s\n", s.c_str()); + } + fprintf(stderr, "\nBuiltins with cache:\n"); + for (const auto& s : builtins_with_cache) { + fprintf(stderr, "%s\n", s.c_str()); + } + fprintf(stderr, "\nStatic bindings (need to be registered):\n"); + for (const auto mod : internal_bindings) { + fprintf(stderr, "%s:%s\n", mod->nm_filename, mod->nm_modname); + } + fprintf(stderr, "End of the Realm.\n"); } diff --git a/src/node_realm.h b/src/node_realm.h index 45d41e05c74534..af0f25ccd77f90 100644 --- a/src/node_realm.h +++ b/src/node_realm.h @@ -12,6 +12,7 @@ namespace node { struct RealmSerializeInfo { + std::vector builtins; std::vector persistent_values; std::vector native_objects; @@ -99,6 +100,13 @@ class Realm : public MemoryRetainer { PER_REALM_STRONG_PERSISTENT_VALUES(V) #undef V + std::set internal_bindings; + std::set builtins_with_cache; + std::set builtins_without_cache; + // This is only filled during deserialization. We use a vector since + // it's only used for tests. + std::vector builtins_in_snapshot; + private: void InitializeContext(v8::Local context, const RealmSerializeInfo* realm_info); diff --git a/src/node_snapshotable.cc b/src/node_snapshotable.cc index f858ba493b49dc..81ae67f066b6f3 100644 --- a/src/node_snapshotable.cc +++ b/src/node_snapshotable.cc @@ -99,6 +99,9 @@ std::ostream& operator<<(std::ostream& output, std::ostream& operator<<(std::ostream& output, const RealmSerializeInfo& i) { output << "{\n" + << "// -- builtins begins --\n" + << i.builtins << ",\n" + << "// -- builtins ends --\n" << "// -- persistent_values begins --\n" << i.persistent_values << ",\n" << "// -- persistent_values ends --\n" @@ -112,9 +115,6 @@ std::ostream& operator<<(std::ostream& output, const RealmSerializeInfo& i) { std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) { output << "{\n" - << "// -- builtins begins --\n" - << i.builtins << ",\n" - << "// -- builtins ends --\n" << "// -- async_hooks begins --\n" << i.async_hooks << ",\n" << "// -- async_hooks ends --\n" @@ -704,6 +704,7 @@ template <> RealmSerializeInfo FileReader::Read() { per_process::Debug(DebugCategory::MKSNAPSHOT, "Read()\n"); RealmSerializeInfo result; + result.builtins = ReadVector(); result.persistent_values = ReadVector(); result.native_objects = ReadVector(); result.context = Read(); @@ -718,7 +719,8 @@ size_t FileWriter::Write(const RealmSerializeInfo& data) { } // Use += here to ensure order of evaluation. - size_t written_total = WriteVector(data.persistent_values); + size_t written_total = WriteVector(data.builtins); + written_total += WriteVector(data.persistent_values); written_total += WriteVector(data.native_objects); written_total += Write(data.context); @@ -730,7 +732,6 @@ template <> EnvSerializeInfo FileReader::Read() { per_process::Debug(DebugCategory::MKSNAPSHOT, "Read()\n"); EnvSerializeInfo result; - result.builtins = ReadVector(); result.async_hooks = Read(); result.tick_info = Read(); result.immediate_info = Read(); @@ -751,8 +752,7 @@ size_t FileWriter::Write(const EnvSerializeInfo& data) { } // Use += here to ensure order of evaluation. - size_t written_total = WriteVector(data.builtins); - written_total += Write(data.async_hooks); + size_t written_total = Write(data.async_hooks); written_total += Write(data.tick_info); written_total += Write(data.immediate_info); written_total += Write( diff --git a/src/util.cc b/src/util.cc index 584a6a32d6cc5b..762bf393e8e086 100644 --- a/src/util.cc +++ b/src/util.cc @@ -356,6 +356,23 @@ void SetMethod(Local context, function->SetName(name_string); // NODE_SET_METHOD() compatibility. } +void SetMethod(v8::Isolate* isolate, + v8::Local that, + const char* name, + v8::FunctionCallback callback) { + Local t = + NewFunctionTemplate(isolate, + callback, + Local(), + v8::ConstructorBehavior::kThrow, + v8::SideEffectType::kHasSideEffect); + // kInternalized strings are created in the old space. + const v8::NewStringType type = v8::NewStringType::kInternalized; + Local name_string = + v8::String::NewFromUtf8(isolate, name, type).ToLocalChecked(); + that->Set(name_string, t); +} + void SetFastMethod(Local context, Local that, const char* name, diff --git a/src/util.h b/src/util.h index 7a3885ec8f7b07..fa4b28745dbe6c 100644 --- a/src/util.h +++ b/src/util.h @@ -881,6 +881,11 @@ void SetMethod(v8::Local context, v8::Local that, const char* name, v8::FunctionCallback callback); +// Similar to SetProtoMethod but without receiver signature checks. +void SetMethod(v8::Isolate* isolate, + v8::Local that, + const char* name, + v8::FunctionCallback callback); void SetFastMethod(v8::Local context, v8::Local that,