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