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