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