Skip to content

Commit

Permalink
chore: update src_preload_function_for_environment.patch (#41503)
Browse files Browse the repository at this point in the history
* chore: update src_preload_function_for_environment.patch

* chore: update patches

---------

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
  • Loading branch information
zcbenz and patchup[bot] committed Mar 4, 2024
1 parent 7b2c526 commit f53fad8
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 172 deletions.
276 changes: 110 additions & 166 deletions patches/node/src_preload_function_for_environment.patch
@@ -1,23 +1,9 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Cheng Zhao <zcbenz@gmail.com>
Date: Mon, 22 Jan 2024 13:45:55 +0900
Date: Mon, 4 Mar 2024 11:41:18 +0900
Subject: src: preload function for Environment

https://github.com/nodejs/node/pull/51539

This PR adds a |preload| arg to the node::CreateEnvironment to allow
embedders to set a preload function for the environment, which will run
after the environment is loaded and before the main script runs.

This is similiar to the --require CLI option, but runs a C++ function,
and can only be set by embedders.

The preload function can be used by embedders to inject scripts before
running the main script, for example:
1. In Electron it is used to initialize the ASAR virtual filesystem,
inject custom process properties, etc.
2. In VS Code it can be used to reset the module search paths for
extensions.
Backport https://github.com/nodejs/node/pull/51539

diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js
index 31f5d1528c840a2926b59b2b1c82ff265588a37a..1e37bd59f53d3a194b2492c83f8e7299c42c828c 100644
Expand Down Expand Up @@ -45,120 +31,80 @@ index 31f5d1528c840a2926b59b2b1c82ff265588a37a..1e37bd59f53d3a194b2492c83f8e7299
// For user code, we preload modules if `-r` is passed
const preloadModules = getOptionValue('--require');
diff --git a/src/api/environment.cc b/src/api/environment.cc
index c4caef25af670658965fc740ce03c2d2c4ed3e66..19443a9672441da5b98921eab9385083a72e3b7e 100644
index c4caef25af670658965fc740ce03c2d2c4ed3e66..465ff36b79c36d29777c7b1abe3a35d3be5de93e 100644
--- a/src/api/environment.cc
+++ b/src/api/environment.cc
@@ -404,14 +404,16 @@ Environment* CreateEnvironment(
const std::vector<std::string>& exec_args,
EnvironmentFlags::Flags flags,
ThreadId thread_id,
- std::unique_ptr<InspectorParentHandle> inspector_parent_handle) {
+ std::unique_ptr<InspectorParentHandle> inspector_parent_handle,
@@ -484,18 +484,22 @@ NODE_EXTERN std::unique_ptr<InspectorParentHandle> GetInspectorParentHandle(
#endif
}

-MaybeLocal<Value> LoadEnvironment(
- Environment* env,
- StartExecutionCallback cb) {
+MaybeLocal<Value> LoadEnvironment(Environment* env,
+ StartExecutionCallback cb,
+ EmbedderPreloadCallback preload) {
env->InitializeLibuv();
env->InitializeDiagnostics();
+ if (preload) {
+ env->set_embedder_preload(std::move(preload));
+ }

return StartExecution(env, cb);
}

MaybeLocal<Value> LoadEnvironment(
Environment* env,
- const char* main_script_source_utf8) {
+ const char* main_script_source_utf8,
+ EmbedderPreloadCallback preload) {
Isolate* isolate = context->GetIsolate();
HandleScope handle_scope(isolate);
Context::Scope context_scope(context);
// TODO(addaleax): This is a much better place for parsing per-Environment
// options than the global parse call.
Environment* env = new Environment(
- isolate_data, context, args, exec_args, nullptr, flags, thread_id);
+ isolate_data, context, args, exec_args, nullptr, flags, thread_id,
CHECK_NOT_NULL(main_script_source_utf8);
return LoadEnvironment(
env, [&](const StartExecutionCallbackInfo& info) -> MaybeLocal<Value> {
@@ -508,7 +512,8 @@ MaybeLocal<Value> LoadEnvironment(
std::vector<Local<Value>> args = {realm->process_object(),
realm->builtin_module_require()};
return realm->ExecuteBootstrapper(name.c_str(), &args);
- });
+ },
+ std::move(preload));
}

#if HAVE_INSPECTOR
if (env->should_create_inspector()) {
Environment* GetCurrentEnvironment(Local<Context> context) {
diff --git a/src/env-inl.h b/src/env-inl.h
index debd982c75805c51ea7d01229b9d635550060503..6af9217acb6f22c89bc92708aa9ab3d021c5e5bf 100644
index debd982c75805c51ea7d01229b9d635550060503..a6f160b6e28a01a31d6bb06fcfa384c748463c50 100644
--- a/src/env-inl.h
+++ b/src/env-inl.h
@@ -388,6 +388,10 @@ inline std::vector<double>* Environment::destroy_async_id_list() {
@@ -388,6 +388,14 @@ inline std::vector<double>* Environment::destroy_async_id_list() {
return &destroy_async_id_list_;
}

+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.cc b/src/env.cc
index 6e8b314680c9175d8d513cc72382012ae5e70b26..e416bca327f181884ff8dad2b3a82ad826c0f3b5 100644
--- a/src/env.cc
+++ b/src/env.cc
@@ -645,7 +645,8 @@ Environment::Environment(IsolateData* isolate_data,
const std::vector<std::string>& exec_args,
const EnvSerializeInfo* env_info,
EnvironmentFlags::Flags flags,
- ThreadId thread_id)
+ ThreadId thread_id,
+ EmbedderPreloadCallback preload)
: isolate_(isolate),
isolate_data_(isolate_data),
async_hooks_(isolate, MAYBE_FIELD_PTR(env_info, async_hooks)),
@@ -668,7 +669,8 @@ Environment::Environment(IsolateData* isolate_data,
flags_(flags),
thread_id_(thread_id.id == static_cast<uint64_t>(-1)
? AllocateEnvironmentThreadId().id
- : thread_id.id) {
+ : thread_id.id),
+ embedder_preload_(std::move(preload)) {
// We'll be creating new objects so make sure we've entered the context.
HandleScope handle_scope(isolate);

@@ -738,14 +740,16 @@ Environment::Environment(IsolateData* isolate_data,
const std::vector<std::string>& exec_args,
const EnvSerializeInfo* env_info,
EnvironmentFlags::Flags flags,
- ThreadId thread_id)
+ ThreadId thread_id,
+ EmbedderPreloadCallback preload)
: Environment(isolate_data,
context->GetIsolate(),
args,
exec_args,
env_info,
flags,
- thread_id) {
+ thread_id,
+ std::move(preload)) {
InitializeMainContext(context, env_info);
}

diff --git a/src/env.h b/src/env.h
index c914b621f50bcd6bce2617fef9e48737235aa516..d2e7f8534498ca171986cf77ef19d2fc9b950a5b 100644
index c914b621f50bcd6bce2617fef9e48737235aa516..e8ea98f193fcf579c20c3126dd441850c608752e 100644
--- a/src/env.h
+++ b/src/env.h
@@ -579,7 +579,8 @@ class Environment : public MemoryRetainer {
const std::vector<std::string>& exec_args,
const EnvSerializeInfo* env_info,
EnvironmentFlags::Flags flags,
- ThreadId thread_id);
+ ThreadId thread_id,
+ EmbedderPreloadCallback preload);
void InitializeMainContext(v8::Local<v8::Context> context,
const EnvSerializeInfo* env_info);
// Create an Environment and initialize the provided principal context for it.
@@ -589,7 +590,8 @@ class Environment : public MemoryRetainer {
const std::vector<std::string>& exec_args,
const EnvSerializeInfo* env_info,
EnvironmentFlags::Flags flags,
- ThreadId thread_id);
+ ThreadId thread_id,
+ EmbedderPreloadCallback preload);
~Environment() override;

void InitializeLibuv();
@@ -933,6 +935,8 @@ class Environment : public MemoryRetainer {
@@ -933,6 +933,9 @@ class Environment : public MemoryRetainer {

#endif // HAVE_INSPECTOR

+ inline const EmbedderPreloadCallback& embedder_preload() const;
+ inline void set_embedder_preload(EmbedderPreloadCallback fn);
+
inline void set_process_exit_handler(
std::function<void(Environment*, int)>&& handler);

@@ -1101,6 +1105,7 @@ class Environment : public MemoryRetainer {
@@ -1101,6 +1104,7 @@ class Environment : public MemoryRetainer {
DefaultProcessExitHandler };

std::unique_ptr<Realm> principal_realm_ = nullptr;
Expand All @@ -167,55 +113,45 @@ index c914b621f50bcd6bce2617fef9e48737235aa516..d2e7f8534498ca171986cf77ef19d2fc
// Used by allocate_managed_buffer() and release_managed_buffer() to keep
// track of the BackingStore for a given pointer.
diff --git a/src/node.h b/src/node.h
index 26368061a909e6abc62a4cf261a5dbbd79404f1a..bb4065e33164c3ea762a27b71606ab4ed7b1b336 100644
index 26368061a909e6abc62a4cf261a5dbbd79404f1a..0dec1e311d7c00c2b830a0b2a6bde4336aebe68b 100644
--- a/src/node.h
+++ b/src/node.h
@@ -593,9 +593,21 @@ struct InspectorParentHandle {
virtual ~InspectorParentHandle();
};
@@ -630,13 +630,33 @@ struct StartExecutionCallbackInfo {

using StartExecutionCallback =
std::function<v8::MaybeLocal<v8::Value>(const StartExecutionCallbackInfo&)>;
+using EmbedderPreloadCallback =
+ std::function<void(Environment* env,
+ v8::Local<v8::Value> process,
+ v8::Local<v8::Value> require)>;
+
// TODO(addaleax): Maybe move per-Environment options parsing here.
// Returns nullptr when the Environment cannot be created e.g. there are
// pending JavaScript exceptions.

+// Run initialization for the environment.
+//
+// The |preload| function will run before executing the entry point, which
+// 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.
NODE_EXTERN Environment* CreateEnvironment(
IsolateData* isolate_data,
v8::Local<v8::Context> context,
@@ -603,7 +615,8 @@ NODE_EXTERN Environment* CreateEnvironment(
const std::vector<std::string>& exec_args,
EnvironmentFlags::Flags flags = EnvironmentFlags::kDefaultFlags,
ThreadId thread_id = {} /* allocates a thread id automatically */,
- std::unique_ptr<InspectorParentHandle> inspector_parent_handle = {});
+ std::unique_ptr<InspectorParentHandle> inspector_parent_handle = {},
+// The |preload| function, usually used by embedders to inject scripts,
+// will be run by Node.js before Node.js executes the entry point.
+// The function is guaranteed to run before the user land module loader running
+// any user code, so it is safe to assume that at this point, no user code has
+// been run yet.
+// The function will be executed with preload(process, require), and the passed
+// require function has access to internal Node.js modules. There is no
+// stability guarantee about the internals exposed to the internal require
+// function. Expect breakages when updating Node.js versions if the embedder
+// imports internal modules with the internal require function.
+// Worker threads created in the environment will also respect The |preload|
+// function, so make sure the function is thread-safe.
NODE_EXTERN v8::MaybeLocal<v8::Value> LoadEnvironment(
Environment* env,
- StartExecutionCallback cb);
+ StartExecutionCallback cb,
+ EmbedderPreloadCallback preload = nullptr);
NODE_EXTERN v8::MaybeLocal<v8::Value> LoadEnvironment(
Environment* env,
- const char* main_script_source_utf8);
+ const char* main_script_source_utf8,
+ EmbedderPreloadCallback preload = nullptr);
NODE_EXTERN void FreeEnvironment(Environment* env);

// Returns a handle that can be passed to `LoadEnvironment()`, making the
// child Environment accessible to the inspector as if it were a Node.js Worker.
diff --git a/src/node_main_instance.cc b/src/node_main_instance.cc
index a8661c3c2263fc62e55659310b8da12fc414361e..849442aa8c923808420cbc888befea7d3f1f4c1b 100644
--- a/src/node_main_instance.cc
+++ b/src/node_main_instance.cc
@@ -157,7 +157,8 @@ NodeMainInstance::CreateMainEnvironment(int* exit_code) {
exec_args_,
&(snapshot_data_->env_info),
EnvironmentFlags::kDefaultFlags,
- {}));
+ {},
+ nullptr));
context = Context::FromSnapshot(isolate_,
SnapshotData::kNodeMainContextIndex,
{DeserializeNodeInternalFields, env.get()})
// Set a callback that is called when process.exit() is called from JS,
diff --git a/src/node_options.cc b/src/node_options.cc
index 7ad8d80faee840e4dd224d946871b2ff08b0c23c..25842fd531fc7e1485bcd75f1f92aa9bc0640862 100644
--- a/src/node_options.cc
Expand All @@ -234,32 +170,36 @@ index 7ad8d80faee840e4dd224d946871b2ff08b0c23c..25842fd531fc7e1485bcd75f1f92aa9b
}

diff --git a/src/node_snapshotable.cc b/src/node_snapshotable.cc
index bfa048a4a8aa183e747dec84b11b1c1d847db2dd..b8337e68cb946366b2dff78bbd12ce5473ee166c 100644
index bfa048a4a8aa183e747dec84b11b1c1d847db2dd..970cec6c6e9c5e9646bfd758a2cf908e6b5799cf 100644
--- a/src/node_snapshotable.cc
+++ b/src/node_snapshotable.cc
@@ -1462,6 +1462,13 @@ void SerializeSnapshotableObjects(Realm* realm,
@@ -1462,6 +1462,17 @@ void SerializeSnapshotableObjects(Realm* realm,

namespace mksnapshot {

+static void RunEmbedderPreload(const FunctionCallbackInfo<Value>& args) {
+void RunEmbedderPreload(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ CHECK(env->embedder_preload());
+ CHECK_EQ(args.Length(), 2);
+ env->embedder_preload()(env, args[0], args[1]);
+ Local<Value> process_obj = args[0];
+ Local<Value> require_fn = args[1];
+ CHECK(process_obj->IsObject());
+ CHECK(require_fn->IsFunction());
+ env->embedder_preload()(env, process_obj, require_fn);
+}
+
void CompileSerializeMain(const FunctionCallbackInfo<Value>& args) {
CHECK(args[0]->IsString());
Local<String> filename = args[0].As<String>();
@@ -1515,6 +1522,7 @@ void Initialize(Local<Object> target,
@@ -1515,6 +1526,7 @@ void Initialize(Local<Object> target,
Local<Value> unused,
Local<Context> context,
void* priv) {
+ SetMethod(context, target, "runEmbedderPreload", RunEmbedderPreload);
SetMethod(context, target, "compileSerializeMain", CompileSerializeMain);
SetMethod(context, target, "setSerializeCallback", SetSerializeCallback);
SetMethod(context, target, "setDeserializeCallback", SetDeserializeCallback);
@@ -1525,6 +1533,7 @@ void Initialize(Local<Object> target,
@@ -1525,6 +1537,7 @@ void Initialize(Local<Object> target,
}

void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
Expand All @@ -268,7 +208,7 @@ index bfa048a4a8aa183e747dec84b11b1c1d847db2dd..b8337e68cb946366b2dff78bbd12ce54
registry->Register(SetSerializeCallback);
registry->Register(SetDeserializeCallback);
diff --git a/src/node_worker.cc b/src/node_worker.cc
index 6a49144ec4f2059fe75983609b0768e4c2b1817d..dc2eb247b011f9cb1945c173c49e029f068ef103 100644
index 6a49144ec4f2059fe75983609b0768e4c2b1817d..13b0445370c70cf3765a4af44336c16ac2e1035d 100644
--- a/src/node_worker.cc
+++ b/src/node_worker.cc
@@ -60,6 +60,7 @@ Worker::Worker(Environment* env,
Expand All @@ -279,16 +219,20 @@ index 6a49144ec4f2059fe75983609b0768e4c2b1817d..dc2eb247b011f9cb1945c173c49e029f
snapshot_data_(snapshot_data) {
Debug(this, "Creating new worker instance with thread id %llu",
thread_id_.id);
@@ -333,7 +334,8 @@ void Worker::Run() {
std::move(exec_argv_),
static_cast<EnvironmentFlags::Flags>(environment_flags_),
thread_id_,
- std::move(inspector_parent_handle_)));
+ std::move(inspector_parent_handle_),
+ std::move(embedder_preload_)));
if (is_stopped()) return;
CHECK_NOT_NULL(env_);
env_->set_env_vars(std::move(env_vars_));
@@ -354,8 +355,12 @@ void Worker::Run() {
}

Debug(this, "Created message port for worker %llu", thread_id_.id);
- if (LoadEnvironment(env_.get(), StartExecutionCallback{}).IsEmpty())
+ if (LoadEnvironment(env_.get(),
+ StartExecutionCallback{},
+ std::move(embedder_preload_))
+ .IsEmpty()) {
return;
+ }

Debug(this, "Loaded environment for worker %llu", thread_id_.id);
}
diff --git a/src/node_worker.h b/src/node_worker.h
index a77c416735a79feb3f54e40d72a98c8903a20ccd..deab68576f6330f8bcfb4703fd05dbb9c515e473 100644
--- a/src/node_worker.h
Expand All @@ -302,7 +246,7 @@ index a77c416735a79feb3f54e40d72a98c8903a20ccd..deab68576f6330f8bcfb4703fd05dbb9
// A raw flag that is used by creator and worker threads to
// sync up on pre-mature termination of worker - while in the
diff --git a/test/cctest/test_environment.cc b/test/cctest/test_environment.cc
index 547c8ddbffe243113bfe47a51072bb8f1541b94f..19ef2c2a083f908267e6a9365e77b20d46a3feec 100644
index 547c8ddbffe243113bfe47a51072bb8f1541b94f..9f1fec4c4e47376c3b93a549f5c7ddf8e7ed3ac6 100644
--- a/test/cctest/test_environment.cc
+++ b/test/cctest/test_environment.cc
@@ -749,3 +749,31 @@ TEST_F(EnvironmentTest, RequestInterruptAtExit) {
Expand All @@ -320,20 +264,20 @@ index 547c8ddbffe243113bfe47a51072bb8f1541b94f..19ef2c2a083f908267e6a9365e77b20d
+ v8::Local<v8::Value> require) {
+ CHECK(process->IsObject());
+ CHECK(require->IsFunction());
+ process.As<v8::Object>()->Set(
+ env->context(),
+ v8::String::NewFromUtf8Literal(env->isolate(), "prop"),
+ v8::String::NewFromUtf8Literal(env->isolate(), "preload")).Check();
+ process.As<v8::Object>()
+ ->Set(env->context(),
+ v8::String::NewFromUtf8Literal(env->isolate(), "prop"),
+ v8::String::NewFromUtf8Literal(env->isolate(), "preload"))
+ .Check();
+ };
+
+ std::unique_ptr<node::Environment, decltype(&node::FreeEnvironment)> env(
+ node::CreateEnvironment(isolate_data_, context, {}, {},
+ node::EnvironmentFlags::kDefaultFlags, {}, {},
+ preload),
+ node::CreateEnvironment(isolate_data_, context, {}, {}),
+ node::FreeEnvironment);
+
+ v8::Local<v8::Value> main_ret =
+ node::LoadEnvironment(env.get(), "return process.prop;").ToLocalChecked();
+ node::LoadEnvironment(env.get(), "return process.prop;", preload)
+ .ToLocalChecked();
+ node::Utf8Value main_ret_str(isolate_, main_ret);
+ EXPECT_EQ(std::string(*main_ret_str), "preload");
+}

0 comments on commit f53fad8

Please sign in to comment.