diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js index 98533b7828d3ff..fd632be10099b0 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js @@ -186,6 +186,9 @@ function setupUserModules(forceDefaultLoader = false) { initializeESMLoader(forceDefaultLoader); const CJSLoader = require('internal/modules/cjs/loader'); assert(!CJSLoader.hasLoadedAnyUserCJSModule); + if (getEmbedderOptions().hasEmbedderPreload) { + runEmbedderPreload(); + } // Do not enable preload modules if custom loaders are disabled. // For example, loader workers are responsible for doing this themselves. // And preload modules are not supported in ShadowRealm as well. @@ -754,6 +757,10 @@ function initializeFrozenIntrinsics() { } } +function runEmbedderPreload() { + internalBinding('mksnapshot').runEmbedderPreload(process, require); +} + function loadPreloadModules() { // For user code, we preload modules if `-r` is passed const preloadModules = getOptionValue('--require'); diff --git a/src/env-inl.h b/src/env-inl.h index 56a96def344194..2373f90a954af2 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -438,6 +438,14 @@ inline void Environment::set_embedder_entry_point(StartExecutionCallback&& fn) { embedder_entry_point_ = std::move(fn); } +inline const EmbedderPreloadCallback& Environment::embedder_preload() const { + return embedder_preload_; +} + +inline void Environment::set_embedder_preload(EmbedderPreloadCallback&& fn) { + embedder_preload_ = std::move(fn); +} + inline double Environment::new_async_id() { async_hooks()->async_id_fields()[AsyncHooks::kAsyncIdCounter] += 1; return async_hooks()->async_id_fields()[AsyncHooks::kAsyncIdCounter]; diff --git a/src/env.h b/src/env.h index 59ee108e5e8cc1..aacbb2ea2582bd 100644 --- a/src/env.h +++ b/src/env.h @@ -588,6 +588,11 @@ void DefaultProcessExitHandlerInternal(Environment* env, ExitCode exit_code); v8::Maybe SpinEventLoopInternal(Environment* env); v8::Maybe EmitProcessExitInternal(Environment* env); +using EmbedderPreloadCallback = + std::function process, + v8::Local require)>; + /** * Environment is a per-isolate data structure that represents an execution * environment. Each environment has a principal realm. An environment can @@ -1002,6 +1007,15 @@ class Environment : public MemoryRetainer { inline const StartExecutionCallback& embedder_entry_point() const; inline void set_embedder_entry_point(StartExecutionCallback&& fn); + // Set a preload function that will run before executing the entry point, this + // is usually used by embedders to inject scripts. + // The function is executed with preload(process, require), and the passed + // |require| function has access to internal Node.js modules. + // The preload function is inherited by worker threads and thus will run in + // work threads, so make sure the function is thread-safe. + inline const EmbedderPreloadCallback& embedder_preload() const; + inline void set_embedder_preload(EmbedderPreloadCallback&& fn); + inline void set_process_exit_handler( std::function&& handler); @@ -1208,6 +1222,7 @@ class Environment : public MemoryRetainer { builtins::BuiltinLoader builtin_loader_; StartExecutionCallback embedder_entry_point_; + EmbedderPreloadCallback embedder_preload_; // Used by allocate_managed_buffer() and release_managed_buffer() to keep // track of the BackingStore for a given pointer. diff --git a/src/node_options.cc b/src/node_options.cc index 7b5152172c5ce7..f4bd5e16402928 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -1304,6 +1304,12 @@ void GetEmbedderOptions(const FunctionCallbackInfo& args) { .IsNothing()) return; + if (ret->Set(context, + FIXED_ONE_BYTE_STRING(env->isolate(), "hasEmbedderPreload"), + Boolean::New(isolate, env->embedder_preload() != nullptr)) + .IsNothing()) + return; + args.GetReturnValue().Set(ret); } diff --git a/src/node_snapshotable.cc b/src/node_snapshotable.cc index c59a4cdccb9c8a..05abc3d418e4b7 100644 --- a/src/node_snapshotable.cc +++ b/src/node_snapshotable.cc @@ -1453,6 +1453,13 @@ static void RunEmbedderEntryPoint(const FunctionCallbackInfo& args) { } } +static void RunEmbedderPreload(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(env->embedder_preload()); + CHECK_EQ(args.Length(), 2); + env->embedder_preload()(env, args[0], args[1]); +} + void CompileSerializeMain(const FunctionCallbackInfo& args) { CHECK(args[0]->IsString()); Local filename = args[0].As(); @@ -1577,6 +1584,7 @@ void CreatePerIsolateProperties(IsolateData* isolate_data, Local target) { Isolate* isolate = isolate_data->isolate(); SetMethod(isolate, target, "runEmbedderEntryPoint", RunEmbedderEntryPoint); + SetMethod(isolate, target, "runEmbedderPreload", RunEmbedderPreload); SetMethod(isolate, target, "compileSerializeMain", CompileSerializeMain); SetMethod(isolate, target, "setSerializeCallback", SetSerializeCallback); SetMethod(isolate, target, "setDeserializeCallback", SetDeserializeCallback); @@ -1590,6 +1598,7 @@ void CreatePerIsolateProperties(IsolateData* isolate_data, void RegisterExternalReferences(ExternalReferenceRegistry* registry) { registry->Register(RunEmbedderEntryPoint); + registry->Register(RunEmbedderPreload); registry->Register(CompileSerializeMain); registry->Register(SetSerializeCallback); registry->Register(SetDeserializeCallback); diff --git a/src/node_worker.cc b/src/node_worker.cc index 552fdc438a0895..6f2885d4c44da3 100644 --- a/src/node_worker.cc +++ b/src/node_worker.cc @@ -63,6 +63,7 @@ Worker::Worker(Environment* env, thread_id_(AllocateEnvironmentThreadId()), name_(name), env_vars_(env_vars), + embedder_preload_(env->embedder_preload()), snapshot_data_(snapshot_data) { Debug(this, "Creating new worker instance with thread id %llu", thread_id_.id); @@ -370,6 +371,7 @@ void Worker::Run() { if (is_stopped()) return; CHECK_NOT_NULL(env_); env_->set_env_vars(std::move(env_vars_)); + env_->set_embedder_preload(std::move(embedder_preload_)); SetProcessExitHandler(env_.get(), [this](Environment*, int exit_code) { Exit(static_cast(exit_code)); }); diff --git a/src/node_worker.h b/src/node_worker.h index 531e2b5287010f..07fd7b460654e1 100644 --- a/src/node_worker.h +++ b/src/node_worker.h @@ -114,6 +114,7 @@ class Worker : public AsyncWrap { std::unique_ptr child_port_data_; std::shared_ptr env_vars_; + EmbedderPreloadCallback embedder_preload_; // A raw flag that is used by creator and worker threads to // sync up on pre-mature termination of worker - while in the