7120c58297
* chore: bump node in DEPS to v20.12.0 * chore: update build_add_gn_build_files.patch * chore: update patches * chore: bump node in DEPS to v20.12.1 * chore: update patches * build: encode non-ASCII Latin1 characters as one byte in JS2C https://github.com/nodejs/node/pull/51605 * crypto: use EVP_MD_fetch and cache EVP_MD for hashes https://github.com/nodejs/node/pull/51034 * chore: update filenames.json * chore: bump node in DEPS to v20.12.2 * chore: update patches * src: support configurable snapshot https://github.com/nodejs/node/pull/50453 * test: remove test-domain-error-types flaky designation https://github.com/nodejs/node/pull/51717 * src: avoid draining platform tasks at FreeEnvironment https://github.com/nodejs/node/pull/51290 * chore: fix accidentally deleted v8 dep * lib: define FormData and fetch etc. in the built-in snapshot https://github.com/nodejs/node/pull/51598 * chore: rebase on main * chore: remove stray log --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: Cheng <zcbenz@gmail.com> Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com> Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
284 lines
12 KiB
Diff
284 lines
12 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Cheng Zhao <zcbenz@gmail.com>
|
|
Date: Mon, 4 Mar 2024 11:41:18 +0900
|
|
Subject: src: preload function for Environment
|
|
|
|
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 2fc9e2da8aa920acffc101b2c341a5d9b16fe90a..b4272f01e84d7fec263dcad444d92459743780a8 100644
|
|
--- a/lib/internal/process/pre_execution.js
|
|
+++ b/lib/internal/process/pre_execution.js
|
|
@@ -196,6 +196,9 @@ function setupUserModules(forceDefaultLoader = false) {
|
|
} = require('internal/modules/helpers');
|
|
assert(!hasStartedUserCJSExecution());
|
|
assert(!hasStartedUserESMExecution());
|
|
+ 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.
|
|
@@ -713,6 +716,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/api/environment.cc b/src/api/environment.cc
|
|
index 7c2e4430299e0d78539d43070942ea3a585b559c..7741b9358a965231671bce004de581f1c6665c2d 100644
|
|
--- a/src/api/environment.cc
|
|
+++ b/src/api/environment.cc
|
|
@@ -549,25 +549,31 @@ 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,
|
|
- std::string_view main_script_source_utf8) {
|
|
+ std::string_view main_script_source_utf8,
|
|
+ EmbedderPreloadCallback preload) {
|
|
CHECK_NOT_NULL(main_script_source_utf8.data());
|
|
return LoadEnvironment(
|
|
- env, [&](const StartExecutionCallbackInfo& info) -> MaybeLocal<Value> {
|
|
+ env,
|
|
+ [&](const StartExecutionCallbackInfo& info) -> MaybeLocal<Value> {
|
|
Local<Value> main_script =
|
|
ToV8Value(env->context(), main_script_source_utf8).ToLocalChecked();
|
|
return info.run_cjs->Call(
|
|
env->context(), Null(env->isolate()), 1, &main_script);
|
|
- });
|
|
+ },
|
|
+ std::move(preload));
|
|
}
|
|
|
|
Environment* GetCurrentEnvironment(Local<Context> context) {
|
|
diff --git a/src/env-inl.h b/src/env-inl.h
|
|
index ac4295f495e240331a183b4a0a22d7437fc85271..63ce35ba68b48a55d8150395304bf86c2bf23aae 100644
|
|
--- a/src/env-inl.h
|
|
+++ b/src/env-inl.h
|
|
@@ -430,6 +430,14 @@ inline builtins::BuiltinLoader* Environment::builtin_loader() {
|
|
return &builtin_loader_;
|
|
}
|
|
|
|
+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 f0a1d5ef2b45d4c27c5660177c9805acee19e5f2..910c69b6d1d17ef25201dbb39d3d074f4f3f011f 100644
|
|
--- a/src/env.h
|
|
+++ b/src/env.h
|
|
@@ -1003,6 +1003,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*, ExitCode)>&& handler);
|
|
|
|
@@ -1213,6 +1216,7 @@ class Environment : public MemoryRetainer {
|
|
std::unique_ptr<PrincipalRealm> principal_realm_ = nullptr;
|
|
|
|
builtins::BuiltinLoader builtin_loader_;
|
|
+ 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.h b/src/node.h
|
|
index 5ade64874ec08474f05db226cf19a043fa26592e..b3cc7a9f1866111c197b20068275c496fd4aace0 100644
|
|
--- a/src/node.h
|
|
+++ b/src/node.h
|
|
@@ -746,12 +746,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)>;
|
|
|
|
+// Run initialization for the environment.
|
|
+//
|
|
+// 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, std::string_view main_script_source_utf8);
|
|
+ Environment* env,
|
|
+ std::string_view main_script_source_utf8,
|
|
+ EmbedderPreloadCallback preload = nullptr);
|
|
NODE_EXTERN void FreeEnvironment(Environment* env);
|
|
|
|
// 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 a02dcca456556a7cbb2a262edc5fa4a3b25297ed..92adb8323abac2c7ab956fe1adf95cfab62bef6a 100644
|
|
--- a/src/node_options.cc
|
|
+++ b/src/node_options.cc
|
|
@@ -1304,6 +1304,12 @@ void GetEmbedderOptions(const FunctionCallbackInfo<Value>& 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 0309aba6bdc24afe0d1fd24e7af6f472910363d7..55a65deeea6e74b77cb5b97da88e37779f336ce2 100644
|
|
--- a/src/node_snapshotable.cc
|
|
+++ b/src/node_snapshotable.cc
|
|
@@ -1417,6 +1417,17 @@ void SerializeSnapshotableObjects(Realm* realm,
|
|
});
|
|
}
|
|
|
|
+void RunEmbedderPreload(const FunctionCallbackInfo<Value>& args) {
|
|
+ Environment* env = Environment::GetCurrent(args);
|
|
+ CHECK(env->embedder_preload());
|
|
+ CHECK_EQ(args.Length(), 2);
|
|
+ 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>();
|
|
@@ -1540,6 +1551,7 @@ void CreatePerContextProperties(Local<Object> target,
|
|
void CreatePerIsolateProperties(IsolateData* isolate_data,
|
|
Local<ObjectTemplate> target) {
|
|
Isolate* isolate = isolate_data->isolate();
|
|
+ SetMethod(isolate, target, "runEmbedderPreload", RunEmbedderPreload);
|
|
SetMethod(isolate, target, "compileSerializeMain", CompileSerializeMain);
|
|
SetMethod(isolate, target, "setSerializeCallback", SetSerializeCallback);
|
|
SetMethod(isolate, target, "setDeserializeCallback", SetDeserializeCallback);
|
|
@@ -1552,6 +1564,7 @@ void CreatePerIsolateProperties(IsolateData* isolate_data,
|
|
}
|
|
|
|
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
|
+ 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 8b45c0265befaf68af07622d220ebdf96dc94ac4..196eb3bccaee87b52a46b693a061864ae6a5e3b9 100644
|
|
--- a/src/node_worker.cc
|
|
+++ b/src/node_worker.cc
|
|
@@ -62,6 +62,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);
|
|
@@ -386,8 +387,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 531e2b5287010f9206ab4fd7f4dd0f3dec9fe55c..07fd7b460654e169e8b6822474dc3cc70fcec4c0 100644
|
|
--- a/src/node_worker.h
|
|
+++ b/src/node_worker.h
|
|
@@ -114,6 +114,7 @@ class Worker : public AsyncWrap {
|
|
|
|
std::unique_ptr<MessagePortData> child_port_data_;
|
|
std::shared_ptr<KVStore> 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
|
|
diff --git a/test/cctest/test_environment.cc b/test/cctest/test_environment.cc
|
|
index 9b8124081542876ce7245e4aede6810e9bf0e2eb..64e38c83006a004ebc3519a5e9f8b04263244514 100644
|
|
--- a/test/cctest/test_environment.cc
|
|
+++ b/test/cctest/test_environment.cc
|
|
@@ -778,3 +778,31 @@ TEST_F(EnvironmentTest, RequestInterruptAtExit) {
|
|
|
|
context->Exit();
|
|
}
|
|
+
|
|
+TEST_F(EnvironmentTest, EmbedderPreload) {
|
|
+ v8::HandleScope handle_scope(isolate_);
|
|
+ v8::Local<v8::Context> context = node::NewContext(isolate_);
|
|
+ v8::Context::Scope context_scope(context);
|
|
+
|
|
+ node::EmbedderPreloadCallback preload = [](node::Environment* env,
|
|
+ v8::Local<v8::Value> process,
|
|
+ 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();
|
|
+ };
|
|
+
|
|
+ std::unique_ptr<node::Environment, decltype(&node::FreeEnvironment)> env(
|
|
+ node::CreateEnvironment(isolate_data_, context, {}, {}),
|
|
+ node::FreeEnvironment);
|
|
+
|
|
+ v8::Local<v8::Value> main_ret =
|
|
+ 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");
|
|
+}
|