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 <memory>
23 #include <optional>
24 #include <string>
25 #include <string_view>
26 #include <tuple>
27 #include <unordered_map>
28 #include <unordered_set>
29 #include <vector>
30 
31 #include "base/indenter.h"
32 #include "base/variant_map.h"
33 #include "cmdline_parse_result.h"
34 #include "cmdline_result.h"
35 #include "cmdline_type_parser.h"
36 #include "cmdline_types.h"
37 #include "detail/cmdline_debug_detail.h"
38 #include "detail/cmdline_parse_argument_detail.h"
39 #include "detail/cmdline_parser_detail.h"
40 #include "token_range.h"
41 
42 namespace art {
43 // Build a parser for command line arguments with a small domain specific language.
44 // Each parsed type must have a specialized CmdlineType<T> in order to do the string->T parsing.
45 // Each argument must also have a VariantMap::Key<T> in order to do the T storage.
46 template <typename TVariantMap,
47           template <typename TKeyValue> class TVariantMapKey>
48 struct CmdlineParser {
49   template <typename TArg>
50   struct ArgumentBuilder;
51 
52   struct Builder;  // Build the parser.
53   struct UntypedArgumentBuilder;  // Build arguments which weren't yet given a type.
54 
55  private:
56   // Forward declare some functions that we need to use before fully-defining structs.
57   template <typename TArg>
58   static ArgumentBuilder<TArg> CreateArgumentBuilder(Builder& parent);
59   static void AppendCompletedArgument(Builder& builder, detail::CmdlineParseArgumentAny* arg);
60 
61   // Allow argument definitions to save their values when they are parsed,
62   // without having a dependency on CmdlineParser or any of the builders.
63   //
64   // A shared pointer to the save destination is saved into the load/save argument callbacks.
65   //
66   // This also allows the underlying storage (i.e. a variant map) to be released
67   // to the user, without having to recreate all of the callbacks.
68   struct SaveDestination {
SaveDestinationCmdlineParser::SaveDestination69     SaveDestination() : variant_map_(new TVariantMap()) {}
70 
71     // Save value to the variant map.
72     template <typename TArg>
SaveToMapCmdlineParser::SaveDestination73     void SaveToMap(const TVariantMapKey<TArg>& key, TArg& value) {
74       variant_map_->Set(key, value);
75     }
76 
77     // Get the existing value from a map, creating the value if it did not already exist.
78     template <typename TArg>
GetOrCreateFromMapCmdlineParser::SaveDestination79     TArg& GetOrCreateFromMap(const TVariantMapKey<TArg>& key) {
80       auto* ptr = variant_map_->Get(key);
81       if (ptr == nullptr) {
82         variant_map_->Set(key, TArg());
83         ptr = variant_map_->Get(key);
84         assert(ptr != nullptr);
85       }
86 
87       return *ptr;
88     }
89 
90    protected:
91     // Release the map, clearing it as a side-effect.
92     // Future saves will be distinct from previous saves.
ReleaseMapCmdlineParser::SaveDestination93     TVariantMap&& ReleaseMap() {
94       return std::move(*variant_map_);
95     }
96 
97     // Get a read-only reference to the variant map.
GetMapCmdlineParser::SaveDestination98     const TVariantMap& GetMap() {
99       return *variant_map_;
100     }
101 
102     // Clear all potential save targets.
ClearCmdlineParser::SaveDestination103     void Clear() {
104       variant_map_->Clear();
105     }
106 
107    private:
108     // Don't try to copy or move this. Just don't.
109     SaveDestination(const SaveDestination&) = delete;
110     SaveDestination(SaveDestination&&) = delete;
111     SaveDestination& operator=(const SaveDestination&) = delete;
112     SaveDestination& operator=(SaveDestination&&) = delete;
113 
114     std::shared_ptr<TVariantMap> variant_map_;
115 
116     // Allow the parser to change the underlying pointers when we release the underlying storage.
117     friend struct CmdlineParser;
118   };
119 
120  public:
121   // Builder for the argument definition of type TArg. Do not use this type directly,
122   // it is only a separate type to provide compile-time enforcement against doing
123   // illegal builds.
124   template <typename TArg>
125   struct ArgumentBuilder {
126     // Add a range check to this argument.
WithRangeCmdlineParser::ArgumentBuilder127     ArgumentBuilder<TArg>& WithRange(const TArg& min, const TArg& max) {
128       argument_info_.has_range_ = true;
129       argument_info_.min_ = min;
130       argument_info_.max_ = max;
131 
132       return *this;
133     }
134 
135     // Map the list of names into the list of values. List of names must not have
136     // any wildcards '_' in it.
137     //
138     // Do not use if a value map has already been set.
WithValuesCmdlineParser::ArgumentBuilder139     ArgumentBuilder<TArg>& WithValues(std::initializer_list<TArg> value_list) {
140       SetValuesInternal(value_list);
141       return *this;
142     }
143 
144     // When used with a single alias, map the alias into this value.
145     // Same as 'WithValues({value})' , but allows the omission of the curly braces {}.
WithValueCmdlineParser::ArgumentBuilder146     ArgumentBuilder<TArg> WithValue(const TArg& value) {
147       return WithValues({ value });
148     }
149 
150     // Map the parsed string values (from _) onto a concrete value. If no wildcard
151     // has been specified, then map the value directly from the arg name (i.e.
152     // if there are multiple aliases, then use the alias to do the mapping).
153     //
154     // Do not use if a values list has already been set.
WithValueMapCmdlineParser::ArgumentBuilder155     ArgumentBuilder<TArg>& WithValueMap(
156         std::initializer_list<std::pair<const char*, TArg>> key_value_list) {
157       assert(!argument_info_.has_value_list_);
158 
159       argument_info_.has_value_map_ = true;
160       argument_info_.value_map_ = key_value_list;
161 
162       return *this;
163     }
164 
165     // If this argument is seen multiple times, successive arguments mutate the same value
166     // instead of replacing it with a new value.
AppendValuesCmdlineParser::ArgumentBuilder167     ArgumentBuilder<TArg>& AppendValues() {
168       argument_info_.appending_values_ = true;
169 
170       return *this;
171     }
172 
WithMetavarCmdlineParser::ArgumentBuilder173     ArgumentBuilder<TArg>& WithMetavar(const char* sv) {
174       argument_info_.metavar_ = sv;
175       return *this;
176     }
177 
WithHelpCmdlineParser::ArgumentBuilder178     ArgumentBuilder<TArg>& WithHelp(const char* sv) {
179       argument_info_.help_ = sv;
180       return *this;
181     }
182 
183     // Convenience type alias for the variant map key type definition.
184     using MapKey = TVariantMapKey<TArg>;
185 
186     // Write the results of this argument into the key.
187     // To look up the parsed arguments, get the map and then use this key with VariantMap::Get
IntoKeyCmdlineParser::ArgumentBuilder188     CmdlineParser::Builder& IntoKey(const MapKey& key) {
189       // Only capture save destination as a pointer.
190       // This allows the parser to later on change the specific save targets.
191       auto save_destination = save_destination_;
192       save_value_ = [save_destination, &key](TArg& value) {
193         save_destination->SaveToMap(key, value);
194         CMDLINE_DEBUG_LOG << "Saved value into map '"
195             << detail::ToStringAny(value) << "'" << std::endl;
196       };
197 
198       load_value_ = [save_destination, &key]() -> TArg& {
199         TArg& value = save_destination->GetOrCreateFromMap(key);
200         CMDLINE_DEBUG_LOG << "Loaded value from map '" << detail::ToStringAny(value) << "'"
201             << std::endl;
202 
203         return value;
204       };
205 
206       save_value_specified_ = true;
207       load_value_specified_ = true;
208 
209       CompleteArgument();
210       return parent_;
211     }
212 
213     // Ensure we always move this when returning a new builder.
214     ArgumentBuilder(ArgumentBuilder&&) = default;
215 
216    protected:
217     // Used by builder to internally ignore arguments by dropping them on the floor after parsing.
IntoIgnoreCmdlineParser::ArgumentBuilder218     CmdlineParser::Builder& IntoIgnore() {
219       save_value_ = [](TArg& value) {
220         CMDLINE_DEBUG_LOG << "Ignored value '" << detail::ToStringAny(value) << "'" << std::endl;
221       };
222       load_value_ = []() -> TArg& {
223         assert(false && "Should not be appending values to ignored arguments");
224         __builtin_trap();  // Blow up.
225       };
226 
227       save_value_specified_ = true;
228       load_value_specified_ = true;
229 
230       CompleteArgument();
231       return parent_;
232     }
233 
SetValuesInternalCmdlineParser::ArgumentBuilder234     void SetValuesInternal(const std::vector<TArg>&& value_list) {
235       assert(!argument_info_.has_value_map_);
236 
237       argument_info_.has_value_list_ = true;
238       argument_info_.value_list_ = value_list;
239     }
240 
SetNamesCmdlineParser::ArgumentBuilder241     void SetNames(std::vector<const char*>&& names) {
242       argument_info_.names_ = names;
243     }
244 
SetNamesCmdlineParser::ArgumentBuilder245     void SetNames(std::initializer_list<const char*> names) {
246       argument_info_.names_ = names;
247     }
248 
SetHelpCmdlineParser::ArgumentBuilder249     void SetHelp(std::optional<const char*>&& val) {
250       argument_info_.help_ = val;
251     }
252 
SetCategoryCmdlineParser::ArgumentBuilder253     void SetCategory(std::optional<const char*>&& val) {
254       argument_info_.category_ = val;
255     }
SetMetavarCmdlineParser::ArgumentBuilder256     void SetMetavar(std::optional<const char*>&& val) {
257       argument_info_.metavar_ = val;
258     }
259 
260    private:
261     // Copying is bad. Move only.
262     ArgumentBuilder(const ArgumentBuilder&) = delete;
263 
264     // Called by any function that doesn't chain back into this builder.
265     // Completes the argument builder and save the information into the main builder.
CompleteArgumentCmdlineParser::ArgumentBuilder266     void CompleteArgument() {
267       assert(save_value_specified_ &&
268              "No Into... function called, nowhere to save parsed values to");
269       assert(load_value_specified_ &&
270              "No Into... function called, nowhere to load parsed values from");
271 
272       argument_info_.CompleteArgument();
273 
274       // Appending the completed argument is destructive. The object is no longer
275       // usable since all the useful information got moved out of it.
276       AppendCompletedArgument(parent_,
277                               new detail::CmdlineParseArgument<TArg>(
278                                   std::move(argument_info_),
279                                   std::move(save_value_),
280                                   std::move(load_value_)));
281     }
282 
283     friend struct CmdlineParser;
284     friend struct CmdlineParser::Builder;
285     friend struct CmdlineParser::UntypedArgumentBuilder;
286 
ArgumentBuilderCmdlineParser::ArgumentBuilder287     ArgumentBuilder(CmdlineParser::Builder& parser,
288                     std::shared_ptr<SaveDestination> save_destination)
289         : parent_(parser),
290           save_value_specified_(false),
291           load_value_specified_(false),
292           save_destination_(save_destination) {
293       save_value_ = [](TArg&) {
294         assert(false && "No save value function defined");
295       };
296 
297       load_value_ = []() -> TArg& {
298         assert(false && "No load value function defined");
299         __builtin_trap();  // Blow up.
300       };
301     }
302 
303     CmdlineParser::Builder& parent_;
304     std::function<void(TArg&)> save_value_;
305     std::function<TArg&(void)> load_value_;
306     bool save_value_specified_;
307     bool load_value_specified_;
308     detail::CmdlineParserArgumentInfo<TArg> argument_info_;
309 
310     std::shared_ptr<SaveDestination> save_destination_;
311   };
312 
313   struct UntypedArgumentBuilder {
314     // Set a type for this argument. The specific subcommand parser is looked up by the type.
315     template <typename TArg>
WithTypeCmdlineParser::UntypedArgumentBuilder316     ArgumentBuilder<TArg> WithType() {
317       return CreateTypedBuilder<TArg>();
318     }
319 
WithHelpCmdlineParser::UntypedArgumentBuilder320     UntypedArgumentBuilder& WithHelp(const char* sv) {
321       SetHelp(sv);
322       return *this;
323     }
324 
WithCategoryCmdlineParser::UntypedArgumentBuilder325     UntypedArgumentBuilder& WithCategory(const char* sv) {
326       SetCategory(sv);
327       return *this;
328     }
329 
WithMetavarCmdlineParser::UntypedArgumentBuilder330     UntypedArgumentBuilder& WithMetavar(const char* sv) {
331       SetMetavar(sv);
332       return *this;
333     }
334 
335     // When used with multiple aliases, map the position of the alias to the value position.
336     template <typename TArg>
WithValuesCmdlineParser::UntypedArgumentBuilder337     ArgumentBuilder<TArg> WithValues(std::initializer_list<TArg> values) {
338       auto&& a = CreateTypedBuilder<TArg>();
339       a.WithValues(values);
340       return std::move(a);
341     }
342 
343     // When used with a single alias, map the alias into this value.
344     // Same as 'WithValues({value})' , but allows the omission of the curly braces {}.
345     template <typename TArg>
WithValueCmdlineParser::UntypedArgumentBuilder346     ArgumentBuilder<TArg> WithValue(const TArg& value) {
347       return WithValues({ value });
348     }
349 
350     // Set the current building argument to target this key.
351     // When this command line argument is parsed, it can be fetched with this key.
IntoKeyCmdlineParser::UntypedArgumentBuilder352     Builder& IntoKey(const TVariantMapKey<Unit>& key) {
353       return CreateTypedBuilder<Unit>().IntoKey(key);
354     }
355 
356     // Ensure we always move this when returning a new builder.
357     UntypedArgumentBuilder(UntypedArgumentBuilder&&) = default;
358 
359    protected:
SetNamesCmdlineParser::UntypedArgumentBuilder360     void SetNames(std::vector<const char*>&& names) {
361       names_ = std::move(names);
362     }
363 
SetNamesCmdlineParser::UntypedArgumentBuilder364     void SetNames(std::initializer_list<const char*> names) {
365       names_ = names;
366     }
367 
SetHelpCmdlineParser::UntypedArgumentBuilder368     void SetHelp(std::optional<const char*> sv) {
369       help_.swap(sv);
370     }
371 
SetMetavarCmdlineParser::UntypedArgumentBuilder372     void SetMetavar(std::optional<const char*> sv) {
373       metavar_.swap(sv);
374     }
375 
SetCategoryCmdlineParser::UntypedArgumentBuilder376     void SetCategory(std::optional<const char*> sv) {
377       category_.swap(sv);
378     }
379 
380    private:
381     // No copying. Move instead.
382     UntypedArgumentBuilder(const UntypedArgumentBuilder&) = delete;
383 
384     template <typename TArg>
CreateTypedBuilderCmdlineParser::UntypedArgumentBuilder385     ArgumentBuilder<TArg> CreateTypedBuilder() {
386       auto&& b = CreateArgumentBuilder<TArg>(parent_);
387       InitializeTypedBuilder(&b);  // Type-specific initialization
388       b.SetNames(std::move(names_));
389       b.SetHelp(std::move(help_));
390       b.SetCategory(std::move(category_));
391       b.SetMetavar(std::move(metavar_));
392       return std::move(b);
393     }
394 
395     template <typename TArg = Unit>
396     typename std::enable_if<std::is_same<TArg, Unit>::value>::type
InitializeTypedBuilderCmdlineParser::UntypedArgumentBuilder397     InitializeTypedBuilder(ArgumentBuilder<TArg>* arg_builder) {
398       // Every Unit argument implicitly maps to a runtime value of Unit{}
399       std::vector<Unit> values(names_.size(), Unit{});
400       arg_builder->SetValuesInternal(std::move(values));
401     }
402 
403     // No extra work for all other types
InitializeTypedBuilderCmdlineParser::UntypedArgumentBuilder404     void InitializeTypedBuilder(void*) {}
405 
406     template <typename TArg>
407     friend struct ArgumentBuilder;
408     friend struct Builder;
409 
UntypedArgumentBuilderCmdlineParser::UntypedArgumentBuilder410     explicit UntypedArgumentBuilder(CmdlineParser::Builder& parent) : parent_(parent) {}
411     // UntypedArgumentBuilder(UntypedArgumentBuilder&& other) = default;
412 
413     CmdlineParser::Builder& parent_;
414     std::vector<const char*> names_;
415     std::optional<const char*> category_;
416     std::optional<const char*> help_;
417     std::optional<const char*> metavar_;
418   };
419 
420   // Build a new parser given a chain of calls to define arguments.
421   struct Builder {
BuilderCmdlineParser::Builder422     Builder() : save_destination_(new SaveDestination()) {}
423 
424     // Define a single argument. The default type is Unit.
DefineCmdlineParser::Builder425     UntypedArgumentBuilder Define(const char* name) {
426       return Define({name});
427     }
428 
ClearCategoryCmdlineParser::Builder429     Builder& ClearCategory() {
430       default_category_.reset();
431       return *this;
432     }
433 
SetCategoryCmdlineParser::Builder434     Builder& SetCategory(const char* sv) {
435       default_category_ = sv;
436       return *this;
437     }
438 
OrderCategoriesCmdlineParser::Builder439     Builder& OrderCategories(std::vector<const char*> categories) {
440       category_order_.swap(categories);
441       return *this;
442     }
443 
444     // Define a single argument with multiple aliases.
DefineCmdlineParser::Builder445     UntypedArgumentBuilder Define(std::initializer_list<const char*> names) {
446       auto&& b = UntypedArgumentBuilder(*this);
447       b.SetNames(names);
448       b.SetCategory(default_category_);
449       return std::move(b);
450     }
451 
452     // Whether the parser should give up on unrecognized arguments. Not recommended.
IgnoreUnrecognizedCmdlineParser::Builder453     Builder& IgnoreUnrecognized(bool ignore_unrecognized) {
454       ignore_unrecognized_ = ignore_unrecognized;
455       return *this;
456     }
457 
458     // Provide a list of arguments to ignore for backwards compatibility.
IgnoreCmdlineParser::Builder459     Builder& Ignore(std::initializer_list<const char*> ignore_list) {
460       auto current_cat = default_category_;
461       default_category_ = "Ignored";
462       for (auto&& ignore_name : ignore_list) {
463         std::string ign = ignore_name;
464 
465         // Ignored arguments are just like a regular definition which have very
466         // liberal parsing requirements (no range checks, no value checks).
467         // Unlike regular argument definitions, when a value gets parsed into its
468         // stronger type, we just throw it away.
469 
470         if (ign.find('_') != std::string::npos) {  // Does the arg-def have a wildcard?
471           // pretend this is a string, e.g. -Xjitconfig:<anythinggoeshere>
472           auto&& builder = Define(ignore_name).template WithType<std::string>().IntoIgnore();
473           assert(&builder == this);
474           (void)builder;  // Ignore pointless unused warning, it's used in the assert.
475         } else {
476           // pretend this is a unit, e.g. -Xjitblocking
477           auto&& builder = Define(ignore_name).template WithType<Unit>().IntoIgnore();
478           assert(&builder == this);
479           (void)builder;  // Ignore pointless unused warning, it's used in the assert.
480         }
481       }
482       ignore_list_ = ignore_list;
483       default_category_ = current_cat;
484       return *this;
485     }
486 
487     // Finish building the parser; performs a check of the validity. Return value is moved, not
488     // copied. Do not call this more than once.
BuildCmdlineParser::Builder489     CmdlineParser Build() {
490       assert(!built_);
491       built_ = true;
492 
493       auto&& p = CmdlineParser(ignore_unrecognized_,
494                                std::move(ignore_list_),
495                                save_destination_,
496                                std::move(completed_arguments_),
497                                std::move(category_order_));
498 
499       return std::move(p);
500     }
501 
502    protected:
AppendCompletedArgumentCmdlineParser::Builder503     void AppendCompletedArgument(detail::CmdlineParseArgumentAny* arg) {
504       auto smart_ptr = std::unique_ptr<detail::CmdlineParseArgumentAny>(arg);
505       completed_arguments_.push_back(std::move(smart_ptr));
506     }
507 
508    private:
509     // No copying now!
510     Builder(const Builder& other) = delete;
511 
512     template <typename TArg>
513     friend struct ArgumentBuilder;
514     friend struct UntypedArgumentBuilder;
515     friend struct CmdlineParser;
516 
517     bool built_ = false;
518     bool ignore_unrecognized_ = false;
519     std::vector<const char*> ignore_list_;
520     std::shared_ptr<SaveDestination> save_destination_;
521     std::optional<const char*> default_category_;
522     std::vector<const char*> category_order_;
523 
524     std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
525   };
526 
527   void DumpHelp(VariableIndentationOutputStream& vios);
528 
ParseCmdlineParser529   CmdlineResult Parse(const std::string& argv) {
530     std::vector<std::string> tokenized;
531     Split(argv, ' ', &tokenized);
532 
533     return Parse(TokenRange(std::move(tokenized)));
534   }
535 
536   // Parse the arguments; storing results into the arguments map. Returns success value.
ParseCmdlineParser537   CmdlineResult Parse(const char* argv) {
538     return Parse(std::string(argv));
539   }
540 
541   // Parse the arguments; storing the results into the arguments map. Returns success value.
542   // Assumes that argv[0] is a valid argument (i.e. not the program name).
ParseCmdlineParser543   CmdlineResult Parse(const std::vector<const char*>& argv) {
544     return Parse(TokenRange(argv.begin(), argv.end()));
545   }
546 
547   // Parse the arguments; storing the results into the arguments map. Returns success value.
548   // Assumes that argv[0] is a valid argument (i.e. not the program name).
ParseCmdlineParser549   CmdlineResult Parse(const std::vector<std::string>& argv) {
550     return Parse(TokenRange(argv.begin(), argv.end()));
551   }
552 
553   // Parse the arguments (directly from an int main(argv,argc)). Returns success value.
554   // Assumes that argv[0] is the program name, and ignores it.
ParseCmdlineParser555   CmdlineResult Parse(const char* argv[], int argc) {
556     return Parse(TokenRange(&argv[1], argc - 1));  // ignore argv[0] because it's the program name
557   }
558 
559   // Look up the arguments that have been parsed; use the target keys to lookup individual args.
GetArgumentsMapCmdlineParser560   const TVariantMap& GetArgumentsMap() const {
561     return save_destination_->GetMap();
562   }
563 
564   // Release the arguments map that has been parsed; useful for move semantics.
ReleaseArgumentsMapCmdlineParser565   TVariantMap&& ReleaseArgumentsMap() {
566     return save_destination_->ReleaseMap();
567   }
568 
569   // How many arguments were defined?
CountDefinedArgumentsCmdlineParser570   size_t CountDefinedArguments() const {
571     return completed_arguments_.size();
572   }
573 
574   // Ensure we have a default move constructor.
575   CmdlineParser(CmdlineParser&&) = default;
576   // Ensure we have a default move assignment operator.
577   CmdlineParser& operator=(CmdlineParser&&) = default;
578 
579  private:
580   friend struct Builder;
581 
582   // Construct a new parser from the builder. Move all the arguments.
CmdlineParserCmdlineParser583   CmdlineParser(bool ignore_unrecognized,
584                 std::vector<const char*>&& ignore_list,
585                 std::shared_ptr<SaveDestination> save_destination,
586                 std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>>&& completed_arguments,
587                 std::vector<const char*>&& category_order)
588     : ignore_unrecognized_(ignore_unrecognized),
589       ignore_list_(std::move(ignore_list)),
590       save_destination_(save_destination),
591       completed_arguments_(std::move(completed_arguments)),
592       category_order_(category_order) {
593     assert(save_destination != nullptr);
594   }
595 
596   // Parse the arguments; storing results into the arguments map. Returns success value.
597   // The parsing will fail on the first non-success parse result and return that error.
598   //
599   // All previously-parsed arguments are cleared out.
600   // Otherwise, all parsed arguments will be stored into SaveDestination as a side-effect.
601   // A partial parse will result only in a partial save of the arguments.
ParseCmdlineParser602   CmdlineResult Parse(TokenRange&& arguments_list) {
603     save_destination_->Clear();
604 
605     for (size_t i = 0; i < arguments_list.Size(); ) {
606       TokenRange possible_name = arguments_list.Slice(i);
607 
608       size_t best_match_size = 0;  // How many tokens were matched in the best case.
609       size_t best_match_arg_idx = 0;
610       bool matched = false;  // At least one argument definition has been matched?
611 
612       // Find the closest argument definition for the remaining token range.
613       size_t arg_idx = 0;
614       for (auto&& arg : completed_arguments_) {
615         size_t local_match = arg->MaybeMatches(possible_name);
616 
617         if (local_match > best_match_size) {
618           best_match_size = local_match;
619           best_match_arg_idx = arg_idx;
620           matched = true;
621         }
622         arg_idx++;
623       }
624 
625       // Saw some kind of unknown argument
626       if (matched == false) {
627         if (UNLIKELY(ignore_unrecognized_)) {  // This is usually off, we only need it for JNI.
628           // Consume 1 token and keep going, hopefully the next token is a good one.
629           ++i;
630           continue;
631         }
632         // Common case:
633         // Bail out on the first unknown argument with an error.
634         return CmdlineResult(CmdlineResult::kUnknown,
635                              std::string("Unknown argument: ") + possible_name[0]);
636       }
637 
638       // Look at the best-matched argument definition and try to parse against that.
639       auto&& arg = completed_arguments_[best_match_arg_idx];
640 
641       assert(arg->MaybeMatches(possible_name) == best_match_size);
642 
643       // Try to parse the argument now, if we have enough tokens.
644       std::pair<size_t, size_t> num_tokens = arg->GetNumTokens();
645       size_t min_tokens;
646       size_t max_tokens;
647 
648       std::tie(min_tokens, max_tokens) = num_tokens;
649 
650       if ((i + min_tokens) > arguments_list.Size()) {
651         // expected longer command line but it was too short
652         // e.g. if the argv was only "-Xms" without specifying a memory option
653         CMDLINE_DEBUG_LOG << "Parse failure, i = " << i << ", arg list " << arguments_list.Size() <<
654             " num tokens in arg_def: " << min_tokens << "," << max_tokens << std::endl;
655         return CmdlineResult(CmdlineResult::kFailure,
656                              std::string("Argument ") +
657                              possible_name[0] + ": incomplete command line arguments, expected "
658                              + std::to_string(size_t(i + min_tokens) - arguments_list.Size()) +
659                              " more tokens");
660       }
661 
662       if (best_match_size > max_tokens || best_match_size < min_tokens) {
663         // Even our best match was out of range, so parsing would fail instantly.
664         return CmdlineResult(CmdlineResult::kFailure,
665                              std::string("Argument ") + possible_name[0] + ": too few tokens "
666                              "matched " + std::to_string(best_match_size)
667                              + " but wanted " + std::to_string(num_tokens.first));
668       }
669 
670       // We have enough tokens to begin exact parsing.
671       TokenRange exact_range = possible_name.Slice(0, max_tokens);
672 
673       size_t consumed_tokens = 1;  // At least 1 if we ever want to try to resume parsing on error
674       CmdlineResult parse_attempt = arg->ParseArgument(exact_range, &consumed_tokens);
675 
676       if (parse_attempt.IsError()) {
677         // We may also want to continue parsing the other tokens to gather more errors.
678         return parse_attempt;
679       }  // else the value has been successfully stored into the map
680 
681       assert(consumed_tokens > 0);  // Don't hang in an infinite loop trying to parse
682       i += consumed_tokens;
683 
684       // TODO: also handle ignoring arguments for backwards compatibility
685     }  // for
686 
687     return CmdlineResult(CmdlineResult::kSuccess);
688   }
689 
690   bool ignore_unrecognized_ = false;
691   std::vector<const char*> ignore_list_;
692   std::shared_ptr<SaveDestination> save_destination_;
693   std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
694   std::vector<const char*> category_order_;
695 };
696 
697 // This has to be defined after everything else, since we want the builders to call this.
698 template <typename TVariantMap,
699           template <typename TKeyValue> class TVariantMapKey>
700 template <typename TArg>
701 typename CmdlineParser<TVariantMap, TVariantMapKey>::template ArgumentBuilder<TArg>
CreateArgumentBuilder(CmdlineParser<TVariantMap,TVariantMapKey>::Builder & parent)702 CmdlineParser<TVariantMap, TVariantMapKey>::CreateArgumentBuilder(
703     CmdlineParser<TVariantMap, TVariantMapKey>::Builder& parent) {
704   return CmdlineParser<TVariantMap, TVariantMapKey>::ArgumentBuilder<TArg>(
705       parent, parent.save_destination_);
706 }
707 
708 // This has to be defined after everything else, since we want the builders to call this.
709 template <typename TVariantMap,
710           template <typename TKeyValue> class TVariantMapKey>
AppendCompletedArgument(CmdlineParser<TVariantMap,TVariantMapKey>::Builder & builder,detail::CmdlineParseArgumentAny * arg)711 void CmdlineParser<TVariantMap, TVariantMapKey>::AppendCompletedArgument(
712     CmdlineParser<TVariantMap, TVariantMapKey>::Builder& builder,
713     detail::CmdlineParseArgumentAny* arg) {
714   builder.AppendCompletedArgument(arg);
715 }
716 
717 template <typename TVariantMap,
718           template <typename TKeyValue> class TVariantMapKey>
DumpHelp(VariableIndentationOutputStream & vios)719 void CmdlineParser<TVariantMap, TVariantMapKey>::DumpHelp(VariableIndentationOutputStream& vios) {
720   std::vector<detail::CmdlineParseArgumentAny*> uncat;
721   std::unordered_map<std::string, std::vector<detail::CmdlineParseArgumentAny*>> args;
722   for (const std::unique_ptr<detail::CmdlineParseArgumentAny>& it : completed_arguments_) {
723     auto cat = it->GetCategory();
724     if (cat) {
725       if (args.find(cat.value()) == args.end()) {
726         args[cat.value()] = {};
727       }
728       args.at(cat.value()).push_back(it.get());
729     } else {
730       uncat.push_back(it.get());
731     }
732   }
733   args.erase("Ignored");
734   for (auto arg : uncat) {
735     arg->DumpHelp(vios);
736     vios.Stream();
737   }
738   for (auto it : category_order_) {
739     auto cur = args.find(it);
740     if (cur != args.end() && !cur->second.empty()) {
741       vios.Stream() << "The following " << it << " arguments are supported:" << std::endl;
742       ScopedIndentation si(&vios);
743       for (detail::CmdlineParseArgumentAny* arg : cur->second) {
744         arg->DumpHelp(vios);
745         vios.Stream();
746       }
747       args.erase(cur->first);
748     }
749   }
750   for (auto [cat, lst] : args) {
751     vios.Stream() << "The following " << cat << " arguments are supported:" << std::endl;
752     ScopedIndentation si(&vios);
753     for (auto& arg : completed_arguments_) {
754       arg->DumpHelp(vios);
755       vios.Stream();
756     }
757   }
758   vios.Stream() << "The following arguments are ignored for compatibility:" << std::endl;
759   ScopedIndentation si(&vios);
760   for (auto ign : ignore_list_) {
761     vios.Stream() << ign << std::endl;
762   }
763 }
764 
765 }  // namespace art
766 
767 #endif  // ART_CMDLINE_CMDLINE_PARSER_H_
768