• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2006-2008 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/flags/flags.h"
6 
7 #include <cctype>
8 #include <cerrno>
9 #include <cinttypes>
10 #include <cstdlib>
11 #include <cstring>
12 #include <iomanip>
13 #include <sstream>
14 
15 #include "src/base/functional.h"
16 #include "src/base/logging.h"
17 #include "src/base/platform/platform.h"
18 #include "src/codegen/cpu-features.h"
19 #include "src/logging/counters.h"
20 #include "src/logging/tracing-flags.h"
21 #include "src/tracing/tracing-category-observer.h"
22 #include "src/utils/allocation.h"
23 #include "src/utils/memcopy.h"
24 #include "src/utils/ostreams.h"
25 #include "src/utils/utils.h"
26 
27 #if V8_ENABLE_WEBASSEMBLY
28 #include "src/wasm/wasm-limits.h"
29 #endif  // V8_ENABLE_WEBASSEMBLY
30 
31 namespace v8 {
32 namespace internal {
33 
34 // Define all of our flags.
35 #define FLAG_MODE_DEFINE
36 #include "src/flags/flag-definitions.h"  // NOLINT(build/include)
37 
38 // Define all of our flags default values.
39 #define FLAG_MODE_DEFINE_DEFAULTS
40 #include "src/flags/flag-definitions.h"  // NOLINT(build/include)
41 
42 namespace {
43 
NormalizeChar(char ch)44 char NormalizeChar(char ch) { return ch == '_' ? '-' : ch; }
45 
46 struct Flag;
47 Flag* FindFlagByPointer(const void* ptr);
48 Flag* FindFlagByName(const char* name);
49 
50 // This structure represents a single entry in the flag system, with a pointer
51 // to the actual flag, default value, comment, etc.  This is designed to be POD
52 // initialized as to avoid requiring static constructors.
53 struct Flag {
54   enum FlagType {
55     TYPE_BOOL,
56     TYPE_MAYBE_BOOL,
57     TYPE_INT,
58     TYPE_UINT,
59     TYPE_UINT64,
60     TYPE_FLOAT,
61     TYPE_SIZE_T,
62     TYPE_STRING,
63   };
64 
65   enum class SetBy { kDefault, kWeakImplication, kImplication, kCommandLine };
66 
67   FlagType type_;       // What type of flag, bool, int, or string.
68   const char* name_;    // Name of the flag, ex "my_flag".
69   void* valptr_;        // Pointer to the global flag variable.
70   const void* defptr_;  // Pointer to the default value.
71   const char* cmt_;     // A comment about the flags purpose.
72   bool owns_ptr_;       // Does the flag own its string value?
73   SetBy set_by_ = SetBy::kDefault;
74   const char* implied_by_ = nullptr;
75 
typev8::internal::__anon4507bcf00111::Flag76   FlagType type() const { return type_; }
77 
namev8::internal::__anon4507bcf00111::Flag78   const char* name() const { return name_; }
79 
commentv8::internal::__anon4507bcf00111::Flag80   const char* comment() const { return cmt_; }
81 
PointsTov8::internal::__anon4507bcf00111::Flag82   bool PointsTo(const void* ptr) const { return valptr_ == ptr; }
83 
bool_variablev8::internal::__anon4507bcf00111::Flag84   bool bool_variable() const {
85     DCHECK(type_ == TYPE_BOOL);
86     return *reinterpret_cast<bool*>(valptr_);
87   }
88 
set_bool_variablev8::internal::__anon4507bcf00111::Flag89   void set_bool_variable(bool value, SetBy set_by) {
90     DCHECK(type_ == TYPE_BOOL);
91     bool change_flag = *reinterpret_cast<bool*>(valptr_) != value;
92     change_flag = CheckFlagChange(set_by, change_flag);
93     if (change_flag) *reinterpret_cast<bool*>(valptr_) = value;
94   }
95 
maybe_bool_variablev8::internal::__anon4507bcf00111::Flag96   MaybeBoolFlag maybe_bool_variable() const {
97     DCHECK(type_ == TYPE_MAYBE_BOOL);
98     return *reinterpret_cast<MaybeBoolFlag*>(valptr_);
99   }
100 
set_maybe_bool_variablev8::internal::__anon4507bcf00111::Flag101   void set_maybe_bool_variable(MaybeBoolFlag value, SetBy set_by) {
102     DCHECK(type_ == TYPE_MAYBE_BOOL);
103     bool change_flag = *reinterpret_cast<MaybeBoolFlag*>(valptr_) != value;
104     change_flag = CheckFlagChange(set_by, change_flag);
105     if (change_flag) *reinterpret_cast<MaybeBoolFlag*>(valptr_) = value;
106   }
107 
int_variablev8::internal::__anon4507bcf00111::Flag108   int int_variable() const {
109     DCHECK(type_ == TYPE_INT);
110     return *reinterpret_cast<int*>(valptr_);
111   }
112 
set_int_variablev8::internal::__anon4507bcf00111::Flag113   void set_int_variable(int value, SetBy set_by) {
114     DCHECK(type_ == TYPE_INT);
115     bool change_flag = *reinterpret_cast<int*>(valptr_) != value;
116     change_flag = CheckFlagChange(set_by, change_flag);
117     if (change_flag) *reinterpret_cast<int*>(valptr_) = value;
118   }
119 
uint_variablev8::internal::__anon4507bcf00111::Flag120   unsigned int uint_variable() const {
121     DCHECK(type_ == TYPE_UINT);
122     return *reinterpret_cast<unsigned int*>(valptr_);
123   }
124 
set_uint_variablev8::internal::__anon4507bcf00111::Flag125   void set_uint_variable(unsigned int value, SetBy set_by) {
126     DCHECK(type_ == TYPE_UINT);
127     bool change_flag = *reinterpret_cast<unsigned int*>(valptr_) != value;
128     change_flag = CheckFlagChange(set_by, change_flag);
129     if (change_flag) *reinterpret_cast<unsigned int*>(valptr_) = value;
130   }
131 
uint64_variablev8::internal::__anon4507bcf00111::Flag132   uint64_t uint64_variable() const {
133     DCHECK(type_ == TYPE_UINT64);
134     return *reinterpret_cast<uint64_t*>(valptr_);
135   }
136 
set_uint64_variablev8::internal::__anon4507bcf00111::Flag137   void set_uint64_variable(uint64_t value, SetBy set_by) {
138     DCHECK(type_ == TYPE_UINT64);
139     bool change_flag = *reinterpret_cast<uint64_t*>(valptr_) != value;
140     change_flag = CheckFlagChange(set_by, change_flag);
141     if (change_flag) *reinterpret_cast<uint64_t*>(valptr_) = value;
142   }
143 
float_variablev8::internal::__anon4507bcf00111::Flag144   double float_variable() const {
145     DCHECK(type_ == TYPE_FLOAT);
146     return *reinterpret_cast<double*>(valptr_);
147   }
148 
set_float_variablev8::internal::__anon4507bcf00111::Flag149   void set_float_variable(double value, SetBy set_by) {
150     DCHECK(type_ == TYPE_FLOAT);
151     bool change_flag = *reinterpret_cast<double*>(valptr_) != value;
152     change_flag = CheckFlagChange(set_by, change_flag);
153     if (change_flag) *reinterpret_cast<double*>(valptr_) = value;
154   }
155 
size_t_variablev8::internal::__anon4507bcf00111::Flag156   size_t size_t_variable() const {
157     DCHECK(type_ == TYPE_SIZE_T);
158     return *reinterpret_cast<size_t*>(valptr_);
159   }
160 
set_size_t_variablev8::internal::__anon4507bcf00111::Flag161   void set_size_t_variable(size_t value, SetBy set_by) {
162     DCHECK(type_ == TYPE_SIZE_T);
163     bool change_flag = *reinterpret_cast<size_t*>(valptr_) != value;
164     change_flag = CheckFlagChange(set_by, change_flag);
165     if (change_flag) *reinterpret_cast<size_t*>(valptr_) = value;
166   }
167 
string_valuev8::internal::__anon4507bcf00111::Flag168   const char* string_value() const {
169     DCHECK(type_ == TYPE_STRING);
170     return *reinterpret_cast<const char**>(valptr_);
171   }
172 
set_string_valuev8::internal::__anon4507bcf00111::Flag173   void set_string_value(const char* value, bool owns_ptr, SetBy set_by) {
174     DCHECK(type_ == TYPE_STRING);
175     const char** ptr = reinterpret_cast<const char**>(valptr_);
176     bool change_flag = (*ptr == nullptr) != (value == nullptr) ||
177                        (*ptr && value && std::strcmp(*ptr, value) != 0);
178     change_flag = CheckFlagChange(set_by, change_flag);
179     if (change_flag) {
180       if (owns_ptr_ && *ptr != nullptr) DeleteArray(*ptr);
181       *ptr = value;
182       owns_ptr_ = owns_ptr;
183     } else {
184       if (owns_ptr && value != nullptr) DeleteArray(value);
185     }
186   }
187 
bool_defaultv8::internal::__anon4507bcf00111::Flag188   bool bool_default() const {
189     DCHECK(type_ == TYPE_BOOL);
190     return *reinterpret_cast<const bool*>(defptr_);
191   }
192 
int_defaultv8::internal::__anon4507bcf00111::Flag193   int int_default() const {
194     DCHECK(type_ == TYPE_INT);
195     return *reinterpret_cast<const int*>(defptr_);
196   }
197 
uint_defaultv8::internal::__anon4507bcf00111::Flag198   unsigned int uint_default() const {
199     DCHECK(type_ == TYPE_UINT);
200     return *reinterpret_cast<const unsigned int*>(defptr_);
201   }
202 
uint64_defaultv8::internal::__anon4507bcf00111::Flag203   uint64_t uint64_default() const {
204     DCHECK(type_ == TYPE_UINT64);
205     return *reinterpret_cast<const uint64_t*>(defptr_);
206   }
207 
float_defaultv8::internal::__anon4507bcf00111::Flag208   double float_default() const {
209     DCHECK(type_ == TYPE_FLOAT);
210     return *reinterpret_cast<const double*>(defptr_);
211   }
212 
size_t_defaultv8::internal::__anon4507bcf00111::Flag213   size_t size_t_default() const {
214     DCHECK(type_ == TYPE_SIZE_T);
215     return *reinterpret_cast<const size_t*>(defptr_);
216   }
217 
string_defaultv8::internal::__anon4507bcf00111::Flag218   const char* string_default() const {
219     DCHECK(type_ == TYPE_STRING);
220     return *reinterpret_cast<const char* const*>(defptr_);
221   }
222 
ShouldCheckFlagContradictionsv8::internal::__anon4507bcf00111::Flag223   static bool ShouldCheckFlagContradictions() {
224     if (FLAG_allow_overwriting_for_next_flag) {
225       // Setting the flag manually to false before calling Reset() avoids this
226       // becoming re-entrant.
227       FLAG_allow_overwriting_for_next_flag = false;
228       FindFlagByPointer(&FLAG_allow_overwriting_for_next_flag)->Reset();
229       return false;
230     }
231     return FLAG_abort_on_contradictory_flags && !FLAG_fuzzing;
232   }
233 
234   // {change_flag} indicates if we're going to change the flag value.
235   // Returns an updated value for {change_flag}, which is changed to false if a
236   // weak implication is being ignored beause a flag is already set by a normal
237   // implication or from the command-line.
CheckFlagChangev8::internal::__anon4507bcf00111::Flag238   bool CheckFlagChange(SetBy new_set_by, bool change_flag,
239                        const char* implied_by = nullptr) {
240     if (new_set_by == SetBy::kWeakImplication &&
241         (set_by_ == SetBy::kImplication || set_by_ == SetBy::kCommandLine)) {
242       return false;
243     }
244     if (ShouldCheckFlagContradictions()) {
245       // For bool flags, we only check for a conflict if the value actually
246       // changes. So specifying the same flag with the same value multiple times
247       // is allowed.
248       // For other flags, we disallow specifying them explicitly or in the
249       // presence of an implication even if the value is the same.
250       // This is to simplify the rules describing conflicts in variants.py: A
251       // repeated non-boolean flag is considered an error independently of its
252       // value.
253       bool is_bool_flag = type_ == TYPE_MAYBE_BOOL || type_ == TYPE_BOOL;
254       bool check_implications = change_flag;
255       bool check_command_line_flags = change_flag || !is_bool_flag;
256       const char* hint =
257           "To fix this, it might be necessary to specify additional "
258           "contradictory flags in tools/testrunner/local/variants.py.";
259       switch (set_by_) {
260         case SetBy::kDefault:
261           break;
262         case SetBy::kWeakImplication:
263           if (new_set_by == SetBy::kWeakImplication && check_implications) {
264             FATAL(
265                 "Contradictory weak flag implications from --%s and --%s for "
266                 "flag %s\n%s",
267                 implied_by_, implied_by, name(), hint);
268           }
269           break;
270         case SetBy::kImplication:
271           if (new_set_by == SetBy::kImplication && check_implications) {
272             FATAL(
273                 "Contradictory flag implications from --%s and --%s for flag "
274                 "%s\n%s",
275                 implied_by_, implied_by, name(), hint);
276           }
277           break;
278         case SetBy::kCommandLine:
279           if (new_set_by == SetBy::kImplication && check_command_line_flags) {
280             if (is_bool_flag) {
281               FATAL(
282                   "Flag --%s: value implied by --%s conflicts with explicit "
283                   "specification\n%s",
284                   name(), implied_by, hint);
285             } else {
286               FATAL(
287                   "Flag --%s is implied by --%s but also specified "
288                   "explicitly.\n%s",
289                   name(), implied_by, hint);
290             }
291           } else if (new_set_by == SetBy::kCommandLine &&
292                      check_command_line_flags) {
293             if (is_bool_flag) {
294               FATAL(
295                   "Command-line provided flag --%s specified as both true and "
296                   "false.\n%s",
297                   name(), hint);
298             } else {
299               FATAL(
300                   "Command-line provided flag --%s specified multiple "
301                   "times.\n%s",
302                   name(), hint);
303             }
304           }
305           break;
306       }
307     }
308     set_by_ = new_set_by;
309     if (new_set_by == SetBy::kImplication ||
310         new_set_by == SetBy::kWeakImplication) {
311       DCHECK_NOT_NULL(implied_by);
312       implied_by_ = implied_by;
313     }
314     return change_flag;
315   }
316 
317   // Compare this flag's current value against the default.
IsDefaultv8::internal::__anon4507bcf00111::Flag318   bool IsDefault() const {
319     switch (type_) {
320       case TYPE_BOOL:
321         return bool_variable() == bool_default();
322       case TYPE_MAYBE_BOOL:
323         return maybe_bool_variable().has_value == false;
324       case TYPE_INT:
325         return int_variable() == int_default();
326       case TYPE_UINT:
327         return uint_variable() == uint_default();
328       case TYPE_UINT64:
329         return uint64_variable() == uint64_default();
330       case TYPE_FLOAT:
331         return float_variable() == float_default();
332       case TYPE_SIZE_T:
333         return size_t_variable() == size_t_default();
334       case TYPE_STRING: {
335         const char* str1 = string_value();
336         const char* str2 = string_default();
337         if (str2 == nullptr) return str1 == nullptr;
338         if (str1 == nullptr) return str2 == nullptr;
339         return strcmp(str1, str2) == 0;
340       }
341     }
342     UNREACHABLE();
343   }
344 
345   // Set a flag back to it's default value.
Resetv8::internal::__anon4507bcf00111::Flag346   void Reset() {
347     switch (type_) {
348       case TYPE_BOOL:
349         set_bool_variable(bool_default(), SetBy::kDefault);
350         break;
351       case TYPE_MAYBE_BOOL:
352         set_maybe_bool_variable(MaybeBoolFlag::Create(false, false),
353                                 SetBy::kDefault);
354         break;
355       case TYPE_INT:
356         set_int_variable(int_default(), SetBy::kDefault);
357         break;
358       case TYPE_UINT:
359         set_uint_variable(uint_default(), SetBy::kDefault);
360         break;
361       case TYPE_UINT64:
362         set_uint64_variable(uint64_default(), SetBy::kDefault);
363         break;
364       case TYPE_FLOAT:
365         set_float_variable(float_default(), SetBy::kDefault);
366         break;
367       case TYPE_SIZE_T:
368         set_size_t_variable(size_t_default(), SetBy::kDefault);
369         break;
370       case TYPE_STRING:
371         set_string_value(string_default(), false, SetBy::kDefault);
372         break;
373     }
374   }
375 
AllowOverwritingv8::internal::__anon4507bcf00111::Flag376   void AllowOverwriting() { set_by_ = SetBy::kDefault; }
377 };
378 
379 Flag flags[] = {
380 #define FLAG_MODE_META
381 #include "src/flags/flag-definitions.h"  // NOLINT(build/include)
382 };
383 
384 const size_t num_flags = sizeof(flags) / sizeof(*flags);
385 
EqualNames(const char * a,const char * b)386 bool EqualNames(const char* a, const char* b) {
387   for (int i = 0; NormalizeChar(a[i]) == NormalizeChar(b[i]); i++) {
388     if (a[i] == '\0') {
389       return true;
390     }
391   }
392   return false;
393 }
394 
FindFlagByName(const char * name)395 Flag* FindFlagByName(const char* name) {
396   for (size_t i = 0; i < num_flags; ++i) {
397     if (EqualNames(name, flags[i].name())) return &flags[i];
398   }
399   return nullptr;
400 }
401 
FindFlagByPointer(const void * ptr)402 Flag* FindFlagByPointer(const void* ptr) {
403   for (size_t i = 0; i < num_flags; ++i) {
404     if (flags[i].PointsTo(ptr)) return &flags[i];
405   }
406   return nullptr;
407 }
408 
409 }  // namespace
410 
Type2String(Flag::FlagType type)411 static const char* Type2String(Flag::FlagType type) {
412   switch (type) {
413     case Flag::TYPE_BOOL:
414       return "bool";
415     case Flag::TYPE_MAYBE_BOOL:
416       return "maybe_bool";
417     case Flag::TYPE_INT:
418       return "int";
419     case Flag::TYPE_UINT:
420       return "uint";
421     case Flag::TYPE_UINT64:
422       return "uint64";
423     case Flag::TYPE_FLOAT:
424       return "float";
425     case Flag::TYPE_SIZE_T:
426       return "size_t";
427     case Flag::TYPE_STRING:
428       return "string";
429   }
430   UNREACHABLE();
431 }
432 
433 // Helper struct for printing normalize Flag names.
434 struct FlagName {
FlagNamev8::internal::FlagName435   explicit FlagName(const Flag& flag) : flag(flag) {}
436   const Flag& flag;
437 };
438 
operator <<(std::ostream & os,const FlagName & flag_name)439 std::ostream& operator<<(std::ostream& os, const FlagName& flag_name) {
440   for (const char* c = flag_name.flag.name(); *c != '\0'; ++c) {
441     os << NormalizeChar(*c);
442   }
443   return os;
444 }
445 
446 // Helper for printing flag values.
447 struct FlagValue {
FlagValuev8::internal::FlagValue448   explicit FlagValue(const Flag& flag) : flag(flag) {}
449   const Flag& flag;
450 };
451 
operator <<(std::ostream & os,const FlagValue & flag_value)452 std::ostream& operator<<(std::ostream& os, const FlagValue& flag_value) {
453   const Flag& flag = flag_value.flag;
454   switch (flag.type()) {
455     case Flag::TYPE_BOOL:
456       os << (flag.bool_variable() ? "true" : "false");
457       break;
458     case Flag::TYPE_MAYBE_BOOL:
459       os << (flag.maybe_bool_variable().has_value
460                  ? (flag.maybe_bool_variable().value ? "true" : "false")
461                  : "unset");
462       break;
463     case Flag::TYPE_INT:
464       os << flag.int_variable();
465       break;
466     case Flag::TYPE_UINT:
467       os << flag.uint_variable();
468       break;
469     case Flag::TYPE_UINT64:
470       os << flag.uint64_variable();
471       break;
472     case Flag::TYPE_FLOAT:
473       os << flag.float_variable();
474       break;
475     case Flag::TYPE_SIZE_T:
476       os << flag.size_t_variable();
477       break;
478     case Flag::TYPE_STRING: {
479       const char* str = flag.string_value();
480       os << std::quoted(str ? str : "");
481       break;
482     }
483   }
484   return os;
485 }
486 
operator <<(std::ostream & os,const Flag & flag)487 std::ostream& operator<<(std::ostream& os, const Flag& flag) {
488   if (flag.type() == Flag::TYPE_BOOL) {
489     os << (flag.bool_variable() ? "--" : "--no") << FlagName(flag);
490   } else {
491     os << "--" << FlagName(flag) << "=" << FlagValue(flag);
492   }
493   return os;
494 }
495 
496 namespace {
497 
498 static std::atomic<uint32_t> flag_hash(0);
499 
ComputeFlagListHash()500 void ComputeFlagListHash() {
501   std::ostringstream modified_args_as_string;
502   if (COMPRESS_POINTERS_BOOL) modified_args_as_string << "ptr-compr";
503   if (DEBUG_BOOL) modified_args_as_string << "debug";
504   for (const Flag& flag : flags) {
505     if (flag.IsDefault()) continue;
506     // We want to be able to flip --profile-deserialization without
507     // causing the code cache to get invalidated by this hash.
508     if (flag.PointsTo(&FLAG_profile_deserialization)) continue;
509     // Skip FLAG_random_seed to allow predictable code caching.
510     if (flag.PointsTo(&FLAG_random_seed)) continue;
511     modified_args_as_string << flag;
512   }
513   std::string args(modified_args_as_string.str());
514   // Generate a hash that is not 0.
515   uint32_t hash = static_cast<uint32_t>(base::hash_range(
516                       args.c_str(), args.c_str() + args.length())) |
517                   1;
518   DCHECK_NE(hash, 0);
519   flag_hash.store(hash, std::memory_order_relaxed);
520 }
521 
522 }  // namespace
523 
524 // Helper function to parse flags: Takes an argument arg and splits it into
525 // a flag name and flag value (or nullptr if they are missing). negated is set
526 // if the arg started with "-no" or "--no". The buffer may be used to NUL-
527 // terminate the name, it must be large enough to hold any possible name.
SplitArgument(const char * arg,char * buffer,int buffer_size,const char ** name,const char ** value,bool * negated)528 static void SplitArgument(const char* arg, char* buffer, int buffer_size,
529                           const char** name, const char** value,
530                           bool* negated) {
531   *name = nullptr;
532   *value = nullptr;
533   *negated = false;
534 
535   if (arg != nullptr && *arg == '-') {
536     // find the begin of the flag name
537     arg++;  // remove 1st '-'
538     if (*arg == '-') {
539       arg++;                    // remove 2nd '-'
540       DCHECK_NE('\0', arg[0]);  // '--' arguments are handled in the caller.
541     }
542     if (arg[0] == 'n' && arg[1] == 'o') {
543       arg += 2;                                 // remove "no"
544       if (NormalizeChar(arg[0]) == '-') arg++;  // remove dash after "no".
545       *negated = true;
546     }
547     *name = arg;
548 
549     // find the end of the flag name
550     while (*arg != '\0' && *arg != '=') arg++;
551 
552     // get the value if any
553     if (*arg == '=') {
554       // make a copy so we can NUL-terminate flag name
555       size_t n = arg - *name;
556       CHECK(n < static_cast<size_t>(buffer_size));  // buffer is too small
557       MemCopy(buffer, *name, n);
558       buffer[n] = '\0';
559       *name = buffer;
560       // get the value
561       *value = arg + 1;
562     }
563   }
564 }
565 
566 template <typename T>
TryParseUnsigned(Flag * flag,const char * arg,const char * value,char ** endp,T * out_val)567 bool TryParseUnsigned(Flag* flag, const char* arg, const char* value,
568                       char** endp, T* out_val) {
569   // We do not use strtoul because it accepts negative numbers.
570   // Rejects values >= 2**63 when T is 64 bits wide but that
571   // seems like an acceptable trade-off.
572   uint64_t max = static_cast<uint64_t>(std::numeric_limits<T>::max());
573   errno = 0;
574   int64_t val = static_cast<int64_t>(strtoll(value, endp, 10));
575   if (val < 0 || static_cast<uint64_t>(val) > max || errno != 0) {
576     PrintF(stderr,
577            "Error: Value for flag %s of type %s is out of bounds "
578            "[0-%" PRIu64 "]\n",
579            arg, Type2String(flag->type()), max);
580     return false;
581   }
582   *out_val = static_cast<T>(val);
583   return true;
584 }
585 
586 // static
SetFlagsFromCommandLine(int * argc,char ** argv,bool remove_flags,HelpOptions help_options)587 int FlagList::SetFlagsFromCommandLine(int* argc, char** argv, bool remove_flags,
588                                       HelpOptions help_options) {
589   int return_code = 0;
590   // parse arguments
591   for (int i = 1; i < *argc;) {
592     int j = i;  // j > 0
593     const char* arg = argv[i++];
594 
595     // split arg into flag components
596     char buffer[1 * KB];
597     const char* name;
598     const char* value;
599     bool negated;
600     SplitArgument(arg, buffer, sizeof buffer, &name, &value, &negated);
601 
602     if (name != nullptr) {
603       // lookup the flag
604       Flag* flag = FindFlagByName(name);
605       if (flag == nullptr) {
606         if (remove_flags) {
607           // We don't recognize this flag but since we're removing
608           // the flags we recognize we assume that the remaining flags
609           // will be processed somewhere else so this flag might make
610           // sense there.
611           continue;
612         } else {
613           PrintF(stderr, "Error: unrecognized flag %s\n", arg);
614           return_code = j;
615           break;
616         }
617       }
618 
619       // if we still need a flag value, use the next argument if available
620       if (flag->type() != Flag::TYPE_BOOL &&
621           flag->type() != Flag::TYPE_MAYBE_BOOL && value == nullptr) {
622         if (i < *argc) {
623           value = argv[i++];
624         }
625         if (!value) {
626           PrintF(stderr, "Error: missing value for flag %s of type %s\n", arg,
627                  Type2String(flag->type()));
628           return_code = j;
629           break;
630         }
631       }
632 
633       // set the flag
634       char* endp = const_cast<char*>("");  // *endp is only read
635       switch (flag->type()) {
636         case Flag::TYPE_BOOL:
637           flag->set_bool_variable(!negated, Flag::SetBy::kCommandLine);
638           break;
639         case Flag::TYPE_MAYBE_BOOL:
640           flag->set_maybe_bool_variable(MaybeBoolFlag::Create(true, !negated),
641                                         Flag::SetBy::kCommandLine);
642           break;
643         case Flag::TYPE_INT:
644           flag->set_int_variable(static_cast<int>(strtol(value, &endp, 10)),
645                                  Flag::SetBy::kCommandLine);
646           break;
647         case Flag::TYPE_UINT: {
648           unsigned int parsed_value;
649           if (TryParseUnsigned(flag, arg, value, &endp, &parsed_value)) {
650             flag->set_uint_variable(parsed_value, Flag::SetBy::kCommandLine);
651           } else {
652             return_code = j;
653           }
654           break;
655         }
656         case Flag::TYPE_UINT64: {
657           uint64_t parsed_value;
658           if (TryParseUnsigned(flag, arg, value, &endp, &parsed_value)) {
659             flag->set_uint64_variable(parsed_value, Flag::SetBy::kCommandLine);
660           } else {
661             return_code = j;
662           }
663           break;
664         }
665         case Flag::TYPE_FLOAT:
666           flag->set_float_variable(strtod(value, &endp),
667                                    Flag::SetBy::kCommandLine);
668           break;
669         case Flag::TYPE_SIZE_T: {
670           size_t parsed_value;
671           if (TryParseUnsigned(flag, arg, value, &endp, &parsed_value)) {
672             flag->set_size_t_variable(parsed_value, Flag::SetBy::kCommandLine);
673           } else {
674             return_code = j;
675           }
676           break;
677         }
678         case Flag::TYPE_STRING:
679           flag->set_string_value(value ? StrDup(value) : nullptr, true,
680                                  Flag::SetBy::kCommandLine);
681           break;
682       }
683 
684       // handle errors
685       bool is_bool_type = flag->type() == Flag::TYPE_BOOL ||
686                           flag->type() == Flag::TYPE_MAYBE_BOOL;
687       if ((is_bool_type && value != nullptr) || (!is_bool_type && negated) ||
688           *endp != '\0') {
689         // TODO(neis): TryParseUnsigned may return with {*endp == '\0'} even in
690         // an error case.
691         PrintF(stderr, "Error: illegal value for flag %s of type %s\n", arg,
692                Type2String(flag->type()));
693         if (is_bool_type) {
694           PrintF(stderr,
695                  "To set or unset a boolean flag, use --flag or --no-flag.\n");
696         }
697         return_code = j;
698         break;
699       }
700 
701       // remove the flag & value from the command
702       if (remove_flags) {
703         while (j < i) {
704           argv[j++] = nullptr;
705         }
706       }
707     }
708   }
709 
710   if (FLAG_help) {
711     if (help_options.HasUsage()) {
712       PrintF(stdout, "%s", help_options.usage());
713     }
714     PrintHelp();
715     if (help_options.ShouldExit()) {
716       exit(0);
717     }
718   }
719 
720   if (remove_flags) {
721     // shrink the argument list
722     int j = 1;
723     for (int i = 1; i < *argc; i++) {
724       if (argv[i] != nullptr) argv[j++] = argv[i];
725     }
726     *argc = j;
727   } else if (return_code != 0) {
728     if (return_code + 1 < *argc) {
729       PrintF(stderr, "The remaining arguments were ignored:");
730       for (int i = return_code + 1; i < *argc; ++i) {
731         PrintF(stderr, " %s", argv[i]);
732       }
733       PrintF(stderr, "\n");
734     }
735   }
736   if (return_code != 0) PrintF(stderr, "Try --help for options\n");
737 
738   return return_code;
739 }
740 
SkipWhiteSpace(char * p)741 static char* SkipWhiteSpace(char* p) {
742   while (*p != '\0' && isspace(*p) != 0) p++;
743   return p;
744 }
745 
SkipBlackSpace(char * p)746 static char* SkipBlackSpace(char* p) {
747   while (*p != '\0' && isspace(*p) == 0) p++;
748   return p;
749 }
750 
751 // static
SetFlagsFromString(const char * str,size_t len)752 int FlagList::SetFlagsFromString(const char* str, size_t len) {
753   // make a 0-terminated copy of str
754   std::unique_ptr<char[]> copy0{NewArray<char>(len + 1)};
755   MemCopy(copy0.get(), str, len);
756   copy0[len] = '\0';
757 
758   // strip leading white space
759   char* copy = SkipWhiteSpace(copy0.get());
760 
761   // count the number of 'arguments'
762   int argc = 1;  // be compatible with SetFlagsFromCommandLine()
763   for (char* p = copy; *p != '\0'; argc++) {
764     p = SkipBlackSpace(p);
765     p = SkipWhiteSpace(p);
766   }
767 
768   // allocate argument array
769   base::ScopedVector<char*> argv(argc);
770 
771   // split the flags string into arguments
772   argc = 1;  // be compatible with SetFlagsFromCommandLine()
773   for (char* p = copy; *p != '\0'; argc++) {
774     argv[argc] = p;
775     p = SkipBlackSpace(p);
776     if (*p != '\0') *p++ = '\0';  // 0-terminate argument
777     p = SkipWhiteSpace(p);
778   }
779 
780   return SetFlagsFromCommandLine(&argc, argv.begin(), false);
781 }
782 
783 // static
ResetAllFlags()784 void FlagList::ResetAllFlags() {
785   flag_hash = 0;
786   for (size_t i = 0; i < num_flags; ++i) {
787     flags[i].Reset();
788   }
789 }
790 
791 // static
PrintHelp()792 void FlagList::PrintHelp() {
793   CpuFeatures::Probe(false);
794   CpuFeatures::PrintTarget();
795   CpuFeatures::PrintFeatures();
796 
797   StdoutStream os;
798   os << "The following syntax for options is accepted (both '-' and '--' are "
799         "ok):\n"
800         "  --flag        (bool flags only)\n"
801         "  --no-flag     (bool flags only)\n"
802         "  --flag=value  (non-bool flags only, no spaces around '=')\n"
803         "  --flag value  (non-bool flags only)\n"
804         "  --            (captures all remaining args in JavaScript)\n\n";
805   os << "Options:\n";
806 
807   for (const Flag& f : flags) {
808     os << "  --" << FlagName(f) << " (" << f.comment() << ")\n"
809        << "        type: " << Type2String(f.type()) << "  default: " << f
810        << "\n";
811   }
812 }
813 
814 // static
PrintValues()815 void FlagList::PrintValues() {
816   StdoutStream os;
817   for (const Flag& f : flags) {
818     os << f << "\n";
819   }
820 }
821 
822 namespace {
823 
824 template <class A, class B>
TriggerImplication(bool premise,const char * premise_name,A * conclusion_pointer,B value,bool weak_implication)825 bool TriggerImplication(bool premise, const char* premise_name,
826                         A* conclusion_pointer, B value, bool weak_implication) {
827   if (!premise) return false;
828   bool change_flag = *conclusion_pointer != implicit_cast<A>(value);
829   Flag* conclusion_flag = FindFlagByPointer(conclusion_pointer);
830   change_flag = conclusion_flag->CheckFlagChange(
831       weak_implication ? Flag::SetBy::kWeakImplication
832                        : Flag::SetBy::kImplication,
833       change_flag, premise_name);
834   if (change_flag) *conclusion_pointer = value;
835   return change_flag;
836 }
837 
838 }  // namespace
839 
840 // static
EnforceFlagImplications()841 void FlagList::EnforceFlagImplications() {
842   flag_hash = 0;
843   bool changed;
844   do {
845     changed = false;
846 #define FLAG_MODE_DEFINE_IMPLICATIONS
847 #include "src/flags/flag-definitions.h"  // NOLINT(build/include)
848 #undef FLAG_MODE_DEFINE_IMPLICATIONS
849   } while (changed);
850 }
851 
852 // static
Hash()853 uint32_t FlagList::Hash() {
854   if (flag_hash.load() == 0) ComputeFlagListHash();
855   return flag_hash.load();
856 }
857 
858 #undef FLAG_MODE_DEFINE
859 #undef FLAG_MODE_DEFINE_DEFAULTS
860 #undef FLAG_MODE_META
861 
862 }  // namespace internal
863 }  // namespace v8
864