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