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     // Write the results of this argument into a variable pointed to by destination.
214     // An optional is used to tell whether the command line argument was present.
IntoLocationCmdlineParser::ArgumentBuilder215     CmdlineParser::Builder& IntoLocation(std::optional<TArg>* destination) {
216       save_value_ = [destination](TArg& value) {
217         *destination = value;
218       };
219 
220       load_value_ = [destination]() -> TArg& {
221         return destination->value();
222       };
223 
224       save_value_specified_ = true;
225       load_value_specified_ = true;
226 
227       CompleteArgument();
228       return parent_;
229     }
230 
231     // Ensure we always move this when returning a new builder.
232     ArgumentBuilder(ArgumentBuilder&&) = default;
233 
234    protected:
235     // Used by builder to internally ignore arguments by dropping them on the floor after parsing.
IntoIgnoreCmdlineParser::ArgumentBuilder236     CmdlineParser::Builder& IntoIgnore() {
237       save_value_ = [](TArg& value) {
238         CMDLINE_DEBUG_LOG << "Ignored value '" << detail::ToStringAny(value) << "'" << std::endl;
239       };
240       load_value_ = []() -> TArg& {
241         assert(false && "Should not be appending values to ignored arguments");
242         __builtin_trap();  // Blow up.
243       };
244 
245       save_value_specified_ = true;
246       load_value_specified_ = true;
247 
248       CompleteArgument();
249       return parent_;
250     }
251 
SetValuesInternalCmdlineParser::ArgumentBuilder252     void SetValuesInternal(const std::vector<TArg>&& value_list) {
253       assert(!argument_info_.has_value_map_);
254 
255       argument_info_.has_value_list_ = true;
256       argument_info_.value_list_ = value_list;
257     }
258 
SetNamesCmdlineParser::ArgumentBuilder259     void SetNames(std::vector<const char*>&& names) {
260       argument_info_.names_ = names;
261     }
262 
SetNamesCmdlineParser::ArgumentBuilder263     void SetNames(std::initializer_list<const char*> names) {
264       argument_info_.names_ = names;
265     }
266 
SetHelpCmdlineParser::ArgumentBuilder267     void SetHelp(std::optional<const char*>&& val) {
268       argument_info_.help_ = val;
269     }
270 
SetCategoryCmdlineParser::ArgumentBuilder271     void SetCategory(std::optional<const char*>&& val) {
272       argument_info_.category_ = val;
273     }
SetMetavarCmdlineParser::ArgumentBuilder274     void SetMetavar(std::optional<const char*>&& val) {
275       argument_info_.metavar_ = val;
276     }
277 
278    private:
279     // Copying is bad. Move only.
280     ArgumentBuilder(const ArgumentBuilder&) = delete;
281 
282     // Called by any function that doesn't chain back into this builder.
283     // Completes the argument builder and save the information into the main builder.
CompleteArgumentCmdlineParser::ArgumentBuilder284     void CompleteArgument() {
285       assert(save_value_specified_ &&
286              "No Into... function called, nowhere to save parsed values to");
287       assert(load_value_specified_ &&
288              "No Into... function called, nowhere to load parsed values from");
289 
290       argument_info_.CompleteArgument();
291 
292       // Appending the completed argument is destructive. The object is no longer
293       // usable since all the useful information got moved out of it.
294       AppendCompletedArgument(parent_,
295                               new detail::CmdlineParseArgument<TArg>(
296                                   std::move(argument_info_),
297                                   std::move(save_value_),
298                                   std::move(load_value_)));
299     }
300 
301     friend struct CmdlineParser;
302     friend struct CmdlineParser::Builder;
303     friend struct CmdlineParser::UntypedArgumentBuilder;
304 
ArgumentBuilderCmdlineParser::ArgumentBuilder305     ArgumentBuilder(CmdlineParser::Builder& parser,
306                     std::shared_ptr<SaveDestination> save_destination)
307         : parent_(parser),
308           save_value_specified_(false),
309           load_value_specified_(false),
310           save_destination_(save_destination) {
311       save_value_ = [](TArg&) {
312         assert(false && "No save value function defined");
313       };
314 
315       load_value_ = []() -> TArg& {
316         assert(false && "No load value function defined");
317         __builtin_trap();  // Blow up.
318       };
319     }
320 
321     CmdlineParser::Builder& parent_;
322     std::function<void(TArg&)> save_value_;
323     std::function<TArg&(void)> load_value_;
324     bool save_value_specified_;
325     bool load_value_specified_;
326     detail::CmdlineParserArgumentInfo<TArg> argument_info_;
327 
328     std::shared_ptr<SaveDestination> save_destination_;
329   };
330 
331   struct UntypedArgumentBuilder {
332     // Set a type for this argument. The specific subcommand parser is looked up by the type.
333     template <typename TArg>
WithTypeCmdlineParser::UntypedArgumentBuilder334     ArgumentBuilder<TArg> WithType() {
335       return CreateTypedBuilder<TArg>();
336     }
337 
WithHelpCmdlineParser::UntypedArgumentBuilder338     UntypedArgumentBuilder& WithHelp(const char* sv) {
339       SetHelp(sv);
340       return *this;
341     }
342 
WithCategoryCmdlineParser::UntypedArgumentBuilder343     UntypedArgumentBuilder& WithCategory(const char* sv) {
344       SetCategory(sv);
345       return *this;
346     }
347 
WithMetavarCmdlineParser::UntypedArgumentBuilder348     UntypedArgumentBuilder& WithMetavar(const char* sv) {
349       SetMetavar(sv);
350       return *this;
351     }
352 
353     // When used with multiple aliases, map the position of the alias to the value position.
354     template <typename TArg>
WithValuesCmdlineParser::UntypedArgumentBuilder355     ArgumentBuilder<TArg> WithValues(std::initializer_list<TArg> values) {
356       auto&& a = CreateTypedBuilder<TArg>();
357       a.WithValues(values);
358       return std::move(a);
359     }
360 
361     // When used with a single alias, map the alias into this value.
362     // Same as 'WithValues({value})' , but allows the omission of the curly braces {}.
363     template <typename TArg>
WithValueCmdlineParser::UntypedArgumentBuilder364     ArgumentBuilder<TArg> WithValue(const TArg& value) {
365       return WithValues({ value });
366     }
367 
368     // Set the current building argument to target this key.
369     // When this command line argument is parsed, it can be fetched with this key.
IntoKeyCmdlineParser::UntypedArgumentBuilder370     Builder& IntoKey(const TVariantMapKey<Unit>& key) {
371       return CreateTypedBuilder<Unit>().IntoKey(key);
372     }
373 
374     // Ensure we always move this when returning a new builder.
375     UntypedArgumentBuilder(UntypedArgumentBuilder&&) = default;
376 
377    protected:
SetNamesCmdlineParser::UntypedArgumentBuilder378     void SetNames(std::vector<const char*>&& names) {
379       names_ = std::move(names);
380     }
381 
SetNamesCmdlineParser::UntypedArgumentBuilder382     void SetNames(std::initializer_list<const char*> names) {
383       names_ = names;
384     }
385 
SetHelpCmdlineParser::UntypedArgumentBuilder386     void SetHelp(std::optional<const char*> sv) {
387       help_.swap(sv);
388     }
389 
SetMetavarCmdlineParser::UntypedArgumentBuilder390     void SetMetavar(std::optional<const char*> sv) {
391       metavar_.swap(sv);
392     }
393 
SetCategoryCmdlineParser::UntypedArgumentBuilder394     void SetCategory(std::optional<const char*> sv) {
395       category_.swap(sv);
396     }
397 
398    private:
399     // No copying. Move instead.
400     UntypedArgumentBuilder(const UntypedArgumentBuilder&) = delete;
401 
402     template <typename TArg>
CreateTypedBuilderCmdlineParser::UntypedArgumentBuilder403     ArgumentBuilder<TArg> CreateTypedBuilder() {
404       auto&& b = CreateArgumentBuilder<TArg>(parent_);
405       InitializeTypedBuilder(&b);  // Type-specific initialization
406       b.SetNames(std::move(names_));
407       b.SetHelp(std::move(help_));
408       b.SetCategory(std::move(category_));
409       b.SetMetavar(std::move(metavar_));
410       return std::move(b);
411     }
412 
413     template <typename TArg = Unit>
414     typename std::enable_if<std::is_same<TArg, Unit>::value>::type
InitializeTypedBuilderCmdlineParser::UntypedArgumentBuilder415     InitializeTypedBuilder(ArgumentBuilder<TArg>* arg_builder) {
416       // Every Unit argument implicitly maps to a runtime value of Unit{}
417       std::vector<Unit> values(names_.size(), Unit{});
418       arg_builder->SetValuesInternal(std::move(values));
419     }
420 
421     // No extra work for all other types
InitializeTypedBuilderCmdlineParser::UntypedArgumentBuilder422     void InitializeTypedBuilder(void*) {}
423 
424     template <typename TArg>
425     friend struct ArgumentBuilder;
426     friend struct Builder;
427 
UntypedArgumentBuilderCmdlineParser::UntypedArgumentBuilder428     explicit UntypedArgumentBuilder(CmdlineParser::Builder& parent) : parent_(parent) {}
429     // UntypedArgumentBuilder(UntypedArgumentBuilder&& other) = default;
430 
431     CmdlineParser::Builder& parent_;
432     std::vector<const char*> names_;
433     std::optional<const char*> category_;
434     std::optional<const char*> help_;
435     std::optional<const char*> metavar_;
436   };
437 
438   // Build a new parser given a chain of calls to define arguments.
439   struct Builder {
BuilderCmdlineParser::Builder440     Builder() : save_destination_(new SaveDestination()) {}
441 
442     // Define a single argument. The default type is Unit.
DefineCmdlineParser::Builder443     UntypedArgumentBuilder Define(const char* name) {
444       return Define({name});
445     }
446 
ClearCategoryCmdlineParser::Builder447     Builder& ClearCategory() {
448       default_category_.reset();
449       return *this;
450     }
451 
SetCategoryCmdlineParser::Builder452     Builder& SetCategory(const char* sv) {
453       default_category_ = sv;
454       return *this;
455     }
456 
OrderCategoriesCmdlineParser::Builder457     Builder& OrderCategories(std::vector<const char*> categories) {
458       category_order_.swap(categories);
459       return *this;
460     }
461 
462     // Define a single argument with multiple aliases.
DefineCmdlineParser::Builder463     UntypedArgumentBuilder Define(std::initializer_list<const char*> names) {
464       auto&& b = UntypedArgumentBuilder(*this);
465       b.SetNames(names);
466       b.SetCategory(default_category_);
467       return std::move(b);
468     }
469 
470     // Whether the parser should give up on unrecognized arguments. Not recommended.
IgnoreUnrecognizedCmdlineParser::Builder471     Builder& IgnoreUnrecognized(bool ignore_unrecognized) {
472       ignore_unrecognized_ = ignore_unrecognized;
473       return *this;
474     }
475 
476     // Provide a list of arguments to ignore for backwards compatibility.
IgnoreCmdlineParser::Builder477     Builder& Ignore(std::initializer_list<const char*> ignore_list) {
478       auto current_cat = default_category_;
479       default_category_ = "Ignored";
480       for (auto&& ignore_name : ignore_list) {
481         std::string ign = ignore_name;
482 
483         // Ignored arguments are just like a regular definition which have very
484         // liberal parsing requirements (no range checks, no value checks).
485         // Unlike regular argument definitions, when a value gets parsed into its
486         // stronger type, we just throw it away.
487 
488         if (ign.find('_') != std::string::npos) {  // Does the arg-def have a wildcard?
489           // pretend this is a string, e.g. -Xjitconfig:<anythinggoeshere>
490           auto&& builder = Define(ignore_name).template WithType<std::string>().IntoIgnore();
491           assert(&builder == this);
492           (void)builder;  // Ignore pointless unused warning, it's used in the assert.
493         } else {
494           // pretend this is a unit, e.g. -Xjitblocking
495           auto&& builder = Define(ignore_name).template WithType<Unit>().IntoIgnore();
496           assert(&builder == this);
497           (void)builder;  // Ignore pointless unused warning, it's used in the assert.
498         }
499       }
500       ignore_list_ = ignore_list;
501       default_category_ = current_cat;
502       return *this;
503     }
504 
505     // Finish building the parser; performs a check of the validity. Return value is moved, not
506     // copied. Do not call this more than once.
BuildCmdlineParser::Builder507     CmdlineParser Build() {
508       assert(!built_);
509       built_ = true;
510 
511       auto&& p = CmdlineParser(ignore_unrecognized_,
512                                std::move(ignore_list_),
513                                save_destination_,
514                                std::move(completed_arguments_),
515                                std::move(category_order_));
516 
517       return std::move(p);
518     }
519 
520    protected:
AppendCompletedArgumentCmdlineParser::Builder521     void AppendCompletedArgument(detail::CmdlineParseArgumentAny* arg) {
522       auto smart_ptr = std::unique_ptr<detail::CmdlineParseArgumentAny>(arg);
523       completed_arguments_.push_back(std::move(smart_ptr));
524     }
525 
526    private:
527     // No copying now!
528     Builder(const Builder& other) = delete;
529 
530     template <typename TArg>
531     friend struct ArgumentBuilder;
532     friend struct UntypedArgumentBuilder;
533     friend struct CmdlineParser;
534 
535     bool built_ = false;
536     bool ignore_unrecognized_ = false;
537     std::vector<const char*> ignore_list_;
538     std::shared_ptr<SaveDestination> save_destination_;
539     std::optional<const char*> default_category_;
540     std::vector<const char*> category_order_;
541 
542     std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
543   };
544 
545   void DumpHelp(VariableIndentationOutputStream& vios);
546 
ParseCmdlineParser547   CmdlineResult Parse(const std::string& argv) {
548     std::vector<std::string> tokenized;
549     Split(argv, ' ', &tokenized);
550 
551     return Parse(TokenRange(std::move(tokenized)));
552   }
553 
554   // Parse the arguments; storing results into the arguments map. Returns success value.
ParseCmdlineParser555   CmdlineResult Parse(const char* argv) {
556     return Parse(std::string(argv));
557   }
558 
559   // Parse the arguments; storing the results into the arguments map. Returns success value.
560   // Assumes that argv[0] is a valid argument (i.e. not the program name).
ParseCmdlineParser561   CmdlineResult Parse(const std::vector<const char*>& argv) {
562     return Parse(TokenRange(argv.begin(), argv.end()));
563   }
564 
565   // Parse the arguments; storing the results into the arguments map. Returns success value.
566   // Assumes that argv[0] is a valid argument (i.e. not the program name).
ParseCmdlineParser567   CmdlineResult Parse(const std::vector<std::string>& argv) {
568     return Parse(TokenRange(argv.begin(), argv.end()));
569   }
570 
571   // Parse the arguments (directly from an int main(argv,argc)). Returns success value.
572   // Assumes that argv[0] is the program name, and ignores it.
ParseCmdlineParser573   CmdlineResult Parse(const char* argv[], int argc) {
574     return Parse(TokenRange(&argv[1], argc - 1));  // ignore argv[0] because it's the program name
575   }
576 
577   // Look up the arguments that have been parsed; use the target keys to lookup individual args.
GetArgumentsMapCmdlineParser578   const TVariantMap& GetArgumentsMap() const {
579     return save_destination_->GetMap();
580   }
581 
582   // Release the arguments map that has been parsed; useful for move semantics.
ReleaseArgumentsMapCmdlineParser583   TVariantMap&& ReleaseArgumentsMap() {
584     return save_destination_->ReleaseMap();
585   }
586 
587   // How many arguments were defined?
CountDefinedArgumentsCmdlineParser588   size_t CountDefinedArguments() const {
589     return completed_arguments_.size();
590   }
591 
592   // Ensure we have a default move constructor.
593   CmdlineParser(CmdlineParser&&) = default;
594   // Ensure we have a default move assignment operator.
595   CmdlineParser& operator=(CmdlineParser&&) = default;
596 
597  private:
598   friend struct Builder;
599 
600   // Construct a new parser from the builder. Move all the arguments.
CmdlineParserCmdlineParser601   CmdlineParser(bool ignore_unrecognized,
602                 std::vector<const char*>&& ignore_list,
603                 std::shared_ptr<SaveDestination> save_destination,
604                 std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>>&& completed_arguments,
605                 std::vector<const char*>&& category_order)
606     : ignore_unrecognized_(ignore_unrecognized),
607       ignore_list_(std::move(ignore_list)),
608       save_destination_(save_destination),
609       completed_arguments_(std::move(completed_arguments)),
610       category_order_(category_order) {
611     assert(save_destination != nullptr);
612   }
613 
614   // Parse the arguments; storing results into the arguments map. Returns success value.
615   // The parsing will fail on the first non-success parse result and return that error.
616   //
617   // All previously-parsed arguments are cleared out.
618   // Otherwise, all parsed arguments will be stored into SaveDestination as a side-effect.
619   // A partial parse will result only in a partial save of the arguments.
ParseCmdlineParser620   CmdlineResult Parse(TokenRange&& arguments_list) {
621     save_destination_->Clear();
622 
623     for (size_t i = 0; i < arguments_list.Size(); ) {
624       TokenRange possible_name = arguments_list.Slice(i);
625 
626       size_t best_match_size = 0;  // How many tokens were matched in the best case.
627       size_t best_match_arg_idx = 0;
628       bool matched = false;  // At least one argument definition has been matched?
629 
630       // Find the closest argument definition for the remaining token range.
631       size_t arg_idx = 0;
632       for (auto&& arg : completed_arguments_) {
633         size_t local_match = arg->MaybeMatches(possible_name);
634 
635         if (local_match > best_match_size) {
636           best_match_size = local_match;
637           best_match_arg_idx = arg_idx;
638           matched = true;
639         }
640         arg_idx++;
641       }
642 
643       // Saw some kind of unknown argument
644       if (matched == false) {
645         if (UNLIKELY(ignore_unrecognized_)) {  // This is usually off, we only need it for JNI.
646           // Consume 1 token and keep going, hopefully the next token is a good one.
647           ++i;
648           continue;
649         }
650         // Common case:
651         // Bail out on the first unknown argument with an error.
652         return CmdlineResult(CmdlineResult::kUnknown,
653                              std::string("Unknown argument: ") + possible_name[0]);
654       }
655 
656       // Look at the best-matched argument definition and try to parse against that.
657       auto&& arg = completed_arguments_[best_match_arg_idx];
658 
659       assert(arg->MaybeMatches(possible_name) == best_match_size);
660 
661       // Try to parse the argument now, if we have enough tokens.
662       std::pair<size_t, size_t> num_tokens = arg->GetNumTokens();
663       size_t min_tokens;
664       size_t max_tokens;
665 
666       std::tie(min_tokens, max_tokens) = num_tokens;
667 
668       if ((i + min_tokens) > arguments_list.Size()) {
669         // expected longer command line but it was too short
670         // e.g. if the argv was only "-Xms" without specifying a memory option
671         CMDLINE_DEBUG_LOG << "Parse failure, i = " << i << ", arg list " << arguments_list.Size() <<
672             " num tokens in arg_def: " << min_tokens << "," << max_tokens << std::endl;
673         return CmdlineResult(CmdlineResult::kFailure,
674                              std::string("Argument ") +
675                              possible_name[0] + ": incomplete command line arguments, expected "
676                              + std::to_string(size_t(i + min_tokens) - arguments_list.Size()) +
677                              " more tokens");
678       }
679 
680       if (best_match_size > max_tokens || best_match_size < min_tokens) {
681         // Even our best match was out of range, so parsing would fail instantly.
682         return CmdlineResult(CmdlineResult::kFailure,
683                              std::string("Argument ") + possible_name[0] + ": too few tokens "
684                              "matched " + std::to_string(best_match_size)
685                              + " but wanted " + std::to_string(num_tokens.first));
686       }
687 
688       // We have enough tokens to begin exact parsing.
689       TokenRange exact_range = possible_name.Slice(0, max_tokens);
690 
691       size_t consumed_tokens = 1;  // At least 1 if we ever want to try to resume parsing on error
692       CmdlineResult parse_attempt = arg->ParseArgument(exact_range, &consumed_tokens);
693 
694       if (parse_attempt.IsError()) {
695         // We may also want to continue parsing the other tokens to gather more errors.
696         return parse_attempt;
697       }  // else the value has been successfully stored into the map
698 
699       assert(consumed_tokens > 0);  // Don't hang in an infinite loop trying to parse
700       i += consumed_tokens;
701 
702       // TODO: also handle ignoring arguments for backwards compatibility
703     }  // for
704 
705     return CmdlineResult(CmdlineResult::kSuccess);
706   }
707 
708   bool ignore_unrecognized_ = false;
709   std::vector<const char*> ignore_list_;
710   std::shared_ptr<SaveDestination> save_destination_;
711   std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
712   std::vector<const char*> category_order_;
713 };
714 
715 // This has to be defined after everything else, since we want the builders to call this.
716 template <typename TVariantMap,
717           template <typename TKeyValue> class TVariantMapKey>
718 template <typename TArg>
719 typename CmdlineParser<TVariantMap, TVariantMapKey>::template ArgumentBuilder<TArg>
CreateArgumentBuilder(CmdlineParser<TVariantMap,TVariantMapKey>::Builder & parent)720 CmdlineParser<TVariantMap, TVariantMapKey>::CreateArgumentBuilder(
721     CmdlineParser<TVariantMap, TVariantMapKey>::Builder& parent) {
722   return CmdlineParser<TVariantMap, TVariantMapKey>::ArgumentBuilder<TArg>(
723       parent, parent.save_destination_);
724 }
725 
726 // This has to be defined after everything else, since we want the builders to call this.
727 template <typename TVariantMap,
728           template <typename TKeyValue> class TVariantMapKey>
AppendCompletedArgument(CmdlineParser<TVariantMap,TVariantMapKey>::Builder & builder,detail::CmdlineParseArgumentAny * arg)729 void CmdlineParser<TVariantMap, TVariantMapKey>::AppendCompletedArgument(
730     CmdlineParser<TVariantMap, TVariantMapKey>::Builder& builder,
731     detail::CmdlineParseArgumentAny* arg) {
732   builder.AppendCompletedArgument(arg);
733 }
734 
735 template <typename TVariantMap,
736           template <typename TKeyValue> class TVariantMapKey>
DumpHelp(VariableIndentationOutputStream & vios)737 void CmdlineParser<TVariantMap, TVariantMapKey>::DumpHelp(VariableIndentationOutputStream& vios) {
738   std::vector<detail::CmdlineParseArgumentAny*> uncat;
739   std::unordered_map<std::string, std::vector<detail::CmdlineParseArgumentAny*>> args;
740   for (const std::unique_ptr<detail::CmdlineParseArgumentAny>& it : completed_arguments_) {
741     auto cat = it->GetCategory();
742     if (cat) {
743       if (args.find(cat.value()) == args.end()) {
744         args[cat.value()] = {};
745       }
746       args.at(cat.value()).push_back(it.get());
747     } else {
748       uncat.push_back(it.get());
749     }
750   }
751   args.erase("Ignored");
752   for (auto arg : uncat) {
753     arg->DumpHelp(vios);
754     vios.Stream();
755   }
756   for (auto it : category_order_) {
757     auto cur = args.find(it);
758     if (cur != args.end() && !cur->second.empty()) {
759       vios.Stream() << "The following " << it << " arguments are supported:" << std::endl;
760       ScopedIndentation si(&vios);
761       for (detail::CmdlineParseArgumentAny* arg : cur->second) {
762         arg->DumpHelp(vios);
763         vios.Stream();
764       }
765       args.erase(cur->first);
766     }
767   }
768   for (auto [cat, lst] : args) {
769     vios.Stream() << "The following " << cat << " arguments are supported:" << std::endl;
770     ScopedIndentation si(&vios);
771     for (auto& arg : completed_arguments_) {
772       arg->DumpHelp(vios);
773       vios.Stream();
774     }
775   }
776   if (!ignore_list_.empty()) {
777     vios.Stream() << "The following arguments are ignored for compatibility:" << std::endl;
778     ScopedIndentation si(&vios);
779     for (auto ign : ignore_list_) {
780       vios.Stream() << ign << std::endl;
781     }
782   }
783 }
784 
785 }  // namespace art
786 
787 #endif  // ART_CMDLINE_CMDLINE_PARSER_H_
788