Skip to content

Commit eb664c3

Browse files
addaleaxdanbev
authored andcommitted
src: clean up node::Init() wrt embedder scenarios
This makes the STL variant of `node::Init()` a bit more suitable for inclusion in a proper embedder API, as errors or other output are reported to the caller rather than directly being printed, and the process is not exited directly either. PR-URL: #25370 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
1 parent f9b129e commit eb664c3

File tree

1 file changed

+64
-51
lines changed

1 file changed

+64
-51
lines changed

src/node.cc

+64-51
Original file line numberDiff line numberDiff line change
@@ -1147,42 +1147,23 @@ inline void PlatformInit() {
11471147
#endif // _WIN32
11481148
}
11491149

1150-
void ProcessArgv(std::vector<std::string>* args,
1151-
std::vector<std::string>* exec_args,
1152-
bool is_env) {
1150+
int ProcessGlobalArgs(std::vector<std::string>* args,
1151+
std::vector<std::string>* exec_args,
1152+
std::vector<std::string>* errors,
1153+
bool is_env) {
11531154
// Parse a few arguments which are specific to Node.
11541155
std::vector<std::string> v8_args;
1155-
std::vector<std::string> errors{};
11561156

1157-
{
1158-
// TODO(addaleax): The mutex here should ideally be held during the
1159-
// entire function, but that doesn't play well with the exit() calls below.
1160-
Mutex::ScopedLock lock(per_process::cli_options_mutex);
1161-
options_parser::PerProcessOptionsParser::instance.Parse(
1162-
args,
1163-
exec_args,
1164-
&v8_args,
1165-
per_process::cli_options.get(),
1166-
is_env ? kAllowedInEnvironment : kDisallowedInEnvironment,
1167-
&errors);
1168-
}
1169-
1170-
if (!errors.empty()) {
1171-
for (auto const& error : errors) {
1172-
fprintf(stderr, "%s: %s\n", args->at(0).c_str(), error.c_str());
1173-
}
1174-
exit(9);
1175-
}
1157+
Mutex::ScopedLock lock(per_process::cli_options_mutex);
1158+
options_parser::PerProcessOptionsParser::instance.Parse(
1159+
args,
1160+
exec_args,
1161+
&v8_args,
1162+
per_process::cli_options.get(),
1163+
is_env ? kAllowedInEnvironment : kDisallowedInEnvironment,
1164+
errors);
11761165

1177-
if (per_process::cli_options->print_version) {
1178-
printf("%s\n", NODE_VERSION);
1179-
exit(0);
1180-
}
1181-
1182-
if (per_process::cli_options->print_v8_help) {
1183-
V8::SetFlagsFromString("--help", 6);
1184-
exit(0);
1185-
}
1166+
if (!errors->empty()) return 9;
11861167

11871168
for (const std::string& cve : per_process::cli_options->security_reverts)
11881169
Revert(cve.c_str());
@@ -1222,19 +1203,17 @@ void ProcessArgv(std::vector<std::string>* args,
12221203
}
12231204

12241205
// Anything that's still in v8_argv is not a V8 or a node option.
1225-
for (size_t i = 1; i < v8_args_as_char_ptr.size(); i++) {
1226-
fprintf(stderr, "%s: bad option: %s\n",
1227-
args->at(0).c_str(), v8_args_as_char_ptr[i]);
1228-
}
1206+
for (size_t i = 1; i < v8_args_as_char_ptr.size(); i++)
1207+
errors->push_back("bad option: " + std::string(v8_args_as_char_ptr[i]));
12291208

1230-
if (v8_args_as_char_ptr.size() > 1) {
1231-
exit(9);
1232-
}
1233-
}
1209+
if (v8_args_as_char_ptr.size() > 1) return 9;
12341210

1211+
return 0;
1212+
}
12351213

1236-
void Init(std::vector<std::string>* argv,
1237-
std::vector<std::string>* exec_argv) {
1214+
int Init(std::vector<std::string>* argv,
1215+
std::vector<std::string>* exec_argv,
1216+
std::vector<std::string>* errors) {
12381217
// Initialize prog_start_time to get relative uptime.
12391218
per_process::prog_start_time = static_cast<double>(uv_now(uv_default_loop()));
12401219

@@ -1295,11 +1274,13 @@ void Init(std::vector<std::string>* argv,
12951274
std::vector<std::string> env_argv = SplitString("x " + node_options, ' ');
12961275
env_argv[0] = argv->at(0);
12971276

1298-
ProcessArgv(&env_argv, nullptr, true);
1277+
const int exit_code = ProcessGlobalArgs(&env_argv, nullptr, errors, true);
1278+
if (exit_code != 0) return exit_code;
12991279
}
13001280
#endif
13011281

1302-
ProcessArgv(argv, exec_argv, false);
1282+
const int exit_code = ProcessGlobalArgs(argv, exec_argv, errors, false);
1283+
if (exit_code != 0) return exit_code;
13031284

13041285
// Set the process.title immediately after processing argv if --title is set.
13051286
if (!per_process::cli_options->title.empty())
@@ -1313,11 +1294,9 @@ void Init(std::vector<std::string>* argv,
13131294
// Initialize ICU.
13141295
// If icu_data_dir is empty here, it will load the 'minimal' data.
13151296
if (!i18n::InitializeICUDirectory(per_process::cli_options->icu_data_dir)) {
1316-
fprintf(stderr,
1317-
"%s: could not initialize ICU "
1318-
"(check NODE_ICU_DATA or --icu-data-dir parameters)\n",
1319-
argv->at(0).c_str());
1320-
exit(9);
1297+
errors->push_back("could not initialize ICU "
1298+
"(check NODE_ICU_DATA or --icu-data-dir parameters)\n");
1299+
return 9;
13211300
}
13221301
per_process::metadata.versions.InitializeIntlVersions();
13231302
#endif
@@ -1326,6 +1305,7 @@ void Init(std::vector<std::string>* argv,
13261305
// otherwise embedders using node::Init to initialize everything will not be
13271306
// able to set it and native modules will not load for them.
13281307
node_is_initialized = true;
1308+
return 0;
13291309
}
13301310

13311311
// TODO(addaleax): Deprecate and eventually remove this.
@@ -1335,8 +1315,25 @@ void Init(int* argc,
13351315
const char*** exec_argv) {
13361316
std::vector<std::string> argv_(argv, argv + *argc); // NOLINT
13371317
std::vector<std::string> exec_argv_;
1318+
std::vector<std::string> errors;
1319+
1320+
// This (approximately) duplicates some logic that has been moved to
1321+
// node::Start(), with the difference that here we explicitly call `exit()`.
1322+
int exit_code = Init(&argv_, &exec_argv_, &errors);
13381323

1339-
Init(&argv_, &exec_argv_);
1324+
for (const std::string& error : errors)
1325+
fprintf(stderr, "%s: %s\n", argv_.at(0).c_str(), error.c_str());
1326+
if (exit_code != 0) exit(exit_code);
1327+
1328+
if (per_process::cli_options->print_version) {
1329+
printf("%s\n", NODE_VERSION);
1330+
exit(0);
1331+
}
1332+
1333+
if (per_process::cli_options->print_v8_help) {
1334+
V8::SetFlagsFromString("--help", 6); // Doesn't return.
1335+
UNREACHABLE();
1336+
}
13401337

13411338
*argc = argv_.size();
13421339
*exec_argc = exec_argv_.size();
@@ -1653,6 +1650,16 @@ inline int Start(uv_loop_t* event_loop,
16531650
if (isolate == nullptr)
16541651
return 12; // Signal internal error.
16551652

1653+
if (per_process::cli_options->print_version) {
1654+
printf("%s\n", NODE_VERSION);
1655+
return 0;
1656+
}
1657+
1658+
if (per_process::cli_options->print_v8_help) {
1659+
V8::SetFlagsFromString("--help", 6); // Doesn't return.
1660+
UNREACHABLE();
1661+
}
1662+
16561663
{
16571664
Mutex::ScopedLock scoped_lock(per_process::main_isolate_mutex);
16581665
CHECK_NULL(per_process::main_isolate);
@@ -1712,8 +1719,14 @@ int Start(int argc, char** argv) {
17121719

17131720
std::vector<std::string> args(argv, argv + argc);
17141721
std::vector<std::string> exec_args;
1722+
std::vector<std::string> errors;
17151723
// This needs to run *before* V8::Initialize().
1716-
Init(&args, &exec_args);
1724+
{
1725+
const int exit_code = Init(&args, &exec_args, &errors);
1726+
for (const std::string& error : errors)
1727+
fprintf(stderr, "%s: %s\n", args.at(0).c_str(), error.c_str());
1728+
if (exit_code != 0) return exit_code;
1729+
}
17171730

17181731
#if HAVE_OPENSSL
17191732
{

0 commit comments

Comments
 (0)