1 #include "node_options.h" // NOLINT(build/include_inline)
2 #include "node_options-inl.h"
3
4 #include "env-inl.h"
5 #include "node_binding.h"
6 #include "node_external_reference.h"
7 #include "node_internals.h"
8 #include "node_sea.h"
9 #if HAVE_OPENSSL
10 #include "openssl/opensslv.h"
11 #endif
12
13 #include <errno.h>
14 #include <algorithm>
15 #include <cstdlib> // strtoul, errno
16 #include <limits>
17 #include <sstream>
18 #include <string_view>
19
20 using v8::Boolean;
21 using v8::Context;
22 using v8::FunctionCallbackInfo;
23 using v8::Integer;
24 using v8::Isolate;
25 using v8::Local;
26 using v8::Map;
27 using v8::Number;
28 using v8::Object;
29 using v8::Undefined;
30 using v8::Value;
31
32 namespace node {
33
34 namespace per_process {
35 Mutex cli_options_mutex;
36 std::shared_ptr<PerProcessOptions> cli_options{new PerProcessOptions()};
37 } // namespace per_process
38
CheckOptions(std::vector<std::string> * errors,std::vector<std::string> * argv)39 void DebugOptions::CheckOptions(std::vector<std::string>* errors,
40 std::vector<std::string>* argv) {
41 #if !NODE_USE_V8_PLATFORM && !HAVE_INSPECTOR
42 if (inspector_enabled) {
43 errors->push_back("Inspector is not available when Node is compiled "
44 "--without-v8-platform and --without-inspector.");
45 }
46 #endif
47
48 if (deprecated_debug) {
49 errors->push_back("[DEP0062]: `node --debug` and `node --debug-brk` "
50 "are invalid. Please use `node --inspect` and "
51 "`node --inspect-brk` instead.");
52 }
53
54 using std::string_view_literals::operator""sv;
55 const std::vector<std::string_view> destinations =
56 SplitString(inspect_publish_uid_string, ","sv);
57 inspect_publish_uid.console = false;
58 inspect_publish_uid.http = false;
59 for (const std::string_view destination : destinations) {
60 if (destination == "stderr"sv) {
61 inspect_publish_uid.console = true;
62 } else if (destination == "http"sv) {
63 inspect_publish_uid.http = true;
64 } else {
65 errors->push_back("--inspect-publish-uid destination can be "
66 "stderr or http");
67 }
68 }
69 }
70
CheckOptions(std::vector<std::string> * errors,std::vector<std::string> * argv)71 void PerProcessOptions::CheckOptions(std::vector<std::string>* errors,
72 std::vector<std::string>* argv) {
73 #if HAVE_OPENSSL
74 if (use_openssl_ca && use_bundled_ca) {
75 errors->push_back("either --use-openssl-ca or --use-bundled-ca can be "
76 "used, not both");
77 }
78
79 // Any value less than 2 disables use of the secure heap.
80 if (secure_heap >= 2) {
81 if ((secure_heap & (secure_heap - 1)) != 0)
82 errors->push_back("--secure-heap must be a power of 2");
83 secure_heap_min =
84 std::min({
85 secure_heap,
86 secure_heap_min,
87 static_cast<int64_t>(std::numeric_limits<int>::max())});
88 secure_heap_min = std::max(static_cast<int64_t>(2), secure_heap_min);
89 if ((secure_heap_min & (secure_heap_min - 1)) != 0)
90 errors->push_back("--secure-heap-min must be a power of 2");
91 }
92 #endif // HAVE_OPENSSL
93
94 if (use_largepages != "off" &&
95 use_largepages != "on" &&
96 use_largepages != "silent") {
97 errors->push_back("invalid value for --use-largepages");
98 }
99 per_isolate->CheckOptions(errors, argv);
100 }
101
CheckOptions(std::vector<std::string> * errors,std::vector<std::string> * argv)102 void PerIsolateOptions::CheckOptions(std::vector<std::string>* errors,
103 std::vector<std::string>* argv) {
104 per_env->CheckOptions(errors, argv);
105 }
106
CheckOptions(std::vector<std::string> * errors,std::vector<std::string> * argv)107 void EnvironmentOptions::CheckOptions(std::vector<std::string>* errors,
108 std::vector<std::string>* argv) {
109 if (has_policy_integrity_string && experimental_policy.empty()) {
110 errors->push_back("--policy-integrity requires "
111 "--experimental-policy be enabled");
112 }
113 if (has_policy_integrity_string && experimental_policy_integrity.empty()) {
114 errors->push_back("--policy-integrity cannot be empty");
115 }
116
117 if (!input_type.empty()) {
118 if (input_type != "commonjs" && input_type != "module") {
119 errors->push_back("--input-type must be \"module\" or \"commonjs\"");
120 }
121 }
122
123 if (!type.empty()) {
124 if (type != "commonjs" && type != "module") {
125 errors->push_back("--experimental-default-type must be "
126 "\"module\" or \"commonjs\"");
127 }
128 }
129
130 if (!experimental_specifier_resolution.empty()) {
131 if (experimental_specifier_resolution != "node" &&
132 experimental_specifier_resolution != "explicit") {
133 errors->push_back(
134 "invalid value for --experimental-specifier-resolution");
135 }
136 }
137
138 if (syntax_check_only && has_eval_string) {
139 errors->push_back("either --check or --eval can be used, not both");
140 }
141
142 if (!unhandled_rejections.empty() &&
143 unhandled_rejections != "warn-with-error-code" &&
144 unhandled_rejections != "throw" &&
145 unhandled_rejections != "strict" &&
146 unhandled_rejections != "warn" &&
147 unhandled_rejections != "none") {
148 errors->push_back("invalid value for --unhandled-rejections");
149 }
150
151 if (tls_min_v1_3 && tls_max_v1_2) {
152 errors->push_back("either --tls-min-v1.3 or --tls-max-v1.2 can be "
153 "used, not both");
154 }
155
156 if (heap_snapshot_near_heap_limit < 0) {
157 errors->push_back("--heapsnapshot-near-heap-limit must not be negative");
158 }
159
160 if (test_runner) {
161 if (syntax_check_only) {
162 errors->push_back("either --test or --check can be used, not both");
163 }
164
165 if (has_eval_string) {
166 errors->push_back("either --test or --eval can be used, not both");
167 }
168
169 if (force_repl) {
170 errors->push_back("either --test or --interactive can be used, not both");
171 }
172
173 if (watch_mode_paths.size() > 0) {
174 errors->push_back(
175 "--watch-path cannot be used in combination with --test");
176 }
177
178 #ifndef ALLOW_ATTACHING_DEBUGGER_IN_TEST_RUNNER
179 debug_options_.allow_attaching_debugger = false;
180 #endif
181 }
182
183 if (watch_mode) {
184 if (syntax_check_only) {
185 errors->push_back("either --watch or --check can be used, not both");
186 } else if (has_eval_string) {
187 errors->push_back("either --watch or --eval can be used, not both");
188 } else if (force_repl) {
189 errors->push_back("either --watch or --interactive "
190 "can be used, not both");
191 } else if (!test_runner && (argv->size() < 1 || (*argv)[1].empty())) {
192 errors->push_back("--watch requires specifying a file");
193 }
194
195 #ifndef ALLOW_ATTACHING_DEBUGGER_IN_WATCH_MODE
196 debug_options_.allow_attaching_debugger = false;
197 #endif
198 }
199
200 #if HAVE_INSPECTOR
201 if (!cpu_prof) {
202 if (!cpu_prof_name.empty()) {
203 errors->push_back("--cpu-prof-name must be used with --cpu-prof");
204 }
205 if (!cpu_prof_dir.empty()) {
206 errors->push_back("--cpu-prof-dir must be used with --cpu-prof");
207 }
208 // We can't catch the case where the value passed is the default value,
209 // then the option just becomes a noop which is fine.
210 if (cpu_prof_interval != kDefaultCpuProfInterval) {
211 errors->push_back("--cpu-prof-interval must be used with --cpu-prof");
212 }
213 }
214
215 if (cpu_prof && cpu_prof_dir.empty() && !diagnostic_dir.empty()) {
216 cpu_prof_dir = diagnostic_dir;
217 }
218
219 if (!heap_prof) {
220 if (!heap_prof_name.empty()) {
221 errors->push_back("--heap-prof-name must be used with --heap-prof");
222 }
223 if (!heap_prof_dir.empty()) {
224 errors->push_back("--heap-prof-dir must be used with --heap-prof");
225 }
226 // We can't catch the case where the value passed is the default value,
227 // then the option just becomes a noop which is fine.
228 if (heap_prof_interval != kDefaultHeapProfInterval) {
229 errors->push_back("--heap-prof-interval must be used with --heap-prof");
230 }
231 }
232
233 if (heap_prof && heap_prof_dir.empty() && !diagnostic_dir.empty()) {
234 heap_prof_dir = diagnostic_dir;
235 }
236
237 debug_options_.CheckOptions(errors, argv);
238 #endif // HAVE_INSPECTOR
239 }
240
241 namespace options_parser {
242
243 class DebugOptionsParser : public OptionsParser<DebugOptions> {
244 public:
245 DebugOptionsParser();
246 };
247
248 class EnvironmentOptionsParser : public OptionsParser<EnvironmentOptions> {
249 public:
250 EnvironmentOptionsParser();
EnvironmentOptionsParser(const DebugOptionsParser & dop)251 explicit EnvironmentOptionsParser(const DebugOptionsParser& dop)
252 : EnvironmentOptionsParser() {
253 Insert(dop, &EnvironmentOptions::get_debug_options);
254 }
255 };
256
257 class PerIsolateOptionsParser : public OptionsParser<PerIsolateOptions> {
258 public:
259 PerIsolateOptionsParser() = delete;
260 explicit PerIsolateOptionsParser(const EnvironmentOptionsParser& eop);
261 };
262
263 class PerProcessOptionsParser : public OptionsParser<PerProcessOptions> {
264 public:
265 PerProcessOptionsParser() = delete;
266 explicit PerProcessOptionsParser(const PerIsolateOptionsParser& iop);
267 };
268
269 #if HAVE_INSPECTOR
270 const DebugOptionsParser _dop_instance{};
271 const EnvironmentOptionsParser _eop_instance{_dop_instance};
272
273 // This Parse is not dead code. It is used by embedders (e.g., Electron).
274 template <>
Parse(StringVector * const args,StringVector * const exec_args,StringVector * const v8_args,DebugOptions * const options,OptionEnvvarSettings required_env_settings,StringVector * const errors)275 void Parse(
276 StringVector* const args, StringVector* const exec_args,
277 StringVector* const v8_args,
278 DebugOptions* const options,
279 OptionEnvvarSettings required_env_settings, StringVector* const errors) {
280 _dop_instance.Parse(
281 args, exec_args, v8_args, options, required_env_settings, errors);
282 }
283 #else
284 const EnvironmentOptionsParser _eop_instance{};
285 #endif // HAVE_INSPECTOR
286 const PerIsolateOptionsParser _piop_instance{_eop_instance};
287 const PerProcessOptionsParser _ppop_instance{_piop_instance};
288
289 template <>
Parse(StringVector * const args,StringVector * const exec_args,StringVector * const v8_args,PerIsolateOptions * const options,OptionEnvvarSettings required_env_settings,StringVector * const errors)290 void Parse(
291 StringVector* const args, StringVector* const exec_args,
292 StringVector* const v8_args,
293 PerIsolateOptions* const options,
294 OptionEnvvarSettings required_env_settings, StringVector* const errors) {
295 _piop_instance.Parse(
296 args, exec_args, v8_args, options, required_env_settings, errors);
297 }
298
299 template <>
Parse(StringVector * const args,StringVector * const exec_args,StringVector * const v8_args,PerProcessOptions * const options,OptionEnvvarSettings required_env_settings,StringVector * const errors)300 void Parse(
301 StringVector* const args, StringVector* const exec_args,
302 StringVector* const v8_args,
303 PerProcessOptions* const options,
304 OptionEnvvarSettings required_env_settings, StringVector* const errors) {
305 _ppop_instance.Parse(
306 args, exec_args, v8_args, options, required_env_settings, errors);
307 }
308
309 // XXX: If you add an option here, please also add it to doc/node.1 and
310 // doc/api/cli.md
311 // TODO(addaleax): Make that unnecessary.
312
DebugOptionsParser()313 DebugOptionsParser::DebugOptionsParser() {
314 #ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION
315 if (sea::IsSingleExecutable()) return;
316 #endif
317
318 AddOption("--inspect-port",
319 "set host:port for inspector",
320 &DebugOptions::host_port,
321 kAllowedInEnvvar);
322 AddAlias("--debug-port", "--inspect-port");
323
324 AddOption("--inspect",
325 "activate inspector on host:port (default: 127.0.0.1:9229)",
326 &DebugOptions::inspector_enabled,
327 kAllowedInEnvvar);
328 AddAlias("--inspect=", { "--inspect-port", "--inspect" });
329
330 AddOption("--debug", "", &DebugOptions::deprecated_debug);
331 AddAlias("--debug=", "--debug");
332 AddOption("--debug-brk", "", &DebugOptions::deprecated_debug);
333 AddAlias("--debug-brk=", "--debug-brk");
334
335 AddOption("--inspect-brk",
336 "activate inspector on host:port and break at start of user script",
337 &DebugOptions::break_first_line,
338 kAllowedInEnvvar);
339 Implies("--inspect-brk", "--inspect");
340 AddAlias("--inspect-brk=", { "--inspect-port", "--inspect-brk" });
341
342 AddOption("--inspect-brk-node", "", &DebugOptions::break_node_first_line);
343 Implies("--inspect-brk-node", "--inspect");
344 AddAlias("--inspect-brk-node=", { "--inspect-port", "--inspect-brk-node" });
345
346 AddOption("--inspect-publish-uid",
347 "comma separated list of destinations for inspector uid"
348 "(default: stderr,http)",
349 &DebugOptions::inspect_publish_uid_string,
350 kAllowedInEnvvar);
351 }
352
EnvironmentOptionsParser()353 EnvironmentOptionsParser::EnvironmentOptionsParser() {
354 AddOption("--conditions",
355 "additional user conditions for conditional exports and imports",
356 &EnvironmentOptions::conditions,
357 kAllowedInEnvvar);
358 AddAlias("-C", "--conditions");
359 AddOption("--diagnostic-dir",
360 "set dir for all output files"
361 " (default: current working directory)",
362 &EnvironmentOptions::diagnostic_dir,
363 kAllowedInEnvvar);
364 AddOption("--dns-result-order",
365 "set default value of verbatim in dns.lookup. Options are "
366 "'ipv4first' (IPv4 addresses are placed before IPv6 addresses) "
367 "'verbatim' (addresses are in the order the DNS resolver "
368 "returned)",
369 &EnvironmentOptions::dns_result_order,
370 kAllowedInEnvvar);
371 AddOption("--enable-network-family-autoselection",
372 "Enable network address family autodetection algorithm",
373 &EnvironmentOptions::enable_network_family_autoselection,
374 kAllowedInEnvvar);
375 AddOption("--enable-source-maps",
376 "Source Map V3 support for stack traces",
377 &EnvironmentOptions::enable_source_maps,
378 kAllowedInEnvvar);
379 AddOption("--experimental-abortcontroller", "", NoOp{}, kAllowedInEnvvar);
380 AddOption("--experimental-fetch",
381 "experimental Fetch API",
382 &EnvironmentOptions::experimental_fetch,
383 kAllowedInEnvvar,
384 true);
385 AddOption("--experimental-global-customevent",
386 "expose experimental CustomEvent on the global scope",
387 &EnvironmentOptions::experimental_global_customevent,
388 kAllowedInEnvvar);
389 AddOption("--experimental-global-webcrypto",
390 "expose experimental Web Crypto API on the global scope",
391 &EnvironmentOptions::experimental_global_web_crypto,
392 kAllowedInEnvvar);
393 AddOption("--experimental-json-modules", "", NoOp{}, kAllowedInEnvvar);
394 AddOption("--experimental-loader",
395 "use the specified module as a custom loader",
396 &EnvironmentOptions::userland_loaders,
397 kAllowedInEnvvar);
398 AddAlias("--loader", "--experimental-loader");
399 AddOption("--experimental-modules", "", NoOp{}, kAllowedInEnvvar);
400 AddOption("--experimental-network-imports",
401 "experimental https: support for the ES Module loader",
402 &EnvironmentOptions::experimental_https_modules,
403 kAllowedInEnvvar);
404 AddOption("--experimental-wasm-modules",
405 "experimental ES Module support for webassembly modules",
406 &EnvironmentOptions::experimental_wasm_modules,
407 kAllowedInEnvvar);
408 AddOption("--experimental-import-meta-resolve",
409 "experimental ES Module import.meta.resolve() parentURL support",
410 &EnvironmentOptions::experimental_import_meta_resolve,
411 kAllowedInEnvvar);
412 AddOption("--experimental-policy",
413 "use the specified file as a "
414 "security policy",
415 &EnvironmentOptions::experimental_policy,
416 kAllowedInEnvvar);
417 AddOption("[has_policy_integrity_string]",
418 "",
419 &EnvironmentOptions::has_policy_integrity_string);
420 AddOption("--policy-integrity",
421 "ensure the security policy contents match "
422 "the specified integrity",
423 &EnvironmentOptions::experimental_policy_integrity,
424 kAllowedInEnvvar);
425 Implies("--policy-integrity", "[has_policy_integrity_string]");
426 AddOption("--experimental-repl-await",
427 "experimental await keyword support in REPL",
428 &EnvironmentOptions::experimental_repl_await,
429 kAllowedInEnvvar,
430 true);
431 AddOption("--experimental-vm-modules",
432 "experimental ES Module support in vm module",
433 &EnvironmentOptions::experimental_vm_modules,
434 kAllowedInEnvvar);
435 AddOption("--experimental-worker", "", NoOp{}, kAllowedInEnvvar);
436 AddOption("--experimental-report", "", NoOp{}, kAllowedInEnvvar);
437 AddOption(
438 "--experimental-wasi-unstable-preview1", "", NoOp{}, kAllowedInEnvvar);
439 AddOption("--expose-internals", "", &EnvironmentOptions::expose_internals);
440 AddOption("--frozen-intrinsics",
441 "experimental frozen intrinsics support",
442 &EnvironmentOptions::frozen_intrinsics,
443 kAllowedInEnvvar);
444 AddOption("--heapsnapshot-signal",
445 "Generate heap snapshot on specified signal",
446 &EnvironmentOptions::heap_snapshot_signal,
447 kAllowedInEnvvar);
448 AddOption("--heapsnapshot-near-heap-limit",
449 "Generate heap snapshots whenever V8 is approaching "
450 "the heap limit. No more than the specified number of "
451 "heap snapshots will be generated.",
452 &EnvironmentOptions::heap_snapshot_near_heap_limit,
453 kAllowedInEnvvar);
454 AddOption("--http-parser", "", NoOp{}, kAllowedInEnvvar);
455 AddOption("--insecure-http-parser",
456 "use an insecure HTTP parser that accepts invalid HTTP headers",
457 &EnvironmentOptions::insecure_http_parser,
458 kAllowedInEnvvar);
459 AddOption("--input-type",
460 "set module type for string input",
461 &EnvironmentOptions::input_type,
462 kAllowedInEnvvar);
463 AddOption("--experimental-specifier-resolution",
464 "Select extension resolution algorithm for es modules; "
465 "either 'explicit' (default) or 'node'",
466 &EnvironmentOptions::experimental_specifier_resolution,
467 kAllowedInEnvvar);
468 AddAlias("--es-module-specifier-resolution",
469 "--experimental-specifier-resolution");
470 AddOption("--deprecation",
471 "silence deprecation warnings",
472 &EnvironmentOptions::deprecation,
473 kAllowedInEnvvar,
474 true);
475 AddOption("--force-async-hooks-checks",
476 "disable checks for async_hooks",
477 &EnvironmentOptions::force_async_hooks_checks,
478 kAllowedInEnvvar,
479 true);
480 AddOption(
481 "--force-node-api-uncaught-exceptions-policy",
482 "enforces 'uncaughtException' event on Node API asynchronous callbacks",
483 &EnvironmentOptions::force_node_api_uncaught_exceptions_policy,
484 kAllowedInEnvvar,
485 false);
486 AddOption("--addons",
487 "disable loading native addons",
488 &EnvironmentOptions::allow_native_addons,
489 kAllowedInEnvvar,
490 true);
491 AddOption("--global-search-paths",
492 "disable global module search paths",
493 &EnvironmentOptions::global_search_paths,
494 kAllowedInEnvvar,
495 true);
496 AddOption("--warnings",
497 "silence all process warnings",
498 &EnvironmentOptions::warnings,
499 kAllowedInEnvvar,
500 true);
501 AddOption("--force-context-aware",
502 "disable loading non-context-aware addons",
503 &EnvironmentOptions::force_context_aware,
504 kAllowedInEnvvar);
505 AddOption("--pending-deprecation",
506 "emit pending deprecation warnings",
507 &EnvironmentOptions::pending_deprecation,
508 kAllowedInEnvvar);
509 AddOption("--preserve-symlinks",
510 "preserve symbolic links when resolving",
511 &EnvironmentOptions::preserve_symlinks,
512 kAllowedInEnvvar);
513 AddOption("--preserve-symlinks-main",
514 "preserve symbolic links when resolving the main module",
515 &EnvironmentOptions::preserve_symlinks_main,
516 kAllowedInEnvvar);
517 AddOption("--prof",
518 "Generate V8 profiler output.",
519 V8Option{});
520 AddOption("--prof-process",
521 "process V8 profiler output generated using --prof",
522 &EnvironmentOptions::prof_process);
523 // Options after --prof-process are passed through to the prof processor.
524 AddAlias("--prof-process", { "--prof-process", "--" });
525 #if HAVE_INSPECTOR
526 AddOption("--cpu-prof",
527 "Start the V8 CPU profiler on start up, and write the CPU profile "
528 "to disk before exit. If --cpu-prof-dir is not specified, write "
529 "the profile to the current working directory.",
530 &EnvironmentOptions::cpu_prof);
531 AddOption("--cpu-prof-name",
532 "specified file name of the V8 CPU profile generated with "
533 "--cpu-prof",
534 &EnvironmentOptions::cpu_prof_name);
535 AddOption("--cpu-prof-interval",
536 "specified sampling interval in microseconds for the V8 CPU "
537 "profile generated with --cpu-prof. (default: 1000)",
538 &EnvironmentOptions::cpu_prof_interval);
539 AddOption("--cpu-prof-dir",
540 "Directory where the V8 profiles generated by --cpu-prof will be "
541 "placed. Does not affect --prof.",
542 &EnvironmentOptions::cpu_prof_dir);
543 AddOption(
544 "--heap-prof",
545 "Start the V8 heap profiler on start up, and write the heap profile "
546 "to disk before exit. If --heap-prof-dir is not specified, write "
547 "the profile to the current working directory.",
548 &EnvironmentOptions::heap_prof);
549 AddOption("--heap-prof-name",
550 "specified file name of the V8 heap profile generated with "
551 "--heap-prof",
552 &EnvironmentOptions::heap_prof_name);
553 AddOption("--heap-prof-dir",
554 "Directory where the V8 heap profiles generated by --heap-prof "
555 "will be placed.",
556 &EnvironmentOptions::heap_prof_dir);
557 AddOption("--heap-prof-interval",
558 "specified sampling interval in bytes for the V8 heap "
559 "profile generated with --heap-prof. (default: 512 * 1024)",
560 &EnvironmentOptions::heap_prof_interval);
561 #endif // HAVE_INSPECTOR
562 AddOption("--max-http-header-size",
563 "set the maximum size of HTTP headers (default: 16384 (16KB))",
564 &EnvironmentOptions::max_http_header_size,
565 kAllowedInEnvvar);
566 AddOption("--redirect-warnings",
567 "write warnings to file instead of stderr",
568 &EnvironmentOptions::redirect_warnings,
569 kAllowedInEnvvar);
570 AddOption("--test",
571 "launch test runner on startup",
572 &EnvironmentOptions::test_runner);
573 AddOption("--test-concurrency",
574 "specify test runner concurrency",
575 &EnvironmentOptions::test_runner_concurrency);
576 AddOption("--experimental-test-coverage",
577 "enable code coverage in the test runner",
578 &EnvironmentOptions::test_runner_coverage);
579 AddOption("--test-name-pattern",
580 "run tests whose name matches this regular expression",
581 &EnvironmentOptions::test_name_pattern);
582 AddOption("--test-reporter",
583 "report test output using the given reporter",
584 &EnvironmentOptions::test_reporter,
585 kAllowedInEnvvar);
586 AddOption("--test-reporter-destination",
587 "report given reporter to the given destination",
588 &EnvironmentOptions::test_reporter_destination,
589 kAllowedInEnvvar);
590 AddOption("--test-only",
591 "run tests with 'only' option set",
592 &EnvironmentOptions::test_only,
593 kAllowedInEnvvar);
594 AddOption("--test-shard",
595 "run test at specific shard",
596 &EnvironmentOptions::test_shard,
597 kAllowedInEnvvar);
598 AddOption("--test-udp-no-try-send", "", // For testing only.
599 &EnvironmentOptions::test_udp_no_try_send);
600 AddOption("--throw-deprecation",
601 "throw an exception on deprecations",
602 &EnvironmentOptions::throw_deprecation,
603 kAllowedInEnvvar);
604 AddOption("--trace-atomics-wait",
605 "(deprecated) trace Atomics.wait() operations",
606 &EnvironmentOptions::trace_atomics_wait,
607 kAllowedInEnvvar);
608 AddOption("--trace-deprecation",
609 "show stack traces on deprecations",
610 &EnvironmentOptions::trace_deprecation,
611 kAllowedInEnvvar);
612 AddOption("--trace-exit",
613 "show stack trace when an environment exits",
614 &EnvironmentOptions::trace_exit,
615 kAllowedInEnvvar);
616 AddOption("--trace-sync-io",
617 "show stack trace when use of sync IO is detected after the "
618 "first tick",
619 &EnvironmentOptions::trace_sync_io,
620 kAllowedInEnvvar);
621 AddOption("--trace-tls",
622 "prints TLS packet trace information to stderr",
623 &EnvironmentOptions::trace_tls,
624 kAllowedInEnvvar);
625 AddOption("--trace-uncaught",
626 "show stack traces for the `throw` behind uncaught exceptions",
627 &EnvironmentOptions::trace_uncaught,
628 kAllowedInEnvvar);
629 AddOption("--trace-warnings",
630 "show stack traces on process warnings",
631 &EnvironmentOptions::trace_warnings,
632 kAllowedInEnvvar);
633 AddOption("--experimental-default-type",
634 "set module system to use by default",
635 &EnvironmentOptions::type,
636 kAllowedInEnvvar);
637 AddOption("--extra-info-on-fatal-exception",
638 "hide extra information on fatal exception that causes exit",
639 &EnvironmentOptions::extra_info_on_fatal_exception,
640 kAllowedInEnvvar,
641 true);
642 AddOption("--unhandled-rejections",
643 "define unhandled rejections behavior. Options are 'strict' "
644 "(always raise an error), 'throw' (raise an error unless "
645 "'unhandledRejection' hook is set), 'warn' (log a warning), 'none' "
646 "(silence warnings), 'warn-with-error-code' (log a warning and set "
647 "exit code 1 unless 'unhandledRejection' hook is set). (default: "
648 "throw)",
649 &EnvironmentOptions::unhandled_rejections,
650 kAllowedInEnvvar);
651 AddOption("--verify-base-objects",
652 "", /* undocumented, only for debugging */
653 &EnvironmentOptions::verify_base_objects,
654 kAllowedInEnvvar);
655 AddOption("--watch",
656 "run in watch mode",
657 &EnvironmentOptions::watch_mode,
658 kAllowedInEnvvar);
659 AddOption("--watch-path",
660 "path to watch",
661 &EnvironmentOptions::watch_mode_paths,
662 kAllowedInEnvvar);
663 AddOption("--watch-preserve-output",
664 "preserve outputs on watch mode restart",
665 &EnvironmentOptions::watch_mode_preserve_output,
666 kAllowedInEnvvar);
667 Implies("--watch-path", "--watch");
668 AddOption("--check",
669 "syntax check script without executing",
670 &EnvironmentOptions::syntax_check_only);
671 AddAlias("-c", "--check");
672 // This option is only so that we can tell --eval with an empty string from
673 // no eval at all. Having it not start with a dash makes it inaccessible
674 // from the parser itself, but available for using Implies().
675 // TODO(addaleax): When moving --help over to something generated from the
676 // programmatic descriptions, this will need some special care.
677 // (See also [ssl_openssl_cert_store] below.)
678 AddOption("[has_eval_string]", "", &EnvironmentOptions::has_eval_string);
679 AddOption("--eval", "evaluate script", &EnvironmentOptions::eval_string);
680 Implies("--eval", "[has_eval_string]");
681 AddOption("--print",
682 "evaluate script and print result",
683 &EnvironmentOptions::print_eval);
684 AddAlias("-e", "--eval");
685 AddAlias("--print <arg>", "-pe");
686 AddAlias("-pe", { "--print", "--eval" });
687 AddAlias("-p", "--print");
688 AddOption("--require",
689 "CommonJS module to preload (option can be repeated)",
690 &EnvironmentOptions::preload_cjs_modules,
691 kAllowedInEnvvar);
692 AddAlias("-r", "--require");
693 AddOption("--import",
694 "ES module to preload (option can be repeated)",
695 &EnvironmentOptions::preload_esm_modules,
696 kAllowedInEnvironment);
697 AddOption("--interactive",
698 "always enter the REPL even if stdin does not appear "
699 "to be a terminal",
700 &EnvironmentOptions::force_repl);
701 AddAlias("-i", "--interactive");
702
703 AddOption("--napi-modules", "", NoOp{}, kAllowedInEnvvar);
704
705 AddOption("--tls-keylog",
706 "log TLS decryption keys to named file for traffic analysis",
707 &EnvironmentOptions::tls_keylog,
708 kAllowedInEnvvar);
709
710 AddOption("--tls-min-v1.0",
711 "set default TLS minimum to TLSv1.0 (default: TLSv1.2)",
712 &EnvironmentOptions::tls_min_v1_0,
713 kAllowedInEnvvar);
714 AddOption("--tls-min-v1.1",
715 "set default TLS minimum to TLSv1.1 (default: TLSv1.2)",
716 &EnvironmentOptions::tls_min_v1_1,
717 kAllowedInEnvvar);
718 AddOption("--tls-min-v1.2",
719 "set default TLS minimum to TLSv1.2 (default: TLSv1.2)",
720 &EnvironmentOptions::tls_min_v1_2,
721 kAllowedInEnvvar);
722 AddOption("--tls-min-v1.3",
723 "set default TLS minimum to TLSv1.3 (default: TLSv1.2)",
724 &EnvironmentOptions::tls_min_v1_3,
725 kAllowedInEnvvar);
726 AddOption("--tls-max-v1.2",
727 "set default TLS maximum to TLSv1.2 (default: TLSv1.3)",
728 &EnvironmentOptions::tls_max_v1_2,
729 kAllowedInEnvvar);
730 // Current plan is:
731 // - 11.x and below: TLS1.3 is opt-in with --tls-max-v1.3
732 // - 12.x: TLS1.3 is opt-out with --tls-max-v1.2
733 // In either case, support both options they are uniformly available.
734 AddOption("--tls-max-v1.3",
735 "set default TLS maximum to TLSv1.3 (default: TLSv1.3)",
736 &EnvironmentOptions::tls_max_v1_3,
737 kAllowedInEnvvar);
738 }
739
PerIsolateOptionsParser(const EnvironmentOptionsParser & eop)740 PerIsolateOptionsParser::PerIsolateOptionsParser(
741 const EnvironmentOptionsParser& eop) {
742 AddOption("--track-heap-objects",
743 "track heap object allocations for heap snapshots",
744 &PerIsolateOptions::track_heap_objects,
745 kAllowedInEnvvar);
746
747 // Explicitly add some V8 flags to mark them as allowed in NODE_OPTIONS.
748 AddOption("--abort-on-uncaught-exception",
749 "aborting instead of exiting causes a core file to be generated "
750 "for analysis",
751 V8Option{},
752 kAllowedInEnvvar);
753 AddOption("--interpreted-frames-native-stack",
754 "help system profilers to translate JavaScript interpreted frames",
755 V8Option{}, kAllowedInEnvvar);
756 AddOption("--max-old-space-size", "", V8Option{}, kAllowedInEnvvar);
757 AddOption("--max-semi-space-size", "", V8Option{}, kAllowedInEnvvar);
758 AddOption("--perf-basic-prof", "", V8Option{}, kAllowedInEnvvar);
759 AddOption("--perf-basic-prof-only-functions",
760 "",
761 V8Option{},
762 kAllowedInEnvvar);
763 AddOption("--max-old-space-size", "", V8Option{}, kAllowedInEnvvar);
764 AddOption("--perf-basic-prof", "", V8Option{}, kAllowedInEnvvar);
765 AddOption(
766 "--perf-basic-prof-only-functions", "", V8Option{}, kAllowedInEnvvar);
767 AddOption("--perf-prof", "", V8Option{}, kAllowedInEnvvar);
768 AddOption("--perf-prof-unwinding-info", "", V8Option{}, kAllowedInEnvvar);
769 AddOption("--stack-trace-limit", "", V8Option{}, kAllowedInEnvvar);
770 AddOption("--disallow-code-generation-from-strings",
771 "disallow eval and friends",
772 V8Option{},
773 kAllowedInEnvvar);
774 AddOption("--huge-max-old-generation-size",
775 "increase default maximum heap size on machines with 16GB memory "
776 "or more",
777 V8Option{},
778 kAllowedInEnvvar);
779 AddOption("--jitless",
780 "disable runtime allocation of executable memory",
781 V8Option{},
782 kAllowedInEnvvar);
783 AddOption("--report-uncaught-exception",
784 "generate diagnostic report on uncaught exceptions",
785 &PerIsolateOptions::report_uncaught_exception,
786 kAllowedInEnvvar);
787 AddOption("--report-on-signal",
788 "generate diagnostic report upon receiving signals",
789 &PerIsolateOptions::report_on_signal,
790 kAllowedInEnvvar);
791 AddOption("--report-signal",
792 "causes diagnostic report to be produced on provided signal,"
793 " unsupported in Windows. (default: SIGUSR2)",
794 &PerIsolateOptions::report_signal,
795 kAllowedInEnvvar);
796 Implies("--report-signal", "--report-on-signal");
797 AddOption("--enable-etw-stack-walking",
798 "provides heap data to ETW Windows native tracing",
799 V8Option{},
800 kAllowedInEnvironment);
801
802 AddOption("--experimental-top-level-await", "", NoOp{}, kAllowedInEnvvar);
803
804 AddOption("--experimental-shadow-realm",
805 "",
806 &PerIsolateOptions::experimental_shadow_realm,
807 kAllowedInEnvvar);
808 AddOption("--harmony-shadow-realm", "", V8Option{});
809 Implies("--experimental-shadow-realm", "--harmony-shadow-realm");
810 Implies("--harmony-shadow-realm", "--experimental-shadow-realm");
811 ImpliesNot("--no-harmony-shadow-realm", "--experimental-shadow-realm");
812
813 Insert(eop, &PerIsolateOptions::get_per_env_options);
814 }
815
PerProcessOptionsParser(const PerIsolateOptionsParser & iop)816 PerProcessOptionsParser::PerProcessOptionsParser(
817 const PerIsolateOptionsParser& iop) {
818 AddOption("--title",
819 "the process title to use on startup",
820 &PerProcessOptions::title,
821 kAllowedInEnvvar);
822 AddOption("--trace-event-categories",
823 "comma separated list of trace event categories to record",
824 &PerProcessOptions::trace_event_categories,
825 kAllowedInEnvvar);
826 AddOption("--trace-event-file-pattern",
827 "Template string specifying the filepath for the trace-events "
828 "data, it supports ${rotation} and ${pid}.",
829 &PerProcessOptions::trace_event_file_pattern,
830 kAllowedInEnvvar);
831 AddAlias("--trace-events-enabled", {
832 "--trace-event-categories", "v8,node,node.async_hooks" });
833 AddOption("--v8-pool-size",
834 "set V8's thread pool size",
835 &PerProcessOptions::v8_thread_pool_size,
836 kAllowedInEnvvar);
837 AddOption("--zero-fill-buffers",
838 "automatically zero-fill all newly allocated Buffer and "
839 "SlowBuffer instances",
840 &PerProcessOptions::zero_fill_all_buffers,
841 kAllowedInEnvvar);
842 AddOption("--debug-arraybuffer-allocations",
843 "", /* undocumented, only for debugging */
844 &PerProcessOptions::debug_arraybuffer_allocations,
845 kAllowedInEnvvar);
846 AddOption("--disable-proto",
847 "disable Object.prototype.__proto__",
848 &PerProcessOptions::disable_proto,
849 kAllowedInEnvvar);
850 AddOption("--build-snapshot",
851 "Generate a snapshot blob when the process exits."
852 " Currently only supported in the node_mksnapshot binary.",
853 &PerProcessOptions::build_snapshot,
854 kDisallowedInEnvvar);
855 AddOption("--node-snapshot",
856 "", // It's a debug-only option.
857 &PerProcessOptions::node_snapshot,
858 kAllowedInEnvvar);
859 AddOption("--snapshot-blob",
860 "Path to the snapshot blob that's either the result of snapshot"
861 "building, or the blob that is used to restore the application "
862 "state",
863 &PerProcessOptions::snapshot_blob,
864 kAllowedInEnvvar);
865
866 // 12.x renamed this inadvertently, so alias it for consistency within the
867 // release line, while using the original name for consistency with older
868 // release lines.
869 AddOption("--security-revert", "", &PerProcessOptions::security_reverts);
870 AddAlias("--security-reverts", "--security-revert");
871 AddOption("--completion-bash",
872 "print source-able bash completion script",
873 &PerProcessOptions::print_bash_completion);
874 AddOption("--help",
875 "print node command line options",
876 &PerProcessOptions::print_help);
877 AddAlias("-h", "--help");
878 AddOption(
879 "--version", "print Node.js version", &PerProcessOptions::print_version);
880 AddAlias("-v", "--version");
881 AddOption("--v8-options",
882 "print V8 command line options",
883 &PerProcessOptions::print_v8_help);
884 AddOption("--report-compact",
885 "output compact single-line JSON",
886 &PerProcessOptions::report_compact,
887 kAllowedInEnvvar);
888 AddOption("--report-dir",
889 "define custom report pathname."
890 " (default: current working directory)",
891 &PerProcessOptions::report_directory,
892 kAllowedInEnvvar);
893 AddAlias("--report-directory", "--report-dir");
894 AddOption("--report-filename",
895 "define custom report file name."
896 " (default: YYYYMMDD.HHMMSS.PID.SEQUENCE#.txt)",
897 &PerProcessOptions::report_filename,
898 kAllowedInEnvvar);
899 AddOption("--report-on-fatalerror",
900 "generate diagnostic report on fatal (internal) errors",
901 &PerProcessOptions::report_on_fatalerror,
902 kAllowedInEnvvar);
903
904 #ifdef NODE_HAVE_I18N_SUPPORT
905 AddOption("--icu-data-dir",
906 "set ICU data load path to dir (overrides NODE_ICU_DATA)"
907 #ifndef NODE_HAVE_SMALL_ICU
908 " (note: linked-in ICU data is present)"
909 #endif
910 ,
911 &PerProcessOptions::icu_data_dir,
912 kAllowedInEnvvar);
913 #endif
914
915 #if HAVE_OPENSSL
916 AddOption("--openssl-config",
917 "load OpenSSL configuration from the specified file "
918 "(overrides OPENSSL_CONF)",
919 &PerProcessOptions::openssl_config,
920 kAllowedInEnvvar);
921 AddOption("--tls-cipher-list",
922 "use an alternative default TLS cipher list",
923 &PerProcessOptions::tls_cipher_list,
924 kAllowedInEnvvar);
925 AddOption("--use-openssl-ca",
926 "use OpenSSL's default CA store"
927 #if defined(NODE_OPENSSL_CERT_STORE)
928 " (default)"
929 #endif
930 ,
931 &PerProcessOptions::use_openssl_ca,
932 kAllowedInEnvvar);
933 AddOption("--use-bundled-ca",
934 "use bundled CA store"
935 #if !defined(NODE_OPENSSL_CERT_STORE)
936 " (default)"
937 #endif
938 ,
939 &PerProcessOptions::use_bundled_ca,
940 kAllowedInEnvvar);
941 // Similar to [has_eval_string] above, except that the separation between
942 // this and use_openssl_ca only exists for option validation after parsing.
943 // This is not ideal.
944 AddOption("[ssl_openssl_cert_store]",
945 "",
946 &PerProcessOptions::ssl_openssl_cert_store);
947 Implies("--use-openssl-ca", "[ssl_openssl_cert_store]");
948 ImpliesNot("--use-bundled-ca", "[ssl_openssl_cert_store]");
949 AddOption("--enable-fips",
950 "enable FIPS crypto at startup",
951 &PerProcessOptions::enable_fips_crypto,
952 kAllowedInEnvvar);
953 AddOption("--force-fips",
954 "force FIPS crypto (cannot be disabled)",
955 &PerProcessOptions::force_fips_crypto,
956 kAllowedInEnvvar);
957 AddOption("--secure-heap",
958 "total size of the OpenSSL secure heap",
959 &PerProcessOptions::secure_heap,
960 kAllowedInEnvvar);
961 AddOption("--secure-heap-min",
962 "minimum allocation size from the OpenSSL secure heap",
963 &PerProcessOptions::secure_heap_min,
964 kAllowedInEnvvar);
965 #endif // HAVE_OPENSSL
966 #if OPENSSL_VERSION_MAJOR >= 3
967 AddOption("--openssl-legacy-provider",
968 "enable OpenSSL 3.0 legacy provider",
969 &PerProcessOptions::openssl_legacy_provider,
970 kAllowedInEnvvar);
971 AddOption("--openssl-shared-config",
972 "enable OpenSSL shared configuration",
973 &PerProcessOptions::openssl_shared_config,
974 kAllowedInEnvvar);
975
976 #endif // OPENSSL_VERSION_MAJOR
977 AddOption("--use-largepages",
978 "Map the Node.js static code to large pages. Options are "
979 "'off' (the default value, meaning do not map), "
980 "'on' (map and ignore failure, reporting it to stderr), "
981 "or 'silent' (map and silently ignore failure)",
982 &PerProcessOptions::use_largepages,
983 kAllowedInEnvvar);
984
985 AddOption("--trace-sigint",
986 "enable printing JavaScript stacktrace on SIGINT",
987 &PerProcessOptions::trace_sigint,
988 kAllowedInEnvvar);
989
990 Insert(iop, &PerProcessOptions::get_per_isolate_options);
991
992 AddOption("--node-memory-debug",
993 "Run with extra debug checks for memory leaks in Node.js itself",
994 NoOp{},
995 kAllowedInEnvvar);
996 Implies("--node-memory-debug", "--debug-arraybuffer-allocations");
997 Implies("--node-memory-debug", "--verify-base-objects");
998 }
999
RemoveBrackets(const std::string & host)1000 inline std::string RemoveBrackets(const std::string& host) {
1001 if (!host.empty() && host.front() == '[' && host.back() == ']')
1002 return host.substr(1, host.size() - 2);
1003 else
1004 return host;
1005 }
1006
ParseAndValidatePort(const std::string & port,std::vector<std::string> * errors)1007 inline int ParseAndValidatePort(const std::string& port,
1008 std::vector<std::string>* errors) {
1009 char* endptr;
1010 errno = 0;
1011 const unsigned long result = // NOLINT(runtime/int)
1012 strtoul(port.c_str(), &endptr, 10);
1013 if (errno != 0 || *endptr != '\0'||
1014 (result != 0 && result < 1024) || result > 65535) {
1015 errors->push_back(" must be 0 or in range 1024 to 65535.");
1016 }
1017 return static_cast<int>(result);
1018 }
1019
SplitHostPort(const std::string & arg,std::vector<std::string> * errors)1020 HostPort SplitHostPort(const std::string& arg,
1021 std::vector<std::string>* errors) {
1022 // remove_brackets only works if no port is specified
1023 // so if it has an effect only an IPv6 address was specified.
1024 std::string host = RemoveBrackets(arg);
1025 if (host.length() < arg.length())
1026 return HostPort{host, DebugOptions::kDefaultInspectorPort};
1027
1028 size_t colon = arg.rfind(':');
1029 if (colon == std::string::npos) {
1030 // Either a port number or a host name. Assume that
1031 // if it's not all decimal digits, it's a host name.
1032 for (char c : arg) {
1033 if (c < '0' || c > '9') {
1034 return HostPort{arg, DebugOptions::kDefaultInspectorPort};
1035 }
1036 }
1037 return HostPort { "", ParseAndValidatePort(arg, errors) };
1038 }
1039 // Host and port found:
1040 return HostPort { RemoveBrackets(arg.substr(0, colon)),
1041 ParseAndValidatePort(arg.substr(colon + 1), errors) };
1042 }
1043
GetBashCompletion()1044 std::string GetBashCompletion() {
1045 Mutex::ScopedLock lock(per_process::cli_options_mutex);
1046 const auto& parser = _ppop_instance;
1047
1048 std::ostringstream out;
1049
1050 out << "_node_complete() {\n"
1051 " local cur_word options\n"
1052 " cur_word=\"${COMP_WORDS[COMP_CWORD]}\"\n"
1053 " if [[ \"${cur_word}\" == -* ]] ; then\n"
1054 " COMPREPLY=( $(compgen -W '";
1055
1056 for (const auto& item : parser.options_) {
1057 if (item.first[0] != '[') {
1058 out << item.first << " ";
1059 }
1060 }
1061 for (const auto& item : parser.aliases_) {
1062 if (item.first[0] != '[') {
1063 out << item.first << " ";
1064 }
1065 }
1066 if (parser.aliases_.size() > 0) {
1067 out.seekp(-1, out.cur); // Strip the trailing space
1068 }
1069
1070 out << "' -- \"${cur_word}\") )\n"
1071 " return 0\n"
1072 " else\n"
1073 " COMPREPLY=( $(compgen -f \"${cur_word}\") )\n"
1074 " return 0\n"
1075 " fi\n"
1076 "}\n"
1077 "complete -o filenames -o nospace -o bashdefault "
1078 "-F _node_complete node node_g";
1079 return out.str();
1080 }
1081
1082 // Return a map containing all the options and their metadata as well
1083 // as the aliases
GetCLIOptions(const FunctionCallbackInfo<Value> & args)1084 void GetCLIOptions(const FunctionCallbackInfo<Value>& args) {
1085 Mutex::ScopedLock lock(per_process::cli_options_mutex);
1086 Environment* env = Environment::GetCurrent(args);
1087 if (!env->has_run_bootstrapping_code()) {
1088 // No code because this is an assertion.
1089 return env->ThrowError(
1090 "Should not query options before bootstrapping is done");
1091 }
1092 env->set_has_serialized_options(true);
1093
1094 Isolate* isolate = env->isolate();
1095 Local<Context> context = env->context();
1096
1097 // Temporarily act as if the current Environment's/IsolateData's options were
1098 // the default options, i.e. like they are the ones we'd access for global
1099 // options parsing, so that all options are available from the main parser.
1100 auto original_per_isolate = per_process::cli_options->per_isolate;
1101 per_process::cli_options->per_isolate = env->isolate_data()->options();
1102 auto original_per_env = per_process::cli_options->per_isolate->per_env;
1103 per_process::cli_options->per_isolate->per_env = env->options();
1104 auto on_scope_leave = OnScopeLeave([&]() {
1105 per_process::cli_options->per_isolate->per_env = original_per_env;
1106 per_process::cli_options->per_isolate = original_per_isolate;
1107 });
1108
1109 Local<Map> options = Map::New(isolate);
1110 if (options
1111 ->SetPrototype(context, env->primordials_safe_map_prototype_object())
1112 .IsNothing()) {
1113 return;
1114 }
1115
1116 for (const auto& item : _ppop_instance.options_) {
1117 Local<Value> value;
1118 const auto& option_info = item.second;
1119 auto field = option_info.field;
1120 PerProcessOptions* opts = per_process::cli_options.get();
1121 switch (option_info.type) {
1122 case kNoOp:
1123 case kV8Option:
1124 // Special case for --abort-on-uncaught-exception which is also
1125 // respected by Node.js internals
1126 if (item.first == "--abort-on-uncaught-exception") {
1127 value = Boolean::New(
1128 isolate, original_per_env->abort_on_uncaught_exception);
1129 } else {
1130 value = Undefined(isolate);
1131 }
1132 break;
1133 case kBoolean:
1134 value = Boolean::New(isolate,
1135 *_ppop_instance.Lookup<bool>(field, opts));
1136 break;
1137 case kInteger:
1138 value = Number::New(
1139 isolate,
1140 static_cast<double>(*_ppop_instance.Lookup<int64_t>(field, opts)));
1141 break;
1142 case kUInteger:
1143 value = Number::New(
1144 isolate,
1145 static_cast<double>(*_ppop_instance.Lookup<uint64_t>(field, opts)));
1146 break;
1147 case kString:
1148 if (!ToV8Value(context,
1149 *_ppop_instance.Lookup<std::string>(field, opts))
1150 .ToLocal(&value)) {
1151 return;
1152 }
1153 break;
1154 case kStringList:
1155 if (!ToV8Value(context,
1156 *_ppop_instance.Lookup<StringVector>(field, opts))
1157 .ToLocal(&value)) {
1158 return;
1159 }
1160 break;
1161 case kHostPort: {
1162 const HostPort& host_port =
1163 *_ppop_instance.Lookup<HostPort>(field, opts);
1164 Local<Object> obj = Object::New(isolate);
1165 Local<Value> host;
1166 if (!ToV8Value(context, host_port.host()).ToLocal(&host) ||
1167 obj->Set(context, env->host_string(), host).IsNothing() ||
1168 obj->Set(context,
1169 env->port_string(),
1170 Integer::New(isolate, host_port.port()))
1171 .IsNothing()) {
1172 return;
1173 }
1174 value = obj;
1175 break;
1176 }
1177 default:
1178 UNREACHABLE();
1179 }
1180 CHECK(!value.IsEmpty());
1181
1182 Local<Value> name = ToV8Value(context, item.first).ToLocalChecked();
1183 Local<Object> info = Object::New(isolate);
1184 Local<Value> help_text;
1185 if (!ToV8Value(context, option_info.help_text).ToLocal(&help_text) ||
1186 !info->Set(context, env->help_text_string(), help_text)
1187 .FromMaybe(false) ||
1188 !info->Set(context,
1189 env->env_var_settings_string(),
1190 Integer::New(isolate,
1191 static_cast<int>(option_info.env_setting)))
1192 .FromMaybe(false) ||
1193 !info->Set(context,
1194 env->type_string(),
1195 Integer::New(isolate, static_cast<int>(option_info.type)))
1196 .FromMaybe(false) ||
1197 !info->Set(context,
1198 env->default_is_true_string(),
1199 Boolean::New(isolate, option_info.default_is_true))
1200 .FromMaybe(false) ||
1201 info->Set(context, env->value_string(), value).IsNothing() ||
1202 options->Set(context, name, info).IsEmpty()) {
1203 return;
1204 }
1205 }
1206
1207 Local<Value> aliases;
1208 if (!ToV8Value(context, _ppop_instance.aliases_).ToLocal(&aliases)) return;
1209
1210 if (aliases.As<Object>()
1211 ->SetPrototype(context, env->primordials_safe_map_prototype_object())
1212 .IsNothing()) {
1213 return;
1214 }
1215
1216 Local<Object> ret = Object::New(isolate);
1217 if (ret->Set(context, env->options_string(), options).IsNothing() ||
1218 ret->Set(context, env->aliases_string(), aliases).IsNothing()) {
1219 return;
1220 }
1221
1222 args.GetReturnValue().Set(ret);
1223 }
1224
GetEmbedderOptions(const FunctionCallbackInfo<Value> & args)1225 void GetEmbedderOptions(const FunctionCallbackInfo<Value>& args) {
1226 Environment* env = Environment::GetCurrent(args);
1227 if (!env->has_run_bootstrapping_code()) {
1228 // No code because this is an assertion.
1229 return env->ThrowError(
1230 "Should not query options before bootstrapping is done");
1231 }
1232 Isolate* isolate = args.GetIsolate();
1233 Local<Context> context = env->context();
1234 Local<Object> ret = Object::New(isolate);
1235
1236 if (ret->Set(context,
1237 FIXED_ONE_BYTE_STRING(env->isolate(), "shouldNotRegisterESMLoader"),
1238 Boolean::New(isolate, env->should_not_register_esm_loader()))
1239 .IsNothing()) return;
1240
1241 if (ret->Set(context,
1242 FIXED_ONE_BYTE_STRING(env->isolate(), "noGlobalSearchPaths"),
1243 Boolean::New(isolate, env->no_global_search_paths()))
1244 .IsNothing()) return;
1245
1246 args.GetReturnValue().Set(ret);
1247 }
1248
Initialize(Local<Object> target,Local<Value> unused,Local<Context> context,void * priv)1249 void Initialize(Local<Object> target,
1250 Local<Value> unused,
1251 Local<Context> context,
1252 void* priv) {
1253 Environment* env = Environment::GetCurrent(context);
1254 Isolate* isolate = env->isolate();
1255 SetMethodNoSideEffect(context, target, "getCLIOptions", GetCLIOptions);
1256 SetMethodNoSideEffect(
1257 context, target, "getEmbedderOptions", GetEmbedderOptions);
1258
1259 Local<Object> env_settings = Object::New(isolate);
1260 NODE_DEFINE_CONSTANT(env_settings, kAllowedInEnvvar);
1261 NODE_DEFINE_CONSTANT(env_settings, kDisallowedInEnvvar);
1262 target
1263 ->Set(
1264 context, FIXED_ONE_BYTE_STRING(isolate, "envSettings"), env_settings)
1265 .Check();
1266
1267 Local<Object> types = Object::New(isolate);
1268 NODE_DEFINE_CONSTANT(types, kNoOp);
1269 NODE_DEFINE_CONSTANT(types, kV8Option);
1270 NODE_DEFINE_CONSTANT(types, kBoolean);
1271 NODE_DEFINE_CONSTANT(types, kInteger);
1272 NODE_DEFINE_CONSTANT(types, kUInteger);
1273 NODE_DEFINE_CONSTANT(types, kString);
1274 NODE_DEFINE_CONSTANT(types, kHostPort);
1275 NODE_DEFINE_CONSTANT(types, kStringList);
1276 target->Set(context, FIXED_ONE_BYTE_STRING(isolate, "types"), types)
1277 .Check();
1278 }
1279
RegisterExternalReferences(ExternalReferenceRegistry * registry)1280 void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
1281 registry->Register(GetCLIOptions);
1282 registry->Register(GetEmbedderOptions);
1283 }
1284 } // namespace options_parser
1285
HandleEnvOptions(std::shared_ptr<EnvironmentOptions> env_options)1286 void HandleEnvOptions(std::shared_ptr<EnvironmentOptions> env_options) {
1287 HandleEnvOptions(env_options, [](const char* name) {
1288 std::string text;
1289 return credentials::SafeGetenv(name, &text) ? text : "";
1290 });
1291 }
1292
HandleEnvOptions(std::shared_ptr<EnvironmentOptions> env_options,std::function<std::string (const char *)> opt_getter)1293 void HandleEnvOptions(std::shared_ptr<EnvironmentOptions> env_options,
1294 std::function<std::string(const char*)> opt_getter) {
1295 env_options->pending_deprecation =
1296 opt_getter("NODE_PENDING_DEPRECATION") == "1";
1297
1298 env_options->preserve_symlinks = opt_getter("NODE_PRESERVE_SYMLINKS") == "1";
1299
1300 env_options->preserve_symlinks_main =
1301 opt_getter("NODE_PRESERVE_SYMLINKS_MAIN") == "1";
1302
1303 if (env_options->redirect_warnings.empty())
1304 env_options->redirect_warnings = opt_getter("NODE_REDIRECT_WARNINGS");
1305 }
1306
ParseNodeOptionsEnvVar(const std::string & node_options,std::vector<std::string> * errors)1307 std::vector<std::string> ParseNodeOptionsEnvVar(
1308 const std::string& node_options, std::vector<std::string>* errors) {
1309 std::vector<std::string> env_argv;
1310
1311 bool is_in_string = false;
1312 bool will_start_new_arg = true;
1313 for (std::string::size_type index = 0; index < node_options.size(); ++index) {
1314 char c = node_options.at(index);
1315
1316 // Backslashes escape the following character
1317 if (c == '\\' && is_in_string) {
1318 if (index + 1 == node_options.size()) {
1319 errors->push_back("invalid value for NODE_OPTIONS "
1320 "(invalid escape)\n");
1321 return env_argv;
1322 } else {
1323 c = node_options.at(++index);
1324 }
1325 } else if (c == ' ' && !is_in_string) {
1326 will_start_new_arg = true;
1327 continue;
1328 } else if (c == '"') {
1329 is_in_string = !is_in_string;
1330 continue;
1331 }
1332
1333 if (will_start_new_arg) {
1334 env_argv.emplace_back(std::string(1, c));
1335 will_start_new_arg = false;
1336 } else {
1337 env_argv.back() += c;
1338 }
1339 }
1340
1341 if (is_in_string) {
1342 errors->push_back("invalid value for NODE_OPTIONS "
1343 "(unterminated string)\n");
1344 }
1345 return env_argv;
1346 }
1347 } // namespace node
1348
1349 NODE_BINDING_CONTEXT_AWARE_INTERNAL(options, node::options_parser::Initialize)
1350 NODE_BINDING_EXTERNAL_REFERENCE(
1351 options, node::options_parser::RegisterExternalReferences)
1352