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.h" 6 7 #include <cctype> 8 #include <cerrno> 9 #include <cstdlib> 10 #include <sstream> 11 12 #include "src/allocation.h" 13 #include "src/assembler.h" 14 #include "src/base/functional.h" 15 #include "src/base/platform/platform.h" 16 #include "src/ostreams.h" 17 #include "src/utils.h" 18 #include "src/wasm/wasm-limits.h" 19 20 namespace v8 { 21 namespace internal { 22 23 // Define all of our flags. 24 #define FLAG_MODE_DEFINE 25 #include "src/flag-definitions.h" // NOLINT(build/include) 26 27 // Define all of our flags default values. 28 #define FLAG_MODE_DEFINE_DEFAULTS 29 #include "src/flag-definitions.h" // NOLINT(build/include) 30 31 namespace { 32 33 // This structure represents a single entry in the flag system, with a pointer 34 // to the actual flag, default value, comment, etc. This is designed to be POD 35 // initialized as to avoid requiring static constructors. 36 struct Flag { 37 enum FlagType { 38 TYPE_BOOL, 39 TYPE_MAYBE_BOOL, 40 TYPE_INT, 41 TYPE_UINT, 42 TYPE_UINT64, 43 TYPE_FLOAT, 44 TYPE_SIZE_T, 45 TYPE_STRING, 46 TYPE_ARGS 47 }; 48 49 FlagType type_; // What type of flag, bool, int, or string. 50 const char* name_; // Name of the flag, ex "my_flag". 51 void* valptr_; // Pointer to the global flag variable. 52 const void* defptr_; // Pointer to the default value. 53 const char* cmt_; // A comment about the flags purpose. 54 bool owns_ptr_; // Does the flag own its string value? 55 56 FlagType type() const { return type_; } 57 58 const char* name() const { return name_; } 59 60 const char* comment() const { return cmt_; } 61 62 bool* bool_variable() const { 63 DCHECK(type_ == TYPE_BOOL); 64 return reinterpret_cast<bool*>(valptr_); 65 } 66 67 MaybeBoolFlag* maybe_bool_variable() const { 68 DCHECK(type_ == TYPE_MAYBE_BOOL); 69 return reinterpret_cast<MaybeBoolFlag*>(valptr_); 70 } 71 72 int* int_variable() const { 73 DCHECK(type_ == TYPE_INT); 74 return reinterpret_cast<int*>(valptr_); 75 } 76 77 unsigned int* uint_variable() const { 78 DCHECK(type_ == TYPE_UINT); 79 return reinterpret_cast<unsigned int*>(valptr_); 80 } 81 82 uint64_t* uint64_variable() const { 83 DCHECK(type_ == TYPE_UINT64); 84 return reinterpret_cast<uint64_t*>(valptr_); 85 } 86 87 double* float_variable() const { 88 DCHECK(type_ == TYPE_FLOAT); 89 return reinterpret_cast<double*>(valptr_); 90 } 91 92 size_t* size_t_variable() const { 93 DCHECK(type_ == TYPE_SIZE_T); 94 return reinterpret_cast<size_t*>(valptr_); 95 } 96 97 const char* string_value() const { 98 DCHECK(type_ == TYPE_STRING); 99 return *reinterpret_cast<const char**>(valptr_); 100 } 101 102 void set_string_value(const char* value, bool owns_ptr) { 103 DCHECK(type_ == TYPE_STRING); 104 const char** ptr = reinterpret_cast<const char**>(valptr_); 105 if (owns_ptr_ && *ptr != nullptr) DeleteArray(*ptr); 106 *ptr = value; 107 owns_ptr_ = owns_ptr; 108 } 109 110 JSArguments* args_variable() const { 111 DCHECK(type_ == TYPE_ARGS); 112 return reinterpret_cast<JSArguments*>(valptr_); 113 } 114 115 bool bool_default() const { 116 DCHECK(type_ == TYPE_BOOL); 117 return *reinterpret_cast<const bool*>(defptr_); 118 } 119 120 int int_default() const { 121 DCHECK(type_ == TYPE_INT); 122 return *reinterpret_cast<const int*>(defptr_); 123 } 124 125 unsigned int uint_default() const { 126 DCHECK(type_ == TYPE_UINT); 127 return *reinterpret_cast<const unsigned int*>(defptr_); 128 } 129 130 uint64_t uint64_default() const { 131 DCHECK(type_ == TYPE_UINT64); 132 return *reinterpret_cast<const uint64_t*>(defptr_); 133 } 134 135 double float_default() const { 136 DCHECK(type_ == TYPE_FLOAT); 137 return *reinterpret_cast<const double*>(defptr_); 138 } 139 140 size_t size_t_default() const { 141 DCHECK(type_ == TYPE_SIZE_T); 142 return *reinterpret_cast<const size_t*>(defptr_); 143 } 144 145 const char* string_default() const { 146 DCHECK(type_ == TYPE_STRING); 147 return *reinterpret_cast<const char* const *>(defptr_); 148 } 149 150 JSArguments args_default() const { 151 DCHECK(type_ == TYPE_ARGS); 152 return *reinterpret_cast<const JSArguments*>(defptr_); 153 } 154 155 // Compare this flag's current value against the default. 156 bool IsDefault() const { 157 switch (type_) { 158 case TYPE_BOOL: 159 return *bool_variable() == bool_default(); 160 case TYPE_MAYBE_BOOL: 161 return maybe_bool_variable()->has_value == false; 162 case TYPE_INT: 163 return *int_variable() == int_default(); 164 case TYPE_UINT: 165 return *uint_variable() == uint_default(); 166 case TYPE_UINT64: 167 return *uint64_variable() == uint64_default(); 168 case TYPE_FLOAT: 169 return *float_variable() == float_default(); 170 case TYPE_SIZE_T: 171 return *size_t_variable() == size_t_default(); 172 case TYPE_STRING: { 173 const char* str1 = string_value(); 174 const char* str2 = string_default(); 175 if (str2 == nullptr) return str1 == nullptr; 176 if (str1 == nullptr) return str2 == nullptr; 177 return strcmp(str1, str2) == 0; 178 } 179 case TYPE_ARGS: 180 return args_variable()->argc == 0; 181 } 182 UNREACHABLE(); 183 } 184 185 // Set a flag back to it's default value. 186 void Reset() { 187 switch (type_) { 188 case TYPE_BOOL: 189 *bool_variable() = bool_default(); 190 break; 191 case TYPE_MAYBE_BOOL: 192 *maybe_bool_variable() = MaybeBoolFlag::Create(false, false); 193 break; 194 case TYPE_INT: 195 *int_variable() = int_default(); 196 break; 197 case TYPE_UINT: 198 *uint_variable() = uint_default(); 199 break; 200 case TYPE_UINT64: 201 *uint64_variable() = uint64_default(); 202 break; 203 case TYPE_FLOAT: 204 *float_variable() = float_default(); 205 break; 206 case TYPE_SIZE_T: 207 *size_t_variable() = size_t_default(); 208 break; 209 case TYPE_STRING: 210 set_string_value(string_default(), false); 211 break; 212 case TYPE_ARGS: 213 *args_variable() = args_default(); 214 break; 215 } 216 } 217 }; 218 219 Flag flags[] = { 220 #define FLAG_MODE_META 221 #include "src/flag-definitions.h" // NOLINT(build/include) 222 }; 223 224 const size_t num_flags = sizeof(flags) / sizeof(*flags); 225 226 } // namespace 227 228 229 static const char* Type2String(Flag::FlagType type) { 230 switch (type) { 231 case Flag::TYPE_BOOL: return "bool"; 232 case Flag::TYPE_MAYBE_BOOL: return "maybe_bool"; 233 case Flag::TYPE_INT: return "int"; 234 case Flag::TYPE_UINT: 235 return "uint"; 236 case Flag::TYPE_UINT64: 237 return "uint64"; 238 case Flag::TYPE_FLOAT: return "float"; 239 case Flag::TYPE_SIZE_T: 240 return "size_t"; 241 case Flag::TYPE_STRING: return "string"; 242 case Flag::TYPE_ARGS: return "arguments"; 243 } 244 UNREACHABLE(); 245 } 246 247 248 std::ostream& operator<<(std::ostream& os, const Flag& flag) { // NOLINT 249 switch (flag.type()) { 250 case Flag::TYPE_BOOL: 251 os << (*flag.bool_variable() ? "true" : "false"); 252 break; 253 case Flag::TYPE_MAYBE_BOOL: 254 os << (flag.maybe_bool_variable()->has_value 255 ? (flag.maybe_bool_variable()->value ? "true" : "false") 256 : "unset"); 257 break; 258 case Flag::TYPE_INT: 259 os << *flag.int_variable(); 260 break; 261 case Flag::TYPE_UINT: 262 os << *flag.uint_variable(); 263 break; 264 case Flag::TYPE_UINT64: 265 os << *flag.uint64_variable(); 266 break; 267 case Flag::TYPE_FLOAT: 268 os << *flag.float_variable(); 269 break; 270 case Flag::TYPE_SIZE_T: 271 os << *flag.size_t_variable(); 272 break; 273 case Flag::TYPE_STRING: { 274 const char* str = flag.string_value(); 275 os << (str ? str : "nullptr"); 276 break; 277 } 278 case Flag::TYPE_ARGS: { 279 JSArguments args = *flag.args_variable(); 280 if (args.argc > 0) { 281 os << args[0]; 282 for (int i = 1; i < args.argc; i++) { 283 os << args[i]; 284 } 285 } 286 break; 287 } 288 } 289 return os; 290 } 291 292 293 // static 294 std::vector<const char*>* FlagList::argv() { 295 std::vector<const char*>* args = new std::vector<const char*>(8); 296 Flag* args_flag = nullptr; 297 for (size_t i = 0; i < num_flags; ++i) { 298 Flag* f = &flags[i]; 299 if (!f->IsDefault()) { 300 if (f->type() == Flag::TYPE_ARGS) { 301 DCHECK_NULL(args_flag); 302 args_flag = f; // Must be last in arguments. 303 continue; 304 } 305 { 306 bool disabled = f->type() == Flag::TYPE_BOOL && !*f->bool_variable(); 307 std::ostringstream os; 308 os << (disabled ? "--no" : "--") << f->name(); 309 args->push_back(StrDup(os.str().c_str())); 310 } 311 if (f->type() != Flag::TYPE_BOOL) { 312 std::ostringstream os; 313 os << *f; 314 args->push_back(StrDup(os.str().c_str())); 315 } 316 } 317 } 318 if (args_flag != nullptr) { 319 std::ostringstream os; 320 os << "--" << args_flag->name(); 321 args->push_back(StrDup(os.str().c_str())); 322 JSArguments jsargs = *args_flag->args_variable(); 323 for (int j = 0; j < jsargs.argc; j++) { 324 args->push_back(StrDup(jsargs[j])); 325 } 326 } 327 return args; 328 } 329 330 331 inline char NormalizeChar(char ch) { 332 return ch == '_' ? '-' : ch; 333 } 334 335 // Helper function to parse flags: Takes an argument arg and splits it into 336 // a flag name and flag value (or nullptr if they are missing). negated is set 337 // if the arg started with "-no" or "--no". The buffer may be used to NUL- 338 // terminate the name, it must be large enough to hold any possible name. 339 static void SplitArgument(const char* arg, char* buffer, int buffer_size, 340 const char** name, const char** value, 341 bool* negated) { 342 *name = nullptr; 343 *value = nullptr; 344 *negated = false; 345 346 if (arg != nullptr && *arg == '-') { 347 // find the begin of the flag name 348 arg++; // remove 1st '-' 349 if (*arg == '-') { 350 arg++; // remove 2nd '-' 351 if (arg[0] == '\0') { 352 const char* kJSArgumentsFlagName = "js_arguments"; 353 *name = kJSArgumentsFlagName; 354 return; 355 } 356 } 357 if (arg[0] == 'n' && arg[1] == 'o') { 358 arg += 2; // remove "no" 359 if (NormalizeChar(arg[0]) == '-') arg++; // remove dash after "no". 360 *negated = true; 361 } 362 *name = arg; 363 364 // find the end of the flag name 365 while (*arg != '\0' && *arg != '=') 366 arg++; 367 368 // get the value if any 369 if (*arg == '=') { 370 // make a copy so we can NUL-terminate flag name 371 size_t n = arg - *name; 372 CHECK(n < static_cast<size_t>(buffer_size)); // buffer is too small 373 MemCopy(buffer, *name, n); 374 buffer[n] = '\0'; 375 *name = buffer; 376 // get the value 377 *value = arg + 1; 378 } 379 } 380 } 381 382 383 static bool EqualNames(const char* a, const char* b) { 384 for (int i = 0; NormalizeChar(a[i]) == NormalizeChar(b[i]); i++) { 385 if (a[i] == '\0') { 386 return true; 387 } 388 } 389 return false; 390 } 391 392 393 static Flag* FindFlag(const char* name) { 394 for (size_t i = 0; i < num_flags; ++i) { 395 if (EqualNames(name, flags[i].name())) 396 return &flags[i]; 397 } 398 return nullptr; 399 } 400 401 template <typename T> 402 bool TryParseUnsigned(Flag* flag, const char* arg, const char* value, 403 char** endp, T* out_val) { 404 // We do not use strtoul because it accepts negative numbers. 405 // Rejects values >= 2**63 when T is 64 bits wide but that 406 // seems like an acceptable trade-off. 407 uint64_t max = static_cast<uint64_t>(std::numeric_limits<T>::max()); 408 errno = 0; 409 int64_t val = static_cast<int64_t>(strtoll(value, endp, 10)); 410 if (val < 0 || static_cast<uint64_t>(val) > max || errno != 0) { 411 PrintF(stderr, 412 "Error: Value for flag %s of type %s is out of bounds " 413 "[0-%" PRIu64 "]\n", 414 arg, Type2String(flag->type()), max); 415 return false; 416 } 417 *out_val = static_cast<T>(val); 418 return true; 419 } 420 421 // static 422 int FlagList::SetFlagsFromCommandLine(int* argc, 423 char** argv, 424 bool remove_flags) { 425 int return_code = 0; 426 // parse arguments 427 for (int i = 1; i < *argc;) { 428 int j = i; // j > 0 429 const char* arg = argv[i++]; 430 431 // split arg into flag components 432 char buffer[1*KB]; 433 const char* name; 434 const char* value; 435 bool negated; 436 SplitArgument(arg, buffer, sizeof buffer, &name, &value, &negated); 437 438 if (name != nullptr) { 439 // lookup the flag 440 Flag* flag = FindFlag(name); 441 if (flag == nullptr) { 442 if (remove_flags) { 443 // We don't recognize this flag but since we're removing 444 // the flags we recognize we assume that the remaining flags 445 // will be processed somewhere else so this flag might make 446 // sense there. 447 continue; 448 } else { 449 PrintF(stderr, "Error: unrecognized flag %s\n", arg); 450 return_code = j; 451 break; 452 } 453 } 454 455 // if we still need a flag value, use the next argument if available 456 if (flag->type() != Flag::TYPE_BOOL && 457 flag->type() != Flag::TYPE_MAYBE_BOOL && 458 flag->type() != Flag::TYPE_ARGS && value == nullptr) { 459 if (i < *argc) { 460 value = argv[i++]; 461 } 462 if (!value) { 463 PrintF(stderr, "Error: missing value for flag %s of type %s\n", arg, 464 Type2String(flag->type())); 465 return_code = j; 466 break; 467 } 468 } 469 470 // set the flag 471 char* endp = const_cast<char*>(""); // *endp is only read 472 switch (flag->type()) { 473 case Flag::TYPE_BOOL: 474 *flag->bool_variable() = !negated; 475 break; 476 case Flag::TYPE_MAYBE_BOOL: 477 *flag->maybe_bool_variable() = MaybeBoolFlag::Create(true, !negated); 478 break; 479 case Flag::TYPE_INT: 480 *flag->int_variable() = static_cast<int>(strtol(value, &endp, 10)); 481 break; 482 case Flag::TYPE_UINT: 483 if (!TryParseUnsigned(flag, arg, value, &endp, 484 flag->uint_variable())) { 485 return_code = j; 486 } 487 break; 488 case Flag::TYPE_UINT64: 489 if (!TryParseUnsigned(flag, arg, value, &endp, 490 flag->uint64_variable())) { 491 return_code = j; 492 } 493 break; 494 case Flag::TYPE_FLOAT: 495 *flag->float_variable() = strtod(value, &endp); 496 break; 497 case Flag::TYPE_SIZE_T: 498 if (!TryParseUnsigned(flag, arg, value, &endp, 499 flag->size_t_variable())) { 500 return_code = j; 501 } 502 break; 503 case Flag::TYPE_STRING: 504 flag->set_string_value(value ? StrDup(value) : nullptr, true); 505 break; 506 case Flag::TYPE_ARGS: { 507 int start_pos = (value == nullptr) ? i : i - 1; 508 int js_argc = *argc - start_pos; 509 const char** js_argv = NewArray<const char*>(js_argc); 510 if (value != nullptr) { 511 js_argv[0] = StrDup(value); 512 } 513 for (int k = i; k < *argc; k++) { 514 js_argv[k - start_pos] = StrDup(argv[k]); 515 } 516 *flag->args_variable() = JSArguments::Create(js_argc, js_argv); 517 i = *argc; // Consume all arguments 518 break; 519 } 520 } 521 522 // handle errors 523 bool is_bool_type = flag->type() == Flag::TYPE_BOOL || 524 flag->type() == Flag::TYPE_MAYBE_BOOL; 525 if ((is_bool_type && value != nullptr) || (!is_bool_type && negated) || 526 *endp != '\0') { 527 // TODO(neis): TryParseUnsigned may return with {*endp == '\0'} even in 528 // an error case. 529 PrintF(stderr, "Error: illegal value for flag %s of type %s\n", arg, 530 Type2String(flag->type())); 531 if (is_bool_type) { 532 PrintF(stderr, 533 "To set or unset a boolean flag, use --flag or --no-flag.\n"); 534 } 535 return_code = j; 536 break; 537 } 538 539 // remove the flag & value from the command 540 if (remove_flags) { 541 while (j < i) { 542 argv[j++] = nullptr; 543 } 544 } 545 } 546 } 547 548 if (FLAG_help) { 549 PrintHelp(); 550 exit(0); 551 } 552 553 if (remove_flags) { 554 // shrink the argument list 555 int j = 1; 556 for (int i = 1; i < *argc; i++) { 557 if (argv[i] != nullptr) argv[j++] = argv[i]; 558 } 559 *argc = j; 560 } else if (return_code != 0) { 561 if (return_code + 1 < *argc) { 562 PrintF(stderr, "The remaining arguments were ignored:"); 563 for (int i = return_code + 1; i < *argc; ++i) { 564 PrintF(stderr, " %s", argv[i]); 565 } 566 PrintF(stderr, "\n"); 567 } 568 } 569 if (return_code != 0) PrintF(stderr, "Try --help for options\n"); 570 571 return return_code; 572 } 573 574 575 static char* SkipWhiteSpace(char* p) { 576 while (*p != '\0' && isspace(*p) != 0) p++; 577 return p; 578 } 579 580 581 static char* SkipBlackSpace(char* p) { 582 while (*p != '\0' && isspace(*p) == 0) p++; 583 return p; 584 } 585 586 587 // static 588 int FlagList::SetFlagsFromString(const char* str, int len) { 589 // make a 0-terminated copy of str 590 ScopedVector<char> copy0(len + 1); 591 MemCopy(copy0.start(), str, len); 592 copy0[len] = '\0'; 593 594 // strip leading white space 595 char* copy = SkipWhiteSpace(copy0.start()); 596 597 // count the number of 'arguments' 598 int argc = 1; // be compatible with SetFlagsFromCommandLine() 599 for (char* p = copy; *p != '\0'; argc++) { 600 p = SkipBlackSpace(p); 601 p = SkipWhiteSpace(p); 602 } 603 604 // allocate argument array 605 ScopedVector<char*> argv(argc); 606 607 // split the flags string into arguments 608 argc = 1; // be compatible with SetFlagsFromCommandLine() 609 for (char* p = copy; *p != '\0'; argc++) { 610 argv[argc] = p; 611 p = SkipBlackSpace(p); 612 if (*p != '\0') *p++ = '\0'; // 0-terminate argument 613 p = SkipWhiteSpace(p); 614 } 615 616 return SetFlagsFromCommandLine(&argc, argv.start(), false); 617 } 618 619 620 // static 621 void FlagList::ResetAllFlags() { 622 for (size_t i = 0; i < num_flags; ++i) { 623 flags[i].Reset(); 624 } 625 } 626 627 628 // static 629 void FlagList::PrintHelp() { 630 CpuFeatures::Probe(false); 631 CpuFeatures::PrintTarget(); 632 CpuFeatures::PrintFeatures(); 633 634 StdoutStream os; 635 os << "Synopsis:\n" 636 " shell [options] [--shell] [<file>...]\n" 637 " d8 [options] [-e <string>] [--shell] [[--module] <file>...]\n\n" 638 " -e execute a string in V8\n" 639 " --shell run an interactive JavaScript shell\n" 640 " --module execute a file as a JavaScript module\n\n" 641 "Note: the --module option is implicitly enabled for *.mjs files.\n\n" 642 "Options:\n"; 643 644 for (const Flag& f : flags) { 645 os << " --"; 646 for (const char* c = f.name(); *c != '\0'; ++c) { 647 os << NormalizeChar(*c); 648 } 649 os << " (" << f.comment() << ")\n" 650 << " type: " << Type2String(f.type()) << " default: " << f 651 << "\n"; 652 } 653 } 654 655 656 static uint32_t flag_hash = 0; 657 658 659 void ComputeFlagListHash() { 660 std::ostringstream modified_args_as_string; 661 #ifdef DEBUG 662 modified_args_as_string << "debug"; 663 #endif // DEBUG 664 if (FLAG_embedded_builtins) { 665 modified_args_as_string << "embedded"; 666 } 667 for (size_t i = 0; i < num_flags; ++i) { 668 Flag* current = &flags[i]; 669 if (!current->IsDefault()) { 670 modified_args_as_string << i; 671 modified_args_as_string << *current; 672 } 673 } 674 std::string args(modified_args_as_string.str()); 675 flag_hash = static_cast<uint32_t>( 676 base::hash_range(args.c_str(), args.c_str() + args.length())); 677 } 678 679 680 // static 681 void FlagList::EnforceFlagImplications() { 682 #define FLAG_MODE_DEFINE_IMPLICATIONS 683 #include "src/flag-definitions.h" // NOLINT(build/include) 684 #undef FLAG_MODE_DEFINE_IMPLICATIONS 685 ComputeFlagListHash(); 686 } 687 688 689 uint32_t FlagList::Hash() { return flag_hash; } 690 } // namespace internal 691 } // namespace v8 692