1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#ifndef ART_CMDLINE_CMDLINE_PARSER_H_ 18#define ART_CMDLINE_CMDLINE_PARSER_H_ 19 20#define CMDLINE_NDEBUG 1 // Do not output any debugging information for parsing. 21 22#include "cmdline/detail/cmdline_parser_detail.h" 23#include "cmdline/detail/cmdline_parse_argument_detail.h" 24#include "cmdline/detail/cmdline_debug_detail.h" 25 26#include "cmdline_type_parser.h" 27#include "token_range.h" 28#include "cmdline_types.h" 29#include "cmdline_result.h" 30#include "cmdline_parse_result.h" 31 32#include "runtime/base/variant_map.h" 33 34#include <vector> 35#include <memory> 36 37namespace art { 38// Build a parser for command line arguments with a small domain specific language. 39// Each parsed type must have a specialized CmdlineType<T> in order to do the string->T parsing. 40// Each argument must also have a VariantMap::Key<T> in order to do the T storage. 41template <typename TVariantMap, 42 template <typename TKeyValue> class TVariantMapKey> 43struct CmdlineParser { 44 template <typename TArg> 45 struct ArgumentBuilder; 46 47 struct Builder; // Build the parser. 48 struct UntypedArgumentBuilder; // Build arguments which weren't yet given a type. 49 50 private: 51 // Forward declare some functions that we need to use before fully-defining structs. 52 template <typename TArg> 53 static ArgumentBuilder<TArg> CreateArgumentBuilder(Builder& parent); 54 static void AppendCompletedArgument(Builder& builder, detail::CmdlineParseArgumentAny* arg); 55 56 // Allow argument definitions to save their values when they are parsed, 57 // without having a dependency on CmdlineParser or any of the builders. 58 // 59 // A shared pointer to the save destination is saved into the load/save argument callbacks. 60 // 61 // This also allows the underlying storage (i.e. a variant map) to be released 62 // to the user, without having to recreate all of the callbacks. 63 struct SaveDestination { 64 SaveDestination() : variant_map_(new TVariantMap()) {} 65 66 // Save value to the variant map. 67 template <typename TArg> 68 void SaveToMap(const TVariantMapKey<TArg>& key, TArg& value) { 69 variant_map_->Set(key, value); 70 } 71 72 // Get the existing value from a map, creating the value if it did not already exist. 73 template <typename TArg> 74 TArg& GetOrCreateFromMap(const TVariantMapKey<TArg>& key) { 75 auto* ptr = variant_map_->Get(key); 76 if (ptr == nullptr) { 77 variant_map_->Set(key, TArg()); 78 ptr = variant_map_->Get(key); 79 assert(ptr != nullptr); 80 } 81 82 return *ptr; 83 } 84 85 protected: 86 // Release the map, clearing it as a side-effect. 87 // Future saves will be distinct from previous saves. 88 TVariantMap&& ReleaseMap() { 89 return std::move(*variant_map_); 90 } 91 92 // Get a read-only reference to the variant map. 93 const TVariantMap& GetMap() { 94 return *variant_map_; 95 } 96 97 // Clear all potential save targets. 98 void Clear() { 99 variant_map_->Clear(); 100 } 101 102 private: 103 // Don't try to copy or move this. Just don't. 104 SaveDestination(const SaveDestination&) = delete; 105 SaveDestination(SaveDestination&&) = delete; 106 SaveDestination& operator=(const SaveDestination&) = delete; 107 SaveDestination& operator=(SaveDestination&&) = delete; 108 109 std::shared_ptr<TVariantMap> variant_map_; 110 111 // Allow the parser to change the underlying pointers when we release the underlying storage. 112 friend struct CmdlineParser; 113 }; 114 115 public: 116 // Builder for the argument definition of type TArg. Do not use this type directly, 117 // it is only a separate type to provide compile-time enforcement against doing 118 // illegal builds. 119 template <typename TArg> 120 struct ArgumentBuilder { 121 // Add a range check to this argument. 122 ArgumentBuilder<TArg>& WithRange(const TArg& min, const TArg& max) { 123 argument_info_.has_range_ = true; 124 argument_info_.min_ = min; 125 argument_info_.max_ = max; 126 127 return *this; 128 } 129 130 // Map the list of names into the list of values. List of names must not have 131 // any wildcards '_' in it. 132 // 133 // Do not use if a value map has already been set. 134 ArgumentBuilder<TArg>& WithValues(std::initializer_list<TArg> value_list) { 135 SetValuesInternal(value_list); 136 return *this; 137 } 138 139 // When used with a single alias, map the alias into this value. 140 // Same as 'WithValues({value})' , but allows the omission of the curly braces {}. 141 ArgumentBuilder<TArg> WithValue(const TArg& value) { 142 return WithValues({ value }); 143 } 144 145 // Map the parsed string values (from _) onto a concrete value. If no wildcard 146 // has been specified, then map the value directly from the arg name (i.e. 147 // if there are multiple aliases, then use the alias to do the mapping). 148 // 149 // Do not use if a values list has already been set. 150 ArgumentBuilder<TArg>& WithValueMap( 151 std::initializer_list<std::pair<const char*, TArg>> key_value_list) { 152 assert(!argument_info_.has_value_list_); 153 154 argument_info_.has_value_map_ = true; 155 argument_info_.value_map_ = key_value_list; 156 157 return *this; 158 } 159 160 // If this argument is seen multiple times, successive arguments mutate the same value 161 // instead of replacing it with a new value. 162 ArgumentBuilder<TArg>& AppendValues() { 163 argument_info_.appending_values_ = true; 164 165 return *this; 166 } 167 168 // Convenience type alias for the variant map key type definition. 169 using MapKey = TVariantMapKey<TArg>; 170 171 // Write the results of this argument into the key. 172 // To look up the parsed arguments, get the map and then use this key with VariantMap::Get 173 CmdlineParser::Builder& IntoKey(const MapKey& key) { 174 // Only capture save destination as a pointer. 175 // This allows the parser to later on change the specific save targets. 176 auto save_destination = save_destination_; 177 save_value_ = [save_destination, &key](TArg& value) { 178 save_destination->SaveToMap(key, value); 179 CMDLINE_DEBUG_LOG << "Saved value into map '" 180 << detail::ToStringAny(value) << "'" << std::endl; 181 }; 182 183 load_value_ = [save_destination, &key]() -> TArg& { 184 TArg& value = save_destination->GetOrCreateFromMap(key); 185 CMDLINE_DEBUG_LOG << "Loaded value from map '" << detail::ToStringAny(value) << "'" 186 << std::endl; 187 188 return value; 189 }; 190 191 save_value_specified_ = true; 192 load_value_specified_ = true; 193 194 CompleteArgument(); 195 return parent_; 196 } 197 198 // Ensure we always move this when returning a new builder. 199 ArgumentBuilder(ArgumentBuilder&&) = default; 200 201 protected: 202 // Used by builder to internally ignore arguments by dropping them on the floor after parsing. 203 CmdlineParser::Builder& IntoIgnore() { 204 save_value_ = [](TArg& value) { 205 CMDLINE_DEBUG_LOG << "Ignored value '" << detail::ToStringAny(value) << "'" << std::endl; 206 }; 207 load_value_ = []() -> TArg& { 208 assert(false && "Should not be appending values to ignored arguments"); 209 return *reinterpret_cast<TArg*>(0); // Blow up. 210 }; 211 212 save_value_specified_ = true; 213 load_value_specified_ = true; 214 215 CompleteArgument(); 216 return parent_; 217 } 218 219 void SetValuesInternal(const std::vector<TArg>&& value_list) { 220 assert(!argument_info_.has_value_map_); 221 222 argument_info_.has_value_list_ = true; 223 argument_info_.value_list_ = value_list; 224 } 225 226 void SetNames(std::vector<const char*>&& names) { 227 argument_info_.names_ = names; 228 } 229 230 void SetNames(std::initializer_list<const char*> names) { 231 argument_info_.names_ = names; 232 } 233 234 private: 235 // Copying is bad. Move only. 236 ArgumentBuilder(const ArgumentBuilder&) = delete; 237 238 // Called by any function that doesn't chain back into this builder. 239 // Completes the argument builder and save the information into the main builder. 240 void CompleteArgument() { 241 assert(save_value_specified_ && 242 "No Into... function called, nowhere to save parsed values to"); 243 assert(load_value_specified_ && 244 "No Into... function called, nowhere to load parsed values from"); 245 246 argument_info_.CompleteArgument(); 247 248 // Appending the completed argument is destructive. The object is no longer 249 // usable since all the useful information got moved out of it. 250 AppendCompletedArgument(parent_, 251 new detail::CmdlineParseArgument<TArg>( 252 std::move(argument_info_), 253 std::move(save_value_), 254 std::move(load_value_))); 255 } 256 257 friend struct CmdlineParser; 258 friend struct CmdlineParser::Builder; 259 friend struct CmdlineParser::UntypedArgumentBuilder; 260 261 ArgumentBuilder(CmdlineParser::Builder& parser, 262 std::shared_ptr<SaveDestination> save_destination) 263 : parent_(parser), 264 save_value_specified_(false), 265 load_value_specified_(false), 266 save_destination_(save_destination) { 267 save_value_ = [](TArg&) { 268 assert(false && "No save value function defined"); 269 }; 270 271 load_value_ = []() -> TArg& { 272 assert(false && "No load value function defined"); 273 return *reinterpret_cast<TArg*>(0); // Blow up. 274 }; 275 } 276 277 CmdlineParser::Builder& parent_; 278 std::function<void(TArg&)> save_value_; 279 std::function<TArg&(void)> load_value_; 280 bool save_value_specified_; 281 bool load_value_specified_; 282 detail::CmdlineParserArgumentInfo<TArg> argument_info_; 283 284 std::shared_ptr<SaveDestination> save_destination_; 285 }; 286 287 struct UntypedArgumentBuilder { 288 // Set a type for this argument. The specific subcommand parser is looked up by the type. 289 template <typename TArg> 290 ArgumentBuilder<TArg> WithType() { 291 return CreateTypedBuilder<TArg>(); 292 } 293 294 // When used with multiple aliases, map the position of the alias to the value position. 295 template <typename TArg> 296 ArgumentBuilder<TArg> WithValues(std::initializer_list<TArg> values) { 297 auto&& a = CreateTypedBuilder<TArg>(); 298 a.WithValues(values); 299 return std::move(a); 300 } 301 302 // When used with a single alias, map the alias into this value. 303 // Same as 'WithValues({value})' , but allows the omission of the curly braces {}. 304 template <typename TArg> 305 ArgumentBuilder<TArg> WithValue(const TArg& value) { 306 return WithValues({ value }); 307 } 308 309 // Set the current building argument to target this key. 310 // When this command line argument is parsed, it can be fetched with this key. 311 Builder& IntoKey(const TVariantMapKey<Unit>& key) { 312 return CreateTypedBuilder<Unit>().IntoKey(key); 313 } 314 315 // Ensure we always move this when returning a new builder. 316 UntypedArgumentBuilder(UntypedArgumentBuilder&&) = default; 317 318 protected: 319 void SetNames(std::vector<const char*>&& names) { 320 names_ = std::move(names); 321 } 322 323 void SetNames(std::initializer_list<const char*> names) { 324 names_ = names; 325 } 326 327 private: 328 // No copying. Move instead. 329 UntypedArgumentBuilder(const UntypedArgumentBuilder&) = delete; 330 331 template <typename TArg> 332 ArgumentBuilder<TArg> CreateTypedBuilder() { 333 auto&& b = CreateArgumentBuilder<TArg>(parent_); 334 InitializeTypedBuilder(&b); // Type-specific initialization 335 b.SetNames(std::move(names_)); 336 return std::move(b); 337 } 338 339 template <typename TArg = Unit> 340 typename std::enable_if<std::is_same<TArg, Unit>::value>::type 341 InitializeTypedBuilder(ArgumentBuilder<TArg>* arg_builder) { 342 // Every Unit argument implicitly maps to a runtime value of Unit{} 343 std::vector<Unit> values(names_.size(), Unit{}); // NOLINT [whitespace/braces] [5] 344 arg_builder->SetValuesInternal(std::move(values)); 345 } 346 347 // No extra work for all other types 348 void InitializeTypedBuilder(void*) {} 349 350 template <typename TArg> 351 friend struct ArgumentBuilder; 352 friend struct Builder; 353 354 explicit UntypedArgumentBuilder(CmdlineParser::Builder& parent) : parent_(parent) {} 355 // UntypedArgumentBuilder(UntypedArgumentBuilder&& other) = default; 356 357 CmdlineParser::Builder& parent_; 358 std::vector<const char*> names_; 359 }; 360 361 // Build a new parser given a chain of calls to define arguments. 362 struct Builder { 363 Builder() : save_destination_(new SaveDestination()) {} 364 365 // Define a single argument. The default type is Unit. 366 UntypedArgumentBuilder Define(const char* name) { 367 return Define({name}); 368 } 369 370 // Define a single argument with multiple aliases. 371 UntypedArgumentBuilder Define(std::initializer_list<const char*> names) { 372 auto&& b = UntypedArgumentBuilder(*this); 373 b.SetNames(names); 374 return std::move(b); 375 } 376 377 // Whether the parser should give up on unrecognized arguments. Not recommended. 378 Builder& IgnoreUnrecognized(bool ignore_unrecognized) { 379 ignore_unrecognized_ = ignore_unrecognized; 380 return *this; 381 } 382 383 // Provide a list of arguments to ignore for backwards compatibility. 384 Builder& Ignore(std::initializer_list<const char*> ignore_list) { 385 for (auto&& ignore_name : ignore_list) { 386 std::string ign = ignore_name; 387 388 // Ignored arguments are just like a regular definition which have very 389 // liberal parsing requirements (no range checks, no value checks). 390 // Unlike regular argument definitions, when a value gets parsed into its 391 // stronger type, we just throw it away. 392 393 if (ign.find("_") != std::string::npos) { // Does the arg-def have a wildcard? 394 // pretend this is a string, e.g. -Xjitconfig:<anythinggoeshere> 395 auto&& builder = Define(ignore_name).template WithType<std::string>().IntoIgnore(); 396 assert(&builder == this); 397 (void)builder; // Ignore pointless unused warning, it's used in the assert. 398 } else { 399 // pretend this is a unit, e.g. -Xjitblocking 400 auto&& builder = Define(ignore_name).template WithType<Unit>().IntoIgnore(); 401 assert(&builder == this); 402 (void)builder; // Ignore pointless unused warning, it's used in the assert. 403 } 404 } 405 ignore_list_ = ignore_list; 406 return *this; 407 } 408 409 // Finish building the parser; performs sanity checks. Return value is moved, not copied. 410 // Do not call this more than once. 411 CmdlineParser Build() { 412 assert(!built_); 413 built_ = true; 414 415 auto&& p = CmdlineParser(ignore_unrecognized_, 416 std::move(ignore_list_), 417 save_destination_, 418 std::move(completed_arguments_)); 419 420 return std::move(p); 421 } 422 423 protected: 424 void AppendCompletedArgument(detail::CmdlineParseArgumentAny* arg) { 425 auto smart_ptr = std::unique_ptr<detail::CmdlineParseArgumentAny>(arg); 426 completed_arguments_.push_back(std::move(smart_ptr)); 427 } 428 429 private: 430 // No copying now! 431 Builder(const Builder& other) = delete; 432 433 template <typename TArg> 434 friend struct ArgumentBuilder; 435 friend struct UntypedArgumentBuilder; 436 friend struct CmdlineParser; 437 438 bool built_ = false; 439 bool ignore_unrecognized_ = false; 440 std::vector<const char*> ignore_list_; 441 std::shared_ptr<SaveDestination> save_destination_; 442 443 std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_; 444 }; 445 446 CmdlineResult Parse(const std::string& argv) { 447 std::vector<std::string> tokenized; 448 Split(argv, ' ', &tokenized); 449 450 return Parse(TokenRange(std::move(tokenized))); 451 } 452 453 // Parse the arguments; storing results into the arguments map. Returns success value. 454 CmdlineResult Parse(const char* argv) { 455 return Parse(std::string(argv)); 456 } 457 458 // Parse the arguments; storing the results into the arguments map. Returns success value. 459 // Assumes that argv[0] is a valid argument (i.e. not the program name). 460 CmdlineResult Parse(const std::vector<const char*>& argv) { 461 return Parse(TokenRange(argv.begin(), argv.end())); 462 } 463 464 // Parse the arguments; storing the results into the arguments map. Returns success value. 465 // Assumes that argv[0] is a valid argument (i.e. not the program name). 466 CmdlineResult Parse(const std::vector<std::string>& argv) { 467 return Parse(TokenRange(argv.begin(), argv.end())); 468 } 469 470 // Parse the arguments (directly from an int main(argv,argc)). Returns success value. 471 // Assumes that argv[0] is the program name, and ignores it. 472 CmdlineResult Parse(const char* argv[], int argc) { 473 return Parse(TokenRange(&argv[1], argc - 1)); // ignore argv[0] because it's the program name 474 } 475 476 // Look up the arguments that have been parsed; use the target keys to lookup individual args. 477 const TVariantMap& GetArgumentsMap() const { 478 return save_destination_->GetMap(); 479 } 480 481 // Release the arguments map that has been parsed; useful for move semantics. 482 TVariantMap&& ReleaseArgumentsMap() { 483 return save_destination_->ReleaseMap(); 484 } 485 486 // How many arguments were defined? 487 size_t CountDefinedArguments() const { 488 return completed_arguments_.size(); 489 } 490 491 // Ensure we have a default move constructor. 492 CmdlineParser(CmdlineParser&&) = default; 493 // Ensure we have a default move assignment operator. 494 CmdlineParser& operator=(CmdlineParser&&) = default; 495 496 private: 497 friend struct Builder; 498 499 // Construct a new parser from the builder. Move all the arguments. 500 CmdlineParser(bool ignore_unrecognized, 501 std::vector<const char*>&& ignore_list, 502 std::shared_ptr<SaveDestination> save_destination, 503 std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>>&& completed_arguments) 504 : ignore_unrecognized_(ignore_unrecognized), 505 ignore_list_(std::move(ignore_list)), 506 save_destination_(save_destination), 507 completed_arguments_(std::move(completed_arguments)) { 508 assert(save_destination != nullptr); 509 } 510 511 // Parse the arguments; storing results into the arguments map. Returns success value. 512 // The parsing will fail on the first non-success parse result and return that error. 513 // 514 // All previously-parsed arguments are cleared out. 515 // Otherwise, all parsed arguments will be stored into SaveDestination as a side-effect. 516 // A partial parse will result only in a partial save of the arguments. 517 CmdlineResult Parse(TokenRange&& arguments_list) { 518 save_destination_->Clear(); 519 520 for (size_t i = 0; i < arguments_list.Size(); ) { 521 TokenRange possible_name = arguments_list.Slice(i); 522 523 size_t best_match_size = 0; // How many tokens were matched in the best case. 524 size_t best_match_arg_idx = 0; 525 bool matched = false; // At least one argument definition has been matched? 526 527 // Find the closest argument definition for the remaining token range. 528 size_t arg_idx = 0; 529 for (auto&& arg : completed_arguments_) { 530 size_t local_match = arg->MaybeMatches(possible_name); 531 532 if (local_match > best_match_size) { 533 best_match_size = local_match; 534 best_match_arg_idx = arg_idx; 535 matched = true; 536 } 537 arg_idx++; 538 } 539 540 // Saw some kind of unknown argument 541 if (matched == false) { 542 if (UNLIKELY(ignore_unrecognized_)) { // This is usually off, we only need it for JNI. 543 // Consume 1 token and keep going, hopefully the next token is a good one. 544 ++i; 545 continue; 546 } 547 // Common case: 548 // Bail out on the first unknown argument with an error. 549 return CmdlineResult(CmdlineResult::kUnknown, 550 std::string("Unknown argument: ") + possible_name[0]); 551 } 552 553 // Look at the best-matched argument definition and try to parse against that. 554 auto&& arg = completed_arguments_[best_match_arg_idx]; 555 556 assert(arg->MaybeMatches(possible_name) == best_match_size); 557 558 // Try to parse the argument now, if we have enough tokens. 559 std::pair<size_t, size_t> num_tokens = arg->GetNumTokens(); 560 size_t min_tokens; 561 size_t max_tokens; 562 563 std::tie(min_tokens, max_tokens) = num_tokens; 564 565 if ((i + min_tokens) > arguments_list.Size()) { 566 // expected longer command line but it was too short 567 // e.g. if the argv was only "-Xms" without specifying a memory option 568 CMDLINE_DEBUG_LOG << "Parse failure, i = " << i << ", arg list " << arguments_list.Size() << 569 " num tokens in arg_def: " << min_tokens << "," << max_tokens << std::endl; 570 return CmdlineResult(CmdlineResult::kFailure, 571 std::string("Argument ") + 572 possible_name[0] + ": incomplete command line arguments, expected " 573 + std::to_string(size_t(i + min_tokens) - arguments_list.Size()) + 574 " more tokens"); 575 } 576 577 if (best_match_size > max_tokens || best_match_size < min_tokens) { 578 // Even our best match was out of range, so parsing would fail instantly. 579 return CmdlineResult(CmdlineResult::kFailure, 580 std::string("Argument ") + possible_name[0] + ": too few tokens " 581 "matched " + std::to_string(best_match_size) 582 + " but wanted " + std::to_string(num_tokens.first)); 583 } 584 585 // We have enough tokens to begin exact parsing. 586 TokenRange exact_range = possible_name.Slice(0, max_tokens); 587 588 size_t consumed_tokens = 1; // At least 1 if we ever want to try to resume parsing on error 589 CmdlineResult parse_attempt = arg->ParseArgument(exact_range, &consumed_tokens); 590 591 if (parse_attempt.IsError()) { 592 // We may also want to continue parsing the other tokens to gather more errors. 593 return parse_attempt; 594 } // else the value has been successfully stored into the map 595 596 assert(consumed_tokens > 0); // Don't hang in an infinite loop trying to parse 597 i += consumed_tokens; 598 599 // TODO: also handle ignoring arguments for backwards compatibility 600 } // for 601 602 return CmdlineResult(CmdlineResult::kSuccess); 603 } 604 605 bool ignore_unrecognized_ = false; 606 std::vector<const char*> ignore_list_; 607 std::shared_ptr<SaveDestination> save_destination_; 608 std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_; 609}; 610 611// This has to be defined after everything else, since we want the builders to call this. 612template <typename TVariantMap, 613 template <typename TKeyValue> class TVariantMapKey> 614template <typename TArg> 615CmdlineParser<TVariantMap, TVariantMapKey>::ArgumentBuilder<TArg> 616CmdlineParser<TVariantMap, TVariantMapKey>::CreateArgumentBuilder( 617 CmdlineParser<TVariantMap, TVariantMapKey>::Builder& parent) { 618 return CmdlineParser<TVariantMap, TVariantMapKey>::ArgumentBuilder<TArg>( 619 parent, parent.save_destination_); 620} 621 622// This has to be defined after everything else, since we want the builders to call this. 623template <typename TVariantMap, 624 template <typename TKeyValue> class TVariantMapKey> 625void CmdlineParser<TVariantMap, TVariantMapKey>::AppendCompletedArgument( 626 CmdlineParser<TVariantMap, TVariantMapKey>::Builder& builder, 627 detail::CmdlineParseArgumentAny* arg) { 628 builder.AppendCompletedArgument(arg); 629} 630 631} // namespace art 632 633#endif // ART_CMDLINE_CMDLINE_PARSER_H_ 634