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 <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