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