2024-01-24 15:54:32 +00:00
|
|
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
|
|
From: Cheng Zhao <zcbenz@gmail.com>
|
2024-03-04 12:11:14 +00:00
|
|
|
Date: Mon, 4 Mar 2024 11:41:18 +0900
|
2024-01-24 15:54:32 +00:00
|
|
|
Subject: src: preload function for Environment
|
|
|
|
|
2024-03-04 12:11:14 +00:00
|
|
|
Backport https://github.com/nodejs/node/pull/51539
|
2024-01-24 15:54:32 +00:00
|
|
|
|
|
|
|
diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js
|
2024-02-21 17:49:20 +00:00
|
|
|
index eb66f0e37b517a03be20e0829863e7572042c7ed..4d5646b6067a1409df5915cc744bdc38d0191bd9 100644
|
2024-01-24 15:54:32 +00:00
|
|
|
--- a/lib/internal/process/pre_execution.js
|
|
|
|
+++ b/lib/internal/process/pre_execution.js
|
2024-02-21 17:49:20 +00:00
|
|
|
@@ -186,6 +186,9 @@ function setupUserModules(forceDefaultLoader = false) {
|
2024-01-24 15:54:32 +00:00
|
|
|
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.
|
2024-02-21 17:49:20 +00:00
|
|
|
@@ -745,6 +748,10 @@ function initializeFrozenIntrinsics() {
|
2024-01-24 15:54:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
+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
|
2024-03-04 12:11:14 +00:00
|
|
|
index 9045de3b17c93c4864a1bb1024b08f7d1ffa83be..e8669ba2a1ede090fea1869c476cac19a2aebbc6 100644
|
2024-01-24 15:54:32 +00:00
|
|
|
--- a/src/api/environment.cc
|
|
|
|
+++ b/src/api/environment.cc
|
2024-03-04 12:11:14 +00:00
|
|
|
@@ -556,25 +556,31 @@ NODE_EXTERN std::unique_ptr<InspectorParentHandle> GetInspectorParentHandle(
|
|
|
|
#endif
|
|
|
|
}
|
2024-01-24 15:54:32 +00:00
|
|
|
|
2024-03-04 12:11:14 +00:00
|
|
|
-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));
|
|
|
|
+ }
|
2024-01-24 15:54:32 +00:00
|
|
|
|
2024-03-04 12:11:14 +00:00
|
|
|
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) {
|
2024-01-24 15:54:32 +00:00
|
|
|
diff --git a/src/env-inl.h b/src/env-inl.h
|
2024-03-04 12:11:14 +00:00
|
|
|
index 564de2990c09a54693686666f9ad66398ff76ab5..cc448abbc650b5abb7f555872fd8c15bc09fd643 100644
|
2024-01-24 15:54:32 +00:00
|
|
|
--- a/src/env-inl.h
|
|
|
|
+++ b/src/env-inl.h
|
2024-03-04 12:11:14 +00:00
|
|
|
@@ -438,6 +438,14 @@ inline void Environment::set_embedder_entry_point(StartExecutionCallback&& fn) {
|
2024-01-24 15:54:32 +00:00
|
|
|
embedder_entry_point_ = std::move(fn);
|
|
|
|
}
|
|
|
|
|
|
|
|
+inline const EmbedderPreloadCallback& Environment::embedder_preload() const {
|
|
|
|
+ return embedder_preload_;
|
|
|
|
+}
|
2024-03-04 12:11:14 +00:00
|
|
|
+
|
|
|
|
+inline void Environment::set_embedder_preload(EmbedderPreloadCallback fn) {
|
|
|
|
+ embedder_preload_ = std::move(fn);
|
|
|
|
+}
|
2024-01-24 15:54:32 +00:00
|
|
|
+
|
|
|
|
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
|
2024-03-04 12:11:14 +00:00
|
|
|
index 448075e354c760a2dbd1dd763f40b7a645730250..d6956873b1b7bdf49ed0217587729aaa974ae89f 100644
|
2024-01-24 15:54:32 +00:00
|
|
|
--- a/src/env.h
|
|
|
|
+++ b/src/env.h
|
2024-03-04 12:11:14 +00:00
|
|
|
@@ -985,6 +985,8 @@ class Environment : public MemoryRetainer {
|
|
|
|
|
2024-01-24 15:54:32 +00:00
|
|
|
inline const StartExecutionCallback& embedder_entry_point() const;
|
|
|
|
inline void set_embedder_entry_point(StartExecutionCallback&& fn);
|
|
|
|
+ inline const EmbedderPreloadCallback& embedder_preload() const;
|
2024-03-04 12:11:14 +00:00
|
|
|
+ inline void set_embedder_preload(EmbedderPreloadCallback fn);
|
|
|
|
|
2024-01-24 15:54:32 +00:00
|
|
|
inline void set_process_exit_handler(
|
|
|
|
std::function<void(Environment*, ExitCode)>&& handler);
|
2024-03-04 12:11:14 +00:00
|
|
|
@@ -1186,6 +1188,7 @@ class Environment : public MemoryRetainer {
|
2024-01-24 15:54:32 +00:00
|
|
|
|
|
|
|
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.h b/src/node.h
|
2024-03-04 12:11:14 +00:00
|
|
|
index 36da93a7b41ea450a5f288ec17b61adae46ae178..c65c96342d7d02ddb37565477f67a93ef55a78ba 100644
|
2024-01-24 15:54:32 +00:00
|
|
|
--- a/src/node.h
|
|
|
|
+++ b/src/node.h
|
2024-03-04 12:11:14 +00:00
|
|
|
@@ -718,12 +718,33 @@ struct StartExecutionCallbackInfo {
|
2024-01-24 15:54:32 +00:00
|
|
|
|
2024-03-04 12:11:14 +00:00
|
|
|
using StartExecutionCallback =
|
|
|
|
std::function<v8::MaybeLocal<v8::Value>(const StartExecutionCallbackInfo&)>;
|
2024-01-24 15:54:32 +00:00
|
|
|
+using EmbedderPreloadCallback =
|
|
|
|
+ std::function<void(Environment* env,
|
|
|
|
+ v8::Local<v8::Value> process,
|
|
|
|
+ v8::Local<v8::Value> require)>;
|
2024-03-04 12:11:14 +00:00
|
|
|
|
|
|
|
+// Run initialization for the environment.
|
2024-01-24 15:54:32 +00:00
|
|
|
+//
|
2024-03-04 12:11:14 +00:00
|
|
|
+// 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,
|
2024-01-24 15:54:32 +00:00
|
|
|
+ EmbedderPreloadCallback preload = nullptr);
|
2024-03-04 12:11:14 +00:00
|
|
|
NODE_EXTERN void FreeEnvironment(Environment* env);
|
2024-01-24 15:54:32 +00:00
|
|
|
|
2024-03-04 12:11:14 +00:00
|
|
|
// Set a callback that is called when process.exit() is called from JS,
|
2024-01-24 15:54:32 +00:00
|
|
|
diff --git a/src/node_options.cc b/src/node_options.cc
|
|
|
|
index 48ce3f3b68a94fc35e5ce93a385ddbebb03741b9..39d34e18e483882a71145110962109711a1566e2 100644
|
|
|
|
--- a/src/node_options.cc
|
|
|
|
+++ b/src/node_options.cc
|
|
|
|
@@ -1290,6 +1290,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
|
2024-03-04 12:11:14 +00:00
|
|
|
index 562a47ddcc9c8e61590b7b09d84dc08ab4b3653d..431cbe1c2cb77669ceb10602a7b3ef1c2f7e8718 100644
|
2024-01-24 15:54:32 +00:00
|
|
|
--- a/src/node_snapshotable.cc
|
|
|
|
+++ b/src/node_snapshotable.cc
|
2024-03-04 12:11:14 +00:00
|
|
|
@@ -1369,6 +1369,17 @@ static void RunEmbedderEntryPoint(const FunctionCallbackInfo<Value>& args) {
|
2024-01-24 15:54:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-04 12:11:14 +00:00
|
|
|
+void RunEmbedderPreload(const FunctionCallbackInfo<Value>& args) {
|
2024-01-24 15:54:32 +00:00
|
|
|
+ Environment* env = Environment::GetCurrent(args);
|
|
|
|
+ CHECK(env->embedder_preload());
|
|
|
|
+ CHECK_EQ(args.Length(), 2);
|
2024-03-04 12:11:14 +00:00
|
|
|
+ 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);
|
2024-01-24 15:54:32 +00:00
|
|
|
+}
|
|
|
|
+
|
|
|
|
void CompileSerializeMain(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
CHECK(args[0]->IsString());
|
|
|
|
Local<String> filename = args[0].As<String>();
|
2024-03-04 12:11:14 +00:00
|
|
|
@@ -1493,6 +1504,7 @@ void CreatePerIsolateProperties(IsolateData* isolate_data,
|
2024-01-24 15:54:32 +00:00
|
|
|
Local<ObjectTemplate> 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);
|
2024-03-04 12:11:14 +00:00
|
|
|
@@ -1506,6 +1518,7 @@ void CreatePerIsolateProperties(IsolateData* isolate_data,
|
2024-01-24 15:54:32 +00:00
|
|
|
|
|
|
|
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
|
2024-03-04 12:11:14 +00:00
|
|
|
index 900674bbe4c90e9aeb2013c06c9979864b06dcd5..52d7473b05ccb49e5fc915224b6d2972a14191da 100644
|
2024-01-24 15:54:32 +00:00
|
|
|
--- 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);
|
2024-03-04 12:11:14 +00:00
|
|
|
@@ -381,8 +382,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);
|
|
|
|
}
|
2024-01-24 15:54:32 +00:00
|
|
|
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
|
2024-03-04 12:11:14 +00:00
|
|
|
index 2e747c7be58922897abd0424b797f3f12a89ada1..fcffaca89cf5aa24be6e539bfb4d9d6df690a709 100644
|
2024-01-24 15:54:32 +00:00
|
|
|
--- a/test/cctest/test_environment.cc
|
|
|
|
+++ b/test/cctest/test_environment.cc
|
|
|
|
@@ -773,3 +773,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());
|
2024-03-04 12:11:14 +00:00
|
|
|
+ process.As<v8::Object>()
|
|
|
|
+ ->Set(env->context(),
|
|
|
|
+ v8::String::NewFromUtf8Literal(env->isolate(), "prop"),
|
|
|
|
+ v8::String::NewFromUtf8Literal(env->isolate(), "preload"))
|
|
|
|
+ .Check();
|
2024-01-24 15:54:32 +00:00
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ std::unique_ptr<node::Environment, decltype(&node::FreeEnvironment)> env(
|
2024-03-04 12:11:14 +00:00
|
|
|
+ node::CreateEnvironment(isolate_data_, context, {}, {}),
|
2024-01-24 15:54:32 +00:00
|
|
|
+ node::FreeEnvironment);
|
|
|
|
+
|
|
|
|
+ v8::Local<v8::Value> main_ret =
|
2024-03-04 12:11:14 +00:00
|
|
|
+ node::LoadEnvironment(env.get(), "return process.prop;", preload)
|
|
|
|
+ .ToLocalChecked();
|
2024-01-24 15:54:32 +00:00
|
|
|
+ node::Utf8Value main_ret_str(isolate_, main_ret);
|
|
|
|
+ EXPECT_EQ(std::string(*main_ret_str), "preload");
|
|
|
|
+}
|