Where Online Learning is simpler!
The C and C++ Include Header Files
cat -n /usr/include/nodejs/src/node_options-inl.h
1 #ifndef SRC_NODE_OPTIONS_INL_H_ 2 #define SRC_NODE_OPTIONS_INL_H_ 3 4 #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS 5 6 #include <cstdlib> 7 #include "node_options.h" 8 #include "util.h" 9 10 namespace node { 11 12 PerIsolateOptions* PerProcessOptions::get_per_isolate_options() { 13 return per_isolate.get(); 14 } 15 16 EnvironmentOptions* PerIsolateOptions::get_per_env_options() { 17 return per_env.get(); 18 } 19 20 std::shared_ptr<PerIsolateOptions> PerIsolateOptions::Clone() const { 21 auto options = 22 std::shared_ptr<PerIsolateOptions>(new PerIsolateOptions(*this)); 23 options->per_env = std::make_shared<EnvironmentOptions>(*per_env); 24 return options; 25 } 26 27 namespace options_parser { 28 29 template <typename Options> 30 void OptionsParser<Options>::AddOption(const char* name, 31 const char* help_text, 32 bool Options::* field, 33 OptionEnvvarSettings env_setting, 34 bool default_is_true) { 35 options_.emplace(name, 36 OptionInfo{kBoolean, 37 std::make_shared<SimpleOptionField<bool>>(field), 38 env_setting, 39 help_text, 40 default_is_true}); 41 } 42 43 template <typename Options> 44 void OptionsParser<Options>::AddOption(const char* name, 45 const char* help_text, 46 uint64_t Options::* field, 47 OptionEnvvarSettings env_setting) { 48 options_.emplace( 49 name, 50 OptionInfo{kUInteger, 51 std::make_shared<SimpleOptionField<uint64_t>>(field), 52 env_setting, 53 help_text}); 54 } 55 56 template <typename Options> 57 void OptionsParser<Options>::AddOption(const char* name, 58 const char* help_text, 59 int64_t Options::* field, 60 OptionEnvvarSettings env_setting) { 61 options_.emplace( 62 name, 63 OptionInfo{kInteger, 64 std::make_shared<SimpleOptionField<int64_t>>(field), 65 env_setting, 66 help_text}); 67 } 68 69 template <typename Options> 70 void OptionsParser<Options>::AddOption(const char* name, 71 const char* help_text, 72 std::string Options::* field, 73 OptionEnvvarSettings env_setting) { 74 options_.emplace( 75 name, 76 OptionInfo{kString, 77 std::make_shared<SimpleOptionField<std::string>>(field), 78 env_setting, 79 help_text}); 80 } 81 82 template <typename Options> 83 void OptionsParser<Options>::AddOption( 84 const char* name, 85 const char* help_text, 86 std::vector<std::string> Options::* field, 87 OptionEnvvarSettings env_setting) { 88 options_.emplace(name, OptionInfo { 89 kStringList, 90 std::make_shared<SimpleOptionField<std::vector<std::string>>>(field), 91 env_setting, 92 help_text 93 }); 94 } 95 96 template <typename Options> 97 void OptionsParser<Options>::AddOption(const char* name, 98 const char* help_text, 99 HostPort Options::* field, 100 OptionEnvvarSettings env_setting) { 101 options_.emplace( 102 name, 103 OptionInfo{kHostPort, 104 std::make_shared<SimpleOptionField<HostPort>>(field), 105 env_setting, 106 help_text}); 107 } 108 109 template <typename Options> 110 void OptionsParser<Options>::AddOption(const char* name, 111 const char* help_text, 112 NoOp no_op_tag, 113 OptionEnvvarSettings env_setting) { 114 options_.emplace(name, OptionInfo{kNoOp, nullptr, env_setting, help_text}); 115 } 116 117 template <typename Options> 118 void OptionsParser<Options>::AddOption(const char* name, 119 const char* help_text, 120 V8Option v8_option_tag, 121 OptionEnvvarSettings env_setting) { 122 options_.emplace(name, 123 OptionInfo{kV8Option, nullptr, env_setting, help_text}); 124 } 125 126 template <typename Options> 127 void OptionsParser<Options>::AddAlias(const char* from, 128 const char* to) { 129 aliases_[from] = { to }; 130 } 131 132 template <typename Options> 133 void OptionsParser<Options>::AddAlias(const char* from, 134 const std::vector<std::string>& to) { 135 aliases_[from] = to; 136 } 137 138 template <typename Options> 139 void OptionsParser<Options>::AddAlias( 140 const char* from, 141 const std::initializer_list<std::string>& to) { 142 AddAlias(from, std::vector<std::string>(to)); 143 } 144 145 template <typename Options> 146 void OptionsParser<Options>::Implies(const char* from, 147 const char* to) { 148 auto it = options_.find(to); 149 CHECK_NE(it, options_.end()); 150 CHECK(it->second.type == kBoolean || it->second.type == kV8Option); 151 implications_.emplace( 152 from, Implication{it->second.type, to, it->second.field, true}); 153 } 154 155 template <typename Options> 156 void OptionsParser<Options>::ImpliesNot(const char* from, 157 const char* to) { 158 auto it = options_.find(to); 159 CHECK_NE(it, options_.end()); 160 CHECK_EQ(it->second.type, kBoolean); 161 implications_.emplace( 162 from, Implication{it->second.type, to, it->second.field, false}); 163 } 164 165 template <typename Options> 166 template <typename OriginalField, typename ChildOptions> 167 auto OptionsParser<Options>::Convert( 168 std::shared_ptr<OriginalField> original, 169 ChildOptions* (Options::* get_child)()) { 170 // If we have a field on ChildOptions, and we want to access it from an 171 // Options instance, we call get_child() on the original Options and then 172 // access it, i.e. this class implements a kind of function chaining. 173 struct AdaptedField : BaseOptionField { 174 void* LookupImpl(Options* options) const override { 175 return original->LookupImpl((options->*get_child)()); 176 } 177 178 AdaptedField( 179 std::shared_ptr<OriginalField> original, 180 ChildOptions* (Options::* get_child)()) 181 : original(original), get_child(get_child) {} 182 183 std::shared_ptr<OriginalField> original; 184 ChildOptions* (Options::* get_child)(); 185 }; 186 187 return std::shared_ptr<BaseOptionField>( 188 new AdaptedField(original, get_child)); 189 } 190 template <typename Options> 191 template <typename ChildOptions> 192 auto OptionsParser<Options>::Convert( 193 typename OptionsParser<ChildOptions>::OptionInfo original, 194 ChildOptions* (Options::* get_child)()) { 195 return OptionInfo{original.type, 196 Convert(original.field, get_child), 197 original.env_setting, 198 original.help_text, 199 original.default_is_true}; 200 } 201 202 template <typename Options> 203 template <typename ChildOptions> 204 auto OptionsParser<Options>::Convert( 205 typename OptionsParser<ChildOptions>::Implication original, 206 ChildOptions* (Options::* get_child)()) { 207 return Implication{ 208 original.type, 209 original.name, 210 Convert(original.target_field, get_child), 211 original.target_value, 212 }; 213 } 214 215 template <typename Options> 216 template <typename ChildOptions> 217 void OptionsParser<Options>::Insert( 218 const OptionsParser<ChildOptions>& child_options_parser, 219 ChildOptions* (Options::* get_child)()) { 220 aliases_.insert(std::begin(child_options_parser.aliases_), 221 std::end(child_options_parser.aliases_)); 222 223 for (const auto& pair : child_options_parser.options_) 224 options_.emplace(pair.first, Convert(pair.second, get_child)); 225 226 for (const auto& pair : child_options_parser.implications_) 227 implications_.emplace(pair.first, Convert(pair.second, get_child)); 228 } 229 230 inline std::string NotAllowedInEnvErr(const std::string& arg) { 231 return arg + " is not allowed in NODE_OPTIONS"; 232 } 233 234 inline std::string RequiresArgumentErr(const std::string& arg) { 235 return arg + " requires an argument"; 236 } 237 238 inline std::string NegationImpliesBooleanError(const std::string& arg) { 239 return arg + " is an invalid negation because it is not a boolean option"; 240 } 241 242 // We store some of the basic information around a single Parse call inside 243 // this struct, to separate storage of command line arguments and their 244 // handling. In particular, this makes it easier to introduce 'synthetic' 245 // arguments that get inserted by expanding option aliases. 246 struct ArgsInfo { 247 // Generally, the idea here is that the first entry in `*underlying` stores 248 // the "0th" argument (the program name), then `synthetic_args` are inserted, 249 // followed by the remainder of `*underlying`. 250 std::vector<std::string>* underlying; 251 std::vector<std::string> synthetic_args; 252 253 std::vector<std::string>* exec_args; 254 255 ArgsInfo(std::vector<std::string>* args, 256 std::vector<std::string>* exec_args) 257 : underlying(args), exec_args(exec_args) {} 258 259 size_t remaining() const { 260 // -1 to account for the program name. 261 return underlying->size() - 1 + synthetic_args.size(); 262 } 263 264 bool empty() const { return remaining() == 0; } 265 const std::string& program_name() const { return underlying->at(0); } 266 267 std::string& first() { 268 return synthetic_args.empty() ? underlying->at(1) : synthetic_args.front(); 269 } 270 271 std::string pop_first() { 272 std::string ret = std::move(first()); 273 if (synthetic_args.empty()) { 274 // Only push arguments to `exec_args` that were also originally passed 275 // on the command line (i.e. not generated through alias expansion). 276 // '--' is a special case here since its purpose is to end `exec_argv`, 277 // which is why we do not include it. 278 if (exec_args != nullptr && ret != "--") 279 exec_args->push_back(ret); 280 underlying->erase(underlying->begin() + 1); 281 } else { 282 synthetic_args.erase(synthetic_args.begin()); 283 } 284 return ret; 285 } 286 }; 287 288 template <typename Options> 289 void OptionsParser<Options>::Parse( 290 std::vector<std::string>* const orig_args, 291 std::vector<std::string>* const exec_args, 292 std::vector<std::string>* const v8_args, 293 Options* const options, 294 OptionEnvvarSettings required_env_settings, 295 std::vector<std::string>* const errors) const { 296 ArgsInfo args(orig_args, exec_args); 297 298 // The first entry is the process name. Make sure it ends up in the V8 argv, 299 // since V8::SetFlagsFromCommandLine() expects that to hold true for that 300 // array as well. 301 if (v8_args->empty()) 302 v8_args->push_back(args.program_name()); 303 304 while (!args.empty() && errors->empty()) { 305 if (args.first().size() <= 1 || args.first()[0] != '-') break; 306 307 // We know that we're either going to consume this 308 // argument or fail completely. 309 const std::string arg = args.pop_first(); 310 311 if (arg == "--") { 312 if (required_env_settings == kAllowedInEnvvar) 313 errors->push_back(NotAllowedInEnvErr("--")); 314 break; 315 } 316 317 // Only allow --foo=bar notation for options starting with double dashes. 318 // (E.g. -e=a is not allowed as shorthand for --eval=a, which would 319 // otherwise be the result of alias expansion.) 320 const std::string::size_type equals_index = 321 arg[0] == '-' && arg[1] == '-' ? arg.find('=') : std::string::npos; 322 std::string name = 323 equals_index == std::string::npos ? arg : arg.substr(0, equals_index); 324 325 // Store the 'original name' of the argument. This name differs from 326 // 'name' in that it contains a possible '=' sign and is not affected 327 // by alias expansion. 328 std::string original_name = name; 329 if (equals_index != std::string::npos) 330 original_name += '='; 331 332 auto missing_argument = [&]() { 333 errors->push_back(RequiresArgumentErr(original_name)); 334 }; 335 336 // Normalize by replacing `_` with `-` in options. 337 for (std::string::size_type i = 2; i < name.size(); ++i) { 338 if (name[i] == '_') 339 name[i] = '-'; 340 } 341 342 // Convert --no-foo to --foo and keep in mind that we're negating. 343 bool is_negation = false; 344 if (name.find("--no-") == 0) { 345 name.erase(2, 3); // remove no- 346 is_negation = true; 347 } 348 349 { 350 auto it = aliases_.end(); 351 // Expand aliases: 352 // - If `name` can be found in `aliases_`. 353 // - If `name` + '=' can be found in `aliases_`. 354 // - If `name` + " <arg>" can be found in `aliases_`, and we have 355 // a subsequent argument that does not start with '-' itself. 356 while ((it = aliases_.find(name)) != aliases_.end() || 357 (equals_index != std::string::npos && 358 (it = aliases_.find(name + '=')) != aliases_.end()) || 359 (!args.empty() && 360 !args.first().empty() && 361 args.first()[0] != '-' && 362 (it = aliases_.find(name + " <arg>")) != aliases_.end())) { 363 const std::string prev_name = std::move(name); 364 const std::vector<std::string>& expansion = it->second; 365 366 // Use the first entry in the expansion as the new 'name'. 367 name = expansion.front(); 368 369 if (expansion.size() > 1) { 370 // The other arguments, if any, are going to be handled later. 371 args.synthetic_args.insert( 372 args.synthetic_args.begin(), 373 expansion.begin() + 1, 374 expansion.end()); 375 } 376 377 if (name == prev_name) break; 378 } 379 } 380 381 auto it = options_.find(name); 382 383 if ((it == options_.end() || 384 it->second.env_setting == kDisallowedInEnvvar) && 385 required_env_settings == kAllowedInEnvvar) { 386 errors->push_back(NotAllowedInEnvErr(original_name)); 387 break; 388 } 389 390 { 391 std::string implied_name = name; 392 if (is_negation) { 393 // Implications for negated options are defined with "--no-". 394 implied_name.insert(2, "no-"); 395 } 396 auto implications = implications_.equal_range(implied_name); 397 for (auto imp = implications.first; imp != implications.second; ++imp) { 398 if (imp->second.type == kV8Option) { 399 v8_args->push_back(imp->second.name); 400 } else { 401 *imp->second.target_field->template Lookup<bool>(options) = 402 imp->second.target_value; 403 } 404 } 405 } 406 407 if (it == options_.end()) { 408 v8_args->push_back(arg); 409 continue; 410 } 411 412 const OptionInfo& info = it->second; 413 414 // Some V8 options can be negated and they are validated by V8 later. 415 if (is_negation && info.type != kBoolean && info.type != kV8Option) { 416 errors->push_back(NegationImpliesBooleanError(arg)); 417 break; 418 } 419 420 std::string value; 421 if (info.type != kBoolean && info.type != kNoOp && info.type != kV8Option) { 422 if (equals_index != std::string::npos) { 423 value = arg.substr(equals_index + 1); 424 if (value.empty()) { 425 missing_argument(); 426 break; 427 } 428 } else { 429 if (args.empty()) { 430 missing_argument(); 431 break; 432 } 433 434 value = args.pop_first(); 435 436 if (!value.empty() && value[0] == '-') { 437 missing_argument(); 438 break; 439 } else { 440 if (!value.empty() && value[0] == '\\' && value[1] == '-') 441 value = value.substr(1); // Treat \- as escaping an -. 442 } 443 } 444 } 445 446 switch (info.type) { 447 case kBoolean: 448 *Lookup<bool>(info.field, options) = !is_negation; 449 break; 450 case kInteger: { 451 // Special case to pass --stack-trace-limit down to V8. 452 if (name == "--stack-trace-limit") { 453 v8_args->push_back(arg); 454 } 455 *Lookup<int64_t>(info.field, options) = std::atoll(value.c_str()); 456 break; 457 } 458 case kUInteger: 459 *Lookup<uint64_t>(info.field, options) = 460 std::strtoull(value.c_str(), nullptr, 10); 461 break; 462 case kString: 463 *Lookup<std::string>(info.field, options) = value; 464 break; 465 case kStringList: 466 Lookup<std::vector<std::string>>(info.field, options) 467 ->emplace_back(std::move(value)); 468 break; 469 case kHostPort: 470 Lookup<HostPort>(info.field, options) 471 ->Update(SplitHostPort(value, errors)); 472 break; 473 case kNoOp: 474 break; 475 case kV8Option: 476 v8_args->push_back(arg); 477 break; 478 default: 479 UNREACHABLE(); 480 } 481 } 482 options->CheckOptions(errors, orig_args); 483 } 484 485 } // namespace options_parser 486 } // namespace node 487 488 #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS 489 490 #endif // SRC_NODE_OPTIONS_INL_H_