Skip to content

Commit 3803b02

Browse files
authored
src: share common code paths for SEA and embedder script
Since SEA is very similar in principle to embedding functionality, it makes sense to share code paths where possible. This commit does so and addresses a `TODO` while doing so. It also adds a utility to directly run CJS code to the embedder startup callback, which comes in handy for this purpose. Finally, this commit is breaking because it aligns the behavior of `require()`ing internal modules; previously, embedders could use the `require` function that they received to do so. (If this is not considered breaking because accessing internals is not covered by the API, then this would need ABI compatibility patches for becoming fully non-breaking.) PR-URL: #46825 Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Minwoo Jung <nodecorelab@gmail.com>
1 parent 7bd909b commit 3803b02

16 files changed

Lines changed: 145 additions & 184 deletions

‎lib/internal/main/embedding.js‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use strict';
2+
const{
3+
prepareMainThreadExecution,
4+
markBootstrapComplete,
5+
}=require('internal/process/pre_execution');
6+
const{ isSea }=internalBinding('sea');
7+
const{ emitExperimentalWarning }=require('internal/util');
8+
const{ embedderRequire, embedderRunCjs }=require('internal/util/embedding');
9+
const{ getEmbedderEntryFunction }=internalBinding('mksnapshot');
10+
11+
prepareMainThreadExecution(false,true);
12+
markBootstrapComplete();
13+
14+
if(isSea()){
15+
emitExperimentalWarning('Single executable application');
16+
}
17+
18+
returngetEmbedderEntryFunction()(embedderRequire,embedderRunCjs);

‎lib/internal/main/environment.js‎

Lines changed: 0 additions & 13 deletions
This file was deleted.

‎lib/internal/main/mksnapshot.js‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,16 +119,25 @@ function main() {
119119
const{
120120
prepareMainThreadExecution
121121
}=require('internal/process/pre_execution');
122+
constpath=require('path');
122123

123124
letserializeMainFunction=getEmbedderEntryFunction();
124125
constserializeMainArgs=[requireForUserSnapshot];
125126

126127
if(serializeMainFunction){// embedded case
127128
prepareMainThreadExecution(false,false);
129+
// TODO(addaleax): Make this `embedderRunCjs` once require('module')
130+
// is supported in snapshots.
131+
constfilename=process.execPath;
132+
constdirname=path.dirname(filename);
133+
functionminimalRunCjs(source){
134+
constfn=compileSerializeMain(filename,source);
135+
returnfn(requireForUserSnapshot,filename,dirname);
136+
}
137+
serializeMainArgs.push(minimalRunCjs);
128138
}else{
129139
prepareMainThreadExecution(true,false);
130140
constfile=process.argv[1];
131-
constpath=require('path');
132141
constfilename=path.resolve(file);
133142
constdirname=path.dirname(filename);
134143
constsource=readFileSync(file,'utf-8');

‎lib/internal/main/single_executable_application.js‎

Lines changed: 0 additions & 55 deletions
This file was deleted.

‎lib/internal/util/embedding.js‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict';
2+
const{codes: {ERR_UNKNOWN_BUILTIN_MODULE}}=require('internal/errors');
3+
const{ Module, wrapSafe }=require('internal/modules/cjs/loader');
4+
5+
// This is roughly the same as:
6+
//
7+
// const mod = new Module(filename);
8+
// mod._compile(contents, filename);
9+
//
10+
// but the code has been duplicated because currently there is no way to set the
11+
// value of require.main to module.
12+
//
13+
// TODO(RaisinTen): Find a way to deduplicate this.
14+
15+
functionembedderRunCjs(contents){
16+
constfilename=process.execPath;
17+
constcompiledWrapper=wrapSafe(filename,contents);
18+
19+
constcustomModule=newModule(filename,null);
20+
customModule.filename=filename;
21+
customModule.paths=Module._nodeModulePaths(customModule.path);
22+
23+
constcustomExports=customModule.exports;
24+
25+
embedderRequire.main=customModule;
26+
27+
constcustomFilename=customModule.filename;
28+
29+
constcustomDirname=customModule.path;
30+
31+
returncompiledWrapper(
32+
customExports,
33+
embedderRequire,
34+
customModule,
35+
customFilename,
36+
customDirname);
37+
}
38+
39+
functionembedderRequire(path){
40+
if(!Module.isBuiltin(path)){
41+
thrownewERR_UNKNOWN_BUILTIN_MODULE(path);
42+
}
43+
44+
returnrequire(path);
45+
}
46+
47+
module.exports={ embedderRequire, embedderRunCjs };

‎src/api/environment.cc‎

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -528,17 +528,15 @@ MaybeLocal<Value> LoadEnvironment(
528528
returnStartExecution(env, cb);
529529
}
530530

531-
MaybeLocal<Value> LoadEnvironment(
532-
Environment* env,
533-
constchar* main_script_source_utf8) {
534-
CHECK_NOT_NULL(main_script_source_utf8);
531+
MaybeLocal<Value> LoadEnvironment(Environment* env,
532+
std::string_view main_script_source_utf8) {
533+
CHECK_NOT_NULL(main_script_source_utf8.data());
535534
returnLoadEnvironment(
536535
env, [&](const StartExecutionCallbackInfo& info) -> MaybeLocal<Value> {
537-
std::string name = "embedder_main_" + std::to_string(env->thread_id());
538-
env->builtin_loader()->Add(name.c_str(), main_script_source_utf8);
539-
Realm* realm = env->principal_realm();
540-
541-
return realm->ExecuteBootstrapper(name.c_str());
536+
Local<Value> main_script =
537+
ToV8Value(env->context(), main_script_source_utf8).ToLocalChecked();
538+
return info.run_cjs->Call(
539+
env->context(), Null(env->isolate()), 1, &main_script);
542540
});
543541
}
544542

‎src/env-inl.h‎

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -406,14 +406,12 @@ inline builtins::BuiltinLoader* Environment::builtin_loader() {
406406
return &builtin_loader_;
407407
}
408408

409-
inlineconst StartExecutionCallback&
410-
Environment::embedder_mksnapshot_entry_point() const {
411-
return embedder_mksnapshot_entry_point_;
409+
inlineconst StartExecutionCallback& Environment::embedder_entry_point() const {
410+
return embedder_entry_point_;
412411
}
413412

414-
inlinevoidEnvironment::set_embedder_mksnapshot_entry_point(
415-
StartExecutionCallback&& fn) {
416-
embedder_mksnapshot_entry_point_ = std::move(fn);
413+
inlinevoidEnvironment::set_embedder_entry_point(StartExecutionCallback&& fn) {
414+
embedder_entry_point_ = std::move(fn);
417415
}
418416

419417
inlinedoubleEnvironment::new_async_id() {

‎src/env.h‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -948,8 +948,8 @@ class Environment : public MemoryRetainer {
948948

949949
#endif// HAVE_INSPECTOR
950950

951-
inlineconst StartExecutionCallback& embedder_mksnapshot_entry_point() const;
952-
inlinevoidset_embedder_mksnapshot_entry_point(StartExecutionCallback&& fn);
951+
inlineconst StartExecutionCallback& embedder_entry_point() const;
952+
inlinevoidset_embedder_entry_point(StartExecutionCallback&& fn);
953953

954954
inlinevoidset_process_exit_handler(
955955
std::function<void(Environment*, ExitCode)>&& handler);
@@ -1133,7 +1133,7 @@ class Environment : public MemoryRetainer {
11331133
std::unique_ptr<Realm> principal_realm_ = nullptr;
11341134

11351135
builtins::BuiltinLoader builtin_loader_;
1136-
StartExecutionCallback embedder_mksnapshot_entry_point_;
1136+
StartExecutionCallback embedder_entry_point_;
11371137

11381138
// Used by allocate_managed_buffer() and release_managed_buffer() to keep
11391139
// track of the BackingStore for a given pointer.

‎src/node.cc‎

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -277,22 +277,17 @@ MaybeLocal<Value> StartExecution(Environment* env, StartExecutionCallback cb) {
277277

278278
if (cb != nullptr) {
279279
EscapableHandleScope scope(env->isolate());
280+
// TODO(addaleax): pass the callback to the main script more directly,
281+
// e.g. by making StartExecution(env, builtin) parametrizable
282+
env->set_embedder_entry_point(std::move(cb));
283+
auto reset_entry_point =
284+
OnScopeLeave([&]() { env->set_embedder_entry_point({}); });
280285

281-
if (env->isolate_data()->options()->build_snapshot) {
282-
// TODO(addaleax): pass the callback to the main script more directly,
283-
// e.g. by making StartExecution(env, builtin) parametrizable
284-
env->set_embedder_mksnapshot_entry_point(std::move(cb));
285-
auto reset_entry_point =
286-
OnScopeLeave([&]() { env->set_embedder_mksnapshot_entry_point({}); });
286+
constchar* entry = env->isolate_data()->options()->build_snapshot
287+
? "internal/main/mksnapshot"
288+
: "internal/main/embedding";
287289

288-
returnStartExecution(env, "internal/main/mksnapshot");
289-
}
290-
291-
if (StartExecution(env, "internal/main/environment").IsEmpty()) return {};
292-
return scope.EscapeMaybe(cb({
293-
env->process_object(),
294-
env->builtin_module_require(),
295-
}));
290+
return scope.EscapeMaybe(StartExecution(env, entry));
296291
}
297292

298293
// TODO(joyeecheung): move these conditions into JS land and let the
@@ -312,18 +307,6 @@ MaybeLocal<Value> StartExecution(Environment* env, StartExecutionCallback cb) {
312307
first_argv = env->argv()[1];
313308
}
314309

315-
#ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION
316-
if (sea::IsSingleExecutable()) {
317-
// TODO(addaleax): Find a way to reuse:
318-
//
319-
// LoadEnvironment(Environment*, const char*)
320-
//
321-
// instead and not add yet another main entry point here because this
322-
// already duplicates existing code.
323-
returnStartExecution(env, "internal/main/single_executable_application");
324-
}
325-
#endif
326-
327310
if (first_argv == "inspect") {
328311
returnStartExecution(env, "internal/main/inspect");
329312
}

‎src/node.h‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,7 @@ NODE_EXTERN std::unique_ptr<InspectorParentHandle> GetInspectorParentHandle(
682682
structStartExecutionCallbackInfo {
683683
v8::Local<v8::Object> process_object;
684684
v8::Local<v8::Function> native_require;
685+
v8::Local<v8::Function> run_cjs;
685686
};
686687

687688
using StartExecutionCallback =
@@ -691,8 +692,7 @@ NODE_EXTERN v8::MaybeLocal<v8::Value> LoadEnvironment(
691692
Environment* env,
692693
StartExecutionCallback cb);
693694
NODE_EXTERN v8::MaybeLocal<v8::Value> LoadEnvironment(
694-
Environment* env,
695-
constchar* main_script_source_utf8);
695+
Environment* env, std::string_view main_script_source_utf8);
696696
NODE_EXTERNvoidFreeEnvironment(Environment* env);
697697

698698
// Set a callback that is called when process.exit() is called from JS,

0 commit comments

Comments
 (0)