summaryrefslogtreecommitdiff
path: root/src/node_options.h
diff options
context:
space:
mode:
authorAnna Henningsen <anna@addaleax.net>2018-08-10 02:45:28 +0200
committerAnna Henningsen <anna@addaleax.net>2018-08-22 23:37:19 +0200
commit29a71bae40ffa0bbc8ba6b2bdf051a09987da7f7 (patch)
tree1d66e5b012218ede32c5219d9918d2e2a897ee7b /src/node_options.h
parent92880f31da1eca98a42e0f61708b10d9d8d83955 (diff)
downloadandroid-node-v8-29a71bae40ffa0bbc8ba6b2bdf051a09987da7f7.tar.gz
android-node-v8-29a71bae40ffa0bbc8ba6b2bdf051a09987da7f7.tar.bz2
android-node-v8-29a71bae40ffa0bbc8ba6b2bdf051a09987da7f7.zip
src: refactor options parsing
This is a major refactor of our Node’s parser. See `node_options.cc` for how it is used, and `node_options-inl.h` for the bulk of its implementation. Unfortunately, the implementation has come to have some complexity, in order to meet the following goals: - Make it easy to *use* for defining or changing options. - Keep it (mostly) backwards-compatible. - No tests were harmed as part of this commit. - Be as consistent as possible. - In particular, options can now generally accept arguments through both `--foo=bar` notation and `--foo bar` notation. We were previously very inconsistent on this point. - Separate into different levels of scope, namely per-process (global), per-Isolate and per-Environment (+ debug options). - Allow programmatic accessibility in the future. - This includes a possible expansion for `--help` output. This commit also leaves a number of `TODO` comments, mostly for improving consistency even more (possibly with having to modify tests), improving embedder support, as well as removing pieces of exposed configuration variables that should never have become part of the public API but unfortunately are at this point. PR-URL: https://github.com/nodejs/node/pull/22392 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Gus Caplan <me@gus.host>
Diffstat (limited to 'src/node_options.h')
-rw-r--r--src/node_options.h356
1 files changed, 356 insertions, 0 deletions
diff --git a/src/node_options.h b/src/node_options.h
new file mode 100644
index 0000000000..957e2b729d
--- /dev/null
+++ b/src/node_options.h
@@ -0,0 +1,356 @@
+#ifndef SRC_NODE_OPTIONS_H_
+#define SRC_NODE_OPTIONS_H_
+
+#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
+
+#include <string>
+#include <vector>
+#include <unordered_map>
+#include <memory>
+#include "node_constants.h"
+
+namespace node {
+
+struct HostPort {
+ std::string host_name;
+ int port;
+
+ void Update(const HostPort& other) {
+ if (!other.host_name.empty()) host_name = other.host_name;
+ if (other.port >= 0) port = other.port;
+ }
+};
+
+// These options are currently essentially per-Environment, but it can be nice
+// to keep them separate since they are a group of options applying to a very
+// specific part of Node. It might also make more sense for them to be
+// per-Isolate, rather than per-Environment.
+class DebugOptions {
+ public:
+ bool inspector_enabled = false;
+ bool deprecated_debug = false;
+ bool break_first_line = false;
+ bool break_node_first_line = false;
+ HostPort host_port = {"127.0.0.1", -1};
+ enum { kDefaultInspectorPort = 9229 };
+
+ bool deprecated_invocation() const {
+ return deprecated_debug &&
+ inspector_enabled &&
+ break_first_line;
+ }
+
+ bool invalid_invocation() const {
+ return deprecated_debug && !inspector_enabled;
+ }
+
+ bool wait_for_connect() const {
+ return break_first_line || break_node_first_line;
+ }
+
+ const std::string& host() {
+ return host_port.host_name;
+ }
+
+ int port() {
+ return host_port.port < 0 ? kDefaultInspectorPort : host_port.port;
+ }
+};
+
+class EnvironmentOptions {
+ public:
+ std::shared_ptr<DebugOptions> debug_options { new DebugOptions() };
+ bool experimental_modules = false;
+ bool experimental_repl_await = false;
+ bool experimental_vm_modules = false;
+ bool experimental_worker = false;
+ bool expose_internals = false;
+ bool no_deprecation = false;
+ bool no_force_async_hooks_checks = false;
+ bool no_warnings = false;
+ bool pending_deprecation = false;
+ bool preserve_symlinks = false;
+ bool preserve_symlinks_main = false;
+ bool prof_process = false;
+ std::string redirect_warnings;
+ bool throw_deprecation = false;
+ bool trace_deprecation = false;
+ bool trace_sync_io = false;
+ bool trace_warnings = false;
+ std::string userland_loader;
+
+ bool syntax_check_only = false;
+ bool has_eval_string = false;
+ std::string eval_string;
+ bool print_eval = false;
+ bool force_repl = false;
+
+ std::vector<std::string> preload_modules;
+
+ std::vector<std::string> user_argv;
+
+ inline DebugOptions* get_debug_options();
+};
+
+class PerIsolateOptions {
+ public:
+ std::shared_ptr<EnvironmentOptions> per_env { new EnvironmentOptions() };
+ bool track_heap_objects = false;
+
+ inline EnvironmentOptions* get_per_env_options();
+};
+
+class PerProcessOptions {
+ public:
+ std::shared_ptr<PerIsolateOptions> per_isolate { new PerIsolateOptions() };
+
+ std::string title;
+ std::string trace_event_categories;
+ std::string trace_event_file_pattern = "node_trace.${rotation}.log";
+ int64_t v8_thread_pool_size = 4;
+ bool zero_fill_all_buffers = false;
+
+ std::vector<std::string> security_reverts;
+ bool print_help = false;
+ bool print_v8_help = false;
+ bool print_version = false;
+
+#ifdef NODE_HAVE_I18N_SUPPORT
+ std::string icu_data_dir;
+#endif
+
+ // TODO(addaleax): Some of these could probably be per-Environment.
+#if HAVE_OPENSSL
+ std::string openssl_config;
+ std::string tls_cipher_list = DEFAULT_CIPHER_LIST_CORE;
+#ifdef NODE_OPENSSL_CERT_STORE
+ bool ssl_openssl_cert_store = true;
+#else
+ bool ssl_openssl_cert_store = false;
+#endif
+ bool use_openssl_ca = false;
+ bool use_bundled_ca = false;
+#if NODE_FIPS_MODE
+ bool enable_fips_crypto = false;
+ bool force_fips_crypto = false;
+#endif
+#endif
+
+ inline PerIsolateOptions* get_per_isolate_options();
+};
+
+// The actual options parser, as opposed to the structs containing them:
+
+HostPort SplitHostPort(const std::string& arg, std::string* error);
+
+enum OptionEnvvarSettings {
+ kAllowedInEnvironment,
+ kDisallowedInEnvironment
+};
+
+enum OptionType {
+ kNoOp,
+ kV8Option,
+ kBoolean,
+ kInteger,
+ kString,
+ kHostPort,
+ kStringList,
+};
+
+template <typename Options>
+class OptionsParser {
+ public:
+ virtual ~OptionsParser() {}
+
+ typedef Options TargetType;
+
+ struct NoOp {};
+ struct V8Option {};
+
+ // TODO(addaleax): A lot of the `std::string` usage here could be reduced
+ // to simple `const char*`s if it's reasonable to expect the values to be
+ // known at compile-time.
+
+ // These methods add a single option to the parser. Optionally, it can be
+ // specified whether the option should be allowed from environment variable
+ // sources (i.e. NODE_OPTIONS).
+ void AddOption(const std::string& name,
+ bool Options::* field,
+ OptionEnvvarSettings env_setting = kDisallowedInEnvironment);
+ void AddOption(const std::string& name,
+ int64_t Options::* field,
+ OptionEnvvarSettings env_setting = kDisallowedInEnvironment);
+ void AddOption(const std::string& name,
+ std::string Options::* field,
+ OptionEnvvarSettings env_setting = kDisallowedInEnvironment);
+ void AddOption(const std::string& name,
+ std::vector<std::string> Options::* field,
+ OptionEnvvarSettings env_setting = kDisallowedInEnvironment);
+ void AddOption(const std::string& name,
+ HostPort Options::* field,
+ OptionEnvvarSettings env_setting = kDisallowedInEnvironment);
+ void AddOption(const std::string& name,
+ NoOp no_op_tag,
+ OptionEnvvarSettings env_setting = kDisallowedInEnvironment);
+ void AddOption(const std::string& name,
+ V8Option v8_option_tag,
+ OptionEnvvarSettings env_setting = kDisallowedInEnvironment);
+
+ // Adds aliases. An alias can be of the form "--option-a" -> "--option-b",
+ // or have a more complex group expansion, like
+ // "--option-a" -> { "--option-b", "--harmony-foobar", "--eval", "42" }
+ // If `from` has the form "--option-a=", the alias will only be expanded if
+ // the option is presented in that form (i.e. with a '=').
+ // If `from` has the form "--option-a <arg>", the alias will only be expanded
+ // if the option has a non-option argument (not starting with -) following it.
+ void AddAlias(const std::string& from, const std::string& to);
+ void AddAlias(const std::string& from, const std::vector<std::string>& to);
+ void AddAlias(const std::string& from,
+ const std::initializer_list<std::string>& to);
+
+ // Add implications from some arbitary option to a boolean one, either
+ // in a way that makes `from` set `to` to true or to false.
+ void Implies(const std::string& from, const std::string& to);
+ void ImpliesNot(const std::string& from, const std::string& to);
+
+ // Insert options from another options parser into this one, along with
+ // a method that yields the target options type from this parser's options
+ // type.
+ template <typename ChildOptions>
+ void Insert(OptionsParser<ChildOptions>* child_options_parser,
+ ChildOptions* (Options::* get_child)());
+
+ // Parse a sequence of options into an options struct, a list of
+ // arguments that were parsed as options, a list of unknown/JS engine options,
+ // and leave the remainder in the input `args` vector.
+ //
+ // For example, an `args` input of
+ //
+ // node --foo --harmony-bar --fizzle=42 -- /path/to/cow moo
+ //
+ // expands as
+ //
+ // - `args` -> { "node", "/path/to/cow", "moo" }
+ // - `exec_args` -> { "--foo", "--harmony-bar", "--fizzle=42" }
+ // - `v8_args` -> `{ "node", "--harmony-bar" }
+ // - `options->foo == true`, `options->fizzle == 42`.
+ //
+ // If `*error` is set, the result of the parsing should be discarded and the
+ // contents of any of the argument vectors should be considered undefined.
+ virtual void Parse(std::vector<std::string>* const args,
+ std::vector<std::string>* const exec_args,
+ std::vector<std::string>* const v8_args,
+ Options* const options,
+ OptionEnvvarSettings required_env_settings,
+ std::string* const error);
+
+ private:
+ // We support the wide variety of different option types by remembering
+ // how to access them, given a certain `Options` struct.
+
+ // Represents a field within `Options`.
+ class BaseOptionField {
+ public:
+ virtual ~BaseOptionField() {}
+ virtual void* LookupImpl(Options* options) const = 0;
+ };
+
+ // Represents a field of type T within `Options`.
+ template <typename T>
+ class OptionField : public BaseOptionField {
+ public:
+ typedef T Type;
+
+ T* Lookup(Options* options) const {
+ return static_cast<T*>(this->LookupImpl(options));
+ }
+ };
+
+ // Represents a field of type T withing `Options` that can be looked up
+ // as a C++ member field.
+ template <typename T>
+ class SimpleOptionField : public OptionField<T> {
+ public:
+ explicit SimpleOptionField(T Options::* field) : field_(field) {}
+ void* LookupImpl(Options* options) const override {
+ return static_cast<void*>(&(options->*field_));
+ }
+
+ private:
+ T Options::* field_;
+ };
+
+ // An option consists of:
+ // - A type.
+ // - A way to store/access the property value.
+ // - The information of whether it may occur in an env var or not.
+ struct OptionInfo {
+ OptionType type;
+ std::shared_ptr<BaseOptionField> field;
+ OptionEnvvarSettings env_setting;
+ };
+
+ // An implied option is composed of the information on where to store a
+ // specific boolean value (if another specific option is encountered).
+ struct Implication {
+ std::shared_ptr<OptionField<bool>> target_field;
+ bool target_value;
+ };
+
+ // These are helpers that make `Insert()` support properties of other
+ // options structs, if we know how to access them.
+ template <typename OriginalField, typename ChildOptions>
+ static auto Convert(
+ std::shared_ptr<OriginalField> original,
+ ChildOptions* (Options::* get_child)());
+ template <typename ChildOptions>
+ static auto Convert(
+ typename OptionsParser<ChildOptions>::OptionInfo original,
+ ChildOptions* (Options::* get_child)());
+ template <typename ChildOptions>
+ static auto Convert(
+ typename OptionsParser<ChildOptions>::Implication original,
+ ChildOptions* (Options::* get_child)());
+
+ std::unordered_map<std::string, OptionInfo> options_;
+ std::unordered_map<std::string, std::vector<std::string>> aliases_;
+ std::unordered_multimap<std::string, Implication> implications_;
+
+ template <typename OtherOptions>
+ friend class OptionsParser;
+};
+
+class DebugOptionsParser : public OptionsParser<DebugOptions> {
+ public:
+ DebugOptionsParser();
+
+ static DebugOptionsParser instance;
+};
+
+class EnvironmentOptionsParser : public OptionsParser<EnvironmentOptions> {
+ public:
+ EnvironmentOptionsParser();
+
+ static EnvironmentOptionsParser instance;
+};
+
+class PerIsolateOptionsParser : public OptionsParser<PerIsolateOptions> {
+ public:
+ PerIsolateOptionsParser();
+
+ static PerIsolateOptionsParser instance;
+};
+
+class PerProcessOptionsParser : public OptionsParser<PerProcessOptions> {
+ public:
+ PerProcessOptionsParser();
+
+ static PerProcessOptionsParser instance;
+};
+
+} // namespace node
+
+#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
+
+#endif // SRC_NODE_OPTIONS_H_