Home | History | Annotate | Download | only in src
      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