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