• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
typev8::internal::__anon792b1cab0111::Flag46   FlagType type() const { return type_; }
47 
namev8::internal::__anon792b1cab0111::Flag48   const char* name() const { return name_; }
49 
commentv8::internal::__anon792b1cab0111::Flag50   const char* comment() const { return cmt_; }
51 
bool_variablev8::internal::__anon792b1cab0111::Flag52   bool* bool_variable() const {
53     DCHECK(type_ == TYPE_BOOL);
54     return reinterpret_cast<bool*>(valptr_);
55   }
56 
maybe_bool_variablev8::internal::__anon792b1cab0111::Flag57   MaybeBoolFlag* maybe_bool_variable() const {
58     DCHECK(type_ == TYPE_MAYBE_BOOL);
59     return reinterpret_cast<MaybeBoolFlag*>(valptr_);
60   }
61 
int_variablev8::internal::__anon792b1cab0111::Flag62   int* int_variable() const {
63     DCHECK(type_ == TYPE_INT);
64     return reinterpret_cast<int*>(valptr_);
65   }
66 
float_variablev8::internal::__anon792b1cab0111::Flag67   double* float_variable() const {
68     DCHECK(type_ == TYPE_FLOAT);
69     return reinterpret_cast<double*>(valptr_);
70   }
71 
string_valuev8::internal::__anon792b1cab0111::Flag72   const char* string_value() const {
73     DCHECK(type_ == TYPE_STRING);
74     return *reinterpret_cast<const char**>(valptr_);
75   }
76 
set_string_valuev8::internal::__anon792b1cab0111::Flag77   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 
args_variablev8::internal::__anon792b1cab0111::Flag85   JSArguments* args_variable() const {
86     DCHECK(type_ == TYPE_ARGS);
87     return reinterpret_cast<JSArguments*>(valptr_);
88   }
89 
bool_defaultv8::internal::__anon792b1cab0111::Flag90   bool bool_default() const {
91     DCHECK(type_ == TYPE_BOOL);
92     return *reinterpret_cast<const bool*>(defptr_);
93   }
94 
int_defaultv8::internal::__anon792b1cab0111::Flag95   int int_default() const {
96     DCHECK(type_ == TYPE_INT);
97     return *reinterpret_cast<const int*>(defptr_);
98   }
99 
float_defaultv8::internal::__anon792b1cab0111::Flag100   double float_default() const {
101     DCHECK(type_ == TYPE_FLOAT);
102     return *reinterpret_cast<const double*>(defptr_);
103   }
104 
string_defaultv8::internal::__anon792b1cab0111::Flag105   const char* string_default() const {
106     DCHECK(type_ == TYPE_STRING);
107     return *reinterpret_cast<const char* const *>(defptr_);
108   }
109 
args_defaultv8::internal::__anon792b1cab0111::Flag110   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.
IsDefaultv8::internal::__anon792b1cab0111::Flag116   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.
Resetv8::internal::__anon792b1cab0111::Flag141   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 
Type2String(Flag::FlagType type)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 
operator <<(std::ostream & os,const Flag & flag)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
argv()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 
NormalizeChar(char ch)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.
SplitArgument(const char * arg,char * buffer,int buffer_size,const char ** name,const char ** value,bool * is_bool)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 
EqualNames(const char * a,const char * b)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 
FindFlag(const char * name)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
SetFlagsFromCommandLine(int * argc,char ** argv,bool remove_flags)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         return_code = j;
433         break;
434       }
435 
436       // remove the flag & value from the command
437       if (remove_flags) {
438         while (j < i) {
439           argv[j++] = NULL;
440         }
441       }
442     }
443   }
444 
445   // shrink the argument list
446   if (remove_flags) {
447     int j = 1;
448     for (int i = 1; i < *argc; i++) {
449       if (argv[i] != NULL)
450         argv[j++] = argv[i];
451     }
452     *argc = j;
453   }
454 
455   if (FLAG_help) {
456     PrintHelp();
457     exit(0);
458   }
459   // parsed all flags successfully
460   return return_code;
461 }
462 
463 
SkipWhiteSpace(char * p)464 static char* SkipWhiteSpace(char* p) {
465   while (*p != '\0' && isspace(*p) != 0) p++;
466   return p;
467 }
468 
469 
SkipBlackSpace(char * p)470 static char* SkipBlackSpace(char* p) {
471   while (*p != '\0' && isspace(*p) == 0) p++;
472   return p;
473 }
474 
475 
476 // static
SetFlagsFromString(const char * str,int len)477 int FlagList::SetFlagsFromString(const char* str, int len) {
478   // make a 0-terminated copy of str
479   ScopedVector<char> copy0(len + 1);
480   MemCopy(copy0.start(), str, len);
481   copy0[len] = '\0';
482 
483   // strip leading white space
484   char* copy = SkipWhiteSpace(copy0.start());
485 
486   // count the number of 'arguments'
487   int argc = 1;  // be compatible with SetFlagsFromCommandLine()
488   for (char* p = copy; *p != '\0'; argc++) {
489     p = SkipBlackSpace(p);
490     p = SkipWhiteSpace(p);
491   }
492 
493   // allocate argument array
494   ScopedVector<char*> argv(argc);
495 
496   // split the flags string into arguments
497   argc = 1;  // be compatible with SetFlagsFromCommandLine()
498   for (char* p = copy; *p != '\0'; argc++) {
499     argv[argc] = p;
500     p = SkipBlackSpace(p);
501     if (*p != '\0') *p++ = '\0';  // 0-terminate argument
502     p = SkipWhiteSpace(p);
503   }
504 
505   // set the flags
506   int result = SetFlagsFromCommandLine(&argc, argv.start(), false);
507 
508   return result;
509 }
510 
511 
512 // static
ResetAllFlags()513 void FlagList::ResetAllFlags() {
514   for (size_t i = 0; i < num_flags; ++i) {
515     flags[i].Reset();
516   }
517 }
518 
519 
520 // static
PrintHelp()521 void FlagList::PrintHelp() {
522   CpuFeatures::Probe(false);
523   CpuFeatures::PrintTarget();
524   CpuFeatures::PrintFeatures();
525 
526   OFStream os(stdout);
527   os << "Usage:\n"
528      << "  shell [options] -e string\n"
529      << "    execute string in V8\n"
530      << "  shell [options] file1 file2 ... filek\n"
531      << "    run JavaScript scripts in file1, file2, ..., filek\n"
532      << "  shell [options]\n"
533      << "  shell [options] --shell [file1 file2 ... filek]\n"
534      << "    run an interactive JavaScript shell\n"
535      << "  d8 [options] file1 file2 ... filek\n"
536      << "  d8 [options]\n"
537      << "  d8 [options] --shell [file1 file2 ... filek]\n"
538      << "    run the new debugging shell\n\n"
539      << "Options:\n";
540   for (size_t i = 0; i < num_flags; ++i) {
541     Flag* f = &flags[i];
542     os << "  --" << f->name() << " (" << f->comment() << ")\n"
543        << "        type: " << Type2String(f->type()) << "  default: " << *f
544        << "\n";
545   }
546 }
547 
548 
549 static uint32_t flag_hash = 0;
550 
551 
ComputeFlagListHash()552 void ComputeFlagListHash() {
553   std::ostringstream modified_args_as_string;
554 #ifdef DEBUG
555   modified_args_as_string << "debug";
556 #endif  // DEBUG
557   for (size_t i = 0; i < num_flags; ++i) {
558     Flag* current = &flags[i];
559     if (!current->IsDefault()) {
560       modified_args_as_string << i;
561       modified_args_as_string << *current;
562     }
563   }
564   std::string args(modified_args_as_string.str());
565   flag_hash = static_cast<uint32_t>(
566       base::hash_range(args.c_str(), args.c_str() + args.length()));
567 }
568 
569 
570 // static
EnforceFlagImplications()571 void FlagList::EnforceFlagImplications() {
572 #define FLAG_MODE_DEFINE_IMPLICATIONS
573 #include "src/flag-definitions.h"  // NOLINT(build/include)
574 #undef FLAG_MODE_DEFINE_IMPLICATIONS
575   ComputeFlagListHash();
576 }
577 
578 
Hash()579 uint32_t FlagList::Hash() { return flag_hash; }
580 }  // namespace internal
581 }  // namespace v8
582