Browse Source

chore: remove node patches by using the preload feature (#41114)

Cheng Zhao 1 year ago
parent
commit
116dcfb827

+ 11 - 11
BUILD.gn

@@ -165,15 +165,6 @@ npm_action("build_electron_definitions") {
   outputs = [ "$target_gen_dir/tsc/typings/electron.d.ts" ]
 }
 
-webpack_build("electron_asar_bundle") {
-  deps = [ ":build_electron_definitions" ]
-
-  inputs = auto_filenames.asar_bundle_deps
-
-  config_file = "//electron/build/webpack/webpack.config.asar.js"
-  out_file = "$target_gen_dir/js2c/asar_bundle.js"
-}
-
 webpack_build("electron_browser_bundle") {
   deps = [ ":build_electron_definitions" ]
 
@@ -219,6 +210,15 @@ webpack_build("electron_isolated_renderer_bundle") {
   out_file = "$target_gen_dir/js2c/isolated_bundle.js"
 }
 
+webpack_build("electron_node_bundle") {
+  deps = [ ":build_electron_definitions" ]
+
+  inputs = auto_filenames.node_bundle_deps
+
+  config_file = "//electron/build/webpack/webpack.config.node.js"
+  out_file = "$target_gen_dir/js2c/node_init.js"
+}
+
 webpack_build("electron_utility_bundle") {
   deps = [ ":build_electron_definitions" ]
 
@@ -230,9 +230,9 @@ webpack_build("electron_utility_bundle") {
 
 action("electron_js2c") {
   deps = [
-    ":electron_asar_bundle",
     ":electron_browser_bundle",
     ":electron_isolated_renderer_bundle",
+    ":electron_node_bundle",
     ":electron_renderer_bundle",
     ":electron_sandboxed_renderer_bundle",
     ":electron_utility_bundle",
@@ -240,9 +240,9 @@ action("electron_js2c") {
   ]
 
   sources = [
-    "$target_gen_dir/js2c/asar_bundle.js",
     "$target_gen_dir/js2c/browser_init.js",
     "$target_gen_dir/js2c/isolated_bundle.js",
+    "$target_gen_dir/js2c/node_init.js",
     "$target_gen_dir/js2c/renderer_init.js",
     "$target_gen_dir/js2c/sandbox_bundle.js",
     "$target_gen_dir/js2c/utility_init.js",

+ 0 - 5
build/webpack/webpack.config.asar.js

@@ -1,5 +0,0 @@
-module.exports = require('./webpack.config.base')({
-  target: 'asar',
-  alwaysHasNode: true,
-  targetDeletesNodeGlobals: true
-});

+ 4 - 0
build/webpack/webpack.config.node.js

@@ -0,0 +1,4 @@
+module.exports = require('./webpack.config.base')({
+  target: 'node',
+  alwaysHasNode: true
+});

+ 3 - 4
filenames.auto.gni

@@ -328,10 +328,9 @@ auto_filenames = {
     "typings/internal-electron.d.ts",
   ]
 
-  asar_bundle_deps = [
-    "lib/asar/fs-wrapper.ts",
-    "lib/asar/init.ts",
-    "lib/common/webpack-provider.ts",
+  node_bundle_deps = [
+    "lib/node/asar-fs-wrapper.ts",
+    "lib/node/init.ts",
     "package.json",
     "tsconfig.electron.json",
     "tsconfig.json",

+ 0 - 3
lib/asar/init.ts

@@ -1,3 +0,0 @@
-import { wrapFsWithAsar } from './fs-wrapper';
-
-wrapFsWithAsar(require('fs'));

+ 0 - 0
lib/asar/fs-wrapper.ts → lib/node/asar-fs-wrapper.ts


+ 31 - 0
lib/node/init.ts

@@ -0,0 +1,31 @@
+// Initialize ASAR support in fs module.
+import { wrapFsWithAsar } from './asar-fs-wrapper';
+wrapFsWithAsar(require('fs'));
+
+// Hook child_process.fork.
+const cp = require('child_process');
+const originalFork = cp.fork;
+cp.fork = (modulePath: string, args: any, options: any) => {
+  // Parse optional args.
+  if (args == null) {
+    args = [];
+  } else if (typeof args === 'object' && !Array.isArray(args)) {
+    options = args;
+    args = [];
+  }
+  // Fallback to original fork to report arg type errors.
+  if (typeof modulePath !== 'string' || !Array.isArray(args) ||
+      (typeof options !== 'object' && typeof options !== 'undefined')) {
+    return originalFork(modulePath, args, options);
+  }
+  // When forking a child script, we setup a special environment to make
+  // the electron binary run like upstream Node.js.
+  options = options ?? {};
+  options.env = Object.create(options.env || process.env);
+  options.env.ELECTRON_RUN_AS_NODE = 1;
+  // On mac the child script runs in helper executable.
+  if (!options.execPath && process.platform === 'darwin') {
+    options.execPath = process.helperExecPath;
+  }
+  return originalFork(modulePath, args, options);
+};

+ 1 - 2
patches/node/.patches

@@ -1,5 +1,3 @@
-refactor_alter_child_process_fork_to_use_execute_script_with.patch
-feat_initialize_asar_support.patch
 expose_get_builtin_module_function.patch
 build_add_gn_build_files.patch
 fix_add_default_values_for_variables_in_common_gypi.patch
@@ -55,3 +53,4 @@ win_process_avoid_assert_after_spawning_store_app_4152.patch
 test_fix_edge_snapshot_stack_traces.patch
 chore_remove_use_of_deprecated_kmaxlength.patch
 fix_avx_detection.patch
+src_preload_function_for_environment.patch

+ 2 - 2
patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch

@@ -26,10 +26,10 @@ index 001343b74ce236f89dca030c0fc9dd0299df0b39..6f277daf4ce846f093f634c473daec07
          try {
            resolvedArgv = Module._resolveFilename(process.argv[1], null, false);
 diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js
-index fcbd9ee1af002bc176937e6bb5af55791b2f64b2..cbfb6e3620a7e77658c86a4730c50661b8a937f7 100644
+index b4a24bbffb6c43638d13063e85b6cfba5c0cc9c7..21dbf3d87c813b057602637a27ed226bb91698a5 100644
 --- a/lib/internal/process/pre_execution.js
 +++ b/lib/internal/process/pre_execution.js
-@@ -164,11 +164,13 @@ function patchProcessObject(expandArgv1) {
+@@ -157,11 +157,13 @@ function patchProcessObject(expandArgv1) {
    if (expandArgv1 && process.argv[1] &&
        !StringPrototypeStartsWith(process.argv[1], '-')) {
      // Expand process.argv[1] into a full path.

+ 3 - 3
patches/node/chore_expose_importmoduledynamically_and.patch

@@ -11,10 +11,10 @@ its own blended handler between Node and Blink.
 Not upstreamable.
 
 diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js
-index cbfb6e3620a7e77658c86a4730c50661b8a937f7..ccd48027e3dfebd563fcbe83239174c79c693dd7 100644
+index 21dbf3d87c813b057602637a27ed226bb91698a5..fd5357997a4e05567146dc997e47af408e1fc8f4 100644
 --- a/lib/internal/process/pre_execution.js
 +++ b/lib/internal/process/pre_execution.js
-@@ -567,7 +567,7 @@ function initializeESMLoader() {
+@@ -560,7 +560,7 @@ function initializeESMLoader() {
    // Create this WeakMap in js-land because V8 has no C++ API for WeakMap.
    internalBinding('module_wrap').callbackMap = new SafeWeakMap();
  
@@ -23,7 +23,7 @@ index cbfb6e3620a7e77658c86a4730c50661b8a937f7..ccd48027e3dfebd563fcbe83239174c7
  
    const {
      setImportModuleDynamicallyCallback,
-@@ -576,8 +576,8 @@ function initializeESMLoader() {
+@@ -569,8 +569,8 @@ function initializeESMLoader() {
    const esm = require('internal/process/esm_loader');
    // Setup per-isolate callbacks that locate data or callbacks that we keep
    // track of for different ESM modules.

+ 3 - 3
patches/node/enable_crashpad_linux_node_processes.patch

@@ -8,7 +8,7 @@ to child processes spawned with `ELECTRON_RUN_AS_NODE` which is used
 by the crashpad client to connect with the handler process.
 
 diff --git a/lib/child_process.js b/lib/child_process.js
-index ec39a00ddb791e6e1ebe31aa45d290e7dcc4ebfc..1cd5d8969471b276211c45a9d8d76e9b10b1bb66 100644
+index 5bdc474c80169cb0ceeb082e6afcf9e8fa322ab3..f9234f2ac875cb6cb0c8de2894b8a1d4d8db0c46 100644
 --- a/lib/child_process.js
 +++ b/lib/child_process.js
 @@ -61,6 +61,7 @@ let debug = require('internal/util/debuglog').debuglog(
@@ -19,7 +19,7 @@ index ec39a00ddb791e6e1ebe31aa45d290e7dcc4ebfc..1cd5d8969471b276211c45a9d8d76e9b
  
  const {
    AbortError,
-@@ -162,7 +163,6 @@ function fork(modulePath, args = [], options) {
+@@ -154,7 +155,6 @@ function fork(modulePath, args = [], options) {
        ArrayPrototypeSplice(execArgv, index - 1, 2);
      }
    }
@@ -27,7 +27,7 @@ index ec39a00ddb791e6e1ebe31aa45d290e7dcc4ebfc..1cd5d8969471b276211c45a9d8d76e9b
    args = [...execArgv, modulePath, ...args];
  
    if (typeof options.stdio === 'string') {
-@@ -625,6 +625,21 @@ function normalizeSpawnArguments(file, args, options) {
+@@ -617,6 +617,21 @@ function normalizeSpawnArguments(file, args, options) {
                      'options.windowsVerbatimArguments');
    }
  

+ 0 - 38
patches/node/feat_initialize_asar_support.patch

@@ -1,38 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Shelley Vohr <[email protected]>
-Date: Thu, 13 Sep 2018 08:56:07 -0700
-Subject: feat: initialize asar support
-
-This patch initializes asar support in Node.js.
-
-diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js
-index b4a24bbffb6c43638d13063e85b6cfba5c0cc9c7..fcbd9ee1af002bc176937e6bb5af55791b2f64b2 100644
---- a/lib/internal/process/pre_execution.js
-+++ b/lib/internal/process/pre_execution.js
-@@ -52,6 +52,8 @@ function prepareWorkerThreadExecution() {
-   });
- }
- 
-+
-+let processLinkedBinding = process._linkedBinding;
- function prepareExecution(options) {
-   const { expandArgv1, initializeModules, isMainThread } = options;
- 
-@@ -130,12 +132,17 @@ function setupUserModules() {
-   loadPreloadModules();
-   // Need to be done after --require setup.
-   initializeFrozenIntrinsics();
-+  setupAsarSupport();
- }
- 
- function refreshRuntimeOptions() {
-   refreshOptions();
- }
- 
-+function setupAsarSupport() {
-+  processLinkedBinding('electron_common_asar').initAsarSupport(require);
-+}
-+
- function patchProcessObject(expandArgv1) {
-   const binding = internalBinding('process_methods');
-   binding.patchProcessObject(process);

+ 0 - 27
patches/node/refactor_alter_child_process_fork_to_use_execute_script_with.patch

@@ -1,27 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Shelley Vohr <[email protected]>
-Date: Mon, 30 Jul 2018 10:30:35 -0700
-Subject: refactor: alter child_process.fork to use execute script with
- Electron
-
-When forking a child script, we setup a special environment to make the Electron binary run like the upstream node. On Mac, we use the helper app as node binary.
-
-diff --git a/lib/child_process.js b/lib/child_process.js
-index 5bdc474c80169cb0ceeb082e6afcf9e8fa322ab3..ec39a00ddb791e6e1ebe31aa45d290e7dcc4ebfc 100644
---- a/lib/child_process.js
-+++ b/lib/child_process.js
-@@ -139,6 +139,14 @@ function fork(modulePath, args = [], options) {
-     validateObject(options, 'options');
-   }
-   options = { __proto__: null, ...options, shell: false };
-+  // When forking a child script, we setup a special environment to make
-+  // the electron binary run like upstream Node.js
-+  options.env = Object.create(options.env || process.env)
-+  options.env.ELECTRON_RUN_AS_NODE = 1;
-+
-+  if (!options.execPath && process.type && process.platform == 'darwin') {
-+     options.execPath = process.helperExecPath;
-+  }
-   options.execPath = options.execPath || process.execPath;
-   validateArgumentNullCheck(options.execPath, 'options.execPath');
- 

+ 339 - 0
patches/node/src_preload_function_for_environment.patch

@@ -0,0 +1,339 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Cheng Zhao <[email protected]>
+Date: Mon, 22 Jan 2024 13:45:55 +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.
+
+diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js
+index fd5357997a4e05567146dc997e47af408e1fc8f4..cb53212069794eaac8ceaf28001f5db25e127f0a 100644
+--- a/lib/internal/process/pre_execution.js
++++ b/lib/internal/process/pre_execution.js
+@@ -127,6 +127,9 @@ function setupUserModules() {
+   initializeESMLoader();
+   const CJSLoader = require('internal/modules/cjs/loader');
+   assert(!CJSLoader.hasLoadedAnyUserCJSModule);
++  if (getEmbedderOptions().hasEmbedderPreload) {
++    runEmbedderPreload();
++  }
+   loadPreloadModules();
+   // Need to be done after --require setup.
+   initializeFrozenIntrinsics();
+@@ -601,6 +604,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 c4caef25af670658965fc740ce03c2d2c4ed3e66..19443a9672441da5b98921eab9385083a72e3b7e 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,
++    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,
++      std::move(preload));
+ 
+ #if HAVE_INSPECTOR
+   if (env->should_create_inspector()) {
+diff --git a/src/env-inl.h b/src/env-inl.h
+index 103dc6711e71e15da640edc5e017bc638ddc6ad1..4d12e6e406c1078fd92f3cc837c2f8a926fadd1d 100644
+--- a/src/env-inl.h
++++ b/src/env-inl.h
+@@ -388,6 +388,10 @@ 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 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 5bdbfad4f4d3ef16c41ff8e5dae90f48a6d5f5a2..98fd9bdcf99e7ddcd4ae6baa23998b855cc3ddfe 100644
+--- a/src/env.cc
++++ b/src/env.cc
+@@ -643,7 +643,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)),
+@@ -666,7 +667,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);
+ 
+@@ -736,14 +738,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 36e8e7d960a95a9040ad963c79a7f66c89233c87..1b11c4243d18f14f4aaaad2683295ffff49dfd04 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 {
+ 
+ #endif  // HAVE_INSPECTOR
+ 
++  inline const EmbedderPreloadCallback& embedder_preload() const;
++
+   inline void set_process_exit_handler(
+       std::function<void(Environment*, int)>&& handler);
+ 
+@@ -1102,6 +1106,7 @@ class Environment : public MemoryRetainer {
+       DefaultProcessExitHandler };
+ 
+   std::unique_ptr<Realm> principal_realm_ = nullptr;
++  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 26368061a909e6abc62a4cf261a5dbbd79404f1a..bb4065e33164c3ea762a27b71606ab4ed7b1b336 100644
+--- a/src/node.h
++++ b/src/node.h
+@@ -593,9 +593,21 @@ struct InspectorParentHandle {
+   virtual ~InspectorParentHandle();
+ };
+ 
++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.
++//
++// 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 = {},
++    EmbedderPreloadCallback preload = nullptr);
+ 
+ // 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()})
+diff --git a/src/node_options.cc b/src/node_options.cc
+index 365748f046f9d0f232d4f0ebc7b0c7f56bbd74e2..a076de0c5e577114a6166844ab3b4f02db8065ad 100644
+--- a/src/node_options.cc
++++ b/src/node_options.cc
+@@ -1230,6 +1230,12 @@ void GetEmbedderOptions(const FunctionCallbackInfo<Value>& args) {
+            Boolean::New(isolate, env->no_global_search_paths()))
+       .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 f70e6ddf4303f303d7ace859b257738fd6707853..e6eb9d8602193ee8823724061592ae2ac681a816 100644
+--- a/src/node_snapshotable.cc
++++ b/src/node_snapshotable.cc
+@@ -1462,6 +1462,13 @@ void SerializeSnapshotableObjects(Realm* realm,
+ 
+ namespace mksnapshot {
+ 
++static 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]);
++}
++
+ 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,
+                 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,
+ }
+ 
+ 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 6a49144ec4f2059fe75983609b0768e4c2b1817d..dc2eb247b011f9cb1945c173c49e029f068ef103 100644
+--- a/src/node_worker.cc
++++ b/src/node_worker.cc
+@@ -60,6 +60,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);
+@@ -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_));
+diff --git a/src/node_worker.h b/src/node_worker.h
+index a77c416735a79feb3f54e40d72a98c8903a20ccd..deab68576f6330f8bcfb4703fd05dbb9c515e473 100644
+--- a/src/node_worker.h
++++ b/src/node_worker.h
+@@ -113,6 +113,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 09dcb1dccc1b28048c6300e2c23c2c40722272af..14a76a9baa7ca39a628553f730cde6c3c04c6be9 100644
+--- a/test/cctest/test_environment.cc
++++ b/test/cctest/test_environment.cc
+@@ -740,3 +740,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::EnvironmentFlags::kDefaultFlags, {}, {},
++                              preload),
++      node::FreeEnvironment);
++
++  v8::Local<v8::Value> main_ret =
++      node::LoadEnvironment(env.get(), "return process.prop;").ToLocalChecked();
++  node::Utf8Value main_ret_str(isolate_, main_ret);
++  EXPECT_EQ(std::string(*main_ret_str), "preload");
++}

+ 2 - 2
script/gen-filenames.ts

@@ -38,8 +38,8 @@ const main = async () => {
       config: 'webpack.config.worker.js'
     },
     {
-      name: 'asar_bundle_deps',
-      config: 'webpack.config.asar.js'
+      name: 'node_bundle_deps',
+      config: 'webpack.config.node.js'
     },
     {
       name: 'utility_bundle_deps',

+ 2 - 7
shell/app/node_main.cc

@@ -20,7 +20,6 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
 #include "content/public/common/content_switches.h"
-#include "electron/electron_version.h"
 #include "electron/fuses.h"
 #include "gin/array_buffer.h"
 #include "gin/public/isolate_holder.h"
@@ -260,7 +259,8 @@ int NodeMain(int argc, char* argv[]) {
       env = node::CreateEnvironment(
           isolate_data, isolate->GetCurrentContext(), result->args(),
           result->exec_args(),
-          static_cast<node::EnvironmentFlags::Flags>(env_flags));
+          static_cast<node::EnvironmentFlags::Flags>(env_flags), {}, {},
+          &OnNodePreload);
       CHECK_NE(nullptr, env);
 
       node::SetIsolateUpForNode(isolate);
@@ -282,11 +282,6 @@ int NodeMain(int argc, char* argv[]) {
 #endif
 
       process.Set("crashReporter", reporter);
-
-      gin_helper::Dictionary versions;
-      if (process.Get("versions", &versions)) {
-        versions.SetReadOnly(ELECTRON_PROJECT_NAME, ELECTRON_VERSION_STRING);
-      }
     }
 
     v8::HandleScope scope(isolate);

+ 0 - 15
shell/common/api/electron_api_asar.cc

@@ -10,7 +10,6 @@
 #include "shell/common/gin_converters/file_path_converter.h"
 #include "shell/common/gin_helper/dictionary.h"
 #include "shell/common/node_includes.h"
-#include "shell/common/node_util.h"
 
 namespace {
 
@@ -191,19 +190,6 @@ class Archive : public node::ObjectWrap {
   std::shared_ptr<asar::Archive> archive_;
 };
 
-static void InitAsarSupport(const v8::FunctionCallbackInfo<v8::Value>& args) {
-  auto* isolate = args.GetIsolate();
-  auto require = args[0];
-
-  // Evaluate asar_bundle.js.
-  std::vector<v8::Local<v8::String>> asar_bundle_params = {
-      node::FIXED_ONE_BYTE_STRING(isolate, "require")};
-  std::vector<v8::Local<v8::Value>> asar_bundle_args = {require};
-  electron::util::CompileAndCall(
-      isolate->GetCurrentContext(), "electron/js2c/asar_bundle",
-      &asar_bundle_params, &asar_bundle_args, nullptr);
-}
-
 static void SplitPath(const v8::FunctionCallbackInfo<v8::Value>& args) {
   auto* isolate = args.GetIsolate();
 
@@ -239,7 +225,6 @@ void Initialize(v8::Local<v8::Object> exports,
   exports->Set(context, node::FIXED_ONE_BYTE_STRING(isolate, "Archive"), cons)
       .Check();
   NODE_SET_METHOD(exports, "splitPath", &SplitPath);
-  NODE_SET_METHOD(exports, "initAsarSupport", &InitAsarSupport);
 }
 
 }  // namespace

+ 0 - 8
shell/common/api/electron_bindings.cc

@@ -15,8 +15,6 @@
 #include "base/process/process_handle.h"
 #include "base/process/process_metrics_iocounters.h"
 #include "base/system/sys_info.h"
-#include "chrome/common/chrome_version.h"
-#include "electron/electron_version.h"
 #include "services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.h"
 #include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
 #include "shell/browser/browser.h"
@@ -84,12 +82,6 @@ void ElectronBindings::BindTo(v8::Isolate* isolate,
   dict.SetMethod("activateUvLoop",
                  base::BindRepeating(&ElectronBindings::ActivateUVLoop,
                                      base::Unretained(this)));
-
-  gin_helper::Dictionary versions;
-  if (dict.Get("versions", &versions)) {
-    versions.SetReadOnly(ELECTRON_PROJECT_NAME, ELECTRON_VERSION_STRING);
-    versions.SetReadOnly("chrome", CHROME_VERSION_STRING);
-  }
 }
 
 void ElectronBindings::EnvironmentDestroyed(node::Environment* env) {

+ 30 - 7
shell/common/node_bindings.cc

@@ -19,9 +19,11 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/trace_event/trace_event.h"
+#include "chrome/common/chrome_version.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/content_paths.h"
 #include "electron/buildflags/buildflags.h"
+#include "electron/electron_version.h"
 #include "electron/fuses.h"
 #include "shell/browser/api/electron_api_app.h"
 #include "shell/common/api/electron_bindings.h"
@@ -33,6 +35,7 @@
 #include "shell/common/gin_helper/event_emitter_caller.h"
 #include "shell/common/gin_helper/microtasks_scope.h"
 #include "shell/common/mac/main_application_bundle.h"
+#include "shell/common/node_util.h"
 #include "shell/common/world_ids.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_initializer.h"  // nogncheck
@@ -600,7 +603,6 @@ std::shared_ptr<node::Environment> NodeBindings::CreateEnvironment(
                          electron::fuses::IsOnlyLoadAppFromAsarEnabled()));
   }
 
-  base::FilePath resources_path = GetResourcesPath();
   std::string init_script = "electron/js2c/" + process_type + "_init";
 
   args.insert(args.begin() + 1, init_script);
@@ -639,7 +641,8 @@ std::shared_ptr<node::Environment> NodeBindings::CreateEnvironment(
     v8::TryCatch try_catch(isolate);
     env = node::CreateEnvironment(
         static_cast<node::IsolateData*>(isolate_data), context, args, exec_args,
-        static_cast<node::EnvironmentFlags::Flags>(flags));
+        static_cast<node::EnvironmentFlags::Flags>(flags), {}, {},
+        &OnNodePreload);
 
     if (try_catch.HasCaught()) {
       std::string err_msg =
@@ -725,11 +728,6 @@ std::shared_ptr<node::Environment> NodeBindings::CreateEnvironment(
 
   gin_helper::Dictionary process(context->GetIsolate(), env->process_object());
   process.SetReadOnly("type", process_type);
-  process.Set("resourcesPath", resources_path);
-  // The path to helper app.
-  base::FilePath helper_exec_path;
-  base::PathService::Get(content::CHILD_PROCESS_EXE, &helper_exec_path);
-  process.Set("helperExecPath", helper_exec_path);
 
   if (browser_env_ == BrowserEnvironment::kBrowser ||
       browser_env_ == BrowserEnvironment::kRenderer) {
@@ -921,4 +919,29 @@ void NodeBindings::EmbedThreadRunner(void* arg) {
   }
 }
 
+void OnNodePreload(node::Environment* env,
+                   v8::Local<v8::Value> process,
+                   v8::Local<v8::Value> require) {
+  // Set custom process properties.
+  gin_helper::Dictionary dict(env->isolate(), process.As<v8::Object>());
+  dict.SetReadOnly("resourcesPath", GetResourcesPath());
+  base::FilePath helper_exec_path;  // path to the helper app.
+  base::PathService::Get(content::CHILD_PROCESS_EXE, &helper_exec_path);
+  dict.SetReadOnly("helperExecPath", helper_exec_path);
+  gin_helper::Dictionary versions;
+  if (dict.Get("versions", &versions)) {
+    versions.SetReadOnly(ELECTRON_PROJECT_NAME, ELECTRON_VERSION_STRING);
+    versions.SetReadOnly("chrome", CHROME_VERSION_STRING);
+  }
+
+  // Execute lib/node/init.ts.
+  std::vector<v8::Local<v8::String>> bundle_params = {
+      node::FIXED_ONE_BYTE_STRING(env->isolate(), "process"),
+      node::FIXED_ONE_BYTE_STRING(env->isolate(), "require"),
+  };
+  std::vector<v8::Local<v8::Value>> bundle_args = {process, require};
+  electron::util::CompileAndCall(env->context(), "electron/js2c/node_init",
+                                 &bundle_params, &bundle_args, env);
+}
+
 }  // namespace electron

+ 6 - 0
shell/common/node_bindings.h

@@ -219,6 +219,12 @@ class NodeBindings {
   base::WeakPtrFactory<NodeBindings> weak_factory_{this};
 };
 
+// A thread-safe function responsible for loading preload script which runs for
+// all node environments (including child processes and workers).
+void OnNodePreload(node::Environment* env,
+                   v8::Local<v8::Value> process,
+                   v8::Local<v8::Value> require);
+
 }  // namespace electron
 
 #endif  // ELECTRON_SHELL_COMMON_NODE_BINDINGS_H_

+ 9 - 1
shell/common/node_util.cc

@@ -5,6 +5,7 @@
 #include "shell/common/node_util.h"
 
 #include "base/logging.h"
+#include "gin/converter.h"
 #include "shell/common/node_includes.h"
 
 namespace electron::util {
@@ -29,7 +30,14 @@ v8::MaybeLocal<v8::Value> CompileAndCall(
   // This will only be caught when something has gone terrible wrong as all
   // electron scripts are wrapped in a try {} catch {} by webpack
   if (try_catch.HasCaught()) {
-    LOG(ERROR) << "Failed to CompileAndCall electron script: " << id;
+    std::string msg = "no error message";
+    if (!try_catch.Message().IsEmpty()) {
+      gin::ConvertFromV8(isolate, try_catch.Message()->Get(), &msg);
+    } else if (try_catch.HasTerminated()) {
+      msg = "script execution has been terminated";
+    }
+    LOG(ERROR) << "Failed to CompileAndCall electron script (" << id
+               << "): " << msg;
   }
   return ret;
 }

+ 0 - 1
typings/internal-ambient.d.ts

@@ -93,7 +93,6 @@ declare namespace NodeJS {
       asarPath: string;
       filePath: string;
     };
-    initAsarSupport(require: NodeJS.Require): void;
   }
 
   interface NetBinding {