Skip to content

Commit

Permalink
bootstrap: load user land snapshot entry point from js
Browse files Browse the repository at this point in the history
  • Loading branch information
joyeecheung committed Jun 11, 2021
1 parent e098e27 commit 0624d1c
Show file tree
Hide file tree
Showing 9 changed files with 193 additions and 58 deletions.
110 changes: 110 additions & 0 deletions lib/internal/main/mksnapshot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
'use strict';

const {
SafeSet,
SafeArrayIterator
} = primordials;

const {
compileSnapshotMain
} = internalBinding('mksnapshot');

const {
getOptionValue
} = require('internal/options');

const {
readFileSync
} = require('fs');

const supportedModules = new SafeSet(new SafeArrayIterator([
// '_http_agent',
// '_http_client',
// '_http_common',
// '_http_incoming',
// '_http_outgoing',
// '_http_server',
'_stream_duplex',
'_stream_passthrough',
'_stream_readable',
'_stream_transform',
'_stream_wrap',
'_stream_writable',
// '_tls_common',
// '_tls_wrap',
'assert',
'assert/strict',
// 'async_hooks',
'buffer',
// 'child_process',
// 'cluster',
'console',
'constants',
// 'crypto',
// 'dgram',
// 'diagnostics_channel',
// 'dns',
// 'dns/promises',
// 'domain',
'events',
'fs',
'fs/promises',
// 'http',
// 'http2',
// 'https',
// 'inspector',
// 'module',
// 'net',
'os',
'path',
'path/posix',
'path/win32',
// 'perf_hooks',
'process',
'punycode',
'querystring',
// 'readline',
// 'repl',
'stream',
'stream/promises',
'string_decoder',
'sys',
'timers',
'timers/promises',
// 'tls',
// 'trace_events',
// 'tty',
'url',
'util',
'util/types',
'v8',
// 'vm',
// 'worker_threads',
// 'zlib',
]));

function supportedInUserSnapshot(id) {
return supportedModules.has(id);
}

const warnedModules = new SafeSet();

function requireForUserSnapshot(id) {
if (supportedInUserSnapshot(id)) {
if (!warnedModules.has(id)) {
process.emitWarning(
`built-in module ${id} is not yet supported in user snapshots`);
warnedModules.add(id);
}
}

return require(id);
}

// prepareMainThreadExecution(true);

const mainFile = readFileSync(getOptionValue('--snapshot-main'), 'utf-8');

const snapshotMainFunction = compileSnapshotMain(mainFile);

snapshotMainFunction(requireForUserSnapshot);
1 change: 1 addition & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@
'lib/internal/main/eval_string.js',
'lib/internal/main/eval_stdin.js',
'lib/internal/main/inspect.js',
'lib/internal/main/mksnapshot.js',
'lib/internal/main/print_help.js',
'lib/internal/main/prof_process.js',
'lib/internal/main/repl.js',
Expand Down
4 changes: 4 additions & 0 deletions src/debug_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,10 @@ void CheckedUvLoopClose(uv_loop_t* loop) {
CHECK(0 && "uv_loop_close() while having open handles");
}

void PrintLibuvHandleInformation(Environment* env) {
PrintLibuvHandleInformation(env->event_loop(), stderr);
}

void PrintLibuvHandleInformation(uv_loop_t* loop, FILE* stream) {
struct Info {
std::unique_ptr<NativeSymbolDebuggingContext> ctx;
Expand Down
1 change: 1 addition & 0 deletions src/debug_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ class NativeSymbolDebuggingContext {
// but still aborts the process.
void CheckedUvLoopClose(uv_loop_t* loop);
void PrintLibuvHandleInformation(uv_loop_t* loop, FILE* stream);
void PrintLibuvHandleInformation(Environment* env);

namespace per_process {
extern EnabledDebugList enabled_debug_list;
Expand Down
25 changes: 8 additions & 17 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,10 @@ MaybeLocal<Value> StartExecution(Environment* env, StartExecutionCallback cb) {
return StartExecution(env, "internal/main/worker_thread");
}

if (!per_process::cli_options->snapshot_main.empty()) {
return StartExecution(env, "internal/main/mksnapshot");
}

std::string first_argv;
if (env->argv().size() > 1) {
first_argv = env->argv()[1];
Expand Down Expand Up @@ -1130,23 +1134,10 @@ int Start(int argc, char** argv) {

if (!per_process::cli_options->snapshot_main.empty()) {
SnapshotData data;
{
std::string entry;
int r =
ReadFileSync(&entry, per_process::cli_options->snapshot_main.c_str());
if (r != 0) {
const char* code = uv_err_name(r);
const char* message = uv_strerror(r);
FPrintF(stderr,
"Failed to open %s. %s: %s\n",
per_process::cli_options->snapshot_main.c_str(),
code,
message);
return 1;
}
node::SnapshotBuilder::Generate(
&data, entry, result.args, result.exec_args);
}
node::SnapshotBuilder::Generate(&data,
per_process::cli_options->snapshot_main,
result.args,
result.exec_args);

std::string snapshot_blob_path;
if (!per_process::cli_options->snapshot_blob.empty()) {
Expand Down
1 change: 1 addition & 0 deletions src/node_binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
V(js_udp_wrap) \
V(messaging) \
V(module_wrap) \
V(mksnapshot) \
V(native_module) \
V(options) \
V(os) \
Expand Down
2 changes: 2 additions & 0 deletions src/node_external_reference.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ class ExternalReferenceRegistry {
V(handle_wrap) \
V(heap_utils) \
V(messaging) \
V(mksnapshot) \
V(native_module) \
V(options) \
V(process_methods) \
V(process_object) \
V(task_queue) \
Expand Down
8 changes: 8 additions & 0 deletions src/node_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "env-inl.h"
#include "node_binding.h"
#include "node_external_reference.h"
#include "node_internals.h"

#include <errno.h>
Expand Down Expand Up @@ -1083,6 +1084,10 @@ void Initialize(Local<Object> target,
.Check();
}

static void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(GetOptions);
}

} // namespace options_parser

void HandleEnvOptions(std::shared_ptr<EnvironmentOptions> env_options) {
Expand Down Expand Up @@ -1146,6 +1151,9 @@ std::vector<std::string> ParseNodeOptionsEnvVar(
}
return env_argv;
}

} // namespace node

NODE_MODULE_CONTEXT_AWARE_INTERNAL(options, node::options_parser::Initialize)
NODE_MODULE_EXTERNAL_REFERENCE(options,
node::options_parser::RegisterExternalReferences)
99 changes: 58 additions & 41 deletions src/node_snapshotable.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace node {

using v8::Context;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::HandleScope;
using v8::Isolate;
using v8::Local;
Expand Down Expand Up @@ -589,50 +590,20 @@ void SnapshotBuilder::Generate(SnapshotData* out,
// Run the entry point file
if (!entry_file.empty()) {
TryCatch bootstrapCatch(isolate);
std::string filename_s = std::string("node:snapshot_main");
Local<String> filename =
OneByteString(isolate, filename_s.c_str(), filename_s.size());
ScriptOrigin origin(isolate, filename, 0, 0, true);
Local<String> source = ToV8Value(context, entry_file, isolate)
.ToLocalChecked()
.As<String>();
// TODO(joyee): do we need all of these? Maybe we would want a less
// internal version of them.
std::vector<Local<String>> parameters = {env->require_string(),
env->process_string(),
env->internal_binding_string(),
env->primordials_string()};
ScriptCompiler::Source script_source(source, origin);
Local<Function> fn;
if (!ScriptCompiler::CompileFunctionInContext(
context,
&script_source,
parameters.size(),
parameters.data(),
0,
nullptr,
ScriptCompiler::kEagerCompile)
.ToLocal(&fn)) {
if (bootstrapCatch.HasCaught()) {
PrintCaughtException(isolate, context, bootstrapCatch);
}
abort();
}
std::vector<Local<Value>> args = {env->native_module_require(),
env->process_object(),
env->internal_binding_loader(),
env->primordials()};
Local<Value> result;
if (!fn->Call(context, Undefined(isolate), args.size(), args.data())
.ToLocal(&result)) {
if (bootstrapCatch.HasCaught()) {
PrintCaughtException(isolate, context, bootstrapCatch);
}
abort();
}
// TODO(joyee): we could use the result for something special, like
// setting up initializers that should be invoked at snapshot
// dehydration.
MaybeLocal<Value> result =
LoadEnvironment(env, StartExecutionCallback{});
if (bootstrapCatch.HasCaught()) {
PrintCaughtException(isolate, context, bootstrapCatch);
}
result.ToLocalChecked();
// FIXME(joyee): right now running the loop in the snapshot builder
// seems to intrudoces inconsistencies in JS land that need to be
// synchronized again after snapshot restoration.
// int exit_code = SpinEventLoop(env).FromMaybe(1);
// CHECK_EQ(exit_code, 0);
}

if (per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
Expand Down Expand Up @@ -809,4 +780,50 @@ void SerializeBindingData(Environment* env,
});
}

namespace mksnapshot {

static void CompileSnapshotMain(const FunctionCallbackInfo<Value>& args) {
CHECK(args[0]->IsString());
Local<String> source = args[0].As<String>();
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
std::string filename_s = std::string("node:snapshot_main");
Local<String> filename =
OneByteString(isolate, filename_s.c_str(), filename_s.size());
ScriptOrigin origin(isolate, filename, 0, 0, true);
// TODO(joyee): do we need all of these? Maybe we would want a less
// internal version of them.
std::vector<Local<String>> parameters = {
FIXED_ONE_BYTE_STRING(isolate, "require")};
ScriptCompiler::Source script_source(source, origin);
Local<Function> fn;
if (ScriptCompiler::CompileFunctionInContext(context,
&script_source,
parameters.size(),
parameters.data(),
0,
nullptr,
ScriptCompiler::kEagerCompile)
.ToLocal(&fn)) {
args.GetReturnValue().Set(fn);
}
}

static void Initialize(Local<Object> target,
Local<Value> unused,
Local<Context> context,
void* priv) {
Environment* env = Environment::GetCurrent(context);
env->SetMethod(target, "compileSnapshotMain", CompileSnapshotMain);
}

static void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(CompileSnapshotMain);
registry->Register(MarkBootstrapComplete);
}
} // namespace mksnapshot
} // namespace node

NODE_MODULE_CONTEXT_AWARE_INTERNAL(mksnapshot, node::mksnapshot::Initialize)
NODE_MODULE_EXTERNAL_REFERENCE(mksnapshot,
node::mksnapshot::RegisterExternalReferences)

0 comments on commit 0624d1c

Please sign in to comment.