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